diff --git a/blockchain/blockchain.go b/blockchain/blockchain.go index 8ae7885df3..f941d0cbdb 100644 --- a/blockchain/blockchain.go +++ b/blockchain/blockchain.go @@ -85,6 +85,7 @@ type Reader interface { HeadState() (core.StateReader, StateCloser, error) StateAtBlockHash(blockHash *felt.Felt) (core.StateReader, StateCloser, error) StateAtBlockNumber(blockNumber uint64) (core.StateReader, StateCloser, error) + TrieStateAtBlockNumber(blockNumber uint64) (core.StateReader, StateCloser, error) BlockCommitmentsByNumber(blockNumber uint64) (*core.BlockCommitments, error) @@ -464,6 +465,14 @@ func (b *Blockchain) StateAtBlockHash( return b.stateBackend.StateAtBlockHash(blockHash) } +// TrieStateAtBlockNumber returns a StateReader with trie access at the given block number. +func (b *Blockchain) TrieStateAtBlockNumber( + blockNumber uint64, +) (core.StateReader, StateCloser, error) { + b.listener.OnRead("TrieStateAtBlockNumber") + return b.stateBackend.TrieStateAtBlockNumber(blockNumber) +} + // EventFilter returns an EventFilter object that is tied to a snapshot of the blockchain func (b *Blockchain) EventFilter( addresses []felt.Address, diff --git a/blockchain/statebackend/deprecated.go b/blockchain/statebackend/deprecated.go index 51dd31ae12..021f87887f 100644 --- a/blockchain/statebackend/deprecated.go +++ b/blockchain/statebackend/deprecated.go @@ -63,6 +63,30 @@ func (b *deprecatedStateBackend) StateAtBlockHash( ), NoopStateCloser, nil } +func (b *deprecatedStateBackend) TrieStateAtBlockNumber( + blockNumber uint64, +) (core.StateReader, StateCloser, error) { + if err := pruner.RequireStateRetainedByBlockNumber( + b.database, + b.retentionFloor, + blockNumber, + ); err != nil { + return nil, nil, err + } + + //nolint:staticcheck,nolintlint // used by old state + txn := b.database.NewIndexedBatch() + height, err := core.GetChainHeight(txn) + if err != nil { + return nil, nil, err + } + if height != blockNumber { + return nil, nil, db.ErrKeyNotFound + } + + return deprecatedstate.New(txn), NoopStateCloser, nil +} + func (b *deprecatedStateBackend) Store( block *core.Block, blockCommitments *core.BlockCommitments, diff --git a/blockchain/statebackend/statebackend.go b/blockchain/statebackend/statebackend.go index 22a2d332ea..39ee807b17 100644 --- a/blockchain/statebackend/statebackend.go +++ b/blockchain/statebackend/statebackend.go @@ -77,6 +77,25 @@ func (b *stateBackend) StateAtBlockHash( return &history, NoopStateCloser, nil } +func (b *stateBackend) TrieStateAtBlockNumber( + blockNumber uint64, +) (core.StateReader, StateCloser, error) { + stateRoot, err := pruner.StateRootIfStateRetainedByBlockNumber( + b.database, + b.retentionFloor, + blockNumber, + ) + if err != nil { + return nil, nil, err + } + + st, err := state.NewStateReader(stateRoot, b.stateDB) + if err != nil { + return nil, nil, err + } + return st, NoopStateCloser, nil +} + func (b *stateBackend) Store( block *core.Block, blockCommitments *core.BlockCommitments, diff --git a/blockchain/statebackend/types.go b/blockchain/statebackend/types.go index ad93d24451..81c84e6d97 100644 --- a/blockchain/statebackend/types.go +++ b/blockchain/statebackend/types.go @@ -15,6 +15,7 @@ type StateBackend interface { HeadState() (core.StateReader, StateCloser, error) StateAtBlockNumber(blockNumber uint64) (core.StateReader, StateCloser, error) StateAtBlockHash(blockHash *felt.Felt) (core.StateReader, StateCloser, error) + TrieStateAtBlockNumber(blockNumber uint64) (core.StateReader, StateCloser, error) Store( block *core.Block, commitments *core.BlockCommitments, diff --git a/mocks/mock_blockchain.go b/mocks/mock_blockchain.go index 2fe1e8f9e9..b668a77828 100644 --- a/mocks/mock_blockchain.go +++ b/mocks/mock_blockchain.go @@ -349,6 +349,22 @@ func (mr *MockReaderMockRecorder) StateAtBlockNumber(blockNumber any) *gomock.Ca return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateAtBlockNumber", reflect.TypeOf((*MockReader)(nil).StateAtBlockNumber), blockNumber) } +// TrieStateAtBlockNumber mocks base method. +func (m *MockReader) TrieStateAtBlockNumber(blockNumber uint64) (core.StateReader, blockchain.StateCloser, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "TrieStateAtBlockNumber", blockNumber) + ret0, _ := ret[0].(core.StateReader) + ret1, _ := ret[1].(blockchain.StateCloser) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +// TrieStateAtBlockNumber indicates an expected call of TrieStateAtBlockNumber. +func (mr *MockReaderMockRecorder) TrieStateAtBlockNumber(blockNumber any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TrieStateAtBlockNumber", reflect.TypeOf((*MockReader)(nil).TrieStateAtBlockNumber), blockNumber) +} + // StateUpdateByHash mocks base method. func (m *MockReader) StateUpdateByHash(hash *felt.Felt) (*core.StateUpdate, error) { m.ctrl.T.Helper() diff --git a/rpc/v10/storage.go b/rpc/v10/storage.go index 3b300eb7fa..f796413a11 100644 --- a/rpc/v10/storage.go +++ b/rpc/v10/storage.go @@ -206,7 +206,7 @@ func (h *Handler) StorageProof( return nil, rpccore.ErrInternal.CloneWithData(err) } - state, closer, err := h.bcReader.HeadState() + state, closer, err := h.bcReader.TrieStateAtBlockNumber(chainHeight) if err != nil { return nil, rpccore.ErrInternal.CloneWithData(err) } diff --git a/rpc/v10/storage_bench_test.go b/rpc/v10/storage_bench_test.go index 96c85e1985..c24c43580c 100644 --- a/rpc/v10/storage_bench_test.go +++ b/rpc/v10/storage_bench_test.go @@ -150,7 +150,10 @@ func (f *storageProofBenchmarkFixture) setup( mockReader.EXPECT().Height().Return(f.blockNumber, nil).AnyTimes() mockReader.EXPECT().BlockHeaderHashByNumber(f.blockNumber).Return(f.blkHash, nil).AnyTimes() - mockReader.EXPECT().HeadState().Return(mockState, func() error { return nil }, nil).AnyTimes() + mockReader.EXPECT(). + TrieStateAtBlockNumber(f.blockNumber). + Return(mockState, func() error { return nil }, nil). + AnyTimes() mockState.EXPECT().ClassTrie().Return(classTrie, nil).AnyTimes() mockState.EXPECT().ContractTrie().Return(contractTrie, nil).AnyTimes() diff --git a/rpc/v10/storage_test.go b/rpc/v10/storage_test.go index c0dfe66f23..a5023b449c 100644 --- a/rpc/v10/storage_test.go +++ b/rpc/v10/storage_test.go @@ -694,7 +694,10 @@ func TestStorageProof(t *testing.T) { mockReader := mocks.NewMockReader(mockCtrl) mockState := mocks.NewMockStateReader(mockCtrl) - mockReader.EXPECT().HeadState().Return(mockState, func() error { return nil }, nil).AnyTimes() + mockReader.EXPECT(). + TrieStateAtBlockNumber(blockNumber). + Return(mockState, func() error { return nil }, nil). + AnyTimes() mockReader.EXPECT().Head().Return(headBlock, nil).AnyTimes() mockReader.EXPECT().BlockByNumber(blockNumber).Return(headBlock, nil).AnyTimes() mockReader.EXPECT().Height().Return(blockNumber, nil).AnyTimes() diff --git a/rpc/v9/storage.go b/rpc/v9/storage.go index a41a77e7f2..5c773b1610 100644 --- a/rpc/v9/storage.go +++ b/rpc/v9/storage.go @@ -93,7 +93,7 @@ func (h *Handler) StorageProof( return nil, rpccore.ErrInternal.CloneWithData(err) } - state, closer, err := h.bcReader.HeadState() + state, closer, err := h.bcReader.TrieStateAtBlockNumber(chainHeight) if err != nil { return nil, rpccore.ErrInternal.CloneWithData(err) } diff --git a/rpc/v9/storage_test.go b/rpc/v9/storage_test.go index ddeef52ce4..ea5b64de16 100644 --- a/rpc/v9/storage_test.go +++ b/rpc/v9/storage_test.go @@ -351,7 +351,10 @@ func TestStorageProof(t *testing.T) { mockReader := mocks.NewMockReader(mockCtrl) mockState := mocks.NewMockStateReader(mockCtrl) - mockReader.EXPECT().HeadState().Return(mockState, func() error { return nil }, nil).AnyTimes() + mockReader.EXPECT(). + TrieStateAtBlockNumber(blockNumber). + Return(mockState, func() error { return nil }, nil). + AnyTimes() mockReader.EXPECT().Head().Return(headBlock, nil).AnyTimes() mockReader.EXPECT().BlockByNumber(blockNumber).Return(headBlock, nil).AnyTimes() mockReader.EXPECT().Height().Return(blockNumber, nil).AnyTimes()