From 8092e70f200acc4f91ba32819363d4eb9fca8bb8 Mon Sep 17 00:00:00 2001 From: Md Amaan <114795592+Redidacove@users.noreply.github.com> Date: Tue, 13 Aug 2024 18:11:47 +0530 Subject: [PATCH] Process withdrawal (#14297) * process_withdrawal_fn and isParentfull test * suggestions applied * minor change * removed * lint * lint fix * removed Latestheader * test added with nil error * tests passing * IsParentNode Test added * lint * fix test * updated godoc * fix in godoc * comment removed * fixed braces * removed var * removed var * Update beacon-chain/core/blocks/withdrawals.go * Update beacon-chain/core/blocks/withdrawals_test.go * gazelle * test added and removed previous changes in Testprocesswithdrawal * added check for nil state * decrease chromatic complexity --------- Co-authored-by: Potuz Co-authored-by: Potuz --- beacon-chain/core/blocks/BUILD.bazel | 1 + beacon-chain/core/blocks/withdrawals.go | 155 +++++--- beacon-chain/core/blocks/withdrawals_test.go | 395 ++++++++++++++++++- 3 files changed, 495 insertions(+), 56 deletions(-) diff --git a/beacon-chain/core/blocks/BUILD.bazel b/beacon-chain/core/blocks/BUILD.bazel index 64d5861acd63..32bba9d1952e 100644 --- a/beacon-chain/core/blocks/BUILD.bazel +++ b/beacon-chain/core/blocks/BUILD.bazel @@ -90,6 +90,7 @@ go_test( "//config/fieldparams:go_default_library", "//config/params:go_default_library", "//consensus-types/blocks:go_default_library", + "//consensus-types/epbs:go_default_library", "//consensus-types/interfaces:go_default_library", "//consensus-types/primitives:go_default_library", "//container/trie:go_default_library", diff --git a/beacon-chain/core/blocks/withdrawals.go b/beacon-chain/core/blocks/withdrawals.go index e2f202ce9081..33cae6371e90 100644 --- a/beacon-chain/core/blocks/withdrawals.go +++ b/beacon-chain/core/blocks/withdrawals.go @@ -14,8 +14,8 @@ import ( "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" "github.com/prysmaticlabs/prysm/v5/crypto/bls" "github.com/prysmaticlabs/prysm/v5/crypto/hash" - "github.com/prysmaticlabs/prysm/v5/encoding/bytesutil" "github.com/prysmaticlabs/prysm/v5/encoding/ssz" + enginev1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1" ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1" "github.com/prysmaticlabs/prysm/v5/runtime/version" ) @@ -115,15 +115,97 @@ func ValidateBLSToExecutionChange(st state.ReadOnlyBeaconState, signed *ethpb.Si return val, nil } +func checkWithdrawalsAgainstPayload( + executionData interfaces.ExecutionData, + numExpected int, + expectedRoot [32]byte, +) error { + var wdRoot [32]byte + if executionData.IsBlinded() { + r, err := executionData.WithdrawalsRoot() + if err != nil { + return errors.Wrap(err, "could not get withdrawals root") + } + copy(wdRoot[:], r) + } else { + wds, err := executionData.Withdrawals() + if err != nil { + return errors.Wrap(err, "could not get withdrawals") + } + + if len(wds) != numExpected { + return fmt.Errorf("execution payload header has %d withdrawals when %d were expected", len(wds), numExpected) + } + + wdRoot, err = ssz.WithdrawalSliceRoot(wds, fieldparams.MaxWithdrawalsPerPayload) + if err != nil { + return errors.Wrap(err, "could not get withdrawals root") + } + } + if expectedRoot != wdRoot { + return fmt.Errorf("expected withdrawals root %#x, got %#x", expectedRoot, wdRoot) + } + return nil +} + +func processWithdrawalStateTransition( + st state.BeaconState, + expectedWithdrawals []*enginev1.Withdrawal, + partialWithdrawalsCount uint64, +) (err error) { + for _, withdrawal := range expectedWithdrawals { + err := helpers.DecreaseBalance(st, withdrawal.ValidatorIndex, withdrawal.Amount) + if err != nil { + return errors.Wrap(err, "could not decrease balance") + } + } + if st.Version() >= version.Electra { + if err := st.DequeuePartialWithdrawals(partialWithdrawalsCount); err != nil { + return fmt.Errorf("unable to dequeue partial withdrawals from state: %w", err) + } + } + + if len(expectedWithdrawals) > 0 { + if err := st.SetNextWithdrawalIndex(expectedWithdrawals[len(expectedWithdrawals)-1].Index + 1); err != nil { + return errors.Wrap(err, "could not set next withdrawal index") + } + } + var nextValidatorIndex primitives.ValidatorIndex + if uint64(len(expectedWithdrawals)) < params.BeaconConfig().MaxWithdrawalsPerPayload { + nextValidatorIndex, err = st.NextWithdrawalValidatorIndex() + if err != nil { + return errors.Wrap(err, "could not get next withdrawal validator index") + } + nextValidatorIndex += primitives.ValidatorIndex(params.BeaconConfig().MaxValidatorsPerWithdrawalsSweep) + nextValidatorIndex = nextValidatorIndex % primitives.ValidatorIndex(st.NumValidators()) + } else { + nextValidatorIndex = expectedWithdrawals[len(expectedWithdrawals)-1].ValidatorIndex + 1 + if nextValidatorIndex == primitives.ValidatorIndex(st.NumValidators()) { + nextValidatorIndex = 0 + } + } + if err := st.SetNextWithdrawalValidatorIndex(nextValidatorIndex); err != nil { + return errors.Wrap(err, "could not set next withdrawal validator index") + } + return nil +} + // ProcessWithdrawals processes the validator withdrawals from the provided execution payload // into the beacon state. // // Spec pseudocode definition: // // def process_withdrawals(state: BeaconState, payload: ExecutionPayload) -> None: +// if state.fork.current_version >= EIP7732_FORK_VERSION : +// if not is_parent_block_full(state): # [New in EIP-7732] +// return +// // expected_withdrawals, partial_withdrawals_count = get_expected_withdrawals(state) # [Modified in Electra:EIP7251] // -// assert len(payload.withdrawals) == len(expected_withdrawals) +// if state.fork.current_version >= EIP7732_FORK_VERSION : +// state.latest_withdrawals_root = hash_tree_root(expected_withdrawals) # [New in EIP-7732] +// else : +// assert len(payload.withdrawals) == len(expected_withdrawals) // // for expected_withdrawal, withdrawal in zip(expected_withdrawals, payload.withdrawals): // assert withdrawal == expected_withdrawal @@ -148,76 +230,39 @@ func ValidateBLSToExecutionChange(st state.ReadOnlyBeaconState, signed *ethpb.Si // next_validator_index = ValidatorIndex(next_index % len(state.validators)) // state.next_withdrawal_validator_index = next_validator_index func ProcessWithdrawals(st state.BeaconState, executionData interfaces.ExecutionData) (state.BeaconState, error) { - expectedWithdrawals, partialWithdrawalsCount, err := st.ExpectedWithdrawals() - if err != nil { - return nil, errors.Wrap(err, "could not get expected withdrawals") - } - - var wdRoot [32]byte - if executionData.IsBlinded() { - r, err := executionData.WithdrawalsRoot() + if st.Version() >= version.EPBS { + IsParentBlockFull, err := st.IsParentBlockFull() 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") + return nil, errors.Wrap(err, "could not check if parent block is full") } - if len(wds) != len(expectedWithdrawals) { - return nil, fmt.Errorf("execution payload header has %d withdrawals when %d were expected", len(wds), len(expectedWithdrawals)) + if !IsParentBlockFull { + return nil, nil } + } - wdRoot, err = ssz.WithdrawalSliceRoot(wds, fieldparams.MaxWithdrawalsPerPayload) - if err != nil { - return nil, errors.Wrap(err, "could not get withdrawals root") - } + expectedWithdrawals, partialWithdrawalsCount, err := st.ExpectedWithdrawals() + if err != nil { + return nil, errors.Wrap(err, "could not get expected withdrawals") } expectedRoot, err := ssz.WithdrawalSliceRoot(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 st.Version() >= version.EPBS { + err = st.SetLastWithdrawalsRoot(expectedRoot[:]) if err != nil { - return nil, errors.Wrap(err, "could not decrease balance") - } - } - - if st.Version() >= version.Electra { - if err := st.DequeuePartialWithdrawals(partialWithdrawalsCount); err != nil { - return nil, fmt.Errorf("unable to dequeue partial withdrawals from state: %w", err) + return nil, errors.Wrap(err, "could not set withdrawals root") } - } - - 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(expectedWithdrawals)) < params.BeaconConfig().MaxWithdrawalsPerPayload { - nextValidatorIndex, err = st.NextWithdrawalValidatorIndex() - if err != nil { - return nil, errors.Wrap(err, "could not get next withdrawal validator index") - } - nextValidatorIndex += primitives.ValidatorIndex(params.BeaconConfig().MaxValidatorsPerWithdrawalsSweep) - nextValidatorIndex = nextValidatorIndex % primitives.ValidatorIndex(st.NumValidators()) } else { - nextValidatorIndex = expectedWithdrawals[len(expectedWithdrawals)-1].ValidatorIndex + 1 - if nextValidatorIndex == primitives.ValidatorIndex(st.NumValidators()) { - nextValidatorIndex = 0 + if err := checkWithdrawalsAgainstPayload(executionData, len(expectedWithdrawals), expectedRoot); err != nil { + return nil, err } } - if err := st.SetNextWithdrawalValidatorIndex(nextValidatorIndex); err != nil { - return nil, errors.Wrap(err, "could not set next withdrawal validator index") + if err := processWithdrawalStateTransition(st, expectedWithdrawals, partialWithdrawalsCount); err != nil { + return nil, err } return st, nil } diff --git a/beacon-chain/core/blocks/withdrawals_test.go b/beacon-chain/core/blocks/withdrawals_test.go index 7d7dc5dc08f6..3a0385cad55f 100644 --- a/beacon-chain/core/blocks/withdrawals_test.go +++ b/beacon-chain/core/blocks/withdrawals_test.go @@ -12,6 +12,7 @@ import ( fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams" "github.com/prysmaticlabs/prysm/v5/config/params" consensusblocks "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks" + "github.com/prysmaticlabs/prysm/v5/consensus-types/epbs" "github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces" "github.com/prysmaticlabs/prysm/v5/consensus-types/primitives" "github.com/prysmaticlabs/prysm/v5/crypto/bls" @@ -1132,13 +1133,405 @@ func TestProcessWithdrawals(t *testing.T) { checkPostState(t, test.Control, post) } params.BeaconConfig().MaxValidatorsPerWithdrawalsSweep = saved - }) } }) } } +func TestProcessWithdrawalsEPBS(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 + PendingPartialWithdrawalIndices []primitives.ValidatorIndex + Withdrawals []*enginev1.Withdrawal + PendingPartialWithdrawals []*ethpb.PendingPartialWithdrawal // Electra + LatestBlockHash []byte // EIP-7732 + } + type control struct { + NextWithdrawalValidatorIndex primitives.ValidatorIndex + NextWithdrawalIndex uint64 + 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), + } + } + PendingPartialWithdrawal := 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, + PendingPartialWithdrawalIndices: []primitives.ValidatorIndex{7}, + Withdrawals: []*enginev1.Withdrawal{ + PendingPartialWithdrawal(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, + PendingPartialWithdrawalIndices: []primitives.ValidatorIndex{7, 19, 28}, + Withdrawals: []*enginev1.Withdrawal{ + PendingPartialWithdrawal(7, 22), PendingPartialWithdrawal(19, 23), PendingPartialWithdrawal(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}, + PendingPartialWithdrawalIndices: []primitives.ValidatorIndex{2, 1, 89, 15}, + Withdrawals: []*enginev1.Withdrawal{ + PendingPartialWithdrawal(89, 22), PendingPartialWithdrawal(1, 23), PendingPartialWithdrawal(2, 24), + fullWithdrawal(7, 25), PendingPartialWithdrawal(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 many withdrawals with pending partial withdrawals in state", + NextWithdrawalIndex: 22, + NextWithdrawalValidatorIndex: 88, + FullWithdrawalIndices: []primitives.ValidatorIndex{7, 19, 28}, + PendingPartialWithdrawalIndices: []primitives.ValidatorIndex{2, 1, 89, 15}, + Withdrawals: []*enginev1.Withdrawal{ + PendingPartialWithdrawal(89, 22), PendingPartialWithdrawal(1, 23), PendingPartialWithdrawal(2, 24), + fullWithdrawal(7, 25), PendingPartialWithdrawal(15, 26), fullWithdrawal(19, 27), + fullWithdrawal(28, 28), + }, + PendingPartialWithdrawals: []*ethpb.PendingPartialWithdrawal{ + { + Index: 11, + Amount: withdrawalAmount(11) - maxEffectiveBalance, + }, + }, + }, + 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, + PendingPartialWithdrawalIndices: []primitives.ValidatorIndex{1, 2, 3, 4, 5, 6, 7, 8, 9, 21, 22, 23, 24, 25, 26, 27, 29, 35, 89}, + Withdrawals: []*enginev1.Withdrawal{ + PendingPartialWithdrawal(1, 22), PendingPartialWithdrawal(2, 23), PendingPartialWithdrawal(3, 24), + PendingPartialWithdrawal(4, 25), PendingPartialWithdrawal(5, 26), PendingPartialWithdrawal(6, 27), + PendingPartialWithdrawal(7, 28), PendingPartialWithdrawal(8, 29), PendingPartialWithdrawal(9, 30), + PendingPartialWithdrawal(21, 31), PendingPartialWithdrawal(22, 32), PendingPartialWithdrawal(23, 33), + PendingPartialWithdrawal(24, 34), PendingPartialWithdrawal(25, 35), PendingPartialWithdrawal(26, 36), + PendingPartialWithdrawal(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: "Parent Node is not full", + NextWithdrawalIndex: 22, + NextWithdrawalValidatorIndex: 4, + FullWithdrawalIndices: []primitives.ValidatorIndex{7, 19, 28, 1}, + Withdrawals: []*enginev1.Withdrawal{ + fullWithdrawal(7, 22), fullWithdrawal(19, 23), fullWithdrawal(28, 24), + }, + LatestBlockHash: []byte{1, 2, 3}, + }, + }, + } + + 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 state.BeaconState, arguments args) error { + validators := make([]*ethpb.Validator, numValidators) + if err := st.SetBalances(make([]uint64, numValidators)); err != nil { + return err + } + for i := range validators { + v := ðpb.Validator{} + v.EffectiveBalance = maxEffectiveBalance + v.WithdrawableEpoch = epochInFuture + v.WithdrawalCredentials = make([]byte, 32) + v.WithdrawalCredentials[31] = byte(i) + if err := st.UpdateBalancesAtIndex(primitives.ValidatorIndex(i), v.EffectiveBalance-uint64(rand.Intn(1000))); err != nil { + return err + } + validators[i] = v + } + for _, idx := range arguments.FullWithdrawalIndices { + if idx != notWithdrawableIndex { + validators[idx].WithdrawableEpoch = epochInPast + } + if err := st.UpdateBalancesAtIndex(idx, withdrawalAmount(idx)); err != nil { + return err + } + validators[idx].WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte + } + for _, idx := range arguments.PendingPartialWithdrawalIndices { + validators[idx].WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte + if err := st.UpdateBalancesAtIndex(idx, withdrawalAmount(idx)); err != nil { + return err + } + } + return st.SetValidators(validators) + } + + 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.PendingPartialWithdrawalIndices == nil { + test.Args.PendingPartialWithdrawalIndices = make([]primitives.ValidatorIndex, 0) + } + slot, err := slots.EpochStart(currentEpoch) + require.NoError(t, err) + var st state.BeaconState + var p interfaces.ExecutionData + spb := ðpb.BeaconStateEPBS{ + Slot: slot, + NextWithdrawalValidatorIndex: test.Args.NextWithdrawalValidatorIndex, + NextWithdrawalIndex: test.Args.NextWithdrawalIndex, + PendingPartialWithdrawals: test.Args.PendingPartialWithdrawals, + LatestExecutionPayloadHeader: &enginev1.ExecutionPayloadHeaderEPBS{ + BlockHash: []byte{}, + }, + LatestBlockHash: test.Args.LatestBlockHash, + } + st, err = state_native.InitializeFromProtoUnsafeEpbs(spb) + require.NoError(t, err) + wp, err := epbs.WrappedROExecutionPayloadEnvelope(&enginev1.ExecutionPayloadEnvelope{Payload: &enginev1.ExecutionPayloadElectra{Withdrawals: test.Args.Withdrawals}}) + require.NoError(t, err) + p, err = wp.Execution() + require.NoError(t, err) + err = prepareValidators(st, test.Args) + require.NoError(t, err) + post, err := blocks.ProcessWithdrawals(st, p) + if test.Args.Name == "Parent Node is not full" { + require.IsNil(t, post) + require.IsNil(t, err) + } else { + require.NoError(t, err) + checkPostState(t, test.Control, post) + } + params.BeaconConfig().MaxValidatorsPerWithdrawalsSweep = saved + }) + } +} + func TestProcessBLSToExecutionChanges(t *testing.T) { spb := ðpb.BeaconStateCapella{ Fork: ðpb.Fork{