2023-11-01 03:32:09 +00:00
|
|
|
# Nimbus
|
2024-03-21 10:45:57 +00:00
|
|
|
# Copyright (c) 2020-2024 Status Research & Development GmbH
|
2023-11-01 03:32:09 +00:00
|
|
|
# Licensed under either of
|
|
|
|
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
|
|
|
# http://www.apache.org/licenses/LICENSE-2.0)
|
|
|
|
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or
|
|
|
|
# http://opensource.org/licenses/MIT)
|
|
|
|
# at your option. This file may not be copied, modified, or distributed except
|
|
|
|
# according to those terms.
|
|
|
|
|
2019-01-15 08:55:39 +00:00
|
|
|
import
|
2021-10-28 09:42:39 +00:00
|
|
|
std/[json, tables, hashes],
|
2022-12-02 04:39:12 +00:00
|
|
|
eth/trie/trie_defs,
|
2021-10-28 09:42:39 +00:00
|
|
|
stint, stew/byteutils, chronicles,
|
2022-12-02 04:39:12 +00:00
|
|
|
../nimbus/[vm_state, vm_types],
|
|
|
|
../nimbus/utils/utils,
|
|
|
|
../nimbus/tracer,
|
Optional accounts cache module for creating genesis (#1897)
* Split off `ReadOnlyStateDB` from `AccountStateDB` from `state_db.nim`
why:
Apart from testing, applications use `ReadOnlyStateDB` as an easy
way to access the accounts ledger. This is well supported by the
`Aristo` db, but writable mode is only parially supported.
The writable AccountStateDB` object for modifying accounts is not
used by production code.
So, for lecgacy and testing apps, the full support of the previous
`AccountStateDB` is now enabled by `import db/state_db/read_write`
and the `import db/state_db` provides read-only mode.
* Encapsulate `AccountStateDB` as `GenesisLedgerRef` or genesis creation
why:
`AccountStateDB` has poor support for `Aristo` and is not widely used
in favour of `AccountsLedger` (which will be abstracted as `ledger`.)
Currently, using other than the `AccountStateDB` ledgers within the
`GenesisLedgerRef` wrapper is experimental and test only. Eventually,
the wrapper should disappear so that the `Ledger` object (which
encapsulates `AccountsCache` and `AccountsLedger`) will prevail.
* For the `Ledger`, provide access to raw accounts `MPT`
why:
This gives to the `CoreDbMptRef` descriptor from the `CoreDb` (which is
the legacy version of CoreDxMptRef`.) For the new `ledger` API, the
accounts are based on the `CoreDxMAccRef` descriptor which uses a
particular sub-system for accounts while legacy applications use the
`CoreDbPhkRef` equivalent of the `SecureHexaryTrie`.
The only place where this feature will currently be used is the
`genesis.nim` source file.
* Fix `Aristo` bugs, missing boundary checks, typos, etc.
* Verify root vertex in `MPT` and account constructors
why:
Was missing so far, in particular the accounts constructor must
verify `VertexID(1)
* Fix include file
2023-11-20 11:51:43 +00:00
|
|
|
../nimbus/db/[core_db, state_db/read_write],
|
2022-12-02 04:39:12 +00:00
|
|
|
../nimbus/core/executor,
|
|
|
|
../nimbus/common/common,
|
|
|
|
"."/[configuration, downloader, parser, premixcore]
|
2019-07-07 10:12:01 +00:00
|
|
|
|
2019-01-15 08:55:39 +00:00
|
|
|
const
|
|
|
|
emptyCodeHash = blankStringHash
|
|
|
|
|
2023-08-04 11:10:09 +00:00
|
|
|
proc store(memoryDB: CoreDbRef, branch: JsonNode) =
|
2019-01-15 08:55:39 +00:00
|
|
|
for p in branch:
|
|
|
|
let rlp = hexToSeqByte(p.getStr)
|
2019-03-07 15:53:09 +00:00
|
|
|
let hash = keccakHash(rlp)
|
2023-08-04 11:10:09 +00:00
|
|
|
memoryDB.kvt.put(hash.data, rlp)
|
2019-01-15 08:55:39 +00:00
|
|
|
|
|
|
|
proc parseAddress(address: string): EthAddress =
|
|
|
|
hexToByteArray(address, result)
|
|
|
|
|
2022-04-08 04:54:11 +00:00
|
|
|
proc parseU256(val: string): UInt256 =
|
2019-01-15 08:55:39 +00:00
|
|
|
UInt256.fromHex(val)
|
|
|
|
|
2023-08-04 11:10:09 +00:00
|
|
|
proc prepareBlockEnv(parent: BlockHeader, thisBlock: Block): CoreDbRef =
|
2019-01-15 08:55:39 +00:00
|
|
|
var
|
|
|
|
accounts = requestPostState(thisBlock)
|
2023-08-04 11:10:09 +00:00
|
|
|
memoryDB = newCoreDbRef LegacyDbMemory
|
2019-01-15 08:55:39 +00:00
|
|
|
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])
|
2020-04-20 18:12:44 +00:00
|
|
|
let code = hexToSeqByte(codeStr.getStr)
|
2019-01-15 08:55:39 +00:00
|
|
|
accountDB.setCode(address, code)
|
2019-01-21 13:44:42 +00:00
|
|
|
|
|
|
|
accountDB.setAccount(address, acc)
|
|
|
|
|
2019-01-15 08:55:39 +00:00
|
|
|
result = memoryDB
|
|
|
|
|
2019-02-22 08:52:53 +00:00
|
|
|
type
|
|
|
|
HunterVMState = ref object of BaseVMState
|
|
|
|
headers: Table[BlockNumber, BlockHeader]
|
|
|
|
|
2022-04-08 04:54:11 +00:00
|
|
|
proc hash*(x: UInt256): Hash =
|
2023-09-13 02:32:38 +00:00
|
|
|
result = hash(x.toBytesBE)
|
2019-02-22 08:52:53 +00:00
|
|
|
|
2022-12-02 04:39:12 +00:00
|
|
|
proc new(T: type HunterVMState; parent, header: BlockHeader, com: CommonRef): T =
|
2019-02-22 08:52:53 +00:00
|
|
|
new result
|
2022-12-02 04:39:12 +00:00
|
|
|
result.init(parent, header, com)
|
2019-02-22 08:52:53 +00:00
|
|
|
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
|
|
|
|
|
2023-08-04 11:10:09 +00:00
|
|
|
proc putAncestorsIntoDB(vmState: HunterVMState, db: CoreDbRef) =
|
2019-02-22 08:52:53 +00:00
|
|
|
for header in vmState.headers.values:
|
|
|
|
db.addBlockNumberToHashLookup(header)
|
|
|
|
|
2022-04-08 04:54:11 +00:00
|
|
|
proc huntProblematicBlock(blockNumber: UInt256): ValidationResult =
|
2019-01-15 08:55:39 +00:00
|
|
|
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
|
2022-12-02 04:39:12 +00:00
|
|
|
com = CommonRef.new(memoryDB, false)
|
2019-01-15 08:55:39 +00:00
|
|
|
|
2022-12-02 04:39:12 +00:00
|
|
|
discard com.db.setHead(parentBlock.header, true)
|
2019-01-15 08:55:39 +00:00
|
|
|
|
2019-02-07 10:10:04 +00:00
|
|
|
let transaction = memoryDB.beginTransaction()
|
|
|
|
defer: transaction.dispose()
|
2019-01-15 08:55:39 +00:00
|
|
|
let
|
2022-12-02 04:39:12 +00:00
|
|
|
vmState = HunterVMState.new(parentBlock.header, thisBlock.header, com)
|
2023-10-05 03:04:12 +00:00
|
|
|
validationResult = vmState.processBlock(thisBlock.header, thisBlock.body)
|
2019-01-15 08:55:39 +00:00
|
|
|
|
|
|
|
if validationResult != ValidationResult.OK:
|
2019-02-22 08:22:20 +00:00
|
|
|
transaction.rollback()
|
2022-12-02 04:39:12 +00:00
|
|
|
putAncestorsIntoDB(vmState, com.db)
|
2024-03-21 10:45:57 +00:00
|
|
|
vmState.dumpDebuggingMetaData(thisBlock.header, thisBlock.body, false)
|
2019-01-15 08:55:39 +00:00
|
|
|
|
|
|
|
result = validationResult
|
|
|
|
|
2020-07-21 06:15:06 +00:00
|
|
|
proc main() {.used.} =
|
2019-01-15 08:55:39 +00:00
|
|
|
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
|
2022-04-08 04:54:11 +00:00
|
|
|
problematicBlocks = newSeq[UInt256]()
|
2019-01-15 08:55:39 +00:00
|
|
|
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()
|