2022-10-08 17:20:50 +00:00
|
|
|
# Nimbus
|
2022-08-04 08:04:30 +00:00
|
|
|
# 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
|
2022-09-16 07:24:12 +00:00
|
|
|
std/[math, sequtils, hashes],
|
2022-09-03 18:15:35 +00:00
|
|
|
eth/common/eth_types_rlp,
|
2022-08-04 08:04:30 +00:00
|
|
|
stew/[byteutils, interval_set],
|
|
|
|
stint,
|
|
|
|
../../constants,
|
2022-08-24 13:44:18 +00:00
|
|
|
../protocol,
|
2022-08-04 08:04:30 +00:00
|
|
|
../types
|
|
|
|
|
|
|
|
{.push raises: [Defect].}
|
|
|
|
|
|
|
|
type
|
2022-09-16 07:24:12 +00:00
|
|
|
ByteArray32* = array[32,byte]
|
|
|
|
## Used for 32 byte database keys
|
|
|
|
|
|
|
|
NodeKey* = distinct ByteArray32
|
|
|
|
## Hash key without the hash wrapper (as opposed to `NodeTag` which is a
|
|
|
|
## number)
|
|
|
|
|
2022-10-19 10:04:06 +00:00
|
|
|
NodeTag* = distinct UInt256
|
|
|
|
## Trie leaf item, account hash etc.
|
|
|
|
|
2022-10-14 16:40:32 +00:00
|
|
|
NodeTagRange* = Interval[NodeTag,UInt256]
|
2022-08-04 08:04:30 +00:00
|
|
|
## Interval `[minPt,maxPt]` of` NodeTag` elements, can be managed in an
|
|
|
|
## `IntervalSet` data type.
|
|
|
|
|
2022-10-14 16:40:32 +00:00
|
|
|
NodeTagRangeSet* = IntervalSetRef[NodeTag,UInt256]
|
|
|
|
## Managed structure to handle non-adjacent `NodeTagRange` intervals
|
2022-08-04 08:04:30 +00:00
|
|
|
|
2022-08-24 13:44:18 +00:00
|
|
|
PackedAccountRange* = object
|
|
|
|
## Re-packed version of `SnapAccountRange`. The reason why repacking is
|
|
|
|
## needed is that the `snap/1` protocol uses another RLP encoding than is
|
|
|
|
## used for storing in the database. So the `PackedAccount` is `BaseDB`
|
|
|
|
## trie compatible.
|
|
|
|
accounts*: seq[PackedAccount] ## List of re-packed accounts data
|
|
|
|
proof*: SnapAccountProof ## Boundary proofs
|
|
|
|
|
|
|
|
PackedAccount* = object
|
|
|
|
## In fact, the `snap/1` driver returns the `Account` structure which is
|
|
|
|
## unwanted overhead, gere.
|
|
|
|
accHash*: Hash256
|
|
|
|
accBlob*: Blob
|
|
|
|
|
2022-09-02 18:16:09 +00:00
|
|
|
AccountSlotsHeader* = object
|
|
|
|
## Storage root header
|
2022-10-14 16:40:32 +00:00
|
|
|
accHash*: Hash256 ## Owner account, maybe unnecessary
|
|
|
|
storageRoot*: Hash256 ## Start of storage tree
|
|
|
|
subRange*: Option[NodeTagRange] ## Sub-range of slot range covered
|
2022-09-02 18:16:09 +00:00
|
|
|
|
|
|
|
AccountStorageRange* = object
|
|
|
|
## List of storage descriptors, the last `AccountSlots` storage data might
|
|
|
|
## be incomplete and tthe `proof` is needed for proving validity.
|
2022-10-14 16:40:32 +00:00
|
|
|
storages*: seq[AccountSlots] ## List of accounts and storage data
|
|
|
|
proof*: SnapStorageProof ## Boundary proofs for last entry
|
2022-09-02 18:16:09 +00:00
|
|
|
|
|
|
|
AccountSlots* = object
|
|
|
|
## Account storage descriptor
|
|
|
|
account*: AccountSlotsHeader
|
|
|
|
data*: seq[SnapStorage]
|
|
|
|
|
2022-08-04 08:04:30 +00:00
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# Public helpers
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
|
2022-09-16 07:24:12 +00:00
|
|
|
proc to*(tag: NodeTag; T: type Hash256): T =
|
2022-08-04 08:04:30 +00:00
|
|
|
## Convert to serialised equivalent
|
2022-09-16 07:24:12 +00:00
|
|
|
result.data = tag.UInt256.toBytesBE
|
|
|
|
|
|
|
|
proc to*(key: NodeKey; T: type NodeTag): T =
|
|
|
|
## Convert from serialised equivalent
|
|
|
|
UInt256.fromBytesBE(key.ByteArray32).T
|
2022-08-04 08:04:30 +00:00
|
|
|
|
2022-09-16 07:24:12 +00:00
|
|
|
proc to*(key: Hash256; T: type NodeTag): T =
|
2022-08-04 08:04:30 +00:00
|
|
|
## Syntactic sugar
|
2022-09-16 07:24:12 +00:00
|
|
|
key.data.NodeKey.to(T)
|
2022-08-04 08:04:30 +00:00
|
|
|
|
2022-09-16 07:24:12 +00:00
|
|
|
proc to*(tag: NodeTag; T: type NodeKey): T =
|
|
|
|
## Syntactic sugar
|
|
|
|
tag.UInt256.toBytesBE.T
|
|
|
|
|
|
|
|
proc to*(hash: Hash256; T: type NodeKey): T =
|
|
|
|
## Syntactic sugar
|
|
|
|
hash.data.NodeKey
|
2022-08-04 08:04:30 +00:00
|
|
|
|
2022-10-19 10:04:06 +00:00
|
|
|
proc to*(key: NodeKey; T: type Hash256): T =
|
|
|
|
## Syntactic sugar
|
|
|
|
T(data: key.ByteArray32)
|
|
|
|
|
2022-09-16 07:24:12 +00:00
|
|
|
proc to*(key: NodeKey; T: type Blob): T =
|
2022-08-04 08:04:30 +00:00
|
|
|
## Syntactic sugar
|
2022-09-16 07:24:12 +00:00
|
|
|
key.ByteArray32.toSeq
|
2022-08-04 08:04:30 +00:00
|
|
|
|
|
|
|
proc to*(n: SomeUnsignedInt|UInt256; T: type NodeTag): T =
|
|
|
|
## Syntactic sugar
|
|
|
|
n.u256.T
|
|
|
|
|
2022-10-19 10:04:06 +00:00
|
|
|
|
|
|
|
proc hash*(a: NodeKey): Hash =
|
|
|
|
## Table/KeyedQueue mixin
|
|
|
|
a.ByteArray32.hash
|
|
|
|
|
|
|
|
proc `==`*(a, b: NodeKey): bool =
|
|
|
|
## Table/KeyedQueue mixin
|
|
|
|
a.ByteArray32 == b.ByteArray32
|
|
|
|
|
2022-08-04 08:04:30 +00:00
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# Public constructors
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
|
2022-09-16 07:24:12 +00:00
|
|
|
proc init*(key: var NodeKey; data: openArray[byte]): bool =
|
2022-10-14 16:40:32 +00:00
|
|
|
## Import argument `data` into `key` which must have length either `32`, or
|
2022-09-16 07:24:12 +00:00
|
|
|
## `0`. The latter case is equivalent to an all zero byte array of size `32`.
|
2022-08-04 08:04:30 +00:00
|
|
|
if data.len == 32:
|
2022-09-16 07:24:12 +00:00
|
|
|
(addr key.ByteArray32[0]).copyMem(unsafeAddr data[0], data.len)
|
2022-08-04 08:04:30 +00:00
|
|
|
return true
|
|
|
|
elif data.len == 0:
|
2022-09-16 07:24:12 +00:00
|
|
|
key.reset
|
2022-08-04 08:04:30 +00:00
|
|
|
return true
|
|
|
|
|
2022-09-16 07:24:12 +00:00
|
|
|
proc init*(tag: var NodeTag; data: openArray[byte]): bool =
|
|
|
|
## Similar to `init(key: var NodeHash; .)`.
|
|
|
|
var key: NodeKey
|
|
|
|
if key.init(data):
|
|
|
|
tag = key.to(NodeTag)
|
2022-08-04 08:04:30 +00:00
|
|
|
return true
|
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# Public rlp support
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
proc read*(rlp: var Rlp, T: type NodeTag): T
|
2022-08-24 13:44:18 +00:00
|
|
|
{.gcsafe, raises: [Defect,RlpError].} =
|
2022-08-04 08:04:30 +00:00
|
|
|
rlp.read(Hash256).to(T)
|
|
|
|
|
|
|
|
proc append*(writer: var RlpWriter, nid: NodeTag) =
|
|
|
|
writer.append(nid.to(Hash256))
|
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
2022-10-14 16:40:32 +00:00
|
|
|
# Public `NodeTag` and `NodeTagRange` functions
|
2022-08-04 08:04:30 +00:00
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
proc u256*(lp: NodeTag): UInt256 = lp.UInt256
|
|
|
|
proc low*(T: type NodeTag): T = low(UInt256).T
|
|
|
|
proc high*(T: type NodeTag): T = high(UInt256).T
|
|
|
|
|
|
|
|
proc `+`*(a: NodeTag; b: UInt256): NodeTag = (a.u256+b).NodeTag
|
|
|
|
proc `-`*(a: NodeTag; b: UInt256): NodeTag = (a.u256-b).NodeTag
|
|
|
|
proc `-`*(a, b: NodeTag): UInt256 = (a.u256 - b.u256)
|
|
|
|
|
|
|
|
proc `==`*(a, b: NodeTag): bool = a.u256 == b.u256
|
|
|
|
proc `<=`*(a, b: NodeTag): bool = a.u256 <= b.u256
|
|
|
|
proc `<`*(a, b: NodeTag): bool = a.u256 < b.u256
|
|
|
|
|
2022-08-12 15:42:07 +00:00
|
|
|
proc cmp*(x, y: NodeTag): int = cmp(x.UInt256, y.UInt256)
|
|
|
|
|
2022-08-04 08:04:30 +00:00
|
|
|
proc hash*(a: NodeTag): Hash =
|
|
|
|
## Mixin for `Table` or `keyedQueue`
|
|
|
|
a.to(Hash256).data.hash
|
|
|
|
|
|
|
|
proc digestTo*(data: Blob; T: type NodeTag): T =
|
|
|
|
## Hash the `data` argument
|
2022-09-03 18:15:35 +00:00
|
|
|
keccakHash(data).to(T)
|
2022-08-04 08:04:30 +00:00
|
|
|
|
2022-10-14 16:40:32 +00:00
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# Public functions: `NodeTagRange` helpers
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
proc isEmpty*(lrs: NodeTagRangeSet): bool =
|
|
|
|
## Returns `true` if the argument set `lrs` of intervals is empty
|
|
|
|
lrs.total == 0 and lrs.chunks == 0
|
|
|
|
|
|
|
|
proc isEmpty*(lrs: openArray[NodeTagRangeSet]): bool =
|
|
|
|
## Variant of `isEmpty()` where intervals are distributed across several
|
|
|
|
## sets.
|
|
|
|
for ivSet in lrs:
|
|
|
|
if 0 < ivSet.total or 0 < ivSet.chunks:
|
|
|
|
return false
|
2022-10-20 16:59:54 +00:00
|
|
|
true
|
2022-10-08 17:20:50 +00:00
|
|
|
|
2022-10-14 16:40:32 +00:00
|
|
|
proc isFull*(lrs: NodeTagRangeSet): bool =
|
|
|
|
## Returns `true` if the argument set `lrs` contains of the single
|
|
|
|
## interval [low(NodeTag),high(NodeTag)].
|
|
|
|
lrs.total == 0 and 0 < lrs.chunks
|
|
|
|
|
|
|
|
|
|
|
|
proc emptyFactor*(lrs: NodeTagRangeSet): float =
|
2022-10-08 17:20:50 +00:00
|
|
|
## Relative uncovered total, i.e. `#points-not-covered / 2^256` to be used
|
|
|
|
## in statistics or triggers.
|
2022-08-04 08:04:30 +00:00
|
|
|
if 0 < lrs.total:
|
|
|
|
((high(NodeTag) - lrs.total).u256 + 1).to(float) / (2.0^256)
|
|
|
|
elif lrs.chunks == 0:
|
2022-08-17 07:30:11 +00:00
|
|
|
1.0 # `total` represents the residue class `mod 2^256` from `0`..`(2^256-1)`
|
2022-08-04 08:04:30 +00:00
|
|
|
else:
|
2022-10-08 17:20:50 +00:00
|
|
|
0.0 # number of points in `lrs` is `2^256 + 1`
|
|
|
|
|
2022-10-14 16:40:32 +00:00
|
|
|
proc emptyFactor*(lrs: openArray[NodeTagRangeSet]): float =
|
2022-10-08 17:20:50 +00:00
|
|
|
## Variant of `emptyFactor()` where intervals are distributed across several
|
|
|
|
## sets. This function makes sense only if the interval sets are mutually
|
|
|
|
## disjunct.
|
2022-10-19 10:04:06 +00:00
|
|
|
var accu: NodeTag
|
2022-10-08 17:20:50 +00:00
|
|
|
for ivSet in lrs:
|
|
|
|
if 0 < ivSet.total:
|
|
|
|
if high(NodeTag) - ivSet.total < accu:
|
|
|
|
return 0.0
|
|
|
|
accu = accu + ivSet.total
|
|
|
|
elif ivSet.chunks == 0:
|
|
|
|
discard
|
|
|
|
else: # number of points in `ivSet` is `2^256 + 1`
|
|
|
|
return 0.0
|
2022-10-14 16:40:32 +00:00
|
|
|
if accu == 0.to(NodeTag):
|
|
|
|
return 1.0
|
2022-10-08 17:20:50 +00:00
|
|
|
((high(NodeTag) - accu).u256 + 1).to(float) / (2.0^256)
|
2022-08-04 08:04:30 +00:00
|
|
|
|
2022-10-19 10:04:06 +00:00
|
|
|
|
2022-10-14 16:40:32 +00:00
|
|
|
proc fullFactor*(lrs: NodeTagRangeSet): float =
|
2022-10-08 17:20:50 +00:00
|
|
|
## Relative covered total, i.e. `#points-covered / 2^256` to be used
|
|
|
|
## in statistics or triggers
|
2022-08-17 07:30:11 +00:00
|
|
|
if 0 < lrs.total:
|
|
|
|
lrs.total.u256.to(float) / (2.0^256)
|
|
|
|
elif lrs.chunks == 0:
|
2022-10-08 17:20:50 +00:00
|
|
|
0.0 # `total` represents the residue class `mod 2^256` from `0`..`(2^256-1)`
|
2022-08-17 07:30:11 +00:00
|
|
|
else:
|
2022-10-08 17:20:50 +00:00
|
|
|
1.0 # number of points in `lrs` is `2^256 + 1`
|
|
|
|
|
2022-10-19 10:04:06 +00:00
|
|
|
proc fullFactor*(lrs: openArray[NodeTagRangeSet]): float =
|
|
|
|
## Variant of `fullFactor()` where intervals are distributed across several
|
|
|
|
## sets. This function makes sense only if the interval sets are mutually
|
|
|
|
## disjunct.
|
|
|
|
var accu: NodeTag
|
|
|
|
for ivSet in lrs:
|
|
|
|
if 0 < ivSet.total:
|
|
|
|
if high(NodeTag) - ivSet.total < accu:
|
|
|
|
return 1.0
|
|
|
|
accu = accu + ivSet.total
|
|
|
|
elif ivSet.chunks == 0:
|
|
|
|
discard
|
|
|
|
else: # number of points in `ivSet` is `2^256 + 1`
|
|
|
|
return 1.0
|
|
|
|
if accu == 0.to(NodeTag):
|
|
|
|
return 0.0
|
|
|
|
accu.u256.to(float) / (2.0^256)
|
|
|
|
|
2022-10-14 16:40:32 +00:00
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# Public functions: printing & pretty printing
|
|
|
|
# ------------------------------------------------------------------------------
|
2022-10-08 17:20:50 +00:00
|
|
|
|
2022-08-04 08:04:30 +00:00
|
|
|
proc `$`*(nt: NodeTag): string =
|
|
|
|
if nt == high(NodeTag):
|
|
|
|
"high(NodeTag)"
|
|
|
|
elif nt == 0.u256.NodeTag:
|
|
|
|
"0"
|
|
|
|
else:
|
|
|
|
nt.to(Hash256).data.toHex
|
|
|
|
|
|
|
|
proc leafRangePp*(a, b: NodeTag): string =
|
|
|
|
## Needed for macro generated DSL files like `snap.nim` because the
|
|
|
|
## `distinct` flavour of `NodeTag` is discarded there.
|
|
|
|
result = "[" & $a
|
|
|
|
if a != b:
|
|
|
|
result &= ',' & $b
|
|
|
|
result &= "]"
|
|
|
|
|
|
|
|
proc `$`*(a, b: NodeTag): string =
|
|
|
|
## Prettyfied prototype
|
|
|
|
leafRangePp(a,b)
|
|
|
|
|
2022-10-14 16:40:32 +00:00
|
|
|
proc `$`*(iv: NodeTagRange): string =
|
2022-08-04 08:04:30 +00:00
|
|
|
leafRangePp(iv.minPt, iv.maxPt)
|
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# End
|
|
|
|
# ------------------------------------------------------------------------------
|