2023-06-12 13:48:47 +00:00
|
|
|
# nimbus-eth1
|
2024-02-01 21:27:48 +00:00
|
|
|
# Copyright (c) 2023-2024 Status Research & Development GmbH
|
2023-06-12 13:48:47 +00:00
|
|
|
# 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.
|
|
|
|
|
|
|
|
## Aristo DB -- Identifier types
|
|
|
|
## =============================
|
|
|
|
##
|
|
|
|
|
|
|
|
{.push raises: [].}
|
|
|
|
|
|
|
|
import
|
2024-02-22 08:24:58 +00:00
|
|
|
std/[algorithm, sequtils, sets, strutils, hashes],
|
2024-06-22 20:33:37 +00:00
|
|
|
eth/common,
|
2023-11-08 16:52:25 +00:00
|
|
|
stew/byteutils,
|
2023-11-09 12:22:51 +00:00
|
|
|
chronicles,
|
2023-11-08 12:18:32 +00:00
|
|
|
results,
|
2024-06-22 20:33:37 +00:00
|
|
|
stint,
|
|
|
|
./desc_nibbles
|
|
|
|
|
|
|
|
export
|
|
|
|
desc_nibbles
|
2023-06-12 13:48:47 +00:00
|
|
|
|
|
|
|
type
|
|
|
|
VertexID* = distinct uint64
|
|
|
|
## Unique identifier for a vertex of the `Aristo Trie`. The vertex is the
|
|
|
|
## prefix tree (aka `Patricia Trie`) component. When augmented by hash
|
|
|
|
## keys, the vertex component will be called a node. On the persistent
|
|
|
|
## backend of the database, there is no other reference to the node than
|
2023-06-12 18:16:03 +00:00
|
|
|
## the very same `VertexID`.
|
2024-06-05 18:17:50 +00:00
|
|
|
##
|
|
|
|
## Vertex IDs are generated on the fly and thrown away when not needed,
|
|
|
|
## anymore. They are not recycled. A quick estimate
|
|
|
|
##
|
|
|
|
## (2^64) / (100 * 365.25 * 24 * 3600) / 1000 / 1000 / 1000 = 5.86
|
|
|
|
##
|
|
|
|
## shows that the `uint64` scalar space is not exhausted in a 100 years
|
|
|
|
## if the database consumes somewhat less than 6 IDs per nanosecond.
|
|
|
|
##
|
|
|
|
## A simple recycling mechanism was tested which slowed down the system
|
|
|
|
## considerably because large swaths of database vertices were regularly
|
|
|
|
## freed so recycling had do deal with extensive lists of non-consecutive
|
|
|
|
## IDs.
|
2023-06-12 18:16:03 +00:00
|
|
|
|
2024-07-04 13:46:52 +00:00
|
|
|
RootedVertexID* = tuple[root, vid: VertexID]
|
|
|
|
## Vertex and the root it belongs to in the MPT. Used to group a set of
|
|
|
|
## verticies, for example to store them together in the database or perform
|
|
|
|
## range operations.
|
|
|
|
##
|
|
|
|
## `vid` may be a branch, extension or leaf.
|
|
|
|
##
|
|
|
|
## To reference the root itself, use (root, root).
|
|
|
|
|
2023-11-08 12:18:32 +00:00
|
|
|
HashKey* = object
|
2024-07-17 13:48:21 +00:00
|
|
|
## Ethereum reference MPTs use Keccak hashes as node links if the size of
|
|
|
|
## an RLP encoded node is at least 32 bytes. Otherwise, the RLP encoded
|
|
|
|
## node value is used as a pseudo node link (rather than a hash.) This is
|
|
|
|
## specified in the yellow paper, appendix D. Only for the root hash, the
|
|
|
|
## top level node is always referred to by the Keccak hash.
|
2023-11-08 12:18:32 +00:00
|
|
|
##
|
2024-07-17 13:48:21 +00:00
|
|
|
## On the `Aristo` database node links are called keys which are of this
|
|
|
|
## very type `HashKey`. For key-value tables (which assign a key to a
|
|
|
|
## vertex), the keys are always stored as such with length probably
|
|
|
|
## smaller than 32, including for root vertex keys. Only when used as a
|
|
|
|
## root state, the key of the latter is digested to a Keccak hash
|
|
|
|
## on-the-fly.
|
|
|
|
##
|
|
|
|
## This compaction feature nees an abstraction of the hash link object
|
2024-10-01 21:03:10 +00:00
|
|
|
## which is either a `Hash32` or a `seq[byte]` of length at most 31 bytes.
|
2023-11-08 12:18:32 +00:00
|
|
|
## This leaves two ways of representing an empty/void `HashKey` type.
|
2024-10-01 21:03:10 +00:00
|
|
|
## It may be available as an empty `seq[byte]` of zero length, or the
|
|
|
|
## `Hash32` type of the Keccak hash of an empty `seq[byte]` (see constant
|
2023-11-08 12:18:32 +00:00
|
|
|
## `EMPTY_ROOT_HASH`.)
|
|
|
|
##
|
2024-07-17 13:48:21 +00:00
|
|
|
## For performance, storing blobs as `seq` is avoided, instead storing
|
|
|
|
## their length and sharing the data "space".
|
|
|
|
##
|
2024-10-01 21:03:10 +00:00
|
|
|
buf: array[32, byte] # Either Hash32 or blob data, depending on `len`
|
2024-05-23 15:37:51 +00:00
|
|
|
len: int8 # length in the case of blobs, or 32 when it's a hash
|
2023-06-12 18:16:03 +00:00
|
|
|
|
2023-10-27 21:36:51 +00:00
|
|
|
PathID* = object
|
|
|
|
## Path into the `Patricia Trie`. This is a chain of maximal 64 nibbles
|
|
|
|
## (which is 32 bytes.) In most cases, the length is 64. So the path is
|
|
|
|
## encoded as a numeric value which is often easier to handle than a
|
|
|
|
## chain of nibbles.
|
|
|
|
##
|
|
|
|
## The path ID should be kept normalised, i.e.
|
|
|
|
## * 0 <= `length` <= 64
|
2024-02-01 21:27:48 +00:00
|
|
|
## * the unused trailing nibbles in `pfx` are set to `0`
|
2023-10-27 21:36:51 +00:00
|
|
|
##
|
|
|
|
pfx*: UInt256
|
|
|
|
length*: uint8
|
|
|
|
|
2023-06-12 18:16:03 +00:00
|
|
|
# ----------
|
2023-06-12 13:48:47 +00:00
|
|
|
|
|
|
|
LeafTie* = object
|
|
|
|
## Unique access key for a leaf vertex. It identifies a root vertex
|
|
|
|
## followed by a nibble path along the `Patricia Trie` down to a leaf
|
|
|
|
## vertex. So this implies an obvious injection from the set of `LeafTie`
|
|
|
|
## objects *into* the set of `VertexID` obvious (which is typically *into*
|
|
|
|
## only, not a bijection.)
|
|
|
|
##
|
|
|
|
## Note that `LeafTie` objects have no representation in the `Aristo Trie`.
|
|
|
|
## They are used temporarily and in caches or backlog tables.
|
|
|
|
root*: VertexID ## Root ID for the sub-trie
|
2023-10-27 21:36:51 +00:00
|
|
|
path*: PathID ## Path into the `Patricia Trie`
|
2023-06-12 13:48:47 +00:00
|
|
|
|
2023-11-09 12:22:51 +00:00
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# Chronicles formatters
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
chronicles.formatIt(VertexID): $it
|
|
|
|
|
2023-06-12 13:48:47 +00:00
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# Public helpers: `VertexID` scalar data model
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
|
2023-06-20 13:26:25 +00:00
|
|
|
func `<`*(a, b: VertexID): bool {.borrow.}
|
2023-06-30 22:22:33 +00:00
|
|
|
func `<=`*(a, b: VertexID): bool {.borrow.}
|
2023-06-20 13:26:25 +00:00
|
|
|
func `==`*(a, b: VertexID): bool {.borrow.}
|
|
|
|
func cmp*(a, b: VertexID): int {.borrow.}
|
2024-02-22 08:24:58 +00:00
|
|
|
|
|
|
|
func `$`*(vid: VertexID): string =
|
|
|
|
"$" & (if vid == VertexID(0): "ø"
|
|
|
|
else: vid.uint64.toHex.strip(trailing=false,chars={'0'}).toLowerAscii)
|
2023-06-12 13:48:47 +00:00
|
|
|
|
2024-07-04 13:46:52 +00:00
|
|
|
func `$`*(rvid: RootedVertexID): string =
|
|
|
|
$rvid.root & "/" & $rvid.vid
|
|
|
|
|
2023-08-25 22:53:59 +00:00
|
|
|
func `==`*(a: VertexID; b: static[uint]): bool = (a == VertexID(b))
|
2023-06-12 13:48:47 +00:00
|
|
|
|
2023-08-25 22:53:59 +00:00
|
|
|
# Scalar model extension as in `IntervalSetRef[VertexID,uint64]`
|
|
|
|
func `+`*(a: VertexID; b: uint64): VertexID = (a.uint64+b).VertexID
|
|
|
|
func `-`*(a: VertexID; b: uint64): VertexID = (a.uint64-b).VertexID
|
|
|
|
func `-`*(a, b: VertexID): uint64 = (a.uint64 - b.uint64)
|
|
|
|
|
2023-06-12 18:16:03 +00:00
|
|
|
# ------------------------------------------------------------------------------
|
2023-10-27 21:36:51 +00:00
|
|
|
# Public helpers: `PathID` ordered scalar data model
|
2023-06-12 18:16:03 +00:00
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
|
2023-10-27 21:36:51 +00:00
|
|
|
func high*(_: type PathID): PathID =
|
|
|
|
## Highest possible `PathID` object for given root vertex.
|
|
|
|
PathID(pfx: high(UInt256), length: 64)
|
|
|
|
|
|
|
|
func low*(_: type PathID): PathID =
|
|
|
|
## Lowest possible `PathID` object for given root vertex.
|
|
|
|
PathID()
|
|
|
|
|
|
|
|
func next*(pid: PathID): PathID =
|
|
|
|
## Return a `PathID` object with incremented path field. This function might
|
|
|
|
## return also a modified `length` field.
|
|
|
|
##
|
|
|
|
## The function returns the argument `pid` if it is already at its
|
|
|
|
## maximum value `high(PathID)`.
|
2024-02-16 09:08:07 +00:00
|
|
|
if pid.pfx.isZero and pid.length < 64:
|
2023-10-27 21:36:51 +00:00
|
|
|
PathID(length: pid.length + 1)
|
|
|
|
elif pid.pfx < high(UInt256):
|
|
|
|
PathID(pfx: pid.pfx + 1, length: 64)
|
|
|
|
else:
|
|
|
|
pid
|
|
|
|
|
|
|
|
func prev*(pid: PathID): PathID =
|
|
|
|
## Return a `PathID` object with decremented path field. This function might
|
|
|
|
## return also a modified `length` field.
|
|
|
|
##
|
|
|
|
## The function returns the argument `pid` if it is already at its
|
|
|
|
## minimum value `low(PathID)`.
|
|
|
|
if 0 < pid.pfx:
|
|
|
|
PathID(pfx: pid.pfx - 1, length: 64)
|
|
|
|
elif 0 < pid.length:
|
|
|
|
PathID(length: pid.length - 1)
|
|
|
|
else:
|
|
|
|
pid
|
2023-06-12 18:16:03 +00:00
|
|
|
|
2023-10-27 21:36:51 +00:00
|
|
|
func `<`*(a, b: PathID): bool =
|
|
|
|
## This function assumes that the arguments `a` and `b` are normalised
|
|
|
|
## (see `normal()`.)
|
|
|
|
a.pfx < b.pfx or (a.pfx == b.pfx and a.length < b.length)
|
2023-06-12 18:16:03 +00:00
|
|
|
|
2023-10-27 21:36:51 +00:00
|
|
|
func `<=`*(a, b: PathID): bool =
|
|
|
|
not (b < a)
|
2023-06-12 18:16:03 +00:00
|
|
|
|
2023-10-27 21:36:51 +00:00
|
|
|
func `==`*(a, b: PathID): bool =
|
|
|
|
## This function assumes that the arguments `a` and `b` are normalised
|
|
|
|
## (see `normal()`.)
|
|
|
|
a.pfx == b.pfx and a.length == b.length
|
2023-06-12 18:16:03 +00:00
|
|
|
|
2023-11-08 12:18:32 +00:00
|
|
|
func cmp*(a, b: PathID): int =
|
|
|
|
if a < b: -1 elif b < a: 1 else: 0
|
|
|
|
|
2024-06-03 20:10:35 +00:00
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# Public helpers: `HashKey` ordered scalar data model
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
func len*(lid: HashKey): int =
|
|
|
|
lid.len.int # if lid.isHash: 32 else: lid.blob.len
|
|
|
|
|
2024-05-23 15:37:51 +00:00
|
|
|
template data*(lid: HashKey): openArray[byte] =
|
|
|
|
lid.buf.toOpenArray(0, lid.len - 1)
|
|
|
|
|
2024-02-01 21:27:48 +00:00
|
|
|
func to*(lid: HashKey; T: type PathID): T =
|
|
|
|
## Helper to bowrrow certain properties from `PathID`
|
2024-05-23 15:37:51 +00:00
|
|
|
if lid.len == 32:
|
|
|
|
PathID(pfx: UInt256.fromBytesBE lid.data, length: 64)
|
|
|
|
elif 0 < lid.len:
|
|
|
|
doAssert lid.len < 32
|
2024-02-01 21:27:48 +00:00
|
|
|
var a32: array[32,byte]
|
2024-05-23 15:37:51 +00:00
|
|
|
(addr a32[0]).copyMem(unsafeAddr lid.data[0], lid.len)
|
|
|
|
PathID(pfx: UInt256.fromBytesBE a32, length: 2 * lid.len.uint8)
|
2024-02-01 21:27:48 +00:00
|
|
|
else:
|
|
|
|
PathID()
|
|
|
|
|
2023-11-08 12:18:32 +00:00
|
|
|
func fromBytes*(T: type HashKey; data: openArray[byte]): Result[T,void] =
|
|
|
|
## Write argument `data` of length 0 or between 2 and 32 bytes as a `HashKey`.
|
|
|
|
##
|
|
|
|
## A function argument `data` of length 32 is used as-is.
|
|
|
|
##
|
|
|
|
## For a function argument `data` of length between 2 and 31, the first
|
|
|
|
## byte must be the start of an RLP encoded list, i.e. `0xc0 + len` where
|
|
|
|
## where `len` is one less as the `data` length.
|
|
|
|
##
|
|
|
|
if data.len == 32:
|
|
|
|
var lid: T
|
2024-05-23 15:37:51 +00:00
|
|
|
lid.len = 32
|
|
|
|
(addr lid.data[0]).copyMem(unsafeAddr data[0], data.len)
|
2023-11-08 12:18:32 +00:00
|
|
|
return ok lid
|
|
|
|
if data.len == 0:
|
|
|
|
return ok HashKey()
|
|
|
|
if 1 < data.len and data.len < 32 and data[0].int == 0xbf + data.len:
|
2024-05-23 15:37:51 +00:00
|
|
|
var lid: T
|
|
|
|
lid.len = int8 data.len
|
|
|
|
(addr lid.data[0]).copyMem(unsafeAddr data[0], data.len)
|
|
|
|
return ok lid
|
2023-11-08 12:18:32 +00:00
|
|
|
err()
|
|
|
|
|
|
|
|
func `<`*(a, b: HashKey): bool =
|
|
|
|
## Slow, but useful for debug sorting
|
|
|
|
a.to(PathID) < b.to(PathID)
|
|
|
|
|
|
|
|
func `==`*(a, b: HashKey): bool =
|
2024-05-23 15:37:51 +00:00
|
|
|
a.data == b.data
|
2023-11-08 12:18:32 +00:00
|
|
|
|
|
|
|
func cmp*(a, b: HashKey): int =
|
|
|
|
## Slow, but useful for debug sorting
|
2024-05-23 15:37:51 +00:00
|
|
|
cmp(a.data, b.data)
|
2023-11-08 12:18:32 +00:00
|
|
|
|
2023-07-12 23:03:14 +00:00
|
|
|
# ------------------------------------------------------------------------------
|
2023-10-27 21:36:51 +00:00
|
|
|
# Public helpers: `LeafTie` ordered scalar data model
|
2023-07-12 23:03:14 +00:00
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
func high*(_: type LeafTie; root = VertexID(1)): LeafTie =
|
|
|
|
## Highest possible `LeafTie` object for given root vertex.
|
2023-10-27 21:36:51 +00:00
|
|
|
LeafTie(root: root, path: high(PathID))
|
2023-07-12 23:03:14 +00:00
|
|
|
|
|
|
|
func low*(_: type LeafTie; root = VertexID(1)): LeafTie =
|
|
|
|
## Lowest possible `LeafTie` object for given root vertex.
|
2023-10-27 21:36:51 +00:00
|
|
|
LeafTie(root: root, path: low(PathID))
|
2023-07-12 23:03:14 +00:00
|
|
|
|
2023-10-27 21:36:51 +00:00
|
|
|
func next*(lty: LeafTie): LeafTie =
|
|
|
|
## Return a `LeafTie` object with the `next()` path field.
|
|
|
|
LeafTie(root: lty.root, path: lty.path.next)
|
2023-07-12 23:03:14 +00:00
|
|
|
|
2023-10-27 21:36:51 +00:00
|
|
|
func prev*(lty: LeafTie): LeafTie =
|
|
|
|
## Return a `LeafTie` object with the `prev()` path field.
|
|
|
|
LeafTie(root: lty.root, path: lty.path.prev)
|
2023-07-12 23:03:14 +00:00
|
|
|
|
2023-10-27 21:36:51 +00:00
|
|
|
func `<`*(a, b: LeafTie): bool =
|
|
|
|
## This function assumes that the arguments `a` and `b` are normalised
|
|
|
|
## (see `normal()`.)
|
|
|
|
a.root < b.root or (a.root == b.root and a.path < b.path)
|
|
|
|
|
|
|
|
func `==`*(a, b: LeafTie): bool =
|
|
|
|
## This function assumes that the arguments `a` and `b` are normalised
|
|
|
|
## (see `normal()`.)
|
|
|
|
a.root == b.root and a.path == b.path
|
2023-06-12 18:16:03 +00:00
|
|
|
|
2023-10-27 21:36:51 +00:00
|
|
|
func cmp*(a, b: LeafTie): int =
|
|
|
|
## This function assumes that the arguments `a` and `b` are normalised
|
|
|
|
## (see `normal()`.)
|
|
|
|
if a < b: -1 elif a == b: 0 else: 1
|
2023-06-12 18:16:03 +00:00
|
|
|
|
2023-10-27 21:36:51 +00:00
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# Public helpers: Reversible conversions between `PathID`, `HashKey`, etc.
|
|
|
|
# ------------------------------------------------------------------------------
|
2023-06-12 18:16:03 +00:00
|
|
|
|
2024-06-22 20:33:37 +00:00
|
|
|
func to*(pid: PathID; T: type NibblesBuf): T =
|
2023-11-08 12:18:32 +00:00
|
|
|
## Representation of a `PathID` as `NibbleSeq` (preserving full information)
|
2024-06-22 20:33:37 +00:00
|
|
|
let nibbles = NibblesBuf.fromBytes(pid.pfx.toBytesBE)
|
2023-10-27 21:36:51 +00:00
|
|
|
if pid.length < 64:
|
|
|
|
nibbles.slice(0, pid.length.int)
|
|
|
|
else:
|
|
|
|
nibbles
|
2023-06-12 18:16:03 +00:00
|
|
|
|
2024-10-01 21:03:10 +00:00
|
|
|
func `@`*(pid: PathID): seq[byte] =
|
|
|
|
## Representation of a `PathID` as a `seq[byte]`. The result is left padded
|
2024-04-03 15:48:35 +00:00
|
|
|
## by a zero LSB if the path length was odd.
|
|
|
|
result = pid.pfx.toBytesBE.toSeq
|
|
|
|
if pid.length < 63:
|
|
|
|
result.setLen((pid.length + 1) shl 1)
|
|
|
|
|
2024-10-01 21:03:10 +00:00
|
|
|
func to*(lid: HashKey; T: type Hash32): T =
|
2023-11-08 12:18:32 +00:00
|
|
|
## Returns the `Hash236` key if available, otherwise the Keccak hash of
|
2024-10-01 21:03:10 +00:00
|
|
|
## the `seq[byte]` version.
|
2024-05-23 15:37:51 +00:00
|
|
|
if lid.len == 32:
|
2024-10-01 21:03:10 +00:00
|
|
|
Hash32(lid.buf)
|
2024-05-23 15:37:51 +00:00
|
|
|
elif 0 < lid.len:
|
2024-10-01 21:03:10 +00:00
|
|
|
lid.data.keccak256
|
2023-11-08 12:18:32 +00:00
|
|
|
else:
|
|
|
|
EMPTY_ROOT_HASH
|
|
|
|
|
2024-10-01 21:03:10 +00:00
|
|
|
func to*(key: Hash32; T: type HashKey): T =
|
2023-11-08 12:18:32 +00:00
|
|
|
## This is an efficient version of `HashKey.fromBytes(key.data).value`, not
|
|
|
|
## to be confused with `digestTo(HashKey)`.
|
2024-02-22 08:24:58 +00:00
|
|
|
if key == EMPTY_ROOT_HASH:
|
|
|
|
T()
|
|
|
|
else:
|
2024-05-23 15:37:51 +00:00
|
|
|
T(len: 32, buf: key.data)
|
2023-11-08 12:18:32 +00:00
|
|
|
|
2024-06-14 07:31:08 +00:00
|
|
|
func to*(n: SomeUnsignedInt; T: type PathID): T =
|
2023-10-27 21:36:51 +00:00
|
|
|
## Representation of a scalar as `PathID` (preserving full information)
|
|
|
|
T(pfx: n.u256, length: 64)
|
|
|
|
|
2024-06-14 07:31:08 +00:00
|
|
|
func to*(n: UInt256; T: type PathID): T =
|
|
|
|
## Representation of a scalar as `PathID` (preserving full information)
|
|
|
|
T(pfx: n, length: 64)
|
|
|
|
|
2024-08-01 10:41:20 +00:00
|
|
|
func to*(a: PathID; T: type UInt256): T =
|
|
|
|
if not a.pfx.isZero:
|
|
|
|
assert a.length < 64 # debugging only
|
|
|
|
result = a.pfx shr (4 * (64 - a.length))
|
|
|
|
|
2023-10-27 21:36:51 +00:00
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# Public helpers: Miscellaneous mappings
|
|
|
|
# ------------------------------------------------------------------------------
|
2023-06-12 18:16:03 +00:00
|
|
|
|
2024-07-17 13:48:21 +00:00
|
|
|
func digestTo*(data: openArray[byte]; T: type HashKey): T =
|
2023-11-08 12:18:32 +00:00
|
|
|
## For argument `data` with length smaller than 32, import them as-is into
|
|
|
|
## the result. Otherwise import the Keccak hash of the argument `data`.
|
No ext update (#2494)
* Imported/rebase from `no-ext`, PR #2485
Store extension nodes together with the branch
Extension nodes must be followed by a branch - as such, it makes sense
to store the two together both in the database and in memory:
* fewer reads, writes and updates to traverse the tree
* simpler logic for maintaining the node structure
* less space used, both memory and storage, because there are fewer
nodes overall
There is also a downside: hashes can no longer be cached for an
extension - instead, only the extension+branch hash can be cached - this
seems like a fine tradeoff since computing it should be fast.
TODO: fix commented code
* Fix merge functions and `toNode()`
* Update `merkleSignCommit()` prototype
why:
Result is always a 32bit hash
* Update short Merkle hash key generation
details:
Ethereum reference MPTs use Keccak hashes as node links if the size of
an RLP encoded node is at least 32 bytes. Otherwise, the RLP encoded
node value is used as a pseudo node link (rather than a hash.) This is
specified in the yellow paper, appendix D.
Different to the `Aristo` implementation, the reference MPT would not
store such a node on the key-value database. Rather the RLP encoded node value is stored instead of a node link in a parent node
is stored as a node link on the parent database.
Only for the root hash, the top level node is always referred to by the
hash.
* Fix/update `Extension` sections
why:
Were commented out after removal of a dedicated `Extension` type which
left the system disfunctional.
* Clean up unused error codes
* Update unit tests
* Update docu
---------
Co-authored-by: Jacek Sieka <jacek@status.im>
2024-07-16 19:47:59 +00:00
|
|
|
##
|
2024-07-17 13:48:21 +00:00
|
|
|
## The `data` argument is only hashed if the `data` length is at least
|
|
|
|
## 32 bytes. Otherwise it is converted as-is to a `HashKey` type result.
|
No ext update (#2494)
* Imported/rebase from `no-ext`, PR #2485
Store extension nodes together with the branch
Extension nodes must be followed by a branch - as such, it makes sense
to store the two together both in the database and in memory:
* fewer reads, writes and updates to traverse the tree
* simpler logic for maintaining the node structure
* less space used, both memory and storage, because there are fewer
nodes overall
There is also a downside: hashes can no longer be cached for an
extension - instead, only the extension+branch hash can be cached - this
seems like a fine tradeoff since computing it should be fast.
TODO: fix commented code
* Fix merge functions and `toNode()`
* Update `merkleSignCommit()` prototype
why:
Result is always a 32bit hash
* Update short Merkle hash key generation
details:
Ethereum reference MPTs use Keccak hashes as node links if the size of
an RLP encoded node is at least 32 bytes. Otherwise, the RLP encoded
node value is used as a pseudo node link (rather than a hash.) This is
specified in the yellow paper, appendix D.
Different to the `Aristo` implementation, the reference MPT would not
store such a node on the key-value database. Rather the RLP encoded node value is stored instead of a node link in a parent node
is stored as a node link on the parent database.
Only for the root hash, the top level node is always referred to by the
hash.
* Fix/update `Extension` sections
why:
Were commented out after removal of a dedicated `Extension` type which
left the system disfunctional.
* Clean up unused error codes
* Update unit tests
* Update docu
---------
Co-authored-by: Jacek Sieka <jacek@status.im>
2024-07-16 19:47:59 +00:00
|
|
|
##
|
2024-07-17 13:48:21 +00:00
|
|
|
## Note that for calculating a root state (when `data` is a serialised
|
2024-10-01 21:03:10 +00:00
|
|
|
## vertex), one would use the expression `data.digestTo(HashKey).to(Hash32)`
|
2024-07-17 13:48:21 +00:00
|
|
|
## which would always hash the `data` argument regardless of its length
|
|
|
|
## (and might result in an `EMPTY_ROOT_HASH`.) See the comment at the
|
|
|
|
## definition of the `HashKey` type for an explanation of its usage.
|
No ext update (#2494)
* Imported/rebase from `no-ext`, PR #2485
Store extension nodes together with the branch
Extension nodes must be followed by a branch - as such, it makes sense
to store the two together both in the database and in memory:
* fewer reads, writes and updates to traverse the tree
* simpler logic for maintaining the node structure
* less space used, both memory and storage, because there are fewer
nodes overall
There is also a downside: hashes can no longer be cached for an
extension - instead, only the extension+branch hash can be cached - this
seems like a fine tradeoff since computing it should be fast.
TODO: fix commented code
* Fix merge functions and `toNode()`
* Update `merkleSignCommit()` prototype
why:
Result is always a 32bit hash
* Update short Merkle hash key generation
details:
Ethereum reference MPTs use Keccak hashes as node links if the size of
an RLP encoded node is at least 32 bytes. Otherwise, the RLP encoded
node value is used as a pseudo node link (rather than a hash.) This is
specified in the yellow paper, appendix D.
Different to the `Aristo` implementation, the reference MPT would not
store such a node on the key-value database. Rather the RLP encoded node value is stored instead of a node link in a parent node
is stored as a node link on the parent database.
Only for the root hash, the top level node is always referred to by the
hash.
* Fix/update `Extension` sections
why:
Were commented out after removal of a dedicated `Extension` type which
left the system disfunctional.
* Clean up unused error codes
* Update unit tests
* Update docu
---------
Co-authored-by: Jacek Sieka <jacek@status.im>
2024-07-16 19:47:59 +00:00
|
|
|
##
|
2024-05-23 15:37:51 +00:00
|
|
|
if data.len == 0:
|
|
|
|
result.len = 0
|
2024-07-17 13:48:21 +00:00
|
|
|
elif data.len < 32:
|
2024-05-23 15:37:51 +00:00
|
|
|
result.len = int8 data.len
|
|
|
|
(addr result.data[0]).copyMem(unsafeAddr data[0], data.len)
|
2023-11-08 12:18:32 +00:00
|
|
|
else:
|
2024-05-23 15:37:51 +00:00
|
|
|
result.len = 32
|
2024-10-01 21:03:10 +00:00
|
|
|
result.buf = data.keccak256.data
|
2023-06-12 18:16:03 +00:00
|
|
|
|
2023-10-27 21:36:51 +00:00
|
|
|
func normal*(a: PathID): PathID =
|
|
|
|
## Normalise path ID representation
|
|
|
|
result = a
|
|
|
|
if 64 < a.length:
|
|
|
|
result.length = 64
|
|
|
|
elif a.length < 64:
|
|
|
|
result.pfx = a.pfx and not (1.u256 shl (4 * (64 - a.length))) - 1.u256
|
|
|
|
|
2023-06-12 18:16:03 +00:00
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# Public helpers: `Tables` and `Rlp` support
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
|
2023-10-27 21:36:51 +00:00
|
|
|
func hash*(a: PathID): Hash =
|
2023-06-12 18:16:03 +00:00
|
|
|
## Table/KeyedQueue mixin
|
2023-10-27 21:36:51 +00:00
|
|
|
var h: Hash = 0
|
|
|
|
h = h !& a.pfx.toBytesBE.hash
|
|
|
|
h = h !& a.length.hash
|
|
|
|
!$h
|
2023-06-12 18:16:03 +00:00
|
|
|
|
2023-11-08 12:18:32 +00:00
|
|
|
func hash*(a: HashKey): Hash =
|
|
|
|
## Table/KeyedQueue mixin
|
2024-05-23 15:37:51 +00:00
|
|
|
hash(a.data)
|
2023-06-12 18:16:03 +00:00
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# Miscellaneous helpers
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
|
2024-02-22 08:24:58 +00:00
|
|
|
func `$`*(vids: seq[VertexID]): string =
|
|
|
|
"[" & vids.toSeq.mapIt(
|
|
|
|
"$" & it.uint64.toHex.strip(trailing=false,chars={'0'})
|
|
|
|
).join(",") & "]"
|
|
|
|
|
|
|
|
func `$`*(vids: HashSet[VertexID]): string =
|
|
|
|
"{" & vids.toSeq.sorted.mapIt(
|
|
|
|
"$" & it.uint64.toHex.strip(trailing=false,chars={'0'})
|
|
|
|
).join(",") & "}"
|
|
|
|
|
2023-11-08 16:52:25 +00:00
|
|
|
func `$`*(key: HashKey): string =
|
2024-05-23 15:37:51 +00:00
|
|
|
toHex(key.data)
|
2023-11-08 16:52:25 +00:00
|
|
|
|
2023-10-27 21:36:51 +00:00
|
|
|
func `$`*(a: PathID): string =
|
2024-02-16 09:08:07 +00:00
|
|
|
if a.pfx.isZero.not:
|
2023-11-08 12:18:32 +00:00
|
|
|
var dgts = $a.pfx.toHex
|
|
|
|
if a.length < 64:
|
|
|
|
dgts = dgts[0 ..< a.length]
|
|
|
|
result = dgts.strip(
|
|
|
|
leading=true, trailing=false, chars={'0'})
|
2023-10-27 21:36:51 +00:00
|
|
|
elif a.length != 0:
|
|
|
|
result = "0"
|
|
|
|
if a.length < 64:
|
|
|
|
result &= "(" & $a.length & ")"
|
|
|
|
|
|
|
|
func `$`*(a: LeafTie): string =
|
|
|
|
if a.root != 0:
|
|
|
|
result = ($a.root.uint64.toHex).strip(
|
2023-11-08 12:18:32 +00:00
|
|
|
leading=true, trailing=false, chars={'0'})
|
2023-10-27 21:36:51 +00:00
|
|
|
else:
|
|
|
|
result = "0"
|
|
|
|
result &= ":" & $a.path
|
2023-06-12 18:16:03 +00:00
|
|
|
|
2023-06-12 13:48:47 +00:00
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# End
|
|
|
|
# ------------------------------------------------------------------------------
|