Progress towards #991; Enable the distinct ssz.List type
This commit is contained in:
parent
ea95021073
commit
a99977b772
|
@ -115,17 +115,17 @@ proc testMerkleMinimal*(): bool =
|
|||
|
||||
block: # SSZ Sanity checks vs Python impl
|
||||
block: # 3 leaves
|
||||
let leaves = sszList(@[a, b, c], 3'i64)
|
||||
let leaves = List[Eth2Digest, 3](@[a, b, c])
|
||||
let root = hash_tree_root(leaves)
|
||||
doAssert $root == "9ff412e827b7c9d40fc7df2725021fd579ab762581d1ff5c270316682868456e".toUpperAscii
|
||||
|
||||
block: # 2^3 leaves
|
||||
let leaves = sszList(@[a, b, c], int64(1 shl 3))
|
||||
let leaves = List[Eth2Digest, int64(1 shl 3)](@[a, b, c])
|
||||
let root = hash_tree_root(leaves)
|
||||
doAssert $root == "5248085b588fab1dd1e03f3cd62201602b12e6560665935964f46e805977e8c5".toUpperAscii
|
||||
|
||||
block: # 2^10 leaves
|
||||
let leaves = sszList(@[a, b, c], int64(1 shl 10))
|
||||
let leaves = List[Eth2Digest, int64(1 shl 10)](@[a, b, c])
|
||||
let root = hash_tree_root(leaves)
|
||||
doAssert $root == "9fb7d518368dc14e8cc588fb3fd2749beef9f493fef70ae34af5721543c67173".toUpperAscii
|
||||
|
||||
|
|
|
@ -265,8 +265,7 @@ proc initialize_beacon_state_from_eth1*(
|
|||
validator.activation_epoch = GENESIS_EPOCH
|
||||
|
||||
# Set genesis validators root for domain separation and chain versioning
|
||||
state.genesis_validators_root =
|
||||
hash_tree_root(sszList(state.validators, VALIDATOR_REGISTRY_LIMIT))
|
||||
state.genesis_validators_root = hash_tree_root(state.validators)
|
||||
|
||||
state
|
||||
|
||||
|
@ -402,8 +401,10 @@ proc is_valid_indexed_attestation*(
|
|||
# https://github.com/status-im/nim-chronicles/issues/62
|
||||
|
||||
# Verify indices are sorted and unique
|
||||
# TODO: A simple loop can verify that the indicates are monotonically
|
||||
# increasing and non-repeating here!
|
||||
let indices = indexed_attestation.attesting_indices
|
||||
if indices != sorted(toHashSet(indices).toSeq, system.cmp):
|
||||
if indices.asSeq != sorted(toHashSet(indices.asSeq).toSeq, system.cmp):
|
||||
notice "indexed attestation: indices not sorted"
|
||||
return false
|
||||
|
||||
|
@ -457,7 +458,8 @@ func get_indexed_attestation*(state: BeaconState, attestation: Attestation,
|
|||
|
||||
IndexedAttestation(
|
||||
attesting_indices:
|
||||
sorted(mapIt(attesting_indices.toSeq, it.uint64), system.cmp),
|
||||
List[uint64, MAX_VALIDATORS_PER_COMMITTEE](
|
||||
sorted(mapIt(attesting_indices.toSeq, it.uint64), system.cmp)),
|
||||
data: attestation.data,
|
||||
signature: attestation.signature
|
||||
)
|
||||
|
|
|
@ -25,7 +25,10 @@
|
|||
import
|
||||
macros, hashes, json, strutils, tables,
|
||||
stew/[byteutils, bitseqs], chronicles,
|
||||
../ssz/types, ./crypto, ./digest
|
||||
../ssz/types as sszTypes, ./crypto, ./digest
|
||||
|
||||
export
|
||||
sszTypes
|
||||
|
||||
# TODO Data types:
|
||||
# Presently, we're reusing the data types from the serialization (uint64) in the
|
||||
|
@ -125,8 +128,6 @@ type
|
|||
Gwei* = uint64
|
||||
CommitteeIndex* = distinct uint64
|
||||
|
||||
BitList*[maxLen: static int] = distinct BitSeq
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.2/specs/phase0/beacon-chain.md#proposerslashing
|
||||
ProposerSlashing* = object
|
||||
signed_header_1*: SignedBeaconBlockHeader
|
||||
|
@ -273,7 +274,7 @@ type
|
|||
|
||||
# Registry
|
||||
validators*: List[Validator, VALIDATOR_REGISTRY_LIMIT]
|
||||
balances*: seq[uint64]
|
||||
balances*: List[uint64, VALIDATOR_REGISTRY_LIMIT]
|
||||
|
||||
# Randomness
|
||||
randao_mixes*: array[EPOCHS_PER_HISTORICAL_VECTOR, Eth2Digest]
|
||||
|
@ -405,43 +406,6 @@ type
|
|||
Table[Epoch, seq[ValidatorIndex]]
|
||||
committee_count_cache*: Table[Epoch, uint64]
|
||||
|
||||
macro fieldMaxLen*(x: typed): untyped =
|
||||
# TODO This macro is a temporary solution for the lack of a
|
||||
# more proper way to specify the max length of the List[T; N]
|
||||
# objects in the spec.
|
||||
# May be replaced with `getCustomPragma` once we upgrade to
|
||||
# Nim 0.20.2 or with a distinct List type, which would require
|
||||
# more substantial refactorings in the spec code.
|
||||
if x.kind != nnkDotExpr:
|
||||
return newLit(0)
|
||||
|
||||
let size = case $x[1]
|
||||
# Obsolete
|
||||
of "pubkeys",
|
||||
"compact_validators",
|
||||
"aggregation_bits",
|
||||
"custody_bits": int64(MAX_VALIDATORS_PER_COMMITTEE)
|
||||
# IndexedAttestation
|
||||
of "attesting_indices": MAX_VALIDATORS_PER_COMMITTEE
|
||||
# BeaconBlockBody
|
||||
of "proposer_slashings": MAX_PROPOSER_SLASHINGS
|
||||
of "attester_slashings": MAX_ATTESTER_SLASHINGS
|
||||
of "attestations": MAX_ATTESTATIONS
|
||||
of "deposits": MAX_DEPOSITS
|
||||
of "voluntary_exits": MAX_VOLUNTARY_EXITS
|
||||
# BeaconState
|
||||
of "historical_roots": HISTORICAL_ROOTS_LIMIT
|
||||
of "eth1_data_votes":
|
||||
EPOCHS_PER_ETH1_VOTING_PERIOD * SLOTS_PER_EPOCH
|
||||
of "validators": VALIDATOR_REGISTRY_LIMIT
|
||||
of "balances": VALIDATOR_REGISTRY_LIMIT
|
||||
of "previous_epoch_attestations",
|
||||
"current_epoch_attestations": MAX_ATTESTATIONS *
|
||||
SLOTS_PER_EPOCH
|
||||
else: 0
|
||||
|
||||
newLit size
|
||||
|
||||
func shortValidatorKey*(state: BeaconState, validatorIdx: int): string =
|
||||
($state.validators[validatorIdx].pubkey)[0..7]
|
||||
|
||||
|
@ -549,6 +513,14 @@ Json.useCustomSerialization(BitSeq):
|
|||
write:
|
||||
writer.writeValue "0x" & seq[byte](value).toHex
|
||||
|
||||
template readValue*(reader: var JsonReader, value: var List) =
|
||||
type T = type(value)
|
||||
type E = ElemType(T)
|
||||
value = T readValue(reader, seq[E])
|
||||
|
||||
template writeValue*(writer: var JsonWriter, value: List) =
|
||||
writeValue(writer, asSeq value)
|
||||
|
||||
template readValue*(reader: var JsonReader, value: var BitList) =
|
||||
type T = type(value)
|
||||
value = T readValue(reader, BitSeq)
|
||||
|
@ -565,32 +537,6 @@ template newClone*[T: not ref](x: T): ref T =
|
|||
template newClone*[T](x: ref T not nil): ref T =
|
||||
newClone(x[])
|
||||
|
||||
template init*(T: type BitList, len: int): auto = T init(BitSeq, len)
|
||||
template len*(x: BitList): auto = len(BitSeq(x))
|
||||
template bytes*(x: BitList): auto = bytes(BitSeq(x))
|
||||
template `[]`*(x: BitList, idx: auto): auto = BitSeq(x)[idx]
|
||||
template `[]=`*(x: var BitList, idx: auto, val: bool) = BitSeq(x)[idx] = val
|
||||
template `==`*(a, b: BitList): bool = BitSeq(a) == BitSeq(b)
|
||||
template setBit*(x: var BitList, idx: int) = setBit(BitSeq(x), idx)
|
||||
template clearBit*(x: var BitList, idx: int) = clearBit(BitSeq(x), idx)
|
||||
template overlaps*(a, b: BitList): bool = overlaps(BitSeq(a), BitSeq(b))
|
||||
template combine*(a: var BitList, b: BitList) = combine(BitSeq(a), BitSeq(b))
|
||||
template isSubsetOf*(a, b: BitList): bool = isSubsetOf(BitSeq(a), BitSeq(b))
|
||||
template `$`*(a: BitList): string = $(BitSeq(a))
|
||||
iterator items*(x: BitList): bool =
|
||||
for i in 0 ..< x.len:
|
||||
yield x[i]
|
||||
|
||||
when useListType:
|
||||
template len*[T; N](x: List[T, N]): auto = len(seq[T](x))
|
||||
template `[]`*[T; N](x: List[T, N], idx: auto): auto = seq[T](x)[idx]
|
||||
template `[]=`*[T; N](x: List[T, N], idx: auto, val: bool) = seq[T](x)[idx] = val
|
||||
template `==`*[T; N](a, b: List[T, N]): bool = seq[T](a) == seq[T](b)
|
||||
template asSeq*[T; N](x: List[T, N]): auto = seq[T](x)
|
||||
template `&`*[T; N](a, b: List[T, N]): List[T, N] = seq[T](a) & seq[T](b)
|
||||
else:
|
||||
template asSeq*[T; N](x: List[T, N]): auto = x
|
||||
|
||||
func `$`*(v: ForkDigest | Version): string =
|
||||
toHex(array[4, byte](v))
|
||||
|
||||
|
|
|
@ -133,7 +133,7 @@ proc process_randao(
|
|||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#eth1-data
|
||||
func process_eth1_data(state: var BeaconState, body: BeaconBlockBody) {.nbench.}=
|
||||
state.eth1_data_votes.add body.eth1_data
|
||||
if state.eth1_data_votes.count(body.eth1_data) * 2 > SLOTS_PER_ETH1_VOTING_PERIOD.int:
|
||||
if state.eth1_data_votes.asSeq.count(body.eth1_data) * 2 > SLOTS_PER_ETH1_VOTING_PERIOD.int:
|
||||
state.eth1_data = body.eth1_data
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.1/specs/phase0/beacon-chain.md#is_slashable_validator
|
||||
|
@ -250,8 +250,8 @@ proc process_attester_slashing*(
|
|||
var slashed_any = false
|
||||
|
||||
for index in sorted(toSeq(intersection(
|
||||
toHashSet(attestation_1.attesting_indices),
|
||||
toHashSet(attestation_2.attesting_indices)).items), system.cmp):
|
||||
toHashSet(attestation_1.attesting_indices.asSeq),
|
||||
toHashSet(attestation_2.attesting_indices.asSeq)).items), system.cmp):
|
||||
if is_slashable_validator(
|
||||
state.validators[index.int], get_current_epoch(state)):
|
||||
slash_validator(state, index.ValidatorIndex, stateCache)
|
||||
|
@ -478,9 +478,8 @@ proc makeBeaconBlock*(
|
|||
randao_reveal: randao_reveal,
|
||||
eth1_data: eth1data,
|
||||
graffiti: graffiti,
|
||||
attestations: attestations,
|
||||
deposits: deposits)
|
||||
)
|
||||
attestations: List[Attestation, MAX_ATTESTATIONS](attestations),
|
||||
deposits: List[Deposit, MAX_DEPOSITS](deposits)))
|
||||
|
||||
let tmpState = newClone(state)
|
||||
let ok = process_block(tmpState[], blck, {skipBlsValidation}, cache)
|
||||
|
|
|
@ -77,13 +77,13 @@ func get_total_active_balance*(state: BeaconState): Gwei =
|
|||
get_active_validator_indices(state, get_current_epoch(state)))
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.2/specs/phase0/beacon-chain.md#helper-functions-1
|
||||
func get_matching_source_attestations(state: BeaconState, epoch: Epoch):
|
||||
seq[PendingAttestation] =
|
||||
func get_matching_source_attestations(state: BeaconState,
|
||||
epoch: Epoch): seq[PendingAttestation] =
|
||||
doAssert epoch in [get_current_epoch(state), get_previous_epoch(state)]
|
||||
if epoch == get_current_epoch(state):
|
||||
state.current_epoch_attestations
|
||||
state.current_epoch_attestations.asSeq
|
||||
else:
|
||||
state.previous_epoch_attestations
|
||||
state.previous_epoch_attestations.asSeq
|
||||
|
||||
func get_matching_target_attestations(state: BeaconState, epoch: Epoch):
|
||||
seq[PendingAttestation] =
|
||||
|
@ -384,7 +384,7 @@ func process_final_updates*(state: var BeaconState) {.nbench.}=
|
|||
|
||||
# Reset eth1 data votes
|
||||
if next_epoch mod EPOCHS_PER_ETH1_VOTING_PERIOD == 0:
|
||||
state.eth1_data_votes = @[]
|
||||
state.eth1_data_votes = typeof(state.eth1_data_votes) @[]
|
||||
|
||||
# Update effective balances with hysteresis
|
||||
for index, validator in state.validators:
|
||||
|
@ -420,7 +420,7 @@ func process_final_updates*(state: var BeaconState) {.nbench.}=
|
|||
|
||||
# Rotate current/previous epoch attestations
|
||||
state.previous_epoch_attestations = state.current_epoch_attestations
|
||||
state.current_epoch_attestations = @[]
|
||||
state.current_epoch_attestations = typeof(state.current_epoch_attestations) @[]
|
||||
|
||||
# https://github.com/ethereum/eth2.0-specs/blob/v0.11.2/specs/phase0/beacon-chain.md#epoch-processing
|
||||
proc process_epoch*(state: var BeaconState, updateFlags: UpdateFlags)
|
||||
|
|
|
@ -13,9 +13,9 @@
|
|||
#{.push raises: [Defect].}
|
||||
|
||||
import
|
||||
stew/shims/macros, options, algorithm, options, strformat,
|
||||
options, algorithm, options, strformat, typetraits,
|
||||
stint, stew/[bitops2, bitseqs, objects, varints, ptrops],
|
||||
stew/ranges/[ptr_arith, stackarrays],
|
||||
stew/ranges/[ptr_arith, stackarrays], stew/shims/macros,
|
||||
faststreams/[inputs, outputs, buffers],
|
||||
serialization, serialization/testing/tracing,
|
||||
./spec/[crypto, datatypes, digest],
|
||||
|
@ -44,14 +44,12 @@ type
|
|||
SszWriter* = object
|
||||
stream: OutputStream
|
||||
|
||||
BasicType = char|bool|SomeUnsignedInt|StUint|ValidatorIndex
|
||||
BasicType = byte|char|bool|SomeUnsignedInt|StUint
|
||||
|
||||
SszChunksMerkleizer = object
|
||||
combinedChunks: StackArray[Eth2Digest]
|
||||
totalChunks: uint64
|
||||
|
||||
TypeWithMaxLen*[T; maxLen: static int64] = distinct T
|
||||
|
||||
SizePrefixed*[T] = distinct T
|
||||
SszMaxSizeExceeded* = object of SerializationError
|
||||
|
||||
|
@ -89,20 +87,6 @@ method formatMsg*(err: ref SszSizeMismatchError, filename: string): string {.gcs
|
|||
except CatchableError:
|
||||
"SSZ size mismatch"
|
||||
|
||||
when false:
|
||||
# TODO: Nim can't handle yet this simpler definition. File an issue.
|
||||
template valueOf[T; N](x: TypeWithMaxLen[T, N]): auto = T(x)
|
||||
else:
|
||||
proc unwrapImpl[T; N: static int64](x: ptr TypeWithMaxLen[T, N]): ptr T =
|
||||
cast[ptr T](x)
|
||||
|
||||
template valueOf(x: TypeWithMaxLen): auto =
|
||||
let xaddr = unsafeAddr x
|
||||
unwrapImpl(xaddr)[]
|
||||
|
||||
template sszList*(x: seq|array, maxLen: static int64): auto =
|
||||
TypeWithMaxLen[type(x), maxLen](x)
|
||||
|
||||
template toSszType*(x: auto): auto =
|
||||
mixin toSszType
|
||||
|
||||
|
@ -110,9 +94,8 @@ template toSszType*(x: auto): auto =
|
|||
elif x is Eth2Digest: x.data
|
||||
elif x is BlsCurveType: toRaw(x)
|
||||
elif x is BitSeq|BitList: ByteList(x)
|
||||
elif x is TypeWithMaxLen: toSszType valueOf(x)
|
||||
elif x is ForkDigest|Version: array[4, byte](x)
|
||||
elif useListType and x is List: seq[x.T](x)
|
||||
elif x is List: asSeq(x)
|
||||
else: x
|
||||
|
||||
proc writeFixedSized(s: var (OutputStream|WriteCursor), x: auto) {.raises: [Defect, IOError].} =
|
||||
|
@ -279,11 +262,6 @@ proc writeValue*[T](w: var SszWriter, x: SizePrefixed[T]) {.raises: [Defect, IOE
|
|||
buf.writeVarint length
|
||||
cursor.finalWrite buf.writtenBytes
|
||||
|
||||
template fromSszBytes*[T; N](_: type TypeWithMaxLen[T, N],
|
||||
bytes: openarray[byte]): auto =
|
||||
mixin fromSszBytes
|
||||
fromSszBytes(T, bytes)
|
||||
|
||||
proc readValue*[T](r: var SszReader, val: var T) {.raises: [Defect, MalformedSszError, SszSizeMismatchError, IOError].} =
|
||||
when isFixedSize(T):
|
||||
const minimalSize = fixedPortionSize(T)
|
||||
|
@ -435,18 +413,15 @@ template merkleizeFields(totalElements: int, body: untyped): Eth2Digest =
|
|||
addChunk(merkleizer, hash.data)
|
||||
trs "CHUNK ADDED"
|
||||
|
||||
template addField2(field) {.used.}=
|
||||
const maxLen = fieldMaxLen(field)
|
||||
when maxLen > 0:
|
||||
type FieldType = type field
|
||||
addField TypeWithMaxLen[FieldType, maxLen](field)
|
||||
else:
|
||||
addField field
|
||||
|
||||
body
|
||||
|
||||
getFinalHash(merkleizer)
|
||||
|
||||
template writeBytesLE(chunk: var array[bytesPerChunk, byte], atParam: int,
|
||||
val: SomeUnsignedInt|StUint) =
|
||||
let at = atParam
|
||||
chunk[at ..< at + sizeof(val)] = toBytesLE(val)
|
||||
|
||||
func chunkedHashTreeRootForBasicTypes[T](merkleizer: var SszChunksMerkleizer,
|
||||
arr: openarray[T]): Eth2Digest =
|
||||
static:
|
||||
|
@ -473,21 +448,17 @@ func chunkedHashTreeRootForBasicTypes[T](merkleizer: var SszChunksMerkleizer,
|
|||
|
||||
else:
|
||||
static:
|
||||
assert T is SomeUnsignedInt|StUInt|ValidatorIndex
|
||||
assert T is SomeUnsignedInt|StUInt
|
||||
assert bytesPerChunk mod sizeof(Т) == 0
|
||||
|
||||
const valuesPerChunk = bytesPerChunk div sizeof(Т)
|
||||
|
||||
template writeAt(chunk: var array[bytesPerChunk], at: int, val: SomeUnsignedInt|StUint) =
|
||||
type ArrType = type toBytesLE(val)
|
||||
(cast[ptr ArrType](addr chunk[at]))[] = toBytesLE(val)
|
||||
|
||||
var writtenValues = 0
|
||||
|
||||
var chunk: array[bytesPerChunk, byte]
|
||||
while writtenValues < arr.len - valuesPerChunk:
|
||||
for i in 0 ..< valuesPerChunk:
|
||||
chunk.writeAt(i * sizeof(T), arr[writtenValues + i])
|
||||
chunk.writeBytesLE(i * sizeof(T), arr[writtenValues + i])
|
||||
merkleizer.addChunk chunk
|
||||
inc writtenValues, valuesPerChunk
|
||||
|
||||
|
@ -495,7 +466,7 @@ func chunkedHashTreeRootForBasicTypes[T](merkleizer: var SszChunksMerkleizer,
|
|||
if remainingValues > 0:
|
||||
var lastChunk: array[bytesPerChunk, byte]
|
||||
for i in 0 ..< remainingValues:
|
||||
chunk.writeAt(i * sizeof(T), arr[writtenValues + i])
|
||||
chunk.writeBytesLE(i * sizeof(T), arr[writtenValues + i])
|
||||
merkleizer.addChunk lastChunk
|
||||
|
||||
getFinalHash(merkleizer)
|
||||
|
@ -552,9 +523,9 @@ func bitlistHashTreeRoot(merkleizer: var SszChunksMerkleizer, x: BitSeq): Eth2Di
|
|||
mixInLength contentsHash, x.len
|
||||
|
||||
func maxChunksCount(T: type, maxLen: int64): int64 =
|
||||
when T is BitList:
|
||||
when T is BitSeq|BitList:
|
||||
(maxLen + bitsPerChunk - 1) div bitsPerChunk
|
||||
elif T is seq|array:
|
||||
elif T is array|List|seq|openarray:
|
||||
type E = ElemType(T)
|
||||
when E is BasicType:
|
||||
(maxLen * sizeof(E) + bytesPerChunk - 1) div bytesPerChunk
|
||||
|
@ -563,45 +534,37 @@ func maxChunksCount(T: type, maxLen: int64): int64 =
|
|||
else:
|
||||
unsupported T # This should never happen
|
||||
|
||||
func hashTreeRootImpl[T](x: T): Eth2Digest =
|
||||
func hashTreeRootAux[T](x: T): Eth2Digest =
|
||||
when T is SignedBeaconBlock:
|
||||
unsupported T # Blocks are identified by htr(BeaconBlock) so we avoid these
|
||||
elif T is bool|char:
|
||||
result.data[0] = byte(x)
|
||||
elif T is SomeUnsignedInt|StUint|ValidatorIndex:
|
||||
elif T is SomeUnsignedInt|StUint:
|
||||
when cpuEndian == bigEndian:
|
||||
result.data[0..<sizeof(x)] = toBytesLE(x)
|
||||
else:
|
||||
copyMem(addr result.data[0], unsafeAddr x, sizeof x)
|
||||
elif (when T is array: ElemType(T) is byte and
|
||||
sizeof(T) == sizeof(Eth2Digest) else: false):
|
||||
# TODO is this sizeof comparison guranteed? it's whole structure vs field
|
||||
trs "ETH2DIGEST; IDENTITY MAPPING"
|
||||
Eth2Digest(data: x)
|
||||
elif (when T is array: ElemType(T) is BasicType else: false):
|
||||
trs "FIXED TYPE; USE CHUNK STREAM"
|
||||
var markleizer = createMerkleizer(maxChunksCount(T, x.len))
|
||||
chunkedHashTreeRootForBasicTypes(markleizer, x)
|
||||
elif T is string or (when T is (seq|openarray): ElemType(T) is BasicType else: false):
|
||||
trs "TYPE WITH LENGTH"
|
||||
var markleizer = createMerkleizer(maxChunksCount(T, x.len))
|
||||
mixInLength chunkedHashTreeRootForBasicTypes(markleizer, x), x.len
|
||||
type E = ElemType(T)
|
||||
when sizeof(T) <= sizeof(result.data):
|
||||
when E is byte|char|bool or cpuEndian == littleEndian:
|
||||
copyMem(addr result.data[0], unsafeAddr x, sizeof x)
|
||||
else:
|
||||
var pos = 0
|
||||
for e in x:
|
||||
writeBytesLE(result.data, pos, e)
|
||||
pos += sizeof(E)
|
||||
else:
|
||||
trs "FIXED TYPE; USE CHUNK STREAM"
|
||||
var markleizer = createMerkleizer(maxChunksCount(T, x.len))
|
||||
chunkedHashTreeRootForBasicTypes(markleizer, x)
|
||||
elif T is array|object|tuple:
|
||||
trs "MERKLEIZING FIELDS"
|
||||
const totalFields = when T is array: len(x)
|
||||
else: totalSerializedFields(T)
|
||||
merkleizeFields(totalFields):
|
||||
x.enumerateSubFields(f):
|
||||
const maxLen = fieldMaxLen(f)
|
||||
when maxLen > 0:
|
||||
type FieldType = type f
|
||||
addField TypeWithMaxLen[FieldType, maxLen](f)
|
||||
else:
|
||||
addField f
|
||||
elif T is seq:
|
||||
trs "SEQ WITH VAR SIZE"
|
||||
let hash = merkleizeFields(x.len, for e in x: addField e)
|
||||
mixInLength hash, x.len
|
||||
addField f
|
||||
#elif isCaseObject(T):
|
||||
# # TODO implement this
|
||||
else:
|
||||
|
@ -610,28 +573,26 @@ func hashTreeRootImpl[T](x: T): Eth2Digest =
|
|||
func hash_tree_root*(x: auto): Eth2Digest {.raises: [Defect], nbench.} =
|
||||
trs "STARTING HASH TREE ROOT FOR TYPE ", name(type(x))
|
||||
mixin toSszType
|
||||
when x is TypeWithMaxLen:
|
||||
const maxLen = x.maxLen
|
||||
type T = type valueOf(x)
|
||||
when x is List|BitList:
|
||||
const maxLen = static(x.maxLen)
|
||||
type T = type(x)
|
||||
const limit = maxChunksCount(T, maxLen)
|
||||
var merkleizer = createMerkleizer(limit)
|
||||
|
||||
when T is BitList:
|
||||
result = merkleizer.bitlistHashTreeRoot(BitSeq valueOf(x))
|
||||
elif T is seq:
|
||||
when x is BitList:
|
||||
result = merkleizer.bitlistHashTreeRoot(BitSeq x)
|
||||
else:
|
||||
type E = ElemType(T)
|
||||
let contentsHash = when E is BasicType:
|
||||
chunkedHashTreeRootForBasicTypes(merkleizer, valueOf(x))
|
||||
chunkedHashTreeRootForBasicTypes(merkleizer, asSeq x)
|
||||
else:
|
||||
for elem in valueOf(x):
|
||||
for elem in x:
|
||||
let elemHash = hash_tree_root(elem)
|
||||
merkleizer.addChunk(elemHash.data)
|
||||
merkleizer.getFinalHash()
|
||||
result = mixInLength(contentsHash, valueOf(x).len)
|
||||
else:
|
||||
unsupported T # This should never happen
|
||||
result = mixInLength(contentsHash, x.len)
|
||||
else:
|
||||
result = hashTreeRootImpl toSszType(x)
|
||||
result = hashTreeRootAux toSszType(x)
|
||||
|
||||
trs "HASH TREE ROOT FOR ", name(type x), " = ", "0x", $result
|
||||
|
||||
|
|
|
@ -97,7 +97,7 @@ func readSszValue*(input: openarray[byte], T: type): T {.raisesssz.} =
|
|||
template readOffset(n: int): int {.used.}=
|
||||
int fromSszBytes(uint32, input.toOpenArray(n, n + offsetSize - 1))
|
||||
|
||||
when useListType and result is List:
|
||||
when result is List:
|
||||
type ElemType = type result[0]
|
||||
result = T readSszValue(input, seq[ElemType])
|
||||
|
||||
|
@ -199,8 +199,16 @@ func readSszValue*(input: openarray[byte], T: type): T {.raisesssz.} =
|
|||
SszType)
|
||||
trs "READING COMPLETE ", fieldName
|
||||
|
||||
elif useListType and FieldType is List:
|
||||
field = readSszValue(
|
||||
elif FieldType is List:
|
||||
# TODO
|
||||
# The `typeof(field)` coercion below is required to deal with a Nim
|
||||
# bug. For some reason, Nim gets confused about the type of the list
|
||||
# returned from the `readSszValue` function. This could be a generics
|
||||
# caching issue caused by the use of distinct types. Such an issue
|
||||
# would be very scary in general, but in this particular situation
|
||||
# it shouldn't matter, because the different flavours of `List[T, N]`
|
||||
# won't produce different serializations.
|
||||
field = typeof(field) readSszValue(
|
||||
input.toOpenArray(startOffset, endOffset - 1),
|
||||
FieldType)
|
||||
|
||||
|
|
|
@ -120,7 +120,7 @@ template `[]`*[R, T](n: SszNavigator[array[R, T]], idx: int): SszNavigator[T] =
|
|||
func `[]`*[T](n: SszNavigator[T]): T {.raisesssz.} =
|
||||
mixin toSszType, fromSszBytes
|
||||
type SszRepr = type toSszType(declval T)
|
||||
when type(SszRepr) is type(T):
|
||||
when type(SszRepr) is type(T) or T is List:
|
||||
readSszValue(toOpenArray(n.m), T)
|
||||
else:
|
||||
fromSszBytes(T, toOpenArray(n.m))
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
{.push raises: [Defect].}
|
||||
|
||||
import
|
||||
tables, options,
|
||||
tables, options, typetraits,
|
||||
stew/shims/macros, stew/[objects, bitseqs],
|
||||
serialization/[object_serialization, errors]
|
||||
|
||||
const
|
||||
useListType* = false
|
||||
offsetSize* = 4
|
||||
|
||||
type
|
||||
|
@ -63,10 +62,41 @@ type
|
|||
of Field:
|
||||
discard
|
||||
|
||||
when useListType:
|
||||
type List*[T; maxLen: static int64] = distinct seq[T]
|
||||
else:
|
||||
type List*[T; maxLen: static int64] = seq[T]
|
||||
List*[T; maxLen: static int64] = distinct seq[T]
|
||||
BitList*[maxLen: static int] = distinct BitSeq
|
||||
|
||||
template add*(x: List, val: x.T) = add(distinctBase x, val)
|
||||
template len*(x: List): auto = len(distinctBase x)
|
||||
template low*(x: List): auto = low(distinctBase x)
|
||||
template high*(x: List): auto = high(distinctBase x)
|
||||
template `[]`*(x: List, idx: auto): auto = distinctBase(x)[idx]
|
||||
template `[]=`*[T; N](x: List[T, N], idx: auto, val: T) = seq[T](x)[idx] = val
|
||||
template `==`*(a, b: List): bool = distinctBase(a) == distinctBase(b)
|
||||
template asSeq*(x: List): auto = distinctBase x
|
||||
template `&`*[T; N](a, b: List[T, N]): List[T, N] = List[T, N](seq[T](a) & seq[T](b))
|
||||
template `$`*(x: List): auto = $(distinctBase x)
|
||||
|
||||
template items* (x: List): untyped = items(distinctBase x)
|
||||
template pairs* (x: List): untyped = pairs(distinctBase x)
|
||||
template mitems*(x: List): untyped = mitems(distinctBase x)
|
||||
template mpairs*(x: List): untyped = mpairs(distinctBase x)
|
||||
|
||||
template init*(T: type BitList, len: int): auto = T init(BitSeq, len)
|
||||
template len*(x: BitList): auto = len(BitSeq(x))
|
||||
template bytes*(x: BitList): auto = bytes(BitSeq(x))
|
||||
template `[]`*(x: BitList, idx: auto): auto = BitSeq(x)[idx]
|
||||
template `[]=`*(x: var BitList, idx: auto, val: bool) = BitSeq(x)[idx] = val
|
||||
template `==`*(a, b: BitList): bool = BitSeq(a) == BitSeq(b)
|
||||
template setBit*(x: var BitList, idx: int) = setBit(BitSeq(x), idx)
|
||||
template clearBit*(x: var BitList, idx: int) = clearBit(BitSeq(x), idx)
|
||||
template overlaps*(a, b: BitList): bool = overlaps(BitSeq(a), BitSeq(b))
|
||||
template combine*(a: var BitList, b: BitList) = combine(BitSeq(a), BitSeq(b))
|
||||
template isSubsetOf*(a, b: BitList): bool = isSubsetOf(BitSeq(a), BitSeq(b))
|
||||
template `$`*(a: BitList): string = $(BitSeq(a))
|
||||
|
||||
iterator items*(x: BitList): bool =
|
||||
for i in 0 ..< x.len:
|
||||
yield x[i]
|
||||
|
||||
macro unsupported*(T: typed): untyped =
|
||||
# TODO: {.fatal.} breaks compilation even in `compiles()` context,
|
||||
|
|
|
@ -54,7 +54,7 @@ proc addLocalValidator*(node: BeaconNode,
|
|||
privKey: ValidatorPrivKey) =
|
||||
let pubKey = privKey.toPubKey()
|
||||
|
||||
let idx = state.validators.findIt(it.pubKey == pubKey)
|
||||
let idx = state.validators.asSeq.findIt(it.pubKey == pubKey)
|
||||
if idx == -1:
|
||||
# We allow adding a validator even if its key is not in the state registry:
|
||||
# it might be that the deposit for this validator has not yet been processed
|
||||
|
|
|
@ -210,5 +210,5 @@ proc mockUpdateStateForNewDeposit*(
|
|||
# but confirmed by running it
|
||||
state.eth1_deposit_index = 0
|
||||
state.eth1_data.deposit_root =
|
||||
hash_tree_root(sszList(@[result.data], 2'i64^DEPOSIT_CONTRACT_TREE_DEPTH))
|
||||
hash_tree_root(List[DepositData, 2'i64^DEPOSIT_CONTRACT_TREE_DEPTH](@[result.data]))
|
||||
state.eth1_data.deposit_count = 1
|
||||
|
|
|
@ -27,9 +27,9 @@ proc addMockAttestations*(
|
|||
# Alias the attestations container
|
||||
var attestations: ptr seq[PendingAttestation]
|
||||
if state.get_current_epoch() == epoch:
|
||||
attestations = state.current_epoch_attestations.addr
|
||||
attestations = state.current_epoch_attestations.asSeq.addr
|
||||
elif state.get_previous_epoch() == epoch:
|
||||
attestations = state.previous_epoch_attestations.addr
|
||||
attestations = state.previous_epoch_attestations.asSeq.addr
|
||||
else:
|
||||
raise newException(ValueError, &"Cannot include attestations from epoch {state.get_current_epoch()} in epoch {epoch}")
|
||||
|
||||
|
|
|
@ -93,11 +93,11 @@ suiteReport "SSZ navigator":
|
|||
let b = [byte 0x04, 0x05, 0x06].toDigest
|
||||
let c = [byte 0x07, 0x08, 0x09].toDigest
|
||||
|
||||
let leaves = sszList(@[a, b, c], int64(1 shl 3))
|
||||
let leaves = List[Eth2Digest, int64(1 shl 3)](@[a, b, c])
|
||||
let root = hash_tree_root(leaves)
|
||||
check $root == "5248085B588FAB1DD1E03F3CD62201602B12E6560665935964F46E805977E8C5"
|
||||
|
||||
let leaves2 = sszList(@[a, b, c], int64(1 shl 10))
|
||||
let leaves2 = List[Eth2Digest, int64(1 shl 10)](@[a, b, c])
|
||||
let root2 = hash_tree_root(leaves2)
|
||||
check $root2 == "9FB7D518368DC14E8CC588FB3FD2749BEEF9F493FEF70AE34AF5721543C67173"
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 7ff764ca1f921ee709f05219aa740ab6c42872fc
|
||||
Subproject commit b61fcb51ad817d1d7bba6d6cd255dec3eb2f6987
|
Loading…
Reference in New Issue