# 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 | @[, nibble-pair, ..] # 1 | 0001 | extension | @[, nibble-pair, ..] # 2 | 0010 | leaf | @[, nibble-pair, ..] # 3 | 0011 | leaf | @[, 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 # ------------------------------------------------------------------------------