From fe3613d94b8a01cd4bb0c1d8fd2ecb2927802a07 Mon Sep 17 00:00:00 2001 From: Cal Bera Date: Wed, 18 Dec 2024 01:35:20 -0500 Subject: [PATCH 1/6] wip --- beacon/blockchain/deposit.go | 7 +- beacon/blockchain/init_chain.go | 16 +++- beacon/blockchain/pruning.go | 24 +----- beacon/blockchain/service.go | 4 - beacon/blockchain/types.go | 2 +- cmd/beacond/defaults.go | 4 +- consensus-types/types/block_test.go | 4 +- consensus-types/types/deposit_data.go | 27 +----- consensus-types/types/deposit_data_test.go | 4 - execution/deposit/contract.go | 5 +- node-core/components/chain_service.go | 2 - node-core/components/deposit_store.go | 20 +---- node-core/components/interfaces.go | 3 +- node-core/components/state_processor.go | 3 - state-transition/core/core_test.go | 3 +- state-transition/core/state_processor.go | 4 - .../core/state_processor_genesis.go | 85 +++++++------------ .../core/state_processor_staking.go | 4 +- state-transition/core/types.go | 6 -- storage/deposit/store.go | 61 ++----------- 20 files changed, 77 insertions(+), 211 deletions(-) diff --git a/beacon/blockchain/deposit.go b/beacon/blockchain/deposit.go index be3a595ae0..e6d4ae62cd 100644 --- a/beacon/blockchain/deposit.go +++ b/beacon/blockchain/deposit.go @@ -35,15 +35,16 @@ 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[ diff --git a/beacon/blockchain/init_chain.go b/beacon/blockchain/init_chain.go index e10eb85bc7..9eeebca665 100644 --- a/beacon/blockchain/init_chain.go +++ b/beacon/blockchain/init_chain.go @@ -41,15 +41,27 @@ func (s *Service[ return nil, err } - genesisDeposits := genesisData.GetDepositDatas() + // Store the genesis deposits. + genesisDepositDatas := genesisData.GetDepositDatas() genesisExecutionPayloadHeader := genesisData.GetExecutionPayloadHeader() - if err := s.depositStore.EnqueueDepositDatas(genesisDeposits); err != nil { + if err := s.depositStore.EnqueueDepositDatas(genesisDepositDatas); 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..97707ff325 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[ @@ -36,9 +35,10 @@ func (s *Service[ } // prune deposit store - start, end = depositPruneRangeFn(beaconBlk.GetBody().GetDepositDatas(), s.chainSpec) + finalizedEthBlock := beaconBlk.GetBody().GetExecutionPayload().Number.Unwrap() + end = finalizedEthBlock - s.chainSpec.Eth1FollowDistance() + start = end - s.chainSpec.SlotsPerEpoch() err = s.depositStore.Prune(start, end) - if err != nil { return err } @@ -46,24 +46,8 @@ func (s *Service[ 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/cmd/beacond/defaults.go b/cmd/beacond/defaults.go index fae7afb4f6..25f12b4123 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, *KVStore, ], components.ProvideKVStore, components.ProvideStorageBackend[ 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..d6ce89b7c8 100644 --- a/execution/deposit/contract.go +++ b/execution/deposit/contract.go @@ -105,7 +105,7 @@ func (dc *WrappedDepositContract) ReadDeposits( } 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, )) if blockHash == (common.ExecutionHash{}) { @@ -113,7 +113,8 @@ func (dc *WrappedDepositContract) ReadDeposits( } 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(), ) } 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..f464fca882 100644 --- a/node-core/components/interfaces.go +++ b/node-core/components/interfaces.go @@ -456,7 +456,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/node-core/components/state_processor.go b/node-core/components/state_processor.go index 9756032014..bf1497ef2c 100644 --- a/node-core/components/state_processor.go +++ b/node-core/components/state_processor.go @@ -43,7 +43,6 @@ type StateProcessorInput[ *engineprimitives.PayloadAttributes, PayloadID, ] - DepositStore DepositStore Signer crypto.BLSSigner TelemetrySink *metrics.TelemetrySink } @@ -55,7 +54,6 @@ func ProvideStateProcessor[ BeaconBlockT BeaconBlock[BeaconBlockT], BeaconStateT BeaconState[BeaconStateT, BeaconStateMarshallableT, KVStoreT], BeaconStateMarshallableT any, - DepositStoreT DepositStore, KVStoreT BeaconStore[KVStoreT], ]( in StateProcessorInput[LoggerT], @@ -73,7 +71,6 @@ func ProvideStateProcessor[ in.Logger.With("service", "state-processor"), in.ChainSpec, in.ExecutionEngine, - in.DepositStore, in.Signer, crypto.GetAddressFromPubKey, in.TelemetrySink, diff --git a/state-transition/core/core_test.go b/state-transition/core/core_test.go index 046ebb662f..bf70bd4863 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 } @@ -162,7 +162,6 @@ func setupState( noop.NewLogger[any](), cs, execEngine, - depositStore, mocksSigner, func(bytes.B48) ([]byte, error) { return dummyProposerAddr, nil diff --git a/state-transition/core/state_processor.go b/state-transition/core/state_processor.go index d3f6014b14..295140f3b9 100644 --- a/state-transition/core/state_processor.go +++ b/state-transition/core/state_processor.go @@ -54,8 +54,6 @@ type StateProcessor[ fGetAddressFromPubKey func(crypto.BLSPubkey) ([]byte, error) // executionEngine is the engine responsible for executing transactions. executionEngine ExecutionEngine - // ds allows checking payload deposits against the deposit contract - ds DepositStore // metrics is the metrics for the service. metrics *stateProcessorMetrics } @@ -70,7 +68,6 @@ func NewStateProcessor[ logger log.Logger, cs chain.ChainSpec, executionEngine ExecutionEngine, - ds DepositStore, signer crypto.BLSSigner, fGetAddressFromPubKey func(crypto.BLSPubkey) ([]byte, error), telemetrySink TelemetrySink, @@ -89,7 +86,6 @@ func NewStateProcessor[ executionEngine: executionEngine, signer: signer, fGetAddressFromPubKey: fGetAddressFromPubKey, - ds: ds, metrics: newStateProcessorMetrics(telemetrySink), } } 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_staking.go b/state-transition/core/state_processor_staking.go index a4f3136836..3bfd43af40 100644 --- a/state-transition/core/state_processor_staking.go +++ b/state-transition/core/state_processor_staking.go @@ -179,7 +179,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 } @@ -197,7 +197,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/types.go b/state-transition/core/types.go index e5545fde0c..f8e108ff97 100644 --- a/state-transition/core/types.go +++ b/state-transition/core/types.go @@ -73,12 +73,6 @@ type Context interface { GetConsensusTime() math.U64 } -// DepositStore defines the interface for deposit storage. -type DepositStore interface { - // GetDepositsByIndex returns `numView` or less deposits. - GetDepositsByIndex(startIndex, numView uint64) (ctypes.Deposits, common.Root, error) -} - // Withdrawals defines the interface for managing withdrawal operations. type Withdrawals interface { Len() int diff --git a/storage/deposit/store.go b/storage/deposit/store.go index 0595ec9235..ff245a9c6e 100644 --- a/storage/deposit/store.go +++ b/storage/deposit/store.go @@ -21,27 +21,20 @@ 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/constants" "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 @@ -49,29 +42,15 @@ type Store struct { // pendingDepositsToRoots maps the deposit tree root after each deposit. pendingDepositsToRoots map[uint64]common.Root - // store is the KV store that holds the deposits. - store sdkcollections.Map[uint64, *ctypes.DepositData] - // mu protects store for concurrent access. mu sync.Mutex } // NewStore creates a new deposit store. -func NewStore(kvsp store.KVStoreService) *Store { - schemaBuilder := sdkcollections.NewSchemaBuilder(kvsp) +func NewStore() *Store { 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.DepositData]{}, - ), - } - if _, err := schemaBuilder.Build(); err != nil { - panic(fmt.Errorf("failed building Store schema: %w", err)) } return res } @@ -94,26 +73,13 @@ func (s *Store) GetDepositsByIndex( ) for i := startIndex; i < maxIndex; i++ { - deposit, err := s.store.Get(context.TODO(), i) - if err != nil { - 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, - ) - } - - var proof [constants.DepositContractDepth + 1]common.Root - proof, err = s.tree.MerkleProof(i) + proof, err := s.tree.MerkleProof(i) if err != nil { return nil, common.Root{}, errors.Wrapf( err, "failed to get merkle proof for deposit %d", i, ) } - deposits = append(deposits, ctypes.NewDeposit(proof, deposit)) + deposits = append(deposits, ctypes.NewDeposit(proof, nil)) delete(s.pendingDepositsToRoots, i-1) } @@ -132,16 +98,11 @@ func (s *Store) EnqueueDepositDatas(deposits []*ctypes.DepositData) error { defer s.mu.Unlock() for _, deposit := range deposits { - idx := deposit.GetIndex().Unwrap() - if err := s.store.Set(context.TODO(), idx, deposit); err != nil { - return errors.Wrapf(err, "failed to set deposit %d in KVStore", idx) - } - if err := s.tree.Insert(deposit.HashTreeRoot()); err != nil { - return errors.Wrapf(err, "failed to insert deposit %d into merkle tree", idx) + return errors.Wrap(err, "failed to insert deposit into merkle tree") } - s.pendingDepositsToRoots[idx] = s.tree.HashTreeRoot() + // s.pendingDepositsToRoots[idx] = s.tree.HashTreeRoot() } return nil @@ -151,20 +112,14 @@ func (s *Store) EnqueueDepositDatas(deposits []*ctypes.DepositData) error { 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, + "DepositStore prune start: %d, end: %d: %w", start, end, pruner.ErrInvalidRange, ) } - var ctx = context.TODO() 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 From 199e5adad1df64cb90bf7006e0b09f80966a55a3 Mon Sep 17 00:00:00 2001 From: Cal Bera Date: Wed, 18 Dec 2024 01:55:25 -0500 Subject: [PATCH 2/6] more wip --- cli/commands/genesis/collect.go | 20 ++----- execution/deposit/contract.go | 9 ++- execution/deposit/types.go | 2 +- .../core/state_processor_genesis_test.go | 27 +++------ .../core/state_processor_staking_test.go | 59 ++++++++++--------- storage/deposit/store.go | 27 ++++----- 6 files changed, 61 insertions(+), 83 deletions(-) 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/execution/deposit/contract.go b/execution/deposit/contract.go index d6ce89b7c8..65c428e82d 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" @@ -93,15 +92,15 @@ 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, 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, 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, blockHash, errors.Wrap(err, "failed reading signature") } deposits = append(deposits, ctypes.NewDepositData( diff --git a/execution/deposit/types.go b/execution/deposit/types.go index 92e6e614df..6bf3e6b065 100644 --- a/execution/deposit/types.go +++ b/execution/deposit/types.go @@ -39,7 +39,7 @@ type Contract interface { // 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 + Prune(start, end uint64) error // EnqueueDepositDatas adds a list of deposits to the deposit store. EnqueueDepositDatas(deposits []*ctypes.DepositData) error // GetDepositsByIndex gets a list of deposits from the deposit store. diff --git a/state-transition/core/state_processor_genesis_test.go b/state-transition/core/state_processor_genesis_test.go index 42440dacee..144ec35255 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_test.go b/state-transition/core/state_processor_staking_test.go index 3e3c137138..8694bd7bf1 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,13 +88,12 @@ func TestTransitionUpdateValidators(t *testing.T) { Pubkey: genDeposits[2].Pubkey, Credentials: emptyCredentials, Amount: 2 * increment, // twice to account for hysteresis - Index: uint64(len(genDeposits)), } // make sure included deposit is already available in deposit store depositDatas := []*types.DepositData{blkDeposit} require.NoError(t, ds.EnqueueDepositDatas(depositDatas)) - deposits, _, err := ds.GetDepositsByIndex(uint64(len(genDeposits)), 1) + deposits, _, err = ds.GetDepositsByIndex(uint64(len(genDeposits)), 1) require.NoError(t, err) blk1 := buildNextBlock( t, @@ -208,7 +207,6 @@ func TestTransitionCreateValidator(t *testing.T) { Pubkey: [48]byte{0x01}, Credentials: emptyCredentials, Amount: minBalance + increment, - Index: uint64(0), }, } genPayloadHeader = new(types.ExecutionPayloadHeader).Empty() @@ -216,7 +214,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)) @@ -225,13 +226,12 @@ func TestTransitionCreateValidator(t *testing.T) { Pubkey: [48]byte{0xff}, // a new key for a new validator Credentials: emptyCredentials, Amount: maxBalance, - Index: uint64(len(genDeposits)), } // make sure included deposit is already available in deposit store depositDatas := []*types.DepositData{blkDeposit} require.NoError(t, ds.EnqueueDepositDatas(depositDatas)) - deposits, _, err := ds.GetDepositsByIndex(uint64(len(genDeposits)), 1) + deposits, _, err = ds.GetDepositsByIndex(uint64(len(genDeposits)), 1) require.NoError(t, err) blk1 := buildNextBlock( @@ -402,13 +402,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() @@ -416,7 +414,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. @@ -494,13 +495,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() @@ -508,7 +507,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. @@ -634,7 +636,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 @@ -647,13 +649,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 @@ -664,13 +668,12 @@ 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 depositDatas := []*types.DepositData{extraValDeposit} require.NoError(t, ds.EnqueueDepositDatas(depositDatas)) - deposits, _, err := ds.GetDepositsByIndex(uint64(len(genDeposits)), 1) + deposits, _, err = ds.GetDepositsByIndex(uint64(len(genDeposits)), 1) require.NoError(t, err) blk1 := buildNextBlock( @@ -860,7 +863,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 @@ -873,17 +876,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)) @@ -894,13 +898,12 @@ 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 depositDatas := []*types.DepositData{extraValDeposit} require.NoError(t, ds.EnqueueDepositDatas(depositDatas)) - deposits, _, err := ds.GetDepositsByIndex(uint64(len(genDeposits)), 1) + deposits, _, err = ds.GetDepositsByIndex(uint64(len(genDeposits)), 1) require.NoError(t, err) blk1 := buildNextBlock( diff --git a/storage/deposit/store.go b/storage/deposit/store.go index ff245a9c6e..0d87bdae2e 100644 --- a/storage/deposit/store.go +++ b/storage/deposit/store.go @@ -21,7 +21,6 @@ package deposit import ( - "fmt" "sync" ctypes "github.com/berachain/beacon-kit/consensus-types/types" @@ -31,7 +30,7 @@ import ( "github.com/berachain/beacon-kit/storage/pruner" ) -const KeyDepositPrefix = "deposit" +// TODO: index deposits by block height. // Store is a simple memory store based implementation that // maintains a merkle tree of the deposits for verification. @@ -39,8 +38,8 @@ 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 + // endOfBlockDepositTreeRoots maps the deposit tree root after each block. + endOfBlockDepositTreeRoots map[uint64]common.Root // mu protects store for concurrent access. mu sync.Mutex @@ -49,8 +48,8 @@ type Store struct { // NewStore creates a new deposit store. func NewStore() *Store { res := &Store{ - tree: merkle.NewDepositTree(), - pendingDepositsToRoots: make(map[uint64]common.Root), + tree: merkle.NewDepositTree(), + endOfBlockDepositTreeRoots: make(map[uint64]common.Root), } return res } @@ -80,12 +79,12 @@ func (s *Store) GetDepositsByIndex( ) } deposits = append(deposits, ctypes.NewDeposit(proof, nil)) - delete(s.pendingDepositsToRoots, i-1) + delete(s.endOfBlockDepositTreeRoots, i-1) } if depTreeRoot == (common.Root{}) { - depTreeRoot = s.pendingDepositsToRoots[maxIndex-1] - delete(s.pendingDepositsToRoots, maxIndex-1) + depTreeRoot = s.endOfBlockDepositTreeRoots[maxIndex-1] + delete(s.endOfBlockDepositTreeRoots, maxIndex-1) } return deposits, depTreeRoot, nil } @@ -102,24 +101,24 @@ func (s *Store) EnqueueDepositDatas(deposits []*ctypes.DepositData) error { return errors.Wrap(err, "failed to insert deposit into merkle tree") } - // s.pendingDepositsToRoots[idx] = s.tree.HashTreeRoot() + // s.endOfBlockDepositTreeRoots[idx] = s.tree.HashTreeRoot() } return nil } -// Prune removes the [start, end) deposits from the store. +// Prune removes the deposits from heights [start, end). func (s *Store) Prune(start, end uint64) error { if start > end { - return fmt.Errorf( - "DepositStore prune start: %d, end: %d: %w", start, end, pruner.ErrInvalidRange, + return errors.Wrapf( + pruner.ErrInvalidRange, "DepositStore prune start: %d, end: %d", start, end, ) } s.mu.Lock() defer s.mu.Unlock() for i := range end { - delete(s.pendingDepositsToRoots, start+i) + delete(s.endOfBlockDepositTreeRoots, start+i) } return nil From b09a5200dedb7d8368c76a2b7b7517036687afad Mon Sep 17 00:00:00 2001 From: Cal Bera Date: Wed, 18 Dec 2024 16:05:33 -0500 Subject: [PATCH 3/6] building --- cmd/beacond/defaults.go | 2 +- storage/deposit/store.go | 56 ++++++++++++++++++++-------------------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/cmd/beacond/defaults.go b/cmd/beacond/defaults.go index 25f12b4123..4689053516 100644 --- a/cmd/beacond/defaults.go +++ b/cmd/beacond/defaults.go @@ -78,7 +78,7 @@ func DefaultComponents() []any { ], components.ProvideSidecarFactory[*BeaconBlock], components.ProvideStateProcessor[ - *Logger, *BeaconBlock, *BeaconState, *BeaconStateMarshallable, *KVStore, + *Logger, *BeaconBlock, *BeaconState, *BeaconStateMarshallable, *DepositStore, *KVStore, ], components.ProvideKVStore, components.ProvideStorageBackend[ diff --git a/storage/deposit/store.go b/storage/deposit/store.go index 925eea9681..73f61338fe 100644 --- a/storage/deposit/store.go +++ b/storage/deposit/store.go @@ -67,30 +67,30 @@ func (s *Store) GetDepositsByIndex( defer s.mu.RUnlock() var ( deposits = ctypes.Deposits{} - maxIndex = startIndex + numView + // 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 - } + // 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 - } + // 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, - ) - } + // 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] - } + // if depTreeRoot == (common.Root{}) { + // depTreeRoot = s.pendingDepositsToRoots[maxIndex-1] + // } return deposits, depTreeRoot, nil } @@ -103,20 +103,20 @@ func (s *Store) EnqueueDepositDatas(depositDatas []*ctypes.DepositData) error { defer s.mu.Unlock() for _, depositData := range depositDatas { - idx := depositData.GetIndex().Unwrap() + // idx := depositData.GetIndex().Unwrap() 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", 0) } - proof, err := s.tree.MerkleProof(idx) - 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) - } + // proof, err := s.tree.MerkleProof(0) + // if err != nil { + // return errors.Wrapf(err, "failed to get merkle proof for deposit %d", 0) + // } + // 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) + // } // s.endOfBlockDepositTreeRoots[idx] = s.tree.HashTreeRoot() } From 86be4e4f5f418f06bc168906b6910d19f995a453 Mon Sep 17 00:00:00 2001 From: Cal Bera Date: Wed, 18 Dec 2024 16:40:02 -0500 Subject: [PATCH 4/6] wip --- beacon/blockchain/pruning.go | 8 ++-- beacon/validator/errors.go | 1 - cmd/beacond/types.go | 3 -- execution/deposit/types.go | 4 +- node-api/backend/types.go | 4 +- node-core/components/interfaces.go | 4 +- .../core/state_processor_staking.go | 5 +- storage/deposit/block.go | 46 +++++++++++++++++++ storage/deposit/store.go | 43 +++++++++-------- 9 files changed, 80 insertions(+), 38 deletions(-) create mode 100644 storage/deposit/block.go diff --git a/beacon/blockchain/pruning.go b/beacon/blockchain/pruning.go index 97707ff325..8f1a3a4bbc 100644 --- a/beacon/blockchain/pruning.go +++ b/beacon/blockchain/pruning.go @@ -34,12 +34,10 @@ func (s *Service[ return err } - // prune deposit store + // Delete the deposits of the block previous to the one we just stored. finalizedEthBlock := beaconBlk.GetBody().GetExecutionPayload().Number.Unwrap() - end = finalizedEthBlock - s.chainSpec.Eth1FollowDistance() - start = end - s.chainSpec.SlotsPerEpoch() - err = s.depositStore.Prune(start, end) - if err != nil { + height := finalizedEthBlock - s.chainSpec.Eth1FollowDistance() - 1 + if err = s.depositStore.Prune(height); err != nil { return err } 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/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/execution/deposit/types.go b/execution/deposit/types.go index 6bf3e6b065..33bc1c2caa 100644 --- a/execution/deposit/types.go +++ b/execution/deposit/types.go @@ -38,8 +38,8 @@ type Contract interface { // Store defines the interface for managing deposit operations. type Store interface { - // Prune prunes the deposit store of [start, end) - Prune(start, end uint64) error + // Prune prunes the deposit store of the given height. + Prune(height uint64) error // EnqueueDepositDatas adds a list of deposits to the deposit store. EnqueueDepositDatas(deposits []*ctypes.DepositData) error // GetDepositsByIndex gets a list of deposits from the deposit store. diff --git a/node-api/backend/types.go b/node-api/backend/types.go index c10d631679..5dca6aefd5 100644 --- a/node-api/backend/types.go +++ b/node-api/backend/types.go @@ -64,8 +64,8 @@ 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 + // Prune prunes the deposit store of the given height. + Prune(height uint64) error // EnqueueDepositDatas adds a list of deposits to the deposit store. EnqueueDepositDatas(deposits []*ctypes.DepositData) error } diff --git a/node-core/components/interfaces.go b/node-core/components/interfaces.go index f464fca882..e0551d54b7 100644 --- a/node-core/components/interfaces.go +++ b/node-core/components/interfaces.go @@ -368,8 +368,8 @@ 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 + // Prune prunes the deposit store of the given height. + Prune(height uint64) error // EnqueueDepositDatas adds a list of deposits to the deposit store. EnqueueDepositDatas(deposits []*ctypes.DepositData) error } diff --git a/state-transition/core/state_processor_staking.go b/state-transition/core/state_processor_staking.go index 456e780c07..6eec9e07d3 100644 --- a/state-transition/core/state_processor_staking.go +++ b/state-transition/core/state_processor_staking.go @@ -51,15 +51,14 @@ 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. 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", diff --git a/storage/deposit/block.go b/storage/deposit/block.go new file mode 100644 index 0000000000..513753e66e --- /dev/null +++ b/storage/deposit/block.go @@ -0,0 +1,46 @@ +// 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 pending deposits in a block. +type Block struct { + // the deposits included in the block. + deposits ctypes.Deposits + + // the index of the last deposit in the block. + lastDepositIndex uint64 + + // the root of the deposit tree at the end of the block (i.e. after all deposits + // from this block are inserted in the tree). + root common.Root + + // the number of the finalized execution block. + executionNumber math.U64 + + // the hash of the finalized execution block. + executionHash common.ExecutionHash +} diff --git a/storage/deposit/store.go b/storage/deposit/store.go index 73f61338fe..4932b9c460 100644 --- a/storage/deposit/store.go +++ b/storage/deposit/store.go @@ -27,19 +27,17 @@ import ( "github.com/berachain/beacon-kit/errors" "github.com/berachain/beacon-kit/primitives/common" "github.com/berachain/beacon-kit/storage/deposit/merkle" - "github.com/berachain/beacon-kit/storage/pruner" ) -// TODO: index deposits by block height. - // 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 - // endOfBlockDepositTreeRoots maps the deposit tree root after each block. - endOfBlockDepositTreeRoots map[uint64]common.Root + // pendingDeposits holds the pending deposits for blocks that have yet to be + // processed by the CL. + pendingDeposits map[uint64]*Block // mu protects store for concurrent access. mu sync.RWMutex @@ -48,8 +46,8 @@ type Store struct { // NewStore creates a new deposit store. func NewStore() *Store { res := &Store{ - tree: merkle.NewDepositTree(), - endOfBlockDepositTreeRoots: make(map[uint64]common.Root), + tree: merkle.NewDepositTree(), + pendingDeposits: make(map[uint64]*Block), } return res } @@ -58,15 +56,13 @@ func NewStore() *Store { // 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() var ( - deposits = ctypes.Deposits{} + deposits = ctypes.Deposits{} // maxIndex = startIndex + numView depTreeRoot common.Root ) @@ -124,18 +120,25 @@ func (s *Store) EnqueueDepositDatas(depositDatas []*ctypes.DepositData) error { return nil } -// Prune removes the deposits from heights [start, end). -func (s *Store) Prune(start, end uint64) error { - if start > end { - return errors.Wrapf( - pruner.ErrInvalidRange, "DepositStore prune start: %d, end: %d", start, end, - ) - } - +// Prune removes the deposits from the given height. +func (s *Store) Prune(height uint64) error { s.mu.Lock() defer s.mu.Unlock() - for i := range end { - delete(s.endOfBlockDepositTreeRoots, start+i) + + block, ok := s.pendingDeposits[height] + if !ok { + return nil + } + + // Remove the block from the pending deposits. + delete(s.pendingDeposits, height) + + // Finalize the block's deposits in the tree. Error returned here means the + // EIP 4881 merkle library is broken. + if err := s.tree.Finalize( + block.lastDepositIndex, block.executionHash, block.executionNumber, + ); err != nil { + return errors.Wrapf(err, "failed to finalize deposits in tree for block %d", height) } return nil From bc99c1e88bb7cd04a579543936c8cf7bc0d6e897 Mon Sep 17 00:00:00 2001 From: Cal Bera Date: Wed, 18 Dec 2024 17:19:47 -0500 Subject: [PATCH 5/6] wip --- beacon/blockchain/deposit.go | 6 +- beacon/blockchain/init_chain.go | 11 ++- beacon/blockchain/pruning.go | 2 + execution/deposit/contract.go | 14 ++-- execution/deposit/types.go | 11 ++- node-api/backend/types.go | 9 ++- node-core/components/interfaces.go | 11 ++- .../core/state_processor_staking.go | 7 ++ storage/deposit/block.go | 29 +++---- storage/deposit/store.go | 78 +++++++++++-------- 10 files changed, 115 insertions(+), 63 deletions(-) diff --git a/beacon/blockchain/deposit.go b/beacon/blockchain/deposit.go index e6d4ae62cd..3709f639f4 100644 --- a/beacon/blockchain/deposit.go +++ b/beacon/blockchain/deposit.go @@ -50,7 +50,7 @@ func (s *Service[ 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( @@ -68,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 9eeebca665..88650e61cb 100644 --- a/beacon/blockchain/init_chain.go +++ b/beacon/blockchain/init_chain.go @@ -43,8 +43,17 @@ func (s *Service[ // 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(genesisDepositDatas); 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 } diff --git a/beacon/blockchain/pruning.go b/beacon/blockchain/pruning.go index 8f1a3a4bbc..d55d9369bd 100644 --- a/beacon/blockchain/pruning.go +++ b/beacon/blockchain/pruning.go @@ -35,6 +35,8 @@ func (s *Service[ } // 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 { diff --git a/execution/deposit/contract.go b/execution/deposit/contract.go index 65c428e82d..f8cb5389db 100644 --- a/execution/deposit/contract.go +++ b/execution/deposit/contract.go @@ -67,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, @@ -76,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() { @@ -92,20 +93,21 @@ func (dc *WrappedDepositContract) ReadDeposits( ) pubKey, err = bytes.ToBytes48(logs.Event.Pubkey) if err != nil { - return nil, blockHash, errors.Wrap(err, "failed reading pub key") + return nil, nil, blockHash, errors.Wrap(err, "failed reading pub key") } cred, err = bytes.ToBytes32(logs.Event.Credentials) if err != nil { - return nil, blockHash, errors.Wrap(err, "failed reading credentials") + return nil, nil, blockHash, errors.Wrap(err, "failed reading credentials") } sign, err = bytes.ToBytes96(logs.Event.Signature) if err != nil { - return nil, blockHash, errors.Wrap(err, "failed reading signature") + 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, )) + indexes = append(indexes, logs.Event.Index) if blockHash == (common.ExecutionHash{}) { blockHash = common.ExecutionHash(logs.Event.Raw.BlockHash) @@ -117,5 +119,5 @@ func (dc *WrappedDepositContract) ReadDeposits( ) } - return deposits, blockHash, nil + return deposits, indexes, blockHash, nil } diff --git a/execution/deposit/types.go b/execution/deposit/types.go index 33bc1c2caa..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 the given height. Prune(height uint64) error - // EnqueueDepositDatas adds a list of deposits to the deposit store. - EnqueueDepositDatas(deposits []*ctypes.DepositData) 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 5dca6aefd5..f2c1d6b8ef 100644 --- a/node-api/backend/types.go +++ b/node-api/backend/types.go @@ -66,8 +66,13 @@ type DepositStore interface { GetDepositsByIndex(startIndex uint64, numView uint64) (ctypes.Deposits, common.Root, error) // Prune prunes the deposit store of the given height. Prune(height uint64) error - // EnqueueDepositDatas adds a list of deposits to the deposit store. - EnqueueDepositDatas(deposits []*ctypes.DepositData) 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/interfaces.go b/node-core/components/interfaces.go index e0551d54b7..57e66376d3 100644 --- a/node-core/components/interfaces.go +++ b/node-core/components/interfaces.go @@ -370,8 +370,13 @@ type ( GetDepositsByIndex(startIndex, numView uint64) (ctypes.Deposits, common.Root, error) // Prune prunes the deposit store of the given height. Prune(height uint64) error - // EnqueueDepositDatas adds a list of deposits to the deposit store. - EnqueueDepositDatas(deposits []*ctypes.DepositData) 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,7 @@ type ( // InitializePreminedBeaconStateFromEth1 initializes the premined beacon // state from the eth1 deposits. InitializePreminedBeaconStateFromEth1( - BeaconStateT, ctypes.Deposits, common.Root, + BeaconStateT, ctypes.Deposits, common.Root, *ctypes.ExecutionPayloadHeader, common.Version, ) (transition.ValidatorUpdates, error) // ProcessSlot processes the slot. diff --git a/state-transition/core/state_processor_staking.go b/state-transition/core/state_processor_staking.go index 6eec9e07d3..a280f4e37b 100644 --- a/state-transition/core/state_processor_staking.go +++ b/state-transition/core/state_processor_staking.go @@ -57,6 +57,13 @@ func (sp *StateProcessor[ // 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, ) { diff --git a/storage/deposit/block.go b/storage/deposit/block.go index 513753e66e..20a550793d 100644 --- a/storage/deposit/block.go +++ b/storage/deposit/block.go @@ -26,21 +26,24 @@ import ( "github.com/berachain/beacon-kit/primitives/math" ) -// Block holds the necessary information of pending deposits in a block. -type Block struct { - // the deposits included in the block. - deposits ctypes.Deposits +// block holds the necessary information of pending deposits in a block. +type block struct { + // the number of the finalized execution block, provided by EL. + executionNumber math.U64 - // the index of the last deposit in the block. - lastDepositIndex uint64 + // the hash of the finalized execution block, provided by EL. + executionHash common.ExecutionHash - // the root of the deposit tree at the end of the block (i.e. after all deposits - // from this block are inserted in the tree). - root common.Root + // the deposits (with the proofs) included in the block, determined by CL merkle tree. + deposits ctypes.Deposits - // the number of the finalized execution block. - executionNumber math.U64 + // the root of the deposit tree at the end of processing each deposit in + // the tree, determined by CL merkle tree. + root []common.Root +} - // the hash of the finalized execution block. - executionHash common.ExecutionHash +// 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. } diff --git a/storage/deposit/store.go b/storage/deposit/store.go index 4932b9c460..25573c5f1e 100644 --- a/storage/deposit/store.go +++ b/storage/deposit/store.go @@ -26,6 +26,7 @@ import ( 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" ) @@ -37,7 +38,10 @@ type Store struct { // pendingDeposits holds the pending deposits for blocks that have yet to be // processed by the CL. - pendingDeposits map[uint64]*Block + pendingDeposits []*Block + + // lastUsedIndex is the index of the last deposit included in CL blocks. + lastUsedIndex uint64 // mu protects store for concurrent access. mu sync.RWMutex @@ -47,7 +51,7 @@ type Store struct { func NewStore() *Store { res := &Store{ tree: merkle.NewDepositTree(), - pendingDeposits: make(map[uint64]*Block), + pendingDeposits: make([]*Block, 0), } return res } @@ -91,55 +95,63 @@ func (s *Store) GetDepositsByIndex( 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", 0) + return errors.Wrapf(err, + "failed to insert deposit %d into merkle tree, execution number: %d", + indexes[i], executionNumber, + ) } - // proof, err := s.tree.MerkleProof(0) - // if err != nil { - // return errors.Wrapf(err, "failed to get merkle proof for deposit %d", 0) - // } - // 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) - // } + proof, err := s.tree.MerkleProof(indexes[i]) + if err != nil { + return errors.Wrapf(err, + "failed to get merkle proof for deposit %d, execution number: %d", + indexes[i], executionNumber, + ) + } + block.deposits[i] = ctypes.NewDeposit(proof, depositData) + block.root[i] = s.tree.HashTreeRoot() + } + s.pendingDeposits = append(s.pendingDeposits, block) - // s.endOfBlockDepositTreeRoots[idx] = s.tree.HashTreeRoot() + // 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 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() - block, ok := s.pendingDeposits[height] - if !ok { - return nil - } - - // Remove the block from the pending deposits. - delete(s.pendingDeposits, height) - - // Finalize the block's deposits in the tree. Error returned here means the - // EIP 4881 merkle library is broken. - if err := s.tree.Finalize( - block.lastDepositIndex, block.executionHash, block.executionNumber, - ); err != nil { - return errors.Wrapf(err, "failed to finalize deposits in tree for block %d", height) - } - return nil } From 1ef7e2174a78a964d6c4da285d51f2a24383d109 Mon Sep 17 00:00:00 2001 From: Cal Bera Date: Wed, 18 Dec 2024 17:33:33 -0500 Subject: [PATCH 6/6] wip --- storage/deposit/block.go | 3 ++- storage/deposit/store.go | 52 +++++++++++++--------------------------- 2 files changed, 19 insertions(+), 36 deletions(-) diff --git a/storage/deposit/block.go b/storage/deposit/block.go index 20a550793d..02ddac8359 100644 --- a/storage/deposit/block.go +++ b/storage/deposit/block.go @@ -26,7 +26,7 @@ import ( "github.com/berachain/beacon-kit/primitives/math" ) -// block holds the necessary information of pending deposits in a block. +// 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 @@ -46,4 +46,5 @@ type block struct { // 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 25573c5f1e..54413e7e36 100644 --- a/storage/deposit/store.go +++ b/storage/deposit/store.go @@ -38,59 +38,41 @@ type Store struct { // pendingDeposits holds the pending deposits for blocks that have yet to be // processed by the CL. - pendingDeposits []*Block + pendingDeposits []*block - // lastUsedIndex is the index of the last deposit included in CL blocks. - lastUsedIndex uint64 + // 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() *Store { - res := &Store{ + return &Store{ tree: merkle.NewDepositTree(), - pendingDeposits: make([]*Block, 0), + 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. -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 + deposits = ctypes.Deposits{} 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 } @@ -108,7 +90,7 @@ func (s *Store) EnqueueDepositDatas( defer s.mu.Unlock() // Build the deposits information for the block while inserting into the deposit tree. - block := &Block{ + block := &block{ executionHash: executionHash, executionNumber: executionNumber, deposits: make(ctypes.Deposits, len(depositDatas)),