From 91704cd3aeeddc801aa4e95bc73274271767eb8a Mon Sep 17 00:00:00 2001 From: andri lim Date: Fri, 25 Aug 2023 16:07:20 +0700 Subject: [PATCH] Add current computation to each EVM tracer interface (#1712) --- nimbus/core/executor/process_transaction.nim | 2 + nimbus/evm/computation.nim | 7 +++- nimbus/evm/interpreter_dispatch.nim | 5 ++- nimbus/evm/state.nim | 43 ++++++++++---------- nimbus/evm/tracer/json_tracer.nim | 30 ++++++-------- nimbus/evm/tracer/legacy_tracer.nim | 25 +++--------- nimbus/evm/types.nim | 23 ++++++----- nimbus/transaction/call_common.nim | 4 +- tools/t8n/testdata/00-520/exp.txt | 2 +- 9 files changed, 65 insertions(+), 76 deletions(-) diff --git a/nimbus/core/executor/process_transaction.nim b/nimbus/core/executor/process_transaction.nim index 197d747bf..66c734a86 100644 --- a/nimbus/core/executor/process_transaction.nim +++ b/nimbus/core/executor/process_transaction.nim @@ -107,9 +107,11 @@ proc asyncProcessTransactionImpl( vmState.stateDB.clearTransientStorage() # Execute the transaction. + vmState.captureTxStart(tx.gasLimit) let accTx = vmState.stateDB.beginSavepoint gasBurned = tx.txCallEvm(sender, vmState, fork) + vmState.captureTxEnd(tx.gasLimit - gasBurned) res = commitOrRollbackDependingOnGasUsed(vmState, accTx, header, tx, gasBurned, priorityFee) else: diff --git a/nimbus/evm/computation.nim b/nimbus/evm/computation.nim index f0cab5142..fe924ac58 100644 --- a/nimbus/evm/computation.nim +++ b/nimbus/evm/computation.nim @@ -390,16 +390,18 @@ proc tracingEnabled*(c: Computation): bool = proc traceOpCodeStarted*(c: Computation, op: Op): int {.gcsafe, raises: [].} = c.vmState.captureOpStart( + c, c.code.pc - 1, op, c.gasMeter.gasRemaining, c.msg.depth + 1) proc traceCallFamilyGas*(c: Computation, op: Op, gas: GasInt) {.gcsafe, raises: [].} = - c.vmState.callFamilyGas(op, gas, c.msg.depth + 1) + c.vmState.callFamilyGas(c, op, gas, c.msg.depth + 1) proc traceOpCodeEnded*(c: Computation, op: Op, opIndex: int) {.gcsafe, raises: [].} = c.vmState.captureOpEnd( + c, c.code.pc - 1, op, c.gasMeter.gasRemaining, @@ -410,6 +412,7 @@ proc traceOpCodeEnded*(c: Computation, op: Op, opIndex: int) {.gcsafe, raises: [ proc traceError*(c: Computation) {.gcsafe, raises: [].} = c.vmState.captureFault( + c, c.code.pc - 1, c.instr, c.gasMeter.gasRemaining, @@ -419,7 +422,7 @@ proc traceError*(c: Computation) {.gcsafe, raises: [].} = some(c.error.info)) proc prepareTracer*(c: Computation) = - c.vmState.capturePrepare(c.msg.depth) + c.vmState.capturePrepare(c, c.msg.depth) # ------------------------------------------------------------------------------ # End diff --git a/nimbus/evm/interpreter_dispatch.nim b/nimbus/evm/interpreter_dispatch.nim index 52a958504..3324e6339 100644 --- a/nimbus/evm/interpreter_dispatch.nim +++ b/nimbus/evm/interpreter_dispatch.nim @@ -190,7 +190,8 @@ proc beforeExec(c: Computation): bool {.gcsafe, raises: [ValueError].} = if c.msg.depth > 0: - c.vmState.captureEnter(msgToOp(c.msg), + c.vmState.captureEnter(c, + msgToOp(c.msg), c.msg.sender, c.msg.contractAddress, c.msg.data, c.msg.gas, c.msg.value) @@ -215,7 +216,7 @@ proc afterExec(c: Computation) some(c.error.info) else: none(string) - c.vmState.captureExit(c.output, gasUsed, error) + c.vmState.captureExit(c, c.output, gasUsed, error) # ------------------------------------------------------------------------------ # Public functions diff --git a/nimbus/evm/state.nim b/nimbus/evm/state.nim index 6458f4240..4bd1a117a 100644 --- a/nimbus/evm/state.nim +++ b/nimbus/evm/state.nim @@ -363,56 +363,57 @@ proc captureTxEnd*(vmState: BaseVMState, restGas: GasInt) = if vmState.tracingEnabled: vmState.tracer.captureTxEnd(restGas) -proc captureStart*(vmState: BaseVMState, c: Computation, +proc captureStart*(vmState: BaseVMState, comp: Computation, sender: EthAddress, to: EthAddress, create: bool, input: openArray[byte], - gas: GasInt, value: UInt256) = + gasLimit: GasInt, value: UInt256) = if vmState.tracingEnabled: - vmState.tracer.captureStart(c, sender, to, create, input, gas, value) + vmState.tracer.captureStart(comp, sender, to, create, input, gasLimit, value) -proc captureEnd*(vmState: BaseVMState, output: openArray[byte], +proc captureEnd*(vmState: BaseVMState, comp: Computation, output: openArray[byte], gasUsed: GasInt, error: Option[string]) = if vmState.tracingEnabled: - vmState.tracer.captureEnd(output, gasUsed, error) + vmState.tracer.captureEnd(comp, output, gasUsed, error) -proc captureEnter*(vmState: BaseVMState, op: Op, +proc captureEnter*(vmState: BaseVMState, comp: Computation, op: Op, sender: EthAddress, to: EthAddress, - input: openArray[byte], gas: GasInt, + input: openArray[byte], gasLimit: GasInt, value: UInt256) = if vmState.tracingEnabled: - vmState.tracer.captureEnter(op, sender, to, input, gas, value) + vmState.tracer.captureEnter(comp, op, sender, to, input, gasLimit, value) -proc captureExit*(vmState: BaseVMState, output: openArray[byte], +proc captureExit*(vmState: BaseVMState, comp: Computation, output: openArray[byte], gasUsed: GasInt, error: Option[string]) = if vmState.tracingEnabled: - vmState.tracer.captureExit(output, gasUsed, error) + vmState.tracer.captureExit(comp, output, gasUsed, error) -proc captureOpStart*(vmState: BaseVMState, pc: int, +proc captureOpStart*(vmState: BaseVMState, comp: Computation, pc: int, op: Op, gas: GasInt, depth: int): int = if vmState.tracingEnabled: - result = vmState.tracer.captureOpStart(pc, op, gas, depth) + result = vmState.tracer.captureOpStart(comp, pc, op, gas, depth) proc callFamilyGas*(vmState: BaseVMState, + comp: Computation, op: Op, gas: GasInt, depth: int) = - if vmState.tracingEnabled: - vmState.tracer.callFamilyGas(op, gas, depth) - -proc captureOpEnd*(vmState: BaseVMState, pc: int, + if vmState.tracingEnabled: + vmState.tracer.callFamilyGas(comp, op, gas, depth) + +proc captureOpEnd*(vmState: BaseVMState, comp: Computation, pc: int, op: Op, gas: GasInt, refund: GasInt, rData: openArray[byte], depth: int, opIndex: int) = if vmState.tracingEnabled: - vmState.tracer.captureOpEnd(pc, op, gas, refund, rData, depth, opIndex) + vmState.tracer.captureOpEnd(comp, pc, op, gas, refund, rData, depth, opIndex) -proc captureFault*(vmState: BaseVMState, pc: int, +proc captureFault*(vmState: BaseVMState, comp: Computation, pc: int, op: Op, gas: GasInt, refund: GasInt, rData: openArray[byte], depth: int, error: Option[string]) = if vmState.tracingEnabled: - vmState.tracer.captureFault(pc, op, gas, refund, rData, depth, error) + vmState.tracer.captureFault(comp, pc, op, gas, refund, rData, depth, error) -proc capturePrepare*(vmState: BaseVMState, depth: int) = +proc capturePrepare*(vmState: BaseVMState, comp: Computation, depth: int) = if vmState.tracingEnabled: - vmState.tracer.capturePrepare(depth) + vmState.tracer.capturePrepare(comp, depth) diff --git a/nimbus/evm/tracer/json_tracer.nim b/nimbus/evm/tracer/json_tracer.nim index 7b1dff8de..7a9469f06 100644 --- a/nimbus/evm/tracer/json_tracer.nim +++ b/nimbus/evm/tracer/json_tracer.nim @@ -23,7 +23,6 @@ type JsonTracer* = ref object of TracerRef stream: Stream pretty: bool - comp: Computation gas: GasInt pc: int stack: JsonNode @@ -76,13 +75,12 @@ iterator storage(ctx: JsonTracer, compDepth: int): UInt256 = for key in ctx.storageKeys[compDepth]: yield key -proc captureOpImpl(ctx: JsonTracer, pc: int, +proc captureOpImpl(ctx: JsonTracer, c: Computation, pc: int, op: Op, gas: GasInt, refund: GasInt, rData: openArray[byte], depth: int, error: Option[string]) {.gcsafe.} = let gasCost = ctx.gas - gas - c = ctx.comp var res = %{ "pc": %(ctx.pc), @@ -139,7 +137,7 @@ proc newJsonTracer*(stream: Stream, flags: set[TracerFlags], pretty: bool): Json pretty: pretty ) -method capturePrepare*(ctx: JsonTracer, depth: int) {.gcsafe.} = +method capturePrepare*(ctx: JsonTracer, comp: Computation, depth: int) {.gcsafe.} = if depth >= ctx.storageKeys.len: let prevLen = ctx.storageKeys.len ctx.storageKeys.setLen(depth + 1) @@ -149,13 +147,13 @@ method capturePrepare*(ctx: JsonTracer, depth: int) {.gcsafe.} = ctx.storageKeys[depth] = initHashSet[UInt256]() # Top call frame -method captureStart*(ctx: JsonTracer, c: Computation, +method captureStart*(ctx: JsonTracer, comp: Computation, sender: EthAddress, to: EthAddress, create: bool, input: openArray[byte], - gas: GasInt, value: UInt256) {.gcsafe.} = - ctx.comp = c + gasLimit: GasInt, value: UInt256) {.gcsafe.} = + discard -method captureEnd*(ctx: JsonTracer, output: openArray[byte], +method captureEnd*(ctx: JsonTracer, comp: Computation, output: openArray[byte], gasUsed: GasInt, error: Option[string]) {.gcsafe.} = var res = %{ "output": %(output), @@ -166,21 +164,19 @@ method captureEnd*(ctx: JsonTracer, output: openArray[byte], ctx.writeJson(res) # Opcode level -method captureOpStart*(ctx: JsonTracer, pc: int, +method captureOpStart*(ctx: JsonTracer, c: Computation, pc: int, op: Op, gas: GasInt, depth: int): int {.gcsafe.} = ctx.gas = gas ctx.pc = pc if TracerFlags.DisableStack notin ctx.flags: - let c = ctx.comp ctx.stack = newJArray() for v in c.stack.values: ctx.stack.add(%("0x" & v.dumpHex.stripLeadingZeros)) if TracerFlags.DisableStorage notin ctx.flags and op == SSTORE: try: - let c = ctx.comp if c.stack.values.len > 1: ctx.rememberStorageKey(c.msg.depth, c.stack[^1, UInt256]) except InsufficientStack as ex: @@ -190,7 +186,7 @@ method captureOpStart*(ctx: JsonTracer, pc: int, if op in callFamily: try: - ctx.captureOpImpl(pc, op, 0, 0, [], depth, none(string)) + ctx.captureOpImpl(c, pc, op, 0, 0, [], depth, none(string)) except RlpError as ex: error "JsonTracer captureOpEnd", msg=ex.msg @@ -198,7 +194,7 @@ method captureOpStart*(ctx: JsonTracer, pc: int, result = ctx.index inc ctx.index -method callFamilyGas*(ctx: JsonTracer, +method callFamilyGas*(ctx: JsonTracer, comp: Computation, op: Op, gas: GasInt, depth: int) {.gcsafe.} = doAssert(op in callFamily) @@ -207,7 +203,7 @@ method callFamilyGas*(ctx: JsonTracer, res["gasCost"] = encodeHexInt(gas) ctx.writeJson(res) -method captureOpEnd*(ctx: JsonTracer, pc: int, +method captureOpEnd*(ctx: JsonTracer, comp: Computation, pc: int, op: Op, gas: GasInt, refund: GasInt, rData: openArray[byte], depth: int, opIndex: int) {.gcsafe.} = @@ -217,16 +213,16 @@ method captureOpEnd*(ctx: JsonTracer, pc: int, return try: - ctx.captureOpImpl(pc, op, gas, refund, rData, depth, none(string)) + ctx.captureOpImpl(comp, pc, op, gas, refund, rData, depth, none(string)) except RlpError as ex: error "JsonTracer captureOpEnd", msg=ex.msg -method captureFault*(ctx: JsonTracer, pc: int, +method captureFault*(ctx: JsonTracer, comp: Computation, pc: int, op: Op, gas: GasInt, refund: GasInt, rData: openArray[byte], depth: int, error: Option[string]) {.gcsafe.} = try: - ctx.captureOpImpl(pc, op, gas, refund, rData, depth, error) + ctx.captureOpImpl(comp, pc, op, gas, refund, rData, depth, error) except RlpError as ex: error "JsonTracer captureOpEnd", msg=ex.msg diff --git a/nimbus/evm/tracer/legacy_tracer.nim b/nimbus/evm/tracer/legacy_tracer.nim index 851258e16..479a20590 100644 --- a/nimbus/evm/tracer/legacy_tracer.nim +++ b/nimbus/evm/tracer/legacy_tracer.nim @@ -24,7 +24,6 @@ type trace: JsonNode accounts: HashSet[EthAddress] storageKeys: seq[HashSet[UInt256]] - comp: Computation gas: GasInt proc hash*(x: UInt256): Hash = @@ -62,7 +61,7 @@ proc newLegacyTracer*(flags: set[TracerFlags]): LegacyTracer = trace: trace ) -method capturePrepare*(ctx: LegacyTracer, depth: int) {.gcsafe.} = +method capturePrepare*(ctx: LegacyTracer, comp: Computation, depth: int) {.gcsafe.} = if depth >= ctx.storageKeys.len: let prevLen = ctx.storageKeys.len ctx.storageKeys.setLen(depth + 1) @@ -71,25 +70,13 @@ method capturePrepare*(ctx: LegacyTracer, depth: int) {.gcsafe.} = ctx.storageKeys[depth] = initHashSet[UInt256]() -# Top call frame -method captureStart*(ctx: LegacyTracer, c: Computation, - sender: EthAddress, to: EthAddress, - create: bool, input: openArray[byte], - gas: GasInt, value: UInt256) {.gcsafe.} = - ctx.comp = c - -method captureEnd*(ctx: LegacyTracer, output: openArray[byte], - gasUsed: GasInt, error: Option[string]) {.gcsafe.} = - discard - # Opcode level -method captureOpStart*(ctx: LegacyTracer, pc: int, +method captureOpStart*(ctx: LegacyTracer, c: Computation, pc: int, op: Op, gas: GasInt, depth: int): int {.gcsafe.} = try: let j = newJObject() - c = ctx.comp ctx.trace["structLogs"].add(j) j["op"] = %(($op).toUpperAscii) @@ -139,14 +126,13 @@ method captureOpStart*(ctx: LegacyTracer, pc: int, except InsufficientStack as ex: error "LegacyTracer captureOpEnd", msg=ex.msg -method captureOpEnd*(ctx: LegacyTracer, pc: int, +method captureOpEnd*(ctx: LegacyTracer, c: Computation, pc: int, op: Op, gas: GasInt, refund: GasInt, rData: openArray[byte], depth: int, opIndex: int) {.gcsafe.} = try: let j = ctx.trace["structLogs"].elems[opIndex] - c = ctx.comp # TODO: figure out how to get storage # when contract execution interrupted by exception @@ -170,15 +156,14 @@ method captureOpEnd*(ctx: LegacyTracer, pc: int, except RlpError as ex: error "LegacyTracer captureOpEnd", msg=ex.msg -method captureFault*(ctx: LegacyTracer, pc: int, +method captureFault*(ctx: LegacyTracer, comp: Computation, pc: int, op: Op, gas: GasInt, refund: GasInt, rData: openArray[byte], depth: int, error: Option[string]) {.gcsafe.} = try: - let c = ctx.comp if ctx.trace["structLogs"].elems.len > 0: let j = ctx.trace["structLogs"].elems[^1] - j["error"] = %(c.error.info) + j["error"] = %(comp.error.info) j["gasCost"] = %(ctx.gas - gas) ctx.trace["failed"] = %true diff --git a/nimbus/evm/types.nim b/nimbus/evm/types.nim index f82251381..6e00febae 100644 --- a/nimbus/evm/types.nim +++ b/nimbus/evm/types.nim @@ -139,6 +139,7 @@ type flags*: set[TracerFlags] # Transaction level +# This is called once fo each transaction method captureTxStart*(ctx: TracerRef, gasLimit: GasInt) {.base, gcsafe.} = discard @@ -146,50 +147,50 @@ method captureTxEnd*(ctx: TracerRef, restGas: GasInt) {.base, gcsafe.} = discard # Top call frame -method captureStart*(ctx: TracerRef, c: Computation, +method captureStart*(ctx: TracerRef, comp: Computation, sender: EthAddress, to: EthAddress, create: bool, input: openArray[byte], - gas: GasInt, value: UInt256) {.base, gcsafe.} = + gasLimit: GasInt, value: UInt256) {.base, gcsafe.} = discard -method captureEnd*(ctx: TracerRef, output: openArray[byte], +method captureEnd*(ctx: TracerRef, comp: Computation, output: openArray[byte], gasUsed: GasInt, error: Option[string]) {.base, gcsafe.} = discard # Rest of call frames -method captureEnter*(ctx: TracerRef, op: Op, +method captureEnter*(ctx: TracerRef, comp: Computation, op: Op, sender: EthAddress, to: EthAddress, - input: openArray[byte], gas: GasInt, + input: openArray[byte], gasLimit: GasInt, value: UInt256) {.base, gcsafe.} = discard -method captureExit*(ctx: TracerRef, output: openArray[byte], +method captureExit*(ctx: TracerRef, comp: Computation, output: openArray[byte], gasUsed: GasInt, error: Option[string]) {.base, gcsafe.} = discard # Opcode level -method captureOpStart*(ctx: TracerRef, pc: int, +method captureOpStart*(ctx: TracerRef, comp: Computation, pc: int, op: Op, gas: GasInt, depth: int): int {.base, gcsafe.} = discard -method callFamilyGas*(ctx: TracerRef, +method callFamilyGas*(ctx: TracerRef, comp: Computation, op: Op, gas: GasInt, depth: int) {.base, gcsafe.} = discard -method captureOpEnd*(ctx: TracerRef, pc: int, +method captureOpEnd*(ctx: TracerRef, comp: Computation, pc: int, op: Op, gas: GasInt, refund: GasInt, rData: openArray[byte], depth: int, opIndex: int) {.base, gcsafe.} = discard -method captureFault*(ctx: TracerRef, pc: int, +method captureFault*(ctx: TracerRef, comp: Computation, pc: int, op: Op, gas: GasInt, refund: GasInt, rData: openArray[byte], depth: int, error: Option[string]) {.base, gcsafe.} = discard -method capturePrepare*(ctx: TracerRef, depth: int) {.base, gcsafe.} = +method capturePrepare*(ctx: TracerRef, comp: Computation, depth: int) {.base, gcsafe.} = discard diff --git a/nimbus/transaction/call_common.nim b/nimbus/transaction/call_common.nim index b9378d74b..90e830ea1 100644 --- a/nimbus/transaction/call_common.nim +++ b/nimbus/transaction/call_common.nim @@ -194,7 +194,7 @@ proc setupHost(call: CallParams): TransactionHost = vmState.captureStart(host.computation, call.sender, call.to, call.isCreate, call.input, call.gasLimit, call.value) - + return host when defined(evmc_enabled): @@ -283,7 +283,7 @@ proc finishRunningComputation(host: TransactionHost, call: CallParams): CallResu else: none(string) - host.vmState.captureEnd(c.output, gasUsed, error) + host.vmState.captureEnd(c, c.output, gasUsed, error) result.isError = c.isError result.gasUsed = call.gasLimit - gasRemaining diff --git a/tools/t8n/testdata/00-520/exp.txt b/tools/t8n/testdata/00-520/exp.txt index 80c82bc4f..5d5527b07 100644 --- a/tools/t8n/testdata/00-520/exp.txt +++ b/tools/t8n/testdata/00-520/exp.txt @@ -6,7 +6,7 @@ {"pc":10,"op":96,"gas":"0x79bf79","gasCost":"0x3","memSize":0,"stack":["0x0","0x0","0x0","0x0","0x0"],"depth":1,"refund":0,"opName":"PUSH1"} {"pc":12,"op":90,"gas":"0x79bf76","gasCost":"0x2","memSize":0,"stack":["0x0","0x0","0x0","0x0","0x0","0xf3"],"depth":1,"refund":0,"opName":"GAS"} {"pc":13,"op":241,"gas":"0x79bf74","gasCost":"0x77d89f","memSize":0,"stack":["0x0","0x0","0x0","0x0","0x0","0xf3","0x79bf74"],"depth":1,"refund":0,"opName":"CALL"} -{"pc":0,"op":11,"gas":"0x77ce77","gasCost":"0x5","memSize":0,"stack":["0x0"],"depth":2,"refund":0,"opName":"SIGNEXTEND","error":"Opcode Dispatch Error: Stack underflow, expect 2, got 0, depth=2"} +{"pc":0,"op":11,"gas":"0x77ce77","gasCost":"0x5","memSize":0,"stack":[],"depth":2,"refund":0,"opName":"SIGNEXTEND","error":"Opcode Dispatch Error: Stack underflow, expect 2, got 0, depth=2"} {"pc":14,"op":80,"gas":"0x1e6d5","gasCost":"0x2","memSize":0,"stack":["0x0"],"depth":1,"refund":0,"opName":"POP"} {"pc":15,"op":152,"gas":"0x1e6d3","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"SWAP9","error":"Opcode Dispatch Error: Stack underflow for SWAP9, depth=1"} {"output":"","gasUsed":"0x79bf88","error":"Opcode Dispatch Error: Stack underflow for SWAP9, depth=1"}