@@ -2,14 +2,18 @@ package blockwatch
22
33import (
44 "context"
5+ "errors"
6+ "fmt"
57 "math/big"
68 "time"
79
810 "github.com/0xProject/0x-mesh/meshdb"
911 "github.com/ethereum/go-ethereum"
1012 "github.com/ethereum/go-ethereum/common"
13+ "github.com/ethereum/go-ethereum/common/math"
1114 "github.com/ethereum/go-ethereum/core/types"
1215 "github.com/ethereum/go-ethereum/ethclient"
16+ "github.com/ethereum/go-ethereum/rpc"
1317)
1418
1519// Client defines the methods needed to satisfy the client expected when
@@ -22,29 +26,64 @@ type Client interface {
2226
2327// RpcClient is a Client for fetching Ethereum blocks from a specific JSON-RPC endpoint.
2428type RpcClient struct {
29+ rpcClient * rpc.Client
2530 client * ethclient.Client
2631 requestTimeout time.Duration
2732}
2833
2934// NewRpcClient returns a new Client for fetching Ethereum blocks using the given
3035// ethclient.Client.
31- func NewRpcClient (ethClient * ethclient.Client , requestTimeout time.Duration ) (* RpcClient , error ) {
32- return & RpcClient {ethClient , requestTimeout }, nil
36+ func NewRpcClient (rpcURL string , requestTimeout time.Duration ) (* RpcClient , error ) {
37+ ethClient , err := ethclient .Dial (rpcURL )
38+ if err != nil {
39+ return nil , err
40+ }
41+ rpcClient , err := rpc .Dial (rpcURL )
42+ if err != nil {
43+ return nil , err
44+ }
45+ return & RpcClient {rpcClient : rpcClient , client : ethClient , requestTimeout : requestTimeout }, nil
46+ }
47+
48+ type GetBlockByNumberResponse struct {
49+ Hash common.Hash `json:"hash"`
50+ ParentHash common.Hash `json:"parentHash"`
51+ Number string `json:"number"`
3352}
3453
3554// HeaderByNumber fetches a block header by its number. If no `number` is supplied, it will return the latest
3655// block header. If no block exists with this number it will return a `ethereum.NotFound` error.
3756func (rc * RpcClient ) HeaderByNumber (number * big.Int ) (* meshdb.MiniHeader , error ) {
3857 ctx , cancel := context .WithTimeout (context .Background (), rc .requestTimeout )
3958 defer cancel ()
40- header , err := rc .client .HeaderByNumber (ctx , number )
59+
60+ var blockParam string
61+ if number == nil {
62+ blockParam = "latest"
63+ } else {
64+ blockParam = fmt .Sprintf ("0x%s" , common .Bytes2Hex (number .Bytes ()))
65+ }
66+ shouldIncludeTransactions := false
67+
68+ // Note(fabio): We use a raw RPC call here instead of `EthClient`'s `BlockByNumber()` method because block
69+ // hashes are computed differently on Kovan vs. mainnet, resulting in the wrong block hash being returned by
70+ // `BlockByNumber` when using Kovan. By doing a raw RPC call, we can simply use the blockHash returned in the
71+ // RPC response rather than re-compute it from the block header.
72+ // Source: https://github.com/ethereum/go-ethereum/pull/18166
73+ var header GetBlockByNumberResponse
74+ err := rc .rpcClient .CallContext (ctx , & header , "eth_getBlockByNumber" , blockParam , shouldIncludeTransactions )
4175 if err != nil {
4276 return nil , err
4377 }
78+
79+ blockNum , ok := math .ParseBig256 (header .Number )
80+ if ! ok {
81+ return nil , errors .New ("Failed to parse big.Int value from hex-encoded block number returned from eth_getBlockByNumber" )
82+ }
4483 miniHeader := & meshdb.MiniHeader {
45- Hash : header .Hash () ,
84+ Hash : header .Hash ,
4685 Parent : header .ParentHash ,
47- Number : header . Number ,
86+ Number : blockNum ,
4887 }
4988 return miniHeader , nil
5089}
0 commit comments