326 lines
9.7 KiB
Nim
326 lines
9.7 KiB
Nim
# nimbus-eth1
|
|
# Copyright (c) 2023-2024 Status Research & Development GmbH
|
|
# Licensed under either of
|
|
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
|
|
# http://www.apache.org/licenses/LICENSE-2.0)
|
|
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or
|
|
# http://opensource.org/licenses/MIT)
|
|
# at your option. This file may not be copied, modified, or distributed
|
|
# except according to those terms.
|
|
|
|
## Aristo DB -- Obects Retrival Via Traversal Path
|
|
## ===============================================
|
|
##
|
|
{.push raises: [].}
|
|
|
|
import
|
|
std/typetraits,
|
|
eth/common,
|
|
results,
|
|
"."/[aristo_compute, aristo_desc, aristo_get, aristo_layers, aristo_hike]
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Private functions
|
|
# ------------------------------------------------------------------------------
|
|
|
|
func mustBeGeneric(
|
|
root: VertexID;
|
|
): Result[void,AristoError] =
|
|
## Verify that `root` is neither from an accounts tree nor a strorage tree.
|
|
if not root.isValid:
|
|
return err(FetchRootVidMissing)
|
|
elif root == VertexID(1):
|
|
return err(FetchAccRootNotAccepted)
|
|
elif LEAST_FREE_VID <= root.distinctBase:
|
|
return err(FetchStoRootNotAccepted)
|
|
ok()
|
|
|
|
|
|
proc retrieveLeaf(
|
|
db: AristoDbRef;
|
|
root: VertexID;
|
|
path: openArray[byte];
|
|
): Result[VertexRef,AristoError] =
|
|
if path.len == 0:
|
|
return err(FetchPathInvalid)
|
|
|
|
for step in stepUp(NibblesBuf.fromBytes(path), root, db):
|
|
let vtx = step.valueOr:
|
|
if error in HikeAcceptableStopsNotFound:
|
|
return err(FetchPathNotFound)
|
|
return err(error)
|
|
|
|
if vtx.vType == Leaf:
|
|
return ok vtx
|
|
|
|
return err(FetchPathNotFound)
|
|
|
|
proc retrieveAccountPayload(
|
|
db: AristoDbRef;
|
|
accPath: Hash256;
|
|
): Result[LeafPayload,AristoError] =
|
|
if (let leafVtx = db.layersGetAccLeaf(accPath); leafVtx.isSome()):
|
|
if not leafVtx[].isValid():
|
|
return err(FetchPathNotFound)
|
|
return ok leafVtx[].lData
|
|
|
|
let accKey = accPath.to(AccountKey)
|
|
if (let leafVtx = db.accLeaves.lruFetch(accKey); leafVtx.isSome()):
|
|
if not leafVtx[].isValid():
|
|
return err(FetchPathNotFound)
|
|
return ok leafVtx[].lData
|
|
|
|
# Updated payloads are stored in the layers so if we didn't find them there,
|
|
# it must have been in the database
|
|
let
|
|
leafVtx = db.retrieveLeaf(VertexID(1), accPath.data).valueOr:
|
|
if error == FetchAccInaccessible:
|
|
return err(FetchPathNotFound)
|
|
return err(error)
|
|
|
|
ok db.accLeaves.lruAppend(accKey, leafVtx, accLruSize).lData
|
|
|
|
proc retrieveMerkleHash(
|
|
db: AristoDbRef;
|
|
root: VertexID;
|
|
updateOk: bool;
|
|
): Result[Hash256,AristoError] =
|
|
let key =
|
|
if updateOk:
|
|
db.computeKey((root, root)).valueOr:
|
|
if error == GetVtxNotFound:
|
|
return ok(EMPTY_ROOT_HASH)
|
|
return err(error)
|
|
else:
|
|
let (key, _) = db.getKeyRc((root, root)).valueOr:
|
|
if error == GetKeyNotFound:
|
|
return ok(EMPTY_ROOT_HASH) # empty sub-tree
|
|
return err(error)
|
|
key
|
|
ok key.to(Hash256)
|
|
|
|
|
|
proc hasPayload(
|
|
db: AristoDbRef;
|
|
root: VertexID;
|
|
path: openArray[byte];
|
|
): Result[bool,AristoError] =
|
|
let error = db.retrieveLeaf(root, path).errorOr:
|
|
return ok(true)
|
|
|
|
if error == FetchPathNotFound:
|
|
return ok(false)
|
|
err(error)
|
|
|
|
proc hasAccountPayload(
|
|
db: AristoDbRef;
|
|
accPath: Hash256;
|
|
): Result[bool,AristoError] =
|
|
let error = db.retrieveAccountPayload(accPath).errorOr:
|
|
return ok(true)
|
|
|
|
if error == FetchPathNotFound:
|
|
return ok(false)
|
|
err(error)
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Public helpers
|
|
# ------------------------------------------------------------------------------
|
|
|
|
proc fetchAccountHike*(
|
|
db: AristoDbRef; # Database
|
|
accPath: Hash256; # Implies a storage ID (if any)
|
|
): Result[Hike,AristoError] =
|
|
## Verify that the `accPath` argument properly referres to a storage root
|
|
## vertex ID. The function will reset the keys along the `accPath` for
|
|
## being modified.
|
|
##
|
|
## On success, the function will return an account leaf pair with the leaf
|
|
## vertex and the vertex ID.
|
|
##
|
|
# Expand vertex path to account leaf
|
|
var hike = accPath.hikeUp(VertexID(1), db).valueOr:
|
|
return err(FetchAccInaccessible)
|
|
|
|
# Extract the account payload from the leaf
|
|
let wp = hike.legs[^1].wp
|
|
if wp.vtx.vType != Leaf:
|
|
return err(FetchAccPathWithoutLeaf)
|
|
assert wp.vtx.lData.pType == AccountData # debugging only
|
|
|
|
ok(move(hike))
|
|
|
|
proc fetchStorageID*(
|
|
db: AristoDbRef;
|
|
accPath: Hash256;
|
|
): Result[VertexID,AristoError] =
|
|
## Public helper function for retrieving a storage (vertex) ID for a
|
|
## given account.
|
|
let
|
|
payload = ?db.retrieveAccountPayload(accPath)
|
|
stoID = payload.stoID
|
|
|
|
if not stoID.isValid:
|
|
return err(FetchPathNotFound)
|
|
|
|
ok stoID
|
|
|
|
proc retrieveStoragePayload(
|
|
db: AristoDbRef;
|
|
accPath: Hash256;
|
|
stoPath: Hash256;
|
|
): Result[UInt256,AristoError] =
|
|
let mixPath = AccountKey.mixUp(accPath, stoPath)
|
|
if (let leafVtx = db.layersGetStoLeaf(mixPath); leafVtx.isSome()):
|
|
if not leafVtx[].isValid():
|
|
return err(FetchPathNotFound)
|
|
return ok leafVtx[].lData.stoData
|
|
|
|
let mixKey = mixPath.to(AccountKey)
|
|
if (let leafVtx = db.stoLeaves.lruFetch(mixKey); leafVtx.isSome()):
|
|
if not leafVtx[].isValid():
|
|
return err(FetchPathNotFound)
|
|
return ok leafVtx[].lData.stoData
|
|
|
|
# Updated payloads are stored in the layers so if we didn't find them there,
|
|
# it must have been in the database
|
|
let
|
|
leafVtx = db.retrieveLeaf(? db.fetchStorageID(accPath), stoPath.data).valueOr:
|
|
return err(error)
|
|
|
|
ok db.stoLeaves.lruAppend(mixKey, leafVtx, accLruSize).lData.stoData
|
|
|
|
proc hasStoragePayload(
|
|
db: AristoDbRef;
|
|
accPath: Hash256;
|
|
stoPath: Hash256;
|
|
): Result[bool,AristoError] =
|
|
let error = db.retrieveStoragePayload(accPath, stoPath).errorOr:
|
|
return ok(true)
|
|
|
|
if error == FetchPathNotFound:
|
|
return ok(false)
|
|
err(error)
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Public functions
|
|
# ------------------------------------------------------------------------------
|
|
|
|
proc fetchLastSavedState*(
|
|
db: AristoDbRef;
|
|
): Result[SavedState,AristoError] =
|
|
## Wrapper around `getLstUbe()`. The function returns the state of the last
|
|
## saved state. This is a Merkle hash tag for vertex with ID 1 and a bespoke
|
|
## `uint64` identifier (may be interpreted as block number.)
|
|
db.getLstUbe()
|
|
|
|
proc fetchAccountRecord*(
|
|
db: AristoDbRef;
|
|
accPath: Hash256;
|
|
): Result[AristoAccount,AristoError] =
|
|
## Fetch an account record from the database indexed by `accPath`.
|
|
##
|
|
let pyl = ? db.retrieveAccountPayload(accPath)
|
|
assert pyl.pType == AccountData # debugging only
|
|
|
|
ok pyl.account
|
|
|
|
proc fetchAccountState*(
|
|
db: AristoDbRef;
|
|
updateOk: bool;
|
|
): Result[Hash256,AristoError] =
|
|
## Fetch the Merkle hash of the account root.
|
|
db.retrieveMerkleHash(VertexID(1), updateOk)
|
|
|
|
proc hasPathAccount*(
|
|
db: AristoDbRef;
|
|
accPath: Hash256;
|
|
): Result[bool,AristoError] =
|
|
## For an account record indexed by `accPath` query whether this record exists
|
|
## on the database.
|
|
##
|
|
db.hasAccountPayload(accPath)
|
|
|
|
proc fetchGenericData*(
|
|
db: AristoDbRef;
|
|
root: VertexID;
|
|
path: openArray[byte];
|
|
): Result[Blob,AristoError] =
|
|
## For a generic sub-tree starting at `root`, fetch the data record
|
|
## indexed by `path`.
|
|
##
|
|
? root.mustBeGeneric()
|
|
let pyl = ? db.retrieveLeaf(root, path)
|
|
assert pyl.lData.pType == RawData # debugging only
|
|
ok pyl.lData.rawBlob
|
|
|
|
proc fetchGenericState*(
|
|
db: AristoDbRef;
|
|
root: VertexID;
|
|
updateOk: bool;
|
|
): Result[Hash256,AristoError] =
|
|
## Fetch the Merkle hash of the argument `root`.
|
|
db.retrieveMerkleHash(root, updateOk)
|
|
|
|
proc hasPathGeneric*(
|
|
db: AristoDbRef;
|
|
root: VertexID;
|
|
path: openArray[byte];
|
|
): Result[bool,AristoError] =
|
|
## For a generic sub-tree starting at `root` and indexed by `path`, query
|
|
## whether this record exists on the database.
|
|
##
|
|
? root.mustBeGeneric()
|
|
db.hasPayload(root, path)
|
|
|
|
proc fetchStorageData*(
|
|
db: AristoDbRef;
|
|
accPath: Hash256;
|
|
stoPath: Hash256;
|
|
): Result[UInt256,AristoError] =
|
|
## For a storage tree related to account `accPath`, fetch the data record
|
|
## from the database indexed by `path`.
|
|
##
|
|
let leafVtx = ? db.retrieveLeaf(? db.fetchStorageID accPath, stoPath.data)
|
|
assert leafVtx.lData.pType == StoData # debugging only
|
|
ok leafVtx.lData.stoData
|
|
|
|
proc fetchStorageState*(
|
|
db: AristoDbRef;
|
|
accPath: Hash256;
|
|
updateOk: bool;
|
|
): Result[Hash256,AristoError] =
|
|
## Fetch the Merkle hash of the storage root related to `accPath`.
|
|
let stoID = db.fetchStorageID(accPath).valueOr:
|
|
if error == FetchPathNotFound:
|
|
return ok(EMPTY_ROOT_HASH) # no sub-tree
|
|
return err(error)
|
|
db.retrieveMerkleHash(stoID, updateOk)
|
|
|
|
proc hasPathStorage*(
|
|
db: AristoDbRef;
|
|
accPath: Hash256;
|
|
stoPath: Hash256;
|
|
): Result[bool,AristoError] =
|
|
## For a storage tree related to account `accPath`, query whether the data
|
|
## record indexed by `path` exists on the database.
|
|
##
|
|
db.hasStoragePayload(accPath, stoPath)
|
|
|
|
proc hasStorageData*(
|
|
db: AristoDbRef;
|
|
accPath: Hash256;
|
|
): Result[bool,AristoError] =
|
|
## For a storage tree related to account `accPath`, query whether there
|
|
## is a non-empty data storage area at all.
|
|
##
|
|
let stoID = db.fetchStorageID(accPath).valueOr:
|
|
if error == FetchPathNotFound:
|
|
return ok(false) # no sub-tree
|
|
return err(error)
|
|
ok stoID.isValid
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# End
|
|
# ------------------------------------------------------------------------------
|