nimbus-eth1/tests/test_aristo/test_aristo_cache.nim
Jordan Hrycaj 2fc349feb9
Aristo db merkle hashify functionality added (#1593)
* Keep vertex ID generator state with each db-layer

why:
  The vertex ID generator state is part of the difference to the below
  layer

* Move otherwise unused source to test directory

* Add Merkle hash generator

also:
  * Verification facility for debugging
  * Empty Merkle key hashes encoded as `EMPTY_ROOT_HASH`
2023-05-30 22:21:15 +01:00

186 lines
5.8 KiB
Nim

# nimbus-eth1
# Copyright (c) 2021 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: [].}
## Parked here, currently uded only for trancode tests
import
std/tables,
eth/common,
stew/results,
../../nimbus/db/aristo/[
aristo_constants, aristo_desc, aristo_error, aristo_transcode, aristo_vid]
# ------------------------------------------------------------------------------
# Private helpers
# ------------------------------------------------------------------------------
proc convertPartially(
db: AristoDbRef;
vtx: VertexRef;
nd: var NodeRef;
): seq[VertexID] =
## Returns true if completely converted by looking up the cached hashes.
## This function does not recurse. It will return the vertex IDs that are
## are missing in order to convert in a single step.
case vtx.vType:
of Leaf:
nd = NodeRef(
vType: Leaf,
lPfx: vtx.lPfx,
lData: vtx.lData)
of Extension:
nd = NodeRef(
vType: Extension,
ePfx: vtx.ePfx,
eVid: vtx.eVid)
let key = db.kMap.getOrDefault(vtx.eVid, EMPTY_ROOT_KEY)
if key != EMPTY_ROOT_KEY:
nd.key[0] = key
return
result.add vtx.eVid
of Branch:
nd = NodeRef(
vType: Branch,
bVid: vtx.bVid)
for n in 0..15:
if not vtx.bVid[n].isZero:
let key = db.kMap.getOrDefault(vtx.bVid[n], EMPTY_ROOT_KEY)
if key != EMPTY_ROOT_KEY:
nd.key[n] = key
continue
result.add vtx.bVid[n]
proc convertPartiallyOk(
db: AristoDbRef;
vtx: VertexRef;
nd: var NodeRef;
): bool =
## Variant of `convertPartially()`, shortcut for `convertPartially().le==0`.
case vtx.vType:
of Leaf:
nd = NodeRef(
vType: Leaf,
lPfx: vtx.lPfx,
lData: vtx.lData)
result = true
of Extension:
nd = NodeRef(
vType: Extension,
ePfx: vtx.ePfx,
eVid: vtx.eVid)
let key = db.kMap.getOrDefault(vtx.eVid, EMPTY_ROOT_KEY)
if key != EMPTY_ROOT_KEY:
nd.key[0] = key
result = true
of Branch:
nd = NodeRef(
vType: Branch,
bVid: vtx.bVid)
result = true
for n in 0..15:
if not vtx.bVid[n].isZero:
let key = db.kMap.getOrDefault(vtx.bVid[n], EMPTY_ROOT_KEY)
if key != EMPTY_ROOT_KEY:
nd.key[n] = key
continue
return false
proc cachedVID(db: AristoDbRef; nodeKey: NodeKey): VertexID =
## Get vertex ID from reverse cache
let vid = db.pAmk.getOrDefault(nodeKey, VertexID(0))
if vid != VertexID(0):
result = vid
else:
result = db.vidFetch()
db.pAmk[nodeKey] = result
db.kMap[result] = nodeKey
# ------------------------------------------------------------------------------
# Public functions for `VertexID` => `NodeKey` mapping
# ------------------------------------------------------------------------------
proc pal*(db: AristoDbRef; vid: VertexID): NodeKey =
## Retrieve the cached `Merkel` hash (aka `NodeKey` object) associated with
## the argument `VertexID` type argument `vid`. Return a zero `NodeKey` if
## there is none.
##
## If the vertex ID `vid` is not found in the cache, then the structural
## table is checked whether the cache can be updated.
if not db.isNil:
let key = db.kMap.getOrDefault(vid, EMPTY_ROOT_KEY)
if key != EMPTY_ROOT_KEY:
return key
let vtx = db.sTab.getOrDefault(vid, VertexRef(nil))
if vtx != VertexRef(nil):
var node: NodeRef
if db.convertPartiallyOk(vtx,node):
var w = initRlpWriter()
w.append node
result = w.finish.keccakHash.data.NodeKey
db.kMap[vid] = result
# ------------------------------------------------------------------------------
# Public funcions extending/completing vertex records
# ------------------------------------------------------------------------------
proc updated*(nd: NodeRef; db: AristoDbRef): NodeRef =
## Return a copy of the argument node `nd` with updated missing vertex IDs.
##
## For a `Leaf` node, the payload data `PayloadRef` type reference is *not*
## duplicated and returned as-is.
##
## This function will not complain if all `Merkel` hashes (aka `NodeKey`
## objects) are zero for either `Extension` or `Leaf` nodes.
if not nd.isNil:
case nd.vType:
of Leaf:
result = NodeRef(
vType: Leaf,
lPfx: nd.lPfx,
lData: nd.lData)
of Extension:
result = NodeRef(
vType: Extension,
ePfx: nd.ePfx)
if not nd.key[0].isEmpty:
result.eVid = db.cachedVID nd.key[0]
result.key[0] = nd.key[0]
of Branch:
result = NodeRef(
vType: Branch,
key: nd.key)
for n in 0..15:
if not nd.key[n].isEmpty:
result.bVid[n] = db.cachedVID nd.key[n]
proc asNode*(vtx: VertexRef; db: AristoDbRef): NodeRef =
## Return a `NodeRef` object by augmenting missing `Merkel` hashes (aka
## `NodeKey` objects) from the cache or from calculated cached vertex
## entries, if available.
##
## If not all `Merkel` hashes are available in a single lookup, then the
## result object is a wrapper around an error code.
if not db.convertPartiallyOk(vtx, result):
return NodeRef(error: CacheMissingNodekeys)
proc asNode*(rc: Result[VertexRef,AristoError]; db: AristoDbRef): NodeRef =
## Variant of `asNode()`.
if rc.isErr:
return NodeRef(error: rc.error)
rc.value.asNode(db)
# ------------------------------------------------------------------------------
# End
# ------------------------------------------------------------------------------