nimbus-eth1/nimbus/db/aristo/aristo_path.nim
Jordan Hrycaj 143f2e99f5
Core db+aristo fixes and tx handling updates (#2164)
* Aristo: Rename journal related sources and functions

why:
  Previously, the naming was hinged on the phrases `fifo`, `filter` etc.
  which reflect the inner workings of cascaded filters. This was
  unfortunate for reading/understanding the source code for actions where
  the focus is the journal as a whole.

* Aristo: Fix buffer overflow (path length truncating error)

* Aristo: Tighten `hikeUp()` stop check, update error code

why:
  Detect dangling vertex links. These are legit with `snap` sync
  processing but not with regular processing.

* Aristo: Raise assert in regular mode `merge()` at a dangling link/edge

why:
  With `snap` sync processing, partial trees are ok and can be amended.
  Not so in regular mode.

  Previously there was only a debug message when a non-legit dangling edge
  was encountered.

* Aristo: Make sure that vertices are copied before modification

why:
  Otherwise vertices from lower layers might also be modified

* Aristo: Fix relaxed mode for validity checker `check()`

* Remove cruft

* Aristo: Update API for transaction handling

details:
+ Split `aristo_tx.nim` into sub-modules
+ Split `forkWith()` into `findTx()` + `forkTx()`
+ Removed `forkTop()`, `forkBase()` (now superseded by new `forkTx()`)

* CoreDb+Aristo: Fix initialiser (missing methods)
2024-05-03 17:38:17 +00:00

108 lines
4.1 KiB
Nim

# nimbus-eth1
# Copyright (c) 2023-2024 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/sequtils,
eth/[common, trie/nibbles],
results,
./aristo_desc
# Info snippet (just a reminder to keep somewhere)
#
# Extension of a compact encoded as prefixed sequence of nibbles (i.e.
# half bytes with 4 bits.)
#
# pfx | bits | vertex type | layout
# ----+ -----+-------------+----------------------------------------
# 0 | 0000 | extension | @[<pfx, ignored>, nibble-pair, ..]
# 1 | 0001 | extension | @[<pfx, first-nibble>, nibble-pair, ..]
# 2 | 0010 | leaf | @[<pfx, ignored>, nibble-pair, ..]
# 3 | 0011 | leaf | @[<pfx, first-nibble>, nibble-pair, ..]
#
# where the `ignored` part is typically expected a zero nibble.
func pathPfxPad*(pfx: NibblesSeq; dblNibble: static[byte]): NibblesSeq
# ------------------------------------------------------------------------------
# Public functions
# ------------------------------------------------------------------------------
func pathAsBlob*(tag: PathID): Blob =
## Convert the `tag` argument to a sequence of an even number of nibbles
## represented by a `Blob`. If the argument `tag` represents an odd number
## of nibbles, a zero nibble is appendend.
##
## This function is useful only if there is a tacit agreement that all paths
## used to index database leaf values can be represented as `Blob`, i.e.
## `PathID` type paths with an even number of nibbles.
if 0 < tag.length:
let key = @(tag.pfx.toBytesBE)
if 64 <= tag.length:
return key
else:
return key[0 .. (tag.length - 1) div 2]
func pathAsHEP*(tag: PathID; isLeaf = false): Blob =
## Convert the `tag` argument to a hex encoded partial path as used in `eth`
## or `snap` protocol where full paths of nibble length 64 are encoded as 32
## byte `Blob` and non-leaf partial paths are *compact encoded* (i.e. per
## the Ethereum wire protocol.)
if 64 <= tag.length:
@(tag.pfx.toBytesBE)
else:
tag.to(NibblesSeq).hexPrefixEncode(isLeaf=true)
func pathToTag*(partPath: NibblesSeq): Result[PathID,AristoError] =
## Convert the argument `partPath` to a `PathID` type value.
if partPath.len == 0:
return ok VOID_PATH_ID
if partPath.len <= 64:
return ok PathID(
pfx: UInt256.fromBytesBE partPath.pathPfxPad(0).getBytes(),
length: partPath.len.uint8)
err(PathAtMost64Nibbles)
func pathToTag*(partPath: openArray[byte]): Result[PathID,AristoError] =
## Variant of `pathToTag()`
if partPath.len == 0:
return ok VOID_PATH_ID
if partPath.len <= 32:
return ok PathID(
pfx: UInt256.fromBytesBE @partPath & 0u8.repeat(32-partPath.len),
length: 2 * partPath.len.uint8)
err(PathAtMost64Nibbles)
# --------------------
func pathPfxPad*(pfx: NibblesSeq; dblNibble: static[byte]): NibblesSeq =
## Extend (or cut) the argument nibbles sequence `pfx` for generating a
## `NibblesSeq` with exactly 64 nibbles, the equivalent of a path key.
##
## This function must be handled with some care regarding a meaningful value
## for the `dblNibble` argument. Currently, only static values `0` and `255`
## are allowed for padding. This is checked at compile time.
static:
doAssert dblNibble == 0 or dblNibble == 255
let padLen = 64 - pfx.len
if 0 <= padLen:
result = pfx & dblNibble.repeat(padLen div 2).mapIt(it.byte).initNibbleRange
if (padLen and 1) == 1:
result = result & @[dblNibble.byte].initNibbleRange.slice(1)
else:
let nope = seq[byte].default.initNibbleRange
result = pfx.slice(0,64) & nope # nope forces re-alignment
# ------------------------------------------------------------------------------
# End
# ------------------------------------------------------------------------------