internal/ethapi: support block number or hash on state-related methods (#19491)
This change adds support for EIP-1898.
This commit is contained in:
committed by
Felix Lange
parent
62391ddbeb
commit
ad03d9801c
93
rpc/types.go
93
rpc/types.go
@ -18,10 +18,12 @@ package rpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
)
|
||||
|
||||
@ -105,3 +107,94 @@ func (bn *BlockNumber) UnmarshalJSON(data []byte) error {
|
||||
func (bn BlockNumber) Int64() int64 {
|
||||
return (int64)(bn)
|
||||
}
|
||||
|
||||
type BlockNumberOrHash struct {
|
||||
BlockNumber *BlockNumber `json:"blockNumber,omitempty"`
|
||||
BlockHash *common.Hash `json:"blockHash,omitempty"`
|
||||
RequireCanonical bool `json:"requireCanonical,omitempty"`
|
||||
}
|
||||
|
||||
func (bnh *BlockNumberOrHash) UnmarshalJSON(data []byte) error {
|
||||
type erased BlockNumberOrHash
|
||||
e := erased{}
|
||||
err := json.Unmarshal(data, &e)
|
||||
if err == nil {
|
||||
if e.BlockNumber != nil && e.BlockHash != nil {
|
||||
return fmt.Errorf("cannot specify both BlockHash and BlockNumber, choose one or the other")
|
||||
}
|
||||
bnh.BlockNumber = e.BlockNumber
|
||||
bnh.BlockHash = e.BlockHash
|
||||
bnh.RequireCanonical = e.RequireCanonical
|
||||
return nil
|
||||
}
|
||||
var input string
|
||||
err = json.Unmarshal(data, &input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
switch input {
|
||||
case "earliest":
|
||||
bn := EarliestBlockNumber
|
||||
bnh.BlockNumber = &bn
|
||||
return nil
|
||||
case "latest":
|
||||
bn := LatestBlockNumber
|
||||
bnh.BlockNumber = &bn
|
||||
return nil
|
||||
case "pending":
|
||||
bn := PendingBlockNumber
|
||||
bnh.BlockNumber = &bn
|
||||
return nil
|
||||
default:
|
||||
if len(input) == 66 {
|
||||
hash := common.Hash{}
|
||||
err := hash.UnmarshalText([]byte(input))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bnh.BlockHash = &hash
|
||||
return nil
|
||||
} else {
|
||||
blckNum, err := hexutil.DecodeUint64(input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if blckNum > math.MaxInt64 {
|
||||
return fmt.Errorf("blocknumber too high")
|
||||
}
|
||||
bn := BlockNumber(blckNum)
|
||||
bnh.BlockNumber = &bn
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (bnh *BlockNumberOrHash) Number() (BlockNumber, bool) {
|
||||
if bnh.BlockNumber != nil {
|
||||
return *bnh.BlockNumber, true
|
||||
}
|
||||
return BlockNumber(0), false
|
||||
}
|
||||
|
||||
func (bnh *BlockNumberOrHash) Hash() (common.Hash, bool) {
|
||||
if bnh.BlockHash != nil {
|
||||
return *bnh.BlockHash, true
|
||||
}
|
||||
return common.Hash{}, false
|
||||
}
|
||||
|
||||
func BlockNumberOrHashWithNumber(blockNr BlockNumber) BlockNumberOrHash {
|
||||
return BlockNumberOrHash{
|
||||
BlockNumber: &blockNr,
|
||||
BlockHash: nil,
|
||||
RequireCanonical: false,
|
||||
}
|
||||
}
|
||||
|
||||
func BlockNumberOrHashWithHash(hash common.Hash, canonical bool) BlockNumberOrHash {
|
||||
return BlockNumberOrHash{
|
||||
BlockNumber: nil,
|
||||
BlockHash: &hash,
|
||||
RequireCanonical: canonical,
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
)
|
||||
|
||||
@ -64,3 +65,60 @@ func TestBlockNumberJSONUnmarshal(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBlockNumberOrHash_UnmarshalJSON(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
mustFail bool
|
||||
expected BlockNumberOrHash
|
||||
}{
|
||||
0: {`"0x"`, true, BlockNumberOrHash{}},
|
||||
1: {`"0x0"`, false, BlockNumberOrHashWithNumber(0)},
|
||||
2: {`"0X1"`, false, BlockNumberOrHashWithNumber(1)},
|
||||
3: {`"0x00"`, true, BlockNumberOrHash{}},
|
||||
4: {`"0x01"`, true, BlockNumberOrHash{}},
|
||||
5: {`"0x1"`, false, BlockNumberOrHashWithNumber(1)},
|
||||
6: {`"0x12"`, false, BlockNumberOrHashWithNumber(18)},
|
||||
7: {`"0x7fffffffffffffff"`, false, BlockNumberOrHashWithNumber(math.MaxInt64)},
|
||||
8: {`"0x8000000000000000"`, true, BlockNumberOrHash{}},
|
||||
9: {"0", true, BlockNumberOrHash{}},
|
||||
10: {`"ff"`, true, BlockNumberOrHash{}},
|
||||
11: {`"pending"`, false, BlockNumberOrHashWithNumber(PendingBlockNumber)},
|
||||
12: {`"latest"`, false, BlockNumberOrHashWithNumber(LatestBlockNumber)},
|
||||
13: {`"earliest"`, false, BlockNumberOrHashWithNumber(EarliestBlockNumber)},
|
||||
14: {`someString`, true, BlockNumberOrHash{}},
|
||||
15: {`""`, true, BlockNumberOrHash{}},
|
||||
16: {``, true, BlockNumberOrHash{}},
|
||||
17: {`"0x0000000000000000000000000000000000000000000000000000000000000000"`, false, BlockNumberOrHashWithHash(common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), false)},
|
||||
18: {`{"blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000"}`, false, BlockNumberOrHashWithHash(common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), false)},
|
||||
19: {`{"blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","requireCanonical":false}`, false, BlockNumberOrHashWithHash(common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), false)},
|
||||
20: {`{"blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","requireCanonical":true}`, false, BlockNumberOrHashWithHash(common.HexToHash("0x0000000000000000000000000000000000000000000000000000000000000000"), true)},
|
||||
21: {`{"blockNumber":"0x1"}`, false, BlockNumberOrHashWithNumber(1)},
|
||||
22: {`{"blockNumber":"pending"}`, false, BlockNumberOrHashWithNumber(PendingBlockNumber)},
|
||||
23: {`{"blockNumber":"latest"}`, false, BlockNumberOrHashWithNumber(LatestBlockNumber)},
|
||||
24: {`{"blockNumber":"earliest"}`, false, BlockNumberOrHashWithNumber(EarliestBlockNumber)},
|
||||
25: {`{"blockNumber":"0x1", "blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000"}`, true, BlockNumberOrHash{}},
|
||||
}
|
||||
|
||||
for i, test := range tests {
|
||||
var bnh BlockNumberOrHash
|
||||
err := json.Unmarshal([]byte(test.input), &bnh)
|
||||
if test.mustFail && err == nil {
|
||||
t.Errorf("Test %d should fail", i)
|
||||
continue
|
||||
}
|
||||
if !test.mustFail && err != nil {
|
||||
t.Errorf("Test %d should pass but got err: %v", i, err)
|
||||
continue
|
||||
}
|
||||
hash, hashOk := bnh.Hash()
|
||||
expectedHash, expectedHashOk := test.expected.Hash()
|
||||
num, numOk := bnh.Number()
|
||||
expectedNum, expectedNumOk := test.expected.Number()
|
||||
if bnh.RequireCanonical != test.expected.RequireCanonical ||
|
||||
hash != expectedHash || hashOk != expectedHashOk ||
|
||||
num != expectedNum || numOk != expectedNumOk {
|
||||
t.Errorf("Test %d got unexpected value, want %v, got %v", i, test.expected, bnh)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user