diff --git a/beacon/blockchain/finalize_block.go b/beacon/blockchain/finalize_block.go index 7d2b865b37..7d7df65f27 100644 --- a/beacon/blockchain/finalize_block.go +++ b/beacon/blockchain/finalize_block.go @@ -31,6 +31,7 @@ import ( "github.com/berachain/beacon-kit/primitives/crypto" "github.com/berachain/beacon-kit/primitives/math" "github.com/berachain/beacon-kit/primitives/transition" + "github.com/berachain/beacon-kit/primitives/version" statedb "github.com/berachain/beacon-kit/state-transition/core/state" cmtabci "github.com/cometbft/cometbft/abci/types" sdk "github.com/cosmos/cosmos-sdk/types" @@ -126,9 +127,12 @@ func (s *Service) PostFinalizeBlockOps(ctx sdk.Context, blk *ctypes.BeaconBlock) // TODO: consider extracting LatestExecutionPayloadHeader instead of using state here st := s.storageBackend.StateFromContext(ctx) - // Fetch and store the deposit for the block. - blockNum := blk.GetBody().GetExecutionPayload().GetNumber() - s.depositFetcher(ctx, blockNum) + // Before Electra1, deposits must be fetched from the EL directly in the CL. + if version.IsBefore(blk.GetForkVersion(), version.Electra1()) { + // Fetch and store the deposit for the block. + blockNum := blk.GetBody().GetExecutionPayload().GetNumber() + s.depositFetcher(ctx, blockNum) + } // Store the finalized block in the KVStore. // diff --git a/beacon/validator/block_builder.go b/beacon/validator/block_builder.go index 949f343431..a80c4ec3a4 100644 --- a/beacon/validator/block_builder.go +++ b/beacon/validator/block_builder.go @@ -317,35 +317,45 @@ func (s *Service) buildBlockBody( // Set the KZG commitments on the block body. body.SetBlobKzgCommitments(blobsBundle.GetCommitments()) - // Dequeue deposits from the state. - depositIndex, err := st.GetEth1DepositIndex() - if err != nil { - return fmt.Errorf("failed loading eth1 deposit index: %w", err) - } + // Before Electra1, deposits are processed from the beacon block body directly. + if version.IsBefore(body.GetForkVersion(), version.Electra1()) { + // Dequeue deposits from the state. + depositIndex, err := st.GetEth1DepositIndex() + if err != nil { + return fmt.Errorf("failed loading eth1 deposit index: %w", err) + } - // Grab all previous deposits from genesis up to the current index + max deposits per block. - deposits, localDepositRoot, err := s.sb.DepositStore().GetDepositsByIndex( - ctx, - constants.FirstDepositIndex, - depositIndex+s.chainSpec.MaxDepositsPerBlock(), - ) - if err != nil { - return err - } - if uint64(len(deposits)) < depositIndex { - return errors.Wrapf(ErrDepositStoreIncomplete, - "all historical deposits not available, expected: %d, got: %d", - depositIndex, len(deposits), + // Grab all previous deposits from genesis up to the current index + max deposits per block. + deposits, localDepositRoot, err := s.sb.DepositStore().GetDepositsByIndex( + ctx, + constants.FirstDepositIndex, + depositIndex+s.chainSpec.MaxDepositsPerBlock(), + ) + if err != nil { + return err + } + if uint64(len(deposits)) < depositIndex { + return errors.Wrapf(ErrDepositStoreIncomplete, + "all historical deposits not available, expected: %d, got: %d", + depositIndex, len(deposits), + ) + } + if uint64(len(deposits)) < depositIndex { + return errors.Wrapf(ErrDepositStoreIncomplete, + "all historical deposits not available, expected: %d, got: %d", + depositIndex, len(deposits), + ) + } + + s.logger.Info( + "Building block body with local deposits", + "start_index", depositIndex, "num_deposits", uint64(len(deposits))-depositIndex, ) - } - s.logger.Info( - "Building block body with local deposits", - "start_index", depositIndex, "num_deposits", uint64(len(deposits))-depositIndex, - ) - eth1Data := ctypes.NewEth1Data(localDepositRoot) - body.SetEth1Data(eth1Data) - body.SetDeposits(deposits[depositIndex:]) + eth1Data := ctypes.NewEth1Data(localDepositRoot) + body.SetEth1Data(eth1Data) + body.SetDeposits(deposits[depositIndex:]) + } // Set the graffiti on the block body. sizedGraffiti := bytes.ExtendToSize([]byte(s.cfg.Graffiti), bytes.B32Size) diff --git a/consensus/cometbft/service/finalize_block.go b/consensus/cometbft/service/finalize_block.go index a04f5ea48f..d8d880653d 100644 --- a/consensus/cometbft/service/finalize_block.go +++ b/consensus/cometbft/service/finalize_block.go @@ -130,7 +130,6 @@ func (s *Service) finalizeBlock( return s.calculateFinalizeBlockResponse(req, valUpdates) } -//nolint:lll // long message on one line for readability. func (s *Service) nextBlockDelay(req *cmtabci.FinalizeBlockRequest) time.Duration { // c0. SBT is not enabled => use the old block delay. if s.cmtConsensusParams.Feature.SBTEnableHeight <= 0 { @@ -170,8 +169,13 @@ func (s *Service) nextBlockDelay(req *cmtabci.FinalizeBlockRequest) time.Duratio // // Looks like we've skipped SBTEnableHeight (probably restoring from the // snapshot) => panic. - panic(fmt.Sprintf("nil block delay at height %d past SBTEnableHeight %d. This is only possible w/ statesync, which is not supported by SBT atm", - req.Height, s.cmtConsensusParams.Feature.SBTEnableHeight)) + panic( + fmt.Sprintf( + "nil block delay at height %d past SBTEnableHeight %d. This is only possible w/ statesync, which is not supported by SBT atm", + req.Height, + s.cmtConsensusParams.Feature.SBTEnableHeight, + ), + ) } // workingHash gets the apphash that will be finalized in commit. diff --git a/kurtosis/src/networks/kurtosis-devnet/network-configs/genesis.json.template b/kurtosis/src/networks/kurtosis-devnet/network-configs/genesis.json.template index f1c9a759d1..ebba2c9ea8 100644 --- a/kurtosis/src/networks/kurtosis-devnet/network-configs/genesis.json.template +++ b/kurtosis/src/networks/kurtosis-devnet/network-configs/genesis.json.template @@ -35,6 +35,7 @@ "baseFeeUpdateFraction": 3338477 } }, + "depositContractAddress": "0x4242424242424242424242424242424242424242", "berachain": { "prague1": { "time": 0, diff --git a/state-transition/core/state_processor_forks.go b/state-transition/core/state_processor_forks.go index 1159224d83..849733a54b 100644 --- a/state-transition/core/state_processor_forks.go +++ b/state-transition/core/state_processor_forks.go @@ -260,7 +260,11 @@ func (sp *StateProcessor) logElectra1Fork( + ⛓️ current beacon epoch: %d ⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️⏭️ +<<<<<<< HEAD + +======= +>>>>>>> cb4e9ea4c1d2459d6a98fffd433c65aa581c15e8 `, version.Name(previousVersion), previousVersion.String(), sp.cs.Electra1ForkTime(), diff --git a/state-transition/core/state_processor_staking.go b/state-transition/core/state_processor_staking.go index 41cc4437d8..3cb99b10c2 100644 --- a/state-transition/core/state_processor_staking.go +++ b/state-transition/core/state_processor_staking.go @@ -37,50 +37,75 @@ func (sp *StateProcessor) processOperations( st *state.StateDB, blk *ctypes.BeaconBlock, ) error { - // Verify that outstanding deposits are processed up to the maximum number of deposits. - // - // Unlike Eth 2.0 specs we don't check that - // `len(body.deposits) == min(MAX_DEPOSITS, state.eth1_data.deposit_count - state.eth1_deposit_index)` - deposits := blk.GetBody().GetDeposits() - if uint64(len(deposits)) > sp.cs.MaxDepositsPerBlock() { - return errors.Wrapf( - ErrExceedsBlockDepositLimit, "expected: %d, got: %d", - sp.cs.MaxDepositsPerBlock(), len(deposits), - ) + var ( + requests *ctypes.ExecutionRequests + err error + ) + if version.EqualsOrIsAfter(blk.GetForkVersion(), version.Electra()) { + // Validators increase/decrease stake through execution requests starting in Electra. + requests, err = blk.GetBody().GetExecutionRequests() + if err != nil { + return err + } } - // Instead we directly compare block deposits with our local store ones. - if err := ValidateNonGenesisDeposits( - ctx.ConsensusCtx(), - st, - sp.ds, - sp.cs.MaxDepositsPerBlock(), - deposits, - blk.GetBody().GetEth1Data().DepositRoot, - ); err != nil { - return err - } + var deposits []*ctypes.Deposit + if version.IsBefore(blk.GetForkVersion(), version.Electra1()) { + // Before Electra1 however, deposits are taken from the beacon block body directly. + // + // Verify that outstanding deposits are processed up to the maximum number of deposits. + // Unlike Ethereum 2.0 specs, we don't check that + // `len(body.deposits) == min(MAX_DEPOSITS, state.eth1_data.deposit_count - state.eth1_deposit_index)`. + deposits = blk.GetBody().GetDeposits() + if uint64(len(deposits)) > sp.cs.MaxDepositsPerBlock() { + return errors.Wrapf( + ErrExceedsBlockDepositLimit, "expected: %d, got: %d", + sp.cs.MaxDepositsPerBlock(), len(deposits), + ) + } - for _, dep := range deposits { - if err := sp.processDeposit(st, dep); err != nil { + // Instead, we directly compare block deposits with our local store ones. + if err = ValidateNonGenesisDeposits( + ctx.ConsensusCtx(), + st, + sp.ds, + sp.cs.MaxDepositsPerBlock(), + deposits, + blk.GetBody().GetEth1Data().DepositRoot, + ); err != nil { return err } + } else if requests != nil { + // Starting in Electra1, EIP-6110 style deposit requests are used. + deposits = requests.Deposits } - if version.EqualsOrIsAfter(blk.GetForkVersion(), version.Electra()) { - // After Electra, validators can request withdrawals through execution requests which must be handled. - requests, err := blk.GetBody().GetExecutionRequests() - if err != nil { + // Process the deposits. + for _, dep := range deposits { + if err = sp.processDeposit(st, dep); err != nil { return err } + } + + // Starting in Electra, process the EIP-7002 withdrawal requests. + if requests != nil { for _, withdrawal := range requests.Withdrawals { - if withdrawErr := sp.processWithdrawalRequest(st, withdrawal); withdrawErr != nil { - return withdrawErr + if err = sp.processWithdrawalRequest(st, withdrawal); err != nil { + return err } } } - return st.SetEth1Data(blk.GetBody().Eth1Data) + // Set the eth1 data to state. + var eth1Data *ctypes.Eth1Data + if version.IsBefore(blk.GetForkVersion(), version.Electra1()) { + // Before Electra1 however, the eth1 data is applied to state from the beacon block body. + eth1Data = blk.GetBody().Eth1Data + } else { + // Starting in Electra1, the eth1 data is unused so set in state as empty. + eth1Data = ctypes.NewEmptyEth1Data() + } + return st.SetEth1Data(eth1Data) } // processDeposit processes the deposit and ensures it matches the local state. diff --git a/state-transition/core/validation_deposits.go b/state-transition/core/validation_deposits.go index 1838450efe..b160072899 100644 --- a/state-transition/core/validation_deposits.go +++ b/state-transition/core/validation_deposits.go @@ -70,6 +70,7 @@ func validateGenesisDeposits( return nil } +// NOTE: only used before Electra1. func ValidateNonGenesisDeposits( ctx context.Context, st *statedb.StateDB, diff --git a/testing/files/eth-genesis.json b/testing/files/eth-genesis.json index ee6f627e09..d2822ab465 100644 --- a/testing/files/eth-genesis.json +++ b/testing/files/eth-genesis.json @@ -35,6 +35,7 @@ "baseFeeUpdateFraction": 3338477 } }, + "depositContractAddress": "0x4242424242424242424242424242424242424242", "berachain": { "prague1": { "time": 0, diff --git a/testing/networks/80069/eth-genesis.json b/testing/networks/80069/eth-genesis.json index 4e73032552..3809978a47 100644 --- a/testing/networks/80069/eth-genesis.json +++ b/testing/networks/80069/eth-genesis.json @@ -95,6 +95,7 @@ "baseFeeUpdateFraction": 3338477 } }, + "depositContractAddress": "0x4242424242424242424242424242424242424242", "berachain": { "prague1": { "time": 1754496000, diff --git a/testing/networks/80094/eth-genesis.json b/testing/networks/80094/eth-genesis.json index bbc3897586..0662e8983a 100644 --- a/testing/networks/80094/eth-genesis.json +++ b/testing/networks/80094/eth-genesis.json @@ -34,6 +34,7 @@ "baseFeeUpdateFraction": 3338477 } }, + "depositContractAddress": "0x4242424242424242424242424242424242424242", "berachain": { "prague1": { "time": 1756915200,