2023-11-01 03:32:09 +00:00
|
|
|
# Nimbus
|
|
|
|
# Copyright (c) 2020-2023 Status Research & Development GmbH
|
|
|
|
# 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.
|
|
|
|
|
2019-01-06 15:21:34 +00:00
|
|
|
import
|
2023-10-18 02:16:11 +00:00
|
|
|
json, strutils, options, os,
|
2023-01-31 12:38:08 +00:00
|
|
|
eth/common, httputils, nimcrypto/utils,
|
2019-07-07 10:12:01 +00:00
|
|
|
stint, stew/byteutils
|
2019-01-06 15:21:34 +00:00
|
|
|
|
2021-09-14 09:49:31 +00:00
|
|
|
import ../nimbus/transaction, ../nimbus/utils/ec_recover
|
2023-12-08 09:35:50 +00:00
|
|
|
from web3/ethhexstrings import encodeQuantity
|
2019-01-06 15:21:34 +00:00
|
|
|
|
2019-01-11 06:53:18 +00:00
|
|
|
func hexToInt*(s: string, T: typedesc[SomeInteger]): T =
|
2019-01-06 15:21:34 +00:00
|
|
|
var i = 0
|
|
|
|
if s[i] == '0' and (s[i+1] in {'x', 'X'}): inc(i, 2)
|
|
|
|
if s.len - i > sizeof(T) * 2:
|
|
|
|
raise newException(ValueError, "input hex too big for destination int")
|
|
|
|
while i < s.len:
|
|
|
|
result = result shl 4 or readHexChar(s[i]).T
|
|
|
|
inc(i)
|
|
|
|
|
|
|
|
proc prefixHex*(x: Hash256): string =
|
|
|
|
"0x" & toLowerAscii($x)
|
|
|
|
|
|
|
|
proc prefixHex*(x: int64 | uint64 | byte | int): string =
|
2020-07-23 07:54:32 +00:00
|
|
|
toLowerAscii(encodeQuantity(x.uint64).string)
|
2019-01-06 15:21:34 +00:00
|
|
|
|
|
|
|
proc prefixHex*(x: openArray[byte]): string =
|
|
|
|
"0x" & toHex(x, true)
|
|
|
|
|
|
|
|
proc prefixHex*(x: UInt256): string =
|
|
|
|
"0x" & stint.toHex(x)
|
|
|
|
|
|
|
|
proc prefixHex*(x: string): string =
|
|
|
|
"0x" & toLowerAscii(x)
|
|
|
|
|
|
|
|
type
|
2019-09-03 15:06:43 +00:00
|
|
|
SomeData* = EthAddress | BloomFilter | BlockNonce
|
2019-01-06 15:21:34 +00:00
|
|
|
|
2019-09-03 15:06:43 +00:00
|
|
|
proc fromJson*(n: JsonNode, name: string, x: var SomeData) =
|
2021-09-14 09:49:31 +00:00
|
|
|
let node = n[name]
|
|
|
|
if node.kind == JString:
|
|
|
|
hexToByteArray(node.getStr(), x)
|
|
|
|
doAssert(x.prefixHex == toLowerAscii(node.getStr()), name)
|
|
|
|
else:
|
|
|
|
hexToByteArray(node["value"].getStr(), x)
|
|
|
|
doAssert(x.prefixHex == toLowerAscii(node["value"].getStr()), name)
|
2019-09-03 15:06:43 +00:00
|
|
|
|
|
|
|
proc fromJson*(n: JsonNode, name: string, x: var Hash256) =
|
2021-09-14 09:49:31 +00:00
|
|
|
let node = n[name]
|
|
|
|
if node.kind == JString:
|
|
|
|
hexToByteArray(node.getStr(), x.data)
|
|
|
|
doAssert(x.prefixHex == toLowerAscii(node.getStr()), name)
|
|
|
|
else:
|
|
|
|
hexToByteArray(node["value"].getStr(), x.data)
|
|
|
|
doAssert(x.prefixHex == toLowerAscii(node["value"].getStr()), name)
|
2019-01-06 15:21:34 +00:00
|
|
|
|
2019-09-03 15:06:43 +00:00
|
|
|
proc fromJson*(n: JsonNode, name: string, x: var Blob) =
|
2019-01-06 15:21:34 +00:00
|
|
|
x = hexToSeqByte(n[name].getStr())
|
2019-09-03 15:06:43 +00:00
|
|
|
doAssert(x.prefixHex == toLowerAscii(n[name].getStr()), name)
|
2019-01-06 15:21:34 +00:00
|
|
|
|
2019-09-03 15:06:43 +00:00
|
|
|
proc fromJson*(n: JsonNode, name: string, x: var UInt256) =
|
2021-09-14 09:49:31 +00:00
|
|
|
let node = n[name]
|
|
|
|
if node.kind == JString:
|
|
|
|
x = UInt256.fromHex(node.getStr())
|
|
|
|
doAssert(x.prefixHex == toLowerAscii(node.getStr()), name)
|
|
|
|
else:
|
|
|
|
x = node.getInt().u256
|
|
|
|
doAssert($x == $node.getInt, name)
|
2019-01-06 15:21:34 +00:00
|
|
|
|
2019-09-03 15:06:43 +00:00
|
|
|
proc fromJson*(n: JsonNode, name: string, x: var SomeInteger) =
|
2021-09-14 09:49:31 +00:00
|
|
|
let node = n[name]
|
|
|
|
if node.kind == JString:
|
|
|
|
x = hexToInt(node.getStr(), type(x))
|
|
|
|
doAssert(x.prefixHex == toLowerAscii(node.getStr()), name)
|
|
|
|
else:
|
|
|
|
type T = type x
|
|
|
|
x = T(node.getInt)
|
|
|
|
doAssert($x == $node.getInt, name)
|
2019-01-06 15:21:34 +00:00
|
|
|
|
2019-09-03 15:06:43 +00:00
|
|
|
proc fromJson*(n: JsonNode, name: string, x: var EthTime) =
|
2023-10-18 02:16:11 +00:00
|
|
|
x = EthTime(hexToInt(n[name].getStr(), uint64))
|
|
|
|
doAssert(x.uint64.prefixHex == toLowerAscii(n[name].getStr()), name)
|
2019-01-06 15:21:34 +00:00
|
|
|
|
2021-06-30 13:41:29 +00:00
|
|
|
proc fromJson*[T](n: JsonNode, name: string, x: var Option[T]) =
|
|
|
|
if name in n:
|
|
|
|
var val: T
|
|
|
|
n.fromJson(name, val)
|
|
|
|
x = some(val)
|
|
|
|
|
2021-09-26 03:38:41 +00:00
|
|
|
proc fromJson*(n: JsonNode, name: string, x: var TxType) =
|
|
|
|
let node = n[name]
|
|
|
|
if node.kind == JInt:
|
|
|
|
x = TxType(node.getInt)
|
|
|
|
else:
|
|
|
|
x = hexToInt(node.getStr(), int).TxType
|
|
|
|
|
2023-08-21 02:10:18 +00:00
|
|
|
proc fromJson*(n: JsonNode, name: string, x: var seq[Hash256]) =
|
|
|
|
let node = n[name]
|
|
|
|
var h: Hash256
|
|
|
|
x = newSeqOfCap[Hash256](node.len)
|
|
|
|
for v in node:
|
|
|
|
hexToByteArray(v.getStr(), h.data)
|
|
|
|
x.add h
|
|
|
|
|
2019-01-06 15:21:34 +00:00
|
|
|
proc parseBlockHeader*(n: JsonNode): BlockHeader =
|
|
|
|
n.fromJson "parentHash", result.parentHash
|
|
|
|
n.fromJson "sha3Uncles", result.ommersHash
|
|
|
|
n.fromJson "miner", result.coinbase
|
|
|
|
n.fromJson "stateRoot", result.stateRoot
|
|
|
|
n.fromJson "transactionsRoot", result.txRoot
|
|
|
|
n.fromJson "receiptsRoot", result.receiptRoot
|
|
|
|
n.fromJson "logsBloom", result.bloom
|
|
|
|
n.fromJson "difficulty", result.difficulty
|
|
|
|
n.fromJson "number", result.blockNumber
|
|
|
|
n.fromJson "gasLimit", result.gasLimit
|
|
|
|
n.fromJson "gasUsed", result.gasUsed
|
|
|
|
n.fromJson "timestamp", result.timestamp
|
|
|
|
n.fromJson "extraData", result.extraData
|
|
|
|
n.fromJson "mixHash", result.mixDigest
|
|
|
|
n.fromJson "nonce", result.nonce
|
2021-06-30 13:41:29 +00:00
|
|
|
n.fromJson "baseFeePerGas", result.fee
|
2023-03-09 23:40:55 +00:00
|
|
|
n.fromJson "withdrawalsRoot", result.withdrawalsRoot
|
2023-09-24 23:53:20 +00:00
|
|
|
n.fromJson "blobGasUsed", result.blobGasUsed
|
|
|
|
n.fromJson "excessBlobGas", result.excessBlobGas
|
|
|
|
n.fromJson "parentBeaconBlockRoot", result.parentBeaconBlockRoot
|
2019-01-06 15:21:34 +00:00
|
|
|
|
2021-09-14 09:49:31 +00:00
|
|
|
if result.baseFee == 0.u256:
|
|
|
|
# probably geth bug
|
2022-04-08 04:54:11 +00:00
|
|
|
result.fee = none(UInt256)
|
2021-09-14 09:49:31 +00:00
|
|
|
|
|
|
|
proc parseAccessPair(n: JsonNode): AccessPair =
|
|
|
|
n.fromJson "address", result.address
|
|
|
|
let keys = n["storageKeys"]
|
|
|
|
for kn in keys:
|
|
|
|
result.storageKeys.add hexToByteArray[32](kn.getStr())
|
|
|
|
|
2019-01-06 15:21:34 +00:00
|
|
|
proc parseTransaction*(n: JsonNode): Transaction =
|
2021-06-27 04:19:43 +00:00
|
|
|
var tx = Transaction(txType: TxLegacy)
|
2021-05-15 06:37:40 +00:00
|
|
|
n.fromJson "nonce", tx.nonce
|
|
|
|
n.fromJson "gasPrice", tx.gasPrice
|
|
|
|
n.fromJson "gas", tx.gasLimit
|
|
|
|
|
2021-06-27 04:19:43 +00:00
|
|
|
if n["to"].kind != JNull:
|
|
|
|
var to: EthAddress
|
|
|
|
n.fromJson "to", to
|
|
|
|
tx.to = some(to)
|
2021-05-15 06:37:40 +00:00
|
|
|
|
|
|
|
n.fromJson "value", tx.value
|
|
|
|
n.fromJson "input", tx.payload
|
|
|
|
n.fromJson "v", tx.V
|
|
|
|
n.fromJson "r", tx.R
|
|
|
|
n.fromJson "s", tx.S
|
|
|
|
|
2022-04-11 10:00:39 +00:00
|
|
|
if n.hasKey("type") and n["type"].kind != JNull:
|
2021-09-26 03:38:41 +00:00
|
|
|
n.fromJson "type", tx.txType
|
2021-09-14 09:49:31 +00:00
|
|
|
|
2021-09-26 03:38:41 +00:00
|
|
|
if tx.txType >= TxEip1559:
|
2021-09-14 09:49:31 +00:00
|
|
|
n.fromJson "maxPriorityFeePerGas", tx.maxPriorityFee
|
|
|
|
n.fromJson "maxFeePerGas", tx.maxFee
|
|
|
|
|
2021-09-26 03:38:41 +00:00
|
|
|
if tx.txType >= TxEip2930:
|
|
|
|
if n.hasKey("chainId"):
|
|
|
|
let id = hexToInt(n["chainId"].getStr(), int)
|
|
|
|
tx.chainId = ChainId(id)
|
|
|
|
|
2021-09-14 09:49:31 +00:00
|
|
|
let accessList = n["accessList"]
|
|
|
|
if accessList.len > 0:
|
|
|
|
for acn in accessList:
|
|
|
|
tx.accessList.add parseAccessPair(acn)
|
2023-08-21 02:10:18 +00:00
|
|
|
|
|
|
|
if tx.txType >= TxEip4844:
|
|
|
|
n.fromJson "maxFeePerBlobGas", tx.maxFeePerBlobGas
|
|
|
|
|
|
|
|
if n.hasKey("versionedHashes") and n["versionedHashes"].kind != JNull:
|
|
|
|
n.fromJson "versionedHashes", tx.versionedHashes
|
|
|
|
|
2021-09-14 09:49:31 +00:00
|
|
|
tx
|
|
|
|
|
2023-03-09 23:40:55 +00:00
|
|
|
proc parseWithdrawal*(n: JsonNode): Withdrawal =
|
|
|
|
n.fromJson "index", result.index
|
|
|
|
n.fromJson "validatorIndex", result.validatorIndex
|
|
|
|
n.fromJson "address", result.address
|
|
|
|
n.fromJson "amount", result.amount
|
|
|
|
|
2021-09-14 09:49:31 +00:00
|
|
|
proc validateTxSenderAndHash*(n: JsonNode, tx: Transaction) =
|
2021-05-15 06:37:40 +00:00
|
|
|
var sender = tx.getSender()
|
2021-09-14 09:49:31 +00:00
|
|
|
var fromAddr: EthAddress
|
|
|
|
n.fromJson "from", fromAddr
|
|
|
|
doAssert sender.prefixHex == fromAddr.prefixHex
|
2021-05-15 06:37:40 +00:00
|
|
|
doAssert n["hash"].getStr() == tx.rlpHash().prefixHex
|
2019-01-06 15:21:34 +00:00
|
|
|
|
|
|
|
proc parseLog(n: JsonNode): Log =
|
|
|
|
n.fromJson "address", result.address
|
|
|
|
n.fromJson "data", result.data
|
|
|
|
let topics = n["topics"]
|
|
|
|
result.topics = newSeqOfCap[Topic](n.len)
|
|
|
|
var topicHash: Topic
|
|
|
|
for tp in topics:
|
|
|
|
hexToByteArray(tp.getStr(), topicHash)
|
|
|
|
result.topics.add topicHash
|
|
|
|
|
|
|
|
proc parseLogs(n: JsonNode): seq[Log] =
|
|
|
|
if n.len > 0:
|
|
|
|
result = newSeqOfCap[Log](n.len)
|
|
|
|
for log in n:
|
|
|
|
result.add parseLog(log)
|
|
|
|
else:
|
|
|
|
result = @[]
|
|
|
|
|
|
|
|
proc parseReceipt*(n: JsonNode): Receipt =
|
2021-06-27 04:19:43 +00:00
|
|
|
var rec = Receipt(receiptType: LegacyReceipt)
|
2019-01-06 15:21:34 +00:00
|
|
|
if n.hasKey("root"):
|
|
|
|
var hash: Hash256
|
|
|
|
n.fromJson "root", hash
|
2021-06-27 04:19:43 +00:00
|
|
|
rec.isHash = true
|
|
|
|
rec.hash = hash
|
2019-01-06 15:21:34 +00:00
|
|
|
else:
|
|
|
|
var status: int
|
|
|
|
n.fromJson "status", status
|
2021-06-27 04:19:43 +00:00
|
|
|
rec.isHash = false
|
|
|
|
rec.status = status == 1
|
2019-01-06 15:21:34 +00:00
|
|
|
|
2021-05-15 06:37:40 +00:00
|
|
|
n.fromJson "cumulativeGasUsed", rec.cumulativeGasUsed
|
|
|
|
n.fromJson "logsBloom", rec.bloom
|
|
|
|
rec.logs = parseLogs(n["logs"])
|
2021-06-27 04:19:43 +00:00
|
|
|
rec
|
2019-01-06 15:21:34 +00:00
|
|
|
|
|
|
|
proc headerHash*(n: JsonNode): Hash256 =
|
|
|
|
n.fromJson "hash", result
|
2019-01-15 08:55:39 +00:00
|
|
|
|
|
|
|
proc parseAccount*(n: JsonNode): Account =
|
|
|
|
n.fromJson "nonce", result.nonce
|
|
|
|
n.fromJson "balance", result.balance
|
|
|
|
n.fromJson "storageRoot", result.storageRoot
|
|
|
|
n.fromJson "codeHash", result.codeHash
|