diff --git a/beacon/blockchain/deposit.go b/beacon/blockchain/deposit.go index be3a595ae0..3709f639f4 100644 --- a/beacon/blockchain/deposit.go +++ b/beacon/blockchain/deposit.go @@ -35,21 +35,22 @@ const defaultRetryInterval = 20 * time.Second func (s *Service[ _, _, _, _, _, _, _, _, _, ]) depositFetcher(ctx context.Context, blockNum math.U64) { - if blockNum <= s.eth1FollowDistance { + eth1FollowDistance := math.U64(s.chainSpec.Eth1FollowDistance()) + if blockNum <= eth1FollowDistance { s.logger.Info( "depositFetcher, nothing to fetch", - "block num", blockNum, "eth1FollowDistance", s.eth1FollowDistance, + "block_num", blockNum, "eth1_follow_distance", eth1FollowDistance, ) return } - s.fetchAndStoreDeposits(ctx, blockNum-s.eth1FollowDistance) + s.fetchAndStoreDeposits(ctx, blockNum-eth1FollowDistance) } func (s *Service[ _, _, _, _, _, _, _, _, _, ]) fetchAndStoreDeposits(ctx context.Context, blockNum math.U64) { - deposits, _, err := s.depositContract.ReadDeposits(ctx, blockNum) + deposits, indexes, executionHash, err := s.depositContract.ReadDeposits(ctx, blockNum) if err != nil { s.logger.Error("Failed to read deposits", "error", err) s.metrics.sink.IncrementCounter( @@ -67,7 +68,9 @@ func (s *Service[ ) } - if err = s.depositStore.EnqueueDepositDatas(deposits); err != nil { + if err = s.depositStore.EnqueueDepositDatas( + deposits, indexes, executionHash, blockNum, + ); err != nil { s.logger.Error("Failed to store deposits", "error", err) s.failedBlocksMu.Lock() s.failedBlocks[blockNum] = struct{}{} diff --git a/beacon/blockchain/init_chain.go b/beacon/blockchain/init_chain.go index e10eb85bc7..88650e61cb 100644 --- a/beacon/blockchain/init_chain.go +++ b/beacon/blockchain/init_chain.go @@ -41,15 +41,36 @@ func (s *Service[ return nil, err } - genesisDeposits := genesisData.GetDepositDatas() + // Store the genesis deposits. + genesisDepositDatas := genesisData.GetDepositDatas() + genesisDepositIndexes := make([]uint64, len(genesisDepositDatas)) + for i := range genesisDepositDatas { + genesisDepositIndexes[i] = uint64(i) + } genesisExecutionPayloadHeader := genesisData.GetExecutionPayloadHeader() - if err := s.depositStore.EnqueueDepositDatas(genesisDeposits); err != nil { + if err := s.depositStore.EnqueueDepositDatas( + genesisDepositDatas, + genesisDepositIndexes, + genesisExecutionPayloadHeader.BlockHash, + genesisExecutionPayloadHeader.Number, + ); err != nil { s.logger.Error("Failed to store genesis deposits", "error", err) return nil, err } + // Get the genesis deposits, with their proofs. + genesisDeposits, genesisDepositsRoot, err := s.depositStore.GetDepositsByIndex( + 0, uint64(len(genesisDepositDatas)), + ) + if err != nil { + s.logger.Error("Failed to retrieve genesis deposits with proofs", "error", err) + return nil, err + } + return s.stateProcessor.InitializePreminedBeaconStateFromEth1( s.storageBackend.StateFromContext(ctx), + genesisDeposits, + genesisDepositsRoot, genesisExecutionPayloadHeader, genesisData.GetForkVersion(), ) diff --git a/beacon/blockchain/pruning.go b/beacon/blockchain/pruning.go index 5bf4273e0c..d55d9369bd 100644 --- a/beacon/blockchain/pruning.go +++ b/beacon/blockchain/pruning.go @@ -22,7 +22,6 @@ package blockchain import ( "github.com/berachain/beacon-kit/chain-spec/chain" - ctypes "github.com/berachain/beacon-kit/consensus-types/types" ) func (s *Service[ @@ -35,35 +34,20 @@ func (s *Service[ return err } - // prune deposit store - start, end = depositPruneRangeFn(beaconBlk.GetBody().GetDepositDatas(), s.chainSpec) - err = s.depositStore.Prune(start, end) - - if err != nil { + // Delete the deposits of the block previous to the one we just stored. + // TODO: remove this here. Pruning should be invoked when we know for sure that + // the deposits were included in a block. + finalizedEthBlock := beaconBlk.GetBody().GetExecutionPayload().Number.Unwrap() + height := finalizedEthBlock - s.chainSpec.Eth1FollowDistance() - 1 + if err = s.depositStore.Prune(height); err != nil { return err } return nil } -// depositPruneRangeFn effectively prunes at most the max deposits per block -// behind the last included deposit. -func depositPruneRangeFn(deposits []*ctypes.DepositData, cs chain.ChainSpec) (uint64, uint64) { - if len(deposits) == 0 || cs.MaxDepositsPerBlock() == 0 { - return 0, 0 - } - - index := deposits[len(deposits)-1].GetIndex().Unwrap() - - if index < cs.MaxDepositsPerBlock() { - return 0, index - } - return index - cs.MaxDepositsPerBlock(), index -} - //nolint:unparam // this is ok -func availabilityPruneRangeFn( - slot uint64, cs chain.ChainSpec) (uint64, uint64) { +func availabilityPruneRangeFn(slot uint64, cs chain.ChainSpec) (uint64, uint64) { window := cs.MinEpochsForBlobsSidecarsRequest() * cs.SlotsPerEpoch() if slot < window { return 0, 0 diff --git a/beacon/blockchain/service.go b/beacon/blockchain/service.go index 7da37b01a4..473983012b 100644 --- a/beacon/blockchain/service.go +++ b/beacon/blockchain/service.go @@ -59,8 +59,6 @@ type Service[ // depositContract is the contract interface for interacting with the // deposit contract. depositContract deposit.Contract - // eth1FollowDistance is the follow distance for Ethereum 1.0 blocks. - eth1FollowDistance math.U64 // failedBlocksMu protects failedBlocks for concurrent access. failedBlocksMu sync.RWMutex // failedBlocks is a map of blocks that failed to be processed @@ -105,7 +103,6 @@ func NewService[ blockStore BlockStoreT, depositStore deposit.Store, depositContract deposit.Contract, - eth1FollowDistance math.U64, logger log.Logger, chainSpec chain.ChainSpec, executionEngine ExecutionEngine[PayloadAttributesT], @@ -127,7 +124,6 @@ func NewService[ blockStore: blockStore, depositStore: depositStore, depositContract: depositContract, - eth1FollowDistance: eth1FollowDistance, failedBlocks: make(map[math.Slot]struct{}), logger: logger, chainSpec: chainSpec, diff --git a/beacon/blockchain/types.go b/beacon/blockchain/types.go index 808cdd1c79..48f02de8fc 100644 --- a/beacon/blockchain/types.go +++ b/beacon/blockchain/types.go @@ -180,7 +180,7 @@ type StateProcessor[ // state // from the eth1 deposits. InitializePreminedBeaconStateFromEth1( - BeaconStateT, *ctypes.ExecutionPayloadHeader, common.Version, + BeaconStateT, ctypes.Deposits, common.Root, *ctypes.ExecutionPayloadHeader, common.Version, ) (transition.ValidatorUpdates, error) // ProcessSlots processes the state transition for a range of slots. ProcessSlots( diff --git a/beacon/validator/errors.go b/beacon/validator/errors.go index fbd003b98f..a037d9ba6a 100644 --- a/beacon/validator/errors.go +++ b/beacon/validator/errors.go @@ -23,7 +23,6 @@ package validator import "github.com/berachain/beacon-kit/errors" var ( - // ErrNilPayload is an error for when there is no payload // in a beacon block. ErrNilPayload = errors.New("nil payload in beacon block") diff --git a/cli/commands/genesis/collect.go b/cli/commands/genesis/collect.go index f99fed4a6f..39b248b67e 100644 --- a/cli/commands/genesis/collect.go +++ b/cli/commands/genesis/collect.go @@ -59,10 +59,10 @@ func CollectGenesisDepositsCmd() *cobra.Command { return err } - var deposits []*types.DepositData - if deposits, err = CollectValidatorJSONFiles( - filepath.Join(config.RootDir, "config", "premined-deposits"), - appGenesis, + genesisInfo := &types.Genesis{} + + if genesisInfo.DepositDatas, err = CollectValidatorJSONFiles( + filepath.Join(config.RootDir, "config", "premined-deposits"), appGenesis, ); err != nil { return errors.Wrap( err, @@ -70,20 +70,10 @@ func CollectGenesisDepositsCmd() *cobra.Command { ) } - genesisInfo := &types.Genesis{} - - if err = json.Unmarshal( - appGenesisState["beacon"], genesisInfo, - ); err != nil { + if err = json.Unmarshal(appGenesisState["beacon"], genesisInfo); err != nil { return errors.Wrap(err, "failed to unmarshal beacon genesis") } - for i, deposit := range deposits { - //#nosec:G701 // won't realistically overflow. - deposit.Index = uint64(i) - genesisInfo.DepositDatas = append(genesisInfo.DepositDatas, deposit) - } - appGenesisState["beacon"], err = json.Marshal(genesisInfo) if err != nil { return errors.Wrap(err, "failed to marshal beacon genesis") diff --git a/cmd/beacond/defaults.go b/cmd/beacond/defaults.go index fae7afb4f6..4689053516 100644 --- a/cmd/beacond/defaults.go +++ b/cmd/beacond/defaults.go @@ -78,9 +78,7 @@ func DefaultComponents() []any { ], components.ProvideSidecarFactory[*BeaconBlock], components.ProvideStateProcessor[ - *Logger, *BeaconBlock, - *BeaconState, *BeaconStateMarshallable, *DepositStore, - *KVStore, + *Logger, *BeaconBlock, *BeaconState, *BeaconStateMarshallable, *DepositStore, *KVStore, ], components.ProvideKVStore, components.ProvideStorageBackend[ diff --git a/cmd/beacond/types.go b/cmd/beacond/types.go index 13b7eb8689..451dae951d 100644 --- a/cmd/beacond/types.go +++ b/cmd/beacond/types.go @@ -253,7 +253,4 @@ type ( type ( // DAPruner is a type alias for the DA pruner. DAPruner = pruner.Pruner[*IndexDB] - - // DepositPruner is a type alias for the deposit pruner. - DepositPruner = pruner.Pruner[*DepositStore] ) diff --git a/consensus-types/types/block_test.go b/consensus-types/types/block_test.go index a2f8ed01d5..e9e95b563e 100644 --- a/consensus-types/types/block_test.go +++ b/consensus-types/types/block_test.go @@ -58,9 +58,7 @@ func generateValidBeaconBlock() *types.BeaconBlock { Eth1Data: &types.Eth1Data{}, Deposits: []*types.Deposit{ { - Data: &types.DepositData{ - Index: 1, - }, + Data: &types.DepositData{Amount: 1}, }, }, BlobKzgCommitments: []eip4844.KZGCommitment{ diff --git a/consensus-types/types/deposit_data.go b/consensus-types/types/deposit_data.go index 33d7ba83c0..22e5dad5db 100644 --- a/consensus-types/types/deposit_data.go +++ b/consensus-types/types/deposit_data.go @@ -30,7 +30,7 @@ import ( ) // DepositDataSize is the size of the SSZ encoding of a DepositData. -const DepositDataSize = 192 // 48 + 32 + 8 + 96 + 8 +const DepositDataSize = 184 // 48 + 32 + 8 + 96 // Compile-time assertions to ensure Deposit implements necessary interfaces. var ( @@ -50,8 +50,6 @@ type DepositData struct { Amount math.Gwei `json:"amount"` // Signature of the deposit data. Signature crypto.BLSSignature `json:"signature"` - // Index of the deposit in the deposit contract. - Index uint64 `json:"index"` } // NewDepositData creates a new DepositData instance. @@ -60,14 +58,12 @@ func NewDepositData( credentials WithdrawalCredentials, amount math.Gwei, signature crypto.BLSSignature, - index uint64, ) *DepositData { return &DepositData{ Pubkey: pubkey, Credentials: credentials, Amount: amount, Signature: signature, - Index: index, } } @@ -82,10 +78,9 @@ func (d *DepositData) New( credentials WithdrawalCredentials, amount math.Gwei, signature crypto.BLSSignature, - index uint64, ) *DepositData { return NewDepositData( - pubkey, credentials, amount, signature, index, + pubkey, credentials, amount, signature, ) } @@ -117,7 +112,6 @@ func (d *DepositData) DefineSSZ(c *ssz.Codec) { ssz.DefineStaticBytes(c, &d.Credentials) ssz.DefineUint64(c, &d.Amount) ssz.DefineStaticBytes(c, &d.Signature) - ssz.DefineUint64(c, &d.Index) } // MarshalSSZ marshals the DepositData object to SSZ format. @@ -171,9 +165,6 @@ func (d *DepositData) HashTreeRootWith(hh fastssz.HashWalker) error { // Field (3) 'Signature' hh.PutBytes(d.Signature[:]) - // Field (4) 'Index' - hh.PutUint64(d.Index) - hh.Merkleize(indx) return nil } @@ -187,15 +178,6 @@ func (d *DepositData) GetTree() (*fastssz.Node, error) { /* Getters and Setters */ /* -------------------------------------------------------------------------- */ -// Equals returns true if the DepositData is equal to the other. -func (d *DepositData) Equals(rhs *DepositData) bool { - return d.Pubkey == rhs.Pubkey && - d.Credentials == rhs.Credentials && - d.Amount == rhs.Amount && - d.Signature == rhs.Signature && - d.Index == rhs.Index -} - // GetAmount returns the deposit amount in gwei. func (d *DepositData) GetAmount() math.Gwei { return d.Amount @@ -206,11 +188,6 @@ func (d *DepositData) GetPubkey() crypto.BLSPubkey { return d.Pubkey } -// GetIndex returns the index of the deposit in the deposit contract. -func (d *DepositData) GetIndex() math.U64 { - return math.U64(d.Index) -} - // GetSignature returns the signature of the deposit data. func (d *DepositData) GetSignature() crypto.BLSSignature { return d.Signature diff --git a/consensus-types/types/deposit_data_test.go b/consensus-types/types/deposit_data_test.go index add3259097..d2bb7c15c8 100644 --- a/consensus-types/types/deposit_data_test.go +++ b/consensus-types/types/deposit_data_test.go @@ -39,14 +39,12 @@ func generateValidDeposit() *types.DepositData { var signature crypto.BLSSignature var credentials types.WithdrawalCredentials amount := math.Gwei(32) - index := uint64(1) return &types.DepositData{ Pubkey: pubKey, Credentials: credentials, Amount: amount, Signature: signature, - Index: index, } } @@ -57,7 +55,6 @@ func TestDeposit_New(t *testing.T) { deposit.Credentials, deposit.Amount, deposit.Signature, - deposit.Index, ) require.Equal(t, deposit, newDeposit) } @@ -150,5 +147,4 @@ func TestDeposit_Getters(t *testing.T) { require.Equal(t, deposit.Credentials, deposit.GetWithdrawalCredentials()) require.Equal(t, deposit.Amount, deposit.GetAmount()) require.Equal(t, deposit.Signature, deposit.GetSignature()) - require.Equal(t, math.U64(deposit.Index), deposit.GetIndex()) } diff --git a/execution/deposit/contract.go b/execution/deposit/contract.go index 32b1c86ea3..f8cb5389db 100644 --- a/execution/deposit/contract.go +++ b/execution/deposit/contract.go @@ -22,10 +22,9 @@ package deposit import ( "context" - "errors" - "fmt" ctypes "github.com/berachain/beacon-kit/consensus-types/types" + "github.com/berachain/beacon-kit/errors" gethprimitives "github.com/berachain/beacon-kit/geth-primitives" "github.com/berachain/beacon-kit/geth-primitives/bind" "github.com/berachain/beacon-kit/geth-primitives/deposit" @@ -68,7 +67,7 @@ func NewWrappedDepositContract( // ReadDeposits reads deposits from the deposit contract. func (dc *WrappedDepositContract) ReadDeposits( ctx context.Context, blkNum math.U64, -) ([]*ctypes.DepositData, common.ExecutionHash, error) { +) ([]*ctypes.DepositData, []uint64, common.ExecutionHash, error) { logs, err := dc.FilterDeposit( &bind.FilterOpts{ Context: ctx, @@ -77,12 +76,13 @@ func (dc *WrappedDepositContract) ReadDeposits( }, ) if err != nil { - return nil, common.ExecutionHash{}, err + return nil, nil, common.ExecutionHash{}, err } var ( blockNumStr = blkNum.Base10() deposits = make([]*ctypes.DepositData, 0) + indexes = make([]uint64, 0) blockHash common.ExecutionHash ) for logs.Next() { @@ -93,29 +93,31 @@ func (dc *WrappedDepositContract) ReadDeposits( ) pubKey, err = bytes.ToBytes48(logs.Event.Pubkey) if err != nil { - return nil, blockHash, fmt.Errorf("failed reading pub key: %w", err) + return nil, nil, blockHash, errors.Wrap(err, "failed reading pub key") } cred, err = bytes.ToBytes32(logs.Event.Credentials) if err != nil { - return nil, blockHash, fmt.Errorf("failed reading credentials: %w", err) + return nil, nil, blockHash, errors.Wrap(err, "failed reading credentials") } sign, err = bytes.ToBytes96(logs.Event.Signature) if err != nil { - return nil, blockHash, fmt.Errorf("failed reading signature: %w", err) + return nil, nil, blockHash, errors.Wrap(err, "failed reading signature") } deposits = append(deposits, ctypes.NewDepositData( - pubKey, ctypes.WithdrawalCredentials(cred), math.U64(logs.Event.Amount), sign, logs.Event.Index, + pubKey, ctypes.WithdrawalCredentials(cred), math.U64(logs.Event.Amount), sign, )) + indexes = append(indexes, logs.Event.Index) if blockHash == (common.ExecutionHash{}) { blockHash = common.ExecutionHash(logs.Event.Raw.BlockHash) } dc.telemetrySink.IncrementCounter( - "beacon_kit.execution.deposits_read", "block_num", blockNumStr, "block_hash", blockHash.Hex(), + "beacon_kit.execution.deposits_read", + "block_num", blockNumStr, "block_hash", blockHash.Hex(), ) } - return deposits, blockHash, nil + return deposits, indexes, blockHash, nil } diff --git a/execution/deposit/types.go b/execution/deposit/types.go index 92e6e614df..a9f0b850af 100644 --- a/execution/deposit/types.go +++ b/execution/deposit/types.go @@ -33,15 +33,20 @@ type Contract interface { // ReadDeposits reads deposits from the deposit contract. ReadDeposits( ctx context.Context, blockNumber math.U64, - ) ([]*ctypes.DepositData, common.ExecutionHash, error) + ) ([]*ctypes.DepositData, []uint64, common.ExecutionHash, error) } // Store defines the interface for managing deposit operations. type Store interface { - // Prune prunes the deposit store of [start, end) - Prune(index uint64, numPrune uint64) error - // EnqueueDepositDatas adds a list of deposits to the deposit store. - EnqueueDepositDatas(deposits []*ctypes.DepositData) error + // Prune prunes the deposit store of the given height. + Prune(height uint64) error + // EnqueueDepositDatas adds a list of deposits to the deposit store for a given EL block. + EnqueueDepositDatas( + depositDatas []*ctypes.DepositData, + indexes []uint64, + executionHash common.ExecutionHash, + executionNumber math.U64, + ) error // GetDepositsByIndex gets a list of deposits from the deposit store. GetDepositsByIndex(startIndex, numView uint64) (ctypes.Deposits, common.Root, error) } diff --git a/node-api/backend/types.go b/node-api/backend/types.go index c10d631679..f2c1d6b8ef 100644 --- a/node-api/backend/types.go +++ b/node-api/backend/types.go @@ -64,10 +64,15 @@ type BlockStore[BeaconBlockT any] interface { type DepositStore interface { // GetDepositsByIndex returns `numView` or less deposits. GetDepositsByIndex(startIndex uint64, numView uint64) (ctypes.Deposits, common.Root, error) - // Prune prunes the deposit store of [start, end) - Prune(start, end uint64) error - // EnqueueDepositDatas adds a list of deposits to the deposit store. - EnqueueDepositDatas(deposits []*ctypes.DepositData) error + // Prune prunes the deposit store of the given height. + Prune(height uint64) error + // EnqueueDepositDatas adds a list of deposits to the deposit store for a given EL block. + EnqueueDepositDatas( + depositDatas []*ctypes.DepositData, + indexes []uint64, + executionHash common.ExecutionHash, + executionNumber math.U64, + ) error } // Node is the interface for a node. diff --git a/node-core/components/chain_service.go b/node-core/components/chain_service.go index 351aa25f8f..ca121e56b6 100644 --- a/node-core/components/chain_service.go +++ b/node-core/components/chain_service.go @@ -33,7 +33,6 @@ import ( "github.com/berachain/beacon-kit/log" "github.com/berachain/beacon-kit/node-core/components/metrics" "github.com/berachain/beacon-kit/primitives/crypto" - "github.com/berachain/beacon-kit/primitives/math" "github.com/cosmos/cosmos-sdk/client/flags" "github.com/spf13/cast" ) @@ -112,7 +111,6 @@ func ProvideChainService[ in.BlockStore, in.DepositStore, in.BeaconDepositContract, - math.U64(in.ChainSpec.Eth1FollowDistance()), in.Logger.With("service", "blockchain"), in.ChainSpec, in.ExecutionEngine, diff --git a/node-core/components/deposit_store.go b/node-core/components/deposit_store.go index 4e362c25b9..c8c557a798 100644 --- a/node-core/components/deposit_store.go +++ b/node-core/components/deposit_store.go @@ -22,29 +22,15 @@ package components import ( "cosmossdk.io/depinject" - storev2 "cosmossdk.io/store/v2/db" - "github.com/berachain/beacon-kit/config" - "github.com/berachain/beacon-kit/node-core/components/storage" "github.com/berachain/beacon-kit/storage/deposit" - "github.com/cosmos/cosmos-sdk/client/flags" - "github.com/spf13/cast" ) // DepositStoreInput is the input for the dep inject framework. type DepositStoreInput struct { depinject.In - AppOpts config.AppOptions } -// ProvideDepositStore is a function that provides the module to the -// application. -func ProvideDepositStore(in DepositStoreInput) (*deposit.Store, error) { - name := "deposits" - dir := cast.ToString(in.AppOpts.Get(flags.FlagHome)) + "/data" - kvp, err := storev2.NewDB(storev2.DBTypePebbleDB, name, dir, nil) - if err != nil { - return nil, err - } - - return deposit.NewStore(storage.NewKVStoreProvider(kvp)), nil +// ProvideDepositStore is a function that provides the module to the application. +func ProvideDepositStore(in DepositStoreInput) *deposit.Store { + return deposit.NewStore() } diff --git a/node-core/components/interfaces.go b/node-core/components/interfaces.go index 2c38b27ee7..57e66376d3 100644 --- a/node-core/components/interfaces.go +++ b/node-core/components/interfaces.go @@ -368,10 +368,15 @@ type ( DepositStore interface { // GetDepositsByIndex returns `numView` or less deposits. GetDepositsByIndex(startIndex, numView uint64) (ctypes.Deposits, common.Root, error) - // Prune prunes the deposit store of [start, end) - Prune(start, end uint64) error - // EnqueueDepositDatas adds a list of deposits to the deposit store. - EnqueueDepositDatas(deposits []*ctypes.DepositData) error + // Prune prunes the deposit store of the given height. + Prune(height uint64) error + // EnqueueDepositDatas adds a list of deposits to the deposit store for a given EL block. + EnqueueDepositDatas( + depositDatas []*ctypes.DepositData, + indexes []uint64, + executionHash common.ExecutionHash, + executionNumber math.U64, + ) error } // Genesis is the interface for the genesis. @@ -456,7 +461,8 @@ type ( // InitializePreminedBeaconStateFromEth1 initializes the premined beacon // state from the eth1 deposits. InitializePreminedBeaconStateFromEth1( - BeaconStateT, *ctypes.ExecutionPayloadHeader, common.Version, + BeaconStateT, ctypes.Deposits, common.Root, + *ctypes.ExecutionPayloadHeader, common.Version, ) (transition.ValidatorUpdates, error) // ProcessSlot processes the slot. ProcessSlots( diff --git a/state-transition/core/core_test.go b/state-transition/core/core_test.go index 8df1cd4dbb..d6dd54c524 100644 --- a/state-transition/core/core_test.go +++ b/state-transition/core/core_test.go @@ -111,7 +111,7 @@ func initTestStores() (*beacondb.KVStore, *deposit.Store, error) { testStoreService, testCodec, ), - deposit.NewStore(testStoreService), + deposit.NewStore(), nil } diff --git a/state-transition/core/state_processor_genesis.go b/state-transition/core/state_processor_genesis.go index 79b3040005..8707c8fbb9 100644 --- a/state-transition/core/state_processor_genesis.go +++ b/state-transition/core/state_processor_genesis.go @@ -21,10 +21,8 @@ package core import ( - "fmt" - - "github.com/berachain/beacon-kit/config/spec" ctypes "github.com/berachain/beacon-kit/consensus-types/types" + "github.com/berachain/beacon-kit/errors" "github.com/berachain/beacon-kit/primitives/common" "github.com/berachain/beacon-kit/primitives/constants" "github.com/berachain/beacon-kit/primitives/math" @@ -40,6 +38,8 @@ func (sp *StateProcessor[ _, BeaconStateT, _, _, ]) InitializePreminedBeaconStateFromEth1( st BeaconStateT, + deposits ctypes.Deposits, + depositsRoot common.Root, execPayloadHeader *ctypes.ExecutionPayloadHeader, genesisVersion common.Version, ) (transition.ValidatorUpdates, error) { @@ -56,6 +56,12 @@ func (sp *StateProcessor[ var blkBody *ctypes.BeaconBlockBody blkBody = blkBody.Empty(version.ToUint32(genesisVersion)) + var eth1Data *ctypes.Eth1Data + eth1Data = eth1Data.New(depositsRoot, math.U64(len(deposits)), execPayloadHeader.GetBlockHash()) + if err := st.SetEth1Data(eth1Data); err != nil { + return nil, err + } + var blkHeader *ctypes.BeaconBlockHeader blkHeader = blkHeader.New( 0, // slot @@ -77,25 +83,8 @@ func (sp *StateProcessor[ } } - // Get each genesis deposit from the deposit store, with their proofs. - for i := range sp.cs.ValidatorSetCap() { - deposits, depositRoot, err := sp.ds.GetDepositsByIndex(i, 1) - if err != nil { - return nil, err - } - if len(deposits) == 0 { - break - } - - var eth1Data *ctypes.Eth1Data - eth1Data = eth1Data.New( - depositRoot, math.U64(i+1), execPayloadHeader.GetBlockHash(), - ) - if err = st.SetEth1Data(eth1Data); err != nil { - return nil, err - } - - if err = sp.processDeposit(st, deposits[0]); err != nil { + for _, deposit := range deposits { + if err := sp.processDeposit(st, deposit); err != nil { return nil, err } } @@ -152,40 +141,28 @@ func (sp *StateProcessor[ ]) processGenesisActivation( st BeaconStateT, ) error { - switch { - case sp.cs.DepositEth1ChainID() == spec.BartioChainID: - // nothing to do - return nil - case sp.cs.DepositEth1ChainID() == spec.BoonetEth1ChainID: - // nothing to do - return nil - default: - vals, err := st.GetValidators() + vals, err := st.GetValidators() + if err != nil { + return errors.Wrap(err, "genesis activation, failed listing validators") + } + minEffectiveBalance := math.Gwei( + sp.cs.EjectionBalance() + sp.cs.EffectiveBalanceIncrement(), + ) + + var idx math.ValidatorIndex + for _, val := range vals { + if val.GetEffectiveBalance() < minEffectiveBalance { + continue + } + val.SetActivationEligibilityEpoch(0) + val.SetActivationEpoch(0) + idx, err = st.ValidatorIndexByPubkey(val.GetPubkey()) if err != nil { - return fmt.Errorf( - "genesis activation, failed listing validators: %w", - err, - ) + return err } - minEffectiveBalance := math.Gwei( - sp.cs.EjectionBalance() + sp.cs.EffectiveBalanceIncrement(), - ) - - var idx math.ValidatorIndex - for _, val := range vals { - if val.GetEffectiveBalance() < minEffectiveBalance { - continue - } - val.SetActivationEligibilityEpoch(0) - val.SetActivationEpoch(0) - idx, err = st.ValidatorIndexByPubkey(val.GetPubkey()) - if err != nil { - return err - } - if err = st.UpdateValidatorAtIndex(idx, val); err != nil { - return err - } + if err = st.UpdateValidatorAtIndex(idx, val); err != nil { + return err } - return nil } + return nil } diff --git a/state-transition/core/state_processor_genesis_test.go b/state-transition/core/state_processor_genesis_test.go index c2a53786ee..be7e0ddfb2 100644 --- a/state-transition/core/state_processor_genesis_test.go +++ b/state-transition/core/state_processor_genesis_test.go @@ -52,7 +52,6 @@ func TestInitialize(t *testing.T) { Credentials: types.NewCredentialsFromExecutionAddress( common.ExecutionAddress{0x01}, ), - Index: uint64(0), }, { Pubkey: [48]byte{0x02}, @@ -60,7 +59,6 @@ func TestInitialize(t *testing.T) { Credentials: types.NewCredentialsFromExecutionAddress( common.ExecutionAddress{0x02}, ), - Index: uint64(1), }, { Pubkey: [48]byte{0x03}, @@ -68,7 +66,6 @@ func TestInitialize(t *testing.T) { Credentials: types.NewCredentialsFromExecutionAddress( common.ExecutionAddress{0x03}, ), - Index: uint64(2), }, { Pubkey: [48]byte{0x04}, @@ -76,7 +73,6 @@ func TestInitialize(t *testing.T) { Credentials: types.NewCredentialsFromExecutionAddress( common.ExecutionAddress{0x04}, ), - Index: uint64(3), }, { Pubkey: [48]byte{0x05}, @@ -84,7 +80,6 @@ func TestInitialize(t *testing.T) { Credentials: types.NewCredentialsFromExecutionAddress( common.ExecutionAddress{0x05}, ), - Index: uint64(4), }, { Pubkey: [48]byte{0x06}, @@ -92,7 +87,6 @@ func TestInitialize(t *testing.T) { Credentials: types.NewCredentialsFromExecutionAddress( common.ExecutionAddress{0x06}, ), - Index: uint64(5), }, { Pubkey: [48]byte{0x07}, @@ -100,7 +94,6 @@ func TestInitialize(t *testing.T) { Credentials: types.NewCredentialsFromExecutionAddress( common.ExecutionAddress{0x07}, ), - Index: uint64(6), }, { Pubkey: [48]byte{0x08}, @@ -108,7 +101,6 @@ func TestInitialize(t *testing.T) { Credentials: types.NewCredentialsFromExecutionAddress( common.ExecutionAddress{0x08}, ), - Index: uint64(7), }, } goodDeposits = []*types.DepositData{ @@ -123,10 +115,12 @@ func TestInitialize(t *testing.T) { ) require.NoError(t, ds.EnqueueDepositDatas(genDeposits)) + deposits, root, err := ds.GetDepositsByIndex(0, uint64(len(genDeposits))) + require.NoError(t, err) // run test genVals, err := sp.InitializePreminedBeaconStateFromEth1( - st, executionPayloadHeader, fork.CurrentVersion, + st, deposits, root, executionPayloadHeader, fork.CurrentVersion, ) require.NoError(t, err) require.Len(t, genVals, len(goodDeposits)) @@ -201,7 +195,6 @@ func TestInitializeBartio(t *testing.T) { Credentials: types.NewCredentialsFromExecutionAddress( common.ExecutionAddress{0x01}, ), - Index: uint64(0), }, { Pubkey: [48]byte{0x02}, @@ -209,7 +202,6 @@ func TestInitializeBartio(t *testing.T) { Credentials: types.NewCredentialsFromExecutionAddress( common.ExecutionAddress{0x02}, ), - Index: uint64(1), }, { Pubkey: [48]byte{0x03}, @@ -217,7 +209,6 @@ func TestInitializeBartio(t *testing.T) { Credentials: types.NewCredentialsFromExecutionAddress( common.ExecutionAddress{0x03}, ), - Index: uint64(2), }, { Pubkey: [48]byte{0x04}, @@ -225,7 +216,6 @@ func TestInitializeBartio(t *testing.T) { Credentials: types.NewCredentialsFromExecutionAddress( common.ExecutionAddress{0x04}, ), - Index: uint64(3), }, { Pubkey: [48]byte{0x05}, @@ -233,7 +223,6 @@ func TestInitializeBartio(t *testing.T) { Credentials: types.NewCredentialsFromExecutionAddress( common.ExecutionAddress{0x05}, ), - Index: uint64(4), }, { Pubkey: [48]byte{0x06}, @@ -241,7 +230,6 @@ func TestInitializeBartio(t *testing.T) { Credentials: types.NewCredentialsFromExecutionAddress( common.ExecutionAddress{0x06}, ), - Index: uint64(5), }, { Pubkey: [48]byte{0x07}, @@ -249,7 +237,6 @@ func TestInitializeBartio(t *testing.T) { Credentials: types.NewCredentialsFromExecutionAddress( common.ExecutionAddress{0x07}, ), - Index: uint64(6), }, { Pubkey: [48]byte{0x08}, @@ -257,12 +244,10 @@ func TestInitializeBartio(t *testing.T) { Credentials: types.NewCredentialsFromExecutionAddress( common.ExecutionAddress{0x08}, ), - Index: uint64(7), }, } goodDeposits = []*types.DepositData{ - genDeposits[0], genDeposits[1], genDeposits[3], - genDeposits[5], genDeposits[6], + genDeposits[0], genDeposits[1], genDeposits[3], genDeposits[5], genDeposits[6], } executionPayloadHeader = new(types.ExecutionPayloadHeader).Empty() fork = &types.Fork{ @@ -273,10 +258,12 @@ func TestInitializeBartio(t *testing.T) { ) require.NoError(t, ds.EnqueueDepositDatas(genDeposits)) + deposits, root, err := ds.GetDepositsByIndex(0, uint64(len(genDeposits))) + require.NoError(t, err) // run test genVals, err := sp.InitializePreminedBeaconStateFromEth1( - st, executionPayloadHeader, fork.CurrentVersion, + st, deposits, root, executionPayloadHeader, fork.CurrentVersion, ) require.NoError(t, err) require.Len(t, genVals, len(goodDeposits)) diff --git a/state-transition/core/state_processor_staking.go b/state-transition/core/state_processor_staking.go index f9203e8b0e..a280f4e37b 100644 --- a/state-transition/core/state_processor_staking.go +++ b/state-transition/core/state_processor_staking.go @@ -51,15 +51,21 @@ func (sp *StateProcessor[ return err } eth1Data := blk.GetBody().GetEth1Data() - if eth1Data.DepositRoot != localDepositsRoot { + if localDepositsRoot != eth1Data.DepositRoot { return errors.New("local deposit tree root does not match the block deposit tree root") } // Verify that the provided deposit count is consistent with our local view of the // deposit tree. + blockDeposits := blk.GetBody().GetDeposits() + if len(blockDeposits) != len(localDeposits) { + return errors.Wrapf( + ErrDepositCountMismatch, "expected: %d, got: %d", + len(localDeposits), len(blockDeposits), + ) + } if uint64(len(localDeposits)) != min( - sp.cs.MaxDepositsPerBlock(), - eth1Data.DepositCount.Unwrap()-depositIndex, + sp.cs.MaxDepositsPerBlock(), eth1Data.DepositCount.Unwrap()-depositIndex, ) { return errors.Wrapf( ErrDepositCountMismatch, "expected: %d, got: %d", @@ -197,7 +203,7 @@ func (sp *StateProcessor[ // Ignore deposits with non-ETH1 withdrawal credentials. sp.logger.Info( "ignoring deposit with non-ETH1 withdrawal credentials", - "deposit_index", dep.GetIndex(), + // "deposit_index", dep.GetIndex(), ) return nil } @@ -215,7 +221,7 @@ func (sp *StateProcessor[ if err != nil { // Ignore deposits that fail the signature check. sp.logger.Info( - "failed deposit signature verification", "deposit_index", dep.GetIndex(), "error", err, + "failed deposit signature verification", "error", err, // "deposit_index", dep.GetIndex(), ) return nil } diff --git a/state-transition/core/state_processor_staking_test.go b/state-transition/core/state_processor_staking_test.go index f703a90396..bc190fa0ff 100644 --- a/state-transition/core/state_processor_staking_test.go +++ b/state-transition/core/state_processor_staking_test.go @@ -59,19 +59,16 @@ func TestTransitionUpdateValidators(t *testing.T) { Pubkey: [48]byte{0x00}, Credentials: emptyCredentials, Amount: minBalance + increment, - Index: uint64(0), }, { Pubkey: [48]byte{0x01}, Credentials: emptyCredentials, Amount: maxBalance - 6*increment, - Index: uint64(1), }, { Pubkey: [48]byte{0x03}, Credentials: emptyCredentials, Amount: maxBalance - 3*increment, - Index: uint64(2), }, } genPayloadHeader = new(types.ExecutionPayloadHeader).Empty() @@ -79,7 +76,10 @@ func TestTransitionUpdateValidators(t *testing.T) { ) require.NoError(t, ds.EnqueueDepositDatas(genDeposits)) - valDiff, err := sp.InitializePreminedBeaconStateFromEth1(st, genPayloadHeader, genVersion) + deposits, root, err := ds.GetDepositsByIndex(0, uint64(len(genDeposits))) + require.NoError(t, err) + valDiff, err := sp.InitializePreminedBeaconStateFromEth1( + st, deposits, root, genPayloadHeader, genVersion) require.NoError(t, err) require.Len(t, valDiff, len(genDeposits)) @@ -88,7 +88,6 @@ func TestTransitionUpdateValidators(t *testing.T) { Pubkey: genDeposits[2].Pubkey, Credentials: emptyCredentials, Amount: 2 * increment, // twice to account for hysteresis - Index: 3, } // make sure included deposit is already available in deposit store @@ -213,7 +212,6 @@ func TestTransitionCreateValidator(t *testing.T) { Pubkey: [48]byte{0x01}, Credentials: emptyCredentials, Amount: minBalance + increment, - Index: 0, }, } genPayloadHeader = new(types.ExecutionPayloadHeader).Empty() @@ -221,7 +219,10 @@ func TestTransitionCreateValidator(t *testing.T) { ) require.NoError(t, ds.EnqueueDepositDatas(genDeposits)) - genVals, err := sp.InitializePreminedBeaconStateFromEth1(st, genPayloadHeader, genVersion) + deposits, root, err := ds.GetDepositsByIndex(0, uint64(len(genDeposits))) + require.NoError(t, err) + genVals, err := sp.InitializePreminedBeaconStateFromEth1( + st, deposits, root, genPayloadHeader, genVersion) require.NoError(t, err) require.Len(t, genVals, len(genDeposits)) @@ -230,7 +231,6 @@ func TestTransitionCreateValidator(t *testing.T) { Pubkey: [48]byte{0xff}, // a new key for a new validator Credentials: emptyCredentials, Amount: maxBalance, - Index: 1, } // make sure included deposit is already available in deposit store @@ -411,13 +411,11 @@ func TestTransitionWithdrawals(t *testing.T) { Pubkey: [48]byte{0x00}, Credentials: credentials0, Amount: maxBalance - 3*minBalance, - Index: 0, }, { Pubkey: [48]byte{0x01}, Credentials: credentials1, Amount: maxBalance + minBalance, - Index: 1, }, } genPayloadHeader = new(types.ExecutionPayloadHeader).Empty() @@ -425,7 +423,10 @@ func TestTransitionWithdrawals(t *testing.T) { ) require.NoError(t, ds.EnqueueDepositDatas(genDeposits)) - _, err := sp.InitializePreminedBeaconStateFromEth1(st, genPayloadHeader, genVersion) + deposits, root, err := ds.GetDepositsByIndex(0, uint64(len(genDeposits))) + require.NoError(t, err) + _, err = sp.InitializePreminedBeaconStateFromEth1( + st, deposits, root, genPayloadHeader, genVersion) require.NoError(t, err) // Progress state to fork 2. @@ -509,13 +510,11 @@ func TestTransitionMaxWithdrawals(t *testing.T) { Pubkey: [48]byte{0x00}, Credentials: credentials0, Amount: maxBalance + minBalance, - Index: 0, }, { Pubkey: [48]byte{0x01}, Credentials: credentials1, Amount: maxBalance + minBalance, - Index: 1, }, } genPayloadHeader = new(types.ExecutionPayloadHeader).Empty() @@ -523,7 +522,10 @@ func TestTransitionMaxWithdrawals(t *testing.T) { ) require.NoError(t, ds.EnqueueDepositDatas(genDeposits)) - _, err = sp.InitializePreminedBeaconStateFromEth1(st, genPayloadHeader, genVersion) + deposits, root, err := ds.GetDepositsByIndex(0, uint64(len(genDeposits))) + require.NoError(t, err) + _, err = sp.InitializePreminedBeaconStateFromEth1( + st, deposits, root, genPayloadHeader, genVersion) require.NoError(t, err) // Progress state to fork 2. @@ -655,7 +657,7 @@ func TestTransitionHittingValidatorsCap_ExtraSmall(t *testing.T) { ) // let genesis define all available validators - for idx := range cs.ValidatorSetCap() { + for range cs.ValidatorSetCap() { var ( key bytes.B48 creds types.WithdrawalCredentials @@ -668,13 +670,15 @@ func TestTransitionHittingValidatorsCap_ExtraSmall(t *testing.T) { Pubkey: key, Credentials: creds, Amount: maxBalance, - Index: idx, }, ) } require.NoError(t, ds.EnqueueDepositDatas(genDeposits)) - _, err := sp.InitializePreminedBeaconStateFromEth1(st, genPayloadHeader, genVersion) + deposits, root, err := ds.GetDepositsByIndex(0, uint64(len(genDeposits))) + require.NoError(t, err) + _, err = sp.InitializePreminedBeaconStateFromEth1( + st, deposits, root, genPayloadHeader, genVersion) require.NoError(t, err) // STEP 1: Try and add an extra validator @@ -685,7 +689,6 @@ func TestTransitionHittingValidatorsCap_ExtraSmall(t *testing.T) { Pubkey: extraValKey, Credentials: extraValCreds, Amount: minBalance, - Index: uint64(len(genDeposits)), } // make sure included deposit is already available in deposit store @@ -885,7 +888,7 @@ func TestTransitionHittingValidatorsCap_ExtraBig(t *testing.T) { ) // let genesis define all available validators - for idx := range cs.ValidatorSetCap() { + for range cs.ValidatorSetCap() { var ( key bytes.B48 creds types.WithdrawalCredentials @@ -898,17 +901,18 @@ func TestTransitionHittingValidatorsCap_ExtraBig(t *testing.T) { Pubkey: key, Credentials: creds, Amount: maxBalance, - Index: idx, }, ) } + // make a deposit small to be ready for eviction genDeposits[0].Amount = minBalance require.NoError(t, ds.EnqueueDepositDatas(genDeposits)) - - var genVals transition.ValidatorUpdates - genVals, err := sp.InitializePreminedBeaconStateFromEth1(st, genPayloadHeader, genVersion) + deposits, root, err := ds.GetDepositsByIndex(0, uint64(len(genDeposits))) + require.NoError(t, err) + genVals, err := sp.InitializePreminedBeaconStateFromEth1( + st, deposits, root, genPayloadHeader, genVersion) require.NoError(t, err) require.Len(t, genVals, len(genDeposits)) @@ -919,7 +923,6 @@ func TestTransitionHittingValidatorsCap_ExtraBig(t *testing.T) { Pubkey: extraValKey, Credentials: extraValCreds, Amount: maxBalance, - Index: uint64(len(genDeposits)), } // make sure included deposit is already available in deposit store diff --git a/storage/deposit/block.go b/storage/deposit/block.go new file mode 100644 index 0000000000..02ddac8359 --- /dev/null +++ b/storage/deposit/block.go @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: BUSL-1.1 +// +// Copyright (C) 2024, Berachain Foundation. All rights reserved. +// Use of this software is governed by the Business Source License included +// in the LICENSE file of this repository and at www.mariadb.com/bsl11. +// +// ANY USE OF THE LICENSED WORK IN VIOLATION OF THIS LICENSE WILL AUTOMATICALLY +// TERMINATE YOUR RIGHTS UNDER THIS LICENSE FOR THE CURRENT AND ALL OTHER +// VERSIONS OF THE LICENSED WORK. +// +// THIS LICENSE DOES NOT GRANT YOU ANY RIGHT IN ANY TRADEMARK OR LOGO OF +// LICENSOR OR ITS AFFILIATES (PROVIDED THAT YOU MAY USE A TRADEMARK OR LOGO OF +// LICENSOR AS EXPRESSLY REQUIRED BY THIS LICENSE). +// +// TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON +// AN “AS IS” BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS, +// EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND +// TITLE. + +package deposit + +import ( + ctypes "github.com/berachain/beacon-kit/consensus-types/types" + "github.com/berachain/beacon-kit/primitives/common" + "github.com/berachain/beacon-kit/primitives/math" +) + +// block holds the necessary information of deposits in a block. +type block struct { + // the number of the finalized execution block, provided by EL. + executionNumber math.U64 + + // the hash of the finalized execution block, provided by EL. + executionHash common.ExecutionHash + + // the deposits (with the proofs) included in the block, determined by CL merkle tree. + deposits ctypes.Deposits + + // the root of the deposit tree at the end of processing each deposit in + // the tree, determined by CL merkle tree. + root []common.Root +} + +// retrievalInfo holds the necessary information to retrieve deposits for the next CL +// block request. +type retrievalInfo struct { + // the index of the block that should be searched from. + nextBlocksIndex int +} diff --git a/storage/deposit/store.go b/storage/deposit/store.go index 038d5a9cd1..54413e7e36 100644 --- a/storage/deposit/store.go +++ b/storage/deposit/store.go @@ -21,149 +21,119 @@ package deposit import ( - "context" - "fmt" "sync" - sdkcollections "cosmossdk.io/collections" - "cosmossdk.io/core/store" ctypes "github.com/berachain/beacon-kit/consensus-types/types" "github.com/berachain/beacon-kit/errors" "github.com/berachain/beacon-kit/primitives/common" + "github.com/berachain/beacon-kit/primitives/math" "github.com/berachain/beacon-kit/storage/deposit/merkle" - "github.com/berachain/beacon-kit/storage/encoding" - "github.com/berachain/beacon-kit/storage/pruner" ) -const KeyDepositPrefix = "deposit" - -// Store is a simple KV store based implementation that assumes -// the deposit indexes are tracked outside of the s store. -// It also maintains a merkle tree of the deposits for verification, -// which will remove the need for indexed based tracking. +// Store is a simple memory store based implementation that +// maintains a merkle tree of the deposits for verification. type Store struct { // tree is the EIP-4881 compliant deposit merkle tree. tree *merkle.DepositTree - // pendingDepositsToRoots maps the deposit tree root after each deposit. - pendingDepositsToRoots map[uint64]common.Root + // pendingDeposits holds the pending deposits for blocks that have yet to be + // processed by the CL. + pendingDeposits []*block - // store is the KV store that holds the deposits. - store sdkcollections.Map[uint64, *ctypes.Deposit] + // retrievalInfo holds the necessary information to retrieve deposits for the next CL + // block request. + retrievalInfo retrievalInfo // mu protects store for concurrent access. - mu sync.RWMutex + mu sync.Mutex } // NewStore creates a new deposit store. -func NewStore(kvsp store.KVStoreService) *Store { - schemaBuilder := sdkcollections.NewSchemaBuilder(kvsp) - res := &Store{ - tree: merkle.NewDepositTree(), - pendingDepositsToRoots: make(map[uint64]common.Root), - store: sdkcollections.NewMap( - schemaBuilder, - sdkcollections.NewPrefix([]byte(KeyDepositPrefix)), - KeyDepositPrefix, - sdkcollections.Uint64Key, - encoding.SSZValueCodec[*ctypes.Deposit]{}, - ), - } - if _, err := schemaBuilder.Build(); err != nil { - panic(fmt.Errorf("failed building Store schema: %w", err)) +func NewStore() *Store { + return &Store{ + tree: merkle.NewDepositTree(), + pendingDeposits: make([]*block, 0), } - return res } // GetDepositsByIndex returns the first N deposits starting from the given // index. If N is greater than the number of deposits, it returns up to the // last deposit available. It also returns the deposit tree root at the end of // the range. -// -// TODO: figure out when to finalize. Need to do after proof has been generated. -func (s *Store) GetDepositsByIndex( - startIndex, numView uint64, -) (ctypes.Deposits, common.Root, error) { - s.mu.RLock() - defer s.mu.RUnlock() +func (s *Store) GetDepositsByIndex(numView uint64) (ctypes.Deposits, common.Root, error) { + s.mu.Lock() + defer s.mu.Unlock() + var ( deposits = ctypes.Deposits{} - maxIndex = startIndex + numView depTreeRoot common.Root ) - for i := startIndex; i < maxIndex; i++ { - deposit, err := s.store.Get(context.TODO(), i) - if err == nil { - deposits = append(deposits, deposit) - continue - } - - if errors.Is(err, sdkcollections.ErrNotFound) { - depTreeRoot = s.pendingDepositsToRoots[i-1] - break - } - - return nil, common.Root{}, errors.Wrapf( - err, "failed to get deposit %d, start: %d, end: %d", i, startIndex, maxIndex, - ) - } - - if depTreeRoot == (common.Root{}) { - depTreeRoot = s.pendingDepositsToRoots[maxIndex-1] + startBlock := s.pendingDeposits[s.retrievalInfo.nextBlocksIndex] + for i := s.retrievalInfo.nextBlockDepositsIndex; i < len(startBlock.deposits); i++ { + deposits = append(deposits, startBlock.deposits[i]) } return deposits, depTreeRoot, nil } -// EnqueueDepositDatas pushes multiple deposits to the queue. +// EnqueueDepositDatas pushes multiple deposits to the queue for a given EL block. // // TODO: ensure that in-order is maintained. i.e. ignore any deposits we've already seen. -func (s *Store) EnqueueDepositDatas(depositDatas []*ctypes.DepositData) error { +func (s *Store) EnqueueDepositDatas( + depositDatas []*ctypes.DepositData, + indexes []uint64, + executionHash common.ExecutionHash, + executionNumber math.U64, +) error { s.mu.Lock() defer s.mu.Unlock() - for _, depositData := range depositDatas { - idx := depositData.GetIndex().Unwrap() - + // Build the deposits information for the block while inserting into the deposit tree. + block := &block{ + executionHash: executionHash, + executionNumber: executionNumber, + deposits: make(ctypes.Deposits, len(depositDatas)), + root: make([]common.Root, len(depositDatas)), + } + for i, depositData := range depositDatas { if err := s.tree.Insert(depositData.HashTreeRoot()); err != nil { - return errors.Wrapf(err, "failed to insert deposit %d into merkle tree", idx) + return errors.Wrapf(err, + "failed to insert deposit %d into merkle tree, execution number: %d", + indexes[i], executionNumber, + ) } - proof, err := s.tree.MerkleProof(idx) + proof, err := s.tree.MerkleProof(indexes[i]) if err != nil { - return errors.Wrapf(err, "failed to get merkle proof for deposit %d", idx) - } - deposit := ctypes.NewDeposit(proof, depositData) - if err = s.store.Set(context.TODO(), idx, deposit); err != nil { - return errors.Wrapf(err, "failed to set deposit %d in KVStore", idx) + return errors.Wrapf(err, + "failed to get merkle proof for deposit %d, execution number: %d", + indexes[i], executionNumber, + ) } - - s.pendingDepositsToRoots[idx] = s.tree.HashTreeRoot() + block.deposits[i] = ctypes.NewDeposit(proof, depositData) + block.root[i] = s.tree.HashTreeRoot() + } + s.pendingDeposits = append(s.pendingDeposits, block) + + // Finalize the block's deposits in the tree. Error returned here means the + // EIP 4881 merkle library is broken. // TODO: could move this to when we delete block. + lastDepositIndex := indexes[len(indexes)-1] + if err := s.tree.Finalize(lastDepositIndex, executionHash, executionNumber); err != nil { + return errors.Wrapf( + err, "failed to finalize deposits in tree for block %d, index %d", + executionNumber, lastDepositIndex, + ) } return nil } -// Prune removes the [start, end) deposits from the store. -func (s *Store) Prune(start, end uint64) error { - if start > end { - return fmt.Errorf( - "DepositKVStore Prune start: %d, end: %d: %w", start, end, pruner.ErrInvalidRange, - ) - } - - var ctx = context.TODO() +// Prune removes the deposits from the given height. +// NO-OP for now since the pruning call is not at the right time. +func (s *Store) Prune(height uint64) error { s.mu.Lock() defer s.mu.Unlock() - for i := range end { - delete(s.pendingDepositsToRoots, start+i) - - // This only errors if the key passed in cannot be encoded. - if err := s.store.Remove(ctx, start+i); err != nil { - return errors.Wrapf(err, "failed to prune deposit %d", start+i) - } - } return nil }