add geth compatibility flag to emvstate tool and transaction tracer

This commit is contained in:
jangko 2022-12-14 16:42:55 +07:00
parent abafc3e91f
commit 2b5195c526
No known key found for this signature in database
GPG Key ID: 31702AE10541E6B9
6 changed files with 95 additions and 21 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -38,6 +38,7 @@ export
vms.status,
vms.`status=`,
vms.tracedAccounts,
vms.tracedAccountsPairs
vms.tracedAccountsPairs,
vms.tracerGasUsed
# End

View File

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