From b3b42448a597777e02c4953f94f584ca4c002c17 Mon Sep 17 00:00:00 2001 From: aggris2 Date: Wed, 2 Nov 2022 18:16:18 +0000 Subject: [PATCH] Total balance refactor to big.Int --- beacon-chain/blockchain/metrics.go | 25 +++- beacon-chain/blockchain/testing/mock.go | 3 +- beacon-chain/cache/active_balance.go | 12 +- beacon-chain/cache/active_balance_test.go | 7 +- beacon-chain/core/altair/attestation.go | 7 +- beacon-chain/core/altair/attestation_test.go | 5 +- beacon-chain/core/altair/block.go | 5 +- beacon-chain/core/altair/block_test.go | 15 ++- beacon-chain/core/altair/epoch_precompute.go | 31 ++--- .../core/altair/epoch_precompute_test.go | 28 ++-- beacon-chain/core/altair/reward.go | 12 +- beacon-chain/core/altair/reward_test.go | 31 ++--- beacon-chain/core/altair/sync_committee.go | 6 +- beacon-chain/core/epoch/epoch_processing.go | 24 +++- .../core/epoch/epoch_processing_test.go | 2 +- .../core/epoch/precompute/BUILD.bazel | 2 - .../core/epoch/precompute/attestation.go | 43 +++--- .../core/epoch/precompute/attestation_test.go | 63 +++++---- .../precompute/justification_finalization.go | 12 +- .../justification_finalization_test.go | 10 +- beacon-chain/core/epoch/precompute/new.go | 20 ++- .../core/epoch/precompute/new_test.go | 8 +- .../core/epoch/precompute/reward_penalty.go | 19 +-- .../epoch/precompute/reward_penalty_test.go | 52 +++++--- .../core/epoch/precompute/slashing.go | 24 +++- .../core/epoch/precompute/slashing_test.go | 15 ++- beacon-chain/core/epoch/precompute/type.go | 20 +-- .../core/helpers/rewards_penalties.go | 32 +++-- .../core/helpers/rewards_penalties_test.go | 10 +- .../core/helpers/weak_subjectivity.go | 6 +- .../doubly-linked-tree/forkchoice.go | 36 ++--- .../doubly-linked-tree/forkchoice_test.go | 57 ++++---- .../forkchoice/doubly-linked-tree/node.go | 13 +- .../doubly-linked-tree/node_test.go | 39 +++--- .../doubly-linked-tree/proposer_boost.go | 12 +- .../doubly-linked-tree/proposer_boost_test.go | 15 ++- .../doubly-linked-tree/reorg_late_blocks.go | 31 ++++- .../reorg_late_blocks_test.go | 15 ++- .../forkchoice/doubly-linked-tree/store.go | 7 +- .../forkchoice/doubly-linked-tree/types.go | 7 +- .../unrealized_justification_test.go | 12 +- beacon-chain/forkchoice/interfaces.go | 3 +- beacon-chain/forkchoice/ro.go | 4 +- beacon-chain/forkchoice/ro_test.go | 5 +- .../rpc/prysm/v1alpha1/beacon/validators.go | 36 +++-- .../prysm/v1alpha1/beacon/validators_test.go | 121 +++++++++++------ beacon-chain/state/interfaces.go | 3 +- .../state-native/getters_participation.go | 8 +- .../getters_participation_test.go | 18 +-- .../stateutil/unrealized_justification.go | 41 +++--- .../unrealized_justification_test.go | 84 ++++++------ consensus-types/forkchoice/types.go | 6 +- proto/prysm/v1alpha1/validator.pb.go | 72 +++++----- proto/prysm/v1alpha1/validator.proto | 18 +-- pulse/pulse_rewards_test.go | 123 ++++++++++++++++++ validator/client/metrics.go | 6 +- 56 files changed, 841 insertions(+), 500 deletions(-) create mode 100644 pulse/pulse_rewards_test.go diff --git a/beacon-chain/blockchain/metrics.go b/beacon-chain/blockchain/metrics.go index c712b574d..aaa322d91 100644 --- a/beacon-chain/blockchain/metrics.go +++ b/beacon-chain/blockchain/metrics.go @@ -2,6 +2,7 @@ package blockchain import ( "context" + "strconv" "github.com/pkg/errors" "github.com/prometheus/client_golang/prometheus" @@ -353,10 +354,26 @@ func reportEpochMetrics(ctx context.Context, postState, headState state.BeaconSt return errors.Errorf("invalid state type provided: %T", headState.ToProtoUnsafe()) } - prevEpochActiveBalances.Set(float64(b.ActivePrevEpoch)) - prevEpochSourceBalances.Set(float64(b.PrevEpochAttested)) - prevEpochTargetBalances.Set(float64(b.PrevEpochTargetAttested)) - prevEpochHeadBalances.Set(float64(b.PrevEpochHeadAttested)) + parsedActivePrevEpoch, err := strconv.ParseFloat(b.ActivePrevEpoch.String(), 64) + if err != nil { + parsedActivePrevEpoch = float64(0) + } + parsedPrevEpochAttested, err := strconv.ParseFloat(b.PrevEpochAttested.String(), 64) + if err != nil { + parsedPrevEpochAttested = float64(0) + } + parsedPrevEpochTargetAttested, err := strconv.ParseFloat(b.PrevEpochTargetAttested.String(), 64) + if err != nil { + parsedPrevEpochTargetAttested = float64(0) + } + parsedPrevEpochHeadAttested, err := strconv.ParseFloat(b.PrevEpochHeadAttested.String(), 64) + if err != nil { + parsedPrevEpochHeadAttested = float64(0) + } + prevEpochActiveBalances.Set(parsedActivePrevEpoch) + prevEpochSourceBalances.Set(parsedPrevEpochAttested) + prevEpochTargetBalances.Set(parsedPrevEpochTargetAttested) + prevEpochHeadBalances.Set(parsedPrevEpochHeadAttested) refMap := postState.FieldReferencesCount() for name, val := range refMap { diff --git a/beacon-chain/blockchain/testing/mock.go b/beacon-chain/blockchain/testing/mock.go index 27083f671..9a03fa142 100644 --- a/beacon-chain/blockchain/testing/mock.go +++ b/beacon-chain/blockchain/testing/mock.go @@ -5,6 +5,7 @@ package testing import ( "bytes" "context" + "math/big" "sync" "time" @@ -370,7 +371,7 @@ func (s *ChainService) CurrentSlot() primitives.Slot { } // Participation mocks the same method in the chain service. -func (s *ChainService) Participation(_ uint64) *precompute.Balance { +func (s *ChainService) Participation(_ *big.Int) *precompute.Balance { return s.Balance } diff --git a/beacon-chain/cache/active_balance.go b/beacon-chain/cache/active_balance.go index f79f5e896..93ed1fc69 100644 --- a/beacon-chain/cache/active_balance.go +++ b/beacon-chain/cache/active_balance.go @@ -4,6 +4,7 @@ package cache import ( "encoding/binary" + "math/big" "sync" lru "github.com/hashicorp/golang-lru" @@ -55,7 +56,7 @@ func (c *BalanceCache) Clear() { } // AddTotalEffectiveBalance adds a new total effective balance entry for current balance for state `st` into the cache. -func (c *BalanceCache) AddTotalEffectiveBalance(st state.ReadOnlyBeaconState, balance uint64) error { +func (c *BalanceCache) AddTotalEffectiveBalance(st state.ReadOnlyBeaconState, balance *big.Int) error { key, err := balanceCacheKey(st) if err != nil { return err @@ -69,10 +70,11 @@ func (c *BalanceCache) AddTotalEffectiveBalance(st state.ReadOnlyBeaconState, ba } // Get returns the current epoch's effective balance for state `st` in cache. -func (c *BalanceCache) Get(st state.ReadOnlyBeaconState) (uint64, error) { +func (c *BalanceCache) Get(st state.ReadOnlyBeaconState) (*big.Int, error) { key, err := balanceCacheKey(st) + zero := big.NewInt(0) if err != nil { - return 0, err + return zero, err } c.lock.RLock() @@ -81,10 +83,10 @@ func (c *BalanceCache) Get(st state.ReadOnlyBeaconState) (uint64, error) { value, exists := c.cache.Get(key) if !exists { balanceCacheMiss.Inc() - return 0, ErrNotFound + return zero, ErrNotFound } balanceCacheHit.Inc() - return value.(uint64), nil + return value.(*big.Int), nil } // Given input state `st`, balance key is constructed as: diff --git a/beacon-chain/cache/active_balance_test.go b/beacon-chain/cache/active_balance_test.go index b821a6d15..39658888d 100644 --- a/beacon-chain/cache/active_balance_test.go +++ b/beacon-chain/cache/active_balance_test.go @@ -5,6 +5,7 @@ package cache import ( "encoding/binary" "math" + "math/big" "testing" state_native "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/state-native" @@ -31,7 +32,7 @@ func TestBalanceCache_AddGetBalance(t *testing.T) { _, err = cache.Get(st) require.ErrorContains(t, ErrNotFound.Error(), err) - b := uint64(100) + b := big.NewInt(100) require.NoError(t, cache.AddTotalEffectiveBalance(st, b)) cachedB, err := cache.Get(st) require.NoError(t, err) @@ -41,7 +42,7 @@ func TestBalanceCache_AddGetBalance(t *testing.T) { _, err = cache.Get(st) require.ErrorContains(t, ErrNotFound.Error(), err) - b = uint64(200) + b = big.NewInt(200) require.NoError(t, cache.AddTotalEffectiveBalance(st, b)) cachedB, err = cache.Get(st) require.NoError(t, err) @@ -51,7 +52,7 @@ func TestBalanceCache_AddGetBalance(t *testing.T) { _, err = cache.Get(st) require.ErrorContains(t, ErrNotFound.Error(), err) - b = uint64(300) + b = big.NewInt(300) require.NoError(t, cache.AddTotalEffectiveBalance(st, b)) cachedB, err = cache.Get(st) require.NoError(t, err) diff --git a/beacon-chain/core/altair/attestation.go b/beacon-chain/core/altair/attestation.go index f54a6d043..ae339e583 100644 --- a/beacon-chain/core/altair/attestation.go +++ b/beacon-chain/core/altair/attestation.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "fmt" + "math/big" "github.com/pkg/errors" "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/blocks" @@ -49,7 +50,7 @@ func ProcessAttestationNoVerifySignature( ctx context.Context, beaconState state.BeaconState, att *ethpb.Attestation, - totalBalance uint64, + totalBalance *big.Int, ) (state.BeaconState, error) { ctx, span := trace.StartSpan(ctx, "altair.ProcessAttestationNoVerifySignature") defer span.End() @@ -105,7 +106,7 @@ func SetParticipationAndRewardProposer( beaconState state.BeaconState, targetEpoch primitives.Epoch, indices []uint64, - participatedFlags map[uint8]bool, totalBalance uint64) (state.BeaconState, error) { + participatedFlags map[uint8]bool, totalBalance *big.Int) (state.BeaconState, error) { var proposerRewardNumerator uint64 currentEpoch := time.CurrentEpoch(beaconState) var stateErr error @@ -165,7 +166,7 @@ func AddValidatorFlag(flag, flagPosition uint8) (uint8, error) { // if flag_index in participation_flag_indices and not has_flag(epoch_participation[index], flag_index): // epoch_participation[index] = add_flag(epoch_participation[index], flag_index) // proposer_reward_numerator += get_base_reward(state, index) * weight -func EpochParticipation(beaconState state.BeaconState, indices []uint64, epochParticipation []byte, participatedFlags map[uint8]bool, totalBalance uint64) (uint64, []byte, error) { +func EpochParticipation(beaconState state.BeaconState, indices []uint64, epochParticipation []byte, participatedFlags map[uint8]bool, totalBalance *big.Int) (uint64, []byte, error) { cfg := params.BeaconConfig() sourceFlagIndex := cfg.TimelySourceFlagIndex targetFlagIndex := cfg.TimelyTargetFlagIndex diff --git a/beacon-chain/core/altair/attestation_test.go b/beacon-chain/core/altair/attestation_test.go index d548c5c4e..0fe721f6c 100644 --- a/beacon-chain/core/altair/attestation_test.go +++ b/beacon-chain/core/altair/attestation_test.go @@ -512,9 +512,10 @@ func TestSetParticipationAndRewardProposer(t *testing.T) { i, err := helpers.BeaconProposerIndex(context.Background(), st) require.NoError(t, err) - b, err = beaconState.BalanceAtIndex(i) + var bal uint64 + bal, err = beaconState.BalanceAtIndex(i) require.NoError(t, err) - require.Equal(t, test.wantedBalance, b) + require.Equal(t, test.wantedBalance, bal) if test.epoch == currentEpoch { p, err := beaconState.CurrentEpochParticipation() diff --git a/beacon-chain/core/altair/block.go b/beacon-chain/core/altair/block.go index a8d9a51e0..8fae32f51 100644 --- a/beacon-chain/core/altair/block.go +++ b/beacon-chain/core/altair/block.go @@ -2,6 +2,7 @@ package altair import ( "context" + "math/big" "github.com/pkg/errors" "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers" @@ -148,13 +149,13 @@ func VerifySyncCommitteeSig(s state.BeaconState, syncKeys []bls.PublicKey, syncS } // SyncRewards returns the proposer reward and the sync participant reward given the total active balance in state. -func SyncRewards(activeBalance uint64) (proposerReward, participantReward uint64, err error) { +func SyncRewards(activeBalance *big.Int) (proposerReward, participantReward uint64, err error) { cfg := params.BeaconConfig() - totalActiveIncrements := activeBalance / cfg.EffectiveBalanceIncrement baseRewardPerInc, err := BaseRewardPerIncrement(activeBalance) if err != nil { return 0, 0, err } + totalActiveIncrements := new(big.Int).Div(activeBalance, new(big.Int).SetUint64(cfg.EffectiveBalanceIncrement)).Uint64() totalBaseRewards := baseRewardPerInc * totalActiveIncrements maxParticipantRewards := totalBaseRewards * cfg.SyncRewardWeight / cfg.WeightDenominator / uint64(cfg.SlotsPerEpoch) participantReward = maxParticipantRewards / cfg.SyncCommitteeSize diff --git a/beacon-chain/core/altair/block_test.go b/beacon-chain/core/altair/block_test.go index 5b6fb0337..50058abdb 100644 --- a/beacon-chain/core/altair/block_test.go +++ b/beacon-chain/core/altair/block_test.go @@ -3,6 +3,7 @@ package altair_test import ( "context" "math" + "math/big" "testing" "github.com/prysmaticlabs/go-bitfield" @@ -290,49 +291,49 @@ func Test_VerifySyncCommitteeSig(t *testing.T) { func Test_SyncRewards(t *testing.T) { tests := []struct { name string - activeBalance uint64 + activeBalance *big.Int wantProposerReward uint64 wantParticipantReward uint64 errString string }{ { name: "active balance is 0", - activeBalance: 0, + activeBalance: big.NewInt(0), wantProposerReward: 0, wantParticipantReward: 0, errString: "active balance can't be 0", }, { name: "active balance is 1", - activeBalance: 1, + activeBalance: big.NewInt(1), wantProposerReward: 0, wantParticipantReward: 0, errString: "", }, { name: "active balance is 1eth", - activeBalance: params.BeaconConfig().EffectiveBalanceIncrement, + activeBalance: new(big.Int).SetUint64(params.BeaconConfig().EffectiveBalanceIncrement), wantProposerReward: 0, wantParticipantReward: 3, errString: "", }, { name: "active balance is 32eth", - activeBalance: params.BeaconConfig().MaxEffectiveBalance, + activeBalance: new(big.Int).SetUint64(params.BeaconConfig().MaxEffectiveBalance), wantProposerReward: 3, wantParticipantReward: 21, errString: "", }, { name: "active balance is 32eth * 1m validators", - activeBalance: params.BeaconConfig().MaxEffectiveBalance * 1e9, + activeBalance: new(big.Int).SetUint64(params.BeaconConfig().MaxEffectiveBalance * 1e9), wantProposerReward: 62780, wantParticipantReward: 439463, errString: "", }, { name: "active balance is max uint64", - activeBalance: math.MaxUint64, + activeBalance: new(big.Int).SetUint64(math.MaxUint64), wantProposerReward: 70368, wantParticipantReward: 492581, errString: "", diff --git a/beacon-chain/core/altair/epoch_precompute.go b/beacon-chain/core/altair/epoch_precompute.go index dbe519830..9aff4f2f2 100644 --- a/beacon-chain/core/altair/epoch_precompute.go +++ b/beacon-chain/core/altair/epoch_precompute.go @@ -2,6 +2,7 @@ package altair import ( "context" + "math/big" "github.com/pkg/errors" "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/epoch/precompute" @@ -28,7 +29,7 @@ func InitializePrecomputeValidators(ctx context.Context, beaconState state.Beaco _, span := trace.StartSpan(ctx, "altair.InitializePrecomputeValidators") defer span.End() vals := make([]*precompute.Validator, beaconState.NumValidators()) - bal := &precompute.Balance{} + bal := precompute.NewBalance() prevEpoch := time.PrevEpoch(beaconState) currentEpoch := time.CurrentEpoch(beaconState) inactivityScores, err := beaconState.InactivityScores() @@ -43,6 +44,7 @@ func InitializePrecomputeValidators(ctx context.Context, beaconState state.Beaco } if err := beaconState.ReadFromEveryValidator(func(idx int, val state.ReadOnlyValidator) error { // Set validator's balance, inactivity score and slashed/withdrawable status. + effectiveBalance := new(big.Int).SetUint64(val.EffectiveBalance()) v := &precompute.Validator{ CurrentEpochEffectiveBalance: val.EffectiveBalance(), InactivityScore: inactivityScores[idx], @@ -52,18 +54,12 @@ func InitializePrecomputeValidators(ctx context.Context, beaconState state.Beaco // Set validator's active status for current epoch. if helpers.IsActiveValidatorUsingTrie(val, currentEpoch) { v.IsActiveCurrentEpoch = true - bal.ActiveCurrentEpoch, err = math.Add64(bal.ActiveCurrentEpoch, val.EffectiveBalance()) - if err != nil { - return err - } + bal.ActiveCurrentEpoch.Add(bal.ActiveCurrentEpoch, effectiveBalance) } // Set validator's active status for previous epoch. if helpers.IsActiveValidatorUsingTrie(val, prevEpoch) { v.IsActivePrevEpoch = true - bal.ActivePrevEpoch, err = math.Add64(bal.ActivePrevEpoch, val.EffectiveBalance()) - if err != nil { - return err - } + bal.ActivePrevEpoch.Add(bal.ActivePrevEpoch, effectiveBalance) } vals[idx] = v return nil @@ -274,7 +270,8 @@ func AttestationsDelta(beaconState state.BeaconState, bal *precompute.Balance, v finalizedEpoch := beaconState.FinalizedCheckpointEpoch() increment := cfg.EffectiveBalanceIncrement factor := cfg.BaseRewardFactor - baseRewardMultiplier := increment * factor / math.CachedSquareRoot(bal.ActiveCurrentEpoch) + activeCurrentEpochSqrt := new(big.Int).Sqrt(bal.ActiveCurrentEpoch).Uint64() + baseRewardMultiplier := increment * factor / activeCurrentEpochSqrt leak := helpers.IsInInactivityLeak(prevEpoch, finalizedEpoch) // Modified in Altair and Bellatrix. @@ -302,15 +299,15 @@ func attestationDelta( inactivityLeak bool) (*AttDelta, error) { eligible := val.IsActivePrevEpoch || (val.IsSlashed && !val.IsWithdrawableCurrentEpoch) // Per spec `ActiveCurrentEpoch` can't be 0 to process attestation delta. - if !eligible || bal.ActiveCurrentEpoch == 0 { + if !eligible || bal.ActiveCurrentEpoch.Cmp(big.NewInt(0)) == 0 { return &AttDelta{}, nil } cfg := params.BeaconConfig() - increment := cfg.EffectiveBalanceIncrement + increment := new(big.Int).SetUint64(cfg.EffectiveBalanceIncrement) effectiveBalance := val.CurrentEpochEffectiveBalance - baseReward := (effectiveBalance / increment) * baseRewardMultiplier - activeIncrement := bal.ActiveCurrentEpoch / increment + baseReward := (effectiveBalance / cfg.EffectiveBalanceIncrement) * baseRewardMultiplier + activeIncrement := new(big.Int).Div(bal.ActiveCurrentEpoch, increment).Uint64() weightDenominator := cfg.WeightDenominator srcWeight := cfg.TimelySourceWeight @@ -320,7 +317,7 @@ func attestationDelta( // Process source reward / penalty if val.IsPrevEpochSourceAttester && !val.IsSlashed { if !inactivityLeak { - n := baseReward * srcWeight * (bal.PrevEpochAttested / increment) + n := baseReward * srcWeight * (new(big.Int).Div(bal.PrevEpochAttested, increment)).Uint64() attDelta.SourceReward += n / (activeIncrement * weightDenominator) } } else { @@ -330,7 +327,7 @@ func attestationDelta( // Process target reward / penalty if val.IsPrevEpochTargetAttester && !val.IsSlashed { if !inactivityLeak { - n := baseReward * tgtWeight * (bal.PrevEpochTargetAttested / increment) + n := baseReward * tgtWeight * (new(big.Int).Div(bal.PrevEpochTargetAttested, increment)).Uint64() attDelta.TargetReward += n / (activeIncrement * weightDenominator) } } else { @@ -340,7 +337,7 @@ func attestationDelta( // Process head reward / penalty if val.IsPrevEpochHeadAttester && !val.IsSlashed { if !inactivityLeak { - n := baseReward * headWeight * (bal.PrevEpochHeadAttested / increment) + n := baseReward * headWeight * (new(big.Int).Div(bal.PrevEpochHeadAttested, increment)).Uint64() attDelta.HeadReward += n / (activeIncrement * weightDenominator) } } diff --git a/beacon-chain/core/altair/epoch_precompute_test.go b/beacon-chain/core/altair/epoch_precompute_test.go index 3b051f891..c39ab7fa1 100644 --- a/beacon-chain/core/altair/epoch_precompute_test.go +++ b/beacon-chain/core/altair/epoch_precompute_test.go @@ -3,6 +3,7 @@ package altair import ( "context" "math" + "math/big" "testing" "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/epoch/precompute" @@ -56,10 +57,10 @@ func TestInitializeEpochValidators_Ok(t *testing.T) { InactivityScore: 3, }, v[3], "Incorrect validator 3 status") - wantedBalances := &precompute.Balance{ - ActiveCurrentEpoch: 100, - ActivePrevEpoch: 200, - } + wantedBalances := precompute.NewBalance() + wantedBalances.ActiveCurrentEpoch = big.NewInt(100) + wantedBalances.ActivePrevEpoch = big.NewInt(200) + assert.DeepEqual(t, wantedBalances, b, "Incorrect wanted balance") } @@ -75,7 +76,8 @@ func TestInitializeEpochValidators_Overflow(t *testing.T) { }) require.NoError(t, err) _, _, err = InitializePrecomputeValidators(context.Background(), s) - require.ErrorContains(t, "could not read every validator: addition overflows", err) + // This no longer overflows as total balance is big.int + require.NoError(t, err) } func TestInitializeEpochValidators_BadState(t *testing.T) { @@ -133,10 +135,10 @@ func TestProcessEpochParticipation(t *testing.T) { IsPrevEpochTargetAttester: true, IsPrevEpochHeadAttester: true, }, validators[3]) - require.Equal(t, params.BeaconConfig().MaxEffectiveBalance*3, balance.PrevEpochAttested) - require.Equal(t, balance.CurrentEpochTargetAttested, params.BeaconConfig().MaxEffectiveBalance*2) - require.Equal(t, balance.PrevEpochTargetAttested, params.BeaconConfig().MaxEffectiveBalance*2) - require.Equal(t, balance.PrevEpochHeadAttested, params.BeaconConfig().MaxEffectiveBalance*1) + require.Equal(t, params.BeaconConfig().MaxEffectiveBalance*3, balance.PrevEpochAttested.Uint64()) + require.Equal(t, balance.CurrentEpochTargetAttested.Uint64(), params.BeaconConfig().MaxEffectiveBalance*2) + require.Equal(t, balance.PrevEpochTargetAttested.Uint64(), params.BeaconConfig().MaxEffectiveBalance*2) + require.Equal(t, balance.PrevEpochHeadAttested.Uint64(), params.BeaconConfig().MaxEffectiveBalance*1) } func TestProcessEpochParticipation_InactiveValidator(t *testing.T) { @@ -200,10 +202,10 @@ func TestProcessEpochParticipation_InactiveValidator(t *testing.T) { IsPrevEpochTargetAttester: true, IsPrevEpochHeadAttester: true, }, validators[2]) - require.Equal(t, balance.PrevEpochAttested, 2*params.BeaconConfig().MaxEffectiveBalance) - require.Equal(t, balance.CurrentEpochTargetAttested, params.BeaconConfig().MaxEffectiveBalance) - require.Equal(t, balance.PrevEpochTargetAttested, 2*params.BeaconConfig().MaxEffectiveBalance) - require.Equal(t, balance.PrevEpochHeadAttested, params.BeaconConfig().MaxEffectiveBalance) + require.Equal(t, balance.PrevEpochAttested.Uint64(), 2*params.BeaconConfig().MaxEffectiveBalance) + require.Equal(t, balance.CurrentEpochTargetAttested.Uint64(), params.BeaconConfig().MaxEffectiveBalance) + require.Equal(t, balance.PrevEpochTargetAttested.Uint64(), 2*params.BeaconConfig().MaxEffectiveBalance) + require.Equal(t, balance.PrevEpochHeadAttested.Uint64(), params.BeaconConfig().MaxEffectiveBalance) } func TestAttestationsDelta(t *testing.T) { diff --git a/beacon-chain/core/altair/reward.go b/beacon-chain/core/altair/reward.go index a3ca9125f..5e75e5711 100644 --- a/beacon-chain/core/altair/reward.go +++ b/beacon-chain/core/altair/reward.go @@ -1,12 +1,13 @@ package altair import ( + "math/big" + "github.com/pkg/errors" "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers" "github.com/prysmaticlabs/prysm/v5/beacon-chain/state" "github.com/prysmaticlabs/prysm/v5/config/params" "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" - "github.com/prysmaticlabs/prysm/v5/math" ) // BaseReward takes state and validator index and calculate @@ -33,7 +34,7 @@ func BaseReward(s state.ReadOnlyBeaconState, index primitives.ValidatorIndex) (u } // BaseRewardWithTotalBalance calculates the base reward with the provided total balance. -func BaseRewardWithTotalBalance(s state.ReadOnlyBeaconState, index primitives.ValidatorIndex, totalBalance uint64) (uint64, error) { +func BaseRewardWithTotalBalance(s state.ReadOnlyBeaconState, index primitives.ValidatorIndex, totalBalance *big.Int) (uint64, error) { val, err := s.ValidatorAtIndexReadOnly(index) if err != nil { return 0, err @@ -53,10 +54,11 @@ func BaseRewardWithTotalBalance(s state.ReadOnlyBeaconState, index primitives.Va // def get_base_reward_per_increment(state: BeaconState) -> Gwei: // // return Gwei(EFFECTIVE_BALANCE_INCREMENT * BASE_REWARD_FACTOR // integer_squareroot(get_total_active_balance(state))) -func BaseRewardPerIncrement(activeBalance uint64) (uint64, error) { - if activeBalance == 0 { +func BaseRewardPerIncrement(activeBalance *big.Int) (uint64, error) { + if activeBalance.Cmp(big.NewInt(0)) == 0 { return 0, errors.New("active balance can't be 0") } cfg := params.BeaconConfig() - return cfg.EffectiveBalanceIncrement * cfg.BaseRewardFactor / math.CachedSquareRoot(activeBalance), nil + activeBalanceSqrt := new(big.Int).Sqrt(activeBalance).Uint64() + return cfg.EffectiveBalanceIncrement * cfg.BaseRewardFactor / activeBalanceSqrt, nil } diff --git a/beacon-chain/core/altair/reward_test.go b/beacon-chain/core/altair/reward_test.go index 55f623cf6..8253f5b12 100644 --- a/beacon-chain/core/altair/reward_test.go +++ b/beacon-chain/core/altair/reward_test.go @@ -2,6 +2,7 @@ package altair_test import ( "math" + "math/big" "testing" "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/altair" @@ -73,56 +74,56 @@ func Test_BaseRewardWithTotalBalance(t *testing.T) { tests := []struct { name string valIdx primitives.ValidatorIndex - activeBalance uint64 + activeBalance *big.Int want uint64 errString string }{ { name: "active balance is 0", valIdx: 0, - activeBalance: 0, + activeBalance: big.NewInt(0), want: 0, errString: "active balance can't be 0", }, { name: "unknown validator", valIdx: 2, - activeBalance: 1, + activeBalance: big.NewInt(1), want: 0, errString: "validator index 2 does not exist", }, { name: "active balance is 1", valIdx: 0, - activeBalance: 1, + activeBalance: big.NewInt(1), want: 2048000000000, errString: "", }, { name: "active balance is 1eth", valIdx: 0, - activeBalance: params.BeaconConfig().EffectiveBalanceIncrement, + activeBalance: new(big.Int).SetUint64(params.BeaconConfig().EffectiveBalanceIncrement), want: 64765024, errString: "", }, { name: "active balance is 32eth", valIdx: 0, - activeBalance: params.BeaconConfig().MaxEffectiveBalance, + activeBalance: new(big.Int).SetUint64(params.BeaconConfig().MaxEffectiveBalance), want: 11448672, errString: "", }, { name: "active balance is 32eth * 1m validators", valIdx: 0, - activeBalance: params.BeaconConfig().MaxEffectiveBalance * 1e9, + activeBalance: new(big.Int).SetUint64(params.BeaconConfig().MaxEffectiveBalance * 1e9), want: 544, errString: "", }, { name: "active balance is max uint64", valIdx: 0, - activeBalance: math.MaxUint64, + activeBalance: new(big.Int).SetUint64(math.MaxUint64), want: 448, errString: "", }, @@ -143,43 +144,43 @@ func Test_BaseRewardPerIncrement(t *testing.T) { helpers.ClearCache() tests := []struct { name string - activeBalance uint64 + activeBalance *big.Int want uint64 errString string }{ { name: "active balance is 0", - activeBalance: 0, + activeBalance: big.NewInt(0), want: 0, errString: "active balance can't be 0", }, { name: "active balance is 1", - activeBalance: 1, + activeBalance: big.NewInt(1), want: 64000000000, errString: "", }, { name: "active balance is 1eth", - activeBalance: params.BeaconConfig().EffectiveBalanceIncrement, + activeBalance: new(big.Int).SetUint64(params.BeaconConfig().EffectiveBalanceIncrement), want: 2023907, errString: "", }, { name: "active balance is 32eth", - activeBalance: params.BeaconConfig().MaxEffectiveBalance, + activeBalance: new(big.Int).SetUint64(params.BeaconConfig().MaxEffectiveBalance), want: 357771, errString: "", }, { name: "active balance is 32eth * 1m validators", - activeBalance: params.BeaconConfig().MaxEffectiveBalance * 1e9, + activeBalance: new(big.Int).SetUint64(params.BeaconConfig().MaxEffectiveBalance * 1e9), want: 17, errString: "", }, { name: "active balance is max uint64", - activeBalance: math.MaxUint64, + activeBalance: new(big.Int).SetUint64(math.MaxUint64), want: 14, errString: "", }, diff --git a/beacon-chain/core/altair/sync_committee.go b/beacon-chain/core/altair/sync_committee.go index 9bfcd8434..1fa5f2c2e 100644 --- a/beacon-chain/core/altair/sync_committee.go +++ b/beacon-chain/core/altair/sync_committee.go @@ -4,6 +4,7 @@ import ( "context" goErrors "errors" "fmt" + "math/big" "time" "github.com/pkg/errors" @@ -139,8 +140,9 @@ func NextSyncCommitteeIndices(ctx context.Context, s state.BeaconState) ([]primi return nil, err } - effectiveBal := v.EffectiveBalance() - if effectiveBal*maxRandomByte >= cfg.MaxEffectiveBalance*uint64(randomByte) { + effectiveBal := new(big.Int).SetUint64(v.EffectiveBalance()) + effectiveBal.Mul(effectiveBal, new(big.Int).SetUint64(maxRandomByte)) + if effectiveBal.Cmp(new(big.Int).SetUint64(cfg.MaxEffectiveBalance*uint64(randomByte))) != -1 { cIndices = append(cIndices, cIndex) } } diff --git a/beacon-chain/core/epoch/epoch_processing.go b/beacon-chain/core/epoch/epoch_processing.go index 41d3e748c..f0fa1a47f 100644 --- a/beacon-chain/core/epoch/epoch_processing.go +++ b/beacon-chain/core/epoch/epoch_processing.go @@ -7,6 +7,7 @@ package epoch import ( "context" "fmt" + "math/big" "sort" "github.com/pkg/errors" @@ -59,10 +60,10 @@ func (s sortableIndices) Less(i, j int) bool { // Note: ``get_total_balance`` returns ``EFFECTIVE_BALANCE_INCREMENT`` Gwei minimum to avoid divisions by zero. // """ // return get_total_balance(state, get_unslashed_attesting_indices(state, attestations)) -func AttestingBalance(ctx context.Context, state state.ReadOnlyBeaconState, atts []*ethpb.PendingAttestation) (uint64, error) { +func AttestingBalance(ctx context.Context, state state.ReadOnlyBeaconState, atts []*ethpb.PendingAttestation) (*big.Int, error) { indices, err := UnslashedAttestingIndices(ctx, state, atts) if err != nil { - return 0, errors.Wrap(err, "could not get attesting indices") + return big.NewInt(0), errors.Wrap(err, "could not get attesting indices") } return helpers.TotalBalance(state, indices), nil } @@ -196,14 +197,23 @@ func ProcessSlashings(state state.BeaconState, slashingMultiplier uint64) (state // a callback is used here to apply the following actions to all validators // below equally. - increment := params.BeaconConfig().EffectiveBalanceIncrement - minSlashing := math.Min(totalSlashing*slashingMultiplier, totalBalance) + increment := new(big.Int).SetUint64(params.BeaconConfig().EffectiveBalanceIncrement) + totalSlashingTimesMultiplier := new(big.Int).SetUint64(totalSlashing * slashingMultiplier) + minSlashing := totalBalance + if totalSlashingTimesMultiplier.Cmp(totalBalance) == -1 { + minSlashing = totalSlashingTimesMultiplier + } else { + minSlashing = totalBalance + } err = state.ApplyToEveryValidator(func(idx int, val *ethpb.Validator) (bool, *ethpb.Validator, error) { correctEpoch := (currentEpoch + exitLength/2) == val.WithdrawableEpoch if val.Slashed && correctEpoch { - penaltyNumerator := val.EffectiveBalance / increment * minSlashing - penalty := penaltyNumerator / totalBalance * increment - if err := helpers.DecreaseBalance(state, primitives.ValidatorIndex(idx), penalty); err != nil { + penalty := new(big.Int).SetUint64(val.EffectiveBalance) // val.EffectiveBalance + penalty.Div(penalty, increment) // val.EffectiveBalance / increment + penalty.Mul(penalty, minSlashing) // penaltyNumerator = val.EffectiveBalance / increment * minSlashing + penalty.Div(penalty, totalBalance) // penaltyNumerator / totalBalance + penalty = penalty.Mul(penalty, increment) // penalty = penaltyNumerator / totalBalance * increment + if err := helpers.DecreaseBalance(state, primitives.ValidatorIndex(idx), penalty.Uint64()); err != nil { return false, val, err } return true, val, nil diff --git a/beacon-chain/core/epoch/epoch_processing_test.go b/beacon-chain/core/epoch/epoch_processing_test.go index 71ebe63cd..c0953d16d 100644 --- a/beacon-chain/core/epoch/epoch_processing_test.go +++ b/beacon-chain/core/epoch/epoch_processing_test.go @@ -146,7 +146,7 @@ func TestAttestingBalance_CorrectBalance(t *testing.T) { balance, err := epoch.AttestingBalance(context.Background(), beaconState, atts) require.NoError(t, err) wanted := 256 * params.BeaconConfig().MaxEffectiveBalance - assert.Equal(t, wanted, balance) + assert.Equal(t, wanted, balance.Uint64()) } func TestProcessSlashings_NotSlashed(t *testing.T) { diff --git a/beacon-chain/core/epoch/precompute/BUILD.bazel b/beacon-chain/core/epoch/precompute/BUILD.bazel index 18a12269b..96ed2f18a 100644 --- a/beacon-chain/core/epoch/precompute/BUILD.bazel +++ b/beacon-chain/core/epoch/precompute/BUILD.bazel @@ -22,7 +22,6 @@ go_library( "//beacon-chain/state:go_default_library", "//config/params:go_default_library", "//consensus-types/primitives:go_default_library", - "//math:go_default_library", "//monitoring/tracing:go_default_library", "//proto/prysm/v1alpha1:go_default_library", "//proto/prysm/v1alpha1/attestation:go_default_library", @@ -55,7 +54,6 @@ go_test( "//config/fieldparams:go_default_library", "//config/params:go_default_library", "//consensus-types/primitives:go_default_library", - "//math:go_default_library", "//proto/prysm/v1alpha1:go_default_library", "//proto/prysm/v1alpha1/attestation:go_default_library", "//runtime/version:go_default_library", diff --git a/beacon-chain/core/epoch/precompute/attestation.go b/beacon-chain/core/epoch/precompute/attestation.go index 9ec7fd0f1..f44aa3bbd 100644 --- a/beacon-chain/core/epoch/precompute/attestation.go +++ b/beacon-chain/core/epoch/precompute/attestation.go @@ -3,6 +3,7 @@ package precompute import ( "bytes" "context" + "math/big" "github.com/pkg/errors" "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers" @@ -174,23 +175,24 @@ func UpdateValidator(vp []*Validator, record *Validator, indices []uint64, a *et func UpdateBalance(vp []*Validator, bBal *Balance, stateVersion int) *Balance { for _, v := range vp { if !v.IsSlashed { + currentEpochEffectiveBalance := new(big.Int).SetUint64(v.CurrentEpochEffectiveBalance) if v.IsCurrentEpochAttester { - bBal.CurrentEpochAttested += v.CurrentEpochEffectiveBalance + bBal.CurrentEpochAttested.Add(bBal.CurrentEpochAttested, currentEpochEffectiveBalance) } if v.IsCurrentEpochTargetAttester { - bBal.CurrentEpochTargetAttested += v.CurrentEpochEffectiveBalance + bBal.CurrentEpochTargetAttested.Add(bBal.CurrentEpochTargetAttested, currentEpochEffectiveBalance) } if stateVersion == version.Phase0 && v.IsPrevEpochAttester { - bBal.PrevEpochAttested += v.CurrentEpochEffectiveBalance + bBal.PrevEpochAttested.Add(bBal.PrevEpochAttested, currentEpochEffectiveBalance) } if stateVersion >= version.Altair && v.IsPrevEpochSourceAttester { - bBal.PrevEpochAttested += v.CurrentEpochEffectiveBalance + bBal.PrevEpochAttested.Add(bBal.PrevEpochAttested, currentEpochEffectiveBalance) } if v.IsPrevEpochTargetAttester { - bBal.PrevEpochTargetAttested += v.CurrentEpochEffectiveBalance + bBal.PrevEpochTargetAttested.Add(bBal.PrevEpochTargetAttested, currentEpochEffectiveBalance) } if v.IsPrevEpochHeadAttester { - bBal.PrevEpochHeadAttested += v.CurrentEpochEffectiveBalance + bBal.PrevEpochHeadAttested.Add(bBal.PrevEpochHeadAttested, currentEpochEffectiveBalance) } } } @@ -202,26 +204,27 @@ func UpdateBalance(vp []*Validator, bBal *Balance, stateVersion int) *Balance { // have EffectiveBalanceIncrement(1 eth) as a lower bound. func EnsureBalancesLowerBound(bBal *Balance) *Balance { ebi := params.BeaconConfig().EffectiveBalanceIncrement - if ebi > bBal.ActiveCurrentEpoch { - bBal.ActiveCurrentEpoch = ebi + ebiBig := new(big.Int).SetUint64(ebi) + if ebiBig.Cmp(bBal.ActiveCurrentEpoch) == 1 { + bBal.ActiveCurrentEpoch = ebiBig } - if ebi > bBal.ActivePrevEpoch { - bBal.ActivePrevEpoch = ebi + if ebiBig.Cmp(bBal.ActivePrevEpoch) == 1 { + bBal.ActivePrevEpoch = ebiBig } - if ebi > bBal.CurrentEpochAttested { - bBal.CurrentEpochAttested = ebi + if ebiBig.Cmp(bBal.CurrentEpochAttested) == 1 { + bBal.CurrentEpochAttested = ebiBig } - if ebi > bBal.CurrentEpochTargetAttested { - bBal.CurrentEpochTargetAttested = ebi + if ebiBig.Cmp(bBal.CurrentEpochTargetAttested) == 1 { + bBal.CurrentEpochTargetAttested = ebiBig } - if ebi > bBal.PrevEpochAttested { - bBal.PrevEpochAttested = ebi + if ebiBig.Cmp(bBal.PrevEpochAttested) == 1 { + bBal.PrevEpochAttested = ebiBig } - if ebi > bBal.PrevEpochTargetAttested { - bBal.PrevEpochTargetAttested = ebi + if ebiBig.Cmp(bBal.PrevEpochTargetAttested) == 1 { + bBal.PrevEpochTargetAttested = ebiBig } - if ebi > bBal.PrevEpochHeadAttested { - bBal.PrevEpochHeadAttested = ebi + if ebiBig.Cmp(bBal.PrevEpochHeadAttested) == 1 { + bBal.PrevEpochHeadAttested = ebiBig } return bBal } diff --git a/beacon-chain/core/epoch/precompute/attestation_test.go b/beacon-chain/core/epoch/precompute/attestation_test.go index 1801afa9f..d71f8b3e9 100644 --- a/beacon-chain/core/epoch/precompute/attestation_test.go +++ b/beacon-chain/core/epoch/precompute/attestation_test.go @@ -2,6 +2,7 @@ package precompute_test import ( "context" + "math/big" "testing" "github.com/prysmaticlabs/go-bitfield" @@ -47,6 +48,10 @@ func TestUpdateValidator_InclusionOnlyCountsPrevEpoch(t *testing.T) { } func TestUpdateBalance(t *testing.T) { + ebi := new(big.Int).SetUint64(params.BeaconConfig().EffectiveBalanceIncrement) + ebiTimesHundred := big.NewInt(100).Mul(big.NewInt(100), ebi) + ebiTimesTwoHundred := big.NewInt(200).Mul(big.NewInt(200), ebi) + ebiTimesThreeHundred := big.NewInt(300).Mul(big.NewInt(300), ebi) vp := []*precompute.Validator{ {IsCurrentEpochAttester: true, CurrentEpochEffectiveBalance: 100 * params.BeaconConfig().EffectiveBalanceIncrement}, {IsCurrentEpochTargetAttester: true, IsCurrentEpochAttester: true, CurrentEpochEffectiveBalance: 100 * params.BeaconConfig().EffectiveBalanceIncrement}, @@ -58,19 +63,23 @@ func TestUpdateBalance(t *testing.T) { {IsSlashed: true, IsCurrentEpochAttester: true, CurrentEpochEffectiveBalance: 100 * params.BeaconConfig().EffectiveBalanceIncrement}, } wantedPBal := &precompute.Balance{ - ActiveCurrentEpoch: params.BeaconConfig().EffectiveBalanceIncrement, - ActivePrevEpoch: params.BeaconConfig().EffectiveBalanceIncrement, - CurrentEpochAttested: 200 * params.BeaconConfig().EffectiveBalanceIncrement, - CurrentEpochTargetAttested: 200 * params.BeaconConfig().EffectiveBalanceIncrement, - PrevEpochAttested: 300 * params.BeaconConfig().EffectiveBalanceIncrement, - PrevEpochTargetAttested: 100 * params.BeaconConfig().EffectiveBalanceIncrement, - PrevEpochHeadAttested: 200 * params.BeaconConfig().EffectiveBalanceIncrement, + ActiveCurrentEpoch: ebi, + ActivePrevEpoch: ebi, + CurrentEpochAttested: ebiTimesTwoHundred, + CurrentEpochTargetAttested: ebiTimesTwoHundred, + PrevEpochAttested: ebiTimesThreeHundred, + PrevEpochTargetAttested: ebiTimesHundred, + PrevEpochHeadAttested: ebiTimesTwoHundred, } - pBal := precompute.UpdateBalance(vp, &precompute.Balance{}, version.Phase0) + pBal := precompute.NewBalance() + pBal = precompute.UpdateBalance(vp, pBal, version.Phase0) assert.DeepEqual(t, wantedPBal, pBal, "Incorrect balance calculations") } func TestUpdateBalanceDifferentVersions(t *testing.T) { + ebi := new(big.Int).SetUint64(params.BeaconConfig().EffectiveBalanceIncrement) + ebiTimesHundred := big.NewInt(100).Mul(big.NewInt(100), ebi) + ebiTimesTwoHundred := big.NewInt(200).Mul(big.NewInt(200), ebi) vp := []*precompute.Validator{ {IsCurrentEpochAttester: true, CurrentEpochEffectiveBalance: 100 * params.BeaconConfig().EffectiveBalanceIncrement}, {IsCurrentEpochTargetAttester: true, IsCurrentEpochAttester: true, CurrentEpochEffectiveBalance: 100 * params.BeaconConfig().EffectiveBalanceIncrement}, @@ -82,18 +91,19 @@ func TestUpdateBalanceDifferentVersions(t *testing.T) { {IsSlashed: true, IsCurrentEpochAttester: true, CurrentEpochEffectiveBalance: 100 * params.BeaconConfig().EffectiveBalanceIncrement}, } wantedPBal := &precompute.Balance{ - ActiveCurrentEpoch: params.BeaconConfig().EffectiveBalanceIncrement, - ActivePrevEpoch: params.BeaconConfig().EffectiveBalanceIncrement, - CurrentEpochAttested: 200 * params.BeaconConfig().EffectiveBalanceIncrement, - CurrentEpochTargetAttested: 200 * params.BeaconConfig().EffectiveBalanceIncrement, - PrevEpochAttested: params.BeaconConfig().EffectiveBalanceIncrement, - PrevEpochTargetAttested: 100 * params.BeaconConfig().EffectiveBalanceIncrement, - PrevEpochHeadAttested: 200 * params.BeaconConfig().EffectiveBalanceIncrement, + ActiveCurrentEpoch: ebi, + ActivePrevEpoch: ebi, + CurrentEpochAttested: ebiTimesTwoHundred, + CurrentEpochTargetAttested: ebiTimesTwoHundred, + PrevEpochAttested: ebi, + PrevEpochTargetAttested: ebiTimesHundred, + PrevEpochHeadAttested: ebiTimesTwoHundred, } - pBal := precompute.UpdateBalance(vp, &precompute.Balance{}, version.Bellatrix) + pBal := precompute.NewBalance() + pBal = precompute.UpdateBalance(vp, pBal, version.Bellatrix) assert.DeepEqual(t, wantedPBal, pBal, "Incorrect balance calculations") - pBal = precompute.UpdateBalance(vp, &precompute.Balance{}, version.Capella) + pBal = precompute.UpdateBalance(vp, precompute.NewBalance(), version.Capella) assert.DeepEqual(t, wantedPBal, pBal, "Incorrect balance calculations") } @@ -178,6 +188,7 @@ func TestProcessAttestations(t *testing.T) { params.OverrideBeaconConfig(params.MinimalSpecConfig()) validators := uint64(128) + pBal := precompute.NewBalance() beaconState, _ := util.DeterministicGenesisState(t, validators) require.NoError(t, beaconState.SetSlot(params.BeaconConfig().SlotsPerEpoch)) c := helpers.SlotCommitteeCount(validators) @@ -206,7 +217,7 @@ func TestProcessAttestations(t *testing.T) { for i := 0; i < len(pVals); i++ { pVals[i] = &precompute.Validator{CurrentEpochEffectiveBalance: 100} } - pVals, _, err = precompute.ProcessAttestations(context.Background(), beaconState, pVals, &precompute.Balance{}) + pVals, _, err = precompute.ProcessAttestations(context.Background(), beaconState, pVals, pBal) require.NoError(t, err) committee, err := helpers.BeaconCommitteeFromState(context.Background(), beaconState, att1.Data.Slot, att1.Data.CommitteeIndex) @@ -230,14 +241,14 @@ func TestProcessAttestations(t *testing.T) { } func TestEnsureBalancesLowerBound(t *testing.T) { - b := &precompute.Balance{} + b := precompute.NewBalance() b = precompute.EnsureBalancesLowerBound(b) balanceIncrement := params.BeaconConfig().EffectiveBalanceIncrement - assert.Equal(t, balanceIncrement, b.ActiveCurrentEpoch, "Did not get wanted active current balance") - assert.Equal(t, balanceIncrement, b.ActivePrevEpoch, "Did not get wanted active previous balance") - assert.Equal(t, balanceIncrement, b.CurrentEpochAttested, "Did not get wanted current attested balance") - assert.Equal(t, balanceIncrement, b.CurrentEpochTargetAttested, "Did not get wanted target attested balance") - assert.Equal(t, balanceIncrement, b.PrevEpochAttested, "Did not get wanted prev attested balance") - assert.Equal(t, balanceIncrement, b.PrevEpochTargetAttested, "Did not get wanted prev target attested balance") - assert.Equal(t, balanceIncrement, b.PrevEpochHeadAttested, "Did not get wanted prev head attested balance") + assert.Equal(t, balanceIncrement, b.ActiveCurrentEpoch.Uint64(), "Did not get wanted active current balance") + assert.Equal(t, balanceIncrement, b.ActivePrevEpoch.Uint64(), "Did not get wanted active previous balance") + assert.Equal(t, balanceIncrement, b.CurrentEpochAttested.Uint64(), "Did not get wanted current attested balance") + assert.Equal(t, balanceIncrement, b.CurrentEpochTargetAttested.Uint64(), "Did not get wanted target attested balance") + assert.Equal(t, balanceIncrement, b.PrevEpochAttested.Uint64(), "Did not get wanted prev attested balance") + assert.Equal(t, balanceIncrement, b.PrevEpochTargetAttested.Uint64(), "Did not get wanted prev target attested balance") + assert.Equal(t, balanceIncrement, b.PrevEpochHeadAttested.Uint64(), "Did not get wanted prev head attested balance") } diff --git a/beacon-chain/core/epoch/precompute/justification_finalization.go b/beacon-chain/core/epoch/precompute/justification_finalization.go index e31d95be7..b8bca83a0 100644 --- a/beacon-chain/core/epoch/precompute/justification_finalization.go +++ b/beacon-chain/core/epoch/precompute/justification_finalization.go @@ -1,6 +1,8 @@ package precompute import ( + "math/big" + "github.com/pkg/errors" "github.com/prysmaticlabs/go-bitfield" "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers" @@ -69,15 +71,19 @@ func ProcessJustificationAndFinalizationPreCompute(state state.BeaconState, pBal } // processJustificationBits processes the justification bits during epoch processing. -func processJustificationBits(state state.BeaconState, totalActiveBalance, prevEpochTargetBalance, currEpochTargetBalance uint64) bitfield.Bitvector4 { +func processJustificationBits(state state.BeaconState, totalActiveBalance, prevEpochTargetBalance, currEpochTargetBalance *big.Int) bitfield.Bitvector4 { newBits := state.JustificationBits() newBits.Shift(1) // If 2/3 or more of total balance attested in the previous epoch. - if 3*prevEpochTargetBalance >= 2*totalActiveBalance { + three := big.NewInt(3) + prevEpochTargetBalanceTimesThree := new(big.Int).Mul(three, prevEpochTargetBalance) + currEpochTargetBalanceTimesThree := new(big.Int).Mul(three, currEpochTargetBalance) + totalActiveBalanceTimesTwo := new(big.Int).Mul(big.NewInt(2), totalActiveBalance) + if prevEpochTargetBalanceTimesThree.Cmp(totalActiveBalanceTimesTwo) != -1 { newBits.SetBitAt(1, true) } - if 3*currEpochTargetBalance >= 2*totalActiveBalance { + if currEpochTargetBalanceTimesThree.Cmp(totalActiveBalanceTimesTwo) != -1 { newBits.SetBitAt(0, true) } diff --git a/beacon-chain/core/epoch/precompute/justification_finalization_test.go b/beacon-chain/core/epoch/precompute/justification_finalization_test.go index 4fd755636..bbb5abc25 100644 --- a/beacon-chain/core/epoch/precompute/justification_finalization_test.go +++ b/beacon-chain/core/epoch/precompute/justification_finalization_test.go @@ -2,6 +2,7 @@ package precompute_test import ( "context" + "math/big" "testing" "github.com/prysmaticlabs/go-bitfield" @@ -42,7 +43,8 @@ func TestProcessJustificationAndFinalizationPreCompute_ConsecutiveEpochs(t *test state, err := state_native.InitializeFromProtoPhase0(base) require.NoError(t, err) attestedBalance := 4 * uint64(e) * 3 / 2 - b := &precompute.Balance{PrevEpochTargetAttested: attestedBalance} + b := precompute.NewBalance() + b.PrevEpochTargetAttested = new(big.Int).SetUint64(attestedBalance) newState, err := precompute.ProcessJustificationAndFinalizationPreCompute(state, b) require.NoError(t, err) rt := [32]byte{byte(64)} @@ -79,7 +81,8 @@ func TestProcessJustificationAndFinalizationPreCompute_JustifyCurrentEpoch(t *te state, err := state_native.InitializeFromProtoPhase0(base) require.NoError(t, err) attestedBalance := 4 * uint64(e) * 3 / 2 - b := &precompute.Balance{PrevEpochTargetAttested: attestedBalance} + b := precompute.NewBalance() + b.PrevEpochTargetAttested = new(big.Int).SetUint64(attestedBalance) newState, err := precompute.ProcessJustificationAndFinalizationPreCompute(state, b) require.NoError(t, err) rt := [32]byte{byte(64)} @@ -115,7 +118,8 @@ func TestProcessJustificationAndFinalizationPreCompute_JustifyPrevEpoch(t *testi state, err := state_native.InitializeFromProtoPhase0(base) require.NoError(t, err) attestedBalance := 4 * uint64(e) * 3 / 2 - b := &precompute.Balance{PrevEpochTargetAttested: attestedBalance} + b := precompute.NewBalance() + b.PrevEpochTargetAttested = new(big.Int).SetUint64(attestedBalance) newState, err := precompute.ProcessJustificationAndFinalizationPreCompute(state, b) require.NoError(t, err) rt := [32]byte{byte(64)} diff --git a/beacon-chain/core/epoch/precompute/new.go b/beacon-chain/core/epoch/precompute/new.go index b4f74900e..ecdfdb76c 100644 --- a/beacon-chain/core/epoch/precompute/new.go +++ b/beacon-chain/core/epoch/precompute/new.go @@ -5,6 +5,7 @@ package precompute import ( "context" + "math/big" "github.com/pkg/errors" "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers" @@ -22,7 +23,7 @@ func New(ctx context.Context, s state.BeaconState) ([]*Validator, *Balance, erro defer span.End() pValidators := make([]*Validator, s.NumValidators()) - pBal := &Balance{} + pBal := NewBalance() currentEpoch := time.CurrentEpoch(s) prevEpoch := time.PrevEpoch(s) @@ -30,6 +31,7 @@ func New(ctx context.Context, s state.BeaconState) ([]*Validator, *Balance, erro if err := s.ReadFromEveryValidator(func(idx int, val state.ReadOnlyValidator) error { // Was validator withdrawable or slashed withdrawable := prevEpoch+1 >= val.WithdrawableEpoch() + effectiveBalance := new(big.Int).SetUint64(val.EffectiveBalance()) pVal := &Validator{ IsSlashed: val.Slashed(), IsWithdrawableCurrentEpoch: withdrawable, @@ -38,12 +40,12 @@ func New(ctx context.Context, s state.BeaconState) ([]*Validator, *Balance, erro // Was validator active current epoch if helpers.IsActiveValidatorUsingTrie(val, currentEpoch) { pVal.IsActiveCurrentEpoch = true - pBal.ActiveCurrentEpoch += val.EffectiveBalance() + pBal.ActiveCurrentEpoch.Add(pBal.ActiveCurrentEpoch, effectiveBalance) } // Was validator active previous epoch if helpers.IsActiveValidatorUsingTrie(val, prevEpoch) { pVal.IsActivePrevEpoch = true - pBal.ActivePrevEpoch += val.EffectiveBalance() + pBal.ActivePrevEpoch.Add(pBal.ActivePrevEpoch, effectiveBalance) } // Set inclusion slot and inclusion distance to be max, they will be compared and replaced // with the lower values @@ -57,3 +59,15 @@ func New(ctx context.Context, s state.BeaconState) ([]*Validator, *Balance, erro } return pValidators, pBal, nil } + +func NewBalance() *Balance { + return &Balance{ + ActiveCurrentEpoch: big.NewInt(0), + ActivePrevEpoch: big.NewInt(0), + CurrentEpochAttested: big.NewInt(0), + CurrentEpochTargetAttested: big.NewInt(0), + PrevEpochAttested: big.NewInt(0), + PrevEpochHeadAttested: big.NewInt(0), + PrevEpochTargetAttested: big.NewInt(0), + } +} diff --git a/beacon-chain/core/epoch/precompute/new_test.go b/beacon-chain/core/epoch/precompute/new_test.go index b87a5f3fd..684151633 100644 --- a/beacon-chain/core/epoch/precompute/new_test.go +++ b/beacon-chain/core/epoch/precompute/new_test.go @@ -2,6 +2,7 @@ package precompute_test import ( "context" + "math/big" "testing" "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/epoch/precompute" @@ -57,9 +58,8 @@ func TestNew(t *testing.T) { InclusionSlot: e, }, v[3], "Incorrect validator 3 status") - wantedBalances := &precompute.Balance{ - ActiveCurrentEpoch: 100, - ActivePrevEpoch: 200, - } + wantedBalances := precompute.NewBalance() + wantedBalances.ActiveCurrentEpoch = big.NewInt(100) + wantedBalances.ActivePrevEpoch = big.NewInt(200) assert.DeepEqual(t, wantedBalances, b, "Incorrect wanted balance") } diff --git a/beacon-chain/core/epoch/precompute/reward_penalty.go b/beacon-chain/core/epoch/precompute/reward_penalty.go index 0ee21db77..654710a60 100644 --- a/beacon-chain/core/epoch/precompute/reward_penalty.go +++ b/beacon-chain/core/epoch/precompute/reward_penalty.go @@ -1,13 +1,14 @@ package precompute import ( + "math/big" + "github.com/pkg/errors" "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers" "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/time" "github.com/prysmaticlabs/prysm/v5/beacon-chain/state" "github.com/prysmaticlabs/prysm/v5/config/params" "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" - "github.com/prysmaticlabs/prysm/v5/math" ) type attesterRewardsFunc func(state.ReadOnlyBeaconState, *Balance, []*Validator) ([]uint64, []uint64, error) @@ -72,7 +73,7 @@ func AttestationsDelta(state state.ReadOnlyBeaconState, pBal *Balance, vp []*Val prevEpoch := time.PrevEpoch(state) finalizedEpoch := state.FinalizedCheckpointEpoch() - sqrtActiveCurrentEpoch := math.CachedSquareRoot(pBal.ActiveCurrentEpoch) + sqrtActiveCurrentEpoch := new(big.Int).Sqrt(pBal.ActiveCurrentEpoch).Uint64() for i, v := range vp { rewards[i], penalties[i] = attestationDelta(pBal, sqrtActiveCurrentEpoch, v, prevEpoch, finalizedEpoch) } @@ -80,16 +81,16 @@ func AttestationsDelta(state state.ReadOnlyBeaconState, pBal *Balance, vp []*Val } func attestationDelta(pBal *Balance, sqrtActiveCurrentEpoch uint64, v *Validator, prevEpoch, finalizedEpoch primitives.Epoch) (uint64, uint64) { - if !EligibleForRewards(v) || pBal.ActiveCurrentEpoch == 0 { + if !EligibleForRewards(v) || pBal.ActiveCurrentEpoch.Cmp(big.NewInt(0)) != 1 { return 0, 0 } baseRewardsPerEpoch := params.BeaconConfig().BaseRewardsPerEpoch - effectiveBalanceIncrement := params.BeaconConfig().EffectiveBalanceIncrement + effectiveBalanceIncrement := new(big.Int).SetUint64(params.BeaconConfig().EffectiveBalanceIncrement) vb := v.CurrentEpochEffectiveBalance br := vb * params.BeaconConfig().BaseRewardFactor / sqrtActiveCurrentEpoch / baseRewardsPerEpoch r, p := uint64(0), uint64(0) - currentEpochBalance := pBal.ActiveCurrentEpoch / effectiveBalanceIncrement + currentEpochBalance := new(big.Int).Div(pBal.ActiveCurrentEpoch, effectiveBalanceIncrement).Uint64() // Process source reward / penalty if v.IsPrevEpochAttester && !v.IsSlashed { @@ -102,7 +103,7 @@ func attestationDelta(pBal *Balance, sqrtActiveCurrentEpoch uint64, v *Validator // optimal participation receives full base reward compensation here. r += br } else { - rewardNumerator := br * (pBal.PrevEpochAttested / effectiveBalanceIncrement) + rewardNumerator := br * (new(big.Int).Div(pBal.PrevEpochAttested, effectiveBalanceIncrement)).Uint64() r += rewardNumerator / currentEpochBalance } } else { @@ -116,7 +117,7 @@ func attestationDelta(pBal *Balance, sqrtActiveCurrentEpoch uint64, v *Validator // optimal participation receives full base reward compensation here. r += br } else { - rewardNumerator := br * (pBal.PrevEpochTargetAttested / effectiveBalanceIncrement) + rewardNumerator := br * (new(big.Int).Div(pBal.PrevEpochTargetAttested, effectiveBalanceIncrement)).Uint64() r += rewardNumerator / currentEpochBalance } } else { @@ -130,7 +131,7 @@ func attestationDelta(pBal *Balance, sqrtActiveCurrentEpoch uint64, v *Validator // optimal participation receives full base reward compensation here. r += br } else { - rewardNumerator := br * (pBal.PrevEpochHeadAttested / effectiveBalanceIncrement) + rewardNumerator := br * (new(big.Int).Div(pBal.PrevEpochHeadAttested, effectiveBalanceIncrement)).Uint64() r += rewardNumerator / currentEpochBalance } } else { @@ -160,7 +161,7 @@ func ProposersDelta(state state.ReadOnlyBeaconState, pBal *Balance, vp []*Valida rewards := make([]uint64, numofVals) totalBalance := pBal.ActiveCurrentEpoch - balanceSqrt := math.CachedSquareRoot(totalBalance) + balanceSqrt := new(big.Int).Sqrt(totalBalance).Uint64() // Balance square root cannot be 0, this prevents division by 0. if balanceSqrt == 0 { balanceSqrt = 1 diff --git a/beacon-chain/core/epoch/precompute/reward_penalty_test.go b/beacon-chain/core/epoch/precompute/reward_penalty_test.go index 4c4e3c409..13952788d 100644 --- a/beacon-chain/core/epoch/precompute/reward_penalty_test.go +++ b/beacon-chain/core/epoch/precompute/reward_penalty_test.go @@ -2,6 +2,7 @@ package precompute import ( "context" + "math/big" "testing" "github.com/pkg/errors" @@ -14,7 +15,6 @@ import ( fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams" "github.com/prysmaticlabs/prysm/v5/config/params" "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" - "github.com/prysmaticlabs/prysm/v5/math" ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" "github.com/prysmaticlabs/prysm/v5/runtime/version" "github.com/prysmaticlabs/prysm/v5/testing/assert" @@ -97,8 +97,10 @@ func TestAttestationDeltaPrecompute(t *testing.T) { // Add some variances to target and head balances. // See: https://github.com/prysmaticlabs/prysm/issues/5593 - bp.PrevEpochTargetAttested /= 2 - bp.PrevEpochHeadAttested = bp.PrevEpochHeadAttested * 2 / 3 + two := big.NewInt(2) + three := big.NewInt(3) + bp.PrevEpochTargetAttested.Div(bp.PrevEpochTargetAttested, two) + bp.PrevEpochHeadAttested.Div(new(big.Int).Mul(bp.PrevEpochHeadAttested, two), three) rewards, penalties, err := AttestationsDelta(beaconState, bp, vp) require.NoError(t, err) attestedBalance, err := epoch.AttestingBalance(context.Background(), beaconState, atts) @@ -112,14 +114,23 @@ func TestAttestationDeltaPrecompute(t *testing.T) { require.NoError(t, err, "Could not get base reward") // Base rewards for getting source right - wanted := attestedBalance*base/totalBalance + - bp.PrevEpochTargetAttested*base/totalBalance + - bp.PrevEpochHeadAttested*base/totalBalance + baseBig := new(big.Int).SetUint64(base) + attestedBalanceTimesBase := new(big.Int).Mul(attestedBalance, baseBig) + prevEpochTargetAttestedTimesBase := new(big.Int).Mul(bp.PrevEpochTargetAttested, baseBig) + prevEpochHeadAttestedTimesBase := new(big.Int).Mul(bp.PrevEpochHeadAttested, baseBig) + + attestedBalanceTimesBaseDivTotalBal := new(big.Int).Div(attestedBalanceTimesBase, totalBalance) + prevEpochTargetAttestedTimesBaseDivTotalBal := new(big.Int).Div(prevEpochTargetAttestedTimesBase, totalBalance) + prevEpochHeadAttestedTimesBaseDivTotalBal := new(big.Int).Div(prevEpochHeadAttestedTimesBase, totalBalance) + wanted := new(big.Int).Add(new(big.Int).Add(attestedBalanceTimesBaseDivTotalBal, prevEpochTargetAttestedTimesBaseDivTotalBal), prevEpochHeadAttestedTimesBaseDivTotalBal) // Base rewards for proposer and attesters working together getting attestation // on chain in the fatest manner proposerReward := base / params.BeaconConfig().ProposerRewardQuotient - wanted += (base-proposerReward)*uint64(params.BeaconConfig().MinAttestationInclusionDelay) - 1 - assert.Equal(t, wanted, rewards[i], "Unexpected reward balance for validator with index %d", i) + baseSubProposerReward := new(big.Int).Sub(baseBig, new(big.Int).SetUint64(proposerReward)) + baseSubProposerRewardTimesMinAttestIncDelay := new(big.Int).Mul(baseSubProposerReward, new(big.Int).SetUint64(uint64(params.BeaconConfig().MinAttestationInclusionDelay))) + baseSubProposerRewardTimesMinAttestIncDelaySubOne := new(big.Int).Sub(baseSubProposerRewardTimesMinAttestIncDelay, big.NewInt(1)) + wanted.Add(wanted, baseSubProposerRewardTimesMinAttestIncDelaySubOne) + assert.Equal(t, wanted.Uint64(), rewards[i], "Unexpected reward balance for validator with index %d", i) // Since all these validators attested, they shouldn't get penalized. assert.Equal(t, uint64(0), penalties[i], "Unexpected penalty balance") } @@ -173,7 +184,7 @@ func TestAttestationDeltas_ZeroEpoch(t *testing.T) { pVals, pBal, err = ProcessAttestations(context.Background(), beaconState, pVals, pBal) require.NoError(t, err) - pBal.ActiveCurrentEpoch = 0 // Could cause a divide by zero panic. + pBal.ActiveCurrentEpoch = big.NewInt(0) // Could cause a divide by zero panic. _, _, err = AttestationsDelta(beaconState, pBal, pVals) require.NoError(t, err) @@ -307,18 +318,22 @@ func TestProposerDeltaPrecompute_HappyCase(t *testing.T) { require.NoError(t, err) proposerIndex := primitives.ValidatorIndex(1) - b := &Balance{ActiveCurrentEpoch: 1000} + b := NewBalance() + b.ActiveCurrentEpoch = big.NewInt(1000) v := []*Validator{ {IsPrevEpochAttester: true, CurrentEpochEffectiveBalance: 32, ProposerIndex: proposerIndex}, } r, err := ProposersDelta(beaconState, b, v) require.NoError(t, err) - baseReward := v[0].CurrentEpochEffectiveBalance * params.BeaconConfig().BaseRewardFactor / - math.IntegerSquareRoot(b.ActiveCurrentEpoch) / params.BeaconConfig().BaseRewardsPerEpoch - proposerReward := baseReward / params.BeaconConfig().ProposerRewardQuotient + activeCurrEpochSqrt := new(big.Int).Sqrt(b.ActiveCurrentEpoch) + currEpochEffectBal := new(big.Int).SetUint64(v[0].CurrentEpochEffectiveBalance) + currEpochEffBalTimesBrFact := new(big.Int).Mul(currEpochEffectBal, new(big.Int).SetUint64(params.BeaconConfig().BaseRewardFactor)) + currEpochEffBalTimesBrFactDivSqrt := new(big.Int).Div(currEpochEffBalTimesBrFact, activeCurrEpochSqrt) + baseReward := new(big.Int).Div(currEpochEffBalTimesBrFactDivSqrt, new(big.Int).SetUint64(params.BeaconConfig().BaseRewardsPerEpoch)) + proposerReward := new(big.Int).Div(baseReward, new(big.Int).SetUint64(params.BeaconConfig().ProposerRewardQuotient)) - assert.Equal(t, proposerReward, r[proposerIndex], "Unexpected proposer reward") + assert.Equal(t, proposerReward.Uint64(), r[proposerIndex], "Unexpected proposer reward") } func TestProposerDeltaPrecompute_ValidatorIndexOutOfRange(t *testing.T) { @@ -329,7 +344,8 @@ func TestProposerDeltaPrecompute_ValidatorIndexOutOfRange(t *testing.T) { require.NoError(t, err) proposerIndex := primitives.ValidatorIndex(validatorCount) - b := &Balance{ActiveCurrentEpoch: 1000} + b := NewBalance() + b.ActiveCurrentEpoch = big.NewInt(1000) v := []*Validator{ {IsPrevEpochAttester: true, CurrentEpochEffectiveBalance: 32, ProposerIndex: proposerIndex}, } @@ -345,7 +361,8 @@ func TestProposerDeltaPrecompute_SlashedCase(t *testing.T) { require.NoError(t, err) proposerIndex := primitives.ValidatorIndex(1) - b := &Balance{ActiveCurrentEpoch: 1000} + b := NewBalance() + b.ActiveCurrentEpoch = big.NewInt(1000) v := []*Validator{ {IsPrevEpochAttester: true, CurrentEpochEffectiveBalance: 32, ProposerIndex: proposerIndex, IsSlashed: true}, } @@ -373,7 +390,8 @@ func baseReward(state state.ReadOnlyBeaconState, index primitives.ValidatorIndex return 0, err } effectiveBalance := val.EffectiveBalance() + totalBalanceSqrt := new(big.Int).Sqrt(totalBalance).Uint64() baseReward := effectiveBalance * params.BeaconConfig().BaseRewardFactor / - math.IntegerSquareRoot(totalBalance) / params.BeaconConfig().BaseRewardsPerEpoch + totalBalanceSqrt / params.BeaconConfig().BaseRewardsPerEpoch return baseReward, nil } diff --git a/beacon-chain/core/epoch/precompute/slashing.go b/beacon-chain/core/epoch/precompute/slashing.go index 03e0cd6ef..0e62cee3b 100644 --- a/beacon-chain/core/epoch/precompute/slashing.go +++ b/beacon-chain/core/epoch/precompute/slashing.go @@ -1,12 +1,13 @@ package precompute import ( + "math/big" + "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers" "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/time" "github.com/prysmaticlabs/prysm/v5/beacon-chain/state" "github.com/prysmaticlabs/prysm/v5/config/params" "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" - "github.com/prysmaticlabs/prysm/v5/math" ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" ) @@ -23,7 +24,14 @@ func ProcessSlashingsPrecompute(s state.BeaconState, pBal *Balance) error { totalSlashing += slashing } - minSlashing := math.Min(totalSlashing*params.BeaconConfig().ProportionalSlashingMultiplier, pBal.ActiveCurrentEpoch) + minSlashing := pBal.ActiveCurrentEpoch + totalSlashingTimesPropSlashMul := new(big.Int).SetUint64(totalSlashing * params.BeaconConfig().ProportionalSlashingMultiplier) + + if totalSlashingTimesPropSlashMul.Cmp(pBal.ActiveCurrentEpoch) == -1 { + minSlashing = totalSlashingTimesPropSlashMul + } else { + minSlashing = pBal.ActiveCurrentEpoch + } epochToWithdraw := currentEpoch + exitLength/2 var hasSlashing bool @@ -43,15 +51,19 @@ func ProcessSlashingsPrecompute(s state.BeaconState, pBal *Balance) error { return nil } - increment := params.BeaconConfig().EffectiveBalanceIncrement + increment := new(big.Int).SetUint64(params.BeaconConfig().EffectiveBalanceIncrement) validatorFunc := func(idx int, val *ethpb.Validator) (bool, *ethpb.Validator, error) { correctEpoch := epochToWithdraw == val.WithdrawableEpoch if val.Slashed && correctEpoch { - penaltyNumerator := val.EffectiveBalance / increment * minSlashing - penalty := penaltyNumerator / pBal.ActiveCurrentEpoch * increment - if err := helpers.DecreaseBalance(s, primitives.ValidatorIndex(idx), penalty); err != nil { + penalty := new(big.Int).SetUint64(val.EffectiveBalance) // valEffectiveBal + penalty.Div(penalty, increment) // val.EffectiveBalance / increment + penalty.Mul(penalty, minSlashing) // penaltyNumerator = val.EffectiveBalance / increment * minSlashing + penalty.Div(penalty, pBal.ActiveCurrentEpoch) // penaltyNumerator / pBal.ActiveCurrentEpoch + penalty = penalty.Mul(penalty, increment) // penalty = penaltyNumerator / pBal.ActiveCurrentEpoch * increment + if err := helpers.DecreaseBalance(s, primitives.ValidatorIndex(idx), penalty.Uint64()); err != nil { return false, val, err } + return true, val, nil } return false, val, nil diff --git a/beacon-chain/core/epoch/precompute/slashing_test.go b/beacon-chain/core/epoch/precompute/slashing_test.go index 3b8982342..256b25678 100644 --- a/beacon-chain/core/epoch/precompute/slashing_test.go +++ b/beacon-chain/core/epoch/precompute/slashing_test.go @@ -2,6 +2,7 @@ package precompute_test import ( "fmt" + "math/big" "testing" "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/epoch/precompute" @@ -21,7 +22,8 @@ func TestProcessSlashingsPrecompute_NotSlashedWithSlashedTrue(t *testing.T) { Slashings: []uint64{0, 1e9}, }) require.NoError(t, err) - pBal := &precompute.Balance{ActiveCurrentEpoch: params.BeaconConfig().MaxEffectiveBalance} + pBal := precompute.NewBalance() + pBal.ActiveCurrentEpoch = new(big.Int).SetUint64(params.BeaconConfig().MaxEffectiveBalance) require.NoError(t, precompute.ProcessSlashingsPrecompute(s, pBal)) wanted := params.BeaconConfig().MaxEffectiveBalance @@ -36,7 +38,8 @@ func TestProcessSlashingsPrecompute_NotSlashedWithSlashedFalse(t *testing.T) { Slashings: []uint64{0, 1e9}, }) require.NoError(t, err) - pBal := &precompute.Balance{ActiveCurrentEpoch: params.BeaconConfig().MaxEffectiveBalance} + pBal := precompute.NewBalance() + pBal.ActiveCurrentEpoch = new(big.Int).SetUint64(params.BeaconConfig().MaxEffectiveBalance) require.NoError(t, precompute.ProcessSlashingsPrecompute(s, pBal)) wanted := params.BeaconConfig().MaxEffectiveBalance @@ -112,16 +115,16 @@ func TestProcessSlashingsPrecompute_SlashedLess(t *testing.T) { for i, tt := range tests { t.Run(fmt.Sprint(i), func(t *testing.T) { - ab := uint64(0) + ab := big.NewInt(0) for i, b := range tt.state.Balances { // Skip validator 0 since it's slashed if i == 0 { continue } - ab += b + ab.Add(ab, new(big.Int).SetUint64(b)) } - pBal := &precompute.Balance{ActiveCurrentEpoch: ab} - + pBal := precompute.NewBalance() + pBal.ActiveCurrentEpoch = ab original := proto.Clone(tt.state) state, err := state_native.InitializeFromProtoPhase0(tt.state) require.NoError(t, err) diff --git a/beacon-chain/core/epoch/precompute/type.go b/beacon-chain/core/epoch/precompute/type.go index bcd73598e..63df7b2dd 100644 --- a/beacon-chain/core/epoch/precompute/type.go +++ b/beacon-chain/core/epoch/precompute/type.go @@ -1,6 +1,10 @@ package precompute -import "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" +import ( + "math/big" + + "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" +) // Validator stores the pre computation of individual validator's attesting records these records // consist of attestation votes, block inclusion record. Pre computing and storing such record @@ -48,20 +52,20 @@ type Validator struct { // Pre computing and storing such record is essential for process epoch optimizations. type Balance struct { // ActiveCurrentEpoch is the total effective balance of all active validators during current epoch. - ActiveCurrentEpoch uint64 + ActiveCurrentEpoch *big.Int // ActivePrevEpoch is the total effective balance of all active validators during prev epoch. - ActivePrevEpoch uint64 + ActivePrevEpoch *big.Int // CurrentEpochAttested is the total effective balance of all validators who attested during current epoch. - CurrentEpochAttested uint64 + CurrentEpochAttested *big.Int // CurrentEpochTargetAttested is the total effective balance of all validators who attested // for epoch boundary block during current epoch. - CurrentEpochTargetAttested uint64 + CurrentEpochTargetAttested *big.Int // PrevEpochAttested is the total effective balance of all validators who attested during prev epoch. - PrevEpochAttested uint64 + PrevEpochAttested *big.Int // PrevEpochTargetAttested is the total effective balance of all validators who attested // for epoch boundary block during prev epoch. - PrevEpochTargetAttested uint64 + PrevEpochTargetAttested *big.Int // PrevEpochHeadAttested is the total effective balance of all validators who attested // correctly for head block during prev epoch. - PrevEpochHeadAttested uint64 + PrevEpochHeadAttested *big.Int } diff --git a/beacon-chain/core/helpers/rewards_penalties.go b/beacon-chain/core/helpers/rewards_penalties.go index 1e8cbd54e..b567bfa70 100644 --- a/beacon-chain/core/helpers/rewards_penalties.go +++ b/beacon-chain/core/helpers/rewards_penalties.go @@ -2,6 +2,7 @@ package helpers import ( "errors" + "math/big" "github.com/prysmaticlabs/prysm/v5/beacon-chain/cache" "github.com/prysmaticlabs/prysm/v5/beacon-chain/state" @@ -25,20 +26,22 @@ var balanceCache = cache.NewEffectiveBalanceCache() // Math safe up to ~10B ETH, afterwhich this overflows uint64. // """ // return Gwei(max(EFFECTIVE_BALANCE_INCREMENT, sum([state.validators[index].effective_balance for index in indices]))) -func TotalBalance(state state.ReadOnlyValidators, indices []primitives.ValidatorIndex) uint64 { - total := uint64(0) +func TotalBalance(state state.ReadOnlyValidators, indices []primitives.ValidatorIndex) *big.Int { + total := big.NewInt(0) for _, idx := range indices { val, err := state.ValidatorAtIndexReadOnly(idx) if err != nil { continue } - total += val.EffectiveBalance() + effectiveBalance := val.EffectiveBalance() + total.Add(total, new(big.Int).SetUint64(effectiveBalance)) } // EFFECTIVE_BALANCE_INCREMENT is the lower bound for total balance. - if total < params.BeaconConfig().EffectiveBalanceIncrement { - return params.BeaconConfig().EffectiveBalanceIncrement + effectiveBalanceIncrement := new(big.Int).SetUint64(params.BeaconConfig().EffectiveBalanceIncrement) + if total.Cmp(effectiveBalanceIncrement) == -1 { + return effectiveBalanceIncrement } return total @@ -55,8 +58,9 @@ func TotalBalance(state state.ReadOnlyValidators, indices []primitives.Validator // Note: ``get_total_balance`` returns ``EFFECTIVE_BALANCE_INCREMENT`` Gwei minimum to avoid divisions by zero. // """ // return get_total_balance(state, set(get_active_validator_indices(state, get_current_epoch(state)))) -func TotalActiveBalance(s state.ReadOnlyBeaconState) (uint64, error) { +func TotalActiveBalance(s state.ReadOnlyBeaconState) (*big.Int, error) { bal, err := balanceCache.Get(s) + zero := big.NewInt(0) switch { case err == nil: return bal, nil @@ -64,24 +68,28 @@ func TotalActiveBalance(s state.ReadOnlyBeaconState) (uint64, error) { // Do nothing if we receive a not found error. default: // In the event, we encounter another error we return it. - return 0, err + return zero, err } - total := uint64(0) + total := big.NewInt(0) epoch := slots.ToEpoch(s.Slot()) if err := s.ReadFromEveryValidator(func(idx int, val state.ReadOnlyValidator) error { if IsActiveValidatorUsingTrie(val, epoch) { - total += val.EffectiveBalance() + effectiveBalance := val.EffectiveBalance() + total.Add(total, new(big.Int).SetUint64(effectiveBalance)) } return nil }); err != nil { - return 0, err + return zero, err } // Spec defines `EffectiveBalanceIncrement` as min to avoid divisions by zero. - total = mathutil.Max(params.BeaconConfig().EffectiveBalanceIncrement, total) + effectiveBalanceIncrement := new(big.Int).SetUint64(params.BeaconConfig().EffectiveBalanceIncrement) + if effectiveBalanceIncrement.Cmp(total) == 1 { + total = effectiveBalanceIncrement + } if err := balanceCache.AddTotalEffectiveBalance(s, total); err != nil { - return 0, err + return zero, err } return total, nil diff --git a/beacon-chain/core/helpers/rewards_penalties_test.go b/beacon-chain/core/helpers/rewards_penalties_test.go index 4aeeeb739..bc1ce2bd3 100644 --- a/beacon-chain/core/helpers/rewards_penalties_test.go +++ b/beacon-chain/core/helpers/rewards_penalties_test.go @@ -25,7 +25,7 @@ func TestTotalBalance_OK(t *testing.T) { balance := TotalBalance(state, []primitives.ValidatorIndex{0, 1, 2, 3}) wanted := state.Validators()[0].EffectiveBalance + state.Validators()[1].EffectiveBalance + state.Validators()[2].EffectiveBalance + state.Validators()[3].EffectiveBalance - assert.Equal(t, wanted, balance, "Incorrect TotalBalance") + assert.Equal(t, wanted, balance.Uint64(), "Incorrect TotalBalance") } func TestTotalBalance_ReturnsEffectiveBalanceIncrement(t *testing.T) { @@ -36,7 +36,7 @@ func TestTotalBalance_ReturnsEffectiveBalanceIncrement(t *testing.T) { balance := TotalBalance(state, []primitives.ValidatorIndex{}) wanted := params.BeaconConfig().EffectiveBalanceIncrement - assert.Equal(t, wanted, balance, "Incorrect TotalBalance") + assert.Equal(t, wanted, balance.Uint64(), "Incorrect TotalBalance") } func TestGetBalance_OK(t *testing.T) { @@ -78,7 +78,7 @@ func TestTotalActiveBalance(t *testing.T) { require.NoError(t, err) bal, err := TotalActiveBalance(state) require.NoError(t, err) - require.Equal(t, uint64(test.vCount)*params.BeaconConfig().MaxEffectiveBalance, bal) + require.Equal(t, uint64(test.vCount)*params.BeaconConfig().MaxEffectiveBalance, bal.Uint64()) } } @@ -101,7 +101,7 @@ func TestTotalActiveBal_ReturnMin(t *testing.T) { require.NoError(t, err) bal, err := TotalActiveBalance(state) require.NoError(t, err) - require.Equal(t, params.BeaconConfig().EffectiveBalanceIncrement, bal) + require.Equal(t, params.BeaconConfig().EffectiveBalanceIncrement, bal.Uint64()) } } @@ -125,7 +125,7 @@ func TestTotalActiveBalance_WithCache(t *testing.T) { require.NoError(t, err) bal, err := TotalActiveBalance(state) require.NoError(t, err) - require.Equal(t, uint64(test.wantCount)*params.BeaconConfig().MaxEffectiveBalance, bal) + require.Equal(t, uint64(test.wantCount)*params.BeaconConfig().MaxEffectiveBalance, bal.Uint64()) } } diff --git a/beacon-chain/core/helpers/weak_subjectivity.go b/beacon-chain/core/helpers/weak_subjectivity.go index 111563c25..cb272106b 100644 --- a/beacon-chain/core/helpers/weak_subjectivity.go +++ b/beacon-chain/core/helpers/weak_subjectivity.go @@ -5,6 +5,7 @@ import ( "encoding/hex" "errors" "fmt" + "math/big" "strconv" "strings" @@ -70,11 +71,12 @@ func ComputeWeakSubjectivityPeriod(ctx context.Context, st state.ReadOnlyBeaconS } // Average effective balance in the given validator set, in Ether. - t, err := TotalActiveBalance(st) + total, err := TotalActiveBalance(st) if err != nil { return 0, fmt.Errorf("cannot find total active balance of validators: %w", err) } - t = t / N / cfg.GweiPerEth + balancePerValidator := new(big.Int).Div(total, new(big.Int).SetUint64(N)) + t := new(big.Int).Div(balancePerValidator, new(big.Int).SetUint64(cfg.GweiPerEth)).Uint64() // Maximum effective balance per validator. T := cfg.MaxEffectiveBalance / cfg.GweiPerEth diff --git a/beacon-chain/forkchoice/doubly-linked-tree/forkchoice.go b/beacon-chain/forkchoice/doubly-linked-tree/forkchoice.go index 941d3c47d..93b7f2725 100644 --- a/beacon-chain/forkchoice/doubly-linked-tree/forkchoice.go +++ b/beacon-chain/forkchoice/doubly-linked-tree/forkchoice.go @@ -3,6 +3,7 @@ package doublylinkedtree import ( "context" "fmt" + "math/big" "time" "github.com/pkg/errors" @@ -31,6 +32,7 @@ func New() *ForkChoice { prevJustifiedCheckpoint: &forkchoicetypes.Checkpoint{}, finalizedCheckpoint: &forkchoicetypes.Checkpoint{}, proposerBoostRoot: [32]byte{}, + committeeWeight: big.NewInt(0), nodeByRoot: make(map[[fieldparams.RootLength]byte]*Node), nodeByPayload: make(map[[fieldparams.RootLength]byte]*Node), slashedIndices: make(map[primitives.ValidatorIndex]bool), @@ -278,19 +280,19 @@ func (f *ForkChoice) updateBalances() error { continue } - oldBalance := uint64(0) - newBalance := uint64(0) + oldBalance := big.NewInt(0) + newBalance := big.NewInt(0) // If the validator index did not exist in `f.balances` or // `newBalances` list above, the balance is just 0. if index < len(f.balances) { - oldBalance = f.balances[index] + oldBalance = new(big.Int).SetUint64(f.balances[index]) } if index < len(newBalances) { - newBalance = newBalances[index] + newBalance = new(big.Int).SetUint64(newBalances[index]) } // Update only if the validator's balance or vote has changed. - if vote.currentRoot != vote.nextRoot || oldBalance != newBalance { + if vote.currentRoot != vote.nextRoot || oldBalance.Cmp(newBalance) != 0 { // Ignore the vote if the root is not in fork choice // store, that means we have not seen the block before. nextNode, ok := f.store.nodeByRoot[vote.nextRoot] @@ -299,7 +301,7 @@ func (f *ForkChoice) updateBalances() error { if nextNode == nil { return errors.Wrap(ErrNilNode, "could not update balances") } - nextNode.balance += newBalance + nextNode.balance.Add(nextNode.balance, newBalance) } currentNode, ok := f.store.nodeByRoot[vote.currentRoot] @@ -308,7 +310,7 @@ func (f *ForkChoice) updateBalances() error { if currentNode == nil { return errors.Wrap(ErrNilNode, "could not update balances") } - if currentNode.balance < oldBalance { + if currentNode.balance.Cmp(oldBalance) == -1 { log.WithFields(logrus.Fields{ "nodeRoot": fmt.Sprintf("%#x", bytesutil.Trunc(vote.currentRoot[:])), "oldBalance": oldBalance, @@ -318,9 +320,9 @@ func (f *ForkChoice) updateBalances() error { "previousProposerBoostRoot": fmt.Sprintf("%#x", bytesutil.Trunc(f.store.previousProposerBoostRoot[:])), "previousProposerBoostScore": f.store.previousProposerBoostScore, }).Warning("node with invalid balance, setting it to zero") - currentNode.balance = 0 + currentNode.balance = big.NewInt(0) } else { - currentNode.balance -= oldBalance + currentNode.balance.Sub(currentNode.balance, oldBalance) } } } @@ -397,10 +399,10 @@ func (f *ForkChoice) InsertSlashedIndex(_ context.Context, index primitives.Vali return } - if node.balance < f.balances[index] { - node.balance = 0 + if node.balance.Cmp(new(big.Int).SetUint64(f.balances[index])) == -1 { + node.balance = big.NewInt(0) } else { - node.balance -= f.balances[index] + node.balance.Sub(node.balance, new(big.Int).SetUint64(f.balances[index])) } } @@ -602,10 +604,10 @@ func (f *ForkChoice) SetBalancesByRooter(handler forkchoice.BalancesByRooter) { } // Weight returns the weight of the given root if found on the store -func (f *ForkChoice) Weight(root [32]byte) (uint64, error) { +func (f *ForkChoice) Weight(root [32]byte) (*big.Int, error) { n, ok := f.store.nodeByRoot[root] if !ok || n == nil { - return 0, ErrNilNode + return big.NewInt(0), ErrNilNode } return n.weight, nil } @@ -617,15 +619,15 @@ func (f *ForkChoice) updateJustifiedBalances(ctx context.Context, root [32]byte) return errors.Wrap(err, "could not get justified balances") } f.justifiedBalances = balances - f.store.committeeWeight = 0 + f.store.committeeWeight = big.NewInt(0) f.numActiveValidators = 0 for _, val := range balances { if val > 0 { - f.store.committeeWeight += val + f.store.committeeWeight.Add(f.store.committeeWeight, new(big.Int).SetUint64(val)) f.numActiveValidators++ } } - f.store.committeeWeight /= uint64(params.BeaconConfig().SlotsPerEpoch) + f.store.committeeWeight.Div(f.store.committeeWeight, new(big.Int).SetUint64(uint64(params.BeaconConfig().SlotsPerEpoch))) return nil } diff --git a/beacon-chain/forkchoice/doubly-linked-tree/forkchoice_test.go b/beacon-chain/forkchoice/doubly-linked-tree/forkchoice_test.go index 62472e288..074e92835 100644 --- a/beacon-chain/forkchoice/doubly-linked-tree/forkchoice_test.go +++ b/beacon-chain/forkchoice/doubly-linked-tree/forkchoice_test.go @@ -3,6 +3,7 @@ package doublylinkedtree import ( "context" "encoding/binary" + "math/big" "testing" "time" @@ -85,9 +86,9 @@ func TestForkChoice_UpdateBalancesPositiveChange(t *testing.T) { f.justifiedBalances = []uint64{10, 20, 30} require.NoError(t, f.updateBalances()) s := f.store - assert.Equal(t, uint64(10), s.nodeByRoot[indexToHash(1)].balance) - assert.Equal(t, uint64(20), s.nodeByRoot[indexToHash(2)].balance) - assert.Equal(t, uint64(30), s.nodeByRoot[indexToHash(3)].balance) + assert.Equal(t, uint64(10), s.nodeByRoot[indexToHash(1)].balance.Uint64()) + assert.Equal(t, uint64(20), s.nodeByRoot[indexToHash(2)].balance.Uint64()) + assert.Equal(t, uint64(30), s.nodeByRoot[indexToHash(3)].balance.Uint64()) } func TestForkChoice_UpdateBalancesNegativeChange(t *testing.T) { @@ -103,9 +104,9 @@ func TestForkChoice_UpdateBalancesNegativeChange(t *testing.T) { require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, st, blkRoot)) s := f.store - s.nodeByRoot[indexToHash(1)].balance = 100 - s.nodeByRoot[indexToHash(2)].balance = 100 - s.nodeByRoot[indexToHash(3)].balance = 100 + s.nodeByRoot[indexToHash(1)].balance = big.NewInt(100) + s.nodeByRoot[indexToHash(2)].balance = big.NewInt(100) + s.nodeByRoot[indexToHash(3)].balance = big.NewInt(100) f.balances = []uint64{100, 100, 100} f.votes = []Vote{ @@ -116,9 +117,9 @@ func TestForkChoice_UpdateBalancesNegativeChange(t *testing.T) { f.justifiedBalances = []uint64{10, 20, 30} require.NoError(t, f.updateBalances()) - assert.Equal(t, uint64(10), s.nodeByRoot[indexToHash(1)].balance) - assert.Equal(t, uint64(20), s.nodeByRoot[indexToHash(2)].balance) - assert.Equal(t, uint64(30), s.nodeByRoot[indexToHash(3)].balance) + assert.Equal(t, uint64(10), s.nodeByRoot[indexToHash(1)].balance.Uint64()) + assert.Equal(t, uint64(20), s.nodeByRoot[indexToHash(2)].balance.Uint64()) + assert.Equal(t, uint64(30), s.nodeByRoot[indexToHash(3)].balance.Uint64()) } func TestForkChoice_UpdateBalancesUnderflow(t *testing.T) { @@ -134,9 +135,9 @@ func TestForkChoice_UpdateBalancesUnderflow(t *testing.T) { require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, st, blkRoot)) s := f.store - s.nodeByRoot[indexToHash(1)].balance = 100 - s.nodeByRoot[indexToHash(2)].balance = 100 - s.nodeByRoot[indexToHash(3)].balance = 100 + s.nodeByRoot[indexToHash(1)].balance = big.NewInt(100) + s.nodeByRoot[indexToHash(2)].balance = big.NewInt(100) + s.nodeByRoot[indexToHash(3)].balance = big.NewInt(100) f.balances = []uint64{125, 125, 125} f.votes = []Vote{ @@ -147,9 +148,9 @@ func TestForkChoice_UpdateBalancesUnderflow(t *testing.T) { f.justifiedBalances = []uint64{10, 20, 30} require.NoError(t, f.updateBalances()) - assert.Equal(t, uint64(0), s.nodeByRoot[indexToHash(1)].balance) - assert.Equal(t, uint64(0), s.nodeByRoot[indexToHash(2)].balance) - assert.Equal(t, uint64(5), s.nodeByRoot[indexToHash(3)].balance) + assert.Equal(t, uint64(0), s.nodeByRoot[indexToHash(1)].balance.Uint64()) + assert.Equal(t, uint64(0), s.nodeByRoot[indexToHash(2)].balance.Uint64()) + assert.Equal(t, uint64(5), s.nodeByRoot[indexToHash(3)].balance.Uint64()) } func TestForkChoice_IsCanonical(t *testing.T) { @@ -205,10 +206,10 @@ func TestForkChoice_IsCanonicalReorg(t *testing.T) { require.NoError(t, err) require.NoError(t, f.InsertNode(ctx, st, blkRoot)) - f.store.nodeByRoot[[32]byte{'3'}].balance = 10 + f.store.nodeByRoot[[32]byte{'3'}].balance = big.NewInt(10) require.NoError(t, f.store.treeRootNode.applyWeightChanges(ctx)) - require.Equal(t, uint64(10), f.store.nodeByRoot[[32]byte{'1'}].weight) - require.Equal(t, uint64(0), f.store.nodeByRoot[[32]byte{'2'}].weight) + require.Equal(t, uint64(10), f.store.nodeByRoot[[32]byte{'1'}].weight.Uint64()) + require.Equal(t, uint64(0), f.store.nodeByRoot[[32]byte{'2'}].weight.Uint64()) require.NoError(t, f.store.treeRootNode.updateBestDescendant(ctx, 1, 1, 1)) require.DeepEqual(t, [32]byte{'3'}, f.store.treeRootNode.bestDescendant.root) @@ -323,21 +324,21 @@ func TestForkChoice_RemoveEquivocating(t *testing.T) { // Process b's slashing, c is now head f.InsertSlashedIndex(ctx, 1) - require.Equal(t, uint64(200), f.store.nodeByRoot[[32]byte{'b'}].balance) + require.Equal(t, uint64(200), f.store.nodeByRoot[[32]byte{'b'}].balance.Uint64()) f.justifiedBalances = []uint64{100, 200, 200, 300} head, err = f.Head(ctx) - require.Equal(t, uint64(200), f.store.nodeByRoot[[32]byte{'b'}].weight) - require.Equal(t, uint64(300), f.store.nodeByRoot[[32]byte{'c'}].weight) + require.Equal(t, uint64(200), f.store.nodeByRoot[[32]byte{'b'}].weight.Uint64()) + require.Equal(t, uint64(300), f.store.nodeByRoot[[32]byte{'c'}].weight.Uint64()) require.NoError(t, err) require.Equal(t, [32]byte{'c'}, head) // Process b's slashing again, should be a noop f.InsertSlashedIndex(ctx, 1) - require.Equal(t, uint64(200), f.store.nodeByRoot[[32]byte{'b'}].balance) + require.Equal(t, uint64(200), f.store.nodeByRoot[[32]byte{'b'}].balance.Uint64()) f.justifiedBalances = []uint64{100, 200, 200, 300} head, err = f.Head(ctx) - require.Equal(t, uint64(200), f.store.nodeByRoot[[32]byte{'b'}].weight) - require.Equal(t, uint64(300), f.store.nodeByRoot[[32]byte{'c'}].weight) + require.Equal(t, uint64(200), f.store.nodeByRoot[[32]byte{'b'}].weight.Uint64()) + require.Equal(t, uint64(300), f.store.nodeByRoot[[32]byte{'c'}].weight.Uint64()) require.NoError(t, err) require.Equal(t, [32]byte{'c'}, head) @@ -720,14 +721,14 @@ func TestWeight(t *testing.T) { n, ok := f.store.nodeByRoot[root] require.Equal(t, true, ok) - n.weight = 10 + n.weight = big.NewInt(10) w, err := f.Weight(root) require.NoError(t, err) - require.Equal(t, uint64(10), w) + require.Equal(t, uint64(10), w.Uint64()) w, err = f.Weight([32]byte{'b'}) require.ErrorIs(t, err, ErrNilNode) - require.Equal(t, uint64(0), w) + require.Equal(t, uint64(0), w.Uint64()) } func TestForkchoice_UpdateJustifiedBalances(t *testing.T) { @@ -738,7 +739,7 @@ func TestForkchoice_UpdateJustifiedBalances(t *testing.T) { } require.NoError(t, f.updateJustifiedBalances(context.Background(), [32]byte{})) require.Equal(t, uint64(7), f.numActiveValidators) - require.Equal(t, uint64(430)/32, f.store.committeeWeight) + require.Equal(t, uint64(430)/32, f.store.committeeWeight.Uint64()) require.DeepEqual(t, balances, f.justifiedBalances) } diff --git a/beacon-chain/forkchoice/doubly-linked-tree/node.go b/beacon-chain/forkchoice/doubly-linked-tree/node.go index 2c4253b43..54b4d2815 100644 --- a/beacon-chain/forkchoice/doubly-linked-tree/node.go +++ b/beacon-chain/forkchoice/doubly-linked-tree/node.go @@ -3,6 +3,7 @@ package doublylinkedtree import ( "bytes" "context" + "math/big" "github.com/pkg/errors" "github.com/prysmaticlabs/prysm/v5/config/params" @@ -19,7 +20,7 @@ const ProcessAttestationsThreshold = 10 // using the current balance stored in each node. func (n *Node) applyWeightChanges(ctx context.Context) error { // Recursively calling the children to sum their weights. - childrenWeight := uint64(0) + childrenWeight := big.NewInt(0) for _, child := range n.children { if ctx.Err() != nil { return ctx.Err() @@ -27,12 +28,12 @@ func (n *Node) applyWeightChanges(ctx context.Context) error { if err := child.applyWeightChanges(ctx); err != nil { return err } - childrenWeight += child.weight + childrenWeight.Add(childrenWeight, child.weight) } if n.root == params.BeaconConfig().ZeroHash { return nil } - n.weight = n.balance + childrenWeight + n.weight.Add(n.balance, childrenWeight) return nil } @@ -48,7 +49,7 @@ func (n *Node) updateBestDescendant(ctx context.Context, justifiedEpoch, finaliz } var bestChild *Node - bestWeight := uint64(0) + bestWeight := big.NewInt(0) hasViableDescendant := false for _, child := range n.children { if child == nil { @@ -66,12 +67,12 @@ func (n *Node) updateBestDescendant(ctx context.Context, justifiedEpoch, finaliz hasViableDescendant = true } else if childLeadsToViableHead { // If both are viable, compare their weights. - if child.weight == bestWeight { + if child.weight.Cmp(bestWeight) == 0 { // Tie-breaker of equal weights by root. if bytes.Compare(child.root[:], bestChild.root[:]) > 0 { bestChild = child } - } else if child.weight > bestWeight { + } else if child.weight.Cmp(bestWeight) == 1 { bestChild = child bestWeight = child.weight } diff --git a/beacon-chain/forkchoice/doubly-linked-tree/node_test.go b/beacon-chain/forkchoice/doubly-linked-tree/node_test.go index 66bd0e607..27270941c 100644 --- a/beacon-chain/forkchoice/doubly-linked-tree/node_test.go +++ b/beacon-chain/forkchoice/doubly-linked-tree/node_test.go @@ -2,6 +2,7 @@ package doublylinkedtree import ( "context" + "math/big" "testing" "github.com/prysmaticlabs/prysm/v5/config/params" @@ -27,15 +28,15 @@ func TestNode_ApplyWeightChanges_PositiveChange(t *testing.T) { // The updated balances of each node is 100 s := f.store - s.nodeByRoot[indexToHash(1)].balance = 100 - s.nodeByRoot[indexToHash(2)].balance = 100 - s.nodeByRoot[indexToHash(3)].balance = 100 + s.nodeByRoot[indexToHash(1)].balance = big.NewInt(100) + s.nodeByRoot[indexToHash(2)].balance = big.NewInt(100) + s.nodeByRoot[indexToHash(3)].balance = big.NewInt(100) assert.NoError(t, s.treeRootNode.applyWeightChanges(ctx)) - assert.Equal(t, uint64(300), s.nodeByRoot[indexToHash(1)].weight) - assert.Equal(t, uint64(200), s.nodeByRoot[indexToHash(2)].weight) - assert.Equal(t, uint64(100), s.nodeByRoot[indexToHash(3)].weight) + assert.Equal(t, uint64(300), s.nodeByRoot[indexToHash(1)].weight.Uint64()) + assert.Equal(t, uint64(200), s.nodeByRoot[indexToHash(2)].weight.Uint64()) + assert.Equal(t, uint64(100), s.nodeByRoot[indexToHash(3)].weight.Uint64()) } func TestNode_ApplyWeightChanges_NegativeChange(t *testing.T) { @@ -53,19 +54,19 @@ func TestNode_ApplyWeightChanges_NegativeChange(t *testing.T) { // The updated balances of each node is 100 s := f.store - s.nodeByRoot[indexToHash(1)].weight = 400 - s.nodeByRoot[indexToHash(2)].weight = 400 - s.nodeByRoot[indexToHash(3)].weight = 400 + s.nodeByRoot[indexToHash(1)].weight = big.NewInt(400) + s.nodeByRoot[indexToHash(2)].weight = big.NewInt(400) + s.nodeByRoot[indexToHash(3)].weight = big.NewInt(400) - s.nodeByRoot[indexToHash(1)].balance = 100 - s.nodeByRoot[indexToHash(2)].balance = 100 - s.nodeByRoot[indexToHash(3)].balance = 100 + s.nodeByRoot[indexToHash(1)].balance = big.NewInt(100) + s.nodeByRoot[indexToHash(2)].balance = big.NewInt(100) + s.nodeByRoot[indexToHash(3)].balance = big.NewInt(100) assert.NoError(t, s.treeRootNode.applyWeightChanges(ctx)) - assert.Equal(t, uint64(300), s.nodeByRoot[indexToHash(1)].weight) - assert.Equal(t, uint64(200), s.nodeByRoot[indexToHash(2)].weight) - assert.Equal(t, uint64(100), s.nodeByRoot[indexToHash(3)].weight) + assert.Equal(t, uint64(300), s.nodeByRoot[indexToHash(1)].weight.Uint64()) + assert.Equal(t, uint64(200), s.nodeByRoot[indexToHash(2)].weight.Uint64()) + assert.Equal(t, uint64(100), s.nodeByRoot[indexToHash(3)].weight.Uint64()) } func TestNode_UpdateBestDescendant_NonViableChild(t *testing.T) { @@ -108,8 +109,8 @@ func TestNode_UpdateBestDescendant_HigherWeightChild(t *testing.T) { require.NoError(t, f.InsertNode(ctx, state, blkRoot)) s := f.store - s.nodeByRoot[indexToHash(1)].weight = 100 - s.nodeByRoot[indexToHash(2)].weight = 200 + s.nodeByRoot[indexToHash(1)].weight = big.NewInt(100) + s.nodeByRoot[indexToHash(2)].weight = big.NewInt(200) assert.NoError(t, s.treeRootNode.updateBestDescendant(ctx, 1, 1, 1)) assert.Equal(t, 2, len(s.treeRootNode.children)) @@ -128,8 +129,8 @@ func TestNode_UpdateBestDescendant_LowerWeightChild(t *testing.T) { require.NoError(t, f.InsertNode(ctx, state, blkRoot)) s := f.store - s.nodeByRoot[indexToHash(1)].weight = 200 - s.nodeByRoot[indexToHash(2)].weight = 100 + s.nodeByRoot[indexToHash(1)].weight = big.NewInt(200) + s.nodeByRoot[indexToHash(2)].weight = big.NewInt(100) assert.NoError(t, s.treeRootNode.updateBestDescendant(ctx, 1, 1, 1)) assert.Equal(t, 2, len(s.treeRootNode.children)) diff --git a/beacon-chain/forkchoice/doubly-linked-tree/proposer_boost.go b/beacon-chain/forkchoice/doubly-linked-tree/proposer_boost.go index 0ebbd83de..c03905831 100644 --- a/beacon-chain/forkchoice/doubly-linked-tree/proposer_boost.go +++ b/beacon-chain/forkchoice/doubly-linked-tree/proposer_boost.go @@ -2,6 +2,7 @@ package doublylinkedtree import ( "fmt" + "math/big" fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams" "github.com/prysmaticlabs/prysm/v5/config/params" @@ -11,13 +12,13 @@ import ( // relevant nodes. func (f *ForkChoice) applyProposerBoostScore() error { s := f.store - proposerScore := uint64(0) + proposerScore := big.NewInt(0) if s.previousProposerBoostRoot != params.BeaconConfig().ZeroHash { previousNode, ok := s.nodeByRoot[s.previousProposerBoostRoot] if !ok || previousNode == nil { log.WithError(errInvalidProposerBoostRoot).Errorf(fmt.Sprintf("invalid prev root %#x", s.previousProposerBoostRoot)) } else { - previousNode.balance -= s.previousProposerBoostScore + previousNode.balance.Sub(previousNode.balance, new(big.Int).SetUint64(s.previousProposerBoostScore)) } } @@ -26,12 +27,13 @@ func (f *ForkChoice) applyProposerBoostScore() error { if !ok || currentNode == nil { log.WithError(errInvalidProposerBoostRoot).Errorf(fmt.Sprintf("invalid current root %#x", s.proposerBoostRoot)) } else { - proposerScore = (s.committeeWeight * params.BeaconConfig().ProposerScoreBoost) / 100 - currentNode.balance += proposerScore + proposerScore = new(big.Int).Mul(s.committeeWeight, new(big.Int).SetUint64(params.BeaconConfig().ProposerScoreBoost)) + proposerScore.Div(proposerScore, big.NewInt(100)) + currentNode.balance.Add(currentNode.balance, proposerScore) } } s.previousProposerBoostRoot = s.proposerBoostRoot - s.previousProposerBoostScore = proposerScore + s.previousProposerBoostScore = proposerScore.Uint64() return nil } diff --git a/beacon-chain/forkchoice/doubly-linked-tree/proposer_boost_test.go b/beacon-chain/forkchoice/doubly-linked-tree/proposer_boost_test.go index 28b1be753..8eecd7689 100644 --- a/beacon-chain/forkchoice/doubly-linked-tree/proposer_boost_test.go +++ b/beacon-chain/forkchoice/doubly-linked-tree/proposer_boost_test.go @@ -2,6 +2,7 @@ package doublylinkedtree import ( "context" + "math/big" "testing" "time" @@ -34,7 +35,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { t.Run("back-propagates boost score to ancestors after proposer boosting", func(t *testing.T) { f := setup(jEpoch, fEpoch) f.justifiedBalances = balances - f.store.committeeWeight = uint64(len(balances)*10) / uint64(params.BeaconConfig().SlotsPerEpoch) + f.store.committeeWeight = new(big.Int).SetUint64(uint64(len(balances)*10) / uint64(params.BeaconConfig().SlotsPerEpoch)) f.numActiveValidators = uint64(len(balances)) // The head should always start at the finalized block. @@ -154,7 +155,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { // Expect nodes to have a boosted, back-propagated score. // Ancestors have the added weights of their children. Genesis is a special exception at 0 weight, - require.Equal(t, f.store.treeRootNode.weight, uint64(0)) + require.Equal(t, f.store.treeRootNode.weight.Uint64(), uint64(0)) // Proposer boost score with these test parameters is 8 // Each of the nodes received one attestation accounting for 10. @@ -164,13 +165,13 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { // \--------------->(4: 18) // node1 := f.store.nodeByRoot[indexToHash(1)] - require.Equal(t, node1.weight, uint64(48)) + require.Equal(t, node1.weight.Uint64(), uint64(48)) node2 := f.store.nodeByRoot[indexToHash(2)] - require.Equal(t, node2.weight, uint64(38)) + require.Equal(t, node2.weight.Uint64(), uint64(38)) node3 := f.store.nodeByRoot[indexToHash(3)] - require.Equal(t, node3.weight, uint64(10)) + require.Equal(t, node3.weight.Uint64(), uint64(10)) node4 := f.store.nodeByRoot[indexToHash(4)] - require.Equal(t, node4.weight, uint64(18)) + require.Equal(t, node4.weight.Uint64(), uint64(18)) // Regression: process attestations for C, check that it // becomes head, we need two attestations to have C.weight = 30 > 24 = D.weight @@ -327,7 +328,7 @@ func TestForkChoice_BoostProposerRoot_PreventsExAnteAttack(t *testing.T) { // Block D received at N+3 — D is head f := setup(jEpoch, fEpoch) f.justifiedBalances = balances - f.store.committeeWeight = uint64(len(balances)*10) / uint64(params.BeaconConfig().SlotsPerEpoch) + f.store.committeeWeight = new(big.Int).SetUint64(uint64(len(balances)*10) / uint64(params.BeaconConfig().SlotsPerEpoch)) f.numActiveValidators = uint64(len(balances)) a := zeroHash diff --git a/beacon-chain/forkchoice/doubly-linked-tree/reorg_late_blocks.go b/beacon-chain/forkchoice/doubly-linked-tree/reorg_late_blocks.go index 8bc717802..78d25e2b6 100644 --- a/beacon-chain/forkchoice/doubly-linked-tree/reorg_late_blocks.go +++ b/beacon-chain/forkchoice/doubly-linked-tree/reorg_late_blocks.go @@ -1,6 +1,7 @@ package doublylinkedtree import ( + "math/big" "time" "github.com/prysmaticlabs/prysm/v5/config/params" @@ -78,7 +79,7 @@ func (f *ForkChoice) ShouldOverrideFCU() (override bool) { // } // Only orphan a block if the head LMD vote is weak - if head.weight*100 > f.store.committeeWeight*params.BeaconConfig().ReorgWeightThreshold { + if f.headVoteIsStrong() { return } @@ -92,7 +93,7 @@ func (f *ForkChoice) ShouldOverrideFCU() (override bool) { return true } // Only orphan a block if the parent LMD vote is strong - if parent.weight*100 < f.store.committeeWeight*params.BeaconConfig().ReorgParentWeightThreshold { + if f.parentVoteIsWeak() { return } return true @@ -143,12 +144,12 @@ func (f *ForkChoice) GetProposerHead() [32]byte { } // Only orphan a block if the head LMD vote is weak - if head.weight*100 > f.store.committeeWeight*params.BeaconConfig().ReorgWeightThreshold { + if f.headVoteIsStrong() { return head.root } // Only orphan a block if the parent LMD vote is strong - if parent.weight*100 < f.store.committeeWeight*params.BeaconConfig().ReorgParentWeightThreshold { + if f.parentVoteIsWeak() { return head.root } @@ -163,3 +164,25 @@ func (f *ForkChoice) GetProposerHead() [32]byte { } return parent.root } + +// headVoteIsStrong returns true if the head LMD vote is explicitly above the threshold. +func (f *ForkChoice) headVoteIsStrong() bool { + // This function replaces the following upstream code, using big.Int: + // head.weight*100 > f.store.committeeWeight*params.BeaconConfig().ReorgWeightThreshold + head := f.store.headNode + scaledHeadWeight := new(big.Int).Mul(head.weight, big.NewInt(100)) + threshold := new(big.Int).SetUint64(params.BeaconConfig().ReorgWeightThreshold) + threshold.Mul(threshold, f.store.committeeWeight) + return scaledHeadWeight.Cmp(threshold) == 1 +} + +// parentVoteIsWeak returns true if the parent LMD vote is explicitly below the threshold. +func (f *ForkChoice) parentVoteIsWeak() bool { + // This function replaces the following upstream code, using big.Int: + // parent.weight*100 < f.store.committeeWeight*params.BeaconConfig().ReorgParentWeightThreshold + parent := f.store.headNode.parent + scaledParentWeight := new(big.Int).Mul(parent.weight, big.NewInt(100)) + threshold := new(big.Int).SetUint64(params.BeaconConfig().ReorgParentWeightThreshold) + threshold.Mul(threshold, f.store.committeeWeight) + return scaledParentWeight.Cmp(threshold) == -1 +} diff --git a/beacon-chain/forkchoice/doubly-linked-tree/reorg_late_blocks_test.go b/beacon-chain/forkchoice/doubly-linked-tree/reorg_late_blocks_test.go index dd6694aed..bedee1d56 100644 --- a/beacon-chain/forkchoice/doubly-linked-tree/reorg_late_blocks_test.go +++ b/beacon-chain/forkchoice/doubly-linked-tree/reorg_late_blocks_test.go @@ -2,6 +2,7 @@ package doublylinkedtree import ( "context" + "math/big" "testing" "github.com/prysmaticlabs/prysm/v5/config/params" @@ -14,9 +15,9 @@ func TestForkChoice_ShouldOverrideFCU(t *testing.T) { f.justifiedBalances = make([]uint64, f.numActiveValidators) for i := range f.justifiedBalances { f.justifiedBalances[i] = uint64(10) - f.store.committeeWeight += uint64(10) + f.store.committeeWeight.Add(f.store.committeeWeight, big.NewInt(10)) } - f.store.committeeWeight /= uint64(params.BeaconConfig().SlotsPerEpoch) + f.store.committeeWeight.Div(f.store.committeeWeight, new(big.Int).SetUint64(uint64(params.BeaconConfig().SlotsPerEpoch))) ctx := context.Background() driftGenesisTime(f, 1, 0) st, root, err := prepareForkchoiceState(ctx, 1, [32]byte{'a'}, [32]byte{}, [32]byte{'A'}, 0, 0) @@ -87,14 +88,14 @@ func TestForkChoice_ShouldOverrideFCU(t *testing.T) { }) t.Run("parent is weak early call", func(t *testing.T) { saved := f.store.headNode.parent.weight - f.store.headNode.parent.weight = 0 + f.store.headNode.parent.weight = big.NewInt(0) require.Equal(t, true, f.ShouldOverrideFCU()) f.store.headNode.parent.weight = saved }) t.Run("parent is weak late call", func(t *testing.T) { saved := f.store.headNode.parent.weight driftGenesisTime(f, 2, 11) - f.store.headNode.parent.weight = 0 + f.store.headNode.parent.weight = big.NewInt(0) require.Equal(t, false, f.ShouldOverrideFCU()) f.store.headNode.parent.weight = saved driftGenesisTime(f, 2, orphanLateBlockFirstThreshold+1) @@ -111,9 +112,9 @@ func TestForkChoice_GetProposerHead(t *testing.T) { f.justifiedBalances = make([]uint64, f.numActiveValidators) for i := range f.justifiedBalances { f.justifiedBalances[i] = uint64(10) - f.store.committeeWeight += uint64(10) + f.store.committeeWeight.Add(f.store.committeeWeight, big.NewInt(10)) } - f.store.committeeWeight /= uint64(params.BeaconConfig().SlotsPerEpoch) + f.store.committeeWeight.Div(f.store.committeeWeight, new(big.Int).SetUint64(uint64(params.BeaconConfig().SlotsPerEpoch))) ctx := context.Background() driftGenesisTime(f, 1, 0) parentRoot := [32]byte{'a'} @@ -187,7 +188,7 @@ func TestForkChoice_GetProposerHead(t *testing.T) { }) t.Run("parent is weak", func(t *testing.T) { saved := f.store.headNode.parent.weight - f.store.headNode.parent.weight = 0 + f.store.headNode.parent.weight = big.NewInt(0) require.Equal(t, false, f.ShouldOverrideFCU()) f.store.headNode.parent.weight = saved }) diff --git a/beacon-chain/forkchoice/doubly-linked-tree/store.go b/beacon-chain/forkchoice/doubly-linked-tree/store.go index 23b58a058..821cf8559 100644 --- a/beacon-chain/forkchoice/doubly-linked-tree/store.go +++ b/beacon-chain/forkchoice/doubly-linked-tree/store.go @@ -3,6 +3,7 @@ package doublylinkedtree import ( "context" "fmt" + "math/big" "time" "github.com/pkg/errors" @@ -45,8 +46,10 @@ func (s *Store) head(ctx context.Context) ([32]byte, error) { currentEpoch := slots.EpochsSinceGenesis(time.Unix(int64(s.genesisTime), 0)) if !bestDescendant.viableForHead(s.justifiedCheckpoint.Epoch, currentEpoch) { s.allTipsAreInvalid = true + weight := big.NewInt(10e9) + weight.Div(bestDescendant.weight, weight) return [32]byte{}, fmt.Errorf("head at slot %d with weight %d is not eligible, finalizedEpoch, justified Epoch %d, %d != %d, %d", - bestDescendant.slot, bestDescendant.weight/10e9, bestDescendant.finalizedEpoch, bestDescendant.justifiedEpoch, s.finalizedCheckpoint.Epoch, s.justifiedCheckpoint.Epoch) + bestDescendant.slot, weight, bestDescendant.finalizedEpoch, bestDescendant.justifiedEpoch, s.finalizedCheckpoint.Epoch, s.justifiedCheckpoint.Epoch) } s.allTipsAreInvalid = false @@ -85,6 +88,8 @@ func (s *Store) insert(ctx context.Context, unrealizedFinalizedEpoch: finalizedEpoch, optimistic: true, payloadHash: payloadHash, + balance: big.NewInt(0), + weight: big.NewInt(0), timestamp: uint64(time.Now().Unix()), } diff --git a/beacon-chain/forkchoice/doubly-linked-tree/types.go b/beacon-chain/forkchoice/doubly-linked-tree/types.go index ad5bffa79..82b88b315 100644 --- a/beacon-chain/forkchoice/doubly-linked-tree/types.go +++ b/beacon-chain/forkchoice/doubly-linked-tree/types.go @@ -1,6 +1,7 @@ package doublylinkedtree import ( + "math/big" "sync" "github.com/prysmaticlabs/prysm/v5/beacon-chain/forkchoice" @@ -30,7 +31,7 @@ type Store struct { proposerBoostRoot [fieldparams.RootLength]byte // latest block root that was boosted after being received in a timely manner. previousProposerBoostRoot [fieldparams.RootLength]byte // previous block root that was boosted after being received in a timely manner. previousProposerBoostScore uint64 // previous proposer boosted root score. - committeeWeight uint64 // tracks the total active validator balance divided by the number of slots per Epoch. + committeeWeight *big.Int // tracks the total active validator balance divided by the number of slots per Epoch. treeRootNode *Node // the root node of the store tree. headNode *Node // last head Node nodeByRoot map[[fieldparams.RootLength]byte]*Node // nodes indexed by roots. @@ -56,8 +57,8 @@ type Node struct { unrealizedJustifiedEpoch primitives.Epoch // the epoch that would be justified if the block would be advanced to the next epoch. finalizedEpoch primitives.Epoch // finalizedEpoch of this node. unrealizedFinalizedEpoch primitives.Epoch // the epoch that would be finalized if the block would be advanced to the next epoch. - balance uint64 // the balance that voted for this node directly - weight uint64 // weight of this node: the total balance including children + balance *big.Int // the balance that voted for this node directly + weight *big.Int // weight of this node: the total balance including children bestDescendant *Node // bestDescendant node of this node. optimistic bool // whether the block has been fully validated or not timestamp uint64 // The timestamp when the node was inserted. diff --git a/beacon-chain/forkchoice/doubly-linked-tree/unrealized_justification_test.go b/beacon-chain/forkchoice/doubly-linked-tree/unrealized_justification_test.go index bddf0fe7c..9d0776b0e 100644 --- a/beacon-chain/forkchoice/doubly-linked-tree/unrealized_justification_test.go +++ b/beacon-chain/forkchoice/doubly-linked-tree/unrealized_justification_test.go @@ -99,8 +99,8 @@ func TestStore_LongFork(t *testing.T) { headRoot, err = f.Head(ctx) require.NoError(t, err) require.Equal(t, [32]byte{'c'}, headRoot) - require.Equal(t, uint64(0), f.store.nodeByRoot[[32]byte{'d'}].weight) - require.Equal(t, uint64(100), f.store.nodeByRoot[[32]byte{'c'}].weight) + require.Equal(t, uint64(0), f.store.nodeByRoot[[32]byte{'d'}].weight.Uint64()) + require.Equal(t, uint64(100), f.store.nodeByRoot[[32]byte{'c'}].weight.Uint64()) } // Epoch 1 Epoch 2 Epoch 3 @@ -244,8 +244,8 @@ func TestStore_ForkNextEpoch(t *testing.T) { require.NoError(t, err) require.Equal(t, [32]byte{'d'}, headRoot) require.Equal(t, primitives.Epoch(2), f.JustifiedCheckpoint().Epoch) - require.Equal(t, uint64(0), f.store.nodeByRoot[[32]byte{'d'}].weight) - require.Equal(t, uint64(100), f.store.nodeByRoot[[32]byte{'h'}].weight) + require.Equal(t, uint64(0), f.store.nodeByRoot[[32]byte{'d'}].weight.Uint64()) + require.Equal(t, uint64(100), f.store.nodeByRoot[[32]byte{'h'}].weight.Uint64()) // Set current epoch to 3, and H's unrealized checkpoint. Check it's head driftGenesisTime(f, 99, 0) require.NoError(t, f.store.setUnrealizedJustifiedEpoch([32]byte{'h'}, 2)) @@ -253,8 +253,8 @@ func TestStore_ForkNextEpoch(t *testing.T) { require.NoError(t, err) require.Equal(t, [32]byte{'h'}, headRoot) require.Equal(t, primitives.Epoch(2), f.JustifiedCheckpoint().Epoch) - require.Equal(t, uint64(0), f.store.nodeByRoot[[32]byte{'d'}].weight) - require.Equal(t, uint64(100), f.store.nodeByRoot[[32]byte{'h'}].weight) + require.Equal(t, uint64(0), f.store.nodeByRoot[[32]byte{'d'}].weight.Uint64()) + require.Equal(t, uint64(100), f.store.nodeByRoot[[32]byte{'h'}].weight.Uint64()) } func TestStore_PullTips_Heuristics(t *testing.T) { diff --git a/beacon-chain/forkchoice/interfaces.go b/beacon-chain/forkchoice/interfaces.go index d8d94e246..bd5c25d75 100644 --- a/beacon-chain/forkchoice/interfaces.go +++ b/beacon-chain/forkchoice/interfaces.go @@ -2,6 +2,7 @@ package forkchoice import ( "context" + "math/big" forkchoicetypes "github.com/prysmaticlabs/prysm/v5/beacon-chain/forkchoice/types" "github.com/prysmaticlabs/prysm/v5/beacon-chain/state" @@ -79,7 +80,7 @@ type FastGetter interface { Slot([32]byte) (primitives.Slot, error) TargetRootForEpoch([32]byte, primitives.Epoch) ([32]byte, error) UnrealizedJustifiedPayloadBlockHash() [32]byte - Weight(root [32]byte) (uint64, error) + Weight(root [32]byte) (*big.Int, error) } // Setter allows to set forkchoice information diff --git a/beacon-chain/forkchoice/ro.go b/beacon-chain/forkchoice/ro.go index cf536c522..6653cb918 100644 --- a/beacon-chain/forkchoice/ro.go +++ b/beacon-chain/forkchoice/ro.go @@ -1,6 +1,8 @@ package forkchoice import ( + "math/big" + forkchoicetypes "github.com/prysmaticlabs/prysm/v5/beacon-chain/forkchoice/types" fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams" "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" @@ -129,7 +131,7 @@ func (ro *ROForkChoice) ReceivedBlocksLastEpoch() (uint64, error) { } // Weight delegates to the underlying forkchoice call, under a lock. -func (ro *ROForkChoice) Weight(root [32]byte) (uint64, error) { +func (ro *ROForkChoice) Weight(root [32]byte) (*big.Int, error) { ro.l.RLock() defer ro.l.RUnlock() return ro.getter.Weight(root) diff --git a/beacon-chain/forkchoice/ro_test.go b/beacon-chain/forkchoice/ro_test.go index e24e2fcdf..fd3a0f1c8 100644 --- a/beacon-chain/forkchoice/ro_test.go +++ b/beacon-chain/forkchoice/ro_test.go @@ -2,6 +2,7 @@ package forkchoice import ( "io" + "math/big" "testing" forkchoicetypes "github.com/prysmaticlabs/prysm/v5/beacon-chain/forkchoice/types" @@ -261,9 +262,9 @@ func (ro *mockROForkchoice) ReceivedBlocksLastEpoch() (uint64, error) { return 0, nil } -func (ro *mockROForkchoice) Weight(_ [32]byte) (uint64, error) { +func (ro *mockROForkchoice) Weight(_ [32]byte) (*big.Int, error) { ro.calls = append(ro.calls, weightCalled) - return 0, nil + return new(big.Int), nil } func (ro *mockROForkchoice) IsOptimistic(_ [32]byte) (bool, error) { diff --git a/beacon-chain/rpc/prysm/v1alpha1/beacon/validators.go b/beacon-chain/rpc/prysm/v1alpha1/beacon/validators.go index efad2fa89..3fa9cb36e 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/beacon/validators.go +++ b/beacon-chain/rpc/prysm/v1alpha1/beacon/validators.go @@ -3,6 +3,7 @@ package beacon import ( "context" "fmt" + "math/big" "sort" "strconv" @@ -543,21 +544,36 @@ func (bs *Server) GetValidatorParticipation( } cp := bs.FinalizationFetcher.FinalizedCheckpt() + var globalParticipationRate float32 + zero := big.NewInt(0) + if b.PrevEpochTargetAttested.Cmp(zero) == 1 || b.ActivePrevEpoch.Cmp(zero) == 1 { + prevEpochTargetAttested, f1 := new(big.Float).SetString(b.PrevEpochTargetAttested.String()) + if !f1 { + globalParticipationRate = float32(0) + } + activePrevEpoch, f2 := new(big.Float).SetString(b.ActivePrevEpoch.String()) + if !f2 { + globalParticipationRate = float32(0) + } + if f1 && f2 { + globalParticipationRate, _ = new(big.Float).Quo(prevEpochTargetAttested, activePrevEpoch).Float32() + } + } p := ðpb.ValidatorParticipationResponse{ Epoch: requestedEpoch, Finalized: requestedEpoch <= cp.Epoch, Participation: ðpb.ValidatorParticipation{ // TODO(7130): Remove these three deprecated fields. - GlobalParticipationRate: float32(b.PrevEpochTargetAttested) / float32(b.ActivePrevEpoch), - VotedEther: b.PrevEpochTargetAttested, - EligibleEther: b.ActivePrevEpoch, - CurrentEpochActiveGwei: b.ActiveCurrentEpoch, - CurrentEpochAttestingGwei: b.CurrentEpochAttested, - CurrentEpochTargetAttestingGwei: b.CurrentEpochTargetAttested, - PreviousEpochActiveGwei: b.ActivePrevEpoch, - PreviousEpochAttestingGwei: b.PrevEpochAttested, - PreviousEpochTargetAttestingGwei: b.PrevEpochTargetAttested, - PreviousEpochHeadAttestingGwei: b.PrevEpochHeadAttested, + GlobalParticipationRate: globalParticipationRate, + VotedEther: b.PrevEpochTargetAttested.String(), + EligibleEther: b.ActivePrevEpoch.String(), + CurrentEpochActiveGwei: b.ActiveCurrentEpoch.String(), + CurrentEpochAttestingGwei: b.CurrentEpochAttested.String(), + CurrentEpochTargetAttestingGwei: b.CurrentEpochTargetAttested.String(), + PreviousEpochActiveGwei: b.ActivePrevEpoch.String(), + PreviousEpochAttestingGwei: b.PrevEpochAttested.String(), + PreviousEpochTargetAttestingGwei: b.PrevEpochTargetAttested.String(), + PreviousEpochHeadAttestingGwei: b.PrevEpochHeadAttested.String(), }, } diff --git a/beacon-chain/rpc/prysm/v1alpha1/beacon/validators_test.go b/beacon-chain/rpc/prysm/v1alpha1/beacon/validators_test.go index 56a6a3f3e..9e71817a4 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/beacon/validators_test.go +++ b/beacon-chain/rpc/prysm/v1alpha1/beacon/validators_test.go @@ -4,6 +4,7 @@ import ( "context" "encoding/binary" "fmt" + "math/big" "sort" "strconv" "testing" @@ -1566,18 +1567,35 @@ func TestServer_GetValidatorParticipation_CurrentAndPrevEpoch(t *testing.T) { res, err := bs.GetValidatorParticipation(ctx, ðpb.GetValidatorParticipationRequest{QueryFilter: ðpb.GetValidatorParticipationRequest_Epoch{Epoch: 1}}) require.NoError(t, err) - + effectiveBal := new(big.Int).SetUint64(params.BeaconConfig().MaxEffectiveBalance) + effectiveBalIncr := new(big.Int).SetUint64(params.BeaconConfig().EffectiveBalanceIncrement) + valCountTimesEffBal := new(big.Int).Mul(new(big.Int).SetUint64(validatorCount), effectiveBal) + var globalParticipationRate float32 + zero := big.NewInt(0) + if effectiveBalIncr.Cmp(zero) == 1 || valCountTimesEffBal.Cmp(zero) == 1 { + prevEpochTargetAttested, f1 := new(big.Float).SetString(effectiveBalIncr.String()) + if !f1 { + globalParticipationRate = float32(0) + } + activePrevEpoch, f2 := new(big.Float).SetString(valCountTimesEffBal.String()) + if !f2 { + globalParticipationRate = float32(0) + } + if f1 && f2 { + globalParticipationRate, _ = new(big.Float).Quo(prevEpochTargetAttested, activePrevEpoch).Float32() + } + } wanted := ðpb.ValidatorParticipation{ - GlobalParticipationRate: float32(params.BeaconConfig().EffectiveBalanceIncrement) / float32(validatorCount*params.BeaconConfig().MaxEffectiveBalance), - VotedEther: params.BeaconConfig().EffectiveBalanceIncrement, - EligibleEther: validatorCount * params.BeaconConfig().MaxEffectiveBalance, - CurrentEpochActiveGwei: validatorCount * params.BeaconConfig().MaxEffectiveBalance, - CurrentEpochAttestingGwei: params.BeaconConfig().EffectiveBalanceIncrement, - CurrentEpochTargetAttestingGwei: params.BeaconConfig().EffectiveBalanceIncrement, - PreviousEpochActiveGwei: validatorCount * params.BeaconConfig().MaxEffectiveBalance, - PreviousEpochAttestingGwei: params.BeaconConfig().EffectiveBalanceIncrement, - PreviousEpochTargetAttestingGwei: params.BeaconConfig().EffectiveBalanceIncrement, - PreviousEpochHeadAttestingGwei: params.BeaconConfig().EffectiveBalanceIncrement, + GlobalParticipationRate: globalParticipationRate, + VotedEther: effectiveBalIncr.String(), + EligibleEther: valCountTimesEffBal.String(), + CurrentEpochActiveGwei: valCountTimesEffBal.String(), + CurrentEpochAttestingGwei: effectiveBalIncr.String(), + CurrentEpochTargetAttestingGwei: effectiveBalIncr.String(), + PreviousEpochActiveGwei: valCountTimesEffBal.String(), + PreviousEpochAttestingGwei: effectiveBalIncr.String(), + PreviousEpochTargetAttestingGwei: effectiveBalIncr.String(), + PreviousEpochHeadAttestingGwei: effectiveBalIncr.String(), } assert.DeepEqual(t, true, res.Finalized, "Incorrect validator participation respond") assert.DeepEqual(t, wanted, res.Participation, "Incorrect validator participation respond") @@ -1646,17 +1664,35 @@ func TestServer_GetValidatorParticipation_OrphanedUntilGenesis(t *testing.T) { res, err := bs.GetValidatorParticipation(ctx, ðpb.GetValidatorParticipationRequest{QueryFilter: ðpb.GetValidatorParticipationRequest_Epoch{Epoch: 1}}) require.NoError(t, err) + effectiveBal := new(big.Int).SetUint64(params.BeaconConfig().MaxEffectiveBalance) + effectiveBalIncr := new(big.Int).SetUint64(params.BeaconConfig().EffectiveBalanceIncrement) + valCountTimesEffBal := new(big.Int).Mul(new(big.Int).SetUint64(validatorCount), effectiveBal) + var globalParticipationRate float32 + zero := big.NewInt(0) + if effectiveBalIncr.Cmp(zero) == 1 || valCountTimesEffBal.Cmp(zero) == 1 { + prevEpochTargetAttested, f1 := new(big.Float).SetString(effectiveBalIncr.String()) + if !f1 { + globalParticipationRate = float32(0) + } + activePrevEpoch, f2 := new(big.Float).SetString(valCountTimesEffBal.String()) + if !f2 { + globalParticipationRate = float32(0) + } + if f1 && f2 { + globalParticipationRate, _ = new(big.Float).Quo(prevEpochTargetAttested, activePrevEpoch).Float32() + } + } wanted := ðpb.ValidatorParticipation{ - GlobalParticipationRate: float32(params.BeaconConfig().EffectiveBalanceIncrement) / float32(validatorCount*params.BeaconConfig().MaxEffectiveBalance), - VotedEther: params.BeaconConfig().EffectiveBalanceIncrement, - EligibleEther: validatorCount * params.BeaconConfig().MaxEffectiveBalance, - CurrentEpochActiveGwei: validatorCount * params.BeaconConfig().MaxEffectiveBalance, - CurrentEpochAttestingGwei: params.BeaconConfig().EffectiveBalanceIncrement, - CurrentEpochTargetAttestingGwei: params.BeaconConfig().EffectiveBalanceIncrement, - PreviousEpochActiveGwei: validatorCount * params.BeaconConfig().MaxEffectiveBalance, - PreviousEpochAttestingGwei: params.BeaconConfig().EffectiveBalanceIncrement, - PreviousEpochTargetAttestingGwei: params.BeaconConfig().EffectiveBalanceIncrement, - PreviousEpochHeadAttestingGwei: params.BeaconConfig().EffectiveBalanceIncrement, + GlobalParticipationRate: globalParticipationRate, + VotedEther: effectiveBalIncr.String(), + EligibleEther: valCountTimesEffBal.String(), + CurrentEpochActiveGwei: valCountTimesEffBal.String(), + CurrentEpochAttestingGwei: effectiveBalIncr.String(), + CurrentEpochTargetAttestingGwei: effectiveBalIncr.String(), + PreviousEpochActiveGwei: valCountTimesEffBal.String(), + PreviousEpochAttestingGwei: effectiveBalIncr.String(), + PreviousEpochTargetAttestingGwei: effectiveBalIncr.String(), + PreviousEpochHeadAttestingGwei: effectiveBalIncr.String(), } assert.DeepEqual(t, true, res.Finalized, "Incorrect validator participation respond") assert.DeepEqual(t, wanted, res.Participation, "Incorrect validator participation respond") @@ -1757,17 +1793,20 @@ func runGetValidatorParticipationCurrentAndPrevEpoch(t *testing.T, genState stat res, err := bs.GetValidatorParticipation(ctx, ðpb.GetValidatorParticipationRequest{QueryFilter: ðpb.GetValidatorParticipationRequest_Epoch{Epoch: 0}}) require.NoError(t, err) + effectiveBal := new(big.Int).SetUint64(params.BeaconConfig().MaxEffectiveBalance) + effectiveBalIncr := new(big.Int).SetUint64(params.BeaconConfig().EffectiveBalanceIncrement) + valCountTimesEffBal := new(big.Int).Mul(new(big.Int).SetUint64(validatorCount), effectiveBal) wanted := ðpb.ValidatorParticipation{ - GlobalParticipationRate: 1, - VotedEther: validatorCount * params.BeaconConfig().MaxEffectiveBalance, - EligibleEther: validatorCount * params.BeaconConfig().MaxEffectiveBalance, - CurrentEpochActiveGwei: validatorCount * params.BeaconConfig().MaxEffectiveBalance, - CurrentEpochAttestingGwei: validatorCount * params.BeaconConfig().MaxEffectiveBalance, - CurrentEpochTargetAttestingGwei: validatorCount * params.BeaconConfig().MaxEffectiveBalance, - PreviousEpochActiveGwei: validatorCount * params.BeaconConfig().MaxEffectiveBalance, - PreviousEpochAttestingGwei: validatorCount * params.BeaconConfig().MaxEffectiveBalance, - PreviousEpochTargetAttestingGwei: validatorCount * params.BeaconConfig().MaxEffectiveBalance, - PreviousEpochHeadAttestingGwei: validatorCount * params.BeaconConfig().MaxEffectiveBalance, + GlobalParticipationRate: float32(1), + VotedEther: valCountTimesEffBal.String(), + EligibleEther: valCountTimesEffBal.String(), + CurrentEpochActiveGwei: valCountTimesEffBal.String(), + CurrentEpochAttestingGwei: valCountTimesEffBal.String(), + CurrentEpochTargetAttestingGwei: valCountTimesEffBal.String(), + PreviousEpochActiveGwei: valCountTimesEffBal.String(), + PreviousEpochAttestingGwei: valCountTimesEffBal.String(), + PreviousEpochTargetAttestingGwei: valCountTimesEffBal.String(), + PreviousEpochHeadAttestingGwei: valCountTimesEffBal.String(), } assert.DeepEqual(t, true, res.Finalized, "Incorrect validator participation respond") assert.DeepEqual(t, wanted, res.Participation, "Incorrect validator participation respond") @@ -1776,16 +1815,16 @@ func runGetValidatorParticipationCurrentAndPrevEpoch(t *testing.T, genState stat require.NoError(t, err) wanted = ðpb.ValidatorParticipation{ - GlobalParticipationRate: 1, - VotedEther: validatorCount * params.BeaconConfig().MaxEffectiveBalance, - EligibleEther: validatorCount * params.BeaconConfig().MaxEffectiveBalance, - CurrentEpochActiveGwei: validatorCount * params.BeaconConfig().MaxEffectiveBalance, - CurrentEpochAttestingGwei: params.BeaconConfig().EffectiveBalanceIncrement, // Empty because after one epoch, current participation rotates to previous - CurrentEpochTargetAttestingGwei: params.BeaconConfig().EffectiveBalanceIncrement, - PreviousEpochActiveGwei: validatorCount * params.BeaconConfig().MaxEffectiveBalance, - PreviousEpochAttestingGwei: validatorCount * params.BeaconConfig().MaxEffectiveBalance, - PreviousEpochTargetAttestingGwei: validatorCount * params.BeaconConfig().MaxEffectiveBalance, - PreviousEpochHeadAttestingGwei: validatorCount * params.BeaconConfig().MaxEffectiveBalance, + GlobalParticipationRate: float32(1), + VotedEther: valCountTimesEffBal.String(), + EligibleEther: valCountTimesEffBal.String(), + CurrentEpochActiveGwei: valCountTimesEffBal.String(), + CurrentEpochAttestingGwei: effectiveBalIncr.String(), // Empty because after one epoch, current participation rotates to previous + CurrentEpochTargetAttestingGwei: effectiveBalIncr.String(), + PreviousEpochActiveGwei: valCountTimesEffBal.String(), + PreviousEpochAttestingGwei: valCountTimesEffBal.String(), + PreviousEpochTargetAttestingGwei: valCountTimesEffBal.String(), + PreviousEpochHeadAttestingGwei: valCountTimesEffBal.String(), } assert.DeepEqual(t, true, res.Finalized, "Incorrect validator participation respond") assert.DeepEqual(t, wanted, res.Participation, "Incorrect validator participation respond") diff --git a/beacon-chain/state/interfaces.go b/beacon-chain/state/interfaces.go index 11d063ebf..5e2158d6c 100644 --- a/beacon-chain/state/interfaces.go +++ b/beacon-chain/state/interfaces.go @@ -6,6 +6,7 @@ package state import ( "context" "encoding/json" + "math/big" "github.com/prysmaticlabs/go-bitfield" fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams" @@ -143,7 +144,7 @@ type ReadOnlyCheckpoint interface { FinalizedCheckpoint() *ethpb.Checkpoint FinalizedCheckpointEpoch() primitives.Epoch JustificationBits() bitfield.Bitvector4 - UnrealizedCheckpointBalances() (uint64, uint64, uint64, error) + UnrealizedCheckpointBalances() (*big.Int, *big.Int, *big.Int, error) } // ReadOnlyBlockRoots defines a struct which only has read access to block roots methods. diff --git a/beacon-chain/state/state-native/getters_participation.go b/beacon-chain/state/state-native/getters_participation.go index 2693695b3..3ca21b1a9 100644 --- a/beacon-chain/state/state-native/getters_participation.go +++ b/beacon-chain/state/state-native/getters_participation.go @@ -1,6 +1,8 @@ package state_native import ( + "math/big" + "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/time" "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/stateutil" "github.com/prysmaticlabs/prysm/v5/config/features" @@ -42,9 +44,9 @@ func (b *BeaconState) PreviousEpochParticipation() ([]byte, error) { // UnrealizedCheckpointBalances returns the total balances: active, target attested in // current epoch and target attested in previous epoch. This function is used to // compute the "unrealized justification" that a synced Beacon Block will have. -func (b *BeaconState) UnrealizedCheckpointBalances() (uint64, uint64, uint64, error) { +func (b *BeaconState) UnrealizedCheckpointBalances() (*big.Int, *big.Int, *big.Int, error) { if b.version == version.Phase0 { - return 0, 0, 0, errNotSupported("UnrealizedCheckpointBalances", b.version) + return big.NewInt(0), big.NewInt(0), big.NewInt(0), errNotSupported("UnrealizedCheckpointBalances", b.version) } currentEpoch := time.CurrentEpoch(b) @@ -54,7 +56,7 @@ func (b *BeaconState) UnrealizedCheckpointBalances() (uint64, uint64, uint64, er cp := b.currentEpochParticipation pp := b.previousEpochParticipation if cp == nil || pp == nil { - return 0, 0, 0, ErrNilParticipation + return big.NewInt(0), big.NewInt(0), big.NewInt(0), ErrNilParticipation } if features.Get().EnableExperimentalState { diff --git a/beacon-chain/state/state-native/getters_participation_test.go b/beacon-chain/state/state-native/getters_participation_test.go index b5aac094e..ce05127e1 100644 --- a/beacon-chain/state/state-native/getters_participation_test.go +++ b/beacon-chain/state/state-native/getters_participation_test.go @@ -34,9 +34,9 @@ func TestState_UnrealizedCheckpointBalances(t *testing.T) { allActive := params.BeaconConfig().MinGenesisActiveValidatorCount * params.BeaconConfig().MaxEffectiveBalance active, previous, current, err := state.UnrealizedCheckpointBalances() require.NoError(t, err) - require.Equal(t, allActive, active) - require.Equal(t, params.BeaconConfig().EffectiveBalanceIncrement, current) - require.Equal(t, params.BeaconConfig().EffectiveBalanceIncrement, previous) + require.Equal(t, allActive, active.Uint64()) + require.Equal(t, params.BeaconConfig().EffectiveBalanceIncrement, current.Uint64()) + require.Equal(t, params.BeaconConfig().EffectiveBalanceIncrement, previous.Uint64()) // Add some votes in the last two epochs: base.CurrentEpochParticipation[0] = 0xFF @@ -47,9 +47,9 @@ func TestState_UnrealizedCheckpointBalances(t *testing.T) { require.NoError(t, err) active, previous, current, err = state.UnrealizedCheckpointBalances() require.NoError(t, err) - require.Equal(t, allActive, active) - require.Equal(t, params.BeaconConfig().MaxEffectiveBalance, current) - require.Equal(t, 2*params.BeaconConfig().MaxEffectiveBalance, previous) + require.Equal(t, allActive, active.Uint64()) + require.Equal(t, params.BeaconConfig().MaxEffectiveBalance, current.Uint64()) + require.Equal(t, 2*params.BeaconConfig().MaxEffectiveBalance, previous.Uint64()) // Slash some validators validators[0].Slashed = true @@ -57,8 +57,8 @@ func TestState_UnrealizedCheckpointBalances(t *testing.T) { require.NoError(t, err) active, previous, current, err = state.UnrealizedCheckpointBalances() require.NoError(t, err) - require.Equal(t, allActive, active) - require.Equal(t, params.BeaconConfig().EffectiveBalanceIncrement, current) - require.Equal(t, params.BeaconConfig().MaxEffectiveBalance, previous) + require.Equal(t, allActive, active.Uint64()) + require.Equal(t, params.BeaconConfig().EffectiveBalanceIncrement, current.Uint64()) + require.Equal(t, params.BeaconConfig().MaxEffectiveBalance, previous.Uint64()) } diff --git a/beacon-chain/state/stateutil/unrealized_justification.go b/beacon-chain/state/stateutil/unrealized_justification.go index 7ca4eac6b..3ef1de93f 100644 --- a/beacon-chain/state/stateutil/unrealized_justification.go +++ b/beacon-chain/state/stateutil/unrealized_justification.go @@ -1,10 +1,11 @@ package stateutil import ( + "math/big" + "github.com/pkg/errors" "github.com/prysmaticlabs/prysm/v5/config/params" "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" - "github.com/prysmaticlabs/prysm/v5/math" ) // UnrealizedCheckpointBalances returns the total current active balance, the @@ -12,58 +13,50 @@ import ( // current epoch correctly attested for target balance. It takes the current and // previous epoch participation bits as parameters so implicitly only works for // beacon states post-Altair. -func UnrealizedCheckpointBalances(cp, pp []byte, validators ValReader, currentEpoch primitives.Epoch) (uint64, uint64, uint64, error) { +func UnrealizedCheckpointBalances(cp, pp []byte, validators ValReader, currentEpoch primitives.Epoch) (*big.Int, *big.Int, *big.Int, error) { targetIdx := params.BeaconConfig().TimelyTargetFlagIndex - activeBalance := uint64(0) - currentTarget := uint64(0) - prevTarget := uint64(0) + activeBalance := big.NewInt(0) + currentTarget := big.NewInt(0) + prevTarget := big.NewInt(0) if len(cp) < validators.Len() || len(pp) < validators.Len() { - return 0, 0, 0, errors.New("participation does not match validator set") + return activeBalance, currentTarget, prevTarget, errors.New("participation does not match validator set") } valLength := validators.Len() for i := 0; i < valLength; i++ { v, err := validators.At(i) if err != nil { - return 0, 0, 0, err + return activeBalance, currentTarget, prevTarget, err } activeCurrent := v.ActivationEpoch <= currentEpoch && currentEpoch < v.ExitEpoch + effectiveBalance := new(big.Int).SetUint64(v.EffectiveBalance) if activeCurrent { - activeBalance, err = math.Add64(activeBalance, v.EffectiveBalance) - if err != nil { - return 0, 0, 0, err - } + activeBalance.Add(activeBalance, effectiveBalance) } if v.Slashed { continue } if activeCurrent && ((cp[i]>>targetIdx)&1) == 1 { - currentTarget, err = math.Add64(currentTarget, v.EffectiveBalance) - if err != nil { - return 0, 0, 0, err - } + currentTarget.Add(currentTarget, effectiveBalance) } activePrevious := v.ActivationEpoch < currentEpoch && currentEpoch <= v.ExitEpoch if activePrevious && ((pp[i]>>targetIdx)&1) == 1 { - prevTarget, err = math.Add64(prevTarget, v.EffectiveBalance) - if err != nil { - return 0, 0, 0, err - } + prevTarget.Add(prevTarget, effectiveBalance) } } activeBalance, prevTarget, currentTarget = ensureLowerBound(activeBalance, prevTarget, currentTarget) return activeBalance, prevTarget, currentTarget, nil } -func ensureLowerBound(activeCurrEpoch, prevTargetAttested, currTargetAttested uint64) (uint64, uint64, uint64) { - ebi := params.BeaconConfig().EffectiveBalanceIncrement - if ebi > activeCurrEpoch { +func ensureLowerBound(activeCurrEpoch, prevTargetAttested, currTargetAttested *big.Int) (*big.Int, *big.Int, *big.Int) { + ebi := new(big.Int).SetUint64(params.BeaconConfig().EffectiveBalanceIncrement) + if ebi.Cmp(activeCurrEpoch) > 0 { activeCurrEpoch = ebi } - if ebi > prevTargetAttested { + if ebi.Cmp(prevTargetAttested) > 0 { prevTargetAttested = ebi } - if ebi > currTargetAttested { + if ebi.Cmp(currTargetAttested) > 0 { currTargetAttested = ebi } return activeCurrEpoch, prevTargetAttested, currTargetAttested diff --git a/beacon-chain/state/stateutil/unrealized_justification_test.go b/beacon-chain/state/stateutil/unrealized_justification_test.go index e87c240ec..abd5d6f0e 100644 --- a/beacon-chain/state/stateutil/unrealized_justification_test.go +++ b/beacon-chain/state/stateutil/unrealized_justification_test.go @@ -28,9 +28,9 @@ func TestState_UnrealizedCheckpointBalances(t *testing.T) { t.Run("No one voted last two epochs", func(tt *testing.T) { active, previous, current, err := UnrealizedCheckpointBalances(cp, pp, NewValSliceReader(validators), 0) require.NoError(tt, err) - require.Equal(tt, expectedActive, active) - require.Equal(tt, params.BeaconConfig().EffectiveBalanceIncrement, current) - require.Equal(tt, params.BeaconConfig().EffectiveBalanceIncrement, previous) + require.Equal(tt, expectedActive, active.Uint64()) + require.Equal(tt, params.BeaconConfig().EffectiveBalanceIncrement, current.Uint64()) + require.Equal(tt, params.BeaconConfig().EffectiveBalanceIncrement, previous.Uint64()) }) t.Run("bad votes in last two epochs", func(tt *testing.T) { @@ -38,9 +38,9 @@ func TestState_UnrealizedCheckpointBalances(t *testing.T) { copy(pp, []byte{0x00, 0x00, 0x00, 0x00}) active, previous, current, err := UnrealizedCheckpointBalances(cp, pp, NewValSliceReader(validators), 1) require.NoError(tt, err) - require.Equal(tt, expectedActive, active) - require.Equal(tt, params.BeaconConfig().EffectiveBalanceIncrement, current) - require.Equal(tt, params.BeaconConfig().EffectiveBalanceIncrement, previous) + require.Equal(tt, expectedActive, active.Uint64()) + require.Equal(tt, params.BeaconConfig().EffectiveBalanceIncrement, current.Uint64()) + require.Equal(tt, params.BeaconConfig().EffectiveBalanceIncrement, previous.Uint64()) }) t.Run("two votes in last epoch", func(tt *testing.T) { @@ -48,9 +48,9 @@ func TestState_UnrealizedCheckpointBalances(t *testing.T) { copy(pp, []byte{0x00, 0x00, 0x00, 0x00, 0xFF ^ (1 << targetFlag)}) active, previous, current, err := UnrealizedCheckpointBalances(cp, pp, NewValSliceReader(validators), 1) require.NoError(tt, err) - require.Equal(tt, expectedActive, active) - require.Equal(tt, 2*params.BeaconConfig().MaxEffectiveBalance, current) - require.Equal(tt, params.BeaconConfig().EffectiveBalanceIncrement, previous) + require.Equal(tt, expectedActive, active.Uint64()) + require.Equal(tt, 2*params.BeaconConfig().MaxEffectiveBalance, current.Uint64()) + require.Equal(tt, params.BeaconConfig().EffectiveBalanceIncrement, previous.Uint64()) }) t.Run("two votes in previous epoch", func(tt *testing.T) { @@ -58,9 +58,9 @@ func TestState_UnrealizedCheckpointBalances(t *testing.T) { copy(pp, []byte{0xFF ^ (1 << targetFlag), 0xFF ^ (1 << targetFlag), 0xFF ^ (1 << targetFlag), 0x00, 1 << targetFlag, 1 << targetFlag}) active, previous, current, err := UnrealizedCheckpointBalances(cp, pp, NewValSliceReader(validators), 1) require.NoError(tt, err) - require.Equal(tt, expectedActive, active) - require.Equal(tt, params.BeaconConfig().EffectiveBalanceIncrement, current) - require.Equal(tt, 2*params.BeaconConfig().MaxEffectiveBalance, previous) + require.Equal(tt, expectedActive, active.Uint64()) + require.Equal(tt, params.BeaconConfig().EffectiveBalanceIncrement, current.Uint64()) + require.Equal(tt, 2*params.BeaconConfig().MaxEffectiveBalance, previous.Uint64()) }) t.Run("votes in both epochs, decreased balance in first validator", func(tt *testing.T) { @@ -70,27 +70,27 @@ func TestState_UnrealizedCheckpointBalances(t *testing.T) { active, previous, current, err := UnrealizedCheckpointBalances(cp, pp, NewValSliceReader(validators), 1) require.NoError(tt, err) expectedActive -= params.BeaconConfig().MinDepositAmount - require.Equal(tt, expectedActive, active) - require.Equal(tt, 2*params.BeaconConfig().MaxEffectiveBalance-params.BeaconConfig().MinDepositAmount, current) - require.Equal(tt, 2*params.BeaconConfig().MaxEffectiveBalance, previous) + require.Equal(tt, expectedActive, active.Uint64()) + require.Equal(tt, 2*params.BeaconConfig().MaxEffectiveBalance-params.BeaconConfig().MinDepositAmount, current.Uint64()) + require.Equal(tt, 2*params.BeaconConfig().MaxEffectiveBalance, previous.Uint64()) }) t.Run("slash a validator", func(tt *testing.T) { validators[1].Slashed = true active, previous, current, err := UnrealizedCheckpointBalances(cp, pp, NewValSliceReader(validators), 1) require.NoError(tt, err) - require.Equal(tt, expectedActive, active) - require.Equal(tt, params.BeaconConfig().MaxEffectiveBalance-params.BeaconConfig().MinDepositAmount, current) - require.Equal(tt, 2*params.BeaconConfig().MaxEffectiveBalance, previous) + require.Equal(tt, expectedActive, active.Uint64()) + require.Equal(tt, params.BeaconConfig().MaxEffectiveBalance-params.BeaconConfig().MinDepositAmount, current.Uint64()) + require.Equal(tt, 2*params.BeaconConfig().MaxEffectiveBalance, previous.Uint64()) }) t.Run("Exit a validator", func(tt *testing.T) { validators[4].ExitEpoch = 1 active, previous, current, err := UnrealizedCheckpointBalances(cp, pp, NewValSliceReader(validators), 2) require.NoError(tt, err) expectedActive -= params.BeaconConfig().MaxEffectiveBalance - require.Equal(tt, expectedActive, active) - require.Equal(tt, params.BeaconConfig().MaxEffectiveBalance-params.BeaconConfig().MinDepositAmount, current) - require.Equal(tt, params.BeaconConfig().MaxEffectiveBalance, previous) + require.Equal(tt, expectedActive, active.Uint64()) + require.Equal(tt, params.BeaconConfig().MaxEffectiveBalance-params.BeaconConfig().MinDepositAmount, current.Uint64()) + require.Equal(tt, params.BeaconConfig().MaxEffectiveBalance, previous.Uint64()) }) } @@ -117,9 +117,9 @@ func TestState_MVSlice_UnrealizedCheckpointBalances(t *testing.T) { t.Run("No one voted last two epochs", func(tt *testing.T) { active, previous, current, err := UnrealizedCheckpointBalances(cp, pp, NewValMultiValueSliceReader(mv, &testObject{id: 0}), 0) require.NoError(tt, err) - require.Equal(tt, expectedActive, active) - require.Equal(tt, params.BeaconConfig().EffectiveBalanceIncrement, current) - require.Equal(tt, params.BeaconConfig().EffectiveBalanceIncrement, previous) + require.Equal(tt, expectedActive, active.Uint64()) + require.Equal(tt, params.BeaconConfig().EffectiveBalanceIncrement, current.Uint64()) + require.Equal(tt, params.BeaconConfig().EffectiveBalanceIncrement, previous.Uint64()) }) t.Run("bad votes in last two epochs", func(tt *testing.T) { @@ -127,9 +127,9 @@ func TestState_MVSlice_UnrealizedCheckpointBalances(t *testing.T) { copy(pp, []byte{0x00, 0x00, 0x00, 0x00}) active, previous, current, err := UnrealizedCheckpointBalances(cp, pp, NewValMultiValueSliceReader(mv, &testObject{id: 0}), 1) require.NoError(tt, err) - require.Equal(tt, expectedActive, active) - require.Equal(tt, params.BeaconConfig().EffectiveBalanceIncrement, current) - require.Equal(tt, params.BeaconConfig().EffectiveBalanceIncrement, previous) + require.Equal(tt, expectedActive, active.Uint64()) + require.Equal(tt, params.BeaconConfig().EffectiveBalanceIncrement, current.Uint64()) + require.Equal(tt, params.BeaconConfig().EffectiveBalanceIncrement, previous.Uint64()) }) t.Run("two votes in last epoch", func(tt *testing.T) { @@ -137,9 +137,9 @@ func TestState_MVSlice_UnrealizedCheckpointBalances(t *testing.T) { copy(pp, []byte{0x00, 0x00, 0x00, 0x00, 0xFF ^ (1 << targetFlag)}) active, previous, current, err := UnrealizedCheckpointBalances(cp, pp, NewValMultiValueSliceReader(mv, &testObject{id: 0}), 1) require.NoError(tt, err) - require.Equal(tt, expectedActive, active) - require.Equal(tt, 2*params.BeaconConfig().MaxEffectiveBalance, current) - require.Equal(tt, params.BeaconConfig().EffectiveBalanceIncrement, previous) + require.Equal(tt, expectedActive, active.Uint64()) + require.Equal(tt, 2*params.BeaconConfig().MaxEffectiveBalance, current.Uint64()) + require.Equal(tt, params.BeaconConfig().EffectiveBalanceIncrement, previous.Uint64()) }) t.Run("two votes in previous epoch", func(tt *testing.T) { @@ -147,9 +147,9 @@ func TestState_MVSlice_UnrealizedCheckpointBalances(t *testing.T) { copy(pp, []byte{0xFF ^ (1 << targetFlag), 0xFF ^ (1 << targetFlag), 0xFF ^ (1 << targetFlag), 0x00, 1 << targetFlag, 1 << targetFlag}) active, previous, current, err := UnrealizedCheckpointBalances(cp, pp, NewValMultiValueSliceReader(mv, &testObject{id: 0}), 1) require.NoError(tt, err) - require.Equal(tt, expectedActive, active) - require.Equal(tt, params.BeaconConfig().EffectiveBalanceIncrement, current) - require.Equal(tt, 2*params.BeaconConfig().MaxEffectiveBalance, previous) + require.Equal(tt, expectedActive, active.Uint64()) + require.Equal(tt, params.BeaconConfig().EffectiveBalanceIncrement, current.Uint64()) + require.Equal(tt, 2*params.BeaconConfig().MaxEffectiveBalance, previous.Uint64()) }) t.Run("votes in both epochs, decreased balance in first validator", func(tt *testing.T) { @@ -159,27 +159,27 @@ func TestState_MVSlice_UnrealizedCheckpointBalances(t *testing.T) { active, previous, current, err := UnrealizedCheckpointBalances(cp, pp, NewValMultiValueSliceReader(mv, &testObject{id: 0}), 1) require.NoError(tt, err) expectedActive -= params.BeaconConfig().MinDepositAmount - require.Equal(tt, expectedActive, active) - require.Equal(tt, 2*params.BeaconConfig().MaxEffectiveBalance-params.BeaconConfig().MinDepositAmount, current) - require.Equal(tt, 2*params.BeaconConfig().MaxEffectiveBalance, previous) + require.Equal(tt, expectedActive, active.Uint64()) + require.Equal(tt, 2*params.BeaconConfig().MaxEffectiveBalance-params.BeaconConfig().MinDepositAmount, current.Uint64()) + require.Equal(tt, 2*params.BeaconConfig().MaxEffectiveBalance, previous.Uint64()) }) t.Run("slash a validator", func(tt *testing.T) { validators[1].Slashed = true active, previous, current, err := UnrealizedCheckpointBalances(cp, pp, NewValMultiValueSliceReader(mv, &testObject{id: 0}), 1) require.NoError(tt, err) - require.Equal(tt, expectedActive, active) - require.Equal(tt, params.BeaconConfig().MaxEffectiveBalance-params.BeaconConfig().MinDepositAmount, current) - require.Equal(tt, 2*params.BeaconConfig().MaxEffectiveBalance, previous) + require.Equal(tt, expectedActive, active.Uint64()) + require.Equal(tt, params.BeaconConfig().MaxEffectiveBalance-params.BeaconConfig().MinDepositAmount, current.Uint64()) + require.Equal(tt, 2*params.BeaconConfig().MaxEffectiveBalance, previous.Uint64()) }) t.Run("Exit a validator", func(tt *testing.T) { validators[4].ExitEpoch = 1 active, previous, current, err := UnrealizedCheckpointBalances(cp, pp, NewValMultiValueSliceReader(mv, &testObject{id: 0}), 2) require.NoError(tt, err) expectedActive -= params.BeaconConfig().MaxEffectiveBalance - require.Equal(tt, expectedActive, active) - require.Equal(tt, params.BeaconConfig().MaxEffectiveBalance-params.BeaconConfig().MinDepositAmount, current) - require.Equal(tt, params.BeaconConfig().MaxEffectiveBalance, previous) + require.Equal(tt, expectedActive, active.Uint64()) + require.Equal(tt, params.BeaconConfig().MaxEffectiveBalance-params.BeaconConfig().MinDepositAmount, current.Uint64()) + require.Equal(tt, params.BeaconConfig().MaxEffectiveBalance, previous.Uint64()) }) } diff --git a/consensus-types/forkchoice/types.go b/consensus-types/forkchoice/types.go index 3707f80c5..c7db0da41 100644 --- a/consensus-types/forkchoice/types.go +++ b/consensus-types/forkchoice/types.go @@ -1,6 +1,8 @@ package forkchoice import ( + "math/big" + "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" eth "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" ) @@ -45,8 +47,8 @@ type Node struct { FinalizedEpoch primitives.Epoch UnrealizedJustifiedEpoch primitives.Epoch UnrealizedFinalizedEpoch primitives.Epoch - Balance uint64 - Weight uint64 + Balance *big.Int + Weight *big.Int Timestamp uint64 BlockRoot []byte ParentRoot []byte diff --git a/proto/prysm/v1alpha1/validator.pb.go b/proto/prysm/v1alpha1/validator.pb.go index 55edb0726..7f3bec0b0 100755 --- a/proto/prysm/v1alpha1/validator.pb.go +++ b/proto/prysm/v1alpha1/validator.pb.go @@ -1904,16 +1904,16 @@ type ValidatorParticipation struct { // Deprecated: Marked as deprecated in proto/prysm/v1alpha1/validator.proto. GlobalParticipationRate float32 `protobuf:"fixed32,1,opt,name=global_participation_rate,json=globalParticipationRate,proto3" json:"global_participation_rate,omitempty"` // Deprecated: Marked as deprecated in proto/prysm/v1alpha1/validator.proto. - VotedEther uint64 `protobuf:"varint,2,opt,name=voted_ether,json=votedEther,proto3" json:"voted_ether,omitempty"` + VotedEther string `protobuf:"bytes,2,opt,name=voted_ether,json=votedEther,proto3" json:"voted_ether,omitempty"` // Deprecated: Marked as deprecated in proto/prysm/v1alpha1/validator.proto. - EligibleEther uint64 `protobuf:"varint,3,opt,name=eligible_ether,json=eligibleEther,proto3" json:"eligible_ether,omitempty"` - CurrentEpochActiveGwei uint64 `protobuf:"varint,4,opt,name=current_epoch_active_gwei,json=currentEpochActiveGwei,proto3" json:"current_epoch_active_gwei,omitempty"` - CurrentEpochAttestingGwei uint64 `protobuf:"varint,5,opt,name=current_epoch_attesting_gwei,json=currentEpochAttestingGwei,proto3" json:"current_epoch_attesting_gwei,omitempty"` - CurrentEpochTargetAttestingGwei uint64 `protobuf:"varint,6,opt,name=current_epoch_target_attesting_gwei,json=currentEpochTargetAttestingGwei,proto3" json:"current_epoch_target_attesting_gwei,omitempty"` - PreviousEpochActiveGwei uint64 `protobuf:"varint,7,opt,name=previous_epoch_active_gwei,json=previousEpochActiveGwei,proto3" json:"previous_epoch_active_gwei,omitempty"` - PreviousEpochAttestingGwei uint64 `protobuf:"varint,8,opt,name=previous_epoch_attesting_gwei,json=previousEpochAttestingGwei,proto3" json:"previous_epoch_attesting_gwei,omitempty"` - PreviousEpochTargetAttestingGwei uint64 `protobuf:"varint,9,opt,name=previous_epoch_target_attesting_gwei,json=previousEpochTargetAttestingGwei,proto3" json:"previous_epoch_target_attesting_gwei,omitempty"` - PreviousEpochHeadAttestingGwei uint64 `protobuf:"varint,10,opt,name=previous_epoch_head_attesting_gwei,json=previousEpochHeadAttestingGwei,proto3" json:"previous_epoch_head_attesting_gwei,omitempty"` + EligibleEther string `protobuf:"bytes,3,opt,name=eligible_ether,json=eligibleEther,proto3" json:"eligible_ether,omitempty"` + CurrentEpochActiveGwei string `protobuf:"bytes,4,opt,name=current_epoch_active_gwei,json=currentEpochActiveGwei,proto3" json:"current_epoch_active_gwei,omitempty"` + CurrentEpochAttestingGwei string `protobuf:"bytes,5,opt,name=current_epoch_attesting_gwei,json=currentEpochAttestingGwei,proto3" json:"current_epoch_attesting_gwei,omitempty"` + CurrentEpochTargetAttestingGwei string `protobuf:"bytes,6,opt,name=current_epoch_target_attesting_gwei,json=currentEpochTargetAttestingGwei,proto3" json:"current_epoch_target_attesting_gwei,omitempty"` + PreviousEpochActiveGwei string `protobuf:"bytes,7,opt,name=previous_epoch_active_gwei,json=previousEpochActiveGwei,proto3" json:"previous_epoch_active_gwei,omitempty"` + PreviousEpochAttestingGwei string `protobuf:"bytes,8,opt,name=previous_epoch_attesting_gwei,json=previousEpochAttestingGwei,proto3" json:"previous_epoch_attesting_gwei,omitempty"` + PreviousEpochTargetAttestingGwei string `protobuf:"bytes,9,opt,name=previous_epoch_target_attesting_gwei,json=previousEpochTargetAttestingGwei,proto3" json:"previous_epoch_target_attesting_gwei,omitempty"` + PreviousEpochHeadAttestingGwei string `protobuf:"bytes,10,opt,name=previous_epoch_head_attesting_gwei,json=previousEpochHeadAttestingGwei,proto3" json:"previous_epoch_head_attesting_gwei,omitempty"` } func (x *ValidatorParticipation) Reset() { @@ -1957,68 +1957,68 @@ func (x *ValidatorParticipation) GetGlobalParticipationRate() float32 { } // Deprecated: Marked as deprecated in proto/prysm/v1alpha1/validator.proto. -func (x *ValidatorParticipation) GetVotedEther() uint64 { +func (x *ValidatorParticipation) GetVotedEther() string { if x != nil { return x.VotedEther } - return 0 + return "" } // Deprecated: Marked as deprecated in proto/prysm/v1alpha1/validator.proto. -func (x *ValidatorParticipation) GetEligibleEther() uint64 { +func (x *ValidatorParticipation) GetEligibleEther() string { if x != nil { return x.EligibleEther } - return 0 + return "" } -func (x *ValidatorParticipation) GetCurrentEpochActiveGwei() uint64 { +func (x *ValidatorParticipation) GetCurrentEpochActiveGwei() string { if x != nil { return x.CurrentEpochActiveGwei } - return 0 + return "" } -func (x *ValidatorParticipation) GetCurrentEpochAttestingGwei() uint64 { +func (x *ValidatorParticipation) GetCurrentEpochAttestingGwei() string { if x != nil { return x.CurrentEpochAttestingGwei } - return 0 + return "" } -func (x *ValidatorParticipation) GetCurrentEpochTargetAttestingGwei() uint64 { +func (x *ValidatorParticipation) GetCurrentEpochTargetAttestingGwei() string { if x != nil { return x.CurrentEpochTargetAttestingGwei } - return 0 + return "" } -func (x *ValidatorParticipation) GetPreviousEpochActiveGwei() uint64 { +func (x *ValidatorParticipation) GetPreviousEpochActiveGwei() string { if x != nil { return x.PreviousEpochActiveGwei } - return 0 + return "" } -func (x *ValidatorParticipation) GetPreviousEpochAttestingGwei() uint64 { +func (x *ValidatorParticipation) GetPreviousEpochAttestingGwei() string { if x != nil { return x.PreviousEpochAttestingGwei } - return 0 + return "" } -func (x *ValidatorParticipation) GetPreviousEpochTargetAttestingGwei() uint64 { +func (x *ValidatorParticipation) GetPreviousEpochTargetAttestingGwei() string { if x != nil { return x.PreviousEpochTargetAttestingGwei } - return 0 + return "" } -func (x *ValidatorParticipation) GetPreviousEpochHeadAttestingGwei() uint64 { +func (x *ValidatorParticipation) GetPreviousEpochHeadAttestingGwei() string { if x != nil { return x.PreviousEpochHeadAttestingGwei } - return 0 + return "" } type ValidatorInfo struct { @@ -3417,39 +3417,39 @@ var file_proto_prysm_v1alpha1_validator_proto_rawDesc = []byte{ 0x72, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x02, 0x42, 0x02, 0x18, 0x01, 0x52, 0x17, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x50, 0x61, 0x72, 0x74, 0x69, 0x63, 0x69, 0x70, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x61, 0x74, 0x65, 0x12, 0x23, 0x0a, 0x0b, 0x76, 0x6f, 0x74, 0x65, 0x64, - 0x5f, 0x65, 0x74, 0x68, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x18, 0x01, + 0x5f, 0x65, 0x74, 0x68, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0a, 0x76, 0x6f, 0x74, 0x65, 0x64, 0x45, 0x74, 0x68, 0x65, 0x72, 0x12, 0x29, 0x0a, 0x0e, 0x65, 0x6c, 0x69, 0x67, 0x69, 0x62, 0x6c, 0x65, 0x5f, 0x65, 0x74, 0x68, 0x65, 0x72, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0d, 0x65, 0x6c, 0x69, 0x67, 0x69, 0x62, + 0x20, 0x01, 0x28, 0x09, 0x42, 0x02, 0x18, 0x01, 0x52, 0x0d, 0x65, 0x6c, 0x69, 0x67, 0x69, 0x62, 0x6c, 0x65, 0x45, 0x74, 0x68, 0x65, 0x72, 0x12, 0x39, 0x0a, 0x19, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, - 0x67, 0x77, 0x65, 0x69, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x16, 0x63, 0x75, 0x72, 0x72, + 0x67, 0x77, 0x65, 0x69, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x16, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x47, 0x77, 0x65, 0x69, 0x12, 0x3f, 0x0a, 0x1c, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x5f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x67, 0x77, - 0x65, 0x69, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x19, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, + 0x65, 0x69, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x19, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x47, 0x77, 0x65, 0x69, 0x12, 0x4c, 0x0a, 0x23, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x61, 0x74, 0x74, 0x65, - 0x73, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x67, 0x77, 0x65, 0x69, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, + 0x73, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x67, 0x77, 0x65, 0x69, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x47, 0x77, 0x65, 0x69, 0x12, 0x3b, 0x0a, 0x1a, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x67, 0x77, 0x65, 0x69, 0x18, - 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x17, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x45, + 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x17, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x41, 0x63, 0x74, 0x69, 0x76, 0x65, 0x47, 0x77, 0x65, 0x69, 0x12, 0x41, 0x0a, 0x1d, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x5f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x67, 0x77, 0x65, 0x69, 0x18, - 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x1a, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x45, + 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1a, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x47, 0x77, 0x65, 0x69, 0x12, 0x4e, 0x0a, 0x24, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x61, 0x74, 0x74, 0x65, 0x73, - 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x67, 0x77, 0x65, 0x69, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x67, 0x77, 0x65, 0x69, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x20, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x47, 0x77, 0x65, 0x69, 0x12, 0x4a, 0x0a, 0x22, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x65, 0x70, 0x6f, 0x63, 0x68, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x69, - 0x6e, 0x67, 0x5f, 0x67, 0x77, 0x65, 0x69, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x1e, 0x70, + 0x6e, 0x67, 0x5f, 0x67, 0x77, 0x65, 0x69, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1e, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x45, 0x70, 0x6f, 0x63, 0x68, 0x48, 0x65, 0x61, 0x64, 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x47, 0x77, 0x65, 0x69, 0x22, 0xad, 0x03, 0x0a, 0x0d, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49, 0x6e, 0x66, 0x6f, 0x12, diff --git a/proto/prysm/v1alpha1/validator.proto b/proto/prysm/v1alpha1/validator.proto index 80e5e1eb7..c4dca5dcf 100644 --- a/proto/prysm/v1alpha1/validator.proto +++ b/proto/prysm/v1alpha1/validator.proto @@ -665,24 +665,24 @@ message ValidatorParticipation { // contains a value between 0 and 1. float global_participation_rate = 1 [deprecated = true]; // The total amount of ether, in gwei, that has been used in voting. - uint64 voted_ether = 2 [deprecated = true]; + string voted_ether = 2 [deprecated = true]; // The total amount of ether, in gwei, that is eligible for voting. - uint64 eligible_ether = 3 [deprecated = true]; + string eligible_ether = 3 [deprecated = true]; // Total staked gwei that was active (i.e. eligible to vote) during the current epoch. - uint64 current_epoch_active_gwei = 4; + string current_epoch_active_gwei = 4; // Total staked gwei that had attestations included in a block during the current epoch, // attestations by the same validator do not increase this figure. - uint64 current_epoch_attesting_gwei = 5; + string current_epoch_attesting_gwei = 5; // Total staked gwei that attested to the majority-elected Casper FFG target epoch during the current epoch. - uint64 current_epoch_target_attesting_gwei = 6; + string current_epoch_target_attesting_gwei = 6; // Same as current_epoch_active_gwei but for previous epoch. - uint64 previous_epoch_active_gwei = 7; + string previous_epoch_active_gwei = 7; // Same as current_epoch_attesting_gwei but for previous epoch. - uint64 previous_epoch_attesting_gwei = 8; + string previous_epoch_attesting_gwei = 8; // Same as current_epoch_target_attesting_gwei but for previous epoch. - uint64 previous_epoch_target_attesting_gwei = 9; + string previous_epoch_target_attesting_gwei = 9; // Total staked gwei that attested to a head beacon block that is in the canonical chain. - uint64 previous_epoch_head_attesting_gwei = 10; + string previous_epoch_head_attesting_gwei = 10; } // ValidatorInfo gives information about the state of a validator at a certain epoch. diff --git a/pulse/pulse_rewards_test.go b/pulse/pulse_rewards_test.go new file mode 100644 index 000000000..6abb1f6d7 --- /dev/null +++ b/pulse/pulse_rewards_test.go @@ -0,0 +1,123 @@ +package pulse_test + +import ( + "context" + "math/big" + "testing" + + "github.com/prysmaticlabs/go-bitfield" + "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/altair" + "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers" + "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/signing" + "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/time" + p2pType "github.com/prysmaticlabs/prysm/v5/beacon-chain/p2p/types" + "github.com/prysmaticlabs/prysm/v5/config/params" + types "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" + "github.com/prysmaticlabs/prysm/v5/crypto/bls" + ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" + "github.com/prysmaticlabs/prysm/v5/testing/require" + "github.com/prysmaticlabs/prysm/v5/testing/util" + "github.com/prysmaticlabs/prysm/v5/time/slots" +) + +func TestPulseChainValidatorRewardsMock(t *testing.T) { + cfg := params.MainnetConfig() + cfg.BaseRewardFactor = 780000 + cfg.MinDepositAmount = 16000000000000000 + cfg.MaxEffectiveBalance = 32000000000000000 + params.OverrideBeaconConfig(cfg) + // Vars + numOfVals := uint64(18312) + slotsPerEpoch := uint64(32) + amountOfEpochsToProcess := 5 + + // Generate genesis Beaconchain state with required num of vals and deposit amounts + beaconState, privKeys := util.DeterministicGenesisStateBellatrix(t, numOfVals) + validators, balance, err := altair.InitializePrecomputeValidators(context.Background(), beaconState) + require.NoError(t, err) + + // Generate perfect participation object + // All validators are participating 100% meaning no penalties accounted + participation := make([]byte, numOfVals) + inds := make([]uint64, numOfVals) + for i := 0; i < len(participation); i++ { + participation[i] = generateParticipation(params.BeaconConfig().TimelySourceFlagIndex, params.BeaconConfig().TimelyTargetFlagIndex, params.BeaconConfig().TimelyHeadFlagIndex) + inds[i] = uint64(i) + } + + // Loop for simulating slot progression + // Sync rewards being processed per each slot + // Epoch rewards being processed per each epoch + var slotCounter uint64 + for i := 0; i < int(slotsPerEpoch)*amountOfEpochsToProcess; i++ { + // Progress 1 slot each itteration + require.NoError(t, beaconState.SetSlot(types.Slot(i+1))) + slotCounter++ + + // Assign sync committee + committee, err := altair.NextSyncCommittee(context.Background(), beaconState) + require.NoError(t, err) + require.NoError(t, beaconState.SetCurrentSyncCommittee(committee)) + + // Define signatures + syncBits := bitfield.NewBitvector512() + for i := range syncBits { + syncBits[i] = 0xff + } + indices, err := altair.NextSyncCommitteeIndices(context.Background(), beaconState) + require.NoError(t, err) + ps := slots.PrevSlot(beaconState.Slot()) + pbr, err := helpers.BlockRootAtSlot(beaconState, ps) + require.NoError(t, err) + sigs := make([]bls.Signature, len(indices)) + for i, indice := range indices { + b := p2pType.SSZBytes(pbr) + sb, err := signing.ComputeDomainAndSign(beaconState, time.CurrentEpoch(beaconState), &b, params.BeaconConfig().DomainSyncCommittee, privKeys[indice]) + require.NoError(t, err) + sig, err := bls.SignatureFromBytes(sb) + require.NoError(t, err) + sigs[i] = sig + } + aggregatedSig := bls.AggregateSignatures(sigs).Marshal() + syncAggregate := ðpb.SyncAggregate{ + SyncCommitteeBits: syncBits, + SyncCommitteeSignature: aggregatedSig, + } + + // Process Sync aggregation and process rewards + beaconState, _, err = altair.ProcessSyncAggregate(context.Background(), beaconState, syncAggregate) + require.NoError(t, err) + + // If epoch passed - process participation and rewards + if slotCounter == 32 { + slotCounter = 0 + require.NoError(t, beaconState.SetCurrentParticipationBits(participation)) + require.NoError(t, beaconState.SetPreviousParticipationBits(participation)) + validators, balance, err = altair.ProcessEpochParticipation(context.Background(), beaconState, balance, validators) + require.NoError(t, err) + beaconState, err = altair.ProcessRewardsAndPenaltiesPrecompute(beaconState, balance, validators) + require.NoError(t, err) + } + } + + // Count total rewards after simulation + var totalRewards *big.Int = big.NewInt(0) + bal := beaconState.Balances() + for i := 0; i < len(bal); i++ { + totalRewards.Add(totalRewards, new(big.Int).Sub(new(big.Int).SetUint64(bal[i]), new(big.Int).SetUint64(cfg.MaxEffectiveBalance))) + } + t.Log("totalRewards:", totalRewards) +} + +// Helper function to generate participation +func generateParticipation(flags ...uint8) byte { + b := byte(0) + var err error + for _, flag := range flags { + b, err = altair.AddValidatorFlag(b, flag) + if err != nil { + return 0 + } + } + return b +} diff --git a/validator/client/metrics.go b/validator/client/metrics.go index 69462d50a..e7400f110 100644 --- a/validator/client/metrics.go +++ b/validator/client/metrics.go @@ -338,9 +338,9 @@ func (v *validator) logForEachValidator(index int, pubKey []byte, resp *ethpb.Va "correctlyVotedSource": correctlyVotedSource, "correctlyVotedTarget": correctlyVotedTarget, "correctlyVotedHead": correctlyVotedHead, - "startBalance": startBalance, - "oldBalance": prevBalance, - "newBalance": newBalance, + "startBalance": fmt.Sprintf("%f", startBalance), + "oldBalance": fmt.Sprintf("%f", prevBalance), + "newBalance": fmt.Sprintf("%f", newBalance), "percentChange": fmt.Sprintf("%.5f%%", percentNet*100), "percentChangeSinceStart": fmt.Sprintf("%.5f%%", percentSinceStart*100), }