2023-03-22 20:11:49 +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.
|
|
|
|
|
|
|
|
## Helpers to treat persistent and in-memory database in a similar way
|
|
|
|
|
|
|
|
{.push raises: [].}
|
|
|
|
|
|
|
|
import
|
|
|
|
std/[sequtils, tables],
|
|
|
|
eth/[common, trie/nibbles],
|
|
|
|
stew/results,
|
|
|
|
../../range_desc,
|
|
|
|
"."/[hexary_desc, hexary_error]
|
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# Public helpers
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
proc isZeroLink*(a: Blob): bool =
|
|
|
|
## Persistent database has `Blob` as key
|
|
|
|
a.len == 0
|
|
|
|
|
|
|
|
proc isZeroLink*(a: RepairKey): bool =
|
|
|
|
## Persistent database has `RepairKey` as key
|
|
|
|
a.isZero
|
|
|
|
|
|
|
|
proc `==`*(a, b: XNodeObj): bool =
|
|
|
|
if a.kind == b.kind:
|
|
|
|
case a.kind:
|
|
|
|
of Leaf:
|
|
|
|
return a.lPfx == b.lPfx and a.lData == b.lData
|
|
|
|
of Extension:
|
|
|
|
return a.ePfx == b.ePfx and a.eLink == b.eLink
|
|
|
|
of Branch:
|
|
|
|
return a.bLink == b.bLink
|
|
|
|
|
|
|
|
# ------------------
|
|
|
|
|
|
|
|
proc toBranchNode*(
|
|
|
|
rlp: Rlp
|
|
|
|
): XNodeObj
|
|
|
|
{.gcsafe, raises: [RlpError]} =
|
|
|
|
var rlp = rlp
|
|
|
|
XNodeObj(kind: Branch, bLink: rlp.read(array[17,Blob]))
|
|
|
|
|
|
|
|
proc toLeafNode*(
|
|
|
|
rlp: Rlp;
|
|
|
|
pSegm: NibblesSeq
|
|
|
|
): XNodeObj
|
|
|
|
{.gcsafe, raises: [RlpError]} =
|
|
|
|
XNodeObj(kind: Leaf, lPfx: pSegm, lData: rlp.listElem(1).toBytes)
|
|
|
|
|
|
|
|
proc toExtensionNode*(
|
|
|
|
rlp: Rlp;
|
|
|
|
pSegm: NibblesSeq
|
|
|
|
): XNodeObj
|
|
|
|
{.gcsafe, raises: [RlpError]} =
|
|
|
|
XNodeObj(kind: Extension, ePfx: pSegm, eLink: rlp.listElem(1).toBytes)
|
|
|
|
|
|
|
|
# ------------------
|
|
|
|
|
|
|
|
proc getNode*(
|
|
|
|
nodeKey: RepairKey; # Node key
|
|
|
|
db: HexaryTreeDbRef; # Database
|
|
|
|
): Result[RNodeRef,HexaryError]
|
|
|
|
{.gcsafe, raises: [KeyError].} =
|
|
|
|
## Fetch root node for given path
|
|
|
|
if db.tab.hasKey(nodeKey):
|
|
|
|
return ok(db.tab[nodeKey])
|
|
|
|
err(NearbyDanglingLink)
|
|
|
|
|
|
|
|
proc getNode*(
|
|
|
|
nodeKey: openArray[byte]; # Node key
|
|
|
|
getFn: HexaryGetFn; # Database abstraction
|
|
|
|
): Result[XNodeObj,HexaryError]
|
|
|
|
{.gcsafe, raises: [CatchableError].} =
|
|
|
|
## Variant of `getRootNode()`
|
|
|
|
let nodeData = nodeKey.getFn
|
|
|
|
if 0 < nodeData.len:
|
|
|
|
let nodeRlp = rlpFromBytes nodeData
|
|
|
|
case nodeRlp.listLen:
|
|
|
|
of 17:
|
|
|
|
return ok(nodeRlp.toBranchNode)
|
|
|
|
of 2:
|
|
|
|
let (isLeaf,pfx) = hexPrefixDecode nodeRlp.listElem(0).toBytes
|
|
|
|
if isleaf:
|
|
|
|
return ok(nodeRlp.toLeafNode pfx)
|
|
|
|
else:
|
|
|
|
return ok(nodeRlp.toExtensionNode pfx)
|
|
|
|
else:
|
|
|
|
return err(NearbyGarbledNode)
|
|
|
|
err(NearbyDanglingLink)
|
|
|
|
|
|
|
|
proc getNode*(
|
|
|
|
nodeKey: NodeKey; # Node key
|
|
|
|
getFn: HexaryGetFn; # Database abstraction
|
|
|
|
): Result[XNodeObj,HexaryError]
|
|
|
|
{.gcsafe, raises: [CatchableError].} =
|
|
|
|
## Variant of `getRootNode()`
|
|
|
|
nodeKey.ByteArray32.getNode(getFn)
|
|
|
|
|
|
|
|
# ------------------
|
2023-03-25 10:44:48 +00:00
|
|
|
|
|
|
|
proc branchNibbleMin*(node: XNodeObj|RNodeRef; minInx: int8): int8 =
|
|
|
|
## Find the least index for an argument branch `node` link with index
|
|
|
|
## greater or equal the argument `nibble`.
|
|
|
|
if node.kind == Branch:
|
|
|
|
for n in minInx .. 15:
|
|
|
|
if not node.bLink[n].isZeroLink:
|
|
|
|
return n
|
|
|
|
-1
|
|
|
|
|
|
|
|
proc branchNibbleMax*(node: XNodeObj|RNodeRef; maxInx: int8): int8 =
|
|
|
|
## Find the greatest index for an argument branch `node` link with index
|
|
|
|
## less or equal the argument `nibble`.
|
|
|
|
if node.kind == Branch:
|
|
|
|
for n in maxInx.countDown 0:
|
|
|
|
if not node.bLink[n].isZeroLink:
|
|
|
|
return n
|
|
|
|
-1
|
|
|
|
|
|
|
|
# --------------------
|
2023-03-22 20:11:49 +00:00
|
|
|
|
|
|
|
proc padPartialPath*(pfx: NibblesSeq; dblNibble: byte): NodeKey =
|
|
|
|
## Extend (or cut) `partialPath` nibbles sequence and generate `NodeKey`.
|
|
|
|
## This function must be handled with some care regarding a meaningful value
|
|
|
|
## for the `dblNibble` argument. Using values `0` or `255` is typically used
|
|
|
|
## to create the minimum or maximum envelope value from the `pfx` argument.
|
|
|
|
# Pad with zeroes
|
|
|
|
var padded: NibblesSeq
|
|
|
|
|
|
|
|
let padLen = 64 - pfx.len
|
|
|
|
if 0 <= padLen:
|
|
|
|
padded = pfx & dblNibble.repeat(padlen div 2).initNibbleRange
|
|
|
|
if (padLen and 1) == 1:
|
|
|
|
padded = padded & @[dblNibble].initNibbleRange.slice(1)
|
|
|
|
else:
|
|
|
|
let nope = seq[byte].default.initNibbleRange
|
|
|
|
padded = pfx.slice(0,64) & nope # nope forces re-alignment
|
|
|
|
|
|
|
|
let bytes = padded.getBytes
|
|
|
|
(addr result.ByteArray32[0]).copyMem(unsafeAddr bytes[0], bytes.len)
|
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# End
|
|
|
|
# ------------------------------------------------------------------------------
|