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

236 lines
7.8 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
std/math,
eth/common/eth_types,
stew/byteutils,
stint,
../../utils/interval_set,
../types
{.push raises: [Defect].}
type
LeafItem* =
distinct UInt256
LeafRange* = ##\
## Interval `[minPt,maxPt]` of` LeafItem` elements, can be managed in an
## `IntervalSet` data type.
Interval[LeafItem,UInt256]
LeafRangeSet* = ##\
## Managed structure to handle non-adjacent `LeafRange` intervals
IntervalSetRef[LeafItem,UInt256]
LeafItemData* = ##\
## Serialisation of `LeafItem`
array[32,byte]
InteriorPath* = object
## Path to an interior node in an Ethereum hexary trie.
bytes: LeafItemData ## at most 64 nibbles (unused nibbles must be zero)
nDigits: byte ## left prefix length, number of nibbles
#const
# interiorPathMaxDepth = 2 * sizeof(LeafItemData)
# ------------------------------------------------------------------------------
# Public helpers
# ------------------------------------------------------------------------------
proc to*(lp: LeafItem; T: type LeafItemData): T =
lp.UInt256.toBytesBE.T
proc to*(data: LeafItemData; T: type LeafItem): T =
UInt256.fromBytesBE(data).T
proc to*(n: SomeUnsignedInt; T: type LeafItem): T =
n.u256.T
proc to*(hash: UInt256; T: type LeafItem): T =
hash.T
#[
proc to*(ip: InteriorPath; T: type LeafItem): T =
ip.bytes.to(T)
proc to*(lp: LeafItem; T: type InteriorPath): T =
InteriorPath(bytes: lp.to(LeafItemData), numDigits: interiorPathMaxDepth)
# ------------------------------------------------------------------------------
# Public `InteriorPath` functions
# ------------------------------------------------------------------------------
proc maxDepth*(_: InteriorPath | typedesc[InteriorPath]): int =
interiorPathMaxDepth
proc depth*(ip: InteriorPath): int =
ip.numDigits.int
proc digit*(ip: InteriorPath, index: int): int =
doAssert 0 <= index and index < ip.depth
let b = ip.bytes[index shr 1]
(if (index and 1) == 0: (b shr 4) else: (b and 0x0f)).int
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 0 < path.numDigits
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 low*(T: type InteriorPath): T = low(UInt256).to(LeafItem).to(T)
proc high*(T: type InteriorPath): T = high(UInt256).to(LeafItem).to(T)
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 `<`*(path1, path2: InteriorPath): bool = not(path2 <= path1)
#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 prefix*(lp: LeafItem; digits: byte): InteriorPath =
## From the argument item `lp`, return the prefix made up by preserving the
## leading `digit` nibbles (ie. `(digits+1)/2` bytes.)
doAssert digits <= interiorPathMaxDepth
result = InteriorPath(
bytes: lp.to(LeafItemData),
numDigits: digits)
let tailInx = (digits + 1) shr 1
# reset the tail to zero
for inx in tailInx ..< interiorPathMaxDepth:
result.bytes[inx] = 0.byte
if (digits and 1) != 0: # fix leftlost nibble
result.bytes[digits shr 1] = result.bytes[digits shr 1] and 0xf0.byte
proc `in`*(ip: InteriorPath; iv: LeafRange): bool =
iv.minPt.prefix(ip.numDigits) <= ip and ip <= iv.maxPt.prefix(ip.numDigits)
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 `LeafItem` and `LeafRange` functions
# ------------------------------------------------------------------------------
proc u256*(lp: LeafItem): UInt256 = lp.UInt256
proc low*(T: type LeafItem): T = low(UInt256).T
proc high*(T: type LeafItem): T = high(UInt256).T
proc `+`*(a: LeafItem; b: UInt256): LeafItem = (a.u256+b).LeafItem
proc `-`*(a: LeafItem; b: UInt256): LeafItem = (a.u256-b).LeafItem
proc `-`*(a, b: LeafItem): UInt256 = (a.u256 - b.u256)
proc `==`*(a, b: LeafItem): bool = a.u256 == b.u256
proc `<=`*(a, b: LeafItem): bool = a.u256 <= b.u256
proc `<`*(a, b: LeafItem): bool = a.u256 < b.u256
# RLP serialisation for `LeafItem`.
proc read*(rlp: var Rlp, T: type LeafItem): T
{.gcsafe, raises: [Defect,RlpError]} =
rlp.read(LeafItemData).to(T)
proc append*(rlpWriter: var RlpWriter, leafPath: LeafItem) =
rlpWriter.append(leafPath.to(LeafItemData))
proc freeFactor*(lrs: LeafRangeSet): float =
## Free factor, ie. `#items-free / 2^256` to be used in statistics
if 0 < lrs.total:
((high(LeafItem) - lrs.total).u256 + 1).to(float) / (2.0^256)
elif lrs.chunks == 0:
1.0
else:
0.0
# Printing & pretty printing
proc toHex*(lp: LeafItem): string = lp.to(LeafItemData).toHex
proc `$`*(lp: LeafItem): string = lp.toHex
proc leafRangePp*(a, b: LeafItem): string =
## Needed for macro generated DSL files like `snap.nim` because the
## `distinct` flavour of `LeafItem` is discarded there.
result = "[" & $a
if a < b:
result &= ',' & (if b < high(LeafItem): $b else: "high")
result &= "]"
proc `$`*(a, b: LeafItem): string =
## Prettyfied prototype
leafRangePp(a,b)
proc `$`*(iv: LeafRange): string =
leafRangePp(iv.minPt, iv.maxPt)
# ------------------------------------------------------------------------------
# End
# ------------------------------------------------------------------------------