nimbus-eth1/nimbus/sync/snap/worker/db/hexary_nodes_helper.nim
Jordan Hrycaj c01045c246
Update snap client account healing (#1521)
* Update nearby/neighbour leaf nodes finder

details:
  Update return error codes so that in the case that there is no more
  leaf node beyond the search direction, the particular error code
  `NearbyBeyondRange` is returned.

* Compile largest interval range containing only this leaf point

why:
  Will be needed in snap sync for adding single leaf nodes to the range
  of already allocated nodes.

* Reorg `hexary_inspect.nim`

why:
 Merged the nodes collecting algorithm for persistent and in-memory
 into a single generic function `hexary_inspect.inspectTrieImpl()`

* Update fetching accounts range failure handling in `rangeFetchAccounts()`

why:
  Rejected response leads now to fetching for another account range. Only
  repeated failures (or all done) terminate the algorithm.

* Update accounts healing

why:
+ Fixed looping over a bogus node response that could not inserted into
  the database. As a solution, these nodes are locally registered and not
  asked for in this download cycle.
+ Sub-optimal handling of interval range for a healed account leaf node.
  Now the maximal range interval containing this node is registered as
  processed which leafs to de-fragementation of the processed (and
  unprocessed) range list(s). So *gap* ranges which are known not to
  cover any account leaf node are not asked for on the network, anymore.
+ Sporadically remove empty interval ranges (if any)

* Update logging, better variable names
2023-03-25 10:44:48 +00:00

154 lines
4.7 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.
## 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)
# ------------------
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
# --------------------
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
# ------------------------------------------------------------------------------