mirror of
https://github.com/status-im/nimbus-eth1.git
synced 2025-01-10 04:15:54 +00:00
df1217b7ca
* Removed some Windows specific unit test annoyances details: + Short put()/get() cycles on persistent database have a race condition with vendor rocksdb. On a specific (and slow) qemu/win7 a 50ms `sleep()` in between will mostly do the job (i.e. unless heavy CPU load.) This issue was not observed on github/ci. + Removed annoyances when qemu/Win7 keeps the rocksdb database files locked even after closing the db. The problem is solved by strictly using fresh names for each test. No assumption made to be able to properly clean up. This issue was not observed on github/ci. * Silence some compiler gossip -- part 7, misc/non(sync or graphql) details: Adding some missing exception annotation
168 lines
6.0 KiB
Nim
168 lines
6.0 KiB
Nim
# Nimbus - Trace EVMC host calls when EVM code is run for a transaction
|
|
#
|
|
# Copyright (c) 2021 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.
|
|
|
|
{.push raises: [].}
|
|
|
|
import
|
|
macros, strformat, strutils, stint, chronicles,
|
|
stew/byteutils, stew/ranges/ptr_arith,
|
|
./host_types
|
|
|
|
# Set `true` or `false` to control host call tracing.
|
|
const showTxCalls = false
|
|
const showTxNested = true
|
|
|
|
proc `$`(val: EvmcCallKind | EvmcStatusCode |
|
|
EvmcStorageStatus | EvmcAccessStatus): string =
|
|
result = val.repr
|
|
result.removePrefix("EVMC_")
|
|
|
|
proc `$`(address: HostAddress): string = toHex(address)
|
|
|
|
# Don't use both types in the overload, as Nim <= 1.2.x gives "ambiguous call".
|
|
#func `$`(n: HostKey | HostValue): string = toHex(n)
|
|
proc `$`(n: HostKey): string =
|
|
# Show small keys and values as decimal, and large ones as "0x"-prefix hex.
|
|
# These are often small numbers despite the 256-bit type, so low digits is
|
|
# helpful. But "key=d" looks odd. Hex must be used for large values as they
|
|
# are sometimes 256-bit hashes, and then decimal is unhelpful.
|
|
if n <= 65535.u256.HostKey:
|
|
$n.truncate(uint16)
|
|
else:
|
|
"0x" & n.toHex
|
|
|
|
proc depthPrefix(host: TransactionHost): string =
|
|
let depth = host.depth
|
|
if depth <= 20:
|
|
return spaces(depth * 2)
|
|
else:
|
|
let num = '(' & $depth & ')'
|
|
return num & spaces(42 - num.len)
|
|
|
|
proc showEvmcMessage(msg: EvmcMessage): string
|
|
{.gcsafe, raises: [ValueError].} =
|
|
let kindStr =
|
|
if msg.flags == {}: $msg.kind
|
|
elif msg.flags == {EVMC_STATIC} and msg.kind == EVMC_CALL: "CALL_STATIC"
|
|
else: &"{$msg.kind} flags={$msg.flags}"
|
|
var inputStr = "(" & $msg.input_size & ")"
|
|
if msg.input_size > 0:
|
|
inputStr.add toHex(makeOpenArray(msg.input_data,
|
|
min(msg.input_size, 256).int))
|
|
if msg.input_size > 256:
|
|
inputStr.add "..."
|
|
result = &"kind={kindStr}" &
|
|
&" depth={$msg.depth}" &
|
|
&" gas={$msg.gas}" &
|
|
&" value={$msg.value.fromEvmc}" &
|
|
&" sender={$msg.sender.fromEvmc}" &
|
|
&" recipient={$msg.recipient.fromEvmc}" &
|
|
&" code_address={$msg.code_address.fromEvmc}" &
|
|
&" input_data={inputStr}"
|
|
if msg.kind == EVMC_CREATE2:
|
|
result.add &" create2_salt={$msg.create2_salt.fromEvmc}"
|
|
|
|
proc showEvmcResult(res: EvmcResult, withCreateAddress = true): string
|
|
{.gcsafe, raises: [ValueError].} =
|
|
if res.status_code != EVMC_SUCCESS and res.status_code != EVMC_REVERT and
|
|
res.gas_left == 0 and res.output_size == 0:
|
|
return &"status={$res.status_code}"
|
|
|
|
var outputStr = "(" & $res.output_size & ")"
|
|
if res.output_size > 0:
|
|
outputStr.add toHex(makeOpenArray(res.output_data,
|
|
min(res.output_size, 256).int))
|
|
if res.output_size > 256:
|
|
outputStr.add "..."
|
|
|
|
result = &"status={$res.status_code}" &
|
|
&" gas_left={$res.gas_left}" &
|
|
&" output_data={outputStr}"
|
|
if withCreateAddress:
|
|
result.add &" create_address={$res.create_address.fromEvmc}"
|
|
|
|
proc showEvmcTxContext(txc: EvmcTxContext): string
|
|
{.gcsafe, raises: [ValueError].} =
|
|
return &"tx_gas_price={$txc.tx_gas_price.fromEvmc}" &
|
|
&" tx_origin={$txc.tx_origin.fromEvmc}" &
|
|
&" block_coinbase={$txc.block_coinbase.fromEvmc}" &
|
|
&" block_number={$txc.block_number}" &
|
|
&" block_timestamp={$txc.block_timestamp}" &
|
|
&" block_gas_limit={$txc.block_gas_limit}" &
|
|
&" block_prev_randao={$txc.block_prev_randao.fromEvmc}" &
|
|
&" chain_id={$txc.chain_id.fromEvmc}" &
|
|
&" block_base_fee={$txc.block_base_fee.fromEvmc}"
|
|
|
|
proc showEvmcArgsExpr(fn: NimNode, callName: string): auto =
|
|
var args: seq[NimNode] = newSeq[NimNode]()
|
|
var types: seq[NimNode] = newSeq[NimNode]()
|
|
for i in 1 ..< fn.params.len:
|
|
let idents = fn.params[i]
|
|
for j in 0 ..< idents.len-2:
|
|
args.add idents[j]
|
|
types.add idents[^2]
|
|
let hostExpr = args[0]
|
|
var msgExpr = quote do:
|
|
`depthPrefix`(`hostExpr`) & "evmc." & `callName` & ":"
|
|
var skip = 0
|
|
for i in 1 ..< args.len:
|
|
if i == skip:
|
|
continue
|
|
var arg = args[i]
|
|
let argNameString = " " & $arg & "="
|
|
if (types[i].repr == "ptr byte" or types[i].repr == "ptr HostTopic") and
|
|
(i < args.len-1 and types[i+1].repr == "HostSize"):
|
|
skip = i+1
|
|
arg = newPar(args[i], args[i+1])
|
|
msgExpr = quote do:
|
|
`msgExpr` & `argNameString` & $(`arg`)
|
|
return (msgExpr, args)
|
|
|
|
macro show*(fn: untyped): auto =
|
|
if not showTxCalls:
|
|
return fn
|
|
|
|
let (msgExpr, args) = showEvmcArgsExpr(fn, $fn.name)
|
|
let hostExpr = args[0]
|
|
if fn.params[0].kind == nnkEmpty:
|
|
fn.body.insert 0, quote do:
|
|
if showTxNested or `hostExpr`.depth > 1:
|
|
echo `msgExpr`
|
|
else:
|
|
let innerFn = newProc(name = fn.name, body = fn.body)
|
|
innerFn.params = fn.params.copy
|
|
innerFn.addPragma(newIdentNode("inline"))
|
|
fn.body = newStmtList(innerFn)
|
|
let call = newCall(fn.name, args)
|
|
let msgVar = genSym(nskLet, "msg")
|
|
fn.body.add quote do:
|
|
if not (showTxNested or `hostExpr`.depth > 1):
|
|
return `call`
|
|
let `msgVar` = `msgExpr`
|
|
result = `call`
|
|
if fn.params[0].repr == "EvmcTxContext":
|
|
fn.body.add quote do:
|
|
echo `msgVar` & " -> " & showEvmcTxContext(result)
|
|
else:
|
|
fn.body.add quote do:
|
|
echo `msgVar` & " -> result=" & $result
|
|
return fn
|
|
|
|
template showCallEntry*(host: TransactionHost, msg: EvmcMessage) =
|
|
if showTxCalls and (showTxNested or host.depth > 0):
|
|
echo depthPrefix(host) & "evmc.call: " &
|
|
showEvmcMessage(msg)
|
|
inc host.depth
|
|
|
|
template showCallReturn*(host: TransactionHost, res: EvmcResult,
|
|
forNestedCreate = false) =
|
|
if showTxCalls and (showTxNested or host.depth > 1):
|
|
echo depthPrefix(host) & "evmc.return -> " &
|
|
showEvmcResult(res, forNestedCreate)
|
|
dec host.depth
|