add internal transactions dump

This commit is contained in:
andri lim 2018-12-25 14:31:12 +07:00 committed by zah
parent 5fc134ac2a
commit 8a6d351c22
5 changed files with 47 additions and 16 deletions

View File

@ -52,6 +52,7 @@ const
recipientName = "recipient"
minerName = "miner"
uncleName = "uncle"
internalTxName = "internalTx"
proc traceTransaction*(db: BaseChainDB, header: BlockHeader,
body: BlockBody, txIndex: int, tracerFlags: set[TracerFlags] = {}): JsonNode =
@ -63,9 +64,11 @@ proc traceTransaction*(db: BaseChainDB, header: BlockHeader,
captureDB = newCaptureDB(db.db, memoryDB)
captureTrieDB = trieDB captureDB
captureChainDB = newBaseChainDB(captureTrieDB, false) # prune or not prune?
vmState = newBaseVMState(parent, captureChainDB, tracerFlags)
vmState = newBaseVMState(parent, captureChainDB, tracerFlags + {EnableAccount})
var stateDb = newAccountStateDB(captureTrieDB, parent.stateRoot, db.pruneTrie)
var stateBefore = newAccountStateDB(captureTrieDB, parent.stateRoot, db.pruneTrie)
# stateDb and stateBefore will not the same after transaction execution
if header.txRoot == BLANK_ROOT_HASH: return newJNull()
assert(body.transactions.calcTxRoot == header.txRoot)
assert(body.transactions.len != 0)
@ -85,6 +88,7 @@ proc traceTransaction*(db: BaseChainDB, header: BlockHeader,
before.captureAccount(stateDb, sender, senderName)
before.captureAccount(stateDb, recipient, recipientName)
before.captureAccount(stateDb, header.coinbase, minerName)
stateDiff["beforeRoot"] = %($vmState.blockHeader.stateRoot)
let txFee = processTransaction(stateDb, tx, sender, vmState)
stateDb.addBalance(header.coinbase, txFee)
@ -94,8 +98,16 @@ proc traceTransaction*(db: BaseChainDB, header: BlockHeader,
after.captureAccount(stateDb, sender, senderName)
after.captureAccount(stateDb, recipient, recipientName)
after.captureAccount(stateDb, header.coinbase, minerName)
stateDiff["afterRoot"] = %($vmState.blockHeader.stateRoot)
break
# internal transactions:
for idx, acc in tracedAccountsPairs(vmState):
before.captureAccount(stateBefore, acc, internalTxName & $idx)
for idx, acc in tracedAccountsPairs(vmState):
after.captureAccount(stateDb, acc, internalTxName & $idx)
result = vmState.getTracingResult()
result["gas"] = %gasUsed
result["statediff"] = stateDiff
@ -105,20 +117,6 @@ proc traceTransaction*(db: BaseChainDB, header: BlockHeader,
result.dumpMemoryDB(memoryDB)
proc dumpBlockState*(db: BaseChainDB, header: BlockHeader, body: BlockBody, dumpState = false): JsonNode =
# TODO: scan tracing result and find internal transaction address
# then do account dump as usual, before and after
# cons: unreliable and prone to error.
# pros: no need to map account secure key back to address.
# alternative: capture state trie using captureDB, and then dump every
# account for this block.
# cons: we need to map account secure key back to account address.
# pros: no account will be missed, we will get all account touched by this block
# we can turn this address capture/mapping only during dumping block state and
# disable it during normal operation.
# also the order of capture needs to be reversed, collecting addresses *after*
# processing block then using that addresses to collect accounts from parent block
let
parent = db.getParentHeader(header)
memoryDB = newMemoryDB()
@ -126,7 +124,7 @@ proc dumpBlockState*(db: BaseChainDB, header: BlockHeader, body: BlockBody, dump
captureTrieDB = trieDB captureDB
captureChainDB = newBaseChainDB(captureTrieDB, false)
# we only need stack dump if we want to scan for internal transaction address
vmState = newBaseVMState(parent, captureChainDB, {EnableTracing, DisableMemory, DisableStorage})
vmState = newBaseVMState(parent, captureChainDB, {EnableTracing, DisableMemory, DisableStorage, EnableAccount})
var
before = newJArray()
@ -158,6 +156,13 @@ proc dumpBlockState*(db: BaseChainDB, header: BlockHeader, body: BlockBody, dump
for idx, uncle in body.uncles:
after.captureAccount(stateAfter, uncle.coinbase, uncleName & $idx)
# internal transactions:
for idx, acc in tracedAccountsPairs(vmState):
before.captureAccount(stateBefore, acc, internalTxName & $idx)
for idx, acc in tracedAccountsPairs(vmState):
after.captureAccount(stateAfter, acc, internalTxName & $idx)
result = %{"before": before, "after": after}
if dumpState:

View File

@ -122,3 +122,7 @@ proc peek*(stack: Stack): UInt256 =
proc `$`*(stack: Stack): string =
let values = stack.values.mapIt(&" {$it}").join("\n")
&"Stack:\n{values}"
proc `[]`*(stack: Stack, i: BackwardsIndex, T: typedesc): T =
# This should be used only for tracer/test/debugging
fromStackElement(stack.values[i], result)

View File

@ -18,6 +18,7 @@ proc initTracer*(tracer: var TransactionTracer, flags: set[TracerFlags] = {}) =
tracer.trace["structLogs"] = newJArray()
tracer.flags = flags
tracer.accounts = @[]
proc traceOpCodeStarted*(tracer: var TransactionTracer, c: BaseComputation, op: Op) =
if unlikely tracer.trace.isNil:
@ -48,6 +49,17 @@ proc traceOpCodeStarted*(tracer: var TransactionTracer, c: BaseComputation, op:
mem.add(%c.memory.bytes.toOpenArray(i * chunkLen, (i + 1) * chunkLen - 1).toHex())
j["memory"] = mem
if TracerFlags.EnableAccount in tracer.flags:
case op
of Call, CallCode, DelegateCall, StaticCall:
assert(c.stack.values.len > 2)
tracer.accounts.add c.stack[^2, EthAddress]
of SelfDestruct:
assert(c.stack.values.len > 1)
tracer.accounts.add c.stack[^1, EthAddress]
else:
discard
proc traceOpCodeEnded*(tracer: var TransactionTracer, c: BaseComputation, op: Op) =
let j = tracer.trace["structLogs"].elems[^1]

View File

@ -134,3 +134,11 @@ proc enableTracing*(vmState: BaseVMState) =
proc disableTracing*(vmState: BaseVMState) =
vmState.tracingEnabled = false
iterator tracedAccounts*(vmState: BaseVMState): EthAddress =
for acc in vmState.tracer.accounts:
yield acc
iterator tracedAccountsPairs*(vmState: BaseVMState): (int, EthAddress) =
for idx, acc in vmState.tracer.accounts:
yield (idx, acc)

View File

@ -34,11 +34,13 @@ type
DisableMemory
DisableStack
DisableState
EnableAccount
TransactionTracer* = object
trace*: JsonNode
gasRemaining*: GasInt
flags*: set[TracerFlags]
accounts*: seq[EthAddress]
OpcodeExecutor* = proc(computation: var BaseComputation)