From 19a9b4b064f62ae5d19277f3c42c3b40a23fef88 Mon Sep 17 00:00:00 2001 From: terence tsao Date: Sat, 10 Apr 2021 09:12:52 -0700 Subject: [PATCH] Refactor finality helpers to helpers pkg (#8734) Co-authored-by: Victor Farazdagi --- .../core/epoch/precompute/reward_penalty.go | 28 +----- .../epoch/precompute/reward_penalty_test.go | 52 ----------- .../core/helpers/rewards_penalties.go | 18 ++++ .../core/helpers/rewards_penalties_test.go | 91 +++++++++++++++++++ 4 files changed, 114 insertions(+), 75 deletions(-) diff --git a/beacon-chain/core/epoch/precompute/reward_penalty.go b/beacon-chain/core/epoch/precompute/reward_penalty.go index fc09821139f6..ac2dfa513e1f 100644 --- a/beacon-chain/core/epoch/precompute/reward_penalty.go +++ b/beacon-chain/core/epoch/precompute/reward_penalty.go @@ -88,7 +88,7 @@ func attestationDelta(pBal *Balance, v *Validator, prevEpoch, finalizedEpoch typ maxAttesterReward := br - proposerReward r += maxAttesterReward / uint64(v.InclusionDistance) - if isInInactivityLeak(prevEpoch, finalizedEpoch) { + if helpers.IsInInactivityLeak(prevEpoch, finalizedEpoch) { // Since full base reward will be canceled out by inactivity penalty deltas, // optimal participation receives full base reward compensation here. r += br @@ -103,7 +103,7 @@ func attestationDelta(pBal *Balance, v *Validator, prevEpoch, finalizedEpoch typ // Process target reward / penalty if v.IsPrevEpochTargetAttester && !v.IsSlashed { - if isInInactivityLeak(prevEpoch, finalizedEpoch) { + if helpers.IsInInactivityLeak(prevEpoch, finalizedEpoch) { // Since full base reward will be canceled out by inactivity penalty deltas, // optimal participation receives full base reward compensation here. r += br @@ -117,7 +117,7 @@ func attestationDelta(pBal *Balance, v *Validator, prevEpoch, finalizedEpoch typ // Process head reward / penalty if v.IsPrevEpochHeadAttester && !v.IsSlashed { - if isInInactivityLeak(prevEpoch, finalizedEpoch) { + if helpers.IsInInactivityLeak(prevEpoch, finalizedEpoch) { // Since full base reward will be canceled out by inactivity penalty deltas, // optimal participation receives full base reward compensation here. r += br @@ -130,9 +130,9 @@ func attestationDelta(pBal *Balance, v *Validator, prevEpoch, finalizedEpoch typ } // Process finality delay penalty - finalityDelay := finalityDelay(prevEpoch, finalizedEpoch) + finalityDelay := helpers.FinalityDelay(prevEpoch, finalizedEpoch) - if isInInactivityLeak(prevEpoch, finalizedEpoch) { + if helpers.IsInInactivityLeak(prevEpoch, finalizedEpoch) { // If validator is performing optimally, this cancels all rewards for a neutral balance. proposerReward := br / params.BeaconConfig().ProposerRewardQuotient p += baseRewardsPerEpoch*br - proposerReward @@ -177,21 +177,3 @@ func ProposersDelta(state iface.ReadOnlyBeaconState, pBal *Balance, vp []*Valida } return rewards, nil } - -// isInInactivityLeak returns true if the state is experiencing inactivity leak. -// -// Spec code: -// def is_in_inactivity_leak(state: BeaconState) -> bool: -// return get_finality_delay(state) > MIN_EPOCHS_TO_INACTIVITY_PENALTY -func isInInactivityLeak(prevEpoch, finalizedEpoch types.Epoch) bool { - return finalityDelay(prevEpoch, finalizedEpoch) > params.BeaconConfig().MinEpochsToInactivityPenalty -} - -// finalityDelay returns the finality delay using the beacon state. -// -// Spec code: -// def get_finality_delay(state: BeaconState) -> uint64: -// return get_previous_epoch(state) - state.finalized_checkpoint.epoch -func finalityDelay(prevEpoch, finalizedEpoch types.Epoch) types.Epoch { - return prevEpoch - finalizedEpoch -} diff --git a/beacon-chain/core/epoch/precompute/reward_penalty_test.go b/beacon-chain/core/epoch/precompute/reward_penalty_test.go index de29ae52ce53..d5cd8f94954f 100644 --- a/beacon-chain/core/epoch/precompute/reward_penalty_test.go +++ b/beacon-chain/core/epoch/precompute/reward_penalty_test.go @@ -350,55 +350,3 @@ func TestProposerDeltaPrecompute_SlashedCase(t *testing.T) { require.NoError(t, err) assert.Equal(t, uint64(0), r[proposerIndex], "Unexpected proposer reward for slashed") } - -func TestFinalityDelay(t *testing.T) { - base := buildState(params.BeaconConfig().SlotsPerEpoch*10, 1) - base.FinalizedCheckpoint = ðpb.Checkpoint{Epoch: 3} - beaconState, err := stateV0.InitializeFromProto(base) - require.NoError(t, err) - prevEpoch := types.Epoch(0) - finalizedEpoch := types.Epoch(0) - // Set values for each test case - setVal := func() { - prevEpoch = helpers.PrevEpoch(beaconState) - finalizedEpoch = beaconState.FinalizedCheckpointEpoch() - } - setVal() - d := finalityDelay(prevEpoch, finalizedEpoch) - w := helpers.PrevEpoch(beaconState) - beaconState.FinalizedCheckpointEpoch() - assert.Equal(t, w, d, "Did not get wanted finality delay") - - require.NoError(t, beaconState.SetFinalizedCheckpoint(ðpb.Checkpoint{Epoch: 4})) - setVal() - d = finalityDelay(prevEpoch, finalizedEpoch) - w = helpers.PrevEpoch(beaconState) - beaconState.FinalizedCheckpointEpoch() - assert.Equal(t, w, d, "Did not get wanted finality delay") - - require.NoError(t, beaconState.SetFinalizedCheckpoint(ðpb.Checkpoint{Epoch: 5})) - setVal() - d = finalityDelay(prevEpoch, finalizedEpoch) - w = helpers.PrevEpoch(beaconState) - beaconState.FinalizedCheckpointEpoch() - assert.Equal(t, w, d, "Did not get wanted finality delay") -} - -func TestIsInInactivityLeak(t *testing.T) { - base := buildState(params.BeaconConfig().SlotsPerEpoch*10, 1) - base.FinalizedCheckpoint = ðpb.Checkpoint{Epoch: 3} - beaconState, err := stateV0.InitializeFromProto(base) - require.NoError(t, err) - prevEpoch := types.Epoch(0) - finalizedEpoch := types.Epoch(0) - // Set values for each test case - setVal := func() { - prevEpoch = helpers.PrevEpoch(beaconState) - finalizedEpoch = beaconState.FinalizedCheckpointEpoch() - } - setVal() - assert.Equal(t, true, isInInactivityLeak(prevEpoch, finalizedEpoch), "Wanted inactivity leak true") - require.NoError(t, beaconState.SetFinalizedCheckpoint(ðpb.Checkpoint{Epoch: 4})) - setVal() - assert.Equal(t, true, isInInactivityLeak(prevEpoch, finalizedEpoch), "Wanted inactivity leak true") - require.NoError(t, beaconState.SetFinalizedCheckpoint(ðpb.Checkpoint{Epoch: 5})) - setVal() - assert.Equal(t, false, isInInactivityLeak(prevEpoch, finalizedEpoch), "Wanted inactivity leak false") -} diff --git a/beacon-chain/core/helpers/rewards_penalties.go b/beacon-chain/core/helpers/rewards_penalties.go index 34b96f984538..85b6db02df6b 100644 --- a/beacon-chain/core/helpers/rewards_penalties.go +++ b/beacon-chain/core/helpers/rewards_penalties.go @@ -119,3 +119,21 @@ func DecreaseBalanceWithVal(currBalance, delta uint64) uint64 { } return currBalance - delta } + +// IsInInactivityLeak returns true if the state is experiencing inactivity leak. +// +// Spec code: +// def is_in_inactivity_leak(state: BeaconState) -> bool: +// return get_finality_delay(state) > MIN_EPOCHS_TO_INACTIVITY_PENALTY +func IsInInactivityLeak(prevEpoch, finalizedEpoch types.Epoch) bool { + return FinalityDelay(prevEpoch, finalizedEpoch) > params.BeaconConfig().MinEpochsToInactivityPenalty +} + +// FinalityDelay returns the finality delay using the beacon state. +// +// Spec code: +// def get_finality_delay(state: BeaconState) -> uint64: +// return get_previous_epoch(state) - state.finalized_checkpoint.epoch +func FinalityDelay(prevEpoch, finalizedEpoch types.Epoch) types.Epoch { + return prevEpoch - finalizedEpoch +} diff --git a/beacon-chain/core/helpers/rewards_penalties_test.go b/beacon-chain/core/helpers/rewards_penalties_test.go index 35c1608e4ed5..2f6636ad082d 100644 --- a/beacon-chain/core/helpers/rewards_penalties_test.go +++ b/beacon-chain/core/helpers/rewards_penalties_test.go @@ -126,3 +126,94 @@ func TestDecreaseBalance_OK(t *testing.T) { assert.Equal(t, test.eb, state.Balances()[test.i], "Incorrect Validator balance") } } + +func TestFinalityDelay(t *testing.T) { + base := buildState(params.BeaconConfig().SlotsPerEpoch*10, 1) + base.FinalizedCheckpoint = ðpb.Checkpoint{Epoch: 3} + beaconState, err := stateV0.InitializeFromProto(base) + require.NoError(t, err) + prevEpoch := types.Epoch(0) + finalizedEpoch := types.Epoch(0) + // Set values for each test case + setVal := func() { + prevEpoch = PrevEpoch(beaconState) + finalizedEpoch = beaconState.FinalizedCheckpointEpoch() + } + setVal() + d := FinalityDelay(prevEpoch, finalizedEpoch) + w := PrevEpoch(beaconState) - beaconState.FinalizedCheckpointEpoch() + assert.Equal(t, w, d, "Did not get wanted finality delay") + + require.NoError(t, beaconState.SetFinalizedCheckpoint(ðpb.Checkpoint{Epoch: 4})) + setVal() + d = FinalityDelay(prevEpoch, finalizedEpoch) + w = PrevEpoch(beaconState) - beaconState.FinalizedCheckpointEpoch() + assert.Equal(t, w, d, "Did not get wanted finality delay") + + require.NoError(t, beaconState.SetFinalizedCheckpoint(ðpb.Checkpoint{Epoch: 5})) + setVal() + d = FinalityDelay(prevEpoch, finalizedEpoch) + w = PrevEpoch(beaconState) - beaconState.FinalizedCheckpointEpoch() + assert.Equal(t, w, d, "Did not get wanted finality delay") +} + +func TestIsInInactivityLeak(t *testing.T) { + base := buildState(params.BeaconConfig().SlotsPerEpoch*10, 1) + base.FinalizedCheckpoint = ðpb.Checkpoint{Epoch: 3} + beaconState, err := stateV0.InitializeFromProto(base) + require.NoError(t, err) + prevEpoch := types.Epoch(0) + finalizedEpoch := types.Epoch(0) + // Set values for each test case + setVal := func() { + prevEpoch = PrevEpoch(beaconState) + finalizedEpoch = beaconState.FinalizedCheckpointEpoch() + } + setVal() + assert.Equal(t, true, IsInInactivityLeak(prevEpoch, finalizedEpoch), "Wanted inactivity leak true") + require.NoError(t, beaconState.SetFinalizedCheckpoint(ðpb.Checkpoint{Epoch: 4})) + setVal() + assert.Equal(t, true, IsInInactivityLeak(prevEpoch, finalizedEpoch), "Wanted inactivity leak true") + require.NoError(t, beaconState.SetFinalizedCheckpoint(ðpb.Checkpoint{Epoch: 5})) + setVal() + assert.Equal(t, false, IsInInactivityLeak(prevEpoch, finalizedEpoch), "Wanted inactivity leak false") +} + +func buildState(slot types.Slot, validatorCount uint64) *pb.BeaconState { + validators := make([]*ethpb.Validator, validatorCount) + for i := 0; i < len(validators); i++ { + validators[i] = ðpb.Validator{ + ExitEpoch: params.BeaconConfig().FarFutureEpoch, + EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance, + } + } + validatorBalances := make([]uint64, len(validators)) + for i := 0; i < len(validatorBalances); i++ { + validatorBalances[i] = params.BeaconConfig().MaxEffectiveBalance + } + latestActiveIndexRoots := make( + [][]byte, + params.BeaconConfig().EpochsPerHistoricalVector, + ) + for i := 0; i < len(latestActiveIndexRoots); i++ { + latestActiveIndexRoots[i] = params.BeaconConfig().ZeroHash[:] + } + latestRandaoMixes := make( + [][]byte, + params.BeaconConfig().EpochsPerHistoricalVector, + ) + for i := 0; i < len(latestRandaoMixes); i++ { + latestRandaoMixes[i] = params.BeaconConfig().ZeroHash[:] + } + return &pb.BeaconState{ + Slot: slot, + Balances: validatorBalances, + Validators: validators, + RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector), + Slashings: make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector), + BlockRoots: make([][]byte, params.BeaconConfig().SlotsPerEpoch*10), + FinalizedCheckpoint: ðpb.Checkpoint{Root: make([]byte, 32)}, + PreviousJustifiedCheckpoint: ðpb.Checkpoint{Root: make([]byte, 32)}, + CurrentJustifiedCheckpoint: ðpb.Checkpoint{Root: make([]byte, 32)}, + } +}