nimbus-eth1/nimbus/db/core_db/base.nim

894 lines
31 KiB
Nim

# Nimbus
# Copyright (c) 2018-2023 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/typetraits,
chronicles,
eth/common,
results,
"../.."/[constants, errors],
../aristo/aristo_constants, # `EmptyBlob`
./base/[base_desc, validate]
export
CoreDbAccount,
CoreDbApiError,
CoreDbBackendRef,
CoreDbCaptFlags,
CoreDbErrorCode,
CoreDbErrorRef,
CoreDbAccBackendRef,
CoreDbKvtBackendRef,
CoreDbMptBackendRef,
CoreDbRef,
CoreDbType,
CoreDbVidRef,
CoreDxAccRef,
CoreDxCaptRef,
CoreDxKvtRef,
CoreDxMptRef,
CoreDxPhkRef,
CoreDxTxID,
CoreDxTxRef
when defined(release):
const AutoValidateDescriptors = false
else:
const AutoValidateDescriptors = true
const
ProvideCoreDbLegacyAPI* = true # and false
EnableApiTracking = true and false
## When enabled, functions using this tracking facility need to import
## `chronicles`, as well. Tracking is enabled by setting the `trackLegaApi`
## and/or the `trackNewApi` flags to `true`.
# Annotation helpers
{.pragma: noRaise, gcsafe, raises: [].}
{.pragma: apiRaise, gcsafe, raises: [CoreDbApiError].}
{.pragma: catchRaise, gcsafe, raises: [CatchableError].}
when ProvideCoreDbLegacyAPI:
type
TxWrapperApiError* = object of CoreDbApiError
## For re-routing exception on tx/action template
CoreDbKvtRef* = distinct CoreDxKvtRef ## Let methods defect on error
CoreDbMptRef* = distinct CoreDxMptRef ## ...
CoreDbPhkRef* = distinct CoreDxPhkRef
CoreDbTxRef* = distinct CoreDxTxRef
CoreDbTxID* = distinct CoreDxTxID
CoreDbCaptRef* = distinct CoreDxCaptRef
CoreDbTrieRefs* = CoreDbMptRef | CoreDbPhkRef
## Shortcut, *MPT* modules for (legacy API)
CoreDbChldRefs* = CoreDbKvtRef | CoreDbTrieRefs | CoreDbTxRef | CoreDbTxID |
CoreDbCaptRef
## Shortcut, all modules with a `parent` entry (for legacy API)
type
CoreDxTrieRefs = CoreDxMptRef | CoreDxPhkRef | CoreDxAccRef
## Shortcut, *MPT* descriptors
CoreDxTrieRelated = CoreDxTrieRefs | CoreDxTxRef | CoreDxTxID | CoreDxCaptRef
## Shortcut, descriptors for sub-modules running on an *MPT*
CoreDbBackends = CoreDbBackendRef | CoreDbKvtBackendRef |
CoreDbMptBackendRef | CoreDbAccBackendRef
## Shortcut, all backend descriptors.
CoreDxChldRefs = CoreDxKvtRef | CoreDxTrieRelated | CoreDbVidRef |
CoreDbBackends | CoreDbErrorRef
## Shortcut, all descriptors with a `parent` entry.
# ------------------------------------------------------------------------------
# Private helpers
# ------------------------------------------------------------------------------
template logTxt(info: static[string]): static[string] =
"CoreDb " & info
template itNotImplemented(db: CoreDbRef, name: string) =
warn logTxt "iterator not implemented", dbType=db.dbType, meth=name
# ---------
when EnableApiTracking:
import std/[sequtils, strutils], stew/byteutils
func getParent(w: CoreDxChldRefs): auto =
## Avoida inifinite call to `parent()` in `ifTrack*Api()` tmplates
w.parent
when ProvideCoreDbLegacyAPI:
template legaApiTxt(info: static[string]): static[string] =
logTxt "legacy API " & info
template setTrackLegaApiOnly(w: CoreDbChldRefs|CoreDbRef) =
when typeof(w) is CoreDbRef:
let db = w
else:
let db = w.distinctBase.getParent
let save = db.trackNewApi
# Prevent from cascaded logging
db.trackNewApi = false
defer: db.trackNewApi = save
template ifTrackLegaApi(w: CoreDbChldRefs|CoreDbRef; code: untyped) =
block:
when typeof(w) is CoreDbRef:
let db = w
else:
let db = w.distinctBase.getParent
if db.trackLegaApi:
code
template newApiTxt(info: static[string]): static[string] =
logTxt "new API " & info
template ifTrackNewApi(w: CoreDxChldRefs|CoreDbRef; code: untyped) =
block:
when typeof(w) is CoreDbRef:
let db = w
else:
if w.isNil: break
let db = w.getParent
if db.trackNewApi:
code
proc oaToStr(w: openArray[byte]): string =
w.toHex.toLowerAscii
proc toStr(w: Hash256): string =
if w == EMPTY_ROOT_HASH: "EMPTY_ROOT_HASH" else: w.data.oaToStr
proc toStr(p: CoreDbVidRef): string =
if p.isNil: "vidRef(nil)" else: "vidRef"
proc toStr(w: Blob): string =
if 0 < w.len and w.len < 5: "<" & w.oaToStr & ">"
else: "Blob[" & $w.len & "]"
proc toStr(w: openArray[byte]): string =
w.oaToStr
proc toStr(w: set[CoreDbCaptFlags]): string =
"Flags[" & $w.len & "]"
proc toStr(rc: CoreDbRc[bool]): string =
if rc.isOk: "ok(" & $rc.value & ")" else: "err(..)"
proc toStr(rc: CoreDbRc[void]): string =
if rc.isOk: "ok()" else:"err()"
proc toStr(rc: CoreDbRc[Blob]): string =
if rc.isOk: "ok(Blob[" & $rc.value.len & "])" else: "err(..)"
proc toStr(rc: Result[Hash256,void]): string =
if rc.isOk: "ok(" & rc.value.toStr & ")" else: "err()"
proc toStr(rc: Result[Account,void]): string =
if rc.isOk: "ok(Account)" else: "err()"
proc toStr[T](rc: CoreDbRc[T]; ifOk: static[string]): string =
if rc.isOk: "ok(" & ifOk & ")" else: "err(..)"
proc toStr(rc: CoreDbRc[CoreDbRef]): string = rc.toStr "dbRef"
proc toStr(rc: CoreDbRc[CoreDbVidRef]): string = rc.toStr "vidRef"
proc toStr(rc: CoreDbRc[CoreDbAccount]): string = rc.toStr "accRef"
proc toStr(rc: CoreDbRc[CoreDxTxID]): string = rc.toStr "txId"
proc toStr(rc: CoreDbRc[CoreDxTxRef]): string = rc.toStr "txRef"
proc toStr(rc: CoreDbRc[CoreDxCaptRef]): string = rc.toStr "captRef"
else:
when ProvideCoreDbLegacyAPI:
template setTrackLegaApiOnly(w: CoreDbChldRefs|CoreDbRef) = discard
template ifTrackLegaApi(w: CoreDbChldRefs|CoreDbRef; c: untyped) = discard
template ifTrackNewApi(w: CoreDxChldRefs|CoreDbRef; code: untyped) = discard
# ---------
func toCoreDxPhkRef(mpt: CoreDxMptRef): CoreDxPhkRef =
## MPT => pre-hashed MPT (aka PHK)
result = CoreDxPhkRef(
fromMpt: mpt,
methods: mpt.methods)
result.methods.fetchFn =
proc(k: openArray[byte]): CoreDbRc[Blob] =
mpt.methods.fetchFn(k.keccakHash.data)
result.methods.deleteFn =
proc(k: openArray[byte]): CoreDbRc[void] =
mpt.methods.deleteFn(k.keccakHash.data)
result.methods.mergeFn =
proc(k:openArray[byte]; v: openArray[byte]): CoreDbRc[void] =
mpt.methods.mergeFn(k.keccakHash.data, v)
result.methods.hasPathFn =
proc(k: openArray[byte]): CoreDbRc[bool] =
mpt.methods.hasPathFn(k.keccakHash.data)
result.methods.pairsIt =
iterator(): (Blob, Blob) {.apiRaise.} =
mpt.parent.itNotImplemented("phk/pairs()")
result.methods.replicateIt =
iterator(): (Blob, Blob) {.apiRaise.} =
mpt.parent.itNotImplemented("phk/replicate()")
when AutoValidateDescriptors:
result.validate
func parent(phk: CoreDxPhkRef): CoreDbRef =
phk.fromMpt.parent
# ------------------------------------------------------------------------------
# Public constructor helper
# ------------------------------------------------------------------------------
proc bless*(db: CoreDbRef): CoreDbRef =
## Verify descriptor
when AutoValidateDescriptors:
db.validate
db.ifTrackNewApi: info newApiTxt "CoreDbRef.init()", dbType=db.dbType
db
proc bless*(db: CoreDbRef; child: CoreDbVidRef): CoreDbVidRef =
## Complete sub-module descriptor, fill in `parent` and actvate it.
child.parent = db
child.ready = true
when AutoValidateDescriptors:
child.validate
child
proc bless*(db: CoreDbRef; child: CoreDxKvtRef): CoreDxKvtRef =
## Complete sub-module descriptor, fill in `parent` and de-actvate
## iterator for persistent database.
child.parent = db
# Disable interator for non-memory instances
if db.dbType in CoreDbPersistentTypes:
child.methods.pairsIt = iterator(): (Blob, Blob) =
db.itNotImplemented "pairs/kvt"
when AutoValidateDescriptors:
child.validate
child
proc bless*[T: CoreDxTrieRelated | CoreDbBackends](
db: CoreDbRef;
child: T;
): auto =
## Complete sub-module descriptor, fill in `parent`.
child.parent = db
when AutoValidateDescriptors:
child.validate
child
proc bless*(
db: CoreDbRef;
error: CoreDbErrorCode;
child: CoreDbErrorRef;
): CoreDbErrorRef =
child.parent = db
child.error = error
when AutoValidateDescriptors:
child.validate
child
# ------------------------------------------------------------------------------
# Public main descriptor methods
# ------------------------------------------------------------------------------
proc dbType*(db: CoreDbRef): CoreDbType =
## Getter
result = db.dbType
db.ifTrackNewApi: info newApiTxt "dbType()", result
proc compensateLegacySetup*(db: CoreDbRef) =
## On the persistent legacy hexary trie, this function is needed for
## bootstrapping and Genesis setup when the `purge` flag is activated.
## Otherwise the database backend may defect on an internal inconsistency.
db.methods.legacySetupFn()
db.ifTrackNewApi: info newApiTxt "compensateLegacySetup()"
proc parent*(cld: CoreDxChldRefs): CoreDbRef =
## Getter, common method for all sub-modules
result = cld.parent
proc backend*(dsc: CoreDxKvtRef | CoreDxTrieRelated | CoreDbRef): auto =
## Getter, retrieves the *raw* backend object for special/localised support.
result = dsc.methods.backendFn()
dsc.ifTrackNewApi: info newApiTxt "backend()"
proc finish*(db: CoreDbRef; flush = false) =
## Database destructor. If the argument `flush` is set `false`, the database
## is left as-is and only the in-memory handlers are cleaned up.
##
## Otherwise the destructor is allowed to remove the database. This feature
## depends on the backend database. Currently, only the `AristoDbRocks` type
## backend removes the database on `true`.
db.methods.destroyFn flush
db.ifTrackNewApi: info newApiTxt "finish()"
proc `$$`*(e: CoreDbErrorRef): string =
## Pretty print error symbol, note that this directive may have side effects
## as it calls a backend function.
result = $e.error & "(" & e.parent.methods.errorPrintFn(e) & ")"
e.ifTrackNewApi: info newApiTxt "$$()", result
proc hash*(vid: CoreDbVidRef): Result[Hash256,void] =
## Getter (well, sort of), retrieves the hash for a `vid` argument. The
## function might fail if there is currently no hash available (e.g. on
## `Aristo`.) Note that this is different from succeeding with an
## `EMPTY_ROOT_HASH` value.
##
## The value `EMPTY_ROOT_HASH` is also returned on an empty `vid` argument
## `CoreDbVidRef(nil)`, say.
##
result = block:
if not vid.isNil and vid.ready:
vid.parent.methods.vidHashFn vid
else:
ok EMPTY_ROOT_HASH
# Note: tracker will be silent if `vid` is NIL
vid.ifTrackNewApi: info newApiTxt "hash()", result=result.toStr
proc hashOrEmpty*(vid: CoreDbVidRef): Hash256 =
## Convenience wrapper, returns `EMPTY_ROOT_HASH` where `hash()` would fail.
vid.hash.valueOr: EMPTY_ROOT_HASH
proc recast*(account: CoreDbAccount): Result[Account,void] =
## Convert the argument `account` to the portable Ethereum representation
## of an account. This conversion may fail if the storage root hash (see
## `hash()` above) is currently unavailable.
##
## Note that for the legacy backend, this function always succeeds.
##
let vid = account.storageVid
result = block:
let rc = vid.hash
if rc.isOk:
ok Account(
nonce: account.nonce,
balance: account.balance,
codeHash: account.codeHash,
storageRoot: rc.value)
else:
err()
vid.ifTrackNewApi: info newApiTxt "recast()", result=result.toStr
proc getRoot*(
db: CoreDbRef;
root: Hash256;
createOk = false;
): CoreDbRc[CoreDbVidRef] =
## Find root node with argument hash `root` in database and return the
## corresponding `CoreDbVidRef` object. If the `root` arguent is set
## `EMPTY_CODE_HASH`, this function always succeeds, otherwise it fails
## unless a root node with the corresponding hash exists.
##
## This function is intended to open a virtual accounts trie database as in:
## ::
## proc openAccountLedger(db: CoreDbRef, rootHash: Hash256): CoreDxMptRef =
## let root = db.getRoot(rootHash).valueOr:
## # some error handling
## return
## db.newAccMpt root
##
result = db.methods.getRootFn(root, createOk)
db.ifTrackNewApi:
info newApiTxt "getRoot()", root=root.toStr, result=result.toStr
# ------------------------------------------------------------------------------
# Public key-value table methods
# ------------------------------------------------------------------------------
proc newKvt*(db: CoreDbRef): CoreDxKvtRef =
## Getter (pseudo constructor)
result = db.methods.newKvtFn()
db.ifTrackNewApi: info newApiTxt "newKvt()"
proc get*(kvt: CoreDxKvtRef; key: openArray[byte]): CoreDbRc[Blob] =
## This function always returns a non-empty `Blob` or an error code.
result = kvt.methods.getFn key
kvt.ifTrackNewApi:
info newApiTxt "kvt/get()", key=key.toStr, result=result.toStr
proc getOrEmpty*(kvt: CoreDxKvtRef; key: openArray[byte]): CoreDbRc[Blob] =
## This function sort of mimics the behaviour of the legacy database
## returning an empty `Blob` if the argument `key` is not found on the
## database.
result = kvt.methods.getFn key
if result.isErr and result.error.error == KvtNotFound:
result = CoreDbRc[Blob].ok(EmptyBlob)
kvt.ifTrackNewApi:
info newApiTxt "kvt/getOrEmpty()", key=key.toStr, result=result.toStr
proc del*(kvt: CoreDxKvtRef; key: openArray[byte]): CoreDbRc[void] =
result = kvt.methods.delFn key
kvt.ifTrackNewApi:
info newApiTxt "kvt/del()", key=key.toStr, result=result.toStr
proc put*(
kvt: CoreDxKvtRef;
key: openArray[byte];
val: openArray[byte];
): CoreDbRc[void] =
result = kvt.methods.putFn(key, val)
kvt.ifTrackNewApi: info newApiTxt "kvt/put()",
key=key.toStr, val=val.toSeq.toStr, result=result.toStr
proc hasKey*(kvt: CoreDxKvtRef; key: openArray[byte]): CoreDbRc[bool] =
## Would be named `contains` if it returned `bool` rather than `Result[]`.
result = kvt.methods.hasKeyFn key
kvt.ifTrackNewApi:
info newApiTxt "kvt/hasKey()", key=key.toStr, result=result.toStr
iterator pairs*(kvt: CoreDxKvtRef): (Blob, Blob) {.apiRaise.} =
## Iterator supported on memory DB (otherwise implementation dependent)
for k,v in kvt.methods.pairsIt():
yield (k,v)
kvt.ifTrackNewApi: info newApiTxt "kvt/pairs()"
# ------------------------------------------------------------------------------
# Public Merkle Patricia Tree, hexary trie constructors
# ------------------------------------------------------------------------------
proc newMpt*(db: CoreDbRef; root: CoreDbVidRef; prune = true): CoreDxMptRef =
## Constructor, will defect on failure (note that the legacy backend
## always succeeds)
result = db.methods.newMptFn(root, prune).valueOr:
raiseAssert $$error
db.ifTrackNewApi: info newApiTxt "newMpt", root=root.toStr, prune
proc newMpt*(db: CoreDbRef; prune = true): CoreDxMptRef =
## Shortcut for `db.newMpt CoreDbVidRef()`
result = db.methods.newMptFn(CoreDbVidRef(), prune).valueOr:
raiseAssert $$error
db.ifTrackNewApi: info newApiTxt "newMpt", prune
proc newAccMpt*(db: CoreDbRef; root: CoreDbVidRef; prune = true): CoreDxAccRef =
## Similar to `newMpt()` for handling accounts. Although this sub-trie can
## be emulated by means of `newMpt(..).toPhk()`, it is recommended using
## this constructor which implies its own subset of methods to handle that
## trie.
result = db.methods.newAccFn(root, prune).valueOr: raiseAssert $$error
db.ifTrackNewApi: info newApiTxt "newAccMpt", root=root.toStr, prune
proc toMpt*(phk: CoreDxPhkRef): CoreDxMptRef =
## Replaces the pre-hashed argument trie `phk` by the non pre-hashed *MPT*.
## Note that this does not apply to an accounts trie that was created by
## `newAccMpt()`.
result = phk.fromMpt
phk.ifTrackNewApi: info newApiTxt "phk/toMpt()"
proc toPhk*(mpt: CoreDxMptRef): CoreDxPhkRef =
## Replaces argument `mpt` by a pre-hashed *MPT*.
## Note that this does not apply to an accounts trie that was created by
## `newAaccMpt()`.
result = mpt.toCoreDxPhkRef
mpt.ifTrackNewApi: info newApiTxt "mpt/toPhk()"
# ------------------------------------------------------------------------------
# Public common methods for all hexary trie databases (`mpt`, `phk`, or `acc`)
# ------------------------------------------------------------------------------
proc isPruning*(dsc: CoreDxTrieRefs | CoreDxAccRef): bool =
## Getter
result = dsc.methods.isPruningFn()
dsc.ifTrackNewApi: info newApiTxt "isPruning()", result
proc rootVid*(dsc: CoreDxTrieRefs | CoreDxAccRef): CoreDbVidRef =
## Getter, result is not `nil`
result = dsc.methods.rootVidFn()
dsc.ifTrackNewApi: info newApiTxt "rootVid()", result=result.toStr
# ------------------------------------------------------------------------------
# Public generic hexary trie database methods (`mpt` or `phk`)
# ------------------------------------------------------------------------------
proc fetch*(trie: CoreDxTrieRefs; key: openArray[byte]): CoreDbRc[Blob] =
## Fetch data from the argument `trie`. The function always returns a
## non-empty `Blob` or an error code.
result = trie.methods.fetchFn(key)
trie.ifTrackNewApi:
info newApiTxt "trie/fetch()", key=key.toStr, result=result.toStr
proc fetchOrEmpty*(trie: CoreDxTrieRefs; key: openArray[byte]): CoreDbRc[Blob] =
## This function returns an empty `Blob` if the argument `key` is not found
## on the database.
result = trie.methods.fetchFn(key)
if result.isErr and result.error.error == MptNotFound:
result = ok(EmptyBlob)
trie.ifTrackNewApi:
info newApiTxt "trie/fetch()", key=key.toStr, result=result.toStr
proc delete*(trie: CoreDxTrieRefs; key: openArray[byte]): CoreDbRc[void] =
result = trie.methods.deleteFn key
trie.ifTrackNewApi:
info newApiTxt "trie/delete()", key=key.toStr, result=result.toStr
proc merge*(
trie: CoreDxTrieRefs;
key: openArray[byte];
val: openArray[byte];
): CoreDbRc[void] =
when trie is CoreDxMptRef:
const info = "mpt/merge()"
else:
const info = "phk/merge()"
result = trie.methods.mergeFn(key, val)
trie.ifTrackNewApi: info newApiTxt info,
key=key.toStr, val=val.toSeq.toStr, result=result.toStr
proc hasPath*(trie: CoreDxTrieRefs; key: openArray[byte]): CoreDbRc[bool] =
## Would be named `contains` if it returned `bool` rather than `Result[]`.
result = trie.methods.hasPathFn key
trie.ifTrackNewApi:
info newApiTxt "trie/hasKey()", key=key.toStr, result=result.toStr
iterator pairs*(mpt: CoreDxMptRef): (Blob, Blob) {.apiRaise.} =
## Trie traversal, only supported for `CoreDxMptRef`
for k,v in mpt.methods.pairsIt():
yield (k,v)
mpt.ifTrackNewApi: info newApiTxt "mpt/pairs()"
iterator replicate*(mpt: CoreDxMptRef): (Blob, Blob) {.apiRaise.} =
## Low level trie dump, only supported for `CoreDxMptRef`
for k,v in mpt.methods.replicateIt():
yield (k,v)
mpt.ifTrackNewApi: info newApiTxt "mpt/replicate()"
# ------------------------------------------------------------------------------
# Public trie database methods for accounts
# ------------------------------------------------------------------------------
proc fetch*(acc: CoreDxAccRef; address: EthAddress): CoreDbRc[CoreDbAccount] =
## Fetch data from the argument `trie`.
result = acc.methods.fetchFn address
acc.ifTrackNewApi:
info newApiTxt "acc/fetch()", address=address.toStr, result=result.toStr
proc delete*(acc: CoreDxAccRef; address: EthAddress): CoreDbRc[void] =
result = acc.methods.deleteFn address
acc.ifTrackNewApi:
info newApiTxt "acc/delete()", address=address.toStr, result=result.toStr
proc merge*(
acc: CoreDxAccRef;
address: EthAddress;
account: CoreDbAccount;
): CoreDbRc[void] =
result = acc.methods.mergeFn(address, account)
acc.ifTrackNewApi:
info newApiTxt "acc/merge()", address=address.toStr, result=result.toStr
proc hasPath*(acc: CoreDxAccRef; address: EthAddress): CoreDbRc[bool] =
## Would be named `contains` if it returned `bool` rather than `Result[]`.
result = acc.methods.hasPathFn address
acc.ifTrackNewApi:
info newApiTxt "acc/hasKey()", address=address.toStr, result=result.toStr
# ------------------------------------------------------------------------------
# Public transaction related methods
# ------------------------------------------------------------------------------
proc toTransactionID*(db: CoreDbRef): CoreDbRc[CoreDxTxID] =
## Getter, current transaction state
result = db.methods.getIdFn()
db.ifTrackNewApi: info newApiTxt "toTransactionID()", result=result.toStr
proc shortTimeReadOnly*(
id: CoreDxTxID;
action: proc() {.noRaise.};
): CoreDbRc[void] =
## Run `action()` in an earlier transaction environment.
result = id.methods.roWrapperFn action
id.ifTrackNewApi: info newApiTxt "shortTimeReadOnly()", result=result.toStr
proc newTransaction*(db: CoreDbRef): CoreDbRc[CoreDxTxRef] =
## Constructor
result = db.methods.beginFn()
db.ifTrackNewApi: info newApiTxt "newTransaction()", result=result.toStr
proc commit*(tx: CoreDxTxRef, applyDeletes = true): CoreDbRc[void] =
result = tx.methods.commitFn applyDeletes
tx.ifTrackNewApi: info newApiTxt "tx/commit()", result=result.toStr
proc rollback*(tx: CoreDxTxRef): CoreDbRc[void] =
result = tx.methods.rollbackFn()
tx.ifTrackNewApi: info newApiTxt "tx/rollback()", result=result.toStr
proc dispose*(tx: CoreDxTxRef): CoreDbRc[void] =
result = tx.methods.disposeFn()
tx.ifTrackNewApi: info newApiTxt "tx/dispose()", result=result.toStr
proc safeDispose*(tx: CoreDxTxRef): CoreDbRc[void] =
result = tx.methods.safeDisposeFn()
tx.ifTrackNewApi: info newApiTxt "tx/safeDispose()", result=result.toStr
# ------------------------------------------------------------------------------
# Public tracer methods
# ------------------------------------------------------------------------------
proc newCapture*(
db: CoreDbRef;
flags: set[CoreDbCaptFlags] = {};
): CoreDbRc[CoreDxCaptRef] =
## Constructor
result = db.methods.captureFn flags
db.ifTrackNewApi: info newApiTxt "db/capture()", result=result.toStr
proc recorder*(cp: CoreDxCaptRef): CoreDbRc[CoreDbRef] =
## Getter
result = cp.methods.recorderFn()
cp.ifTrackNewApi: info newApiTxt "capt/recorder()", result=result.toStr
proc logDb*(cp: CoreDxCaptRef): CoreDbRc[CoreDbRef] =
result = cp.methods.logDbFn()
cp.ifTrackNewApi: info newApiTxt "capt/logDb()", result=result.toStr
proc flags*(cp: CoreDxCaptRef): set[CoreDbCaptFlags] =
## Getter
result = cp.methods.getFlagsFn()
cp.ifTrackNewApi: info newApiTxt "capt/flags()", result=result.toStr
# ------------------------------------------------------------------------------
# Public methods, legacy API
# ------------------------------------------------------------------------------
when ProvideCoreDbLegacyAPI:
proc parent*(cld: CoreDbChldRefs): CoreDbRef =
## Getter, common method for all sub-modules
result = cld.distinctBase.parent
proc backend*(dsc: CoreDbChldRefs): auto =
dsc.setTrackLegaApiOnly
result = dsc.distinctBase.backend
dsc.ifTrackLegaApi: info legaApiTxt "parent()"
# ----------------
proc kvt*(db: CoreDbRef): CoreDbKvtRef =
## Legacy pseudo constructor, see `toKvt()` for production constructor
db.setTrackLegaApiOnly
result = db.newKvt().CoreDbKvtRef
db.ifTrackLegaApi: info legaApiTxt "kvt()"
proc get*(kvt: CoreDbKvtRef; key: openArray[byte]): Blob =
kvt.setTrackLegaApiOnly
const info = "kvt/get()"
result = kvt.distinctBase.getOrEmpty(key).expect info
kvt.ifTrackLegaApi:
info legaApiTxt info, key=key.toStr, result=result.toStr
proc del*(kvt: CoreDbKvtRef; key: openArray[byte]): void =
kvt.setTrackLegaApiOnly
const info = "kvt/del()"
kvt.distinctBase.del(key).expect info
kvt.ifTrackLegaApi: info legaApiTxt info, key=key.toStr
proc put*(kvt: CoreDbKvtRef; key: openArray[byte]; val: openArray[byte]) =
kvt.setTrackLegaApiOnly
const info = "kvt/put()"
kvt.distinctBase.put(key, val).expect info
kvt.ifTrackLegaApi:
info legaApiTxt info, key=key.toStr, val=val.toSeq.toStr
proc contains*(kvt: CoreDbKvtRef; key: openArray[byte]): bool =
kvt.setTrackLegaApiOnly
const info = "kvt/contains()"
result = kvt.distinctBase.hasKey(key).expect info
kvt.ifTrackLegaApi: info legaApiTxt info, key=key.toStr, result
iterator pairs*(kvt: CoreDbKvtRef): (Blob, Blob) {.apiRaise.} =
kvt.setTrackLegaApiOnly
for k,v in kvt.distinctBase.pairs():
yield (k,v)
kvt.ifTrackLegaApi: info legaApiTxt "kvt/pairs()"
# ----------------
proc toMpt*(phk: CoreDbPhkRef): CoreDbMptRef =
phk.setTrackLegaApiOnly
result = phk.distinctBase.toMpt.CoreDbMptRef
phk.ifTrackLegaApi: info legaApiTxt "phk/toMpt()"
proc mptPrune*(db: CoreDbRef; root: Hash256; prune = true): CoreDbMptRef =
db.setTrackLegaApiOnly
const info = "mptPrune()"
let vid = db.getRoot(root, createOk=true).expect info
result = db.newMpt(vid, prune).CoreDbMptRef
db.ifTrackLegaApi: info legaApiTxt info, root=root.toStr, prune
proc mptPrune*(db: CoreDbRef; prune = true): CoreDbMptRef =
db.newMpt(CoreDbVidRef(nil), prune).CoreDbMptRef
# ----------------
proc toPhk*(mpt: CoreDbMptRef): CoreDbPhkRef =
mpt.setTrackLegaApiOnly
result = mpt.distinctBase.toPhk.CoreDbPhkRef
mpt.ifTrackLegaApi: info legaApiTxt "mpt/toMpt()"
proc phkPrune*(db: CoreDbRef; root: Hash256; prune = true): CoreDbPhkRef =
db.setTrackLegaApiOnly
const info = "phkPrune()"
let vid = db.getRoot(root, createOk=true).expect info
result = db.newMpt(vid, prune).toCoreDxPhkRef.CoreDbPhkRef
db.ifTrackLegaApi: info legaApiTxt info, root=root.toStr, prune
proc phkPrune*(db: CoreDbRef; prune = true): CoreDbPhkRef =
db.newMpt(CoreDbVidRef(nil), prune).toCoreDxPhkRef.CoreDbPhkRef
# ----------------
proc isPruning*(trie: CoreDbTrieRefs): bool =
trie.setTrackLegaApiOnly
result = trie.distinctBase.isPruning()
trie.ifTrackLegaApi: info legaApiTxt "trie/isPruning()", result
proc get*(trie: CoreDbTrieRefs; key: openArray[byte]): Blob =
trie.setTrackLegaApiOnly
const info = "trie/get()"
result = trie.distinctBase.fetchOrEmpty(key).expect "trie/get()"
trie.ifTrackLegaApi:
info legaApiTxt info, key=key.toStr, result=result.toStr
proc del*(trie: CoreDbTrieRefs; key: openArray[byte]) =
trie.setTrackLegaApiOnly
const info = "trie/del()"
trie.distinctBase.delete(key).expect info
trie.ifTrackLegaApi: info legaApiTxt info, key=key.toStr
proc put*(trie: CoreDbTrieRefs; key: openArray[byte]; val: openArray[byte]) =
trie.setTrackLegaApiOnly
when trie is CoreDbMptRef:
const info = "mpt/put()"
else:
const info = "phk/put()"
trie.distinctBase.merge(key, val).expect info
trie.ifTrackLegaApi:
info legaApiTxt info, key=key.toStr, val=val.toSeq.toStr
proc contains*(trie: CoreDbTrieRefs; key: openArray[byte]): bool =
trie.setTrackLegaApiOnly
const info = "trie/contains()"
result = trie.distinctBase.hasPath(key).expect info
trie.ifTrackLegaApi: info legaApiTxt info, key=key.toStr, result
proc rootHash*(trie: CoreDbTrieRefs): Hash256 =
trie.setTrackLegaApiOnly
const info = "trie/rootHash()"
result = trie.distinctBase.rootVid().hash().expect info
trie.ifTrackLegaApi: info legaApiTxt info, result=result.toStr
iterator pairs*(mpt: CoreDbMptRef): (Blob, Blob) {.apiRaise.} =
## Trie traversal, not supported for `CoreDbPhkRef`
mpt.setTrackLegaApiOnly
for k,v in mpt.distinctBase.pairs():
yield (k,v)
mpt.ifTrackLegaApi: info legaApiTxt "mpt/pairs()"
iterator replicate*(mpt: CoreDbMptRef): (Blob, Blob) {.apiRaise.} =
## Low level trie dump, not supported for `CoreDbPhkRef`
mpt.setTrackLegaApiOnly
for k,v in mpt.distinctBase.replicate():
yield (k,v)
mpt.ifTrackLegaApi: info legaApiTxt "mpt/replicate()"
# ----------------
proc getTransactionID*(db: CoreDbRef): CoreDbTxID =
db.setTrackLegaApiOnly
const info = "getTransactionID()"
result = (db.toTransactionID().expect info).CoreDbTxID
db.ifTrackLegaApi: info legaApiTxt info
proc shortTimeReadOnly*(
id: CoreDbTxID;
action: proc() {.catchRaise.};
) {.catchRaise.} =
id.setTrackLegaApiOnly
const info = "txId/shortTimeReadOnly()"
var oops = none(ref CatchableError)
proc safeFn() =
try:
action()
except CatchableError as e:
oops = some(e)
# Action has finished now
id.distinctBase.shortTimeReadOnly(safeFn).expect info
# Delayed exception
if oops.isSome:
let
e = oops.unsafeGet
msg = "delayed and reraised" &
", name=\"" & $e.name & "\", msg=\"" & e.msg & "\""
raise (ref TxWrapperApiError)(msg: msg)
id.ifTrackLegaApi: info legaApiTxt info
proc beginTransaction*(db: CoreDbRef): CoreDbTxRef =
db.setTrackLegaApiOnly
const info = "newTransaction()"
result = (db.distinctBase.newTransaction().expect info).CoreDbTxRef
db.ifTrackLegaApi: info legaApiTxt info
proc commit*(tx: CoreDbTxRef, applyDeletes = true) =
tx.setTrackLegaApiOnly
const info = "tx/commit()"
tx.distinctBase.commit(applyDeletes).expect info
tx.ifTrackLegaApi: info legaApiTxt info
proc rollback*(tx: CoreDbTxRef) =
tx.setTrackLegaApiOnly
const info = "tx/rollback()"
tx.distinctBase.rollback().expect info
tx.ifTrackLegaApi: info legaApiTxt info
proc dispose*(tx: CoreDbTxRef) =
tx.setTrackLegaApiOnly
const info = "tx/dispose()"
tx.distinctBase.dispose().expect info
tx.ifTrackLegaApi: info legaApiTxt info
proc safeDispose*(tx: CoreDbTxRef) =
tx.setTrackLegaApiOnly
const info = "tx/safeDispose()"
tx.distinctBase.safeDispose().expect info
tx.ifTrackLegaApi: info legaApiTxt info
# ----------------
proc capture*(
db: CoreDbRef;
flags: set[CoreDbCaptFlags] = {};
): CoreDbCaptRef =
db.setTrackLegaApiOnly
const info = "db/capture()"
result = db.newCapture(flags).expect(info).CoreDbCaptRef
db.ifTrackLegaApi: info legaApiTxt info
proc recorder*(cp: CoreDbCaptRef): CoreDbRef =
cp.setTrackLegaApiOnly
const info = "capt/recorder()"
result = cp.distinctBase.recorder().expect info
cp.ifTrackLegaApi: info legaApiTxt info
proc logDb*(cp: CoreDbCaptRef): CoreDbRef =
cp.setTrackLegaApiOnly
const info = "capt/logDb()"
result = cp.distinctBase.logDb().expect info
cp.ifTrackLegaApi: info legaApiTxt info
proc flags*(cp: CoreDbCaptRef): set[CoreDbCaptFlags] =
cp.setTrackLegaApiOnly
result = cp.distinctBase.flags()
cp.ifTrackLegaApi: info legaApiTxt "capt/flags()", result=result.toStr
# ------------------------------------------------------------------------------
# End
# ------------------------------------------------------------------------------