nimbus-eth1/nimbus/db/aristo/aristo_fetch.nim
Jacek Sieka 58cde36656
Remove RawData from possible leaf payload types (#2794)
This kind of data is not used except in tests where it is used only to
create databases that don't match actual usage of aristo.

Removing simplifies future optimizations that can focus on processing
specific leaf types more efficiently.

A casualty of this removal is some test code as well as some proof
generation code that is unused - on the surface, it looks like it should
be possible to port both of these to the more specific data types -
doing so would ensure that a database written by one part of the
codebase can interact with the other - as it stands, there is confusion
on this point since using the proof generation code will result in a
database of a shape that is incompatible with the rest of eth1.
2024-11-02 10:29:16 +01:00

299 lines
8.9 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
# ------------------------------------------------------------------------------
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 cachedAccLeaf*(db: AristoDbRef; accPath: Hash32): Opt[VertexRef] =
# Return vertex from layers or cache, `nil` if it's known to not exist and
# none otherwise
db.layersGetAccLeaf(accPath) or
db.accLeaves.get(accPath) or
Opt.none(VertexRef)
proc cachedStoLeaf*(db: AristoDbRef; mixPath: Hash32): Opt[VertexRef] =
# Return vertex from layers or cache, `nil` if it's known to not exist and
# none otherwise
db.layersGetStoLeaf(mixPath) or
db.stoLeaves.get(mixPath) or
Opt.none(VertexRef)
proc retrieveAccountLeaf(
db: AristoDbRef;
accPath: Hash32;
): Result[VertexRef,AristoError] =
if (let leafVtx = db.cachedAccLeaf(accPath); leafVtx.isSome()):
if not leafVtx[].isValid():
return err(FetchPathNotFound)
return ok leafVtx[]
# 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 == FetchPathNotFound:
db.accLeaves.put(accPath, nil)
return err(error)
db.accLeaves.put(accPath, leafVtx)
ok leafVtx
proc retrieveMerkleHash(
db: AristoDbRef;
root: VertexID;
updateOk: bool;
): Result[Hash32,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(Hash32)
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: Hash32;
): Result[bool,AristoError] =
let error = db.retrieveAccountLeaf(accPath).errorOr:
return ok(true)
if error == FetchPathNotFound:
return ok(false)
err(error)
proc fetchStorageIdImpl(
db: AristoDbRef;
accPath: Hash32;
enaStoRootMissing = false;
): Result[VertexID,AristoError] =
## Helper function for retrieving a storage (vertex) ID for a given account.
let
leafVtx = ?db.retrieveAccountLeaf(accPath)
stoID = leafVtx[].lData.stoID
if stoID.isValid:
ok stoID.vid
elif enaStoRootMissing:
err(FetchPathStoRootMissing)
else:
err(FetchPathNotFound)
# ------------------------------------------------------------------------------
# Public helpers
# ------------------------------------------------------------------------------
proc fetchAccountHike*(
db: AristoDbRef; # Database
accPath: Hash32; # Implies a storage ID (if any)
accHike: var Hike
): Result[void,AristoError] =
## Expand account path to account leaf or return failure
# Prefer the leaf cache so as not to burden the lower layers
let leaf = db.cachedAccLeaf(accPath)
if leaf == Opt.some(VertexRef(nil)):
return err(FetchAccInaccessible)
accPath.hikeUp(VertexID(1), db, leaf, accHike).isOkOr:
return err(FetchAccInaccessible)
# Extract the account payload from the leaf
if accHike.legs.len == 0 or accHike.legs[^1].wp.vtx.vType != Leaf:
return err(FetchAccPathWithoutLeaf)
assert accHike.legs[^1].wp.vtx.lData.pType == AccountData
ok()
proc fetchStorageID*(
db: AristoDbRef;
accPath: Hash32;
): Result[VertexID,AristoError] =
## Public helper function for retrieving a storage (vertex) ID for a given account. This
## function returns a separate error `FetchPathStoRootMissing` (from `FetchPathNotFound`)
## if the account for the argument path `accPath` exists but has no storage root.
##
db.fetchStorageIdImpl(accPath, enaStoRootMissing=true)
proc retrieveStoragePayload(
db: AristoDbRef;
accPath: Hash32;
stoPath: Hash32;
): Result[UInt256,AristoError] =
let mixPath = mixUp(accPath, stoPath)
if (let leafVtx = db.cachedStoLeaf(mixPath); 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.fetchStorageIdImpl(accPath), stoPath.data).valueOr:
if error == FetchPathNotFound:
db.stoLeaves.put(mixPath, nil)
return err(error)
db.stoLeaves.put(mixPath, leafVtx)
ok leafVtx.lData.stoData
proc hasStoragePayload(
db: AristoDbRef;
accPath: Hash32;
stoPath: Hash32;
): 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: Hash32;
): Result[AristoAccount,AristoError] =
## Fetch an account record from the database indexed by `accPath`.
##
let leafVtx = ? db.retrieveAccountLeaf(accPath)
assert leafVtx.lData.pType == AccountData # debugging only
ok leafVtx.lData.account
proc fetchAccountStateRoot*(
db: AristoDbRef;
updateOk: bool;
): Result[Hash32,AristoError] =
## Fetch the Merkle hash of the account root.
db.retrieveMerkleHash(VertexID(1), updateOk)
proc hasPathAccount*(
db: AristoDbRef;
accPath: Hash32;
): Result[bool,AristoError] =
## For an account record indexed by `accPath` query whether this record exists
## on the database.
##
db.hasAccountPayload(accPath)
proc fetchStorageData*(
db: AristoDbRef;
accPath: Hash32;
stoPath: Hash32;
): Result[UInt256,AristoError] =
## For a storage tree related to account `accPath`, fetch the data record
## from the database indexed by `path`.
##
db.retrieveStoragePayload(accPath, stoPath)
proc fetchStorageRoot*(
db: AristoDbRef;
accPath: Hash32;
updateOk: bool;
): Result[Hash32,AristoError] =
## Fetch the Merkle hash of the storage root related to `accPath`.
let stoID = db.fetchStorageIdImpl(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: Hash32;
stoPath: Hash32;
): 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: Hash32;
): 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.fetchStorageIdImpl(accPath).valueOr:
if error == FetchPathNotFound:
return ok(false) # no sub-tree
return err(error)
ok stoID.isValid
# ------------------------------------------------------------------------------
# End
# ------------------------------------------------------------------------------