parent
443c6d1f8e
commit
b23795ab39
|
@ -61,27 +61,14 @@ proc checkTopStrict*(
|
|||
proc checkTopProofMode*(
|
||||
db: AristoDbRef; # Database, top layer
|
||||
): Result[void,(VertexID,AristoError)] =
|
||||
if 0 < db.pPrf.len:
|
||||
for vid in db.pPrf:
|
||||
let vtx = db.layersGetVtxOrVoid vid
|
||||
for (vid,key) in db.layersWalkKey:
|
||||
if key.isValid: # Otherwise to be deleted
|
||||
let vtx = db.getVtx vid
|
||||
if vtx.isValid:
|
||||
let node = vtx.toNode(db).valueOr:
|
||||
return err((vid,CheckRlxVtxIncomplete))
|
||||
|
||||
let key = db.layersGetKeyOrVoid vid
|
||||
if not key.isValid:
|
||||
return err((vid,CheckRlxVtxKeyMissing))
|
||||
continue
|
||||
if key != node.digestTo(HashKey):
|
||||
return err((vid,CheckRlxVtxKeyMismatch))
|
||||
else:
|
||||
for (vid,key) in db.layersWalkKey:
|
||||
if key.isValid: # Otherwise to be deleted
|
||||
let vtx = db.getVtx vid
|
||||
if vtx.isValid:
|
||||
let node = vtx.toNode(db).valueOr:
|
||||
continue
|
||||
if key != node.digestTo(HashKey):
|
||||
return err((vid,CheckRlxVtxKeyMismatch))
|
||||
ok()
|
||||
|
||||
|
||||
|
@ -145,9 +132,6 @@ proc checkTopCommon*(
|
|||
if kMapNilCount != 0 and kMapNilCount < nNilVtx:
|
||||
return err((VertexID(0),CheckAnyVtxEmptyKeyMismatch))
|
||||
|
||||
for vid in db.pPrf:
|
||||
if db.layersGetKey(vid).isErr:
|
||||
return err((vid,CheckAnyVtxLockWithoutKey))
|
||||
ok()
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
|
|
@ -86,18 +86,6 @@ proc stripZeros(a: string; toExp = false): string =
|
|||
elif 2 < n:
|
||||
result &= "↑" & $n
|
||||
|
||||
proc vidCode(key: HashKey, db: AristoDbRef): uint64 =
|
||||
if key.isValid:
|
||||
block:
|
||||
let vid = db.layerGetProofVidOrVoid key
|
||||
if vid.isValid:
|
||||
db.xMap.add(key, vid)
|
||||
return vid.uint64
|
||||
block:
|
||||
let vids = db.xMap.getOrVoid key
|
||||
if vids.isValid:
|
||||
return vids.sortedKeys[0].uint64
|
||||
|
||||
# ---------------------
|
||||
|
||||
proc ppKeyOk(
|
||||
|
@ -106,11 +94,6 @@ proc ppKeyOk(
|
|||
vid: VertexID;
|
||||
): string =
|
||||
if key.isValid and vid.isValid:
|
||||
block:
|
||||
let vid = db.layerGetProofVidOrVoid key
|
||||
if vid.isValid:
|
||||
db.xMap.add(key, vid)
|
||||
return
|
||||
block:
|
||||
let vids = db.xMap.getOrVoid key
|
||||
if vids.isValid:
|
||||
|
@ -162,11 +145,6 @@ proc ppVidList(vLst: openArray[VertexID]): string =
|
|||
|
||||
proc ppKey(key: HashKey; db: AristoDbRef; pfx = true): string =
|
||||
proc getVids(): tuple[vids: HashSet[VertexID], xMapTag: string] =
|
||||
block:
|
||||
let vid = db.layerGetProofVidOrVoid key
|
||||
if vid.isValid:
|
||||
db.xMap.add(key, vid)
|
||||
return (@[vid].toHashSet, "")
|
||||
block:
|
||||
let vids = db.xMap.getOrVoid key
|
||||
if vids.isValid:
|
||||
|
@ -221,7 +199,7 @@ proc ppVtx(nd: VertexRef, db: AristoDbRef, vid: VertexID): string =
|
|||
if not nd.isValid:
|
||||
result = "ø"
|
||||
else:
|
||||
if not vid.isValid or vid in db.pPrf:
|
||||
if not vid.isValid:
|
||||
result = ["L(", "X(", "B("][nd.vType.ord]
|
||||
elif db.layersGetKey(vid).isOk:
|
||||
result = ["l(", "x(", "b("][nd.vType.ord]
|
||||
|
@ -250,21 +228,6 @@ proc ppSTab(
|
|||
.mapIt("(" & it[0].ppVid & "," & it[1].ppVtx(db,it[0]) & ")")
|
||||
.join(indent.toPfx(1)) & "}"
|
||||
|
||||
proc ppPPrf(pPrf: HashSet[VertexID]): string =
|
||||
result = "{"
|
||||
if 0 < pPrf.len:
|
||||
let isr = IntervalSetRef[VertexID,uint64].init()
|
||||
for w in pPrf:
|
||||
doAssert isr.merge(w,w) == 1
|
||||
for iv in isr.increasing():
|
||||
result &= iv.minPt.ppVid
|
||||
if 1 < iv.len:
|
||||
result &= ".. " & iv.maxPt.ppVid
|
||||
result &= ", "
|
||||
result.setlen(result.len - 2)
|
||||
#result &= pPrf.sortedKeys.mapIt(it.ppVid).join(",")
|
||||
result &= "}"
|
||||
|
||||
proc ppXMap*(
|
||||
db: AristoDbRef;
|
||||
kMap: Table[VertexID,HashKey];
|
||||
|
@ -341,10 +304,7 @@ proc ppXMap*(
|
|||
for vid in kMap.sortedKeys:
|
||||
let key = kMap.getOrVoid vid
|
||||
if key.isValid:
|
||||
cache.add (vid.uint64, key.vidCode(db), vid in multi)
|
||||
let vids = db.xMap.getOrVoid key
|
||||
if (0 < vids.len and vid notin vids) or key.len < 32:
|
||||
cache[^1][2] = true
|
||||
discard # TODO obsolete - clean up?
|
||||
else:
|
||||
cache.add (vid.uint64, 0u64, true)
|
||||
|
||||
|
@ -377,16 +337,6 @@ proc ppXMap*(
|
|||
else:
|
||||
result &= "}"
|
||||
|
||||
proc ppFRpp(
|
||||
fRpp: Table[HashKey,VertexID];
|
||||
db: AristoDbRef;
|
||||
indent = 4;
|
||||
): string =
|
||||
let
|
||||
xMap = fRpp.pairs.toSeq.mapIt((it[1],it[0])).toTable
|
||||
xStr = db.ppXMap(xMap, indent)
|
||||
"<" & xStr[1..^2] & ">"
|
||||
|
||||
proc ppFilter(
|
||||
fl: LayerDeltaRef;
|
||||
db: AristoDbRef;
|
||||
|
@ -468,14 +418,12 @@ proc ppLayer(
|
|||
vTopOk: bool;
|
||||
sTabOk: bool;
|
||||
kMapOk: bool;
|
||||
pPrfOk: bool;
|
||||
fRppOk: bool;
|
||||
indent = 4;
|
||||
): string =
|
||||
let
|
||||
pfx1 = indent.toPfx(1)
|
||||
pfx2 = indent.toPfx(2)
|
||||
nOKs = vTopOk.ord + sTabOk.ord + kMapOk.ord + pPrfOk.ord + fRppOk.ord
|
||||
nOKs = vTopOk.ord + sTabOk.ord + kMapOk.ord
|
||||
tagOk = 1 < nOKs
|
||||
var
|
||||
pfy = ""
|
||||
|
@ -510,16 +458,6 @@ proc ppLayer(
|
|||
info = "kMap(" & lInf & ")"
|
||||
result &= info.doPrefix(0 < tLen + uLen)
|
||||
result &= db.ppXMap(layer.delta.kMap, indent+2)
|
||||
if pPrfOk:
|
||||
let
|
||||
tLen = layer.final.pPrf.len
|
||||
info = "pPrf(" & $tLen & ")"
|
||||
result &= info.doPrefix(0 < tLen) & layer.final.pPrf.ppPPrf
|
||||
if fRppOk:
|
||||
let
|
||||
tLen = layer.final.fRpp.len
|
||||
info = "fRpp(" & $tLen & ")"
|
||||
result &= info.doPrefix(0 < tLen) & layer.final.fRpp.ppFRpp(db,indent+2)
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Public functions
|
||||
|
@ -610,9 +548,6 @@ proc pp*(
|
|||
): string =
|
||||
sTab.ppSTab(db.orDefault)
|
||||
|
||||
proc pp*(pPrf: HashSet[VertexID]): string =
|
||||
pPrf.ppPPrf
|
||||
|
||||
proc pp*(leg: Leg; db = AristoDbRef(nil)): string =
|
||||
let db = db.orDefault()
|
||||
result = "(" & leg.wp.vid.ppVid & ","
|
||||
|
@ -669,7 +604,7 @@ proc pp*(
|
|||
indent = 4;
|
||||
): string =
|
||||
layer.ppLayer(
|
||||
db, vTopOk=true, sTabOk=true, kMapOk=true, pPrfOk=true, fRppOk=true)
|
||||
db, vTopOk=true, sTabOk=true, kMapOk=true)
|
||||
|
||||
proc pp*(
|
||||
layer: LayerRef;
|
||||
|
@ -678,7 +613,7 @@ proc pp*(
|
|||
indent = 4;
|
||||
): string =
|
||||
layer.ppLayer(
|
||||
db, vTopOk=true, sTabOk=xTabOk, kMapOk=true, pPrfOk=true, fRppOk=true)
|
||||
db, vTopOk=true, sTabOk=xTabOk, kMapOk=true)
|
||||
|
||||
proc pp*(
|
||||
layer: LayerRef;
|
||||
|
@ -689,7 +624,7 @@ proc pp*(
|
|||
indent = 4;
|
||||
): string =
|
||||
layer.ppLayer(
|
||||
db, vTopOk=other, sTabOk=xTabOk, kMapOk=kMapOk, pPrfOk=other, fRppOk=other)
|
||||
db, vTopOk=other, sTabOk=xTabOk, kMapOk=kMapOk)
|
||||
|
||||
|
||||
proc pp*(
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
{.push raises: [].}
|
||||
|
||||
import
|
||||
std/[sets, typetraits],
|
||||
std/typetraits,
|
||||
eth/common,
|
||||
results,
|
||||
"."/[aristo_desc, aristo_fetch, aristo_get, aristo_hike, aristo_layers,
|
||||
|
@ -274,8 +274,6 @@ proc deleteImpl(
|
|||
let lf = hike.legs[^1].wp
|
||||
if lf.vtx.vType != Leaf:
|
||||
return err((lf.vid,DelLeafExpexted))
|
||||
if lf.vid in db.pPrf:
|
||||
return err((lf.vid, DelLeafLocked))
|
||||
|
||||
db.disposeOfVtx(hike.root, lf.vid)
|
||||
|
||||
|
@ -295,8 +293,6 @@ proc deleteImpl(
|
|||
# Clear all Merkle hash keys up to the root key
|
||||
for n in 0 .. hike.legs.len - 2:
|
||||
let vid = hike.legs[n].wp.vid
|
||||
if vid in db.top.final.pPrf:
|
||||
return err((vid, DelBranchLocked))
|
||||
db.layersResKey(hike.root, vid)
|
||||
|
||||
let nibble = block:
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
{.push raises: [].}
|
||||
|
||||
import
|
||||
std/[hashes, sets, tables],
|
||||
std/[hashes, tables],
|
||||
eth/common,
|
||||
"."/[desc_error, desc_identifiers]
|
||||
|
||||
|
@ -114,23 +114,11 @@ type
|
|||
|
||||
accSids*: Table[Hash256, VertexID] ## Account path -> stoID
|
||||
|
||||
LayerFinalRef* = ref object
|
||||
## Final tables fully supersede tables on lower layers when stacked as a
|
||||
## whole. Missing entries on a higher layers are the final state (for the
|
||||
## the top layer version of the table.)
|
||||
##
|
||||
## These structures are used for tables which are typically smaller then
|
||||
## the ones on the `LayerDelta` object.
|
||||
##
|
||||
pPrf*: HashSet[VertexID] ## Locked vertices (proof nodes)
|
||||
fRpp*: Table[HashKey,VertexID] ## Key lookup for `pPrf[]` (proof nodes)
|
||||
|
||||
LayerRef* = ref LayerObj
|
||||
LayerObj* = object
|
||||
## Hexary trie database layer structures. Any layer holds the full
|
||||
## change relative to the backend.
|
||||
delta*: LayerDeltaRef ## Most structural tables held as deltas
|
||||
final*: LayerFinalRef ## Stored as latest version
|
||||
txUid*: uint ## Transaction identifier if positive
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
@ -139,8 +127,7 @@ type
|
|||
|
||||
func init*(T: type LayerRef): T =
|
||||
## Constructor, returns empty layer
|
||||
T(delta: LayerDeltaRef(),
|
||||
final: LayerFinalRef())
|
||||
T(delta: LayerDeltaRef())
|
||||
|
||||
func hash*(node: NodeRef): Hash =
|
||||
## Table/KeyedQueue/HashSet mixin
|
||||
|
@ -271,12 +258,6 @@ func dup*(node: NodeRef): NodeRef =
|
|||
bVid: node.bVid,
|
||||
key: node.key)
|
||||
|
||||
func dup*(final: LayerFinalRef): LayerFinalRef =
|
||||
## Duplicate final layer.
|
||||
LayerFinalRef(
|
||||
pPrf: final.pPrf,
|
||||
fRpp: final.fRpp)
|
||||
|
||||
func dup*(wp: VidVtxPair): VidVtxPair =
|
||||
## Safe copy of `wp` argument
|
||||
VidVtxPair(
|
||||
|
|
|
@ -52,8 +52,7 @@ proc newAristoRdbDbRef(
|
|||
rc.value
|
||||
ok((AristoDbRef(
|
||||
top: LayerRef(
|
||||
delta: LayerDeltaRef(vTop: vTop),
|
||||
final: LayerFinalRef()),
|
||||
delta: LayerDeltaRef(vTop: vTop)),
|
||||
backend: be), oCfs))
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
|
|
@ -29,9 +29,6 @@ func dup(sTab: Table[VertexID,VertexRef]): Table[VertexID,VertexRef] =
|
|||
# Public getters: lazy value lookup for read only versions
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
func pPrf*(db: AristoDbRef): lent HashSet[VertexID] =
|
||||
db.top.final.pPrf
|
||||
|
||||
func vTop*(db: AristoDbRef): VertexID =
|
||||
db.top.delta.vTop
|
||||
|
||||
|
@ -94,20 +91,6 @@ func layersGetKeyOrVoid*(db: AristoDbRef; vid: VertexID): HashKey =
|
|||
## Simplified version of `layersGetKey()`
|
||||
db.layersGetKey(vid).valueOr: VOID_HASH_KEY
|
||||
|
||||
|
||||
func layerGetProofKeyOrVoid*(db: AristoDbRef; vid: VertexID): HashKey =
|
||||
## Get the hash key of a proof node if it was registered as such.
|
||||
if vid in db.top.final.pPrf:
|
||||
db.top.delta.kMap.getOrVoid vid
|
||||
else:
|
||||
VOID_HASH_KEY
|
||||
|
||||
func layerGetProofVidOrVoid*(db: AristoDbRef; key: HashKey): VertexID =
|
||||
## Reverse look up for a registered proof node or a link key for such a
|
||||
## node. The vertex for a returned vertex ID might not exist if the
|
||||
## argument `key` refers to a link key of a registered proof node.
|
||||
db.top.final.fRpp.getOrVoid key
|
||||
|
||||
func layersGetStoID*(db: AristoDbRef; accPath: Hash256): Opt[VertexID] =
|
||||
db.top.delta.accSids.withValue(accPath, item):
|
||||
return Opt.some(item[])
|
||||
|
@ -156,27 +139,6 @@ func layersResKey*(db: AristoDbRef; root: VertexID; vid: VertexID) =
|
|||
## equivalent of a delete function.
|
||||
db.layersPutKey(root, vid, VOID_HASH_KEY)
|
||||
|
||||
|
||||
func layersPutProof*(db: AristoDbRef; vid: VertexID; key: HashKey) =
|
||||
## Register a link key of a proof node.
|
||||
let lKey = db.layersGetKeyOrVoid vid
|
||||
if not lKey.isValid or lKey != key:
|
||||
db.top.delta.kMap[vid] = key
|
||||
db.top.final.fRpp[key] = vid
|
||||
|
||||
func layersPutProof*(
|
||||
db: AristoDbRef;
|
||||
vid: VertexID;
|
||||
key: HashKey;
|
||||
vtx: VertexRef;
|
||||
) =
|
||||
## Register a full proof node (not only a link key.)
|
||||
let lVtx = db.layersGetVtxOrVoid vid
|
||||
if not lVtx.isValid or lVtx != vtx:
|
||||
db.top.delta.sTab[vid] = vtx
|
||||
db.top.final.pPrf.incl vid
|
||||
db.layersPutProof(vid, key)
|
||||
|
||||
func layersPutStoID*(db: AristoDbRef; accPath: Hash256; stoID: VertexID) =
|
||||
db.top.delta.accSids[accPath] = stoID
|
||||
|
||||
|
@ -188,7 +150,6 @@ func layersMergeOnto*(src: LayerRef; trg: var LayerObj) =
|
|||
## Merges the argument `src` into the argument `trg` and returns `trg`. For
|
||||
## the result layer, the `txUid` value set to `0`.
|
||||
##
|
||||
trg.final = src.final
|
||||
trg.txUid = 0
|
||||
|
||||
for (vid,vtx) in src.delta.sTab.pairs:
|
||||
|
@ -209,7 +170,6 @@ func layersCc*(db: AristoDbRef; level = high(int)): LayerRef =
|
|||
|
||||
# Set up initial layer (bottom layer)
|
||||
result = LayerRef(
|
||||
final: layers[^1].final.dup, # Pre-merged/final values
|
||||
delta: LayerDeltaRef(
|
||||
sTab: layers[0].delta.sTab.dup, # explicit dup for ref values
|
||||
kMap: layers[0].delta.kMap,
|
||||
|
|
|
@ -29,10 +29,7 @@ import
|
|||
eth/common,
|
||||
results,
|
||||
"."/[aristo_desc, aristo_fetch, aristo_layers, aristo_utils, aristo_vid],
|
||||
./aristo_merge/[merge_payload_helper, merge_proof]
|
||||
|
||||
export
|
||||
merge_proof
|
||||
./aristo_merge/merge_payload_helper
|
||||
|
||||
const
|
||||
MergeNoAction = {MergeLeafPathCachedAlready, MergeLeafPathOnBackendAlready}
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
{.push raises: [].}
|
||||
|
||||
import
|
||||
std/[sets, typetraits],
|
||||
std/typetraits,
|
||||
eth/common,
|
||||
results,
|
||||
".."/[aristo_desc, aristo_get, aristo_hike, aristo_layers, aristo_vid]
|
||||
|
@ -100,10 +100,7 @@ proc insertBranch(
|
|||
# Install `forkVtx`
|
||||
block:
|
||||
# Clear Merkle hashes (aka hash keys) unless proof mode.
|
||||
if db.pPrf.len == 0:
|
||||
db.clearMerkleKeys(hike, linkID)
|
||||
elif linkID in db.pPrf:
|
||||
return err(MergeNonBranchProofModeLock)
|
||||
db.clearMerkleKeys(hike, linkID)
|
||||
|
||||
if linkVtx.vType == Leaf:
|
||||
# Double check path prefix
|
||||
|
@ -196,10 +193,7 @@ proc concatBranchAndLeaf(
|
|||
return err(MergeRootBranchLinkBusy)
|
||||
|
||||
# Clear Merkle hashes (aka hash keys) unless proof mode.
|
||||
if db.pPrf.len == 0:
|
||||
db.clearMerkleKeys(hike, brVid)
|
||||
elif brVid in db.pPrf:
|
||||
return err(MergeBranchProofModeLock) # Ooops
|
||||
db.clearMerkleKeys(hike, brVid)
|
||||
|
||||
# Append branch vertex
|
||||
var okHike = Hike(root: hike.root, legs: hike.legs)
|
||||
|
@ -264,26 +258,13 @@ proc mergePayloadTopIsBranchAddLeaf(
|
|||
#
|
||||
# <-------- immutable ------------> <---- mutable ----> ..
|
||||
#
|
||||
if db.pPrf.len == 0:
|
||||
# Not much else that can be done here
|
||||
raiseAssert "Dangling edge:" &
|
||||
" pfx=" & $hike.legsTo(hike.legs.len-1,NibblesBuf) &
|
||||
" branch=" & $parent &
|
||||
" nibble=" & $nibble &
|
||||
" edge=" & $linkID &
|
||||
" tail=" & $hike.tail
|
||||
|
||||
# Reuse placeholder entry in table
|
||||
let vtx = VertexRef(
|
||||
vType: Leaf,
|
||||
lPfx: hike.tail,
|
||||
lData: payload)
|
||||
db.setVtxAndKey(hike.root, linkID, vtx)
|
||||
var okHike = Hike(root: hike.root, legs: hike.legs)
|
||||
okHike.legs.add Leg(wp: VidVtxPair(vid: linkID, vtx: vtx), nibble: -1)
|
||||
if parent notin db.pPrf:
|
||||
db.layersResKey(hike.root, parent)
|
||||
return ok(okHike)
|
||||
# Not much else that can be done here
|
||||
raiseAssert "Dangling edge:" &
|
||||
" pfx=" & $hike.legsTo(hike.legs.len-1,NibblesBuf) &
|
||||
" branch=" & $parent &
|
||||
" nibble=" & $nibble &
|
||||
" edge=" & $linkID &
|
||||
" tail=" & $hike.tail
|
||||
|
||||
if linkVtx.vType == Branch:
|
||||
# Slot link to a branch vertex should be handled by `hikeUp()`
|
||||
|
@ -347,10 +328,7 @@ proc mergePayloadTopIsExtAddLeaf(
|
|||
return err(MergeRootBranchLinkBusy)
|
||||
|
||||
# Clear Merkle hashes (aka hash keys) unless proof mode
|
||||
if db.pPrf.len == 0:
|
||||
db.clearMerkleKeys(hike, brVid)
|
||||
elif brVid in db.pPrf:
|
||||
return err(MergeBranchProofModeLock)
|
||||
db.clearMerkleKeys(hike, brVid)
|
||||
|
||||
let
|
||||
brDup = brVtx.dup
|
||||
|
@ -382,10 +360,7 @@ proc mergePayloadTopIsEmptyAddLeaf(
|
|||
return err(MergeRootBranchLinkBusy)
|
||||
|
||||
# Clear Merkle hashes (aka hash keys) unless proof mode
|
||||
if db.pPrf.len == 0:
|
||||
db.clearMerkleKeys(hike, hike.root)
|
||||
elif hike.root in db.pPrf:
|
||||
return err(MergeBranchProofModeLock)
|
||||
db.clearMerkleKeys(hike, hike.root)
|
||||
|
||||
let
|
||||
rootDup = rootVtx.dup
|
||||
|
@ -416,8 +391,6 @@ proc mergePayloadUpdate(
|
|||
# Update payloads if they differ
|
||||
if leafLeg.wp.vtx.lData != payload:
|
||||
let vid = leafLeg.wp.vid
|
||||
if vid in db.pPrf:
|
||||
return err(MergeLeafProofModeLock)
|
||||
|
||||
# Update accounts storage root which is handled implicitly
|
||||
if hike.root == VertexID(1):
|
||||
|
|
|
@ -1,364 +0,0 @@
|
|||
# nimbus-eth1
|
||||
# Copyright (c) 2023-2024 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
|
||||
std/[algorithm, sets, tables],
|
||||
eth/common,
|
||||
results,
|
||||
stew/keyed_queue,
|
||||
../../../sync/protocol/snap/snap_types,
|
||||
".."/[aristo_desc, aristo_get, aristo_layers, aristo_serialise, aristo_vid]
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Private functions: add Merkle proof node
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
proc mergeNodeImpl(
|
||||
db: AristoDbRef; # Database, top layer
|
||||
hashKey: HashKey; # Merkel hash of node (or so)
|
||||
node: NodeRef; # Node derived from RLP representation
|
||||
rootVid: VertexID; # Current sub-trie
|
||||
): Result[void,AristoError] =
|
||||
## The function merges the argument hash key `lid` as expanded from the
|
||||
## node RLP representation into the `Aristo Trie` database. The vertex is
|
||||
## split off from the node and stored separately. So are the Merkle hashes.
|
||||
## The vertex is labelled `locked`.
|
||||
##
|
||||
## The `node` argument is *not* checked, whether the vertex IDs have been
|
||||
## allocated, already. If the node comes straight from the `decode()` RLP
|
||||
## decoder as expected, these vertex IDs will be all zero.
|
||||
##
|
||||
## This function expects that the parent for the argument `node` has already
|
||||
## been installed.
|
||||
##
|
||||
## Caveat:
|
||||
## Proof of concept, not in production yet.
|
||||
##
|
||||
# Check for error after RLP decoding
|
||||
doAssert node.error == AristoError(0)
|
||||
|
||||
# Verify arguments
|
||||
if not rootVid.isValid:
|
||||
return err(MergeRootKeyInvalid)
|
||||
if not hashKey.isValid:
|
||||
return err(MergeHashKeyInvalid)
|
||||
|
||||
# Make sure that the `vid<->key` reverse mapping is updated.
|
||||
let vid = db.layerGetProofVidOrVoid hashKey
|
||||
if not vid.isValid:
|
||||
return err(MergeRevVidMustHaveBeenCached)
|
||||
|
||||
# Use the vertex ID `vid` to be populated by the argument root node
|
||||
let key = db.layersGetKeyOrVoid vid
|
||||
if key.isValid and key != hashKey:
|
||||
return err(MergeHashKeyDiffersFromCached)
|
||||
|
||||
# Set up vertex.
|
||||
let (vtx, newVtxFromNode) = block:
|
||||
let vty = db.getVtx vid
|
||||
if vty.isValid:
|
||||
(vty, false)
|
||||
else:
|
||||
(node.to(VertexRef), true)
|
||||
|
||||
# The `vertexID <-> hashKey` mappings need to be set up now (if any)
|
||||
case node.vType:
|
||||
of Leaf:
|
||||
# Check whether there is need to convert the payload to `Account` payload
|
||||
if rootVid == VertexID(1) and newVtxFromNode:
|
||||
try:
|
||||
let
|
||||
# `aristo_serialise.read()` always decodes raw data payloaf
|
||||
acc = rlp.decode(node.lData.rawBlob, Account)
|
||||
pyl = PayloadRef(
|
||||
pType: AccountData,
|
||||
account: AristoAccount(
|
||||
nonce: acc.nonce,
|
||||
balance: acc.balance,
|
||||
codeHash: acc.codeHash))
|
||||
if acc.storageRoot.isValid:
|
||||
var sid = db.layerGetProofVidOrVoid acc.storageRoot.to(HashKey)
|
||||
if not sid.isValid:
|
||||
sid = db.vidFetch
|
||||
db.layersPutProof(sid, acc.storageRoot.to(HashKey))
|
||||
pyl.stoID = sid
|
||||
vtx.lData = pyl
|
||||
except RlpError:
|
||||
return err(MergeNodeAccountPayloadError)
|
||||
|
||||
of Extension:
|
||||
if node.key[0].isValid:
|
||||
let eKey = node.key[0]
|
||||
if newVtxFromNode:
|
||||
vtx.eVid = db.layerGetProofVidOrVoid eKey
|
||||
if not vtx.eVid.isValid:
|
||||
# Brand new reverse lookup link for this vertex
|
||||
vtx.eVid = db.vidFetch
|
||||
elif not vtx.eVid.isValid:
|
||||
return err(MergeNodeVidMissing)
|
||||
else:
|
||||
let yEke = db.getKey vtx.eVid
|
||||
if yEke.isValid and eKey != yEke:
|
||||
return err(MergeNodeVtxDiffersFromExisting)
|
||||
db.layersPutProof(vtx.eVid, eKey)
|
||||
|
||||
of Branch:
|
||||
for n in 0..15:
|
||||
if node.key[n].isValid:
|
||||
let bKey = node.key[n]
|
||||
if newVtxFromNode:
|
||||
vtx.bVid[n] = db.layerGetProofVidOrVoid bKey
|
||||
if not vtx.bVid[n].isValid:
|
||||
# Brand new reverse lookup link for this vertex
|
||||
vtx.bVid[n] = db.vidFetch
|
||||
elif not vtx.bVid[n].isValid:
|
||||
return err(MergeNodeVidMissing)
|
||||
else:
|
||||
let yEkb = db.getKey vtx.bVid[n]
|
||||
if yEkb.isValid and yEkb != bKey:
|
||||
return err(MergeNodeVtxDiffersFromExisting)
|
||||
db.layersPutProof(vtx.bVid[n], bKey)
|
||||
|
||||
# Store and lock vertex
|
||||
db.layersPutProof(vid, key, vtx)
|
||||
|
||||
ok()
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Public functions
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
proc mergeProof*(
|
||||
db: AristoDbRef; # Database, top layer
|
||||
proof: openArray[SnapProof]; # RLP encoded node records
|
||||
rootVid = VertexID(0); # Current sub-trie
|
||||
): Result[int, AristoError]
|
||||
{.gcsafe, raises: [RlpError].} =
|
||||
## The function merges the argument `proof` list of RLP encoded node records
|
||||
## into the `Aristo Trie` database. This function is intended to be used with
|
||||
## the proof nodes as returened by `snap/1` messages.
|
||||
##
|
||||
## If there is no root vertex ID passed, the function tries to find out what
|
||||
## the root hashes are and allocates new vertices with static IDs `$2`, `$3`,
|
||||
## etc.
|
||||
##
|
||||
## Caveat:
|
||||
## Proof of concept, not in production yet.
|
||||
##
|
||||
proc update(
|
||||
seen: var Table[HashKey,NodeRef];
|
||||
todo: var KeyedQueueNV[NodeRef];
|
||||
key: HashKey;
|
||||
) {.gcsafe, raises: [RlpError].} =
|
||||
## Check for embedded nodes, i.e. fully encoded node instead of a hash.
|
||||
## They need to be treated as full nodes, here.
|
||||
if key.isValid and key.len < 32:
|
||||
let lid = key.data.digestTo(HashKey)
|
||||
if not seen.hasKey lid:
|
||||
let node = key.data.decode(NodeRef)
|
||||
discard todo.append node
|
||||
seen[lid] = node
|
||||
|
||||
let rootKey = block:
|
||||
if rootVid.isValid:
|
||||
let vidKey = db.getKey rootVid
|
||||
if not vidKey.isValid:
|
||||
return err(MergeRootKeyInvalid)
|
||||
# Make sure that the reverse lookup for the root vertex key is available.
|
||||
if not db.layerGetProofVidOrVoid(vidKey).isValid:
|
||||
return err(MergeProofInitMissing)
|
||||
vidKey
|
||||
else:
|
||||
VOID_HASH_KEY
|
||||
|
||||
# Expand and collect hash keys and nodes and parent indicator
|
||||
var
|
||||
nodeTab: Table[HashKey,NodeRef]
|
||||
rootKeys: HashSet[HashKey] # Potential root node hashes
|
||||
for w in proof:
|
||||
let
|
||||
key = w.Blob.digestTo(HashKey)
|
||||
node = rlp.decode(w.Blob,NodeRef)
|
||||
if node.error != AristoError(0):
|
||||
return err(node.error)
|
||||
nodeTab[key] = node
|
||||
rootKeys.incl key
|
||||
|
||||
# Check for embedded nodes, i.e. fully encoded node instead of a hash.
|
||||
# They will be added as full nodes to the `nodeTab[]`.
|
||||
var embNodes: KeyedQueueNV[NodeRef]
|
||||
discard embNodes.append node
|
||||
while true:
|
||||
let node = embNodes.shift.valueOr: break
|
||||
case node.vType:
|
||||
of Leaf:
|
||||
discard
|
||||
of Branch:
|
||||
for n in 0 .. 15:
|
||||
nodeTab.update(embNodes, node.key[n])
|
||||
of Extension:
|
||||
nodeTab.update(embNodes, node.key[0])
|
||||
|
||||
# Create a table with back links
|
||||
var
|
||||
backLink: Table[HashKey,HashKey]
|
||||
blindNodes: HashSet[HashKey]
|
||||
for (key,node) in nodeTab.pairs:
|
||||
case node.vType:
|
||||
of Leaf:
|
||||
blindNodes.incl key
|
||||
of Extension:
|
||||
if nodeTab.hasKey node.key[0]:
|
||||
backLink[node.key[0]] = key
|
||||
rootKeys.excl node.key[0] # predecessor => not root
|
||||
else:
|
||||
blindNodes.incl key
|
||||
of Branch:
|
||||
var isBlind = true
|
||||
for n in 0 .. 15:
|
||||
if nodeTab.hasKey node.key[n]:
|
||||
isBlind = false
|
||||
backLink[node.key[n]] = key
|
||||
rootKeys.excl node.key[n] # predecessor => not root
|
||||
if isBlind:
|
||||
blindNodes.incl key
|
||||
|
||||
# If it exists, the root key must be in the set `mayBeRoot` in order
|
||||
# to work.
|
||||
var roots: Table[HashKey,VertexID]
|
||||
if rootVid.isValid:
|
||||
if rootKey notin rootKeys:
|
||||
return err(MergeRootKeyNotInProof)
|
||||
roots[rootKey] = rootVid
|
||||
elif rootKeys.len == 0:
|
||||
return err(MergeRootKeysMissing)
|
||||
else:
|
||||
# Add static root keys different from VertexID(1)
|
||||
var count = 2
|
||||
for key in rootKeys.items:
|
||||
while true:
|
||||
# Check for already allocated nodes
|
||||
let vid1 = db.layerGetProofVidOrVoid key
|
||||
if vid1.isValid:
|
||||
roots[key] = vid1
|
||||
break
|
||||
# Use the next free static free vertex ID
|
||||
let vid2 = VertexID(count)
|
||||
count.inc
|
||||
if not db.getKey(vid2).isValid:
|
||||
doAssert not db.layerGetProofVidOrVoid(key).isValid
|
||||
db.layersPutProof(vid2, key)
|
||||
roots[key] = vid2
|
||||
break
|
||||
if LEAST_FREE_VID <= count:
|
||||
return err(MergeRootKeysOverflow)
|
||||
|
||||
# Run over blind nodes and build chains from a blind/bottom level node up
|
||||
# to the root node. Select only chains that end up at the pre-defined root
|
||||
# node.
|
||||
var
|
||||
accounts: seq[seq[HashKey]] # This one separated, to be processed last
|
||||
chains: seq[seq[HashKey]]
|
||||
for w in blindNodes:
|
||||
# Build a chain of nodes up to the root node
|
||||
var
|
||||
chain: seq[HashKey]
|
||||
nodeKey = w
|
||||
while nodeKey.isValid and nodeTab.hasKey nodeKey:
|
||||
chain.add nodeKey
|
||||
nodeKey = backLink.getOrVoid nodeKey
|
||||
if 0 < chain.len and chain[^1] in roots:
|
||||
if roots.getOrVoid(chain[0]) == VertexID(1):
|
||||
accounts.add chain
|
||||
else:
|
||||
chains.add chain
|
||||
|
||||
# Process over chains in reverse mode starting with the root node. This
|
||||
# allows the algorithm to find existing nodes on the backend.
|
||||
var
|
||||
seen: HashSet[HashKey]
|
||||
merged = 0
|
||||
# Process the root ID which is common to all chains
|
||||
for chain in chains & accounts:
|
||||
let chainRootVid = roots.getOrVoid chain[^1]
|
||||
for key in chain.reversed:
|
||||
if key notin seen:
|
||||
seen.incl key
|
||||
let node = nodeTab.getOrVoid key
|
||||
db.mergeNodeImpl(key, node, chainRootVid).isOkOr:
|
||||
return err(error)
|
||||
merged.inc
|
||||
|
||||
ok merged
|
||||
|
||||
|
||||
proc mergeProof*(
|
||||
db: AristoDbRef; # Database, top layer
|
||||
rootHash: Hash256; # Merkle hash for root
|
||||
rootVid = VertexID(0); # Optionally, force root vertex ID
|
||||
): Result[VertexID,AristoError] =
|
||||
## Set up a `rootKey` associated with a vertex ID for use with proof nodes.
|
||||
##
|
||||
## If argument `rootVid` is unset then a new dybamic root vertex (i.e.
|
||||
## the ID will be at least `LEAST_FREE_VID`) will be installed.
|
||||
##
|
||||
## Otherwise, if the argument `rootVid` is set then a sub-trie with root
|
||||
## `rootVid` is checked for. An error is returned if it is set up already
|
||||
## with a different `rootHash`.
|
||||
##
|
||||
## Upon successful return, the vertex ID assigned to the root key is returned.
|
||||
##
|
||||
## Caveat:
|
||||
## Proof of concept, not in production yet.
|
||||
##
|
||||
let rootKey = rootHash.to(HashKey)
|
||||
|
||||
if rootVid.isValid:
|
||||
let key = db.getKey rootVid
|
||||
if key.isValid:
|
||||
if rootKey.isValid and key != rootKey:
|
||||
# Cannot use installed root key differing from hash argument
|
||||
return err(MergeRootKeyDiffersForVid)
|
||||
# Confirm root ID and key for proof nodes processing
|
||||
db.layersPutProof(rootVid, key) # note that `rootKey` might be void
|
||||
return ok rootVid
|
||||
|
||||
if not rootHash.isValid:
|
||||
return err(MergeRootArgsIncomplete)
|
||||
if db.getVtx(rootVid).isValid:
|
||||
# Cannot use verify root key for existing root vertex
|
||||
return err(MergeRootKeyMissing)
|
||||
|
||||
# Confirm root ID and hash key for proof nodes processing
|
||||
db.layersPutProof(rootVid, rootKey)
|
||||
return ok rootVid
|
||||
|
||||
if not rootHash.isValid:
|
||||
return err(MergeRootArgsIncomplete)
|
||||
|
||||
# Now there is no root vertex ID, only the hash argument.
|
||||
# So Create and assign a new root key.
|
||||
let vid = db.vidFetch
|
||||
db.layersPutProof(vid, rootKey)
|
||||
return ok vid
|
||||
|
||||
|
||||
proc mergeProof*(
|
||||
db: AristoDbRef; # Database, top layer
|
||||
rootVid: VertexID; # Root ID
|
||||
): Result[VertexID,AristoError] =
|
||||
## Variant of `mergeProof()` for missing `rootHash`
|
||||
db.mergeProof(EMPTY_ROOT_HASH, rootVid)
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# End
|
||||
# ------------------------------------------------------------------------------
|
|
@ -65,8 +65,7 @@ proc txFork*(
|
|||
let rc = db.getTuvBE()
|
||||
if rc.isOk:
|
||||
LayerRef(
|
||||
delta: LayerDeltaRef(vTop: rc.value),
|
||||
final: LayerFinalRef())
|
||||
delta: LayerDeltaRef(vTop: rc.value))
|
||||
elif rc.error == GetTuvNotFound:
|
||||
LayerRef.init()
|
||||
else:
|
||||
|
|
|
@ -85,7 +85,6 @@ proc txFrameBegin*(db: AristoDbRef): Result[AristoTxRef,AristoError] =
|
|||
db.stack.add db.top
|
||||
db.top = LayerRef(
|
||||
delta: LayerDeltaRef(vTop: vTop),
|
||||
final: db.top.final.dup,
|
||||
txUid: db.getTxUid)
|
||||
|
||||
db.txRef = AristoTxRef(
|
||||
|
|
|
@ -100,11 +100,6 @@ proc txStow*(
|
|||
if rc.isErr and rc.error != TxPrettyPointlessLayer:
|
||||
return err(rc.error)
|
||||
|
||||
# Special treatment for `snap` proofs (aka `chunkedMpt`)
|
||||
let final =
|
||||
if chunkedMpt: LayerFinalRef(fRpp: db.top.final.fRpp)
|
||||
else: LayerFinalRef()
|
||||
|
||||
# Move/merge/install `top` layer onto `balancer`
|
||||
if rc.isOk:
|
||||
db.topMerge(rc.value).isOkOr:
|
||||
|
@ -112,8 +107,7 @@ proc txStow*(
|
|||
|
||||
# New empty top layer (probably with `snap` proofs and `vTop` carry over)
|
||||
db.top = LayerRef(
|
||||
delta: LayerDeltaRef(),
|
||||
final: final)
|
||||
delta: LayerDeltaRef())
|
||||
if db.balancer.isValid:
|
||||
db.top.delta.vTop = db.balancer.vTop
|
||||
else:
|
||||
|
@ -137,7 +131,6 @@ proc txStow*(
|
|||
# New empty top layer (probably with `snap` proofs carry over)
|
||||
db.top = LayerRef(
|
||||
delta: LayerDeltaRef(vTop: db.vTop),
|
||||
final: final,
|
||||
txUid: db.top.txUid)
|
||||
ok()
|
||||
|
||||
|
|
|
@ -20,7 +20,6 @@ import
|
|||
stew/byteutils,
|
||||
"../.."/[errors, constants],
|
||||
".."/[aristo, storage_types],
|
||||
./backend/aristo_db,
|
||||
"."/base
|
||||
|
||||
logScope:
|
||||
|
|
|
@ -27,7 +27,6 @@ import
|
|||
aristo_hike,
|
||||
aristo_init/persistent,
|
||||
aristo_layers,
|
||||
aristo_merge,
|
||||
aristo_nearby,
|
||||
aristo_tx],
|
||||
../replay/xcheck,
|
||||
|
@ -569,19 +568,6 @@ proc testTxMergeProofAndKvpList*(
|
|||
|
||||
# var lst = w.kvpLst.mapRootVid testRootVid
|
||||
|
||||
if 0 < w.proof.len:
|
||||
let root = block:
|
||||
let rc = db.mergeProof(rootKey, testRootVid)
|
||||
xCheckRc rc.error == 0
|
||||
rc.value
|
||||
|
||||
let nMerged = block:
|
||||
let rc = db.mergeProof(w.proof, root)
|
||||
xCheckRc rc.error == 0
|
||||
rc.value
|
||||
|
||||
xCheck w.proof.len == nMerged
|
||||
xCheck db.nLayersVtx() <= nMerged + sTabLen
|
||||
|
||||
let merged = db.mergeList leafs
|
||||
xCheck merged.error in {AristoError(0), MergeLeafPathCachedAlready}
|
||||
|
|
|
@ -70,11 +70,6 @@ proc preLoadAristoDb(cdb: CoreDbRef; jKvp: JsonNode; num: BlockNumber) =
|
|||
# Set up production MPT
|
||||
doAssert adb.mergeProof(proof).isOk
|
||||
|
||||
# Remove locks so that hashify can re-assign changed nodes
|
||||
adb.top.final.pPrf.clear
|
||||
adb.top.final.fRpp.clear
|
||||
|
||||
|
||||
# use tracerTestGen.nim to generate additional test data
|
||||
proc testFixtureImpl(node: JsonNode, testStatusIMPL: var TestStatus, memoryDB: CoreDbRef) =
|
||||
setErrorLevel()
|
||||
|
|
Loading…
Reference in New Issue