Skip to content
This repository was archived by the owner on Oct 11, 2024. It is now read-only.

Commit 30ffca4

Browse files
authored
Merge pull request #181 from 0xProject/fix/kovanBlockHashIssue
blockwatch/core: Fix Kovan incorrect block hash bug
2 parents c2e0dbd + 538ea42 commit 30ffca4

File tree

4 files changed

+68
-11
lines changed

4 files changed

+68
-11
lines changed

core/core.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ func New(config Config) (*App, error) {
8989
}
9090

9191
// Initialize block watcher (but don't start it yet).
92-
blockWatcherClient, err := blockwatch.NewRpcClient(ethClient, ethereumRPCRequestTimeout)
92+
blockWatcherClient, err := blockwatch.NewRpcClient(config.EthereumRPCURL, ethereumRPCRequestTimeout)
9393
if err != nil {
9494
return nil, err
9595
}
@@ -104,6 +104,16 @@ func New(config Config) (*App, error) {
104104
Client: blockWatcherClient,
105105
}
106106
blockWatcher := blockwatch.New(blockWatcherConfig)
107+
go func() {
108+
for {
109+
err, isOpen := <-blockWatcher.Errors
110+
if isOpen {
111+
log.WithField("error", err).Error("BlockWatcher error encountered")
112+
} else {
113+
return // Exit when the error channel is closed
114+
}
115+
}
116+
}()
107117

108118
// Initialize order watcher (but don't start it yet).
109119
orderWatcher, err := orderwatch.New(db, blockWatcher, ethClient, config.EthereumNetworkID, config.OrderExpirationBuffer)
@@ -257,6 +267,6 @@ func (app *App) Close() {
257267
if err := app.orderWatcher.Stop(); err != nil {
258268
log.WithField("error", err.Error()).Error("error while closing orderWatcher")
259269
}
260-
app.blockWatcher.StopPolling()
270+
app.blockWatcher.Stop()
261271
app.db.Close()
262272
}

ethereum/blockwatch/block_watcher.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,8 @@ func (w *Watcher) startPollingLoop() {
118118
}
119119
}
120120

121-
// StopPolling stops the block poller
122-
func (w *Watcher) StopPolling() {
121+
// stopPolling stops the block poller
122+
func (w *Watcher) stopPolling() {
123123
w.mu.Lock()
124124
defer w.mu.Unlock()
125125
w.isWatching = false
@@ -129,6 +129,14 @@ func (w *Watcher) StopPolling() {
129129
w.ticker = nil
130130
}
131131

132+
// Stop stops the BlockWatcher
133+
func (w *Watcher) Stop() {
134+
if w.isWatching {
135+
w.stopPolling()
136+
}
137+
close(w.Errors)
138+
}
139+
132140
// Subscribe allows one to subscribe to the block events emitted by the Watcher.
133141
// To unsubscribe, simply call `Unsubscribe` on the returned subscription.
134142
// The sink channel should have ample buffer space to avoid blocking other subscribers.

ethereum/blockwatch/block_watcher_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ func TestWatcherStartStop(t *testing.T) {
8585
}
8686
watcher := New(config)
8787
require.NoError(t, watcher.StartPolling())
88-
watcher.StopPolling()
88+
watcher.stopPolling()
8989
require.NoError(t, watcher.StartPolling())
90-
watcher.StopPolling()
90+
watcher.Stop()
9191
}

ethereum/blockwatch/client.go

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,18 @@ package blockwatch
22

33
import (
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.
2428
type 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.
3756
func (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

Comments
 (0)