Provide `Aristo` backend for `CoreDb` (#1895)
This commit is contained in:
parent
c47f021596
commit
4825ab1566
|
@ -0,0 +1,285 @@
|
||||||
|
# Nimbus
|
||||||
|
# Copyright (c) 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
|
||||||
|
eth/common,
|
||||||
|
results,
|
||||||
|
"../.."/[aristo, aristo/aristo_desc, aristo/aristo_init/memory_only],
|
||||||
|
"../.."/[kvt, kvt/kvt_desc, kvt/kvt_init/memory_only],
|
||||||
|
".."/[base, base/base_desc],
|
||||||
|
./aristo_db/[common_desc, handlers_aristo, handlers_kvt]
|
||||||
|
|
||||||
|
# Annotation helpers
|
||||||
|
{.pragma: noRaise, gcsafe, raises: [].}
|
||||||
|
{.pragma: rlpRaise, gcsafe, raises: [AristoApiRlpError].}
|
||||||
|
|
||||||
|
export
|
||||||
|
AristoApiRlpError,
|
||||||
|
AristoCoreDbKvtBE,
|
||||||
|
memory_only
|
||||||
|
|
||||||
|
type
|
||||||
|
AristoCoreDbRef* = ref object of CoreDbRef
|
||||||
|
## Main descriptor
|
||||||
|
kdbBase: KvtBaseRef ## Kvt subsystem
|
||||||
|
adbBase: AristoBaseRef ## Aristo subsystem
|
||||||
|
|
||||||
|
AristoCoreDbBE = ref object of CoreDbBackendRef
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Private helpers
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template valueOrApiError[U,V](rc: Result[U,V]; info: static[string]): U =
|
||||||
|
rc.valueOr: raise (ref AristoApiRlpError)(msg: info)
|
||||||
|
|
||||||
|
func notImplemented[T](
|
||||||
|
_: typedesc[T];
|
||||||
|
db: AristoCoreDbRef;
|
||||||
|
info: string;
|
||||||
|
): CoreDbRc[T] {.gcsafe.} =
|
||||||
|
## Applies only to `Aristo` methods
|
||||||
|
err((VertexID(0),aristo.NotImplemented).toError(db, info))
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Private call back functions (too large for embedding to maintain)
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
iterator kvtPairs(
|
||||||
|
T: typedesc;
|
||||||
|
dsc: CoreDxKvtRef;
|
||||||
|
info: static[string];
|
||||||
|
): (Blob,Blob) =
|
||||||
|
let p = dsc.kvt.forkTop.valueOrApiError info
|
||||||
|
defer: discard p.forget()
|
||||||
|
|
||||||
|
dsc.methods.pairsIt = iterator(): (Blob, Blob) =
|
||||||
|
for (n,k,v) in T.walkPairs p:
|
||||||
|
yield (k,v)
|
||||||
|
|
||||||
|
|
||||||
|
iterator mptReplicate(
|
||||||
|
T: typedesc;
|
||||||
|
dsc: CoreDxMptRef;
|
||||||
|
info: static[string];
|
||||||
|
): (Blob,Blob)
|
||||||
|
{.rlpRaise.} =
|
||||||
|
let p = dsc.mpt.forkTop.valueOrApiError info
|
||||||
|
defer: discard p.forget()
|
||||||
|
|
||||||
|
let root = dsc.root
|
||||||
|
for (vid,key,vtx,node) in T.replicate p:
|
||||||
|
if key.len == 32:
|
||||||
|
yield (@key, node.encode)
|
||||||
|
elif vid == root:
|
||||||
|
yield (@(key.to(Hash256).data), node.encode)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Private tx and base methods
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
proc txMethods(
|
||||||
|
db: AristoCoreDbRef;
|
||||||
|
aTx: AristoTxRef;
|
||||||
|
kTx: KvtTxRef;
|
||||||
|
): CoreDbTxFns =
|
||||||
|
## To be constructed by some `CoreDbBaseFns` function
|
||||||
|
CoreDbTxFns(
|
||||||
|
commitFn: proc(ignore: bool): CoreDbRc[void] =
|
||||||
|
const info = "commitFn()"
|
||||||
|
? aTx.commit.toVoidRc(db, info)
|
||||||
|
? kTx.commit.toVoidRc(db, info)
|
||||||
|
ok(),
|
||||||
|
|
||||||
|
rollbackFn: proc(): CoreDbRc[void] =
|
||||||
|
const info = "rollbackFn()"
|
||||||
|
? aTx.rollback.toVoidRc(db, info)
|
||||||
|
? kTx.rollback.toVoidRc(db, info)
|
||||||
|
ok(),
|
||||||
|
|
||||||
|
disposeFn: proc(): CoreDbRc[void] =
|
||||||
|
const info = "disposeFn()"
|
||||||
|
if aTx.isTop: ? aTx.rollback.toVoidRc(db, info)
|
||||||
|
if kTx.isTop: ? kTx.rollback.toVoidRc(db, info)
|
||||||
|
ok(),
|
||||||
|
|
||||||
|
safeDisposeFn: proc(): CoreDbRc[void] =
|
||||||
|
const info = "safeDisposeFn()"
|
||||||
|
if aTx.isTop: ? aTx.rollback.toVoidRc(db, info)
|
||||||
|
if kTx.isTop: ? kTx.rollback.toVoidRc(db, info)
|
||||||
|
ok())
|
||||||
|
|
||||||
|
|
||||||
|
proc baseMethods(
|
||||||
|
db: AristoCoreDbRef;
|
||||||
|
A: typedesc;
|
||||||
|
K: typedesc;
|
||||||
|
): CoreDbBaseFns =
|
||||||
|
let db = db
|
||||||
|
CoreDbBaseFns(
|
||||||
|
backendFn: proc(): CoreDbBackendRef =
|
||||||
|
db.bless(AristoCoreDbBE()),
|
||||||
|
|
||||||
|
destroyFn: proc(flush: bool) =
|
||||||
|
db.adbBase.destroy(flush)
|
||||||
|
db.kdbBase.destroy(flush),
|
||||||
|
|
||||||
|
vidHashFn: proc(vid: CoreDbVidRef; update: bool): CoreDbRc[Hash256] =
|
||||||
|
db.adbBase.getHash(vid, update, "vidHashFn()"),
|
||||||
|
|
||||||
|
errorPrintFn: proc(e: CoreDbErrorRef): string =
|
||||||
|
e.errorPrint(),
|
||||||
|
|
||||||
|
legacySetupFn: proc() =
|
||||||
|
discard,
|
||||||
|
|
||||||
|
getRootFn: proc(root: Hash256; createOk: bool): CoreDbRc[CoreDbVidRef] =
|
||||||
|
db.adbBase.getVid(root, createOk, "getRootFn()"),
|
||||||
|
|
||||||
|
newKvtFn: proc(saveMode: CoreDbSaveFlags): CoreDbRc[CoreDxKvtRef] =
|
||||||
|
db.kdbBase.gc()
|
||||||
|
let dsc = ? db.kdbBase.newKvtHandler(saveMode, "newKvtFn()")
|
||||||
|
when K is MemBackendRef:
|
||||||
|
dsc.methods.pairsIt = iterator(): (Blob, Blob) =
|
||||||
|
for (n,k,v) in K.kvtPairs dsc:
|
||||||
|
yield (k,v)
|
||||||
|
ok(dsc),
|
||||||
|
|
||||||
|
newMptFn: proc(
|
||||||
|
root: CoreDbVidRef;
|
||||||
|
prune: bool;
|
||||||
|
saveMode: CoreDbSaveFlags;
|
||||||
|
): CoreDbRc[CoreDxMptRef] =
|
||||||
|
db.kdbBase.gc()
|
||||||
|
let dsc = ? db.adbBase.newMptHandler(root, prune, saveMode, "newMptFn()")
|
||||||
|
when K is MemBackendRef:
|
||||||
|
dsc.methods.replicateIt = iterator: (Blob,Blob) {.rlpRaise.} =
|
||||||
|
for w in T.mptReplicate(dsc, "forkTop() for replicateIt()"):
|
||||||
|
yield w
|
||||||
|
ok(dsc),
|
||||||
|
|
||||||
|
newAccFn: proc(
|
||||||
|
root: CoreDbVidRef;
|
||||||
|
prune: bool;
|
||||||
|
saveMode: CoreDbSaveFlags;
|
||||||
|
): CoreDbRc[CoreDxAccRef] =
|
||||||
|
db.kdbBase.gc()
|
||||||
|
ok(? db.adbBase.newAccHandler(prune, saveMode, "newAccFn()")),
|
||||||
|
|
||||||
|
beginFn: proc(): CoreDbRc[CoreDxTxRef] =
|
||||||
|
const info = "beginFn()"
|
||||||
|
let
|
||||||
|
aTx = ? db.adbBase.txBegin(info)
|
||||||
|
kTx = ? db.kdbBase.txBegin(info)
|
||||||
|
ok(db.bless CoreDxTxRef(methods: db.txMethods(aTx, kTx))),
|
||||||
|
|
||||||
|
getIdFn: proc(): CoreDbRc[CoreDxTxID] =
|
||||||
|
CoreDxTxID.notImplemented(db, "getIdFn()"),
|
||||||
|
|
||||||
|
captureFn: proc(flags: set[CoreDbCaptFlags]): CoreDbRc[CoreDxCaptRef] =
|
||||||
|
CoreDxCaptRef.notImplemented(db, "capture()"))
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Private constructor helpers
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
proc create(
|
||||||
|
dbType: CoreDbType;
|
||||||
|
kdb: KvtDbRef;
|
||||||
|
K: typedesc;
|
||||||
|
adb: AristoDbRef;
|
||||||
|
A: typedesc;
|
||||||
|
): 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(A,K)
|
||||||
|
db.bless
|
||||||
|
|
||||||
|
proc init(
|
||||||
|
dbType: CoreDbType;
|
||||||
|
K: typedesc;
|
||||||
|
A: typedesc;
|
||||||
|
qlr: QidLayoutRef;
|
||||||
|
): CoreDbRef =
|
||||||
|
dbType.create(KvtDbRef.init(K), K, AristoDbRef.init(A, qlr), A)
|
||||||
|
|
||||||
|
proc init(
|
||||||
|
dbType: CoreDbType;
|
||||||
|
K: typedesc;
|
||||||
|
A: typedesc;
|
||||||
|
): CoreDbRef =
|
||||||
|
dbType.create(KvtDbRef.init(K), K, AristoDbRef.init(A), A)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Public constructor helpers
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
proc init*(
|
||||||
|
dbType: CoreDbType;
|
||||||
|
K: typedesc;
|
||||||
|
A: typedesc;
|
||||||
|
path: string;
|
||||||
|
qlr: QidLayoutRef;
|
||||||
|
): CoreDbRef =
|
||||||
|
dbType.create(
|
||||||
|
KvtDbRef.init(K, path).expect "Kvt/RocksDB init() failed", K,
|
||||||
|
AristoDbRef.init(A, path, qlr).expect "Aristo/RocksDB init() failed", A)
|
||||||
|
|
||||||
|
proc init*(
|
||||||
|
dbType: CoreDbType;
|
||||||
|
K: typedesc;
|
||||||
|
A: typedesc;
|
||||||
|
path: string;
|
||||||
|
): CoreDbRef =
|
||||||
|
dbType.create(
|
||||||
|
KvtDbRef.init(K, path).expect "Kvt/RocksDB init() failed", K,
|
||||||
|
AristoDbRef.init(A, path).expect "Aristo/RocksDB init() failed", A)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Public constructor
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
proc newAristoMemoryCoreDbRef*(qlr: QidLayoutRef): CoreDbRef =
|
||||||
|
AristoDbMemory.init(kvt.MemBackendRef, aristo.MemBackendRef, qlr)
|
||||||
|
|
||||||
|
proc newAristoMemoryCoreDbRef*(): CoreDbRef =
|
||||||
|
AristoDbMemory.init(kvt.MemBackendRef, aristo.MemBackendRef)
|
||||||
|
|
||||||
|
proc newAristoVoidCoreDbRef*(): CoreDbRef =
|
||||||
|
AristoDbVoid.init(kvt.VoidBackendRef, aristo.VoidBackendRef)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Public helpers for direct backend access
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func toAristo*(be: CoreDbKvtBackendRef): KvtDbRef =
|
||||||
|
if be.parent.isAristo:
|
||||||
|
return be.AristoCoreDbKvtBE.kdb
|
||||||
|
|
||||||
|
func toAristo*(be: CoreDbMptBackendRef): AristoDbRef =
|
||||||
|
if be.parent.isAristo:
|
||||||
|
return be.AristoCoreDbMptBE.adb
|
||||||
|
|
||||||
|
func toAristo*(be: CoreDbAccBackendRef): AristoDbRef =
|
||||||
|
if be.parent.isAristo:
|
||||||
|
return be.AristoCoreDbAccBE.adb
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# End
|
||||||
|
# ------------------------------------------------------------------------------
|
|
@ -0,0 +1,53 @@
|
||||||
|
# Nimbus
|
||||||
|
# Copyright (c) 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
|
||||||
|
../../../../errors,
|
||||||
|
"../../.."/[aristo, kvt],
|
||||||
|
../../base/base_desc
|
||||||
|
|
||||||
|
type
|
||||||
|
AristoApiRlpError* = object of CoreDbApiError
|
||||||
|
## For re-routing exceptions in iterator closure
|
||||||
|
|
||||||
|
AristoCoreDbError* = ref object of CoreDbErrorRef
|
||||||
|
## Error return code
|
||||||
|
ctx*: string ## Context where the exception or error occured
|
||||||
|
case isAristo*: bool
|
||||||
|
of true:
|
||||||
|
aVid*: VertexID
|
||||||
|
aErr*: AristoError
|
||||||
|
else:
|
||||||
|
kErr*: KvtError
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Public helpers
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func isAristo*(be: CoreDbRef): bool =
|
||||||
|
be.dbType in {AristoDbMemory, AristoDbRocks}
|
||||||
|
|
||||||
|
func errorPrint*(e: CoreDbErrorRef): string =
|
||||||
|
if not e.isNil:
|
||||||
|
let e = e.AristoCoreDbError
|
||||||
|
result = if e.isAristo: "Aristo: " else: "Kvt: "
|
||||||
|
result &= "ctx=\"" & $e.ctx & "\"" & ", "
|
||||||
|
if e.isAristo:
|
||||||
|
if e.aVid.isValid:
|
||||||
|
result &= "vid=\"" & $e.aVid & "\"" & ", "
|
||||||
|
result &= "error=\"" & $e.aErr & "\""
|
||||||
|
else:
|
||||||
|
result &= "error=\"" & $e.kErr & "\""
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# End
|
||||||
|
# ------------------------------------------------------------------------------
|
|
@ -0,0 +1,586 @@
|
||||||
|
# Nimbus
|
||||||
|
# Copyright (c) 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
|
||||||
|
chronicles,
|
||||||
|
eth/common,
|
||||||
|
results,
|
||||||
|
../../../aristo,
|
||||||
|
../../../aristo/aristo_desc,
|
||||||
|
../../base,
|
||||||
|
../../base/base_desc,
|
||||||
|
./common_desc
|
||||||
|
|
||||||
|
type
|
||||||
|
AristoBaseRef* = ref object
|
||||||
|
parent: CoreDbRef ## Opaque top level descriptor
|
||||||
|
adb: AristoDbRef ## Aristo MPT database
|
||||||
|
gq: seq[AristoChildDbRef] ## Garbage queue, deferred disposal
|
||||||
|
|
||||||
|
AristoChildDbRef = ref AristoChildDbObj
|
||||||
|
AristoChildDbObj = object
|
||||||
|
## Sub-handle for triggering destructor when it goes out of scope
|
||||||
|
base: AristoBaseRef ## Local base descriptor
|
||||||
|
root: VertexID ## State root
|
||||||
|
prune: bool ## Currently unused
|
||||||
|
mpt: AristoDbRef ## Descriptor, may be copy of `base.adb`
|
||||||
|
saveMode: CoreDbSaveFlags ## When to store/discard
|
||||||
|
|
||||||
|
AristoCoreDxMptRef = ref object of CoreDxMptRef
|
||||||
|
## Some extendion to recover embedded state
|
||||||
|
ctx: AristoChildDbRef ## Embedded state, typical var name: `cMpt`
|
||||||
|
|
||||||
|
AristoCoreDbVid* = ref object of CoreDbVidRef
|
||||||
|
## Vertex ID wrapper, optinally with *MPT* context
|
||||||
|
ctx: AristoDbRef ## Optional *MPT* context, might be `nil`
|
||||||
|
aVid: VertexID ## Refers to root vertex
|
||||||
|
createOk: bool ## Create new root vertex when appropriate
|
||||||
|
expHash: Hash256 ## Deferred validation
|
||||||
|
|
||||||
|
AristoCoreDbMptBE* = ref object of CoreDbMptBackendRef
|
||||||
|
adb*: AristoDbRef
|
||||||
|
|
||||||
|
AristoCoreDbAccBE* = ref object of CoreDbAccBackendRef
|
||||||
|
adb*: AristoDbRef
|
||||||
|
|
||||||
|
logScope:
|
||||||
|
topics = "aristo-hdl"
|
||||||
|
|
||||||
|
proc gc*(base: AristoBaseRef) {.gcsafe.}
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Private helpers
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template logTxt(info: static[string]): static[string] =
|
||||||
|
"CoreDb/adb " & info
|
||||||
|
|
||||||
|
func isValid(vid: CoreDbVidRef): bool =
|
||||||
|
not vid.isNil and vid.ready
|
||||||
|
|
||||||
|
func to*(vid: CoreDbVidRef; T: type VertexID): T =
|
||||||
|
if vid.isValid:
|
||||||
|
return vid.AristoCoreDbVid.aVid
|
||||||
|
|
||||||
|
func createOk(vid: CoreDbVidRef): bool =
|
||||||
|
if vid.isValid:
|
||||||
|
return vid.AristoCoreDbVid.createOk
|
||||||
|
|
||||||
|
# --------------
|
||||||
|
|
||||||
|
func toCoreDbAccount(
|
||||||
|
cMpt: AristoChildDbRef;
|
||||||
|
acc: AristoAccount;
|
||||||
|
): CoreDbAccount =
|
||||||
|
let db = cMpt.base.parent
|
||||||
|
CoreDbAccount(
|
||||||
|
nonce: acc.nonce,
|
||||||
|
balance: acc.balance,
|
||||||
|
codeHash: acc.codeHash,
|
||||||
|
storageVid: db.bless AristoCoreDbVid(ctx: cMpt.mpt, aVid: acc.storageID))
|
||||||
|
|
||||||
|
func toPayloadRef(acc: CoreDbAccount): PayloadRef =
|
||||||
|
PayloadRef(
|
||||||
|
pType: AccountData,
|
||||||
|
account: AristoAccount(
|
||||||
|
nonce: acc.nonce,
|
||||||
|
balance: acc.balance,
|
||||||
|
storageID: acc.storageVid.to(VertexID),
|
||||||
|
codeHash: acc.codeHash))
|
||||||
|
|
||||||
|
# --------------
|
||||||
|
|
||||||
|
func toErrorImpl(
|
||||||
|
e: AristoError;
|
||||||
|
db: CoreDbRef;
|
||||||
|
info: string;
|
||||||
|
error = Unspecified;
|
||||||
|
): CoreDbErrorRef =
|
||||||
|
db.bless(error, AristoCoreDbError(
|
||||||
|
ctx: info,
|
||||||
|
isAristo: true,
|
||||||
|
aErr: e))
|
||||||
|
|
||||||
|
func toErrorImpl(
|
||||||
|
e: (VertexID,AristoError);
|
||||||
|
db: CoreDbRef;
|
||||||
|
info: string;
|
||||||
|
error = Unspecified;
|
||||||
|
): CoreDbErrorRef =
|
||||||
|
db.bless(error, AristoCoreDbError(
|
||||||
|
ctx: info,
|
||||||
|
isAristo: true,
|
||||||
|
aVid: e[0],
|
||||||
|
aErr: e[1]))
|
||||||
|
|
||||||
|
|
||||||
|
func toRcImpl[T](
|
||||||
|
rc: Result[T,(VertexID,AristoError)];
|
||||||
|
db: CoreDbRef;
|
||||||
|
info: string;
|
||||||
|
error = Unspecified;
|
||||||
|
): CoreDbRc[T] =
|
||||||
|
if rc.isOk:
|
||||||
|
when T is void:
|
||||||
|
return ok()
|
||||||
|
else:
|
||||||
|
return ok(rc.value)
|
||||||
|
err rc.error.toErrorImpl(db, info, error)
|
||||||
|
|
||||||
|
func toRcImpl[T](
|
||||||
|
rc: Result[T,AristoError];
|
||||||
|
db: CoreDbRef;
|
||||||
|
info: string;
|
||||||
|
error = Unspecified;
|
||||||
|
): CoreDbRc[T] =
|
||||||
|
if rc.isOk:
|
||||||
|
when T is void:
|
||||||
|
return ok()
|
||||||
|
else:
|
||||||
|
return ok(rc.value)
|
||||||
|
err((VertexID(0),rc.error).toErrorImpl(db, info, error))
|
||||||
|
|
||||||
|
|
||||||
|
func toVoidRcImpl[T](
|
||||||
|
rc: Result[T,(VertexID,AristoError)];
|
||||||
|
db: CoreDbRef;
|
||||||
|
info: string;
|
||||||
|
error = Unspecified;
|
||||||
|
): CoreDbRc[void] =
|
||||||
|
if rc.isOk:
|
||||||
|
return ok()
|
||||||
|
err rc.error.toErrorImpl(db, info, error)
|
||||||
|
|
||||||
|
func toVoidRcImpl[T](
|
||||||
|
rc: Result[T,AristoError];
|
||||||
|
db: CoreDbRef;
|
||||||
|
info: string;
|
||||||
|
error = Unspecified;
|
||||||
|
): CoreDbRc[void] =
|
||||||
|
if rc.isOk:
|
||||||
|
return ok()
|
||||||
|
err((VertexID(0),rc.error).toErrorImpl(db, info, error))
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Private call back functions (too large for keeping inline)
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
proc finish(
|
||||||
|
cMpt: AristoChildDbRef;
|
||||||
|
info: static[string];
|
||||||
|
): CoreDbRc[void] =
|
||||||
|
## Hexary trie destructor to be called automagically when the argument
|
||||||
|
## wrapper gets out of scope.
|
||||||
|
let
|
||||||
|
base = cMpt.base
|
||||||
|
db = base.parent
|
||||||
|
|
||||||
|
result = ok()
|
||||||
|
|
||||||
|
if cMpt.mpt != base.adb:
|
||||||
|
let rc = cMpt.mpt.forget()
|
||||||
|
if rc.isErr:
|
||||||
|
result = err(rc.error.toErrorImpl(db, info))
|
||||||
|
cMpt.mpt = AristoDbRef(nil) # disables `=destroy`
|
||||||
|
|
||||||
|
if cMpt.saveMode == AutoSave:
|
||||||
|
if base.adb.level == 0:
|
||||||
|
let rc = base.adb.stow(persistent = true)
|
||||||
|
if rc.isErr:
|
||||||
|
result = err(rc.error.toErrorImpl(db, info))
|
||||||
|
|
||||||
|
proc `=destroy`(cMpt: var AristoChildDbObj) =
|
||||||
|
## Auto destructor
|
||||||
|
if not cMpt.mpt.isNil:
|
||||||
|
# Add to destructor batch queue unless direct reference
|
||||||
|
if cMpt.mpt != cMpt.base.adb or
|
||||||
|
cMpt.saveMode == AutoSave:
|
||||||
|
cMpt.base.gq.add AristoChildDbRef(
|
||||||
|
base: cMpt.base,
|
||||||
|
mpt: cMpt.mpt,
|
||||||
|
saveMode: cMpt.saveMode)
|
||||||
|
|
||||||
|
# -------------------------------
|
||||||
|
|
||||||
|
proc mptFetch(
|
||||||
|
cMpt: AristoChildDbRef;
|
||||||
|
k: openArray[byte];
|
||||||
|
info: static[string];
|
||||||
|
): CoreDbRc[Blob] =
|
||||||
|
let
|
||||||
|
db = cMpt.base.parent
|
||||||
|
rc = cMpt.mpt.fetchPayload(cMpt.root, k)
|
||||||
|
if rc.isOk:
|
||||||
|
return cMpt.mpt.serialise(rc.value).toRcImpl(db, info)
|
||||||
|
|
||||||
|
if rc.error[1] != FetchPathNotFound:
|
||||||
|
return err(rc.error.toErrorImpl(db, info))
|
||||||
|
|
||||||
|
err rc.error.toErrorImpl(db, info, MptNotFound)
|
||||||
|
|
||||||
|
|
||||||
|
proc mptMerge(
|
||||||
|
cMpt: AristoChildDbRef;
|
||||||
|
k: openArray[byte];
|
||||||
|
v: openArray[byte];
|
||||||
|
info: static[string];
|
||||||
|
): CoreDbRc[void] =
|
||||||
|
let rc = cMpt.mpt.merge(cMpt.root, k, v)
|
||||||
|
if rc.isErr:
|
||||||
|
return err(rc.error.toErrorImpl(cMpt.base.parent, info))
|
||||||
|
ok()
|
||||||
|
|
||||||
|
|
||||||
|
proc mptDelete(
|
||||||
|
cMpt: AristoChildDbRef;
|
||||||
|
k: openArray[byte];
|
||||||
|
info: static[string];
|
||||||
|
): CoreDbRc[void] =
|
||||||
|
let rc = cMpt.mpt.delete(cMpt.root, k)
|
||||||
|
if rc.isErr:
|
||||||
|
return err(rc.error.toErrorImpl(cMpt.base.parent, info))
|
||||||
|
ok()
|
||||||
|
|
||||||
|
# -------------------------------
|
||||||
|
|
||||||
|
proc accFetch(
|
||||||
|
cMpt: AristoChildDbRef;
|
||||||
|
address: EthAddress;
|
||||||
|
info: static[string];
|
||||||
|
): CoreDbRc[CoreDbAccount] =
|
||||||
|
let
|
||||||
|
db = cMpt.base.parent
|
||||||
|
pyl = block:
|
||||||
|
let rc = cMpt.mpt.fetchPayload(cMpt.root, address.keccakHash.data)
|
||||||
|
if rc.isOk:
|
||||||
|
rc.value
|
||||||
|
elif rc.error[1] != FetchPathNotFound:
|
||||||
|
return err(rc.error.toErrorImpl(db, info))
|
||||||
|
else:
|
||||||
|
return err(rc.error.toErrorImpl(db, info, AccNotFound))
|
||||||
|
|
||||||
|
if pyl.pType != AccountData:
|
||||||
|
let vePair = (pyl.account.storageID, PayloadTypeUnsupported)
|
||||||
|
return err(vePair.toErrorImpl(db, info & "/" & $pyl.pType))
|
||||||
|
|
||||||
|
ok cMpt.toCoreDbAccount pyl.account
|
||||||
|
|
||||||
|
|
||||||
|
proc accMerge(
|
||||||
|
cMpt: AristoChildDbRef;
|
||||||
|
address: EthAddress;
|
||||||
|
acc: CoreDbAccount;
|
||||||
|
info: static[string];
|
||||||
|
): CoreDbRc[void] =
|
||||||
|
let
|
||||||
|
key = address.keccakHash.data
|
||||||
|
val = acc.toPayloadRef()
|
||||||
|
rc = cMpt.mpt.merge(cMpt.root, key, val)
|
||||||
|
if rc.isErr:
|
||||||
|
return rc.toVoidRcImpl(cMpt.base.parent, info)
|
||||||
|
ok()
|
||||||
|
|
||||||
|
|
||||||
|
proc accDelete(
|
||||||
|
cMpt: AristoChildDbRef;
|
||||||
|
address: EthAddress;
|
||||||
|
info: static[string];
|
||||||
|
): CoreDbRc[void] =
|
||||||
|
let
|
||||||
|
key = address.keccakHash.data
|
||||||
|
rc = cMpt.mpt.delete(cMpt.root, key)
|
||||||
|
if rc.isErr:
|
||||||
|
return rc.toVoidRcImpl(cMpt.base.parent, info)
|
||||||
|
ok()
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Private database methods function tables
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
proc mptMethods(cMpt: AristoChildDbRef): CoreDbMptFns =
|
||||||
|
## Hexary trie database handlers
|
||||||
|
let db = cMpt.base.parent
|
||||||
|
CoreDbMptFns(
|
||||||
|
backendFn: proc(): CoreDbMptBackendRef =
|
||||||
|
AristoCoreDbMptBE(adb: cMpt.mpt),
|
||||||
|
|
||||||
|
fetchFn: proc(k: openArray[byte]): CoreDbRc[Blob] =
|
||||||
|
cMpt.mptFetch(k, "fetchFn()"),
|
||||||
|
|
||||||
|
deleteFn: proc(k: openArray[byte]): CoreDbRc[void] =
|
||||||
|
cMpt.mptDelete(k, "deleteFn()"),
|
||||||
|
|
||||||
|
mergeFn: proc(k: openArray[byte]; v: openArray[byte]): CoreDbRc[void] =
|
||||||
|
cMpt.mptMerge(k, v, "mergeFn()"),
|
||||||
|
|
||||||
|
hasPathFn: proc(k: openArray[byte]): CoreDbRc[bool] =
|
||||||
|
cMpt.mpt.hasPath(cMpt.root, k).toRcImpl(db, "hasPathFn()"),
|
||||||
|
|
||||||
|
rootVidFn: proc(): CoreDbVidRef =
|
||||||
|
var w = AristoCoreDbVid(ctx: cMpt.mpt, aVid: cMpt.root)
|
||||||
|
db.bless(w),
|
||||||
|
|
||||||
|
isPruningFn: proc(): bool =
|
||||||
|
cMpt.prune,
|
||||||
|
|
||||||
|
destroyFn: proc(saveMode: CoreDbSaveFlags): CoreDbRc[void] =
|
||||||
|
cMpt.base.gc()
|
||||||
|
result = cMpt.finish "destroyFn()"
|
||||||
|
cMpt.mpt = AristoDbRef(nil), # Disables `=destroy()` action
|
||||||
|
|
||||||
|
pairsIt: iterator: (Blob,Blob) =
|
||||||
|
for (k,v) in cMpt.mpt.right LeafTie(root: cMpt.root):
|
||||||
|
yield (k.path.pathAsBlob, cMpt.mpt.serialise(v).valueOr(EmptyBlob)),
|
||||||
|
|
||||||
|
replicateIt: iterator: (Blob,Blob) {.gcsafe, raises: [AristoApiRlpError].} =
|
||||||
|
discard)
|
||||||
|
|
||||||
|
proc accMethods(cMpt: AristoChildDbRef): CoreDbAccFns =
|
||||||
|
## Hexary trie database handlers
|
||||||
|
let db = cMpt.base.parent
|
||||||
|
CoreDbAccFns(
|
||||||
|
backendFn: proc(): CoreDbAccBackendRef =
|
||||||
|
db.bless(AristoCoreDbAccBE(adb: cMpt.mpt)),
|
||||||
|
|
||||||
|
fetchFn: proc(address: EthAddress): CoreDbRc[CoreDbAccount] =
|
||||||
|
cMpt.accFetch(address, "fetchFn()"),
|
||||||
|
|
||||||
|
deleteFn: proc(address: EthAddress): CoreDbRc[void] =
|
||||||
|
cMpt.mptDelete(address, "deleteFn()"),
|
||||||
|
|
||||||
|
mergeFn: proc(address: EthAddress; acc: CoreDbAccount): CoreDbRc[void] =
|
||||||
|
cMpt.accMerge(address, acc, "mergeFn()"),
|
||||||
|
|
||||||
|
hasPathFn: proc(address: EthAddress): CoreDbRc[bool] =
|
||||||
|
let key = address.keccakHash.data
|
||||||
|
cMpt.mpt.hasPath(cMpt.root, key).toRcImpl(db, "hasPathFn()"),
|
||||||
|
|
||||||
|
rootVidFn: proc(): CoreDbVidRef =
|
||||||
|
db.bless(AristoCoreDbVid(ctx: cMpt.mpt, aVid: cMpt.root)),
|
||||||
|
|
||||||
|
isPruningFn: proc(): bool =
|
||||||
|
cMpt.prune,
|
||||||
|
|
||||||
|
destroyFn: proc(saveMode: CoreDbSaveFlags): CoreDbRc[void] =
|
||||||
|
cMpt.base.gc()
|
||||||
|
result = cMpt.finish "destroyFn()"
|
||||||
|
cMpt.mpt = AristoDbRef(nil)) # Disables `=destroy()` action
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Public handlers and helpers
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func toError*(
|
||||||
|
e: (VertexID,AristoError);
|
||||||
|
db: CoreDbRef;
|
||||||
|
info: string;
|
||||||
|
error = Unspecified;
|
||||||
|
): CoreDbErrorRef =
|
||||||
|
e.toErrorImpl(db, info, error)
|
||||||
|
|
||||||
|
func toVoidRc*[T](
|
||||||
|
rc: Result[T,AristoError];
|
||||||
|
db: CoreDbRef;
|
||||||
|
info: string;
|
||||||
|
error = Unspecified;
|
||||||
|
): CoreDbRc[void] =
|
||||||
|
rc.toVoidRcImpl(db, info, error)
|
||||||
|
|
||||||
|
proc gc*(base: AristoBaseRef) =
|
||||||
|
## Run deferred destructors when it is safe. It is needed to run the
|
||||||
|
## destructors at the same scheduler level as the API call back functions.
|
||||||
|
## Any of the API functions can be intercepted by the `=destroy()` hook at
|
||||||
|
## inconvenient times so that atomicity would be violated if the actual
|
||||||
|
## destruction took place in `=destroy()`.
|
||||||
|
##
|
||||||
|
## Note: In practice the `db.gq` queue should not have much more than one
|
||||||
|
## entry and mostly be empty.
|
||||||
|
const info = "gc()"
|
||||||
|
while 0 < base.gq.len:
|
||||||
|
var q: typeof base.gq
|
||||||
|
base.gq.swap q # now `=destroy()` may refill while destructing, below
|
||||||
|
for cMpt in q:
|
||||||
|
cMpt.finish(info).isOkOr:
|
||||||
|
debug logTxt info, `error`=error.errorPrint
|
||||||
|
continue # terminates `isOkOr()`
|
||||||
|
|
||||||
|
func mpt*(dsc: CoreDxMptRef): AristoDbRef =
|
||||||
|
dsc.AristoCoreDxMptRef.ctx.mpt
|
||||||
|
|
||||||
|
func rootID*(dsc: CoreDxMptRef): VertexID =
|
||||||
|
dsc.AristoCoreDxMptRef.ctx.root
|
||||||
|
|
||||||
|
# ---------------------
|
||||||
|
|
||||||
|
func txTop*(
|
||||||
|
base: AristoBaseRef;
|
||||||
|
info: static[string];
|
||||||
|
): CoreDbRc[AristoTxRef] =
|
||||||
|
base.adb.txTop.toRcImpl(base.parent, info)
|
||||||
|
|
||||||
|
func txBegin*(
|
||||||
|
base: AristoBaseRef;
|
||||||
|
info: static[string];
|
||||||
|
): CoreDbRc[AristoTxRef] =
|
||||||
|
base.adb.txBegin.toRcImpl(base.parent, info)
|
||||||
|
|
||||||
|
|
||||||
|
proc getHash*(
|
||||||
|
base: AristoBaseRef;
|
||||||
|
vid: CoreDbVidRef;
|
||||||
|
update: bool;
|
||||||
|
info: static[string];
|
||||||
|
): CoreDbRc[Hash256] =
|
||||||
|
let
|
||||||
|
db = base.parent
|
||||||
|
aVid = vid.to(VertexID)
|
||||||
|
|
||||||
|
if not aVid.isValid:
|
||||||
|
return ok(EMPTY_ROOT_HASH)
|
||||||
|
|
||||||
|
let mpt = vid.AristoCoreDbVid.ctx
|
||||||
|
if update:
|
||||||
|
? mpt.hashify.toVoidRcImpl(db, info, HashNotAvailable)
|
||||||
|
|
||||||
|
let key = block:
|
||||||
|
let rc = mpt.getKeyRc aVid
|
||||||
|
if rc.isErr:
|
||||||
|
doAssert rc.error in {GetKeyNotFound,GetKeyTempLocked}
|
||||||
|
return err(rc.error.toErrorImpl(db, info, HashNotAvailable))
|
||||||
|
rc.value
|
||||||
|
|
||||||
|
ok key.to(Hash256)
|
||||||
|
|
||||||
|
|
||||||
|
proc getVid*(
|
||||||
|
base: AristoBaseRef;
|
||||||
|
root: Hash256;
|
||||||
|
createOk: bool;
|
||||||
|
info: static[string];
|
||||||
|
): CoreDbRc[CoreDbVidRef] =
|
||||||
|
let
|
||||||
|
db = base.parent
|
||||||
|
adb = base.adb
|
||||||
|
|
||||||
|
if root == VOID_CODE_HASH:
|
||||||
|
return ok(db.bless AristoCoreDbVid())
|
||||||
|
|
||||||
|
block:
|
||||||
|
base.gc() # update pending changes
|
||||||
|
let rc = adb.hashify()
|
||||||
|
? adb.hashify.toVoidRcImpl(db, info, HashNotAvailable)
|
||||||
|
|
||||||
|
# Check whether hash is available as state root on main trie
|
||||||
|
block:
|
||||||
|
let rc = adb.getKeyRc VertexID(1)
|
||||||
|
if rc.isErr:
|
||||||
|
doAssert rc.error == GetKeyNotFound
|
||||||
|
elif rc.value == root.to(HashKey):
|
||||||
|
return ok(db.bless AristoCoreDbVid(aVid: VertexID(1), ctx: adb))
|
||||||
|
else:
|
||||||
|
discard
|
||||||
|
|
||||||
|
# Check whether the `root` is avalilable in backlog
|
||||||
|
block:
|
||||||
|
# ..
|
||||||
|
discard
|
||||||
|
|
||||||
|
# Check whether the root vertex should be created
|
||||||
|
if createOk:
|
||||||
|
return ok(db.bless AristoCoreDbVid(createOk: true, expHash: root))
|
||||||
|
|
||||||
|
err(aristo.GenericError.toErrorImpl(db, info, RootNotFound))
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Public constructors and related
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
proc newMptHandler*(
|
||||||
|
base: AristoBaseRef;
|
||||||
|
root: CoreDbVidRef;
|
||||||
|
prune: bool;
|
||||||
|
saveMode: CoreDbSaveFlags;
|
||||||
|
info: static[string];
|
||||||
|
): CoreDbRc[CoreDxMptRef] =
|
||||||
|
base.gc()
|
||||||
|
|
||||||
|
var rootID = root.to(VertexID)
|
||||||
|
if not rootID.isValid:
|
||||||
|
let rc = base.adb.getKeyRc VertexID(1)
|
||||||
|
if rc.isErr and rc.error == GetKeyNotFound:
|
||||||
|
rootID = VertexID(1)
|
||||||
|
|
||||||
|
let
|
||||||
|
db = base.parent
|
||||||
|
|
||||||
|
(mode, mpt) = block:
|
||||||
|
if saveMode == Companion:
|
||||||
|
(saveMode, ? base.adb.forkTop.toRcImpl(db, info))
|
||||||
|
elif base.adb.backend.isNil:
|
||||||
|
(Cached, base.adb)
|
||||||
|
else:
|
||||||
|
(saveMode, base.adb)
|
||||||
|
|
||||||
|
cMpt = AristoChildDbRef(
|
||||||
|
base: base,
|
||||||
|
root: rootID,
|
||||||
|
prune: prune,
|
||||||
|
mpt: mpt,
|
||||||
|
saveMode: mode)
|
||||||
|
|
||||||
|
dsc = AristoCoreDxMptRef(
|
||||||
|
ctx: cMpt,
|
||||||
|
methods: cMpt.mptMethods)
|
||||||
|
|
||||||
|
ok(db.bless dsc)
|
||||||
|
|
||||||
|
|
||||||
|
proc newAccHandler*(
|
||||||
|
base: AristoBaseRef;
|
||||||
|
prune: bool;
|
||||||
|
saveMode: CoreDbSaveFlags;
|
||||||
|
info: static[string];
|
||||||
|
): CoreDbRc[CoreDxAccRef] =
|
||||||
|
base.gc()
|
||||||
|
|
||||||
|
let
|
||||||
|
db = base.parent
|
||||||
|
|
||||||
|
(mode, mpt) = block:
|
||||||
|
if saveMode == Companion:
|
||||||
|
(saveMode, ? base.adb.forkTop.toRcImpl(db, info))
|
||||||
|
elif base.adb.backend.isNil:
|
||||||
|
(Cached, base.adb)
|
||||||
|
else:
|
||||||
|
(saveMode, base.adb)
|
||||||
|
|
||||||
|
cMpt = AristoChildDbRef(
|
||||||
|
base: base,
|
||||||
|
root: VertexID(1),
|
||||||
|
prune: prune,
|
||||||
|
mpt: mpt,
|
||||||
|
saveMode: mode)
|
||||||
|
|
||||||
|
ok(db.bless CoreDxAccRef(methods: cMpt.accMethods))
|
||||||
|
|
||||||
|
|
||||||
|
proc destroy*(base: AristoBaseRef; flush: bool) =
|
||||||
|
base.gc()
|
||||||
|
base.adb.finish(flush)
|
||||||
|
|
||||||
|
|
||||||
|
func init*(T: type AristoBaseRef; db: CoreDbRef; adb: AristoDbRef): T =
|
||||||
|
T(parent: db, adb: adb)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# End
|
||||||
|
# ------------------------------------------------------------------------------
|
|
@ -0,0 +1,285 @@
|
||||||
|
# Nimbus
|
||||||
|
# Copyright (c) 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
|
||||||
|
chronicles,
|
||||||
|
eth/common,
|
||||||
|
results,
|
||||||
|
../../../kvt,
|
||||||
|
../../../kvt/kvt_desc,
|
||||||
|
../../base,
|
||||||
|
../../base/base_desc,
|
||||||
|
./common_desc
|
||||||
|
|
||||||
|
type
|
||||||
|
KvtBaseRef* = ref object
|
||||||
|
parent: CoreDbRef ## Opaque top level descriptor
|
||||||
|
kdb: KvtDbRef ## Key-value table
|
||||||
|
gq: seq[KvtChildDbRef] ## Garbage queue, deferred disposal
|
||||||
|
|
||||||
|
KvtChildDbRef = ref KvtChildDbObj
|
||||||
|
KvtChildDbObj = object
|
||||||
|
## Sub-handle for triggering destructor when it goes out of scope
|
||||||
|
base: KvtBaseRef ## Local base descriptor
|
||||||
|
kvt: KvtDbRef ## Descriptor
|
||||||
|
saveMode: CoreDbSaveFlags ## When to store/discard
|
||||||
|
|
||||||
|
KvtCoreDxKvtRef = ref object of CoreDxKvtRef
|
||||||
|
## Some extendion to recover embedded state
|
||||||
|
ctx: KvtChildDbRef ## Embedded state, typical var name: `cKvt`
|
||||||
|
|
||||||
|
AristoCoreDbKvtBE* = ref object of CoreDbKvtBackendRef
|
||||||
|
kdb*: KvtDbRef
|
||||||
|
|
||||||
|
logScope:
|
||||||
|
topics = "kvt-hdl"
|
||||||
|
|
||||||
|
proc gc*(base: KvtBaseRef) {.gcsafe.}
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Private helpers
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template logTxt(info: static[string]): static[string] =
|
||||||
|
"CoreDb/kdb " & info
|
||||||
|
|
||||||
|
func toErrorImpl(
|
||||||
|
e: KvtError;
|
||||||
|
db: CoreDbRef;
|
||||||
|
info: string;
|
||||||
|
error = Unspecified;
|
||||||
|
): CoreDbErrorRef =
|
||||||
|
db.bless(error, AristoCoreDbError(
|
||||||
|
ctx: info,
|
||||||
|
isAristo: false,
|
||||||
|
kErr: e))
|
||||||
|
|
||||||
|
func toRcImpl[T](
|
||||||
|
rc: Result[T,KvtError];
|
||||||
|
db: CoreDbRef;
|
||||||
|
info: string;
|
||||||
|
error = Unspecified;
|
||||||
|
): CoreDbRc[T] =
|
||||||
|
if rc.isOk:
|
||||||
|
when T is void:
|
||||||
|
return ok()
|
||||||
|
else:
|
||||||
|
return ok(rc.value)
|
||||||
|
err rc.error.toErrorImpl(db, info, error)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Private call back functions (too large for keeping inline)
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
proc finish(
|
||||||
|
cKvt: KvtChildDbRef;
|
||||||
|
info: static[string];
|
||||||
|
): CoreDbRc[void] =
|
||||||
|
## key-value table destructor to be called automagically when the argument
|
||||||
|
## wrapper gets out of scope
|
||||||
|
let
|
||||||
|
base = cKvt.base
|
||||||
|
db = base.parent
|
||||||
|
|
||||||
|
result = ok()
|
||||||
|
|
||||||
|
if cKvt.kvt != base.kdb:
|
||||||
|
let rc = cKvt.kvt.forget()
|
||||||
|
if rc.isErr:
|
||||||
|
result = err(rc.error.toErrorImpl(db, info))
|
||||||
|
cKvt.kvt = KvtDbRef(nil) # Disables `=destroy()` action
|
||||||
|
|
||||||
|
if cKvt.saveMode == AutoSave:
|
||||||
|
if base.kdb.level == 0:
|
||||||
|
let rc = base.kdb.stow()
|
||||||
|
if rc.isErr:
|
||||||
|
result = err(rc.error.toErrorImpl(db, info))
|
||||||
|
|
||||||
|
proc `=destroy`(cKvt: var KvtChildDbObj) =
|
||||||
|
## Auto destructor
|
||||||
|
if not cKvt.kvt.isNil:
|
||||||
|
# Add to destructor batch queue unless direct reference
|
||||||
|
if cKvt.kvt != cKvt.base.kdb or
|
||||||
|
cKvt.saveMode == AutoSave:
|
||||||
|
cKvt.base.gq.add KvtChildDbRef(
|
||||||
|
base: cKvt.base,
|
||||||
|
kvt: cKvt.kvt,
|
||||||
|
saveMode: cKvt.saveMode)
|
||||||
|
|
||||||
|
# -------------------------------
|
||||||
|
|
||||||
|
proc kvtGet(
|
||||||
|
cKvt: KvtChildDbRef;
|
||||||
|
k: openArray[byte];
|
||||||
|
info: static[string];
|
||||||
|
): CoreDbRc[Blob] =
|
||||||
|
## Member of `CoreDbKvtFns`
|
||||||
|
let rc = cKvt.kvt.get(k)
|
||||||
|
if rc.isErr:
|
||||||
|
let db = cKvt.base.parent
|
||||||
|
if rc.error == GetNotFound:
|
||||||
|
return err(rc.error.toErrorImpl(db, info, KvtNotFound))
|
||||||
|
else:
|
||||||
|
return rc.toRcImpl(db, info)
|
||||||
|
ok(rc.value)
|
||||||
|
|
||||||
|
proc kvtPut(
|
||||||
|
cKvt: KvtChildDbRef;
|
||||||
|
k: openArray[byte];
|
||||||
|
v: openArray[byte];
|
||||||
|
info: static[string];
|
||||||
|
): CoreDbRc[void] =
|
||||||
|
let rc = cKvt.kvt.put(k,v)
|
||||||
|
if rc.isErr:
|
||||||
|
return err(rc.error.toErrorImpl(cKvt.base.parent, info))
|
||||||
|
ok()
|
||||||
|
|
||||||
|
proc kvtDel(
|
||||||
|
cKvt: KvtChildDbRef;
|
||||||
|
k: openArray[byte];
|
||||||
|
info: static[string];
|
||||||
|
): CoreDbRc[void] =
|
||||||
|
let rc = cKvt.kvt.del k
|
||||||
|
if rc.isErr:
|
||||||
|
return err(rc.error.toErrorImpl(cKvt.base.parent, info))
|
||||||
|
ok()
|
||||||
|
|
||||||
|
proc kvtHasKey(
|
||||||
|
cKvt: KvtChildDbRef;
|
||||||
|
k: openArray[byte];
|
||||||
|
info: static[string];
|
||||||
|
): CoreDbRc[bool] =
|
||||||
|
cKvt.kvt.hasKey(k).toRcImpl(cKvt.base.parent, info)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Private database methods function table
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
proc kvtMethods(cKvt: KvtChildDbRef): CoreDbKvtFns =
|
||||||
|
## Key-value database table handlers
|
||||||
|
let db = cKvt.base.parent
|
||||||
|
CoreDbKvtFns(
|
||||||
|
backendFn: proc(): CoreDbKvtBackendRef =
|
||||||
|
db.bless(AristoCoreDbKvtBE(kdb: cKvt.kvt)),
|
||||||
|
|
||||||
|
getFn: proc(k: openArray[byte]): CoreDbRc[Blob] =
|
||||||
|
cKvt.kvtGet(k, "getFn()"),
|
||||||
|
|
||||||
|
delFn: proc(k: openArray[byte]): CoreDbRc[void] =
|
||||||
|
cKvt.kvtDel(k, "delFn()"),
|
||||||
|
|
||||||
|
putFn: proc(k: openArray[byte]; v: openArray[byte]): CoreDbRc[void] =
|
||||||
|
cKvt.kvtPut(k, v, "putFn()"),
|
||||||
|
|
||||||
|
hasKeyFn: proc(k: openArray[byte]): CoreDbRc[bool] =
|
||||||
|
cKvt.kvtHasKey(k, "hasKeyFn()"),
|
||||||
|
|
||||||
|
destroyFn: proc(saveMode: CoreDbSaveFlags): CoreDbRc[void] =
|
||||||
|
cKvt.base.gc()
|
||||||
|
cKvt.finish("destroyFn()"),
|
||||||
|
|
||||||
|
pairsIt: iterator(): (Blob, Blob) =
|
||||||
|
discard)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Public handlers and helpers
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func toVoidRc*[T](
|
||||||
|
rc: Result[T,KvtError];
|
||||||
|
db: CoreDbRef;
|
||||||
|
info: string;
|
||||||
|
error = Unspecified;
|
||||||
|
): CoreDbRc[void] =
|
||||||
|
if rc.isOk:
|
||||||
|
return ok()
|
||||||
|
err rc.error.toErrorImpl(db, info, error)
|
||||||
|
|
||||||
|
proc gc*(base: KvtBaseRef) =
|
||||||
|
## Run deferred destructors when it is safe. It is needed to run the
|
||||||
|
## destructors at the same scheduler level as the API call back functions.
|
||||||
|
## Any of the API functions can be intercepted by the `=destroy()` hook at
|
||||||
|
## inconvenient times so that atomicity would be violated if the actual
|
||||||
|
## destruction took place in `=destroy()`.
|
||||||
|
##
|
||||||
|
## Note: In practice the garbage queue should not have much more than one
|
||||||
|
## entry and mostly be empty.
|
||||||
|
const info = "gc()"
|
||||||
|
while 0 < base.gq.len:
|
||||||
|
var q: typeof base.gq
|
||||||
|
base.gq.swap q # now `=destroy()` may refill while destructing, below
|
||||||
|
for cKvt in q:
|
||||||
|
cKvt.finish(info).isOkOr:
|
||||||
|
debug logTxt info, `error`=error.errorPrint
|
||||||
|
continue # terminates `isOkOr()`
|
||||||
|
|
||||||
|
func kvt*(dsc: CoreDxKvtRef): KvtDbRef =
|
||||||
|
dsc.KvtCoreDxKvtRef.ctx.kvt
|
||||||
|
|
||||||
|
# ---------------------
|
||||||
|
|
||||||
|
func txTop*(
|
||||||
|
base: KvtBaseRef;
|
||||||
|
info: static[string];
|
||||||
|
): CoreDbRc[KvtTxRef] =
|
||||||
|
base.kdb.txTop.toRcImpl(base.parent, info)
|
||||||
|
|
||||||
|
func txBegin*(
|
||||||
|
base: KvtBaseRef;
|
||||||
|
info: static[string];
|
||||||
|
): CoreDbRc[KvtTxRef] =
|
||||||
|
base.kdb.txBegin.toRcImpl(base.parent, info)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Public constructors and related
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
proc newKvtHandler*(
|
||||||
|
base: KvtBaseRef;
|
||||||
|
saveMode: CoreDbSaveFlags;
|
||||||
|
info: static[string];
|
||||||
|
): CoreDbRc[CoreDxKvtRef] =
|
||||||
|
base.gc()
|
||||||
|
|
||||||
|
let
|
||||||
|
db = base.parent
|
||||||
|
|
||||||
|
(mode, kvt) = block:
|
||||||
|
if saveMode == Companion:
|
||||||
|
(saveMode, ? base.kdb.forkTop.toRcImpl(db, info))
|
||||||
|
elif base.kdb.backend.isNil:
|
||||||
|
(Cached, base.kdb)
|
||||||
|
else:
|
||||||
|
(saveMode, base.kdb)
|
||||||
|
|
||||||
|
cKvt = KvtChildDbRef(
|
||||||
|
base: base,
|
||||||
|
kvt: kvt,
|
||||||
|
saveMode: saveMode)
|
||||||
|
|
||||||
|
dsc = KvtCoreDxKvtRef(
|
||||||
|
ctx: cKvt,
|
||||||
|
methods: cKvt.kvtMethods)
|
||||||
|
|
||||||
|
ok(db.bless dsc)
|
||||||
|
|
||||||
|
|
||||||
|
proc destroy*(base: KvtBaseRef; flush: bool) =
|
||||||
|
base.gc()
|
||||||
|
base.kdb.finish(flush)
|
||||||
|
|
||||||
|
func init*(T: type KvtBaseRef; db: CoreDbRef; kdb: KvtDbRef): T =
|
||||||
|
T(parent: db, kdb: kdb)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# End
|
||||||
|
# ------------------------------------------------------------------------------
|
|
@ -0,0 +1,38 @@
|
||||||
|
# Nimbus
|
||||||
|
# Copyright (c) 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
|
||||||
|
eth/common,
|
||||||
|
results,
|
||||||
|
"../.."/[aristo, aristo/aristo_persistent, kvt, kvt/kvt_persistent],
|
||||||
|
../base,
|
||||||
|
./aristo_db
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Public constructor
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
proc newAristoRocksDbCoreDbRef*(path: string; qlr: QidLayoutRef): CoreDbRef =
|
||||||
|
AristoDbRocks.init(
|
||||||
|
kvt_persistent.RdbBackendRef,
|
||||||
|
aristo_persistent.RdbBackendRef,
|
||||||
|
path, qlr)
|
||||||
|
|
||||||
|
proc newAristoRocksDbCoreDbRef*(path: string): CoreDbRef =
|
||||||
|
AristoDbRocks.init(
|
||||||
|
kvt_persistent.RdbBackendRef,
|
||||||
|
aristo_persistent.RdbBackendRef,
|
||||||
|
path)
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# End
|
||||||
|
# ------------------------------------------------------------------------------
|
|
@ -24,9 +24,12 @@ type
|
||||||
Ooops
|
Ooops
|
||||||
LegacyDbMemory
|
LegacyDbMemory
|
||||||
LegacyDbPersistent
|
LegacyDbPersistent
|
||||||
|
AristoDbMemory ## Memory backend emulator
|
||||||
|
AristoDbRocks ## RocksDB backend
|
||||||
|
AristoDbVoid ## No backend
|
||||||
|
|
||||||
const
|
const
|
||||||
CoreDbPersistentTypes* = {LegacyDbPersistent}
|
CoreDbPersistentTypes* = {LegacyDbPersistent, AristoDbRocks}
|
||||||
|
|
||||||
type
|
type
|
||||||
CoreDbRc*[T] = Result[T,CoreDbErrorRef]
|
CoreDbRc*[T] = Result[T,CoreDbErrorRef]
|
||||||
|
|
|
@ -14,7 +14,7 @@ import
|
||||||
std/options,
|
std/options,
|
||||||
eth/[common, trie/db],
|
eth/[common, trie/db],
|
||||||
../aristo,
|
../aristo,
|
||||||
./backend/legacy_db,
|
./backend/[aristo_db, legacy_db],
|
||||||
./base,
|
./base,
|
||||||
#./core_apps_legacy as core_apps
|
#./core_apps_legacy as core_apps
|
||||||
./core_apps_newapi as core_apps
|
./core_apps_newapi as core_apps
|
||||||
|
@ -135,9 +135,34 @@ proc newCoreDbRef*(
|
||||||
when dbType == LegacyDbMemory:
|
when dbType == LegacyDbMemory:
|
||||||
newLegacyMemoryCoreDbRef()
|
newLegacyMemoryCoreDbRef()
|
||||||
|
|
||||||
|
elif dbType == AristoDbMemory:
|
||||||
|
newAristoMemoryCoreDbRef()
|
||||||
|
|
||||||
|
elif dbType == AristoDbVoid:
|
||||||
|
newAristoVoidCoreDbRef()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
{.error: "Unsupported constructor " & $dbType & ".newCoreDbRef()".}
|
{.error: "Unsupported constructor " & $dbType & ".newCoreDbRef()".}
|
||||||
|
|
||||||
|
proc newCoreDbRef*(
|
||||||
|
dbType: static[CoreDbType]; # Database type symbol
|
||||||
|
qidLayout: QidLayoutRef; # `Aristo` only
|
||||||
|
): CoreDbRef =
|
||||||
|
## Constructor for volatile/memory type DB
|
||||||
|
##
|
||||||
|
## Note: Using legacy notation `newCoreDbRef()` rather than
|
||||||
|
## `CoreDbRef.init()` because of compiler coughing.
|
||||||
|
##
|
||||||
|
when dbType == AristoDbMemory:
|
||||||
|
newAristoMemoryCoreDbRef(DefaultQidLayoutRef)
|
||||||
|
|
||||||
|
elif dbType == AristoDbVoid:
|
||||||
|
newAristoVoidCoreDbRef()
|
||||||
|
|
||||||
|
else:
|
||||||
|
{.error: "Unsupported constructor " & $dbType & ".newCoreDbRef()" &
|
||||||
|
" with qidLayout argument".}
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Public template wrappers
|
# Public template wrappers
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
|
|
||||||
import
|
import
|
||||||
../aristo,
|
../aristo,
|
||||||
./backend/[legacy_rocksdb],
|
./backend/[aristo_rocksdb, legacy_rocksdb],
|
||||||
./memory_only
|
./memory_only
|
||||||
|
|
||||||
export
|
export
|
||||||
|
@ -35,7 +35,26 @@ proc newCoreDbRef*(
|
||||||
when dbType == LegacyDbPersistent:
|
when dbType == LegacyDbPersistent:
|
||||||
newLegacyPersistentCoreDbRef path
|
newLegacyPersistentCoreDbRef path
|
||||||
|
|
||||||
|
elif dbType == AristoDbRocks:
|
||||||
|
newAristoRocksDbCoreDbRef path
|
||||||
|
|
||||||
else:
|
else:
|
||||||
{.error: "Unsupported dbType for persistent newCoreDbRef()".}
|
{.error: "Unsupported dbType for persistent newCoreDbRef()".}
|
||||||
|
|
||||||
|
proc newCoreDbRef*(
|
||||||
|
dbType: static[CoreDbType]; # Database type symbol
|
||||||
|
path: string; # Storage path for database
|
||||||
|
qidLayout: QidLayoutRef; # Optional for `Aristo`, ignored by others
|
||||||
|
): CoreDbRef =
|
||||||
|
## Constructor for persistent type DB
|
||||||
|
##
|
||||||
|
## Note: Using legacy notation `newCoreDbRef()` rather than
|
||||||
|
## `CoreDbRef.init()` because of compiler coughing.
|
||||||
|
when dbType == AristoDbRocks:
|
||||||
|
newAristoRocksDbCoreDbRef(path, qlr)
|
||||||
|
|
||||||
|
else:
|
||||||
|
{.error: "Unsupported dbType for persistent newCoreDbRef()" &
|
||||||
|
" with qidLayout argument".}
|
||||||
|
|
||||||
# End
|
# End
|
||||||
|
|
Loading…
Reference in New Issue