2022-10-14 16:40:32 +00:00
|
|
|
# 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.
|
|
|
|
|
2023-03-03 20:01:59 +00:00
|
|
|
{.push raises: [].}
|
|
|
|
|
2022-10-14 16:40:32 +00:00
|
|
|
import
|
2023-03-17 14:46:50 +00:00
|
|
|
std/tables,
|
2022-10-14 16:40:32 +00:00
|
|
|
chronicles,
|
2022-11-08 18:56:04 +00:00
|
|
|
eth/[common, p2p, trie/db, trie/nibbles],
|
2022-10-20 16:59:54 +00:00
|
|
|
../../../../db/[select_backend, storage_types],
|
2023-02-15 10:14:40 +00:00
|
|
|
../../../protocol,
|
2022-10-14 16:40:32 +00:00
|
|
|
../../range_desc,
|
2023-04-24 20:24:07 +00:00
|
|
|
"."/[hexary_desc, hexary_error, hexary_import, hexary_nearby, hexary_paths,
|
|
|
|
rocky_bulk_load]
|
2022-10-14 16:40:32 +00:00
|
|
|
|
|
|
|
logScope:
|
|
|
|
topics = "snap-db"
|
|
|
|
|
2022-10-20 16:59:54 +00:00
|
|
|
const
|
2023-04-24 20:24:07 +00:00
|
|
|
extraTraceMessages = false # or true
|
2022-11-08 18:56:04 +00:00
|
|
|
|
2022-10-20 16:59:54 +00:00
|
|
|
RockyBulkCache* = "accounts.sst"
|
|
|
|
## Name of temporary file to accomodate SST records for `rocksdb`
|
|
|
|
|
2022-10-14 16:40:32 +00:00
|
|
|
type
|
|
|
|
SnapDbRef* = ref object
|
|
|
|
## Global, re-usable descriptor
|
|
|
|
keyMap: Table[RepairKey,uint] ## For debugging only (will go away)
|
|
|
|
db: TrieDatabaseRef ## General database
|
|
|
|
rocky: RocksStoreRef ## Set if rocksdb is available
|
|
|
|
|
|
|
|
SnapDbBaseRef* = ref object of RootRef
|
|
|
|
## Session descriptor
|
2022-11-08 18:56:04 +00:00
|
|
|
xDb: HexaryTreeDbRef ## Hexary database, memory based
|
2022-10-14 16:40:32 +00:00
|
|
|
base: SnapDbRef ## Back reference to common parameters
|
|
|
|
root*: NodeKey ## Session DB root node key
|
|
|
|
|
2023-04-24 20:24:07 +00:00
|
|
|
when extraTraceMessages:
|
|
|
|
import hexary_debug
|
|
|
|
|
2022-10-14 16:40:32 +00:00
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# Private debugging helpers
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
|
2023-03-17 14:46:50 +00:00
|
|
|
template noKeyError(info: static[string]; code: untyped) =
|
2022-10-14 16:40:32 +00:00
|
|
|
try:
|
|
|
|
code
|
|
|
|
except KeyError as e:
|
|
|
|
raiseAssert "Not possible (" & info & "): " & e.msg
|
|
|
|
|
2023-03-17 14:46:50 +00:00
|
|
|
proc keyPp(a: RepairKey; pv: SnapDbRef): string =
|
|
|
|
if a.isZero:
|
|
|
|
return "ø"
|
|
|
|
if not pv.keyMap.hasKey(a):
|
|
|
|
pv.keyMap[a] = pv.keyMap.len.uint + 1
|
|
|
|
result = if a.isNodeKey: "$" else: "@"
|
|
|
|
noKeyError("pp(RepairKey)"):
|
|
|
|
result &= $pv.keyMap[a]
|
2022-10-14 16:40:32 +00:00
|
|
|
|
2022-10-20 16:59:54 +00:00
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# Private helper
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
proc clearRockyCacheFile(rocky: RocksStoreRef): bool =
|
|
|
|
if not rocky.isNil:
|
|
|
|
# A cache file might hang about from a previous crash
|
|
|
|
try:
|
|
|
|
discard rocky.clearCacheFile(RockyBulkCache)
|
|
|
|
return true
|
|
|
|
except OSError as e:
|
|
|
|
error "Cannot clear rocksdb cache", exception=($e.name), msg=e.msg
|
|
|
|
|
2022-10-14 16:40:32 +00:00
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# Public constructor
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
proc init*(
|
|
|
|
T: type SnapDbRef;
|
|
|
|
db: TrieDatabaseRef
|
|
|
|
): T =
|
|
|
|
## Main object constructor
|
|
|
|
T(db: db)
|
|
|
|
|
|
|
|
proc init*(
|
|
|
|
T: type SnapDbRef;
|
|
|
|
db: ChainDb
|
|
|
|
): T =
|
|
|
|
## Variant of `init()` allowing bulk import on rocksdb backend
|
|
|
|
result = T(db: db.trieDB, rocky: db.rocksStoreRef)
|
2022-10-20 16:59:54 +00:00
|
|
|
if not result.rocky.clearRockyCacheFile():
|
2022-10-14 16:40:32 +00:00
|
|
|
result.rocky = nil
|
|
|
|
|
|
|
|
proc init*(
|
|
|
|
T: type HexaryTreeDbRef;
|
|
|
|
pv: SnapDbRef;
|
|
|
|
): T =
|
|
|
|
## Constructor for inner hexary trie database
|
|
|
|
let xDb = HexaryTreeDbRef()
|
2023-03-17 14:46:50 +00:00
|
|
|
xDb.keyPp = proc(key: RepairKey): string = key.keyPp(pv) # will go away
|
2022-10-14 16:40:32 +00:00
|
|
|
return xDb
|
|
|
|
|
|
|
|
proc init*(
|
|
|
|
T: type HexaryTreeDbRef;
|
|
|
|
ps: SnapDbBaseRef;
|
|
|
|
): T =
|
|
|
|
## Constructor variant
|
|
|
|
HexaryTreeDbRef.init(ps.base)
|
|
|
|
|
|
|
|
# ---------------
|
|
|
|
|
|
|
|
proc init*(
|
|
|
|
ps: SnapDbBaseRef;
|
|
|
|
pv: SnapDbRef;
|
|
|
|
root: NodeKey;
|
2022-11-08 18:56:04 +00:00
|
|
|
) =
|
2022-10-14 16:40:32 +00:00
|
|
|
## Session base constructor
|
|
|
|
ps.base = pv
|
|
|
|
ps.root = root
|
|
|
|
ps.xDb = HexaryTreeDbRef.init(pv)
|
|
|
|
|
|
|
|
proc init*(
|
|
|
|
T: type SnapDbBaseRef;
|
|
|
|
ps: SnapDbBaseRef;
|
|
|
|
root: NodeKey;
|
2022-11-08 18:56:04 +00:00
|
|
|
): T =
|
2022-10-14 16:40:32 +00:00
|
|
|
## Variant of session base constructor
|
|
|
|
new result
|
2022-10-19 10:04:06 +00:00
|
|
|
result.init(ps.base, root)
|
2022-10-14 16:40:32 +00:00
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# Public getters
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
proc hexaDb*(ps: SnapDbBaseRef): HexaryTreeDbRef =
|
|
|
|
## Getter, low level access to underlying session DB
|
|
|
|
ps.xDb
|
|
|
|
|
|
|
|
proc rockDb*(ps: SnapDbBaseRef): RocksStoreRef =
|
|
|
|
## Getter, low level access to underlying persistent rock DB interface
|
|
|
|
ps.base.rocky
|
|
|
|
|
2023-04-21 21:11:04 +00:00
|
|
|
proc rockDb*(pv: SnapDbRef): RocksStoreRef =
|
|
|
|
## Getter variant
|
|
|
|
pv.rocky
|
|
|
|
|
2022-10-14 16:40:32 +00:00
|
|
|
proc kvDb*(ps: SnapDbBaseRef): TrieDatabaseRef =
|
|
|
|
## Getter, low level access to underlying persistent key-value DB
|
|
|
|
ps.base.db
|
|
|
|
|
|
|
|
proc kvDb*(pv: SnapDbRef): TrieDatabaseRef =
|
|
|
|
## Getter, low level access to underlying persistent key-value DB
|
|
|
|
pv.db
|
|
|
|
|
2022-10-20 16:59:54 +00:00
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# Public functions, select sub-tables for persistent storage
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
|
2023-04-14 22:28:57 +00:00
|
|
|
proc toBlockHeaderKey*(a: Hash256): ByteArray33 =
|
|
|
|
a.genericHashKey.data
|
|
|
|
|
|
|
|
proc toBlockNumberKey*(a: BlockNumber): ByteArray33 =
|
|
|
|
static:
|
|
|
|
doAssert 32 == sizeof BlockNumber # needed in `blockNumberToHashKey()`
|
|
|
|
a.blockNumberToHashKey.data
|
|
|
|
|
2023-04-21 21:11:04 +00:00
|
|
|
proc toContractHashKey*(a: NodeKey): ByteArray33 =
|
|
|
|
a.to(Hash256).contractHashKey.data
|
|
|
|
|
2023-04-14 22:28:57 +00:00
|
|
|
when false:
|
|
|
|
proc toAccountsKey*(a: NodeKey): ByteArray33 =
|
|
|
|
a.ByteArray32.snapSyncAccountKey.data
|
|
|
|
|
|
|
|
proc toStorageSlotsKey*(a: NodeKey): ByteArray33 =
|
|
|
|
a.ByteArray32.snapSyncStorageSlotKey.data
|
|
|
|
else:
|
|
|
|
proc toAccountsKey*(a: NodeKey): ByteArray32 =
|
|
|
|
a.ByteArray32
|
|
|
|
|
|
|
|
proc toStorageSlotsKey*(a: NodeKey): ByteArray32 =
|
|
|
|
a.ByteArray32
|
2022-10-20 16:59:54 +00:00
|
|
|
|
2022-11-16 23:51:06 +00:00
|
|
|
proc toStateRootKey*(a: NodeKey): ByteArray33 =
|
|
|
|
a.ByteArray32.snapSyncStateRootKey.data
|
|
|
|
|
2022-10-20 16:59:54 +00:00
|
|
|
template toOpenArray*(k: ByteArray32): openArray[byte] =
|
|
|
|
k.toOpenArray(0, 31)
|
|
|
|
|
|
|
|
template toOpenArray*(k: ByteArray33): openArray[byte] =
|
|
|
|
k.toOpenArray(0, 32)
|
|
|
|
|
2022-10-14 16:40:32 +00:00
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# Public functions
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
proc dbBackendRocksDb*(pv: SnapDbRef): bool =
|
|
|
|
## Returns `true` if rocksdb features are available
|
|
|
|
not pv.rocky.isNil
|
|
|
|
|
|
|
|
proc dbBackendRocksDb*(ps: SnapDbBaseRef): bool =
|
|
|
|
## Returns `true` if rocksdb features are available
|
|
|
|
not ps.base.rocky.isNil
|
|
|
|
|
|
|
|
proc mergeProofs*(
|
2023-03-07 14:23:22 +00:00
|
|
|
xDb: HexaryTreeDbRef; ## Session database
|
|
|
|
root: NodeKey; ## State root
|
2023-02-15 10:14:40 +00:00
|
|
|
proof: seq[SnapProof]; ## Node records
|
2023-03-07 14:23:22 +00:00
|
|
|
peer = Peer(); ## For log messages
|
2022-10-14 16:40:32 +00:00
|
|
|
freeStandingOk = false; ## Remove freestanding nodes
|
2022-11-28 09:03:23 +00:00
|
|
|
): Result[void,HexaryError]
|
2023-01-30 22:10:23 +00:00
|
|
|
{.gcsafe, raises: [RlpError,KeyError].} =
|
2022-10-14 16:40:32 +00:00
|
|
|
## Import proof records (as received with snap message) into a hexary trie
|
|
|
|
## of the repair table. These hexary trie records can be extended to a full
|
|
|
|
## trie at a later stage and used for validating account data.
|
|
|
|
var
|
|
|
|
nodes: HashSet[RepairKey]
|
2023-03-07 14:23:22 +00:00
|
|
|
refs = @[root.to(RepairKey)].toHashSet
|
2022-10-14 16:40:32 +00:00
|
|
|
|
|
|
|
for n,rlpRec in proof:
|
2023-03-07 14:23:22 +00:00
|
|
|
let report = xDb.hexaryImport(rlpRec.to(Blob), nodes, refs)
|
2023-04-24 20:24:07 +00:00
|
|
|
if report.error != HexaryError(0):
|
2022-10-14 16:40:32 +00:00
|
|
|
let error = report.error
|
|
|
|
trace "mergeProofs()", peer, item=n, proofs=proof.len, error
|
|
|
|
return err(error)
|
|
|
|
|
|
|
|
# Remove free standing nodes (if any)
|
|
|
|
if 0 < nodes.len:
|
|
|
|
let rest = nodes - refs
|
|
|
|
if 0 < rest.len:
|
|
|
|
if freeStandingOk:
|
|
|
|
trace "mergeProofs() detected unrelated nodes", peer, nodes=nodes.len
|
|
|
|
discard
|
|
|
|
else:
|
|
|
|
# Delete unreferenced nodes
|
|
|
|
for nodeKey in nodes:
|
2023-03-07 14:23:22 +00:00
|
|
|
xDb.tab.del(nodeKey)
|
2022-10-14 16:40:32 +00:00
|
|
|
trace "mergeProofs() ignoring unrelated nodes", peer, nodes=nodes.len
|
|
|
|
|
|
|
|
ok()
|
|
|
|
|
2022-11-08 18:56:04 +00:00
|
|
|
|
|
|
|
proc verifyLowerBound*(
|
2023-03-07 14:23:22 +00:00
|
|
|
xDb: HexaryTreeDbRef; ## Session database
|
|
|
|
root: NodeKey; ## State root
|
2022-11-08 18:56:04 +00:00
|
|
|
base: NodeTag; ## Before or at first account entry in `data`
|
2023-03-07 14:23:22 +00:00
|
|
|
first: NodeTag; ## First account/storage key
|
|
|
|
peer = Peer(); ## For log messages
|
2022-11-28 09:03:23 +00:00
|
|
|
): Result[void,HexaryError]
|
2023-02-14 23:38:33 +00:00
|
|
|
{.gcsafe, raises: [CatchableError].} =
|
2022-11-08 18:56:04 +00:00
|
|
|
## Verify that `base` is to the left of the first leaf entry and there is
|
|
|
|
## nothing in between.
|
2022-12-06 17:35:56 +00:00
|
|
|
var error: HexaryError
|
2022-11-08 18:56:04 +00:00
|
|
|
|
2023-03-07 14:23:22 +00:00
|
|
|
let rc = base.hexaryNearbyRight(root, xDb)
|
2022-12-06 17:35:56 +00:00
|
|
|
if rc.isErr:
|
|
|
|
error = rc.error
|
|
|
|
elif first == rc.value:
|
|
|
|
return ok()
|
|
|
|
else:
|
|
|
|
error = LowerBoundProofError
|
2022-11-08 18:56:04 +00:00
|
|
|
|
|
|
|
when extraTraceMessages:
|
2022-12-06 17:35:56 +00:00
|
|
|
trace "verifyLowerBound()", peer, base=base.to(NodeKey).pp,
|
2022-11-08 18:56:04 +00:00
|
|
|
first=first.to(NodeKey).pp, error
|
|
|
|
err(error)
|
|
|
|
|
2022-12-06 17:35:56 +00:00
|
|
|
|
2022-11-08 18:56:04 +00:00
|
|
|
proc verifyNoMoreRight*(
|
2023-03-07 14:23:22 +00:00
|
|
|
xDb: HexaryTreeDbRef; ## Session database
|
|
|
|
root: NodeKey; ## State root
|
2022-11-08 18:56:04 +00:00
|
|
|
base: NodeTag; ## Before or at first account entry in `data`
|
2023-03-07 14:23:22 +00:00
|
|
|
peer = Peer(); ## For log messages
|
2022-11-28 09:03:23 +00:00
|
|
|
): Result[void,HexaryError]
|
2023-02-14 23:38:33 +00:00
|
|
|
{.gcsafe, raises: [CatchableError].} =
|
2022-11-08 18:56:04 +00:00
|
|
|
## Verify that there is are no more leaf entries to the right of and
|
|
|
|
## including `base`.
|
2023-03-25 10:44:48 +00:00
|
|
|
var error: HexaryError
|
|
|
|
|
|
|
|
let rc = base.hexaryNearbyRight(root, xDb)
|
|
|
|
if rc.isOk:
|
|
|
|
error = LowerBoundProofError
|
|
|
|
elif rc.error != NearbyBeyondRange:
|
|
|
|
error = rc.error
|
|
|
|
else:
|
2022-11-08 18:56:04 +00:00
|
|
|
return ok()
|
|
|
|
|
|
|
|
when extraTraceMessages:
|
2023-03-25 10:44:48 +00:00
|
|
|
trace "verifyLeftmostBound()", peer, base, root, error
|
2022-11-08 18:56:04 +00:00
|
|
|
err(error)
|
|
|
|
|
2022-10-14 16:40:32 +00:00
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# End
|
|
|
|
# ------------------------------------------------------------------------------
|