Compare commits

...

4 Commits

Author SHA1 Message Date
terence
c5ad54fe31 Ignore nil cs (#13990) 2024-05-24 14:56:09 -05:00
Preston Van Loon
452f6c3460 [Prototype] EIP-7251: MaxEB
Add has_compounding_withdrawal_credential helper

Progress

Tuesday night progress

Thursday night progress

Sunday afternoon progress

Tuesday progress

Monday night progress: tests are passing

Progress from last week, mostly on state

monday night progress: had to refactor a lot of tests to be blackbox tests

tuesday progress

wednesday progress

thursday progress

Friday progress

Monday progress

Fix up eip-7251 branch, fix issues, update to spec v1.5.0-alpha.0

More updates to spec 1.5.0-alpha.0, refactorings

Prepping for spectests

WIP: Electra beacon state HTR

Progress on custom HTR for beaconStateElectra

Rename eip7251 package to electra package

Working on beacon state changes and cleanup

Fix TestGetSpec

Fix ExpectedWithdrawals

Electra: full beacon config

Fixing conflicts

Resolve rebasing conflicts

Resolve rebasing conflicts

Update spectest to v1.5.0-alpha.1

Add PeerDAS config change

Fix API changes from beacon state PR
2024-05-24 14:56:09 -05:00
Preston Van Loon
a0d5882315 PR feedback from the amazing @rkapka 2024-05-24 14:03:10 -05:00
Preston Van Loon
345a1f6e7f eip-7251: process_effective_balance_updates
Spectests for process_effective_balance_updates

process_effective_balance_updates unit tests
2024-05-24 14:01:39 -05:00
44 changed files with 674 additions and 80 deletions

View File

@@ -28,6 +28,7 @@ type MockBuilderService struct {
Payload *v1.ExecutionPayload
PayloadCapella *v1.ExecutionPayloadCapella
PayloadDeneb *v1.ExecutionPayloadDeneb
PayloadElectra *v1.ExecutionPayloadElectra
BlobBundle *v1.BlobsBundle
ErrSubmitBlindedBlock error
Bid *ethpb.SignedBuilderBid
@@ -65,6 +66,12 @@ func (s *MockBuilderService) SubmitBlindedBlock(_ context.Context, b interfaces.
return nil, nil, errors.Wrap(err, "could not wrap deneb payload")
}
return w, s.BlobBundle, s.ErrSubmitBlindedBlock
case version.Electra:
w, err := blocks.WrappedExecutionPayloadElectra(s.PayloadElectra)
if err != nil {
return nil, nil, errors.Wrap(err, "could not wrap electra payload")
}
return w, s.BlobBundle, s.ErrSubmitBlindedBlock
default:
return nil, nil, errors.New("unknown block version for mocking")
}

View File

@@ -9,6 +9,7 @@ import (
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/signing"
"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/container/trie"
"github.com/prysmaticlabs/prysm/v5/contracts/deposit"
"github.com/prysmaticlabs/prysm/v5/crypto/bls"
@@ -136,26 +137,25 @@ func BatchVerifyDepositsSignatures(ctx context.Context, deposits []*ethpb.Deposi
//
// pubkey = deposit.data.pubkey
// amount = deposit.data.amount
// validator_pubkeys = [v.pubkey for v in state.validators]
// if pubkey not in validator_pubkeys:
// # Verify the deposit signature (proof of possession) which is not checked by the deposit contract
// deposit_message = DepositMessage(
// pubkey=deposit.data.pubkey,
// withdrawal_credentials=deposit.data.withdrawal_credentials,
// amount=deposit.data.amount,
// )
// domain = compute_domain(DOMAIN_DEPOSIT) # Fork-agnostic domain since deposits are valid across forks
// signing_root = compute_signing_root(deposit_message, domain)
// if not bls.Verify(pubkey, signing_root, deposit.data.signature):
// return
//
// # Add validator and balance entries
// state.validators.append(get_validator_from_deposit(state, deposit))
// state.balances.append(amount)
// else:
// # Increase balance by deposit amount
// index = ValidatorIndex(validator_pubkeys.index(pubkey))
// increase_balance(state, index, amount)
// TODO: This is apply_deposit, refactor to another function for readability.
//
// validator_pubkeys = [v.pubkey for v in state.validators]
// if pubkey not in validator_pubkeys:
// # Verify the deposit signature (proof of possession) which is not checked by the deposit contract
// deposit_message = DepositMessage(
// pubkey=deposit.data.pubkey,
// withdrawal_credentials=deposit.data.withdrawal_credentials,
// amount=deposit.data.amount,
// )
// domain = compute_domain(DOMAIN_DEPOSIT) # Fork-agnostic domain since deposits are valid across forks
// signing_root = compute_signing_root(deposit_message, domain)
// if bls.Verify(pubkey, signing_root, signature):
// add_validator_to_registry(state, pubkey, withdrawal_credentials, amount) // TODO: Missing spec def here
// else:
// # Increase balance by deposit amount
// index = ValidatorIndex(validator_pubkeys.index(pubkey))
// state.pending_balance_deposits.append(PendingBalanceDeposit(index, amount)) # [Modified in EIP-7251]
func ProcessDeposit(beaconState state.BeaconState, deposit *ethpb.Deposit, verifySignature bool) (state.BeaconState, bool, error) {
var newValidator bool
if err := verifyDeposit(beaconState, deposit); err != nil {
@@ -167,6 +167,7 @@ func ProcessDeposit(beaconState state.BeaconState, deposit *ethpb.Deposit, verif
if err := beaconState.SetEth1DepositIndex(beaconState.Eth1DepositIndex() + 1); err != nil {
return nil, newValidator, err
}
isElectraOrLater := beaconState.Fork() != nil && beaconState.Fork().Epoch >= params.BeaconConfig().ElectraForkEpoch
pubKey := deposit.Data.PublicKey
amount := deposit.Data.Amount
index, ok := beaconState.ValidatorIndexByPubkey(bytesutil.ToBytes48(pubKey))
@@ -183,10 +184,16 @@ func ProcessDeposit(beaconState state.BeaconState, deposit *ethpb.Deposit, verif
}
}
// NOTE: This is get_validator_from_deposit. There are changes in EIP-7251.
effectiveBalance := amount - (amount % params.BeaconConfig().EffectiveBalanceIncrement)
if params.BeaconConfig().MaxEffectiveBalance < effectiveBalance {
effectiveBalance = params.BeaconConfig().MaxEffectiveBalance
}
if isElectraOrLater {
// In EIP-7251, the balance updates happen in the process_pending_balance_deposits method of
// the epoch transition function.
effectiveBalance = 0 // New in EIP-7251
}
if err := beaconState.AppendValidator(&ethpb.Validator{
PublicKey: pubKey,
WithdrawalCredentials: deposit.Data.WithdrawalCredentials,
@@ -202,10 +209,24 @@ func ProcessDeposit(beaconState state.BeaconState, deposit *ethpb.Deposit, verif
if err := beaconState.AppendBalance(amount); err != nil {
return nil, newValidator, err
}
} else if err := helpers.IncreaseBalance(beaconState, index, amount); err != nil {
return nil, newValidator, err
numVals := beaconState.NumValidators()
if numVals == 0 { // Cautiously prevent impossible underflow
return nil, false, errors.New("underflow: zero validators")
}
index = primitives.ValidatorIndex(numVals - 1)
} else {
if !isElectraOrLater {
if err := helpers.IncreaseBalance(beaconState, index, amount); err != nil {
return nil, newValidator, err
}
}
}
if isElectraOrLater {
if err := beaconState.AppendPendingBalanceDeposit(index, amount); err != nil {
return nil, newValidator, err
}
}
return beaconState, newValidator, nil
}

View File

@@ -40,6 +40,8 @@ var ValidatorCannotExitYetMsg = "validator has not been active long enough to ex
// assert get_current_epoch(state) >= voluntary_exit.epoch
// # Verify the validator has been active long enough
// assert get_current_epoch(state) >= validator.activation_epoch + SHARD_COMMITTEE_PERIOD
// # Only exit validator if it has no pending withdrawals in the queue
// assert get_pending_balance_to_withdraw(state, voluntary_exit.validator_index) == 0 # [New in EIP7251]
// # Verify signature
// domain = get_domain(state, DOMAIN_VOLUNTARY_EXIT, voluntary_exit.epoch)
// signing_root = compute_signing_root(voluntary_exit, domain)
@@ -98,6 +100,8 @@ func ProcessVoluntaryExits(
// assert get_current_epoch(state) >= voluntary_exit.epoch
// # Verify the validator has been active long enough
// assert get_current_epoch(state) >= validator.activation_epoch + SHARD_COMMITTEE_PERIOD
// # Only exit validator if it has no pending withdrawals in the queue
// assert get_pending_balance_to_withdraw(state, voluntary_exit.validator_index) == 0 # [New in EIP7251]
// # Verify signature
// domain = get_domain(state, DOMAIN_VOLUNTARY_EXIT, voluntary_exit.epoch)
// signing_root = compute_signing_root(voluntary_exit, domain)
@@ -128,7 +132,7 @@ func VerifyExitAndSignature(
}
exit := signed.Exit
if err := verifyExitConditions(validator, currentSlot, exit); err != nil {
if err := verifyExitConditions(state, validator, currentSlot, exit); err != nil {
return err
}
domain, err := signing.Domain(fork, exit.Epoch, params.BeaconConfig().DomainVoluntaryExit, genesisRoot)
@@ -157,13 +161,15 @@ func VerifyExitAndSignature(
// assert get_current_epoch(state) >= voluntary_exit.epoch
// # Verify the validator has been active long enough
// assert get_current_epoch(state) >= validator.activation_epoch + SHARD_COMMITTEE_PERIOD
// # Only exit validator if it has no pending withdrawals in the queue
// assert get_pending_balance_to_withdraw(state, voluntary_exit.validator_index) == 0 # [New in Electra:EIP7251]
// # Verify signature
// domain = get_domain(state, DOMAIN_VOLUNTARY_EXIT, voluntary_exit.epoch)
// signing_root = compute_signing_root(voluntary_exit, domain)
// assert bls.Verify(validator.pubkey, signing_root, signed_voluntary_exit.signature)
// # Initiate exit
// initiate_validator_exit(state, voluntary_exit.validator_index)
func verifyExitConditions(validator state.ReadOnlyValidator, currentSlot primitives.Slot, exit *ethpb.VoluntaryExit) error {
func verifyExitConditions(st state.ReadOnlyBeaconState, validator state.ReadOnlyValidator, currentSlot primitives.Slot, exit *ethpb.VoluntaryExit) error {
currentEpoch := slots.ToEpoch(currentSlot)
// Verify the validator is active.
if !helpers.IsActiveValidatorUsingTrie(validator, currentEpoch) {
@@ -187,5 +193,14 @@ func verifyExitConditions(validator state.ReadOnlyValidator, currentSlot primiti
validator.ActivationEpoch()+params.BeaconConfig().ShardCommitteePeriod,
)
}
if st.Version() >= version.Electra {
pending, err := st.PendingBalanceToWithdraw(exit.ValidatorIndex)
if err != nil {
return err
}
if pending != 0 {
return fmt.Errorf("validator with index %d has pending balance to withdraw and cannot exit", exit.ValidatorIndex)
}
}
return nil
}

View File

@@ -118,56 +118,46 @@ func ValidateBLSToExecutionChange(st state.ReadOnlyBeaconState, signed *ethpb.Si
// ProcessWithdrawals processes the validator withdrawals from the provided execution payload
// into the beacon state.
//
// Spec pseudocode definition:
// Spec definition:
//
// def process_withdrawals(state: BeaconState, payload: ExecutionPayload) -> None:
// def process_withdrawals(state: BeaconState, payload: ExecutionPayload) -> None:
// expected_withdrawals, partial_withdrawals_count = get_expected_withdrawals(state) # [Modified in Electra:EIP7251]
//
// expected_withdrawals = get_expected_withdrawals(state)
// assert len(payload.withdrawals) == len(expected_withdrawals)
// assert len(payload.withdrawals) == len(expected_withdrawals)
//
// for expected_withdrawal, withdrawal in zip(expected_withdrawals, payload.withdrawals):
// assert withdrawal == expected_withdrawal
// decrease_balance(state, withdrawal.validator_index, withdrawal.amount)
// for expected_withdrawal, withdrawal in zip(expected_withdrawals, payload.withdrawals):
// assert withdrawal == expected_withdrawal
// decrease_balance(state, withdrawal.validator_index, withdrawal.amount)
//
// # Update the next withdrawal index if this block contained withdrawals
// if len(expected_withdrawals) != 0:
// latest_withdrawal = expected_withdrawals[-1]
// state.next_withdrawal_index = WithdrawalIndex(latest_withdrawal.index + 1)
// # Update pending partial withdrawals [New in Electra:EIP7251]
// state.pending_partial_withdrawals = state.pending_partial_withdrawals[partial_withdrawals_count:]
//
// # Update the next validator index to start the next withdrawal sweep
// if len(expected_withdrawals) == MAX_WITHDRAWALS_PER_PAYLOAD:
// # Next sweep starts after the latest withdrawal's validator index
// next_validator_index = ValidatorIndex((expected_withdrawals[-1].validator_index + 1) % len(state.validators))
// state.next_withdrawal_validator_index = next_validator_index
// else:
// # Advance sweep by the max length of the sweep if there was not a full set of withdrawals
// next_index = state.next_withdrawal_validator_index + MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP
// next_validator_index = ValidatorIndex(next_index % len(state.validators))
// state.next_withdrawal_validator_index = next_validator_index
// # Update the next withdrawal index if this block contained withdrawals
// if len(expected_withdrawals) != 0:
// latest_withdrawal = expected_withdrawals[-1]
// state.next_withdrawal_index = WithdrawalIndex(latest_withdrawal.index + 1)
//
// # Update the next validator index to start the next withdrawal sweep
// if len(expected_withdrawals) == MAX_WITHDRAWALS_PER_PAYLOAD:
// # Next sweep starts after the latest withdrawal's validator index
// next_validator_index = ValidatorIndex((expected_withdrawals[-1].validator_index + 1) % len(state.validators))
// state.next_withdrawal_validator_index = next_validator_index
// else:
// # Advance sweep by the max length of the sweep if there was not a full set of withdrawals
// next_index = state.next_withdrawal_validator_index + MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP
// 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, _, err := st.ExpectedWithdrawals()
expectedWithdrawals, partials, 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 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(wds, fieldparams.MaxWithdrawalsPerPayload)
if err != nil {
return nil, errors.Wrap(err, "could not get withdrawals root")
}
// Instead of checking "assert withdrawal == expected_withdrawal", we check the withdrawals root.
wdRoot, err := withdrawalsRootFromMaybeBlindedExecutionData(executionData)
if err != nil {
return nil, errors.Wrap(err, "could not get withdrawals root")
}
expectedRoot, err := ssz.WithdrawalSliceRoot(expectedWithdrawals, fieldparams.MaxWithdrawalsPerPayload)
if err != nil {
return nil, errors.Wrap(err, "could not get expected withdrawals root")
@@ -182,6 +172,13 @@ func ProcessWithdrawals(st state.BeaconState, executionData interfaces.Execution
return nil, errors.Wrap(err, "could not decrease balance")
}
}
if partials > 0 {
if err := st.DequeuePartialWithdrawals(partials); err != nil {
return nil, fmt.Errorf("could not dequeue partial withdrawals: %w", err)
}
}
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")
@@ -207,6 +204,21 @@ func ProcessWithdrawals(st state.BeaconState, executionData interfaces.Execution
return st, nil
}
func withdrawalsRootFromMaybeBlindedExecutionData(ed interfaces.ExecutionData) ([32]byte, error) {
if ed.IsBlinded() {
r, err := ed.WithdrawalsRoot()
if err != nil {
return [32]byte{}, errors.Wrap(err, "could not get withdrawals root from blinded execution data")
}
return bytesutil.ToBytes32(r), nil
}
wds, err := ed.Withdrawals()
if err != nil {
return [32]byte{}, errors.Wrap(err, "could not get withdrawals")
}
return ssz.WithdrawalSliceRoot(wds, fieldparams.MaxWithdrawalsPerPayload)
}
// BLSChangesSignatureBatch extracts the relevant signatures from the provided execution change
// messages and transforms them into a signature batch object.
func BLSChangesSignatureBatch(

View File

@@ -7,6 +7,7 @@ go_library(
"consolidations.go",
"deposits.go",
"effective_balance_updates.go",
"epoch_processing.go",
"registry_updates.go",
"transition.go",
"upgrade.go",
@@ -22,6 +23,7 @@ go_library(
"//beacon-chain/core/helpers:go_default_library",
"//beacon-chain/core/signing:go_default_library",
"//beacon-chain/core/time:go_default_library",
"//beacon-chain/core/validators:go_default_library",
"//beacon-chain/state:go_default_library",
"//beacon-chain/state/state-native:go_default_library",
"//config/params:go_default_library",
@@ -43,6 +45,7 @@ go_test(
"churn_test.go",
"consolidations_test.go",
"deposits_test.go",
"effective_balance_updates_test.go",
"upgrade_test.go",
"validator_test.go",
],

View File

@@ -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.
//
@@ -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)
}

View File

@@ -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)
}
})
}
}

View File

@@ -0,0 +1,27 @@
package electra
import (
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
)
// sortableIndices implements the Sort interface to sort newly activated validator indices
// by activation epoch and by index number.
type sortableIndices struct {
indices []primitives.ValidatorIndex
validators []*ethpb.Validator
}
// Len is the number of elements in the collection.
func (s sortableIndices) Len() int { return len(s.indices) }
// Swap swaps the elements with indexes i and j.
func (s sortableIndices) Swap(i, j int) { s.indices[i], s.indices[j] = s.indices[j], s.indices[i] }
// Less reports whether the element with index i must sort before the element with index j.
func (s sortableIndices) Less(i, j int) bool {
if s.validators[s.indices[i]].ActivationEligibilityEpoch == s.validators[s.indices[j]].ActivationEligibilityEpoch {
return s.indices[i] < s.indices[j]
}
return s.validators[s.indices[i]].ActivationEligibilityEpoch < s.validators[s.indices[j]].ActivationEligibilityEpoch
}

View File

@@ -27,6 +27,10 @@ var (
ProcessSyncCommitteeUpdates = altair.ProcessSyncCommitteeUpdates
AttestationsDelta = altair.AttestationsDelta
ProcessSyncAggregate = altair.ProcessSyncAggregate
// These need to be rewritten for electra.
ProcessDeposits = altair.ProcessDeposits
ProcessAttestationsNoVerifySignature = altair.ProcessAttestationsNoVerifySignature
)
// ProcessEpoch describes the per epoch operations that are performed on the beacon state.
@@ -47,7 +51,7 @@ var (
// process_slashings_reset(state)
// process_randao_mixes_reset(state)
func ProcessEpoch(ctx context.Context, state state.BeaconState) (state.BeaconState, error) {
_, span := trace.StartSpan(ctx, "electra.ProcessEpoch")
ctx, span := trace.StartSpan(ctx, "electra.ProcessEpoch")
defer span.End()
if state == nil || state.IsNil() {

View File

@@ -95,8 +95,8 @@ func CanUpgradeToDeneb(slot primitives.Slot) bool {
// If state.slot % SLOTS_PER_EPOCH == 0 and compute_epoch_at_slot(state.slot) == ELECTRA_FORK_EPOCH
func CanUpgradeToElectra(slot primitives.Slot) bool {
epochStart := slots.IsEpochStart(slot)
electraEpoch := slots.ToEpoch(slot) == params.BeaconConfig().ElectraForkEpoch
return epochStart && electraEpoch
ElectraEpoch := slots.ToEpoch(slot) == params.BeaconConfig().ElectraForkEpoch
return epochStart && ElectraEpoch
}
// CanProcessEpoch checks the eligibility to process epoch.

View File

@@ -453,7 +453,6 @@ func electraOperations(
if err != nil {
return nil, errors.Wrap(err, "could not process deposit receipts")
}
if err := electra.ProcessConsolidations(ctx, st, bod.Consolidations()); err != nil {
return nil, errors.Wrap(err, "could not process consolidations")
}

View File

@@ -89,6 +89,8 @@ func (s *Server) getBeaconStateV2(ctx context.Context, w http.ResponseWriter, id
httputil.HandleError(w, errMsgStateFromConsensus+": "+err.Error(), http.StatusInternalServerError)
return
}
case version.Electra:
panic("implement me")
default:
httputil.HandleError(w, "Unsupported state version", http.StatusInternalServerError)
return

View File

@@ -475,6 +475,8 @@ func (s *Server) sendPayloadAttributes(ctx context.Context, w http.ResponseWrite
Withdrawals: structs.WithdrawalsFromConsensus(withdrawals),
ParentBeaconBlockRoot: hexutil.Encode(parentRoot[:]),
}
case version.Electra:
panic("implement me")
default:
return write(w, flusher, "Payload version %s is not supported", version.String(headState.Version()))
}

View File

@@ -209,3 +209,4 @@ message SyncAggregate {
// BLS aggregated signature of the sync committee for the ones that voted.
bytes sync_committee_signature = 2 [(ethereum.eth.ext.ssz_size) = "96"];
}

View File

@@ -156,6 +156,11 @@ func (s *PremineGenesisConfig) empty() (state.BeaconState, error) {
if err != nil {
return nil, err
}
case version.Electra:
e, err = state_native.InitializeFromProtoElectra(&ethpb.BeaconStateElectra{})
if err != nil {
return nil, err
}
default:
return nil, errUnsupportedVersion
}
@@ -335,6 +340,8 @@ func (s *PremineGenesisConfig) setFork(g state.BeaconState) error {
pv, cv = params.BeaconConfig().BellatrixForkVersion, params.BeaconConfig().CapellaForkVersion
case version.Deneb:
pv, cv = params.BeaconConfig().CapellaForkVersion, params.BeaconConfig().DenebForkVersion
case version.Electra:
pv, cv = params.BeaconConfig().DenebForkVersion, params.BeaconConfig().ElectraForkVersion
default:
return errUnsupportedVersion
}
@@ -523,6 +530,37 @@ func (s *PremineGenesisConfig) setLatestBlockHeader(g state.BeaconState) error {
BlsToExecutionChanges: make([]*ethpb.SignedBLSToExecutionChange, 0),
BlobKzgCommitments: make([][]byte, 0),
}
case version.Electra:
body = &ethpb.BeaconBlockBodyElectra{
RandaoReveal: make([]byte, 96),
Eth1Data: &ethpb.Eth1Data{
DepositRoot: make([]byte, 32),
BlockHash: make([]byte, 32),
},
Graffiti: make([]byte, 32),
SyncAggregate: &ethpb.SyncAggregate{
SyncCommitteeBits: make([]byte, fieldparams.SyncCommitteeLength/8),
SyncCommitteeSignature: make([]byte, fieldparams.BLSSignatureLength),
},
ExecutionPayload: &enginev1.ExecutionPayloadElectra{
ParentHash: make([]byte, 32),
FeeRecipient: make([]byte, 20),
StateRoot: make([]byte, 32),
ReceiptsRoot: make([]byte, 32),
LogsBloom: make([]byte, 256),
PrevRandao: make([]byte, 32),
ExtraData: make([]byte, 0),
BaseFeePerGas: make([]byte, 32),
BlockHash: make([]byte, 32),
Transactions: make([][]byte, 0),
Withdrawals: make([]*enginev1.Withdrawal, 0),
DepositReceipts: make([]*enginev1.DepositReceipt, 0),
WithdrawalRequests: make([]*enginev1.ExecutionLayerWithdrawalRequest, 0),
},
BlsToExecutionChanges: make([]*ethpb.SignedBLSToExecutionChange, 0),
BlobKzgCommitments: make([][]byte, 0),
Consolidations: make([]*ethpb.SignedConsolidation, 0),
}
default:
return errUnsupportedVersion
}
@@ -639,6 +677,44 @@ func (s *PremineGenesisConfig) setExecutionPayload(g state.BeaconState) error {
if err != nil {
return err
}
case version.Electra:
payload := &enginev1.ExecutionPayloadElectra{
ParentHash: gb.ParentHash().Bytes(),
FeeRecipient: gb.Coinbase().Bytes(),
StateRoot: gb.Root().Bytes(),
ReceiptsRoot: gb.ReceiptHash().Bytes(),
LogsBloom: gb.Bloom().Bytes(),
PrevRandao: params.BeaconConfig().ZeroHash[:],
BlockNumber: gb.NumberU64(),
GasLimit: gb.GasLimit(),
GasUsed: gb.GasUsed(),
Timestamp: gb.Time(),
ExtraData: gb.Extra()[:32],
BaseFeePerGas: bytesutil.PadTo(bytesutil.ReverseByteOrder(gb.BaseFee().Bytes()), fieldparams.RootLength),
BlockHash: gb.Hash().Bytes(),
Transactions: make([][]byte, 0),
Withdrawals: make([]*enginev1.Withdrawal, 0),
ExcessBlobGas: *gb.ExcessBlobGas(),
BlobGasUsed: *gb.BlobGasUsed(),
DepositReceipts: make([]*enginev1.DepositReceipt, 0),
WithdrawalRequests: make([]*enginev1.ExecutionLayerWithdrawalRequest, 0),
}
wep, err := blocks.WrappedExecutionPayloadElectra(payload)
if err != nil {
return err
}
wepe, ok := wep.(interfaces.ExecutionDataElectra)
if !ok {
return errors.New("unexpected execution type data is not electra")
}
eph, err := blocks.PayloadToHeaderElectra(wepe)
if err != nil {
return err
}
ed, err = blocks.WrappedExecutionPayloadHeaderElectra(eph)
if err != nil {
return err
}
default:
return errUnsupportedVersion
}

View File

@@ -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",
@@ -11,6 +12,7 @@ go_test(
"pending_balance_updates_test.go",
"pending_consolidations_test.go",
"randao_mixes_reset_test.go",
"registry_updates_test.go",
"rewards_and_penalties_test.go",
"slashings_reset_test.go",
"slashings_test.go",

View File

@@ -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")
}

View File

@@ -0,0 +1,11 @@
package epoch_processing
import (
"testing"
"github.com/prysmaticlabs/prysm/v5/testing/spectest/shared/electra/epoch_processing"
)
func TestMainnet_Electra_EpochProcessing_ResetRegistryUpdates(t *testing.T) {
epoch_processing.RunRegistryUpdatesTests(t, "mainnet")
}

View File

@@ -7,6 +7,5 @@ import (
)
func TestMainnet_Electra_Finality(t *testing.T) {
t.Skip("TODO: Electra")
finality.RunFinalityTest(t, "mainnet")
}

View File

@@ -7,6 +7,5 @@ import (
)
func TestMainnet_Electra_Transition(t *testing.T) {
t.Skip("TODO: Electra")
fork.RunForkTransitionTest(t, "mainnet")
}

View File

@@ -8,6 +8,5 @@ import (
)
func TestMainnet_Electra_Forkchoice(t *testing.T) {
t.Skip("TODO: Electra")
forkchoice.Run(t, "mainnet", version.Electra)
}

View File

@@ -3,10 +3,12 @@ load("@prysm//tools/go:def.bzl", "go_test")
go_test(
name = "go_default_test",
srcs = [
"attestation_test.go",
"attester_slashing_test.go",
"block_header_test.go",
"bls_to_execution_change_test.go",
"consolidation_test.go",
"deposit_test.go",
"execution_payload_test.go",
"proposer_slashing_test.go",
"sync_committee_test.go",

View File

@@ -0,0 +1,11 @@
package operations
import (
"testing"
"github.com/prysmaticlabs/prysm/v5/testing/spectest/shared/electra/operations"
)
func TestMainnet_Electra_Operations_Attestation(t *testing.T) {
operations.RunAttestationTest(t, "mainnet")
}

View File

@@ -0,0 +1,11 @@
package operations
import (
"testing"
"github.com/prysmaticlabs/prysm/v5/testing/spectest/shared/electra/operations"
)
func TestMainnet_Electra_Operations_Deposit(t *testing.T) {
operations.RunDepositTest(t, "mainnet")
}

View File

@@ -7,6 +7,5 @@ import (
)
func TestMainnet_Electra_Operations_VoluntaryExit(t *testing.T) {
t.Skip("TODO: Electra")
operations.RunVoluntaryExitTest(t, "mainnet")
}

View File

@@ -7,6 +7,5 @@ import (
)
func TestMainnet_Electra_Random(t *testing.T) {
t.Skip("TODO: Electra")
sanity.RunBlockProcessingTest(t, "mainnet", "random/random/pyspec_tests")
}

View File

@@ -7,6 +7,5 @@ import (
)
func TestMainnet_Electra_Sanity_Blocks(t *testing.T) {
t.Skip("TODO: Electra")
sanity.RunBlockProcessingTest(t, "mainnet", "sanity/blocks/pyspec_tests")
}

View File

@@ -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",
@@ -11,6 +12,7 @@ go_test(
"pending_balance_updates_test.go",
"pending_consolidations_test.go",
"randao_mixes_reset_test.go",
"registry_updates_test.go",
"rewards_and_penalties_test.go",
"slashings_reset_test.go",
"slashings_test.go",

View File

@@ -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")
}

View File

@@ -0,0 +1,11 @@
package epoch_processing
import (
"testing"
"github.com/prysmaticlabs/prysm/v5/testing/spectest/shared/electra/epoch_processing"
)
func TestMinimal_Electra_EpochProcessing_ResetRegistryUpdates(t *testing.T) {
epoch_processing.RunRegistryUpdatesTests(t, "minimal")
}

View File

@@ -7,6 +7,5 @@ import (
)
func TestMinimal_Electra_Finality(t *testing.T) {
t.Skip("TODO: Electra")
finality.RunFinalityTest(t, "minimal")
}

View File

@@ -8,6 +8,5 @@ import (
)
func TestMinimal_Electra_Forkchoice(t *testing.T) {
t.Skip("TODO: Electra")
forkchoice.Run(t, "minimal", version.Electra)
}

View File

@@ -3,10 +3,12 @@ load("@prysm//tools/go:def.bzl", "go_test")
go_test(
name = "go_default_test",
srcs = [
"attestation_test.go",
"attester_slashing_test.go",
"block_header_test.go",
"bls_to_execution_change_test.go",
"consolidation_test.go",
"deposit_test.go",
"execution_payload_test.go",
"proposer_slashing_test.go",
"sync_committee_test.go",

View File

@@ -0,0 +1,11 @@
package operations
import (
"testing"
"github.com/prysmaticlabs/prysm/v5/testing/spectest/shared/electra/operations"
)
func TestMinimal_Electra_Operations_Attestation(t *testing.T) {
operations.RunAttestationTest(t, "minimal")
}

View File

@@ -0,0 +1,11 @@
package operations
import (
"testing"
"github.com/prysmaticlabs/prysm/v5/testing/spectest/shared/electra/operations"
)
func TestMinimal_Electra_Operations_Deposit(t *testing.T) {
operations.RunDepositTest(t, "minimal")
}

View File

@@ -7,6 +7,5 @@ import (
)
func TestMinimal_Electra_Operations_VoluntaryExit(t *testing.T) {
t.Skip("TODO: Electra")
operations.RunVoluntaryExitTest(t, "minimal")
}

View File

@@ -7,6 +7,5 @@ import (
)
func TestMinimal_Electra_Random(t *testing.T) {
t.Skip("TODO: Electra")
sanity.RunBlockProcessingTest(t, "minimal", "random/random/pyspec_tests")
}

View File

@@ -7,6 +7,5 @@ import (
)
func TestMinimal_Electra_Sanity_Blocks(t *testing.T) {
t.Skip("TODO: Electra")
sanity.RunBlockProcessingTest(t, "minimal", "sanity/blocks/pyspec_tests")
}

View File

@@ -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",
@@ -13,6 +14,7 @@ go_library(
"pending_balance_updates.go",
"pending_consolidations.go",
"randao_mixes_reset.go",
"registry_updates.go",
"rewards_and_penalties.go",
"slashings.go",
"slashings_reset.go",

View File

@@ -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
}

View File

@@ -0,0 +1,32 @@
package epoch_processing
import (
"context"
"path"
"testing"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/electra"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v5/testing/require"
"github.com/prysmaticlabs/prysm/v5/testing/spectest/utils"
)
// RunRegistryUpdatesTests executes "epoch_processing/registry_updates" tests.
func RunRegistryUpdatesTests(t *testing.T, config string) {
require.NoError(t, utils.SetConfig(t, config))
testFolders, testsFolderPath := utils.TestFolders(t, config, "electra", "epoch_processing/registry_updates/pyspec_tests")
for _, folder := range testFolders {
t.Run(folder.Name(), func(t *testing.T) {
// Important to clear cache for every test or else the old value of active validator count gets reused.
helpers.ClearCache()
folderPath := path.Join(testsFolderPath, folder.Name())
RunEpochOperationTest(t, folderPath, processRegistryUpdatesWrapper)
})
}
}
func processRegistryUpdatesWrapper(_ *testing.T, state state.BeaconState) (state.BeaconState, error) {
return electra.ProcessRegistryUpdates(context.Background(), state)
}

View File

@@ -4,10 +4,12 @@ go_library(
name = "go_default_library",
testonly = True,
srcs = [
"attestation.go",
"attester_slashing.go",
"block_header.go",
"bls_to_execution_changes.go",
"consolidations.go",
"deposit.go",
"deposit_receipt.go",
"execution_layer_withdrawal_request.go",
"execution_payload.go",

View File

@@ -0,0 +1,56 @@
package operations
import (
"context"
"errors"
"path"
"testing"
"github.com/golang/snappy"
b "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/electra"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/testing/require"
"github.com/prysmaticlabs/prysm/v5/testing/spectest/utils"
"github.com/prysmaticlabs/prysm/v5/testing/util"
)
func RunAttestationTest(t *testing.T, config string) {
require.NoError(t, utils.SetConfig(t, config))
testFolders, testsFolderPath := utils.TestFolders(t, config, "electra", "operations/attestation/pyspec_tests")
for _, folder := range testFolders {
t.Run(folder.Name(), func(t *testing.T) {
folderPath := path.Join(testsFolderPath, folder.Name())
attestationFile, err := util.BazelFileBytes(folderPath, "attestation.ssz_snappy")
require.NoError(t, err)
attestationSSZ, err := snappy.Decode(nil /* dst */, attestationFile)
require.NoError(t, err, "Failed to decompress")
att := &ethpb.AttestationElectra{}
require.NoError(t, att.UnmarshalSSZ(attestationSSZ), "Failed to unmarshal")
body := &ethpb.BeaconBlockBodyElectra{Attestations: []*ethpb.AttestationElectra{att}}
processAtt := func(ctx context.Context, st state.BeaconState, blk interfaces.SignedBeaconBlock) (state.BeaconState, error) {
st, err = electra.ProcessAttestationsNoVerifySignature(ctx, st, blk.Block())
if err != nil {
return nil, err
}
aSet, err := b.AttestationSignatureBatch(ctx, st, blk.Block().Body().Attestations())
if err != nil {
return nil, err
}
verified, err := aSet.Verify()
if err != nil {
return nil, err
}
if !verified {
return nil, errors.New("could not batch verify attestation signature")
}
return st, nil
}
RunBlockOperationTest(t, folderPath, body, processAtt)
})
}
}

View File

@@ -0,0 +1,38 @@
package operations
import (
"context"
"path"
"testing"
"github.com/golang/snappy"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/electra"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/testing/require"
"github.com/prysmaticlabs/prysm/v5/testing/spectest/utils"
"github.com/prysmaticlabs/prysm/v5/testing/util"
)
func RunDepositTest(t *testing.T, config string) {
require.NoError(t, utils.SetConfig(t, config))
testFolders, testsFolderPath := utils.TestFolders(t, config, "electra", "operations/deposit/pyspec_tests")
for _, folder := range testFolders {
t.Run(folder.Name(), func(t *testing.T) {
folderPath := path.Join(testsFolderPath, folder.Name())
depositFile, err := util.BazelFileBytes(folderPath, "deposit.ssz_snappy")
require.NoError(t, err)
depositSSZ, err := snappy.Decode(nil /* dst */, depositFile)
require.NoError(t, err, "Failed to decompress")
deposit := &ethpb.Deposit{}
require.NoError(t, deposit.UnmarshalSSZ(depositSSZ), "Failed to unmarshal")
body := &ethpb.BeaconBlockBodyElectra{Deposits: []*ethpb.Deposit{deposit}}
processDepositsFunc := func(ctx context.Context, s state.BeaconState, b interfaces.SignedBeaconBlock) (state.BeaconState, error) {
return electra.ProcessDeposits(ctx, s, b.Block().Body().Deposits())
}
RunBlockOperationTest(t, folderPath, body, processDepositsFunc)
})
}
}