-
Notifications
You must be signed in to change notification settings - Fork 5
/
client_execution.go
172 lines (144 loc) · 4.02 KB
/
client_execution.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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
package main
import (
"context"
"fmt"
"math/big"
"sync"
"time"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/rpc"
"gopkg.in/inconshreveable/log15.v2"
)
type TotalDifficulty struct {
TotalDifficulty *hexutil.Big `json:"totalDifficulty"`
}
type ExecutionClient struct {
Type ClientType
ID int
RPCUrl string
Eth *ethclient.Client
RPC *rpc.Client
// Merge related
TTD TTD
TTDBlockNumber *uint64
TTDBlockTimestamp uint64
UpdateTTDTimestamp func(uint64)
// Lock
l sync.Mutex
// Context related
lastCtx context.Context
lastCancel context.CancelFunc
}
func NewExecutionClient(clientType ClientType, id int, rpcUrl string) (*ExecutionClient, error) {
rpcClient, err := rpc.DialHTTP(rpcUrl)
if err != nil {
return nil, err
}
return &ExecutionClient{
Type: clientType,
ID: id,
RPCUrl: rpcUrl,
Eth: ethclient.NewClient(rpcClient),
RPC: rpcClient,
}, nil
}
func (el *ExecutionClient) ClientLayer() ClientLayer {
return Execution
}
func (el *ExecutionClient) ClientVersion() (string, error) {
var clientVersion *string
if err := el.RPC.CallContext(el.Ctx(), &clientVersion, "web3_clientVersion"); err != nil {
return "", err
}
return *clientVersion, nil
}
func (el *ExecutionClient) UpdateGetTTDBlockSlot() (*uint64, error) {
el.l.Lock()
defer el.l.Unlock()
if el.TTDBlockNumber == nil {
var td *TotalDifficulty
if err := el.RPC.CallContext(el.Ctx(), &td, "eth_getBlockByNumber", "latest", false); err != nil {
return nil, err
}
if td.TotalDifficulty.ToInt().Cmp(el.TTD.Int) >= 0 {
// TTD has been reached, we need to go backwards from latest block to find the non-zero difficulty block
latestHeader, err := el.Eth.BlockByNumber(el.Ctx(), nil)
if err != nil {
return nil, err
}
for currentNumber := int64(latestHeader.NumberU64()); currentNumber >= 0; currentNumber-- {
currentHeader, err := el.Eth.BlockByNumber(el.Ctx(), big.NewInt(currentNumber))
if err != nil {
return nil, err
}
if currentHeader.Difficulty().Cmp(big.NewInt(0)) > 0 {
// We got the first block from head with a non-zero difficulty, this is the TTD block
bn := currentHeader.Number().Uint64()
el.TTDBlockNumber = &bn
el.TTDBlockTimestamp = currentHeader.Time()
if el.UpdateTTDTimestamp != nil {
el.UpdateTTDTimestamp(el.TTDBlockTimestamp)
}
log15.Info("TTD Block Reached", "client", el.ClientID(), "block", bn)
break
}
if currentNumber == 0 {
return nil, fmt.Errorf("unable to get TTD Block")
}
}
}
}
return el.TTDBlockNumber, nil
}
func (el *ExecutionClient) GetLatestBlockSlotNumber() (uint64, error) {
el.l.Lock()
defer el.l.Unlock()
return el.Eth.BlockNumber(el.Ctx())
}
func (el *ExecutionClient) GetDataPoint(dataName MetricName, blockNumber uint64) (interface{}, error) {
el.l.Lock()
defer el.l.Unlock()
header, err := el.Eth.HeaderByNumber(el.Ctx(), big.NewInt(int64(blockNumber)))
if err != nil {
return nil, err
}
switch dataName {
case ExecutionBlockCount:
// no error occured, we have a block
return uint64(1), nil
case ExecutionBaseFee:
return header.BaseFee, nil
case ExecutionGasUsed:
return header.GasUsed, nil
case ExecutionDifficulty:
return header.Difficulty, nil
case ExecutionMixHash:
return header.MixDigest.Big(), nil
case ExecutionUnclesHash:
return header.UncleHash.Big(), nil
case ExecutionNonce:
return header.Nonce.Uint64(), nil
}
return nil, fmt.Errorf("invalid data name: %s", dataName)
}
func (el *ExecutionClient) Ctx() context.Context {
if el.lastCtx != nil {
el.lastCancel()
}
el.lastCtx, el.lastCancel = context.WithTimeout(context.Background(), 10*time.Second)
return el.lastCtx
}
func (el *ExecutionClient) String() string {
return el.RPCUrl
}
func (el *ExecutionClient) ClientType() ClientType {
return el.Type
}
func (el *ExecutionClient) ClientID() int {
return el.ID
}
func (el *ExecutionClient) Close() error {
el.Eth.Close()
return nil
}