mirror of
https://github.com/status-im/nimbus-eth1.git
synced 2025-01-29 13:35:38 +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.
162 lines
5.4 KiB
Nim
162 lines
5.4 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
|
|
std/[sequtils, sets, tables],
|
|
eth/[common, trie/nibbles],
|
|
results,
|
|
".."/[aristo_desc, aristo_get, aristo_serialise, aristo_utils]
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Public functions
|
|
# ------------------------------------------------------------------------------
|
|
|
|
proc checkTopStrict*(
|
|
db: AristoDbRef; # Database, top layer
|
|
): Result[void,(VertexID,AristoError)] =
|
|
for (vid,vtx) in db.top.sTab.pairs:
|
|
if vtx.isValid:
|
|
let node = vtx.toNode(db).valueOr:
|
|
return err((vid,CheckStkVtxIncomplete))
|
|
|
|
let lbl = db.top.kMap.getOrVoid vid
|
|
if not lbl.isValid:
|
|
return err((vid,CheckStkVtxKeyMissing))
|
|
if lbl.key != node.digestTo(HashKey):
|
|
return err((vid,CheckStkVtxKeyMismatch))
|
|
|
|
let revVids = db.top.pAmk.getOrVoid lbl
|
|
if not revVids.isValid:
|
|
return err((vid,CheckStkRevKeyMissing))
|
|
if vid notin revVids:
|
|
return err((vid,CheckStkRevKeyMismatch))
|
|
|
|
let
|
|
pAmkVtxCount = db.top.pAmk.values.toSeq.foldl(a + b.len, 0)
|
|
sTabVtxCount = db.top.sTab.values.toSeq.filterIt(it.isValid).len
|
|
# Non-zero values mist sum up the same
|
|
if pAmkVtxCount < sTabVtxCount:
|
|
return err((VertexID(0),CheckStkVtxCountMismatch))
|
|
|
|
ok()
|
|
|
|
|
|
proc checkTopRelaxed*(
|
|
db: AristoDbRef; # Database, top layer
|
|
): Result[void,(VertexID,AristoError)] =
|
|
if 0 < db.top.pPrf.len:
|
|
for vid in db.top.pPrf:
|
|
let vtx = db.top.sTab.getOrVoid vid
|
|
if vtx.isValid:
|
|
let node = vtx.toNode(db).valueOr:
|
|
return err((vid,CheckRlxVtxIncomplete))
|
|
|
|
let lbl = db.top.kMap.getOrVoid vid
|
|
if not lbl.isValid:
|
|
return err((vid,CheckRlxVtxKeyMissing))
|
|
if lbl.key != node.digestTo(HashKey):
|
|
return err((vid,CheckRlxVtxKeyMismatch))
|
|
|
|
let revVids = db.top.pAmk.getOrVoid lbl
|
|
if not revVids.isValid:
|
|
return err((vid,CheckRlxRevKeyMissing))
|
|
if vid notin revVids:
|
|
return err((vid,CheckRlxRevKeyMismatch))
|
|
else:
|
|
for (vid,lbl) in db.top.kMap.pairs:
|
|
if lbl.isValid: # Otherwise to be deleted
|
|
let vtx = db.getVtx vid
|
|
if vtx.isValid:
|
|
let node = vtx.toNode(db).valueOr:
|
|
continue
|
|
if lbl.key != node.digestTo(HashKey):
|
|
return err((vid,CheckRlxVtxKeyMismatch))
|
|
|
|
let revVids = db.top.pAmk.getOrVoid lbl
|
|
if not revVids.isValid:
|
|
return err((vid,CheckRlxRevKeyMissing))
|
|
if vid notin revVids:
|
|
return err((vid,CheckRlxRevKeyMismatch))
|
|
ok()
|
|
|
|
|
|
proc checkTopCommon*(
|
|
db: AristoDbRef; # Database, top layer
|
|
): Result[void,(VertexID,AristoError)] =
|
|
# Some `kMap[]` entries may ne void indicating backend deletion
|
|
let
|
|
kMapCount = db.top.kMap.values.toSeq.filterIt(it.isValid).len
|
|
kMapNilCount = db.top.kMap.len - kMapCount
|
|
|
|
# Collect leafs and check deleted entries
|
|
var
|
|
nNilVtx = 0
|
|
leafs = db.top.lTab.values.toSeq.filterIt(it.isValid).toHashSet
|
|
for (vid,vtx) in db.top.sTab.pairs:
|
|
if vtx.isValid:
|
|
case vtx.vType:
|
|
of Leaf:
|
|
if vid notin leafs:
|
|
return err((vid,CheckAnyLeafUnregistered))
|
|
leafs.excl vid
|
|
of Branch:
|
|
block check42Links:
|
|
var seen = false
|
|
for n in 0 .. 15:
|
|
if vtx.bVid[n].isValid:
|
|
if seen:
|
|
break check42Links
|
|
seen = true
|
|
return err((vid,CheckAnyVtxBranchLinksMissing))
|
|
of Extension:
|
|
if vtx.ePfx.len == 0:
|
|
return err((vid,CheckAnyVtxExtPfxMissing))
|
|
else:
|
|
nNilVtx.inc
|
|
discard db.getVtxBE(vid).valueOr:
|
|
return err((vid,CheckAnyVidVtxMissing))
|
|
if not db.top.kMap.hasKey vid:
|
|
return err((vid,CheckAnyVtxEmptyKeyMissing))
|
|
if db.top.kMap.getOrVoid(vid).isValid:
|
|
return err((vid,CheckAnyVtxEmptyKeyExpected))
|
|
|
|
# Check for dangling leaf records
|
|
if 0 < leafs.len:
|
|
return err((leafs.toSeq[0],CheckAnyLeafVidDangling))
|
|
|
|
# If present, there are at least as many deleted hashes as there are deleted
|
|
# vertices.
|
|
if kMapNilCount != 0 and kMapNilCount < nNilVtx:
|
|
return err((VertexID(0),CheckAnyVtxEmptyKeyMismatch))
|
|
|
|
let pAmkVtxCount = db.top.pAmk.values.toSeq.foldl(a + b.len, 0)
|
|
if pAmkVtxCount != kMapCount:
|
|
var knownKeys: HashSet[VertexID]
|
|
for (key,vids) in db.top.pAmk.pairs:
|
|
for vid in vids:
|
|
if not db.top.kMap.hasKey(vid):
|
|
return err((vid,CheckAnyRevVtxMissing))
|
|
if vid in knownKeys:
|
|
return err((vid,CheckAnyRevVtxDup))
|
|
knownKeys.incl vid
|
|
return err((VertexID(0),CheckAnyRevCountMismatch)) # should not apply(!)
|
|
|
|
for vid in db.top.pPrf:
|
|
if not db.top.kMap.hasKey(vid):
|
|
return err((vid,CheckAnyVtxLockWithoutKey))
|
|
ok()
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# End
|
|
# ------------------------------------------------------------------------------
|
|
|