mirror of
https://github.com/status-im/nimbus-eth1.git
synced 2025-01-15 23:04:34 +00:00
657379f484
* 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.
149 lines
4.5 KiB
Nim
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
|
|
# ------------------------------------------------------------------------------
|