nimbus-eth1/premix/hunter.nim
jangko baf508f6ae
move stateDB from VMState to chainDB
previously, every time the VMState was created, it will also create
new stateDB, and this action will nullify the advantages of cached accounts.

the new changes will conserve the accounts cache if the executed blocks
are contiguous. if not the stateDB need to be reinited.

this changes also allow rpcCallEvm and rpcEstimateGas executed properly
using current stateDB instead of creating new one each time they are called.
2021-10-28 18:57:08 +07:00

155 lines
4.6 KiB
Nim

import
std/[json, tables, hashes],
eth/common, eth/trie/[trie_defs, db],
stint, stew/byteutils, chronicles,
../nimbus/[tracer, vm_state, utils, vm_types],
../nimbus/db/[db_chain, state_db, accounts_cache],
../nimbus/p2p/executor, premixcore,
"."/configuration, downloader, parser
const
emptyCodeHash = blankStringHash
proc store(memoryDB: TrieDatabaseRef, branch: JsonNode) =
for p in branch:
let rlp = hexToSeqByte(p.getStr)
let hash = keccakHash(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)
accountDB.setCode(address, code)
accountDB.setAccount(address, acc)
result = memoryDB
type
HunterVMState = ref object of BaseVMState
headers: Table[BlockNumber, BlockHeader]
proc hash*(x: Uint256): Hash =
result = hash(x.toByteArrayBE)
proc newHunterVMState(ac: AccountsCache, header: BlockHeader, chainDB: BaseChainDB): HunterVMState =
new result
result.init(ac, header, chainDB)
result.headers = initTable[BlockNumber, BlockHeader]()
method getAncestorHash*(vmState: HunterVMState, blockNumber: BlockNumber): Hash256 {.gcsafe.} =
if blockNumber in vmState.headers:
result = vmState.headers[blockNumber].hash
else:
let data = requestHeader(blockNumber)
let header = parseBlockHeader(data)
result = header.hash
vmState.headers[blockNumber] = header
proc putAncestorsIntoDB(vmState: HunterVMState, db: BaseChainDB) =
for header in vmState.headers.values:
db.addBlockNumberToHashLookup(header)
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)
chainDB.initStateDB(parentBlock.header.stateRoot)
let transaction = memoryDB.beginTransaction()
defer: transaction.dispose()
let
vmState = newHunterVMState(chainDB.stateDB, thisBlock.header, chainDB)
validationResult = vmState.processBlockNotPoA(thisBlock.header, thisBlock.body)
if validationResult != ValidationResult.OK:
transaction.rollback()
putAncestorsIntoDB(vmState, chainDB)
dumpDebuggingMetaData(chainDB, thisBlock.header, thisBlock.body, vmState, false)
result = validationResult
proc main() {.used.} =
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()