From 6da1903139131e3dd59ee6b9ccf536e64ae85401 Mon Sep 17 00:00:00 2001 From: Jared Wasinger Date: Wed, 12 Mar 2025 17:38:45 +0100 Subject: [PATCH] return prunedHistoryError from GetLogs/FilterLogs if accessing pruned state. duplicate prunedHistoryError into eth/filters package. add HistoryCutoff method to eth backend --- eth/api_backend.go | 7 +++++++ eth/filters/api.go | 4 ++++ eth/filters/filter_system.go | 6 ++++++ eth/filters/filter_system_test.go | 8 ++++++++ internal/ethapi/api_test.go | 3 +++ internal/ethapi/backend.go | 1 + internal/ethapi/transaction_args_test.go | 2 ++ 7 files changed, 31 insertions(+) diff --git a/eth/api_backend.go b/eth/api_backend.go index 1b4f596380..26511025c2 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -251,11 +251,18 @@ func (b *EthAPIBackend) StateAndHeaderByNumberOrHash(ctx context.Context, blockN return nil, nil, errors.New("invalid arguments; neither block nor hash specified") } +func (b *EthAPIBackend) HistoryCutoff() uint64 { + return b.eth.blockchain.HistoryCutoff() +} + func (b *EthAPIBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) { return b.eth.blockchain.GetReceiptsByHash(hash), nil } func (b *EthAPIBackend) GetLogs(ctx context.Context, hash common.Hash, number uint64) ([][]*types.Log, error) { + if number < b.eth.blockchain.HistoryCutoff() { + return nil, &prunedHistoryError{} + } return rawdb.ReadLogs(b.eth.chainDb, hash, number), nil } diff --git a/eth/filters/api.go b/eth/filters/api.go index e3057a2af2..54e6e16604 100644 --- a/eth/filters/api.go +++ b/eth/filters/api.go @@ -342,6 +342,7 @@ func (api *FilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([]*type } var filter *Filter if crit.BlockHash != nil { + // TODO: return pruned history error here where it would apply // Block filter requested, construct a single-shot filter filter = api.sys.NewBlockFilter(*crit.BlockHash, crit.Addresses, crit.Topics) } else { @@ -357,6 +358,9 @@ func (api *FilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([]*type if begin > 0 && end > 0 && begin > end { return nil, errInvalidBlockRange } + if begin < int64(api.events.backend.HistoryCutoff()) { + return nil, &prunedHistoryError{} + } // Construct the range filter filter = api.sys.NewRangeFilter(begin, end, crit.Addresses, crit.Topics) } diff --git a/eth/filters/filter_system.go b/eth/filters/filter_system.go index 7531a1ecfc..bf4f6559e0 100644 --- a/eth/filters/filter_system.go +++ b/eth/filters/filter_system.go @@ -64,6 +64,7 @@ type Backend interface { CurrentHeader() *types.Header ChainConfig() *params.ChainConfig + HistoryCutoff() uint64 SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription @@ -139,6 +140,11 @@ func (sys *FilterSystem) cachedGetBody(ctx context.Context, elem *logCacheElem, return body, nil } +type prunedHistoryError struct{} + +func (e *prunedHistoryError) Error() string { return "Pruned history unavailable" } +func (e *prunedHistoryError) ErrorCode() int { return 4444 } + // Type determines the kind of filter and is used to put the filter in to // the correct bucket when added. type Type byte diff --git a/eth/filters/filter_system_test.go b/eth/filters/filter_system_test.go index c35d823f5a..5670fb4a01 100644 --- a/eth/filters/filter_system_test.go +++ b/eth/filters/filter_system_test.go @@ -181,7 +181,15 @@ func (b *testBackend) setPending(block *types.Block, receipts types.Receipts) { b.pendingReceipts = receipts } +<<<<<<< HEAD func newTestFilterSystem(db ethdb.Database, cfg Config) (*testBackend, *FilterSystem) { +======= +func (b *testBackend) HistoryCutoff() uint64 { + return 0 +} + +func newTestFilterSystem(t testing.TB, db ethdb.Database, cfg Config) (*testBackend, *FilterSystem) { +>>>>>>> 7139f930b0 (return prunedHistoryError from GetLogs/FilterLogs if accessing pruned state. duplicate prunedHistoryError into eth/filters package. add HistoryCutoff method to eth backend) backend := &testBackend{db: db} sys := NewFilterSystem(backend, cfg) return backend, sys diff --git a/internal/ethapi/api_test.go b/internal/ethapi/api_test.go index 1ed1a8c8d8..6b66358c1e 100644 --- a/internal/ethapi/api_test.go +++ b/internal/ethapi/api_test.go @@ -618,6 +618,9 @@ func (b testBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscripti func (b testBackend) NewMatcherBackend() filtermaps.MatcherBackend { panic("implement me") } + +func (b testBackend) HistoryCutoff() uint64 { return b.chain.HistoryCutoff() } + func TestEstimateGas(t *testing.T) { t.Parallel() // Initialize test accounts diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go index 9e2ea2c876..31f0478c09 100644 --- a/internal/ethapi/backend.go +++ b/internal/ethapi/backend.go @@ -85,6 +85,7 @@ type Backend interface { ChainConfig() *params.ChainConfig Engine() consensus.Engine + HistoryCutoff() uint64 // This is copied from filters.Backend // eth/filters needs to be initialized from this backend type, so methods needed by diff --git a/internal/ethapi/transaction_args_test.go b/internal/ethapi/transaction_args_test.go index a5fd9bb0d4..b73243d915 100644 --- a/internal/ethapi/transaction_args_test.go +++ b/internal/ethapi/transaction_args_test.go @@ -402,3 +402,5 @@ func (b *backendMock) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) func (b *backendMock) Engine() consensus.Engine { return nil } func (b *backendMock) NewMatcherBackend() filtermaps.MatcherBackend { return nil } + +func (b *backendMock) HistoryCutoff() uint64 { return b.HistoryCutoff() }