Fix EVM tracer crash bug when serializing nil stack (#1697)

* Fix EVM tracer crash bug when serializing nil stack

* Fix t8n tracer doc

add following description to reflect new functionality
  `stdout` - into the stdout output.
  `stderr` - into the stderr output.
  <file>   - into the file <file>-<txIndex>.jsonl.
  none     - output.basedir/trace-<txIndex>-<txhash>.jsonl.
This commit is contained in:
andri lim 2023-08-20 11:15:11 +07:00 committed by GitHub
parent bf65378006
commit fd79c5c264
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 124 additions and 44 deletions

View File

@ -88,6 +88,9 @@ proc captureOpImpl(ctx: JsonTracer, pc: int,
res["memory"] = mem
if TracerFlags.DisableStack notin ctx.flags:
if ctx.stack.isNil:
res["stack"] = newJArray()
else:
res["stack"] = ctx.stack
if TracerFlags.DisableReturnData notin ctx.flags:

View File

@ -28,9 +28,15 @@ type
T8NConf* = object of RootObj
traceEnabled* {.
desc: "Output full trace logs to files trace-<txIndex>-<txhash>.jsonl"
defaultValue: false
name: "trace" }: bool
desc: "Enable and set where to put full EVM trace logs"
longDesc:
"`stdout` - into the stdout output\n" &
"`stderr` - into the stderr output\n" &
"<file> - into the file <file>-<txIndex>.jsonl\n" &
"none - output.basedir/trace-<txIndex>-<txhash>.jsonl\n"
defaultValue: none(string)
defaultValueDesc: "disabled"
name: "trace" }: Option[string]
traceMemory* {.
desc: "Enable full memory dump in traces"
@ -166,7 +172,7 @@ proc convertToNimStyle(cmds: openArray[string]): seq[string] =
const
Copyright = "Copyright (c) 2022 Status Research & Development GmbH"
Version = "Nimbus-t8n 0.1.2"
Version = "Nimbus-t8n 0.2.2"
# force the compiler to instantiate T8NConf.load
# rather than have to export parseCmdArg

View File

@ -36,7 +36,11 @@ t8n [OPTIONS]...
The following options are available:
--trace Output full trace logs to files trace-<txIndex>-<txhash>.jsonl [=false].
--trace Enable and set where to put full EVM trace logs [=disabled].
`stdout` - into the stdout output.
`stderr` - into the stderr output.
<file> - into the file <file>-<txIndex>.jsonl.
none - output.basedir/trace-<txIndex>-<txhash>.jsonl.
--trace.memory Enable full memory dump in traces [=false].
--trace.nostack Disable stack output in traces [=false].
--trace.returndata Enable return data output in traces [=false].

View File

@ -25,6 +25,7 @@ type
alloc : bool
result: bool
body : bool
trace : bool
TestSpec = object
name : string
@ -71,6 +72,9 @@ proc get(opt: T8nOutput): string =
else:
result.add(" --output.body")
if opt.trace:
result.add(" --trace stdout")
template exit(jsc: var JsonComparator, msg: string) =
jsc.path = path
jsc.error = msg
@ -137,6 +141,7 @@ proc runTest(appDir: string, spec: TestSpec): bool =
return false
if spec.expOut.len > 0:
if spec.expOut.endsWith(".json"):
let path = base / spec.expOut
let want = json.parseFile(path)
let have = json.parseJson(res)
@ -147,6 +152,14 @@ proc runTest(appDir: string, spec: TestSpec): bool =
echo "path: $1, error: $2" %
[jsc.path, jsc.error]
return false
else:
# compare as regular text
let path = base / spec.expOut
let want = readFile(path)
if want.replace("\x0D\x0A", "\n") != res:
echo "test $1: output wrong, have \n$2\nwant\n$3\n" %
[spec.name, res, want]
return false
return true
const
@ -484,6 +497,15 @@ const
output: T8nOutput(alloc: true, result: true),
expOut: "exp.json",
),
TestSpec(
name : "EVM tracer crash bug",
base : "testdata/00-519",
input : t8nInput(
"alloc.json", "txs.json", "env.json", "Shanghai", "0",
),
output: T8nOutput(trace: true),
expOut: "exp.txt",
),
]
proc main() =

5
tools/t8n/testdata/00-519/alloc.json vendored Normal file
View File

@ -0,0 +1,5 @@
{
"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
"balance": "0x3635c9adc5dea00000"
}
}

20
tools/t8n/testdata/00-519/env.json vendored Normal file
View File

@ -0,0 +1,20 @@
{
"currentCoinbase": "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentGasLimit": "100000000000000000",
"currentNumber": "2",
"currentTimestamp": "24",
"currentRandom": "0",
"currentDifficulty": "0",
"blockHashes": {
"0": "0xea2d7e0192d890c222f0302d972a02db0bf0c6d08257d73aa1210d08d24f30c3",
"1": "0x068f4313da4cb34b1b6b18ff37eb6ca9f7ad9d294357db81c75cb7790d25dd67"
},
"ommers": [],
"withdrawals": [],
"parentDifficulty": "0",
"parentTimestamp": "12",
"parentBaseFee": "7",
"parentGasUsed": "0",
"parentGasLimit": "100000000000000000",
"parentUncleHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
}

2
tools/t8n/testdata/00-519/exp.txt vendored Normal file
View File

@ -0,0 +1,2 @@
{"pc":0,"op":0,"gas":"0x0","gasCost":"0xfffffffffffecb68","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"STOP","error":"Blake2b F function invalid input"}
{"output":"0x","gasUsed":"0x13498","error":"Blake2b F function invalid input"}

16
tools/t8n/testdata/00-519/txs.json vendored Normal file
View File

@ -0,0 +1,16 @@
[
{
"type": "0x0",
"chainId": "0x1",
"nonce": "0x0",
"gasPrice": "0xa",
"gas": "0x186a0",
"to": "0x0000000000000000000000000000000000000009",
"value": "0x0",
"input": "0x",
"v": "0x26",
"r": "0x803b06b78b7bd29d0faf9401f2df5d71e8a445ad1ac0a45d2e5256ba23c43ed1",
"s": "0x6634f86b79da86a904b1900a52e470847ffe730ef4ec32a3b0f7eece7bfaae96",
"sender": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b"
}
]

View File

@ -147,18 +147,22 @@ proc calcLogsHash(receipts: openArray[Receipt]): Hash256 =
logs.add rec.logs
rlpHash(logs)
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]
proc defaultTraceStream(conf: T8NConf, txIndex: int, txHash: Hash256): Stream =
let
txHash = "0x" & toLowerAscii($txHash)
baseDir = if conf.outputBaseDir.len > 0:
conf.outputBaseDir
else:
"."
fName = "$1/trace-$2-$3.jsonl" % [baseDir, $txIndex, txHash]
newFileStream(fName, fmWrite)
proc encodeHexInt(x: SomeInteger): JsonNode =
%("0x" & x.toHex.stripLeadingZeros.toLowerAscii)
proc toHex(x: Hash256): string =
"0x" & x.data.toHex
proc traceToFileStream(path: string, txIndex: int): Stream =
# replace whatever `.ext` to `-${txIndex}.jsonl`
let
file = path.splitFile
fName = "$1/$2-$3.jsonl" % [file.dir, file.name, $txIndex]
newFileStream(fName, fmWrite)
proc setupTrace(conf: T8NConf, txIndex: int, txHash: Hash256, vmState: BaseVMstate) =
var tracerFlags = {
@ -169,22 +173,20 @@ proc setupTrace(conf: T8NConf, txIndex: int, txHash: Hash256, vmState: BaseVMsta
TracerFlags.DisableReturnData
}
if conf.traceEnabled:
if conf.traceMemory: tracerFlags.excl TracerFlags.DisableMemory
if conf.traceNostack: tracerFlags.incl TracerFlags.DisableStack
if conf.traceReturnData: tracerFlags.excl TracerFlags.DisableReturnData
let
txHash = "0x" & toLowerAscii($txHash)
baseDir = if conf.outputBaseDir.len > 0:
conf.outputBaseDir
let traceMode = conf.traceEnabled.get
let stream = if traceMode == "stdout":
newFileStream(stdout)
elif traceMode == "stderr":
newFileStream(stderr)
elif traceMode.len > 0:
traceToFileStream(traceMode, txIndex)
else:
"."
fName = "$1/trace-$2-$3.jsonl" % [baseDir, $txIndex, txHash]
stream = newFileStream(fName, fmWrite)
tracerInst = newJsonTracer(stream, tracerFlags, false)
vmState.tracer = tracerInst
defaultTraceStream(conf, txIndex, txHash)
vmState.tracer = newJsonTracer(stream, tracerFlags, false)
proc closeTrace(vmState: BaseVMstate) =
let tracer = JsonTracer(vmState.tracer)
@ -233,12 +235,12 @@ proc exec(ctx: var TransContext,
)
continue
if conf.traceEnabled:
if conf.traceEnabled.isSome:
setupTrace(conf, txIndex, rlpHash(tx), vmState)
let rc = vmState.processTransaction(tx, sender, header)
if conf.traceEnabled:
if conf.traceEnabled.isSome:
closeTrace(vmState)
if rc.isErr: