nimbus-eth1/nimbus/sync/snap/path_desc.nim

219 lines
8.3 KiB
Nim
Raw Normal View History

# Nimbus - Types, data structures and shared utilities used in network sync
#
# Copyright (c) 2018-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.
import
eth/[common/eth_types, p2p],
stew/byteutils,
stint
{.push raises: [Defect].}
type
InteriorPath* = object
## Path to an interior node in an Ethereum hexary trie. This is a sequence
## of 0 to 64 hex digits. 0 digits means the root node, and 64 digits
## means a leaf node whose path hasn't been converted to `LeafPath` yet.
bytes: array[32, byte]
numDigits: byte
LeafPath* = object
## Path to a leaf in an Ethereum hexary trie. Individually, each leaf path
## is a hash, but rather than being the hash of the contents, it's the hash
## of the item's address. Collectively, these hashes have some 256-bit
## numerical properties: ordering, intervals and meaningful difference.
number: UInt256
LeafRange* = object
leafLow*, leafHigh*: LeafPath
const
interiorPathMaxDepth = 64
leafPathBytes = sizeof(LeafPath().number.toBytesBE)
# ------------------------------------------------------------------------------
# Public getters
# ------------------------------------------------------------------------------
proc maxDepth*(_: InteriorPath | typedesc[InteriorPath]): int =
interiorPathMaxDepth
proc depth*(path: InteriorPath): int =
path.numDigits.int
proc digit*(path: InteriorPath, index: int): int =
doAssert 0 <= index and index < path.depth
let b = path.bytes[index shr 1]
(if (index and 1) == 0: (b shr 4) else: (b and 0x0f)).int
proc low*(_: LeafPath | type LeafPath): LeafPath =
LeafPath(number: low(UInt256))
proc high*(_: LeafPath | type LeafPath): LeafPath =
LeafPath(number: high(UInt256))
# ------------------------------------------------------------------------------
# Public setters
# ------------------------------------------------------------------------------
# ------------------------------------------------------------------------------
# Public `InteriorPath` functions
# ------------------------------------------------------------------------------
proc toInteriorPath*(interiorPath: InteriorPath): InteriorPath =
interiorPath
proc toInteriorPath*(leafPath: LeafPath): InteriorPath =
doAssert sizeof(leafPath.number.toBytesBE) * 2 == interiorPathMaxDepth
doAssert sizeof(leafPath.number.toBytesBE) == sizeof(InteriorPath().bytes)
InteriorPath(bytes: leafPath.number.toBytesBE,
numDigits: interiorPathMaxDepth)
proc add*(path: var InteriorPath, digit: byte) =
doAssert path.numDigits < interiorPathMaxDepth
inc path.numDigits
if (path.numDigits and 1) != 0:
path.bytes[path.numDigits shr 1] = (digit shl 4)
else:
path.bytes[(path.numDigits shr 1) - 1] += (digit and 0x0f)
proc addPair*(path: var InteriorPath, digitPair: byte) =
doAssert path.numDigits < interiorPathMaxDepth - 1
path.numDigits += 2
if (path.numDigits and 1) == 0:
path.bytes[(path.numDigits shr 1) - 1] = digitPair
else:
path.bytes[(path.numDigits shr 1) - 1] += (digitPair shr 4)
path.bytes[path.numDigits shr 1] = (digitPair shl 4)
proc pop*(path: var InteriorPath) =
doAssert path.numDigits >= 1
dec path.numDigits
path.bytes[path.numDigits shr 1] =
if (path.numDigits and 1) == 0: 0.byte
else: path.bytes[path.numDigits shr 1] and 0xf0
# ------------------------------------------------------------------------------
# Public comparison functions for `InteriorPath`
# ------------------------------------------------------------------------------
proc `==`*(path1, path2: InteriorPath): bool =
# Paths are zero-padded to the end of the array, so comparison is easy.
for i in 0 ..< (max(path1.numDigits, path2.numDigits).int + 1) shr 1:
if path1.bytes[i] != path2.bytes[i]:
return false
return true
proc `<=`*(path1, path2: InteriorPath): bool =
# Paths are zero-padded to the end of the array, so comparison is easy.
for i in 0 ..< (max(path1.numDigits, path2.numDigits).int + 1) shr 1:
if path1.bytes[i] != path2.bytes[i]:
return path1.bytes[i] <= path2.bytes[i]
return true
proc cmp*(path1, path2: InteriorPath): int =
# Paths are zero-padded to the end of the array, so comparison is easy.
for i in 0 ..< (max(path1.numDigits, path2.numDigits).int + 1) shr 1:
if path1.bytes[i] != path2.bytes[i]:
return path1.bytes[i].int - path2.bytes[i].int
return 0
proc `!=`*(path1, path2: InteriorPath): bool = not(path1 == path2)
proc `<`*(path1, path2: InteriorPath): bool = not(path2 <= path1)
proc `>=`*(path1, path2: InteriorPath): bool = path2 <= path1
proc `>`*(path1, path2: InteriorPath): bool = not(path1 <= path2)
# ------------------------------------------------------------------------------
# Public string output functions for `LeafPath`
# ------------------------------------------------------------------------------
proc toHex*(path: InteriorPath, withEllipsis = true): string =
const hexChars = "0123456789abcdef"
let digits = path.depth
if not withEllipsis:
result = newString(digits)
else:
result = newString(min(digits + 3, 64))
result[^3] = '.'
result[^2] = '.'
result[^1] = '.'
for i in 0 ..< digits:
result[i] = hexChars[path.digit(i)]
proc pathRange*(path1, path2: InteriorPath): string =
path1.toHex(withEllipsis = false) & '-' & path2.toHex(withEllipsis = false)
proc `$`*(path: InteriorPath): string =
path.toHex
proc `$`*(paths: (InteriorPath, InteriorPath)): string =
pathRange(paths[0], paths[1])
# ------------------------------------------------------------------------------
# Public `LeafPath` functions
# ------------------------------------------------------------------------------
proc toLeafPath*(leafPath: LeafPath): LeafPath =
leafPath
proc toLeafPath*(interiorPath: InteriorPath): LeafPath =
doAssert interiorPath.depth == InteriorPath.maxDepth
doAssert sizeof(interiorPath.bytes) * 2 == InteriorPath.maxDepth
doAssert sizeof(interiorPath.bytes) == leafPathBytes
LeafPath(number: UInt256.fromBytesBE(interiorPath.bytes))
proc toLeafPath*(bytes: array[leafPathBytes, byte]): LeafPath =
doAssert sizeof(bytes) == leafPathBytes
LeafPath(number: UInt256.fromBytesBE(bytes))
proc toBytes*(leafPath: LeafPath): array[leafPathBytes, byte] =
doAssert sizeof(LeafPath().number.toBytesBE) == leafPathBytes
leafPath.number.toBytesBE
# Note, `{.borrow.}` didn't work for these symbols (with Nim 1.2.12) when we
# defined `LeafPath = distinct UInt256`. The `==` didn't match any symbol to
# borrow from, and the auto-generated `<` failed to compile, with a peculiar
# type mismatch error.
proc `==`*(path1, path2: LeafPath): bool = path1.number == path2.number
proc `!=`*(path1, path2: LeafPath): bool = path1.number != path2.number
proc `<`*(path1, path2: LeafPath): bool = path1.number < path2.number
proc `<=`*(path1, path2: LeafPath): bool = path1.number <= path2.number
proc `>`*(path1, path2: LeafPath): bool = path1.number > path2.number
proc `>=`*(path1, path2: LeafPath): bool = path1.number >= path2.number
proc cmp*(path1, path2: LeafPath): int = cmp(path1.number, path2.number)
proc `-`*(path1, path2: LeafPath): UInt256 =
path1.number - path2.number
proc `+`*(base: LeafPath, step: UInt256): LeafPath =
LeafPath(number: base.number + step)
proc `+`*(base: LeafPath, step: SomeInteger): LeafPath =
LeafPath(number: base.number + step.u256)
proc `-`*(base: LeafPath, step: UInt256): LeafPath =
LeafPath(number: base.number - step)
proc `-`*(base: LeafPath, step: SomeInteger): LeafPath =
LeafPath(number: base.number - step.u256)
# ------------------------------------------------------------------------------
# Public string output functions for `LeafPath`
# ------------------------------------------------------------------------------
proc toHex*(path: LeafPath): string =
path.number.toBytesBE.toHex
proc `$`*(path: LeafPath): string =
path.toHex
proc pathRange*(path1, path2: LeafPath): string =
path1.toHex & '-' & path2.toHex
# ------------------------------------------------------------------------------
# End
# ------------------------------------------------------------------------------