Aristo uses pre classified tree types cont1 (#2389)
* Provide dedicated functions for deleteing accounts and storage trees why: Storage trees are always linked to an account, so there is no need for an application to fiddle about (e.g. re-cycling, unlinking) storage tree vertex IDs. * Remove `delete()` and other cruft from API, `aristo_delete`, etc. * clean up delete functions details: The delete implementations `deleteImpl()` and `delTreeImpl()` do not need to be super generic anymore as all the edge cases are covered by the specialised `deleteAccountPayload()`, `deleteGenericData()`, etc. * Avoid unnecessary re-calculations of account keys why: The function `registerAccountForUpdate()` did extract the storage ID (if any) and automatically marked the Merkle keys along the account path for re-hashing. This would also apply if there was later detected that the account or the storage tree did not need to be updated. So the `registerAccountForUpdate()` function was split into a part which retrieved the storage ID, and another one which marked the Merkle keys for re-calculation to be applied only when needed.
This commit is contained in:
parent
51f02090b8
commit
8727307ef4
|
@ -49,41 +49,56 @@ type
|
||||||
## previous layer. The previous transaction is returned if there
|
## previous layer. The previous transaction is returned if there
|
||||||
## was any.
|
## was any.
|
||||||
|
|
||||||
AristoApiDeleteFn* =
|
AristoApiDeleteAccountPayloadFn* =
|
||||||
|
proc(db: AristoDbRef;
|
||||||
|
path: openArray[byte];
|
||||||
|
): Result[void,AristoError]
|
||||||
|
{.noRaise.}
|
||||||
|
## Delete the account leaf entry addressed by the argument `path`. If
|
||||||
|
## this leaf entry referres to a storage tree, this one will be deleted
|
||||||
|
## as well.
|
||||||
|
|
||||||
|
AristoApiDeleteGenericDataFn* =
|
||||||
proc(db: AristoDbRef;
|
proc(db: AristoDbRef;
|
||||||
root: VertexID;
|
root: VertexID;
|
||||||
path: openArray[byte];
|
path: openArray[byte];
|
||||||
accPath: PathID;
|
): Result[bool,AristoError]
|
||||||
): Result[bool,(VertexID,AristoError)]
|
|
||||||
{.noRaise.}
|
{.noRaise.}
|
||||||
## Delete a leaf with path `path` starting at root `root`.
|
## Delete the leaf data entry addressed by the argument `path`. The MPT
|
||||||
|
## sub-tree the leaf data entry is subsumed under is passed as argument
|
||||||
|
## `root` which must be greater than `VertexID(1)` and smaller than
|
||||||
|
## `LEAST_FREE_VID`.
|
||||||
##
|
##
|
||||||
## For a `root` with `VertexID` greater than `LEAST_FREE_VID`, the
|
## The return value is `true` if the argument `path` deleted was the last
|
||||||
## sub-tree generated by `payload.root` is considered a storage trie
|
## one and the tree does not exist anymore.
|
||||||
## 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 `root` vertex ID.
|
|
||||||
##
|
|
||||||
## The return code is `true` iff the trie has become empty.
|
|
||||||
|
|
||||||
AristoApiDelTreeFn* =
|
AristoApiDeleteGenericTreeFn* =
|
||||||
proc(db: AristoDbRef;
|
proc(db: AristoDbRef;
|
||||||
root: VertexID;
|
root: VertexID;
|
||||||
accPath: PathID;
|
): Result[void,AristoError]
|
||||||
): Result[void,(VertexID,AristoError)]
|
|
||||||
{.noRaise.}
|
{.noRaise.}
|
||||||
## Delete sub-trie below `root`. The maximum supported sub-tree size
|
## Variant of `deleteGenericData()` for purging the whole MPT sub-tree.
|
||||||
## is `SUB_TREE_DISPOSAL_MAX`. Larger tries must be disposed by
|
|
||||||
## walk-deleting leaf nodes using `left()` or `right()` traversal
|
AristoApiDeleteStorageDataFn* =
|
||||||
## functions.
|
proc(db: AristoDbRef;
|
||||||
##
|
path: openArray[byte];
|
||||||
## For a `root` argument greater than `LEAST_FREE_VID`, the sub-tree
|
accPath: PathID;
|
||||||
## spanned by `root` is considered a storage trie linked to an account
|
): Result[bool,AristoError]
|
||||||
## leaf referred to by a valid `accPath` (i.e. different from
|
{.noRaise.}
|
||||||
## `VOID_PATH_ID`.) In that case, an account must exists. If there is
|
## For a given account argument `accPath`, this function deletes the
|
||||||
## payload of type `AccountData`, its `storageID` field must be unset
|
## argument `path` from the associated storage tree (if any, at all.) If
|
||||||
## or equal to the `hike.root` vertex ID.
|
## the if the argument `path` deleted was the last one on the storage
|
||||||
|
## tree, account leaf referred to by `accPath` will be updated so that
|
||||||
|
## it will not refer to a storage tree anymore. In the latter case only
|
||||||
|
## the function will return `true`.
|
||||||
|
|
||||||
|
AristoApiDeleteStorageTreeFn* =
|
||||||
|
proc(db: AristoDbRef;
|
||||||
|
accPath: PathID;
|
||||||
|
): Result[void,AristoError]
|
||||||
|
{.noRaise.}
|
||||||
|
## Variant of `deleteStorageData()` for purging the whole storage tree
|
||||||
|
## associated to the account argument `accPath`.
|
||||||
|
|
||||||
AristoApiFetchLastSavedStateFn* =
|
AristoApiFetchLastSavedStateFn* =
|
||||||
proc(db: AristoDbRef
|
proc(db: AristoDbRef
|
||||||
|
@ -363,8 +378,11 @@ type
|
||||||
AristoApiObj* = object of RootObj
|
AristoApiObj* = object of RootObj
|
||||||
## Useful set of `Aristo` fuctions that can be filtered, stacked etc.
|
## Useful set of `Aristo` fuctions that can be filtered, stacked etc.
|
||||||
commit*: AristoApiCommitFn
|
commit*: AristoApiCommitFn
|
||||||
delete*: AristoApiDeleteFn
|
deleteAccountPayload*: AristoApiDeleteAccountPayloadFn
|
||||||
delTree*: AristoApiDelTreeFn
|
deleteGenericData*: AristoApiDeleteGenericDataFn
|
||||||
|
deleteGenericTree*: AristoApiDeleteGenericTreeFn
|
||||||
|
deleteStorageData*: AristoApiDeleteStorageDataFn
|
||||||
|
deleteStorageTree*: AristoApiDeleteStorageTreeFn
|
||||||
fetchLastSavedState*: AristoApiFetchLastSavedStateFn
|
fetchLastSavedState*: AristoApiFetchLastSavedStateFn
|
||||||
fetchPayload*: AristoApiFetchPayloadFn
|
fetchPayload*: AristoApiFetchPayloadFn
|
||||||
findTx*: AristoApiFindTxFn
|
findTx*: AristoApiFindTxFn
|
||||||
|
@ -394,42 +412,45 @@ type
|
||||||
## Index/name mapping for profile slots
|
## Index/name mapping for profile slots
|
||||||
AristoApiProfTotal = "total"
|
AristoApiProfTotal = "total"
|
||||||
|
|
||||||
AristoApiProfCommitFn = "commit"
|
AristoApiProfCommitFn = "commit"
|
||||||
AristoApiProfDeleteFn = "delete"
|
AristoApiProfDeleteAccountPayloadFn = "deleteAccountPayload"
|
||||||
AristoApiProfDelTreeFn = "delTree"
|
AristoApiProfDeleteGenericDataFn = "deleteGnericData"
|
||||||
AristoApiProfFetchLastSavedStateFn = "fetchPayload"
|
AristoApiProfDeleteGenericTreeFn = "deleteGnericTree"
|
||||||
AristoApiProfFetchPayloadFn = "fetchPayload"
|
AristoApiProfDeleteStorageDataFn = "deleteStorageData"
|
||||||
AristoApiProfFindTxFn = "findTx"
|
AristoApiProfDeleteStorageTreeFn = "deleteStorageTree"
|
||||||
AristoApiProfFinishFn = "finish"
|
AristoApiProfFetchLastSavedStateFn = "fetchPayload"
|
||||||
AristoApiProfForgetFn = "forget"
|
AristoApiProfFetchPayloadFn = "fetchPayload"
|
||||||
AristoApiProfForkTxFn = "forkTx"
|
AristoApiProfFindTxFn = "findTx"
|
||||||
AristoApiProfGetKeyRcFn = "getKeyRc"
|
AristoApiProfFinishFn = "finish"
|
||||||
AristoApiProfHashifyFn = "hashify"
|
AristoApiProfForgetFn = "forget"
|
||||||
AristoApiProfHasPathFn = "hasPath"
|
AristoApiProfForkTxFn = "forkTx"
|
||||||
AristoApiProfHikeUpFn = "hikeUp"
|
AristoApiProfGetKeyRcFn = "getKeyRc"
|
||||||
AristoApiProfIsTopFn = "isTop"
|
AristoApiProfHashifyFn = "hashify"
|
||||||
AristoApiProfLevelFn = "level"
|
AristoApiProfHasPathFn = "hasPath"
|
||||||
AristoApiProfNForkedFn = "nForked"
|
AristoApiProfHikeUpFn = "hikeUp"
|
||||||
AristoApiProfMergeAccountPayloadFn = "mergeAccountPayload"
|
AristoApiProfIsTopFn = "isTop"
|
||||||
AristoApiProfMergeGenericDataFn = "mergeGenericData"
|
AristoApiProfLevelFn = "level"
|
||||||
AristoApiProfMergeStorageDataFn = "mergeStorageData"
|
AristoApiProfNForkedFn = "nForked"
|
||||||
AristoApiProfPathAsBlobFn = "pathAsBlob"
|
AristoApiProfMergeAccountPayloadFn = "mergeAccountPayload"
|
||||||
AristoApiProfPersistFn = "persist"
|
AristoApiProfMergeGenericDataFn = "mergeGenericData"
|
||||||
AristoApiProfReCentreFn = "reCentre"
|
AristoApiProfMergeStorageDataFn = "mergeStorageData"
|
||||||
AristoApiProfRollbackFn = "rollback"
|
AristoApiProfPathAsBlobFn = "pathAsBlob"
|
||||||
AristoApiProfSerialiseFn = "serialise"
|
AristoApiProfPersistFn = "persist"
|
||||||
AristoApiProfTxBeginFn = "txBegin"
|
AristoApiProfReCentreFn = "reCentre"
|
||||||
AristoApiProfTxTopFn = "txTop"
|
AristoApiProfRollbackFn = "rollback"
|
||||||
|
AristoApiProfSerialiseFn = "serialise"
|
||||||
|
AristoApiProfTxBeginFn = "txBegin"
|
||||||
|
AristoApiProfTxTopFn = "txTop"
|
||||||
|
|
||||||
AristoApiProfBeGetVtxFn = "be/getVtx"
|
AristoApiProfBeGetVtxFn = "be/getVtx"
|
||||||
AristoApiProfBeGetKeyFn = "be/getKey"
|
AristoApiProfBeGetKeyFn = "be/getKey"
|
||||||
AristoApiProfBeGetTuvFn = "be/getTuv"
|
AristoApiProfBeGetTuvFn = "be/getTuv"
|
||||||
AristoApiProfBeGetLstFn = "be/getLst"
|
AristoApiProfBeGetLstFn = "be/getLst"
|
||||||
AristoApiProfBePutVtxFn = "be/putVtx"
|
AristoApiProfBePutVtxFn = "be/putVtx"
|
||||||
AristoApiProfBePutKeyFn = "be/putKey"
|
AristoApiProfBePutKeyFn = "be/putKey"
|
||||||
AristoApiProfBePutTuvFn = "be/putTuv"
|
AristoApiProfBePutTuvFn = "be/putTuv"
|
||||||
AristoApiProfBePutLstFn = "be/putLst"
|
AristoApiProfBePutLstFn = "be/putLst"
|
||||||
AristoApiProfBePutEndFn = "be/putEnd"
|
AristoApiProfBePutEndFn = "be/putEnd"
|
||||||
|
|
||||||
AristoApiProfRef* = ref object of AristoApiRef
|
AristoApiProfRef* = ref object of AristoApiRef
|
||||||
## Profiling API extension of `AristoApiObj`
|
## Profiling API extension of `AristoApiObj`
|
||||||
|
@ -443,8 +464,11 @@ type
|
||||||
when AutoValidateApiHooks:
|
when AutoValidateApiHooks:
|
||||||
proc validate(api: AristoApiObj|AristoApiRef) =
|
proc validate(api: AristoApiObj|AristoApiRef) =
|
||||||
doAssert not api.commit.isNil
|
doAssert not api.commit.isNil
|
||||||
doAssert not api.delete.isNil
|
doAssert not api.deleteAccountPayload.isNil
|
||||||
doAssert not api.delTree.isNil
|
doAssert not api.deleteGenericData.isNil
|
||||||
|
doAssert not api.deleteGenericTree.isNil
|
||||||
|
doAssert not api.deleteStorageData.isNil
|
||||||
|
doAssert not api.deleteStorageTree.isNil
|
||||||
doAssert not api.fetchLastSavedState.isNil
|
doAssert not api.fetchLastSavedState.isNil
|
||||||
doAssert not api.fetchPayload.isNil
|
doAssert not api.fetchPayload.isNil
|
||||||
doAssert not api.findTx.isNil
|
doAssert not api.findTx.isNil
|
||||||
|
@ -495,8 +519,11 @@ func init*(api: var AristoApiObj) =
|
||||||
when AutoValidateApiHooks:
|
when AutoValidateApiHooks:
|
||||||
api.reset
|
api.reset
|
||||||
api.commit = commit
|
api.commit = commit
|
||||||
api.delete = delete
|
api.deleteAccountPayload = deleteAccountPayload
|
||||||
api.delTree = delTree
|
api.deleteGenericData = deleteGenericData
|
||||||
|
api.deleteGenericTree = deleteGenericTree
|
||||||
|
api.deleteStorageData = deleteStorageData
|
||||||
|
api.deleteStorageTree = deleteStorageTree
|
||||||
api.fetchLastSavedState = fetchLastSavedState
|
api.fetchLastSavedState = fetchLastSavedState
|
||||||
api.fetchPayload = fetchPayload
|
api.fetchPayload = fetchPayload
|
||||||
api.findTx = findTx
|
api.findTx = findTx
|
||||||
|
@ -529,32 +556,35 @@ func init*(T: type AristoApiRef): T =
|
||||||
|
|
||||||
func dup*(api: AristoApiRef): AristoApiRef =
|
func dup*(api: AristoApiRef): AristoApiRef =
|
||||||
result = AristoApiRef(
|
result = AristoApiRef(
|
||||||
commit: api.commit,
|
commit: api.commit,
|
||||||
delete: api.delete,
|
deleteAccountPayload: api.deleteAccountPayload,
|
||||||
delTree: api.delTree,
|
deleteGenericData: api.deleteGenericData,
|
||||||
fetchLastSavedState: api.fetchLastSavedState,
|
deleteGenericTree: api.deleteGenericTree,
|
||||||
fetchPayload: api.fetchPayload,
|
deleteStorageData: api.deleteStorageData,
|
||||||
findTx: api.findTx,
|
deleteStorageTree: api.deleteStorageTree,
|
||||||
finish: api.finish,
|
fetchLastSavedState: api.fetchLastSavedState,
|
||||||
forget: api.forget,
|
fetchPayload: api.fetchPayload,
|
||||||
forkTx: api.forkTx,
|
findTx: api.findTx,
|
||||||
getKeyRc: api.getKeyRc,
|
finish: api.finish,
|
||||||
hashify: api.hashify,
|
forget: api.forget,
|
||||||
hasPath: api.hasPath,
|
forkTx: api.forkTx,
|
||||||
hikeUp: api.hikeUp,
|
getKeyRc: api.getKeyRc,
|
||||||
isTop: api.isTop,
|
hashify: api.hashify,
|
||||||
level: api.level,
|
hasPath: api.hasPath,
|
||||||
nForked: api.nForked,
|
hikeUp: api.hikeUp,
|
||||||
mergeAccountPayload: api.mergeAccountPayload,
|
isTop: api.isTop,
|
||||||
mergeGenericData: api.mergeGenericData,
|
level: api.level,
|
||||||
mergeStorageData: api.mergeStorageData,
|
nForked: api.nForked,
|
||||||
pathAsBlob: api.pathAsBlob,
|
mergeAccountPayload: api.mergeAccountPayload,
|
||||||
persist: api.persist,
|
mergeGenericData: api.mergeGenericData,
|
||||||
reCentre: api.reCentre,
|
mergeStorageData: api.mergeStorageData,
|
||||||
rollback: api.rollback,
|
pathAsBlob: api.pathAsBlob,
|
||||||
serialise: api.serialise,
|
persist: api.persist,
|
||||||
txBegin: api.txBegin,
|
reCentre: api.reCentre,
|
||||||
txTop: api.txTop)
|
rollback: api.rollback,
|
||||||
|
serialise: api.serialise,
|
||||||
|
txBegin: api.txBegin,
|
||||||
|
txTop: api.txTop)
|
||||||
when AutoValidateApiHooks:
|
when AutoValidateApiHooks:
|
||||||
api.validate
|
api.validate
|
||||||
|
|
||||||
|
@ -590,15 +620,30 @@ func init*(
|
||||||
AristoApiProfCommitFn.profileRunner:
|
AristoApiProfCommitFn.profileRunner:
|
||||||
result = api.commit(a)
|
result = api.commit(a)
|
||||||
|
|
||||||
profApi.delete =
|
profApi.deleteAccountPayload =
|
||||||
proc(a: AristoDbRef; b: VertexID; c: openArray[byte]; d: PathID): auto =
|
proc(a: AristoDbRef; b: openArray[byte]): auto =
|
||||||
AristoApiProfDeleteFn.profileRunner:
|
AristoApiProfDeleteAccountPayloadFn.profileRunner:
|
||||||
result = api.delete(a, b, c, d)
|
result = api.deleteAccountPayload(a, b)
|
||||||
|
|
||||||
profApi.delTree =
|
profApi.deleteGenericData =
|
||||||
proc(a: AristoDbRef; b: VertexID; c: PathID): auto =
|
proc(a: AristoDbRef; b: VertexID; c: openArray[byte]): auto =
|
||||||
AristoApiProfDelTreeFn.profileRunner:
|
AristoApiProfDeleteGenericDataFn.profileRunner:
|
||||||
result = api.delTree(a, b, c)
|
result = api.deleteGenericData(a, b, c)
|
||||||
|
|
||||||
|
profApi.deleteGenericTree =
|
||||||
|
proc(a: AristoDbRef; b: VertexID): auto =
|
||||||
|
AristoApiProfDeleteGenericTreeFn.profileRunner:
|
||||||
|
result = api.deleteGenericTree(a, b)
|
||||||
|
|
||||||
|
profApi.deleteStorageData =
|
||||||
|
proc(a: AristoDbRef; b: openArray[byte]; c: PathID): auto =
|
||||||
|
AristoApiProfDeleteStorageDataFn.profileRunner:
|
||||||
|
result = api.deleteStorageData(a, b, c)
|
||||||
|
|
||||||
|
profApi.deleteStorageTree =
|
||||||
|
proc(a: AristoDbRef; b: PathID): auto =
|
||||||
|
AristoApiProfDeleteStorageTreeFn.profileRunner:
|
||||||
|
result = api.deleteStorageTree(a, b)
|
||||||
|
|
||||||
profApi.fetchLastSavedState =
|
profApi.fetchLastSavedState =
|
||||||
proc(a: AristoDbRef): auto =
|
proc(a: AristoDbRef): auto =
|
||||||
|
|
|
@ -253,28 +253,14 @@ proc collapseLeaf(
|
||||||
proc delSubTreeImpl(
|
proc delSubTreeImpl(
|
||||||
db: AristoDbRef; # Database, top layer
|
db: AristoDbRef; # Database, top layer
|
||||||
root: VertexID; # Root vertex
|
root: VertexID; # Root vertex
|
||||||
accPath: PathID; # Needed for real storage tries
|
): Result[void,AristoError] =
|
||||||
): Result[void,(VertexID,AristoError)] =
|
|
||||||
## Implementation of *delete* sub-trie.
|
## Implementation of *delete* sub-trie.
|
||||||
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
|
var
|
||||||
dispose = @[root]
|
dispose = @[root]
|
||||||
rootVtx = db.getVtxRc(root).valueOr:
|
rootVtx = db.getVtxRc(root).valueOr:
|
||||||
if error == GetVtxNotFound:
|
if error == GetVtxNotFound:
|
||||||
return ok()
|
return ok()
|
||||||
return err((root,error))
|
return err(error)
|
||||||
follow = @[rootVtx]
|
follow = @[rootVtx]
|
||||||
|
|
||||||
# Collect list of nodes to delete
|
# Collect list of nodes to delete
|
||||||
|
@ -283,58 +269,31 @@ proc delSubTreeImpl(
|
||||||
for vtx in follow:
|
for vtx in follow:
|
||||||
for vid in vtx.subVids:
|
for vid in vtx.subVids:
|
||||||
# Exiting here leaves the tree as-is
|
# Exiting here leaves the tree as-is
|
||||||
let vtx = ? db.getVtxRc(vid).mapErr toVae(vid)
|
let vtx = ? db.getVtxRc(vid)
|
||||||
redo.add vtx
|
redo.add vtx
|
||||||
dispose.add vid
|
dispose.add vid
|
||||||
redo.swap follow
|
redo.swap follow
|
||||||
|
|
||||||
# Mark nodes deleted
|
# Mark collected vertices to be deleted
|
||||||
for vid in dispose:
|
for vid in dispose:
|
||||||
db.disposeOfVtx(root, vid)
|
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)
|
|
||||||
|
|
||||||
ok()
|
ok()
|
||||||
|
|
||||||
|
|
||||||
proc deleteImpl(
|
proc deleteImpl(
|
||||||
db: AristoDbRef; # Database, top layer
|
db: AristoDbRef; # Database, top layer
|
||||||
hike: Hike; # Fully expanded path
|
hike: Hike; # Fully expanded path
|
||||||
lty: LeafTie; # `Patricia Trie` path root-to-leaf
|
): Result[void,(VertexID,AristoError)] =
|
||||||
accPath: PathID; # Needed for accounts payload
|
|
||||||
): Result[bool,(VertexID,AristoError)] =
|
|
||||||
## Implementation of *delete* functionality.
|
## Implementation of *delete* functionality.
|
||||||
|
|
||||||
let wp = block:
|
# Remove leaf entry
|
||||||
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
|
let lf = hike.legs[^1].wp
|
||||||
if lf.vtx.vType != Leaf:
|
if lf.vtx.vType != Leaf:
|
||||||
return err((lf.vid,DelLeafExpexted))
|
return err((lf.vid,DelLeafExpexted))
|
||||||
if lf.vid in db.pPrf:
|
if lf.vid in db.pPrf:
|
||||||
return err((lf.vid, DelLeafLocked))
|
return err((lf.vid, DelLeafLocked))
|
||||||
|
|
||||||
# Verify that there is no dangling storage trie
|
|
||||||
block:
|
|
||||||
let data = lf.vtx.lData
|
|
||||||
if data.pType == AccountData:
|
|
||||||
let vid = data.account.storageID
|
|
||||||
if vid.isValid and db.getVtx(vid).isValid:
|
|
||||||
return err((vid,DelDanglingStoTrie))
|
|
||||||
|
|
||||||
db.disposeOfVtx(hike.root, lf.vid)
|
db.disposeOfVtx(hike.root, lf.vid)
|
||||||
|
|
||||||
if 1 < hike.legs.len:
|
if 1 < hike.legs.len:
|
||||||
|
@ -350,7 +309,7 @@ proc deleteImpl(
|
||||||
br.vtx.bVid[hike.legs[^2].nibble] = VertexID(0)
|
br.vtx.bVid[hike.legs[^2].nibble] = VertexID(0)
|
||||||
db.layersPutVtx(hike.root, br.vid, br.vtx)
|
db.layersPutVtx(hike.root, br.vid, br.vtx)
|
||||||
|
|
||||||
# Clear all keys up to the root key
|
# Clear all Merkle hash keys up to the root key
|
||||||
for n in 0 .. hike.legs.len - 2:
|
for n in 0 .. hike.legs.len - 2:
|
||||||
let vid = hike.legs[n].wp.vid
|
let vid = hike.legs[n].wp.vid
|
||||||
if vid in db.top.final.pPrf:
|
if vid in db.top.final.pPrf:
|
||||||
|
@ -381,97 +340,155 @@ proc deleteImpl(
|
||||||
of Leaf:
|
of Leaf:
|
||||||
? db.collapseLeaf(hike, nibble.byte, nxt.vtx)
|
? db.collapseLeaf(hike, nibble.byte, nxt.vtx)
|
||||||
|
|
||||||
let emptySubTreeOk = not db.getVtx(hike.root).isValid
|
ok()
|
||||||
|
|
||||||
# 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)
|
|
||||||
|
|
||||||
ok(emptySubTreeOk)
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Public functions
|
# Public functions
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
proc delTree*(
|
proc deleteAccountPayload*(
|
||||||
db: AristoDbRef; # Database, top layer
|
db: AristoDbRef;
|
||||||
root: VertexID; # Root vertex
|
path: openArray[byte];
|
||||||
accPath: PathID; # Needed for real storage tries
|
): Result[void,AristoError] =
|
||||||
): Result[void,(VertexID,AristoError)] =
|
## Delete the account leaf entry addressed by the argument `path`. If this
|
||||||
## Delete sub-trie below `root`.
|
## leaf entry referres to a storage tree, this one will be deleted as well.
|
||||||
##
|
##
|
||||||
## Note that the accounts trie hinging on `VertexID(1)` cannot be deleted.
|
let
|
||||||
##
|
hike = path.initNibbleRange.hikeUp(VertexID(1), db).valueOr:
|
||||||
## If the `root` argument belongs to a well known sub trie (i.e. it does
|
if error[1] in HikeAcceptableStopsNotFound:
|
||||||
## not exceed `LEAST_FREE_VID`) the `accPath` argument is ignored and the
|
return err(DelPathNotFound)
|
||||||
## sub-trie will just be deleted.
|
return err(error[1])
|
||||||
##
|
stoID = hike.legs[^1].wp.vtx.lData.account.storageID
|
||||||
## 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)
|
|
||||||
|
|
||||||
proc delete*(
|
# Delete storage tree if present
|
||||||
db: AristoDbRef; # Database, top layer
|
if stoID.isValid:
|
||||||
hike: Hike; # Fully expanded chain of vertices
|
? db.delSubTreeImpl stoID
|
||||||
accPath: PathID; # Needed for accounts payload
|
|
||||||
): Result[bool,(VertexID,AristoError)] =
|
|
||||||
## 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.
|
|
||||||
##
|
|
||||||
## 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.
|
|
||||||
##
|
|
||||||
## 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,
|
|
||||||
path: ? hike.to(NibblesSeq).pathToTag().mapErr toVae)
|
|
||||||
db.deleteImpl(hike, lty, accPath)
|
|
||||||
|
|
||||||
proc delete*(
|
db.deleteImpl(hike).isOkOr:
|
||||||
db: AristoDbRef; # Database, top layer
|
return err(error[1])
|
||||||
lty: LeafTie; # `Patricia Trie` path root-to-leaf
|
|
||||||
accPath: PathID; # Needed for accounts payload
|
|
||||||
): Result[bool,(VertexID,AristoError)] =
|
|
||||||
## Variant of `delete()`
|
|
||||||
##
|
|
||||||
db.deleteImpl(? lty.hikeUp(db).mapErr toVae, lty, accPath)
|
|
||||||
|
|
||||||
proc delete*(
|
ok()
|
||||||
|
|
||||||
|
|
||||||
|
proc deleteGenericData*(
|
||||||
db: AristoDbRef;
|
db: AristoDbRef;
|
||||||
root: VertexID;
|
root: VertexID;
|
||||||
path: openArray[byte];
|
path: openArray[byte];
|
||||||
accPath: PathID; # Needed for accounts payload
|
): Result[bool,AristoError] =
|
||||||
): Result[bool,(VertexID,AristoError)] =
|
## Delete the leaf data entry addressed by the argument `path`. The MPT
|
||||||
## Variant of `delete()`
|
## sub-tree the leaf data entry is subsumed under is passed as argument
|
||||||
|
## `root` which must be greater than `VertexID(1)` and smaller than
|
||||||
|
## `LEAST_FREE_VID`.
|
||||||
##
|
##
|
||||||
let rc = path.initNibbleRange.hikeUp(root, db)
|
## The return value is `true` if the argument `path` deleted was the last
|
||||||
if rc.isOk:
|
## one and the tree does not exist anymore.
|
||||||
return db.delete(rc.value, accPath)
|
##
|
||||||
if rc.error[1] in HikeAcceptableStopsNotFound:
|
# Verify that `root` is neither an accounts tree nor a strorage tree.
|
||||||
return err((rc.error[0], DelPathNotFound))
|
if not root.isValid:
|
||||||
err((rc.error[0],rc.error[1]))
|
return err(DelRootVidMissing)
|
||||||
|
elif root == VertexID(1):
|
||||||
|
return err(DelAccRootNotAccepted)
|
||||||
|
elif LEAST_FREE_VID <= root.distinctBase:
|
||||||
|
return err(DelStoRootNotAccepted)
|
||||||
|
|
||||||
|
let hike = path.initNibbleRange.hikeUp(root, db).valueOr:
|
||||||
|
if error[1] in HikeAcceptableStopsNotFound:
|
||||||
|
return err(DelPathNotFound)
|
||||||
|
return err(error[1])
|
||||||
|
|
||||||
|
db.deleteImpl(hike).isOkOr:
|
||||||
|
return err(error[1])
|
||||||
|
|
||||||
|
ok(not db.getVtx(root).isValid)
|
||||||
|
|
||||||
|
proc deleteGenericTree*(
|
||||||
|
db: AristoDbRef; # Database, top layer
|
||||||
|
root: VertexID; # Root vertex
|
||||||
|
): Result[void,AristoError] =
|
||||||
|
## Variant of `deleteGenericData()` for purging the whole MPT sub-tree.
|
||||||
|
##
|
||||||
|
# Verify that `root` is neither an accounts tree nor a strorage tree.
|
||||||
|
if not root.isValid:
|
||||||
|
return err(DelRootVidMissing)
|
||||||
|
elif root == VertexID(1):
|
||||||
|
return err(DelAccRootNotAccepted)
|
||||||
|
elif LEAST_FREE_VID <= root.distinctBase:
|
||||||
|
return err(DelStoRootNotAccepted)
|
||||||
|
|
||||||
|
db.delSubTreeImpl root
|
||||||
|
|
||||||
|
|
||||||
|
proc deleteStorageData*(
|
||||||
|
db: AristoDbRef;
|
||||||
|
path: openArray[byte];
|
||||||
|
accPath: PathID; # Needed for accounts payload
|
||||||
|
): Result[bool,AristoError] =
|
||||||
|
## For a given account argument `accPath`, this function deletes the
|
||||||
|
## argument `path` from the associated storage tree (if any, at all.) If
|
||||||
|
## the if the argument `path` deleted was the last one on the storage tree,
|
||||||
|
## account leaf referred to by `accPath` will be updated so that it will
|
||||||
|
## not refer to a storage tree anymore. In the latter case only the function
|
||||||
|
## will return `true`.
|
||||||
|
##
|
||||||
|
let
|
||||||
|
accHike = ? db.retrieveStoAccHike accPath
|
||||||
|
wpAcc = accHike.legs[^1].wp
|
||||||
|
stoID = wpAcc.vtx.lData.account.storageID
|
||||||
|
|
||||||
|
if not stoID.isValid:
|
||||||
|
return err(DelStoRootMissing)
|
||||||
|
|
||||||
|
let stoHike = path.initNibbleRange.hikeUp(stoID, db).valueOr:
|
||||||
|
if error[1] in HikeAcceptableStopsNotFound:
|
||||||
|
return err(DelPathNotFound)
|
||||||
|
return err(error[1])
|
||||||
|
|
||||||
|
# Mark account path for update for `hashify()`
|
||||||
|
db.updateAccountForHasher accHike
|
||||||
|
|
||||||
|
db.deleteImpl(stoHike).isOkOr:
|
||||||
|
return err(error[1])
|
||||||
|
|
||||||
|
# Make sure that an account leaf has no dangling sub-trie
|
||||||
|
if db.getVtx(stoID).isValid:
|
||||||
|
return ok(false)
|
||||||
|
|
||||||
|
# De-register the deleted storage tree from the account record
|
||||||
|
let leaf = wpAcc.vtx.dup # Dup on modify
|
||||||
|
leaf.lData.account.storageID = VertexID(0)
|
||||||
|
db.layersPutVtx(VertexID(1), wpAcc.vid, leaf)
|
||||||
|
db.layersResKey(VertexID(1), wpAcc.vid)
|
||||||
|
ok(true)
|
||||||
|
|
||||||
|
proc deleteStorageTree*(
|
||||||
|
db: AristoDbRef; # Database, top layer
|
||||||
|
accPath: PathID; # Needed for accounts payload
|
||||||
|
): Result[void,AristoError] =
|
||||||
|
## Variant of `deleteStorageData()` for purging the whole storage tree
|
||||||
|
## associated to the account argument `accPath`.
|
||||||
|
##
|
||||||
|
let
|
||||||
|
accHike = db.retrieveStoAccHike(accPath).valueOr:
|
||||||
|
if error == UtilsAccInaccessible:
|
||||||
|
return err(DelStoAccMissing)
|
||||||
|
return err(error)
|
||||||
|
wpAcc = accHike.legs[^1].wp
|
||||||
|
stoID = wpAcc.vtx.lData.account.storageID
|
||||||
|
|
||||||
|
if not stoID.isValid:
|
||||||
|
return err(DelStoRootMissing)
|
||||||
|
|
||||||
|
# Mark account path for update for `hashify()`
|
||||||
|
db.updateAccountForHasher accHike
|
||||||
|
|
||||||
|
? db.delSubTreeImpl stoID
|
||||||
|
|
||||||
|
# De-register the deleted storage tree from the accounts record
|
||||||
|
let leaf = wpAcc.vtx.dup # Dup on modify
|
||||||
|
leaf.lData.account.storageID = VertexID(0)
|
||||||
|
db.layersPutVtx(VertexID(1), wpAcc.vid, leaf)
|
||||||
|
db.layersResKey(VertexID(1), wpAcc.vid)
|
||||||
|
ok()
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# End
|
# End
|
||||||
|
|
|
@ -190,6 +190,7 @@ type
|
||||||
NearbyVidInvalid
|
NearbyVidInvalid
|
||||||
|
|
||||||
# Deletion of vertices, `delete()`
|
# Deletion of vertices, `delete()`
|
||||||
|
DelAccRootNotAccepted
|
||||||
DelBranchExpexted
|
DelBranchExpexted
|
||||||
DelBranchLocked
|
DelBranchLocked
|
||||||
DelBranchWithoutRefs
|
DelBranchWithoutRefs
|
||||||
|
@ -200,6 +201,10 @@ type
|
||||||
DelLeafUnexpected
|
DelLeafUnexpected
|
||||||
DelPathNotFound
|
DelPathNotFound
|
||||||
DelPathTagError
|
DelPathTagError
|
||||||
|
DelRootVidMissing
|
||||||
|
DelStoAccMissing
|
||||||
|
DelStoRootMissing
|
||||||
|
DelStoRootNotAccepted
|
||||||
DelSubTreeAccRoot
|
DelSubTreeAccRoot
|
||||||
DelSubTreeVoidRoot
|
DelSubTreeVoidRoot
|
||||||
DelVidStaleVtx
|
DelVidStaleVtx
|
||||||
|
|
|
@ -53,6 +53,10 @@ proc mergeAccountPayload*(
|
||||||
## unset/invalid or referring to a existing vertex which will be assumed
|
## unset/invalid or referring to a existing vertex which will be assumed
|
||||||
## to be a storage tree.
|
## to be a storage tree.
|
||||||
##
|
##
|
||||||
|
## On success, the function returns `true` if the `accPayload` argument was
|
||||||
|
## merged into the database ot updated, and `false` if it was on the database
|
||||||
|
## already.
|
||||||
|
##
|
||||||
let
|
let
|
||||||
pyl = PayloadRef(pType: AccountData, account: accPayload)
|
pyl = PayloadRef(pType: AccountData, account: accPayload)
|
||||||
rc = db.mergePayloadImpl(VertexID(1), accKey, pyl, VidVtxPair())
|
rc = db.mergePayloadImpl(VertexID(1), accKey, pyl, VidVtxPair())
|
||||||
|
@ -73,6 +77,10 @@ proc mergeGenericData*(
|
||||||
## Variant of `mergeXXX()` for generic sub-trees, i.e. for arguments
|
## Variant of `mergeXXX()` for generic sub-trees, i.e. for arguments
|
||||||
## `root` greater than `VertexID(1)` and smaller than `LEAST_FREE_VID`.
|
## `root` greater than `VertexID(1)` and smaller than `LEAST_FREE_VID`.
|
||||||
##
|
##
|
||||||
|
## On success, the function returns `true` if the `data` argument was merged
|
||||||
|
## into the database ot updated, and `false` if it was on the database
|
||||||
|
## already.
|
||||||
|
##
|
||||||
# Verify that `root` is neither an accounts tree nor a strorage tree.
|
# Verify that `root` is neither an accounts tree nor a strorage tree.
|
||||||
if not root.isValid:
|
if not root.isValid:
|
||||||
return err(MergeRootVidMissing)
|
return err(MergeRootVidMissing)
|
||||||
|
@ -112,7 +120,8 @@ proc mergeStorageData*(
|
||||||
## otherwise `VertexID(0)`.
|
## otherwise `VertexID(0)`.
|
||||||
##
|
##
|
||||||
let
|
let
|
||||||
wpAcc = ? db.registerAccountForUpdate accPath
|
accHike = ?db.retrieveStoAccHike accPath
|
||||||
|
wpAcc = accHike.legs[^1].wp
|
||||||
stoID = wpAcc.vtx.lData.account.storageID
|
stoID = wpAcc.vtx.lData.account.storageID
|
||||||
|
|
||||||
# Provide new storage ID when needed
|
# Provide new storage ID when needed
|
||||||
|
@ -123,6 +132,9 @@ proc mergeStorageData*(
|
||||||
rc = db.mergePayloadImpl(useID, stoKey, pyl, wpAcc)
|
rc = db.mergePayloadImpl(useID, stoKey, pyl, wpAcc)
|
||||||
|
|
||||||
if rc.isOk:
|
if rc.isOk:
|
||||||
|
# Mark account path for update for `hashify()`
|
||||||
|
db.updateAccountForHasher accHike
|
||||||
|
|
||||||
if stoID.isValid:
|
if stoID.isValid:
|
||||||
return ok VertexID(0)
|
return ok VertexID(0)
|
||||||
|
|
||||||
|
@ -138,7 +150,8 @@ proc mergeStorageData*(
|
||||||
assert stoID.isValid # debugging only
|
assert stoID.isValid # debugging only
|
||||||
return ok VertexID(0)
|
return ok VertexID(0)
|
||||||
|
|
||||||
# else
|
# Error: mark account path for update for `hashify()`
|
||||||
|
db.updateAccountForHasher accHike
|
||||||
err(rc.error)
|
err(rc.error)
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
|
@ -172,69 +172,16 @@ proc subVids*(vtx: VertexRef): seq[VertexID] =
|
||||||
|
|
||||||
# ---------------------
|
# ---------------------
|
||||||
|
|
||||||
proc registerAccount*(
|
proc retrieveStoAccHike*(
|
||||||
db: AristoDbRef; # Database, top layer
|
db: AristoDbRef; # Database
|
||||||
stoRoot: VertexID; # Storage root ID
|
accPath: PathID; # Implies a storage ID (if any)
|
||||||
accPath: PathID; # Needed for accounts payload
|
): Result[Hike,AristoError] =
|
||||||
): Result[VidVtxPair,AristoError] =
|
## Verify that the `accPath` argument properly referres to a storage root
|
||||||
## Verify that the `stoRoot` argument is properly referred to by the
|
## vertex ID. The function will reset the keys along the `accPath` for
|
||||||
## account data (if any) implied to by the `accPath` argument.
|
## being modified.
|
||||||
##
|
##
|
||||||
## The function will return an account leaf node if there was any, or an empty
|
## On success, the function will return an account leaf pair with the leaf
|
||||||
## `VidVtxPair()` object.
|
## vertex and the vertex ID.
|
||||||
##
|
|
||||||
# Verify storage root and account path
|
|
||||||
if not stoRoot.isValid:
|
|
||||||
return err(UtilsStoRootMissing)
|
|
||||||
if not accPath.isValid:
|
|
||||||
return err(UtilsAccPathMissing)
|
|
||||||
|
|
||||||
# Get account leaf with account data
|
|
||||||
let hike = LeafTie(root: VertexID(1), path: accPath).hikeUp(db).valueOr:
|
|
||||||
return err(UtilsAccInaccessible)
|
|
||||||
|
|
||||||
# Check account payload
|
|
||||||
let wp = hike.legs[^1].wp
|
|
||||||
if wp.vtx.vType != Leaf:
|
|
||||||
return err(UtilsAccPathWithoutLeaf)
|
|
||||||
if wp.vtx.lData.pType != AccountData:
|
|
||||||
return err(UtilsAccLeafPayloadExpected)
|
|
||||||
|
|
||||||
# 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 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):
|
|
||||||
db.layersResKey(hike.root, w)
|
|
||||||
|
|
||||||
# Signal to `hashify()` where to start rebuilding Merkel hashes
|
|
||||||
db.top.final.dirty.incl hike.root
|
|
||||||
db.top.final.dirty.incl wp.vid
|
|
||||||
|
|
||||||
ok(wp)
|
|
||||||
|
|
||||||
|
|
||||||
proc registerAccountForUpdate*(
|
|
||||||
db: AristoDbRef; # Database, top layer
|
|
||||||
accPath: PathID; # Needed for accounts payload
|
|
||||||
): Result[VidVtxPair,AristoError] =
|
|
||||||
## ...
|
|
||||||
##
|
##
|
||||||
# Expand vertex path to account leaf
|
# Expand vertex path to account leaf
|
||||||
let hike = (@accPath).initNibbleRange.hikeUp(VertexID(1), db).valueOr:
|
let hike = (@accPath).initNibbleRange.hikeUp(VertexID(1), db).valueOr:
|
||||||
|
@ -253,15 +200,23 @@ proc registerAccountForUpdate*(
|
||||||
discard db.getVtxRc(acc.storageID).valueOr:
|
discard db.getVtxRc(acc.storageID).valueOr:
|
||||||
return err(UtilsStoRootInaccessible)
|
return err(UtilsStoRootInaccessible)
|
||||||
|
|
||||||
|
ok(hike)
|
||||||
|
|
||||||
|
proc updateAccountForHasher*(
|
||||||
|
db: AristoDbRef; # Database
|
||||||
|
hike: Hike; # Return value from `retrieveStorageID()`
|
||||||
|
) =
|
||||||
|
## For a successful run of `retrieveStoAccHike()`, the argument `hike` is
|
||||||
|
## used to mark/reset the keys along the `accPath` for being re-calculated
|
||||||
|
## by `hashify()`.
|
||||||
|
##
|
||||||
# Clear Merkle keys so that `hasify()` can calculate the re-hash forest/tree
|
# Clear Merkle keys so that `hasify()` can calculate the re-hash forest/tree
|
||||||
for w in hike.legs.mapIt(it.wp.vid):
|
for w in hike.legs.mapIt(it.wp.vid):
|
||||||
db.layersResKey(hike.root, w)
|
db.layersResKey(hike.root, w)
|
||||||
|
|
||||||
# Signal to `hashify()` where to start rebuilding Merkel hashes
|
# Signal to `hashify()` where to start rebuilding Merkel hashes
|
||||||
db.top.final.dirty.incl hike.root
|
db.top.final.dirty.incl hike.root
|
||||||
db.top.final.dirty.incl wp.vid
|
db.top.final.dirty.incl hike.legs[^1].wp.vid
|
||||||
|
|
||||||
ok(wp)
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# End
|
# End
|
||||||
|
|
|
@ -256,19 +256,25 @@ proc mptMethods(cMpt: AristoCoreDxMptRef): CoreDbMptFns =
|
||||||
proc mptDelete(key: openArray[byte]): CoreDbRc[void] =
|
proc mptDelete(key: openArray[byte]): CoreDbRc[void] =
|
||||||
const info = "deleteFn()"
|
const info = "deleteFn()"
|
||||||
|
|
||||||
if not cMpt.mptRoot.isValid and cMpt.accPath.isValid:
|
let rc = block:
|
||||||
# This is insane but legit. A storage column was announced for an account
|
if cMpt.accPath.isValid:
|
||||||
# but no data have been added, yet.
|
api.deleteStorageData(mpt, key, cMpt.accPath)
|
||||||
return ok()
|
else:
|
||||||
let rc = api.delete(mpt, cMpt.mptRoot, key, cMpt.accPath)
|
api.deleteGenericData(mpt, cMpt.mptRoot, key)
|
||||||
|
|
||||||
if rc.isErr:
|
if rc.isErr:
|
||||||
if rc.error[1] == DelPathNotFound:
|
if rc.error == DelPathNotFound:
|
||||||
return err(rc.error.toError(base, info, MptNotFound))
|
return err(rc.error.toError(base, info, MptNotFound))
|
||||||
|
if rc.error == DelStoRootMissing:
|
||||||
|
# This is insane but legit. A storage column was announced for an
|
||||||
|
# account but no data have been added, yet.
|
||||||
|
return ok()
|
||||||
return err(rc.error.toError(base, info))
|
return err(rc.error.toError(base, info))
|
||||||
|
|
||||||
if rc.value:
|
if rc.value:
|
||||||
# Column has become empty
|
# Column has become empty
|
||||||
cMpt.mptRoot = VoidVID
|
cMpt.mptRoot = VoidVID
|
||||||
|
|
||||||
ok()
|
ok()
|
||||||
|
|
||||||
proc mptHasPath(key: openArray[byte]): CoreDbRc[bool] =
|
proc mptHasPath(key: openArray[byte]): CoreDbRc[bool] =
|
||||||
|
@ -357,30 +363,21 @@ proc accMethods(cAcc: AristoCoreDxAccRef): CoreDbAccFns =
|
||||||
proc accDelete(address: EthAddress): CoreDbRc[void] =
|
proc accDelete(address: EthAddress): CoreDbRc[void] =
|
||||||
const info = "acc/deleteFn()"
|
const info = "acc/deleteFn()"
|
||||||
|
|
||||||
let
|
let key = address.keccakHash.data
|
||||||
key = address.keccakHash.data
|
api.deleteAccountPayload(mpt, key).isOkOr:
|
||||||
rc = api.delete(mpt, AccountsVID, key, VOID_PATH_ID)
|
if error == DelPathNotFound:
|
||||||
if rc.isErr:
|
return err(error.toError(base, info, AccNotFound))
|
||||||
if rc.error[1] == DelPathNotFound:
|
return err(error.toError(base, info))
|
||||||
return err(rc.error.toError(base, info, AccNotFound))
|
|
||||||
return err(rc.error.toError(base, info))
|
|
||||||
ok()
|
ok()
|
||||||
|
|
||||||
proc accStoDelete(address: EthAddress): CoreDbRc[void] =
|
proc accStoDelete(address: EthAddress): CoreDbRc[void] =
|
||||||
const info = "stoDeleteFn()"
|
const info = "stoDeleteFn()"
|
||||||
|
|
||||||
let
|
let rc = api.deleteStorageTree(mpt, address.to(PathID))
|
||||||
key = address.keccakHash.data
|
if rc.isErr and rc.error notin {DelStoRootMissing,DelStoAccMissing}:
|
||||||
pyl = api.fetchPayload(mpt, AccountsVID, key).valueOr:
|
return err(rc.error.toError(base, info))
|
||||||
return ok()
|
|
||||||
|
|
||||||
# Use storage ID from account and delete that column
|
|
||||||
if pyl.pType == AccountData:
|
|
||||||
let stoID = pyl.account.storageID
|
|
||||||
if stoID.isValid:
|
|
||||||
let rc = api.delTree(mpt, stoID, address.to(PathID))
|
|
||||||
if rc.isErr:
|
|
||||||
return err(rc.error.toError(base, info))
|
|
||||||
ok()
|
ok()
|
||||||
|
|
||||||
proc accHasPath(address: EthAddress): CoreDbRc[bool] =
|
proc accHasPath(address: EthAddress): CoreDbRc[bool] =
|
||||||
|
@ -500,8 +497,9 @@ proc ctxMethods(cCtx: AristoCoreDbCtxRef): CoreDbCtxFns =
|
||||||
# Reset column. This a emulates the behaviour of a new empty MPT on the
|
# Reset column. This a emulates the behaviour of a new empty MPT on the
|
||||||
# legacy database.
|
# legacy database.
|
||||||
if reset:
|
if reset:
|
||||||
let rc = api.delTree(mpt, newMpt.mptRoot, VOID_PATH_ID)
|
let rc = api.deleteGenericTree(mpt, newMpt.mptRoot)
|
||||||
if rc.isErr:
|
if rc.isErr:
|
||||||
|
raiseAssert "find me"
|
||||||
return err(rc.error.toError(base, info, AutoFlushFailed))
|
return err(rc.error.toError(base, info, AutoFlushFailed))
|
||||||
col.reset = false
|
col.reset = false
|
||||||
|
|
||||||
|
|
|
@ -157,10 +157,7 @@ proc freeStorage*(al: AccountLedger, eAddr: EthAddress) =
|
||||||
|
|
||||||
proc delete*(al: AccountLedger, eAddr: EthAddress) =
|
proc delete*(al: AccountLedger, eAddr: EthAddress) =
|
||||||
const info = "AccountLedger/delete()"
|
const info = "AccountLedger/delete()"
|
||||||
# Flush associated storage trie
|
# Delete account and associated storage tree (if any)
|
||||||
al.distinctBase.stoDelete(eAddr).isOkOr:
|
|
||||||
raiseAssert info & $$error
|
|
||||||
# Clear account
|
|
||||||
al.distinctBase.delete(eAddr).isOkOr:
|
al.distinctBase.delete(eAddr).isOkOr:
|
||||||
if error.error == MptNotFound:
|
if error.error == MptNotFound:
|
||||||
return
|
return
|
||||||
|
|
|
@ -13,8 +13,7 @@ import
|
||||||
eth/common,
|
eth/common,
|
||||||
stew/endians2,
|
stew/endians2,
|
||||||
../../nimbus/db/aristo/[
|
../../nimbus/db/aristo/[
|
||||||
aristo_debug, aristo_desc, aristo_delete,
|
aristo_debug, aristo_desc, aristo_hashify, aristo_hike, aristo_merge],
|
||||||
aristo_hashify, aristo_hike, aristo_merge],
|
|
||||||
../../nimbus/db/kvstore_rocksdb,
|
../../nimbus/db/kvstore_rocksdb,
|
||||||
../../nimbus/sync/protocol/snap/snap_types,
|
../../nimbus/sync/protocol/snap/snap_types,
|
||||||
../replay/[pp, undump_accounts, undump_storages],
|
../replay/[pp, undump_accounts, undump_storages],
|
||||||
|
@ -211,42 +210,6 @@ proc hashify*(
|
||||||
else:
|
else:
|
||||||
aristo_hashify.hashify(db)
|
aristo_hashify.hashify(db)
|
||||||
|
|
||||||
|
|
||||||
proc delete*(
|
|
||||||
db: AristoDbRef;
|
|
||||||
root: VertexID;
|
|
||||||
path: openArray[byte];
|
|
||||||
accPath: PathID;
|
|
||||||
noisy: bool;
|
|
||||||
): Result[bool,(VertexID,AristoError)] =
|
|
||||||
when declared(aristo_delete.noisy):
|
|
||||||
aristo_delete.exec(noisy, aristo_delete.delete(db, root, path, accPath))
|
|
||||||
else:
|
|
||||||
aristo_delete.delete(db, root, path, accPath)
|
|
||||||
|
|
||||||
proc delete*(
|
|
||||||
db: AristoDbRef;
|
|
||||||
lty: LeafTie;
|
|
||||||
accPath: PathID;
|
|
||||||
noisy: bool;
|
|
||||||
): Result[bool,(VertexID,AristoError)] =
|
|
||||||
when declared(aristo_delete.noisy):
|
|
||||||
aristo_delete.exec(noisy, aristo_delete.delete(db, lty, accPath))
|
|
||||||
else:
|
|
||||||
aristo_delete.delete(db, lty, accPath)
|
|
||||||
|
|
||||||
proc delTree*(
|
|
||||||
db: AristoDbRef;
|
|
||||||
root: VertexID;
|
|
||||||
accPath: PathID;
|
|
||||||
noisy: bool;
|
|
||||||
): Result[void,(VertexID,AristoError)] =
|
|
||||||
when declared(aristo_delete.noisy):
|
|
||||||
aristo_delete.exec(noisy, aristo_delete.delTree(db, root, accPath))
|
|
||||||
else:
|
|
||||||
aristo_delete.delTree(db, root, accPath)
|
|
||||||
|
|
||||||
|
|
||||||
proc mergeGenericData*(
|
proc mergeGenericData*(
|
||||||
db: AristoDbRef; # Database, top layer
|
db: AristoDbRef; # Database, top layer
|
||||||
leaf: LeafTiePayload; # Leaf item to add to the database
|
leaf: LeafTiePayload; # Leaf item to add to the database
|
||||||
|
|
|
@ -394,8 +394,8 @@ proc testTxMergeAndDeleteOneByOne*(
|
||||||
|
|
||||||
# Delete leaf
|
# Delete leaf
|
||||||
block:
|
block:
|
||||||
let rc = db.delete(leaf, VOID_PATH_ID)
|
let rc = db.deleteGenericData(leaf.root, @(leaf.path))
|
||||||
xCheckRc rc.error == (0,0)
|
xCheckRc rc.error == 0
|
||||||
|
|
||||||
# Update list of remaininf leafs
|
# Update list of remaininf leafs
|
||||||
leafsLeft.excl leaf
|
leafsLeft.excl leaf
|
||||||
|
@ -492,8 +492,8 @@ proc testTxMergeAndDeleteSubTree*(
|
||||||
""
|
""
|
||||||
# Delete sub-tree
|
# Delete sub-tree
|
||||||
block:
|
block:
|
||||||
let rc = db.delTree(testRootVid, VOID_PATH_ID)
|
let rc = db.deleteGenericTree testRootVid
|
||||||
xCheckRc rc.error == (0,0):
|
xCheckRc rc.error == 0:
|
||||||
noisy.say "***", "del(2)",
|
noisy.say "***", "del(2)",
|
||||||
" n=", n, "/", list.len,
|
" n=", n, "/", list.len,
|
||||||
"\n db\n ", db.pp(backendOk=true),
|
"\n db\n ", db.pp(backendOk=true),
|
||||||
|
|
Loading…
Reference in New Issue