nimbus-eth1/nimbus/db/aristo/aristo_hike.nim
Jordan Hrycaj 657379f484
Aristo db update merkle hasher (#1925)
* Register paths for added leafs because of trie re-balancing

why:
  While the payload would not change, the prefix in the leaf vertex
  would. So it needs to be flagged for hash recompilation for the
  `hashify()` module.

also:
  Make sure that `Hike` paths which might have vertex links into the
  backend filter are replaced by vertex copies before manipulating.
  Otherwise the vertices on the immutable filter might be involuntarily
  changed.

* Also check for paths where the leaf vertex is on the backend, already

why:
  A a path can have dome vertices on the top layer cache with the
  `Leaf` vertex on  the backend.

* Re-define a void `HashLabel` type.

why:
  A `HashLabel` type is a pair `(root-vertex-ID, Keccak-hash)`. Previously,
  a valid `HashLabel` consisted of a non-empty hash and a non-zero vertex
  ID. This definition leads to a non-unique representation of a void
  `HashLabel` with either root-ID or has void. This has been changed to
  the unique void `HashLabel` exactly if the hash entry is void.

* Update consistency checkers

* Re-org `hashify()` procedure

why:
  Syncing against block chain showed serious deficiencies which produced
  wrong hashes or simply bailed out with error.

  So all fringe cases (mainly due to deleted entries) could be integrated
  into the labelling schedule rather than handling separate fringe cases.
2023-12-04 20:39:26 +00:00

149 lines
4.5 KiB
Nim

# nimbus-eth1
# Copyright (c) 2023 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
eth/[common, trie/nibbles],
results,
"."/[aristo_desc, aristo_get]
type
Leg* = object
## For constructing a `VertexPath`
wp*: VidVtxPair ## Vertex ID and data ref
nibble*: int8 ## Next vertex selector for `Branch` (if any)
backend*: bool ## Sources from backend if `true`
Hike* = object
## Trie traversal path
root*: VertexID ## Handy for some fringe cases
legs*: seq[Leg] ## Chain of vertices and IDs
tail*: NibblesSeq ## Portion of non completed path
# ------------------------------------------------------------------------------
# Private functions
# ------------------------------------------------------------------------------
func getNibblesImpl(hike: Hike; start = 0; maxLen = high(int)): NibblesSeq =
## May be needed for partial rebuild, as well
for n in start ..< min(hike.legs.len, maxLen):
let leg = hike.legs[n]
case leg.wp.vtx.vType:
of Branch:
result = result & @[leg.nibble.byte].initNibbleRange.slice(1)
of Extension:
result = result & leg.wp.vtx.ePfx
of Leaf:
result = result & leg.wp.vtx.lPfx
# ------------------------------------------------------------------------------
# Public functions
# ------------------------------------------------------------------------------
func to*(rc: Result[Hike,(Hike,AristoError)]; T: type Hike): T =
## Extract `Hike` from either ok ot error part of argument `rc`.
if rc.isOk: rc.value else: rc.error[0]
func to*(hike: Hike; T: type NibblesSeq): T =
## Convert back
hike.getNibblesImpl() & hike.tail
func legsTo*(hike: Hike; T: type NibblesSeq): T =
## Convert back
hike.getNibblesImpl()
func legsTo*(hike: Hike; numLegs: int; T: type NibblesSeq): T =
## variant of `legsTo()`
hike.getNibblesImpl(0, numLegs)
# --------
proc hikeUp*(
path: NibblesSeq; # Partial path
root: VertexID; # Start vertex
db: AristoDbRef; # Database
): Result[Hike,(Hike,AristoError)] =
## For the argument `path`, find and return the logest possible path in the
## argument database `db`.
var hike = Hike(
root: root,
tail: path)
if not root.isValid:
return err((hike,HikeRootMissing))
if path.len == 0:
return err((hike,HikeEmptyPath))
var vid = root
while vid.isValid:
var leg = Leg(wp: VidVtxPair(vid: vid), nibble: -1)
# Fetch vertex to be checked on this lap
leg.wp.vtx = db.top.sTab.getOrVoid vid
if not leg.wp.vtx.isValid:
# Register vertex fetched from backend (if any)
let rc = db.getVtxBE vid
if rc.isErr:
break
leg.backend = true
leg.wp.vtx = rc.value
case leg.wp.vtx.vType:
of Leaf:
if hike.tail.len == hike.tail.sharedPrefixLen(leg.wp.vtx.lPfx):
# Bingo, got full path
hike.legs.add leg
hike.tail = EmptyNibbleSeq
break
return err((hike,HikeLeafTooEarly))
of Branch:
if hike.tail.len == 0:
hike.legs.add leg
return err((hike,HikeBranchTailEmpty))
let
nibble = hike.tail[0].int8
nextVid = leg.wp.vtx.bVid[nibble]
if not nextVid.isValid:
return err((hike,HikeBranchBlindEdge))
leg.nibble = nibble
hike.legs.add leg
hike.tail = hike.tail.slice(1)
vid = nextVid
of Extension:
if hike.tail.len == 0:
hike.legs.add leg
hike.tail = EmptyNibbleSeq
return err((hike,HikeExtTailEmpty)) # Well, somehow odd
if leg.wp.vtx.ePfx.len != hike.tail.sharedPrefixLen(leg.wp.vtx.ePfx):
return err((hike,HikeExtTailMismatch)) # Need to branch from here
hike.legs.add leg
hike.tail = hike.tail.slice(leg.wp.vtx.ePfx.len)
vid = leg.wp.vtx.eVid
ok hike
proc hikeUp*(lty: LeafTie; db: AristoDbRef): Result[Hike,(Hike,AristoError)] =
## Variant of `hike()`
lty.path.to(NibblesSeq).hikeUp(lty.root, db)
# ------------------------------------------------------------------------------
# End
# ------------------------------------------------------------------------------