nimbus-eth1/nimbus/db/core_db/backend/aristo_db.nim

302 lines
9.2 KiB
Nim
Raw Normal View History

# Nimbus
Core db update storage root management for sub tries (#1964) * Aristo: Re-phrase `LayerDelta` and `LayerFinal` as object references why: Avoids copying in some cases * Fix copyright header * Aristo: Verify `leafTie.root` function argument for `merge()` proc why: Zero root will lead to inconsistent DB entry * Aristo: Update failure condition for hash labels compiler `hashify()` why: Node need not be rejected as long as links are on the schedule. In that case, `redo[]` is to become `wff.base[]` at a later stage. This amends an earlier fix, part of #1952 by also testing against the target nodes of the `wff.base[]` sets. * Aristo: Add storage root glue record to `hashify()` schedule why: An account leaf node might refer to a non-resolvable storage root ID. Storage root node chains will end up at the storage root. So the link `storage-root->account-leaf` needs an extra item in the schedule. * Aristo: fix error code returned by `fetchPayload()` details: Final error code is implied by the error code form the `hikeUp()` function. * CoreDb: Discard `createOk` argument in API `getRoot()` function why: Not needed for the legacy DB. For the `Arsto` DB, a lazy approach is implemented where a stprage root node is created on-the-fly. * CoreDb: Prevent `$$` logging in some cases why: Logging the function `$$` is not useful when it is used for internal use, i.e. retrieving an an error text for logging. * CoreDb: Add `tryHashFn()` to API for pretty printing why: Pretty printing must not change the hashification status for the `Aristo` DB. So there is an independent API wrapper for getting the node hash which never updated the hashes. * CoreDb: Discard `update` argument in API `hash()` function why: When calling the API function `hash()`, the latest state is always wanted. For a version that uses the current state as-is without checking, the function `tryHash()` was added to the backend. * CoreDb: Update opaque vertex ID objects for the `Aristo` backend why: For `Aristo`, vID objects encapsulate a numeric `VertexID` referencing a vertex (rather than a node hash as used on the legacy backend.) For storage sub-tries, there might be no initial vertex known when the descriptor is created. So opaque vertex ID objects are supported without a valid `VertexID` which will be initalised on-the-fly when the first item is merged. * CoreDb: Add pretty printer for opaque vertex ID objects * Cosmetics, printing profiling data * CoreDb: Fix segfault in `Aristo` backend when creating MPT descriptor why: Missing initialisation error * CoreDb: Allow MPT to inherit shared context on `Aristo` backend why: Creates descriptors with different storage roots for the same shared `Aristo` DB descriptor. * Cosmetics, update diagnostic message items for `Aristo` backend * Fix Copyright year
2024-01-11 19:11:38 +00:00
# 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.
{.push raises: [].}
import
std/[tables, typetraits],
eth/common,
results,
../../aristo as use_ari,
../../aristo/aristo_walk,
../../kvt as use_kvt,
../../kvt/[kvt_init/memory_only, kvt_walk],
".."/[base, base/base_desc],
./aristo_db/[common_desc, handlers_aristo, handlers_kvt, handlers_trace]
import
../../aristo/aristo_init/memory_only as aristo_memory_only
# Caveat:
# additional direct include(s) -- not import(s) -- is placed near
# the end of this source file
# Annotation helper(s)
{.pragma: noRaise, gcsafe, raises: [].}
{.pragma: rlpRaise, gcsafe, raises: [AristoApiRlpError].}
export
AristoApiRlpError,
AristoCoreDbKvtBE,
isAristo
type
AristoCoreDbRef* = ref object of CoreDbRef
## Main descriptor
kdbBase: KvtBaseRef ## Kvt subsystem
adbBase: AristoBaseRef ## Aristo subsystem
tracer: AristoTracerRef ## Currently active recorder
AristoTracerRef = ref object of TraceRecorderRef
## Sub-handle for tracer
parent: AristoCoreDbRef
proc newAristoVoidCoreDbRef*(): CoreDbRef {.noRaise.}
# ------------------------------------------------------------------------------
# Private tx and base methods
# ------------------------------------------------------------------------------
proc txMethods(
db: AristoCoreDbRef;
aTx: AristoTxRef;
kTx: KvtTxRef;
): CoreDbTxFns =
## To be constructed by some `CoreDbBaseFns` function
let
adbBase = db.adbBase
kdbBase = db.kdbBase
adbApi = adbBase.api
kdbApi = kdbBase.api
CoreDbTxFns(
levelFn: proc(): int =
aTx.level,
commitFn: proc(ignore: bool): CoreDbRc[void] =
const info = "commitFn()"
? adbApi.commit(aTx).toVoidRc(adbBase, info)
? kdbApi.commit(kTx).toVoidRc(kdbBase, info)
ok(),
rollbackFn: proc(): CoreDbRc[void] =
const info = "rollbackFn()"
? adbApi.rollback(aTx).toVoidRc(adbBase, info)
? kdbApi.rollback(kTx).toVoidRc(kdbBase, info)
ok(),
disposeFn: proc(): CoreDbRc[void] =
const info = "disposeFn()"
if adbApi.isTop(aTx): ? adbApi.rollback(aTx).toVoidRc(adbBase, info)
if kdbApi.isTop(kTx): ? kdbApi.rollback(kTx).toVoidRc(kdbBase, info)
ok(),
safeDisposeFn: proc(): CoreDbRc[void] =
const info = "safeDisposeFn()"
if adbApi.isTop(aTx): ? adbApi.rollback(aTx).toVoidRc(adbBase, info)
if kdbApi.isTop(kTx): ? kdbApi.rollback(kTx).toVoidRc(kdbBase, info)
ok())
proc cptMethods(
tracer: AristoTracerRef;
): CoreDbCaptFns =
let
tr = tracer # So it can savely be captured
db = tr.parent # Will not change and can be captured
log = tr.topInst() # Ditto
CoreDbCaptFns(
recorderFn: proc(): CoreDbRef =
db,
logDbFn: proc(): TableRef[Blob,Blob] =
log.kLog,
getFlagsFn: proc(): set[CoreDbCaptFlags] =
log.flags,
forgetFn: proc() =
if not tracer.pop():
tr.parent.tracer = AristoTracerRef(nil)
tr.restore())
proc baseMethods(db: AristoCoreDbRef): CoreDbBaseFns =
let
aBase = db.adbBase
kBase = db.kdbBase
proc tracerSetup(flags: set[CoreDbCaptFlags]): CoreDxCaptRef =
if db.tracer.isNil:
db.tracer = AristoTracerRef(parent: db)
db.tracer.init(kBase, aBase, flags)
else:
db.tracer.push(flags)
CoreDxCaptRef(methods: db.tracer.cptMethods)
proc persistent(bn: Option[BlockNumber]): CoreDbRc[void] =
const info = "persistentFn()"
let fid =
if bn.isNone: none(FilterID)
else: some(bn.unsafeGet.truncate(uint64).FilterID)
? kBase.persistent info
? aBase.persistent(fid, info)
ok()
CoreDbBaseFns(
destroyFn: proc(flush: bool) =
aBase.destroy(flush)
kBase.destroy(flush),
levelFn: proc(): int =
aBase.getLevel,
colStateFn: proc(col: CoreDbColRef): CoreDbRc[Hash256] =
aBase.rootHash(col, "rootHashFn()"),
Core db update storage root management for sub tries (#1964) * Aristo: Re-phrase `LayerDelta` and `LayerFinal` as object references why: Avoids copying in some cases * Fix copyright header * Aristo: Verify `leafTie.root` function argument for `merge()` proc why: Zero root will lead to inconsistent DB entry * Aristo: Update failure condition for hash labels compiler `hashify()` why: Node need not be rejected as long as links are on the schedule. In that case, `redo[]` is to become `wff.base[]` at a later stage. This amends an earlier fix, part of #1952 by also testing against the target nodes of the `wff.base[]` sets. * Aristo: Add storage root glue record to `hashify()` schedule why: An account leaf node might refer to a non-resolvable storage root ID. Storage root node chains will end up at the storage root. So the link `storage-root->account-leaf` needs an extra item in the schedule. * Aristo: fix error code returned by `fetchPayload()` details: Final error code is implied by the error code form the `hikeUp()` function. * CoreDb: Discard `createOk` argument in API `getRoot()` function why: Not needed for the legacy DB. For the `Arsto` DB, a lazy approach is implemented where a stprage root node is created on-the-fly. * CoreDb: Prevent `$$` logging in some cases why: Logging the function `$$` is not useful when it is used for internal use, i.e. retrieving an an error text for logging. * CoreDb: Add `tryHashFn()` to API for pretty printing why: Pretty printing must not change the hashification status for the `Aristo` DB. So there is an independent API wrapper for getting the node hash which never updated the hashes. * CoreDb: Discard `update` argument in API `hash()` function why: When calling the API function `hash()`, the latest state is always wanted. For a version that uses the current state as-is without checking, the function `tryHash()` was added to the backend. * CoreDb: Update opaque vertex ID objects for the `Aristo` backend why: For `Aristo`, vID objects encapsulate a numeric `VertexID` referencing a vertex (rather than a node hash as used on the legacy backend.) For storage sub-tries, there might be no initial vertex known when the descriptor is created. So opaque vertex ID objects are supported without a valid `VertexID` which will be initalised on-the-fly when the first item is merged. * CoreDb: Add pretty printer for opaque vertex ID objects * Cosmetics, printing profiling data * CoreDb: Fix segfault in `Aristo` backend when creating MPT descriptor why: Missing initialisation error * CoreDb: Allow MPT to inherit shared context on `Aristo` backend why: Creates descriptors with different storage roots for the same shared `Aristo` DB descriptor. * Cosmetics, update diagnostic message items for `Aristo` backend * Fix Copyright year
2024-01-11 19:11:38 +00:00
colPrintFn: proc(vid: CoreDbColRef): string =
aBase.colPrint(vid),
errorPrintFn: proc(e: CoreDbErrorRef): string =
e.errorPrint(),
legacySetupFn: proc() =
discard,
newKvtFn: proc(offSite: bool): CoreDbRc[CoreDxKvtRef] =
kBase.newKvtHandler(offSite, "newKvtFn()"),
newCtxFn: proc(): CoreDbCtxRef =
aBase.ctx,
newCtxFromTxFn: proc(r: Hash256; k: CoreDbColType): CoreDbRc[CoreDbCtxRef] =
CoreDbCtxRef.init(db.adbBase, r, k),
swapCtxFn: proc(ctx: CoreDbCtxRef): CoreDbCtxRef =
aBase.swapCtx(ctx),
beginFn: proc(): CoreDbRc[CoreDxTxRef] =
const info = "beginFn()"
let dsc = CoreDxTxRef(
methods: db.txMethods(? aBase.txBegin info, ? kBase.txBegin info))
ok(db.bless dsc),
newCaptureFn: proc(flags: set[CoreDbCaptFlags]): CoreDbRc[CoreDxCaptRef] =
ok(db.bless flags.tracerSetup()),
persistentFn: proc(bn: Option[BlockNumber]): CoreDbRc[void] =
persistent(bn))
# ------------------------------------------------------------------------------
# Public constructor and helper
# ------------------------------------------------------------------------------
proc create*(dbType: CoreDbType; kdb: KvtDbRef; adb: AristoDbRef): CoreDbRef =
## Constructor helper
# Local extensions
var db = AristoCoreDbRef()
db.adbBase = AristoBaseRef.init(db, adb)
db.kdbBase = KvtBaseRef.init(db, kdb)
# Base descriptor
db.dbType = dbType
db.methods = db.baseMethods()
db.bless()
proc newAristoMemoryCoreDbRef*(qlr: QidLayoutRef): CoreDbRef =
AristoDbMemory.create(
KvtDbRef.init(use_kvt.MemBackendRef),
AristoDbRef.init(use_ari.MemBackendRef, qlr))
proc newAristoMemoryCoreDbRef*(): CoreDbRef =
AristoDbMemory.create(
KvtDbRef.init(use_kvt.MemBackendRef),
AristoDbRef.init(use_ari.MemBackendRef))
proc newAristoVoidCoreDbRef*(): CoreDbRef =
AristoDbVoid.create(
KvtDbRef.init(use_kvt.VoidBackendRef),
AristoDbRef.init(use_ari.VoidBackendRef))
# ------------------------------------------------------------------------------
# Public helpers, e.g. for direct backend access
# ------------------------------------------------------------------------------
func toAristoProfData*(
db: CoreDbRef;
): tuple[aristo: AristoDbProfListRef, kvt: KvtDbProfListRef] =
when CoreDbEnableApiProfiling:
if db.isAristo:
result.aristo = db.AristoCoreDbRef.adbBase.api.AristoApiProfRef.data
result.kvt = db.AristoCoreDbRef.kdbBase.api.KvtApiProfRef.data
func toAristoApi*(kvt: CoreDxKvtRef): KvtApiRef =
if kvt.parent.isAristo:
return AristoCoreDbRef(kvt.parent).kdbBase.api
func toAristoApi*(mpt: CoreDxMptRef): AristoApiRef =
if mpt.parent.isAristo:
return mpt.to(AristoApiRef)
func toAristo*(kBe: CoreDbKvtBackendRef): KvtDbRef =
if not kBe.isNil and kBe.parent.isAristo:
return kBe.AristoCoreDbKvtBE.kdb
func toAristo*(mBe: CoreDbMptBackendRef): AristoDbRef =
if not mBe.isNil and mBe.parent.isAristo:
return mBe.AristoCoreDbMptBE.adb
proc toAristoOldestState*(
mBe: CoreDbMptBackendRef;
): tuple[stateRoot: Hash256, blockNumber: BlockNumber] =
if not mBe.isNil and mBe.parent.isAristo:
let fil = mBe.parent.AristoCoreDbRef.adbBase.getFromJournal none(FilterID)
if not fil.isNil:
return (fil.trg, fil.fid.distinctBase.toBlockNumber)
(EMPTY_ROOT_HASH, 0.toBlockNumber)
# ------------------------------------------------------------------------------
# Public aristo iterators
# ------------------------------------------------------------------------------
include
./aristo_db/aristo_replicate
# ------------------------
iterator aristoKvtPairsVoid*(dsc: CoreDxKvtRef): (Blob,Blob) {.rlpRaise.} =
let
api = dsc.toAristoApi()
p = api.forkTx(dsc.to(KvtDbRef),0).valueOrApiError "aristoKvtPairs()"
defer: discard api.forget(p)
for (k,v) in use_kvt.VoidBackendRef.walkPairs p:
yield (k,v)
iterator aristoKvtPairsMem*(dsc: CoreDxKvtRef): (Blob,Blob) {.rlpRaise.} =
let
api = dsc.toAristoApi()
p = api.forkTx(dsc.to(KvtDbRef),0).valueOrApiError "aristoKvtPairs()"
defer: discard api.forget(p)
for (k,v) in use_kvt.MemBackendRef.walkPairs p:
yield (k,v)
iterator aristoMptPairs*(dsc: CoreDxMptRef): (Blob,Blob) {.noRaise.} =
let
api = dsc.to(AristoApiRef)
mpt = dsc.to(AristoDbRef)
for (k,v) in mpt.rightPairs LeafTie(root: dsc.rootID):
yield (api.pathAsBlob(k.path), api.serialise(mpt, v).valueOr(EmptyBlob))
iterator aristoReplicateMem*(dsc: CoreDxMptRef): (Blob,Blob) {.rlpRaise.} =
## Instantiation for `MemBackendRef`
for k,v in aristoReplicate[use_ari.MemBackendRef](dsc):
yield (k,v)
iterator aristoReplicateVoid*(dsc: CoreDxMptRef): (Blob,Blob) {.rlpRaise.} =
## Instantiation for `VoidBackendRef`
for k,v in aristoReplicate[use_ari.VoidBackendRef](dsc):
yield (k,v)
# ------------------------------------------------------------------------------
# End
# ------------------------------------------------------------------------------