2021-07-14 15:13:27 +00:00
|
|
|
# Nimbus
|
2024-02-13 09:49:41 +00:00
|
|
|
# Copyright (c) 2018-2024 Status Research & Development GmbH
|
2021-07-14 15:13:27 +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.
|
|
|
|
|
2023-02-14 20:27:17 +00:00
|
|
|
{.push raises: [].}
|
|
|
|
|
2021-07-14 15:13:27 +00:00
|
|
|
import
|
2024-05-31 07:13:56 +00:00
|
|
|
results,
|
2023-12-12 19:12:56 +00:00
|
|
|
../../db/ledger,
|
2021-07-14 15:13:27 +00:00
|
|
|
../../vm_state,
|
2022-01-18 16:19:32 +00:00
|
|
|
../../vm_types,
|
2021-07-14 15:13:27 +00:00
|
|
|
../executor,
|
|
|
|
../validate,
|
|
|
|
./chain_desc,
|
|
|
|
chronicles,
|
|
|
|
stint
|
|
|
|
|
|
|
|
when not defined(release):
|
2021-07-30 14:06:51 +00:00
|
|
|
import
|
|
|
|
../../tracer,
|
2022-12-06 17:35:56 +00:00
|
|
|
../../utils/utils
|
2021-07-14 15:13:27 +00:00
|
|
|
|
2024-05-31 07:13:56 +00:00
|
|
|
export results
|
|
|
|
|
2022-07-04 12:31:41 +00:00
|
|
|
type
|
|
|
|
PersistBlockFlag = enum
|
|
|
|
NoPersistHeader
|
|
|
|
NoSaveTxs
|
|
|
|
NoSaveReceipts
|
2023-05-23 05:25:42 +00:00
|
|
|
NoSaveWithdrawals
|
2022-07-04 12:31:41 +00:00
|
|
|
|
|
|
|
PersistBlockFlags = set[PersistBlockFlag]
|
|
|
|
|
2024-05-31 07:13:56 +00:00
|
|
|
PersistStats = tuple
|
|
|
|
blocks: int
|
|
|
|
txs: int
|
|
|
|
gas: GasInt
|
|
|
|
|
2024-04-26 13:43:52 +00:00
|
|
|
const
|
|
|
|
CleanUpEpoch = 30_000.u256
|
2024-05-22 21:01:19 +00:00
|
|
|
## Regular checks for history clean up (applies to single state DB). This
|
|
|
|
## is mainly a debugging/testing feature so that the database can be held
|
|
|
|
## a bit smaller. It is not applicable to a full node.
|
2024-04-26 13:43:52 +00:00
|
|
|
|
2021-07-14 15:13:27 +00:00
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# Private
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
|
2023-10-05 03:04:12 +00:00
|
|
|
proc getVmState(c: ChainRef, header: BlockHeader):
|
2024-05-31 07:13:56 +00:00
|
|
|
Result[BaseVMState, string] =
|
2023-10-05 03:04:12 +00:00
|
|
|
let vmState = BaseVMState()
|
2024-05-31 07:13:56 +00:00
|
|
|
try:
|
|
|
|
# TODO clean up exception handling
|
|
|
|
if not vmState.init(header, c.com):
|
|
|
|
return err("Could not initialise VMState")
|
|
|
|
except CatchableError as exc:
|
|
|
|
return err("Error while initializing VMState: " & exc.msg)
|
2023-10-05 03:04:12 +00:00
|
|
|
|
2024-05-31 07:13:56 +00:00
|
|
|
ok(vmState)
|
2024-05-22 21:01:19 +00:00
|
|
|
|
|
|
|
proc purgeOutOfJournalBlocks(db: CoreDbRef) {.inline, raises: [RlpError].} =
|
2024-04-26 13:43:52 +00:00
|
|
|
## Remove non-reachable blocks from KVT database
|
|
|
|
var blkNum = db.getOldestJournalBlockNumber()
|
|
|
|
if 0 < blkNum:
|
|
|
|
blkNum = blkNum - 1
|
|
|
|
while 0 < blkNum:
|
|
|
|
if not db.forgetHistory blkNum:
|
|
|
|
break
|
|
|
|
blkNum = blkNum - 1
|
|
|
|
|
2022-12-02 04:35:41 +00:00
|
|
|
proc persistBlocksImpl(c: ChainRef; headers: openArray[BlockHeader];
|
2022-07-04 12:31:41 +00:00
|
|
|
bodies: openArray[BlockBody],
|
2024-05-31 07:13:56 +00:00
|
|
|
flags: PersistBlockFlags = {}): Result[PersistStats, string]
|
|
|
|
{.raises: [CatchableError] .} =
|
2023-10-05 03:04:12 +00:00
|
|
|
let dbTx = c.db.beginTransaction()
|
|
|
|
defer: dbTx.dispose()
|
2021-07-14 15:13:27 +00:00
|
|
|
|
2022-12-05 08:46:37 +00:00
|
|
|
c.com.hardForkTransition(headers[0])
|
2022-12-02 04:35:41 +00:00
|
|
|
|
2022-01-18 16:19:32 +00:00
|
|
|
# Note that `0 < headers.len`, assured when called from `persistBlocks()`
|
2024-05-31 07:13:56 +00:00
|
|
|
let vmState = ?c.getVmState(headers[0])
|
2022-07-21 12:14:41 +00:00
|
|
|
|
2024-04-26 13:43:52 +00:00
|
|
|
let (fromBlock, toBlock) = (headers[0].blockNumber, headers[^1].blockNumber)
|
|
|
|
trace "Persisting blocks", fromBlock, toBlock
|
2024-05-22 21:01:19 +00:00
|
|
|
|
2024-05-31 07:13:56 +00:00
|
|
|
var txs = 0
|
2021-07-14 15:13:27 +00:00
|
|
|
for i in 0 ..< headers.len:
|
2024-03-21 10:45:57 +00:00
|
|
|
let (header, body) = (headers[i], bodies[i])
|
|
|
|
|
|
|
|
# This transaction keeps the current state open for inspection
|
|
|
|
# if an error occurs (as needed for `Aristo`.).
|
|
|
|
let lapTx = c.db.beginTransaction()
|
|
|
|
defer: lapTx.dispose()
|
2022-01-18 16:19:32 +00:00
|
|
|
|
2022-12-05 08:46:37 +00:00
|
|
|
c.com.hardForkTransition(header)
|
2022-12-02 04:35:41 +00:00
|
|
|
|
2022-01-18 16:19:32 +00:00
|
|
|
if not vmState.reinit(header):
|
|
|
|
debug "Cannot update VmState",
|
|
|
|
blockNumber = header.blockNumber,
|
|
|
|
item = i
|
2024-05-31 07:13:56 +00:00
|
|
|
return err("Cannot update VmState to block " & $header.blockNumber)
|
2022-01-18 16:19:32 +00:00
|
|
|
|
2023-10-05 03:04:12 +00:00
|
|
|
if c.validateBlock and c.extraValidation and
|
|
|
|
c.verifyFrom <= header.blockNumber:
|
|
|
|
|
2024-05-31 07:13:56 +00:00
|
|
|
? c.com.validateHeaderAndKinship(
|
2024-05-25 14:12:14 +00:00
|
|
|
header,
|
|
|
|
body,
|
|
|
|
checkSealOK = false) # TODO: how to checkseal from here
|
2023-10-05 03:04:12 +00:00
|
|
|
|
2024-02-13 09:49:41 +00:00
|
|
|
if c.generateWitness:
|
|
|
|
vmState.generateWitness = true
|
|
|
|
|
2022-01-18 16:19:32 +00:00
|
|
|
let
|
2024-02-13 09:49:41 +00:00
|
|
|
validationResult = if c.validateBlock or c.generateWitness:
|
2023-10-05 03:04:12 +00:00
|
|
|
vmState.processBlock(header, body)
|
2022-09-16 04:23:25 +00:00
|
|
|
else:
|
|
|
|
ValidationResult.OK
|
2021-07-14 15:13:27 +00:00
|
|
|
when not defined(release):
|
|
|
|
if validationResult == ValidationResult.Error and
|
|
|
|
body.transactions.calcTxRoot == header.txRoot:
|
2024-03-21 10:45:57 +00:00
|
|
|
vmState.dumpDebuggingMetaData(header, body)
|
|
|
|
warn "Validation error. Debugging metadata dumped."
|
2021-07-14 15:13:27 +00:00
|
|
|
|
|
|
|
if validationResult != ValidationResult.OK:
|
2024-05-31 07:13:56 +00:00
|
|
|
return err("Failed to validate block")
|
2022-03-11 08:13:59 +00:00
|
|
|
|
2024-02-13 09:49:41 +00:00
|
|
|
if c.generateWitness:
|
|
|
|
let dbTx = c.db.beginTransaction()
|
|
|
|
defer: dbTx.dispose()
|
|
|
|
|
|
|
|
let
|
|
|
|
mkeys = vmState.stateDB.makeMultiKeys()
|
|
|
|
# Reset state to what it was before executing the block of transactions
|
|
|
|
initialState = BaseVMState.new(header, c.com)
|
|
|
|
witness = initialState.buildWitness(mkeys)
|
|
|
|
|
|
|
|
dbTx.rollback()
|
|
|
|
|
|
|
|
c.db.setBlockWitness(header.blockHash(), witness)
|
|
|
|
|
2022-07-04 12:31:41 +00:00
|
|
|
if NoPersistHeader notin flags:
|
2023-04-14 22:28:57 +00:00
|
|
|
discard c.db.persistHeaderToDb(
|
|
|
|
header, c.com.consensus == ConsensusType.POS, c.com.startOfHistory)
|
2022-03-11 08:13:59 +00:00
|
|
|
|
2022-07-04 12:31:41 +00:00
|
|
|
if NoSaveTxs notin flags:
|
|
|
|
discard c.db.persistTransactions(header.blockNumber, body.transactions)
|
|
|
|
|
|
|
|
if NoSaveReceipts notin flags:
|
|
|
|
discard c.db.persistReceipts(vmState.receipts)
|
2021-07-14 15:13:27 +00:00
|
|
|
|
2023-05-23 05:25:42 +00:00
|
|
|
if NoSaveWithdrawals notin flags and body.withdrawals.isSome:
|
|
|
|
discard c.db.persistWithdrawals(body.withdrawals.get)
|
|
|
|
|
2021-07-14 15:13:27 +00:00
|
|
|
# update currentBlock *after* we persist it
|
|
|
|
# so the rpc return consistent result
|
|
|
|
# between eth_blockNumber and eth_syncing
|
2022-12-02 04:35:41 +00:00
|
|
|
c.com.syncCurrent = header.blockNumber
|
2022-03-11 08:13:59 +00:00
|
|
|
|
2024-03-22 17:31:56 +00:00
|
|
|
# Done with this block
|
2024-03-21 10:45:57 +00:00
|
|
|
lapTx.commit()
|
|
|
|
|
2024-05-31 07:13:56 +00:00
|
|
|
txs += body.transactions.len
|
|
|
|
|
2023-10-05 03:04:12 +00:00
|
|
|
dbTx.commit()
|
2021-07-14 15:13:27 +00:00
|
|
|
|
2024-05-31 09:43:31 +00:00
|
|
|
# Save and record the block number before the last saved block state.
|
|
|
|
c.db.persistent(headers[^1].blockNumber - 1)
|
2024-04-26 13:43:52 +00:00
|
|
|
|
2024-05-22 21:01:19 +00:00
|
|
|
if c.com.pruneHistory:
|
|
|
|
# There is a feature for test systems to regularly clean up older blocks
|
|
|
|
# from the database, not appicable to a full node set up.
|
|
|
|
if(fromBlock mod CleanUpEpoch) <= (toBlock - fromBlock):
|
|
|
|
c.db.purgeOutOfJournalBlocks()
|
2024-04-26 13:43:52 +00:00
|
|
|
|
2024-05-31 07:13:56 +00:00
|
|
|
ok((headers.len, txs, vmState.cumulativeGasUsed))
|
2024-04-19 18:37:27 +00:00
|
|
|
|
2022-03-11 08:13:59 +00:00
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# Public `ChainDB` methods
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
|
2022-12-02 04:35:41 +00:00
|
|
|
proc insertBlockWithoutSetHead*(c: ChainRef, header: BlockHeader,
|
2024-05-31 07:13:56 +00:00
|
|
|
body: BlockBody): Result[void, string] =
|
|
|
|
try:
|
|
|
|
discard ? c.persistBlocksImpl(
|
|
|
|
[header], [body], {NoPersistHeader, NoSaveReceipts})
|
|
|
|
|
2023-04-14 22:28:57 +00:00
|
|
|
c.db.persistHeaderToDbWithoutSetHead(header, c.com.startOfHistory)
|
2024-05-31 07:13:56 +00:00
|
|
|
ok()
|
|
|
|
except CatchableError as exc:
|
|
|
|
err(exc.msg)
|
2022-07-04 12:31:41 +00:00
|
|
|
|
2024-05-31 07:13:56 +00:00
|
|
|
proc setCanonical*(c: ChainRef, header: BlockHeader): Result[void, string] =
|
|
|
|
try:
|
|
|
|
if header.parentHash == Hash256():
|
|
|
|
discard c.db.setHead(header.blockHash)
|
|
|
|
return ok()
|
2022-07-04 12:31:41 +00:00
|
|
|
|
2024-05-31 07:13:56 +00:00
|
|
|
var body: BlockBody
|
|
|
|
if not c.db.getBlockBody(header, body):
|
|
|
|
debug "Failed to get BlockBody",
|
|
|
|
hash = header.blockHash
|
|
|
|
return err("Could not get block body")
|
2022-07-04 12:31:41 +00:00
|
|
|
|
2024-05-31 07:13:56 +00:00
|
|
|
discard ? c.persistBlocksImpl([header], [body], {NoPersistHeader, NoSaveTxs})
|
2022-07-04 12:31:41 +00:00
|
|
|
|
2023-02-14 20:27:17 +00:00
|
|
|
discard c.db.setHead(header.blockHash)
|
2024-05-31 07:13:56 +00:00
|
|
|
ok()
|
|
|
|
except CatchableError as exc:
|
|
|
|
err(exc.msg)
|
2022-07-04 12:31:41 +00:00
|
|
|
|
2024-05-31 07:13:56 +00:00
|
|
|
proc setCanonical*(c: ChainRef, blockHash: Hash256): Result[void, string] =
|
2022-07-04 12:31:41 +00:00
|
|
|
var header: BlockHeader
|
|
|
|
if not c.db.getBlockHeader(blockHash, header):
|
|
|
|
debug "Failed to get BlockHeader",
|
|
|
|
hash = blockHash
|
2024-05-31 07:13:56 +00:00
|
|
|
return err("Could not get block header")
|
2022-07-04 12:31:41 +00:00
|
|
|
|
|
|
|
setCanonical(c, header)
|
2022-03-11 08:13:59 +00:00
|
|
|
|
2022-12-02 04:35:41 +00:00
|
|
|
proc persistBlocks*(c: ChainRef; headers: openArray[BlockHeader];
|
2024-05-31 07:13:56 +00:00
|
|
|
bodies: openArray[BlockBody]): Result[PersistStats, string] =
|
2021-07-14 15:13:27 +00:00
|
|
|
# Run the VM here
|
|
|
|
if headers.len != bodies.len:
|
|
|
|
debug "Number of headers not matching number of bodies"
|
2024-05-31 07:13:56 +00:00
|
|
|
return err("Mismatching headers and bodies")
|
2021-07-14 15:13:27 +00:00
|
|
|
|
|
|
|
if headers.len == 0:
|
|
|
|
debug "Nothing to do"
|
2024-05-31 07:13:56 +00:00
|
|
|
return ok(default(PersistStats)) # TODO not nice to return nil
|
2021-07-14 15:13:27 +00:00
|
|
|
|
2024-05-31 07:13:56 +00:00
|
|
|
try:
|
|
|
|
c.persistBlocksImpl(headers,bodies)
|
|
|
|
except CatchableError as exc:
|
|
|
|
err(exc.msg)
|
2021-07-14 15:13:27 +00:00
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# End
|
|
|
|
# ------------------------------------------------------------------------------
|