Skip to content

Commit

Permalink
rpc: change BlockNumber constant values to match ethclient (#27219)
Browse files Browse the repository at this point in the history
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 <fjl@twurst.com>
  • Loading branch information
holiman and fjl committed May 23, 2023
1 parent 1a18283 commit 9231770
Show file tree
Hide file tree
Showing 6 changed files with 54 additions and 47 deletions.
2 changes: 1 addition & 1 deletion eth/filters/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
3 changes: 3 additions & 0 deletions eth/filters/filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
27 changes: 15 additions & 12 deletions eth/filters/filter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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))
Expand All @@ -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())
Expand Down
18 changes: 7 additions & 11 deletions ethclient/ethclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -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("<invalid %d>", number)
}

func toCallArg(msg ethereum.CallMsg) interface{} {
Expand Down
19 changes: 8 additions & 11 deletions ethclient/gethclient/gethclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ package gethclient
import (
"context"
"encoding/json"
"fmt"
"math/big"
"runtime"
"runtime/debug"
Expand Down Expand Up @@ -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("<invalid %d>", number)
}

func toCallArg(msg ethereum.CallMsg) interface{} {
Expand Down
32 changes: 20 additions & 12 deletions rpc/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
)

Expand Down Expand Up @@ -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("<invalid %d>", 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"`
Expand Down

0 comments on commit 9231770

Please sign in to comment.