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

152 lines
4.7 KiB
Nim
Raw Normal View History

# 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
2024-06-04 15:05:13 +00:00
std/tables,
results,
../aristo_delta/delta_merge,
2024-06-04 15:05:13 +00:00
".."/[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)
2024-06-04 15:05:13 +00:00
# New empty top layer (probably with `snap` proofs and `vTop` carry over)
db.top = LayerRef(
delta: LayerDeltaRef(),
final: final)
if db.balancer.isValid:
2024-06-04 15:05:13 +00:00
db.top.delta.vTop = db.balancer.vTop
else:
2024-06-04 15:05:13 +00:00
let rc = db.getTuvUbe()
if rc.isOk:
2024-06-04 15:05:13 +00:00
db.top.delta.vTop = rc.value
else:
2024-06-04 15:05:13 +00:00
# It is OK if there was no `vTop`. Otherwise something serious happened
# and there is no way to recover easily.
2024-06-04 15:05:13 +00:00
doAssert rc.error == GetTuvNotFound
Aristo avoid storage trie update race conditions (#2251) * Update TDD suite logger output format choices why: New format is not practical for TDD as it just dumps data across a wide range (considerably larder than 80 columns.) So the new format can be turned on by function argument. * Update unit tests samples configuration why: Slightly changed the way to find the `era1` directory * Remove compiler warnings (fix deprecated expressions and phrases) * Update `Aristo` debugging tools * Always update the `storageID` field of account leaf vertices why: Storage tries are weekly linked to an account leaf object in that the `storageID` field is updated by the application. Previously, `Aristo` verified that leaf objects make sense when passed to the database. As a consequence * the database was inconsistent for a short while * the burden for correctness was all on the application which led to delayed error handling which is hard to debug. So `Aristo` will internally update the account leaf objects so that there are no race conditions due to the storage trie handling * Aristo: Let `stow()`/`persist()` bail out unless there is a `VertexID(1)` why: The journal and filter logic depends on the hash of the `VertexID(1)` which is commonly known as the state root. This implies that all changes to the database are somehow related to that. * Make sure that a `Ledger` account does not overwrite the storage trie reference why: Due to the abstraction of a sub-trie (now referred to as column with a hash describing its state) there was a weakness in the `Aristo` handler where an account leaf could be overwritten though changing the validity of the database. This has been changed and the database will now reject such changes. This patch fixes the behaviour on the application layer. In particular, the column handle returned by the `CoreDb` needs to be updated by the `Aristo` database state. This mitigates the problem that a storage trie might have vanished or re-apperaed with a different vertex ID. * Fix sub-trie deletion test why: Was originally hinged on `VertexID(1)` which cannot be wholesale deleted anymore after the last Aristo update. Also, running with `VertexID(2)` needs an artificial `VertexID(1)` for making `stow()` or `persist()` work. * Cosmetics * Activate `test_generalstate_json` * Temporarily `deactivate test_tracer_json` * Fix copyright header --------- Co-authored-by: jordan <jordan@dry.pudding> Co-authored-by: Jacek Sieka <jacek@status.im>
2024-05-30 17:48:38 +00:00
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(
2024-06-04 15:05:13 +00:00
delta: LayerDeltaRef(vTop: db.vTop),
final: final,
txUid: db.top.txUid)
ok()
# ------------------------------------------------------------------------------
# End
# ------------------------------------------------------------------------------