Initial TransactionTracer impl
This commit is contained in:
parent
5095bfa8d8
commit
bac452f589
|
@ -11,7 +11,7 @@ import
|
||||||
./interpreter/[opcode_values, opcodes_impl, vm_forks, gas_costs, gas_meter, utils/macros_gen_opcodes],
|
./interpreter/[opcode_values, opcodes_impl, vm_forks, gas_costs, gas_meter, utils/macros_gen_opcodes],
|
||||||
./code_stream,
|
./code_stream,
|
||||||
../vm_types, ../errors,
|
../vm_types, ../errors,
|
||||||
./stack, ./computation, terminal # Those are only needed for logging
|
./stack, ./computation, ./transaction_tracer, terminal # Those are only needed for logging
|
||||||
|
|
||||||
func invalidInstruction*(computation: var BaseComputation) {.inline.} =
|
func invalidInstruction*(computation: var BaseComputation) {.inline.} =
|
||||||
raise newException(ValueError, "Invalid instruction, received an opcode not implemented in the current fork.")
|
raise newException(ValueError, "Invalid instruction, received an opcode not implemented in the current fork.")
|
||||||
|
@ -191,12 +191,20 @@ proc opTableToCaseStmt(opTable: array[Op, NimNode], computation: NimNode): NimNo
|
||||||
let asOp = quote do: Op(`op`) # TODO: unfortunately when passing to runtime, ops are transformed into int
|
let asOp = quote do: Op(`op`) # TODO: unfortunately when passing to runtime, ops are transformed into int
|
||||||
if BaseGasCosts[op].kind == GckFixed:
|
if BaseGasCosts[op].kind == GckFixed:
|
||||||
quote do:
|
quote do:
|
||||||
|
if `computation`.tracingEnabled:
|
||||||
|
`computation`.tracer.traceOpCodeStarted(`computation`, $`asOp`)
|
||||||
`computation`.gasMeter.consumeGas(`computation`.gasCosts[`asOp`].cost, reason = $`asOp`)
|
`computation`.gasMeter.consumeGas(`computation`.gasCosts[`asOp`].cost, reason = $`asOp`)
|
||||||
`opImpl`(`computation`)
|
`opImpl`(`computation`)
|
||||||
|
if `computation`.tracingEnabled:
|
||||||
|
`computation`.tracer.traceOpCodeEnded(`computation`)
|
||||||
`instr` = `computation`.code.next()
|
`instr` = `computation`.code.next()
|
||||||
else:
|
else:
|
||||||
quote do:
|
quote do:
|
||||||
|
if `computation`.tracingEnabled:
|
||||||
|
`computation`.tracer.traceOpCodeStarted(`computation`, $`asOp`)
|
||||||
`opImpl`(`computation`)
|
`opImpl`(`computation`)
|
||||||
|
if `computation`.tracingEnabled:
|
||||||
|
`computation`.tracer.traceOpCodeEnded(`computation`)
|
||||||
when `asOp` in {Return, Revert, SelfDestruct}:
|
when `asOp` in {Return, Revert, SelfDestruct}:
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
import
|
||||||
|
json, strutils,
|
||||||
|
eth_common, stint, byteutils,
|
||||||
|
../vm_types, memory, stack
|
||||||
|
|
||||||
|
proc initTrace(t: var TransactionTracer) =
|
||||||
|
t.trace = newJObject()
|
||||||
|
t.trace["structLogs"] = newJArray()
|
||||||
|
|
||||||
|
proc traceOpCodeStarted*(t: var TransactionTracer, c: BaseComputation, op: string) =
|
||||||
|
if unlikely t.trace.isNil:
|
||||||
|
t.initTrace()
|
||||||
|
|
||||||
|
let j = newJObject()
|
||||||
|
t.trace["structLogs"].add(j)
|
||||||
|
|
||||||
|
j["op"] = %op.toUpperAscii
|
||||||
|
j["pc"] = %(c.code.pc - 1)
|
||||||
|
j["depth"] = %1 # stub
|
||||||
|
j["gas"] = %c.gasMeter.gasRemaining
|
||||||
|
t.gasRemaining = c.gasMeter.gasRemaining
|
||||||
|
|
||||||
|
# log stack
|
||||||
|
let st = newJArray()
|
||||||
|
for v in c.stack.values:
|
||||||
|
st.add(%v.dumpHex())
|
||||||
|
j["stack"] = st
|
||||||
|
# log memory
|
||||||
|
let mem = newJArray()
|
||||||
|
const chunkLen = 32
|
||||||
|
let numChunks = c.memory.len div chunkLen
|
||||||
|
for i in 0 ..< numChunks:
|
||||||
|
mem.add(%c.memory.bytes.toOpenArray(i * chunkLen, (i + 1) * chunkLen - 1).toHex())
|
||||||
|
j["memory"] = mem
|
||||||
|
# TODO: log storage
|
||||||
|
|
||||||
|
proc traceOpCodeEnded*(t: var TransactionTracer, c: BaseComputation) =
|
||||||
|
let j = t.trace["structLogs"].elems[^1]
|
||||||
|
j["gasCost"] = %(t.gasRemaining - c.gasMeter.gasRemaining)
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||||
|
|
||||||
import
|
import
|
||||||
tables,
|
tables, json,
|
||||||
eth_common,
|
eth_common,
|
||||||
./constants, ./vm_state,
|
./constants, ./vm_state,
|
||||||
./vm/[memory, stack, code_stream],
|
./vm/[memory, stack, code_stream],
|
||||||
|
@ -35,6 +35,8 @@ type
|
||||||
precompiles*: Table[string, Opcode]
|
precompiles*: Table[string, Opcode]
|
||||||
gasCosts*: GasCosts # TODO - will be hidden at a lower layer
|
gasCosts*: GasCosts # TODO - will be hidden at a lower layer
|
||||||
opCodeExec*: OpcodeExecutor
|
opCodeExec*: OpcodeExecutor
|
||||||
|
tracingEnabled*: bool
|
||||||
|
tracer*: TransactionTracer
|
||||||
|
|
||||||
Error* = ref object
|
Error* = ref object
|
||||||
info*: string
|
info*: string
|
||||||
|
@ -105,3 +107,8 @@ type
|
||||||
createAddress*: EthAddress
|
createAddress*: EthAddress
|
||||||
codeAddress*: EthAddress
|
codeAddress*: EthAddress
|
||||||
flags*: MsgFlags
|
flags*: MsgFlags
|
||||||
|
|
||||||
|
TransactionTracer* = object
|
||||||
|
trace*: JsonNode
|
||||||
|
gasRemaining*: GasInt
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue