2023-05-30 21:21:15 +00:00
|
|
|
# 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
|
2023-05-30 21:21:15 +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.
|
|
|
|
|
|
|
|
## Aristo DB -- Patricia Trie Merkleisation
|
|
|
|
## ========================================
|
|
|
|
##
|
|
|
|
## For the current state of the `Patricia Trie`, keys (equivalent to hashes)
|
|
|
|
## are associated with the vertex IDs. Existing key associations are checked
|
|
|
|
## (i.e. recalculated and compared) unless the ID is locked. In the latter
|
|
|
|
## case, the key is assumed to be correct without checking.
|
|
|
|
##
|
2023-12-04 20:39:26 +00:00
|
|
|
## The folllowing properties are required from the top layer cache.
|
|
|
|
##
|
|
|
|
## * All recently (i.e. not saved to backend) added entries must have an
|
|
|
|
## `lTab[]` entry with `(root-vertex,path,leaf-vertex-ID)`.
|
|
|
|
##
|
|
|
|
## * All recently (i.e. not saved to backend) deleted entries must have an
|
|
|
|
## `lTab[]` entry with `(root-vertex,path,VertexID(0))`.
|
|
|
|
##
|
|
|
|
## * All vertices where the key (aka Merkle hash) has changed must have a
|
2024-02-14 19:11:59 +00:00
|
|
|
## top layer cache `kMap[]` entry `(vertex-ID,VOID_HASH_KEY)` indicating
|
2023-12-04 20:39:26 +00:00
|
|
|
## that there is no key available for this vertex. This also applies for
|
|
|
|
## backend verices where the key has changed while the structural logic
|
|
|
|
## did not change.
|
|
|
|
##
|
2023-05-30 21:21:15 +00:00
|
|
|
## The association algorithm is an optimised version of:
|
|
|
|
##
|
2023-12-04 20:39:26 +00:00
|
|
|
## * For all leaf vertices which have all child links on the top layer cache
|
|
|
|
## where the node keys (aka hashes) can be compiled, proceed with the parent
|
|
|
|
## vertex. Note that a top layer cache vertex can only have a key on the top
|
|
|
|
## top layer cache (whereas a bachend b
|
2023-05-30 21:21:15 +00:00
|
|
|
##
|
2023-12-04 20:39:26 +00:00
|
|
|
## Apparently, keys (aka hashes) can be compiled for leaf vertices. The same
|
|
|
|
## holds for follow up vertices where the child keys were available, alteady.
|
|
|
|
## This process stops when a vertex has children on the backend or children
|
|
|
|
## lead to a chain not sorted, yet.
|
2023-05-30 21:21:15 +00:00
|
|
|
##
|
2023-12-04 20:39:26 +00:00
|
|
|
## * For the remaining vertex chains (where the process stopped) up to the root
|
|
|
|
## vertex, set up a width-first schedule starting at the vertex where the
|
|
|
|
## previous chain broke off and follow up to the root vertex.
|
2023-05-30 21:21:15 +00:00
|
|
|
##
|
2023-12-04 20:39:26 +00:00
|
|
|
## * Follow the width-first schedule fo labelling all vertices with a hash key.
|
2023-05-30 21:21:15 +00:00
|
|
|
##
|
2023-12-04 20:39:26 +00:00
|
|
|
## Note that there are some tweaks for `proof` nodes with incomplete tries and
|
|
|
|
## handling of possible stray vertices on the top layer cache left over from
|
|
|
|
## deletion processes.
|
2023-05-30 21:21:15 +00:00
|
|
|
##
|
|
|
|
{.push raises: [].}
|
|
|
|
|
|
|
|
import
|
2023-12-12 17:47:41 +00:00
|
|
|
std/[sequtils, sets, tables],
|
2023-05-30 21:21:15 +00:00
|
|
|
chronicles,
|
|
|
|
eth/common,
|
2023-09-15 15:23:53 +00:00
|
|
|
results,
|
2023-12-04 20:39:26 +00:00
|
|
|
stew/byteutils,
|
2023-12-19 12:39:23 +00:00
|
|
|
"."/[aristo_desc, aristo_get, aristo_hike, aristo_layers, aristo_serialise,
|
2024-02-08 16:32:16 +00:00
|
|
|
aristo_utils, aristo_vid]
|
2023-05-30 21:21:15 +00:00
|
|
|
|
2023-06-20 13:26:25 +00:00
|
|
|
type
|
2023-12-04 20:39:26 +00:00
|
|
|
FollowUpVid = object
|
|
|
|
## Link item: VertexID -> VertexID
|
|
|
|
root: VertexID ## Root vertex, might be void unless known
|
|
|
|
toVid: VertexID ## Valid next/follow up vertex
|
2023-06-20 13:26:25 +00:00
|
|
|
|
|
|
|
BackVidTab =
|
2023-12-04 20:39:26 +00:00
|
|
|
Table[VertexID,FollowUpVid]
|
2023-06-20 13:26:25 +00:00
|
|
|
|
2023-12-04 20:39:26 +00:00
|
|
|
WidthFirstForest = object
|
|
|
|
## Collected width first search trees
|
2023-12-12 17:47:41 +00:00
|
|
|
completed: HashSet[VertexID] ## Top level, root targets reached
|
|
|
|
root: HashSet[VertexID] ## Top level, root targets not reached yet
|
2023-12-04 20:39:26 +00:00
|
|
|
pool: BackVidTab ## Upper links pool
|
|
|
|
base: BackVidTab ## Width-first leaf level links
|
2023-08-17 13:42:01 +00:00
|
|
|
|
|
|
|
const
|
|
|
|
SubTreeSearchDepthMax = 64
|
|
|
|
|
2023-05-30 21:21:15 +00:00
|
|
|
logScope:
|
|
|
|
topics = "aristo-hashify"
|
|
|
|
|
2023-06-20 13:26:25 +00:00
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# Private helpers
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
template logTxt(info: static[string]): static[string] =
|
|
|
|
"Hashify " & info
|
|
|
|
|
2023-09-15 15:23:53 +00:00
|
|
|
|
2023-12-04 20:39:26 +00:00
|
|
|
func getOrVoid(tab: BackVidTab; vid: VertexID): FollowUpVid =
|
|
|
|
tab.getOrDefault(vid, FollowUpVid())
|
2023-06-20 13:26:25 +00:00
|
|
|
|
2023-12-04 20:39:26 +00:00
|
|
|
func isValid(w: FollowUpVid): bool =
|
|
|
|
w.toVid.isValid
|
|
|
|
|
|
|
|
func contains(wff: WidthFirstForest; vid: VertexID): bool =
|
2023-12-12 17:47:41 +00:00
|
|
|
vid in wff.base or vid in wff.pool or vid in wff.root or vid in wff.completed
|
2023-06-20 13:26:25 +00:00
|
|
|
|
2023-05-30 21:21:15 +00:00
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# Private functions
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
|
2023-12-04 20:39:26 +00:00
|
|
|
proc cloudConnect(
|
|
|
|
cloud: HashSet[VertexID]; # Vertex IDs to start connecting from
|
2023-07-04 18:24:03 +00:00
|
|
|
db: AristoDbRef; # Database, top layer
|
2023-12-04 20:39:26 +00:00
|
|
|
target: BackVidTab; # Vertices to arrive to
|
|
|
|
): tuple[paths: WidthFirstForest, unresolved: HashSet[VertexID]] =
|
|
|
|
## For each vertex ID from argument `cloud` find a chain of `FollowUpVid`
|
|
|
|
## type links reaching into argument `target`. The `paths` entry from the
|
|
|
|
## `result` tuple contains the connections to the `target` argument and the
|
|
|
|
## `unresolved` entries the IDs left over from `cloud`.
|
|
|
|
if 0 < cloud.len:
|
|
|
|
result.unresolved = cloud
|
|
|
|
var hold = target
|
|
|
|
while 0 < hold.len:
|
|
|
|
# Greedily trace back `bottomUp[]` entries for finding parents of
|
|
|
|
# unresolved vertices from `cloud`
|
|
|
|
var redo: BackVidTab
|
|
|
|
for (vid,val) in hold.pairs:
|
|
|
|
let vtx = db.getVtx vid
|
|
|
|
if vtx.isValid:
|
|
|
|
result.paths.pool[vid] = val
|
|
|
|
# Grab child links
|
|
|
|
for sub in vtx.subVids:
|
|
|
|
let w = FollowUpVid(
|
|
|
|
root: val.root,
|
|
|
|
toVid: vid)
|
|
|
|
if sub notin cloud:
|
|
|
|
redo[sub] = w
|
|
|
|
else:
|
|
|
|
result.paths.base[sub] = w # ok, use this
|
|
|
|
result.unresolved.excl sub
|
|
|
|
if result.unresolved.len == 0:
|
|
|
|
return
|
|
|
|
redo.swap hold
|
|
|
|
|
|
|
|
|
2023-12-12 17:47:41 +00:00
|
|
|
proc setNextLink(
|
2023-12-04 20:39:26 +00:00
|
|
|
wff: var WidthFirstForest; # Search tree to update
|
2023-12-12 17:47:41 +00:00
|
|
|
redo: var BackVidTab; # Temporary `base` list
|
|
|
|
val: FollowUpVid; # Current vertex value to follow up
|
2023-12-04 20:39:26 +00:00
|
|
|
) =
|
2023-12-12 17:47:41 +00:00
|
|
|
## Given the follow up argument `vid`, update the `redo[]` argument (an
|
|
|
|
## optional substitute for the `wff.base[]` list) so that the `redo[]`
|
|
|
|
## list contains the next `from->to` vertex pair from the `wff.pool[]`
|
|
|
|
## list.
|
2023-12-04 20:39:26 +00:00
|
|
|
##
|
2023-12-12 17:47:41 +00:00
|
|
|
## Unless the `redo` argument is passed as `wff.base`, this function
|
|
|
|
## supports the following construct:
|
|
|
|
## ::
|
|
|
|
## while 0 < wff.base.len:
|
|
|
|
## var redo: BackVidTab
|
|
|
|
## for (vid,val) in wff.base.pairs:
|
|
|
|
## ...
|
|
|
|
## wff.setNextLink(redo, val)
|
|
|
|
## wff.base.swap redo
|
2023-12-04 20:39:26 +00:00
|
|
|
##
|
2023-12-12 17:47:41 +00:00
|
|
|
## Otherwise, one would use the function as in
|
|
|
|
## ::
|
|
|
|
## wff.base.del vid
|
|
|
|
## wff.setNextLink(wff.pool, val)
|
2023-12-04 20:39:26 +00:00
|
|
|
##
|
2023-12-12 17:47:41 +00:00
|
|
|
# Get current `from->to` vertex pair
|
|
|
|
if val.isValid:
|
|
|
|
# Find follow up `from->to` vertex pair in `pool`
|
|
|
|
let nextVal = wff.pool.getOrVoid val.toVid
|
|
|
|
if nextVal.isValid:
|
|
|
|
|
|
|
|
# Make sure that strict hierachial order is kept. If the successor
|
|
|
|
# is in the temporary `redo[]` base list, move it to the `pool[]`.
|
|
|
|
if nextVal.toVid in redo:
|
|
|
|
wff.pool[nextVal.toVid] = redo.getOrVoid nextVal.toVid
|
|
|
|
redo.del nextVal.toVid
|
|
|
|
|
|
|
|
elif val.toVid in redo.values.toSeq.mapIt(it.toVid):
|
|
|
|
# The follow up vertex ID is already a follow up ID for some
|
|
|
|
# `from->to` vertex pair in the temporary `redo[]` base list.
|
|
|
|
return
|
|
|
|
|
|
|
|
# Move next `from->to vertex` pair to `redo[]`
|
|
|
|
wff.pool.del val.toVid
|
|
|
|
redo[val.toVid] = nextVal
|
|
|
|
|
|
|
|
|
|
|
|
proc updateSchedule(
|
|
|
|
wff: var WidthFirstForest; # Search tree to update
|
|
|
|
db: AristoDbRef; # Database, top layer
|
|
|
|
hike: Hike; # Chain of vertices
|
|
|
|
) =
|
|
|
|
## Use vertices from the `hike` argument and link them leaf-to-root in a way
|
|
|
|
## so so that they can be traversed later in a width-first search.
|
2023-12-04 20:39:26 +00:00
|
|
|
##
|
2023-12-12 17:47:41 +00:00
|
|
|
let
|
|
|
|
root = hike.root
|
|
|
|
var
|
|
|
|
legInx = 0 # find index of first unresolved vertex
|
|
|
|
unresolved: seq[VertexID] # vtx links, reason for unresolved vertex
|
|
|
|
# Find the index `legInx` of the first vertex that could not be compiled as
|
|
|
|
# node all from the top layer cache keys.
|
|
|
|
block findlegInx:
|
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
|
|
|
# Directly set tail vertex key (typically a leaf vertex)
|
2023-12-12 17:47:41 +00:00
|
|
|
let
|
|
|
|
leaf = hike.legs[^1].wp
|
|
|
|
node = leaf.vtx.toNode(db, stopEarly=false, beKeyOk=false).valueOr:
|
|
|
|
# Oops, depends on unresolved storage trie?
|
|
|
|
legInx = hike.legs.len - 1
|
|
|
|
unresolved = error
|
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
|
|
|
if leaf.vtx.vType == Leaf:
|
|
|
|
let stoRoot = unresolved.toSeq[0]
|
|
|
|
if stoRoot notin wff.base and
|
|
|
|
stoRoot notin wff.pool:
|
|
|
|
wff.root.incl stoRoot
|
|
|
|
wff.base[stoRoot] = FollowUpVid(
|
2024-02-14 10:02:09 +00:00
|
|
|
root: root, # Jump to main tree
|
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
|
|
|
toVid: leaf.vid)
|
2023-12-12 17:47:41 +00:00
|
|
|
break findlegInx
|
|
|
|
vid = leaf.vid
|
2023-12-19 12:39:23 +00:00
|
|
|
|
2023-12-12 17:47:41 +00:00
|
|
|
# If possible, compute a node from the current vertex with all links
|
|
|
|
# resolved on the cache layer. If this is not possible, stop here and
|
|
|
|
# return the list of vertex IDs that could not be resolved (see option
|
|
|
|
# `stopEarly=false`.)
|
|
|
|
for n in (hike.legs.len-2).countDown(0):
|
|
|
|
let vtx = hike.legs[n].wp.vtx
|
|
|
|
discard vtx.toNode(db, stopEarly=false, beKeyOk=false).valueOr:
|
|
|
|
legInx = n
|
|
|
|
unresolved = error
|
|
|
|
break findlegInx
|
|
|
|
|
|
|
|
# All done this `hike`
|
2023-12-19 12:39:23 +00:00
|
|
|
if db.layersGetKeyOrVoid(root).isValid:
|
2023-12-12 17:47:41 +00:00
|
|
|
wff.root.excl root
|
2024-02-01 21:27:48 +00:00
|
|
|
wff.completed.incl root
|
|
|
|
else:
|
|
|
|
wff.root.incl root
|
2023-12-12 17:47:41 +00:00
|
|
|
return
|
|
|
|
|
|
|
|
# Unresolved root target to reach via width-first search
|
|
|
|
if root notin wff.completed:
|
|
|
|
wff.root.incl root
|
|
|
|
|
|
|
|
# Current situation:
|
|
|
|
#
|
|
|
|
# ..unresolved hash keys.. | ..all set here..
|
|
|
|
# |
|
|
|
|
# |
|
|
|
|
# hike.legs: (leg[0], leg[1], ..leg[legInx], ..)
|
|
|
|
# | | | |
|
|
|
|
# | <---- | <----- | +-------+---- \
|
|
|
|
# | | | |
|
|
|
|
# | wff.pool[] | +---- | vertices from the
|
|
|
|
# : | `unresoved` set
|
|
|
|
# |
|
|
|
|
# +---- /
|
2023-12-04 20:39:26 +00:00
|
|
|
|
|
|
|
# Add unresolved nodes for top level links
|
2023-12-12 17:47:41 +00:00
|
|
|
for u in 1 .. legInx:
|
2023-12-04 20:39:26 +00:00
|
|
|
let vid = hike.legs[u].wp.vid
|
|
|
|
# Make sure that `base[]` and `pool[]` are disjunkt, possibly moving
|
|
|
|
# `base[]` entries to the `pool[]`.
|
|
|
|
wff.base.del vid
|
|
|
|
wff.pool[vid] = FollowUpVid(
|
2023-12-12 17:47:41 +00:00
|
|
|
root: root,
|
2023-12-04 20:39:26 +00:00
|
|
|
toVid: hike.legs[u-1].wp.vid)
|
|
|
|
|
|
|
|
# These ones have been resolved, already
|
2023-12-12 17:47:41 +00:00
|
|
|
for u in legInx+1 ..< hike.legs.len:
|
2023-12-04 20:39:26 +00:00
|
|
|
let vid = hike.legs[u].wp.vid
|
|
|
|
wff.pool.del vid
|
|
|
|
wff.base.del vid
|
|
|
|
|
2023-12-12 17:47:41 +00:00
|
|
|
assert 0 < unresolved.len # debugging, only
|
|
|
|
let vid = hike.legs[legInx].wp.vid
|
|
|
|
for sub in unresolved:
|
2023-12-04 20:39:26 +00:00
|
|
|
# Update request for unresolved sub-links by adding a new tail
|
|
|
|
# entry (unless registered, already.)
|
|
|
|
if sub notin wff:
|
|
|
|
wff.base[sub] = FollowUpVid(
|
2023-12-12 17:47:41 +00:00
|
|
|
root: root,
|
2023-12-04 20:39:26 +00:00
|
|
|
toVid: vid)
|
2023-08-17 13:42:01 +00:00
|
|
|
|
2023-05-30 21:21:15 +00:00
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# Public functions
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
proc hashify*(
|
2023-07-04 18:24:03 +00:00
|
|
|
db: AristoDbRef; # Database, top layer
|
2023-06-09 11:17:37 +00:00
|
|
|
): Result[HashSet[VertexID],(VertexID,AristoError)] =
|
2023-05-30 21:21:15 +00:00
|
|
|
## Add keys to the `Patricia Trie` so that it becomes a `Merkle Patricia
|
2023-07-12 23:03:14 +00:00
|
|
|
## Tree`. If successful, the function returns the keys (aka Merkle hash) of
|
|
|
|
## the root vertices.
|
2023-05-30 21:21:15 +00:00
|
|
|
var
|
2023-12-04 20:39:26 +00:00
|
|
|
deleted = false # Need extra check for orphaned vertices
|
|
|
|
wff: WidthFirstForest # Leaf-to-root traversal structure
|
2023-08-11 17:23:57 +00:00
|
|
|
|
2023-12-19 12:39:23 +00:00
|
|
|
if not db.dirty:
|
2023-12-12 17:47:41 +00:00
|
|
|
return ok wff.completed
|
2023-06-09 11:17:37 +00:00
|
|
|
|
2023-12-19 12:39:23 +00:00
|
|
|
for (lky,lfVid) in db.lTab.pairs:
|
2023-12-04 20:39:26 +00:00
|
|
|
let
|
|
|
|
rc = lky.hikeUp db
|
|
|
|
hike = rc.to(Hike)
|
|
|
|
|
|
|
|
if not lfVid.isValid:
|
|
|
|
# Remember that there are left overs from a delete proedure which have
|
|
|
|
# to be eventually found before starting width-first processing.
|
|
|
|
deleted = true
|
|
|
|
|
|
|
|
if hike.legs.len == 0:
|
|
|
|
# Ignore left over path from deleted entry.
|
|
|
|
if not lfVid.isValid:
|
2023-12-12 17:47:41 +00:00
|
|
|
# FIXME: Is there a case for adding unresolved child-to-root links
|
|
|
|
# to the `wff` schedule?
|
2023-12-04 20:39:26 +00:00
|
|
|
continue
|
2024-02-08 16:32:16 +00:00
|
|
|
doAssert rc.isErr # see implementation of `hikeUp()`
|
|
|
|
return err((lfVid,rc.error[1]))
|
2023-05-30 21:21:15 +00:00
|
|
|
|
2023-12-12 17:47:41 +00:00
|
|
|
# Compile width-first forest search schedule
|
|
|
|
wff.updateSchedule(db, hike)
|
2023-12-04 20:39:26 +00:00
|
|
|
|
|
|
|
if deleted:
|
2023-12-12 17:47:41 +00:00
|
|
|
# Update unresolved keys left over after delete operations when overlay
|
|
|
|
# vertices have been added and there was no `hike` path to capture them.
|
|
|
|
#
|
|
|
|
# Considering a list of updated paths to these vertices after deleting
|
|
|
|
# a `Leaf` vertex is deemed too expensive and more error prone. So it
|
|
|
|
# is the task to search for unresolved node keys and add glue paths to
|
|
|
|
# the width-first schedule.
|
2023-12-04 20:39:26 +00:00
|
|
|
var unresolved: HashSet[VertexID]
|
2024-02-14 19:11:59 +00:00
|
|
|
for (vid,key) in db.layersWalkKey:
|
|
|
|
if not key.isValid and
|
2023-12-19 12:39:23 +00:00
|
|
|
vid notin wff:
|
|
|
|
let rc = db.layersGetVtx vid
|
|
|
|
if rc.isErr or rc.value.isValid:
|
|
|
|
unresolved.incl vid
|
2023-12-04 20:39:26 +00:00
|
|
|
|
|
|
|
let glue = unresolved.cloudConnect(db, wff.base)
|
|
|
|
if 0 < glue.unresolved.len:
|
|
|
|
return err((glue.unresolved.toSeq[0],HashifyNodeUnresolved))
|
|
|
|
# Add glue items to `wff.base[]` and `wff.pool[]` tables
|
|
|
|
for (vid,val) in glue.paths.base.pairs:
|
|
|
|
# Add vid to `wff.base[]` list
|
|
|
|
wff.base[vid] = val
|
|
|
|
# Move tail of VertexID chain to `wff.pool[]`
|
|
|
|
var toVid = val.toVid
|
|
|
|
while true:
|
|
|
|
let w = glue.paths.pool.getOrVoid toVid
|
|
|
|
if not w.isValid:
|
|
|
|
break
|
|
|
|
wff.base.del toVid
|
|
|
|
wff.pool[toVid] = w
|
|
|
|
toVid = w.toVid
|
|
|
|
|
|
|
|
# Traverse width-first schedule and update remaining hashes.
|
|
|
|
while 0 < wff.base.len:
|
|
|
|
var redo: BackVidTab
|
|
|
|
for (vid,val) in wff.base.pairs:
|
2023-12-12 17:47:41 +00:00
|
|
|
|
|
|
|
let vtx = db.getVtx vid
|
|
|
|
if not vtx.isValid:
|
|
|
|
# This might happen when proof nodes (see `snap` protocol) are on
|
|
|
|
# an incomplete trie where this `vid` has a key but no vertex yet.
|
2024-02-12 19:37:00 +00:00
|
|
|
# Also, the key (as part of the proof data) must be on the backend.
|
2023-12-12 17:47:41 +00:00
|
|
|
discard db.getKeyBE(vid).valueOr:
|
|
|
|
return err((vid,HashifyNodeUnresolved))
|
|
|
|
else:
|
2023-12-04 20:39:26 +00:00
|
|
|
# Try to convert the vertex to a node. This is possible only if all
|
|
|
|
# link references have Merkle hash keys, already.
|
2023-12-12 17:47:41 +00:00
|
|
|
let node = vtx.toNode(db, stopEarly=false).valueOr:
|
|
|
|
# Cannot complete this vertex unless its child node keys are compiled.
|
|
|
|
for w in error:
|
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
|
|
|
if w notin wff.base and
|
|
|
|
w notin redo and
|
2024-02-14 10:02:09 +00:00
|
|
|
w notin wff.base.values.toSeq.mapit(it.toVid) and
|
|
|
|
w notin wff.pool.values.toSeq.mapit(it.toVid):
|
2023-12-19 12:39:23 +00:00
|
|
|
if db.layersGetVtx(w).isErr:
|
2023-12-12 17:47:41 +00:00
|
|
|
# Ooops, should have been marked for update
|
|
|
|
return err((w,HashifyNodeUnresolved))
|
2024-02-14 10:02:09 +00:00
|
|
|
# Add the child vertex to `redo[]` for the schedule `base[]` list.
|
2023-12-12 17:47:41 +00:00
|
|
|
redo[w] = FollowUpVid(root: val.root, toVid: vid)
|
2024-02-14 10:02:09 +00:00
|
|
|
# Do this vertex later, i.e. add the vertex to the `pool[]`.
|
|
|
|
wff.pool[vid] = val
|
|
|
|
continue
|
|
|
|
# End `valueOr` terminates error clause
|
2023-12-04 20:39:26 +00:00
|
|
|
|
|
|
|
# Could resolve => update Merkle hash
|
2024-02-14 19:11:59 +00:00
|
|
|
db.layersPutKey(vid, node.digestTo HashKey)
|
2023-12-04 20:39:26 +00:00
|
|
|
|
2023-12-12 17:47:41 +00:00
|
|
|
# Set follow up link for next round
|
|
|
|
wff.setNextLink(redo, val)
|
2024-02-14 10:02:09 +00:00
|
|
|
# End `for (vid,val)..`
|
2023-12-04 20:39:26 +00:00
|
|
|
|
|
|
|
# Restart `wff.base[]`
|
|
|
|
wff.base.swap redo
|
|
|
|
|
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
|
|
|
# Make sure that all keys exist (actually, that set should be empty anyway)
|
|
|
|
for vid in wff.pool.keys:
|
|
|
|
discard db.getKeyRc(vid).valueOr:
|
|
|
|
return err((vid,HashifyNodeUnresolved))
|
|
|
|
|
2023-12-04 20:39:26 +00:00
|
|
|
# Update root nodes
|
2023-12-19 12:39:23 +00:00
|
|
|
for vid in wff.root - db.pPrf:
|
2023-12-04 20:39:26 +00:00
|
|
|
# Convert root vertex to a node.
|
|
|
|
let node = db.getVtx(vid).toNode(db,stopEarly=false).valueOr:
|
|
|
|
return err((vid,HashifyRootNodeUnresolved))
|
2024-02-14 19:11:59 +00:00
|
|
|
db.layersPutKey(vid, node.digestTo(HashKey))
|
2023-12-12 17:47:41 +00:00
|
|
|
wff.completed.incl vid
|
2023-05-30 21:21:15 +00:00
|
|
|
|
2024-02-08 16:32:16 +00:00
|
|
|
db.top.final.dirty = false # Mark top layer clean
|
|
|
|
db.top.final.lTab.clear # Done with leafs
|
|
|
|
db.top.final.vGen = db.vGen.vidReorg() # Squeze list of recycled vertex IDs
|
|
|
|
|
2023-12-12 17:47:41 +00:00
|
|
|
ok wff.completed
|
2023-05-30 21:21:15 +00:00
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# End
|
|
|
|
# ------------------------------------------------------------------------------
|