Add current computation to each EVM tracer interface (#1712)

This commit is contained in:
andri lim 2023-08-25 16:07:20 +07:00 committed by GitHub
parent 7aead6151e
commit 91704cd3ae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 65 additions and 76 deletions

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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"}