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
|
|
|
|
## Ethereum MPTs use Keccak hashes as node links if the size of an RLP
|
|
|
|
## encoded node is of size at least 32 bytes. Otherwise, the RLP encoded
|
|
|
|
## node value is used as a pseudo node link (rather than a hash.) Such a
|
|
|
|
## node is nor stored on key-value database. Rather the RLP encoded node
|
|
|
|
## value is stored instead of a lode link in a parent node instead. Only
|
|
|
|
## for the root hash, the top level node is always referred to by the
|
|
|
|
## hash.
|
|
|
|
##
|
|
|
|
## This compaction feature needed an abstraction of the `HashKey` object
|
|
|
|
## which is either a `Hash256` or a `Blob` of length at most 31 bytes.
|
|
|
|
## This leaves two ways of representing an empty/void `HashKey` type.
|
|
|
|
## It may be available as an empty `Blob` of zero length, or the
|
|
|
|
## `Hash256` type of the Keccak hash of an empty `Blob` (see constant
|
|
|
|
## `EMPTY_ROOT_HASH`.)
|
|
|
|
##
|
2024-05-23 15:37:51 +00:00
|
|
|
## For performance, we avoid storing blobs as `seq`, instead storing their
|
|
|
|
## length and sharing the data "space".
|
|
|
|
## TODO can we skip one byte of hash and reduce this type to 32 bytes?
|
|
|
|
buf: array[32, byte] # Either Hash256 or blob data, depending on `len`
|
|
|
|
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-04-03 15:48:35 +00:00
|
|
|
func `@`*(pid: PathID): Blob =
|
|
|
|
## Representation of a `PathID` as a `Blob`. The result is left padded
|
|
|
|
## 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)
|
|
|
|
|
2023-11-08 12:18:32 +00:00
|
|
|
func to*(lid: HashKey; T: type Hash256): T =
|
|
|
|
## Returns the `Hash236` key if available, otherwise the Keccak hash of
|
|
|
|
## the `Blob` version.
|
2024-05-23 15:37:51 +00:00
|
|
|
if lid.len == 32:
|
|
|
|
Hash256(data: lid.buf)
|
|
|
|
elif 0 < lid.len:
|
|
|
|
lid.data.keccakHash
|
2023-11-08 12:18:32 +00:00
|
|
|
else:
|
|
|
|
EMPTY_ROOT_HASH
|
|
|
|
|
|
|
|
func to*(key: Hash256; T: type HashKey): T =
|
|
|
|
## 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)
|
|
|
|
|
2023-10-27 21:36:51 +00:00
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# Public helpers: Miscellaneous mappings
|
|
|
|
# ------------------------------------------------------------------------------
|
2023-06-12 18:16:03 +00:00
|
|
|
|
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
|
|
|
func digestTo*(data: openArray[byte]; T: type HashKey; forceRoot = false): 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
|
|
|
##
|
|
|
|
## If the argument `forceRoot` is set `true`, the `data` argument is always
|
|
|
|
## hashed.
|
|
|
|
##
|
|
|
|
## Otherwise it is only hashed if the `data` length is at least 32 bytes.
|
|
|
|
##
|
|
|
|
## Otherwise it is converted as-is to a `HashKey` type result.
|
|
|
|
##
|
2024-05-23 15:37:51 +00:00
|
|
|
if data.len == 0:
|
|
|
|
result.len = 0
|
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
|
|
|
elif data.len < 32 and not forceRoot:
|
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
|
|
|
|
result.buf = data.keccakHash.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 12:18:32 +00:00
|
|
|
func `$`*(key: Hash256): string =
|
|
|
|
let w = UInt256.fromBytesBE key.data
|
2023-10-27 21:36:51 +00:00
|
|
|
if w == high(UInt256):
|
2023-06-12 18:16:03 +00:00
|
|
|
"2^256-1"
|
2023-10-27 21:36:51 +00:00
|
|
|
elif w == 0.u256:
|
2023-06-12 18:16:03 +00:00
|
|
|
"0"
|
2023-10-27 21:36:51 +00:00
|
|
|
elif w == 2.u256.pow 255:
|
2023-06-12 18:16:03 +00:00
|
|
|
"2^255" # 800...
|
2023-10-27 21:36:51 +00:00
|
|
|
elif w == 2.u256.pow 254:
|
2023-06-12 18:16:03 +00:00
|
|
|
"2^254" # 400..
|
2023-10-27 21:36:51 +00:00
|
|
|
elif w == 2.u256.pow 253:
|
2023-06-12 18:16:03 +00:00
|
|
|
"2^253" # 200...
|
2023-10-27 21:36:51 +00:00
|
|
|
elif w == 2.u256.pow 251:
|
2023-06-12 18:16:03 +00:00
|
|
|
"2^252" # 100...
|
|
|
|
else:
|
2023-10-27 21:36:51 +00:00
|
|
|
w.toHex
|
2023-06-12 18:16:03 +00:00
|
|
|
|
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
|
|
|
|
# ------------------------------------------------------------------------------
|