nimbus-eth1/nimbus/db/aristo/aristo_layers.nim

272 lines
9.1 KiB
Nim
Raw Normal View History

# nimbus-eth1
Core db update storage root management for sub tries (#1964) * Aristo: Re-phrase `LayerDelta` and `LayerFinal` as object references why: Avoids copying in some cases * Fix copyright header * Aristo: Verify `leafTie.root` function argument for `merge()` proc why: Zero root will lead to inconsistent DB entry * Aristo: Update failure condition for hash labels compiler `hashify()` why: Node need not be rejected as long as links are on the schedule. In that case, `redo[]` is to become `wff.base[]` at a later stage. This amends an earlier fix, part of #1952 by also testing against the target nodes of the `wff.base[]` sets. * Aristo: Add storage root glue record to `hashify()` schedule why: An account leaf node might refer to a non-resolvable storage root ID. Storage root node chains will end up at the storage root. So the link `storage-root->account-leaf` needs an extra item in the schedule. * Aristo: fix error code returned by `fetchPayload()` details: Final error code is implied by the error code form the `hikeUp()` function. * CoreDb: Discard `createOk` argument in API `getRoot()` function why: Not needed for the legacy DB. For the `Arsto` DB, a lazy approach is implemented where a stprage root node is created on-the-fly. * CoreDb: Prevent `$$` logging in some cases why: Logging the function `$$` is not useful when it is used for internal use, i.e. retrieving an an error text for logging. * CoreDb: Add `tryHashFn()` to API for pretty printing why: Pretty printing must not change the hashification status for the `Aristo` DB. So there is an independent API wrapper for getting the node hash which never updated the hashes. * CoreDb: Discard `update` argument in API `hash()` function why: When calling the API function `hash()`, the latest state is always wanted. For a version that uses the current state as-is without checking, the function `tryHash()` was added to the backend. * CoreDb: Update opaque vertex ID objects for the `Aristo` backend why: For `Aristo`, vID objects encapsulate a numeric `VertexID` referencing a vertex (rather than a node hash as used on the legacy backend.) For storage sub-tries, there might be no initial vertex known when the descriptor is created. So opaque vertex ID objects are supported without a valid `VertexID` which will be initalised on-the-fly when the first item is merged. * CoreDb: Add pretty printer for opaque vertex ID objects * Cosmetics, printing profiling data * CoreDb: Fix segfault in `Aristo` backend when creating MPT descriptor why: Missing initialisation error * CoreDb: Allow MPT to inherit shared context on `Aristo` backend why: Creates descriptors with different storage roots for the same shared `Aristo` DB descriptor. * Cosmetics, update diagnostic message items for `Aristo` backend * Fix Copyright year
2024-01-11 19:11:38 +00:00
# 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.
{.push raises: [].}
import
std/[sequtils, sets, tables],
eth/common,
results,
./aristo_desc
# ------------------------------------------------------------------------------
# Private functions
# ------------------------------------------------------------------------------
func dup(sTab: Table[VertexID,VertexRef]): Table[VertexID,VertexRef] =
## Explicit dup for `VertexRef` values
for (k,v) in sTab.pairs:
result[k] = v.dup
# ------------------------------------------------------------------------------
# Public getters: lazy value lookup for read only versions
# ------------------------------------------------------------------------------
2024-05-24 09:27:17 +00:00
func dirty*(db: AristoDbRef): lent HashSet[VertexID] =
db.top.final.dirty
2024-05-24 09:27:17 +00:00
func pPrf*(db: AristoDbRef): lent HashSet[VertexID] =
db.top.final.pPrf
2024-05-24 09:27:17 +00:00
func vGen*(db: AristoDbRef): lent seq[VertexID] =
db.top.delta.vGen
# ------------------------------------------------------------------------------
# Public getters/helpers
# ------------------------------------------------------------------------------
func nLayersVtx*(db: AristoDbRef): int =
## Number of vertex ID/vertex entries on the cache layers. This is an upper
## bound for the number of effective vertex ID mappings held on the cache
## layers as there might be duplicate entries for the same vertex ID on
## different layers.
##
db.stack.mapIt(it.delta.sTab.len).foldl(a + b, db.top.delta.sTab.len)
func nLayersKey*(db: AristoDbRef): int =
## Number of vertex ID/key entries on the cache layers. This is an upper
## bound for the number of effective vertex ID mappingss held on the cache
## layers as there might be duplicate entries for the same vertex ID on
## different layers.
##
db.stack.mapIt(it.delta.kMap.len).foldl(a + b, db.top.delta.kMap.len)
# ------------------------------------------------------------------------------
# Public functions: getter variants
# ------------------------------------------------------------------------------
func layersGetVtx*(db: AristoDbRef; vid: VertexID): Result[VertexRef,void] =
## Find a vertex on the cache layers. An `ok()` result might contain a
## `nil` vertex if it is stored on the cache that way.
##
if db.top.delta.sTab.hasKey vid:
return ok(db.top.delta.sTab.getOrVoid vid)
for w in db.rstack:
if w.delta.sTab.hasKey vid:
return ok(w.delta.sTab.getOrVoid vid)
err()
func layersGetVtxOrVoid*(db: AristoDbRef; vid: VertexID): VertexRef =
## Simplified version of `layersGetVtx()`
db.layersGetVtx(vid).valueOr: VertexRef(nil)
func layersGetKey*(db: AristoDbRef; vid: VertexID): Result[HashKey,void] =
## Find a hash key on the cache layers. An `ok()` result might contain a void
## hash key if it is stored on the cache that way.
##
if db.top.delta.kMap.hasKey vid:
# This is ok regardless of the `dirty` flag. If this vertex has become
# dirty, there is an empty `kMap[]` entry on this layer.
return ok(db.top.delta.kMap.getOrVoid vid)
for w in db.rstack:
if w.delta.kMap.hasKey vid:
# Same reasoning as above regarding the `dirty` flag.
return ok(w.delta.kMap.getOrVoid vid)
err()
func layersGetKeyOrVoid*(db: AristoDbRef; vid: VertexID): HashKey =
## Simplified version of `layersGetkey()`
db.layersGetKey(vid).valueOr: VOID_HASH_KEY
func layerGetProofKeyOrVoid*(db: AristoDbRef; vid: VertexID): HashKey =
## Get the hash key of a proof node if it was registered as such.
if vid in db.top.final.pPrf:
db.top.delta.kMap.getOrVoid vid
else:
VOID_HASH_KEY
func layerGetProofVidOrVoid*(db: AristoDbRef; key: HashKey): VertexID =
## Reverse look up for a registered proof node or a link key for such a
## node. The vertex for a returned vertex ID might not exist if the
## argument `key` refers to a link key of a registered proof node.
db.top.final.fRpp.getOrVoid key
# ------------------------------------------------------------------------------
# Public functions: setter variants
# ------------------------------------------------------------------------------
func layersPutVtx*(
db: AristoDbRef;
root: VertexID;
vid: VertexID;
vtx: VertexRef;
) =
## Store a (potentally empty) vertex on the top layer
db.top.delta.sTab[vid] = vtx
db.top.final.dirty.incl root
func layersResVtx*(
db: AristoDbRef;
root: VertexID;
vid: VertexID;
) =
## Shortcut for `db.layersPutVtx(vid, VertexRef(nil))`. It is sort of the
## equivalent of a delete function.
db.layersPutVtx(root, vid, VertexRef(nil))
func layersPutKey*(
db: AristoDbRef;
root: VertexID;
vid: VertexID;
key: HashKey;
) =
## Store a (potentally void) hash key on the top layer
db.top.delta.kMap[vid] = key
db.top.final.dirty.incl root # Modified top cache layers => hashify
func layersResKey*(db: AristoDbRef; root: VertexID; vid: VertexID) =
## Shortcut for `db.layersPutKey(vid, VOID_HASH_KEY)`. It is sort of the
## equivalent of a delete function.
db.layersPutKey(root, vid, VOID_HASH_KEY)
func layersPutProof*(db: AristoDbRef; vid: VertexID; key: HashKey) =
## Register a link key of a proof node.
let lKey = db.layersGetKeyOrVoid vid
if not lKey.isValid or lKey != key:
db.top.delta.kMap[vid] = key
db.top.final.fRpp[key] = vid
func layersPutProof*(
db: AristoDbRef;
vid: VertexID;
key: HashKey;
vtx: VertexRef;
) =
## Register a full proof node (not only a link key.)
let lVtx = db.layersGetVtxOrVoid vid
if not lVtx.isValid or lVtx != vtx:
db.top.delta.sTab[vid] = vtx
db.top.final.pPrf.incl vid
db.layersPutProof(vid, key)
# ------------------------------------------------------------------------------
# Public functions
# ------------------------------------------------------------------------------
func layersMergeOnto*(src: LayerRef; trg: var LayerObj) =
## Merges the argument `src` into the argument `trg` and returns `trg`. For
## the result layer, the `txUid` value set to `0`.
##
trg.final = src.final
trg.txUid = 0
for (vid,vtx) in src.delta.sTab.pairs:
trg.delta.sTab[vid] = vtx
for (vid,key) in src.delta.kMap.pairs:
trg.delta.kMap[vid] = key
trg.delta.vGen = src.delta.vGen
func layersCc*(db: AristoDbRef; level = high(int)): LayerRef =
## Provide a collapsed copy of layers up to a particular transaction level.
## If the `level` argument is too large, the maximum transaction level is
## returned. For the result layer, the `txUid` value set to `0`.
##
let layers = if db.stack.len <= level: db.stack & @[db.top]
else: db.stack[0 .. level]
# Set up initial layer (bottom layer)
result = LayerRef(
Core db update storage root management for sub tries (#1964) * Aristo: Re-phrase `LayerDelta` and `LayerFinal` as object references why: Avoids copying in some cases * Fix copyright header * Aristo: Verify `leafTie.root` function argument for `merge()` proc why: Zero root will lead to inconsistent DB entry * Aristo: Update failure condition for hash labels compiler `hashify()` why: Node need not be rejected as long as links are on the schedule. In that case, `redo[]` is to become `wff.base[]` at a later stage. This amends an earlier fix, part of #1952 by also testing against the target nodes of the `wff.base[]` sets. * Aristo: Add storage root glue record to `hashify()` schedule why: An account leaf node might refer to a non-resolvable storage root ID. Storage root node chains will end up at the storage root. So the link `storage-root->account-leaf` needs an extra item in the schedule. * Aristo: fix error code returned by `fetchPayload()` details: Final error code is implied by the error code form the `hikeUp()` function. * CoreDb: Discard `createOk` argument in API `getRoot()` function why: Not needed for the legacy DB. For the `Arsto` DB, a lazy approach is implemented where a stprage root node is created on-the-fly. * CoreDb: Prevent `$$` logging in some cases why: Logging the function `$$` is not useful when it is used for internal use, i.e. retrieving an an error text for logging. * CoreDb: Add `tryHashFn()` to API for pretty printing why: Pretty printing must not change the hashification status for the `Aristo` DB. So there is an independent API wrapper for getting the node hash which never updated the hashes. * CoreDb: Discard `update` argument in API `hash()` function why: When calling the API function `hash()`, the latest state is always wanted. For a version that uses the current state as-is without checking, the function `tryHash()` was added to the backend. * CoreDb: Update opaque vertex ID objects for the `Aristo` backend why: For `Aristo`, vID objects encapsulate a numeric `VertexID` referencing a vertex (rather than a node hash as used on the legacy backend.) For storage sub-tries, there might be no initial vertex known when the descriptor is created. So opaque vertex ID objects are supported without a valid `VertexID` which will be initalised on-the-fly when the first item is merged. * CoreDb: Add pretty printer for opaque vertex ID objects * Cosmetics, printing profiling data * CoreDb: Fix segfault in `Aristo` backend when creating MPT descriptor why: Missing initialisation error * CoreDb: Allow MPT to inherit shared context on `Aristo` backend why: Creates descriptors with different storage roots for the same shared `Aristo` DB descriptor. * Cosmetics, update diagnostic message items for `Aristo` backend * Fix Copyright year
2024-01-11 19:11:38 +00:00
final: layers[^1].final.dup, # Pre-merged/final values
delta: LayerDeltaRef(
sTab: layers[0].delta.sTab.dup, # explicit dup for ref values
kMap: layers[0].delta.kMap,
vGen: layers[^1].delta.vGen))
# Consecutively merge other layers on top
for n in 1 ..< layers.len:
for (vid,vtx) in layers[n].delta.sTab.pairs:
result.delta.sTab[vid] = vtx
for (vid,key) in layers[n].delta.kMap.pairs:
result.delta.kMap[vid] = key
# ------------------------------------------------------------------------------
# Public iterators
# ------------------------------------------------------------------------------
iterator layersWalkVtx*(
db: AristoDbRef;
seen: var HashSet[VertexID];
): tuple[vid: VertexID, vtx: VertexRef] =
## Walk over all `(VertexID,VertexRef)` pairs on the cache layers. Note that
## entries are unsorted.
##
## The argument `seen` collects a set of all visited vertex IDs including
## the one with a zero vertex which are othewise skipped by the iterator.
## The `seen` argument must not be modified while the iterator is active.
##
for (vid,vtx) in db.top.delta.sTab.pairs:
yield (vid,vtx)
seen.incl vid
for w in db.rstack:
for (vid,vtx) in w.delta.sTab.pairs:
if vid notin seen:
yield (vid,vtx)
seen.incl vid
iterator layersWalkVtx*(
db: AristoDbRef;
): tuple[vid: VertexID, vtx: VertexRef] =
## Variant of `layersWalkVtx()`.
var seen: HashSet[VertexID]
for (vid,vtx) in db.layersWalkVtx seen:
yield (vid,vtx)
iterator layersWalkKey*(
db: AristoDbRef;
): tuple[vid: VertexID, key: HashKey] =
## Walk over all `(VertexID,HashKey)` pairs on the cache layers. Note that
## entries are unsorted.
var seen: HashSet[VertexID]
for (vid,key) in db.top.delta.kMap.pairs:
yield (vid,key)
seen.incl vid
for w in db.rstack:
for (vid,key) in w.delta.kMap.pairs:
if vid notin seen:
yield (vid,key)
seen.incl vid
# ------------------------------------------------------------------------------
# End
# ------------------------------------------------------------------------------