add hunter tool
This commit is contained in:
parent
c1881fb061
commit
6ebe8ef2d4
|
@ -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()
|
|
@ -127,3 +127,9 @@ proc parseReceipt*(n: JsonNode): Receipt =
|
||||||
|
|
||||||
proc headerHash*(n: JsonNode): Hash256 =
|
proc headerHash*(n: JsonNode): Hash256 =
|
||||||
n.fromJson "hash", result
|
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
|
||||||
|
|
|
@ -4,17 +4,6 @@ import
|
||||||
js_tracer, eth_common, byteutils, parser,
|
js_tracer, eth_common, byteutils, parser,
|
||||||
nimcrypto, premixcore
|
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 =
|
proc generateGethData(thisBlock: Block, blockNumber: Uint256, accounts: JsonNode): JsonNode =
|
||||||
let
|
let
|
||||||
receipts = toJson(thisBlock.receipts)
|
receipts = toJson(thisBlock.receipts)
|
||||||
|
@ -46,7 +35,7 @@ proc main() =
|
||||||
echo "usage: premix debugxxx.json"
|
echo "usage: premix debugxxx.json"
|
||||||
quit(QuitFailure)
|
quit(QuitFailure)
|
||||||
|
|
||||||
block:
|
try:
|
||||||
let
|
let
|
||||||
nimbus = json.parseFile(paramStr(1))
|
nimbus = json.parseFile(paramStr(1))
|
||||||
blockNumber = UInt256.fromHex(nimbus["blockNumber"].getStr())
|
blockNumber = UInt256.fromHex(nimbus["blockNumber"].getStr())
|
||||||
|
@ -66,7 +55,7 @@ proc main() =
|
||||||
generatePrestate(nimbus, geth, blockNumber, parentBlock.header, thisBlock.header, thisBlock.body)
|
generatePrestate(nimbus, geth, blockNumber, parentBlock.header, thisBlock.header, thisBlock.body)
|
||||||
|
|
||||||
printDebugInstruction(blockNumber)
|
printDebugInstruction(blockNumber)
|
||||||
#except:
|
except:
|
||||||
#echo getCurrentExceptionMsg()
|
echo getCurrentExceptionMsg()
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|
|
@ -65,15 +65,6 @@ proc generatePremixData*(nimbus, geth: JsonNode) =
|
||||||
var data = "var premixData = " & premixData.pretty & "\n"
|
var data = "var premixData = " & premixData.pretty & "\n"
|
||||||
writeFile("premixData.js", data)
|
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 =
|
proc hasInternalTx(tx: Transaction, blockNumber: Uint256): bool =
|
||||||
let
|
let
|
||||||
number = %(blockNumber.prefixHex)
|
number = %(blockNumber.prefixHex)
|
||||||
|
@ -95,7 +86,7 @@ proc requestInternalTx(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 requestAccount*(premix: var Premix, blockNumber: Uint256, address: EthAddress) =
|
proc requestAccount*(premix: JsonNode, blockNumber: Uint256, address: EthAddress) =
|
||||||
let
|
let
|
||||||
number = %(blockNumber.prefixHex)
|
number = %(blockNumber.prefixHex)
|
||||||
address = address.prefixHex
|
address = address.prefixHex
|
||||||
|
@ -108,17 +99,18 @@ proc requestAccount*(premix: var Premix, blockNumber: Uint256, address: EthAddre
|
||||||
"balance": proof["balance"],
|
"balance": proof["balance"],
|
||||||
"nonce": proof["nonce"],
|
"nonce": proof["nonce"],
|
||||||
"code": newJString("0x"),
|
"code": newJString("0x"),
|
||||||
"storage": newJObject()
|
"storage": newJObject(),
|
||||||
|
"accountProof": proof["accountProof"],
|
||||||
|
"storageProof": proof["storageProof"]
|
||||||
}
|
}
|
||||||
premix.accounts.add account
|
premix.add account
|
||||||
premix.proofs.add proof
|
|
||||||
|
|
||||||
proc padding(x: string): JsonNode =
|
proc padding(x: string): JsonNode =
|
||||||
let val = x.substr(2)
|
let val = x.substr(2)
|
||||||
let pad = repeat('0', 64 - val.len)
|
let pad = repeat('0', 64 - val.len)
|
||||||
result = newJString("0x" & pad & val)
|
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)
|
let number = %(blockNumber.prefixHex)
|
||||||
|
|
||||||
var storage = newJArray()
|
var storage = newJArray()
|
||||||
|
@ -131,12 +123,13 @@ proc updateAccount(address: string, account: JsonNode, blockNumber: Uint256): Js
|
||||||
account["storageRoot"] = proof["storageHash"]
|
account["storageRoot"] = proof["storageHash"]
|
||||||
account["nonce"] = proof["nonce"]
|
account["nonce"] = proof["nonce"]
|
||||||
account["balance"] = proof["balance"]
|
account["balance"] = proof["balance"]
|
||||||
|
account["accountProof"]= proof["accountProof"]
|
||||||
|
account["storageProof"]= proof["storageProof"]
|
||||||
for x in proof["storageProof"]:
|
for x in proof["storageProof"]:
|
||||||
x["value"] = padding(x["value"].getStr())
|
x["value"] = padding(x["value"].getStr())
|
||||||
account["storage"][x["key"].getStr] = x["value"]
|
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"]
|
let txs = n["transactions"]
|
||||||
if txs.len == 0: return
|
if txs.len == 0: return
|
||||||
|
|
||||||
|
@ -146,8 +139,19 @@ proc requestPostState*(premix: var Premix, n: JsonNode, blockNumber: Uint256) =
|
||||||
if hasInternalTx(tx, blockNumber):
|
if hasInternalTx(tx, blockNumber):
|
||||||
let txTrace = requestInternalTx(t["hash"], tracer)
|
let txTrace = requestInternalTx(t["hash"], tracer)
|
||||||
for address, account in txTrace:
|
for address, account in txTrace:
|
||||||
premix.proofs.add updateAccount(address, account, blockNumber)
|
updateAccount(address, account, blockNumber)
|
||||||
premix.accounts.add account
|
premix.add account
|
||||||
else:
|
else:
|
||||||
premix.requestAccount(blockNumber, tx.getRecipient)
|
premix.requestAccount(blockNumber, tx.getRecipient)
|
||||||
premix.requestAccount(blockNumber, tx.getSender)
|
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)
|
||||||
|
|
Loading…
Reference in New Issue