Skip to content

Commit fdf3dd8

Browse files
committed
chore(chainsync): add unit test for reorgs
1 parent 2fd714a commit fdf3dd8

File tree

10 files changed

+308
-13
lines changed

10 files changed

+308
-13
lines changed

rolling-shutter/medley/chainsync/client.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ var noopLogger = &logger.NoopLogger{}
2424
var ErrServiceNotInstantiated = errors.New("service is not instantiated, pass a handler function option")
2525

2626
type Client struct {
27-
client.EthereumClient
27+
client.SyncEthereumClient
2828
log log.Logger
2929

3030
options *options
@@ -136,7 +136,7 @@ func (s *Client) BroadcastEonKey(ctx context.Context, eon uint64, eonPubKey []by
136136
// This value is cached, since it is not expected to change.
137137
func (s *Client) ChainID(ctx context.Context) (*big.Int, error) {
138138
if s.chainID == nil {
139-
cid, err := s.EthereumClient.ChainID(ctx)
139+
cid, err := s.SyncEthereumClient.ChainID(ctx)
140140
if err != nil {
141141
return nil, err
142142
}

rolling-shutter/medley/chainsync/client/client.go

+14-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import (
99
"github.com/ethereum/go-ethereum/core/types"
1010
)
1111

12-
type EthereumClient interface {
12+
type FullEthereumClient interface {
1313
Close()
1414
ChainID(ctx context.Context) (*big.Int, error)
1515
BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error)
@@ -45,3 +45,16 @@ type EthereumClient interface {
4545
EstimateGas(ctx context.Context, msg ethereum.CallMsg) (uint64, error)
4646
SendTransaction(ctx context.Context, tx *types.Transaction) error
4747
}
48+
49+
type SyncEthereumClient interface {
50+
Close()
51+
ChainID(ctx context.Context) (*big.Int, error)
52+
BlockNumber(ctx context.Context) (uint64, error)
53+
HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error)
54+
HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error)
55+
SubscribeNewHead(ctx context.Context, ch chan<- *types.Header) (ethereum.Subscription, error)
56+
FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error)
57+
SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error)
58+
CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error)
59+
TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error)
60+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
package client
2+
3+
import (
4+
"context"
5+
"errors"
6+
"math/big"
7+
8+
"github.com/ethereum/go-ethereum"
9+
"github.com/ethereum/go-ethereum/common"
10+
"github.com/ethereum/go-ethereum/core/types"
11+
)
12+
13+
var ErrNotImplemented = errors.New("not implemented")
14+
15+
var _ SyncEthereumClient = &TestClient{}
16+
17+
type TestClient struct {
18+
headerChain []*types.Header
19+
latestHeadIndex int
20+
intialProgress bool
21+
latestHeadEmitter []chan<- *types.Header
22+
latestHeadSubscription []*Subscription
23+
}
24+
25+
func NewSubscription(idx int) *Subscription {
26+
return &Subscription{
27+
idx: idx,
28+
err: make(chan error, 1),
29+
}
30+
}
31+
32+
type Subscription struct {
33+
idx int
34+
err chan error
35+
}
36+
37+
func (su *Subscription) Unsubscribe() {
38+
// TODO: not implemented yet, but we don't want to panic
39+
}
40+
41+
func (su *Subscription) Err() <-chan error {
42+
return su.err
43+
}
44+
45+
type TestClientController struct {
46+
c *TestClient
47+
}
48+
49+
func NewTestClient() (*TestClient, *TestClientController) {
50+
c := &TestClient{
51+
headerChain: []*types.Header{},
52+
latestHeadIndex: 0,
53+
}
54+
ctrl := &TestClientController{c}
55+
return c, ctrl
56+
}
57+
58+
func (c *TestClientController) ProgressHead() bool {
59+
if c.c.latestHeadIndex >= len(c.c.headerChain)-1 {
60+
return false
61+
}
62+
c.c.latestHeadIndex++
63+
return true
64+
}
65+
66+
func (c *TestClientController) EmitEvents(ctx context.Context) error {
67+
if len(c.c.latestHeadEmitter) == 0 {
68+
return nil
69+
}
70+
h := c.c.getLatestHeader()
71+
for _, em := range c.c.latestHeadEmitter {
72+
select {
73+
case em <- h:
74+
case <-ctx.Done():
75+
return ctx.Err()
76+
}
77+
}
78+
return nil
79+
}
80+
81+
func (c *TestClientController) AppendNextHeaders(h ...*types.Header) {
82+
c.c.headerChain = append(c.c.headerChain, h...)
83+
}
84+
85+
func (t *TestClient) ChainID(_ context.Context) (*big.Int, error) {
86+
return big.NewInt(42), nil
87+
}
88+
89+
func (t *TestClient) Close() {
90+
// TODO: cleanup
91+
}
92+
93+
func (t *TestClient) getLatestHeader() *types.Header {
94+
if len(t.headerChain) == 0 {
95+
return nil
96+
}
97+
return t.headerChain[t.latestHeadIndex]
98+
}
99+
100+
func (t *TestClient) searchBlock(f func(*types.Header) bool) *types.Header {
101+
for i := t.latestHeadIndex; i >= 0; i-- {
102+
h := t.headerChain[i]
103+
if f(h) {
104+
return h
105+
}
106+
}
107+
return nil
108+
}
109+
110+
func (t *TestClient) searchBlockByNumber(number *big.Int) *types.Header {
111+
return t.searchBlock(
112+
func(h *types.Header) bool {
113+
return h.Number.Cmp(number) == 0
114+
})
115+
}
116+
117+
func (t *TestClient) searchBlockByHash(hash common.Hash) *types.Header {
118+
return t.searchBlock(
119+
func(h *types.Header) bool {
120+
return hash.Cmp(h.Hash()) == 0
121+
})
122+
}
123+
124+
func (t *TestClient) BlockNumber(_ context.Context) (uint64, error) {
125+
return t.getLatestHeader().Nonce.Uint64(), nil
126+
}
127+
128+
func (t *TestClient) HeaderByHash(_ context.Context, hash common.Hash) (*types.Header, error) {
129+
h := t.searchBlockByHash(hash)
130+
if h == nil {
131+
// TODO: what error?
132+
}
133+
return h, nil
134+
}
135+
136+
func (t *TestClient) HeaderByNumber(_ context.Context, number *big.Int) (*types.Header, error) {
137+
if number == nil {
138+
return t.getLatestHeader(), nil
139+
}
140+
h := t.searchBlockByNumber(number)
141+
if h == nil {
142+
// TODO: what error?
143+
}
144+
return h, nil
145+
}
146+
147+
func (t *TestClient) SubscribeNewHead(_ context.Context, ch chan<- *types.Header) (ethereum.Subscription, error) {
148+
t.latestHeadEmitter = append(t.latestHeadEmitter, ch)
149+
su := NewSubscription(len(t.latestHeadSubscription) - 1)
150+
t.latestHeadSubscription = append(t.latestHeadSubscription, su)
151+
// TODO: unsubscribe and deleting from the array
152+
// TODO: filling error promise in the subscription
153+
return su, nil
154+
}
155+
156+
func (t *TestClient) FilterLogs(_ context.Context, q ethereum.FilterQuery) ([]types.Log, error) {
157+
panic(ErrNotImplemented)
158+
}
159+
160+
func (t *TestClient) SubscribeFilterLogs(_ context.Context, q ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) {
161+
panic(ErrNotImplemented)
162+
}
163+
164+
func (t *TestClient) CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error) {
165+
panic(ErrNotImplemented)
166+
}
167+
168+
func (t *TestClient) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) {
169+
panic(ErrNotImplemented)
170+
}

rolling-shutter/medley/chainsync/options.go

+5-5
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ type options struct {
2424
keyperSetManagerAddress *common.Address
2525
keyBroadcastContractAddress *common.Address
2626
clientURL string
27-
ethClient syncclient.EthereumClient
27+
ethClient syncclient.FullEthereumClient
2828
logger log.Logger
2929
runner service.Runner
3030
syncStart *number.BlockNumber
@@ -122,7 +122,7 @@ func (o *options) applyHandler(c *Client) error {
122122
// of shutter clients background workers.
123123
func (o *options) apply(ctx context.Context, c *Client) error {
124124
var (
125-
client syncclient.EthereumClient
125+
client syncclient.SyncEthereumClient
126126
err error
127127
)
128128
if o.clientURL != "" {
@@ -132,12 +132,12 @@ func (o *options) apply(ctx context.Context, c *Client) error {
132132
}
133133
}
134134
client = o.ethClient
135-
c.EthereumClient = client
135+
c.SyncEthereumClient = client
136136

137137
// the nil passthrough will use "latest" for each call,
138138
// but we want to harmonize and fix the sync start to a specific block.
139139
if o.syncStart.IsLatest() {
140-
latestBlock, err := c.EthereumClient.BlockNumber(ctx)
140+
latestBlock, err := c.SyncEthereumClient.BlockNumber(ctx)
141141
if err != nil {
142142
return errors.Wrap(err, "polling latest block")
143143
}
@@ -219,7 +219,7 @@ func WithLogger(l log.Logger) Option {
219219
}
220220
}
221221

222-
func WithClient(client syncclient.EthereumClient) Option {
222+
func WithClient(client syncclient.FullEthereumClient) Option {
223223
return func(o *options) error {
224224
o.ethClient = client
225225
return nil

rolling-shutter/medley/chainsync/syncer/eonpubkey.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import (
1717
var _ ManualFilterHandler = &EonPubKeySyncer{}
1818

1919
type EonPubKeySyncer struct {
20-
Client client.EthereumClient
20+
Client client.SyncEthereumClient
2121
Log log.Logger
2222
KeyBroadcast *bindings.KeyBroadcastContract
2323
KeyperSetManager *bindings.KeyperSetManager

rolling-shutter/medley/chainsync/syncer/keyperset.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const channelSize = 10
2323
var _ ManualFilterHandler = &KeyperSetSyncer{}
2424

2525
type KeyperSetSyncer struct {
26-
Client client.EthereumClient
26+
Client client.FullEthereumClient
2727
Contract *bindings.KeyperSetManager
2828
Log log.Logger
2929
Handler event.KeyperSetHandler

rolling-shutter/medley/chainsync/syncer/shutterstate.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import (
1616
var _ ManualFilterHandler = &ShutterStateSyncer{}
1717

1818
type ShutterStateSyncer struct {
19-
Client client.EthereumClient
19+
Client client.SyncEthereumClient
2020
Contract *bindings.KeyperSetManager
2121
Log log.Logger
2222
Handler event.ShutterStateHandler

rolling-shutter/medley/chainsync/syncer/unsafehead.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import (
1717
)
1818

1919
type UnsafeHeadSyncer struct {
20-
Client client.EthereumClient
20+
Client client.SyncEthereumClient
2121
Log log.Logger
2222
Handler event.BlockHandler
2323
// Handler to be manually triggered

0 commit comments

Comments
 (0)