2023-06-02 10:04:29 +00:00
|
|
|
# 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.
|
|
|
|
|
|
|
|
## Aristo DB -- Patricia Trie traversal
|
|
|
|
## ====================================
|
|
|
|
##
|
|
|
|
## This module provides tools to visit leaf vertices in a monotone order,
|
|
|
|
## increasing or decreasing. These tools are intended for
|
|
|
|
## * boundary proof verification
|
|
|
|
## * step along leaf vertices in sorted order
|
|
|
|
## * tree/trie consistency checks when debugging
|
|
|
|
##
|
|
|
|
|
|
|
|
{.push raises: [].}
|
|
|
|
|
|
|
|
import
|
|
|
|
std/tables,
|
|
|
|
eth/[common, trie/nibbles],
|
|
|
|
stew/results,
|
2023-06-12 13:48:47 +00:00
|
|
|
"."/[aristo_desc, aristo_get, aristo_hike, aristo_path]
|
2023-06-02 10:04:29 +00:00
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# Private helpers
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
proc `<=`(a, b: NibblesSeq): bool =
|
|
|
|
## Compare nibbles, different lengths are padded to the right with zeros
|
|
|
|
let abMin = min(a.len, b.len)
|
|
|
|
for n in 0 ..< abMin:
|
|
|
|
if a[n] < b[n]:
|
|
|
|
return true
|
|
|
|
if b[n] < a[n]:
|
|
|
|
return false
|
|
|
|
# otherwise a[n] == b[n]
|
|
|
|
|
|
|
|
# Assuming zero for missing entries
|
|
|
|
if b.len < a.len:
|
|
|
|
for n in abMin + 1 ..< a.len:
|
|
|
|
if 0 < a[n]:
|
|
|
|
return false
|
|
|
|
true
|
|
|
|
|
|
|
|
proc `<`(a, b: NibblesSeq): bool =
|
|
|
|
not (b <= a)
|
|
|
|
|
|
|
|
# ------------------
|
|
|
|
|
|
|
|
proc branchNibbleMin*(vtx: VertexRef; minInx: int8): int8 =
|
|
|
|
## Find the least index for an argument branch `vtx` link with index
|
|
|
|
## greater or equal the argument `nibble`.
|
|
|
|
if vtx.vType == Branch:
|
|
|
|
for n in minInx .. 15:
|
2023-06-12 13:48:47 +00:00
|
|
|
if vtx.bVid[n].isValid:
|
2023-06-02 10:04:29 +00:00
|
|
|
return n
|
|
|
|
-1
|
|
|
|
|
|
|
|
proc branchNibbleMax*(vtx: VertexRef; maxInx: int8): int8 =
|
|
|
|
## Find the greatest index for an argument branch `vtx` link with index
|
|
|
|
## less or equal the argument `nibble`.
|
|
|
|
if vtx.vType == Branch:
|
|
|
|
for n in maxInx.countDown 0:
|
2023-06-12 13:48:47 +00:00
|
|
|
if vtx.bVid[n].isValid:
|
2023-06-02 10:04:29 +00:00
|
|
|
return n
|
|
|
|
-1
|
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# Private functions
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
proc complete(
|
2023-06-02 19:21:46 +00:00
|
|
|
hike: Hike; # Partially expanded chain of vertices
|
2023-06-02 10:04:29 +00:00
|
|
|
vid: VertexID; # Start ID
|
2023-06-09 11:17:37 +00:00
|
|
|
db: AristoDb; # Database layer
|
2023-06-02 10:04:29 +00:00
|
|
|
hikeLenMax: static[int]; # Beware of loops (if any)
|
|
|
|
doLeast: static[bool]; # Direction: *least* or *most*
|
|
|
|
): Hike =
|
|
|
|
## Extend `hike` using least or last vertex without recursion.
|
|
|
|
var
|
|
|
|
vid = vid
|
|
|
|
vtx = db.getVtx vid
|
|
|
|
uHike = Hike(root: hike.root, legs: hike.legs)
|
2023-06-12 13:48:47 +00:00
|
|
|
if not vtx.isValid:
|
2023-06-02 10:04:29 +00:00
|
|
|
return Hike(error: GetVtxNotFound)
|
|
|
|
|
|
|
|
while uHike.legs.len < hikeLenMax:
|
|
|
|
var leg = Leg(wp: VidVtxPair(vid: vid, vtx: vtx), nibble: -1)
|
|
|
|
|
|
|
|
case vtx.vType:
|
|
|
|
of Leaf:
|
|
|
|
uHike.legs.add leg
|
|
|
|
return uHike # done
|
|
|
|
|
|
|
|
of Extension:
|
|
|
|
vid = vtx.eVid
|
2023-06-12 13:48:47 +00:00
|
|
|
if vid.isValid:
|
2023-06-02 10:04:29 +00:00
|
|
|
vtx = db.getVtx vid
|
2023-06-12 13:48:47 +00:00
|
|
|
if vtx.isValid:
|
2023-06-02 10:04:29 +00:00
|
|
|
uHike.legs.add leg
|
|
|
|
continue
|
|
|
|
return Hike(error: NearbyExtensionError) # Oops, no way
|
|
|
|
|
|
|
|
of Branch:
|
|
|
|
when doLeast:
|
|
|
|
leg.nibble = vtx.branchNibbleMin 0
|
|
|
|
else:
|
|
|
|
leg.nibble = vtx.branchNibbleMax 15
|
|
|
|
if 0 <= leg.nibble:
|
|
|
|
vid = vtx.bVid[leg.nibble]
|
|
|
|
vtx = db.getVtx vid
|
2023-06-12 13:48:47 +00:00
|
|
|
if vtx.isValid:
|
2023-06-02 10:04:29 +00:00
|
|
|
uHike.legs.add leg
|
|
|
|
continue
|
|
|
|
return Hike(error: NearbyBranchError) # Oops, no way
|
|
|
|
|
|
|
|
Hike(error: NearbyNestingTooDeep)
|
|
|
|
|
|
|
|
|
|
|
|
proc zeroAdjust(
|
2023-06-02 19:21:46 +00:00
|
|
|
hike: Hike; # Partially expanded chain of vertices
|
2023-06-09 11:17:37 +00:00
|
|
|
db: AristoDb; # Database layer
|
2023-06-02 10:04:29 +00:00
|
|
|
doLeast: static[bool]; # Direction: *least* or *most*
|
|
|
|
): Hike =
|
|
|
|
## Adjust empty argument path to the first node entry to the right. Ths
|
|
|
|
## applies is the argument `hike` is before the first entry in the database.
|
|
|
|
## The result is a hike which is aligned with the first entry.
|
|
|
|
proc accept(p: Hike; pfx: NibblesSeq): bool =
|
|
|
|
when doLeast:
|
|
|
|
p.tail <= pfx
|
|
|
|
else:
|
|
|
|
pfx <= p.tail
|
|
|
|
|
|
|
|
proc branchBorderNibble(w: VertexRef; n: int8): int8 =
|
|
|
|
when doLeast:
|
|
|
|
w.branchNibbleMin n
|
|
|
|
else:
|
|
|
|
w.branchNibbleMax n
|
|
|
|
|
2023-06-09 11:17:37 +00:00
|
|
|
proc toHike(pfx: NibblesSeq, root: VertexID, db: AristoDb): Hike =
|
2023-06-02 10:04:29 +00:00
|
|
|
when doLeast:
|
|
|
|
pfx.pathPfxPad(0).hikeUp(root, db)
|
|
|
|
else:
|
|
|
|
pfx.pathPfxPad(255).hikeUp(root, db)
|
|
|
|
|
|
|
|
if 0 < hike.legs.len:
|
|
|
|
result = hike
|
|
|
|
result.error = AristoError(0)
|
|
|
|
return
|
|
|
|
|
|
|
|
let root = db.getVtx hike.root
|
2023-06-12 13:48:47 +00:00
|
|
|
if root.isValid:
|
2023-06-02 10:04:29 +00:00
|
|
|
block fail:
|
|
|
|
var pfx: NibblesSeq
|
|
|
|
case root.vType:
|
|
|
|
of Branch:
|
|
|
|
# Find first non-dangling link and assign it
|
|
|
|
if hike.tail.len == 0:
|
|
|
|
break fail
|
|
|
|
|
|
|
|
let n = root.branchBorderNibble hike.tail[0].int8
|
|
|
|
if n < 0:
|
|
|
|
# Before or after the database range
|
|
|
|
return Hike(error: NearbyBeyondRange)
|
|
|
|
pfx = @[n.byte].initNibbleRange.slice(1)
|
|
|
|
|
|
|
|
of Extension:
|
|
|
|
let ePfx = root.ePfx
|
|
|
|
# Must be followed by a branch node
|
|
|
|
if hike.tail.len < 2 or not hike.accept(ePfx):
|
|
|
|
break fail
|
|
|
|
let vtx = db.getVtx root.eVid
|
2023-06-12 13:48:47 +00:00
|
|
|
if not vtx.isValid:
|
2023-06-02 10:04:29 +00:00
|
|
|
break fail
|
|
|
|
let ePfxLen = ePfx.len
|
|
|
|
if hike.tail.len <= ePfxLen:
|
|
|
|
return Hike(error: NearbyPathTailInxOverflow)
|
|
|
|
let tailPfx = hike.tail.slice(0,ePfxLen)
|
|
|
|
when doLeast:
|
|
|
|
if ePfx < tailPfx:
|
|
|
|
return Hike(error: NearbyBeyondRange)
|
|
|
|
else:
|
|
|
|
if tailPfx < ePfx:
|
|
|
|
return Hike(error: NearbyBeyondRange)
|
|
|
|
pfx = ePfx
|
|
|
|
|
|
|
|
of Leaf:
|
|
|
|
pfx = root.lPfx
|
|
|
|
if not hike.accept(pfx):
|
|
|
|
# Before or after the database range
|
|
|
|
return Hike(error: NearbyBeyondRange)
|
|
|
|
|
|
|
|
var newHike = pfx.toHike(hike.root, db)
|
|
|
|
if 0 < newHike.legs.len:
|
|
|
|
newHike.error = AristoError(0)
|
|
|
|
return newHike
|
|
|
|
|
|
|
|
Hike(error: NearbyEmptyHike)
|
|
|
|
|
|
|
|
|
|
|
|
proc finalise(
|
2023-06-02 19:21:46 +00:00
|
|
|
hike: Hike; # Partially expanded chain of vertices
|
2023-06-09 11:17:37 +00:00
|
|
|
db: AristoDb; # Database layer
|
2023-06-02 19:21:46 +00:00
|
|
|
moveRight: static[bool]; # Direction of next vertex
|
2023-06-02 10:04:29 +00:00
|
|
|
): Hike =
|
|
|
|
## Handle some pathological cases after main processing failed
|
|
|
|
proc beyond(p: Hike; pfx: NibblesSeq): bool =
|
|
|
|
when moveRight:
|
|
|
|
pfx < p.tail
|
|
|
|
else:
|
|
|
|
p.tail < pfx
|
|
|
|
|
|
|
|
proc branchBorderNibble(w: VertexRef): int8 =
|
|
|
|
when moveRight:
|
|
|
|
w.branchNibbleMax 15
|
|
|
|
else:
|
|
|
|
w.branchNibbleMin 0
|
|
|
|
|
|
|
|
# Just for completeness (this case should have been handled, already)
|
|
|
|
if hike.legs.len == 0:
|
|
|
|
return Hike(error: NearbyEmptyHike)
|
|
|
|
|
|
|
|
# Check whether the path is beyond the database range
|
|
|
|
if 0 < hike.tail.len: # nothing to compare against, otherwise
|
|
|
|
let top = hike.legs[^1]
|
|
|
|
|
|
|
|
# Note that only a `Branch` nodes has a non-zero nibble
|
|
|
|
if 0 <= top.nibble and top.nibble == top.wp.vtx.branchBorderNibble:
|
|
|
|
# Check the following up node
|
|
|
|
let vtx = db.getVtx top.wp.vtx.bVid[top.nibble]
|
2023-06-12 13:48:47 +00:00
|
|
|
if not vtx.isValid:
|
2023-06-02 10:04:29 +00:00
|
|
|
return Hike(error: NearbyDanglingLink)
|
|
|
|
|
|
|
|
var pfx: NibblesSeq
|
|
|
|
case vtx.vType:
|
|
|
|
of Leaf:
|
|
|
|
pfx = vtx.lPfx
|
|
|
|
of Extension:
|
|
|
|
pfx = vtx.ePfx
|
|
|
|
of Branch:
|
|
|
|
pfx = @[vtx.branchBorderNibble.byte].initNibbleRange.slice(1)
|
|
|
|
if hike.beyond pfx:
|
|
|
|
return Hike(error: NearbyBeyondRange)
|
|
|
|
|
|
|
|
# Pathological cases
|
|
|
|
# * finalise right: nfffff.. for n < f or
|
|
|
|
# * finalise left: n00000.. for 0 < n
|
|
|
|
if hike.legs[0].wp.vtx.vType == Branch or
|
|
|
|
(1 < hike.legs.len and hike.legs[1].wp.vtx.vType == Branch):
|
|
|
|
return Hike(error: NearbyFailed) # no more nodes
|
|
|
|
|
|
|
|
Hike(error: NearbyUnexpectedVtx) # error
|
|
|
|
|
|
|
|
|
|
|
|
proc nearbyNext(
|
2023-06-02 19:21:46 +00:00
|
|
|
hike: Hike; # Partially expanded chain of vertices
|
2023-06-09 11:17:37 +00:00
|
|
|
db: AristoDb; # Database layer
|
2023-06-02 10:04:29 +00:00
|
|
|
hikeLenMax: static[int]; # Beware of loops (if any)
|
2023-06-02 19:21:46 +00:00
|
|
|
moveRight: static[bool]; # Direction of next vertex
|
2023-06-02 10:04:29 +00:00
|
|
|
): Hike =
|
|
|
|
## Unified implementation of `nearbyRight()` and `nearbyLeft()`.
|
|
|
|
proc accept(nibble: int8): bool =
|
|
|
|
## Accept `nibble` unless on boundaty dependent on `moveRight`
|
|
|
|
when moveRight:
|
|
|
|
nibble < 15
|
|
|
|
else:
|
|
|
|
0 < nibble
|
|
|
|
|
|
|
|
proc accept(p: Hike; pfx: NibblesSeq): bool =
|
|
|
|
when moveRight:
|
|
|
|
p.tail <= pfx
|
|
|
|
else:
|
|
|
|
pfx <= p.tail
|
|
|
|
|
|
|
|
proc branchNibbleNext(w: VertexRef; n: int8): int8 =
|
|
|
|
when moveRight:
|
|
|
|
w.branchNibbleMin(n + 1)
|
|
|
|
else:
|
|
|
|
w.branchNibbleMax(n - 1)
|
|
|
|
|
|
|
|
# Some easy cases
|
|
|
|
var hike = hike.zeroAdjust(db, doLeast=moveRight)
|
|
|
|
if hike.error != AristoError(0):
|
|
|
|
return hike
|
|
|
|
|
|
|
|
if hike.legs[^1].wp.vtx.vType == Extension:
|
|
|
|
let vid = hike.legs[^1].wp.vtx.eVid
|
|
|
|
return hike.complete(vid, db, hikeLenMax, doLeast=moveRight)
|
|
|
|
|
|
|
|
var
|
|
|
|
uHike = hike
|
|
|
|
start = true
|
|
|
|
while 0 < uHike.legs.len:
|
|
|
|
let top = uHike.legs[^1]
|
|
|
|
case top.wp.vtx.vType:
|
|
|
|
of Leaf:
|
|
|
|
return uHike
|
|
|
|
of Branch:
|
|
|
|
if top.nibble < 0 or uHike.tail.len == 0:
|
|
|
|
return Hike(error: NearbyUnexpectedVtx)
|
|
|
|
of Extension:
|
|
|
|
uHike.tail = top.wp.vtx.ePfx & uHike.tail
|
|
|
|
uHike.legs.setLen(uHike.legs.len - 1)
|
|
|
|
continue
|
|
|
|
|
|
|
|
var
|
|
|
|
step = top
|
|
|
|
let
|
|
|
|
uHikeLen = uHike.legs.len # in case of backtracking
|
|
|
|
uHikeTail = uHike.tail # in case of backtracking
|
|
|
|
|
|
|
|
# Look ahead checking next node
|
|
|
|
if start:
|
|
|
|
let vid = top.wp.vtx.bVid[top.nibble]
|
2023-06-12 13:48:47 +00:00
|
|
|
if not vid.isValid:
|
2023-06-02 10:04:29 +00:00
|
|
|
return Hike(error: NearbyDanglingLink) # error
|
|
|
|
|
|
|
|
let vtx = db.getVtx vid
|
2023-06-12 13:48:47 +00:00
|
|
|
if not vtx.isValid:
|
2023-06-02 10:04:29 +00:00
|
|
|
return Hike(error: GetVtxNotFound) # error
|
|
|
|
|
|
|
|
case vtx.vType
|
|
|
|
of Leaf:
|
|
|
|
if uHike.accept vtx.lPfx:
|
|
|
|
return uHike.complete(vid, db, hikeLenMax, doLeast=moveRight)
|
|
|
|
of Extension:
|
|
|
|
if uHike.accept vtx.ePfx:
|
|
|
|
return uHike.complete(vid, db, hikeLenMax, doLeast=moveRight)
|
|
|
|
of Branch:
|
|
|
|
let nibble = uHike.tail[0].int8
|
|
|
|
if start and accept nibble:
|
|
|
|
# Step down and complete with a branch link on the child node
|
|
|
|
step = Leg(wp: VidVtxPair(vid: vid, vtx: vtx), nibble: nibble)
|
|
|
|
uHike.legs.add step
|
|
|
|
|
|
|
|
# Find the next item to the right/left of the current top entry
|
|
|
|
let n = step.wp.vtx.branchNibbleNext step.nibble
|
|
|
|
if 0 <= n:
|
|
|
|
uHike.legs[^1].nibble = n
|
|
|
|
return uHike.complete(
|
|
|
|
step.wp.vtx.bVid[n], db, hikeLenMax, doLeast=moveRight)
|
|
|
|
|
|
|
|
if start:
|
|
|
|
# Retry without look ahead
|
|
|
|
start = false
|
|
|
|
|
|
|
|
# Restore `uPath` (pop temporary extra step)
|
|
|
|
if uHikeLen < uHike.legs.len:
|
|
|
|
uHike.legs.setLen(uHikeLen)
|
|
|
|
uHike.tail = uHikeTail
|
|
|
|
else:
|
|
|
|
# Pop current `Branch` node on top and append nibble to `tail`
|
|
|
|
uHike.tail = @[top.nibble.byte].initNibbleRange.slice(1) & uHike.tail
|
|
|
|
uHike.legs.setLen(uHike.legs.len - 1)
|
|
|
|
# End while
|
|
|
|
|
|
|
|
# Handle some pathological cases
|
|
|
|
return hike.finalise(db, moveRight)
|
|
|
|
|
|
|
|
|
|
|
|
proc nearbyNext(
|
2023-06-12 13:48:47 +00:00
|
|
|
lty: LeafTie; # Some `Patricia Trie` path
|
2023-06-09 11:17:37 +00:00
|
|
|
db: AristoDb; # Database layer
|
2023-06-02 10:04:29 +00:00
|
|
|
hikeLenMax: static[int]; # Beware of loops (if any)
|
2023-06-09 11:17:37 +00:00
|
|
|
moveRight:static[bool]; # Direction of next vertex
|
2023-06-02 10:04:29 +00:00
|
|
|
): Result[NodeTag,AristoError] =
|
|
|
|
## Variant of `nearbyNext()`, convenience wrapper
|
2023-06-12 13:48:47 +00:00
|
|
|
let hike = lty.hikeUp(db).nearbyNext(db, hikeLenMax, moveRight)
|
2023-06-02 10:04:29 +00:00
|
|
|
if hike.error != AristoError(0):
|
|
|
|
return err(hike.error)
|
|
|
|
|
|
|
|
if 0 < hike.legs.len and hike.legs[^1].wp.vtx.vType == Leaf:
|
|
|
|
let rc = hike.legsTo(NibblesSeq).pathToKey
|
|
|
|
if rc.isOk:
|
|
|
|
return ok rc.value.to(NodeTag)
|
|
|
|
return err(rc.error)
|
|
|
|
|
|
|
|
err(NearbyLeafExpected)
|
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# Public functions, moving and right boundary proof
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
proc nearbyRight*(
|
2023-06-02 19:21:46 +00:00
|
|
|
hike: Hike; # Partially expanded chain of vertices
|
2023-06-09 11:17:37 +00:00
|
|
|
db: AristoDb; # Database layer
|
2023-06-02 10:04:29 +00:00
|
|
|
): Hike =
|
|
|
|
## Extends the maximally extended argument nodes `hike` to the right (i.e.
|
|
|
|
## with non-decreasing path value). This function does not backtrack if
|
|
|
|
## there are dangling links in between. It will return an error in that case.
|
|
|
|
##
|
|
|
|
## If there is no more leaf node to the right of the argument `hike`, the
|
|
|
|
## particular error code `NearbyBeyondRange` is returned.
|
|
|
|
##
|
|
|
|
## This code is intended to be used for verifying a left-bound proof to
|
|
|
|
## verify that there is no leaf node *right* of a boundary path value.
|
|
|
|
hike.nearbyNext(db, 64, moveRight=true)
|
|
|
|
|
|
|
|
proc nearbyRight*(
|
2023-06-12 13:48:47 +00:00
|
|
|
lty: LeafTie; # Some `Patricia Trie` path
|
2023-06-09 11:17:37 +00:00
|
|
|
db: AristoDb; # Database layer
|
2023-06-12 13:48:47 +00:00
|
|
|
): Result[LeafTie,AristoError] =
|
2023-06-02 10:04:29 +00:00
|
|
|
## Variant of `nearbyRight()` working with a `NodeTag` argument instead
|
|
|
|
## of a `Hike`.
|
2023-06-12 13:48:47 +00:00
|
|
|
let rc = lty.nearbyNext(db, 64, moveRight=true)
|
2023-06-09 11:17:37 +00:00
|
|
|
if rc.isErr:
|
|
|
|
return err(rc.error)
|
2023-06-12 13:48:47 +00:00
|
|
|
ok LeafTie(root: lty.root, path: rc.value)
|
2023-06-02 10:04:29 +00:00
|
|
|
|
|
|
|
proc nearbyLeft*(
|
2023-06-02 19:21:46 +00:00
|
|
|
hike: Hike; # Partially expanded chain of vertices
|
2023-06-09 11:17:37 +00:00
|
|
|
db: AristoDb; # Database layer
|
2023-06-02 10:04:29 +00:00
|
|
|
): Hike =
|
|
|
|
## Similar to `nearbyRight()`.
|
|
|
|
##
|
|
|
|
## This code is intended to be used for verifying a right-bound proof to
|
|
|
|
## verify that there is no leaf node *left* to a boundary path value.
|
|
|
|
hike.nearbyNext(db, 64, moveRight=false)
|
|
|
|
|
|
|
|
proc nearbyLeft*(
|
2023-06-12 13:48:47 +00:00
|
|
|
lty: LeafTie; # Some `Patricia Trie` path
|
2023-06-09 11:17:37 +00:00
|
|
|
db: AristoDb; # Database layer
|
2023-06-12 13:48:47 +00:00
|
|
|
): Result[LeafTie,AristoError] =
|
2023-06-02 10:04:29 +00:00
|
|
|
## Similar to `nearbyRight()` for `NodeTag` argument instead
|
|
|
|
## of a `Hike`.
|
2023-06-12 13:48:47 +00:00
|
|
|
let rc = lty.nearbyNext(db, 64, moveRight=false)
|
2023-06-09 11:17:37 +00:00
|
|
|
if rc.isErr:
|
|
|
|
return err(rc.error)
|
2023-06-12 13:48:47 +00:00
|
|
|
ok LeafTie(root: lty.root, path: rc.value)
|
2023-06-02 10:04:29 +00:00
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# Public debugging helpers
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
proc nearbyRightMissing*(
|
2023-06-02 19:21:46 +00:00
|
|
|
hike: Hike; # Partially expanded chain of vertices
|
2023-06-09 11:17:37 +00:00
|
|
|
db: AristoDb; # Database layer
|
2023-06-02 10:04:29 +00:00
|
|
|
): Result[bool,AristoError] =
|
|
|
|
## Returns `true` if the maximally extended argument nodes `hike` is the
|
|
|
|
## rightmost on the hexary trie database. It verifies that there is no more
|
|
|
|
## leaf entry to the right of the argument `hike`. This function is an
|
|
|
|
## an alternative to
|
|
|
|
## ::
|
|
|
|
## let rc = path.nearbyRight(db)
|
|
|
|
## if rc.isOk:
|
|
|
|
## # not at the end => false
|
|
|
|
## ...
|
|
|
|
## elif rc.error != NearbyBeyondRange:
|
|
|
|
## # problem with database => error
|
|
|
|
## ...
|
|
|
|
## else:
|
|
|
|
## # no nore nodes => true
|
|
|
|
## ...
|
|
|
|
## and is intended mainly for debugging.
|
|
|
|
if hike.legs.len == 0:
|
|
|
|
return err(NearbyEmptyHike)
|
|
|
|
if 0 < hike.tail.len:
|
|
|
|
return err(NearbyPathTailUnexpected)
|
|
|
|
|
|
|
|
let top = hike.legs[^1]
|
|
|
|
if top.wp.vtx.vType != Branch or top.nibble < 0:
|
|
|
|
return err(NearbyBranchError)
|
|
|
|
|
|
|
|
let vid = top.wp.vtx.bVid[top.nibble]
|
2023-06-12 13:48:47 +00:00
|
|
|
if not vid.isValid:
|
2023-06-02 10:04:29 +00:00
|
|
|
return err(NearbyDanglingLink) # error
|
|
|
|
|
|
|
|
let vtx = db.getVtx vid
|
2023-06-12 13:48:47 +00:00
|
|
|
if not vtx.isValid:
|
2023-06-02 10:04:29 +00:00
|
|
|
return err(GetVtxNotFound) # error
|
|
|
|
|
|
|
|
case vtx.vType
|
|
|
|
of Leaf:
|
|
|
|
return ok(vtx.lPfx < hike.tail)
|
|
|
|
of Extension:
|
|
|
|
return ok(vtx.ePfx < hike.tail)
|
|
|
|
of Branch:
|
|
|
|
return ok(vtx.branchNibbleMin(hike.tail[0].int8) < 0)
|
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# End
|
|
|
|
# ------------------------------------------------------------------------------
|