nimbus-eth1/nimbus/db/aristo/aristo_fetch.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
# ------------------------------------------------------------------------------