diff --git a/beacon-chain/core/blocks/error.go b/beacon-chain/core/blocks/error.go index 7455ba17f3..7d968e7d5e 100644 --- a/beacon-chain/core/blocks/error.go +++ b/beacon-chain/core/blocks/error.go @@ -6,8 +6,3 @@ var errNilSignedWithdrawalMessage = errors.New("nil SignedBLSToExecutionChange m var errNilWithdrawalMessage = errors.New("nil BLSToExecutionChange message") var errInvalidBLSPrefix = errors.New("withdrawal credential prefix is not a BLS prefix") var errInvalidWithdrawalCredentials = errors.New("withdrawal credentials do not match") -var errInvalidWithdrawalIndex = errors.New("invalid withdrawal index") -var errInvalidValidatorIndex = errors.New("invalid validator index") -var errInvalidWithdrawalAmount = errors.New("invalid withdrawal amount") -var errInvalidExecutionAddress = errors.New("invalid execution address") -var errInvalidWithdrawalNumber = errors.New("invalid number of withdrawals") diff --git a/beacon-chain/core/blocks/payload.go b/beacon-chain/core/blocks/payload.go index 48a7644184..59d6d85a7a 100644 --- a/beacon-chain/core/blocks/payload.go +++ b/beacon-chain/core/blocks/payload.go @@ -200,12 +200,9 @@ func ValidatePayload(st state.BeaconState, payload interfaces.ExecutionData) err // transactions_root=hash_tree_root(payload.transactions), // ) func ProcessPayload(st state.BeaconState, payload interfaces.ExecutionData) (state.BeaconState, error) { + var err error if st.Version() >= version.Capella { - withdrawals, err := payload.Withdrawals() - if err != nil { - return nil, errors.Wrap(err, "could not get payload withdrawals") - } - st, err = ProcessWithdrawals(st, withdrawals) + st, err = ProcessWithdrawals(st, payload) if err != nil { return nil, errors.Wrap(err, "could not process withdrawals") } @@ -267,6 +264,13 @@ func ValidatePayloadHeader(st state.BeaconState, header interfaces.ExecutionData // ProcessPayloadHeader processes the payload header. func ProcessPayloadHeader(st state.BeaconState, header interfaces.ExecutionData) (state.BeaconState, error) { + var err error + if st.Version() >= version.Capella { + st, err = ProcessWithdrawals(st, header) + if err != nil { + return nil, errors.Wrap(err, "could not process withdrawals") + } + } if err := ValidatePayloadHeaderWhenMergeCompletes(st, header); err != nil { return nil, err } diff --git a/beacon-chain/core/blocks/withdrawals.go b/beacon-chain/core/blocks/withdrawals.go index 5bcf9320fb..67bdf45c64 100644 --- a/beacon-chain/core/blocks/withdrawals.go +++ b/beacon-chain/core/blocks/withdrawals.go @@ -2,18 +2,20 @@ package blocks import ( "bytes" + "fmt" "github.com/pkg/errors" "github.com/prysmaticlabs/prysm/v3/beacon-chain/core/helpers" "github.com/prysmaticlabs/prysm/v3/beacon-chain/core/signing" "github.com/prysmaticlabs/prysm/v3/beacon-chain/state" + fieldparams "github.com/prysmaticlabs/prysm/v3/config/fieldparams" "github.com/prysmaticlabs/prysm/v3/config/params" "github.com/prysmaticlabs/prysm/v3/consensus-types/interfaces" "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives" "github.com/prysmaticlabs/prysm/v3/crypto/bls" "github.com/prysmaticlabs/prysm/v3/crypto/hash" + "github.com/prysmaticlabs/prysm/v3/encoding/bytesutil" "github.com/prysmaticlabs/prysm/v3/encoding/ssz" - enginev1 "github.com/prysmaticlabs/prysm/v3/proto/engine/v1" ethpbv2 "github.com/prysmaticlabs/prysm/v3/proto/eth/v2" ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1" "github.com/prysmaticlabs/prysm/v3/runtime/version" @@ -110,39 +112,51 @@ func ValidateBLSToExecutionChange(st state.ReadOnlyBeaconState, signed *ethpb.Si return val, nil } -func ProcessWithdrawals(st state.BeaconState, withdrawals []*enginev1.Withdrawal) (state.BeaconState, error) { - expected, err := st.ExpectedWithdrawals() +func ProcessWithdrawals(st state.BeaconState, executionData interfaces.ExecutionData) (state.BeaconState, error) { + expectedWithdrawals, err := st.ExpectedWithdrawals() if err != nil { return nil, errors.Wrap(err, "could not get expected withdrawals") } - if len(expected) != len(withdrawals) { - return nil, errInvalidWithdrawalNumber + + var wdRoot [32]byte + if executionData.IsBlinded() { + r, err := executionData.WithdrawalsRoot() + if err != nil { + return nil, errors.Wrap(err, "could not get withdrawals root") + } + wdRoot = bytesutil.ToBytes32(r) + } else { + wds, err := executionData.Withdrawals() + if err != nil { + return nil, errors.Wrap(err, "could not get withdrawals") + } + wdRoot, err = ssz.WithdrawalSliceRoot(hash.CustomSHA256Hasher(), wds, fieldparams.MaxWithdrawalsPerPayload) + if err != nil { + return nil, errors.Wrap(err, "could not get withdrawals root") + } } - for i, withdrawal := range withdrawals { - if withdrawal.Index != expected[i].Index { - return nil, errInvalidWithdrawalIndex - } - if withdrawal.ValidatorIndex != expected[i].ValidatorIndex { - return nil, errInvalidValidatorIndex - } - if !bytes.Equal(withdrawal.Address, expected[i].Address) { - return nil, errInvalidExecutionAddress - } - if withdrawal.Amount != expected[i].Amount { - return nil, errInvalidWithdrawalAmount - } + + expectedRoot, err := ssz.WithdrawalSliceRoot(hash.CustomSHA256Hasher(), expectedWithdrawals, fieldparams.MaxWithdrawalsPerPayload) + if err != nil { + return nil, errors.Wrap(err, "could not get expected withdrawals root") + } + if expectedRoot != wdRoot { + return nil, fmt.Errorf("expected withdrawals root %#x, got %#x", expectedRoot, wdRoot) + } + + for _, withdrawal := range expectedWithdrawals { err := helpers.DecreaseBalance(st, withdrawal.ValidatorIndex, withdrawal.Amount) if err != nil { return nil, errors.Wrap(err, "could not decrease balance") } } - if len(withdrawals) > 0 { - if err := st.SetNextWithdrawalIndex(withdrawals[len(withdrawals)-1].Index + 1); err != nil { + if len(expectedWithdrawals) > 0 { + if err := st.SetNextWithdrawalIndex(expectedWithdrawals[len(expectedWithdrawals)-1].Index + 1); err != nil { return nil, errors.Wrap(err, "could not set next withdrawal index") } } var nextValidatorIndex primitives.ValidatorIndex - if uint64(len(withdrawals)) < params.BeaconConfig().MaxWithdrawalsPerPayload { + if uint64(len(expectedWithdrawals)) < params.BeaconConfig().MaxWithdrawalsPerPayload { nextValidatorIndex, err = st.NextWithdrawalValidatorIndex() if err != nil { return nil, errors.Wrap(err, "could not get next withdrawal validator index") @@ -150,7 +164,7 @@ func ProcessWithdrawals(st state.BeaconState, withdrawals []*enginev1.Withdrawal nextValidatorIndex += primitives.ValidatorIndex(params.BeaconConfig().MaxValidatorsPerWithdrawalsSweep) nextValidatorIndex = nextValidatorIndex % primitives.ValidatorIndex(st.NumValidators()) } else { - nextValidatorIndex = withdrawals[len(withdrawals)-1].ValidatorIndex + 1 + nextValidatorIndex = expectedWithdrawals[len(expectedWithdrawals)-1].ValidatorIndex + 1 if nextValidatorIndex == primitives.ValidatorIndex(st.NumValidators()) { nextValidatorIndex = 0 } diff --git a/beacon-chain/core/blocks/withdrawals_test.go b/beacon-chain/core/blocks/withdrawals_test.go index 7428fa7608..5491c8022d 100644 --- a/beacon-chain/core/blocks/withdrawals_test.go +++ b/beacon-chain/core/blocks/withdrawals_test.go @@ -1,6 +1,7 @@ package blocks_test import ( + "math/big" "math/rand" "testing" @@ -9,6 +10,7 @@ import ( "github.com/prysmaticlabs/prysm/v3/beacon-chain/core/time" "github.com/prysmaticlabs/prysm/v3/beacon-chain/state" state_native "github.com/prysmaticlabs/prysm/v3/beacon-chain/state/state-native" + fieldparams "github.com/prysmaticlabs/prysm/v3/config/fieldparams" "github.com/prysmaticlabs/prysm/v3/config/params" consensusblocks "github.com/prysmaticlabs/prysm/v3/consensus-types/blocks" "github.com/prysmaticlabs/prysm/v3/consensus-types/primitives" @@ -236,6 +238,425 @@ func TestProcessBLSToExecutionChange(t *testing.T) { }) } +func TestProcessBlindWithdrawals(t *testing.T) { + const ( + currentEpoch = primitives.Epoch(10) + epochInFuture = primitives.Epoch(12) + epochInPast = primitives.Epoch(8) + numValidators = 128 + notWithdrawableIndex = 127 + notPartiallyWithdrawable = 126 + maxSweep = uint64(80) + ) + maxEffectiveBalance := params.BeaconConfig().MaxEffectiveBalance + + type args struct { + Name string + NextWithdrawalValidatorIndex primitives.ValidatorIndex + NextWithdrawalIndex uint64 + FullWithdrawalIndices []primitives.ValidatorIndex + PartialWithdrawalIndices []primitives.ValidatorIndex + Withdrawals []*enginev1.Withdrawal + } + type control struct { + NextWithdrawalValidatorIndex primitives.ValidatorIndex + NextWithdrawalIndex uint64 + ExpectedError bool + Balances map[uint64]uint64 + } + type Test struct { + Args args + Control control + } + executionAddress := func(i primitives.ValidatorIndex) []byte { + wc := make([]byte, 20) + wc[19] = byte(i) + return wc + } + withdrawalAmount := func(i primitives.ValidatorIndex) uint64 { + return maxEffectiveBalance + uint64(i)*100000 + } + fullWithdrawal := func(i primitives.ValidatorIndex, idx uint64) *enginev1.Withdrawal { + return &enginev1.Withdrawal{ + Index: idx, + ValidatorIndex: i, + Address: executionAddress(i), + Amount: withdrawalAmount(i), + } + } + partialWithdrawal := func(i primitives.ValidatorIndex, idx uint64) *enginev1.Withdrawal { + return &enginev1.Withdrawal{ + Index: idx, + ValidatorIndex: i, + Address: executionAddress(i), + Amount: withdrawalAmount(i) - maxEffectiveBalance, + } + } + tests := []Test{ + { + Args: args{ + Name: "success no withdrawals", + NextWithdrawalValidatorIndex: 10, + NextWithdrawalIndex: 3, + }, + Control: control{ + NextWithdrawalValidatorIndex: 90, + NextWithdrawalIndex: 3, + }, + }, + { + Args: args{ + Name: "success one full withdrawal", + NextWithdrawalIndex: 3, + NextWithdrawalValidatorIndex: 5, + FullWithdrawalIndices: []primitives.ValidatorIndex{70}, + Withdrawals: []*enginev1.Withdrawal{ + fullWithdrawal(70, 3), + }, + }, + Control: control{ + NextWithdrawalValidatorIndex: 85, + NextWithdrawalIndex: 4, + Balances: map[uint64]uint64{70: 0}, + }, + }, + { + Args: args{ + Name: "success one partial withdrawal", + NextWithdrawalIndex: 21, + NextWithdrawalValidatorIndex: 120, + PartialWithdrawalIndices: []primitives.ValidatorIndex{7}, + Withdrawals: []*enginev1.Withdrawal{ + partialWithdrawal(7, 21), + }, + }, + Control: control{ + NextWithdrawalValidatorIndex: 72, + NextWithdrawalIndex: 22, + Balances: map[uint64]uint64{7: maxEffectiveBalance}, + }, + }, + { + Args: args{ + Name: "success many full withdrawals", + NextWithdrawalIndex: 22, + NextWithdrawalValidatorIndex: 4, + FullWithdrawalIndices: []primitives.ValidatorIndex{7, 19, 28, 1}, + Withdrawals: []*enginev1.Withdrawal{ + fullWithdrawal(7, 22), fullWithdrawal(19, 23), fullWithdrawal(28, 24), + }, + }, + Control: control{ + 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: []primitives.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{80: 0, 81: 0, 82: 0, 83: 0}, + }, + }, + { + Args: args{ + Name: "Less than max sweep and beginning", + NextWithdrawalIndex: 22, + NextWithdrawalValidatorIndex: 4, + FullWithdrawalIndices: []primitives.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: []primitives.ValidatorIndex{7, 19, 28}, + Withdrawals: []*enginev1.Withdrawal{ + partialWithdrawal(7, 22), partialWithdrawal(19, 23), partialWithdrawal(28, 24), + }, + }, + Control: control{ + NextWithdrawalValidatorIndex: 84, + NextWithdrawalIndex: 25, + Balances: map[uint64]uint64{ + 7: maxEffectiveBalance, + 19: maxEffectiveBalance, + 28: maxEffectiveBalance, + }, + }, + }, + { + Args: args{ + Name: "success many withdrawals", + NextWithdrawalIndex: 22, + NextWithdrawalValidatorIndex: 88, + FullWithdrawalIndices: []primitives.ValidatorIndex{7, 19, 28}, + PartialWithdrawalIndices: []primitives.ValidatorIndex{2, 1, 89, 15}, + Withdrawals: []*enginev1.Withdrawal{ + partialWithdrawal(89, 22), partialWithdrawal(1, 23), partialWithdrawal(2, 24), + fullWithdrawal(7, 25), partialWithdrawal(15, 26), fullWithdrawal(19, 27), + fullWithdrawal(28, 28), + }, + }, + Control: control{ + NextWithdrawalValidatorIndex: 40, + NextWithdrawalIndex: 29, + Balances: map[uint64]uint64{ + 7: 0, 19: 0, 28: 0, + 2: maxEffectiveBalance, 1: maxEffectiveBalance, 89: maxEffectiveBalance, + 15: maxEffectiveBalance, + }, + }, + }, + { + Args: args{ + Name: "success more than max fully withdrawals", + NextWithdrawalIndex: 22, + NextWithdrawalValidatorIndex: 0, + FullWithdrawalIndices: []primitives.ValidatorIndex{1, 2, 3, 4, 5, 6, 7, 8, 9, 21, 22, 23, 24, 25, 26, 27, 29, 35, 89}, + Withdrawals: []*enginev1.Withdrawal{ + fullWithdrawal(1, 22), fullWithdrawal(2, 23), fullWithdrawal(3, 24), + fullWithdrawal(4, 25), fullWithdrawal(5, 26), fullWithdrawal(6, 27), + fullWithdrawal(7, 28), fullWithdrawal(8, 29), fullWithdrawal(9, 30), + fullWithdrawal(21, 31), fullWithdrawal(22, 32), fullWithdrawal(23, 33), + fullWithdrawal(24, 34), fullWithdrawal(25, 35), fullWithdrawal(26, 36), + fullWithdrawal(27, 37), + }, + }, + Control: control{ + NextWithdrawalValidatorIndex: 28, + NextWithdrawalIndex: 38, + Balances: map[uint64]uint64{ + 1: 0, 2: 0, 3: 0, 4: 0, 5: 0, 6: 0, 7: 0, 8: 0, 9: 0, + 21: 0, 22: 0, 23: 0, 24: 0, 25: 0, 26: 0, 27: 0, + }, + }, + }, + { + Args: args{ + Name: "success more than max partially withdrawals", + NextWithdrawalIndex: 22, + NextWithdrawalValidatorIndex: 0, + PartialWithdrawalIndices: []primitives.ValidatorIndex{1, 2, 3, 4, 5, 6, 7, 8, 9, 21, 22, 23, 24, 25, 26, 27, 29, 35, 89}, + Withdrawals: []*enginev1.Withdrawal{ + partialWithdrawal(1, 22), partialWithdrawal(2, 23), partialWithdrawal(3, 24), + partialWithdrawal(4, 25), partialWithdrawal(5, 26), partialWithdrawal(6, 27), + partialWithdrawal(7, 28), partialWithdrawal(8, 29), partialWithdrawal(9, 30), + partialWithdrawal(21, 31), partialWithdrawal(22, 32), partialWithdrawal(23, 33), + partialWithdrawal(24, 34), partialWithdrawal(25, 35), partialWithdrawal(26, 36), + partialWithdrawal(27, 37), + }, + }, + Control: control{ + NextWithdrawalValidatorIndex: 28, + NextWithdrawalIndex: 38, + Balances: map[uint64]uint64{ + 1: maxEffectiveBalance, + 2: maxEffectiveBalance, + 3: maxEffectiveBalance, + 4: maxEffectiveBalance, + 5: maxEffectiveBalance, + 6: maxEffectiveBalance, + 7: maxEffectiveBalance, + 8: maxEffectiveBalance, + 9: maxEffectiveBalance, + 21: maxEffectiveBalance, + 22: maxEffectiveBalance, + 23: maxEffectiveBalance, + 24: maxEffectiveBalance, + 25: maxEffectiveBalance, + 26: maxEffectiveBalance, + 27: maxEffectiveBalance, + }, + }, + }, + { + Args: args{ + Name: "failure wrong number of partial withdrawal", + NextWithdrawalIndex: 21, + NextWithdrawalValidatorIndex: 37, + PartialWithdrawalIndices: []primitives.ValidatorIndex{7}, + Withdrawals: []*enginev1.Withdrawal{ + partialWithdrawal(7, 21), partialWithdrawal(9, 22), + }, + }, + Control: control{ + ExpectedError: true, + }, + }, + { + Args: args{ + Name: "failure invalid withdrawal index", + NextWithdrawalIndex: 22, + NextWithdrawalValidatorIndex: 4, + FullWithdrawalIndices: []primitives.ValidatorIndex{7, 19, 28, 1}, + Withdrawals: []*enginev1.Withdrawal{ + fullWithdrawal(7, 22), fullWithdrawal(19, 23), fullWithdrawal(28, 25), + fullWithdrawal(1, 25), + }, + }, + Control: control{ + ExpectedError: true, + }, + }, + { + Args: args{ + Name: "failure invalid validator index", + NextWithdrawalIndex: 22, + NextWithdrawalValidatorIndex: 4, + FullWithdrawalIndices: []primitives.ValidatorIndex{7, 19, 28, 1}, + Withdrawals: []*enginev1.Withdrawal{ + fullWithdrawal(7, 22), fullWithdrawal(19, 23), fullWithdrawal(27, 24), + fullWithdrawal(1, 25), + }, + }, + Control: control{ + ExpectedError: true, + }, + }, + { + Args: args{ + Name: "failure invalid withdrawal amount", + NextWithdrawalIndex: 22, + NextWithdrawalValidatorIndex: 4, + FullWithdrawalIndices: []primitives.ValidatorIndex{7, 19, 28, 1}, + Withdrawals: []*enginev1.Withdrawal{ + fullWithdrawal(7, 22), fullWithdrawal(19, 23), partialWithdrawal(28, 24), + fullWithdrawal(1, 25), + }, + }, + Control: control{ + ExpectedError: true, + }, + }, + { + Args: args{ + Name: "failure validator not fully withdrawable", + NextWithdrawalIndex: 22, + NextWithdrawalValidatorIndex: 4, + FullWithdrawalIndices: []primitives.ValidatorIndex{notWithdrawableIndex}, + Withdrawals: []*enginev1.Withdrawal{ + fullWithdrawal(notWithdrawableIndex, 22), + }, + }, + Control: control{ + ExpectedError: true, + }, + }, + { + Args: args{ + Name: "failure validator not partially withdrawable", + NextWithdrawalIndex: 22, + NextWithdrawalValidatorIndex: 4, + PartialWithdrawalIndices: []primitives.ValidatorIndex{notPartiallyWithdrawable}, + Withdrawals: []*enginev1.Withdrawal{ + fullWithdrawal(notPartiallyWithdrawable, 22), + }, + }, + Control: control{ + ExpectedError: true, + }, + }, + } + + checkPostState := func(t *testing.T, expected control, st state.BeaconState) { + l, err := st.NextWithdrawalValidatorIndex() + require.NoError(t, err) + require.Equal(t, expected.NextWithdrawalValidatorIndex, l) + + n, err := st.NextWithdrawalIndex() + require.NoError(t, err) + require.Equal(t, expected.NextWithdrawalIndex, n) + balances := st.Balances() + for idx, bal := range expected.Balances { + require.Equal(t, bal, balances[idx]) + } + } + + prepareValidators := func(st *ethpb.BeaconStateCapella, arguments args) (state.BeaconState, error) { + validators := make([]*ethpb.Validator, numValidators) + st.Balances = make([]uint64, numValidators) + for i := range validators { + v := ðpb.Validator{} + v.EffectiveBalance = maxEffectiveBalance + v.WithdrawableEpoch = epochInFuture + v.WithdrawalCredentials = make([]byte, 32) + v.WithdrawalCredentials[31] = byte(i) + st.Balances[i] = v.EffectiveBalance - uint64(rand.Intn(1000)) + validators[i] = v + } + for _, idx := range arguments.FullWithdrawalIndices { + if idx != notWithdrawableIndex { + validators[idx].WithdrawableEpoch = epochInPast + } + st.Balances[idx] = withdrawalAmount(idx) + validators[idx].WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte + } + for _, idx := range arguments.PartialWithdrawalIndices { + validators[idx].WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte + st.Balances[idx] = withdrawalAmount(idx) + } + st.Validators = validators + return state_native.InitializeFromProtoCapella(st) + } + + 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) + } + if test.Args.FullWithdrawalIndices == nil { + test.Args.FullWithdrawalIndices = make([]primitives.ValidatorIndex, 0) + } + if test.Args.PartialWithdrawalIndices == nil { + test.Args.PartialWithdrawalIndices = make([]primitives.ValidatorIndex, 0) + } + slot, err := slots.EpochStart(currentEpoch) + require.NoError(t, err) + spb := ðpb.BeaconStateCapella{ + Slot: slot, + NextWithdrawalValidatorIndex: test.Args.NextWithdrawalValidatorIndex, + NextWithdrawalIndex: test.Args.NextWithdrawalIndex, + } + st, err := prepareValidators(spb, test.Args) + require.NoError(t, err) + wdRoot, err := ssz.WithdrawalSliceRoot(hash.CustomSHA256Hasher(), test.Args.Withdrawals, fieldparams.MaxWithdrawalsPerPayload) + require.NoError(t, err) + p, err := consensusblocks.WrappedExecutionPayloadHeaderCapella(&enginev1.ExecutionPayloadHeaderCapella{WithdrawalsRoot: wdRoot[:]}, big.NewInt(0)) + require.NoError(t, err) + post, err := blocks.ProcessWithdrawals(st, p) + if test.Control.ExpectedError { + require.NotNil(t, err) + } else { + require.NoError(t, err) + checkPostState(t, test.Control, post) + } + params.BeaconConfig().MaxValidatorsPerWithdrawalsSweep = saved + }) + } +} func TestProcessWithdrawals(t *testing.T) { const ( @@ -641,7 +1062,9 @@ func TestProcessWithdrawals(t *testing.T) { } st, err := prepareValidators(spb, test.Args) require.NoError(t, err) - post, err := blocks.ProcessWithdrawals(st, test.Args.Withdrawals) + p, err := consensusblocks.WrappedExecutionPayloadCapella(&enginev1.ExecutionPayloadCapella{Withdrawals: test.Args.Withdrawals}, big.NewInt(0)) + require.NoError(t, err) + post, err := blocks.ProcessWithdrawals(st, p) if test.Control.ExpectedError { require.NotNil(t, err) } else { diff --git a/beacon-chain/rpc/eth/validator/validator_test.go b/beacon-chain/rpc/eth/validator/validator_test.go index 933d905ade..872368c06c 100644 --- a/beacon-chain/rpc/eth/validator/validator_test.go +++ b/beacon-chain/rpc/eth/validator/validator_test.go @@ -2694,6 +2694,10 @@ func TestProduceBlindedBlock(t *testing.T) { require.NoError(t, beaconState.SetGenesisTime(uint64(ti.Unix()))) random, err := helpers.RandaoMix(beaconState, coreTime.CurrentEpoch(beaconState)) require.NoError(t, err) + wds, err := beaconState.ExpectedWithdrawals() + require.NoError(t, err) + wr, err := ssz.WithdrawalSliceRoot(hash.CustomSHA256Hasher(), wds, fieldparams.MaxWithdrawalsPerPayload) + require.NoError(t, err) bid := ðpbalpha.BuilderBidCapella{ Header: &enginev1.ExecutionPayloadHeaderCapella{ ParentHash: make([]byte, fieldparams.RootLength), @@ -2707,7 +2711,7 @@ func TestProduceBlindedBlock(t *testing.T) { TransactionsRoot: make([]byte, fieldparams.RootLength), BlockNumber: 1, Timestamp: uint64(ts.Unix()), - WithdrawalsRoot: make([]byte, fieldparams.RootLength), + WithdrawalsRoot: wr[:], }, Pubkey: sk.PublicKey().Marshal(), Value: bytesutil.PadTo([]byte{1, 2, 3}, 32), diff --git a/config/params/testnet_e2e_config.go b/config/params/testnet_e2e_config.go index e2792656b5..584d999e1a 100644 --- a/config/params/testnet_e2e_config.go +++ b/config/params/testnet_e2e_config.go @@ -62,7 +62,6 @@ func E2EMainnetTestConfig() *BeaconChainConfig { e2eConfig.MinGenesisActiveValidatorCount = 256 e2eConfig.GenesisDelay = 25 // 25 seconds so E2E has enough time to process deposits and get started. e2eConfig.ChurnLimitQuotient = 65536 - e2eConfig.MaxValidatorsPerWithdrawalsSweep = 128 // Time parameters. e2eConfig.SecondsPerSlot = 6 diff --git a/consensus-types/blocks/execution.go b/consensus-types/blocks/execution.go index f3bbbf9b30..a4c49b4323 100644 --- a/consensus-types/blocks/execution.go +++ b/consensus-types/blocks/execution.go @@ -36,6 +36,11 @@ func (e executionPayload) IsNil() bool { return e.p == nil } +// IsBlinded returns true if the underlying data is blinded. +func (e executionPayload) IsBlinded() bool { + return false +} + // MarshalSSZ -- func (e executionPayload) MarshalSSZ() ([]byte, error) { return e.p.MarshalSSZ() @@ -192,6 +197,11 @@ func (e executionPayloadHeader) IsNil() bool { return e.p == nil } +// IsBlinded returns true if the underlying data is a header. +func (e executionPayloadHeader) IsBlinded() bool { + return true +} + // MarshalSSZ -- func (e executionPayloadHeader) MarshalSSZ() ([]byte, error) { return e.p.MarshalSSZ() @@ -377,6 +387,11 @@ func (e executionPayloadCapella) IsNil() bool { return e.p == nil } +// IsBlinded returns true if the underlying data is blinded. +func (e executionPayloadCapella) IsBlinded() bool { + return false +} + // MarshalSSZ -- func (e executionPayloadCapella) MarshalSSZ() ([]byte, error) { return e.p.MarshalSSZ() @@ -534,6 +549,11 @@ func (e executionPayloadHeaderCapella) IsNil() bool { return e.p == nil } +// IsBlinded returns true if the underlying data is blinded. +func (e executionPayloadHeaderCapella) IsBlinded() bool { + return true +} + // MarshalSSZ -- func (e executionPayloadHeaderCapella) MarshalSSZ() ([]byte, error) { return e.p.MarshalSSZ() diff --git a/consensus-types/interfaces/beacon_block.go b/consensus-types/interfaces/beacon_block.go index 6e8d4a8525..8a0b9b7bd9 100644 --- a/consensus-types/interfaces/beacon_block.go +++ b/consensus-types/interfaces/beacon_block.go @@ -102,6 +102,7 @@ type ExecutionData interface { ssz.Unmarshaler ssz.HashRoot IsNil() bool + IsBlinded() bool Proto() proto.Message ParentHash() []byte FeeRecipient() []byte diff --git a/testing/endtoend/evaluators/operations.go b/testing/endtoend/evaluators/operations.go index ebb6527f7b..42d195af10 100644 --- a/testing/endtoend/evaluators/operations.go +++ b/testing/endtoend/evaluators/operations.go @@ -611,6 +611,11 @@ func submitWithdrawal(ec *e2etypes.EvaluationContext, conns ...*grpc.ClientConn) } func validatorsAreWithdrawn(ec *e2etypes.EvaluationContext, conns ...*grpc.ClientConn) error { + // We skip this for multiclient runs as lighthouse does not have the ability + // to configure the withdrawal sweep for the end to end test. + if e2e.TestParams.LighthouseBeaconNodeCount > 0 { + return nil + } conn := conns[0] beaconClient := ethpb.NewBeaconChainClient(conn) debugClient := ethpb.NewDebugClient(conn) @@ -642,7 +647,9 @@ func validatorsAreWithdrawn(ec *e2etypes.EvaluationContext, conns ...*grpc.Clien if err != nil { return err } - if bal != 0 { + // Only return an error if the validator has more than 1 eth + // in its balance. + if bal > 1*params.BeaconConfig().GweiPerEth { return errors.Errorf("Validator index %d with key %#x hasn't withdrawn. Their balance is %d.", valIdx, key, bal) } diff --git a/testing/spectest/shared/capella/operations/withdrawals.go b/testing/spectest/shared/capella/operations/withdrawals.go index 252b5c9c5f..66be6e201a 100644 --- a/testing/spectest/shared/capella/operations/withdrawals.go +++ b/testing/spectest/shared/capella/operations/withdrawals.go @@ -2,12 +2,14 @@ package operations import ( "context" + "math/big" "path" "testing" "github.com/golang/snappy" "github.com/prysmaticlabs/prysm/v3/beacon-chain/core/blocks" "github.com/prysmaticlabs/prysm/v3/beacon-chain/state" + consensusblocks "github.com/prysmaticlabs/prysm/v3/consensus-types/blocks" "github.com/prysmaticlabs/prysm/v3/consensus-types/interfaces" enginev1 "github.com/prysmaticlabs/prysm/v3/proto/engine/v1" ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1" @@ -42,7 +44,9 @@ func RunWithdrawalsTest(t *testing.T, config string) { if err != nil { return nil, err } - return blocks.ProcessWithdrawals(s, withdrawals) + p, err := consensusblocks.WrappedExecutionPayloadCapella(&enginev1.ExecutionPayloadCapella{Withdrawals: withdrawals}, big.NewInt(0)) + require.NoError(t, err) + return blocks.ProcessWithdrawals(s, p) }) }) }