-
Notifications
You must be signed in to change notification settings - Fork 0
/
retryable_ethclient.go
151 lines (117 loc) · 3.66 KB
/
retryable_ethclient.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package scanner
import (
"context"
"errors"
"math/big"
"time"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/sirupsen/logrus"
)
type RetryableEthclient struct {
client *ethclient.Client
logger *logrus.Entry
retryInterval time.Duration
maxRetryTime uint
}
func NewRetryableEthclient(ctx context.Context, rpcUrl string, logger *logrus.Entry, retryInterval time.Duration, maxRetryTime uint) (*RetryableEthclient, error) {
logger.Debugf("Connecting to ethereum RPC endpoint %v", rpcUrl)
rawEthclient, err := ethclient.DialContext(ctx, rpcUrl)
if err != nil {
return nil, err
}
client := &RetryableEthclient{
client: rawEthclient,
logger: logger,
retryInterval: retryInterval,
maxRetryTime: maxRetryTime,
}
return client, nil
}
func (cli *RetryableEthclient) Close() {
if cli.client != nil {
cli.client.Close()
cli.client = nil
}
}
func (cli *RetryableEthclient) Execute(ctx context.Context, executionName string, execution func(context.Context, *ethclient.Client) error) (uint, error) {
retriedTimes := uint(0)
var err error
for retriedTimes < cli.maxRetryTime {
err = execution(ctx, cli.client)
if err == nil {
break
} else if errors.Is(err, context.Canceled) {
return 0, err
}
if retriedTimes < cli.maxRetryTime {
cli.logger.WithError(err).WithField("execution", executionName).Errorf("Exection fails after %v attempts. %v attempts left", retriedTimes+1, cli.maxRetryTime-retriedTimes)
retriedTimes++
time.Sleep(cli.retryInterval)
} else {
break
}
}
if errors.Is(err, context.Canceled) {
return 0, err
} else if err != nil {
cli.logger.WithError(err).WithField("execution", executionName).Errorf("Exection fails after %v attempts", cli.maxRetryTime+1)
return retriedTimes, err
}
return uint(retriedTimes), nil
}
// BlockNumber returns the most recent block number
func (cli *RetryableEthclient) BlockNumber(ctx context.Context) (uint64, error) {
count := uint64(0)
_, err := cli.Execute(ctx, "BlockNumber", func(ctx context.Context, c *ethclient.Client) error {
internalCount, err := c.BlockNumber(ctx)
if err != nil {
return err
}
count = internalCount
return nil
})
return count, err
}
// HeaderByNumber returns a block header from the current canonical chain. If number is
// nil, the latest known header is returned.
func (cli *RetryableEthclient) HeaderByNumber(ctx context.Context, number uint64) (*types.Header, error) {
var result *types.Header
blockNumber := big.NewInt(int64(number))
_, err := cli.Execute(ctx, "HeaderByNumber", func(ctx context.Context, c *ethclient.Client) error {
internalResult, err := c.HeaderByNumber(ctx, blockNumber)
if err != nil {
return err
}
result = internalResult
return nil
})
return result, err
}
// FilterLogs executes a filter query.
func (cli *RetryableEthclient) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) {
var result []types.Log
_, err := cli.Execute(ctx, "FilterLogs", func(ctx context.Context, c *ethclient.Client) error {
internalResult, err := c.FilterLogs(ctx, q)
if err != nil {
return err
}
result = internalResult
return nil
})
return result, err
}
// BlockByNumber executes ethclient.BlockByNumber.
func (cli *RetryableEthclient) BlockByNumber(ctx context.Context, blockNumber *big.Int) (*types.Block, error) {
var result *types.Block
_, err := cli.Execute(ctx, "BlockByNumber", func(ctx context.Context, c *ethclient.Client) error {
internalResult, err := c.BlockByNumber(ctx, blockNumber)
if err != nil {
return err
}
result = internalResult
return nil
})
return result, err
}