Skip to content

Commit

Permalink
Fix incorrect pending reward calculation (0.94) (#7394)
Browse files Browse the repository at this point in the history
- Use deduped account_balance info in EntityStakeRepository.createEntityStateStart()
- Add SQL migration to remove incorrect entity_stake rows

Signed-off-by: Edwin Greene <[email protected]>
Signed-off-by: Xin Li <[email protected]>
Co-authored-by: Edwin Greene <[email protected]>
  • Loading branch information
xin-hedera and edwin-greene authored Dec 15, 2023
1 parent cde5054 commit 1005ba3
Show file tree
Hide file tree
Showing 4 changed files with 208 additions and 67 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,13 @@ select lower(timestamp_range) as timestamp from entity_history where id = 800
order by epoch_day
limit 1
), balance_timestamp as (
select ab.consensus_timestamp
from account_balance ab, end_period ep
where ab.account_id = 2 and ab.consensus_timestamp <= ep.consensus_timestamp
order by ab.consensus_timestamp desc
limit 1
select ab.consensus_timestamp
from account_balance ab, end_period ep
where ab.account_id = 2 and
ab.consensus_timestamp > (ep.consensus_timestamp - 2678400000000000) and
ab.consensus_timestamp <= ep.consensus_timestamp
order by ab.consensus_timestamp desc
limit 1
), entity_state as (
select
decline_reward,
Expand All @@ -89,9 +91,11 @@ where deleted is not true and type in ('ACCOUNT', 'CONTRACT') and timestamp_rang
order by id, timestamp_range desc
) as latest_history
), balance_snapshot as (
select account_id, balance
from account_balance ab
join balance_timestamp bt on bt.consensus_timestamp = ab.consensus_timestamp
select distinct on (account_id) account_id, balance
from account_balance ab, balance_timestamp bt
where ab.consensus_timestamp > (bt.consensus_timestamp - 2678400000000000) and
ab.consensus_timestamp <= bt.consensus_timestamp
order by account_id, ab.consensus_timestamp desc
)
insert into entity_state_start (balance, decline_reward, id, staked_account_id, staked_node_id, stake_period_start)
select
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
create or replace procedure remove_incorrect_entity_stake() as
$$
declare
one_sec_in_ns constant bigint := 10^9;

last_good_lower_timestamp bigint;
migration_timestamp bigint;
begin
-- The last known good entity stake's timestamp_range should contain the time when V1.89.2 was installed.
select (extract(epoch from installed_on) * one_sec_in_ns)::bigint into migration_timestamp
from flyway_schema_history
where version = '1.89.2';
if not found then
return;
end if;
raise notice 'migration timestamp: %', migration_timestamp;

select lower(timestamp_range) into last_good_lower_timestamp
from entity_stake_history
where id = 800 and timestamp_range @> migration_timestamp;
if not found then
return;
end if;

-- Copy the last known good entity_stake rows to the current table
truncate table entity_stake;
insert into entity_stake (decline_reward_start, end_stake_period, id, pending_reward, staked_node_id_start,
staked_to_me, stake_total_start, timestamp_range)
select
decline_reward_start,
end_stake_period,
id,
pending_reward,
staked_node_id_start,
staked_to_me,
stake_total_start,
int8range(lower(timestamp_range), null)
from entity_stake_history
where timestamp_range @> migration_timestamp;

-- Delete incorrect history rows
raise notice 'deleting entity_stake_history rows at or after: %', last_good_lower_timestamp;
delete from entity_stake_history
where timestamp_range in (
select timestamp_range
from entity_stake_history
where id = 800 and lower(timestamp_range) >= last_good_lower_timestamp
);
end;
$$ language plpgsql;

call remove_incorrect_entity_stake();

drop procedure if exists remove_incorrect_entity_stake();
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ void calculate(boolean skipOnePeriod) {
var newPeriodInstant = TestUtils.asStartOfEpochDay(epochDay + numPeriods);
long nodeStakeTimestamp = DomainUtils.convertToNanosMax(newPeriodInstant.plusNanos(2000L));
long balanceTimestamp = DomainUtils.convertToNanosMax(newPeriodInstant.plusNanos(1000L));
long previousBalanceTimestamp = balanceTimestamp - 1000;

// the lower timestamp is the consensus timestamp of the previous NodeStakeUpdateTransaction
long entityStakeLowerTimestamp = DomainUtils.convertToNanosMax(TestUtils.asStartOfEpochDay(epochDay - 1)) + 20L;
Expand Down Expand Up @@ -138,25 +139,41 @@ void calculate(boolean skipOnePeriod) {
long accountId4 = domainBuilder.id();

// account balance
domainBuilder
.accountBalance()
.customize(ab -> ab.id(new Id(balanceTimestamp, treasury.toEntityId())))
.persist();
domainBuilder
.accountBalance()
.customize(ab -> ab.id(new Id(previousBalanceTimestamp, treasury.toEntityId())))
.persist();
domainBuilder
.accountBalance()
.customize(ab -> ab.id(new Id(balanceTimestamp, account800.toEntityId())))
.persist();
domainBuilder
.accountBalance()
.customize(ab -> ab.id(new Id(balanceTimestamp, treasury.toEntityId())))
.customize(ab -> ab.id(new Id(previousBalanceTimestamp, account800.toEntityId())))
.persist();
domainBuilder
.accountBalance()
.customize(ab -> ab.balance(account1Balance).id(new Id(balanceTimestamp, account1.toEntityId())))
.persist();
domainBuilder
.accountBalance()
.customize(ab -> ab.balance(account2Balance).id(new Id(balanceTimestamp, account2.toEntityId())))
.customize(ab ->
ab.balance(account1Balance - 100).id(new Id(previousBalanceTimestamp, account1.toEntityId())))
.persist();
// Deduped
domainBuilder
.accountBalance()
.customize(ab -> ab.balance(account3Balance).id(new Id(balanceTimestamp, account3.toEntityId())))
.customize(
ab -> ab.balance(account2Balance).id(new Id(previousBalanceTimestamp, account2.toEntityId())))
.persist();
domainBuilder
.accountBalance()
.customize(
ab -> ab.balance(account3Balance).id(new Id(previousBalanceTimestamp, account3.toEntityId())))
.persist();

long creditAmount = 50 * TINYBARS_IN_ONE_HBAR;
Expand Down Expand Up @@ -244,7 +261,8 @@ void calculateTwoPeriods() {
// Account 800's entity stake is up to end period epochDay - 2, calculation for period epochDay - 1 is skipped
// for some reason, when the NodeStakeUpdate transaction for end period epochDay is processed, there should be
// entity stake calculation done for two periods, epochDay - 1 and epochDay
long balanceTimestamp = secondLastNodeStakeTimestamp - 2000L;
long balanceTimestamp = secondLastNodeStakeTimestamp - 2000;
long previousBalanceTimestamp = balanceTimestamp - 1000;
domainBuilder.entity(STAKING_REWARD_ACCOUNT, balanceTimestamp - 5000).persist();
var entityStake800 = domainBuilder
.entityStake()
Expand All @@ -262,11 +280,15 @@ void calculateTwoPeriods() {
// account balance
domainBuilder
.accountBalance()
.customize(ab -> ab.id(new Id(balanceTimestamp, EntityId.of(STAKING_REWARD_ACCOUNT))))
.customize(ab -> ab.id(new Id(balanceTimestamp, EntityId.of(TREASURY))))
.persist();
domainBuilder
.accountBalance()
.customize(ab -> ab.id(new Id(previousBalanceTimestamp, EntityId.of(TREASURY))))
.persist();
domainBuilder
.accountBalance()
.customize(ab -> ab.id(new Id(balanceTimestamp, EntityId.of(TREASURY))))
.customize(ab -> ab.id(new Id(previousBalanceTimestamp, EntityId.of(STAKING_REWARD_ACCOUNT))))
.persist();
domainBuilder
.nodeStake()
Expand Down
Loading

0 comments on commit 1005ba3

Please sign in to comment.