Skip to content

Commit 4182faa

Browse files
s1najwasingerfjl
authored andcommitted
eth, eth/filters: implement API error code for pruned blocks (ethereum#31361)
Implements ethereum#31275 --------- Co-authored-by: Jared Wasinger <[email protected]> Co-authored-by: Felix Lange <[email protected]>
1 parent 5ecc3da commit 4182faa

File tree

12 files changed

+128
-38
lines changed

12 files changed

+128
-38
lines changed

core/blockchain_reader.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,11 @@ func (bc *BlockChain) GetHeaderByNumber(number uint64) *types.Header {
8686
return bc.hc.GetHeaderByNumber(number)
8787
}
8888

89+
// GetBlockNumber retrieves the block number associated with a block hash.
90+
func (bc *BlockChain) GetBlockNumber(hash common.Hash) *uint64 {
91+
return bc.hc.GetBlockNumber(hash)
92+
}
93+
8994
// GetHeadersFrom returns a contiguous segment of headers, in rlp-form, going
9095
// backwards from the given number.
9196
func (bc *BlockChain) GetHeadersFrom(number, count uint64) []rlp.RawValue {

eth/api_backend.go

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import (
3434
"github.com/ethereum/go-ethereum/core/txpool"
3535
"github.com/ethereum/go-ethereum/core/types"
3636
"github.com/ethereum/go-ethereum/core/vm"
37+
"github.com/ethereum/go-ethereum/eth/ethconfig"
3738
"github.com/ethereum/go-ethereum/eth/gasprice"
3839
"github.com/ethereum/go-ethereum/eth/tracers"
3940
"github.com/ethereum/go-ethereum/ethdb"
@@ -91,7 +92,13 @@ func (b *EthAPIBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumb
9192
}
9293
return block, nil
9394
}
94-
return b.eth.blockchain.GetHeaderByNumber(uint64(number)), nil
95+
var bn uint64
96+
if number == rpc.EarliestBlockNumber {
97+
bn = b.eth.blockchain.HistoryPruningCutoff()
98+
} else {
99+
bn = uint64(number)
100+
}
101+
return b.eth.blockchain.GetHeaderByNumber(bn), nil
95102
}
96103

97104
func (b *EthAPIBackend) HeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Header, error) {
@@ -143,22 +150,42 @@ func (b *EthAPIBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumbe
143150
}
144151
return b.eth.blockchain.GetBlock(header.Hash(), header.Number.Uint64()), nil
145152
}
146-
return b.eth.blockchain.GetBlockByNumber(uint64(number)), nil
153+
bn := uint64(number) // the resolved number
154+
if number == rpc.EarliestBlockNumber {
155+
bn = b.eth.blockchain.HistoryPruningCutoff()
156+
}
157+
block := b.eth.blockchain.GetBlockByNumber(bn)
158+
if block == nil && bn < b.eth.blockchain.HistoryPruningCutoff() {
159+
return nil, &ethconfig.PrunedHistoryError{}
160+
}
161+
return block, nil
147162
}
148163

149164
func (b *EthAPIBackend) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) {
150-
return b.eth.blockchain.GetBlockByHash(hash), nil
165+
number := b.eth.blockchain.GetBlockNumber(hash)
166+
if number == nil {
167+
return nil, nil
168+
}
169+
block := b.eth.blockchain.GetBlock(hash, *number)
170+
if block == nil && *number < b.eth.blockchain.HistoryPruningCutoff() {
171+
return nil, &ethconfig.PrunedHistoryError{}
172+
}
173+
return block, nil
151174
}
152175

153176
// GetBody returns body of a block. It does not resolve special block numbers.
154177
func (b *EthAPIBackend) GetBody(ctx context.Context, hash common.Hash, number rpc.BlockNumber) (*types.Body, error) {
155178
if number < 0 || hash == (common.Hash{}) {
156179
return nil, errors.New("invalid arguments; expect hash and no special block numbers")
157180
}
158-
if body := b.eth.blockchain.GetBody(hash); body != nil {
159-
return body, nil
181+
body := b.eth.blockchain.GetBody(hash)
182+
if body == nil {
183+
if uint64(number) < b.eth.blockchain.HistoryPruningCutoff() {
184+
return nil, &ethconfig.PrunedHistoryError{}
185+
}
186+
return nil, errors.New("block body not found")
160187
}
161-
return nil, errors.New("block body not found")
188+
return body, nil
162189
}
163190

164191
func (b *EthAPIBackend) BlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Block, error) {
@@ -175,6 +202,9 @@ func (b *EthAPIBackend) BlockByNumberOrHash(ctx context.Context, blockNrOrHash r
175202
}
176203
block := b.eth.blockchain.GetBlock(hash, header.Number.Uint64())
177204
if block == nil {
205+
if header.Number.Uint64() < b.eth.blockchain.HistoryPruningCutoff() {
206+
return nil, &ethconfig.PrunedHistoryError{}
207+
}
178208
return nil, errors.New("header found, but block body is missing")
179209
}
180210
return block, nil
@@ -234,6 +264,10 @@ func (b *EthAPIBackend) StateAndHeaderByNumberOrHash(ctx context.Context, blockN
234264
return nil, nil, errors.New("invalid arguments; neither block nor hash specified")
235265
}
236266

267+
func (b *EthAPIBackend) HistoryPruningCutoff() uint64 {
268+
return b.eth.blockchain.HistoryPruningCutoff()
269+
}
270+
237271
func (b *EthAPIBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
238272
return b.eth.blockchain.GetReceiptsByHash(hash), nil
239273
}

eth/ethconfig/historymode.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,3 +90,9 @@ var HistoryPrunePoints = map[common.Hash]*HistoryPrunePoint{
9090
BlockHash: common.HexToHash("0x229f6b18ca1552f1d5146deceb5387333f40dc6275aebee3f2c5c4ece07d02db"),
9191
},
9292
}
93+
94+
// PrunedHistoryError is returned when the requested history is pruned.
95+
type PrunedHistoryError struct{}
96+
97+
func (e *PrunedHistoryError) Error() string { return "pruned history unavailable" }
98+
func (e *PrunedHistoryError) ErrorCode() int { return 4444 }

eth/filters/api.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"github.com/ethereum/go-ethereum/common"
3030
"github.com/ethereum/go-ethereum/common/hexutil"
3131
"github.com/ethereum/go-ethereum/core/types"
32+
"github.com/ethereum/go-ethereum/eth/ethconfig"
3233
"github.com/ethereum/go-ethereum/internal/ethapi"
3334
"github.com/ethereum/go-ethereum/rpc"
3435
)
@@ -354,9 +355,13 @@ func (api *FilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([]*type
354355
if crit.ToBlock != nil {
355356
end = crit.ToBlock.Int64()
356357
}
358+
// Block numbers below 0 are special cases.
357359
if begin > 0 && end > 0 && begin > end {
358360
return nil, errInvalidBlockRange
359361
}
362+
if begin > 0 && begin < int64(api.events.backend.HistoryPruningCutoff()) {
363+
return nil, &ethconfig.PrunedHistoryError{}
364+
}
360365
// Construct the range filter
361366
filter = api.sys.NewRangeFilter(begin, end, crit.Addresses, crit.Topics)
362367
}

eth/filters/filter.go

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"github.com/ethereum/go-ethereum/common"
2828
"github.com/ethereum/go-ethereum/core/filtermaps"
2929
"github.com/ethereum/go-ethereum/core/types"
30+
"github.com/ethereum/go-ethereum/eth/ethconfig"
3031
"github.com/ethereum/go-ethereum/log"
3132
"github.com/ethereum/go-ethereum/rpc"
3233
)
@@ -86,6 +87,9 @@ func (f *Filter) Logs(ctx context.Context) ([]*types.Log, error) {
8687
if header == nil {
8788
return nil, errors.New("unknown block")
8889
}
90+
if header.Number.Uint64() < f.sys.backend.HistoryPruningCutoff() {
91+
return nil, &ethconfig.PrunedHistoryError{}
92+
}
8993
return f.blockLogs(ctx, header)
9094
}
9195

@@ -114,11 +118,19 @@ func (f *Filter) Logs(ctx context.Context) ([]*types.Log, error) {
114118
return 0, errors.New("safe header not found")
115119
}
116120
return hdr.Number.Uint64(), nil
121+
case rpc.EarliestBlockNumber.Int64():
122+
earliest := f.sys.backend.HistoryPruningCutoff()
123+
hdr, _ := f.sys.backend.HeaderByNumber(ctx, rpc.BlockNumber(earliest))
124+
if hdr == nil {
125+
return 0, errors.New("earliest header not found")
126+
}
127+
return hdr.Number.Uint64(), nil
128+
default:
129+
if number < 0 {
130+
return 0, errors.New("negative block number")
131+
}
132+
return uint64(number), nil
117133
}
118-
if number < 0 {
119-
return 0, errors.New("negative block number")
120-
}
121-
return uint64(number), nil
122134
}
123135

124136
// range query need to resolve the special begin/end block number

eth/filters/filter_system.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import (
3131
"github.com/ethereum/go-ethereum/core"
3232
"github.com/ethereum/go-ethereum/core/filtermaps"
3333
"github.com/ethereum/go-ethereum/core/types"
34+
"github.com/ethereum/go-ethereum/eth/ethconfig"
3435
"github.com/ethereum/go-ethereum/ethdb"
3536
"github.com/ethereum/go-ethereum/event"
3637
"github.com/ethereum/go-ethereum/log"
@@ -64,6 +65,7 @@ type Backend interface {
6465

6566
CurrentHeader() *types.Header
6667
ChainConfig() *params.ChainConfig
68+
HistoryPruningCutoff() uint64
6769
SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription
6870
SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription
6971
SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription
@@ -304,6 +306,14 @@ func (es *EventSystem) SubscribeLogs(crit ethereum.FilterQuery, logs chan []*typ
304306
return nil, errPendingLogsUnsupported
305307
}
306308

309+
if from == rpc.EarliestBlockNumber {
310+
from = rpc.BlockNumber(es.backend.HistoryPruningCutoff())
311+
}
312+
// Queries beyond the pruning cutoff are not supported.
313+
if uint64(from) < es.backend.HistoryPruningCutoff() {
314+
return nil, &ethconfig.PrunedHistoryError{}
315+
}
316+
307317
// only interested in new mined logs
308318
if from == rpc.LatestBlockNumber && to == rpc.LatestBlockNumber {
309319
return es.subscribeLogs(crit, logs), nil

eth/filters/filter_system_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,10 @@ func (b *testBackend) setPending(block *types.Block, receipts types.Receipts) {
181181
b.pendingReceipts = receipts
182182
}
183183

184+
func (b *testBackend) HistoryPruningCutoff() uint64 {
185+
return 0
186+
}
187+
184188
func newTestFilterSystem(db ethdb.Database, cfg Config) (*testBackend, *FilterSystem) {
185189
backend := &testBackend{db: db}
186190
sys := NewFilterSystem(backend, cfg)

internal/ethapi/api.go

Lines changed: 31 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -549,21 +549,23 @@ func (api *BlockChainAPI) GetUncleByBlockHashAndIndex(ctx context.Context, block
549549
}
550550

551551
// GetUncleCountByBlockNumber returns number of uncles in the block for the given block number
552-
func (api *BlockChainAPI) GetUncleCountByBlockNumber(ctx context.Context, blockNr rpc.BlockNumber) *hexutil.Uint {
553-
if block, _ := api.b.BlockByNumber(ctx, blockNr); block != nil {
552+
func (api *BlockChainAPI) GetUncleCountByBlockNumber(ctx context.Context, blockNr rpc.BlockNumber) (*hexutil.Uint, error) {
553+
block, err := api.b.BlockByNumber(ctx, blockNr)
554+
if block != nil {
554555
n := hexutil.Uint(len(block.Uncles()))
555-
return &n
556+
return &n, nil
556557
}
557-
return nil
558+
return nil, err
558559
}
559560

560561
// GetUncleCountByBlockHash returns number of uncles in the block for the given block hash
561-
func (api *BlockChainAPI) GetUncleCountByBlockHash(ctx context.Context, blockHash common.Hash) *hexutil.Uint {
562-
if block, _ := api.b.BlockByHash(ctx, blockHash); block != nil {
562+
func (api *BlockChainAPI) GetUncleCountByBlockHash(ctx context.Context, blockHash common.Hash) (*hexutil.Uint, error) {
563+
block, err := api.b.BlockByHash(ctx, blockHash)
564+
if block != nil {
563565
n := hexutil.Uint(len(block.Uncles()))
564-
return &n
566+
return &n, nil
565567
}
566-
return nil
568+
return nil, err
567569
}
568570

569571
// GetCode returns the code stored at the given address in the state for the given block number.
@@ -596,9 +598,7 @@ func (api *BlockChainAPI) GetStorageAt(ctx context.Context, address common.Addre
596598
func (api *BlockChainAPI) GetBlockReceipts(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) ([]map[string]interface{}, error) {
597599
block, err := api.b.BlockByNumberOrHash(ctx, blockNrOrHash)
598600
if block == nil || err != nil {
599-
// When the block doesn't exist, the RPC method should return JSON null
600-
// as per specification.
601-
return nil, nil
601+
return nil, err
602602
}
603603
receipts, err := api.b.GetReceipts(ctx, block.Hash())
604604
if err != nil {
@@ -1258,37 +1258,41 @@ func NewTransactionAPI(b Backend, nonceLock *AddrLocker) *TransactionAPI {
12581258
}
12591259

12601260
// GetBlockTransactionCountByNumber returns the number of transactions in the block with the given block number.
1261-
func (api *TransactionAPI) GetBlockTransactionCountByNumber(ctx context.Context, blockNr rpc.BlockNumber) *hexutil.Uint {
1262-
if block, _ := api.b.BlockByNumber(ctx, blockNr); block != nil {
1261+
func (api *TransactionAPI) GetBlockTransactionCountByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*hexutil.Uint, error) {
1262+
block, err := api.b.BlockByNumber(ctx, blockNr)
1263+
if block != nil {
12631264
n := hexutil.Uint(len(block.Transactions()))
1264-
return &n
1265+
return &n, nil
12651266
}
1266-
return nil
1267+
return nil, err
12671268
}
12681269

12691270
// GetBlockTransactionCountByHash returns the number of transactions in the block with the given hash.
1270-
func (api *TransactionAPI) GetBlockTransactionCountByHash(ctx context.Context, blockHash common.Hash) *hexutil.Uint {
1271-
if block, _ := api.b.BlockByHash(ctx, blockHash); block != nil {
1271+
func (api *TransactionAPI) GetBlockTransactionCountByHash(ctx context.Context, blockHash common.Hash) (*hexutil.Uint, error) {
1272+
block, err := api.b.BlockByHash(ctx, blockHash)
1273+
if block != nil {
12721274
n := hexutil.Uint(len(block.Transactions()))
1273-
return &n
1275+
return &n, nil
12741276
}
1275-
return nil
1277+
return nil, err
12761278
}
12771279

12781280
// GetTransactionByBlockNumberAndIndex returns the transaction for the given block number and index.
1279-
func (api *TransactionAPI) GetTransactionByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index hexutil.Uint) *RPCTransaction {
1280-
if block, _ := api.b.BlockByNumber(ctx, blockNr); block != nil {
1281-
return newRPCTransactionFromBlockIndex(block, uint64(index), api.b.ChainConfig())
1281+
func (api *TransactionAPI) GetTransactionByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index hexutil.Uint) (*RPCTransaction, error) {
1282+
block, err := api.b.BlockByNumber(ctx, blockNr)
1283+
if block != nil {
1284+
return newRPCTransactionFromBlockIndex(block, uint64(index), api.b.ChainConfig()), nil
12821285
}
1283-
return nil
1286+
return nil, err
12841287
}
12851288

12861289
// GetTransactionByBlockHashAndIndex returns the transaction for the given block hash and index.
1287-
func (api *TransactionAPI) GetTransactionByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index hexutil.Uint) *RPCTransaction {
1288-
if block, _ := api.b.BlockByHash(ctx, blockHash); block != nil {
1289-
return newRPCTransactionFromBlockIndex(block, uint64(index), api.b.ChainConfig())
1290+
func (api *TransactionAPI) GetTransactionByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index hexutil.Uint) (*RPCTransaction, error) {
1291+
block, err := api.b.BlockByHash(ctx, blockHash)
1292+
if block != nil {
1293+
return newRPCTransactionFromBlockIndex(block, uint64(index), api.b.ChainConfig()), nil
12901294
}
1291-
return nil
1295+
return nil, err
12921296
}
12931297

12941298
// GetRawTransactionByBlockNumberAndIndex returns the bytes of the transaction for the given block number and index.

internal/ethapi/api_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,8 +520,12 @@ func (b testBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumber)
520520
if number == rpc.PendingBlockNumber {
521521
return b.pending, nil
522522
}
523+
if number == rpc.EarliestBlockNumber {
524+
number = 0
525+
}
523526
return b.chain.GetBlockByNumber(uint64(number)), nil
524527
}
528+
525529
func (b testBackend) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) {
526530
return b.chain.GetBlockByHash(hash), nil
527531
}
@@ -618,6 +622,9 @@ func (b testBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscripti
618622
func (b testBackend) NewMatcherBackend() filtermaps.MatcherBackend {
619623
panic("implement me")
620624
}
625+
626+
func (b testBackend) HistoryPruningCutoff() uint64 { return b.chain.HistoryPruningCutoff() }
627+
621628
func TestEstimateGas(t *testing.T) {
622629
t.Parallel()
623630
// Initialize test accounts

internal/ethapi/backend.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ type Backend interface {
8585

8686
ChainConfig() *params.ChainConfig
8787
Engine() consensus.Engine
88+
HistoryPruningCutoff() uint64
8889

8990
// This is copied from filters.Backend
9091
// eth/filters needs to be initialized from this backend type, so methods needed by

0 commit comments

Comments
 (0)