From 1f2d8c0c45b7dc626ae2d0b11ab3a986b1069a56 Mon Sep 17 00:00:00 2001 From: cicr99 Date: Tue, 8 Sep 2026 12:39:20 +0200 Subject: [PATCH 1/2] pin storage proof state to validated height --- rpc/v10/storage.go | 2 +- rpc/v10/storage_bench_test.go | 5 ++++- rpc/v10/storage_test.go | 5 ++++- rpc/v9/storage.go | 2 +- rpc/v9/storage_test.go | 5 ++++- 5 files changed, 14 insertions(+), 5 deletions(-) diff --git a/rpc/v10/storage.go b/rpc/v10/storage.go index 3b300eb7fa..bda5b61595 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.StateAtBlockNumber(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..619bdaca0f 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(). + StateAtBlockNumber(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..c5ee70e980 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(). + StateAtBlockNumber(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..7cb783a02a 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.StateAtBlockNumber(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..f11cc73736 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(). + StateAtBlockNumber(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() From 97dc33bd66db9550a3e1fa228c83950a1205d085 Mon Sep 17 00:00:00 2001 From: cicr99 Date: Tue, 15 Sep 2026 19:31:25 +0200 Subject: [PATCH 2/2] add a method for returning a trie-accessible state reader given the block number --- blockchain/blockchain.go | 9 +++++++++ blockchain/statebackend/deprecated.go | 24 ++++++++++++++++++++++++ blockchain/statebackend/statebackend.go | 19 +++++++++++++++++++ blockchain/statebackend/types.go | 1 + mocks/mock_blockchain.go | 16 ++++++++++++++++ rpc/v10/storage.go | 2 +- rpc/v10/storage_bench_test.go | 2 +- rpc/v10/storage_test.go | 2 +- rpc/v9/storage.go | 2 +- rpc/v9/storage_test.go | 2 +- 10 files changed, 74 insertions(+), 5 deletions(-) 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 bda5b61595..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.StateAtBlockNumber(chainHeight) + 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 619bdaca0f..c24c43580c 100644 --- a/rpc/v10/storage_bench_test.go +++ b/rpc/v10/storage_bench_test.go @@ -151,7 +151,7 @@ func (f *storageProofBenchmarkFixture) setup( mockReader.EXPECT().Height().Return(f.blockNumber, nil).AnyTimes() mockReader.EXPECT().BlockHeaderHashByNumber(f.blockNumber).Return(f.blkHash, nil).AnyTimes() mockReader.EXPECT(). - StateAtBlockNumber(f.blockNumber). + TrieStateAtBlockNumber(f.blockNumber). Return(mockState, func() error { return nil }, nil). AnyTimes() diff --git a/rpc/v10/storage_test.go b/rpc/v10/storage_test.go index c5ee70e980..a5023b449c 100644 --- a/rpc/v10/storage_test.go +++ b/rpc/v10/storage_test.go @@ -695,7 +695,7 @@ func TestStorageProof(t *testing.T) { mockReader := mocks.NewMockReader(mockCtrl) mockState := mocks.NewMockStateReader(mockCtrl) mockReader.EXPECT(). - StateAtBlockNumber(blockNumber). + TrieStateAtBlockNumber(blockNumber). Return(mockState, func() error { return nil }, nil). AnyTimes() mockReader.EXPECT().Head().Return(headBlock, nil).AnyTimes() diff --git a/rpc/v9/storage.go b/rpc/v9/storage.go index 7cb783a02a..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.StateAtBlockNumber(chainHeight) + 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 f11cc73736..ea5b64de16 100644 --- a/rpc/v9/storage_test.go +++ b/rpc/v9/storage_test.go @@ -352,7 +352,7 @@ func TestStorageProof(t *testing.T) { mockReader := mocks.NewMockReader(mockCtrl) mockState := mocks.NewMockStateReader(mockCtrl) mockReader.EXPECT(). - StateAtBlockNumber(blockNumber). + TrieStateAtBlockNumber(blockNumber). Return(mockState, func() error { return nil }, nil). AnyTimes() mockReader.EXPECT().Head().Return(headBlock, nil).AnyTimes()