nimbus-eth1/fluffy/tools/eth_data_exporter/parser.nim

260 lines
7.8 KiB
Nim
Raw Permalink Normal View History

2023-11-01 03:32:09 +00:00
# Nimbus
# Copyright (c) 2020-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.
2019-01-06 15:21:34 +00:00
import
std/[json, strutils, os],
eth/common/transaction_utils,
eth/common/blocks,
eth/common/eth_types_rlp,
httputils,
nimcrypto/utils,
stint,
stew/byteutils
from stew/objects import checkedEnumAssign
template stripLeadingZeros(value: string): string =
var cidx = 0
# ignore the last character so we retain '0' on zero value
while cidx < value.len - 1 and value[cidx] == '0':
cidx.inc
value[cidx .. ^1]
func encodeQuantity(value: SomeUnsignedInt): string =
var hValue = value.toHex.stripLeadingZeros
result = "0x" & hValue.toLowerAscii
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)
2019-01-06 15:21:34 +00:00
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 to0xHex*(x: int64 | uint64 | byte | int): string =
toLowerAscii(encodeQuantity(x.uint64))
2019-01-06 15:21:34 +00:00
proc to0xHex*(x: UInt256): string =
2019-01-06 15:21:34 +00:00
"0x" & stint.toHex(x)
proc to0xHex*(x: string): string =
2019-01-06 15:21:34 +00:00
"0x" & toLowerAscii(x)
type SomeData* = Address | Bloom | Bytes8
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) =
let node = n[name]
if node.kind == JString:
hexToByteArray(node.getStr(), x.data)
doAssert(x.to0xHex == toLowerAscii(node.getStr()), name)
else:
hexToByteArray(node["value"].getStr(), x.data)
doAssert(x.to0xHex == toLowerAscii(node["value"].getStr()), name)
2019-09-03 15:06:43 +00:00
proc fromJson*(n: JsonNode, name: string, x: var (Hash32 | Bytes32)) =
let node = n[name]
if node.kind == JString:
hexToByteArray(node.getStr(), x.data)
doAssert(x.to0xHex == toLowerAscii(node.getStr()), name)
else:
hexToByteArray(node["value"].getStr(), x.data)
doAssert(x.to0xHex == toLowerAscii(node["value"].getStr()), name)
2019-01-06 15:21:34 +00:00
proc fromJson*(n: JsonNode, name: string, x: var seq[byte]) =
2019-01-06 15:21:34 +00:00
x = hexToSeqByte(n[name].getStr())
doAssert(x.to0xHex == 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) =
let node = n[name]
if node.kind == JString:
x = UInt256.fromHex(node.getStr())
doAssert(x.to0xHex == 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) =
let node = n[name]
if node.kind == JString:
x = hexToInt(node.getStr(), type(x))
doAssert(x.to0xHex == 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) =
x = EthTime(hexToInt(n[name].getStr(), uint64))
doAssert(x.uint64.to0xHex == toLowerAscii(n[name].getStr()), name)
2019-01-06 15:21:34 +00:00
proc fromJson*[T](n: JsonNode, name: string, x: var Opt[T]) =
if name in n:
var val: T
n.fromJson(name, val)
x = Opt.some(val)
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
proc fromJson*[T: Bytes32 | Hash32](n: JsonNode, name: string, x: var seq[T]) =
let node = n[name]
2024-10-01 17:40:37 +00:00
var h: T
x = newSeqOfCap[T](node.len)
for v in node:
hexToByteArray(v.getStr(), h.data)
x.add h
proc parseBlockHeader*(n: JsonNode): Header =
2019-01-06 15:21:34 +00:00
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.receiptsRoot
n.fromJson "logsBloom", result.logsBloom
2019-01-06 15:21:34 +00:00
n.fromJson "difficulty", result.difficulty
n.fromJson "number", result.number
2019-01-06 15:21:34 +00:00
n.fromJson "gasLimit", result.gasLimit
n.fromJson "gasUsed", result.gasUsed
n.fromJson "timestamp", result.timestamp
n.fromJson "extraData", result.extraData
n.fromJson "mixHash", result.mixHash
2019-01-06 15:21:34 +00:00
n.fromJson "nonce", result.nonce
n.fromJson "baseFeePerGas", result.baseFeePerGas
n.fromJson "withdrawalsRoot", result.withdrawalsRoot
n.fromJson "blobGasUsed", result.blobGasUsed
n.fromJson "excessBlobGas", result.excessBlobGas
n.fromJson "parentBeaconBlockRoot", result.parentBeaconBlockRoot
2019-01-06 15:21:34 +00:00
if result.baseFeePerGas.get(0.u256) == 0.u256:
# probably geth bug
result.baseFeePerGas = Opt.none(UInt256)
proc parseAccessPair(n: JsonNode): AccessPair =
n.fromJson "address", result.address
let keys = n["storageKeys"]
for kn in keys:
result.storageKeys.add Bytes32.fromHex(kn.getStr())
2019-01-06 15:21:34 +00:00
proc parseTransaction*(n: JsonNode): Transaction =
var tx = Transaction(txType: TxLegacy)
n.fromJson "nonce", tx.nonce
n.fromJson "gasPrice", tx.gasPrice
n.fromJson "gas", tx.gasLimit
if n["to"].kind != JNull:
var to: Address
n.fromJson "to", to
tx.to = Opt.some(to)
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
if n.hasKey("type") and n["type"].kind != JNull:
n.fromJson "type", tx.txType
if tx.txType >= TxEip1559:
n.fromJson "maxPriorityFeePerGas", tx.maxPriorityFeePerGas
n.fromJson "maxFeePerGas", tx.maxFeePerGas
if tx.txType >= TxEip2930:
if n.hasKey("chainId"):
let id = hexToInt(n["chainId"].getStr(), int)
tx.chainId = ChainId(id)
let accessList = n["accessList"]
if accessList.len > 0:
for acn in accessList:
tx.accessList.add parseAccessPair(acn)
if tx.txType >= TxEip4844:
n.fromJson "maxFeePerBlobGas", tx.maxFeePerBlobGas
if n.hasKey("versionedHashes") and n["versionedHashes"].kind != JNull:
n.fromJson "versionedHashes", tx.versionedHashes
tx
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
proc validateTxSenderAndHash*(n: JsonNode, tx: Transaction) =
var sender = tx.recoverSender().expect("valid signature")
var fromAddr: Address
n.fromJson "from", fromAddr
doAssert sender.to0xHex == fromAddr.to0xHex
doAssert n["hash"].getStr() == tx.rlpHash().to0xHex
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.data)
2019-01-06 15:21:34 +00:00
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 =
var recType: byte
n.fromJson "type", recType
var txVal: ReceiptType
var rec =
if checkedEnumAssign(txVal, recType):
Receipt(receiptType: txVal)
else:
raise newException(ValueError, "Unknown receipt type")
2019-01-06 15:21:34 +00:00
if n.hasKey("root"):
var hash: Hash32
2019-01-06 15:21:34 +00:00
n.fromJson "root", hash
rec.isHash = true
rec.hash = hash
2019-01-06 15:21:34 +00:00
else:
var status: int
n.fromJson "status", status
rec.isHash = false
rec.status = status == 1
2019-01-06 15:21:34 +00:00
n.fromJson "cumulativeGasUsed", rec.cumulativeGasUsed
n.fromJson "logsBloom", rec.logsBloom
rec.logs = parseLogs(n["logs"])
rec
2019-01-06 15:21:34 +00:00
proc headerHash*(n: JsonNode): Hash32 =
2019-01-06 15:21:34 +00:00
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