Snap sync fix trie interpolation fringe condition (#1457)

* Cosmetics

details:
+ Update doc generator
+ Fix key type representation in `hexary_desc` for debugging
+ Redefine `isImportOk()` as template for better `check()` line reporting

* Fix fringe condition when interpolating Merkle-Patricia tries

details:
  Small change with profound effect fixing some pathological condition
  that haunted the unit test set on large data sers. There is still one
  condition left which might well be due to an incomplete data set.

* Unit test proof nodes for node range extractor

* Unit tests to run on full extraction set

why:
  Left over from troubleshooting, range length was only 5
This commit is contained in:
Jordan Hrycaj 2023-02-01 18:56:06 +00:00 committed by GitHub
parent 73e93f1f11
commit 6ca6bcd96f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 185 additions and 3019 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -30,15 +30,14 @@ MUFFLE := 2>/dev/null
endif endif
# Support for external NIM compiler unless X=0 # Support for external NIM compiler unless X=0
ifneq ($(if $X,$X,1),0) ifneq ($(if $X,$X,0),0)
# works with external nim 1.5.1 (not the local 1.2.10 one)
PATH := $(SAVED_PATH):$(NIM_PATH):$(NIMBLE_DIR)/bin PATH := $(SAVED_PATH):$(NIM_PATH):$(NIMBLE_DIR)/bin
else else
PATH := $(NIM_PATH):$(NIMBLE_DIR)/bin:$(SAVED_PATH) PATH := $(NIM_PATH):$(NIMBLE_DIR)/bin:$(SAVED_PATH)
endif endif
# Compat version is used with external NIM compiler # Compat version is used with external NIM compiler
NIM_COMPAT := --useVersion:1.2 # NIM_COMPAT := --useVersion:1.2
# Name of NIMDOC compiler, test for newer version on host OS # Name of NIMDOC compiler, test for newer version on host OS
NIM_CMD := nim NIM_CMD := nim
@ -71,7 +70,7 @@ help::
echo "Usage: $(MAKE) <target> [<option> ..]" echo "Usage: $(MAKE) <target> [<option> ..]"
echo echo
echo "<option>: V=1 -- verbose mode" echo "<option>: V=1 -- verbose mode"
echo " X=0 -- preferring local nim compiler (this repo)" echo " X=1 -- preferring local nim compiler (this repo)"
echo " NIMFLAGS=.. -- additional flags for nim-docs generator" echo " NIMFLAGS=.. -- additional flags for nim-docs generator"
echo echo
echo "<target>: docs -- build NIM docs" echo "<target>: docs -- build NIM docs"
@ -116,12 +115,9 @@ docs/ex/%.png : %.png
@mkdir -p $(dir $@) @mkdir -p $(dir $@)
@set -x; cp "$<" "$@" @set -x; cp "$<" "$@"
docs/dochack.js docs/nimdoc.out.css:
cp docs.static/$(notdir $@) $@
.PHONY: docs-index-helper .PHONY: docs-index-helper
.SILENT: docs-index-helper .SILENT: docs-index-helper
docs-index-helper: docs/dochack.js docs/nimdoc.out.css docs-index-helper:
nim=$(NIM_EXE); \ nim=$(NIM_EXE); \
$$nim --version | sed q ; set -x ;\ $$nim --version | sed q ; set -x ;\
$$nim --skipProjCfg buildIndex -o:docs/theindex.html \ $$nim --skipProjCfg buildIndex -o:docs/theindex.html \
@ -211,9 +207,6 @@ clean-test-exe:
.SILENT: clean-docs .SILENT: clean-docs
clean-docs: clean-docs:
for f in docs/dochack.js docs/nimdoc.out.css; do \
[ ! -f "$$f" ] || (set -x; rm -f "$$f"); \
done
for f in $(foreach f,$(EXE_FILES),docs/$f) \ for f in $(foreach f,$(EXE_FILES),docs/$f) \
$(foreach f,$(PNG_FILES),docs/ex/$f) \ $(foreach f,$(PNG_FILES),docs/ex/$f) \
$(foreach f,$(MD_FILES),docs/ex/$f) \ $(foreach f,$(MD_FILES),docs/ex/$f) \
@ -223,10 +216,6 @@ clean-docs:
[ ! -f "$$f.idx" ] || (set -x; rm -f "$$f.idx"); \ [ ! -f "$$f.idx" ] || (set -x; rm -f "$$f.idx"); \
[ ! -f "$$f.png" ] || (set -x; rm -f "$$f.png"); \ [ ! -f "$$f.png" ] || (set -x; rm -f "$$f.png"); \
done done
find docs -name 'nimdoc?out.css' -print 2>/dev/null | \
while read f_css ; do \
[ ! -f "$$f_css" ] || (set -x; rm -f "$$f_css"); \
done
for d in $(shell find docs -depth -type d -print $(MUFFLE)); do \ for d in $(shell find docs -depth -type d -print $(MUFFLE)); do \
(set -x; rmdir "$$d" $(MUFFLE)) || true; \ (set -x; rmdir "$$d" $(MUFFLE)) || true; \
done done

View File

@ -163,6 +163,9 @@ static:
var var
disablePrettyKeys* = false ## Degugging, print raw keys if `true` disablePrettyKeys* = false ## Degugging, print raw keys if `true`
proc isNodeKey*(a: RepairKey): bool {.gcsafe.}
proc isZero*(a: RepairKey): bool {.gcsafe.}
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Private helpers # Private helpers
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
@ -196,6 +199,12 @@ proc ppImpl(s: string; hex = false): string =
"..(" & $s.len & ").." & s[s.len-16 ..< s.len] "..(" & $s.len & ").." & s[s.len-16 ..< s.len]
proc ppImpl(key: RepairKey; db: HexaryTreeDbRef): string = proc ppImpl(key: RepairKey; db: HexaryTreeDbRef): string =
if key.isZero:
return "ø"
if not key.isNodekey:
var num: uint64
(addr num).copyMem(unsafeAddr key.ByteArray33[25], 8)
return "%" & $num
try: try:
if not disablePrettyKeys and not db.keyPp.isNil: if not disablePrettyKeys and not db.keyPp.isNil:
return db.keyPp(key) return db.keyPp(key)
@ -333,7 +342,11 @@ proc init*(key: var RepairKey; data: openArray[byte]): bool =
proc newRepairKey*(db: HexaryTreeDbRef): RepairKey = proc newRepairKey*(db: HexaryTreeDbRef): RepairKey =
db.repairKeyGen.inc db.repairKeyGen.inc
var src = db.repairKeyGen.toBytesBE # Storing in proper endian handy for debugging (but not really important)
when cpuEndian == bigEndian:
var src = db.repairKeyGen.toBytesBE
else:
var src = db.repairKeyGen.toBytesLE
(addr result.ByteArray33[25]).copyMem(addr src[0], 8) (addr result.ByteArray33[25]).copyMem(addr src[0], 8)
result.ByteArray33[0] = 1 result.ByteArray33[0] = 1
@ -355,8 +368,11 @@ proc to*(key: NodeKey; T: type NibblesSeq): T =
proc to*(key: NodeKey; T: type RepairKey): T = proc to*(key: NodeKey; T: type RepairKey): T =
(addr result.ByteArray33[1]).copyMem(unsafeAddr key.ByteArray32[0], 32) (addr result.ByteArray33[1]).copyMem(unsafeAddr key.ByteArray32[0], 32)
proc isZero*[T: NodeTag|NodeKey|RepairKey](a: T): bool = proc isZero*(a: RepairKey): bool =
a == T.default a == typeof(a).default
proc isZero*[T: NodeTag|NodeKey](a: T): bool =
a == typeof(a).default
proc isNodeKey*(a: RepairKey): bool = proc isNodeKey*(a: RepairKey): bool =
a.ByteArray33[0] == 0 a.ByteArray33[0] == 0

View File

@ -144,7 +144,7 @@ proc padPartialPath(pfx: NibblesSeq; dblNibble: byte): NodeKey =
padded = padded & @[dblNibble].initNibbleRange.slice(1) padded = padded & @[dblNibble].initNibbleRange.slice(1)
else: else:
let nope = seq[byte].default.initNibbleRange let nope = seq[byte].default.initNibbleRange
padded = pfx.slice(0,63) & nope # nope forces re-alignment padded = pfx.slice(0,64) & nope # nope forces re-alignment
let bytes = padded.getBytes let bytes = padded.getBytes
(addr result.ByteArray32[0]).copyMem(unsafeAddr bytes[0], bytes.len) (addr result.ByteArray32[0]).copyMem(unsafeAddr bytes[0], bytes.len)

View File

@ -34,13 +34,16 @@ type
# Private debugging helpers # Private debugging helpers
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
#proc pp(w: RPathXStep; db: HexaryTreeDbRef): string = when true:
# let y = if w.canLock: "lockOk" else: "noLock" import std/[sequtils, strutils]
# "(" & $w.pos & "," & y & "," & w.step.pp(db) & ")"
#proc pp(w: seq[RPathXStep]; db: HexaryTreeDbRef; indent = 4): string = proc pp(w: RPathXStep; db: HexaryTreeDbRef): string =
# let pfx = "\n" & " ".repeat(indent) let y = if w.canLock: "lockOk" else: "noLock"
# w.mapIt(it.pp(db)).join(pfx) "(" & $w.pos & "," & y & "," & w.step.pp(db) & ")"
proc pp(w: seq[RPathXStep]; db: HexaryTreeDbRef; indent = 4): string =
let pfx = "\n" & " ".repeat(indent)
w.mapIt(it.pp(db)).join(pfx)
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Private helpers # Private helpers
@ -285,7 +288,8 @@ proc rTreeInterpolate(
proc rTreeUpdateKeys( proc rTreeUpdateKeys(
rPath: RPath; rPath: RPath;
db: HexaryTreeDbRef; db: HexaryTreeDbRef;
): Result[void,bool] = ): Result[void,bool]
{.gcsafe, raises: [KeyError].} =
## The argument `rPath` is assumed to organise database nodes as ## The argument `rPath` is assumed to organise database nodes as
## ##
## root -> ... -> () -> () -> ... -> () -> () ... ## root -> ... -> () -> () -> ... -> () -> () ...
@ -343,7 +347,9 @@ proc rTreeUpdateKeys(
of Leaf: of Leaf:
discard discard
if key != thisKey: if key != thisKey:
return err(false) # no changes were made if not db.tab.hasKey(key) or
db.tab[key].state notin {Mutable,TmpRoot}:
return err(false) # no changes were made
# Ok, replace database records by stack entries # Ok, replace database records by stack entries
var lockOk = true var lockOk = true

View File

@ -236,7 +236,7 @@ proc importAccounts*(
return err(rc.error) return err(rc.error)
accounts = rc.value accounts = rc.value
# Inspect trie for dangling nodes from prrof data (if any.) # Inspect trie for dangling nodes from proof data (if any.)
if 0 < data.proof.len: if 0 < data.proof.len:
proofStats = ps.hexaDb.hexaryInspectTrie(ps.root) proofStats = ps.hexaDb.hexaryInspectTrie(ps.root)

View File

@ -74,7 +74,7 @@ proc toKey(a: NodeKey; ps: SnapDbBaseRef): uint =
# a.to(NodeKey).toKey(ps) # a.to(NodeKey).toKey(ps)
proc ppImpl(a: RepairKey; pv: SnapDbRef): string = proc ppImpl(a: RepairKey; pv: SnapDbRef): string =
if a.isZero: "ø" else:"$" & $a.toKey(pv) "$" & $a.toKey(pv)
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Debugging, pretty printing # Debugging, pretty printing
@ -297,23 +297,28 @@ proc verifyNoMoreRight*(
# Debugging (and playing with the hexary database) # Debugging (and playing with the hexary database)
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
proc assignPrettyKeys*(ps: SnapDbBaseRef) = proc assignPrettyKeys*(xDb: HexaryTreeDbRef; root: NodeKey) =
## Prepare for pretty pringing/debugging. Run early enough this function ## Prepare for pretty pringing/debugging. Run early enough this function
## sets the root key to `"$"`, for instance. ## sets the root key to `"$"`, for instance.
noPpError("validate(1)"): if not xDb.keyPp.isNil:
# Make keys assigned in pretty order for printing noPpError("validate(1)"):
var keysList = toSeq(ps.hexaDb.tab.keys) # Make keys assigned in pretty order for printing
let rootKey = ps.root.to(RepairKey) let rootKey = root.to(RepairKey)
discard rootKey.toKey(ps) discard xDb.keyPp rootKey
if ps.hexaDb.tab.hasKey(rootKey): var keysList = toSeq(xDb.tab.keys)
keysList = @[rootKey] & keysList if xDb.tab.hasKey(rootKey):
for key in keysList: keysList = @[rootKey] & keysList
let node = ps.hexaDb.tab[key] for key in keysList:
discard key.toKey(ps) let node = xDb.tab[key]
case node.kind: discard xDb.keyPp key
of Branch: (for w in node.bLink: discard w.toKey(ps)) case node.kind:
of Extension: discard node.eLink.toKey(ps) of Branch: (for w in node.bLink: discard xDb.keyPp w)
of Leaf: discard of Extension: discard xDb.keyPp node.eLink
of Leaf: discard
proc assignPrettyKeys*(ps: SnapDbBaseRef) =
## Variant of `assignPrettyKeys()`
ps.hexaDb.assignPrettyKeys(ps.root)
proc dumpPath*(ps: SnapDbBaseRef; key: NodeTag): seq[string] = proc dumpPath*(ps: SnapDbBaseRef; key: NodeTag): seq[string] =
## Pretty print helper compiling the path into the repair tree for the ## Pretty print helper compiling the path into the repair tree for the

View File

@ -203,17 +203,20 @@ proc accountsRunner(noisy = true; persistent = true; sample = accSample) =
getFn = desc.getAccountFn getFn = desc.getAccountFn
dbg = if noisy: hexaDb else: nil dbg = if noisy: hexaDb else: nil
desc.assignPrettyKeys() # debugging, make sure that state root ~ "$0"
test &"Proofing {accLst.len} list items for state root ..{root.pp}": test &"Proofing {accLst.len} list items for state root ..{root.pp}":
accLst.test_accountsImport(desc, db.persistent) accLst.test_accountsImport(desc, db.persistent)
# debugging, make sure that state root ~ "$0"
desc.assignPrettyKeys()
# Beware: dumping a large database is not recommended
# true.say "***", "database dump\n ", desc.dumpHexaDB()
test &"Retrieve accounts & proofs for previous account ranges": test &"Retrieve accounts & proofs for previous account ranges":
let nPart = 3
if db.persistent: if db.persistent:
accLst.test_NodeRangeRightProofs(getFn, nPart, dbg) accLst.test_NodeRangeProof(getFn, dbg)
else: else:
accLst.test_NodeRangeRightProofs(hexaDB, nPart, dbg) accLst.test_NodeRangeProof(hexaDB, dbg)
test &"Verify left boundary checks": test &"Verify left boundary checks":
if db.persistent: if db.persistent:
@ -235,8 +238,6 @@ proc accountsRunner(noisy = true; persistent = true; sample = accSample) =
test &"Revisiting {accKeys.len} stored items on ChainDBRef": test &"Revisiting {accKeys.len} stored items on ChainDBRef":
accKeys.test_accountsRevisitStoredItems(desc, noisy) accKeys.test_accountsRevisitStoredItems(desc, noisy)
# Beware: dumping a large database is not recommended
# true.say "***", "database dump\n ", desc.dumpHexaDB()
test &"Decompose path prefix envelopes on {info}": test &"Decompose path prefix envelopes on {info}":
let hexaDb = desc.hexaDb let hexaDb = desc.hexaDb
@ -286,6 +287,7 @@ proc storagesRunner(
test &"Inspecting {stoLst.len} imported storages lists sub-tries": test &"Inspecting {stoLst.len} imported storages lists sub-tries":
stoLst.test_storageSlotsTries(xdb, db.persistent, knownFailures,idPfx) stoLst.test_storageSlotsTries(xdb, db.persistent, knownFailures,idPfx)
proc inspectionRunner( proc inspectionRunner(
noisy = true; noisy = true;
persistent = true; persistent = true;
@ -547,24 +549,20 @@ when isMainModule:
# #
# This one uses dumps from the external `nimbus-eth1-blob` repo # This one uses dumps from the external `nimbus-eth1-blob` repo
when true: # and false: when true and false:
import ./test_sync_snap/snap_other_xx import ./test_sync_snap/snap_other_xx
noisy.showElapsed("accountsRunner()"): noisy.showElapsed("accountsRunner()"):
for n,sam in snapOtherList: for n,sam in snapOtherList:
false.accountsRunner(persistent=true, sam) false.accountsRunner(persistent=true, sam)
#noisy.showElapsed("inspectRunner()"): noisy.showElapsed("inspectRunner()"):
# for n,sam in snapOtherHealingList: for n,sam in snapOtherHealingList:
# false.inspectionRunner(persistent=true, cascaded=false, sam) false.inspectionRunner(persistent=true, cascaded=false, sam)
# This one usues dumps from the external `nimbus-eth1-blob` repo # This one usues dumps from the external `nimbus-eth1-blob` repo
when true and false: when true and false:
import ./test_sync_snap/snap_storage_xx import ./test_sync_snap/snap_storage_xx
let knownFailures = @[ let knownFailures: KnownStorageFailure = @[
("storages3__18__25_dump#11", @[( 233, RightBoundaryProofFailed)]),
("storages4__26__33_dump#11", @[(1193, RightBoundaryProofFailed)]),
("storages5__34__41_dump#10", @[( 508, RootNodeMismatch)]), ("storages5__34__41_dump#10", @[( 508, RootNodeMismatch)]),
("storagesB__84__92_dump#6", @[( 325, RightBoundaryProofFailed)]),
("storagesD_102_109_dump#17", @[(1102, RightBoundaryProofFailed)]),
] ]
noisy.showElapsed("storageRunner()"): noisy.showElapsed("storageRunner()"):
for n,sam in snapStorageList: for n,sam in snapStorageList:
@ -572,14 +570,14 @@ when isMainModule:
# This one uses readily available dumps # This one uses readily available dumps
when true: # and false: when true: # and false:
# false.inspectionRunner() false.inspectionRunner()
for n,sam in snapTestList: for n,sam in snapTestList:
false.accountsRunner(persistent=false, sam) false.accountsRunner(persistent=false, sam)
false.accountsRunner(persistent=true, sam) false.accountsRunner(persistent=true, sam)
for n,sam in snapTestStorageList: for n,sam in snapTestStorageList:
false.accountsRunner(persistent=false, sam) false.accountsRunner(persistent=false, sam)
false.accountsRunner(persistent=true, sam) false.accountsRunner(persistent=true, sam)
# false.storagesRunner(persistent=true, sam) false.storagesRunner(persistent=true, sam)
# This one uses readily available dumps # This one uses readily available dumps
when true and false: when true and false:

View File

@ -9,19 +9,6 @@
# at your option. This file may not be copied, modified, or # at your option. This file may not be copied, modified, or
# distributed except according to those terms. # distributed except according to those terms.
import
std/os,
./test_types# Nimbus - Types, data structures and shared utilities used in network sync
#
# Copyright (c) 2018-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.
import import
std/os, std/os,
./test_types ./test_types

View File

@ -19,17 +19,23 @@ import
../../nimbus/sync/snap/worker/db/[hexary_desc, snapdb_accounts], ../../nimbus/sync/snap/worker/db/[hexary_desc, snapdb_accounts],
../replay/pp ../replay/pp
type
KnownStorageFailure* = seq[(string,seq[(int,HexaryError)])]
## (<sample-name> & "#" <instance>, @[(<slot-id>, <error-symbol>)), ..])
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Public helpers # Public helpers
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
proc isImportOk*(rc: Result[SnapAccountsGaps,HexaryError]): bool = template isImportOk*(rc: Result[SnapAccountsGaps,HexaryError]): bool =
if rc.isErr: if rc.isErr:
check rc.error == NothingSerious # prints an error if different check rc.error == NothingSerious # prints an error if different
false
elif 0 < rc.value.innerGaps.len: elif 0 < rc.value.innerGaps.len:
check rc.value.innerGaps == seq[NodeSpecs].default check rc.value.innerGaps == seq[NodeSpecs].default
false
else: else:
return true true
proc lastTwo*(a: openArray[string]): seq[string] = proc lastTwo*(a: openArray[string]): seq[string] =
if 1 < a.len: @[a[^2],a[^1]] else: a.toSeq if 1 < a.len: @[a[^2],a[^1]] else: a.toSeq

View File

@ -12,15 +12,16 @@
## Snap sync components tester and TDD environment ## Snap sync components tester and TDD environment
import import
std/[sequtils, strformat, strutils], std/[sequtils, sets, strformat, strutils],
eth/[common, p2p, rlp, trie/nibbles], eth/[common, p2p, rlp, trie/nibbles],
stew/[byteutils, interval_set, results], stew/[byteutils, interval_set, results],
unittest2, unittest2,
../../nimbus/sync/types, ../../nimbus/sync/types,
../../nimbus/sync/snap/range_desc, ../../nimbus/sync/snap/range_desc,
../../nimbus/sync/snap/worker/db/[ ../../nimbus/sync/snap/worker/db/[
hexary_desc, hexary_envelope, hexary_error, hexary_nearby, hexary_paths, hexary_desc, hexary_envelope, hexary_error, hexary_interpolate,
hexary_range, snapdb_accounts, snapdb_desc], hexary_import, hexary_nearby, hexary_paths, hexary_range,
snapdb_accounts, snapdb_desc],
../replay/[pp, undump_accounts], ../replay/[pp, undump_accounts],
./test_helpers ./test_helpers
@ -28,6 +29,18 @@ const
cmaNlSp0 = ",\n" & repeat(" ",12) cmaNlSp0 = ",\n" & repeat(" ",12)
cmaNlSpc = ",\n" & repeat(" ",13) cmaNlSpc = ",\n" & repeat(" ",13)
# ------------------------------------------------------------------------------
# Private helpers
# ------------------------------------------------------------------------------
proc ppNodeKeys(a: openArray[Blob], dbg = HexaryTreeDbRef(nil)): string =
result = "["
if dbg.isNil:
result &= a.mapIt(it.digestTo(NodeKey).pp(collapse=true)).join(",")
else:
result &= a.mapIt(it.digestTo(NodeKey).pp(dbg)).join(",")
result &= "]"
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Private functions # Private functions
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
@ -132,18 +145,14 @@ proc printCompareRightLeafs(
else: else:
check rightRc.error == HexaryError(0) # force error printing check rightRc.error == HexaryError(0) # force error printing
noisy.say "\n***", "i=", i, "/", accounts.len, if noisy: true.say "\n***", "i=", i, "/", accounts.len,
"\n", "\n\n prdKey=", prdKey,
"\n prdKey=", prdKey,
"\n ", prdKey.hexaryPath(rootKey,db).pp(dbg), "\n ", prdKey.hexaryPath(rootKey,db).pp(dbg),
"\n", "\n\n nxtKey=", nxtTag,
"\n nxtKey=", nxtTag,
"\n ", nxtPath.pp(dbg), "\n ", nxtPath.pp(dbg),
"\n", "\n\n accKey=", accKey,
"\n accKey=", accKey,
"\n ", accKey.hexaryPath(rootKey,db).pp(dbg), "\n ", accKey.hexaryPath(rootKey,db).pp(dbg),
"\n", "\n\n lfsKey=", lfsKey,
"\n lfsKey=", lfsKey,
"\n ", lfsKey.hexaryPath(rootKey,db).pp(dbg), "\n ", lfsKey.hexaryPath(rootKey,db).pp(dbg),
"\n" "\n"
return return
@ -170,14 +179,12 @@ proc printCompareLeftNearby(
if toLeftKey == leftKey: if toLeftKey == leftKey:
return return
noisy.say "\n***", if noisy: true.say "\n***",
" rightKey=", rightKey, " rightKey=", rightKey,
"\n ", rightKey.hexaryPath(rootKey,db).pp(dbg), "\n ", rightKey.hexaryPath(rootKey,db).pp(dbg),
"\n", "\n\n leftKey=", leftKey,
"\n leftKey=", leftKey,
"\n ", leftKey.hexaryPath(rootKey,db).pp(dbg), "\n ", leftKey.hexaryPath(rootKey,db).pp(dbg),
"\n", "\n\n toLeftKey=", toLeftKey,
"\n toLeftKey=", toLeftKey,
"\n ", toLeftKey.hexaryPath(rootKey,db).pp(dbg), "\n ", toLeftKey.hexaryPath(rootKey,db).pp(dbg),
"\n" "\n"
@ -191,6 +198,52 @@ proc verifyAccountListSizes() =
nonce: high(uint64), nonce: high(uint64),
balance: high(UInt256)).repeat(n).encode.len balance: high(UInt256)).repeat(n).encode.len
proc verifyRangeProof(
rootKey: NodeKey;
leafs: seq[RangeLeaf];
proof: seq[Blob];
dbg = HexaryTreeDbRef(nil);
): Result[void,HexaryError] =
## Re-build temporary database and prove or disprove
let
dumpOk = dbg.isNil.not
noisy = dbg.isNil.not
xDb = HexaryTreeDbRef()
if not dbg.isNil:
xDb.keyPp = dbg.keyPp
# Import proof nodes
var unrefs, refs: HashSet[RepairKey] # values ignored
for rlpRec in proof:
let importError = xDb.hexaryImport(rlpRec, unrefs, refs).error
if importError != HexaryError(0):
check importError == HexaryError(0)
return err(importError)
# Build tree
var lItems = leafs.mapIt(RLeafSpecs(
pathTag: it.key.to(NodeTag),
payload: it.data))
let rc = xDb.hexaryInterpolate(rootKey, lItems)
if rc.isOk:
return ok()
if noisy:
true.say "\n***", "error=", rc.error,
#"\n",
#"\n unrefs=[", unrefs.toSeq.mapIt(it.pp(dbg)).join(","), "]",
#"\n refs=[", refs.toSeq.mapIt(it.pp(dbg)).join(","), "]",
"\n\n proof=", proof.ppNodeKeys(dbg),
"\n\n first=", leafs[0].key,
"\n ", leafs[0].key.hexaryPath(rootKey,xDb).pp(dbg),
"\n\n last=", leafs[^1].key,
"\n ", leafs[^1].key.hexaryPath(rootKey,xDb).pp(dbg),
"\n\n database dump",
"\n ", xDb.dumpHexaDB(rootKey),
"\n"
rc
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# Private functions, pretty printing # Private functions, pretty printing
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
@ -322,10 +375,9 @@ proc test_NodeRangeDecompose*(
if true: quit() if true: quit()
proc test_NodeRangeRightProofs*( proc test_NodeRangeProof*(
inLst: seq[UndumpAccounts]; inLst: seq[UndumpAccounts];
db: HexaryTreeDbRef|HexaryGetFn; ## Database abstraction db: HexaryTreeDbRef|HexaryGetFn; ## Database abstraction
nSplit = 0; ## Also split intervals (unused)
dbg = HexaryTreeDbRef(nil); ## Debugging env dbg = HexaryTreeDbRef(nil); ## Debugging env
) = ) =
## Partition range and provide proofs suitable for `GetAccountRange` message ## Partition range and provide proofs suitable for `GetAccountRange` message
@ -333,6 +385,7 @@ proc test_NodeRangeRightProofs*(
let let
rootKey = inLst[0].root.to(NodeKey) rootKey = inLst[0].root.to(NodeKey)
noisy = not dbg.isNil noisy = not dbg.isNil
maxLen = high(int)
# RLP does not allow static check # RLP does not allow static check
verifyAccountListSizes() verifyAccountListSizes()
@ -340,25 +393,44 @@ proc test_NodeRangeRightProofs*(
# Assuming the `inLst` entries have been stored in the DB already # Assuming the `inLst` entries have been stored in the DB already
for n,w in inLst: for n,w in inLst:
let let
iv = NodeTagRange.new(w.base, w.data.accounts[^1].accKey.to(NodeTag)) accounts = w.data.accounts[0 .. min(w.data.accounts.len,maxLen)-1]
rc = iv.hexaryRangeLeafsProof(rootKey, db, high(int)) iv = NodeTagRange.new(w.base, accounts[^1].accKey.to(NodeTag))
rc = iv.hexaryRangeLeafsProof(rootKey, db, accounts.len)
check rc.isOk check rc.isOk
if rc.isErr: if rc.isErr:
return return
let let leafs = rc.value.leafs
leafs = rc.value.leafs
accounts = w.data.accounts
if leafs.len != accounts.len or accounts[^1].accKey != leafs[^1].key: if leafs.len != accounts.len or accounts[^1].accKey != leafs[^1].key:
noisy.say "***", "n=", n, " something went wrong .." noisy.say "***", "n=", n, " something went wrong .."
check (n,leafs.len) == (n,accounts.len) check (n,leafs.len) == (n,accounts.len)
rootKey.printCompareRightLeafs(w.base, accounts, leafs, db, dbg) rootKey.printCompareRightLeafs(w.base, accounts, leafs, db, dbg)
return return
# FIXME: verify that proof nodes are complete # Import proof nodes and build trie
var rx = rootKey.verifyRangeProof(leafs, rc.value.proof)
if rx.isErr:
rx = rootKey.verifyRangeProof(leafs, rc.value.proof, dbg)
let
baseNbls = iv.minPt.to(NodeKey).to(NibblesSeq)
lastNbls = iv.maxPt.to(NodeKey).to(NibblesSeq)
nPfxNblsLen = baseNbls.sharedPrefixLen lastNbls
pfxNbls = baseNbls.slice(0, nPfxNblsLen)
noisy.say "***", "n=", n,
" leafs=", leafs.len,
" proof=", rc.value.proof.ppNodeKeys(dbg),
"\n\n ",
" base=", iv.minPt,
"\n ", iv.minPt.hexaryPath(rootKey,db).pp(dbg),
"\n\n ",
" pfx=", pfxNbls,
" nPfx=", nPfxNblsLen,
"\n ", pfxNbls.hexaryPath(rootKey,db).pp(dbg),
"\n"
check rx == typeof(rx).ok()
return
check rc.value.proof.len <= w.data.proof.len
check leafs[^1].key.to(NodeTag) <= iv.maxPt
noisy.say "***", "n=", n, noisy.say "***", "n=", n,
" leafs=", leafs.len, " leafs=", leafs.len,
" proof=", rc.value.proof.len, "/", w.data.proof.len " proof=", rc.value.proof.len, "/", w.data.proof.len

View File

@ -58,7 +58,7 @@ proc test_storageSlotsImport*(
inList: seq[UndumpStorages]; inList: seq[UndumpStorages];
dbBase: SnapDbRef; dbBase: SnapDbRef;
persistent: bool; persistent: bool;
ignore: seq[(string,seq[(int,HexaryError)])]; ignore: KnownStorageFailure;
idPfx: string; idPfx: string;
) = ) =
## Import and merge storages lists ## Import and merge storages lists
@ -80,7 +80,7 @@ proc test_storageSlotsTries*(
inList: seq[UndumpStorages]; inList: seq[UndumpStorages];
dbBase: SnapDbRef; dbBase: SnapDbRef;
persistent: bool; persistent: bool;
ignore: seq[(string,seq[(int,HexaryError)])]; ignore: KnownStorageFailure;
idPfx: string; idPfx: string;
) = ) =
## Inspecting imported storages lists sub-tries ## Inspecting imported storages lists sub-tries