# Nimbus # 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/typetraits, chronicles, eth/common, results, "../.."/[constants, errors], ./base/[api_new_desc, api_tracking, base_desc] from ../aristo import EmptyBlob, isValid const ProvideLegacyAPI = true ## Enable legacy API. For now everybody would want this enabled. EnableApiTracking = false ## When enabled, functions using this tracking facility need to import ## `chronicles`, as well. Tracking is enabled by setting `true` the flags ## `trackLegaApi` and/or `trackNewApi` in the `CoreDxTxRef` descriptor. EnableApiProfiling = true ## Enables functions profiling if `EnableApiTracking` is also set `true`. AutoValidateDescriptors = defined(release).not ## No validatinon needed for production suite. export CoreDbAccBackendRef, CoreDbAccount, CoreDbApiError, CoreDbBackendRef, CoreDbCaptFlags, CoreDbErrorCode, CoreDbErrorRef, CoreDbFnInx, CoreDbKvtBackendRef, CoreDbMptBackendRef, CoreDbPersistentTypes, CoreDbRef, CoreDbSaveFlags, CoreDbType, CoreDbVidRef, CoreDxAccRef, CoreDxCaptRef, CoreDxKvtRef, CoreDxMptRef, CoreDxPhkRef, CoreDxTxRef, # Profiling support byElapsed, byMean, byVisits, stats const CoreDbProvideLegacyAPI* = ProvideLegacyAPI CoreDbEnableApiTracking* = EnableApiTracking CoreDbEnableApiProfiling* = EnableApiTracking and EnableApiProfiling when ProvideLegacyAPI: import ./base/api_legacy_desc export api_legacy_desc when AutoValidateDescriptors: import ./base/validate when EnableApiTracking and EnableApiProfiling: var coreDbProfTab*: CoreDbProfFnInx # More settings const logTxt = "CoreDb " legaApiTxt = logTxt & "legacy API" newApiTxt = logTxt & "new API" # Annotation helpers {.pragma: apiRaise, gcsafe, raises: [CoreDbApiError].} {.pragma: catchRaise, gcsafe, raises: [CatchableError].} # ------------------------------------------------------------------------------ # Private helpers # ------------------------------------------------------------------------------ when EnableApiTracking: when EnableApiProfiling: {.warning: "*** Provided API profiling for CoreDB (disabled by default)".} else: {.warning: "*** Provided API logging for CoreDB (disabled by default)".} import std/[sequtils, times] proc `$`[T](rc: CoreDbRc[T]): string = rc.toStr proc `$`(q: set[CoreDbCaptFlags]): string = q.toStr proc `$`(t: Duration): string = t.toStr proc `$`(e: EthAddress): string = e.toStr proc `$`(v: CoreDbVidRef): string = v.toStr proc `$`(h: Hash256): string = h.toStr when ProvideLegacyAPI: when EnableApiTracking: proc `$`(k: CoreDbKvtRef): string = k.toStr template setTrackLegaApi( w: CoreDbApiTrackRef; s: static[CoreDbFnInx]; code: untyped; ) = ## Template with code section that will be discarded if logging is ## disabled at compile time when `EnableApiTracking` is `false`. when EnableApiTracking: w.beginLegaApi() code const ctx {.inject,used.} = s template setTrackLegaApi*( w: CoreDbApiTrackRef; s: static[CoreDbFnInx]; ) = w.setTrackLegaApi(s): discard template ifTrackLegaApi*(w: CoreDbApiTrackRef; code: untyped) = when EnableApiTracking: w.endLegaApiIf: when EnableApiProfiling: coreDbProfTab.update(ctx, elapsed) code template setTrackNewApi( w: CoreDxApiTrackRef; s: static[CoreDbFnInx]; code: untyped; ) = ## Template with code section that will be discarded if logging is ## disabled at compile time when `EnableApiTracking` is `false`. when EnableApiTracking: w.beginNewApi() code const ctx {.inject,used.} = s template setTrackNewApi*( w: CoreDxApiTrackRef; s: static[CoreDbFnInx]; ) = w.setTrackNewApi(s): discard template ifTrackNewApi*(w: CoreDxApiTrackRef; code: untyped) = when EnableApiTracking: w.endNewApiIf: when EnableApiProfiling: coreDbProfTab.update(ctx, elapsed) code # --------- 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) when AutoValidateDescriptors: result.validate func parent(phk: CoreDxPhkRef): CoreDbRef = phk.fromMpt.parent proc prettyText(e: CoreDbErrorRef): string = $e.error & "(" & e.parent.methods.errorPrintFn(e) & ")" proc prettyText(vid: CoreDbVidRef): string = if vid.isNil or not vid.ready: "$ΓΈ" else: vid.parent.methods.vidPrintFn(vid) # ------------------------------------------------------------------------------ # Public constructor helper # ------------------------------------------------------------------------------ proc bless*(db: CoreDbRef): CoreDbRef = ## Verify descriptor when AutoValidateDescriptors: db.validate 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` child.parent = db 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, print DB type identifier ## db.setTrackNewApi BaseDbTypeFn result = db.dbType db.ifTrackNewApi: debug newApiTxt, ctx, elapsed, 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.setTrackNewApi BaseLegacySetupFn db.methods.legacySetupFn() db.ifTrackNewApi: debug newApiTxt, ctx, elapsed proc level*(db: CoreDbRef): int = ## Getter, retrieve transaction level (zero if there is no pending ## transaction) ## db.setTrackNewApi BaseLevelFn result = db.methods.levelFn() db.ifTrackNewApi: debug newApiTxt, ctx, elapsed, result 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. ## dsc.setTrackNewApi AnyBackendFn result = dsc.methods.backendFn() dsc.ifTrackNewApi: debug newApiTxt, ctx, elapsed 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.setTrackNewApi BaseFinishFn db.methods.destroyFn flush db.ifTrackNewApi: debug newApiTxt, ctx, elapsed proc `$$`*(e: CoreDbErrorRef): string = ## Pretty print error symbol, note that this directive may have side effects ## as it calls a backend function. ## e.setTrackNewApi ErrorPrintFn result = e.prettyText() e.ifTrackNewApi: debug newApiTxt, ctx, elapsed, result proc `$$`*(vid: CoreDbVidRef): string = ## Pretty print vertex ID symbol, note that this directive may have side ## effects as it calls a backend function. ## vid.setTrackNewApi VidPrintFn result = vid.prettyText() vid.ifTrackNewApi: debug newApiTxt, ctx, elapsed, result proc hash*(vid: CoreDbVidRef): CoreDbRc[Hash256] = ## 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. ## vid.setTrackNewApi VidHashFn 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: debug newApiTxt, ctx, elapsed, vid, result proc hashOrEmpty*(vid: CoreDbVidRef): Hash256 = ## Convenience wrapper, returns `EMPTY_ROOT_HASH` where `hash()` would fail. vid.hash().valueOr: EMPTY_ROOT_HASH func isValid*(vid: CoreDbVidRef): bool = ## This function returns `true` if the argument `vid` exists and is properly ## initialised. ## not vid.isNil and vid.ready proc recast*(account: CoreDbAccount): CoreDbRc[Account] = ## 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 vid.setTrackNewApi EthAccRecastFn 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(rc.error) vid.ifTrackNewApi: debug newApiTxt, ctx, elapsed, result proc getRoot*( db: CoreDbRef; root: Hash256; ): 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 ## db.setTrackNewApi BaseGetRootFn result = db.methods.getRootFn root db.ifTrackNewApi: debug newApiTxt, ctx, elapsed, root, result # ------------------------------------------------------------------------------ # Public key-value table methods # ------------------------------------------------------------------------------ proc newKvt*(db: CoreDbRef; saveMode = AutoSave): CoreDxKvtRef = ## Constructor, will defect on failure. ## ## Depending on the argument `saveMode`, the contructed object will have ## the following properties. ## ## * `Shared` ## Subscribe to the common base object shared with other subscribed ## `AutoSave` or `Shared` descriptors. So any changes are immediately ## visible among subscribers. On automatic destruction (when the ## constructed object gets out of scope), changes are not saved to the ## backend database but are still available to subscribers. ## ## This mode would used for short time read-only database descriptors. ## ## * `AutoSave` ## This mode works similar to `Shared` with the difference that changes ## are saved to the backend database on automatic destruction when this ## is permissible, i.e. there is a backend available and there is no ## pending transaction on the common base object. ## ## * `TopShot` ## The contructed object will be a new descriptor with a separate snapshot ## of the common shared base object. If there are pending transactions ## on the shared base object, the snapsot will squash them to a single ## pending transaction. On automatic destruction, changes will be discarded. ## ## * `Companion` ## The contructed object will be a new separate descriptor with a clean ## cache (similar to `TopShot` with empty cache and no pending ## transactions.) On automatic destruction, changes will be discarded. ## ## The constructed object can be manually descructed (see `forget()`) without ## saving and can be forced to save (see `persistent()`.) ## ## The legacy backend always assumes `AutoSave` mode regardless of the ## function argument. ## db.setTrackNewApi BaseNewKvtFn result = db.methods.newKvtFn(saveMode).valueOr: raiseAssert error.prettyText() db.ifTrackNewApi: debug newApiTxt, ctx, elapsed, saveMode proc get*(kvt: CoreDxKvtRef; key: openArray[byte]): CoreDbRc[Blob] = ## This function always returns a non-empty `Blob` or an error code. kvt.setTrackNewApi KvtGetFn result = kvt.methods.getFn key kvt.ifTrackNewApi: debug newApiTxt, ctx, elapsed, key=key.toStr, result 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. ## kvt.setTrackNewApi KvtGetOrEmptyFn result = kvt.methods.getFn key if result.isErr and result.error.error == KvtNotFound: result = CoreDbRc[Blob].ok(EmptyBlob) kvt.ifTrackNewApi: debug newApiTxt, ctx, elapsed, key=key.toStr, result proc del*(kvt: CoreDxKvtRef; key: openArray[byte]): CoreDbRc[void] = kvt.setTrackNewApi KvtDelFn result = kvt.methods.delFn key kvt.ifTrackNewApi: debug newApiTxt, ctx, elapsed, key=key.toStr, result proc put*( kvt: CoreDxKvtRef; key: openArray[byte]; val: openArray[byte]; ): CoreDbRc[void] = kvt.setTrackNewApi KvtPutFn result = kvt.methods.putFn(key, val) kvt.ifTrackNewApi: debug newApiTxt, ctx, elapsed, key=key.toStr, val=val.toSeq.toStr, result proc hasKey*(kvt: CoreDxKvtRef; key: openArray[byte]): CoreDbRc[bool] = ## Would be named `contains` if it returned `bool` rather than `Result[]`. ## kvt.setTrackNewApi KvtHasKeyFn result = kvt.methods.hasKeyFn key kvt.ifTrackNewApi: debug newApiTxt, ctx, elapsed, key=key.toStr, result proc persistent*(dsc: CoreDxKvtRef): CoreDbRc[void] {.discardable.} = ## For the legacy database, this function has no effect and succeeds always. ## It will nevertheless return a discardable error if there is a pending ## transaction. ## ## This function saves the current cache to the database if possible, ## regardless of the save/share mode assigned to the constructor. ## ## Caveat: ## If `dsc` is a detached descriptor of `Companion` or `TopShot` mode which ## could be persistently saved, the changes are immediately visible on all ## other descriptors unless they are hidden by newer versions of key-value ## items in the cache. ## dsc.setTrackNewApi KvtPersistentFn result = dsc.methods.persistentFn() dsc.ifTrackNewApi: debug newApiTxt, ctx, elapsed, result proc forget*(dsc: CoreDxKvtRef): CoreDbRc[void] {.discardable.} = ## For the legacy database, this function has no effect and succeeds always. ## ## This function destroys the current descriptor without any further action ## regardless of the save/share mode assigned to the constructor. ## ## For desciptors constructed with modes `saveMode` or`Shared`, nothing will ## change on the current database if there are other descriptors referring ## to the same shared database view. Creating a new `saveMode` or`Shared` ## descriptor will retrieve this state. ## ## For other desciptors constructed as `Companion` ot `TopShot`, the latest ## changes (after the last `persistent()` call) will be discarded. ## dsc.setTrackNewApi KvtForgetFn result = dsc.methods.forgetFn() dsc.ifTrackNewApi: debug newApiTxt, ctx, elapsed, result # ------------------------------------------------------------------------------ # Public Merkle Patricia Tree, hexary trie constructors # ------------------------------------------------------------------------------ proc newMpt*( db: CoreDbRef; root: CoreDbVidRef; prune = true; saveMode = AutoSave; ): CoreDbRc[CoreDxMptRef] = ## Constructor, will defect on failure. The argument `prune` is currently ## ignored on other than the legacy backend. The legacy backend always ## assumes `AutoSave` mode regardless of the function argument. ## ## If the `root` argument is `nil`, a new sub-trie root will be created ## on-the-fly. In order to access that root so that it can be used at a ## later stage, the function `rootVid()` can be used after at least one ## data item was successfully merged (using `merge()`) to that sub-trie. ## ## See the discussion at `newKvt()` for an explanation of the `saveMode` ## argument. ## db.setTrackNewApi BaseNewMptFn result = db.methods.newMptFn(root, prune, saveMode) db.ifTrackNewApi: debug newApiTxt, ctx, elapsed, root, prune, saveMode, result proc newMpt*(db: CoreDbRef; prune=true; saveMode=AutoSave): CoreDxMptRef = ## Shortcut for `newMpt(CoreDbVidRef(nil), prune, saveMode)`. This function ## will always return a non-nil descriptor or throw an exception. ## db.setTrackNewApi BaseNewMptFn result = db.methods.newMptFn(CoreDbVidRef(nil), prune, saveMode).valueOr: raiseAssert error.prettyText() db.ifTrackNewApi: debug newApiTxt, ctx, elapsed, prune, saveMode proc newMpt*(acc: CoreDxAccRef): CoreDxMptRef = ## Constructor, will defect on failure. ## ## Variant of `newMpt()` where the input arguments are taken from the ## current `acc` descriptor settings. ## acc.setTrackNewApi AccToMptFn result = acc.methods.newMptFn().valueOr: raiseAssert error.prettyText() acc.ifTrackNewApi: debug newApiTxt, ctx, elapsed proc newAccMpt*( db: CoreDbRef; root: CoreDbVidRef; prune = true; saveMode = AutoSave; ): CoreDbRc[CoreDxAccRef] = ## Constructor, will defect on failure. The argument `prune` is currently ## ignored on other than the legacy backend. The legacy backend always ## assumes `AutoSave` mode regardless of the function argument. ## ## For a `saveMode` argument value `Shared`, the `root` descriptor argument ## might be pre-set (i.e. non-nil). Other arguments will be rejected by the ## `Aristo` backend and should be avoided for other backends. The pre-set ## `root` argument must refer to the state root for the accounts sub-trie ## which can be retrieved by `getRoot()`. ## ## Example: ## :: ## let vid = db.getRoot().valueOr: ## ... # No node with ## return ## ## let acc = db.newAccMpt(vid, saveMode=Shared) ## ... # Was not the state root for the accounts sub-trie ## return ## ## ## This function works similar to `newMpt()` for handling accounts. Although ## this sub-trie can be emulated by means of `newMpt(..).toPhk()`, it is ## recommended using this particular constructor for accounts because it ## provides its own subset of methods to handle accounts. ## ## See the discussion at `newKvt()` for an explanation of the `saveMode` ## argument. ## db.setTrackNewApi BaseNewAccFn result = db.methods.newAccFn(root, prune, saveMode) db.ifTrackNewApi: debug newApiTxt, ctx, elapsed, root, prune, saveMode, result proc newAccMpt*(db: CoreDbRef; prune=true; saveMode=AutoSave): CoreDxAccRef = ## Shortcut for `newAccMpt(CoreDbVidRef(nil), prune, saveMode)`. This function ## will always return a non-nil descriptor or throw an exception. ## db.setTrackNewApi BaseNewAccFn result = db.methods.newAccFn(CoreDbVidRef(nil), prune, saveMode).valueOr: raiseAssert error.prettyText() db.ifTrackNewApi: debug newApiTxt, ctx, elapsed, prune, saveMode 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()`. ## phk.setTrackNewApi PhkToMptFn result = phk.fromMpt phk.ifTrackNewApi: debug newApiTxt, ctx, elapsed 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()`. ## mpt.setTrackNewApi MptToPhkFn result = mpt.toCoreDxPhkRef mpt.ifTrackNewApi: debug newApiTxt, ctx, elapsed # ------------------------------------------------------------------------------ # Public common methods for all hexary trie databases (`mpt`, `phk`, or `acc`) # ------------------------------------------------------------------------------ proc isPruning*(dsc: CoreDxTrieRefs): bool = ## Getter ## dsc.setTrackNewApi AnyIsPruningFn result = dsc.methods.isPruningFn() dsc.ifTrackNewApi: debug newApiTxt, ctx, elapsed, result proc rootVid*(acc: CoreDxAccRef): CoreDbVidRef = ## Getter, result is not `nil` ## acc.setTrackNewApi AccRootVidFn result = acc.methods.rootVidFn() acc.ifTrackNewApi: debug newApiTxt, ctx, elapsed, result proc rootVid*(mpt: CoreDxMptRef): CoreDbVidRef = ## Variant of `rootVid()` mpt.setTrackNewApi MptRootVidFn result = mpt.methods.rootVidFn() mpt.ifTrackNewApi: debug newApiTxt, ctx, elapsed, result proc rootVid*(phk: CoreDxPhkRef): CoreDbVidRef = ## Variant of `rootVid()` phk.setTrackNewApi PhkRootVidFn result = phk.methods.rootVidFn() phk.ifTrackNewApi: debug newApiTxt, ctx, elapsed, result proc persistent*(acc: CoreDxAccRef): CoreDbRc[void] {.discardable.} = ## For the legacy database, this function has no effect and succeeds always. ## It will nevertheless return a discardable error if there is a pending ## transaction. ## ## This function saves the current cache to the database if possible, ## regardless of the save/share mode assigned to the constructor. ## ## Caveat: ## If `dsc` is a detached descriptor of `Companion` or `TopShot` mode which ## could be persistently saved, no changes are visible on other descriptors. ## This is different from the behaviour of a `Kvt` descriptor. Saving any ## other descriptor will undo the changes. ## acc.setTrackNewApi AccPersistentFn result = acc.methods.persistentFn() acc.ifTrackNewApi: debug newApiTxt, ctx, elapsed, result proc persistent*(mpt: CoreDxMptRef): CoreDbRc[void] {.discardable.} = ## Variant of `persistent()` mpt.setTrackNewApi MptPersistentFn result = mpt.methods.persistentFn() mpt.ifTrackNewApi: debug newApiTxt, ctx, elapsed, result proc persistent*(phk: CoreDxPhkRef): CoreDbRc[void] {.discardable.} = ## Variant of `persistent()` phk.setTrackNewApi PhkPersistentFn result = phk.methods.persistentFn() phk.ifTrackNewApi: debug newApiTxt, ctx, elapsed, result proc forget*(acc: CoreDxAccRef): CoreDbRc[void] {.discardable.} = ## For the legacy database, this function has no effect and succeeds always. ## ## This function destroys the current descriptor without any further action ## regardless of the save/share mode assigned to the constructor. ## ## See the discussion at `forget()` for a `CoreDxKvtRef` type argument ## descriptor an explanation of how this function works. ## acc.setTrackNewApi AccForgetFn result = acc.methods.forgetFn() acc.ifTrackNewApi: debug newApiTxt, ctx, elapsed, result proc forget*(mpt: CoreDxMptRef): CoreDbRc[void] {.discardable.} = ## Variant of `forget()` mpt.setTrackNewApi MptForgetFn result = mpt.methods.forgetFn() mpt.ifTrackNewApi: debug newApiTxt, ctx, elapsed, result proc forget*(phk: CoreDxPhkRef): CoreDbRc[void] {.discardable.} = ## Variant of `forget()` phk.setTrackNewApi PhkForgetFn result = phk.methods.forgetFn() phk.ifTrackNewApi: debug newApiTxt, ctx, elapsed, result # ------------------------------------------------------------------------------ # Public generic hexary trie database methods (`mpt` or `phk`) # ------------------------------------------------------------------------------ proc fetch*(mpt: CoreDxMptRef; key: openArray[byte]): CoreDbRc[Blob] = ## Fetch data from the argument `trie`. The function always returns a ## non-empty `Blob` or an error code. ## mpt.setTrackNewApi MptFetchFn result = mpt.methods.fetchFn key mpt.ifTrackNewApi: debug newApiTxt, ctx, elapsed, key=key.toStr, result proc fetch*(phk: CoreDxPhkRef; key: openArray[byte]): CoreDbRc[Blob] = ## Variant of `fetch()" phk.setTrackNewApi PhkFetchFn result = phk.methods.fetchFn key phk.ifTrackNewApi: debug newApiTxt, ctx, elapsed, key=key.toStr, result proc fetchOrEmpty*(mpt: CoreDxMptRef; key: openArray[byte]): CoreDbRc[Blob] = ## This function returns an empty `Blob` if the argument `key` is not found ## on the database. ## mpt.setTrackNewApi MptFetchOrEmptyFn result = mpt.methods.fetchFn key if result.isErr and result.error.error == MptNotFound: result = CoreDbRc[Blob].ok(EmptyBlob) mpt.ifTrackNewApi: debug newApiTxt, ctx, elapsed, key=key.toStr, result proc fetchOrEmpty*(phk: CoreDxPhkRef; key: openArray[byte]): CoreDbRc[Blob] = ## Variant of `fetchOrEmpty()` phk.setTrackNewApi PhkFetchOrEmptyFn result = phk.methods.fetchFn key if result.isErr and result.error.error == MptNotFound: result = CoreDbRc[Blob].ok(EmptyBlob) phk.ifTrackNewApi: debug newApiTxt, ctx, elapsed, key=key.toStr, result proc delete*(mpt: CoreDxMptRef; key: openArray[byte]): CoreDbRc[void] = mpt.setTrackNewApi MptDeleteFn result = mpt.methods.deleteFn key mpt.ifTrackNewApi: debug newApiTxt, ctx, elapsed, key=key.toStr, result proc delete*(phk: CoreDxPhkRef; key: openArray[byte]): CoreDbRc[void] = phk.setTrackNewApi PhkDeleteFn result = phk.methods.deleteFn key phk.ifTrackNewApi: debug newApiTxt, ctx, elapsed, key=key.toStr, result proc merge*( mpt: CoreDxMptRef; key: openArray[byte]; val: openArray[byte]; ): CoreDbRc[void] = mpt.setTrackNewApi MptMergeFn result = mpt.methods.mergeFn(key, val) mpt.ifTrackNewApi: debug newApiTxt, ctx, elapsed, key=key.toStr, val=val.toSeq.toStr, result proc merge*( phk: CoreDxPhkRef; key: openArray[byte]; val: openArray[byte]; ): CoreDbRc[void] = phk.setTrackNewApi PhkMergeFn result = phk.methods.mergeFn(key, val) phk.ifTrackNewApi: debug newApiTxt, ctx, elapsed, key=key.toStr, val=val.toSeq.toStr, result proc hasPath*(mpt: CoreDxMptRef; key: openArray[byte]): CoreDbRc[bool] = ## This function would be named `contains()` if it returned `bool` rather ## than a `Result[]`. ## mpt.setTrackNewApi MptHasPathFn result = mpt.methods.hasPathFn key mpt.ifTrackNewApi: debug newApiTxt, ctx, elapsed, key=key.toStr, result proc hasPath*(phk: CoreDxPhkRef; key: openArray[byte]): CoreDbRc[bool] = ## Variant of `hasPath()` phk.setTrackNewApi PhkHasPathFn result = phk.methods.hasPathFn key phk.ifTrackNewApi: debug newApiTxt, ctx, elapsed, key=key.toStr, result # ------------------------------------------------------------------------------ # Public trie database methods for accounts # ------------------------------------------------------------------------------ proc fetch*(acc: CoreDxAccRef; address: EthAddress): CoreDbRc[CoreDbAccount] = ## Fetch data from the argument `acc`. ## acc.setTrackNewApi AccFetchFn result = acc.methods.fetchFn address acc.ifTrackNewApi: debug newApiTxt, ctx, elapsed, address, result proc delete*(acc: CoreDxAccRef; address: EthAddress): CoreDbRc[void] = acc.setTrackNewApi AccDeleteFn result = acc.methods.deleteFn address acc.ifTrackNewApi: debug newApiTxt, ctx, elapsed, address, result proc merge*( acc: CoreDxAccRef; address: EthAddress; account: CoreDbAccount; ): CoreDbRc[void] = acc.setTrackNewApi AccMergeFn result = acc.methods.mergeFn(address, account) acc.ifTrackNewApi: debug newApiTxt, ctx, elapsed, address, result proc hasPath*(acc: CoreDxAccRef; address: EthAddress): CoreDbRc[bool] = ## Would be named `contains` if it returned `bool` rather than `Result[]`. ## acc.setTrackNewApi AccHasPathFn result = acc.methods.hasPathFn address acc.ifTrackNewApi: debug newApiTxt, ctx, elapsed, address, result # ------------------------------------------------------------------------------ # Public transaction related methods # ------------------------------------------------------------------------------ proc newTransaction*(db: CoreDbRef): CoreDbRc[CoreDxTxRef] = ## Constructor ## db.setTrackNewApi BaseNewTxFn result = db.methods.beginFn() db.ifTrackNewApi: debug newApiTxt, ctx, elapsed, newLevel=db.methods.levelFn(), result proc level*(tx: CoreDxTxRef): int = ## Print positive argument `tx` transaction level ## tx.setTrackNewApi TxLevelFn result = tx.methods.levelFn() tx.ifTrackNewApi: debug newApiTxt, ctx, elapsed, result proc commit*(tx: CoreDxTxRef, applyDeletes = true): CoreDbRc[void] = tx.setTrackNewApi TxCommitFn: let prvLevel {.used.} = tx.methods.levelFn() result = tx.methods.commitFn applyDeletes tx.ifTrackNewApi: debug newApiTxt, ctx, elapsed, prvLevel, result proc rollback*(tx: CoreDxTxRef): CoreDbRc[void] = tx.setTrackNewApi TxRollbackFn: let prvLevel {.used.} = tx.methods.levelFn() result = tx.methods.rollbackFn() tx.ifTrackNewApi: debug newApiTxt, ctx, elapsed, prvLevel, result proc dispose*(tx: CoreDxTxRef): CoreDbRc[void] = tx.setTrackNewApi TxDisposeFn: let prvLevel {.used.} = tx.methods.levelFn() result = tx.methods.disposeFn() tx.ifTrackNewApi: debug newApiTxt, ctx, elapsed, prvLevel, result proc safeDispose*(tx: CoreDxTxRef): CoreDbRc[void] = tx.setTrackNewApi TxSaveDisposeFn: let prvLevel {.used.} = tx.methods.levelFn() result = tx.methods.safeDisposeFn() tx.ifTrackNewApi: debug newApiTxt, ctx, elapsed, prvLevel, result # ------------------------------------------------------------------------------ # Public tracer methods # ------------------------------------------------------------------------------ proc newCapture*( db: CoreDbRef; flags: set[CoreDbCaptFlags] = {}; ): CoreDbRc[CoreDxCaptRef] = ## Constructor db.setTrackNewApi BaseCaptureFn result = db.methods.captureFn flags db.ifTrackNewApi: debug newApiTxt, ctx, elapsed, result proc recorder*(cp: CoreDxCaptRef): CoreDbRc[CoreDbRef] = ## Getter cp.setTrackNewApi CptRecorderFn result = cp.methods.recorderFn() cp.ifTrackNewApi: debug newApiTxt, ctx, elapsed, result proc logDb*(cp: CoreDxCaptRef): CoreDbRc[CoreDbRef] = cp.setTrackNewApi CptLogDbFn result = cp.methods.logDbFn() cp.ifTrackNewApi: debug newApiTxt, ctx, elapsed, result proc flags*(cp: CoreDxCaptRef): set[CoreDbCaptFlags] = ## Getter cp.setTrackNewApi CptFlagsFn result = cp.methods.getFlagsFn() cp.ifTrackNewApi: debug newApiTxt, ctx, elapsed, result # ------------------------------------------------------------------------------ # Public methods, legacy API # ------------------------------------------------------------------------------ when ProvideLegacyAPI: proc parent*(cld: CoreDbChldRefs): CoreDbRef = ## Getter, common method for all sub-modules result = cld.distinctBase.parent proc backend*(dsc: CoreDbChldRefs): auto = dsc.setTrackLegaApi LegaBackendFn result = dsc.distinctBase.backend dsc.ifTrackLegaApi: debug legaApiTxt, ctx, elapsed # ---------------- proc kvt*(db: CoreDbRef): CoreDbKvtRef = ## Legacy pseudo constructor, see `toKvt()` for production constructor db.setTrackLegaApi LegaNewKvtFn result = db.newKvt().CoreDbKvtRef db.ifTrackLegaApi: debug legaApiTxt, ctx, elapsed, result proc get*(kvt: CoreDbKvtRef; key: openArray[byte]): Blob = kvt.setTrackLegaApi LegaKvtGetFn result = kvt.distinctBase.getOrEmpty(key).expect $ctx kvt.ifTrackLegaApi: debug legaApiTxt, ctx, elapsed, key=key.toStr, result=result.toStr proc del*(kvt: CoreDbKvtRef; key: openArray[byte]): void = kvt.setTrackLegaApi LegaKvtDelFn kvt.distinctBase.del(key).expect $ctx kvt.ifTrackLegaApi: debug legaApiTxt, ctx, elapsed, key=key.toStr proc put*(kvt: CoreDbKvtRef; key: openArray[byte]; val: openArray[byte]) = kvt.setTrackLegaApi LegaKvtPutFn kvt.distinctBase.parent.newKvt().put(key, val).expect $ctx kvt.ifTrackLegaApi: debug legaApiTxt, ctx, elapsed, key=key.toStr, val=val.toSeq.toStr proc contains*(kvt: CoreDbKvtRef; key: openArray[byte]): bool = kvt.setTrackLegaApi LegaKvtContainsFn result = kvt.distinctBase.hasKey(key).expect $ctx kvt.ifTrackLegaApi: debug legaApiTxt, ctx, elapsed, key=key.toStr, result # ---------------- proc toMpt*(phk: CoreDbPhkRef): CoreDbMptRef = phk.setTrackLegaApi LegaToMptFn result = phk.distinctBase.toMpt.CoreDbMptRef phk.ifTrackLegaApi: debug legaApiTxt, ctx, elapsed proc mptPrune*(db: CoreDbRef; root: Hash256; prune = true): CoreDbMptRef = db.setTrackLegaApi LegaNewMptFn let vid = block: if root.isValid: db.getRoot(root).expect $ctx else: CoreDbVidRef(nil) mpt = db.newMpt(vid, prune).valueOr: raiseAssert error.prettyText() result = mpt.CoreDbMptRef db.ifTrackLegaApi: debug legaApiTxt, ctx, elapsed, root, prune proc mptPrune*(db: CoreDbRef; prune = true): CoreDbMptRef = db.setTrackLegaApi LegaNewMptFn result = db.newMpt(prune).CoreDbMptRef db.ifTrackLegaApi: debug legaApiTxt, ctx, elapsed, prune # ---------------- proc toPhk*(mpt: CoreDbMptRef): CoreDbPhkRef = mpt.setTrackLegaApi LegaToPhkFn result = mpt.distinctBase.toPhk.CoreDbPhkRef mpt.ifTrackLegaApi: debug legaApiTxt, ctx, elapsed proc phkPrune*(db: CoreDbRef; root: Hash256; prune = true): CoreDbPhkRef = db.setTrackLegaApi LegaNewPhkFn let vid = block: if root.isValid: db.getRoot(root).expect $ctx else: CoreDbVidRef(nil) phk = db.newMpt(vid, prune).valueOr: raiseAssert error.prettyText() result = phk.toCoreDxPhkRef.CoreDbPhkRef db.ifTrackLegaApi: debug legaApiTxt, ctx, elapsed, root, prune proc phkPrune*(db: CoreDbRef; prune = true): CoreDbPhkRef = db.setTrackLegaApi LegaNewPhkFn result = db.newMpt(prune).toCoreDxPhkRef.CoreDbPhkRef db.ifTrackLegaApi: debug legaApiTxt, ctx, elapsed, prune # ---------------- proc isPruning*(trie: CoreDbTrieRefs): bool = trie.setTrackLegaApi LegaIsPruningFn result = trie.distinctBase.isPruning() trie.ifTrackLegaApi: debug legaApiTxt, ctx, elapsed, result proc get*(mpt: CoreDbMptRef; key: openArray[byte]): Blob = mpt.setTrackLegaApi LegaMptGetFn result = mpt.distinctBase.fetchOrEmpty(key).expect $ctx mpt.ifTrackLegaApi: debug legaApiTxt, ctx, elapsed, key=key.toStr, result=result.toStr proc get*(phk: CoreDbPhkRef; key: openArray[byte]): Blob = phk.setTrackLegaApi LegaPhkGetFn result = phk.distinctBase.fetchOrEmpty(key).expect $ctx phk.ifTrackLegaApi: debug legaApiTxt, ctx, elapsed, key=key.toStr, result=result.toStr proc del*(mpt: CoreDbMptRef; key: openArray[byte]) = mpt.setTrackLegaApi LegaMptDelFn mpt.distinctBase.delete(key).expect $ctx mpt.ifTrackLegaApi: debug legaApiTxt, ctx, elapsed, key=key.toStr proc del*(phk: CoreDbPhkRef; key: openArray[byte]) = phk.setTrackLegaApi LegaPhkDelFn phk.distinctBase.delete(key).expect $ctx phk.ifTrackLegaApi: debug legaApiTxt, ctx, elapsed, key=key.toStr proc put*(mpt: CoreDbMptRef; key: openArray[byte]; val: openArray[byte]) = mpt.setTrackLegaApi LegaMptPutFn mpt.distinctBase.merge(key, val).expect $ctx mpt.ifTrackLegaApi: debug legaApiTxt, ctx, elapsed, key=key.toStr, val=val.toSeq.toStr proc put*(phk: CoreDbPhkRef; key: openArray[byte]; val: openArray[byte]) = phk.setTrackLegaApi LegaPhkPutFn phk.distinctBase.merge(key, val).expect $ctx phk.ifTrackLegaApi: debug legaApiTxt, ctx, elapsed, key=key.toStr, val=val.toSeq.toStr proc contains*(mpt: CoreDbMptRef; key: openArray[byte]): bool = mpt.setTrackLegaApi LegaMptContainsFn result = mpt.distinctBase.hasPath(key).expect $ctx mpt.ifTrackLegaApi: debug legaApiTxt, ctx, elapsed, key=key.toStr, result proc contains*(phk: CoreDbPhkRef; key: openArray[byte]): bool = phk.setTrackLegaApi LegaPhkContainsFn result = phk.distinctBase.hasPath(key).expect $ctx phk.ifTrackLegaApi: debug legaApiTxt, ctx, elapsed, key=key.toStr, result proc rootHash*(mpt: CoreDbMptRef): Hash256 = mpt.setTrackLegaApi LegaMptRootHashFn result = mpt.distinctBase.rootVid().hash().expect $ctx mpt.ifTrackLegaApi: debug legaApiTxt, ctx, elapsed, result=result.toStr proc rootHash*(phk: CoreDbPhkRef): Hash256 = phk.setTrackLegaApi LegaPhkRootHashFn result = phk.distinctBase.rootVid().hash().expect $ctx phk.ifTrackLegaApi: debug legaApiTxt, ctx, elapsed, result=result.toStr # ---------------- proc getTransactionID*(db: CoreDbRef): CoreDbTxID = db.setTrackLegaApi LegaGetTxIdFn result = db.methods.getIdFn().expect($ctx).CoreDbTxID db.ifTrackLegaApi: debug legaApiTxt, ctx, elapsed proc shortTimeReadOnly*( id: CoreDbTxID; action: proc() {.catchRaise.}; ) {.catchRaise.} = id.setTrackLegaApi LegaShortTimeRoFn var oops = none(ref CatchableError) proc safeFn() = try: action() except CatchableError as e: oops = some(e) # Action has finished now id.distinctBase.methods.roWrapperFn(safeFn).expect $ctx # 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: debug legaApiTxt, ctx, elapsed proc beginTransaction*(db: CoreDbRef): CoreDbTxRef = db.setTrackLegaApi LegaBeginTxFn result = (db.distinctBase.methods.beginFn().expect $ctx).CoreDbTxRef db.ifTrackLegaApi: debug legaApiTxt, ctx, elapsed, newLevel=db.methods.levelFn() proc commit*(tx: CoreDbTxRef, applyDeletes = true) = tx.setTrackLegaApi LegaTxCommitFn: let prvLevel {.used.} = tx.distinctBase.methods.levelFn() tx.distinctBase.commit(applyDeletes).expect $ctx tx.ifTrackLegaApi: debug legaApiTxt, ctx, elapsed, prvLevel proc rollback*(tx: CoreDbTxRef) = tx.setTrackLegaApi LegaTxCommitFn: let prvLevel {.used.} = tx.distinctBase.methods.levelFn() tx.distinctBase.rollback().expect $ctx tx.ifTrackLegaApi: debug legaApiTxt, ctx, elapsed, prvLevel proc dispose*(tx: CoreDbTxRef) = tx.setTrackLegaApi LegaTxDisposeFn: let prvLevel {.used.} = tx.distinctBase.methods.levelFn() tx.distinctBase.dispose().expect $ctx tx.ifTrackLegaApi: debug legaApiTxt, ctx, elapsed, prvLevel proc safeDispose*(tx: CoreDbTxRef) = tx.setTrackLegaApi LegaTxSaveDisposeFn: let prvLevel {.used.} = tx.distinctBase.methods.levelFn() tx.distinctBase.safeDispose().expect $ctx tx.ifTrackLegaApi: debug legaApiTxt, ctx, elapsed, prvLevel # ---------------- proc capture*( db: CoreDbRef; flags: set[CoreDbCaptFlags] = {}; ): CoreDbCaptRef = db.setTrackLegaApi LegaCaptureFn result = db.newCapture(flags).expect($ctx).CoreDbCaptRef db.ifTrackLegaApi: debug legaApiTxt, ctx, elapsed proc recorder*(cp: CoreDbCaptRef): CoreDbRef = cp.setTrackLegaApi LegaCptRecorderFn result = cp.distinctBase.recorder().expect $ctx cp.ifTrackLegaApi: debug legaApiTxt, ctx, elapsed proc logDb*(cp: CoreDbCaptRef): CoreDbRef = cp.setTrackLegaApi LegaCptLogDbFn result = cp.distinctBase.logDb().expect $ctx cp.ifTrackLegaApi: debug legaApiTxt, ctx, elapsed proc flags*(cp: CoreDbCaptRef): set[CoreDbCaptFlags] = cp.setTrackLegaApi LegaCptFlagsFn result = cp.distinctBase.flags() cp.ifTrackLegaApi: debug legaApiTxt, ctx, elapsed, result # ------------------------------------------------------------------------------ # End # ------------------------------------------------------------------------------