Aristo avoid storage trie update race conditions (#2251)
* Update TDD suite logger output format choices why: New format is not practical for TDD as it just dumps data across a wide range (considerably larder than 80 columns.) So the new format can be turned on by function argument. * Update unit tests samples configuration why: Slightly changed the way to find the `era1` directory * Remove compiler warnings (fix deprecated expressions and phrases) * Update `Aristo` debugging tools * Always update the `storageID` field of account leaf vertices why: Storage tries are weekly linked to an account leaf object in that the `storageID` field is updated by the application. Previously, `Aristo` verified that leaf objects make sense when passed to the database. As a consequence * the database was inconsistent for a short while * the burden for correctness was all on the application which led to delayed error handling which is hard to debug. So `Aristo` will internally update the account leaf objects so that there are no race conditions due to the storage trie handling * Aristo: Let `stow()`/`persist()` bail out unless there is a `VertexID(1)` why: The journal and filter logic depends on the hash of the `VertexID(1)` which is commonly known as the state root. This implies that all changes to the database are somehow related to that. * Make sure that a `Ledger` account does not overwrite the storage trie reference why: Due to the abstraction of a sub-trie (now referred to as column with a hash describing its state) there was a weakness in the `Aristo` handler where an account leaf could be overwritten though changing the validity of the database. This has been changed and the database will now reject such changes. This patch fixes the behaviour on the application layer. In particular, the column handle returned by the `CoreDb` needs to be updated by the `Aristo` database state. This mitigates the problem that a storage trie might have vanished or re-apperaed with a different vertex ID. * Fix sub-trie deletion test why: Was originally hinged on `VertexID(1)` which cannot be wholesale deleted anymore after the last Aristo update. Also, running with `VertexID(2)` needs an artificial `VertexID(1)` for making `stow()` or `persist()` work. * Cosmetics * Activate `test_generalstate_json` * Temporarily `deactivate test_tracer_json` * Fix copyright header --------- Co-authored-by: jordan <jordan@dry.pudding> Co-authored-by: Jacek Sieka <jacek@status.im>
This commit is contained in:
parent
d814d84b9b
commit
0f430c70fd
|
@ -34,8 +34,6 @@ type
|
|||
|
||||
GenesisRootHashFn = proc: Hash256 {.noRaise.}
|
||||
|
||||
GenesisGetTrieFn = proc: CoreDbMptRef {.noRaise.}
|
||||
|
||||
GenesisLedgerRef* = ref object
|
||||
## Exportable ledger DB just for initialising Genesis.
|
||||
##
|
||||
|
@ -43,7 +41,6 @@ type
|
|||
setStorage: GenesisSetStorageFn
|
||||
commit: GenesisCommitFn
|
||||
rootHash: GenesisRootHashFn
|
||||
getTrie: GenesisGetTrieFn
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Private functions
|
||||
|
@ -77,10 +74,7 @@ proc initAccountsLedgerRef(
|
|||
ac.persist(),
|
||||
|
||||
rootHash: proc(): Hash256 =
|
||||
ac.state(),
|
||||
|
||||
getTrie: proc(): CoreDbMptRef =
|
||||
ac.getMpt())
|
||||
ac.state())
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Public functions
|
||||
|
@ -91,10 +85,6 @@ proc newStateDB*(
|
|||
): GenesisLedgerRef =
|
||||
db.initAccountsLedgerRef()
|
||||
|
||||
proc getTrie*(sdb: GenesisLedgerRef): CoreDbMptRef =
|
||||
## Getter, used in `test_state_network`
|
||||
sdb.getTrie()
|
||||
|
||||
proc toGenesisHeader*(
|
||||
g: Genesis;
|
||||
sdb: GenesisLedgerRef;
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
* Check whether `HashKey` can be reduced to a simple 32 byte array (see
|
||||
*desc_identifiers.nim*)
|
||||
|
||||
* Remove the `RlpData` accounts payload type. It is not needed as a separate
|
||||
data type. An application must know the layout. So it can be subsumed
|
||||
under `RawData` (which could be renamed `PlainData`.)
|
||||
|
||||
* Currently, the data save/store logic only works when there is s VertexID(1)
|
||||
root. In tests without a VertexID(1) a dummy node is set up.
|
|
@ -172,7 +172,7 @@ proc blobify*(filter: FilterRef; data: var Blob): Result[void,AristoError] =
|
|||
##
|
||||
func blobify(lid: HashKey): Blob =
|
||||
let n = lid.len
|
||||
if n < 32: @[n.byte] & @lid & 0u8.repeat(31 - n) else: @lid
|
||||
if n < 32: @[n.byte] & @(lid.data) & 0u8.repeat(31 - n) else: @(lid.data)
|
||||
|
||||
if not filter.isValid:
|
||||
return err(BlobifyNilFilter)
|
||||
|
|
|
@ -92,6 +92,10 @@ proc checkTopCommon*(
|
|||
let
|
||||
kMapCount = db.layersWalkKey.toSeq.mapIt(it[1]).filterIt(it.isValid).len
|
||||
kMapNilCount = db.layersWalkKey.toSeq.len - kMapCount
|
||||
vGen = db.vGen.toHashSet
|
||||
vGenMax = if vGen.len == 0: VertexID(0) else: db.vGen[^1]
|
||||
var
|
||||
stoRoots: HashSet[VertexID]
|
||||
|
||||
# Collect leafs and check deleted entries
|
||||
var nNilVtx = 0
|
||||
|
@ -99,7 +103,14 @@ proc checkTopCommon*(
|
|||
if vtx.isValid:
|
||||
case vtx.vType:
|
||||
of Leaf:
|
||||
discard
|
||||
if vtx.lData.pType == AccountData:
|
||||
let stoVid = vtx.lData.account.storageID
|
||||
if stoVid.isValid:
|
||||
if stoVid in stoRoots:
|
||||
return err((stoVid,CheckAnyVidSharedStorageRoot))
|
||||
if vGenMax.isValid and (vGenMax < stoVid or stoVid in vGen):
|
||||
return err((stoVid,CheckAnyVidDeadStorageRoot))
|
||||
stoRoots.incl stoVid
|
||||
of Branch:
|
||||
block check42Links:
|
||||
var seen = false
|
||||
|
|
|
@ -130,12 +130,17 @@ proc ppVid(vid: VertexID; pfx = true): string =
|
|||
|
||||
proc ppVids(vids: HashSet[VertexID]): string =
|
||||
result = "{"
|
||||
for vid in vids.toSeq.sorted:
|
||||
result = "$"
|
||||
if vid.isValid:
|
||||
result &= vid.toHex.stripZeros.toLowerAscii
|
||||
else:
|
||||
result &= "ø"
|
||||
if vids.len == 0:
|
||||
result &= "}"
|
||||
else:
|
||||
for vid in vids.toSeq.sorted:
|
||||
result &= "$"
|
||||
if vid.isValid:
|
||||
result &= vid.toHex.stripZeros.toLowerAscii
|
||||
else:
|
||||
result &= "ø"
|
||||
result &= ","
|
||||
result[^1] = '}'
|
||||
|
||||
func ppCodeHash(h: Hash256): string =
|
||||
result = "¢"
|
||||
|
@ -173,7 +178,14 @@ proc ppQid(qid: QueueID): string =
|
|||
result &= qid.toHex.stripZeros
|
||||
|
||||
proc ppVidList(vGen: openArray[VertexID]): string =
|
||||
"[" & vGen.mapIt(it.ppVid).join(",") & "]"
|
||||
result = "["
|
||||
if vGen.len <= 250:
|
||||
result &= vGen.mapIt(it.ppVid).join(",")
|
||||
else:
|
||||
result &= vGen[0 .. 99].mapIt(it.ppVid).join(",")
|
||||
result &= ",.."
|
||||
result &= vGen[^100 .. ^1].mapIt(it.ppVid).join(",")
|
||||
result &= "]"
|
||||
|
||||
proc ppKey(key: HashKey; db: AristoDbRef; pfx = true): string =
|
||||
proc getVids(): tuple[vids: HashSet[VertexID], xMapTag: string] =
|
||||
|
@ -204,7 +216,7 @@ proc ppKey(key: HashKey; db: AristoDbRef; pfx = true): string =
|
|||
if 1 < vids.len: result &= "}"
|
||||
result &= tag
|
||||
return
|
||||
result &= @key.toHex.squeeze(hex=true,ignLen=true) & tag
|
||||
result &= @(key.data).toHex.squeeze(hex=true,ignLen=true) & tag
|
||||
|
||||
proc ppLeafTie(lty: LeafTie, db: AristoDbRef): string =
|
||||
let pfx = lty.path.to(NibblesSeq)
|
||||
|
@ -435,7 +447,7 @@ proc ppFilter(
|
|||
result &= $(1+n) & "(" & vid.ppVid & "," & key.ppKey(db) & ")"
|
||||
result &= "}"
|
||||
|
||||
proc ppBe[T](be: T; db: AristoDbRef; indent: int): string =
|
||||
proc ppBe[T](be: T; db: AristoDbRef; limit: int; indent: int): string =
|
||||
## Walk over backend tables
|
||||
let
|
||||
pfx = indent.toPfx
|
||||
|
@ -445,19 +457,21 @@ proc ppBe[T](be: T; db: AristoDbRef; indent: int): string =
|
|||
var (dump,dataOk) = ("",false)
|
||||
dump &= pfx & "vGen"
|
||||
block:
|
||||
let q = be.getIdgFn().get(otherwise = EmptyVidSeq).mapIt(it.ppVid)
|
||||
let q = be.getIdgFn().get(otherwise = EmptyVidSeq)
|
||||
dump &= "(" & $q.len & ")"
|
||||
if 0 < q.len:
|
||||
dataOk = true
|
||||
dump &= pfx1
|
||||
dump &= "[" & q.join(",") & "]"
|
||||
dump &= pfx1 & q.ppVidList()
|
||||
block:
|
||||
dump &= pfx & "sTab"
|
||||
var (n, data) = (0, "")
|
||||
for (vid,vtx) in be.walkVtx:
|
||||
if 0 < n: data &= pfx2
|
||||
n.inc
|
||||
data &= $n & "(" & vid.ppVid & "," & vtx.ppVtx(db,vid) & ")"
|
||||
if n < limit:
|
||||
if 1 < n: data &= pfx2
|
||||
data &= $n & "(" & vid.ppVid & "," & vtx.ppVtx(db,vid) & ")"
|
||||
elif n == limit:
|
||||
data &= pfx2 & ".."
|
||||
dump &= "(" & $n & ")"
|
||||
if 0 < n:
|
||||
dataOk = true
|
||||
|
@ -467,9 +481,12 @@ proc ppBe[T](be: T; db: AristoDbRef; indent: int): string =
|
|||
dump &= pfx & "kMap"
|
||||
var (n, data) = (0, "")
|
||||
for (vid,key) in be.walkKey:
|
||||
if 0 < n: data &= pfx2
|
||||
n.inc
|
||||
data &= $n & "(" & vid.ppVid & "," & key.ppKey(db) & ")"
|
||||
if n < limit:
|
||||
if 1 < n: data &= pfx2
|
||||
data &= $n & "(" & vid.ppVid & "," & key.ppKey(db) & ")"
|
||||
elif n == limit:
|
||||
data &= pfx2 & ".."
|
||||
dump &= "(" & $n & ")"
|
||||
if 0 < n:
|
||||
dataOk = true
|
||||
|
@ -542,7 +559,7 @@ proc ppLayer(
|
|||
if 0 < nOKs:
|
||||
let
|
||||
info = if layer.final.dirty.len == 0: "clean"
|
||||
else: "dirty{" & layer.final.dirty.ppVids & "}"
|
||||
else: "dirty" & layer.final.dirty.ppVids
|
||||
result &= info.doPrefix(false)
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
@ -757,14 +774,15 @@ proc pp*(
|
|||
proc pp*(
|
||||
be: BackendRef;
|
||||
db: AristoDbRef;
|
||||
limit = 100;
|
||||
indent = 4;
|
||||
): string =
|
||||
result = db.roFilter.ppFilter(db, indent+1) & indent.toPfx
|
||||
case be.kind:
|
||||
of BackendMemory:
|
||||
result &= be.MemBackendRef.ppBe(db, indent+1)
|
||||
result &= be.MemBackendRef.ppBe(db, limit, indent+1)
|
||||
of BackendRocksDB:
|
||||
result &= be.RdbBackendRef.ppBe(db, indent+1)
|
||||
result &= be.RdbBackendRef.ppBe(db, limit, indent+1)
|
||||
of BackendVoid:
|
||||
result &= "<NoBackend>"
|
||||
|
||||
|
@ -776,6 +794,7 @@ proc pp*(
|
|||
topOk = true;
|
||||
stackOk = true;
|
||||
kMapOk = true;
|
||||
limit = 100;
|
||||
): string =
|
||||
if topOk:
|
||||
result = db.layersCc.pp(
|
||||
|
@ -796,7 +815,7 @@ proc pp*(
|
|||
lStr &= " " & $m & "=(" & $(l.delta.kMap.len - c) & "," & $c & ")"
|
||||
result &= " =>" & lStr
|
||||
if backendOk:
|
||||
result &= indent.toPfx & db.backend.pp(db)
|
||||
result &= indent.toPfx & db.backend.pp(db, limit=limit, indent)
|
||||
elif filterOk:
|
||||
result &= indent.toPfx & db.roFilter.ppFilter(db, indent+1)
|
||||
|
||||
|
|
|
@ -256,13 +256,19 @@ proc delSubTreeImpl(
|
|||
accPath: PathID; # Needed for real storage tries
|
||||
): Result[void,(VertexID,AristoError)] =
|
||||
## Implementation of *delete* sub-trie.
|
||||
if not root.isValid:
|
||||
return err((root,DelSubTreeVoidRoot))
|
||||
|
||||
if LEAST_FREE_VID <= root.distinctBase:
|
||||
db.registerAccount(root, accPath).isOkOr:
|
||||
return err((root,error))
|
||||
|
||||
let wp = block:
|
||||
if root.distinctBase < LEAST_FREE_VID:
|
||||
if not root.isValid:
|
||||
return err((root,DelSubTreeVoidRoot))
|
||||
if root == VertexID(1):
|
||||
return err((root,DelSubTreeAccRoot))
|
||||
VidVtxPair()
|
||||
else:
|
||||
let rc = db.registerAccount(root, accPath)
|
||||
if rc.isErr:
|
||||
return err((root,rc.error))
|
||||
else:
|
||||
rc.value
|
||||
var
|
||||
dispose = @[root]
|
||||
rootVtx = db.getVtxRc(root).valueOr:
|
||||
|
@ -287,6 +293,13 @@ proc delSubTreeImpl(
|
|||
for vid in dispose:
|
||||
db.disposeOfVtx(root, vid)
|
||||
|
||||
# Make sure that an account leaf has no dangling sub-trie
|
||||
if wp.vid.isValid:
|
||||
let leaf = wp.vtx.dup # Dup on modify
|
||||
leaf.lData.account.storageID = VertexID(0)
|
||||
db.layersPutVtx(VertexID(1), wp.vid, leaf)
|
||||
db.layersResKey(VertexID(1), wp.vid)
|
||||
|
||||
# Squeze list of recycled vertex IDs
|
||||
db.top.final.vGen = db.vGen.vidReorg()
|
||||
ok()
|
||||
|
@ -300,9 +313,15 @@ proc deleteImpl(
|
|||
): Result[bool,(VertexID,AristoError)] =
|
||||
## Implementation of *delete* functionality.
|
||||
|
||||
if LEAST_FREE_VID <= lty.root.distinctBase:
|
||||
db.registerAccount(lty.root, accPath).isOkOr:
|
||||
return err((lty.root,error))
|
||||
let wp = block:
|
||||
if lty.root.distinctBase < LEAST_FREE_VID:
|
||||
VidVtxPair()
|
||||
else:
|
||||
let rc = db.registerAccount(lty.root, accPath)
|
||||
if rc.isErr:
|
||||
return err((lty.root,rc.error))
|
||||
else:
|
||||
rc.value
|
||||
|
||||
# Remove leaf entry on the top
|
||||
let lf = hike.legs[^1].wp
|
||||
|
@ -311,7 +330,7 @@ proc deleteImpl(
|
|||
if lf.vid in db.pPrf:
|
||||
return err((lf.vid, DelLeafLocked))
|
||||
|
||||
# Verify thet there is no dangling storage trie
|
||||
# Verify that there is no dangling storage trie
|
||||
block:
|
||||
let data = lf.vtx.lData
|
||||
if data.pType == AccountData:
|
||||
|
@ -367,6 +386,13 @@ proc deleteImpl(
|
|||
|
||||
let emptySubTreeOk = not db.getVtx(hike.root).isValid
|
||||
|
||||
# Make sure that an account leaf has no dangling sub-trie
|
||||
if emptySubTreeOk and wp.vid.isValid:
|
||||
let leaf = wp.vtx.dup # Dup on modify
|
||||
leaf.lData.account.storageID = VertexID(0)
|
||||
db.layersPutVtx(VertexID(1), wp.vid, leaf)
|
||||
db.layersResKey(VertexID(1), wp.vid)
|
||||
|
||||
# Squeze list of recycled vertex IDs
|
||||
db.top.final.vGen = db.vGen.vidReorg()
|
||||
ok(emptySubTreeOk)
|
||||
|
@ -384,11 +410,18 @@ proc delTree*(
|
|||
## `SUB_TREE_DISPOSAL_MAX`. Larger tries must be disposed by walk-deleting
|
||||
## leaf nodes using `left()` or `right()` traversal functions.
|
||||
##
|
||||
## For a `root` argument greater than `LEAST_FREE_VID`, the sub-tree spanned
|
||||
## by `root` is considered a storage trie linked to an account leaf referred
|
||||
## to by a valid `accPath` (i.e. different from `VOID_PATH_ID`.) In that
|
||||
## case, an account must exists. If there is payload of type `AccountData`,
|
||||
## its `storageID` field must be unset or equal to the `hike.root` vertex ID.
|
||||
## Note that the accounts trie hinging on `VertexID(1)` cannot be deleted.
|
||||
##
|
||||
## If the `root` argument belongs to a well known sub trie (i.e. it does
|
||||
## not exceed `LEAST_FREE_VID`) the `accPath` argument is ignored and the
|
||||
## sub-trie will just be deleted.
|
||||
##
|
||||
## Otherwise, a valid `accPath` (i.e. different from `VOID_PATH_ID`.) is
|
||||
## required relating to an account leaf entry (starting at `VertexID(`)`).
|
||||
## If the payload of that leaf entry is not of type `AccountData` it is
|
||||
## ignored. Otherwise its `storageID` field must be equal to the `hike.root`
|
||||
## vertex ID. This leaf entry `storageID` field will be reset to
|
||||
## `VertexID(0)` after having deleted the sub-trie.
|
||||
##
|
||||
db.delSubTreeImpl(root, accPath)
|
||||
|
||||
|
@ -397,16 +430,26 @@ proc delete*(
|
|||
hike: Hike; # Fully expanded chain of vertices
|
||||
accPath: PathID; # Needed for accounts payload
|
||||
): Result[bool,(VertexID,AristoError)] =
|
||||
## Delete argument `hike` chain of vertices from the database.
|
||||
## Delete argument `hike` chain of vertices from the database. The return
|
||||
## code will be `true` iff the sub-trie starting at `hike.root` will have
|
||||
## become empty.
|
||||
##
|
||||
## For a `hike.root` with `VertexID` greater than `LEAST_FREE_VID`, the
|
||||
## sub-tree generated by `payload.root` is considered a storage trie linked
|
||||
## to an account leaf referred to by a valid `accPath` (i.e. different from
|
||||
## `VOID_PATH_ID`.) In that case, an account must exists. If there is payload
|
||||
## of type `AccountData`, its `storageID` field must be unset or equal to the
|
||||
## `hike.root` vertex ID.
|
||||
## If the `hike` argument referes to aa account entrie (i.e. `hike.root`
|
||||
## equals `VertexID(1)`) and the leaf entry has an `AccountData` payload,
|
||||
## its `storageID` field must have been reset to `VertexID(0)`. the
|
||||
## `accPath` argument will be ignored.
|
||||
##
|
||||
## The return code is `true` iff the trie has become empty.
|
||||
## Otherwise, if the `root` argument belongs to a well known sub trie (i.e.
|
||||
## it does not exceed `LEAST_FREE_VID`) the `accPath` argument is ignored
|
||||
## and the entry will just be deleted.
|
||||
##
|
||||
## Otherwise, a valid `accPath` (i.e. different from `VOID_PATH_ID`.) is
|
||||
## required relating to an account leaf entry (starting at `VertexID(`)`).
|
||||
## If the payload of that leaf entry is not of type `AccountData` it is
|
||||
## ignored. Otherwise its `storageID` field must be equal to the `hike.root`
|
||||
## vertex ID. This leaf entry `storageID` field will be reset to
|
||||
## `VertexID(0)` in case the entry to be deleted will render the sub-trie
|
||||
## empty.
|
||||
##
|
||||
let lty = LeafTie(
|
||||
root: hike.root,
|
||||
|
|
|
@ -83,14 +83,17 @@ type
|
|||
PathExpectedLeaf
|
||||
|
||||
# Merge leaf `merge()`
|
||||
MergeBranchLinkLeafGarbled
|
||||
MergeBranchLinkVtxPfxTooShort
|
||||
MergeAssemblyFailed # Ooops, internal error
|
||||
MergeBranchGarbledNibble
|
||||
MergeBranchGarbledTail
|
||||
MergeBranchLinkLeafGarbled
|
||||
MergeBranchLinkLockedKey
|
||||
MergeBranchLinkProofModeLock
|
||||
MergeBranchLinkVtxPfxTooShort
|
||||
MergeBranchProofModeLock
|
||||
MergeBranchRootExpected
|
||||
MergeLeafCantChangePayloadType
|
||||
MergeLeafCantChangeStorageID
|
||||
MergeLeafGarbledHike
|
||||
MergeLeafPathCachedAlready
|
||||
MergeLeafPathOnBackendAlready
|
||||
|
@ -98,7 +101,6 @@ type
|
|||
MergeNonBranchProofModeLock
|
||||
MergeRootBranchLinkBusy
|
||||
MergeRootMissing
|
||||
MergeAssemblyFailed # Ooops, internal error
|
||||
|
||||
MergeHashKeyInvalid
|
||||
MergeHashKeyDiffersFromCached
|
||||
|
@ -140,6 +142,8 @@ type
|
|||
CheckRlxVtxKeyMissing
|
||||
CheckRlxVtxKeyMismatch
|
||||
|
||||
CheckAnyVidDeadStorageRoot
|
||||
CheckAnyVidSharedStorageRoot
|
||||
CheckAnyVtxEmptyKeyMissing
|
||||
CheckAnyVtxEmptyKeyExpected
|
||||
CheckAnyVtxEmptyKeyMismatch
|
||||
|
@ -202,6 +206,7 @@ type
|
|||
DelLeafUnexpected
|
||||
DelPathNotFound
|
||||
DelPathTagError
|
||||
DelSubTreeAccRoot
|
||||
DelSubTreeTooBig
|
||||
DelSubTreeVoidRoot
|
||||
DelVidStaleVtx
|
||||
|
@ -261,6 +266,7 @@ type
|
|||
RdbHashKeyExpected
|
||||
|
||||
# Transaction wrappers
|
||||
TxAccRootMissing
|
||||
TxArgStaleTx
|
||||
TxArgsUseless
|
||||
TxBackendNotWritable
|
||||
|
|
|
@ -293,16 +293,6 @@ func cmp*(a, b: LeafTie): int =
|
|||
# Public helpers: Reversible conversions between `PathID`, `HashKey`, etc.
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
func to*(key: HashKey; T: type Blob): T {.deprecated.} =
|
||||
## Rewrite `HashKey` argument as `Blob` type of length between 0 and 32. A
|
||||
## blob of length 32 is taken as a representation of a `HashKey` type while
|
||||
## samller blobs are expected to represent an RLP encoded small node.
|
||||
@(key.data)
|
||||
|
||||
func `@`*(lid: HashKey): Blob {.deprecated.} =
|
||||
## Variant of `to(Blob)`
|
||||
lid.to(Blob)
|
||||
|
||||
func to*(pid: PathID; T: type NibblesSeq): T =
|
||||
## Representation of a `PathID` as `NibbleSeq` (preserving full information)
|
||||
let nibbles = pid.pfx.toBytesBE.toSeq.initNibbleRange()
|
||||
|
|
|
@ -397,7 +397,7 @@ iterator walk*(
|
|||
yield (VtxPfx, vid.uint64, data)
|
||||
|
||||
for (vid,key) in be.walkKey:
|
||||
yield (KeyPfx, vid.uint64, @key)
|
||||
yield (KeyPfx, vid.uint64, @(key.data))
|
||||
|
||||
if not be.mdb.noFq:
|
||||
for lid in be.mdb.rFil.keys.toSeq.mapIt(it.uint64).sorted.mapIt(it.QueueID):
|
||||
|
|
|
@ -218,7 +218,7 @@ proc putKeyFn(db: RdbBackendRef): PutKeyFn =
|
|||
var batch: seq[(uint64,Blob)]
|
||||
for (vid,key) in vkps:
|
||||
if key.isValid:
|
||||
batch.add (vid.uint64, @key)
|
||||
batch.add (vid.uint64, @(key.data))
|
||||
else:
|
||||
batch.add (vid.uint64, EmptyBlob)
|
||||
|
||||
|
|
|
@ -470,7 +470,7 @@ proc updatePayload(
|
|||
db: AristoDbRef; # Database, top layer
|
||||
hike: Hike; # No path legs
|
||||
leafTie: LeafTie; # Leaf item to add to the database
|
||||
payload: PayloadRef; # Payload value
|
||||
payload: PayloadRef; # Payload value to add
|
||||
): Result[Hike,AristoError] =
|
||||
## Update leaf vertex if payloads differ
|
||||
let leafLeg = hike.legs[^1]
|
||||
|
@ -481,6 +481,14 @@ proc updatePayload(
|
|||
if vid in db.pPrf:
|
||||
return err(MergeLeafProofModeLock)
|
||||
|
||||
# Verify that the account leaf can be replaced
|
||||
if leafTie.root == VertexID(1):
|
||||
if leafLeg.wp.vtx.lData.pType != payload.pType:
|
||||
return err(MergeLeafCantChangePayloadType)
|
||||
if payload.pType == AccountData and
|
||||
payload.account.storageID != leafLeg.wp.vtx.lData.account.storageID:
|
||||
return err(MergeLeafCantChangeStorageID)
|
||||
|
||||
# Update vertex and hike
|
||||
let vtx = VertexRef(
|
||||
vType: Leaf,
|
||||
|
@ -624,21 +632,41 @@ proc mergePayload*(
|
|||
): Result[Hike,AristoError] =
|
||||
## Merge the argument `leafTie` key-value-pair into the top level vertex
|
||||
## table of the database `db`. The field `path` of the `leafTie` argument is
|
||||
## used to index the leaf vertex on the `Patricia Trie`. The field `payload`
|
||||
## is stored with the leaf vertex in the database unless the leaf vertex
|
||||
## exists already.
|
||||
## used to address the leaf vertex with the payload. It is stored or updated
|
||||
## on the database accordingly.
|
||||
##
|
||||
## For a `payload.root` with `VertexID` greater than `LEAST_FREE_VID`, the
|
||||
## sub-tree generated by `payload.root` is considered a storage trie linked
|
||||
## to an account leaf referred to by a valid `accPath` (i.e. different from
|
||||
## `VOID_PATH_ID`.) In that case, an account must exists. If there is payload
|
||||
## of type `AccountData`, its `storageID` field must be unset or equal to the
|
||||
## `payload.root` vertex ID.
|
||||
## If the `leafTie` argument referes to aa account entrie (i.e. the
|
||||
## `leafTie.root` equals `VertexID(1)`) and the leaf entry has already an
|
||||
## `AccountData` payload, its `storageID` field must be the same as the one
|
||||
## on the database. The `accPath` argument will be ignored.
|
||||
##
|
||||
if LEAST_FREE_VID <= leafTie.root.distinctBase:
|
||||
? db.registerAccount(leafTie.root, accPath)
|
||||
elif not leafTie.root.isValid:
|
||||
return err(MergeRootMissing)
|
||||
## Otherwise, if the `root` argument belongs to a well known sub trie (i.e.
|
||||
## it does not exceed `LEAST_FREE_VID`) the `accPath` argument is ignored
|
||||
## and the entry will just be merged.
|
||||
##
|
||||
## Otherwise, a valid `accPath` (i.e. different from `VOID_PATH_ID`.) is
|
||||
## required relating to an account leaf entry (starting at `VertexID(`)`).
|
||||
## If the payload of that leaf entry is not of type `AccountData` it is
|
||||
## ignored.
|
||||
##
|
||||
## Otherwise, if the sub-trie where the `leafTie` is to be merged into does
|
||||
## not exist yes, the `storageID` field of the `accPath` leaf must have been
|
||||
## reset to `storageID(0)` and will be updated accordingly on the database.
|
||||
##
|
||||
## Otherwise its `storageID` field must be equal to the `leafTie.root` vertex
|
||||
## ID. So vertices can be marked for Merkle hash update.
|
||||
##
|
||||
let wp = block:
|
||||
if leafTie.root.distinctBase < LEAST_FREE_VID:
|
||||
if not leafTie.root.isValid:
|
||||
return err(MergeRootMissing)
|
||||
VidVtxPair()
|
||||
else:
|
||||
let rc = db.registerAccount(leafTie.root, accPath)
|
||||
if rc.isErr:
|
||||
return err(rc.error)
|
||||
else:
|
||||
rc.value
|
||||
|
||||
let hike = leafTie.hikeUp(db).to(Hike)
|
||||
var okHike: Hike
|
||||
|
@ -676,6 +704,13 @@ proc mergePayload*(
|
|||
if rc.isErr or rc.value != leafTie.path:
|
||||
return err(MergeAssemblyFailed) # Ooops
|
||||
|
||||
# Make sure that there is an accounts that refers to that storage trie
|
||||
if wp.vid.isValid and not wp.vtx.lData.account.storageID.isValid:
|
||||
let leaf = wp.vtx.dup # Dup on modify
|
||||
leaf.lData.account.storageID = leafTie.root
|
||||
db.layersPutVtx(VertexID(1), wp.vid, leaf)
|
||||
db.layersResKey(VertexID(1), wp.vid)
|
||||
|
||||
ok okHike
|
||||
|
||||
|
||||
|
@ -752,9 +787,9 @@ proc merge*(
|
|||
## 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.digestTo(HashKey)
|
||||
let lid = @(key.data).digestTo(HashKey)
|
||||
if not seen.hasKey lid:
|
||||
let node = @key.decode(NodeRef)
|
||||
let node = @(key.data).decode(NodeRef)
|
||||
discard todo.append node
|
||||
seen[lid] = node
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
{.push raises: [].}
|
||||
|
||||
import
|
||||
std/options,
|
||||
std/[options, tables],
|
||||
results,
|
||||
".."/[aristo_desc, aristo_get, aristo_journal, aristo_layers, aristo_hashify]
|
||||
|
||||
|
@ -68,6 +68,10 @@ proc txStow*(
|
|||
# It is OK if there was no `Idg`. Otherwise something serious happened
|
||||
# and there is no way to recover easily.
|
||||
doAssert rc.error == GetIdgNotFound
|
||||
elif db.top.delta.sTab.len != 0 and
|
||||
not db.top.delta.sTab.getOrVoid(VertexID(1)).isValid:
|
||||
# Currently, a `VertexID(1)` root node is required
|
||||
return err(TxAccRootMissing)
|
||||
|
||||
if persistent:
|
||||
# Merge/move `roFilter` into persistent tables
|
||||
|
|
|
@ -190,10 +190,13 @@ proc registerAccount*(
|
|||
db: AristoDbRef; # Database, top layer
|
||||
stoRoot: VertexID; # Storage root ID
|
||||
accPath: PathID; # Needed for accounts payload
|
||||
): Result[void,AristoError] =
|
||||
): Result[VidVtxPair,AristoError] =
|
||||
## Verify that the `stoRoot` argument is properly referred to by the
|
||||
## account data (if any) implied to by the `accPath` argument.
|
||||
##
|
||||
## The function will return an account leaf node if there was any, or an empty
|
||||
## `VidVtxPair()` object.
|
||||
##
|
||||
# Verify storage root and account path
|
||||
if not stoRoot.isValid:
|
||||
return err(UtilsStoRootMissing)
|
||||
|
@ -208,12 +211,26 @@ proc registerAccount*(
|
|||
if wp.vtx.vType != Leaf:
|
||||
return err(UtilsAccPathWithoutLeaf)
|
||||
if wp.vtx.lData.pType != AccountData:
|
||||
return ok() # nothing to do
|
||||
return ok(VidVtxPair()) # nothing to do
|
||||
|
||||
# Need to flag for re-hash
|
||||
# Check whether the `stoRoot` exists on the databse
|
||||
let stoVtx = block:
|
||||
let rc = db.getVtxRc stoRoot
|
||||
if rc.isOk:
|
||||
rc.value
|
||||
elif rc.error == GetVtxNotFound:
|
||||
VertexRef(nil)
|
||||
else:
|
||||
return err(rc.error)
|
||||
|
||||
# Verify `stoVtx` against storage root
|
||||
let stoID = wp.vtx.lData.account.storageID
|
||||
if stoID.isValid and stoID != stoRoot:
|
||||
return err(UtilsAccWrongStorageRoot)
|
||||
if stoVtx.isValid:
|
||||
if stoID != stoRoot:
|
||||
return err(UtilsAccWrongStorageRoot)
|
||||
else:
|
||||
if stoID.isValid:
|
||||
return err(UtilsAccWrongStorageRoot)
|
||||
|
||||
# Clear Merkle keys so that `hasify()` can calculate the re-hash forest/tree
|
||||
for w in hike.legs.mapIt(it.wp.vid):
|
||||
|
@ -223,7 +240,7 @@ proc registerAccount*(
|
|||
db.top.final.dirty.incl hike.root
|
||||
db.top.final.dirty.incl wp.vid
|
||||
|
||||
ok()
|
||||
ok(wp)
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# End
|
||||
|
|
|
@ -33,7 +33,7 @@ iterator aristoReplicate[T](
|
|||
defer: discard api.forget(p)
|
||||
for (vid,key,vtx,node) in T.replicate(p):
|
||||
if key.len == 32:
|
||||
yield (@key, node.encode)
|
||||
yield (@(key.data), node.encode)
|
||||
elif vid == root:
|
||||
yield (@(key.to(Hash256).data), node.encode)
|
||||
|
||||
|
|
|
@ -197,19 +197,28 @@ proc mptMethods(cMpt: AristoCoreDxMptRef): CoreDbMptFns =
|
|||
db.bless AristoCoreDbMptBE(adb: mpt)
|
||||
|
||||
proc mptColFn(): CoreDbColRef =
|
||||
let col =
|
||||
if LEAST_FREE_VID <= cMpt.mptRoot.distinctBase:
|
||||
assert cMpt.accPath.isValid # debug mode only
|
||||
AristoColRef(
|
||||
base: base,
|
||||
colType: CtStorage,
|
||||
stoRoot: cMpt.mptRoot,
|
||||
stoAddr: cMpt.address)
|
||||
else:
|
||||
AristoColRef(
|
||||
base: base,
|
||||
colType: CoreDbColType(cMpt.mptRoot))
|
||||
db.bless col
|
||||
if cMpt.mptRoot.distinctBase < LEAST_FREE_VID:
|
||||
return db.bless(AristoColRef(
|
||||
base: base,
|
||||
colType: CoreDbColType(cMpt.mptRoot)))
|
||||
|
||||
assert cMpt.accPath.isValid # debug mode only
|
||||
if cMpt.mptRoot.isValid:
|
||||
# The mpt might have become empty
|
||||
let
|
||||
key = cMpt.address.keccakHash.data
|
||||
pyl = api.fetchPayload(mpt, AccountsVID, key).valueOr:
|
||||
raiseAssert "mptColFn(): " & $error[1] & " at " & $error[0]
|
||||
|
||||
# Update by accounts data
|
||||
doAssert pyl.pType == AccountData
|
||||
cMpt.mptRoot = pyl.account.storageID
|
||||
|
||||
db.bless AristoColRef(
|
||||
base: base,
|
||||
colType: CtStorage,
|
||||
stoRoot: cMpt.mptRoot,
|
||||
stoAddr: cMpt.address)
|
||||
|
||||
proc mptFetch(key: openArray[byte]): CoreDbRc[Blob] =
|
||||
const info = "fetchFn()"
|
||||
|
|
|
@ -362,8 +362,16 @@ proc persistStorage(acc: AccountRef, ac: AccountsLedgerRef, clearCache: bool) =
|
|||
acc.originalStorage.del(slot)
|
||||
acc.overlayStorage.clear()
|
||||
|
||||
# Changing the storage trie might also change the `storage` descriptor when
|
||||
# the trie changes from empty to exixting or v.v.
|
||||
acc.statement.storage = storageLedger.getColumn()
|
||||
|
||||
# No need to hold descriptors for longer than needed
|
||||
let state = acc.statement.storage.state.valueOr:
|
||||
raiseAssert "Storage column state error: " & $$error
|
||||
if state == EMPTY_ROOT_HASH:
|
||||
acc.statement.storage = CoreDbColRef(nil)
|
||||
|
||||
proc makeDirty(ac: AccountsLedgerRef, address: EthAddress, cloneStorage = true): AccountRef =
|
||||
ac.isDirty = true
|
||||
result = ac.getAccount(address)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
# Nimbus
|
||||
# Copyright (c) 2018-2023 Status Research & Development GmbH
|
||||
# Copyright (c) 2018-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)
|
||||
|
@ -16,7 +16,7 @@ import
|
|||
stew/byteutils,
|
||||
nimcrypto,
|
||||
results,
|
||||
../db/core_db,
|
||||
../db/aristo,
|
||||
../constants
|
||||
|
||||
export eth_types_rlp
|
||||
|
|
|
@ -20,8 +20,8 @@ cliBuilder:
|
|||
./test_stack,
|
||||
./test_genesis,
|
||||
/test_precompiles,
|
||||
#./test_generalstate_json, -- fails
|
||||
./test_tracer_json,
|
||||
./test_generalstate_json,
|
||||
#./test_tracer_json, -- temporarily suspended
|
||||
#./test_persistblock_json, -- fails
|
||||
#./test_rpc, -- fails
|
||||
./test_filters,
|
||||
|
|
|
@ -58,7 +58,7 @@ func hash(filter: FilterRef): Hash =
|
|||
h = h !& (w.uint64.toBytesBE.toSeq & data).hash
|
||||
|
||||
for w in filter.kMap.keys.toSeq.mapIt(it.uint64).sorted.mapIt(it.VertexID):
|
||||
let data = @(filter.kMap.getOrVoid(w))
|
||||
let data = @(filter.kMap.getOrVoid(w).data)
|
||||
h = h !& (w.uint64.toBytesBE.toSeq & data).hash
|
||||
|
||||
!$h
|
||||
|
|
|
@ -132,7 +132,7 @@ func to*(a: Hash256; T: type PathID): T =
|
|||
a.to(UInt256).to(T)
|
||||
|
||||
func to*(a: HashKey; T: type UInt256): T =
|
||||
T.fromBytesBE 0u8.repeat(32 - a.len) & @a
|
||||
T.fromBytesBE 0u8.repeat(32 - a.len) & @(a.data)
|
||||
|
||||
func to*(fid: FilterID; T: type Hash256): T =
|
||||
result.data = fid.uint64.u256.toBytesBE
|
||||
|
|
|
@ -443,6 +443,9 @@ proc testTxMergeAndDeleteSubTree*(
|
|||
list: openArray[ProofTrieData];
|
||||
rdbPath: string; # Rocks DB storage directory
|
||||
): bool =
|
||||
const
|
||||
# Need to reconfigure for the test, root ID 1 cannot be deleted as a trie
|
||||
testRootVid = VertexID(2)
|
||||
var
|
||||
prng = PrngDesc.init 42
|
||||
db = AristoDbRef(nil)
|
||||
|
@ -460,6 +463,10 @@ proc testTxMergeAndDeleteSubTree*(
|
|||
else:
|
||||
AristoDbRef.init(MemBackendRef, qidLayout=TxQidLyo)
|
||||
|
||||
if testRootVid != VertexID(1):
|
||||
# Add a dummy entry so the journal logic can be triggered
|
||||
discard db.merge(VertexID(1), @[n.byte], @[42.byte], VOID_PATH_ID)
|
||||
|
||||
# Start transaction (double frame for testing)
|
||||
xCheck db.txTop.isErr
|
||||
var tx = db.txBegin().value.to(AristoDbRef).txBegin().value
|
||||
|
@ -469,9 +476,9 @@ proc testTxMergeAndDeleteSubTree*(
|
|||
# Reset database so that the next round has a clean setup
|
||||
defer: db.innerCleanUp
|
||||
|
||||
# Merge leaf data into main trie (w/vertex ID 1)
|
||||
# Merge leaf data into main trie (w/vertex ID 2)
|
||||
let kvpLeafs = block:
|
||||
var lst = w.kvpLst.mapRootVid VertexID(1)
|
||||
var lst = w.kvpLst.mapRootVid testRootVid
|
||||
# The list might be reduced for isolation of particular properties,
|
||||
# e.g. lst.setLen(min(5,lst.len))
|
||||
lst
|
||||
|
@ -500,12 +507,17 @@ proc testTxMergeAndDeleteSubTree*(
|
|||
""
|
||||
# Delete sub-tree
|
||||
block:
|
||||
let rc = db.delTree(VertexID(1), VOID_PATH_ID)
|
||||
let rc = db.delTree(testRootVid, VOID_PATH_ID)
|
||||
xCheckRc rc.error == (0,0):
|
||||
noisy.say "***", "del(2)",
|
||||
" n=", n, "/", list.len,
|
||||
"\n db\n ", db.pp(backendOk=true),
|
||||
""
|
||||
|
||||
if testRootVid != VertexID(1):
|
||||
# Update dummy entry so the journal logic can be triggered
|
||||
discard db.merge(VertexID(1), @[n.byte], @[43.byte], VOID_PATH_ID)
|
||||
|
||||
block:
|
||||
let saveBeOk = tx.saveToBackend(
|
||||
chunkedMpt=false, relax=false, noisy=noisy, 2 + list.len * n)
|
||||
|
|
|
@ -198,6 +198,7 @@ proc chainSyncRunner(
|
|||
finalDiskCleanUpOk = true;
|
||||
enaLoggingOk = false;
|
||||
lastOneExtraOk = true;
|
||||
oldLogAlign = false;
|
||||
) =
|
||||
|
||||
## Test backend database and ledger
|
||||
|
@ -241,7 +242,8 @@ proc chainSyncRunner(
|
|||
com.db.trackLedgerApi = true
|
||||
|
||||
check noisy.test_chainSync(filePaths, com, numBlocks,
|
||||
lastOneExtra=lastOneExtraOk, enaLogging=enaLoggingOk)
|
||||
lastOneExtra=lastOneExtraOk, enaLogging=enaLoggingOk,
|
||||
oldLogAlign=oldLogAlign)
|
||||
|
||||
|
||||
proc persistentSyncPreLoadAndResumeRunner(
|
||||
|
@ -252,12 +254,10 @@ proc persistentSyncPreLoadAndResumeRunner(
|
|||
finalDiskCleanUpOk = true;
|
||||
enaLoggingOk = false;
|
||||
lastOneExtraOk = true;
|
||||
oldLogAlign = false;
|
||||
) =
|
||||
## Test backend database and ledger
|
||||
let
|
||||
fileInfo = capture.files[0]
|
||||
.splitFile.name.split(".")[0]
|
||||
.strip(leading=false, chars={'0'..'9'})
|
||||
filePaths = capture.files.mapIt(it.findFilePath(baseDir,repoDir).value)
|
||||
baseDir = getTmpDir() / capture.dbName & "-chain-sync"
|
||||
dbDir = baseDir / "tmp"
|
||||
|
@ -294,7 +294,8 @@ proc persistentSyncPreLoadAndResumeRunner(
|
|||
com.db.trackLedgerApi = true
|
||||
|
||||
check noisy.test_chainSync(filePaths, com, firstPart,
|
||||
lastOneExtra=lastOneExtraOk, enaLogging=enaLoggingOk)
|
||||
lastOneExtra=lastOneExtraOk, enaLogging=enaLoggingOk,
|
||||
oldLogAlign=oldLogAlign)
|
||||
|
||||
test &"Continue with rest of sample":
|
||||
let
|
||||
|
@ -310,7 +311,8 @@ proc persistentSyncPreLoadAndResumeRunner(
|
|||
com.db.trackLedgerApi = true
|
||||
|
||||
check noisy.test_chainSync(filePaths, com, secndPart,
|
||||
lastOneExtra=lastOneExtraOk, enaLogging=enaLoggingOk)
|
||||
lastOneExtra=lastOneExtraOk, enaLogging=enaLoggingOk,
|
||||
oldLogAlign=oldLogAlign)
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Main function(s)
|
||||
|
@ -330,7 +332,7 @@ when isMainModule:
|
|||
|
||||
setErrorLevel()
|
||||
|
||||
when true:
|
||||
when true and false:
|
||||
false.coreDbMain()
|
||||
|
||||
# This one uses the readily available dump: `bulkTest0` and some huge replay
|
||||
|
@ -349,6 +351,7 @@ when isMainModule:
|
|||
capture = capture,
|
||||
#profilingOk = true,
|
||||
#finalDiskCleanUpOk = false,
|
||||
oldLogAlign = true
|
||||
)
|
||||
|
||||
noisy.say "***", "total: ", state[0].pp, " sections: ", state[1]
|
||||
|
|
|
@ -78,8 +78,8 @@ let
|
|||
builtIn: true,
|
||||
name: "main",
|
||||
network: MainNet,
|
||||
# will run over all avail files in parent folder
|
||||
files: @["00000.era1"]) # on external repo
|
||||
# The extren repo is identified by a tag file
|
||||
files: @["mainnet-extern.era1"]) # on external repo
|
||||
|
||||
# ------------------
|
||||
|
||||
|
@ -114,6 +114,7 @@ let
|
|||
dbType = AristoDbRocks,
|
||||
dbName = "main-open") # for resuming on the same persistent DB
|
||||
|
||||
# -----------------------
|
||||
|
||||
mainTest5m* = mainSampleEx
|
||||
.cloneWith(
|
||||
|
@ -121,18 +122,38 @@ let
|
|||
numBlocks = 500_000)
|
||||
|
||||
mainTest6r* = mainSampleEx
|
||||
.cloneWith(
|
||||
name = "-ex-ar-some",
|
||||
numBlocks = 257_400,
|
||||
dbType = AristoDbRocks,
|
||||
dbName = "main-open") # for resuming on the same persistent DB
|
||||
|
||||
mainTest7r* = mainSampleEx
|
||||
.cloneWith(
|
||||
name = "-ex-ar-more",
|
||||
numBlocks = 1_460_700, # failure at 1,460,736
|
||||
dbType = AristoDbRocks,
|
||||
dbName = "main-open") # for resuming on the same persistent DB
|
||||
|
||||
mainTest8r* = mainSampleEx
|
||||
.cloneWith(
|
||||
name = "-ex-ar-more2",
|
||||
numBlocks = 1_460_735, # failure at 1,460,736
|
||||
dbType = AristoDbRocks,
|
||||
dbName = "main-open") # for resuming on the same persistent DB
|
||||
|
||||
mainTest9r* = mainSampleEx
|
||||
.cloneWith(
|
||||
name = "-ex-ar",
|
||||
numBlocks = high(int),
|
||||
dbType = AristoDbRocks)
|
||||
|
||||
dbType = AristoDbRocks,
|
||||
dbName = "main-open") # for resuming on the same persistent DB
|
||||
|
||||
# ------------------
|
||||
|
||||
allSamples* = [
|
||||
mainTest0m, mainTest1m,
|
||||
mainTest2r, mainTest3r, mainTest4r,
|
||||
mainTest5m, mainTest6r
|
||||
mainTest0m, mainTest1m, mainTest2r, mainTest3r, mainTest4r,
|
||||
mainTest5m, mainTest6r, mainTest7r, mainTest8r, mainTest9r,
|
||||
]
|
||||
|
||||
# End
|
||||
|
|
|
@ -150,7 +150,8 @@ proc test_chainSync*(
|
|||
com: CommonRef;
|
||||
numBlocks = high(int);
|
||||
enaLogging = true;
|
||||
lastOneExtra = true
|
||||
lastOneExtra = true;
|
||||
oldLogAlign = false;
|
||||
): bool =
|
||||
## Store persistent blocks from dump into chain DB
|
||||
let
|
||||
|
@ -205,7 +206,9 @@ proc test_chainSync*(
|
|||
if blocks > 0:
|
||||
total += blocks
|
||||
let done {.inject.} = toUnixFloat(getTime())
|
||||
noisy.say "", &"{blocks:3} blocks, {(done-sample):2.3}s, {(blocks.float / (done-sample)):4.3f} b/s, avg {(total.float / (done-begin)):4.3f} b/s"
|
||||
noisy.say "", &"{blocks:3} blocks, {(done-sample):2.3}s,",
|
||||
" {(blocks.float / (done-sample)):4.3f} b/s,",
|
||||
" avg {(total.float / (done-begin)):4.3f} b/s"
|
||||
blocks = 0
|
||||
sample = done
|
||||
|
||||
|
@ -219,9 +222,13 @@ proc test_chainSync*(
|
|||
if toBlock < lastBlock:
|
||||
# Message if `[fromBlock,toBlock]` contains a multiple of `sayBlocks`
|
||||
if fromBlock + (toBlock mod sayBlocks.u256) <= toBlock:
|
||||
sayPerf
|
||||
|
||||
noisy.whisper "***", &"processing ...[#{fromBlock:>8},#{toBlock:>8}]..."
|
||||
if oldLogAlign:
|
||||
noisy.whisper "***",
|
||||
&"processing ...[#{fromBlock},#{toBlock}]...\n"
|
||||
else:
|
||||
sayPerf
|
||||
noisy.whisper "***",
|
||||
&"processing ...[#{fromBlock:>8},#{toBlock:>8}]..."
|
||||
if enaLogging:
|
||||
noisy.startLogging(w[0][0].blockNumber)
|
||||
|
||||
|
@ -229,7 +236,7 @@ proc test_chainSync*(
|
|||
let runPersistBlocksRc = chain.persistBlocks(w[0], w[1])
|
||||
xCheck runPersistBlocksRc == ValidationResult.OK:
|
||||
if noisy:
|
||||
# Re-run with logging enabled
|
||||
noisy.whisper "***", "Re-run with logging enabled...\n"
|
||||
setTraceLevel()
|
||||
com.db.trackLegaApi = false
|
||||
com.db.trackNewApi = false
|
||||
|
@ -255,8 +262,12 @@ proc test_chainSync*(
|
|||
let
|
||||
headers1 = w[0][0 ..< pivot]
|
||||
bodies1 = w[1][0 ..< pivot]
|
||||
sayPerf
|
||||
noisy.whisper "***", &"processing {dotsOrSpace}[#{fromBlock:>8},#{(lastBlock-1):>8}]"
|
||||
if oldLogAlign:
|
||||
noisy.whisper "***", &"processing ...[#{fromBlock},#{toBlock}]...\n"
|
||||
else:
|
||||
sayPerf
|
||||
noisy.whisper "***",
|
||||
&"processing {dotsOrSpace}[#{fromBlock:>8},#{(lastBlock-1):>8}]"
|
||||
let runPersistBlocks1Rc = chain.persistBlocks(headers1, bodies1)
|
||||
xCheck runPersistBlocks1Rc == ValidationResult.OK
|
||||
dotsOrSpace = " "
|
||||
|
@ -266,19 +277,30 @@ proc test_chainSync*(
|
|||
let
|
||||
headers0 = headers9[0..0]
|
||||
bodies0 = bodies9[0..0]
|
||||
sayPerf
|
||||
noisy.whisper "***", &"processing {dotsOrSpace}[#{lastBlock:>8},#{lastBlock:>8}]"
|
||||
if oldLogAlign:
|
||||
noisy.whisper "***",
|
||||
&"processing {dotsOrSpace}[#{fromBlock},#{lastBlock-1}]\n"
|
||||
else:
|
||||
sayPerf
|
||||
noisy.whisper "***",
|
||||
&"processing {dotsOrSpace}[#{lastBlock:>8},#{lastBlock:>8}]"
|
||||
noisy.stopLoggingAfter():
|
||||
let runPersistBlocks0Rc = chain.persistBlocks(headers0, bodies0)
|
||||
xCheck runPersistBlocks0Rc == ValidationResult.OK
|
||||
else:
|
||||
sayPerf
|
||||
noisy.whisper "***", &"processing {dotsOrSpace}[#{lastBlock:>8},#{toBlock:>8}]"
|
||||
if oldLogAlign:
|
||||
noisy.whisper "***",
|
||||
&"processing {dotsOrSpace}[#{lastBlock},#{toBlock}]\n"
|
||||
else:
|
||||
sayPerf
|
||||
noisy.whisper "***",
|
||||
&"processing {dotsOrSpace}[#{lastBlock:>8},#{toBlock:>8}]"
|
||||
noisy.stopLoggingAfter():
|
||||
let runPersistBlocks9Rc = chain.persistBlocks(headers9, bodies9)
|
||||
xCheck runPersistBlocks9Rc == ValidationResult.OK
|
||||
break
|
||||
sayPerf
|
||||
if not oldLogAlign:
|
||||
sayPerf
|
||||
|
||||
true
|
||||
|
||||
|
|
Loading…
Reference in New Issue