Skip to content

Commit

Permalink
eip-7251: process_effective_balance_updates (#14003)
Browse files Browse the repository at this point in the history
* eip-7251: process_effective_balance_updates

Spectests for process_effective_balance_updates

process_effective_balance_updates unit tests

* PR feedback from the amazing @rkapka

---------

Co-authored-by: james-prysm <90280386+james-prysm@users.noreply.github.com>
  • Loading branch information
prestonvanloon and james-prysm authored May 25, 2024
1 parent 8e6d39a commit 2542189
Show file tree
Hide file tree
Showing 9 changed files with 239 additions and 3 deletions.
1 change: 1 addition & 0 deletions beacon-chain/core/electra/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ go_test(
"churn_test.go",
"consolidations_test.go",
"deposits_test.go",
"effective_balance_updates_test.go",
"upgrade_test.go",
"validator_test.go",
],
Expand Down
42 changes: 39 additions & 3 deletions beacon-chain/core/electra/effective_balance_updates.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
package electra

import "github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
import (
"fmt"

"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v5/config/params"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
)

// ProcessEffectiveBalanceUpdates processes effective balance updates during epoch processing.
//
Expand All @@ -24,6 +31,35 @@ import "github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
// ):
// validator.effective_balance = min(balance - balance % EFFECTIVE_BALANCE_INCREMENT, EFFECTIVE_BALANCE_LIMIT)
func ProcessEffectiveBalanceUpdates(state state.BeaconState) error {
// TODO: replace with real implementation
return nil
effBalanceInc := params.BeaconConfig().EffectiveBalanceIncrement
hysteresisInc := effBalanceInc / params.BeaconConfig().HysteresisQuotient
downwardThreshold := hysteresisInc * params.BeaconConfig().HysteresisDownwardMultiplier
upwardThreshold := hysteresisInc * params.BeaconConfig().HysteresisUpwardMultiplier

bals := state.Balances()

// Update effective balances with hysteresis.
validatorFunc := func(idx int, val *ethpb.Validator) (bool, *ethpb.Validator, error) {
if val == nil {
return false, nil, fmt.Errorf("validator %d is nil in state", idx)
}
if idx >= len(bals) {
return false, nil, fmt.Errorf("validator index exceeds validator length in state %d >= %d", idx, len(state.Balances()))
}
balance := bals[idx]

effectiveBalanceLimit := params.BeaconConfig().MinActivationBalance
if helpers.HasCompoundingWithdrawalCredential(val) {
effectiveBalanceLimit = params.BeaconConfig().MaxEffectiveBalanceElectra
}

if balance+downwardThreshold < val.EffectiveBalance || val.EffectiveBalance+upwardThreshold < balance {
effectiveBal := min(balance-balance%effBalanceInc, effectiveBalanceLimit)
val.EffectiveBalance = effectiveBal
return false, val, nil
}
return false, val, nil
}

return state.ApplyToEveryValidator(validatorFunc)
}
144 changes: 144 additions & 0 deletions beacon-chain/core/electra/effective_balance_updates_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
package electra_test

import (
"testing"

"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/electra"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
state_native "github.com/prysmaticlabs/prysm/v5/beacon-chain/state/state-native"
"github.com/prysmaticlabs/prysm/v5/config/params"
eth "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/testing/require"
)

func TestProcessEffectiveBalnceUpdates(t *testing.T) {
effBalanceInc := params.BeaconConfig().EffectiveBalanceIncrement
hysteresisInc := effBalanceInc / params.BeaconConfig().HysteresisQuotient
downwardThreshold := hysteresisInc * params.BeaconConfig().HysteresisDownwardMultiplier
upwardThreshold := hysteresisInc * params.BeaconConfig().HysteresisUpwardMultiplier

tests := []struct {
name string
state state.BeaconState
wantErr bool
check func(*testing.T, state.BeaconState)
}{
{
name: "validator with compounding withdrawal credentials updates effective balance",
state: func() state.BeaconState {
pb := &eth.BeaconStateElectra{
Validators: []*eth.Validator{
{
EffectiveBalance: params.BeaconConfig().MinActivationBalance,
WithdrawalCredentials: []byte{params.BeaconConfig().CompoundingWithdrawalPrefixByte, 0x11},
},
},
Balances: []uint64{
params.BeaconConfig().MaxEffectiveBalanceElectra * 2,
},
}
st, err := state_native.InitializeFromProtoElectra(pb)
require.NoError(t, err)
return st
}(),
check: func(t *testing.T, bs state.BeaconState) {
val, err := bs.ValidatorAtIndex(0)
require.NoError(t, err)
require.Equal(t, params.BeaconConfig().MaxEffectiveBalanceElectra, val.EffectiveBalance)
},
},
{
name: "validator without compounding withdrawal credentials updates effective balance",
state: func() state.BeaconState {
pb := &eth.BeaconStateElectra{
Validators: []*eth.Validator{
{
EffectiveBalance: params.BeaconConfig().MinActivationBalance / 2,
WithdrawalCredentials: nil,
},
},
Balances: []uint64{
params.BeaconConfig().MaxEffectiveBalanceElectra,
},
}
st, err := state_native.InitializeFromProtoElectra(pb)
require.NoError(t, err)
return st
}(),
check: func(t *testing.T, bs state.BeaconState) {
val, err := bs.ValidatorAtIndex(0)
require.NoError(t, err)
require.Equal(t, params.BeaconConfig().MinActivationBalance, val.EffectiveBalance)
},
},
{
name: "validator effective balance moves only when outside of threshold",
state: func() state.BeaconState {
pb := &eth.BeaconStateElectra{
Validators: []*eth.Validator{
{
EffectiveBalance: params.BeaconConfig().MinActivationBalance,
WithdrawalCredentials: []byte{params.BeaconConfig().CompoundingWithdrawalPrefixByte, 0x11},
},
{
EffectiveBalance: params.BeaconConfig().MinActivationBalance,
WithdrawalCredentials: []byte{params.BeaconConfig().CompoundingWithdrawalPrefixByte, 0x11},
},
{
EffectiveBalance: params.BeaconConfig().MinActivationBalance,
WithdrawalCredentials: []byte{params.BeaconConfig().CompoundingWithdrawalPrefixByte, 0x11},
},
{
EffectiveBalance: params.BeaconConfig().MinActivationBalance,
WithdrawalCredentials: []byte{params.BeaconConfig().CompoundingWithdrawalPrefixByte, 0x11},
},
},
Balances: []uint64{
params.BeaconConfig().MinActivationBalance - downwardThreshold - 1, // beyond downward threshold
params.BeaconConfig().MinActivationBalance - downwardThreshold + 1, // within downward threshold
params.BeaconConfig().MinActivationBalance + upwardThreshold + 1, // beyond upward threshold
params.BeaconConfig().MinActivationBalance + upwardThreshold - 1, // within upward threshold
},
}
st, err := state_native.InitializeFromProtoElectra(pb)
require.NoError(t, err)
return st
}(),
check: func(t *testing.T, bs state.BeaconState) {
// validator 0 has a balance diff exceeding the threshold so a diff should be applied to
// effective balance and it moves by effective balance increment.
val, err := bs.ValidatorAtIndex(0)
require.NoError(t, err)
require.Equal(t, params.BeaconConfig().MinActivationBalance-params.BeaconConfig().EffectiveBalanceIncrement, val.EffectiveBalance)

// validator 1 has a balance diff within the threshold so the effective balance should not
// have changed.
val, err = bs.ValidatorAtIndex(1)
require.NoError(t, err)
require.Equal(t, params.BeaconConfig().MinActivationBalance, val.EffectiveBalance)

// Validator 2 has a balance diff exceeding the threshold so a diff should be applied to the
// effective balance and it moves by effective balance increment.
val, err = bs.ValidatorAtIndex(2)
require.NoError(t, err)
require.Equal(t, params.BeaconConfig().MinActivationBalance+params.BeaconConfig().EffectiveBalanceIncrement, val.EffectiveBalance)

// Validator 3 has a balance diff within the threshold so the effective balance should not
// have changed.
val, err = bs.ValidatorAtIndex(3)
require.NoError(t, err)
require.Equal(t, params.BeaconConfig().MinActivationBalance, val.EffectiveBalance)
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := electra.ProcessEffectiveBalanceUpdates(tt.state)
require.Equal(t, tt.wantErr, err != nil, "unexpected error returned wanted error=nil (%s), got error=%s", tt.wantErr, err)
if tt.check != nil {
tt.check(t, tt.state)
}
})
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ load("@prysm//tools/go:def.bzl", "go_test")
go_test(
name = "go_default_test",
srcs = [
"effective_balance_updates_test.go",
"eth1_data_reset_test.go",
"historical_summaries_update_test.go",
"inactivity_updates_test.go",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package epoch_processing

import (
"testing"

"github.com/prysmaticlabs/prysm/v5/testing/spectest/shared/electra/epoch_processing"
)

func TestMainnet_electra_EpochProcessing_EffectiveBalanceUpdates(t *testing.T) {
epoch_processing.RunEffectiveBalanceUpdatesTests(t, "mainnet")
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ load("@prysm//tools/go:def.bzl", "go_test")
go_test(
name = "go_default_test",
srcs = [
"effective_balance_updates_test.go",
"eth1_data_reset_test.go",
"historical_summaries_update_test.go",
"inactivity_updates_test.go",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package epoch_processing

import (
"testing"

"github.com/prysmaticlabs/prysm/v5/testing/spectest/shared/electra/epoch_processing"
)

func TestMinimal_electra_EpochProcessing_EffectiveBalanceUpdates(t *testing.T) {
epoch_processing.RunEffectiveBalanceUpdatesTests(t, "minimal")
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ go_library(
name = "go_default_library",
testonly = True,
srcs = [
"effective_balance_updates.go",
"eth1_data_reset.go",
"helpers.go",
"historical_summaries_update.go",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package epoch_processing

import (
"path"
"testing"

"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/electra"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v5/testing/require"
"github.com/prysmaticlabs/prysm/v5/testing/spectest/utils"
)

// RunEffectiveBalanceUpdatesTests executes "epoch_processing/effective_balance_updates" tests.
func RunEffectiveBalanceUpdatesTests(t *testing.T, config string) {
require.NoError(t, utils.SetConfig(t, config))

testFolders, testsFolderPath := utils.TestFolders(t, config, "electra", "epoch_processing/effective_balance_updates/pyspec_tests")
for _, folder := range testFolders {
t.Run(folder.Name(), func(t *testing.T) {
folderPath := path.Join(testsFolderPath, folder.Name())
RunEpochOperationTest(t, folderPath, processEffectiveBalanceUpdatesWrapper)
})
}
}

func processEffectiveBalanceUpdatesWrapper(t *testing.T, st state.BeaconState) (state.BeaconState, error) {
err := electra.ProcessEffectiveBalanceUpdates(st)
require.NoError(t, err, "Could not process final updates")
return st, nil
}

0 comments on commit 2542189

Please sign in to comment.