-
Notifications
You must be signed in to change notification settings - Fork 24
/
eth_api.go
461 lines (383 loc) · 17.1 KB
/
eth_api.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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
package rpc
import (
"context"
"math/big"
"github.com/Conflux-Chain/confura/node"
"github.com/Conflux-Chain/confura/rpc/cache"
"github.com/Conflux-Chain/confura/rpc/handler"
"github.com/Conflux-Chain/confura/store"
"github.com/Conflux-Chain/confura/util"
"github.com/Conflux-Chain/confura/util/metrics"
vfclient "github.com/Conflux-Chain/confura/virtualfilter/client"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
web3Types "github.com/openweb3/web3go/types"
"github.com/sirupsen/logrus"
)
const (
rpcMethodEthGetLogs = "eth_getLogs"
)
var (
ethEmptyLogs = []web3Types.Log{}
)
type EthAPIOption struct {
StoreHandler *handler.EthStoreHandler
LogApiHandler *handler.EthLogsApiHandler
TxnHandler *handler.EthTxnHandler
VirtualFilterClient *vfclient.EthClient
}
// ethAPI provides Ethereum relative API within evm space according to:
// https://github.com/Pana/conflux-doc/blob/master/docs/evm_space_zh.md
type ethAPI struct {
EthAPIOption
provider *node.EthClientProvider
inputBlockMetric metrics.InputBlockMetric
// return empty data before eSpace hardfork block number
hardforkBlockNumber web3Types.BlockNumber
}
func mustNewEthAPI(provider *node.EthClientProvider, option ...EthAPIOption) *ethAPI {
client, err := provider.GetClientRandom()
if err != nil {
logrus.WithError(err).Fatal("Failed to get eth client randomly")
}
chainId, err := client.Eth.ChainId()
if err != nil {
logrus.WithError(err).Fatal("Failed to get eth chain id")
}
if chainId == nil {
logrus.Fatal("chain id on eSpace is nil")
}
var opt EthAPIOption
if len(option) > 0 {
opt = option[0]
}
return ðAPI{
EthAPIOption: opt,
provider: provider,
hardforkBlockNumber: util.GetEthHardforkBlockNumber(*chainId),
}
}
// GetBlockByHash returns the requested block. When fullTx is true all transactions in
// the block are returned in full detail, otherwise only the transaction hash is returned.
func (api *ethAPI) GetBlockByHash(
ctx context.Context, blockHash common.Hash, fullTx bool,
) (*web3Types.Block, error) {
metrics.Registry.RPC.Percentage("eth_getBlockByHash", "fullTx").Mark(fullTx)
logger := logrus.WithFields(logrus.Fields{
"blockHash": blockHash.Hex(), "includeTxs": fullTx,
})
if !store.EthStoreConfig().IsChainBlockDisabled() && !util.IsInterfaceValNil(api.StoreHandler) {
block, err := api.StoreHandler.GetBlockByHash(ctx, blockHash, fullTx)
metrics.Registry.RPC.StoreHit("eth_getBlockByHash", "store").Mark(err == nil)
if err == nil {
logger.Debug("Loading eth data for eth_getBlockByHash hit in the store")
return block, nil
}
logger.WithError(err).Debug("Loading eth data for eth_getBlockByHash missed from the ethstore")
}
logger.Debug("Delegating eth_getBlockByHash rpc request to fullnode")
return GetEthClientFromContext(ctx).Eth.BlockByHash(blockHash, fullTx)
}
// ChainId returns the chainID value for transaction replay protection.
func (api *ethAPI) ChainId(ctx context.Context) (*hexutil.Uint64, error) {
w3c := GetEthClientFromContext(ctx)
return cache.EthDefault.GetChainId(w3c.Client)
}
// BlockNumber returns the block number of the chain head.
func (api *ethAPI) BlockNumber(ctx context.Context) (*hexutil.Big, error) {
w3c := GetEthClientFromContext(ctx)
return cache.EthDefault.GetBlockNumber(w3c)
}
// GetBalance returns the amount of wei for the given address in the state of the
// given block number.
func (api *ethAPI) GetBalance(
ctx context.Context, address common.Address, blockNumOrHash *web3Types.BlockNumberOrHash,
) (*hexutil.Big, error) {
w3c := GetEthClientFromContext(ctx)
api.inputBlockMetric.Update2(blockNumOrHash, "eth_getBalance", w3c.Eth)
balance, err := w3c.Eth.Balance(address, blockNumOrHash)
return (*hexutil.Big)(balance), err
}
// GetBlockByNumber returns the requested canonical block.
// * When blockNr is -1 the chain head is returned.
// * When blockNr is -2 the pending chain head is returned.
// * When fullTx is true all transactions in the block are returned, otherwise
// only the transaction hash is returned.
func (api *ethAPI) GetBlockByNumber(
ctx context.Context, blockNum web3Types.BlockNumber, fullTx bool,
) (*web3Types.Block, error) {
metrics.Registry.RPC.Percentage("eth_getBlockByNumber", "fullTx").Mark(fullTx)
logger := logrus.WithFields(logrus.Fields{
"blockNum": blockNum, "includeTxs": fullTx,
})
w3c := GetEthClientFromContext(ctx)
api.inputBlockMetric.Update1(&blockNum, "eth_getBlockByNumber", w3c.Eth)
if !store.EthStoreConfig().IsChainBlockDisabled() && !util.IsInterfaceValNil(api.StoreHandler) {
block, err := api.StoreHandler.GetBlockByNumber(ctx, &blockNum, fullTx)
metrics.Registry.RPC.StoreHit("eth_getBlockByNumber", "store").Mark(err == nil)
if err == nil {
logger.Debug("Loading eth data for eth_getBlockByNumber hit in the store")
return block, nil
}
logger.WithError(err).Debug("Loading eth data for eth_getBlockByNumber missed from the ethstore")
}
logger.Debug("Delegating eth_getBlockByNumber rpc request to fullnode")
return w3c.Eth.BlockByNumber(blockNum, fullTx)
}
// GetUncleByBlockNumberAndIndex returns the uncle block for the given block hash and index.
func (api *ethAPI) GetUncleByBlockNumberAndIndex(
ctx context.Context, blockNr web3Types.BlockNumber, index hexutil.Uint,
) (*web3Types.Block, error) {
w3c := GetEthClientFromContext(ctx)
api.inputBlockMetric.Update1(&blockNr, "eth_getUncleByBlockNumberAndIndex", w3c.Eth)
return w3c.Eth.UncleByBlockNumberAndIndex(blockNr, uint(index))
}
// GetUncleCountByBlockHash returns the number of uncles in a block from a block matching
// the given block hash.
func (api *ethAPI) GetUncleCountByBlockHash(ctx context.Context, hash common.Hash) (
*hexutil.Big, error,
) {
w3c := GetEthClientFromContext(ctx)
count, err := w3c.Eth.BlockUnclesCountByHash(hash)
return (*hexutil.Big)(count), err
}
// ProtocolVersion returns the current ethereum protocol version.
func (api *ethAPI) ProtocolVersion(ctx context.Context) (string, error) {
return GetEthClientFromContext(ctx).Eth.ProtocolVersion()
}
// GasPrice returns the current gas price in wei.
func (api *ethAPI) GasPrice(ctx context.Context) (*hexutil.Big, error) {
w3c := GetEthClientFromContext(ctx)
return cache.EthDefault.GetGasPrice(w3c.Client)
}
// GetStorageAt returns the value from a storage position at a given address.
func (api *ethAPI) GetStorageAt(
ctx context.Context, address common.Address, location *hexutil.Big, blockNumOrHash *web3Types.BlockNumberOrHash,
) (common.Hash, error) {
w3c := GetEthClientFromContext(ctx)
api.inputBlockMetric.Update2(blockNumOrHash, "eth_getStorageAt", w3c.Eth)
return w3c.Eth.StorageAt(address, (*big.Int)(location), blockNumOrHash)
}
// GetCode returns the contract code of the given account.
// The block number can be nil, in which case the code is taken from the latest known block.
func (api *ethAPI) GetCode(
ctx context.Context, account common.Address, blockNumOrHash *web3Types.BlockNumberOrHash,
) (hexutil.Bytes, error) {
w3c := GetEthClientFromContext(ctx)
api.inputBlockMetric.Update2(blockNumOrHash, "eth_getCode", w3c.Eth)
return w3c.Eth.CodeAt(account, blockNumOrHash)
}
// GetTransactionCount returns the number of transactions (nonce) sent from the given account.
// The block number can be nil, in which case the nonce is taken from the latest known block.
func (api *ethAPI) GetTransactionCount(
ctx context.Context, account common.Address, blockNumOrHash *web3Types.BlockNumberOrHash,
) (*hexutil.Big, error) {
w3c := GetEthClientFromContext(ctx)
api.inputBlockMetric.Update2(blockNumOrHash, "eth_getTransactionCount", w3c.Eth)
count, err := w3c.Eth.TransactionCount(account, blockNumOrHash)
return (*hexutil.Big)(count), err
}
// SendTransaction injects a signed transaction into the pending pool for execution.
//
// If the transaction was a contract creation use the TransactionReceipt method to get the
// contract address after the transaction has been mined.
func (api *ethAPI) SendRawTransaction(ctx context.Context, signedTx hexutil.Bytes) (common.Hash, error) {
w3c := GetEthClientFromContext(ctx)
if api.TxnHandler != nil {
cgroup := GetClientGroupFromContext(ctx)
return api.TxnHandler.SendRawTxn(w3c, cgroup, signedTx)
}
return w3c.Eth.SendRawTransaction(signedTx)
}
// SubmitTransaction is an alias of `SendRawTransaction` method.
func (api *ethAPI) SubmitTransaction(ctx context.Context, signedTx hexutil.Bytes) (common.Hash, error) {
return api.SendRawTransaction(ctx, signedTx)
}
// Call executes a new message call immediately without creating a transaction on the block chain.
func (api *ethAPI) Call(
ctx context.Context, request web3Types.CallRequest, blockNumOrHash *web3Types.BlockNumberOrHash,
) (hexutil.Bytes, error) {
w3c := GetEthClientFromContext(ctx)
api.inputBlockMetric.Update2(blockNumOrHash, "eth_call", w3c.Eth)
return w3c.Eth.Call(request, blockNumOrHash)
}
// EstimateGas generates and returns an estimate of how much gas is necessary to allow the transaction
// to complete. The transaction will not be added to the blockchain. Note that the estimate may be
// significantly more than the amount of gas actually used by the transaction, for a variety of reasons
// including EVM mechanics and node performance or miner policy.
func (api *ethAPI) EstimateGas(
ctx context.Context, request web3Types.CallRequest, blockNumOrHash *web3Types.BlockNumberOrHash,
) (*hexutil.Big, error) {
w3c := GetEthClientFromContext(ctx)
api.inputBlockMetric.Update2(blockNumOrHash, "eth_estimateGas", w3c.Eth)
gas, err := w3c.Eth.EstimateGas(request, blockNumOrHash)
return (*hexutil.Big)(gas), err
}
// TransactionByHash returns the transaction with the given hash.
func (api *ethAPI) GetTransactionByHash(ctx context.Context, hash common.Hash) (*web3Types.TransactionDetail, error) {
logger := logrus.WithField("txHash", hash.Hex())
if !store.EthStoreConfig().IsChainTxnDisabled() && !util.IsInterfaceValNil(api.StoreHandler) {
tx, err := api.StoreHandler.GetTransactionByHash(ctx, hash)
metrics.Registry.RPC.StoreHit("eth_getTransactionByHash", "store").Mark(err == nil)
if err == nil {
logger.Debug("Loading eth data for eth_getTransactionByHash hit in the store")
return tx, nil
}
logger.WithError(err).Debug("Loading eth data for eth_getTransactionByHash missed from the ethstore")
}
logger.Debug("Delegating eth_getTransactionByHash rpc request to fullnode")
w3c := GetEthClientFromContext(ctx)
return w3c.Eth.TransactionByHash(hash)
}
// TransactionReceipt returns the receipt of a transaction by transaction hash.
// Note that the receipt is not available for pending transactions.
func (api *ethAPI) GetTransactionReceipt(ctx context.Context, txHash common.Hash) (*web3Types.Receipt, error) {
logger := logrus.WithField("txHash", txHash.Hex())
if !store.EthStoreConfig().IsChainReceiptDisabled() && !util.IsInterfaceValNil(api.StoreHandler) {
tx, err := api.StoreHandler.GetTransactionReceipt(ctx, txHash)
metrics.Registry.RPC.StoreHit("eth_getTransactionReceipt", "store").Mark(err == nil)
if err == nil {
logger.Debug("Loading eth data for eth_getTransactionReceipt hit in the ethstore")
return tx, nil
}
logger.WithError(err).Debug("Loading eth data for eth_getTransactionReceipt missed from the ethstore")
}
logger.Debug("Delegating eth_getTransactionReceipt rpc request to fullnode")
w3c := GetEthClientFromContext(ctx)
receipt, err := w3c.Eth.TransactionReceipt(txHash)
if err != nil {
metrics.Registry.RPC.Percentage("eth_getTransactionReceipt", "notfound").Mark(receipt == nil)
}
return receipt, err
}
// GetLogs returns an array of all logs matching a given filter object.
func (api *ethAPI) GetLogs(ctx context.Context, fq web3Types.FilterQuery) ([]web3Types.Log, error) {
w3c := GetEthClientFromContext(ctx)
return api.getLogs(ctx, w3c, &fq, rpcMethodEthGetLogs)
}
// getLogs helper method to get logs from store or fullnode.
func (api *ethAPI) getLogs(
ctx context.Context,
w3c *node.Web3goClient,
fq *web3Types.FilterQuery,
rpcMethod string,
) ([]web3Types.Log, error) {
metrics.UpdateEthRpcLogFilter(rpcMethod, w3c.Eth, fq)
flag, ok := ParseEthLogFilterType(fq)
if !ok {
return ethEmptyLogs, ErrInvalidEthLogFilter
}
if err := NormalizeEthLogFilter(w3c.Client, flag, fq, api.hardforkBlockNumber); err != nil {
return ethEmptyLogs, err
}
if err := ValidateEthLogFilter(flag, fq); err != nil {
return ethEmptyLogs, err
}
// return empty directly if filter block range before eSpace hardfork
if fq.ToBlock != nil && *fq.ToBlock <= api.hardforkBlockNumber {
return ethEmptyLogs, nil
}
if api.LogApiHandler != nil {
logs, hitStore, err := api.LogApiHandler.GetLogs(ctx, w3c.Client.Eth, fq, rpcMethod)
metrics.Registry.RPC.StoreHit(rpcMethod, "store").Mark(hitStore)
return uniformEthLogs(logs), err
}
// fail over to fullnode if no handler configured
return w3c.Eth.Logs(*fq)
}
// GetBlockTransactionCountByHash returns the total number of transactions in the given block.
func (api *ethAPI) GetBlockTransactionCountByHash(ctx context.Context, blockHash common.Hash) (*hexutil.Big, error) {
w3c := GetEthClientFromContext(ctx)
count, err := w3c.Eth.BlockTransactionCountByHash(blockHash)
return (*hexutil.Big)(count), err
}
// GetBlockTransactionCountByNumber returns the number of transactions in the block with the given
// block number.
func (api *ethAPI) GetBlockTransactionCountByNumber(ctx context.Context, blockNum web3Types.BlockNumber) (
*hexutil.Big, error,
) {
w3c := GetEthClientFromContext(ctx)
api.inputBlockMetric.Update1(&blockNum, "eth_getBlockTransactionCountByNumber", w3c.Eth)
count, err := w3c.Eth.BlockTransactionCountByNumber(blockNum)
return (*hexutil.Big)(count), err
}
// Syncing returns an object with data about the sync status or false.
// https://openethereum.github.io/JSONRPC-eth-module#eth_syncing
func (api *ethAPI) Syncing(ctx context.Context) (web3Types.SyncStatus, error) {
w3c := GetEthClientFromContext(ctx)
return w3c.Eth.Syncing()
}
// Hashrate returns the number of hashes per second that the node is mining with.
// Only applicable when the node is mining.
func (api *ethAPI) Hashrate(ctx context.Context) (*hexutil.Big, error) {
w3c := GetEthClientFromContext(ctx)
hashrate, err := w3c.Eth.Hashrate()
return (*hexutil.Big)(hashrate), err
}
// Coinbase returns the client coinbase address..
func (api *ethAPI) Coinbase(ctx context.Context) (common.Address, error) {
w3c := GetEthClientFromContext(ctx)
return w3c.Eth.Author()
}
// Mining returns true if client is actively mining new blocks.
func (api *ethAPI) Mining(ctx context.Context) (bool, error) {
w3c := GetEthClientFromContext(ctx)
return w3c.Eth.IsMining()
}
// MaxPriorityFeePerGas returns a fee per gas that is an estimate of how much you can pay as
// a priority fee, or "tip", to get a transaction included in the current block.
func (api *ethAPI) MaxPriorityFeePerGas(ctx context.Context) (*hexutil.Big, error) {
w3c := GetEthClientFromContext(ctx)
priorityFee, err := w3c.Eth.MaxPriorityFeePerGas()
return (*hexutil.Big)(priorityFee), err
}
// Accounts returns a list of addresses owned by client.
func (api *ethAPI) Accounts(ctx context.Context) ([]common.Address, error) {
w3c := GetEthClientFromContext(ctx)
return w3c.Eth.Accounts()
}
// SubmitHashrate used for submitting mining hashrate.
func (api *ethAPI) SubmitHashrate(
ctx context.Context, hashrate *hexutil.Big, clientId common.Hash,
) (bool, error) {
w3c := GetEthClientFromContext(ctx)
return w3c.Eth.SubmitHashrate((*big.Int)(hashrate), clientId)
}
// GetUncleByBlockHashAndIndex returns information about the 'Uncle' of a block by hash and
// the Uncle index position.
func (api *ethAPI) GetUncleByBlockHashAndIndex(
ctx context.Context, hash common.Hash, index hexutil.Uint,
) (*web3Types.Block, error) {
w3c := GetEthClientFromContext(ctx)
return w3c.Eth.UncleByBlockHashAndIndex(hash, index)
}
// GetTransactionByBlockHashAndIndex returns information about a transaction by block hash and
// transaction index position.
func (api *ethAPI) GetTransactionByBlockHashAndIndex(
ctx context.Context, hash common.Hash, index hexutil.Uint,
) (*web3Types.TransactionDetail, error) {
w3c := GetEthClientFromContext(ctx)
return w3c.Eth.TransactionByBlockHashAndIndex(hash, uint(index))
}
// GetTransactionByBlockNumberAndIndex returns information about a transaction by block number and
// transaction index position.
func (api *ethAPI) GetTransactionByBlockNumberAndIndex(
ctx context.Context, blockNum web3Types.BlockNumber, index hexutil.Uint,
) (*web3Types.TransactionDetail, error) {
w3c := GetEthClientFromContext(ctx)
api.inputBlockMetric.Update1(&blockNum, "eth_getTransactionByBlockNumberAndIndex", w3c.Eth)
return w3c.Eth.TransactionByBlockNumberAndIndex(blockNum, uint(index))
}
// TODO: This method should be removed once `web3Types.FilterQuery` is logging friendly.
func (api *ethAPI) filterLogger(filter *web3Types.FilterQuery) *logrus.Entry {
logger := logrus.WithField("filter", filter)
if filter.FromBlock != nil {
logger = logger.WithField("fromBlock", filter.FromBlock.Int64())
}
if filter.ToBlock != nil {
logger = logger.WithField("toBlock", filter.ToBlock.Int64())
}
return logger
}
// The following RPC methods are not supported yet by the fullnode:
// `eth_feeHistory`