mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-02-26 07:37:20 +00:00
eth, eth/filters: implement API error code for pruned blocks (#31361)
Implements #31275 --------- Co-authored-by: Jared Wasinger <j-wasinger@hotmail.com> Co-authored-by: Felix Lange <fjl@twurst.com>
This commit is contained in:
parent
f0cdc40ceb
commit
bc36f2de83
12 changed files with 128 additions and 38 deletions
|
|
@ -86,6 +86,11 @@ func (bc *BlockChain) GetHeaderByNumber(number uint64) *types.Header {
|
|||
return bc.hc.GetHeaderByNumber(number)
|
||||
}
|
||||
|
||||
// GetBlockNumber retrieves the block number associated with a block hash.
|
||||
func (bc *BlockChain) GetBlockNumber(hash common.Hash) *uint64 {
|
||||
return bc.hc.GetBlockNumber(hash)
|
||||
}
|
||||
|
||||
// GetHeadersFrom returns a contiguous segment of headers, in rlp-form, going
|
||||
// backwards from the given number.
|
||||
func (bc *BlockChain) GetHeadersFrom(number, count uint64) []rlp.RawValue {
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core/txpool"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||
"github.com/ethereum/go-ethereum/eth/gasprice"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
|
|
@ -91,7 +92,13 @@ func (b *EthAPIBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumb
|
|||
}
|
||||
return block, nil
|
||||
}
|
||||
return b.eth.blockchain.GetHeaderByNumber(uint64(number)), nil
|
||||
var bn uint64
|
||||
if number == rpc.EarliestBlockNumber {
|
||||
bn = b.eth.blockchain.HistoryPruningCutoff()
|
||||
} else {
|
||||
bn = uint64(number)
|
||||
}
|
||||
return b.eth.blockchain.GetHeaderByNumber(bn), nil
|
||||
}
|
||||
|
||||
func (b *EthAPIBackend) HeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*types.Header, error) {
|
||||
|
|
@ -143,11 +150,27 @@ func (b *EthAPIBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumbe
|
|||
}
|
||||
return b.eth.blockchain.GetBlock(header.Hash(), header.Number.Uint64()), nil
|
||||
}
|
||||
return b.eth.blockchain.GetBlockByNumber(uint64(number)), nil
|
||||
bn := uint64(number) // the resolved number
|
||||
if number == rpc.EarliestBlockNumber {
|
||||
bn = b.eth.blockchain.HistoryPruningCutoff()
|
||||
}
|
||||
block := b.eth.blockchain.GetBlockByNumber(bn)
|
||||
if block == nil && bn < b.eth.blockchain.HistoryPruningCutoff() {
|
||||
return nil, ðconfig.PrunedHistoryError{}
|
||||
}
|
||||
return block, nil
|
||||
}
|
||||
|
||||
func (b *EthAPIBackend) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) {
|
||||
return b.eth.blockchain.GetBlockByHash(hash), nil
|
||||
number := b.eth.blockchain.GetBlockNumber(hash)
|
||||
if number == nil {
|
||||
return nil, nil
|
||||
}
|
||||
block := b.eth.blockchain.GetBlock(hash, *number)
|
||||
if block == nil && *number < b.eth.blockchain.HistoryPruningCutoff() {
|
||||
return nil, ðconfig.PrunedHistoryError{}
|
||||
}
|
||||
return block, nil
|
||||
}
|
||||
|
||||
// GetBody returns body of a block. It does not resolve special block numbers.
|
||||
|
|
@ -155,10 +178,14 @@ func (b *EthAPIBackend) GetBody(ctx context.Context, hash common.Hash, number rp
|
|||
if number < 0 || hash == (common.Hash{}) {
|
||||
return nil, errors.New("invalid arguments; expect hash and no special block numbers")
|
||||
}
|
||||
if body := b.eth.blockchain.GetBody(hash); body != nil {
|
||||
return body, nil
|
||||
body := b.eth.blockchain.GetBody(hash)
|
||||
if body == nil {
|
||||
if uint64(number) < b.eth.blockchain.HistoryPruningCutoff() {
|
||||
return nil, ðconfig.PrunedHistoryError{}
|
||||
}
|
||||
return nil, errors.New("block body not found")
|
||||
}
|
||||
return body, nil
|
||||
}
|
||||
|
||||
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
|
|||
}
|
||||
block := b.eth.blockchain.GetBlock(hash, header.Number.Uint64())
|
||||
if block == nil {
|
||||
if header.Number.Uint64() < b.eth.blockchain.HistoryPruningCutoff() {
|
||||
return nil, ðconfig.PrunedHistoryError{}
|
||||
}
|
||||
return nil, errors.New("header found, but block body is missing")
|
||||
}
|
||||
return block, nil
|
||||
|
|
@ -234,6 +264,10 @@ func (b *EthAPIBackend) StateAndHeaderByNumberOrHash(ctx context.Context, blockN
|
|||
return nil, nil, errors.New("invalid arguments; neither block nor hash specified")
|
||||
}
|
||||
|
||||
func (b *EthAPIBackend) HistoryPruningCutoff() uint64 {
|
||||
return b.eth.blockchain.HistoryPruningCutoff()
|
||||
}
|
||||
|
||||
func (b *EthAPIBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
|
||||
return b.eth.blockchain.GetReceiptsByHash(hash), nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -90,3 +90,9 @@ var HistoryPrunePoints = map[common.Hash]*HistoryPrunePoint{
|
|||
BlockHash: common.HexToHash("0x229f6b18ca1552f1d5146deceb5387333f40dc6275aebee3f2c5c4ece07d02db"),
|
||||
},
|
||||
}
|
||||
|
||||
// PrunedHistoryError is returned when the requested history is pruned.
|
||||
type PrunedHistoryError struct{}
|
||||
|
||||
func (e *PrunedHistoryError) Error() string { return "pruned history unavailable" }
|
||||
func (e *PrunedHistoryError) ErrorCode() int { return 4444 }
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||
"github.com/ethereum/go-ethereum/internal/ethapi"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
)
|
||||
|
|
@ -354,9 +355,13 @@ func (api *FilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([]*type
|
|||
if crit.ToBlock != nil {
|
||||
end = crit.ToBlock.Int64()
|
||||
}
|
||||
// Block numbers below 0 are special cases.
|
||||
if begin > 0 && end > 0 && begin > end {
|
||||
return nil, errInvalidBlockRange
|
||||
}
|
||||
if begin > 0 && begin < int64(api.events.backend.HistoryPruningCutoff()) {
|
||||
return nil, ðconfig.PrunedHistoryError{}
|
||||
}
|
||||
// Construct the range filter
|
||||
filter = api.sys.NewRangeFilter(begin, end, crit.Addresses, crit.Topics)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/filtermaps"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
)
|
||||
|
|
@ -86,6 +87,9 @@ func (f *Filter) Logs(ctx context.Context) ([]*types.Log, error) {
|
|||
if header == nil {
|
||||
return nil, errors.New("unknown block")
|
||||
}
|
||||
if header.Number.Uint64() < f.sys.backend.HistoryPruningCutoff() {
|
||||
return nil, ðconfig.PrunedHistoryError{}
|
||||
}
|
||||
return f.blockLogs(ctx, header)
|
||||
}
|
||||
|
||||
|
|
@ -114,12 +118,20 @@ func (f *Filter) Logs(ctx context.Context) ([]*types.Log, error) {
|
|||
return 0, errors.New("safe header not found")
|
||||
}
|
||||
return hdr.Number.Uint64(), nil
|
||||
case rpc.EarliestBlockNumber.Int64():
|
||||
earliest := f.sys.backend.HistoryPruningCutoff()
|
||||
hdr, _ := f.sys.backend.HeaderByNumber(ctx, rpc.BlockNumber(earliest))
|
||||
if hdr == nil {
|
||||
return 0, errors.New("earliest header not found")
|
||||
}
|
||||
return hdr.Number.Uint64(), nil
|
||||
default:
|
||||
if number < 0 {
|
||||
return 0, errors.New("negative block number")
|
||||
}
|
||||
return uint64(number), nil
|
||||
}
|
||||
}
|
||||
|
||||
// range query need to resolve the special begin/end block number
|
||||
begin, err := resolveSpecial(f.begin)
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/filtermaps"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
|
|
@ -64,6 +65,7 @@ type Backend interface {
|
|||
|
||||
CurrentHeader() *types.Header
|
||||
ChainConfig() *params.ChainConfig
|
||||
HistoryPruningCutoff() uint64
|
||||
SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription
|
||||
SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription
|
||||
SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription
|
||||
|
|
@ -304,6 +306,14 @@ func (es *EventSystem) SubscribeLogs(crit ethereum.FilterQuery, logs chan []*typ
|
|||
return nil, errPendingLogsUnsupported
|
||||
}
|
||||
|
||||
if from == rpc.EarliestBlockNumber {
|
||||
from = rpc.BlockNumber(es.backend.HistoryPruningCutoff())
|
||||
}
|
||||
// Queries beyond the pruning cutoff are not supported.
|
||||
if uint64(from) < es.backend.HistoryPruningCutoff() {
|
||||
return nil, ðconfig.PrunedHistoryError{}
|
||||
}
|
||||
|
||||
// only interested in new mined logs
|
||||
if from == rpc.LatestBlockNumber && to == rpc.LatestBlockNumber {
|
||||
return es.subscribeLogs(crit, logs), nil
|
||||
|
|
|
|||
|
|
@ -181,6 +181,10 @@ func (b *testBackend) setPending(block *types.Block, receipts types.Receipts) {
|
|||
b.pendingReceipts = receipts
|
||||
}
|
||||
|
||||
func (b *testBackend) HistoryPruningCutoff() uint64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func newTestFilterSystem(db ethdb.Database, cfg Config) (*testBackend, *FilterSystem) {
|
||||
backend := &testBackend{db: db}
|
||||
sys := NewFilterSystem(backend, cfg)
|
||||
|
|
|
|||
|
|
@ -549,21 +549,23 @@ func (api *BlockChainAPI) GetUncleByBlockHashAndIndex(ctx context.Context, block
|
|||
}
|
||||
|
||||
// GetUncleCountByBlockNumber returns number of uncles in the block for the given block number
|
||||
func (api *BlockChainAPI) GetUncleCountByBlockNumber(ctx context.Context, blockNr rpc.BlockNumber) *hexutil.Uint {
|
||||
if block, _ := api.b.BlockByNumber(ctx, blockNr); block != nil {
|
||||
func (api *BlockChainAPI) GetUncleCountByBlockNumber(ctx context.Context, blockNr rpc.BlockNumber) (*hexutil.Uint, error) {
|
||||
block, err := api.b.BlockByNumber(ctx, blockNr)
|
||||
if block != nil {
|
||||
n := hexutil.Uint(len(block.Uncles()))
|
||||
return &n
|
||||
return &n, nil
|
||||
}
|
||||
return nil
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// GetUncleCountByBlockHash returns number of uncles in the block for the given block hash
|
||||
func (api *BlockChainAPI) GetUncleCountByBlockHash(ctx context.Context, blockHash common.Hash) *hexutil.Uint {
|
||||
if block, _ := api.b.BlockByHash(ctx, blockHash); block != nil {
|
||||
func (api *BlockChainAPI) GetUncleCountByBlockHash(ctx context.Context, blockHash common.Hash) (*hexutil.Uint, error) {
|
||||
block, err := api.b.BlockByHash(ctx, blockHash)
|
||||
if block != nil {
|
||||
n := hexutil.Uint(len(block.Uncles()))
|
||||
return &n
|
||||
return &n, nil
|
||||
}
|
||||
return nil
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 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
|
|||
func (api *BlockChainAPI) GetBlockReceipts(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) ([]map[string]interface{}, error) {
|
||||
block, err := api.b.BlockByNumberOrHash(ctx, blockNrOrHash)
|
||||
if block == nil || err != nil {
|
||||
// When the block doesn't exist, the RPC method should return JSON null
|
||||
// as per specification.
|
||||
return nil, nil
|
||||
return nil, err
|
||||
}
|
||||
receipts, err := api.b.GetReceipts(ctx, block.Hash())
|
||||
if err != nil {
|
||||
|
|
@ -1258,37 +1258,41 @@ func NewTransactionAPI(b Backend, nonceLock *AddrLocker) *TransactionAPI {
|
|||
}
|
||||
|
||||
// GetBlockTransactionCountByNumber returns the number of transactions in the block with the given block number.
|
||||
func (api *TransactionAPI) GetBlockTransactionCountByNumber(ctx context.Context, blockNr rpc.BlockNumber) *hexutil.Uint {
|
||||
if block, _ := api.b.BlockByNumber(ctx, blockNr); block != nil {
|
||||
func (api *TransactionAPI) GetBlockTransactionCountByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*hexutil.Uint, error) {
|
||||
block, err := api.b.BlockByNumber(ctx, blockNr)
|
||||
if block != nil {
|
||||
n := hexutil.Uint(len(block.Transactions()))
|
||||
return &n
|
||||
return &n, nil
|
||||
}
|
||||
return nil
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// GetBlockTransactionCountByHash returns the number of transactions in the block with the given hash.
|
||||
func (api *TransactionAPI) GetBlockTransactionCountByHash(ctx context.Context, blockHash common.Hash) *hexutil.Uint {
|
||||
if block, _ := api.b.BlockByHash(ctx, blockHash); block != nil {
|
||||
func (api *TransactionAPI) GetBlockTransactionCountByHash(ctx context.Context, blockHash common.Hash) (*hexutil.Uint, error) {
|
||||
block, err := api.b.BlockByHash(ctx, blockHash)
|
||||
if block != nil {
|
||||
n := hexutil.Uint(len(block.Transactions()))
|
||||
return &n
|
||||
return &n, nil
|
||||
}
|
||||
return nil
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// GetTransactionByBlockNumberAndIndex returns the transaction for the given block number and index.
|
||||
func (api *TransactionAPI) GetTransactionByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index hexutil.Uint) *RPCTransaction {
|
||||
if block, _ := api.b.BlockByNumber(ctx, blockNr); block != nil {
|
||||
return newRPCTransactionFromBlockIndex(block, uint64(index), api.b.ChainConfig())
|
||||
func (api *TransactionAPI) GetTransactionByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index hexutil.Uint) (*RPCTransaction, error) {
|
||||
block, err := api.b.BlockByNumber(ctx, blockNr)
|
||||
if block != nil {
|
||||
return newRPCTransactionFromBlockIndex(block, uint64(index), api.b.ChainConfig()), nil
|
||||
}
|
||||
return nil
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// GetTransactionByBlockHashAndIndex returns the transaction for the given block hash and index.
|
||||
func (api *TransactionAPI) GetTransactionByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index hexutil.Uint) *RPCTransaction {
|
||||
if block, _ := api.b.BlockByHash(ctx, blockHash); block != nil {
|
||||
return newRPCTransactionFromBlockIndex(block, uint64(index), api.b.ChainConfig())
|
||||
func (api *TransactionAPI) GetTransactionByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index hexutil.Uint) (*RPCTransaction, error) {
|
||||
block, err := api.b.BlockByHash(ctx, blockHash)
|
||||
if block != nil {
|
||||
return newRPCTransactionFromBlockIndex(block, uint64(index), api.b.ChainConfig()), nil
|
||||
}
|
||||
return nil
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// GetRawTransactionByBlockNumberAndIndex returns the bytes of the transaction for the given block number and index.
|
||||
|
|
|
|||
|
|
@ -520,8 +520,12 @@ func (b testBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumber)
|
|||
if number == rpc.PendingBlockNumber {
|
||||
return b.pending, nil
|
||||
}
|
||||
if number == rpc.EarliestBlockNumber {
|
||||
number = 0
|
||||
}
|
||||
return b.chain.GetBlockByNumber(uint64(number)), nil
|
||||
}
|
||||
|
||||
func (b testBackend) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) {
|
||||
return b.chain.GetBlockByHash(hash), nil
|
||||
}
|
||||
|
|
@ -618,6 +622,9 @@ func (b testBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscripti
|
|||
func (b testBackend) NewMatcherBackend() filtermaps.MatcherBackend {
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func (b testBackend) HistoryPruningCutoff() uint64 { return b.chain.HistoryPruningCutoff() }
|
||||
|
||||
func TestEstimateGas(t *testing.T) {
|
||||
t.Parallel()
|
||||
// Initialize test accounts
|
||||
|
|
|
|||
|
|
@ -85,6 +85,7 @@ type Backend interface {
|
|||
|
||||
ChainConfig() *params.ChainConfig
|
||||
Engine() consensus.Engine
|
||||
HistoryPruningCutoff() uint64
|
||||
|
||||
// This is copied from filters.Backend
|
||||
// eth/filters needs to be initialized from this backend type, so methods needed by
|
||||
|
|
|
|||
|
|
@ -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) HistoryPruningCutoff() uint64 { return 0 }
|
||||
|
|
|
|||
|
|
@ -63,11 +63,11 @@ type jsonWriter interface {
|
|||
type BlockNumber int64
|
||||
|
||||
const (
|
||||
EarliestBlockNumber = BlockNumber(-5)
|
||||
SafeBlockNumber = BlockNumber(-4)
|
||||
FinalizedBlockNumber = BlockNumber(-3)
|
||||
LatestBlockNumber = BlockNumber(-2)
|
||||
PendingBlockNumber = BlockNumber(-1)
|
||||
EarliestBlockNumber = BlockNumber(0)
|
||||
)
|
||||
|
||||
// UnmarshalJSON parses the given JSON fragment into a BlockNumber. It supports:
|
||||
|
|
|
|||
Loading…
Reference in a new issue