poststate processing

This commit is contained in:
andri lim 2019-01-11 13:53:18 +07:00 committed by zah
parent 451eaed620
commit f1bb0d1b31
7 changed files with 138 additions and 81 deletions

View File

@ -39,9 +39,10 @@ task test, "Run tests":
test "all_tests"
# debugging tools don't yet have tests
# but they should be compilable
buildBinary "premix", "premix/"
buildBinary "persist", "premix/"
buildBinary "debug", "premix/"
exec "nim c premix/premix"
exec "nim c premix/persist"
exec "nim c premix/debug"
exec "nim c premix/dumper"
task nimbus, "Build Nimbus":
buildBinary "nimbus", "nimbus/"

View File

@ -2,7 +2,7 @@ import
db/[db_chain, state_db, capturedb], eth_common, utils, json,
constants, vm_state, vm_types, transaction, p2p/executor,
eth_trie/db, nimcrypto, strutils, ranges, ./utils/addresses,
chronicles
chronicles, rpc/hexstrings
proc getParentHeader(self: BaseChainDB, header: BlockHeader): BlockHeader =
self.getBlockHeader(header.parentHash)
@ -46,19 +46,19 @@ proc getRecipient*(tx: Transaction): EthAddress =
proc captureAccount(n: JsonNode, db: AccountStateDB, address: EthAddress, name: string) =
var jaccount = newJObject()
jaccount["name"] = %name
jaccount["address"] = %($address)
jaccount["address"] = %("0x" & $address)
let account = db.getAccount(address)
jaccount["nonce"] = %(account.nonce.toHex)
jaccount["balance"] = %(account.balance.toHex)
jaccount["nonce"] = %(encodeQuantity(account.nonce).toLowerAscii)
jaccount["balance"] = %("0x" & account.balance.toHex)
let code = db.getCode(address)
jaccount["codeHash"] = %(($account.codeHash).toLowerAscii)
jaccount["code"] = %(toHex(code.toOpenArray, true))
jaccount["storageRoot"] = %(($account.storageRoot).toLowerAscii)
jaccount["codeHash"] = %("0x" & ($account.codeHash).toLowerAscii)
jaccount["code"] = %("0x" & toHex(code.toOpenArray, true))
jaccount["storageRoot"] = %("0x" & ($account.storageRoot).toLowerAscii)
var storage = newJObject()
for key, value in db.storage(address):
storage[key.dumpHex] = %(value.dumpHex)
storage["0x" & key.dumpHex] = %("0x" & value.dumpHex)
jaccount["storage"] = storage
n.add jaccount

View File

@ -120,6 +120,7 @@ function opCodeRenderer(txId, nimbus, geth) {
}
function transactionsRenderer(txId, nimbus, geth) {
txId = parseInt(txId);
$('#transactionsTitle').text(`Tx #${(txId+1)}`);
let container = $('#transactionsContainer').empty();
@ -130,13 +131,17 @@ function transactionsRenderer(txId, nimbus, geth) {
premix.renderRow(body, nimbus, geth, x);
}
// TODO: receipt logs
// TODO: render receipt logs
}
txId = parseInt(txId);
let ntx = nimbus.txTraces[txId];
let gtx = geth.txTraces[txId];
if(ntx.returnValue.length == 0) {
ntx.returnValue = "0x";
}
let ncr = $.extend({
gas: ntx.gas,
returnValue: ntx.returnValue
@ -155,36 +160,52 @@ function transactionsRenderer(txId, nimbus, geth) {
}
function headerRenderer(nimbus, geth) {
function emptyAccount() {
return {
address: '',
nonce: '',
balance: '',
codeHash: '',
code: '',
storageRoot: '',
storage: {}
};
}
let container = $('#headerContainer').empty();
let ncs = nimbus.stateDump.after;
let gcs = geth.accounts;
let accounts = [];
for(var n of ncs) {
n.address = '0x' + n.address;
n.balance = '0x' + n.balance;
n.code = '0x' + n.code;
let g = gcs[n.address];
for(var address in ncs) {
let n = ncs[address];
n.address = address;
if(gcs[address]) {
let geth = gcs[address];
geth.address = address;
accounts.push({name: n.name, nimbus: n, geth: geth});
delete gcs[address];
} else {
accounts.push({name: n.name, nimbus: n, geth: emptyAccount()});
}
}
/*"name": "internalTx0",
"address": "0000000000000000000000000000000000000004",
"nonce": "0000000000000000",
"balance": "0",
"codeHash": "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470",
"code": "",
"storageRoot": "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"storage": {}*/
/*"0xf927a40c8b7f6e07c5af7fa2155b4864a4112b13": {
"balance": "0x607c9cea65ef7e19dd8",
"nonce": "0000000000000000",
"code": "0x",
"storage": {}*/
for(var address in gcs) {
let geth = gcs[address];
geth.address = address;
accounts.push({name: "unknown", nimbus: emptyAccount(), geth: geth});
}
for(var acc of accounts) {
$(`<h4>Account Name: ${acc.name}</h4>`).appendTo(container);
let body = premix.newTable(container);
const fields = ['address', 'nonce', 'balance', 'codeHash', 'code', 'storageRoot'];
for(var x of fields) {
premix.renderRow(body, acc.nimbus, acc.geth, x);
}
$('<hr class="uk-divider-icon">').appendTo(container);
}
}
function generateNavigation(txs, nimbus, geth) {
@ -201,8 +222,8 @@ function generateNavigation(txs, nimbus, geth) {
let list = $('<ul class="uk-nav uk-dropdown-nav"/>').appendTo(div);
for(var i in txs) {
let id = i.toString();
$(`<li class="uk-active"><a rel="${id}" href="#">TX #${id}</a></li>`).appendTo(list);
let id = parseInt(i) + 1;
$(`<li class="uk-active"><a rel="${i}" href="#">TX #${id}</a></li>`).appendTo(list);
}
list.find('li a').click(function(ev) {

View File

@ -61,16 +61,16 @@ proc requestReceipts(n: JsonNode): seq[Receipt] =
result.add parseReceipt(rec)
proc requestTxTraces(n: JsonNode): JsonNode =
result = newJArray()
let txs = n["transactions"]
if txs.len > 0:
result = newJArray()
for tx in txs:
let txHash = tx["hash"]
let txTrace = request("debug_traceTransaction", %[txHash])
if txTrace.kind == JNull:
error "requested trace not available", txHash=txHash
raise newException(ValueError, "Error when retrieving transaction trace")
result.add txTrace
if txs.len == 0: return
for tx in txs:
let txHash = tx["hash"]
let txTrace = request("debug_traceTransaction", %[txHash])
if txTrace.kind == JNull:
error "requested trace not available", txHash=txHash
raise newException(ValueError, "Error when retrieving transaction trace")
result.add txTrace
proc requestBlock*(blockNumber: BlockNumber, flags: set[DownloadFlags] = {}): Block =
var header = request("eth_getBlockByNumber", %[%blockNumber.prefixHex, %true])

View File

@ -1,13 +1,11 @@
const postStateTracer* = """{
postState: null,
postState: {},
// lookupAccount injects the specified account into the postState object.
lookupAccount: function(addr, db){
var acc = toHex(addr);
if (this.postState[acc] === undefined) {
this.postState[acc] = {
balance: '0x' + db.getBalance(addr).toString(16),
nonce: db.getNonce(addr),
code: toHex(db.getCode(addr)),
storage: {}
};
@ -19,9 +17,9 @@ const postStateTracer* = """{
lookupStorage: function(addr, key, db){
var acc = toHex(addr);
var idx = toHex(key);
this.lookupAccount(addr, db);
if (this.postState[acc].storage[idx] === undefined) {
this.postState[acc].storage[idx] = "";//toHex(db.getState(addr, key));
this.postState[acc].storage[idx] = "";
}
},

View File

@ -6,7 +6,7 @@ import
import
../nimbus/[transaction, rpc/hexstrings]
func hexToInt(s: string, T: typedesc[SomeInteger]): T =
func hexToInt*(s: string, T: typedesc[SomeInteger]): T =
var i = 0
if s[i] == '0' and (s[i+1] in {'x', 'X'}): inc(i, 2)
if s.len - i > sizeof(T) * 2:

View File

@ -29,20 +29,53 @@ proc requestTrace(txHash, tracer: JsonNode): JsonNode =
raise newException(ValueError, "Error when retrieving transaction postState")
result = txTrace
proc requestPostState(n: JsonNode, jsTracer: string): JsonNode =
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 # "0x"
let code = request("eth_getCode", %[tx["to"], number])
result = code.getStr.len > 2 # "0x"
proc requestPostState(n: JsonNode, jsTracer: string, thisBlock: Block): JsonNode =
let txs = n["transactions"]
result = newJArray()
if txs.len == 0: return
let tracer = jsonTracer(jsTracer)
for tx in txs:
if tx["to"].kind != JNull:
result.add newJObject()
continue
let
txHash = tx["hash"]
txTrace = requestTrace(txHash, tracer)
result.add txTrace
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)
proc padding(x: string): JsonNode =
let val = x.substr(2)
@ -59,45 +92,37 @@ proc requestBlockState(postState: JsonNode, thisBlock: Block) =
storage.add %k
let trace = request("eth_getProof", %[%address, storage, number])
account["codeHash"] = trace["codeHash"]
account["storageHash"] = trace["storageHash"]
account["storageRoot"] = trace["storageHash"]
account["nonce"] = trace["nonce"]
account["balance"] = trace["balance"]
for x in trace["storageProof"]:
account["storage"][x["key"].getStr] = padding(x["value"].getStr())
proc copyAccount(acc: JsonNode): JsonNode =
result = newJObject()
if acc.hasKey("name"):
result["name"] = newJString(acc["name"].getStr)
result["balance"] = newJString(acc["balance"].getStr)
result["nonce"] = newJString(toHex(acc["nonce"].getInt))
result["nonce"] = newJString(acc["nonce"].getStr)
result["code"] = newJString(acc["code"].getStr)
var storage = newJObject()
for k, v in acc["storage"]:
storage[k] = newJString(v.getStr)
result["storage"] = storage
result["storageRoot"] = newJString(acc["storageRoot"].getStr)
result["codeHash"] = newJString(acc["codeHash"].getStr)
proc updateAccount(a, b: JsonNode) =
if b.hasKey("name"):
a["name"] = newJString(b["name"].getStr)
a["balance"] = newJString(b["balance"].getStr)
a["nonce"] = newJString(toHex(b["nonce"].getInt))
a["nonce"] = newJString(b["nonce"].getStr)
a["code"] = newJString(b["code"].getStr)
var storage = a["storage"]
for k, v in b["storage"]:
storage[k] = newJString(v.getStr)
proc requestBlockState(postState: JsonNode, thisBlock: Block, addresses: seq[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"],
"storageHash": trace["storageHash"],
"balance": trace["balance"],
"nonce": trace["nonce"],
"code": newJString("0x"),
"storage": newJObject()
}
txTrace[address] = account
postState.add txTrace
a["storageRoot"] = newJString(b["storageRoot"].getStr)
a["codeHash"] = newJString(b["codeHash"].getStr)
proc processPostState(postState: JsonNode): JsonNode =
var accounts = newJObject()
@ -111,8 +136,19 @@ proc processPostState(postState: JsonNode): JsonNode =
result = accounts
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
proc requestPostState(thisBlock: Block): JsonNode =
let postState = requestPostState(thisBlock.jsonData, postStateTracer)
let postState = requestPostState(thisBlock.jsonData, postStateTracer, thisBlock)
requestBlockState(postState, thisBlock)
var addresses = @[thisBlock.header.coinbase]
@ -170,6 +206,7 @@ proc main() =
thisBlock = downloader.requestBlock(blockNumber, {DownloadReceipts, DownloadTxTrace})
accounts = requestPostState(thisBlock)
removePostStateDup(nimbus)
generatePremixData(nimbus, blockNumber, thisBlock, accounts)
generatePrestate(nimbus, blockNumber, thisBlock)
printDebugInstruction(blockNumber)