poststate processing
This commit is contained in:
parent
451eaed620
commit
f1bb0d1b31
|
@ -39,9 +39,10 @@ task test, "Run tests":
|
||||||
test "all_tests"
|
test "all_tests"
|
||||||
# debugging tools don't yet have tests
|
# debugging tools don't yet have tests
|
||||||
# but they should be compilable
|
# but they should be compilable
|
||||||
buildBinary "premix", "premix/"
|
exec "nim c premix/premix"
|
||||||
buildBinary "persist", "premix/"
|
exec "nim c premix/persist"
|
||||||
buildBinary "debug", "premix/"
|
exec "nim c premix/debug"
|
||||||
|
exec "nim c premix/dumper"
|
||||||
|
|
||||||
task nimbus, "Build Nimbus":
|
task nimbus, "Build Nimbus":
|
||||||
buildBinary "nimbus", "nimbus/"
|
buildBinary "nimbus", "nimbus/"
|
||||||
|
|
|
@ -2,7 +2,7 @@ import
|
||||||
db/[db_chain, state_db, capturedb], eth_common, utils, json,
|
db/[db_chain, state_db, capturedb], eth_common, utils, json,
|
||||||
constants, vm_state, vm_types, transaction, p2p/executor,
|
constants, vm_state, vm_types, transaction, p2p/executor,
|
||||||
eth_trie/db, nimcrypto, strutils, ranges, ./utils/addresses,
|
eth_trie/db, nimcrypto, strutils, ranges, ./utils/addresses,
|
||||||
chronicles
|
chronicles, rpc/hexstrings
|
||||||
|
|
||||||
proc getParentHeader(self: BaseChainDB, header: BlockHeader): BlockHeader =
|
proc getParentHeader(self: BaseChainDB, header: BlockHeader): BlockHeader =
|
||||||
self.getBlockHeader(header.parentHash)
|
self.getBlockHeader(header.parentHash)
|
||||||
|
@ -46,19 +46,19 @@ proc getRecipient*(tx: Transaction): EthAddress =
|
||||||
proc captureAccount(n: JsonNode, db: AccountStateDB, address: EthAddress, name: string) =
|
proc captureAccount(n: JsonNode, db: AccountStateDB, address: EthAddress, name: string) =
|
||||||
var jaccount = newJObject()
|
var jaccount = newJObject()
|
||||||
jaccount["name"] = %name
|
jaccount["name"] = %name
|
||||||
jaccount["address"] = %($address)
|
jaccount["address"] = %("0x" & $address)
|
||||||
let account = db.getAccount(address)
|
let account = db.getAccount(address)
|
||||||
jaccount["nonce"] = %(account.nonce.toHex)
|
jaccount["nonce"] = %(encodeQuantity(account.nonce).toLowerAscii)
|
||||||
jaccount["balance"] = %(account.balance.toHex)
|
jaccount["balance"] = %("0x" & account.balance.toHex)
|
||||||
|
|
||||||
let code = db.getCode(address)
|
let code = db.getCode(address)
|
||||||
jaccount["codeHash"] = %(($account.codeHash).toLowerAscii)
|
jaccount["codeHash"] = %("0x" & ($account.codeHash).toLowerAscii)
|
||||||
jaccount["code"] = %(toHex(code.toOpenArray, true))
|
jaccount["code"] = %("0x" & toHex(code.toOpenArray, true))
|
||||||
jaccount["storageRoot"] = %(($account.storageRoot).toLowerAscii)
|
jaccount["storageRoot"] = %("0x" & ($account.storageRoot).toLowerAscii)
|
||||||
|
|
||||||
var storage = newJObject()
|
var storage = newJObject()
|
||||||
for key, value in db.storage(address):
|
for key, value in db.storage(address):
|
||||||
storage[key.dumpHex] = %(value.dumpHex)
|
storage["0x" & key.dumpHex] = %("0x" & value.dumpHex)
|
||||||
jaccount["storage"] = storage
|
jaccount["storage"] = storage
|
||||||
|
|
||||||
n.add jaccount
|
n.add jaccount
|
||||||
|
|
|
@ -120,6 +120,7 @@ function opCodeRenderer(txId, nimbus, geth) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function transactionsRenderer(txId, nimbus, geth) {
|
function transactionsRenderer(txId, nimbus, geth) {
|
||||||
|
txId = parseInt(txId);
|
||||||
$('#transactionsTitle').text(`Tx #${(txId+1)}`);
|
$('#transactionsTitle').text(`Tx #${(txId+1)}`);
|
||||||
let container = $('#transactionsContainer').empty();
|
let container = $('#transactionsContainer').empty();
|
||||||
|
|
||||||
|
@ -130,13 +131,17 @@ function transactionsRenderer(txId, nimbus, geth) {
|
||||||
premix.renderRow(body, nimbus, geth, x);
|
premix.renderRow(body, nimbus, geth, x);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: receipt logs
|
// TODO: render receipt logs
|
||||||
}
|
}
|
||||||
|
|
||||||
txId = parseInt(txId);
|
txId = parseInt(txId);
|
||||||
let ntx = nimbus.txTraces[txId];
|
let ntx = nimbus.txTraces[txId];
|
||||||
let gtx = geth.txTraces[txId];
|
let gtx = geth.txTraces[txId];
|
||||||
|
|
||||||
|
if(ntx.returnValue.length == 0) {
|
||||||
|
ntx.returnValue = "0x";
|
||||||
|
}
|
||||||
|
|
||||||
let ncr = $.extend({
|
let ncr = $.extend({
|
||||||
gas: ntx.gas,
|
gas: ntx.gas,
|
||||||
returnValue: ntx.returnValue
|
returnValue: ntx.returnValue
|
||||||
|
@ -155,36 +160,52 @@ function transactionsRenderer(txId, nimbus, geth) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function headerRenderer(nimbus, geth) {
|
function headerRenderer(nimbus, geth) {
|
||||||
|
function emptyAccount() {
|
||||||
|
return {
|
||||||
|
address: '',
|
||||||
|
nonce: '',
|
||||||
|
balance: '',
|
||||||
|
codeHash: '',
|
||||||
|
code: '',
|
||||||
|
storageRoot: '',
|
||||||
|
storage: {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
let container = $('#headerContainer').empty();
|
let container = $('#headerContainer').empty();
|
||||||
|
|
||||||
let ncs = nimbus.stateDump.after;
|
let ncs = nimbus.stateDump.after;
|
||||||
let gcs = geth.accounts;
|
let gcs = geth.accounts;
|
||||||
|
let accounts = [];
|
||||||
|
|
||||||
|
for(var address in ncs) {
|
||||||
for(var n of ncs) {
|
let n = ncs[address];
|
||||||
n.address = '0x' + n.address;
|
n.address = address;
|
||||||
n.balance = '0x' + n.balance;
|
if(gcs[address]) {
|
||||||
n.code = '0x' + n.code;
|
let geth = gcs[address];
|
||||||
let g = gcs[n.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",
|
for(var address in gcs) {
|
||||||
"address": "0000000000000000000000000000000000000004",
|
let geth = gcs[address];
|
||||||
"nonce": "0000000000000000",
|
geth.address = address;
|
||||||
"balance": "0",
|
accounts.push({name: "unknown", nimbus: emptyAccount(), geth: geth});
|
||||||
"codeHash": "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470",
|
}
|
||||||
"code": "",
|
|
||||||
"storageRoot": "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
|
||||||
"storage": {}*/
|
|
||||||
|
|
||||||
/*"0xf927a40c8b7f6e07c5af7fa2155b4864a4112b13": {
|
|
||||||
"balance": "0x607c9cea65ef7e19dd8",
|
|
||||||
"nonce": "0000000000000000",
|
|
||||||
"code": "0x",
|
|
||||||
"storage": {}*/
|
|
||||||
|
|
||||||
|
|
||||||
|
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) {
|
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);
|
let list = $('<ul class="uk-nav uk-dropdown-nav"/>').appendTo(div);
|
||||||
|
|
||||||
for(var i in txs) {
|
for(var i in txs) {
|
||||||
let id = i.toString();
|
let id = parseInt(i) + 1;
|
||||||
$(`<li class="uk-active"><a rel="${id}" href="#">TX #${id}</a></li>`).appendTo(list);
|
$(`<li class="uk-active"><a rel="${i}" href="#">TX #${id}</a></li>`).appendTo(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
list.find('li a').click(function(ev) {
|
list.find('li a').click(function(ev) {
|
||||||
|
|
|
@ -61,9 +61,9 @@ proc requestReceipts(n: JsonNode): seq[Receipt] =
|
||||||
result.add parseReceipt(rec)
|
result.add parseReceipt(rec)
|
||||||
|
|
||||||
proc requestTxTraces(n: JsonNode): JsonNode =
|
proc requestTxTraces(n: JsonNode): JsonNode =
|
||||||
let txs = n["transactions"]
|
|
||||||
if txs.len > 0:
|
|
||||||
result = newJArray()
|
result = newJArray()
|
||||||
|
let txs = n["transactions"]
|
||||||
|
if txs.len == 0: return
|
||||||
for tx in txs:
|
for tx in txs:
|
||||||
let txHash = tx["hash"]
|
let txHash = tx["hash"]
|
||||||
let txTrace = request("debug_traceTransaction", %[txHash])
|
let txTrace = request("debug_traceTransaction", %[txHash])
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
const postStateTracer* = """{
|
const postStateTracer* = """{
|
||||||
postState: null,
|
postState: {},
|
||||||
|
|
||||||
// lookupAccount injects the specified account into the postState object.
|
// lookupAccount injects the specified account into the postState object.
|
||||||
lookupAccount: function(addr, db){
|
lookupAccount: function(addr, db){
|
||||||
var acc = toHex(addr);
|
var acc = toHex(addr);
|
||||||
if (this.postState[acc] === undefined) {
|
if (this.postState[acc] === undefined) {
|
||||||
this.postState[acc] = {
|
this.postState[acc] = {
|
||||||
balance: '0x' + db.getBalance(addr).toString(16),
|
|
||||||
nonce: db.getNonce(addr),
|
|
||||||
code: toHex(db.getCode(addr)),
|
code: toHex(db.getCode(addr)),
|
||||||
storage: {}
|
storage: {}
|
||||||
};
|
};
|
||||||
|
@ -19,9 +17,9 @@ const postStateTracer* = """{
|
||||||
lookupStorage: function(addr, key, db){
|
lookupStorage: function(addr, key, db){
|
||||||
var acc = toHex(addr);
|
var acc = toHex(addr);
|
||||||
var idx = toHex(key);
|
var idx = toHex(key);
|
||||||
|
this.lookupAccount(addr, db);
|
||||||
if (this.postState[acc].storage[idx] === undefined) {
|
if (this.postState[acc].storage[idx] === undefined) {
|
||||||
this.postState[acc].storage[idx] = "";//toHex(db.getState(addr, key));
|
this.postState[acc].storage[idx] = "";
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import
|
||||||
import
|
import
|
||||||
../nimbus/[transaction, rpc/hexstrings]
|
../nimbus/[transaction, rpc/hexstrings]
|
||||||
|
|
||||||
func hexToInt(s: string, T: typedesc[SomeInteger]): T =
|
func hexToInt*(s: string, T: typedesc[SomeInteger]): T =
|
||||||
var i = 0
|
var i = 0
|
||||||
if s[i] == '0' and (s[i+1] in {'x', 'X'}): inc(i, 2)
|
if s[i] == '0' and (s[i+1] in {'x', 'X'}): inc(i, 2)
|
||||||
if s.len - i > sizeof(T) * 2:
|
if s.len - i > sizeof(T) * 2:
|
||||||
|
|
|
@ -29,20 +29,53 @@ proc requestTrace(txHash, tracer: JsonNode): JsonNode =
|
||||||
raise newException(ValueError, "Error when retrieving transaction postState")
|
raise newException(ValueError, "Error when retrieving transaction postState")
|
||||||
result = txTrace
|
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"]
|
let txs = n["transactions"]
|
||||||
result = newJArray()
|
result = newJArray()
|
||||||
if txs.len == 0: return
|
if txs.len == 0: return
|
||||||
|
|
||||||
let tracer = jsonTracer(jsTracer)
|
let tracer = jsonTracer(jsTracer)
|
||||||
for tx in txs:
|
for tx in txs:
|
||||||
if tx["to"].kind != JNull:
|
if hasTracerData(tx, thisBlock.header.blockNumber):
|
||||||
result.add newJObject()
|
|
||||||
continue
|
|
||||||
let
|
let
|
||||||
txHash = tx["hash"]
|
txHash = tx["hash"]
|
||||||
txTrace = requestTrace(txHash, tracer)
|
txTrace = requestTrace(txHash, tracer)
|
||||||
result.add txTrace
|
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 =
|
proc padding(x: string): JsonNode =
|
||||||
let val = x.substr(2)
|
let val = x.substr(2)
|
||||||
|
@ -59,45 +92,37 @@ proc requestBlockState(postState: JsonNode, thisBlock: Block) =
|
||||||
storage.add %k
|
storage.add %k
|
||||||
let trace = request("eth_getProof", %[%address, storage, number])
|
let trace = request("eth_getProof", %[%address, storage, number])
|
||||||
account["codeHash"] = trace["codeHash"]
|
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"]:
|
for x in trace["storageProof"]:
|
||||||
account["storage"][x["key"].getStr] = padding(x["value"].getStr())
|
account["storage"][x["key"].getStr] = padding(x["value"].getStr())
|
||||||
|
|
||||||
proc copyAccount(acc: JsonNode): JsonNode =
|
proc copyAccount(acc: JsonNode): JsonNode =
|
||||||
result = newJObject()
|
result = newJObject()
|
||||||
|
if acc.hasKey("name"):
|
||||||
|
result["name"] = newJString(acc["name"].getStr)
|
||||||
result["balance"] = newJString(acc["balance"].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)
|
result["code"] = newJString(acc["code"].getStr)
|
||||||
var storage = newJObject()
|
var storage = newJObject()
|
||||||
for k, v in acc["storage"]:
|
for k, v in acc["storage"]:
|
||||||
storage[k] = newJString(v.getStr)
|
storage[k] = newJString(v.getStr)
|
||||||
result["storage"] = storage
|
result["storage"] = storage
|
||||||
|
result["storageRoot"] = newJString(acc["storageRoot"].getStr)
|
||||||
|
result["codeHash"] = newJString(acc["codeHash"].getStr)
|
||||||
|
|
||||||
proc updateAccount(a, b: JsonNode) =
|
proc updateAccount(a, b: JsonNode) =
|
||||||
|
if b.hasKey("name"):
|
||||||
|
a["name"] = newJString(b["name"].getStr)
|
||||||
a["balance"] = newJString(b["balance"].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)
|
a["code"] = newJString(b["code"].getStr)
|
||||||
var storage = a["storage"]
|
var storage = a["storage"]
|
||||||
for k, v in b["storage"]:
|
for k, v in b["storage"]:
|
||||||
storage[k] = newJString(v.getStr)
|
storage[k] = newJString(v.getStr)
|
||||||
|
a["storageRoot"] = newJString(b["storageRoot"].getStr)
|
||||||
proc requestBlockState(postState: JsonNode, thisBlock: Block, addresses: seq[EthAddress]) =
|
a["codeHash"] = newJString(b["codeHash"].getStr)
|
||||||
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
|
|
||||||
|
|
||||||
proc processPostState(postState: JsonNode): JsonNode =
|
proc processPostState(postState: JsonNode): JsonNode =
|
||||||
var accounts = newJObject()
|
var accounts = newJObject()
|
||||||
|
@ -111,8 +136,19 @@ proc processPostState(postState: JsonNode): JsonNode =
|
||||||
|
|
||||||
result = accounts
|
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 =
|
proc requestPostState(thisBlock: Block): JsonNode =
|
||||||
let postState = requestPostState(thisBlock.jsonData, postStateTracer)
|
let postState = requestPostState(thisBlock.jsonData, postStateTracer, thisBlock)
|
||||||
requestBlockState(postState, thisBlock)
|
requestBlockState(postState, thisBlock)
|
||||||
|
|
||||||
var addresses = @[thisBlock.header.coinbase]
|
var addresses = @[thisBlock.header.coinbase]
|
||||||
|
@ -170,6 +206,7 @@ proc main() =
|
||||||
thisBlock = downloader.requestBlock(blockNumber, {DownloadReceipts, DownloadTxTrace})
|
thisBlock = downloader.requestBlock(blockNumber, {DownloadReceipts, DownloadTxTrace})
|
||||||
accounts = requestPostState(thisBlock)
|
accounts = requestPostState(thisBlock)
|
||||||
|
|
||||||
|
removePostStateDup(nimbus)
|
||||||
generatePremixData(nimbus, blockNumber, thisBlock, accounts)
|
generatePremixData(nimbus, blockNumber, thisBlock, accounts)
|
||||||
generatePrestate(nimbus, blockNumber, thisBlock)
|
generatePrestate(nimbus, blockNumber, thisBlock)
|
||||||
printDebugInstruction(blockNumber)
|
printDebugInstruction(blockNumber)
|
||||||
|
|
Loading…
Reference in New Issue