2018-12-03 10:54:19 +00:00
|
|
|
import
|
2020-06-01 06:45:32 +00:00
|
|
|
db/[db_chain, accounts_cache, capturedb], eth/common, utils, json,
|
2018-12-04 11:42:55 +00:00
|
|
|
constants, vm_state, vm_types, transaction, p2p/executor,
|
2020-04-20 18:12:44 +00:00
|
|
|
eth/trie/db, nimcrypto, strutils,
|
2019-04-23 12:50:45 +00:00
|
|
|
chronicles, rpc/hexstrings, launcher,
|
2020-04-12 10:33:17 +00:00
|
|
|
vm/interpreter/vm_forks, ./config
|
2018-12-03 16:22:08 +00:00
|
|
|
|
2020-06-10 05:54:15 +00:00
|
|
|
when defined(geth):
|
|
|
|
import db/geth_db
|
|
|
|
|
|
|
|
proc getParentHeader(db: BaseChainDB, header: BlockHeader): BlockHeader =
|
|
|
|
db.blockHeader(header.blockNumber.truncate(uint64) - 1)
|
|
|
|
|
|
|
|
else:
|
|
|
|
proc getParentHeader(self: BaseChainDB, header: BlockHeader): BlockHeader =
|
|
|
|
self.getBlockHeader(header.parentHash)
|
2018-12-03 16:22:08 +00:00
|
|
|
|
2018-12-25 10:31:51 +00:00
|
|
|
proc `%`(x: openArray[byte]): JsonNode =
|
|
|
|
result = %toHex(x, false)
|
|
|
|
|
|
|
|
proc toJson(receipt: Receipt): JsonNode =
|
|
|
|
result = newJObject()
|
|
|
|
|
|
|
|
result["cumulativeGasUsed"] = %receipt.cumulativeGasUsed
|
|
|
|
result["bloom"] = %receipt.bloom
|
|
|
|
result["logs"] = %receipt.logs
|
|
|
|
|
|
|
|
if receipt.hasStateRoot:
|
|
|
|
result["root"] = %($receipt.stateRoot)
|
|
|
|
else:
|
|
|
|
result["status"] = %receipt.status
|
|
|
|
|
|
|
|
proc dumpReceipts*(chainDB: BaseChainDB, header: BlockHeader): JsonNode =
|
|
|
|
result = newJArray()
|
|
|
|
for receipt in chainDB.getReceipts(header):
|
|
|
|
result.add receipt.toJson
|
|
|
|
|
2018-12-31 03:27:02 +00:00
|
|
|
proc toJson*(receipts: seq[Receipt]): JsonNode =
|
2018-12-25 10:31:51 +00:00
|
|
|
result = newJArray()
|
|
|
|
for receipt in receipts:
|
|
|
|
result.add receipt.toJson
|
2018-12-03 10:54:19 +00:00
|
|
|
|
2020-06-01 06:45:32 +00:00
|
|
|
proc captureAccount(n: JsonNode, db: AccountsCache, address: EthAddress, name: string) =
|
2018-12-12 03:40:37 +00:00
|
|
|
var jaccount = newJObject()
|
|
|
|
jaccount["name"] = %name
|
2019-01-11 06:53:18 +00:00
|
|
|
jaccount["address"] = %("0x" & $address)
|
2020-06-01 06:45:32 +00:00
|
|
|
|
|
|
|
let nonce = db.getNonce(address)
|
|
|
|
let balance = db.getBalance(address)
|
|
|
|
let codeHash = db.getCodeHash(address)
|
|
|
|
let storageRoot = db.getStorageRoot(address)
|
|
|
|
|
2020-07-22 16:51:26 +00:00
|
|
|
jaccount["nonce"] = %(encodeQuantity(nonce).string.toLowerAscii)
|
2020-06-01 06:45:32 +00:00
|
|
|
jaccount["balance"] = %("0x" & balance.toHex)
|
2018-12-11 09:53:05 +00:00
|
|
|
|
|
|
|
let code = db.getCode(address)
|
2020-06-01 06:45:32 +00:00
|
|
|
jaccount["codeHash"] = %("0x" & ($codeHash).toLowerAscii)
|
2020-04-20 18:12:44 +00:00
|
|
|
jaccount["code"] = %("0x" & toHex(code, true))
|
2020-06-01 06:45:32 +00:00
|
|
|
jaccount["storageRoot"] = %("0x" & ($storageRoot).toLowerAscii)
|
2018-12-11 09:53:05 +00:00
|
|
|
|
|
|
|
var storage = newJObject()
|
|
|
|
for key, value in db.storage(address):
|
2019-01-11 06:53:18 +00:00
|
|
|
storage["0x" & key.dumpHex] = %("0x" & value.dumpHex)
|
2018-12-12 03:40:37 +00:00
|
|
|
jaccount["storage"] = storage
|
2018-12-11 09:53:05 +00:00
|
|
|
|
2018-12-12 03:40:37 +00:00
|
|
|
n.add jaccount
|
2018-12-11 09:53:05 +00:00
|
|
|
|
2018-12-12 15:18:46 +00:00
|
|
|
proc dumpMemoryDB*(node: JsonNode, memoryDB: TrieDatabaseRef) =
|
|
|
|
var n = newJObject()
|
|
|
|
for k, v in pairsInMemoryDB(memoryDB):
|
2018-12-25 10:31:51 +00:00
|
|
|
n[k.toHex(false)] = %v
|
2018-12-12 15:18:46 +00:00
|
|
|
node["state"] = n
|
|
|
|
|
2018-12-12 03:40:37 +00:00
|
|
|
const
|
|
|
|
senderName = "sender"
|
|
|
|
recipientName = "recipient"
|
|
|
|
minerName = "miner"
|
|
|
|
uncleName = "uncle"
|
2018-12-25 07:31:12 +00:00
|
|
|
internalTxName = "internalTx"
|
2018-12-11 09:53:05 +00:00
|
|
|
|
2020-04-12 10:33:17 +00:00
|
|
|
proc traceTransaction*(chainDB: BaseChainDB, header: BlockHeader,
|
2018-12-04 11:42:55 +00:00
|
|
|
body: BlockBody, txIndex: int, tracerFlags: set[TracerFlags] = {}): JsonNode =
|
2018-12-03 16:22:08 +00:00
|
|
|
let
|
2020-04-12 10:33:17 +00:00
|
|
|
parent = chainDB.getParentHeader(header)
|
2018-12-04 02:01:56 +00:00
|
|
|
# we add a memory layer between backend/lower layer db
|
2018-12-04 01:53:21 +00:00
|
|
|
# and capture state db snapshot during transaction execution
|
2018-12-03 16:22:08 +00:00
|
|
|
memoryDB = newMemoryDB()
|
2020-04-12 10:33:17 +00:00
|
|
|
captureDB = newCaptureDB(chainDB.db, memoryDB)
|
2018-12-03 16:22:08 +00:00
|
|
|
captureTrieDB = trieDB captureDB
|
2020-06-19 10:51:53 +00:00
|
|
|
captureChainDB = newBaseChainDB(captureTrieDB, false, PublicNetWork(chainDB.config.chainId)) # prune or not prune?
|
2019-02-14 15:20:41 +00:00
|
|
|
vmState = newBaseVMState(parent.stateRoot, header, captureChainDB, tracerFlags + {EnableAccount})
|
2018-12-04 02:01:56 +00:00
|
|
|
|
2018-12-31 03:27:02 +00:00
|
|
|
var stateDb = vmState.accountDb
|
2018-12-26 03:34:16 +00:00
|
|
|
|
2018-12-12 15:18:46 +00:00
|
|
|
if header.txRoot == BLANK_ROOT_HASH: return newJNull()
|
2019-03-13 21:36:54 +00:00
|
|
|
doAssert(body.transactions.calcTxRoot == header.txRoot)
|
|
|
|
doAssert(body.transactions.len != 0)
|
2018-12-03 10:54:19 +00:00
|
|
|
|
2018-12-11 09:53:05 +00:00
|
|
|
var
|
|
|
|
gasUsed: GasInt
|
2018-12-12 09:41:18 +00:00
|
|
|
before = newJArray()
|
|
|
|
after = newJArray()
|
2018-12-11 09:53:05 +00:00
|
|
|
stateDiff = %{"before": before, "after": after}
|
2018-12-26 03:34:16 +00:00
|
|
|
beforeRoot: Hash256
|
2018-12-11 09:53:05 +00:00
|
|
|
|
2020-06-22 00:48:23 +00:00
|
|
|
let
|
|
|
|
fork = chainDB.config.toFork(header.blockNumber)
|
|
|
|
miner = vmState.coinbase()
|
2019-04-23 12:50:45 +00:00
|
|
|
|
2018-12-03 10:54:19 +00:00
|
|
|
for idx, tx in body.transactions:
|
2018-12-12 03:40:37 +00:00
|
|
|
let sender = tx.getSender
|
|
|
|
let recipient = tx.getRecipient
|
2018-12-11 09:53:05 +00:00
|
|
|
|
2018-12-12 03:40:37 +00:00
|
|
|
if idx == txIndex:
|
|
|
|
vmState.enableTracing()
|
|
|
|
before.captureAccount(stateDb, sender, senderName)
|
|
|
|
before.captureAccount(stateDb, recipient, recipientName)
|
2020-06-22 00:48:23 +00:00
|
|
|
before.captureAccount(stateDb, miner, minerName)
|
2020-06-01 06:45:32 +00:00
|
|
|
stateDb.persist()
|
2018-12-25 10:31:51 +00:00
|
|
|
stateDiff["beforeRoot"] = %($stateDb.rootHash)
|
2018-12-26 03:34:16 +00:00
|
|
|
beforeRoot = stateDb.rootHash
|
2018-12-11 09:53:05 +00:00
|
|
|
|
2019-04-23 12:50:45 +00:00
|
|
|
gasUsed = processTransaction(tx, sender, vmState, fork)
|
2018-12-12 03:40:37 +00:00
|
|
|
|
|
|
|
if idx == txIndex:
|
|
|
|
after.captureAccount(stateDb, sender, senderName)
|
|
|
|
after.captureAccount(stateDb, recipient, recipientName)
|
2020-06-22 00:48:23 +00:00
|
|
|
after.captureAccount(stateDb, miner, minerName)
|
|
|
|
vmState.removeTracedAccounts(sender, recipient, miner)
|
2020-06-01 06:45:32 +00:00
|
|
|
stateDb.persist()
|
2018-12-25 10:31:51 +00:00
|
|
|
stateDiff["afterRoot"] = %($stateDb.rootHash)
|
2018-12-12 03:40:37 +00:00
|
|
|
break
|
2018-12-03 10:54:19 +00:00
|
|
|
|
2018-12-25 07:31:12 +00:00
|
|
|
# internal transactions:
|
2020-06-01 06:45:32 +00:00
|
|
|
var stateBefore = AccountsCache.init(captureTrieDB, beforeRoot, chainDB.pruneTrie)
|
2018-12-25 07:31:12 +00:00
|
|
|
for idx, acc in tracedAccountsPairs(vmState):
|
|
|
|
before.captureAccount(stateBefore, acc, internalTxName & $idx)
|
|
|
|
|
|
|
|
for idx, acc in tracedAccountsPairs(vmState):
|
|
|
|
after.captureAccount(stateDb, acc, internalTxName & $idx)
|
|
|
|
|
2018-12-03 16:22:08 +00:00
|
|
|
result = vmState.getTracingResult()
|
|
|
|
result["gas"] = %gasUsed
|
2018-12-25 10:31:51 +00:00
|
|
|
|
|
|
|
if TracerFlags.DisableStateDiff notin tracerFlags:
|
|
|
|
result["stateDiff"] = stateDiff
|
2018-12-03 16:22:08 +00:00
|
|
|
|
2018-12-04 01:53:21 +00:00
|
|
|
# now we dump captured state db
|
2018-12-04 02:01:56 +00:00
|
|
|
if TracerFlags.DisableState notin tracerFlags:
|
2018-12-12 15:18:46 +00:00
|
|
|
result.dumpMemoryDB(memoryDB)
|
2018-12-11 10:05:49 +00:00
|
|
|
|
2018-12-12 15:18:46 +00:00
|
|
|
proc dumpBlockState*(db: BaseChainDB, header: BlockHeader, body: BlockBody, dumpState = false): JsonNode =
|
2018-12-12 03:40:37 +00:00
|
|
|
let
|
|
|
|
parent = db.getParentHeader(header)
|
|
|
|
memoryDB = newMemoryDB()
|
|
|
|
captureDB = newCaptureDB(db.db, memoryDB)
|
|
|
|
captureTrieDB = trieDB captureDB
|
2020-06-19 10:51:53 +00:00
|
|
|
captureChainDB = newBaseChainDB(captureTrieDB, false, PublicNetWork(db.config.chainId))
|
2018-12-12 03:40:37 +00:00
|
|
|
# we only need stack dump if we want to scan for internal transaction address
|
2019-02-14 15:20:41 +00:00
|
|
|
vmState = newBaseVMState(parent.stateRoot, header, captureChainDB, {EnableTracing, DisableMemory, DisableStorage, EnableAccount})
|
2020-06-22 00:48:23 +00:00
|
|
|
miner = vmState.coinbase()
|
2018-12-12 03:40:37 +00:00
|
|
|
|
|
|
|
var
|
2018-12-12 09:41:18 +00:00
|
|
|
before = newJArray()
|
|
|
|
after = newJArray()
|
2020-06-01 06:45:32 +00:00
|
|
|
stateBefore = AccountsCache.init(captureTrieDB, parent.stateRoot, db.pruneTrie)
|
2018-12-12 03:40:37 +00:00
|
|
|
|
2018-12-12 09:41:18 +00:00
|
|
|
for idx, tx in body.transactions:
|
2018-12-12 03:40:37 +00:00
|
|
|
let sender = tx.getSender
|
|
|
|
let recipient = tx.getRecipient
|
2018-12-12 09:41:18 +00:00
|
|
|
before.captureAccount(stateBefore, sender, senderName & $idx)
|
|
|
|
before.captureAccount(stateBefore, recipient, recipientName & $idx)
|
2018-12-12 03:40:37 +00:00
|
|
|
|
2020-06-22 00:48:23 +00:00
|
|
|
before.captureAccount(stateBefore, miner, minerName)
|
2018-12-12 03:40:37 +00:00
|
|
|
|
|
|
|
for idx, uncle in body.uncles:
|
|
|
|
before.captureAccount(stateBefore, uncle.coinbase, uncleName & $idx)
|
|
|
|
|
2019-03-20 03:22:37 +00:00
|
|
|
discard captureChainDB.processBlock(header, body, vmState)
|
2018-12-12 03:40:37 +00:00
|
|
|
|
2019-02-02 09:20:45 +00:00
|
|
|
var stateAfter = vmState.accountDb
|
|
|
|
|
2018-12-12 09:41:18 +00:00
|
|
|
for idx, tx in body.transactions:
|
2018-12-12 03:40:37 +00:00
|
|
|
let sender = tx.getSender
|
|
|
|
let recipient = tx.getRecipient
|
2018-12-12 09:41:18 +00:00
|
|
|
after.captureAccount(stateAfter, sender, senderName & $idx)
|
|
|
|
after.captureAccount(stateAfter, recipient, recipientName & $idx)
|
2019-01-01 04:06:33 +00:00
|
|
|
vmState.removeTracedAccounts(sender, recipient)
|
2018-12-12 03:40:37 +00:00
|
|
|
|
2020-06-22 00:48:23 +00:00
|
|
|
after.captureAccount(stateAfter, miner, minerName)
|
|
|
|
vmState.removeTracedAccounts(miner)
|
2018-12-12 03:40:37 +00:00
|
|
|
|
|
|
|
for idx, uncle in body.uncles:
|
|
|
|
after.captureAccount(stateAfter, uncle.coinbase, uncleName & $idx)
|
2019-01-01 04:06:33 +00:00
|
|
|
vmState.removeTracedAccounts(uncle.coinbase)
|
2018-12-12 03:40:37 +00:00
|
|
|
|
2018-12-25 07:31:12 +00:00
|
|
|
# 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)
|
|
|
|
|
2018-12-12 03:40:37 +00:00
|
|
|
result = %{"before": before, "after": after}
|
2018-12-12 04:16:40 +00:00
|
|
|
|
2018-12-12 15:18:46 +00:00
|
|
|
if dumpState:
|
|
|
|
result.dumpMemoryDB(memoryDB)
|
|
|
|
|
2020-04-12 10:33:17 +00:00
|
|
|
proc traceBlock*(chainDB: BaseChainDB, header: BlockHeader, body: BlockBody, tracerFlags: set[TracerFlags] = {}): JsonNode =
|
2018-12-12 04:16:40 +00:00
|
|
|
let
|
2020-04-12 10:33:17 +00:00
|
|
|
parent = chainDB.getParentHeader(header)
|
2018-12-12 04:16:40 +00:00
|
|
|
memoryDB = newMemoryDB()
|
2020-04-12 10:33:17 +00:00
|
|
|
captureDB = newCaptureDB(chainDB.db, memoryDB)
|
2018-12-12 04:16:40 +00:00
|
|
|
captureTrieDB = trieDB captureDB
|
2020-06-19 10:51:53 +00:00
|
|
|
captureChainDB = newBaseChainDB(captureTrieDB, false, PublicNetWork(chainDB.config.chainId))
|
2019-02-14 15:20:41 +00:00
|
|
|
vmState = newBaseVMState(parent.stateRoot, header, captureChainDB, tracerFlags + {EnableTracing})
|
2018-12-12 04:16:40 +00:00
|
|
|
|
2018-12-12 15:18:46 +00:00
|
|
|
if header.txRoot == BLANK_ROOT_HASH: return newJNull()
|
2019-03-13 21:36:54 +00:00
|
|
|
doAssert(body.transactions.calcTxRoot == header.txRoot)
|
|
|
|
doAssert(body.transactions.len != 0)
|
2018-12-12 04:16:40 +00:00
|
|
|
|
|
|
|
var gasUsed = GasInt(0)
|
2020-04-12 10:33:17 +00:00
|
|
|
let fork = chainDB.config.toFork(header.blockNumber)
|
2018-12-12 04:16:40 +00:00
|
|
|
|
|
|
|
for tx in body.transactions:
|
2019-02-27 03:30:03 +00:00
|
|
|
let sender = tx.getSender
|
2019-04-23 12:50:45 +00:00
|
|
|
gasUsed = gasUsed + processTransaction(tx, sender, vmState, fork)
|
2018-12-12 04:16:40 +00:00
|
|
|
|
|
|
|
result = vmState.getTracingResult()
|
2018-12-12 04:31:53 +00:00
|
|
|
result["gas"] = %gasUsed
|
|
|
|
|
2018-12-12 15:18:46 +00:00
|
|
|
if TracerFlags.DisableState notin tracerFlags:
|
|
|
|
result.dumpMemoryDB(memoryDB)
|
|
|
|
|
2018-12-25 10:31:51 +00:00
|
|
|
proc traceTransactions*(chainDB: BaseChainDB, header: BlockHeader, blockBody: BlockBody): JsonNode =
|
|
|
|
result = newJArray()
|
|
|
|
for i in 0 ..< blockBody.transactions.len:
|
|
|
|
result.add traceTransaction(chainDB, header, blockBody, i, {DisableState})
|
|
|
|
|
2019-01-12 12:48:28 +00:00
|
|
|
proc dumpDebuggingMetaData*(chainDB: BaseChainDB, header: BlockHeader,
|
2019-02-03 09:08:13 +00:00
|
|
|
blockBody: BlockBody, vmState: BaseVMState, launchDebugger = true) =
|
2018-12-25 10:31:51 +00:00
|
|
|
let
|
|
|
|
blockNumber = header.blockNumber
|
|
|
|
|
|
|
|
var
|
|
|
|
memoryDB = newMemoryDB()
|
|
|
|
captureDB = newCaptureDB(chainDB.db, memoryDB)
|
|
|
|
captureTrieDB = trieDB captureDB
|
2020-06-19 10:51:53 +00:00
|
|
|
captureChainDB = newBaseChainDB(captureTrieDB, false, PublicNetWork(chainDB.config.chainId))
|
2019-02-03 09:08:13 +00:00
|
|
|
bloom = createBloom(vmState.receipts)
|
|
|
|
|
|
|
|
let blockSummary = %{
|
|
|
|
"receiptsRoot": %("0x" & toHex(calcReceiptRoot(vmState.receipts).data)),
|
|
|
|
"stateRoot": %("0x" & toHex(vmState.accountDb.rootHash.data)),
|
|
|
|
"logsBloom": %("0x" & toHex(bloom))
|
|
|
|
}
|
2018-12-25 10:31:51 +00:00
|
|
|
|
|
|
|
var metaData = %{
|
|
|
|
"blockNumber": %blockNumber.toHex,
|
2019-01-10 08:23:18 +00:00
|
|
|
"txTraces": traceTransactions(captureChainDB, header, blockBody),
|
|
|
|
"stateDump": dumpBlockState(captureChainDB, header, blockBody),
|
|
|
|
"blockTrace": traceBlock(captureChainDB, header, blockBody, {DisableState}),
|
2019-02-03 09:08:13 +00:00
|
|
|
"receipts": toJson(vmState.receipts),
|
|
|
|
"block": blockSummary
|
2018-12-25 10:31:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
metaData.dumpMemoryDB(memoryDB)
|
2019-01-12 12:48:28 +00:00
|
|
|
|
|
|
|
let jsonFileName = "debug" & $blockNumber & ".json"
|
|
|
|
if launchDebugger:
|
|
|
|
launchPremix(jsonFileName, metaData)
|
|
|
|
else:
|
|
|
|
writeFile(jsonFileName, metaData.pretty())
|