nimbus-eth1/hive_integration/nodocker/engine/engine_client.nim

222 lines
7.3 KiB
Nim

import
std/[times, json],
stew/byteutils,
eth/[common, rlp], chronos,
web3/engine_api_types,
json_rpc/rpcclient,
../../../tests/rpcclient/eth_api,
../../../premix/parser,
../../../nimbus/rpc/hexstrings,
../../../premix/parser
import web3/engine_api as web3_engine_api
proc forkchoiceUpdatedV1*(client: RpcClient,
update: ForkchoiceStateV1,
payloadAttributes = none(PayloadAttributesV1)):
Result[ForkchoiceUpdatedResponse, string] =
try:
let res = waitFor client.engine_forkchoiceUpdatedV1(update, payloadAttributes)
return ok(res)
except ValueError as e:
return err(e.msg)
proc getPayloadV1*(client: RpcClient, payloadId: PayloadID): Result[ExecutionPayloadV1, string] =
try:
let res = waitFor client.engine_getPayloadV1(payloadId)
return ok(res)
except ValueError as e:
return err(e.msg)
proc newPayloadV1*(client: RpcClient,
payload: ExecutionPayloadV1):
Result[PayloadStatusV1, string] =
try:
let res = waitFor client.engine_newPayloadV1(payload)
return ok(res)
except ValueError as e:
return err(e.msg)
proc toBlockNumber(n: Option[HexQuantityStr]): common.BlockNumber =
if n.isNone:
return 0.toBlockNumber
toBlockNumber(hexToInt(string n.get, uint64))
proc toBlockNonce(n: Option[HexDataStr]): common.BlockNonce =
if n.isNone:
return default(BlockNonce)
hexToByteArray(string n.get, result)
proc toBaseFeePerGas(n: Option[HexQuantityStr]): Option[UInt256] =
if n.isNone:
return none(UInt256)
some(UInt256.fromHex(string n.get))
proc toBlockHeader(bc: eth_api.BlockObject): common.BlockHeader =
common.BlockHeader(
blockNumber: toBlockNumber(bc.number),
parentHash : bc.parentHash,
nonce : toBlockNonce(bc.nonce),
ommersHash : bc.sha3Uncles,
bloom : BloomFilter bc.logsBloom,
txRoot : bc.transactionsRoot,
stateRoot : bc.stateRoot,
receiptRoot: bc.receiptsRoot,
coinbase : bc.miner,
difficulty : UInt256.fromHex(string bc.difficulty),
extraData : hexToSeqByte(string bc.extraData),
mixDigest : bc.mixHash,
gasLimit : hexToInt(string bc.gasLimit, GasInt),
gasUsed : hexToInt(string bc.gasUsed, GasInt),
timestamp : initTime(hexToInt(string bc.timestamp, int64), 0),
fee : toBaseFeePerGas(bc.baseFeePerGas)
)
proc toTransactions(txs: openArray[JsonNode]): seq[Transaction] =
for x in txs:
result.add parseTransaction(x)
proc waitForTTD*(client: RpcClient,
ttd: DifficultyInt): Future[(common.BlockHeader, bool)] {.async.} =
let period = chronos.seconds(5)
var loop = 0
var emptyHeader: common.BlockHeader
while loop < 5:
let res = await client.eth_getBlockByNumber("latest", false)
if res.isNone:
return (emptyHeader, false)
let bc = res.get()
if hexToInt(string bc.totalDifficulty, int64).u256 >= ttd:
return (toBlockHeader(bc), true)
await sleepAsync(period)
inc loop
return (emptyHeader, false)
proc blockNumber*(client: RpcClient): Result[uint64, string] =
try:
let res = waitFor client.eth_blockNumber()
return ok(hexToInt(string res, uint64))
except ValueError as e:
return err(e.msg)
proc headerByNumber*(client: RpcClient, number: uint64, output: var common.BlockHeader): Result[void, string] =
try:
let qty = encodeQuantity(number)
let res = waitFor client.eth_getBlockByNumber(string qty, false)
if res.isNone:
return err("failed to get blockHeader: " & $number)
output = toBlockHeader(res.get())
return ok()
except ValueError as e:
return err(e.msg)
proc blockByNumber*(client: RpcClient, number: uint64, output: var common.EthBlock): Result[void, string] =
try:
let qty = encodeQuantity(number)
let res = waitFor client.eth_getBlockByNumber(string qty, true)
if res.isNone:
return err("failed to get block: " & $number)
let blk = res.get()
output.header = toBlockHeader(blk)
output.txs = toTransactions(blk.transactions)
return ok()
except ValueError as e:
return err(e.msg)
proc latestHeader*(client: RpcClient, output: var common.BlockHeader): Result[void, string] =
try:
let res = waitFor client.eth_getBlockByNumber("latest", false)
if res.isNone:
return err("failed to get latest blockHeader")
output = toBlockHeader(res.get())
return ok()
except ValueError as e:
return err(e.msg)
proc latestBlock*(client: RpcClient, output: var common.EthBlock): Result[void, string] =
try:
let res = waitFor client.eth_getBlockByNumber("latest", true)
if res.isNone:
return err("failed to get latest blockHeader")
let blk = res.get()
output.header = toBlockHeader(blk)
output.txs = toTransactions(blk.transactions)
return ok()
except ValueError as e:
return err(e.msg)
proc sendTransaction*(client: RpcClient, tx: common.Transaction): Result[void, string] =
try:
let encodedTx = rlp.encode(tx)
let res = waitFor client.eth_sendRawTransaction(hexDataStr(encodedTx))
let txHash = rlpHash(tx)
let getHash = Hash256(data: hexToByteArray[32](string res))
if txHash != getHash:
return err("sendTransaction: tx hash mismatch")
return ok()
except ValueError as e:
return err(e.msg)
proc balanceAt*(client: RpcClient, address: EthAddress): Result[UInt256, string] =
try:
let res = waitFor client.eth_getBalance(ethAddressStr(address), "latest")
return ok(UInt256.fromHex(res.string))
except ValueError as e:
return err(e.msg)
proc txReceipt*(client: RpcClient, txHash: Hash256): Result[eth_api.ReceiptObject, string] =
try:
let res = waitFor client.eth_getTransactionReceipt(txHash)
if res.isNone:
return err("failed to get receipt: " & txHash.data.toHex)
return ok(res.get)
except ValueError as e:
return err(e.msg)
proc storageAt*(client: RpcClient, address: EthAddress, slot: UInt256): Result[UInt256, string] =
try:
let res = waitFor client.eth_getStorageAt(ethAddressStr(address), encodeQuantity(slot), "latest")
return ok(UInt256.fromHex(res.string))
except ValueError as e:
return err(e.msg)
proc storageAt*(client: RpcClient, address: EthAddress, slot: UInt256, number: common.BlockNumber): Result[UInt256, string] =
try:
let tag = encodeQuantity(number)
let res = waitFor client.eth_getStorageAt(ethAddressStr(address), encodeQuantity(slot), tag.string)
return ok(UInt256.fromHex(res.string))
except ValueError as e:
return err(e.msg)
proc verifyPoWProgress*(client: RpcClient, lastBlockHash: Hash256): Future[Result[void, string]] {.async.} =
let res = await client.eth_getBlockByHash(lastBlockHash, false)
if res.isNone:
return err("cannot get block by hash " & lastBlockHash.data.toHex)
let header = res.get()
let number = toBlockNumber(header.number)
let period = chronos.seconds(3)
var loop = 0
while loop < 5:
let res = await client.eth_getBlockByNumber("latest", false)
if res.isNone:
return err("cannot get latest block")
# Chain has progressed, check that the next block is also PoW
# Difficulty must NOT be zero
let bc = res.get()
let diff = hexToInt(string bc.difficulty, int64)
if diff == 0:
return err("Expected PoW chain to progress in PoW mode, but following block difficulty: " & $diff)
if toBlockNumber(bc.number) > number:
return ok()
await sleepAsync(period)
inc loop
return err("verify PoW Progress timeout")