nimbus-eth1/nimbus/vm/transaction_tracer.nim

130 lines
4.2 KiB
Nim
Raw Normal View History

2018-10-28 12:11:19 +00:00
import
2019-02-06 10:17:35 +00:00
json, strutils, sets, hashes,
2019-02-05 19:15:50 +00:00
chronicles, nimcrypto, eth/common, stint,
2019-11-13 14:49:39 +00:00
../vm_types, memory, stack, ../db/state_db,
2020-04-15 11:09:49 +00:00
eth/trie/hexary, stew/ranges/typedranges,
./interpreter/opcode_values
2018-10-28 12:11:19 +00:00
logScope:
topics = "vm opcode"
2019-02-06 10:17:35 +00:00
proc hash*(x: Uint256): Hash =
result = hash(x.toByteArrayBE)
2018-12-03 10:54:19 +00:00
proc initTracer*(tracer: var TransactionTracer, flags: set[TracerFlags] = {}) =
tracer.trace = newJObject()
2018-12-03 16:22:08 +00:00
# make appear at the top of json object
tracer.trace["gas"] = %0
tracer.trace["failed"] = %false
tracer.trace["returnValue"] = %""
2018-12-03 10:54:19 +00:00
tracer.trace["structLogs"] = newJArray()
tracer.flags = flags
2019-11-13 14:49:39 +00:00
tracer.accounts = initHashSet[EthAddress]()
tracer.storageKeys = @[]
2019-02-25 13:02:16 +00:00
proc prepare*(tracer: var TransactionTracer, compDepth: int) =
2019-02-27 02:27:26 +00:00
# this uncommon arragement is intentional
# compDepth will be varying up and down: 1,2,3,4,3,3,2,2,1
# see issue #245 and PR #247 discussion
if compDepth >= tracer.storageKeys.len:
let prevLen = tracer.storageKeys.len
tracer.storageKeys.setLen(compDepth + 1)
2019-02-25 13:02:16 +00:00
for i in prevLen ..< tracer.storageKeys.len - 1:
2019-11-13 14:49:39 +00:00
tracer.storageKeys[i] = initHashSet[Uint256]()
2019-11-13 14:49:39 +00:00
tracer.storageKeys[compDepth] = initHashSet[Uint256]()
2019-02-25 13:02:16 +00:00
proc rememberStorageKey(tracer: var TransactionTracer, compDepth: int, key: Uint256) =
tracer.storageKeys[compDepth].incl key
iterator storage(tracer: TransactionTracer, compDepth: int): Uint256 =
2019-03-13 21:36:54 +00:00
doAssert compDepth >= 0 and compDepth < tracer.storageKeys.len
for key in tracer.storageKeys[compDepth]:
yield key
2018-10-28 12:11:19 +00:00
proc traceOpCodeStarted*(tracer: var TransactionTracer, c: Computation, op: Op): int =
2018-12-03 10:54:19 +00:00
if unlikely tracer.trace.isNil:
tracer.initTracer()
2018-10-28 12:11:19 +00:00
let j = newJObject()
2018-12-03 10:54:19 +00:00
tracer.trace["structLogs"].add(j)
2018-10-28 12:11:19 +00:00
j["op"] = %(($op).toUpperAscii)
2018-10-28 12:11:19 +00:00
j["pc"] = %(c.code.pc - 1)
j["depth"] = %(c.msg.depth + 1)
2018-10-28 12:11:19 +00:00
j["gas"] = %c.gasMeter.gasRemaining
# log stack
2018-12-03 10:54:19 +00:00
if TracerFlags.DisableStack notin tracer.flags:
let st = newJArray()
for v in c.stack.values:
st.add(%v.dumpHex())
j["stack"] = st
2018-10-28 12:11:19 +00:00
# log memory
2018-12-03 10:54:19 +00:00
if TracerFlags.DisableMemory notin tracer.flags:
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
2018-10-28 12:11:19 +00:00
2018-12-25 07:31:12 +00:00
if TracerFlags.EnableAccount in tracer.flags:
case op
of Call, CallCode, DelegateCall, StaticCall:
2019-02-23 13:08:59 +00:00
if c.stack.values.len > 2:
tracer.accounts.incl c.stack[^2, EthAddress]
of ExtCodeCopy, ExtCodeSize, Balance, SelfDestruct:
2019-02-23 13:08:59 +00:00
if c.stack.values.len > 1:
tracer.accounts.incl c.stack[^1, EthAddress]
2018-12-25 07:31:12 +00:00
else:
discard
2019-02-06 10:17:35 +00:00
if TracerFlags.DisableStorage notin tracer.flags:
if op == Sstore:
2019-02-23 13:08:59 +00:00
if c.stack.values.len > 1:
tracer.rememberStorageKey(c.msg.depth, c.stack[^1, Uint256])
2019-02-06 10:17:35 +00:00
2019-02-21 08:17:43 +00:00
result = tracer.trace["structLogs"].len - 1
proc traceOpCodeEnded*(tracer: var TransactionTracer, c: Computation, op: Op, lastIndex: int) =
2019-02-21 08:17:43 +00:00
let j = tracer.trace["structLogs"].elems[lastIndex]
2018-12-04 08:13:35 +00:00
2018-12-03 16:22:08 +00:00
# TODO: figure out how to get storage
# when contract execution interrupted by exception
2018-12-03 10:54:19 +00:00
if TracerFlags.DisableStorage notin tracer.flags:
2018-12-03 16:22:08 +00:00
var storage = newJObject()
if c.msg.depth < tracer.storageKeys.len:
var stateDB = c.vmState.accountDb
for key in tracer.storage(c.msg.depth):
let (value, _) = stateDB.getStorage(c.msg.contractAddress, key)
storage[key.dumpHex] = %(value.dumpHex)
j["storage"] = storage
2018-10-28 12:11:19 +00:00
let gasRemaining = j["gas"].getBiggestInt()
2019-02-21 08:17:43 +00:00
j["gasCost"] = %(gasRemaining - c.gasMeter.gasRemaining)
2018-12-03 16:22:08 +00:00
if op in {Return, Revert}:
2020-01-30 10:15:06 +00:00
let returnValue = %("0x" & toHex(c.output, true))
2018-12-04 10:56:16 +00:00
j["returnValue"] = returnValue
tracer.trace["returnValue"] = returnValue
2018-12-04 08:53:03 +00:00
trace "Op", json = j.pretty()
proc traceError*(tracer: var TransactionTracer, c: Computation) =
2019-03-18 06:13:16 +00:00
if tracer.trace["structLogs"].elems.len > 0:
let j = tracer.trace["structLogs"].elems[^1]
j["error"] = %(c.error.info)
trace "Error", json = j.pretty()
2018-12-03 16:22:08 +00:00
2019-04-15 04:34:41 +00:00
# even though the gasCost is incorrect,
# we have something to display,
# it is an error anyway
let gasRemaining = j["gas"].getBiggestInt()
2019-04-15 04:34:41 +00:00
j["gasCost"] = %(gasRemaining - c.gasMeter.gasRemaining)
2018-12-03 16:22:08 +00:00
tracer.trace["failed"] = %true