181 lines
6.2 KiB
Nim
181 lines
6.2 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.
|
||
|
|
||
|
{.push raises: [].}
|
||
|
|
||
|
import
|
||
|
std/[algorithm, sequtils, tables],
|
||
|
eth/[common, trie/nibbles],
|
||
|
stew/results,
|
||
|
../../range_desc,
|
||
|
"."/[hexary_debug, hexary_desc, hexary_error, hexary_paths, snapdb_desc]
|
||
|
|
||
|
# ------------------------------------------------------------------------------
|
||
|
# Private debugging helpers
|
||
|
# ------------------------------------------------------------------------------
|
||
|
|
||
|
template noPpError(info: static[string]; code: untyped) =
|
||
|
try:
|
||
|
code
|
||
|
except ValueError as e:
|
||
|
raiseAssert "Inconveivable (" & info & "): " & e.msg
|
||
|
except KeyError as e:
|
||
|
raiseAssert "Not possible (" & info & "): " & e.msg
|
||
|
except CatchableError as e:
|
||
|
raiseAssert "Ooops (" & info & ") " & $e.name & ": " & e.msg
|
||
|
|
||
|
# ------------------------------------------------------------------------------
|
||
|
# Private helpers
|
||
|
# ------------------------------------------------------------------------------
|
||
|
|
||
|
proc convertTo(data: openArray[byte]; T: type Hash256): T =
|
||
|
discard result.data.NodeKey.init(data) # size error => zero
|
||
|
|
||
|
template noKeyError(info: static[string]; code: untyped) =
|
||
|
try:
|
||
|
code
|
||
|
except KeyError as e:
|
||
|
raiseAssert "Not possible (" & info & "): " & e.msg
|
||
|
|
||
|
template noExceptionOops(info: static[string]; code: untyped) =
|
||
|
try:
|
||
|
code
|
||
|
except KeyError as e:
|
||
|
raiseAssert "Not possible -- " & info & ": " & e.msg
|
||
|
except RlpError:
|
||
|
return err(RlpEncoding)
|
||
|
except CatchableError as e:
|
||
|
return err(AccountNotFound)
|
||
|
|
||
|
# ------------------------------------------------------------------------------
|
||
|
# Public functions, pretty printing
|
||
|
# ------------------------------------------------------------------------------
|
||
|
|
||
|
proc pp*(a: RepairKey; ps: SnapDbBaseRef): string =
|
||
|
if not ps.isNil:
|
||
|
let toKey = ps.hexaDb.keyPp
|
||
|
if not toKey.isNil:
|
||
|
try:
|
||
|
return a.toKey
|
||
|
except CatchableError:
|
||
|
discard
|
||
|
$a.ByteArray33
|
||
|
|
||
|
proc pp*(a: NodeKey; ps: SnapDbBaseRef): string =
|
||
|
if not ps.isNil:
|
||
|
let toKey = ps.hexaDb.keyPp
|
||
|
if not toKey.isNil:
|
||
|
try:
|
||
|
return a.to(RepairKey).toKey
|
||
|
except CatchableError:
|
||
|
discard
|
||
|
$a.ByteArray32
|
||
|
|
||
|
proc pp*(a: NodeTag; ps: SnapDbBaseRef): string =
|
||
|
a.to(NodeKey).pp(ps)
|
||
|
|
||
|
# ------------------------------------------------------------------------------
|
||
|
# Public constructor
|
||
|
# ------------------------------------------------------------------------------
|
||
|
|
||
|
proc init*(
|
||
|
T: type HexaryTreeDbRef;
|
||
|
): T =
|
||
|
## Constructor variant. It provides a `HexaryTreeDbRef()` with a key cache
|
||
|
## attached for pretty printing. So this one is mainly for debugging.
|
||
|
HexaryTreeDbRef.init(SnapDbRef())
|
||
|
|
||
|
# ------------------------------------------------------------------------------
|
||
|
# Public functions
|
||
|
# ------------------------------------------------------------------------------
|
||
|
|
||
|
proc sortMerge*(base: openArray[NodeTag]): NodeTag =
|
||
|
## Helper for merging several `(NodeTag,seq[PackedAccount])` data sets
|
||
|
## so that there are no overlap which would be rejected by `merge()`.
|
||
|
##
|
||
|
## This function selects a `NodeTag` from a list.
|
||
|
result = high(NodeTag)
|
||
|
for w in base:
|
||
|
if w < result:
|
||
|
result = w
|
||
|
|
||
|
proc sortMerge*(acc: openArray[seq[PackedAccount]]): seq[PackedAccount] =
|
||
|
## Helper for merging several `(NodeTag,seq[PackedAccount])` data sets
|
||
|
## so that there are no overlap which would be rejected by `merge()`.
|
||
|
##
|
||
|
## This function flattens and sorts the argument account lists.
|
||
|
noKeyError("sortMergeAccounts"):
|
||
|
var accounts: Table[NodeTag,PackedAccount]
|
||
|
for accList in acc:
|
||
|
for item in accList:
|
||
|
accounts[item.accKey.to(NodeTag)] = item
|
||
|
result = toSeq(accounts.keys).sorted(cmp).mapIt(accounts[it])
|
||
|
|
||
|
proc nextAccountsChainDbKey*(
|
||
|
ps: SnapDbBaseRef;
|
||
|
accKey: NodeKey;
|
||
|
getFn: HexaryGetFn;
|
||
|
): Result[NodeKey,HexaryError] =
|
||
|
## Fetch the account path on the `ChainDBRef`, the one next to the
|
||
|
## argument account key.
|
||
|
noExceptionOops("getChainDbAccount()"):
|
||
|
let path = accKey
|
||
|
.hexaryPath(ps.root, getFn) # ps.getAccountFn)
|
||
|
.next(getFn) # ps.getAccountFn)
|
||
|
.getNibbles
|
||
|
if 64 == path.len:
|
||
|
return ok(path.getBytes.convertTo(Hash256).to(NodeKey))
|
||
|
|
||
|
err(AccountNotFound)
|
||
|
|
||
|
proc prevAccountsChainDbKey*(
|
||
|
ps: SnapDbBaseRef;
|
||
|
accKey: NodeKey;
|
||
|
getFn: HexaryGetFn;
|
||
|
): Result[NodeKey,HexaryError] =
|
||
|
## Fetch the account path on the `ChainDBRef`, the one before to the
|
||
|
## argument account.
|
||
|
noExceptionOops("getChainDbAccount()"):
|
||
|
let path = accKey
|
||
|
.hexaryPath(ps.root, getFn) # ps.getAccountFn)
|
||
|
.prev(getFn) # ps.getAccountFn)
|
||
|
.getNibbles
|
||
|
if 64 == path.len:
|
||
|
return ok(path.getBytes.convertTo(Hash256).to(NodeKey))
|
||
|
|
||
|
err(AccountNotFound)
|
||
|
|
||
|
# ------------------------------------------------------------------------------
|
||
|
# More debugging (and playing with the hexary database)
|
||
|
# ------------------------------------------------------------------------------
|
||
|
|
||
|
proc assignPrettyKeys*(xDb: HexaryTreeDbRef; root: NodeKey) =
|
||
|
## Prepare for pretty pringing/debugging. Run early enough this function
|
||
|
## sets the root key to `"$"`, for instance.
|
||
|
if not xDb.keyPp.isNil:
|
||
|
noPpError("validate(1)"):
|
||
|
# Make keys assigned in pretty order for printing
|
||
|
let rootKey = root.to(RepairKey)
|
||
|
discard xDb.keyPp rootKey
|
||
|
var keysList = toSeq(xDb.tab.keys)
|
||
|
if xDb.tab.hasKey(rootKey):
|
||
|
keysList = @[rootKey] & keysList
|
||
|
for key in keysList:
|
||
|
let node = xDb.tab[key]
|
||
|
discard xDb.keyPp key
|
||
|
case node.kind:
|
||
|
of Branch: (for w in node.bLink: discard xDb.keyPp w)
|
||
|
of Extension: discard xDb.keyPp node.eLink
|
||
|
of Leaf: discard
|
||
|
|
||
|
# ------------------------------------------------------------------------------
|
||
|
# End
|
||
|
# ------------------------------------------------------------------------------
|