From 9231770811cda0473a7fa4e2bccc95bf62aae634 Mon Sep 17 00:00:00 2001 From: Martin Holst Swende Date: Tue, 23 May 2023 07:18:38 -0400 Subject: [PATCH] rpc: change BlockNumber constant values to match ethclient (#27219) ethclient accepts certain negative block number values as specifiers for the "pending", "safe" and "finalized" block. In case of "pending", the value accepted by ethclient (-1) did not match rpc.PendingBlockNumber (-2). This wasn't really a problem, but other values accepted by ethclient did match the definitions in package rpc, and it's weird to have this one special case where they don't. To fix it, we decided to change the values of the constants rather than changing ethclient. The constant values are not otherwise significant. This is a breaking API change, but we believe not a dangerous one. --------- Co-authored-by: Felix Lange --- eth/filters/api_test.go | 2 +- eth/filters/filter.go | 3 +++ eth/filters/filter_test.go | 27 ++++++++++++++----------- ethclient/ethclient.go | 18 +++++++---------- ethclient/gethclient/gethclient.go | 19 ++++++++---------- rpc/types.go | 32 +++++++++++++++++++----------- 6 files changed, 54 insertions(+), 47 deletions(-) diff --git a/eth/filters/api_test.go b/eth/filters/api_test.go index 0a80d0f8ddbd..822bc826f6b0 100644 --- a/eth/filters/api_test.go +++ b/eth/filters/api_test.go @@ -56,7 +56,7 @@ func TestUnmarshalJSONNewFilterArgs(t *testing.T) { // from, to block number var test1 FilterCriteria - vector := fmt.Sprintf(`{"fromBlock":"%#x","toBlock":"%#x"}`, fromBlock, toBlock) + vector := fmt.Sprintf(`{"fromBlock":"%v","toBlock":"%v"}`, fromBlock, toBlock) if err := json.Unmarshal([]byte(vector), &test1); err != nil { t.Fatal(err) } diff --git a/eth/filters/filter.go b/eth/filters/filter.go index 8ba482817e0c..122676e96c81 100644 --- a/eth/filters/filter.go +++ b/eth/filters/filter.go @@ -296,6 +296,9 @@ func (f *Filter) checkMatches(ctx context.Context, header *types.Header) ([]*typ // pendingLogs returns the logs matching the filter criteria within the pending block. func (f *Filter) pendingLogs() ([]*types.Log, error) { block, receipts := f.sys.backend.PendingBlockAndReceipts() + if block == nil { + return nil, errors.New("pending state not available") + } if bloomFilter(block.Bloom(), f.addresses, f.topics) { var unfiltered []*types.Log for _, r := range receipts { diff --git a/eth/filters/filter_test.go b/eth/filters/filter_test.go index d10e0f1d9482..0e42c798ec9e 100644 --- a/eth/filters/filter_test.go +++ b/eth/filters/filter_test.go @@ -29,6 +29,7 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/params" + "github.com/ethereum/go-ethereum/rpc" ) func makeReceipt(addr common.Address) *types.Receipt { @@ -179,7 +180,7 @@ func TestFilters(t *testing.T) { // Set block 998 as Finalized (-3) rawdb.WriteFinalizedBlockHash(db, chain[998].Hash()) - filter := sys.NewRangeFilter(0, -1, []common.Address{addr}, [][]common.Hash{{hash1, hash2, hash3, hash4}}) + filter := sys.NewRangeFilter(0, int64(rpc.LatestBlockNumber), []common.Address{addr}, [][]common.Hash{{hash1, hash2, hash3, hash4}}) logs, _ := filter.Logs(context.Background()) if len(logs) != 4 { t.Error("expected 4 log, got", len(logs)) @@ -193,34 +194,36 @@ func TestFilters(t *testing.T) { sys.NewRangeFilter(900, 999, []common.Address{addr}, [][]common.Hash{{hash3}}), []common.Hash{hash3}, }, { - sys.NewRangeFilter(990, -1, []common.Address{addr}, [][]common.Hash{{hash3}}), + sys.NewRangeFilter(990, int64(rpc.LatestBlockNumber), []common.Address{addr}, [][]common.Hash{{hash3}}), []common.Hash{hash3}, }, { sys.NewRangeFilter(1, 10, nil, [][]common.Hash{{hash1, hash2}}), []common.Hash{hash1, hash2}, }, { - sys.NewRangeFilter(0, -1, nil, [][]common.Hash{{common.BytesToHash([]byte("fail"))}}), + sys.NewRangeFilter(0, int64(rpc.LatestBlockNumber), nil, [][]common.Hash{{common.BytesToHash([]byte("fail"))}}), nil, }, { - sys.NewRangeFilter(0, -1, []common.Address{common.BytesToAddress([]byte("failmenow"))}, nil), + sys.NewRangeFilter(0, int64(rpc.LatestBlockNumber), []common.Address{common.BytesToAddress([]byte("failmenow"))}, nil), nil, }, { - sys.NewRangeFilter(0, -1, nil, [][]common.Hash{{common.BytesToHash([]byte("fail"))}, {hash1}}), + sys.NewRangeFilter(0, int64(rpc.LatestBlockNumber), nil, [][]common.Hash{{common.BytesToHash([]byte("fail"))}, {hash1}}), nil, }, { - sys.NewRangeFilter(-1, -1, nil, nil), []common.Hash{hash4}, + sys.NewRangeFilter(int64(rpc.LatestBlockNumber), int64(rpc.LatestBlockNumber), nil, nil), []common.Hash{hash4}, }, { - sys.NewRangeFilter(-3, -1, nil, nil), []common.Hash{hash3, hash4}, + sys.NewRangeFilter(int64(rpc.FinalizedBlockNumber), int64(rpc.LatestBlockNumber), nil, nil), []common.Hash{hash3, hash4}, }, { - sys.NewRangeFilter(-3, -3, nil, nil), []common.Hash{hash3}, + sys.NewRangeFilter(int64(rpc.FinalizedBlockNumber), int64(rpc.FinalizedBlockNumber), nil, nil), []common.Hash{hash3}, }, { - sys.NewRangeFilter(-1, -3, nil, nil), nil, + sys.NewRangeFilter(int64(rpc.LatestBlockNumber), int64(rpc.FinalizedBlockNumber), nil, nil), nil, }, { - sys.NewRangeFilter(-4, -1, nil, nil), nil, + sys.NewRangeFilter(int64(rpc.SafeBlockNumber), int64(rpc.LatestBlockNumber), nil, nil), nil, }, { - sys.NewRangeFilter(-4, -4, nil, nil), nil, + sys.NewRangeFilter(int64(rpc.SafeBlockNumber), int64(rpc.SafeBlockNumber), nil, nil), nil, }, { - sys.NewRangeFilter(-1, -4, nil, nil), nil, + sys.NewRangeFilter(int64(rpc.LatestBlockNumber), int64(rpc.SafeBlockNumber), nil, nil), nil, + }, { + sys.NewRangeFilter(int64(rpc.PendingBlockNumber), int64(rpc.PendingBlockNumber), nil, nil), nil, }, } { logs, _ := tc.f.Logs(context.Background()) diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index 09d3f37d1ef2..59e95f629ab6 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -592,19 +592,15 @@ func toBlockNumArg(number *big.Int) string { if number == nil { return "latest" } - pending := big.NewInt(-1) - if number.Cmp(pending) == 0 { - return "pending" + if number.Sign() >= 0 { + return hexutil.EncodeBig(number) } - finalized := big.NewInt(int64(rpc.FinalizedBlockNumber)) - if number.Cmp(finalized) == 0 { - return "finalized" + // It's negative. + if number.IsInt64() { + return rpc.BlockNumber(number.Int64()).String() } - safe := big.NewInt(int64(rpc.SafeBlockNumber)) - if number.Cmp(safe) == 0 { - return "safe" - } - return hexutil.EncodeBig(number) + // It's negative and large, which is invalid. + return fmt.Sprintf("", number) } func toCallArg(msg ethereum.CallMsg) interface{} { diff --git a/ethclient/gethclient/gethclient.go b/ethclient/gethclient/gethclient.go index 718cdfea426a..c029611678f0 100644 --- a/ethclient/gethclient/gethclient.go +++ b/ethclient/gethclient/gethclient.go @@ -20,6 +20,7 @@ package gethclient import ( "context" "encoding/json" + "fmt" "math/big" "runtime" "runtime/debug" @@ -207,19 +208,15 @@ func toBlockNumArg(number *big.Int) string { if number == nil { return "latest" } - pending := big.NewInt(-1) - if number.Cmp(pending) == 0 { - return "pending" + if number.Sign() >= 0 { + return hexutil.EncodeBig(number) } - finalized := big.NewInt(int64(rpc.FinalizedBlockNumber)) - if number.Cmp(finalized) == 0 { - return "finalized" + // It's negative. + if number.IsInt64() { + return rpc.BlockNumber(number.Int64()).String() } - safe := big.NewInt(int64(rpc.SafeBlockNumber)) - if number.Cmp(safe) == 0 { - return "safe" - } - return hexutil.EncodeBig(number) + // It's negative and large, which is invalid. + return fmt.Sprintf("", number) } func toCallArg(msg ethereum.CallMsg) interface{} { diff --git a/rpc/types.go b/rpc/types.go index 55d11fbaaf14..34a1451deaa7 100644 --- a/rpc/types.go +++ b/rpc/types.go @@ -65,8 +65,8 @@ type BlockNumber int64 const ( SafeBlockNumber = BlockNumber(-4) FinalizedBlockNumber = BlockNumber(-3) - PendingBlockNumber = BlockNumber(-2) - LatestBlockNumber = BlockNumber(-1) + LatestBlockNumber = BlockNumber(-2) + PendingBlockNumber = BlockNumber(-1) EarliestBlockNumber = BlockNumber(0) ) @@ -111,30 +111,38 @@ func (bn *BlockNumber) UnmarshalJSON(data []byte) error { return nil } +// Int64 returns the block number as int64. +func (bn BlockNumber) Int64() int64 { + return (int64)(bn) +} + // MarshalText implements encoding.TextMarshaler. It marshals: // - "safe", "finalized", "latest", "earliest" or "pending" as strings // - other numbers as hex func (bn BlockNumber) MarshalText() ([]byte, error) { + return []byte(bn.String()), nil +} + +func (bn BlockNumber) String() string { switch bn { case EarliestBlockNumber: - return []byte("earliest"), nil + return "earliest" case LatestBlockNumber: - return []byte("latest"), nil + return "latest" case PendingBlockNumber: - return []byte("pending"), nil + return "pending" case FinalizedBlockNumber: - return []byte("finalized"), nil + return "finalized" case SafeBlockNumber: - return []byte("safe"), nil + return "safe" default: - return hexutil.Uint64(bn).MarshalText() + if bn < 0 { + return fmt.Sprintf("", bn) + } + return hexutil.Uint64(bn).String() } } -func (bn BlockNumber) Int64() int64 { - return (int64)(bn) -} - type BlockNumberOrHash struct { BlockNumber *BlockNumber `json:"blockNumber,omitempty"` BlockHash *common.Hash `json:"blockHash,omitempty"`