document and clean up `ValidatorIndex` usage (#3651)

* document static vs dynamic range checking requirements
* add `vindices` iterator to iterate over valid validator indices in a
state
* clean up spec comments in general

* fixup

Co-authored-by: tersec <tersec@users.noreply.github.com>
This commit is contained in:
Jacek Sieka 2022-05-24 01:39:08 +02:00 committed by GitHub
parent c73239f60b
commit 1101c745b9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 327 additions and 338 deletions

View File

@ -676,7 +676,12 @@ proc validateAggregate*(
# [REJECT] The aggregator's validator index is within the committee -- i.e.
# aggregate_and_proof.aggregator_index in get_beacon_committee(state,
# aggregate.data.slot, aggregate.data.index).
if aggregate_and_proof.aggregator_index.ValidatorIndex notin
let aggregator_index =
ValidatorIndex.init(aggregate_and_proof.aggregator_index).valueOr:
return checkedReject("Aggregate: invalid aggregator index")
if aggregator_index notin
get_beacon_committee(epochRef, slot, committee_index):
return checkedReject("Aggregate: aggregator's validator index not in committee")

View File

@ -963,9 +963,10 @@ func get_sync_committee_cache*(
s.incl(pk)
var pubkeyIndices: Table[ValidatorPubKey, ValidatorIndex]
for i, v in state.validators:
if v.pubkey in s:
pubkeyIndices[v.pubkey] = i.ValidatorIndex
for vidx in state.validators.vindices:
let pubkey = state.validators[vidx].pubkey
if pubkey in s:
pubkeyIndices[pubkey] = vidx
var res: SyncCommitteeCache
try:

View File

@ -5,17 +5,8 @@
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
# at your option. This file may not be copied, modified, or distributed except according to those terms.
# This file contains data types that are part of the spec and thus subject to
# serialization and spec updates.
#
# The spec folder in general contains code that has been hoisted from the
# specification and that follows the spec as closely as possible, so as to make
# it easy to keep up-to-date.
#
# These datatypes are used as specifications for serialization - thus should not
# be altered outside of what the spec says. Likewise, they should not be made
# `ref` - this can be achieved by wrapping them in higher-level
# types / composition
# Types specific to altair (ie known to have changed across hard forks) - see
# `base` for types and guidelines common across forks
# TODO Careful, not nil analysis is broken / incomplete and the semantics will
# likely change in future versions of the language:
@ -106,43 +97,43 @@ type
# https://github.com/ethereum/consensus-specs/blob/v1.2.0-rc.1/specs/altair/validator.md#synccommitteemessage
SyncCommitteeMessage* = object
slot*: Slot ##\
## Slot to which this contribution pertains
slot*: Slot
## Slot to which this contribution pertains
beacon_block_root*: Eth2Digest ##\
## Block root for this signature
beacon_block_root*: Eth2Digest
## Block root for this signature
validator_index*: uint64 ##\
## Index of the validator that produced this signature
validator_index*: uint64 # `ValidatorIndex` after validation
## Index of the validator that produced this signature
signature*: ValidatorSig ##\
## Signature by the validator over the block root of `slot`
signature*: ValidatorSig
## Signature by the validator over the block root of `slot`
# https://github.com/ethereum/consensus-specs/blob/v1.2.0-rc.1/specs/altair/validator.md#synccommitteecontribution
SyncCommitteeAggregationBits* =
BitArray[SYNC_SUBCOMMITTEE_SIZE]
SyncCommitteeContribution* = object
slot*: Slot ##\
## Slot to which this contribution pertains
slot*: Slot
## Slot to which this contribution pertains
beacon_block_root*: Eth2Digest ##\
## Block root for this contribution
beacon_block_root*: Eth2Digest
## Block root for this contribution
subcommittee_index*: uint64 ##\
## The subcommittee this contribution pertains to out of the broader sync
## committee
subcommittee_index*: uint64 # `SyncSubcommitteeIndex` after validation
## The subcommittee this contribution pertains to out of the broader sync
## committee
aggregation_bits*: SyncCommitteeAggregationBits ##\
## A bit is set if a signature from the validator at the corresponding
## index in the subcommittee is present in the aggregate `signature`.
aggregation_bits*: SyncCommitteeAggregationBits
## A bit is set if a signature from the validator at the corresponding
## index in the subcommittee is present in the aggregate `signature`.
signature*: ValidatorSig ##\
## Signature by the validator(s) over the block root of `slot`
signature*: ValidatorSig
## Signature by the validator(s) over the block root of `slot`
# https://github.com/ethereum/consensus-specs/blob/v1.2.0-rc.1/specs/altair/validator.md#contributionandproof
ContributionAndProof* = object
aggregator_index*: uint64
aggregator_index*: uint64 # `ValidatorIndex` after validation
contribution*: SyncCommitteeContribution
selection_proof*: ValidatorSig
@ -154,27 +145,29 @@ type
# https://github.com/ethereum/consensus-specs/blob/v1.2.0-rc.1/specs/altair/validator.md#syncaggregatorselectiondata
SyncAggregatorSelectionData* = object
slot*: Slot
subcommittee_index*: uint64
subcommittee_index*: uint64 # `SyncSubcommitteeIndex` after validation
### Modified/overloaded
# https://github.com/ethereum/consensus-specs/blob/vFuture/specs/altair/sync-protocol.md#lightclientbootstrap
LightClientBootstrap* = object
# The requested beacon block header
header*: BeaconBlockHeader
## The requested beacon block header
# Current sync committee corresponding to `header`
current_sync_committee*: SyncCommittee
## Current sync committee corresponding to `header`
current_sync_committee_branch*:
array[log2trunc(CURRENT_SYNC_COMMITTEE_INDEX), Eth2Digest]
# https://github.com/ethereum/consensus-specs/blob/vFuture/specs/altair/sync-protocol.md#lightclientupdate
LightClientUpdate* = object
# The beacon block header that is attested to by the sync committee
attested_header*: BeaconBlockHeader
## The beacon block header that is attested to by the sync committee
# Next sync committee corresponding to `attested_header`
next_sync_committee*: SyncCommittee
## Next sync committee corresponding to `attested_header`,
## if signature is from current sync committee
next_sync_committee_branch*:
array[log2trunc(NEXT_SYNC_COMMITTEE_INDEX), Eth2Digest]
@ -183,10 +176,9 @@ type
finality_branch*:
array[log2trunc(FINALIZED_ROOT_INDEX), Eth2Digest]
# Sync committee aggregate signature
sync_aggregate*: SyncAggregate
# Slot at which the aggregate signature was created (untrusted)
signature_slot*: Slot
## Slot at which the aggregate signature was created (untrusted)
# https://github.com/ethereum/consensus-specs/blob/vFuture/specs/altair/sync-protocol.md#lightclientfinalityupdate
LightClientFinalityUpdate* = object
@ -231,22 +223,22 @@ type
# https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/altair/sync-protocol.md#lightclientstore
LightClientStore* = object
finalized_header*: BeaconBlockHeader ##\
## Beacon block header that is finalized
finalized_header*: BeaconBlockHeader
## Beacon block header that is finalized
# Sync committees corresponding to the header
current_sync_committee*: SyncCommittee
## Sync committees corresponding to the header
next_sync_committee*: SyncCommittee
best_valid_update*: Option[LightClientUpdate] ##\
## Best available header to switch finalized head to if we see nothing else
best_valid_update*: Option[LightClientUpdate]
## Best available header to switch finalized head to if we see nothing else
optimistic_header*: BeaconBlockHeader ##\
## Most recent available reasonably-safe header
optimistic_header*: BeaconBlockHeader
## Most recent available reasonably-safe header
# Max number of active participants in a sync committee (used to calculate
# safety threshold)
previous_max_active_participants*: uint64
## Max number of active participants in a sync committee (used to compute
## safety threshold)
current_max_active_participants*: uint64
# https://github.com/ethereum/consensus-specs/blob/v1.2.0-rc.1/specs/altair/beacon-chain.md#beaconstate
@ -258,11 +250,11 @@ type
fork*: Fork
# History
latest_block_header*: BeaconBlockHeader ##\
## `latest_block_header.state_root == ZERO_HASH` temporarily
latest_block_header*: BeaconBlockHeader
## `latest_block_header.state_root == ZERO_HASH` temporarily
block_roots*: HashArray[Limit SLOTS_PER_HISTORICAL_ROOT, Eth2Digest] ##\
## Needed to process attestations, older to newer
block_roots*: HashArray[Limit SLOTS_PER_HISTORICAL_ROOT, Eth2Digest]
## Needed to process attestations, older to newer
state_roots*: HashArray[Limit SLOTS_PER_HISTORICAL_ROOT, Eth2Digest]
historical_roots*: HashList[Eth2Digest, Limit HISTORICAL_ROOTS_LIMIT]
@ -275,14 +267,14 @@ type
# Registry
validators*: HashList[Validator, Limit VALIDATOR_REGISTRY_LIMIT]
balances*: HashList[uint64, Limit VALIDATOR_REGISTRY_LIMIT]
balances*: HashList[Gwei, Limit VALIDATOR_REGISTRY_LIMIT]
# Randomness
randao_mixes*: HashArray[Limit EPOCHS_PER_HISTORICAL_VECTOR, Eth2Digest]
# Slashings
slashings*: HashArray[Limit EPOCHS_PER_SLASHINGS_VECTOR, uint64] ##\
## Per-epoch sums of slashed effective balances
slashings*: HashArray[Limit EPOCHS_PER_SLASHINGS_VECTOR, Gwei]
## Per-epoch sums of slashed effective balances
# Participation
previous_epoch_participation*: EpochParticipationFlags
@ -291,8 +283,8 @@ type
# Finality
justification_bits*: JustificationBits
previous_justified_checkpoint*: Checkpoint ##\
## Previous epoch snapshot
previous_justified_checkpoint*: Checkpoint
## Previous epoch snapshot
current_justified_checkpoint*: Checkpoint
finalized_checkpoint*: Checkpoint
@ -343,27 +335,28 @@ type
## is formed.
slot*: Slot
proposer_index*: uint64
proposer_index*: uint64 # `ValidatorIndex` after validation
parent_root*: Eth2Digest ##\
## Root hash of the previous block
parent_root*: Eth2Digest
## Root hash of the previous block
state_root*: Eth2Digest ##\
## The state root, _after_ this block has been processed
state_root*: Eth2Digest
## The state root, _after_ this block has been processed
body*: BeaconBlockBody
SigVerifiedBeaconBlock* = object
## A BeaconBlock that contains verified signatures
## but that has not been verified for state transition
slot*: Slot
proposer_index*: uint64
proposer_index*: uint64 # `ValidatorIndex` after validation
parent_root*: Eth2Digest ##\
## Root hash of the previous block
parent_root*: Eth2Digest
## Root hash of the previous block
state_root*: Eth2Digest ##\
## The state root, _after_ this block has been processed
state_root*: Eth2Digest
## The state root, _after_ this block has been processed
body*: SigVerifiedBeaconBlockBody
@ -385,19 +378,19 @@ type
## then, the type must be manually kept compatible with its untrusted
## cousin.
slot*: Slot
proposer_index*: uint64
parent_root*: Eth2Digest ##\
state_root*: Eth2Digest ##\
proposer_index*: uint64 # `ValidatorIndex` after validation
parent_root*: Eth2Digest
state_root*: Eth2Digest
body*: TrustedBeaconBlockBody
# https://github.com/ethereum/consensus-specs/blob/v1.2.0-rc.1/specs/altair/beacon-chain.md#beaconblockbody
BeaconBlockBody* = object
randao_reveal*: ValidatorSig
eth1_data*: Eth1Data ##\
## Eth1 data vote
eth1_data*: Eth1Data
## Eth1 data vote
graffiti*: GraffitiBytes ##\
## Arbitrary data
graffiti*: GraffitiBytes
## Arbitrary data
# Operations
proposer_slashings*: List[ProposerSlashing, Limit MAX_PROPOSER_SLASHINGS]
@ -531,7 +524,7 @@ type
List[ValidatorStatus, Limit VALIDATOR_REGISTRY_LIMIT]
# Represent in full
balances*: List[uint64, Limit VALIDATOR_REGISTRY_LIMIT]
balances*: List[Gwei, Limit VALIDATOR_REGISTRY_LIMIT]
# Mod-increment
randao_mix*: Eth2Digest

View File

@ -15,7 +15,43 @@
# These datatypes are used as specifications for serialization - thus should not
# be altered outside of what the spec says. Likewise, they should not be made
# `ref` - this can be achieved by wrapping them in higher-level
# types / composition
# types / composition.
#
# Most integers in the spec correspond to little-endian `uint64` in their binary
# SSZ encoding and thus may take values from the full `uint64` range. Some
# integers have maximum valid values specified in spec constants, but still use
# an `uint64` in the SSZ encoding, meaning that out-of-range values may be seen
# in the encoded byte stream.
#
# In types derived from the spec, we use unsigned integers throughout which
# cover the full range of possible values in the binary encoding. This leads to
# some friction compared to "normal" Nim code, and special care must be taken
# on the boundary between the "raw" `uint64` data from the wire (which may not
# be trusted, generally) and "sanitized" data. In many cases, we use `uint64`
# directly in code:
#
# * Unsigned arithmetic is not overflow-checked - instead, it wraps at the
# boundary
# * Converting an unsigned integer to a signed integer may lead to a Defect -
# before doing so, the value must always be checked in the unsigned domain
# * Because unsigned arithmetic was added late to the language, there are
# lingering edge cases and bugs - when using features on the boundary between
# signed and unsigned, careful testing is advised
#
# In cases where the range of valid values are statically known, we further
# "sanitize" data using `distinct` types such as `CommitteeIndex` - in these
# cases, two levels of sanitization is possible:
#
# * static range checking, where valid range is known at compile time
# * dynamic range checking, where valid is tied to a particular state or block,
# such as a list index
#
# For static range checking, we use `distinct` types to mark that the range
# check has been performed. However, this does not imply that the value is
# valid for a particular state or block - dynamic range checking must still be
# performed at every usage site - this applies in particular to `ValidatorIndex`
# and `CommitteeIndex` which have upper bounds in the spec that are far above
# their dynamically valid range when used to access lists in the state.
# TODO Careful, not nil analysis is broken / incomplete and the semantics will
# likely change in future versions of the language:
@ -38,21 +74,6 @@ export
tables, results, json_serialization, timer, sszTypes, beacon_time, crypto,
digest, presets
# Presently, we're reusing the data types from the serialization (uint64) in the
# objects we pass around to the beacon chain logic, thus keeping the two
# similar. This is convenient for keeping up with the specification, but
# will eventually need a more robust approach such that we don't run into
# over- and underflows.
# Some of the open questions are being tracked here:
# https://github.com/ethereum/consensus-specs/issues/224
#
# The present approach causes some problems due to how Nim treats unsigned
# integers - here's no high(uint64), arithmetic support is incomplete, there's
# no over/underflow checking available
#
# Eventually, we could also differentiate between user/tainted data and
# internal state that's gone through sanity checks already.
const SPEC_VERSION* = "1.1.10"
## Spec version we're aiming to be compatible with, right now
@ -113,19 +134,23 @@ type
# https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/beacon-chain.md#custom-types
Eth2Domain* = array[32, byte]
# https://github.com/nim-lang/Nim/issues/574 and be consistent across
# 32-bit and 64-bit word platforms.
# The distinct types here should only be used when data has been de-tainted
# following overflow checks - they cannot be used in SSZ objects as SSZ
# instances are not invalid _per se_ when they hold an out-of-bounds index -
# that is part of consensus.
# VALIDATOR_REGISTRY_LIMIT is 1^40 in spec 1.0, but if the number of
# validators ever grows near 1^32 that we support here, we'll have bigger
# issues than the size of this type to take care of. Until then, we'll use
# uint32 as it halves memory requirements for active validator sets,
# improves consistency on 32-vs-64-bit platforms and works better with
# Nim seq constraints.
ValidatorIndex* = distinct uint32
## Each validator has an index that is used in lieu of the public key to
## identify the validator. The index is assigned according to the order of
## deposits in the deposit contract, skipping the invalid ones
## (deposit index >= validator index).
##
## According to the spec, up to 1^40 (VALIDATOR_REGISTRY_LIMIT) validator
## indices may be assigned, but given that each validator has an entry in
## the beacon state and we keep the full beacon state in memory,
## pragmatically a much lower limit applies.
##
## In the wire encoding, validator indices are `uint64`, but we use `uint32`
## instead to save memory and to work around limitations in seq indexing on
## 32-bit platforms (https://github.com/nim-lang/Nim/issues/574).
##
## `ValidatorIndex` is not used in types used for serialization (to allow
## reading invalid data) - validation happens on use instead, via `init`.
CommitteeIndex* = distinct uint8
## Index identifying a per-slot committee - depending on the active
@ -137,8 +162,8 @@ type
## a committee index is valid for a particular state, see
## `check_attestation_index`.
##
## `CommitteeIndex` is not used in `datatypes` to allow reading invalid data
## (validation happens on use instead, via `init`).
## `CommitteIndex` is not used in types used for serialization (to allow
## reading invalid data) - validation happens on use instead, via `init`.
SubnetId* = distinct uint8
## The subnet id maps which gossip subscription to use to publish an
@ -223,7 +248,7 @@ type
AttestationData* = object
slot*: Slot
index*: uint64
index*: uint64 ## `CommitteeIndex` after validation
# LMD GHOST vote
beacon_block_root*: Eth2Digest
@ -234,8 +259,8 @@ type
# https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/beacon-chain.md#deposit
Deposit* = object
proof*: array[DEPOSIT_CONTRACT_TREE_DEPTH + 1, Eth2Digest] ##\
## Merkle path to deposit root
proof*: array[DEPOSIT_CONTRACT_TREE_DEPTH + 1, Eth2Digest]
## Merkle path to deposit root
data*: DepositData
@ -256,10 +281,9 @@ type
# https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/beacon-chain.md#voluntaryexit
VoluntaryExit* = object
epoch*: Epoch ##\
## Earliest epoch when voluntary exit can be processed
validator_index*: uint64
epoch*: Epoch
## Earliest epoch when voluntary exit can be processed
validator_index*: uint64 # `ValidatorIndex` after validation
SomeAttestation* = Attestation | TrustedAttestation
SomeIndexedAttestation* = IndexedAttestation | TrustedIndexedAttestation
@ -287,23 +311,23 @@ type
Validator* = object
pubkey*: ValidatorPubKey
withdrawal_credentials*: Eth2Digest ##\
## Commitment to pubkey for withdrawals and transfers
withdrawal_credentials*: Eth2Digest
## Commitment to pubkey for withdrawals and transfers
effective_balance*: uint64 ##\
## Balance at stake
effective_balance*: Gwei
## Balance at stake
slashed*: bool
# Status epochs
activation_eligibility_epoch*: Epoch ##\
## When criteria for activation were met
activation_eligibility_epoch*: Epoch
## When criteria for activation were met
activation_epoch*: Epoch
exit_epoch*: Epoch
withdrawable_epoch*: Epoch ##\
## When validator can withdraw or transfer funds
withdrawable_epoch*: Epoch
## When validator can withdraw or transfer funds
# https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/beacon-chain.md#pendingattestation
PendingAttestation* = object
@ -312,7 +336,7 @@ type
inclusion_delay*: uint64
proposer_index*: uint64
proposer_index*: uint64 # `ValidatorIndex` after validation
# https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/beacon-chain.md#historicalbatch
HistoricalBatch* = object
@ -324,8 +348,8 @@ type
previous_version*: Version
current_version*: Version
epoch*: Epoch ##\
## Epoch of latest fork
epoch*: Epoch
## Epoch of latest fork
# https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/beacon-chain.md#eth1data
Eth1Data* = object
@ -345,7 +369,7 @@ type
# https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/beacon-chain.md#beaconblockheader
BeaconBlockHeader* = object
slot*: Slot
proposer_index*: uint64
proposer_index*: uint64 # `ValidatorIndex` after validation
parent_root*: Eth2Digest
state_root*: Eth2Digest
body_root*: Eth2Digest
@ -368,7 +392,7 @@ type
# https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/validator.md#aggregateandproof
AggregateAndProof* = object
aggregator_index*: uint64
aggregator_index*: uint64 # `ValidatorIndex` after validation
aggregate*: Attestation
selection_proof*: ValidatorSig
@ -384,8 +408,7 @@ type
# This doesn't know about forks or branches in the DAG. It's for straight,
# linear chunks of the chain.
StateCache* = object
shuffled_active_validator_indices*:
Table[Epoch, seq[ValidatorIndex]]
shuffled_active_validator_indices*: Table[Epoch, seq[ValidatorIndex]]
beacon_proposer_indices*: Table[Slot, Option[ValidatorIndex]]
sync_committees*: Table[SyncCommitteePeriod, SyncCommitteeCache]
@ -403,23 +426,23 @@ type
pubkey* {.dontSerialize.}: ValidatorPubKey
withdrawal_credentials* {.dontSerialize.}: Eth2Digest ##\
## Commitment to pubkey for withdrawals and transfers
withdrawal_credentials* {.dontSerialize.}: Eth2Digest
## Commitment to pubkey for withdrawals and transfers
effective_balance*: uint64 ##\
## Balance at stake
effective_balance*: Gwei
## Balance at stake
slashed*: bool
# Status epochs
activation_eligibility_epoch*: Epoch ##\
## When criteria for activation were met
activation_eligibility_epoch*: Epoch
## When criteria for activation were met
activation_epoch*: Epoch
exit_epoch*: Epoch
withdrawable_epoch*: Epoch ##\
## When validator can withdraw or transfer funds
withdrawable_epoch*: Epoch
## When validator can withdraw or transfer funds
# https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/p2p-interface.md#eth2-field
ENRForkID* = object
@ -444,11 +467,11 @@ type
penalties*: Gwei
InclusionInfo* = object
# The distance between the attestation slot and the slot that attestation
# was included in block.
delay*: uint64
# The index of the proposer at the slot where the attestation was included.
proposer_index*: uint64
## The distance between the attestation slot and the slot that attestation
## was included in block.
proposer_index*: uint64 # `ValidatorIndex` after validation
## The index of the proposer at the slot where the attestation was included
RewardFlags* {.pure.} = enum
isSlashed
@ -475,7 +498,7 @@ type
## reward processing
# The validator's effective balance in the _current_ epoch.
current_epoch_effective_balance*: uint64
current_epoch_effective_balance*: Gwei
# True if the validator had an attestation included in the _previous_ epoch.
is_previous_epoch_attester*: Option[InclusionInfo]
@ -583,18 +606,38 @@ template makeLimitedUInt*(T: untyped, limit: SomeUnsignedInt) =
template toSszType(x: T): uint64 =
{.error: "Limited types should not be used with SSZ (abi differences)".}
template makeLimitedU64*(T: untyped, limit: uint64) =
makeLimitedUInt(T, limit)
template makeLimitedU8*(T: untyped, limit: uint8) =
makeLimitedUInt(T, limit)
template makeLimitedU16*(T: type, limit: uint16) =
makeLimitedUInt(T, limit)
template makeLimitedU64*(T: untyped, limit: uint64) =
makeLimitedUInt(T, limit)
makeLimitedU64(CommitteeIndex, MAX_COMMITTEES_PER_SLOT)
makeLimitedU64(SubnetId, ATTESTATION_SUBNET_COUNT)
const
validatorIndexLimit = min(uint64(int32.high), VALIDATOR_REGISTRY_LIMIT)
## Because we limit ourselves to what is practically possible to index in a
## seq, we will discard some otherwise "potentially valid" validator indices
## here - this is a tradeoff made for code simplicity.
##
## In general, `ValidatorIndex` is assumed to be convertible to/from an
## `int`. This should be valid for a long time, because
## https://github.com/ethereum/annotated-spec/blob/master/phase0/beacon-chain.md#configuration
## notes that "The maximum supported validator count is 2**22 (=4,194,304),
## or ~134 million ETH staking. Assuming 32 slots per epoch and 64
## committees per slot, this gets us to a max 2048 validators in a
## committee."
## That's only active validators, so in principle, it can grow larger, but
## it should be orders of magnitude more validators than expected in the
## next couple of years, than int32 indexing supports.
static: doAssert high(int) >= high(int32)
makeLimitedU64(ValidatorIndex, validatorIndexLimit)
func init*(T: type CommitteeIndex, index, committees_per_slot: uint64):
Result[CommitteeIndex, cstring] =
if index < min(committees_per_slot, MAX_COMMITTEES_PER_SLOT):
@ -602,14 +645,6 @@ func init*(T: type CommitteeIndex, index, committees_per_slot: uint64):
else:
err("Committee index out of range for epoch")
proc writeValue*(writer: var JsonWriter, value: ValidatorIndex)
{.raises: [IOError, Defect].} =
writeValue(writer, distinctBase value)
proc readValue*(reader: var JsonReader, value: var ValidatorIndex)
{.raises: [IOError, SerializationError, Defect].} =
value = ValidatorIndex reader.readValue(distinctBase ValidatorIndex)
template writeValue*(
writer: var JsonWriter, value: Version | ForkDigest | DomainType) =
writeValue(writer, to0xHex(distinctBase(value)))
@ -638,18 +673,6 @@ proc writeValue*(writer: var JsonWriter, value: JustificationBits)
{.raises: [IOError, Defect].} =
writer.writeValue $value
# In general, ValidatorIndex is assumed to be convertible to/from an int. This
# should be valid for a long time, because
# https://github.com/ethereum/annotated-spec/blob/master/phase0/beacon-chain.md#configuration
# notes that "The maximum supported validator count is 2**22 (=4,194,304), or
# ~134 million ETH staking. Assuming 32 slots per epoch and 64 committees per
# slot, this gets us to a max 2048 validators in a committee."
#
# That's only active validators, so in principle, it can grow larger, but it
# should be orders of magnitude more validators than expected in the next in
# the next couple of years, than int32 indexing supports.
static: doAssert high(int) >= high(int32)
# `ValidatorIndex` seq handling.
template `[]=`*[T](a: var seq[T], b: ValidatorIndex, c: T) =
a[b.int] = c
@ -657,24 +680,15 @@ template `[]=`*[T](a: var seq[T], b: ValidatorIndex, c: T) =
template `[]`*[T](a: seq[T], b: ValidatorIndex): auto = # Also var seq (!)
a[b.int]
# `ValidatorIndex` Nim integration
template `==`*(x, y: ValidatorIndex) : bool =
distinctBase(x) == distinctBase(y)
iterator vindices*(
a: HashList[Validator, Limit VALIDATOR_REGISTRY_LIMIT]): ValidatorIndex =
for i in 0..<a.len():
yield i.ValidatorIndex
template `<`*(x, y: ValidatorIndex): bool =
distinctBase(x) < distinctBase(y)
template hash*(x: ValidatorIndex): Hash =
hash distinctBase(x)
template `$`*(x: ValidatorIndex): string =
$ distinctBase(x)
template `==`*(x: uint64, y: ValidatorIndex): bool =
x == uint64(y)
template `==`*(x: ValidatorIndex, y: uint64): bool =
uint64(x) == y
iterator vindices*(
a: List[Validator, Limit VALIDATOR_REGISTRY_LIMIT]): ValidatorIndex =
for i in 0..<a.len():
yield i.ValidatorIndex
template `==`*(x, y: JustificationBits): bool =
distinctBase(x) == distinctBase(y)

View File

@ -5,6 +5,9 @@
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
# at your option. This file may not be copied, modified, or distributed except according to those terms.
# Types specific to bellatrix (ie known to have changed across hard forks) - see
# `base` for types and guidelines common across forks
# TODO Careful, not nil analysis is broken / incomplete and the semantics will
# likely change in future versions of the language:
# https://github.com/nim-lang/RFCs/issues/250
@ -99,11 +102,11 @@ type
fork*: Fork
# History
latest_block_header*: BeaconBlockHeader ##\
## `latest_block_header.state_root == ZERO_HASH` temporarily
latest_block_header*: BeaconBlockHeader
## `latest_block_header.state_root == ZERO_HASH` temporarily
block_roots*: HashArray[Limit SLOTS_PER_HISTORICAL_ROOT, Eth2Digest] ##\
## Needed to process attestations, older to newer
block_roots*: HashArray[Limit SLOTS_PER_HISTORICAL_ROOT, Eth2Digest]
## Needed to process attestations, older to newer
state_roots*: HashArray[Limit SLOTS_PER_HISTORICAL_ROOT, Eth2Digest]
historical_roots*: HashList[Eth2Digest, Limit HISTORICAL_ROOTS_LIMIT]
@ -116,14 +119,14 @@ type
# Registry
validators*: HashList[Validator, Limit VALIDATOR_REGISTRY_LIMIT]
balances*: HashList[uint64, Limit VALIDATOR_REGISTRY_LIMIT]
balances*: HashList[Gwei, Limit VALIDATOR_REGISTRY_LIMIT]
# Randomness
randao_mixes*: HashArray[Limit EPOCHS_PER_HISTORICAL_VECTOR, Eth2Digest]
# Slashings
slashings*: HashArray[Limit EPOCHS_PER_SLASHINGS_VECTOR, uint64] ##\
## Per-epoch sums of slashed effective balances
slashings*: HashArray[Limit EPOCHS_PER_SLASHINGS_VECTOR, Gwei]
## Per-epoch sums of slashed effective balances
# Participation
previous_epoch_participation*: EpochParticipationFlags
@ -132,8 +135,8 @@ type
# Finality
justification_bits*: JustificationBits
previous_justified_checkpoint*: Checkpoint ##\
## Previous epoch snapshot
previous_justified_checkpoint*: Checkpoint
## Previous epoch snapshot
current_justified_checkpoint*: Checkpoint
finalized_checkpoint*: Checkpoint
@ -167,13 +170,13 @@ type
## is formed.
slot*: Slot
proposer_index*: uint64
proposer_index*: uint64 # `ValidatorIndex` after validation
parent_root*: Eth2Digest ##\
## Root hash of the previous block
parent_root*: Eth2Digest
## Root hash of the previous block
state_root*: Eth2Digest ##\
## The state root, _after_ this block has been processed
state_root*: Eth2Digest
## The state root, _after_ this block has been processed
body*: BeaconBlockBody
@ -181,13 +184,13 @@ type
## A BeaconBlock that contains verified signatures
## but that has not been verified for state transition
slot*: Slot
proposer_index*: uint64
proposer_index*: uint64 # `ValidatorIndex` after validation
parent_root*: Eth2Digest ##\
## Root hash of the previous block
parent_root*: Eth2Digest
## Root hash of the previous block
state_root*: Eth2Digest ##\
## The state root, _after_ this block has been processed
state_root*: Eth2Digest
## The state root, _after_ this block has been processed
body*: SigVerifiedBeaconBlockBody
@ -209,19 +212,19 @@ type
## then, the type must be manually kept compatible with its untrusted
## cousin.
slot*: Slot
proposer_index*: uint64
parent_root*: Eth2Digest ##\
state_root*: Eth2Digest ##\
proposer_index*: uint64 # `ValidatorIndex` after validation
parent_root*: Eth2Digest
state_root*: Eth2Digest
body*: TrustedBeaconBlockBody
# https://github.com/ethereum/consensus-specs/blob/v1.1.7/specs/merge/beacon-chain.md#beaconblockbody
BeaconBlockBody* = object
randao_reveal*: ValidatorSig
eth1_data*: Eth1Data ##\
## Eth1 data vote
eth1_data*: Eth1Data
## Eth1 data vote
graffiti*: GraffitiBytes ##\
## Arbitrary data
graffiti*: GraffitiBytes
## Arbitrary data
# Operations
proposer_slashings*: List[ProposerSlashing, Limit MAX_PROPOSER_SLASHINGS]
@ -247,11 +250,11 @@ type
##
## The block state transition has NOT been verified
randao_reveal*: ValidatorSig
eth1_data*: Eth1Data ##\
## Eth1 data vote
eth1_data*: Eth1Data
## Eth1 data vote
graffiti*: GraffitiBytes ##\
## Arbitrary data
graffiti*: GraffitiBytes
## Arbitrary data
# Operations
proposer_slashings*: List[ProposerSlashing, Limit MAX_PROPOSER_SLASHINGS]
@ -267,11 +270,11 @@ type
TrustedBeaconBlockBody* = object
## A full verified block
randao_reveal*: TrustedSig
eth1_data*: Eth1Data ##\
## Eth1 data vote
eth1_data*: Eth1Data
## Eth1 data vote
graffiti*: GraffitiBytes ##\
## Arbitrary data
graffiti*: GraffitiBytes
## Arbitrary data
# Operations
proposer_slashings*: List[TrustedProposerSlashing, Limit MAX_PROPOSER_SLASHINGS]

View File

@ -5,17 +5,8 @@
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
# at your option. This file may not be copied, modified, or distributed except according to those terms.
# This file contains data types that are part of the spec and thus subject to
# serialization and spec updates.
#
# The spec folder in general contains code that has been hoisted from the
# specification and that follows the spec as closely as possible, so as to make
# it easy to keep up-to-date.
#
# These datatypes are used as specifications for serialization - thus should not
# be altered outside of what the spec says. Likewise, they should not be made
# `ref` - this can be achieved by wrapping them in higher-level
# types / composition
# Types specific to phase0 (ie known to have changed across hard forks) - see
# `base` for types and guidelines common across forks
# TODO Careful, not nil analysis is broken / incomplete and the semantics will
# likely change in future versions of the language:
@ -40,11 +31,11 @@ type
fork*: Fork
# History
latest_block_header*: BeaconBlockHeader ##\
## `latest_block_header.state_root == ZERO_HASH` temporarily
latest_block_header*: BeaconBlockHeader
## `latest_block_header.state_root == ZERO_HASH` temporarily
block_roots*: HashArray[Limit SLOTS_PER_HISTORICAL_ROOT, Eth2Digest] ##\
## Needed to process attestations, older to newer
block_roots*: HashArray[Limit SLOTS_PER_HISTORICAL_ROOT, Eth2Digest]
## Needed to process attestations, older to newer
state_roots*: HashArray[Limit SLOTS_PER_HISTORICAL_ROOT, Eth2Digest]
historical_roots*: HashList[Eth2Digest, Limit HISTORICAL_ROOTS_LIMIT]
@ -57,14 +48,14 @@ type
# Registry
validators*: HashList[Validator, Limit VALIDATOR_REGISTRY_LIMIT]
balances*: HashList[uint64, Limit VALIDATOR_REGISTRY_LIMIT]
balances*: HashList[Gwei, Limit VALIDATOR_REGISTRY_LIMIT]
# Randomness
randao_mixes*: HashArray[Limit EPOCHS_PER_HISTORICAL_VECTOR, Eth2Digest]
# Slashings
slashings*: HashArray[Limit EPOCHS_PER_SLASHINGS_VECTOR, uint64] ##\
## Per-epoch sums of slashed effective balances
slashings*: HashArray[Limit EPOCHS_PER_SLASHINGS_VECTOR, Gwei]
## Per-epoch sums of slashed effective balances
# Attestations
previous_epoch_attestations*:
@ -75,8 +66,8 @@ type
# Finality
justification_bits*: JustificationBits
previous_justified_checkpoint*: Checkpoint ##\
## Previous epoch snapshot
previous_justified_checkpoint*: Checkpoint
## Previous epoch snapshot
current_justified_checkpoint*: Checkpoint
finalized_checkpoint*: Checkpoint
@ -100,13 +91,13 @@ type
## is formed.
slot*: Slot
proposer_index*: uint64
proposer_index*: uint64 # `ValidatorIndex` after validation
parent_root*: Eth2Digest ##\
## Root hash of the previous block
parent_root*: Eth2Digest
## Root hash of the previous block
state_root*: Eth2Digest ##\
## The state root, _after_ this block has been processed
state_root*: Eth2Digest
## The state root, _after_ this block has been processed
body*: BeaconBlockBody
@ -119,13 +110,13 @@ type
## A BeaconBlock that contains verified signatures
## but that has not been verified for state transition
slot*: Slot
proposer_index*: uint64
proposer_index*: uint64 # `ValidatorIndex` after validation
parent_root*: Eth2Digest ##\
## Root hash of the previous block
parent_root*: Eth2Digest
## Root hash of the previous block
state_root*: Eth2Digest ##\
## The state root, _after_ this block has been processed
state_root*: Eth2Digest
## The state root, _after_ this block has been processed
body*: SigVerifiedBeaconBlockBody
@ -147,9 +138,9 @@ type
## then, the type must be manually kept compatible with its untrusted
## cousin.
slot*: Slot
proposer_index*: uint64
parent_root*: Eth2Digest ##\
state_root*: Eth2Digest ##\
proposer_index*: uint64 # `ValidatorIndex` after validation
parent_root*: Eth2Digest
state_root*: Eth2Digest
body*: TrustedBeaconBlockBody
# https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/beacon-chain.md#beaconblockbody

View File

@ -360,22 +360,22 @@ func is_withdrawable_validator*(validator: Validator, epoch: Epoch): bool =
# https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/beacon-chain.md#get_active_validator_indices
iterator get_active_validator_indices*(state: ForkyBeaconState, epoch: Epoch):
ValidatorIndex =
for idx in 0..<state.validators.len:
if is_active_validator(state.validators[idx], epoch):
yield idx.ValidatorIndex
for vidx in state.validators.vindices:
if is_active_validator(state.validators[vidx], epoch):
yield vidx
func get_active_validator_indices*(state: ForkyBeaconState, epoch: Epoch):
seq[ValidatorIndex] =
## Return the sequence of active validator indices at ``epoch``.
var res = newSeqOfCap[ValidatorIndex](state.validators.len)
for idx in get_active_validator_indices(state, epoch):
res.add idx
for vidx in get_active_validator_indices(state, epoch):
res.add vidx
res
func get_active_validator_indices_len*(state: ForkyBeaconState, epoch: Epoch):
uint64 =
for idx in 0..<state.validators.len:
if is_active_validator(state.validators[idx], epoch):
for vidx in state.validators.vindices:
if is_active_validator(state.validators[vidx], epoch):
inc result
func get_active_validator_indices_len*(

View File

@ -45,7 +45,7 @@ func process_block_header*(
if proposer_index.isNone:
return err("process_block_header: proposer missing")
if not (blck.proposer_index.ValidatorIndex == proposer_index.get):
if not (blck.proposer_index == proposer_index.get):
return err("process_block_header: proposer index incorrect")
# Verify that the parent matches
@ -126,16 +126,12 @@ func is_slashable_validator(validator: Validator, epoch: Epoch): bool =
proc check_proposer_slashing*(
state: ForkyBeaconState, proposer_slashing: SomeProposerSlashing,
flags: UpdateFlags):
Result[void, cstring] =
Result[ValidatorIndex, cstring] =
let
header_1 = proposer_slashing.signed_header_1.message
header_2 = proposer_slashing.signed_header_2.message
# Not from spec
if header_1.proposer_index >= state.validators.lenu64:
return err("check_proposer_slashing: invalid proposer index")
# Verify header slots match
if not (header_1.slot == header_2.slot):
return err("check_proposer_slashing: slot mismatch")
@ -149,6 +145,9 @@ proc check_proposer_slashing*(
return err("check_proposer_slashing: headers not different")
# Verify the proposer is slashable
if header_1.proposer_index >= state.validators.lenu64:
return err("check_proposer_slashing: invalid proposer index")
let proposer = unsafeAddr state.validators.asSeq()[header_1.proposer_index]
if not is_slashable_validator(proposer[], get_current_epoch(state)):
return err("check_proposer_slashing: slashed proposer")
@ -163,11 +162,12 @@ proc check_proposer_slashing*(
signed_header.signature):
return err("check_proposer_slashing: invalid signature")
ok()
# Verified above against state.validators
ValidatorIndex.init(header_1.proposer_index)
proc check_proposer_slashing*(
state: var ForkedHashedBeaconState; proposer_slashing: SomeProposerSlashing;
flags: UpdateFlags): Result[void, cstring] =
flags: UpdateFlags): Result[ValidatorIndex, cstring] =
withState(state):
check_proposer_slashing(state.data, proposer_slashing, flags)
@ -177,11 +177,8 @@ proc process_proposer_slashing*(
proposer_slashing: SomeProposerSlashing, flags: UpdateFlags,
cache: var StateCache):
Result[void, cstring] =
? check_proposer_slashing(state, proposer_slashing, flags)
slash_validator(
cfg, state,
proposer_slashing.signed_header_1.message.proposer_index.ValidatorIndex,
cache)
let proposer_index = ? check_proposer_slashing(state, proposer_slashing, flags)
slash_validator(cfg, state, proposer_index, cache)
ok()
# https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/beacon-chain.md#is_slashable_attestation_data
@ -224,7 +221,9 @@ proc check_attester_slashing*(
system.cmp):
if is_slashable_validator(
state.validators.asSeq()[index], get_current_epoch(state)):
slashed_indices.add index.ValidatorIndex
slashed_indices.add ValidatorIndex.init(index).expect(
"checked by is_valid_indexed_attestation")
if slashed_indices.len == 0:
return err("Attester slashing: Trying to slash participant(s) twice")
@ -244,27 +243,24 @@ proc process_attester_slashing*(
flags: UpdateFlags,
cache: var StateCache
): Result[void, cstring] =
let attester_slashing_validity =
check_attester_slashing(state, attester_slashing, flags)
let slashed_attesters =
? check_attester_slashing(state, attester_slashing, flags)
if attester_slashing_validity.isErr:
return err(attester_slashing_validity.error)
for index in attester_slashing_validity.value:
for index in slashed_attesters:
slash_validator(cfg, state, index, cache)
ok()
func findValidatorIndex*(state: ForkyBeaconState, pubkey: ValidatorPubKey): int =
func findValidatorIndex*(state: ForkyBeaconState, pubkey: ValidatorPubKey):
Opt[ValidatorIndex] =
# This linear scan is unfortunate, but should be fairly fast as we do a simple
# byte comparison of the key. The alternative would be to build a Table, but
# given that each block can hold no more than 16 deposits, it's slower to
# build the table and use it for lookups than to scan it like this.
# Once we have a reusable, long-lived cache, this should be revisited
for i in 0 ..< state.validators.len:
if state.validators.asSeq[i].pubkey == pubkey:
return i
-1
for vidx in state.validators.vindices:
if state.validators.asSeq[vidx].pubkey == pubkey:
return Opt[ValidatorIndex].ok(vidx)
proc process_deposit*(cfg: RuntimeConfig,
state: var ForkyBeaconState,
@ -290,9 +286,9 @@ proc process_deposit*(cfg: RuntimeConfig,
amount = deposit.data.amount
index = findValidatorIndex(state, pubkey)
if index != -1:
if index.isSome():
# Increase balance by deposit amount
increase_balance(state, index.ValidatorIndex, amount)
increase_balance(state, index.get(), amount)
else:
# Verify the deposit signature (proof of possession) which is not checked
# by the deposit contract
@ -327,11 +323,10 @@ proc check_voluntary_exit*(
cfg: RuntimeConfig,
state: ForkyBeaconState,
signed_voluntary_exit: SomeSignedVoluntaryExit,
flags: UpdateFlags): Result[void, cstring] =
flags: UpdateFlags): Result[ValidatorIndex, cstring] =
let voluntary_exit = signed_voluntary_exit.message
# Not in spec. Check that validator_index is in range
if voluntary_exit.validator_index >= state.validators.lenu64:
return err("Exit: invalid validator index")
@ -362,23 +357,13 @@ proc check_voluntary_exit*(
validator[].pubkey, signed_voluntary_exit.signature):
return err("Exit: invalid signature")
# Initiate exit
debug "Exit: checking voluntary exit (validator_leaving)",
index = voluntary_exit.validator_index,
num_validators = state.validators.len,
epoch = voluntary_exit.epoch,
current_epoch = get_current_epoch(state),
validator_slashed = validator[].slashed,
validator_withdrawable_epoch = validator[].withdrawable_epoch,
validator_exit_epoch = validator[].exit_epoch,
validator_effective_balance = validator[].effective_balance
ok()
# Checked above
ValidatorIndex.init(voluntary_exit.validator_index)
proc check_voluntary_exit*(
cfg: RuntimeConfig, state: ForkedHashedBeaconState;
signed_voluntary_exit: SomeSignedVoluntaryExit;
flags: UpdateFlags): Result[void, cstring] =
flags: UpdateFlags): Result[ValidatorIndex, cstring] =
withState(state):
check_voluntary_exit(cfg, state.data, signed_voluntary_exit, flags)
@ -389,10 +374,9 @@ proc process_voluntary_exit*(
signed_voluntary_exit: SomeSignedVoluntaryExit,
flags: UpdateFlags,
cache: var StateCache): Result[void, cstring] =
? check_voluntary_exit(cfg, state, signed_voluntary_exit, flags)
initiate_validator_exit(
cfg, state, signed_voluntary_exit.message.validator_index.ValidatorIndex,
cache)
let exited_validator =
? check_voluntary_exit(cfg, state, signed_voluntary_exit, flags)
initiate_validator_exit(cfg, state, exited_validator, cache)
ok()
# https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/beacon-chain.md#operations

View File

@ -664,11 +664,10 @@ iterator get_flag_index_deltas*(
info, flag_index)
active_increments = get_active_increments(info)
for index in 0 ..< state.validators.len:
if not is_eligible_validator(info.validators[index]):
for vidx in state.validators.vindices:
if not is_eligible_validator(info.validators[vidx]):
continue
template vidx: ValidatorIndex = index.ValidatorIndex
let base_reward = get_base_reward_increment(state, vidx, base_reward_per_increment)
yield
if is_unslashed_participating_index(
@ -705,16 +704,15 @@ iterator get_inactivity_penalty_deltas*(
cfg.INACTIVITY_SCORE_BIAS * INACTIVITY_PENALTY_QUOTIENT_ALTAIR
previous_epoch = get_previous_epoch(state)
for index in 0 ..< state.validators.len:
if not is_eligible_validator(info.validators[index]):
for vidx in state.validators.vindices:
if not is_eligible_validator(info.validators[vidx]):
continue
template vidx: untyped = index.ValidatorIndex
if not is_unslashed_participating_index(
state, TIMELY_TARGET_FLAG_INDEX, previous_epoch, vidx):
let
penalty_numerator = state.validators[index].effective_balance *
state.inactivity_scores[index]
penalty_numerator = state.validators[vidx].effective_balance *
state.inactivity_scores[vidx]
yield (vidx, Gwei(penalty_numerator div penalty_denominator))
# https://github.com/ethereum/consensus-specs/blob/v1.2.0-rc.1/specs/bellatrix/beacon-chain.md#modified-get_inactivity_penalty_deltas
@ -729,16 +727,15 @@ iterator get_inactivity_penalty_deltas*(
cfg.INACTIVITY_SCORE_BIAS * INACTIVITY_PENALTY_QUOTIENT_BELLATRIX
previous_epoch = get_previous_epoch(state)
for index in 0 ..< state.validators.len:
if not is_eligible_validator(info.validators[index]):
for vidx in state.validators.vindices:
if not is_eligible_validator(info.validators[vidx]):
continue
template vidx: untyped = index.ValidatorIndex
if not is_unslashed_participating_index(
state, TIMELY_TARGET_FLAG_INDEX, previous_epoch, vidx):
let
penalty_numerator = state.validators[index].effective_balance *
state.inactivity_scores[index]
penalty_numerator = state.validators[vidx].effective_balance *
state.inactivity_scores[vidx]
yield (vidx, Gwei(penalty_numerator div penalty_denominator))
# https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/beacon-chain.md#rewards-and-penalties-1
@ -819,22 +816,22 @@ func process_registry_updates*(
# the current epoch, 1 + MAX_SEED_LOOKAHEAD epochs ahead. Thus caches
# remain valid for this epoch through though this function along with
# the rest of the epoch transition.
for index in 0..<state.validators.len():
if is_eligible_for_activation_queue(state.validators.asSeq()[index]):
state.validators[index].activation_eligibility_epoch =
for vidx in state.validators.vindices:
if is_eligible_for_activation_queue(state.validators.asSeq()[vidx]):
state.validators[vidx].activation_eligibility_epoch =
get_current_epoch(state) + 1
if is_active_validator(state.validators.asSeq()[index], get_current_epoch(state)) and
state.validators.asSeq()[index].effective_balance <= cfg.EJECTION_BALANCE:
initiate_validator_exit(cfg, state, index.ValidatorIndex, cache)
if is_active_validator(state.validators.asSeq()[vidx], get_current_epoch(state)) and
state.validators.asSeq()[vidx].effective_balance <= cfg.EJECTION_BALANCE:
initiate_validator_exit(cfg, state, vidx, cache)
## Queue validators eligible for activation and not dequeued for activation
var activation_queue : seq[tuple[a: Epoch, b: int]] = @[]
for index in 0..<state.validators.len():
let validator = unsafeAddr state.validators.asSeq()[index]
var activation_queue : seq[tuple[a: Epoch, b: ValidatorIndex]] = @[]
for vidx in state.validators.vindices:
let validator = unsafeAddr state.validators.asSeq()[vidx]
if is_eligible_for_activation(state, validator[]):
activation_queue.add (
validator[].activation_eligibility_epoch, index)
validator[].activation_eligibility_epoch, vidx)
activation_queue.sort(system.cmp)
@ -845,8 +842,8 @@ func process_registry_updates*(
if i.uint64 >= churn_limit:
break
let
(_, index) = epoch_and_index
state.validators[index].activation_epoch =
(_, vidx) = epoch_and_index
state.validators[vidx].activation_epoch =
compute_activation_exit_epoch(get_current_epoch(state))
# https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/beacon-chain.md#slashings
@ -895,12 +892,12 @@ func process_slashings*(state: var ForkyBeaconState, total_balance: Gwei) =
adjusted_total_slashing_balance = get_adjusted_total_slashing_balance(
state, total_balance)
for index in 0..<state.validators.len:
let validator = unsafeAddr state.validators.asSeq()[index]
for vidx in state.validators.vindices:
let validator = unsafeAddr state.validators.asSeq()[vidx]
if slashing_penalty_applies(validator[], epoch):
let penalty = validator[].get_slashing_penalty(
adjusted_total_slashing_balance, total_balance)
decrease_balance(state, index.ValidatorIndex, penalty)
decrease_balance(state, vidx, penalty)
# https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/beacon-chain.md#eth1-data-votes-updates
func process_eth1_data_reset*(state: var ForkyBeaconState) =

View File

@ -41,7 +41,7 @@ type
currentEpochParticipation: EpochParticipationFlags
previousEpochParticipation: EpochParticipationFlags
PubkeyToIndexTable = Table[ValidatorPubKey, int]
PubkeyToIndexTable = Table[ValidatorPubKey, ValidatorIndex]
AuxiliaryState* = object
epochParticipationFlags: ParticipationFlags
@ -340,12 +340,13 @@ proc collectFromDeposits(
let pubkey = deposit.data.pubkey
let amount = deposit.data.amount
var index = findValidatorIndex(state.data, pubkey)
if index == -1:
index = pubkeyToIndex.getOrDefault(pubkey, -1)
if index != -1:
rewardsAndPenalties[index].deposits += amount
if index.isNone:
if pubkey in pubkeyToIndex:
index = Opt[ValidatorIndex].ok(pubkeyToIndex[pubkey])
if index.isSome:
rewardsAndPenalties[index.get()].deposits += amount
elif verify_deposit_signature(cfg, deposit.data):
pubkeyToIndex[pubkey] = rewardsAndPenalties.len
pubkeyToIndex[pubkey] = ValidatorIndex(rewardsAndPenalties.len)
rewardsAndPenalties.add(
RewardsAndPenalties(deposits: amount))