diff --git a/premix/hunter.nim b/premix/hunter.nim new file mode 100644 index 000000000..1fbbc03bd --- /dev/null +++ b/premix/hunter.nim @@ -0,0 +1,117 @@ +import + json, downloader, stint, strutils, byteutils, parser, nimcrypto, + chronicles, ../nimbus/tracer, eth_trie/[defs, db], ../nimbus/vm_state, + ../nimbus/db/[db_chain, state_db], ../nimbus/p2p/executor, premixcore, + eth_common, configuration + +const + emptyCodeHash = blankStringHash + emptyStorageHash = emptyRlpHash + +proc store(memoryDB: TrieDatabaseRef, branch: JsonNode) = + for p in branch: + let rlp = hexToSeqByte(p.getStr) + let hash = keccak256.digest(rlp) + memoryDB.put(hash.data, rlp) + +proc parseAddress(address: string): EthAddress = + hexToByteArray(address, result) + +proc parseU256(val: string): Uint256 = + UInt256.fromHex(val) + +proc prepareBlockEnv(parent: BlockHeader, thisBlock: Block): TrieDatabaseRef = + var + accounts = requestPostState(thisBlock) + memoryDB = newMemoryDB() + accountDB = newAccountStateDB(memoryDB, parent.stateRoot, false) + parentNumber = %(parent.blockNumber.prefixHex) + + for address, account in accounts: + updateAccount(address, account, parent.blockNumber) + let + accountProof = account["accountProof"] + storageProof = account["storageProof"] + address = parseAddress(address) + acc = parseAccount(account) + + memoryDB.store(accountProof) + accountDB.setAccount(address, acc) + + for storage in storageProof: + let + key = parseU256(storage["key"].getStr) + val = parseU256(storage["value"].getStr) + proof = storage["proof"] + memoryDB.store(proof) + accountDB.setStorage(address, key, val) + + if acc.codeHash != emptyCodeHash: + let codeStr = request("eth_getCode", %[%address.prefixHex, parentNumber]) + let code = hexToSeqByte(codeStr.getStr).toRange + accountDB.setCode(address, code) + result = memoryDB + +proc huntProblematicBlock(blockNumber: Uint256): ValidationResult = + let + # prepare needed state from previous block + parentNumber = blockNumber - 1 + thisBlock = requestBlock(blockNumber) + parentBlock = requestBlock(parentNumber) + memoryDB = prepareBlockEnv(parentBlock.header, thisBlock) + + # try to execute current block + chainDB = newBaseChainDB(memoryDB, false) + + chainDB.setHead(parentBlock.header, true) + + let + vmState = newBaseVMState(parentBlock.header, chainDB) + validationResult = processBlock(chainDB, parentBlock.header, thisBlock.header, thisBlock.body, vmState) + + if validationResult != ValidationResult.OK: + dumpDebuggingMetaData(chainDB, thisBlock.header, thisBlock.body, vmState.receipts, false) + + result = validationResult + +proc main() = + let conf = getConfiguration() + + if conf.head == 0.u256: + echo "please specify the starting block with `--head:blockNumber`" + quit(QuitFailure) + + if conf.maxBlocks == 0: + echo "please specify the number of problematic blocks you want to hunt with `--maxBlocks:number`" + quit(QuitFailure) + + var + problematicBlocks = newSeq[Uint256]() + blockNumber = conf.head + + while true: + echo blockNumber + if huntProblematicBlock(blockNumber) != ValidationResult.OK: + echo "shot down problematic block: ", blockNumber + problematicBlocks.add blockNumber + blockNumber = blockNumber + 1 + if problematicBlocks.len >= conf.maxBlocks: + echo "Problematic blocks: ", problematicBlocks + break + +when isMainModule: + var message: string + + ## Processing command line arguments + if processArguments(message) != Success: + echo message + quit(QuitFailure) + else: + if len(message) > 0: + echo message + quit(QuitSuccess) + + try: + main() + except: + echo getCurrentExceptionMsg() diff --git a/premix/parser.nim b/premix/parser.nim index f28654720..60b074e49 100644 --- a/premix/parser.nim +++ b/premix/parser.nim @@ -127,3 +127,9 @@ proc parseReceipt*(n: JsonNode): Receipt = proc headerHash*(n: JsonNode): Hash256 = n.fromJson "hash", result + +proc parseAccount*(n: JsonNode): Account = + n.fromJson "nonce", result.nonce + n.fromJson "balance", result.balance + n.fromJson "storageRoot", result.storageRoot + n.fromJson "codeHash", result.codeHash diff --git a/premix/premix.nim b/premix/premix.nim index b0dd081fd..9a0d51547 100644 --- a/premix/premix.nim +++ b/premix/premix.nim @@ -4,17 +4,6 @@ import js_tracer, eth_common, byteutils, parser, nimcrypto, premixcore -proc requestPostState(thisBlock: Block): JsonNode = - let blockNumber = thisBlock.header.blockNumber - var premix = initPremix() - - premix.requestPostState(thisBlock.jsonData, blockNumber) - premix.requestAccount(blockNumber, thisBlock.header.coinbase) - for uncle in thisBlock.body.uncles: - premix.requestAccount(blockNumber, uncle.coinbase) - - removePostStateDup(premix.accounts) - proc generateGethData(thisBlock: Block, blockNumber: Uint256, accounts: JsonNode): JsonNode = let receipts = toJson(thisBlock.receipts) @@ -46,7 +35,7 @@ proc main() = echo "usage: premix debugxxx.json" quit(QuitFailure) - block: + try: let nimbus = json.parseFile(paramStr(1)) blockNumber = UInt256.fromHex(nimbus["blockNumber"].getStr()) @@ -66,7 +55,7 @@ proc main() = generatePrestate(nimbus, geth, blockNumber, parentBlock.header, thisBlock.header, thisBlock.body) printDebugInstruction(blockNumber) - #except: - #echo getCurrentExceptionMsg() + except: + echo getCurrentExceptionMsg() main() diff --git a/premix/premixcore.nim b/premix/premixcore.nim index 1d4ab4279..acb3fce07 100644 --- a/premix/premixcore.nim +++ b/premix/premixcore.nim @@ -65,15 +65,6 @@ proc generatePremixData*(nimbus, geth: JsonNode) = var data = "var premixData = " & premixData.pretty & "\n" writeFile("premixData.js", data) -type - Premix* = object - accounts*: JsonNode - proofs*: JsonNode - -proc initPremix*(): Premix = - result.accounts = newJArray() - result.proofs = newJArray() - proc hasInternalTx(tx: Transaction, blockNumber: Uint256): bool = let number = %(blockNumber.prefixHex) @@ -95,7 +86,7 @@ proc requestInternalTx(txHash, tracer: JsonNode): JsonNode = raise newException(ValueError, "Error when retrieving transaction postState") result = txTrace -proc requestAccount*(premix: var Premix, blockNumber: Uint256, address: EthAddress) = +proc requestAccount*(premix: JsonNode, blockNumber: Uint256, address: EthAddress) = let number = %(blockNumber.prefixHex) address = address.prefixHex @@ -108,17 +99,18 @@ proc requestAccount*(premix: var Premix, blockNumber: Uint256, address: EthAddre "balance": proof["balance"], "nonce": proof["nonce"], "code": newJString("0x"), - "storage": newJObject() + "storage": newJObject(), + "accountProof": proof["accountProof"], + "storageProof": proof["storageProof"] } - premix.accounts.add account - premix.proofs.add proof + premix.add account proc padding(x: string): JsonNode = let val = x.substr(2) let pad = repeat('0', 64 - val.len) result = newJString("0x" & pad & val) -proc updateAccount(address: string, account: JsonNode, blockNumber: Uint256): JsonNode = +proc updateAccount*(address: string, account: JsonNode, blockNumber: Uint256) = let number = %(blockNumber.prefixHex) var storage = newJArray() @@ -131,12 +123,13 @@ proc updateAccount(address: string, account: JsonNode, blockNumber: Uint256): Js account["storageRoot"] = proof["storageHash"] account["nonce"] = proof["nonce"] account["balance"] = proof["balance"] + account["accountProof"]= proof["accountProof"] + account["storageProof"]= proof["storageProof"] for x in proof["storageProof"]: x["value"] = padding(x["value"].getStr()) account["storage"][x["key"].getStr] = x["value"] - proof -proc requestPostState*(premix: var Premix, n: JsonNode, blockNumber: Uint256) = +proc requestPostState*(premix, n: JsonNode, blockNumber: Uint256) = let txs = n["transactions"] if txs.len == 0: return @@ -146,8 +139,19 @@ proc requestPostState*(premix: var Premix, n: JsonNode, blockNumber: Uint256) = if hasInternalTx(tx, blockNumber): let txTrace = requestInternalTx(t["hash"], tracer) for address, account in txTrace: - premix.proofs.add updateAccount(address, account, blockNumber) - premix.accounts.add account + updateAccount(address, account, blockNumber) + premix.add account else: premix.requestAccount(blockNumber, tx.getRecipient) premix.requestAccount(blockNumber, tx.getSender) + +proc requestPostState*(thisBlock: Block): JsonNode = + let blockNumber = thisBlock.header.blockNumber + var premix = newJArray() + + premix.requestPostState(thisBlock.jsonData, blockNumber) + premix.requestAccount(blockNumber, thisBlock.header.coinbase) + for uncle in thisBlock.body.uncles: + premix.requestAccount(blockNumber, uncle.coinbase) + + removePostStateDup(premix)