From 49b7daa39d35b53f57cd269c7c1cda3c2ef4d92a Mon Sep 17 00:00:00 2001 From: Zahary Karadjov Date: Thu, 27 Jan 2022 18:37:30 +0200 Subject: [PATCH] [ncli_db] bugfix: take into account finalization delay in reward calc post Altair This fixes a problem affecting Prater's epoch 64444. --- beacon_chain/spec/state_transition_epoch.nim | 20 ++++++------- ncli/ncli_common.nim | 30 +++++++++++++++---- .../altair/test_fixture_rewards.nim | 4 ++- .../bellatrix/test_fixture_rewards.nim | 4 ++- 4 files changed, 39 insertions(+), 19 deletions(-) diff --git a/beacon_chain/spec/state_transition_epoch.nim b/beacon_chain/spec/state_transition_epoch.nim index 31ff8cc7d..1380791dc 100644 --- a/beacon_chain/spec/state_transition_epoch.nim +++ b/beacon_chain/spec/state_transition_epoch.nim @@ -466,11 +466,6 @@ func is_in_inactivity_leak(finality_delay: uint64): bool = func get_finality_delay*(state: ForkyBeaconState): uint64 = get_previous_epoch(state) - state.finalized_checkpoint.epoch -# https://github.com/ethereum/consensus-specs/blob/v1.1.8/specs/phase0/beacon-chain.md#rewards-and-penalties-1 -func is_in_inactivity_leak(state: altair.BeaconState | bellatrix.BeaconState): bool = - # TODO remove this, see above - get_finality_delay(state) > MIN_EPOCHS_TO_INACTIVITY_PENALTY - func get_attestation_component_reward*(attesting_balance: Gwei, total_balance: Gwei, base_reward: uint64, @@ -636,8 +631,8 @@ func get_base_reward_increment*( func get_flag_index_reward*(state: altair.BeaconState | bellatrix.BeaconState, base_reward: Gwei, active_increments: Gwei, unslashed_participating_increments: Gwei, - weight: uint64): Gwei = - if not is_in_inactivity_leak(state): + weight, finality_delay: uint64): Gwei = + if not is_in_inactivity_leak(finality_delay): let reward_numerator = base_reward * weight * unslashed_participating_increments reward_numerator div (active_increments * WEIGHT_DENOMINATOR) @@ -657,7 +652,8 @@ func get_active_increments*(info: altair.EpochInfo): Gwei = iterator get_flag_index_deltas*( state: altair.BeaconState | bellatrix.BeaconState, flag_index: int, base_reward_per_increment: Gwei, - info: var altair.EpochInfo): + info: var altair.EpochInfo, + finality_delay: uint64): (ValidatorIndex, RewardDelta) = ## Return the deltas for a given ``flag_index`` by scanning through the ## participation flags. @@ -689,7 +685,7 @@ iterator get_flag_index_deltas*( (vidx, RewardDelta( rewards: get_flag_index_reward( state, base_reward, active_increments, - unslashed_participating_increments, weight), + unslashed_participating_increments, weight, finality_delay), penalties: 0.Gwei)) elif flag_index != TIMELY_HEAD_FLAG_INDEX: (vidx, RewardDelta( @@ -780,11 +776,12 @@ func process_rewards_and_penalties( total_active_balance = info.balances.current_epoch base_reward_per_increment = get_base_reward_per_increment( total_active_balance) + finality_delay = get_finality_delay(state) doAssert state.validators.len() == info.validators.len() for flag_index in 0 ..< PARTICIPATION_FLAG_WEIGHTS.len: for validator_index, delta in get_flag_index_deltas( - state, flag_index, base_reward_per_increment, info): + state, flag_index, base_reward_per_increment, info, finality_delay): info.validators[validator_index].delta.add(delta) for validator_index, penalty in get_inactivity_penalty_deltas( @@ -1004,7 +1001,8 @@ func process_inactivity_updates*( let previous_epoch = get_previous_epoch(state) # get_eligible_validator_indices() - not_in_inactivity_leak = not is_in_inactivity_leak(state) + finality_delay = get_finality_delay(state) + not_in_inactivity_leak = not is_in_inactivity_leak(finality_delay) for index in 0'u64 ..< state.validators.lenu64: if not is_eligible_validator(info.validators[index]): diff --git a/ncli/ncli_common.nim b/ncli/ncli_common.nim index cadf14865..ca4865632 100644 --- a/ncli/ncli_common.nim +++ b/ncli/ncli_common.nim @@ -111,7 +111,10 @@ proc collectSlashings( validator[].get_slashing_penalty( adjusted_total_slashing_balance, total_balance).int64 -proc getFinalizedCheckpoint(state: phase0.BeaconState, balances: TotalBalances): +proc getFinalizedCheckpoint(state: ForkyBeaconState, + total_active_balance, + previous_epoch_target_balance, + current_epoch_target_balance: Gwei): Checkpoint = if get_current_epoch(state) <= GENESIS_EPOCH + 1: return state.finalized_checkpoint @@ -128,11 +131,10 @@ proc getFinalizedCheckpoint(state: phase0.BeaconState, balances: TotalBalances): (uint8(state.justification_bits) shl 1) and uint8((2^JUSTIFICATION_BITS_LENGTH) - 1)) - let total_active_balance = balances.current_epoch - if balances.previous_epoch_target_attesters * 3 >= total_active_balance * 2: + if previous_epoch_target_balance * 3 >= total_active_balance * 2: uint8(justification_bits).setBit 1 - if balances.current_epoch_target_attesters * 3 >= total_active_balance * 2: + if current_epoch_target_balance * 3 >= total_active_balance * 2: uint8(justification_bits).setBit 0 # Process finalizations @@ -164,6 +166,19 @@ proc getFinalizedCheckpoint(state: phase0.BeaconState, balances: TotalBalances): return state.finalized_checkpoint +proc getFinalizedCheckpoint(state: phase0.BeaconState, balances: TotalBalances): + Checkpoint = + getFinalizedCheckpoint(state, balances.current_epoch, + balances.previous_epoch_target_attesters, + balances.current_epoch_target_attesters) + +proc getFinalizedCheckpoint( + state: altair.BeaconState | bellatrix.BeaconState, + balances: UnslashedParticipatingBalances): Checkpoint = + getFinalizedCheckpoint(state, balances.current_epoch, + balances.previous_epoch[TIMELY_TARGET_FLAG_INDEX], + balances.current_epoch_TIMELY_TARGET) + func getFinalityDelay*(state: ForkyBeaconState, finalizedCheckpoint: Checkpoint): uint64 = state.get_previous_epoch - finalizedCheckpoint.epoch @@ -249,10 +264,12 @@ proc collectEpochRewardsAndPenalties*( total_active_balance = info.balances.current_epoch base_reward_per_increment = get_base_reward_per_increment( total_active_balance) + finalized_checkpoint = state.getFinalizedCheckpoint(info.balances) + finality_delay = getFinalityDelay(state, finalized_checkpoint) for flag_index in 0 ..< PARTICIPATION_FLAG_WEIGHTS.len: for validator_index, delta in get_flag_index_deltas( - state, flag_index, base_reward_per_increment, info): + state, flag_index, base_reward_per_increment, info, finality_delay): template rp: untyped = rewardsAndPenalties[validator_index] let @@ -264,7 +281,8 @@ proc collectEpochRewardsAndPenalties*( max_flag_index_reward = get_flag_index_reward( state, base_reward, active_increments, unslashed_participating_increment, - PARTICIPATION_FLAG_WEIGHTS[flag_index].uint64) + PARTICIPATION_FLAG_WEIGHTS[flag_index].uint64, + finalityDelay) case flag_index of TIMELY_SOURCE_FLAG_INDEX: diff --git a/tests/consensus_spec/altair/test_fixture_rewards.nim b/tests/consensus_spec/altair/test_fixture_rewards.nim index 631dda4a3..2d2af6c5a 100644 --- a/tests/consensus_spec/altair/test_fixture_rewards.nim +++ b/tests/consensus_spec/altair/test_fixture_rewards.nim @@ -61,9 +61,11 @@ proc runTest(rewardsDir, identifier: string) = Deltas.init(state[].validators.len)] inactivityPenaltyDeltas2 = Deltas.init(state[].validators.len) + let finality_delay = get_finality_delay(state[]) + for flag_index in 0 ..< PARTICIPATION_FLAG_WEIGHTS.len: for validator_index, delta in get_flag_index_deltas( - state[], flag_index, base_reward_per_increment, info): + state[], flag_index, base_reward_per_increment, info, finality_delay): if not is_eligible_validator(info.validators[validator_index]): continue flagDeltas2[flag_index].rewards[validator_index] = delta.rewards diff --git a/tests/consensus_spec/bellatrix/test_fixture_rewards.nim b/tests/consensus_spec/bellatrix/test_fixture_rewards.nim index 6754aae0f..5261ecbd0 100644 --- a/tests/consensus_spec/bellatrix/test_fixture_rewards.nim +++ b/tests/consensus_spec/bellatrix/test_fixture_rewards.nim @@ -61,9 +61,11 @@ proc runTest(rewardsDir, identifier: string) = Deltas.init(state[].validators.len)] inactivityPenaltyDeltas2 = Deltas.init(state[].validators.len) + let finality_delay = get_finality_delay(state[]) + for flag_index in 0 ..< PARTICIPATION_FLAG_WEIGHTS.len: for validator_index, delta in get_flag_index_deltas( - state[], flag_index, base_reward_per_increment, info): + state[], flag_index, base_reward_per_increment, info, finality_delay): if not is_eligible_validator(info.validators[validator_index]): continue flagDeltas2[flag_index].rewards[validator_index] = delta.rewards