219 lines
8.3 KiB
Nim
219 lines
8.3 KiB
Nim
|
# 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
|
||
|
# ------------------------------------------------------------------------------
|