nimbus-eth1/nimbus/db/aristo/aristo_tx/tx_stow.nim

152 lines
4.7 KiB
Nim

# nimbus-eth1
# Copyright (c) 2023-2024 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.
## Aristo DB -- Transaction stow/save helper
## =========================================
##
{.push raises: [].}
import
std/tables,
results,
../aristo_delta/delta_merge,
".."/[aristo_desc, aristo_get, aristo_delta, aristo_layers, aristo_hashify]
# ------------------------------------------------------------------------------
# Private functions
# ------------------------------------------------------------------------------
proc getBeStateRoot(
db: AristoDbRef;
chunkedMpt: bool;
): Result[HashKey,AristoError] =
## Get the Merkle hash key for the current backend state root and check
## validity of top layer.
let srcRoot = block:
let rc = db.getKeyBE VertexID(1)
if rc.isOk:
rc.value
elif rc.error == GetKeyNotFound:
VOID_HASH_KEY
else:
return err(rc.error)
if db.top.delta.kMap.getOrVoid(VertexID 1).isValid:
return ok(srcRoot)
elif not db.top.delta.kMap.hasKey(VertexID 1) and
not db.top.delta.sTab.hasKey(VertexID 1):
# This layer is unusable, need both: vertex and key
return err(TxPrettyPointlessLayer)
elif not db.top.delta.sTab.getOrVoid(VertexID 1).isValid:
# Root key and vertex have been deleted
return ok(srcRoot)
elif chunkedMpt and srcRoot == db.top.delta.kMap.getOrVoid VertexID(1):
# FIXME: this one needs to be double checked with `snap` sunc preload
return ok(srcRoot)
err(TxStateRootMismatch)
proc topMerge(db: AristoDbRef; src: HashKey): Result[void,AristoError] =
## Merge the `top` layer into the read-only balacer layer.
let ubeRoot = block:
let rc = db.getKeyUbe VertexID(1)
if rc.isOk:
rc.value
elif rc.error == GetKeyNotFound:
VOID_HASH_KEY
else:
return err(rc.error)
# Update layer for merge call
db.top.delta.src = src
# This one will return the `db.top.delta` if `db.balancer.isNil`
db.balancer = db.deltaMerge(db.top.delta, db.balancer, ubeRoot).valueOr:
return err(error[1])
ok()
# ------------------------------------------------------------------------------
# Public functions
# ------------------------------------------------------------------------------
proc txStow*(
db: AristoDbRef; # Database
nxtSid: uint64; # Next state ID (aka block number)
persistent: bool; # Stage only unless `true`
chunkedMpt: bool; # Partial data (e.g. from `snap`)
): Result[void,AristoError] =
## Worker for `stow()` and `persist()` variants.
##
if not db.txRef.isNil:
return err(TxPendingTx)
if 0 < db.stack.len:
return err(TxStackGarbled)
if persistent and not db.deltaPersistentOk():
return err(TxBackendNotWritable)
# Update Merkle hashes (unless disabled)
db.hashify().isOkOr:
return err(error[1])
# Verify database consistency and get `src` field for update
let rc = db.getBeStateRoot chunkedMpt
if rc.isErr and rc.error != TxPrettyPointlessLayer:
return err(rc.error)
# Special treatment for `snap` proofs (aka `chunkedMpt`)
let final =
if chunkedMpt: LayerFinalRef(fRpp: db.top.final.fRpp)
else: LayerFinalRef()
# Move/merge/install `top` layer onto `balancer`
if rc.isOk:
db.topMerge(rc.value).isOkOr:
return err(error)
# New empty top layer (probably with `snap` proofs and `vTop` carry over)
db.top = LayerRef(
delta: LayerDeltaRef(),
final: final)
if db.balancer.isValid:
db.top.delta.vTop = db.balancer.vTop
else:
let rc = db.getTuvUbe()
if rc.isOk:
db.top.delta.vTop = rc.value
else:
# It is OK if there was no `vTop`. Otherwise something serious happened
# and there is no way to recover easily.
doAssert rc.error == GetTuvNotFound
elif db.top.delta.sTab.len != 0 and
not db.top.delta.sTab.getOrVoid(VertexID(1)).isValid:
# Currently, a `VertexID(1)` root node is required
return err(TxAccRootMissing)
if persistent:
# Merge/move `balancer` into persistent tables
? db.deltaPersistent nxtSid
# New empty top layer (probably with `snap` proofs carry over)
db.top = LayerRef(
delta: LayerDeltaRef(vTop: db.vTop),
final: final,
txUid: db.top.txUid)
ok()
# ------------------------------------------------------------------------------
# End
# ------------------------------------------------------------------------------