add geth compatibility flag to emvstate tool and transaction tracer
This commit is contained in:
parent
abafc3e91f
commit
2b5195c526
|
@ -365,6 +365,12 @@ proc removeTracedAccounts*(vmState: BaseVMState, accounts: varargs[EthAddress])
|
||||||
for acc in accounts:
|
for acc in accounts:
|
||||||
vmState.tracer.accounts.excl acc
|
vmState.tracer.accounts.excl acc
|
||||||
|
|
||||||
|
proc tracerGasUsed*(vmState: BaseVMState, gasUsed: GasInt) =
|
||||||
|
vmState.tracer.gasUsed = gasUsed
|
||||||
|
|
||||||
|
proc tracerGasUsed*(vmState: BaseVMState): GasInt =
|
||||||
|
vmState.tracer.gasUsed
|
||||||
|
|
||||||
proc status*(vmState: BaseVMState): bool =
|
proc status*(vmState: BaseVMState): bool =
|
||||||
ExecutionOK in vmState.flags
|
ExecutionOK in vmState.flags
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,6 @@ proc initTracer*(tracer: var TransactionTracer, flags: set[TracerFlags] = {}) =
|
||||||
tracer.trace["gas"] = %0
|
tracer.trace["gas"] = %0
|
||||||
tracer.trace["failed"] = %false
|
tracer.trace["failed"] = %false
|
||||||
tracer.trace["returnValue"] = %""
|
tracer.trace["returnValue"] = %""
|
||||||
|
|
||||||
tracer.trace["structLogs"] = newJArray()
|
tracer.trace["structLogs"] = newJArray()
|
||||||
tracer.flags = flags
|
tracer.flags = flags
|
||||||
tracer.accounts = initHashSet[EthAddress]()
|
tracer.accounts = initHashSet[EthAddress]()
|
||||||
|
@ -44,6 +43,16 @@ iterator storage(tracer: TransactionTracer, compDepth: int): UInt256 =
|
||||||
for key in tracer.storageKeys[compDepth]:
|
for key in tracer.storageKeys[compDepth]:
|
||||||
yield key
|
yield key
|
||||||
|
|
||||||
|
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 encodeHexInt(x: SomeInteger): JsonNode =
|
||||||
|
%("0x" & x.toHex.stripLeadingZeros.toLowerAscii)
|
||||||
|
|
||||||
proc traceOpCodeStarted*(tracer: var TransactionTracer, c: Computation, op: Op): int =
|
proc traceOpCodeStarted*(tracer: var TransactionTracer, c: Computation, op: Op): int =
|
||||||
if unlikely tracer.trace.isNil:
|
if unlikely tracer.trace.isNil:
|
||||||
tracer.initTracer()
|
tracer.initTracer()
|
||||||
|
@ -51,17 +60,34 @@ proc traceOpCodeStarted*(tracer: var TransactionTracer, c: Computation, op: Op):
|
||||||
let j = newJObject()
|
let j = newJObject()
|
||||||
tracer.trace["structLogs"].add(j)
|
tracer.trace["structLogs"].add(j)
|
||||||
|
|
||||||
j["op"] = %(($op).toUpperAscii)
|
if TracerFlags.GethCompatibility in tracer.flags:
|
||||||
j["pc"] = %(c.code.pc - 1)
|
j["pc"] = %(c.code.pc - 1)
|
||||||
j["depth"] = %(c.msg.depth + 1)
|
j["op"] = %(op.int)
|
||||||
j["gas"] = %c.gasMeter.gasRemaining
|
j["gas"] = encodeHexInt(c.gasMeter.gasRemaining)
|
||||||
|
j["gasCost"] = %("")
|
||||||
|
j["memSize"] = %c.memory.len
|
||||||
|
j["opName"] = %(($op).toUpperAscii)
|
||||||
|
j["depth"] = %(c.msg.depth + 1)
|
||||||
|
|
||||||
# log stack
|
# log stack
|
||||||
if TracerFlags.DisableStack notin tracer.flags:
|
if TracerFlags.DisableStack notin tracer.flags:
|
||||||
let st = newJArray()
|
let st = newJArray()
|
||||||
for v in c.stack.values:
|
for v in c.stack.values:
|
||||||
st.add(%v.dumpHex())
|
st.add(%("0x" & v.dumpHex.stripLeadingZeros))
|
||||||
j["stack"] = st
|
j["stack"] = st
|
||||||
|
|
||||||
|
else:
|
||||||
|
j["op"] = %(($op).toUpperAscii)
|
||||||
|
j["pc"] = %(c.code.pc - 1)
|
||||||
|
j["depth"] = %(c.msg.depth + 1)
|
||||||
|
j["gas"] = %c.gasMeter.gasRemaining
|
||||||
|
|
||||||
|
# log stack
|
||||||
|
if TracerFlags.DisableStack notin tracer.flags:
|
||||||
|
let st = newJArray()
|
||||||
|
for v in c.stack.values:
|
||||||
|
st.add(%v.dumpHex())
|
||||||
|
j["stack"] = st
|
||||||
|
|
||||||
# log memory
|
# log memory
|
||||||
if TracerFlags.DisableMemory notin tracer.flags:
|
if TracerFlags.DisableMemory notin tracer.flags:
|
||||||
|
@ -101,11 +127,19 @@ proc traceOpCodeEnded*(tracer: var TransactionTracer, c: Computation, op: Op, la
|
||||||
var stateDB = c.vmState.stateDB
|
var stateDB = c.vmState.stateDB
|
||||||
for key in tracer.storage(c.msg.depth):
|
for key in tracer.storage(c.msg.depth):
|
||||||
let value = stateDB.getStorage(c.msg.contractAddress, key)
|
let value = stateDB.getStorage(c.msg.contractAddress, key)
|
||||||
storage[key.dumpHex] = %(value.dumpHex)
|
if TracerFlags.GethCompatibility in tracer.flags:
|
||||||
|
storage["0x" & key.dumpHex.stripLeadingZeros] =
|
||||||
|
%("0x" & value.dumpHex.stripLeadingZeros)
|
||||||
|
else:
|
||||||
|
storage[key.dumpHex] = %(value.dumpHex)
|
||||||
j["storage"] = storage
|
j["storage"] = storage
|
||||||
|
|
||||||
let gasRemaining = j["gas"].getBiggestInt()
|
if TracerFlags.GethCompatibility in tracer.flags:
|
||||||
j["gasCost"] = %(gasRemaining - c.gasMeter.gasRemaining)
|
let gas = fromHex[GasInt](j["gas"].getStr)
|
||||||
|
j["gasCost"] = encodeHexInt(gas - c.gasMeter.gasRemaining)
|
||||||
|
else:
|
||||||
|
let gas = j["gas"].getBiggestInt()
|
||||||
|
j["gasCost"] = %(gas - c.gasMeter.gasRemaining)
|
||||||
|
|
||||||
if op in {Return, Revert} and TracerFlags.DisableReturnData notin tracer.flags:
|
if op in {Return, Revert} and TracerFlags.DisableReturnData notin tracer.flags:
|
||||||
let returnValue = %("0x" & toHex(c.output, true))
|
let returnValue = %("0x" & toHex(c.output, true))
|
||||||
|
@ -123,7 +157,11 @@ proc traceError*(tracer: var TransactionTracer, c: Computation) =
|
||||||
# even though the gasCost is incorrect,
|
# even though the gasCost is incorrect,
|
||||||
# we have something to display,
|
# we have something to display,
|
||||||
# it is an error anyway
|
# it is an error anyway
|
||||||
let gasRemaining = j["gas"].getBiggestInt()
|
if TracerFlags.GethCompatibility in tracer.flags:
|
||||||
j["gasCost"] = %(gasRemaining - c.gasMeter.gasRemaining)
|
let gas = fromHex[GasInt](j["gas"].getStr)
|
||||||
|
j["gasCost"] = encodeHexInt(gas - c.gasMeter.gasRemaining)
|
||||||
|
else:
|
||||||
|
let gas = j["gas"].getBiggestInt()
|
||||||
|
j["gasCost"] = %(gas - c.gasMeter.gasRemaining)
|
||||||
|
|
||||||
tracer.trace["failed"] = %true
|
tracer.trace["failed"] = %true
|
||||||
|
|
|
@ -65,12 +65,14 @@ type
|
||||||
DisableStateDiff
|
DisableStateDiff
|
||||||
EnableAccount
|
EnableAccount
|
||||||
DisableReturnData
|
DisableReturnData
|
||||||
|
GethCompatibility
|
||||||
|
|
||||||
TransactionTracer* = object
|
TransactionTracer* = object
|
||||||
trace*: JsonNode
|
trace*: JsonNode
|
||||||
flags*: set[TracerFlags]
|
flags*: set[TracerFlags]
|
||||||
accounts*: HashSet[EthAddress]
|
accounts*: HashSet[EthAddress]
|
||||||
storageKeys*: seq[HashSet[UInt256]]
|
storageKeys*: seq[HashSet[UInt256]]
|
||||||
|
gasUsed*: GasInt
|
||||||
|
|
||||||
Computation* = ref object
|
Computation* = ref object
|
||||||
# The execution computation
|
# The execution computation
|
||||||
|
|
|
@ -242,6 +242,8 @@ proc finishRunningComputation(host: TransactionHost, call: CallParams): CallResu
|
||||||
let c = host.computation
|
let c = host.computation
|
||||||
|
|
||||||
let gasRemaining = calculateAndPossiblyRefundGas(host, call)
|
let gasRemaining = calculateAndPossiblyRefundGas(host, call)
|
||||||
|
# evm gas used without intrinsic gas
|
||||||
|
host.vmState.tracerGasUsed(host.msg.gas - gasRemaining)
|
||||||
|
|
||||||
result.isError = c.isError
|
result.isError = c.isError
|
||||||
result.gasUsed = call.gasLimit - gasRemaining
|
result.gasUsed = call.gasLimit - gasRemaining
|
||||||
|
|
|
@ -38,6 +38,7 @@ export
|
||||||
vms.status,
|
vms.status,
|
||||||
vms.`status=`,
|
vms.`status=`,
|
||||||
vms.tracedAccounts,
|
vms.tracedAccounts,
|
||||||
vms.tracedAccountsPairs
|
vms.tracedAccountsPairs,
|
||||||
|
vms.tracerGasUsed
|
||||||
|
|
||||||
# End
|
# End
|
||||||
|
|
|
@ -50,6 +50,7 @@ type
|
||||||
StateResult = object
|
StateResult = object
|
||||||
name : string
|
name : string
|
||||||
pass : bool
|
pass : bool
|
||||||
|
root : Hash256
|
||||||
fork : string
|
fork : string
|
||||||
error: string
|
error: string
|
||||||
state: StateDump
|
state: StateDump
|
||||||
|
@ -87,14 +88,14 @@ proc verifyResult(ctx: var StateContext, vmState: BaseVMState) =
|
||||||
let obtainedHash = vmState.readOnlyStateDB.rootHash
|
let obtainedHash = vmState.readOnlyStateDB.rootHash
|
||||||
if obtainedHash != ctx.expectedHash:
|
if obtainedHash != ctx.expectedHash:
|
||||||
ctx.error = "post state root mismatch: got $1, want $2" %
|
ctx.error = "post state root mismatch: got $1, want $2" %
|
||||||
[$obtainedHash, $ctx.expectedHash]
|
[($obtainedHash).toLowerAscii, $ctx.expectedHash]
|
||||||
return
|
return
|
||||||
|
|
||||||
let logEntries = vmState.getAndClearLogEntries()
|
let logEntries = vmState.getAndClearLogEntries()
|
||||||
let actualLogsHash = rlpHash(logEntries)
|
let actualLogsHash = rlpHash(logEntries)
|
||||||
if actualLogsHash != ctx.expectedLogs:
|
if actualLogsHash != ctx.expectedLogs:
|
||||||
ctx.error = "post state log hash mismatch: got $1, want $2" %
|
ctx.error = "post state log hash mismatch: got $1, want $2" %
|
||||||
[$actualLogsHash, $ctx.expectedLogs]
|
[($actualLogsHash).toLowerAscii, $ctx.expectedLogs]
|
||||||
return
|
return
|
||||||
|
|
||||||
proc `%`(x: UInt256): JsonNode =
|
proc `%`(x: UInt256): JsonNode =
|
||||||
|
@ -143,6 +144,7 @@ proc writeResultToStdout(stateRes: seq[StateResult]) =
|
||||||
let z = %{
|
let z = %{
|
||||||
"name" : %(res.name),
|
"name" : %(res.name),
|
||||||
"pass" : %(res.pass),
|
"pass" : %(res.pass),
|
||||||
|
"stateRoot" : %(res.root),
|
||||||
"fork" : %(res.fork),
|
"fork" : %(res.fork),
|
||||||
"error": %(res.error)
|
"error": %(res.error)
|
||||||
}
|
}
|
||||||
|
@ -173,16 +175,37 @@ proc dumpState(vmState: BaseVMState): StateDump =
|
||||||
accounts: dumpAccounts(vmState.stateDB)
|
accounts: dumpAccounts(vmState.stateDB)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
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 encodeHexInt(x: SomeInteger): JsonNode =
|
||||||
|
%("0x" & x.toHex.stripLeadingZeros.toLowerAscii)
|
||||||
|
|
||||||
proc writeTraceToStderr(vmState: BaseVMState, pretty: bool) =
|
proc writeTraceToStderr(vmState: BaseVMState, pretty: bool) =
|
||||||
let trace = vmState.getTracingResult()
|
let trace = vmState.getTracingResult()
|
||||||
|
trace["gasUsed"] = encodeHexInt(vmState.tracerGasUsed)
|
||||||
|
trace.delete("gas")
|
||||||
|
let stateRoot = %{
|
||||||
|
"stateRoot": %(vmState.readOnlyStateDB.rootHash)
|
||||||
|
}
|
||||||
if pretty:
|
if pretty:
|
||||||
stderr.writeLine(trace.pretty)
|
stderr.writeLine(trace.pretty)
|
||||||
else:
|
else:
|
||||||
|
var gasUsed = 0
|
||||||
let logs = trace["structLogs"]
|
let logs = trace["structLogs"]
|
||||||
trace.delete("structLogs")
|
trace.delete("structLogs")
|
||||||
for x in logs:
|
for x in logs:
|
||||||
|
if "error" in x:
|
||||||
|
trace["error"] = x["error"]
|
||||||
|
x.delete("error")
|
||||||
stderr.writeLine($x)
|
stderr.writeLine($x)
|
||||||
|
|
||||||
stderr.writeLine($trace)
|
stderr.writeLine($trace)
|
||||||
|
stderr.writeLine($stateRoot)
|
||||||
|
|
||||||
proc runExecution(ctx: var StateContext, conf: StateConf, pre: JsonNode): StateResult =
|
proc runExecution(ctx: var StateContext, conf: StateConf, pre: JsonNode): StateResult =
|
||||||
let
|
let
|
||||||
|
@ -213,6 +236,7 @@ proc runExecution(ctx: var StateContext, conf: StateConf, pre: JsonNode): StateR
|
||||||
result = StateResult(
|
result = StateResult(
|
||||||
name : ctx.name,
|
name : ctx.name,
|
||||||
pass : ctx.error.len == 0,
|
pass : ctx.error.len == 0,
|
||||||
|
root : vmState.stateDB.rootHash,
|
||||||
fork : toString(ctx.fork),
|
fork : toString(ctx.fork),
|
||||||
error: ctx.error
|
error: ctx.error
|
||||||
)
|
)
|
||||||
|
@ -233,12 +257,13 @@ proc runExecution(ctx: var StateContext, conf: StateConf, pre: JsonNode): StateR
|
||||||
if ctx.fork >= FkSpurious:
|
if ctx.fork >= FkSpurious:
|
||||||
if db.isEmptyAccount(miner):
|
if db.isEmptyAccount(miner):
|
||||||
db.deleteAccount(miner)
|
db.deleteAccount(miner)
|
||||||
db.persist()
|
db.persist(clearCache = false)
|
||||||
|
|
||||||
proc toTracerFlags(conf: Stateconf): set[TracerFlags] =
|
proc toTracerFlags(conf: Stateconf): set[TracerFlags] =
|
||||||
result = {
|
result = {
|
||||||
TracerFlags.DisableStateDiff,
|
TracerFlags.DisableStateDiff,
|
||||||
TracerFlags.EnableTracing
|
TracerFlags.EnableTracing,
|
||||||
|
TracerFlags.GethCompatibility
|
||||||
}
|
}
|
||||||
|
|
||||||
if conf.disableMemory : result.incl TracerFlags.DisableMemory
|
if conf.disableMemory : result.incl TracerFlags.DisableMemory
|
||||||
|
|
Loading…
Reference in New Issue