nimbus-eth1/premix/premix.nim

217 lines
6.7 KiB
Nim
Raw Normal View History

2019-01-09 11:40:25 +00:00
import
2019-01-09 12:10:58 +00:00
json, downloader, stint, strutils, os,
2019-01-10 17:44:53 +00:00
../nimbus/tracer, chronicles, prestate,
js_tracer, eth_common, byteutils, parser,
nimcrypto
2019-01-09 11:40:25 +00:00
proc fakeAlloc(n: JsonNode) =
const
chunk = repeat('0', 64)
for i in 1 ..< n.len:
if not n[i].hasKey("memory"): return
let
prevMem = n[i-1]["memory"]
currMem = n[i]["memory"]
if currMem.len > prevMem.len:
let diff = currMem.len - prevMem.len
for _ in 0 ..< diff:
prevMem.add %chunk
2019-01-10 17:44:53 +00:00
proc jsonTracer(tracer: string): JsonNode =
result = %{ "tracer": %tracer }
proc requestTrace(txHash, tracer: JsonNode): JsonNode =
let txTrace = request("debug_traceTransaction", %[txHash, tracer])
if txTrace.kind == JNull:
error "requested postState not available", txHash=txHash
raise newException(ValueError, "Error when retrieving transaction postState")
result = txTrace
2019-01-11 06:53:18 +00:00
proc requestBlockState(postState: JsonNode, thisBlock: Block, addresses: openArray[EthAddress]) =
let number = %(thisBlock.header.blockNumber.prefixHex)
var txTrace = newJObject()
for a in addresses:
let address = a.prefixHex
let trace = request("eth_getProof", %[%address, %[], number])
let account = %{
"codeHash": trace["codeHash"],
"storageRoot": trace["storageHash"],
"balance": trace["balance"],
"nonce": trace["nonce"],
"code": newJString("0x"),
"storage": newJObject()
}
txTrace[address] = account
postState.add txTrace
proc hasTracerData(tx: JsonNode, blockNumber: Uint256): bool =
let number = %(blockNumber.prefixHex)
if tx["to"].kind == JNull:
let t = parseTransaction(tx)
let code = request("eth_getCode", %[%t.getRecipient.prefixHex, number])
return code.getStr.len > 2 or t.payload.len > 0
2019-01-11 06:53:18 +00:00
let code = request("eth_getCode", %[tx["to"], number])
result = code.getStr.len > 2 # "0x"
proc requestPostState(n: JsonNode, jsTracer: string, thisBlock: Block): JsonNode =
2019-01-09 11:40:25 +00:00
let txs = n["transactions"]
2019-01-10 17:44:53 +00:00
result = newJArray()
if txs.len == 0: return
let tracer = jsonTracer(jsTracer)
for tx in txs:
2019-01-11 06:53:18 +00:00
if hasTracerData(tx, thisBlock.header.blockNumber):
let
txHash = tx["hash"]
txTrace = requestTrace(txHash, tracer)
result.add txTrace
else:
let t = parseTransaction(tx)
var address: array[2, EthAddress]
address[0] = t.getRecipient
address[1] = t.getSender
requestBlockState(result, thisBlock, address)
2019-01-10 03:34:56 +00:00
2019-01-10 17:44:53 +00:00
proc padding(x: string): JsonNode =
let val = x.substr(2)
let pad = repeat('0', 64 - val.len)
result = newJString("0x" & pad & val)
2019-01-10 03:34:56 +00:00
2019-01-10 17:44:53 +00:00
proc requestBlockState(postState: JsonNode, thisBlock: Block) =
let number = %(thisBlock.header.blockNumber.prefixHex)
2019-01-10 03:34:56 +00:00
2019-01-10 17:44:53 +00:00
for state in postState:
for address, account in state:
var storage = newJArray()
for k, _ in account["storage"]:
storage.add %k
let trace = request("eth_getProof", %[%address, storage, number])
account["codeHash"] = trace["codeHash"]
2019-01-11 06:53:18 +00:00
account["storageRoot"] = trace["storageHash"]
account["nonce"] = trace["nonce"]
account["balance"] = trace["balance"]
2019-01-10 17:44:53 +00:00
for x in trace["storageProof"]:
account["storage"][x["key"].getStr] = padding(x["value"].getStr())
2019-01-09 11:40:25 +00:00
proc copyAccount(acc: JsonNode): JsonNode =
result = newJObject()
2019-01-11 06:53:18 +00:00
if acc.hasKey("name"):
result["name"] = newJString(acc["name"].getStr)
2019-01-09 11:40:25 +00:00
result["balance"] = newJString(acc["balance"].getStr)
2019-01-11 06:53:18 +00:00
result["nonce"] = newJString(acc["nonce"].getStr)
2019-01-09 11:40:25 +00:00
result["code"] = newJString(acc["code"].getStr)
var storage = newJObject()
for k, v in acc["storage"]:
storage[k] = newJString(v.getStr)
result["storage"] = storage
2019-01-11 06:53:18 +00:00
result["storageRoot"] = newJString(acc["storageRoot"].getStr)
result["codeHash"] = newJString(acc["codeHash"].getStr)
2019-01-09 11:40:25 +00:00
proc updateAccount(a, b: JsonNode) =
2019-01-11 06:53:18 +00:00
if b.hasKey("name"):
a["name"] = newJString(b["name"].getStr)
2019-01-09 11:40:25 +00:00
a["balance"] = newJString(b["balance"].getStr)
2019-01-11 06:53:18 +00:00
a["nonce"] = newJString(b["nonce"].getStr)
2019-01-09 11:40:25 +00:00
a["code"] = newJString(b["code"].getStr)
var storage = a["storage"]
for k, v in b["storage"]:
storage[k] = newJString(v.getStr)
2019-01-11 06:53:18 +00:00
a["storageRoot"] = newJString(b["storageRoot"].getStr)
a["codeHash"] = newJString(b["codeHash"].getStr)
2019-01-10 17:44:53 +00:00
2019-01-09 11:40:25 +00:00
proc processPostState(postState: JsonNode): JsonNode =
var accounts = newJObject()
for state in postState:
for address, account in state:
if accounts.hasKey(address):
updateAccount(accounts[address], account)
else:
accounts[address] = copyAccount(account)
result = accounts
2019-01-11 06:53:18 +00:00
proc removePostStateDup(nimbus: JsonNode) =
let postState = nimbus["stateDump"]["after"]
var accounts = newJObject()
for acc in postState:
let address = acc["address"].getStr
if accounts.hasKey(address):
updateAccount(accounts[address], acc)
else:
accounts[address] = copyAccount(acc)
nimbus["stateDump"]["after"] = accounts
2019-01-10 17:44:53 +00:00
proc requestPostState(thisBlock: Block): JsonNode =
2019-01-11 06:53:18 +00:00
let postState = requestPostState(thisBlock.jsonData, postStateTracer, thisBlock)
2019-01-10 17:44:53 +00:00
requestBlockState(postState, thisBlock)
var addresses = @[thisBlock.header.coinbase]
for uncle in thisBlock.body.uncles:
addresses.add uncle.coinbase
requestBlockState(postState, thisBlock, addresses)
processPostState(postState)
proc generatePremixData(nimbus: JsonNode, blockNumber: Uint256, thisBlock: Block, accounts: JsonNode) =
2019-01-09 11:40:25 +00:00
let
receipts = toJson(thisBlock.receipts)
txTraces = nimbus["txTraces"]
for trace in txTraces:
trace["structLogs"].fakeAlloc()
var geth = %{
"blockNumber": %blockNumber.toHex,
"txTraces": thisBlock.traces,
"receipts": receipts,
"block": thisBlock.jsonData,
"accounts": accounts
}
2019-01-10 03:34:56 +00:00
var premixData = %{
2019-01-09 11:40:25 +00:00
"nimbus": nimbus,
"geth": geth
}
2019-01-10 03:34:56 +00:00
var data = "var premixData = " & premixData.pretty & "\n"
2019-01-09 11:40:25 +00:00
writeFile("premixData.js", data)
2019-01-09 12:10:58 +00:00
proc printDebugInstruction(blockNumber: Uint256) =
var text = """
Successfully created debugging environment for block $1.
You can continue to find nimbus EVM bug by viewing premix report page `./index.html`.
After that you can try to debug that single block using `nim c -r debug block$1.json` command.
Happy bug hunting
""" % [$blockNumber]
echo text
2019-01-09 11:40:25 +00:00
proc main() =
2019-01-09 12:10:58 +00:00
if paramCount() == 0:
2019-01-10 08:23:18 +00:00
echo "usage: premix debugxxx.json"
2019-01-09 12:10:58 +00:00
quit(QuitFailure)
try:
let
nimbus = json.parseFile(paramStr(1))
blockNumber = UInt256.fromHex(nimbus["blockNumber"].getStr())
thisBlock = downloader.requestBlock(blockNumber, {DownloadReceipts, DownloadTxTrace})
2019-01-10 17:44:53 +00:00
accounts = requestPostState(thisBlock)
2019-01-09 12:10:58 +00:00
2019-01-11 06:53:18 +00:00
removePostStateDup(nimbus)
2019-01-10 17:44:53 +00:00
generatePremixData(nimbus, blockNumber, thisBlock, accounts)
2019-01-09 12:10:58 +00:00
generatePrestate(nimbus, blockNumber, thisBlock)
printDebugInstruction(blockNumber)
except:
echo getCurrentExceptionMsg()
2019-01-09 11:40:25 +00:00
main()