Skip to content

Commit

Permalink
Implement bound on expected withdrawals
Browse files Browse the repository at this point in the history
  • Loading branch information
potuz committed Dec 13, 2022
1 parent 3fcdd58 commit 52a39f3
Show file tree
Hide file tree
Showing 9 changed files with 128 additions and 42 deletions.
18 changes: 14 additions & 4 deletions beacon-chain/core/blocks/withdrawals.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,13 +140,23 @@ func ProcessWithdrawals(st state.BeaconState, withdrawals []*enginev1.Withdrawal
if err := st.SetNextWithdrawalIndex(withdrawals[len(withdrawals)-1].WithdrawalIndex + 1); err != nil {
return nil, errors.Wrap(err, "could not set next withdrawal index")
}
nextValidatorIndex := withdrawals[len(withdrawals)-1].ValidatorIndex + 1
}
var nextValidatorIndex types.ValidatorIndex
if uint64(len(withdrawals)) < params.BeaconConfig().MaxWithdrawalsPerPayload {
nextValidatorIndex, err = st.NextWithdrawalValidatorIndex()
if err != nil {
return nil, errors.Wrap(err, "could not get next withdrawal validator index")
}
nextValidatorIndex += types.ValidatorIndex(params.BeaconConfig().MaxValidatorsPerWithdrawalsSweep)
nextValidatorIndex = types.ValidatorIndex(nextValidatorIndex % types.ValidatorIndex(st.NumValidators()))
} else {
nextValidatorIndex = withdrawals[len(withdrawals)-1].ValidatorIndex + 1
if nextValidatorIndex == types.ValidatorIndex(st.NumValidators()) {
nextValidatorIndex = 0
}
if err := st.SetNextWithdrawalValidatorIndex(nextValidatorIndex); err != nil {
return nil, errors.Wrap(err, "could not set latest withdrawal validator index")
}
}
if err := st.SetNextWithdrawalValidatorIndex(nextValidatorIndex); err != nil {
return nil, errors.Wrap(err, "could not set latest withdrawal validator index")
}
return st, nil
}
Expand Down
74 changes: 54 additions & 20 deletions beacon-chain/core/blocks/withdrawals_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ func TestProcessWithdrawals(t *testing.T) {
numValidators = 128
notWithdrawableIndex = 127
notPartiallyWithdrawable = 126
maxSweep = uint64(80)
)
maxEffectiveBalance := params.BeaconConfig().MaxEffectiveBalance

Expand Down Expand Up @@ -297,7 +298,7 @@ func TestProcessWithdrawals(t *testing.T) {
NextWithdrawalIndex: 3,
},
Control: control{
NextWithdrawalValidatorIndex: 10,
NextWithdrawalValidatorIndex: 90,
NextWithdrawalIndex: 3,
},
},
Expand All @@ -306,29 +307,29 @@ func TestProcessWithdrawals(t *testing.T) {
Name: "success one full withdrawal",
NextWithdrawalIndex: 3,
NextWithdrawalValidatorIndex: 5,
FullWithdrawalIndices: []types.ValidatorIndex{1},
FullWithdrawalIndices: []types.ValidatorIndex{70},
Withdrawals: []*enginev1.Withdrawal{
fullWithdrawal(1, 3),
fullWithdrawal(70, 3),
},
},
Control: control{
NextWithdrawalValidatorIndex: 2,
NextWithdrawalValidatorIndex: 85,
NextWithdrawalIndex: 4,
Balances: map[uint64]uint64{1: 0},
Balances: map[uint64]uint64{70: 0},
},
},
{
Args: args{
Name: "success one partial withdrawal",
NextWithdrawalIndex: 21,
NextWithdrawalValidatorIndex: 37,
NextWithdrawalValidatorIndex: 120,
PartialWithdrawalIndices: []types.ValidatorIndex{7},
Withdrawals: []*enginev1.Withdrawal{
partialWithdrawal(7, 21),
},
},
Control: control{
NextWithdrawalValidatorIndex: 8,
NextWithdrawalValidatorIndex: 72,
NextWithdrawalIndex: 22,
Balances: map[uint64]uint64{7: maxEffectiveBalance},
},
Expand All @@ -341,52 +342,82 @@ func TestProcessWithdrawals(t *testing.T) {
FullWithdrawalIndices: []types.ValidatorIndex{7, 19, 28, 1},
Withdrawals: []*enginev1.Withdrawal{
fullWithdrawal(7, 22), fullWithdrawal(19, 23), fullWithdrawal(28, 24),
fullWithdrawal(1, 25),
},
},
Control: control{
NextWithdrawalValidatorIndex: 2,
NextWithdrawalValidatorIndex: 84,
NextWithdrawalIndex: 25,
Balances: map[uint64]uint64{7: 0, 19: 0, 28: 0},
},
},
{
Args: args{
Name: "Less than max sweep at end",
NextWithdrawalIndex: 22,
NextWithdrawalValidatorIndex: 4,
FullWithdrawalIndices: []types.ValidatorIndex{80, 81, 82, 83},
Withdrawals: []*enginev1.Withdrawal{
fullWithdrawal(80, 22), fullWithdrawal(81, 23), fullWithdrawal(82, 24),
fullWithdrawal(83, 25),
},
},
Control: control{
NextWithdrawalValidatorIndex: 84,
NextWithdrawalIndex: 26,
Balances: map[uint64]uint64{7: 0, 19: 0, 28: 0, 1: 0},
Balances: map[uint64]uint64{80: 0, 81: 0, 82: 0, 83: 0},
},
},
{
Args: args{
Name: "Less than max sweep and beginning",
NextWithdrawalIndex: 22,
NextWithdrawalValidatorIndex: 4,
FullWithdrawalIndices: []types.ValidatorIndex{4, 5, 6},
Withdrawals: []*enginev1.Withdrawal{
fullWithdrawal(4, 22), fullWithdrawal(5, 23), fullWithdrawal(6, 24),
},
},
Control: control{
NextWithdrawalValidatorIndex: 84,
NextWithdrawalIndex: 25,
Balances: map[uint64]uint64{4: 0, 5: 0, 6: 0},
},
},
{
Args: args{
Name: "success many partial withdrawals",
NextWithdrawalIndex: 22,
NextWithdrawalValidatorIndex: 4,
PartialWithdrawalIndices: []types.ValidatorIndex{7, 19, 28, 1},
PartialWithdrawalIndices: []types.ValidatorIndex{7, 19, 28},
Withdrawals: []*enginev1.Withdrawal{
partialWithdrawal(7, 22), partialWithdrawal(19, 23), partialWithdrawal(28, 24),
partialWithdrawal(1, 25),
},
},
Control: control{
NextWithdrawalValidatorIndex: 2,
NextWithdrawalIndex: 26,
NextWithdrawalValidatorIndex: 84,
NextWithdrawalIndex: 25,
Balances: map[uint64]uint64{
7: maxEffectiveBalance,
19: maxEffectiveBalance,
28: maxEffectiveBalance,
1: maxEffectiveBalance,
},
},
},
{
Args: args{
Name: "success many withdrawals",
NextWithdrawalIndex: 22,
NextWithdrawalValidatorIndex: 12,
NextWithdrawalValidatorIndex: 88,
FullWithdrawalIndices: []types.ValidatorIndex{7, 19, 28},
PartialWithdrawalIndices: []types.ValidatorIndex{2, 1, 89, 15},
Withdrawals: []*enginev1.Withdrawal{
partialWithdrawal(15, 22), fullWithdrawal(19, 23), fullWithdrawal(28, 24),
partialWithdrawal(89, 25), partialWithdrawal(1, 26), partialWithdrawal(2, 27),
fullWithdrawal(7, 28),
partialWithdrawal(89, 22), partialWithdrawal(1, 23), partialWithdrawal(2, 24),
fullWithdrawal(7, 25), partialWithdrawal(15, 26), fullWithdrawal(19, 27),
fullWithdrawal(28, 28),
},
},
Control: control{
NextWithdrawalValidatorIndex: 8,
NextWithdrawalValidatorIndex: 40,
NextWithdrawalIndex: 29,
Balances: map[uint64]uint64{
7: 0, 19: 0, 28: 0,
Expand Down Expand Up @@ -589,6 +620,8 @@ func TestProcessWithdrawals(t *testing.T) {

for _, test := range tests {
t.Run(test.Args.Name, func(t *testing.T) {
saved := params.BeaconConfig().MaxValidatorsPerWithdrawalsSweep
params.BeaconConfig().MaxValidatorsPerWithdrawalsSweep = maxSweep
if test.Args.Withdrawals == nil {
test.Args.Withdrawals = make([]*enginev1.Withdrawal, 0)
}
Expand All @@ -614,6 +647,7 @@ func TestProcessWithdrawals(t *testing.T) {
require.NoError(t, err)
checkPostState(t, test.Control, post)
}
params.BeaconConfig().MaxValidatorsPerWithdrawalsSweep = saved
})
}
}
Expand Down
9 changes: 6 additions & 3 deletions beacon-chain/rpc/eth/beacon/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ func TestGetSpec(t *testing.T) {
config.DefaultFeeRecipient = common.HexToAddress("DefaultFeeRecipient")
config.MaxWithdrawalsPerPayload = 74
config.MaxBlsToExecutionChanges = 75
config.MaxValidatorsPerWithdrawalsSweep = 76

var dbp [4]byte
copy(dbp[:], []byte{'0', '0', '0', '1'})
Expand Down Expand Up @@ -137,7 +138,7 @@ func TestGetSpec(t *testing.T) {
resp, err := server.GetSpec(context.Background(), &emptypb.Empty{})
require.NoError(t, err)

assert.Equal(t, 104, len(resp.Data))
assert.Equal(t, 105, len(resp.Data))
for k, v := range resp.Data {
switch k {
case "CONFIG_NAME":
Expand Down Expand Up @@ -356,10 +357,12 @@ func TestGetSpec(t *testing.T) {
assert.Equal(t, "40", v)
case "INTERVALS_PER_SLOT":
assert.Equal(t, "3", v)
case "MAX_BLS_TO_EXECUTION_CHANGES":
assert.Equal(t, "75", v)
case "MAX_WITHDRAWALS_PER_PAYLOAD":
assert.Equal(t, "74", v)
case "MAX_BLS_TO_EXECUTION_CHANGES":
assert.Equal(t, "75", v)
case "MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP":
assert.Equal(t, "76", v)
case "SAFE_SLOTS_TO_IMPORT_OPTIMISTICALLY":
default:
t.Errorf("Incorrect key: %s", k)
Expand Down
1 change: 1 addition & 0 deletions beacon-chain/state/state-native/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ go_library(
"//crypto/hash:go_default_library",
"//encoding/bytesutil:go_default_library",
"//encoding/ssz:go_default_library",
"//math:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//runtime/version:go_default_library",
Expand Down
5 changes: 4 additions & 1 deletion beacon-chain/state/state-native/getters_withdrawal.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"github.com/prysmaticlabs/prysm/v3/config/params"
types "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
mathutil "github.com/prysmaticlabs/prysm/v3/math"
enginev1 "github.com/prysmaticlabs/prysm/v3/proto/engine/v1"
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v3/runtime/version"
Expand Down Expand Up @@ -52,7 +53,9 @@ func (b *BeaconState) ExpectedWithdrawals() ([]*enginev1.Withdrawal, error) {
validatorIndex := b.nextWithdrawalValidatorIndex
withdrawalIndex := b.nextWithdrawalIndex
epoch := slots.ToEpoch(b.slot)
for range b.validators {

bound := mathutil.Min(uint64(len(b.validators)), params.BeaconConfig().MaxValidatorsPerWithdrawalsSweep)
for i := uint64(0); i < bound; i++ {
val := b.validators[validatorIndex]
balance := b.balances[validatorIndex]
if balance > 0 && isFullyWithdrawableValidator(val, epoch) {
Expand Down
32 changes: 32 additions & 0 deletions beacon-chain/state/state-native/getters_withdrawal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -330,4 +330,36 @@ func TestExpectedWithdrawals(t *testing.T) {
require.NoError(t, err)
require.Equal(t, 0, len(expected))
})
t.Run("one partially withdrawable, one above sweep bound", func(t *testing.T) {
s := BeaconState{
version: version.Capella,
validators: make([]*ethpb.Validator, 100),
balances: make([]uint64, 100),
}
for i := range s.validators {
s.balances[i] = params.BeaconConfig().MaxEffectiveBalance
val := &ethpb.Validator{
WithdrawalCredentials: make([]byte, 32),
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
WithdrawableEpoch: types.Epoch(1),
}
val.WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
s.validators[i] = val
}
s.balances[3] += params.BeaconConfig().MinDepositAmount
s.balances[10] += params.BeaconConfig().MinDepositAmount
saved := params.BeaconConfig().MaxValidatorsPerWithdrawalsSweep
params.BeaconConfig().MaxValidatorsPerWithdrawalsSweep = 10
expected, err := s.ExpectedWithdrawals()
require.NoError(t, err)
require.Equal(t, 1, len(expected))
withdrawal := &enginev1.Withdrawal{
WithdrawalIndex: 0,
ValidatorIndex: 3,
ExecutionAddress: s.validators[3].WithdrawalCredentials[12:],
Amount: params.BeaconConfig().MinDepositAmount,
}
require.DeepEqual(t, withdrawal, expected[0])
params.BeaconConfig().MaxValidatorsPerWithdrawalsSweep = saved
})
}
15 changes: 8 additions & 7 deletions config/params/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,13 +94,14 @@ type BeaconChainConfig struct {
ProportionalSlashingMultiplier uint64 `yaml:"PROPORTIONAL_SLASHING_MULTIPLIER" spec:"true"` // ProportionalSlashingMultiplier is used as a multiplier on slashed penalties.

// Max operations per block constants.
MaxProposerSlashings uint64 `yaml:"MAX_PROPOSER_SLASHINGS" spec:"true"` // MaxProposerSlashings defines the maximum number of slashings of proposers possible in a block.
MaxAttesterSlashings uint64 `yaml:"MAX_ATTESTER_SLASHINGS" spec:"true"` // MaxAttesterSlashings defines the maximum number of casper FFG slashings possible in a block.
MaxAttestations uint64 `yaml:"MAX_ATTESTATIONS" spec:"true"` // MaxAttestations defines the maximum allowed attestations in a beacon block.
MaxDeposits uint64 `yaml:"MAX_DEPOSITS" spec:"true"` // MaxDeposits defines the maximum number of validator deposits in a block.
MaxVoluntaryExits uint64 `yaml:"MAX_VOLUNTARY_EXITS" spec:"true"` // MaxVoluntaryExits defines the maximum number of validator exits in a block.
MaxWithdrawalsPerPayload uint64 `yaml:"MAX_WITHDRAWALS_PER_PAYLOAD" spec:"true"` // MaxWithdrawalsPerPayload defines the maximum number of withdrawals in a block.
MaxBlsToExecutionChanges uint64 `yaml:"MAX_BLS_TO_EXECUTION_CHANGES" spec:"true"` // MaxBlsToExecutionChanges defines the maximum number of BLS-to-execution-change objects in a block.
MaxProposerSlashings uint64 `yaml:"MAX_PROPOSER_SLASHINGS" spec:"true"` // MaxProposerSlashings defines the maximum number of slashings of proposers possible in a block.
MaxAttesterSlashings uint64 `yaml:"MAX_ATTESTER_SLASHINGS" spec:"true"` // MaxAttesterSlashings defines the maximum number of casper FFG slashings possible in a block.
MaxAttestations uint64 `yaml:"MAX_ATTESTATIONS" spec:"true"` // MaxAttestations defines the maximum allowed attestations in a beacon block.
MaxDeposits uint64 `yaml:"MAX_DEPOSITS" spec:"true"` // MaxDeposits defines the maximum number of validator deposits in a block.
MaxVoluntaryExits uint64 `yaml:"MAX_VOLUNTARY_EXITS" spec:"true"` // MaxVoluntaryExits defines the maximum number of validator exits in a block.
MaxWithdrawalsPerPayload uint64 `yaml:"MAX_WITHDRAWALS_PER_PAYLOAD" spec:"true"` // MaxWithdrawalsPerPayload defines the maximum number of withdrawals in a block.
MaxBlsToExecutionChanges uint64 `yaml:"MAX_BLS_TO_EXECUTION_CHANGES" spec:"true"` // MaxBlsToExecutionChanges defines the maximum number of BLS-to-execution-change objects in a block.
MaxValidatorsPerWithdrawalsSweep uint64 `yaml:"MAX_VALIDATORS_PER_wITHDRAWAL_SWEEP" spec:"true"` //MaxValidatorsPerWithdrawalsSweep bounds the size of the sweep searching for withdrawals per slot.

// BLS domain values.
DomainBeaconProposer [4]byte `yaml:"DOMAIN_BEACON_PROPOSER" spec:"true"` // DomainBeaconProposer defines the BLS signature domain for beacon proposal verification.
Expand Down
15 changes: 8 additions & 7 deletions config/params/mainnet_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,13 +149,14 @@ var mainnetBeaconConfig = &BeaconChainConfig{
ProportionalSlashingMultiplier: 1,

// Max operations per block constants.
MaxProposerSlashings: 16,
MaxAttesterSlashings: 2,
MaxAttestations: 128,
MaxDeposits: 16,
MaxVoluntaryExits: 16,
MaxWithdrawalsPerPayload: 16,
MaxBlsToExecutionChanges: 16,
MaxProposerSlashings: 16,
MaxAttesterSlashings: 2,
MaxAttestations: 128,
MaxDeposits: 16,
MaxVoluntaryExits: 16,
MaxWithdrawalsPerPayload: 16,
MaxBlsToExecutionChanges: 16,
MaxValidatorsPerWithdrawalsSweep: 16384,

// BLS domain values.
DomainBeaconProposer: bytesutil.Uint32ToBytes4(0x00000000),
Expand Down
1 change: 1 addition & 0 deletions config/params/minimal_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ func MinimalSpecConfig() *BeaconChainConfig {
minimalConfig.MaxVoluntaryExits = 16
minimalConfig.MaxWithdrawalsPerPayload = 4
minimalConfig.MaxBlsToExecutionChanges = 4
minimalConfig.MaxValidatorsPerWithdrawalsSweep = 16

// Signature domains
minimalConfig.DomainBeaconProposer = bytesutil.ToBytes4(bytesutil.Bytes4(0))
Expand Down

0 comments on commit 52a39f3

Please sign in to comment.