nimbus-eth1/nimbus/db/aristo/aristo_layer.nim

145 lines
4.6 KiB
Nim

# nimbus-eth1
# Copyright (c) 2021 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
# http://www.apache.org/licenses/LICENSE-2.0)
# * MIT license ([LICENSE-MIT](LICENSE-MIT) or
# http://opensource.org/licenses/MIT)
# at your option. This file may not be copied, modified, or distributed
# except according to those terms.
## Aristo DB -- Patricia Trie layer management
## ===========================================
##
import
std/[sequtils, tables],
stew/results,
"."/[aristo_desc, aristo_get, aristo_vid]
# ------------------------------------------------------------------------------
# Private functions
# ------------------------------------------------------------------------------
proc cpy(layer: AristoLayerRef): AristoLayerRef =
new result
result[] = layer[]
# ------------------------------------------------------------------------------
# Public functions
# ------------------------------------------------------------------------------
proc retool*(
db: AristoDbRef;
flushStack = false;
): Result[void,AristoError] =
## This function re-initialises the top layer cache to its default value. It
## acts as sort of a `rollback()` without a transaction.
##
if 0 < db.stack.len and not flushStack:
db.top = db.stack[^1].cpy
else:
# Initialise new top layer from the backend
if db.backend.isNil:
db.top = AristoLayerRef()
else:
let rc = db.backend.getIdgFn()
if rc.isErr:
return err(rc.error)
db.top = AristoLayerRef(vGen: rc.value)
if flushStack:
db.stack.setLen(0)
proc push*(db: var AristoDbRef) =
## Save the current cache state on the stack and continue working on a copy
## of that state.
##
db.stack.add db.top.cpy
proc pop*(
db: AristoDbRef;
merge = true;
): Result[void,(VertexID,AristoError)] =
## Reduce the cache state stack. If the argument `merge` is set `true`, the
## current top layer cache remains active and the top item of the stack is
## discarded. Otherwise if `merge` is set `false`, top item of the stack is
## popped onto current top layer cache (so replacing it.)
##
if db.stack.len == 0:
return err((VertexID(0),PopStackUnderflow))
if not merge:
# Roll back to parent layer state.
db.top = db.stack[^1]
db.stack.setLen(db.stack.len-1)
ok()
proc save*(
db: AristoDbRef; # Database to be updated
): Result[void,(VertexID,AristoError)] =
## Save the top layer cache onto the persistent database. There is no check
## whether the current layer is fully consistent as a Merkle Patricia Tree.
## It is advised to run `hashify()` on the top layer before calling `save()`.
##
## After successful storage, all parent layers are cleared as well as the
## the top layer cache.
##
## Upon successful return, the previous state of the backend data is saved
## as a new entry in `history` field of the argument descriptor `db`.
##
let be = db.backend
if be.isNil:
return err((VertexID(0),SaveBackendMissing))
# Get Merkle hash for state root
let key = db.getKey VertexID(1)
if not key.isValid:
return err((VertexID(1),SaveStateRootMissing))
let hst = AristoChangeLogRef(root: key) # Change history, previous state
# Record changed `Leaf` nodes into the history table
for (lky,vid) in db.top.lTab.pairs:
if vid.isValid:
# Get previous payload for this vertex
let rc = db.getVtxBackend vid
if rc.isErr:
if rc.error != GetVtxNotFound:
return err((vid,rc.error)) # Stop
hst.leafs[lky] = PayloadRef(nil) # So this is a new leaf vertex
elif rc.value.vType == Leaf:
hst.leafs[lky] = rc.value.lData # Record previous payload
else:
return err((vid,SaveLeafVidRepurposed)) # Was re-puropsed
else:
hst.leafs[lky] = PayloadRef(nil) # New leaf vertex
# Compact recycled nodes
db.vidReorg()
# Save structural and other table entries
let txFrame = be.putBegFn()
be.putVtxFn(txFrame, db.top.sTab.pairs.toSeq)
be.putKeyFn(txFrame, db.top.kMap.pairs.toSeq.mapIt((it[0],it[1].key)))
be.putIdgFn(txFrame, db.top.vGen)
let w = be.putEndFn txFrame
if w != AristoError(0):
return err((VertexID(0),w))
# Delete stack and clear top
db.stack.setLen(0)
db.top = AristoLayerRef(vGen: db.top.vGen)
# Save history
db.history.add hst
ok()
# ------------------------------------------------------------------------------
# End
# ------------------------------------------------------------------------------