fix traceTransaction

This commit is contained in:
andri lim 2018-12-03 23:22:08 +07:00
parent 5509c66cce
commit 2552d6452a
5 changed files with 70 additions and 16 deletions

View File

@ -1,24 +1,44 @@
import import
db/[db_chain, state_db], eth_common, utils, json, db/[db_chain, state_db, capturedb], eth_common, utils, json,
constants, vm_state, vm_types, transaction, p2p/chain constants, vm_state, vm_types, transaction, p2p/chain,
eth_trie/db, nimcrypto
proc getParentHeader(self: BaseChainDB, header: BlockHeader): BlockHeader =
self.getBlockHeader(header.parentHash)
proc prefixHex(x: openArray[byte]): string =
"0x" & toHex(x, true)
proc traceTransaction*(db: BaseChainDB, header: BlockHeader, proc traceTransaction*(db: BaseChainDB, header: BlockHeader,
body: BlockBody, txIndex: int, tracerFlags: set[TracerFlags]): JsonNode = body: BlockBody, txIndex: int, tracerFlags: set[TracerFlags]): JsonNode =
let head = db.getCanonicalHead() let parent = db.getParentHeader(header)
assert(head.blockNumber == header.blockNumber - 1) var stateDb = newAccountStateDB(db.db, parent.stateRoot, db.pruneTrie)
var stateDb = newAccountStateDB(db.db, head.stateRoot, db.pruneTrie)
assert(body.transactions.calcTxRoot == header.txRoot) assert(body.transactions.calcTxRoot == header.txRoot)
if header.txRoot == BLANK_ROOT_HASH: return if header.txRoot == BLANK_ROOT_HASH: return
let vmState = newBaseVMState(head, db, tracerFlags + {EnableTracing}) let
memoryDB = newMemoryDB()
captureDB = newCaptureDB(db.db, memoryDB)
captureTrieDB = trieDB captureDB
captureChainDB = newBaseChainDB(captureTrieDB, false) # prune or not prune?
let vmState = newBaseVMState(parent, captureChainDB, tracerFlags + {TracerFlags.EnableTracing})
assert(body.transactions.len != 0) assert(body.transactions.len != 0)
var gasUsed: GasInt
for idx, tx in body.transactions: for idx, tx in body.transactions:
var sender: EthAddress var sender: EthAddress
if tx.getSender(sender): if tx.getSender(sender):
discard processTransaction(stateDb, tx, sender, vmState) let txFee = processTransaction(stateDb, tx, sender, vmState)
gasUsed = (txFee div tx.gasPrice.u256).truncate(GasInt)
if idx == txIndex: break if idx == txIndex: break
else: else:
assert(false, "Could not get sender") assert(false, "Could not get sender")
vmState.getTracingResult() result = vmState.getTracingResult()
result["gas"] = %gasUsed
var n = newJObject()
for k, v in pairsInMemoryDB(memoryDB):
n[k.prefixHex] = %v.prefixHex
result["snapshot"] = n

View File

@ -286,11 +286,14 @@ proc getGasRemaining*(c: BaseComputation): GasInt =
else: else:
result = c.gasMeter.gasRemaining result = c.gasMeter.gasRemaining
template tracingEnabled*(c: BaseComputation): bool = proc tracingEnabled*(c: BaseComputation): bool =
c.vmState.tracingEnabled c.vmState.tracingEnabled
template traceOpCodeStarted*(c: BaseComputation, op: string) = proc traceOpCodeStarted*(c: BaseComputation, op: string) =
traceOpCodeStarted(c.vmState.tracer, c, op) traceOpCodeStarted(c.vmState.tracer, c, op)
proc traceOpCodeEnded*(c: BaseComputation) = proc traceOpCodeEnded*(c: BaseComputation) =
c.vmState.tracer.traceOpCodeEnded(c) c.vmState.tracer.traceOpCodeEnded(c)
proc traceError*(c: BaseComputation) =
c.vmState.tracer.traceError(c)

View File

@ -1,10 +1,19 @@
import import
json, strutils, json, strutils,
eth_common, stint, byteutils, eth_common, stint, byteutils,
../vm_types, memory, stack ../vm_types, memory, stack,
../db/[db_chain, state_db],
eth_trie/hexary, ./message,
ranges/typedranges
proc initTracer*(tracer: var TransactionTracer, flags: set[TracerFlags] = {}) = proc initTracer*(tracer: var TransactionTracer, flags: set[TracerFlags] = {}) =
tracer.trace = newJObject() tracer.trace = newJObject()
# make appear at the top of json object
tracer.trace["gas"] = %0
tracer.trace["failed"] = %false
tracer.trace["returnValue"] = %""
tracer.trace["structLogs"] = newJArray() tracer.trace["structLogs"] = newJArray()
tracer.flags = flags tracer.flags = flags
@ -37,11 +46,32 @@ proc traceOpCodeStarted*(tracer: var TransactionTracer, c: BaseComputation, op:
mem.add(%c.memory.bytes.toOpenArray(i * chunkLen, (i + 1) * chunkLen - 1).toHex()) mem.add(%c.memory.bytes.toOpenArray(i * chunkLen, (i + 1) * chunkLen - 1).toHex())
j["memory"] = mem j["memory"] = mem
# TODO: log storage # TODO: this seems very inefficient
# could we improve it?
# TODO: figure out how to get storage
# when contract excecution interrupted by exception
if TracerFlags.DisableStorage notin tracer.flags: if TracerFlags.DisableStorage notin tracer.flags:
let storage = newJArray() var storage = newJObject()
var stateDB = c.vmState.chaindb.getStateDb(c.vmState.blockHeader.stateRoot, readOnly = true)
let storageRoot = stateDB.getStorageRoot(c.msg.storageAddress)
var trie = initHexaryTrie(c.vmState.chaindb.db, storageRoot)
for k, v in trie:
var key = k.toOpenArray.toHex
if key.len != 0:
storage[key] = %(v.toOpenArray.toHex)
j["storage"] = storage j["storage"] = storage
proc traceOpCodeEnded*(tracer: var TransactionTracer, c: BaseComputation) = proc traceOpCodeEnded*(tracer: var TransactionTracer, c: BaseComputation) =
let j = tracer.trace["structLogs"].elems[^1] let j = tracer.trace["structLogs"].elems[^1]
j["gasCost"] = %(tracer.gasRemaining - c.gasMeter.gasRemaining) j["gasCost"] = %(tracer.gasRemaining - c.gasMeter.gasRemaining)
proc traceError*(tracer: var TransactionTracer, c: BaseComputation) =
let j = tracer.trace["structLogs"].elems[^1]
# TODO: figure out how to get gasCost
# when contract execution failed before traceOpCodeEnded called
# because exception raised
#j["gasCost"] = %
j["error"] = %(c.error.info)
tracer.trace["failed"] = %true

View File

@ -102,13 +102,14 @@ proc applyCreateTransaction*(db: var AccountStateDB, t: Transaction, vmState: Ba
db.setCode(contractAddress, ByteRange()) db.setCode(contractAddress, ByteRange())
db.addBalance(sender, (t.gasLimit.u256 - gasUsed2 - codeCost.u256)*t.gasPrice.u256) db.addBalance(sender, (t.gasLimit.u256 - gasUsed2 - codeCost.u256)*t.gasPrice.u256)
return (gasUsed2 + codeCost.u256) * t.gasPrice.u256 return (gasUsed2 + codeCost.u256) * t.gasPrice.u256
else: else:
# FIXME: don't do this revert, but rather only subBalance correctly # FIXME: don't do this revert, but rather only subBalance correctly
# the if transactionfailed at end is what is supposed to pick it up # the if transactionfailed at end is what is supposed to pick it up
# especially when it's cross-function, it's ugly/fragile # especially when it's cross-function, it's ugly/fragile
db.addBalance(sender, t.value) db.addBalance(sender, t.value)
echo "isError: ", c.isError echo "isError: ", c.isError
if c.tracingEnabled:
c.traceError()
return t.gasLimit.u256 * t.gasPrice.u256 return t.gasLimit.u256 * t.gasPrice.u256
method executeTransaction(vmState: BaseVMState, transaction: Transaction): (BaseComputation, BlockHeader) {.base.}= method executeTransaction(vmState: BaseVMState, transaction: Transaction): (BaseComputation, BlockHeader) {.base.}=