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:
parent
c73239f60b
commit
1101c745b9
|
@ -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")
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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,16 +97,16 @@ type
|
|||
|
||||
# https://github.com/ethereum/consensus-specs/blob/v1.2.0-rc.1/specs/altair/validator.md#synccommitteemessage
|
||||
SyncCommitteeMessage* = object
|
||||
slot*: Slot ##\
|
||||
slot*: Slot
|
||||
## Slot to which this contribution pertains
|
||||
|
||||
beacon_block_root*: Eth2Digest ##\
|
||||
beacon_block_root*: Eth2Digest
|
||||
## Block root for this signature
|
||||
|
||||
validator_index*: uint64 ##\
|
||||
validator_index*: uint64 # `ValidatorIndex` after validation
|
||||
## Index of the validator that produced this signature
|
||||
|
||||
signature*: ValidatorSig ##\
|
||||
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
|
||||
|
@ -123,26 +114,26 @@ type
|
|||
BitArray[SYNC_SUBCOMMITTEE_SIZE]
|
||||
|
||||
SyncCommitteeContribution* = object
|
||||
slot*: Slot ##\
|
||||
slot*: Slot
|
||||
## Slot to which this contribution pertains
|
||||
|
||||
beacon_block_root*: Eth2Digest ##\
|
||||
beacon_block_root*: Eth2Digest
|
||||
## Block root for this contribution
|
||||
|
||||
subcommittee_index*: uint64 ##\
|
||||
subcommittee_index*: uint64 # `SyncSubcommitteeIndex` after validation
|
||||
## The subcommittee this contribution pertains to out of the broader sync
|
||||
## committee
|
||||
|
||||
aggregation_bits*: SyncCommitteeAggregationBits ##\
|
||||
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*: 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 ##\
|
||||
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_valid_update*: Option[LightClientUpdate]
|
||||
## Best available header to switch finalized head to if we see nothing else
|
||||
|
||||
optimistic_header*: BeaconBlockHeader ##\
|
||||
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,10 +250,10 @@ type
|
|||
fork*: Fork
|
||||
|
||||
# History
|
||||
latest_block_header*: BeaconBlockHeader ##\
|
||||
latest_block_header*: BeaconBlockHeader
|
||||
## `latest_block_header.state_root == ZERO_HASH` temporarily
|
||||
|
||||
block_roots*: HashArray[Limit SLOTS_PER_HISTORICAL_ROOT, Eth2Digest] ##\
|
||||
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]
|
||||
|
@ -275,13 +267,13 @@ 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] ##\
|
||||
slashings*: HashArray[Limit EPOCHS_PER_SLASHINGS_VECTOR, Gwei]
|
||||
## Per-epoch sums of slashed effective balances
|
||||
|
||||
# Participation
|
||||
|
@ -291,7 +283,7 @@ type
|
|||
# Finality
|
||||
justification_bits*: JustificationBits
|
||||
|
||||
previous_justified_checkpoint*: Checkpoint ##\
|
||||
previous_justified_checkpoint*: Checkpoint
|
||||
## Previous epoch snapshot
|
||||
|
||||
current_justified_checkpoint*: Checkpoint
|
||||
|
@ -343,12 +335,12 @@ type
|
|||
## is formed.
|
||||
|
||||
slot*: Slot
|
||||
proposer_index*: uint64
|
||||
proposer_index*: uint64 # `ValidatorIndex` after validation
|
||||
|
||||
parent_root*: Eth2Digest ##\
|
||||
parent_root*: Eth2Digest
|
||||
## Root hash of the previous block
|
||||
|
||||
state_root*: Eth2Digest ##\
|
||||
state_root*: Eth2Digest
|
||||
## The state root, _after_ this block has been processed
|
||||
|
||||
body*: BeaconBlockBody
|
||||
|
@ -356,13 +348,14 @@ type
|
|||
SigVerifiedBeaconBlock* = object
|
||||
## A BeaconBlock that contains verified signatures
|
||||
## but that has not been verified for state transition
|
||||
slot*: Slot
|
||||
proposer_index*: uint64
|
||||
|
||||
parent_root*: Eth2Digest ##\
|
||||
slot*: Slot
|
||||
proposer_index*: uint64 # `ValidatorIndex` after validation
|
||||
|
||||
parent_root*: Eth2Digest
|
||||
## Root hash of the previous block
|
||||
|
||||
state_root*: Eth2Digest ##\
|
||||
state_root*: Eth2Digest
|
||||
## The state root, _after_ this block has been processed
|
||||
|
||||
body*: SigVerifiedBeaconBlockBody
|
||||
|
@ -385,18 +378,18 @@ 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*: Eth1Data
|
||||
## Eth1 data vote
|
||||
|
||||
graffiti*: GraffitiBytes ##\
|
||||
graffiti*: GraffitiBytes
|
||||
## Arbitrary data
|
||||
|
||||
# Operations
|
||||
|
@ -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
|
||||
|
|
|
@ -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,7 +259,7 @@ 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] ##\
|
||||
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 ##\
|
||||
epoch*: Epoch
|
||||
## Earliest epoch when voluntary exit can be processed
|
||||
|
||||
validator_index*: uint64
|
||||
validator_index*: uint64 # `ValidatorIndex` after validation
|
||||
|
||||
SomeAttestation* = Attestation | TrustedAttestation
|
||||
SomeIndexedAttestation* = IndexedAttestation | TrustedIndexedAttestation
|
||||
|
@ -287,22 +311,22 @@ type
|
|||
Validator* = object
|
||||
pubkey*: ValidatorPubKey
|
||||
|
||||
withdrawal_credentials*: Eth2Digest ##\
|
||||
withdrawal_credentials*: Eth2Digest
|
||||
## Commitment to pubkey for withdrawals and transfers
|
||||
|
||||
effective_balance*: uint64 ##\
|
||||
effective_balance*: Gwei
|
||||
## Balance at stake
|
||||
|
||||
slashed*: bool
|
||||
|
||||
# Status epochs
|
||||
activation_eligibility_epoch*: Epoch ##\
|
||||
activation_eligibility_epoch*: Epoch
|
||||
## When criteria for activation were met
|
||||
|
||||
activation_epoch*: Epoch
|
||||
exit_epoch*: Epoch
|
||||
|
||||
withdrawable_epoch*: Epoch ##\
|
||||
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
|
||||
|
@ -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,7 +348,7 @@ type
|
|||
previous_version*: Version
|
||||
current_version*: Version
|
||||
|
||||
epoch*: Epoch ##\
|
||||
epoch*: Epoch
|
||||
## Epoch of latest fork
|
||||
|
||||
# https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/beacon-chain.md#eth1data
|
||||
|
@ -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,22 +426,22 @@ type
|
|||
|
||||
pubkey* {.dontSerialize.}: ValidatorPubKey
|
||||
|
||||
withdrawal_credentials* {.dontSerialize.}: Eth2Digest ##\
|
||||
withdrawal_credentials* {.dontSerialize.}: Eth2Digest
|
||||
## Commitment to pubkey for withdrawals and transfers
|
||||
|
||||
effective_balance*: uint64 ##\
|
||||
effective_balance*: Gwei
|
||||
## Balance at stake
|
||||
|
||||
slashed*: bool
|
||||
|
||||
# Status epochs
|
||||
activation_eligibility_epoch*: Epoch ##\
|
||||
activation_eligibility_epoch*: Epoch
|
||||
## When criteria for activation were met
|
||||
|
||||
activation_epoch*: Epoch
|
||||
exit_epoch*: Epoch
|
||||
|
||||
withdrawable_epoch*: Epoch ##\
|
||||
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
|
||||
|
@ -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)
|
||||
|
|
|
@ -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,10 +102,10 @@ type
|
|||
fork*: Fork
|
||||
|
||||
# History
|
||||
latest_block_header*: BeaconBlockHeader ##\
|
||||
latest_block_header*: BeaconBlockHeader
|
||||
## `latest_block_header.state_root == ZERO_HASH` temporarily
|
||||
|
||||
block_roots*: HashArray[Limit SLOTS_PER_HISTORICAL_ROOT, Eth2Digest] ##\
|
||||
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]
|
||||
|
@ -116,13 +119,13 @@ 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] ##\
|
||||
slashings*: HashArray[Limit EPOCHS_PER_SLASHINGS_VECTOR, Gwei]
|
||||
## Per-epoch sums of slashed effective balances
|
||||
|
||||
# Participation
|
||||
|
@ -132,7 +135,7 @@ type
|
|||
# Finality
|
||||
justification_bits*: JustificationBits
|
||||
|
||||
previous_justified_checkpoint*: Checkpoint ##\
|
||||
previous_justified_checkpoint*: Checkpoint
|
||||
## Previous epoch snapshot
|
||||
|
||||
current_justified_checkpoint*: Checkpoint
|
||||
|
@ -167,12 +170,12 @@ type
|
|||
## is formed.
|
||||
|
||||
slot*: Slot
|
||||
proposer_index*: uint64
|
||||
proposer_index*: uint64 # `ValidatorIndex` after validation
|
||||
|
||||
parent_root*: Eth2Digest ##\
|
||||
parent_root*: Eth2Digest
|
||||
## Root hash of the previous block
|
||||
|
||||
state_root*: Eth2Digest ##\
|
||||
state_root*: Eth2Digest
|
||||
## The state root, _after_ this block has been processed
|
||||
|
||||
body*: BeaconBlockBody
|
||||
|
@ -181,12 +184,12 @@ 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 ##\
|
||||
parent_root*: Eth2Digest
|
||||
## Root hash of the previous block
|
||||
|
||||
state_root*: Eth2Digest ##\
|
||||
state_root*: Eth2Digest
|
||||
## The state root, _after_ this block has been processed
|
||||
|
||||
body*: SigVerifiedBeaconBlockBody
|
||||
|
@ -209,18 +212,18 @@ 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*: Eth1Data
|
||||
## Eth1 data vote
|
||||
|
||||
graffiti*: GraffitiBytes ##\
|
||||
graffiti*: GraffitiBytes
|
||||
## Arbitrary data
|
||||
|
||||
# Operations
|
||||
|
@ -247,10 +250,10 @@ type
|
|||
##
|
||||
## The block state transition has NOT been verified
|
||||
randao_reveal*: ValidatorSig
|
||||
eth1_data*: Eth1Data ##\
|
||||
eth1_data*: Eth1Data
|
||||
## Eth1 data vote
|
||||
|
||||
graffiti*: GraffitiBytes ##\
|
||||
graffiti*: GraffitiBytes
|
||||
## Arbitrary data
|
||||
|
||||
# Operations
|
||||
|
@ -267,10 +270,10 @@ type
|
|||
TrustedBeaconBlockBody* = object
|
||||
## A full verified block
|
||||
randao_reveal*: TrustedSig
|
||||
eth1_data*: Eth1Data ##\
|
||||
eth1_data*: Eth1Data
|
||||
## Eth1 data vote
|
||||
|
||||
graffiti*: GraffitiBytes ##\
|
||||
graffiti*: GraffitiBytes
|
||||
## Arbitrary data
|
||||
|
||||
# Operations
|
||||
|
|
|
@ -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,10 +31,10 @@ type
|
|||
fork*: Fork
|
||||
|
||||
# History
|
||||
latest_block_header*: BeaconBlockHeader ##\
|
||||
latest_block_header*: BeaconBlockHeader
|
||||
## `latest_block_header.state_root == ZERO_HASH` temporarily
|
||||
|
||||
block_roots*: HashArray[Limit SLOTS_PER_HISTORICAL_ROOT, Eth2Digest] ##\
|
||||
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]
|
||||
|
@ -57,13 +48,13 @@ 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] ##\
|
||||
slashings*: HashArray[Limit EPOCHS_PER_SLASHINGS_VECTOR, Gwei]
|
||||
## Per-epoch sums of slashed effective balances
|
||||
|
||||
# Attestations
|
||||
|
@ -75,7 +66,7 @@ type
|
|||
# Finality
|
||||
justification_bits*: JustificationBits
|
||||
|
||||
previous_justified_checkpoint*: Checkpoint ##\
|
||||
previous_justified_checkpoint*: Checkpoint
|
||||
## Previous epoch snapshot
|
||||
|
||||
current_justified_checkpoint*: Checkpoint
|
||||
|
@ -100,12 +91,12 @@ type
|
|||
## is formed.
|
||||
|
||||
slot*: Slot
|
||||
proposer_index*: uint64
|
||||
proposer_index*: uint64 # `ValidatorIndex` after validation
|
||||
|
||||
parent_root*: Eth2Digest ##\
|
||||
parent_root*: Eth2Digest
|
||||
## Root hash of the previous block
|
||||
|
||||
state_root*: Eth2Digest ##\
|
||||
state_root*: Eth2Digest
|
||||
## The state root, _after_ this block has been processed
|
||||
|
||||
body*: BeaconBlockBody
|
||||
|
@ -119,12 +110,12 @@ 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 ##\
|
||||
parent_root*: Eth2Digest
|
||||
## Root hash of the previous block
|
||||
|
||||
state_root*: Eth2Digest ##\
|
||||
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
|
||||
|
|
|
@ -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*(
|
||||
|
|
|
@ -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] =
|
||||
let exited_validator =
|
||||
? check_voluntary_exit(cfg, state, signed_voluntary_exit, flags)
|
||||
initiate_validator_exit(
|
||||
cfg, state, signed_voluntary_exit.message.validator_index.ValidatorIndex,
|
||||
cache)
|
||||
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
|
||||
|
|
|
@ -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) =
|
||||
|
|
|
@ -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))
|
||||
|
||||
|
|
Loading…
Reference in New Issue