2023-11-01 03:32:09 +00:00
|
|
|
# Nimbus
|
2024-01-13 01:41:57 +00:00
|
|
|
# Copyright (c) 2023-2024 Status Research & Development GmbH
|
2023-11-01 03:32:09 +00:00
|
|
|
# Licensed under either of
|
|
|
|
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0)
|
|
|
|
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or
|
|
|
|
# http://opensource.org/licenses/MIT)
|
|
|
|
# at your option. This file may not be copied, modified, or distributed except
|
|
|
|
# according to those terms.
|
|
|
|
|
2022-04-11 10:00:39 +00:00
|
|
|
import
|
2022-08-04 05:06:31 +00:00
|
|
|
std/[times, json, strutils],
|
2022-04-11 10:00:39 +00:00
|
|
|
stew/byteutils,
|
2024-05-13 08:00:13 +00:00
|
|
|
eth/[common, common/eth_types, common/transaction], chronos,
|
2023-07-09 02:16:22 +00:00
|
|
|
json_rpc/[rpcclient, errors, jsonmarshal],
|
2023-10-23 13:59:57 +00:00
|
|
|
../../../nimbus/beacon/web3_eth_conv,
|
|
|
|
./types
|
2022-04-11 10:00:39 +00:00
|
|
|
|
2023-12-08 09:35:50 +00:00
|
|
|
import
|
|
|
|
web3/eth_api_types,
|
|
|
|
web3/engine_api_types,
|
|
|
|
web3/execution_types,
|
|
|
|
web3/engine_api,
|
|
|
|
web3/eth_api
|
2022-04-11 10:00:39 +00:00
|
|
|
|
2023-09-30 12:20:29 +00:00
|
|
|
export
|
|
|
|
execution_types,
|
|
|
|
rpcclient
|
2023-09-06 09:18:26 +00:00
|
|
|
|
2023-08-21 02:08:54 +00:00
|
|
|
type
|
|
|
|
Hash256 = eth_types.Hash256
|
|
|
|
VersionedHash = engine_api_types.VersionedHash
|
2023-03-09 23:40:55 +00:00
|
|
|
|
2022-08-04 05:06:31 +00:00
|
|
|
template wrapTry(body: untyped) =
|
|
|
|
try:
|
|
|
|
body
|
|
|
|
except ValueError as e:
|
|
|
|
return err(e.msg)
|
|
|
|
except JsonRpcError as ex:
|
|
|
|
return err(ex.msg)
|
|
|
|
|
|
|
|
template wrapTrySimpleRes(body: untyped) =
|
|
|
|
wrapTry:
|
|
|
|
let res = waitFor body
|
|
|
|
return ok(res)
|
|
|
|
|
2022-04-11 10:00:39 +00:00
|
|
|
proc forkchoiceUpdatedV1*(client: RpcClient,
|
|
|
|
update: ForkchoiceStateV1,
|
|
|
|
payloadAttributes = none(PayloadAttributesV1)):
|
|
|
|
Result[ForkchoiceUpdatedResponse, string] =
|
2022-08-04 05:06:31 +00:00
|
|
|
wrapTrySimpleRes:
|
|
|
|
client.engine_forkchoiceUpdatedV1(update, payloadAttributes)
|
2022-04-11 10:00:39 +00:00
|
|
|
|
2023-08-21 02:08:54 +00:00
|
|
|
proc forkchoiceUpdatedV2*(client: RpcClient,
|
|
|
|
update: ForkchoiceStateV1,
|
|
|
|
payloadAttributes = none(PayloadAttributes)):
|
|
|
|
Result[ForkchoiceUpdatedResponse, string] =
|
|
|
|
wrapTrySimpleRes:
|
|
|
|
client.engine_forkchoiceUpdatedV2(update, payloadAttributes)
|
|
|
|
|
|
|
|
proc forkchoiceUpdatedV3*(client: RpcClient,
|
|
|
|
update: ForkchoiceStateV1,
|
|
|
|
payloadAttributes = none(PayloadAttributes)):
|
|
|
|
Result[ForkchoiceUpdatedResponse, string] =
|
|
|
|
wrapTrySimpleRes:
|
|
|
|
client.engine_forkchoiceUpdatedV3(update, payloadAttributes)
|
|
|
|
|
2022-04-11 10:00:39 +00:00
|
|
|
proc getPayloadV1*(client: RpcClient, payloadId: PayloadID): Result[ExecutionPayloadV1, string] =
|
2022-08-04 05:06:31 +00:00
|
|
|
wrapTrySimpleRes:
|
|
|
|
client.engine_getPayloadV1(payloadId)
|
2022-04-11 10:00:39 +00:00
|
|
|
|
2023-08-21 02:08:54 +00:00
|
|
|
proc getPayloadV2*(client: RpcClient, payloadId: PayloadID): Result[GetPayloadV2Response, string] =
|
|
|
|
wrapTrySimpleRes:
|
|
|
|
client.engine_getPayloadV2(payloadId)
|
|
|
|
|
|
|
|
proc getPayloadV3*(client: RpcClient, payloadId: PayloadID): Result[GetPayloadV3Response, string] =
|
|
|
|
wrapTrySimpleRes:
|
|
|
|
client.engine_getPayloadV3(payloadId)
|
|
|
|
|
2024-03-28 07:16:40 +00:00
|
|
|
proc getPayloadV4*(client: RpcClient, payloadId: PayloadID): Result[GetPayloadV4Response, string] =
|
|
|
|
wrapTrySimpleRes:
|
|
|
|
client.engine_getPayloadV4(payloadId)
|
|
|
|
|
2023-09-30 12:20:29 +00:00
|
|
|
proc getPayload*(client: RpcClient,
|
|
|
|
payloadId: PayloadID,
|
|
|
|
version: Version): Result[GetPayloadResponse, string] =
|
2024-03-28 07:16:40 +00:00
|
|
|
if version == Version.V4:
|
|
|
|
let x = client.getPayloadV4(payloadId).valueOr:
|
|
|
|
return err(error)
|
|
|
|
ok(GetPayloadResponse(
|
|
|
|
executionPayload: executionPayload(x.executionPayload),
|
|
|
|
blockValue: some(x.blockValue),
|
|
|
|
blobsBundle: some(x.blobsBundle),
|
|
|
|
shouldOverrideBuilder: some(x.shouldOverrideBuilder),
|
|
|
|
))
|
|
|
|
elif version == Version.V3:
|
2023-09-30 12:20:29 +00:00
|
|
|
let x = client.getPayloadV3(payloadId).valueOr:
|
|
|
|
return err(error)
|
|
|
|
ok(GetPayloadResponse(
|
|
|
|
executionPayload: executionPayload(x.executionPayload),
|
|
|
|
blockValue: some(x.blockValue),
|
2023-10-19 03:28:52 +00:00
|
|
|
blobsBundle: some(x.blobsBundle),
|
|
|
|
shouldOverrideBuilder: some(x.shouldOverrideBuilder),
|
2023-09-30 12:20:29 +00:00
|
|
|
))
|
|
|
|
elif version == Version.V2:
|
|
|
|
let x = client.getPayloadV2(payloadId).valueOr:
|
|
|
|
return err(error)
|
|
|
|
ok(GetPayloadResponse(
|
|
|
|
executionPayload: executionPayload(x.executionPayload),
|
|
|
|
blockValue: some(x.blockValue)
|
|
|
|
))
|
|
|
|
else:
|
|
|
|
let x = client.getPayloadV1(payloadId).valueOr:
|
|
|
|
return err(error)
|
|
|
|
ok(GetPayloadResponse(
|
|
|
|
executionPayload: executionPayload(x),
|
|
|
|
))
|
|
|
|
|
|
|
|
proc forkchoiceUpdated*(client: RpcClient,
|
|
|
|
update: ForkchoiceStateV1,
|
|
|
|
attr: PayloadAttributes):
|
|
|
|
Result[ForkchoiceUpdatedResponse, string] =
|
|
|
|
case attr.version
|
2024-03-28 07:16:40 +00:00
|
|
|
of Version.V1: return client.forkchoiceUpdatedV1(update, some attr.V1)
|
|
|
|
of Version.V2: return client.forkchoiceUpdatedV2(update, some attr)
|
|
|
|
of Version.V3: return client.forkchoiceUpdatedV3(update, some attr)
|
|
|
|
of Version.V4: discard
|
2023-09-30 12:20:29 +00:00
|
|
|
|
|
|
|
proc forkchoiceUpdated*(client: RpcClient,
|
|
|
|
version: Version,
|
|
|
|
update: ForkchoiceStateV1,
|
|
|
|
attr = none(PayloadAttributes)):
|
|
|
|
Result[ForkchoiceUpdatedResponse, string] =
|
|
|
|
case version
|
2024-03-28 07:16:40 +00:00
|
|
|
of Version.V1: return client.forkchoiceUpdatedV1(update, attr.V1)
|
|
|
|
of Version.V2: return client.forkchoiceUpdatedV2(update, attr)
|
|
|
|
of Version.V3: return client.forkchoiceUpdatedV3(update, attr)
|
|
|
|
of Version.V4: discard
|
2023-09-30 12:20:29 +00:00
|
|
|
|
2022-04-11 10:00:39 +00:00
|
|
|
proc newPayloadV1*(client: RpcClient,
|
|
|
|
payload: ExecutionPayloadV1):
|
|
|
|
Result[PayloadStatusV1, string] =
|
2022-08-04 05:06:31 +00:00
|
|
|
wrapTrySimpleRes:
|
|
|
|
client.engine_newPayloadV1(payload)
|
2022-04-11 10:00:39 +00:00
|
|
|
|
2023-03-09 23:40:55 +00:00
|
|
|
proc newPayloadV2*(client: RpcClient,
|
|
|
|
payload: ExecutionPayloadV2):
|
|
|
|
Result[PayloadStatusV1, string] =
|
|
|
|
wrapTrySimpleRes:
|
|
|
|
client.engine_newPayloadV2(payload)
|
|
|
|
|
2023-07-09 02:16:22 +00:00
|
|
|
proc newPayloadV2*(client: RpcClient,
|
|
|
|
payload: ExecutionPayloadV1OrV2):
|
|
|
|
Result[PayloadStatusV1, string] =
|
|
|
|
wrapTrySimpleRes:
|
|
|
|
client.engine_newPayloadV2(payload)
|
|
|
|
|
2023-08-21 02:08:54 +00:00
|
|
|
proc newPayloadV3*(client: RpcClient,
|
|
|
|
payload: ExecutionPayloadV3,
|
|
|
|
versionedHashes: seq[VersionedHash],
|
|
|
|
parentBeaconBlockRoot: FixedBytes[32]
|
|
|
|
):
|
|
|
|
Result[PayloadStatusV1, string] =
|
|
|
|
wrapTrySimpleRes:
|
|
|
|
client.engine_newPayloadV3(payload, versionedHashes, parentBeaconBlockRoot)
|
|
|
|
|
2024-03-28 07:16:40 +00:00
|
|
|
proc newPayloadV4*(client: RpcClient,
|
|
|
|
payload: ExecutionPayloadV4,
|
|
|
|
versionedHashes: seq[VersionedHash],
|
|
|
|
parentBeaconBlockRoot: FixedBytes[32]
|
|
|
|
):
|
|
|
|
Result[PayloadStatusV1, string] =
|
|
|
|
wrapTrySimpleRes:
|
|
|
|
client.engine_newPayloadV4(payload, versionedHashes, parentBeaconBlockRoot)
|
|
|
|
|
2023-10-23 13:59:57 +00:00
|
|
|
proc newPayloadV1*(client: RpcClient,
|
|
|
|
payload: ExecutionPayload):
|
|
|
|
Result[PayloadStatusV1, string] =
|
|
|
|
wrapTrySimpleRes:
|
|
|
|
client.engine_newPayloadV1(payload)
|
|
|
|
|
|
|
|
proc newPayloadV2*(client: RpcClient,
|
|
|
|
payload: ExecutionPayload):
|
|
|
|
Result[PayloadStatusV1, string] =
|
|
|
|
wrapTrySimpleRes:
|
|
|
|
client.engine_newPayloadV2(payload)
|
|
|
|
|
|
|
|
proc newPayloadV3*(client: RpcClient,
|
|
|
|
payload: ExecutionPayload,
|
|
|
|
versionedHashes: Option[seq[VersionedHash]],
|
|
|
|
parentBeaconBlockRoot: Option[FixedBytes[32]]
|
|
|
|
):
|
|
|
|
Result[PayloadStatusV1, string] =
|
|
|
|
wrapTrySimpleRes:
|
|
|
|
client.engine_newPayloadV3(payload, versionedHashes, parentBeaconBlockRoot)
|
|
|
|
|
2024-03-28 07:16:40 +00:00
|
|
|
proc newPayloadV4*(client: RpcClient,
|
|
|
|
payload: ExecutionPayload,
|
|
|
|
versionedHashes: Option[seq[VersionedHash]],
|
|
|
|
parentBeaconBlockRoot: Option[FixedBytes[32]]
|
|
|
|
):
|
|
|
|
Result[PayloadStatusV1, string] =
|
|
|
|
wrapTrySimpleRes:
|
|
|
|
client.engine_newPayloadV4(payload, versionedHashes, parentBeaconBlockRoot)
|
|
|
|
|
2023-09-30 14:31:57 +00:00
|
|
|
proc collectBlobHashes(list: openArray[Web3Tx]): seq[Web3Hash] =
|
|
|
|
for w3tx in list:
|
2024-02-21 09:14:20 +00:00
|
|
|
let tx = ethTx(w3tx)
|
2024-05-13 08:00:13 +00:00
|
|
|
if tx.payload.blob_versioned_hashes.isSome:
|
|
|
|
for h in tx.payload.blob_versioned_hashes.unsafeGet:
|
|
|
|
result.add w3Hash(h)
|
2023-09-30 14:31:57 +00:00
|
|
|
|
2023-09-30 12:20:29 +00:00
|
|
|
proc newPayload*(client: RpcClient,
|
|
|
|
payload: ExecutionPayload,
|
2023-09-30 14:31:57 +00:00
|
|
|
beaconRoot = none(common.Hash256)): Result[PayloadStatusV1, string] =
|
|
|
|
case payload.version
|
|
|
|
of Version.V1: return client.newPayloadV1(payload.V1)
|
|
|
|
of Version.V2: return client.newPayloadV2(payload.V2)
|
|
|
|
of Version.V3:
|
2023-10-05 03:04:12 +00:00
|
|
|
if beaconRoot.isNone:
|
|
|
|
# fallback
|
|
|
|
return client.newPayloadV2(payload.V2)
|
2023-09-30 14:31:57 +00:00
|
|
|
let versionedHashes = collectBlobHashes(payload.transactions)
|
|
|
|
return client.newPayloadV3(payload.V3,
|
|
|
|
versionedHashes,
|
|
|
|
w3Hash beaconRoot.get)
|
2024-03-28 07:16:40 +00:00
|
|
|
of Version.V4:
|
|
|
|
let versionedHashes = collectBlobHashes(payload.transactions)
|
|
|
|
return client.newPayloadV4(payload.V4,
|
|
|
|
versionedHashes,
|
|
|
|
w3Hash beaconRoot.get)
|
2023-09-30 12:20:29 +00:00
|
|
|
|
2023-10-23 13:59:57 +00:00
|
|
|
proc newPayload*(client: RpcClient,
|
|
|
|
version: Version,
|
|
|
|
payload: ExecutableData): Result[PayloadStatusV1, string] =
|
|
|
|
case version
|
|
|
|
of Version.V1: return client.newPayloadV1(payload.basePayload)
|
|
|
|
of Version.V2: return client.newPayloadV2(payload.basePayload)
|
|
|
|
of Version.V3:
|
|
|
|
return client.newPayloadV3(payload.basePayload,
|
|
|
|
w3Hashes payload.versionedHashes,
|
|
|
|
w3Hash payload.beaconRoot)
|
2024-03-28 07:16:40 +00:00
|
|
|
of Version.V4:
|
|
|
|
return client.newPayloadV4(payload.basePayload,
|
|
|
|
w3Hashes payload.versionedHashes,
|
|
|
|
w3Hash payload.beaconRoot)
|
2023-10-23 13:59:57 +00:00
|
|
|
|
2023-08-08 03:44:29 +00:00
|
|
|
proc exchangeCapabilities*(client: RpcClient,
|
|
|
|
methods: seq[string]):
|
|
|
|
Result[seq[string], string] =
|
|
|
|
wrapTrySimpleRes:
|
|
|
|
client.engine_exchangeCapabilities(methods)
|
2023-08-21 02:08:54 +00:00
|
|
|
|
2023-12-08 09:35:50 +00:00
|
|
|
proc toBlockNonce(n: Option[FixedBytes[8]]): common.BlockNonce =
|
2022-04-11 10:00:39 +00:00
|
|
|
if n.isNone:
|
|
|
|
return default(BlockNonce)
|
2023-12-08 09:35:50 +00:00
|
|
|
n.get.bytes
|
2022-04-11 10:00:39 +00:00
|
|
|
|
2023-12-08 09:35:50 +00:00
|
|
|
proc maybeU64(n: Option[Quantity]): Option[uint64] =
|
2023-08-21 02:08:54 +00:00
|
|
|
if n.isNone:
|
|
|
|
return none(uint64)
|
2023-12-08 09:35:50 +00:00
|
|
|
some(n.get.uint64)
|
2023-08-21 02:08:54 +00:00
|
|
|
|
2024-03-21 01:05:22 +00:00
|
|
|
proc maybeU64(n: Option[Web3BlockNumber]): Option[uint64] =
|
|
|
|
if n.isNone:
|
|
|
|
return none(uint64)
|
|
|
|
some(n.get.uint64)
|
|
|
|
|
2023-12-08 09:35:50 +00:00
|
|
|
proc maybeBool(n: Option[Quantity]): Option[bool] =
|
2023-08-21 02:08:54 +00:00
|
|
|
if n.isNone:
|
|
|
|
return none(bool)
|
2023-12-08 09:35:50 +00:00
|
|
|
some(n.get.bool)
|
2023-08-21 02:08:54 +00:00
|
|
|
|
2023-12-08 09:35:50 +00:00
|
|
|
proc maybeChainId(n: Option[Quantity]): Option[ChainId] =
|
2023-08-21 02:08:54 +00:00
|
|
|
if n.isNone:
|
|
|
|
return none(ChainId)
|
2023-12-08 09:35:50 +00:00
|
|
|
some(n.get.ChainId)
|
2023-08-21 02:08:54 +00:00
|
|
|
|
2023-12-08 09:35:50 +00:00
|
|
|
proc maybeInt(n: Option[Quantity]): Option[int] =
|
2023-08-21 02:08:54 +00:00
|
|
|
if n.isNone:
|
|
|
|
return none(int)
|
2023-12-08 09:35:50 +00:00
|
|
|
some(n.get.int)
|
2023-08-21 02:08:54 +00:00
|
|
|
|
2024-03-11 17:20:29 +00:00
|
|
|
proc toBlockHeader*(bc: BlockObject): common.BlockHeader =
|
2022-04-11 10:00:39 +00:00
|
|
|
common.BlockHeader(
|
2024-03-21 01:05:22 +00:00
|
|
|
blockNumber : bc.number.u256,
|
2023-12-08 09:35:50 +00:00
|
|
|
parentHash : ethHash bc.parentHash,
|
2023-08-21 02:08:54 +00:00
|
|
|
nonce : toBlockNonce(bc.nonce),
|
2023-12-08 09:35:50 +00:00
|
|
|
ommersHash : ethHash bc.sha3Uncles,
|
2023-08-21 02:08:54 +00:00
|
|
|
bloom : BloomFilter bc.logsBloom,
|
2023-12-08 09:35:50 +00:00
|
|
|
txRoot : ethHash bc.transactionsRoot,
|
|
|
|
stateRoot : ethHash bc.stateRoot,
|
|
|
|
receiptRoot : ethHash bc.receiptsRoot,
|
|
|
|
coinbase : ethAddr bc.miner,
|
|
|
|
difficulty : bc.difficulty,
|
|
|
|
extraData : bc.extraData.bytes,
|
|
|
|
mixDigest : ethHash bc.mixHash,
|
|
|
|
gasLimit : bc.gasLimit.GasInt,
|
|
|
|
gasUsed : bc.gasUsed.GasInt,
|
|
|
|
timestamp : EthTime bc.timestamp,
|
|
|
|
fee : bc.baseFeePerGas,
|
|
|
|
withdrawalsRoot: ethHash bc.withdrawalsRoot,
|
2023-08-21 02:08:54 +00:00
|
|
|
blobGasUsed : maybeU64(bc.blobGasUsed),
|
|
|
|
excessBlobGas : maybeU64(bc.excessBlobGas),
|
2023-12-08 09:35:50 +00:00
|
|
|
parentBeaconBlockRoot: ethHash bc.parentBeaconBlockRoot,
|
2022-04-11 10:00:39 +00:00
|
|
|
)
|
|
|
|
|
2023-12-08 09:35:50 +00:00
|
|
|
func vHashes(x: Option[seq[Web3Hash]]): seq[common.Hash256] =
|
|
|
|
if x.isNone: return
|
|
|
|
else: ethHashes(x.get)
|
|
|
|
|
2024-05-13 08:00:13 +00:00
|
|
|
proc toTransaction(tx: TransactionObject, chain_id: ChainId): Transaction =
|
|
|
|
var
|
|
|
|
payload: TransactionPayload
|
|
|
|
gasPrice: Opt[UInt256]
|
|
|
|
txChainId: Opt[ChainId]
|
|
|
|
v: UInt256
|
|
|
|
r: UInt256
|
|
|
|
s: UInt256
|
|
|
|
if tx.`type`.isSome:
|
|
|
|
payload.tx_type.ok tx.`type`.get.TxType
|
|
|
|
if tx.chainId.isSome:
|
|
|
|
txChainId.ok tx.chainId.get.ChainId
|
|
|
|
payload.nonce = tx.nonce.AccountNonce
|
|
|
|
payload.max_fee_per_gas = distinctBase(tx.gasPrice).u256
|
|
|
|
if tx.maxPriorityFeePerGas.isSome:
|
|
|
|
payload.max_priority_fee_per_gas.ok(
|
|
|
|
distinctBase(tx.maxPriorityFeePerGas.get).u256)
|
|
|
|
if tx.maxFeePerGas.isSome:
|
|
|
|
gasPrice.ok distinctBase(tx.maxFeePerGas.get).u256
|
|
|
|
payload.gas = distinctBase(tx.gas)
|
|
|
|
if tx.to.isSome:
|
|
|
|
payload.to.ok(ethAddr(tx.to).get)
|
|
|
|
payload.value = tx.value
|
|
|
|
if tx.input.len > payload.input.maxLen:
|
|
|
|
raise (ref ValueError)(msg:
|
|
|
|
"Input cannot fit " & $tx.input.len & " bytes")
|
|
|
|
payload.input = List[byte, Limit MAX_CALLDATA_SIZE].init tx.input
|
|
|
|
if tx.accessList.isSome:
|
|
|
|
payload.access_list.ok ethAccessList(tx.accessList)
|
|
|
|
if tx.maxFeePerBlobGas.isSome:
|
|
|
|
payload.max_fee_per_blob_gas.ok tx.maxFeePerBlobGas.get
|
|
|
|
if tx.blobVersionedHashes.isSome:
|
|
|
|
if tx.blobVersionedHashes.get.len > MAX_BLOB_COMMITMENTS_PER_BLOCK:
|
|
|
|
raise (ref ValueError)(msg:
|
|
|
|
"Access list cannot fit " & $tx.blobVersionedHashes.get.len & " bytes")
|
|
|
|
payload.blob_versioned_hashes.ok(
|
|
|
|
List[eth_types.VersionedHash, Limit MAX_BLOB_COMMITMENTS_PER_BLOCK]
|
|
|
|
.init vHashes(tx.blobVersionedHashes))
|
|
|
|
v = distinctBase(tx.v).u256
|
|
|
|
r = tx.r
|
|
|
|
s = tx.s
|
|
|
|
if gasPrice.get(payload.max_fee_per_gas) != payload.max_fee_per_gas:
|
|
|
|
raise (ref ValueError)(msg: "`gasPrice` and `maxFeePerGas` don't match")
|
|
|
|
if payload.tx_type.get(TxLegacy) != TxLegacy and txChainId.isNone:
|
|
|
|
raise (ref ValueError)(msg: "`chainId` is required")
|
|
|
|
if payload.tx_type == Opt.some(TxLegacy) and txChainId.isNone:
|
|
|
|
payload.tx_type.reset()
|
|
|
|
if txChainId.get(chain_id) != chain_id:
|
|
|
|
raise (ref ValueError)(msg: "Unsupported `chainId`")
|
|
|
|
if r >= SECP256K1N:
|
|
|
|
raise (ref ValueError)(msg: "Invalid `r`")
|
|
|
|
if s < UInt256.one or s >= SECP256K1N:
|
|
|
|
raise (ref ValueError)(msg: "Invalid `s`")
|
|
|
|
let anyTx = AnyTransactionPayload.fromOneOfBase(payload).valueOr:
|
|
|
|
raise (ref ValueError)(msg: "Invalid combination of fields")
|
|
|
|
withTxPayloadVariant(anyTx):
|
|
|
|
let y_parity =
|
|
|
|
when txKind == TransactionKind.Replayable:
|
|
|
|
if v == 27.u256:
|
|
|
|
false
|
|
|
|
elif v == 28.u256:
|
|
|
|
true
|
|
|
|
else:
|
|
|
|
raise (ref ValueError)(msg: "Invalid `v`")
|
|
|
|
elif txKind == TransactionKind.Legacy:
|
|
|
|
let
|
|
|
|
res = v.isEven
|
|
|
|
expected_v =
|
|
|
|
distinctBase(chain_id).u256 * 2 + (if res: 36.u256 else: 35.u256)
|
|
|
|
if v != expected_v:
|
|
|
|
raise (ref ValueError)(msg: "Invalid `v`")
|
|
|
|
res
|
|
|
|
else:
|
|
|
|
if v > UInt256.one:
|
|
|
|
raise (ref ValueError)(msg: "Invalid `v`")
|
|
|
|
v.isOdd
|
|
|
|
var signature: TransactionSignature
|
|
|
|
signature.ecdsa_signature = ecdsa_pack_signature(y_parity, r, s)
|
|
|
|
signature.from_address = ecdsa_recover_from_address(
|
|
|
|
signature.ecdsa_signature,
|
|
|
|
txPayloadVariant.compute_sig_hash(chain_id)).valueOr:
|
|
|
|
raise (ref ValueError)(msg: "Cannot compute `from` address")
|
|
|
|
Transaction(payload: payload, signature: signature)
|
|
|
|
|
|
|
|
proc toTransactions*(
|
|
|
|
txs: openArray[TxOrHash], chain_id: ChainId): seq[Transaction] =
|
2022-04-11 10:00:39 +00:00
|
|
|
for x in txs:
|
2023-12-08 09:35:50 +00:00
|
|
|
doAssert x.kind == tohTx
|
2024-05-13 08:00:13 +00:00
|
|
|
result.add toTransaction(x.tx, chain_id)
|
2022-04-11 10:00:39 +00:00
|
|
|
|
2023-12-08 09:35:50 +00:00
|
|
|
proc toWithdrawal(wd: WithdrawalObject): Withdrawal =
|
2023-08-21 02:08:54 +00:00
|
|
|
Withdrawal(
|
2023-12-08 09:35:50 +00:00
|
|
|
index: wd.index.uint64,
|
|
|
|
validatorIndex: wd.validatorIndex.uint64,
|
|
|
|
address: ethAddr wd.address,
|
|
|
|
amount: wd.amount.uint64,
|
2023-08-21 02:08:54 +00:00
|
|
|
)
|
|
|
|
|
2023-12-08 09:35:50 +00:00
|
|
|
proc toWithdrawals(list: seq[WithdrawalObject]): seq[Withdrawal] =
|
2023-08-21 02:08:54 +00:00
|
|
|
result = newSeqOfCap[Withdrawal](list.len)
|
|
|
|
for wd in list:
|
|
|
|
result.add toWithdrawal(wd)
|
|
|
|
|
2024-03-11 17:20:29 +00:00
|
|
|
proc toWithdrawals*(list: Option[seq[WithdrawalObject]]): Option[seq[Withdrawal]] =
|
2023-08-21 02:08:54 +00:00
|
|
|
if list.isNone:
|
|
|
|
return none(seq[Withdrawal])
|
|
|
|
some(toWithdrawals(list.get))
|
|
|
|
|
|
|
|
type
|
|
|
|
RPCReceipt* = object
|
|
|
|
txHash*: Hash256
|
|
|
|
txIndex*: int
|
|
|
|
blockHash*: Hash256
|
|
|
|
blockNumber*: uint64
|
|
|
|
sender*: EthAddress
|
|
|
|
to*: Option[EthAddress]
|
|
|
|
cumulativeGasUsed*: GasInt
|
|
|
|
gasUsed*: GasInt
|
|
|
|
contractAddress*: Option[EthAddress]
|
2023-12-08 09:35:50 +00:00
|
|
|
logs*: seq[LogObject]
|
2023-08-21 02:08:54 +00:00
|
|
|
logsBloom*: FixedBytes[256]
|
|
|
|
recType*: ReceiptType
|
|
|
|
stateRoot*: Option[Hash256]
|
|
|
|
status*: Option[bool]
|
|
|
|
effectiveGasPrice*: GasInt
|
2023-11-02 04:38:07 +00:00
|
|
|
blobGasUsed*: Option[uint64]
|
|
|
|
blobGasPrice*: Option[UInt256]
|
2023-08-21 02:08:54 +00:00
|
|
|
|
|
|
|
RPCTx* = object
|
|
|
|
txType*: TxType
|
|
|
|
blockHash*: Option[Hash256] # none if pending
|
|
|
|
blockNumber*: Option[uint64]
|
|
|
|
sender*: EthAddress
|
|
|
|
gasLimit*: GasInt
|
|
|
|
gasPrice*: GasInt
|
|
|
|
maxFeePerGas*: GasInt
|
|
|
|
maxPriorityFeePerGas*: GasInt
|
|
|
|
hash*: Hash256
|
|
|
|
payload*: seq[byte]
|
|
|
|
nonce*: AccountNonce
|
|
|
|
to*: Option[EthAddress]
|
|
|
|
txIndex*: Option[int]
|
|
|
|
value*: UInt256
|
|
|
|
v*: int64
|
|
|
|
r*: UInt256
|
|
|
|
s*: UInt256
|
|
|
|
chainId*: Option[ChainId]
|
2023-12-08 09:35:50 +00:00
|
|
|
accessList*: Option[seq[AccessTuple]]
|
2023-10-19 03:28:52 +00:00
|
|
|
maxFeePerBlobGas*: Option[UInt256]
|
2023-08-21 02:08:54 +00:00
|
|
|
versionedHashes*: Option[VersionedHashes]
|
|
|
|
|
2023-12-08 09:35:50 +00:00
|
|
|
proc toRPCReceipt(rec: ReceiptObject): RPCReceipt =
|
2023-08-21 02:08:54 +00:00
|
|
|
RPCReceipt(
|
2023-12-08 09:35:50 +00:00
|
|
|
txHash: ethHash rec.transactionHash,
|
|
|
|
txIndex: rec.transactionIndex.int,
|
|
|
|
blockHash: ethHash rec.blockHash,
|
|
|
|
blockNumber: rec.blockNumber.uint64,
|
|
|
|
sender: ethAddr rec.`from`,
|
|
|
|
to: ethAddr rec.to,
|
|
|
|
cumulativeGasUsed: rec.cumulativeGasUsed.GasInt,
|
|
|
|
gasUsed: rec.gasUsed.GasInt,
|
|
|
|
contractAddress: ethAddr rec.contractAddress,
|
2023-08-21 02:08:54 +00:00
|
|
|
logs: rec.logs,
|
|
|
|
logsBloom: rec.logsBloom,
|
2023-12-08 09:35:50 +00:00
|
|
|
recType: rec.`type`.get(0.Web3Quantity).ReceiptType,
|
|
|
|
stateRoot: ethHash rec.root,
|
2023-08-21 02:08:54 +00:00
|
|
|
status: maybeBool(rec.status),
|
2023-12-08 09:35:50 +00:00
|
|
|
effectiveGasPrice: rec.effectiveGasPrice.GasInt,
|
2023-11-02 04:38:07 +00:00
|
|
|
blobGasUsed: maybeU64(rec.blobGasUsed),
|
2023-12-08 09:35:50 +00:00
|
|
|
blobGasPrice: rec.blobGasPrice,
|
2023-08-21 02:08:54 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
proc toRPCTx(tx: eth_api.TransactionObject): RPCTx =
|
|
|
|
RPCTx(
|
2023-12-08 09:35:50 +00:00
|
|
|
txType: tx.`type`.get(0.Web3Quantity).TxType,
|
|
|
|
blockHash: ethHash tx.blockHash,
|
2023-08-21 02:08:54 +00:00
|
|
|
blockNumber: maybeU64 tx.blockNumber,
|
2023-12-08 09:35:50 +00:00
|
|
|
sender: ethAddr tx.`from`,
|
|
|
|
gasLimit: tx.gas.GasInt,
|
|
|
|
gasPrice: tx.gasPrice.GasInt,
|
|
|
|
maxFeePerGas: tx.maxFeePerGas.get(0.Web3Quantity).GasInt,
|
|
|
|
maxPriorityFeePerGas: tx.maxPriorityFeePerGas.get(0.Web3Quantity).GasInt,
|
|
|
|
hash: ethHash tx.hash,
|
2023-08-21 02:08:54 +00:00
|
|
|
payload: tx.input,
|
2023-12-08 09:35:50 +00:00
|
|
|
nonce: tx.nonce.AccountNonce,
|
|
|
|
to: ethAddr tx.to,
|
2023-08-21 02:08:54 +00:00
|
|
|
txIndex: maybeInt(tx.transactionIndex),
|
2023-12-08 09:35:50 +00:00
|
|
|
value: tx.value,
|
|
|
|
v: tx.v.int64,
|
|
|
|
r: tx.r,
|
|
|
|
s: tx.s,
|
2023-08-21 02:08:54 +00:00
|
|
|
chainId: maybeChainId(tx.chainId),
|
|
|
|
accessList: tx.accessList,
|
2023-12-08 09:35:50 +00:00
|
|
|
maxFeePerBlobGas: tx.maxFeePerBlobGas,
|
|
|
|
versionedHashes: ethHashes tx.blobVersionedHashes,
|
2023-08-21 02:08:54 +00:00
|
|
|
)
|
|
|
|
|
2022-04-11 10:00:39 +00:00
|
|
|
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:
|
2023-12-08 09:35:50 +00:00
|
|
|
let bc = await client.eth_getBlockByNumber("latest", false)
|
|
|
|
if bc.isNil:
|
2022-04-11 10:00:39 +00:00
|
|
|
return (emptyHeader, false)
|
2023-12-08 09:35:50 +00:00
|
|
|
if bc.totalDifficulty >= ttd:
|
2022-04-11 10:00:39 +00:00
|
|
|
return (toBlockHeader(bc), true)
|
|
|
|
|
|
|
|
await sleepAsync(period)
|
|
|
|
inc loop
|
|
|
|
|
|
|
|
return (emptyHeader, false)
|
|
|
|
|
|
|
|
proc blockNumber*(client: RpcClient): Result[uint64, string] =
|
2022-08-04 05:06:31 +00:00
|
|
|
wrapTry:
|
2022-04-11 10:00:39 +00:00
|
|
|
let res = waitFor client.eth_blockNumber()
|
2023-12-08 09:35:50 +00:00
|
|
|
return ok(res.uint64)
|
2022-04-11 10:00:39 +00:00
|
|
|
|
2023-10-31 03:18:37 +00:00
|
|
|
proc headerByNumber*(client: RpcClient, number: uint64): Result[common.BlockHeader, string] =
|
2022-08-04 05:06:31 +00:00
|
|
|
wrapTry:
|
2023-12-08 09:35:50 +00:00
|
|
|
let res = waitFor client.eth_getBlockByNumber(blockId(number), false)
|
|
|
|
if res.isNil:
|
2022-04-11 10:00:39 +00:00
|
|
|
return err("failed to get blockHeader: " & $number)
|
2023-12-08 09:35:50 +00:00
|
|
|
return ok(res.toBlockHeader)
|
|
|
|
|
|
|
|
#proc blockByNumber*(client: RpcClient, number: uint64, output: var common.EthBlock): Result[void, string] =
|
|
|
|
# wrapTry:
|
|
|
|
# let res = waitFor client.eth_getBlockByNumber(blockId(number), true)
|
|
|
|
# if res.isNil:
|
|
|
|
# return err("failed to get block: " & $number)
|
|
|
|
# output.header = toBlockHeader(res)
|
|
|
|
# output.txs = toTransactions(res.transactions)
|
|
|
|
# output.withdrawals = toWithdrawals(res.withdrawals)
|
|
|
|
# return ok()
|
2022-05-29 04:23:03 +00:00
|
|
|
|
2023-10-31 03:18:37 +00:00
|
|
|
proc headerByHash*(client: RpcClient, hash: Hash256): Result[common.BlockHeader, string] =
|
2022-08-04 05:06:31 +00:00
|
|
|
wrapTry:
|
2023-12-08 09:35:50 +00:00
|
|
|
let res = waitFor client.eth_getBlockByHash(w3Hash hash, false)
|
|
|
|
if res.isNil:
|
2022-06-13 09:42:01 +00:00
|
|
|
return err("failed to get block: " & hash.data.toHex)
|
2023-12-08 09:35:50 +00:00
|
|
|
return ok(res.toBlockHeader)
|
2022-06-13 09:42:01 +00:00
|
|
|
|
2023-10-31 03:18:37 +00:00
|
|
|
proc latestHeader*(client: RpcClient): Result[common.BlockHeader, string] =
|
2022-08-04 05:06:31 +00:00
|
|
|
wrapTry:
|
2023-12-08 09:35:50 +00:00
|
|
|
let res = waitFor client.eth_getBlockByNumber(blockId("latest"), false)
|
|
|
|
if res.isNil:
|
2022-04-11 10:00:39 +00:00
|
|
|
return err("failed to get latest blockHeader")
|
2023-12-08 09:35:50 +00:00
|
|
|
return ok(res.toBlockHeader)
|
2022-04-11 10:00:39 +00:00
|
|
|
|
2024-05-13 08:00:13 +00:00
|
|
|
proc latestBlock*(
|
|
|
|
client: RpcClient, chainId: ChainId): Result[common.EthBlock, string] =
|
2022-08-04 05:06:31 +00:00
|
|
|
wrapTry:
|
2023-12-08 09:35:50 +00:00
|
|
|
let res = waitFor client.eth_getBlockByNumber(blockId("latest"), true)
|
|
|
|
if res.isNil:
|
2022-04-11 10:00:39 +00:00
|
|
|
return err("failed to get latest blockHeader")
|
2023-11-01 11:09:49 +00:00
|
|
|
let output = EthBlock(
|
2023-12-08 09:35:50 +00:00
|
|
|
header: toBlockHeader(res),
|
2024-05-13 08:00:13 +00:00
|
|
|
txs: toTransactions(res.transactions, chainId),
|
2023-12-08 09:35:50 +00:00
|
|
|
withdrawals: toWithdrawals(res.withdrawals),
|
2023-11-01 11:09:49 +00:00
|
|
|
)
|
|
|
|
return ok(output)
|
2022-04-11 10:00:39 +00:00
|
|
|
|
2023-10-31 03:18:37 +00:00
|
|
|
proc namedHeader*(client: RpcClient, name: string): Result[common.BlockHeader, string] =
|
2022-08-04 05:06:31 +00:00
|
|
|
wrapTry:
|
2022-06-27 04:15:23 +00:00
|
|
|
let res = waitFor client.eth_getBlockByNumber(name, false)
|
2023-12-08 09:35:50 +00:00
|
|
|
if res.isNil:
|
2022-06-27 04:15:23 +00:00
|
|
|
return err("failed to get named blockHeader")
|
2023-12-08 09:35:50 +00:00
|
|
|
return ok(res.toBlockHeader)
|
2022-06-27 13:18:54 +00:00
|
|
|
|
2024-05-06 20:52:56 +00:00
|
|
|
proc sendTransaction*(
|
2024-05-13 08:00:13 +00:00
|
|
|
client: RpcClient,
|
|
|
|
tx: common.PooledTransaction,
|
|
|
|
chainId: ChainId): Result[void, string] =
|
2022-08-04 05:06:31 +00:00
|
|
|
wrapTry:
|
2024-05-13 08:00:13 +00:00
|
|
|
let encodedTx = tx.toBytes(chainId)
|
2023-12-08 09:35:50 +00:00
|
|
|
let res = waitFor client.eth_sendRawTransaction(encodedTx)
|
2024-05-13 08:00:13 +00:00
|
|
|
let txHash = tx.tx.compute_tx_hash(chainId)
|
2023-12-08 09:35:50 +00:00
|
|
|
let getHash = ethHash res
|
2022-04-11 10:00:39 +00:00
|
|
|
if txHash != getHash:
|
|
|
|
return err("sendTransaction: tx hash mismatch")
|
|
|
|
return ok()
|
|
|
|
|
|
|
|
proc balanceAt*(client: RpcClient, address: EthAddress): Result[UInt256, string] =
|
2022-08-04 05:06:31 +00:00
|
|
|
wrapTry:
|
2023-12-08 09:35:50 +00:00
|
|
|
let res = waitFor client.eth_getBalance(w3Addr(address), blockId("latest"))
|
|
|
|
return ok(res)
|
2022-04-11 10:00:39 +00:00
|
|
|
|
2023-12-08 09:35:50 +00:00
|
|
|
proc balanceAt*(client: RpcClient, address: EthAddress, number: UInt256): Result[UInt256, string] =
|
2023-08-21 02:08:54 +00:00
|
|
|
wrapTry:
|
2023-12-08 09:35:50 +00:00
|
|
|
let res = waitFor client.eth_getBalance(w3Addr(address), blockId(number.truncate(uint64)))
|
|
|
|
return ok(res)
|
2023-08-21 02:08:54 +00:00
|
|
|
|
2023-07-09 02:16:22 +00:00
|
|
|
proc nonceAt*(client: RpcClient, address: EthAddress): Result[AccountNonce, string] =
|
|
|
|
wrapTry:
|
2023-12-08 09:35:50 +00:00
|
|
|
let res = waitFor client.eth_getTransactionCount(w3Addr(address), blockId("latest"))
|
|
|
|
return ok(res.AccountNonce)
|
2023-07-09 02:16:22 +00:00
|
|
|
|
2023-08-21 02:08:54 +00:00
|
|
|
proc txReceipt*(client: RpcClient, txHash: Hash256): Result[RPCReceipt, string] =
|
2022-08-04 05:06:31 +00:00
|
|
|
wrapTry:
|
2023-12-08 09:35:50 +00:00
|
|
|
let res = waitFor client.eth_getTransactionReceipt(w3Hash txHash)
|
|
|
|
if res.isNil:
|
2022-04-11 10:00:39 +00:00
|
|
|
return err("failed to get receipt: " & txHash.data.toHex)
|
2023-12-08 09:35:50 +00:00
|
|
|
return ok(res.toRPCReceipt)
|
2023-08-21 02:08:54 +00:00
|
|
|
|
|
|
|
proc txByHash*(client: RpcClient, txHash: Hash256): Result[RPCTx, string] =
|
|
|
|
wrapTry:
|
2023-12-08 09:35:50 +00:00
|
|
|
let res = waitFor client.eth_getTransactionByHash(w3Hash txHash)
|
|
|
|
if res.isNil:
|
2023-08-21 02:08:54 +00:00
|
|
|
return err("failed to get transaction: " & txHash.data.toHex)
|
2023-12-08 09:35:50 +00:00
|
|
|
return ok(res.toRPCTx)
|
2022-06-27 13:18:54 +00:00
|
|
|
|
2024-01-13 01:41:57 +00:00
|
|
|
proc storageAt*(client: RpcClient, address: EthAddress, slot: UInt256): Result[FixedBytes[32], string] =
|
2022-08-04 05:06:31 +00:00
|
|
|
wrapTry:
|
2023-12-08 09:35:50 +00:00
|
|
|
let res = waitFor client.eth_getStorageAt(w3Addr(address), slot, blockId("latest"))
|
|
|
|
return ok(res)
|
2022-04-11 10:00:39 +00:00
|
|
|
|
2024-01-13 01:41:57 +00:00
|
|
|
proc storageAt*(client: RpcClient, address: EthAddress, slot: UInt256, number: common.BlockNumber): Result[FixedBytes[32], string] =
|
2022-08-04 05:06:31 +00:00
|
|
|
wrapTry:
|
2023-12-08 09:35:50 +00:00
|
|
|
let res = waitFor client.eth_getStorageAt(w3Addr(address), slot, blockId(number.truncate(uint64)))
|
|
|
|
return ok(res)
|
2022-05-29 04:23:03 +00:00
|
|
|
|
|
|
|
proc verifyPoWProgress*(client: RpcClient, lastBlockHash: Hash256): Future[Result[void, string]] {.async.} =
|
2023-12-08 09:35:50 +00:00
|
|
|
let res = await client.eth_getBlockByHash(w3Hash lastBlockHash, false)
|
|
|
|
if res.isNil:
|
2022-05-29 04:23:03 +00:00
|
|
|
return err("cannot get block by hash " & lastBlockHash.data.toHex)
|
|
|
|
|
2023-12-08 09:35:50 +00:00
|
|
|
let header = res
|
2024-03-21 01:05:22 +00:00
|
|
|
let number = header.number.u256
|
2022-05-29 04:23:03 +00:00
|
|
|
|
|
|
|
let period = chronos.seconds(3)
|
|
|
|
var loop = 0
|
|
|
|
while loop < 5:
|
2023-12-08 09:35:50 +00:00
|
|
|
let res = await client.eth_getBlockByNumber(blockId("latest"), false)
|
|
|
|
if res.isNil:
|
2022-05-29 04:23:03 +00:00
|
|
|
return err("cannot get latest block")
|
|
|
|
|
|
|
|
# Chain has progressed, check that the next block is also PoW
|
|
|
|
# Difficulty must NOT be zero
|
2023-12-08 09:35:50 +00:00
|
|
|
let bc = res
|
|
|
|
let diff = bc.difficulty
|
|
|
|
if diff.isZero:
|
2022-05-29 04:23:03 +00:00
|
|
|
return err("Expected PoW chain to progress in PoW mode, but following block difficulty: " & $diff)
|
|
|
|
|
2024-03-21 01:05:22 +00:00
|
|
|
if bc.number.u256 > number:
|
2022-05-29 04:23:03 +00:00
|
|
|
return ok()
|
|
|
|
|
|
|
|
await sleepAsync(period)
|
|
|
|
inc loop
|
|
|
|
|
|
|
|
return err("verify PoW Progress timeout")
|
2022-08-04 05:06:31 +00:00
|
|
|
|
2024-01-13 01:41:57 +00:00
|
|
|
type
|
|
|
|
TraceOpts = object
|
|
|
|
disableStorage: bool
|
|
|
|
disableMemory: bool
|
|
|
|
disableState: bool
|
|
|
|
disableStateDiff: bool
|
|
|
|
|
|
|
|
TraceOpts.useDefaultSerializationIn JrpcConv
|
|
|
|
|
|
|
|
createRpcSigsFromNim(RpcClient):
|
|
|
|
proc debug_traceTransaction(hash: TxHash, opts: TraceOpts): JsonNode
|
2022-08-04 05:06:31 +00:00
|
|
|
|
2024-05-06 20:52:56 +00:00
|
|
|
proc debugPrevRandaoTransaction*(
|
|
|
|
client: RpcClient,
|
|
|
|
tx: PooledTransaction,
|
2024-05-13 08:00:13 +00:00
|
|
|
expectedPrevRandao: Hash256,
|
|
|
|
chain_id: ChainId): Result[void, string] =
|
2022-08-04 05:06:31 +00:00
|
|
|
wrapTry:
|
2024-05-13 08:00:13 +00:00
|
|
|
let hash = tx.tx.compute_tx_hash(chain_id).data.TxHash
|
2022-08-04 05:06:31 +00:00
|
|
|
# we only interested in stack, disable all other elems
|
2024-01-13 01:41:57 +00:00
|
|
|
let opts = TraceOpts(
|
|
|
|
disableStorage: true,
|
|
|
|
disableMemory: true,
|
|
|
|
disableState: true,
|
|
|
|
disableStateDiff: true
|
|
|
|
)
|
|
|
|
|
|
|
|
let res = waitFor client.debug_traceTransaction(hash, opts)
|
2022-08-04 05:06:31 +00:00
|
|
|
let structLogs = res["structLogs"]
|
|
|
|
|
|
|
|
var prevRandaoFound = false
|
|
|
|
for i, x in structLogs.elems:
|
|
|
|
let op = x["op"].getStr
|
|
|
|
if op != "DIFFICULTY": continue
|
|
|
|
|
|
|
|
if i+1 >= structLogs.len:
|
|
|
|
return err("No information after PREVRANDAO operation")
|
|
|
|
|
|
|
|
prevRandaoFound = true
|
|
|
|
let stack = structLogs[i+1]["stack"]
|
|
|
|
if stack.len < 1:
|
|
|
|
return err("Invalid stack after PREVRANDAO operation")
|
|
|
|
|
|
|
|
let stackHash = Hash256(data: hextoByteArray[32](stack[0].getStr))
|
|
|
|
if stackHash != expectedPrevRandao:
|
|
|
|
return err("Invalid stack after PREVRANDAO operation $1 != $2" % [stackHash.data.toHex, expectedPrevRandao.data.toHex])
|
|
|
|
|
|
|
|
if not prevRandaoFound:
|
|
|
|
return err("PREVRANDAO opcode not found")
|
|
|
|
|
|
|
|
return ok()
|
2023-08-21 02:08:54 +00:00
|
|
|
|
|
|
|
template expectBalanceEqual*(res: Result[UInt256, string], account: EthAddress,
|
|
|
|
expectedBalance: UInt256): auto =
|
|
|
|
if res.isErr:
|
|
|
|
return err(res.error)
|
|
|
|
if res.get != expectedBalance:
|
|
|
|
return err("invalid wd balance at $1, expect $2, get $3" % [
|
|
|
|
account.toHex, $expectedBalance, $res.get])
|
|
|
|
|
2024-01-13 01:41:57 +00:00
|
|
|
template expectStorageEqual*(res: Result[FixedBytes[32], string], account: EthAddress,
|
|
|
|
expectedValue: FixedBytes[32]): auto =
|
2023-08-21 02:08:54 +00:00
|
|
|
if res.isErr:
|
|
|
|
return err(res.error)
|
|
|
|
if res.get != expectedValue:
|
|
|
|
return err("invalid wd storage at $1 is $2, expect $3" % [
|
|
|
|
account.toHex, $res.get, $expectedValue])
|
2023-11-05 07:45:56 +00:00
|
|
|
|
|
|
|
proc setBlock*(client: RpcClient, blk: EthBlock, blockNumber: Web3Quantity, stateRoot: Web3Hash): bool =
|
|
|
|
return true
|