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