nimbus-eth1/nimbus/core/chain/persist_blocks.nim
Jordan Hrycaj 221e6c9e2f
Unified database frontend integration (#1670)
* Nimbus folder environment update

details:
* Integrated `CoreDbRef` for the sources in the `nimbus` sub-folder.
* The `nimbus` program does not compile yet as it needs the updates
  in the parallel `stateless` sub-folder.

* Stateless environment update

details:
* Integrated `CoreDbRef` for the sources in the `stateless` sub-folder.
* The `nimbus` program compiles now.

* Premix environment update

details:
* Integrated `CoreDbRef` for the sources in the `premix` sub-folder.

* Fluffy environment update

details:
* Integrated `CoreDbRef` for the sources in the `fluffy` sub-folder.

* Tools environment update

details:
* Integrated `CoreDbRef` for the sources in the `tools` sub-folder.

* Nodocker environment update

details:
* Integrated `CoreDbRef` for the sources in the
  `hive_integration/nodocker` sub-folder.

* Tests environment update

details:
* Integrated `CoreDbRef` for the sources in the `tests` sub-folder.
* The unit tests compile and run cleanly now.

* Generalise `CoreDbRef` to any `select_backend` supported database

why:
  Generalisation was just missed due to overcoming some compiler oddity
  which was tied to rocksdb for testing.

* Suppress compiler warning for `newChainDB()`

why:
  Warning was added to this function which must be wrapped so that
  any `CatchableError` is re-raised as `Defect`.

* Split off persistent `CoreDbRef` constructor into separate file

why:
  This allows to compile a memory only database version without linking
  the backend library.

* Use memory `CoreDbRef` database by default

detail:
 Persistent DB constructor needs to import `db/core_db/persistent

why:
 Most tests use memory DB anyway. This avoids linking `-lrocksdb` or
 any other backend by default.

* fix `toLegacyBackend()` availability check

why:
  got garbled after memory/persistent split.

* Clarify raw access to MPT for snap sync handler

why:
  Logically, `kvt` is not the raw access for the hexary trie (although
  this holds for the legacy database)
2023-08-04 12:10:09 +01:00

194 lines
6.3 KiB
Nim

# Nimbus
# Copyright (c) 2018 Status Research & Development GmbH
# 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.
{.push raises: [].}
import
../../vm_state,
../../vm_types,
../clique/clique_verify,
../clique,
../executor,
../validate,
./chain_desc,
chronicles,
stint
when not defined(release):
import
../../tracer,
../../utils/utils
type
PersistBlockFlag = enum
NoPersistHeader
NoSaveTxs
NoSaveReceipts
NoSaveWithdrawals
PersistBlockFlags = set[PersistBlockFlag]
# ------------------------------------------------------------------------------
# Private
# ------------------------------------------------------------------------------
proc persistBlocksImpl(c: ChainRef; headers: openArray[BlockHeader];
bodies: openArray[BlockBody],
flags: PersistBlockFlags = {}): ValidationResult
# wildcard exception, wrapped below in public section
{.inline, raises: [CatchableError].} =
let transaction = c.db.beginTransaction()
defer: transaction.dispose()
var cliqueState = c.clique.cliqueSave
defer: c.clique.cliqueRestore(cliqueState)
c.com.hardForkTransition(headers[0])
# Note that `0 < headers.len`, assured when called from `persistBlocks()`
let vmState = BaseVMState()
if not vmState.init(headers[0], c.com):
debug "Cannot initialise VmState",
fromBlock = headers[0].blockNumber,
toBlock = headers[^1].blockNumber
return ValidationResult.Error
trace "Persisting blocks",
fromBlock = headers[0].blockNumber,
toBlock = headers[^1].blockNumber
for i in 0 ..< headers.len:
let
(header, body) = (headers[i], bodies[i])
c.com.hardForkTransition(header)
if not vmState.reinit(header):
debug "Cannot update VmState",
blockNumber = header.blockNumber,
item = i
return ValidationResult.Error
let
validationResult = if c.validateBlock:
vmState.processBlock(c.clique, header, body)
else:
ValidationResult.OK
when not defined(release):
if validationResult == ValidationResult.Error and
body.transactions.calcTxRoot == header.txRoot:
dumpDebuggingMetaData(c.com, header, body, vmState)
warn "Validation error. Debugging metadata dumped."
if validationResult != ValidationResult.OK:
return validationResult
if c.validateBlock and c.extraValidation and
c.verifyFrom <= header.blockNumber:
if c.com.consensus == ConsensusType.POA:
var parent = if 0 < i: @[headers[i-1]] else: @[]
let rc = c.clique.cliqueVerify(c.com, header,parent)
if rc.isOk:
# mark it off so it would not auto-restore previous state
c.clique.cliqueDispose(cliqueState)
else:
debug "PoA header verification failed",
blockNumber = header.blockNumber,
msg = $rc.error
return ValidationResult.Error
else:
let res = c.com.validateHeaderAndKinship(
header,
body,
checkSealOK = false) # TODO: how to checkseal from here
if res.isErr:
debug "block validation error",
msg = res.error
return ValidationResult.Error
if NoPersistHeader notin flags:
discard c.db.persistHeaderToDb(
header, c.com.consensus == ConsensusType.POS, c.com.startOfHistory)
if NoSaveTxs notin flags:
discard c.db.persistTransactions(header.blockNumber, body.transactions)
if NoSaveReceipts notin flags:
discard c.db.persistReceipts(vmState.receipts)
if NoSaveWithdrawals notin flags and body.withdrawals.isSome:
discard c.db.persistWithdrawals(body.withdrawals.get)
# update currentBlock *after* we persist it
# so the rpc return consistent result
# between eth_blockNumber and eth_syncing
c.com.syncCurrent = header.blockNumber
transaction.commit()
# ------------------------------------------------------------------------------
# Public `ChainDB` methods
# ------------------------------------------------------------------------------
proc insertBlockWithoutSetHead*(c: ChainRef, header: BlockHeader,
body: BlockBody): ValidationResult
{.gcsafe, raises: [CatchableError].} =
result = c.persistBlocksImpl(
[header], [body], {NoPersistHeader, NoSaveReceipts})
if result == ValidationResult.OK:
c.db.persistHeaderToDbWithoutSetHead(header, c.com.startOfHistory)
proc setCanonical*(c: ChainRef, header: BlockHeader): ValidationResult
{.gcsafe, raises: [CatchableError].} =
if header.parentHash == Hash256():
discard c.db.setHead(header.blockHash)
return ValidationResult.OK
var body: BlockBody
if not c.db.getBlockBody(header, body):
debug "Failed to get BlockBody",
hash = header.blockHash
return ValidationResult.Error
result = c.persistBlocksImpl([header], [body], {NoPersistHeader, NoSaveTxs})
if result == ValidationResult.OK:
discard c.db.setHead(header.blockHash)
proc setCanonical*(c: ChainRef, blockHash: Hash256): ValidationResult
{.gcsafe, raises: [CatchableError].} =
var header: BlockHeader
if not c.db.getBlockHeader(blockHash, header):
debug "Failed to get BlockHeader",
hash = blockHash
return ValidationResult.Error
setCanonical(c, header)
proc persistBlocks*(c: ChainRef; headers: openArray[BlockHeader];
bodies: openArray[BlockBody]): ValidationResult
{.gcsafe, raises: [CatchableError].} =
# Run the VM here
if headers.len != bodies.len:
debug "Number of headers not matching number of bodies"
return ValidationResult.Error
if headers.len == 0:
debug "Nothing to do"
return ValidationResult.OK
c.persistBlocksImpl(headers,bodies)
# ------------------------------------------------------------------------------
# End
# ------------------------------------------------------------------------------