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.
|
# [REJECT] The aggregator's validator index is within the committee -- i.e.
|
||||||
# aggregate_and_proof.aggregator_index in get_beacon_committee(state,
|
# aggregate_and_proof.aggregator_index in get_beacon_committee(state,
|
||||||
# aggregate.data.slot, aggregate.data.index).
|
# 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):
|
get_beacon_committee(epochRef, slot, committee_index):
|
||||||
return checkedReject("Aggregate: aggregator's validator index not in committee")
|
return checkedReject("Aggregate: aggregator's validator index not in committee")
|
||||||
|
|
||||||
|
|
|
@ -963,9 +963,10 @@ func get_sync_committee_cache*(
|
||||||
s.incl(pk)
|
s.incl(pk)
|
||||||
|
|
||||||
var pubkeyIndices: Table[ValidatorPubKey, ValidatorIndex]
|
var pubkeyIndices: Table[ValidatorPubKey, ValidatorIndex]
|
||||||
for i, v in state.validators:
|
for vidx in state.validators.vindices:
|
||||||
if v.pubkey in s:
|
let pubkey = state.validators[vidx].pubkey
|
||||||
pubkeyIndices[v.pubkey] = i.ValidatorIndex
|
if pubkey in s:
|
||||||
|
pubkeyIndices[pubkey] = vidx
|
||||||
|
|
||||||
var res: SyncCommitteeCache
|
var res: SyncCommitteeCache
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -5,17 +5,8 @@
|
||||||
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
# * 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.
|
# 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
|
# Types specific to altair (ie known to have changed across hard forks) - see
|
||||||
# serialization and spec updates.
|
# `base` for types and guidelines common across forks
|
||||||
#
|
|
||||||
# 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
|
|
||||||
|
|
||||||
# TODO Careful, not nil analysis is broken / incomplete and the semantics will
|
# TODO Careful, not nil analysis is broken / incomplete and the semantics will
|
||||||
# likely change in future versions of the language:
|
# 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
|
# https://github.com/ethereum/consensus-specs/blob/v1.2.0-rc.1/specs/altair/validator.md#synccommitteemessage
|
||||||
SyncCommitteeMessage* = object
|
SyncCommitteeMessage* = object
|
||||||
slot*: Slot ##\
|
slot*: Slot
|
||||||
## Slot to which this contribution pertains
|
## Slot to which this contribution pertains
|
||||||
|
|
||||||
beacon_block_root*: Eth2Digest ##\
|
beacon_block_root*: Eth2Digest
|
||||||
## Block root for this signature
|
## Block root for this signature
|
||||||
|
|
||||||
validator_index*: uint64 ##\
|
validator_index*: uint64 # `ValidatorIndex` after validation
|
||||||
## Index of the validator that produced this signature
|
## Index of the validator that produced this signature
|
||||||
|
|
||||||
signature*: ValidatorSig ##\
|
signature*: ValidatorSig
|
||||||
## Signature by the validator over the block root of `slot`
|
## 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
|
# https://github.com/ethereum/consensus-specs/blob/v1.2.0-rc.1/specs/altair/validator.md#synccommitteecontribution
|
||||||
SyncCommitteeAggregationBits* =
|
SyncCommitteeAggregationBits* =
|
||||||
BitArray[SYNC_SUBCOMMITTEE_SIZE]
|
BitArray[SYNC_SUBCOMMITTEE_SIZE]
|
||||||
|
|
||||||
SyncCommitteeContribution* = object
|
SyncCommitteeContribution* = object
|
||||||
slot*: Slot ##\
|
slot*: Slot
|
||||||
## Slot to which this contribution pertains
|
## Slot to which this contribution pertains
|
||||||
|
|
||||||
beacon_block_root*: Eth2Digest ##\
|
beacon_block_root*: Eth2Digest
|
||||||
## Block root for this contribution
|
## 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
|
## The subcommittee this contribution pertains to out of the broader sync
|
||||||
## committee
|
## committee
|
||||||
|
|
||||||
aggregation_bits*: SyncCommitteeAggregationBits ##\
|
aggregation_bits*: SyncCommitteeAggregationBits
|
||||||
## A bit is set if a signature from the validator at the corresponding
|
## A bit is set if a signature from the validator at the corresponding
|
||||||
## index in the subcommittee is present in the aggregate `signature`.
|
## 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`
|
## 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
|
# https://github.com/ethereum/consensus-specs/blob/v1.2.0-rc.1/specs/altair/validator.md#contributionandproof
|
||||||
ContributionAndProof* = object
|
ContributionAndProof* = object
|
||||||
aggregator_index*: uint64
|
aggregator_index*: uint64 # `ValidatorIndex` after validation
|
||||||
contribution*: SyncCommitteeContribution
|
contribution*: SyncCommitteeContribution
|
||||||
selection_proof*: ValidatorSig
|
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
|
# https://github.com/ethereum/consensus-specs/blob/v1.2.0-rc.1/specs/altair/validator.md#syncaggregatorselectiondata
|
||||||
SyncAggregatorSelectionData* = object
|
SyncAggregatorSelectionData* = object
|
||||||
slot*: Slot
|
slot*: Slot
|
||||||
subcommittee_index*: uint64
|
subcommittee_index*: uint64 # `SyncSubcommitteeIndex` after validation
|
||||||
|
|
||||||
### Modified/overloaded
|
### Modified/overloaded
|
||||||
|
|
||||||
# https://github.com/ethereum/consensus-specs/blob/vFuture/specs/altair/sync-protocol.md#lightclientbootstrap
|
# https://github.com/ethereum/consensus-specs/blob/vFuture/specs/altair/sync-protocol.md#lightclientbootstrap
|
||||||
LightClientBootstrap* = object
|
LightClientBootstrap* = object
|
||||||
# The requested beacon block header
|
|
||||||
header*: BeaconBlockHeader
|
header*: BeaconBlockHeader
|
||||||
|
## The requested beacon block header
|
||||||
|
|
||||||
# Current sync committee corresponding to `header`
|
|
||||||
current_sync_committee*: SyncCommittee
|
current_sync_committee*: SyncCommittee
|
||||||
|
## Current sync committee corresponding to `header`
|
||||||
|
|
||||||
current_sync_committee_branch*:
|
current_sync_committee_branch*:
|
||||||
array[log2trunc(CURRENT_SYNC_COMMITTEE_INDEX), Eth2Digest]
|
array[log2trunc(CURRENT_SYNC_COMMITTEE_INDEX), Eth2Digest]
|
||||||
|
|
||||||
# https://github.com/ethereum/consensus-specs/blob/vFuture/specs/altair/sync-protocol.md#lightclientupdate
|
# https://github.com/ethereum/consensus-specs/blob/vFuture/specs/altair/sync-protocol.md#lightclientupdate
|
||||||
LightClientUpdate* = object
|
LightClientUpdate* = object
|
||||||
# The beacon block header that is attested to by the sync committee
|
|
||||||
attested_header*: BeaconBlockHeader
|
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*: SyncCommittee
|
||||||
|
## Next sync committee corresponding to `attested_header`,
|
||||||
|
## if signature is from current sync committee
|
||||||
next_sync_committee_branch*:
|
next_sync_committee_branch*:
|
||||||
array[log2trunc(NEXT_SYNC_COMMITTEE_INDEX), Eth2Digest]
|
array[log2trunc(NEXT_SYNC_COMMITTEE_INDEX), Eth2Digest]
|
||||||
|
|
||||||
|
@ -183,10 +176,9 @@ type
|
||||||
finality_branch*:
|
finality_branch*:
|
||||||
array[log2trunc(FINALIZED_ROOT_INDEX), Eth2Digest]
|
array[log2trunc(FINALIZED_ROOT_INDEX), Eth2Digest]
|
||||||
|
|
||||||
# Sync committee aggregate signature
|
|
||||||
sync_aggregate*: SyncAggregate
|
sync_aggregate*: SyncAggregate
|
||||||
# Slot at which the aggregate signature was created (untrusted)
|
|
||||||
signature_slot*: Slot
|
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
|
# https://github.com/ethereum/consensus-specs/blob/vFuture/specs/altair/sync-protocol.md#lightclientfinalityupdate
|
||||||
LightClientFinalityUpdate* = object
|
LightClientFinalityUpdate* = object
|
||||||
|
@ -231,22 +223,22 @@ type
|
||||||
|
|
||||||
# https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/altair/sync-protocol.md#lightclientstore
|
# https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/altair/sync-protocol.md#lightclientstore
|
||||||
LightClientStore* = object
|
LightClientStore* = object
|
||||||
finalized_header*: BeaconBlockHeader ##\
|
finalized_header*: BeaconBlockHeader
|
||||||
## Beacon block header that is finalized
|
## Beacon block header that is finalized
|
||||||
|
|
||||||
# Sync committees corresponding to the header
|
|
||||||
current_sync_committee*: SyncCommittee
|
current_sync_committee*: SyncCommittee
|
||||||
|
## Sync committees corresponding to the header
|
||||||
next_sync_committee*: SyncCommittee
|
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
|
## 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
|
## 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
|
previous_max_active_participants*: uint64
|
||||||
|
## Max number of active participants in a sync committee (used to compute
|
||||||
|
## safety threshold)
|
||||||
current_max_active_participants*: uint64
|
current_max_active_participants*: uint64
|
||||||
|
|
||||||
# https://github.com/ethereum/consensus-specs/blob/v1.2.0-rc.1/specs/altair/beacon-chain.md#beaconstate
|
# 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
|
fork*: Fork
|
||||||
|
|
||||||
# History
|
# History
|
||||||
latest_block_header*: BeaconBlockHeader ##\
|
latest_block_header*: BeaconBlockHeader
|
||||||
## `latest_block_header.state_root == ZERO_HASH` temporarily
|
## `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
|
## Needed to process attestations, older to newer
|
||||||
|
|
||||||
state_roots*: HashArray[Limit SLOTS_PER_HISTORICAL_ROOT, Eth2Digest]
|
state_roots*: HashArray[Limit SLOTS_PER_HISTORICAL_ROOT, Eth2Digest]
|
||||||
historical_roots*: HashList[Eth2Digest, Limit HISTORICAL_ROOTS_LIMIT]
|
historical_roots*: HashList[Eth2Digest, Limit HISTORICAL_ROOTS_LIMIT]
|
||||||
|
@ -275,14 +267,14 @@ type
|
||||||
|
|
||||||
# Registry
|
# Registry
|
||||||
validators*: HashList[Validator, Limit VALIDATOR_REGISTRY_LIMIT]
|
validators*: HashList[Validator, Limit VALIDATOR_REGISTRY_LIMIT]
|
||||||
balances*: HashList[uint64, Limit VALIDATOR_REGISTRY_LIMIT]
|
balances*: HashList[Gwei, Limit VALIDATOR_REGISTRY_LIMIT]
|
||||||
|
|
||||||
# Randomness
|
# Randomness
|
||||||
randao_mixes*: HashArray[Limit EPOCHS_PER_HISTORICAL_VECTOR, Eth2Digest]
|
randao_mixes*: HashArray[Limit EPOCHS_PER_HISTORICAL_VECTOR, Eth2Digest]
|
||||||
|
|
||||||
# Slashings
|
# Slashings
|
||||||
slashings*: HashArray[Limit EPOCHS_PER_SLASHINGS_VECTOR, uint64] ##\
|
slashings*: HashArray[Limit EPOCHS_PER_SLASHINGS_VECTOR, Gwei]
|
||||||
## Per-epoch sums of slashed effective balances
|
## Per-epoch sums of slashed effective balances
|
||||||
|
|
||||||
# Participation
|
# Participation
|
||||||
previous_epoch_participation*: EpochParticipationFlags
|
previous_epoch_participation*: EpochParticipationFlags
|
||||||
|
@ -291,8 +283,8 @@ type
|
||||||
# Finality
|
# Finality
|
||||||
justification_bits*: JustificationBits
|
justification_bits*: JustificationBits
|
||||||
|
|
||||||
previous_justified_checkpoint*: Checkpoint ##\
|
previous_justified_checkpoint*: Checkpoint
|
||||||
## Previous epoch snapshot
|
## Previous epoch snapshot
|
||||||
|
|
||||||
current_justified_checkpoint*: Checkpoint
|
current_justified_checkpoint*: Checkpoint
|
||||||
finalized_checkpoint*: Checkpoint
|
finalized_checkpoint*: Checkpoint
|
||||||
|
@ -343,27 +335,28 @@ type
|
||||||
## is formed.
|
## is formed.
|
||||||
|
|
||||||
slot*: Slot
|
slot*: Slot
|
||||||
proposer_index*: uint64
|
proposer_index*: uint64 # `ValidatorIndex` after validation
|
||||||
|
|
||||||
parent_root*: Eth2Digest ##\
|
parent_root*: Eth2Digest
|
||||||
## Root hash of the previous block
|
## Root hash of the previous block
|
||||||
|
|
||||||
state_root*: Eth2Digest ##\
|
state_root*: Eth2Digest
|
||||||
## The state root, _after_ this block has been processed
|
## The state root, _after_ this block has been processed
|
||||||
|
|
||||||
body*: BeaconBlockBody
|
body*: BeaconBlockBody
|
||||||
|
|
||||||
SigVerifiedBeaconBlock* = object
|
SigVerifiedBeaconBlock* = object
|
||||||
## A BeaconBlock that contains verified signatures
|
## A BeaconBlock that contains verified signatures
|
||||||
## but that has not been verified for state transition
|
## but that has not been verified for state transition
|
||||||
|
|
||||||
slot*: Slot
|
slot*: Slot
|
||||||
proposer_index*: uint64
|
proposer_index*: uint64 # `ValidatorIndex` after validation
|
||||||
|
|
||||||
parent_root*: Eth2Digest ##\
|
parent_root*: Eth2Digest
|
||||||
## Root hash of the previous block
|
## Root hash of the previous block
|
||||||
|
|
||||||
state_root*: Eth2Digest ##\
|
state_root*: Eth2Digest
|
||||||
## The state root, _after_ this block has been processed
|
## The state root, _after_ this block has been processed
|
||||||
|
|
||||||
body*: SigVerifiedBeaconBlockBody
|
body*: SigVerifiedBeaconBlockBody
|
||||||
|
|
||||||
|
@ -385,19 +378,19 @@ type
|
||||||
## then, the type must be manually kept compatible with its untrusted
|
## then, the type must be manually kept compatible with its untrusted
|
||||||
## cousin.
|
## cousin.
|
||||||
slot*: Slot
|
slot*: Slot
|
||||||
proposer_index*: uint64
|
proposer_index*: uint64 # `ValidatorIndex` after validation
|
||||||
parent_root*: Eth2Digest ##\
|
parent_root*: Eth2Digest
|
||||||
state_root*: Eth2Digest ##\
|
state_root*: Eth2Digest
|
||||||
body*: TrustedBeaconBlockBody
|
body*: TrustedBeaconBlockBody
|
||||||
|
|
||||||
# https://github.com/ethereum/consensus-specs/blob/v1.2.0-rc.1/specs/altair/beacon-chain.md#beaconblockbody
|
# https://github.com/ethereum/consensus-specs/blob/v1.2.0-rc.1/specs/altair/beacon-chain.md#beaconblockbody
|
||||||
BeaconBlockBody* = object
|
BeaconBlockBody* = object
|
||||||
randao_reveal*: ValidatorSig
|
randao_reveal*: ValidatorSig
|
||||||
eth1_data*: Eth1Data ##\
|
eth1_data*: Eth1Data
|
||||||
## Eth1 data vote
|
## Eth1 data vote
|
||||||
|
|
||||||
graffiti*: GraffitiBytes ##\
|
graffiti*: GraffitiBytes
|
||||||
## Arbitrary data
|
## Arbitrary data
|
||||||
|
|
||||||
# Operations
|
# Operations
|
||||||
proposer_slashings*: List[ProposerSlashing, Limit MAX_PROPOSER_SLASHINGS]
|
proposer_slashings*: List[ProposerSlashing, Limit MAX_PROPOSER_SLASHINGS]
|
||||||
|
@ -531,7 +524,7 @@ type
|
||||||
List[ValidatorStatus, Limit VALIDATOR_REGISTRY_LIMIT]
|
List[ValidatorStatus, Limit VALIDATOR_REGISTRY_LIMIT]
|
||||||
|
|
||||||
# Represent in full
|
# Represent in full
|
||||||
balances*: List[uint64, Limit VALIDATOR_REGISTRY_LIMIT]
|
balances*: List[Gwei, Limit VALIDATOR_REGISTRY_LIMIT]
|
||||||
|
|
||||||
# Mod-increment
|
# Mod-increment
|
||||||
randao_mix*: Eth2Digest
|
randao_mix*: Eth2Digest
|
||||||
|
|
|
@ -15,7 +15,43 @@
|
||||||
# These datatypes are used as specifications for serialization - thus should not
|
# 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
|
# 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
|
# `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
|
# TODO Careful, not nil analysis is broken / incomplete and the semantics will
|
||||||
# likely change in future versions of the language:
|
# likely change in future versions of the language:
|
||||||
|
@ -38,21 +74,6 @@ export
|
||||||
tables, results, json_serialization, timer, sszTypes, beacon_time, crypto,
|
tables, results, json_serialization, timer, sszTypes, beacon_time, crypto,
|
||||||
digest, presets
|
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"
|
const SPEC_VERSION* = "1.1.10"
|
||||||
## Spec version we're aiming to be compatible with, right now
|
## 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
|
# https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/beacon-chain.md#custom-types
|
||||||
Eth2Domain* = array[32, byte]
|
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
|
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
|
CommitteeIndex* = distinct uint8
|
||||||
## Index identifying a per-slot committee - depending on the active
|
## 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
|
## a committee index is valid for a particular state, see
|
||||||
## `check_attestation_index`.
|
## `check_attestation_index`.
|
||||||
##
|
##
|
||||||
## `CommitteeIndex` is not used in `datatypes` to allow reading invalid data
|
## `CommitteIndex` is not used in types used for serialization (to allow
|
||||||
## (validation happens on use instead, via `init`).
|
## reading invalid data) - validation happens on use instead, via `init`.
|
||||||
|
|
||||||
SubnetId* = distinct uint8
|
SubnetId* = distinct uint8
|
||||||
## The subnet id maps which gossip subscription to use to publish an
|
## The subnet id maps which gossip subscription to use to publish an
|
||||||
|
@ -223,7 +248,7 @@ type
|
||||||
AttestationData* = object
|
AttestationData* = object
|
||||||
slot*: Slot
|
slot*: Slot
|
||||||
|
|
||||||
index*: uint64
|
index*: uint64 ## `CommitteeIndex` after validation
|
||||||
|
|
||||||
# LMD GHOST vote
|
# LMD GHOST vote
|
||||||
beacon_block_root*: Eth2Digest
|
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
|
# https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/beacon-chain.md#deposit
|
||||||
Deposit* = object
|
Deposit* = object
|
||||||
proof*: array[DEPOSIT_CONTRACT_TREE_DEPTH + 1, Eth2Digest] ##\
|
proof*: array[DEPOSIT_CONTRACT_TREE_DEPTH + 1, Eth2Digest]
|
||||||
## Merkle path to deposit root
|
## Merkle path to deposit root
|
||||||
|
|
||||||
data*: DepositData
|
data*: DepositData
|
||||||
|
|
||||||
|
@ -256,10 +281,9 @@ type
|
||||||
|
|
||||||
# https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/beacon-chain.md#voluntaryexit
|
# https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/beacon-chain.md#voluntaryexit
|
||||||
VoluntaryExit* = object
|
VoluntaryExit* = object
|
||||||
epoch*: Epoch ##\
|
epoch*: Epoch
|
||||||
## Earliest epoch when voluntary exit can be processed
|
## Earliest epoch when voluntary exit can be processed
|
||||||
|
validator_index*: uint64 # `ValidatorIndex` after validation
|
||||||
validator_index*: uint64
|
|
||||||
|
|
||||||
SomeAttestation* = Attestation | TrustedAttestation
|
SomeAttestation* = Attestation | TrustedAttestation
|
||||||
SomeIndexedAttestation* = IndexedAttestation | TrustedIndexedAttestation
|
SomeIndexedAttestation* = IndexedAttestation | TrustedIndexedAttestation
|
||||||
|
@ -287,23 +311,23 @@ type
|
||||||
Validator* = object
|
Validator* = object
|
||||||
pubkey*: ValidatorPubKey
|
pubkey*: ValidatorPubKey
|
||||||
|
|
||||||
withdrawal_credentials*: Eth2Digest ##\
|
withdrawal_credentials*: Eth2Digest
|
||||||
## Commitment to pubkey for withdrawals and transfers
|
## Commitment to pubkey for withdrawals and transfers
|
||||||
|
|
||||||
effective_balance*: uint64 ##\
|
effective_balance*: Gwei
|
||||||
## Balance at stake
|
## Balance at stake
|
||||||
|
|
||||||
slashed*: bool
|
slashed*: bool
|
||||||
|
|
||||||
# Status epochs
|
# Status epochs
|
||||||
activation_eligibility_epoch*: Epoch ##\
|
activation_eligibility_epoch*: Epoch
|
||||||
## When criteria for activation were met
|
## When criteria for activation were met
|
||||||
|
|
||||||
activation_epoch*: Epoch
|
activation_epoch*: Epoch
|
||||||
exit_epoch*: Epoch
|
exit_epoch*: Epoch
|
||||||
|
|
||||||
withdrawable_epoch*: Epoch ##\
|
withdrawable_epoch*: Epoch
|
||||||
## When validator can withdraw or transfer funds
|
## When validator can withdraw or transfer funds
|
||||||
|
|
||||||
# https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/beacon-chain.md#pendingattestation
|
# https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/beacon-chain.md#pendingattestation
|
||||||
PendingAttestation* = object
|
PendingAttestation* = object
|
||||||
|
@ -312,7 +336,7 @@ type
|
||||||
|
|
||||||
inclusion_delay*: uint64
|
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
|
# https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/beacon-chain.md#historicalbatch
|
||||||
HistoricalBatch* = object
|
HistoricalBatch* = object
|
||||||
|
@ -324,8 +348,8 @@ type
|
||||||
previous_version*: Version
|
previous_version*: Version
|
||||||
current_version*: Version
|
current_version*: Version
|
||||||
|
|
||||||
epoch*: Epoch ##\
|
epoch*: Epoch
|
||||||
## Epoch of latest fork
|
## Epoch of latest fork
|
||||||
|
|
||||||
# https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/beacon-chain.md#eth1data
|
# https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/beacon-chain.md#eth1data
|
||||||
Eth1Data* = object
|
Eth1Data* = object
|
||||||
|
@ -345,7 +369,7 @@ type
|
||||||
# https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/beacon-chain.md#beaconblockheader
|
# https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/beacon-chain.md#beaconblockheader
|
||||||
BeaconBlockHeader* = object
|
BeaconBlockHeader* = object
|
||||||
slot*: Slot
|
slot*: Slot
|
||||||
proposer_index*: uint64
|
proposer_index*: uint64 # `ValidatorIndex` after validation
|
||||||
parent_root*: Eth2Digest
|
parent_root*: Eth2Digest
|
||||||
state_root*: Eth2Digest
|
state_root*: Eth2Digest
|
||||||
body_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
|
# https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/validator.md#aggregateandproof
|
||||||
AggregateAndProof* = object
|
AggregateAndProof* = object
|
||||||
aggregator_index*: uint64
|
aggregator_index*: uint64 # `ValidatorIndex` after validation
|
||||||
aggregate*: Attestation
|
aggregate*: Attestation
|
||||||
selection_proof*: ValidatorSig
|
selection_proof*: ValidatorSig
|
||||||
|
|
||||||
|
@ -384,8 +408,7 @@ type
|
||||||
# This doesn't know about forks or branches in the DAG. It's for straight,
|
# This doesn't know about forks or branches in the DAG. It's for straight,
|
||||||
# linear chunks of the chain.
|
# linear chunks of the chain.
|
||||||
StateCache* = object
|
StateCache* = object
|
||||||
shuffled_active_validator_indices*:
|
shuffled_active_validator_indices*: Table[Epoch, seq[ValidatorIndex]]
|
||||||
Table[Epoch, seq[ValidatorIndex]]
|
|
||||||
beacon_proposer_indices*: Table[Slot, Option[ValidatorIndex]]
|
beacon_proposer_indices*: Table[Slot, Option[ValidatorIndex]]
|
||||||
sync_committees*: Table[SyncCommitteePeriod, SyncCommitteeCache]
|
sync_committees*: Table[SyncCommitteePeriod, SyncCommitteeCache]
|
||||||
|
|
||||||
|
@ -403,23 +426,23 @@ type
|
||||||
|
|
||||||
pubkey* {.dontSerialize.}: ValidatorPubKey
|
pubkey* {.dontSerialize.}: ValidatorPubKey
|
||||||
|
|
||||||
withdrawal_credentials* {.dontSerialize.}: Eth2Digest ##\
|
withdrawal_credentials* {.dontSerialize.}: Eth2Digest
|
||||||
## Commitment to pubkey for withdrawals and transfers
|
## Commitment to pubkey for withdrawals and transfers
|
||||||
|
|
||||||
effective_balance*: uint64 ##\
|
effective_balance*: Gwei
|
||||||
## Balance at stake
|
## Balance at stake
|
||||||
|
|
||||||
slashed*: bool
|
slashed*: bool
|
||||||
|
|
||||||
# Status epochs
|
# Status epochs
|
||||||
activation_eligibility_epoch*: Epoch ##\
|
activation_eligibility_epoch*: Epoch
|
||||||
## When criteria for activation were met
|
## When criteria for activation were met
|
||||||
|
|
||||||
activation_epoch*: Epoch
|
activation_epoch*: Epoch
|
||||||
exit_epoch*: Epoch
|
exit_epoch*: Epoch
|
||||||
|
|
||||||
withdrawable_epoch*: Epoch ##\
|
withdrawable_epoch*: Epoch
|
||||||
## When validator can withdraw or transfer funds
|
## When validator can withdraw or transfer funds
|
||||||
|
|
||||||
# https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/p2p-interface.md#eth2-field
|
# https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/p2p-interface.md#eth2-field
|
||||||
ENRForkID* = object
|
ENRForkID* = object
|
||||||
|
@ -444,11 +467,11 @@ type
|
||||||
penalties*: Gwei
|
penalties*: Gwei
|
||||||
|
|
||||||
InclusionInfo* = object
|
InclusionInfo* = object
|
||||||
# The distance between the attestation slot and the slot that attestation
|
|
||||||
# was included in block.
|
|
||||||
delay*: uint64
|
delay*: uint64
|
||||||
# The index of the proposer at the slot where the attestation was included.
|
## The distance between the attestation slot and the slot that attestation
|
||||||
proposer_index*: uint64
|
## 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
|
RewardFlags* {.pure.} = enum
|
||||||
isSlashed
|
isSlashed
|
||||||
|
@ -475,7 +498,7 @@ type
|
||||||
## reward processing
|
## reward processing
|
||||||
|
|
||||||
# The validator's effective balance in the _current_ epoch.
|
# 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.
|
# True if the validator had an attestation included in the _previous_ epoch.
|
||||||
is_previous_epoch_attester*: Option[InclusionInfo]
|
is_previous_epoch_attester*: Option[InclusionInfo]
|
||||||
|
@ -583,18 +606,38 @@ template makeLimitedUInt*(T: untyped, limit: SomeUnsignedInt) =
|
||||||
template toSszType(x: T): uint64 =
|
template toSszType(x: T): uint64 =
|
||||||
{.error: "Limited types should not be used with SSZ (abi differences)".}
|
{.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) =
|
template makeLimitedU8*(T: untyped, limit: uint8) =
|
||||||
makeLimitedUInt(T, limit)
|
makeLimitedUInt(T, limit)
|
||||||
|
|
||||||
template makeLimitedU16*(T: type, limit: uint16) =
|
template makeLimitedU16*(T: type, limit: uint16) =
|
||||||
makeLimitedUInt(T, limit)
|
makeLimitedUInt(T, limit)
|
||||||
|
|
||||||
|
template makeLimitedU64*(T: untyped, limit: uint64) =
|
||||||
|
makeLimitedUInt(T, limit)
|
||||||
|
|
||||||
makeLimitedU64(CommitteeIndex, MAX_COMMITTEES_PER_SLOT)
|
makeLimitedU64(CommitteeIndex, MAX_COMMITTEES_PER_SLOT)
|
||||||
makeLimitedU64(SubnetId, ATTESTATION_SUBNET_COUNT)
|
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):
|
func init*(T: type CommitteeIndex, index, committees_per_slot: uint64):
|
||||||
Result[CommitteeIndex, cstring] =
|
Result[CommitteeIndex, cstring] =
|
||||||
if index < min(committees_per_slot, MAX_COMMITTEES_PER_SLOT):
|
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:
|
else:
|
||||||
err("Committee index out of range for epoch")
|
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*(
|
template writeValue*(
|
||||||
writer: var JsonWriter, value: Version | ForkDigest | DomainType) =
|
writer: var JsonWriter, value: Version | ForkDigest | DomainType) =
|
||||||
writeValue(writer, to0xHex(distinctBase(value)))
|
writeValue(writer, to0xHex(distinctBase(value)))
|
||||||
|
@ -638,18 +673,6 @@ proc writeValue*(writer: var JsonWriter, value: JustificationBits)
|
||||||
{.raises: [IOError, Defect].} =
|
{.raises: [IOError, Defect].} =
|
||||||
writer.writeValue $value
|
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.
|
# `ValidatorIndex` seq handling.
|
||||||
template `[]=`*[T](a: var seq[T], b: ValidatorIndex, c: T) =
|
template `[]=`*[T](a: var seq[T], b: ValidatorIndex, c: T) =
|
||||||
a[b.int] = c
|
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 (!)
|
template `[]`*[T](a: seq[T], b: ValidatorIndex): auto = # Also var seq (!)
|
||||||
a[b.int]
|
a[b.int]
|
||||||
|
|
||||||
# `ValidatorIndex` Nim integration
|
iterator vindices*(
|
||||||
template `==`*(x, y: ValidatorIndex) : bool =
|
a: HashList[Validator, Limit VALIDATOR_REGISTRY_LIMIT]): ValidatorIndex =
|
||||||
distinctBase(x) == distinctBase(y)
|
for i in 0..<a.len():
|
||||||
|
yield i.ValidatorIndex
|
||||||
|
|
||||||
template `<`*(x, y: ValidatorIndex): bool =
|
iterator vindices*(
|
||||||
distinctBase(x) < distinctBase(y)
|
a: List[Validator, Limit VALIDATOR_REGISTRY_LIMIT]): ValidatorIndex =
|
||||||
|
for i in 0..<a.len():
|
||||||
template hash*(x: ValidatorIndex): Hash =
|
yield i.ValidatorIndex
|
||||||
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
|
|
||||||
|
|
||||||
template `==`*(x, y: JustificationBits): bool =
|
template `==`*(x, y: JustificationBits): bool =
|
||||||
distinctBase(x) == distinctBase(y)
|
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).
|
# * 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.
|
# 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
|
# TODO Careful, not nil analysis is broken / incomplete and the semantics will
|
||||||
# likely change in future versions of the language:
|
# likely change in future versions of the language:
|
||||||
# https://github.com/nim-lang/RFCs/issues/250
|
# https://github.com/nim-lang/RFCs/issues/250
|
||||||
|
@ -99,11 +102,11 @@ type
|
||||||
fork*: Fork
|
fork*: Fork
|
||||||
|
|
||||||
# History
|
# History
|
||||||
latest_block_header*: BeaconBlockHeader ##\
|
latest_block_header*: BeaconBlockHeader
|
||||||
## `latest_block_header.state_root == ZERO_HASH` temporarily
|
## `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
|
## Needed to process attestations, older to newer
|
||||||
|
|
||||||
state_roots*: HashArray[Limit SLOTS_PER_HISTORICAL_ROOT, Eth2Digest]
|
state_roots*: HashArray[Limit SLOTS_PER_HISTORICAL_ROOT, Eth2Digest]
|
||||||
historical_roots*: HashList[Eth2Digest, Limit HISTORICAL_ROOTS_LIMIT]
|
historical_roots*: HashList[Eth2Digest, Limit HISTORICAL_ROOTS_LIMIT]
|
||||||
|
@ -116,14 +119,14 @@ type
|
||||||
|
|
||||||
# Registry
|
# Registry
|
||||||
validators*: HashList[Validator, Limit VALIDATOR_REGISTRY_LIMIT]
|
validators*: HashList[Validator, Limit VALIDATOR_REGISTRY_LIMIT]
|
||||||
balances*: HashList[uint64, Limit VALIDATOR_REGISTRY_LIMIT]
|
balances*: HashList[Gwei, Limit VALIDATOR_REGISTRY_LIMIT]
|
||||||
|
|
||||||
# Randomness
|
# Randomness
|
||||||
randao_mixes*: HashArray[Limit EPOCHS_PER_HISTORICAL_VECTOR, Eth2Digest]
|
randao_mixes*: HashArray[Limit EPOCHS_PER_HISTORICAL_VECTOR, Eth2Digest]
|
||||||
|
|
||||||
# Slashings
|
# Slashings
|
||||||
slashings*: HashArray[Limit EPOCHS_PER_SLASHINGS_VECTOR, uint64] ##\
|
slashings*: HashArray[Limit EPOCHS_PER_SLASHINGS_VECTOR, Gwei]
|
||||||
## Per-epoch sums of slashed effective balances
|
## Per-epoch sums of slashed effective balances
|
||||||
|
|
||||||
# Participation
|
# Participation
|
||||||
previous_epoch_participation*: EpochParticipationFlags
|
previous_epoch_participation*: EpochParticipationFlags
|
||||||
|
@ -132,8 +135,8 @@ type
|
||||||
# Finality
|
# Finality
|
||||||
justification_bits*: JustificationBits
|
justification_bits*: JustificationBits
|
||||||
|
|
||||||
previous_justified_checkpoint*: Checkpoint ##\
|
previous_justified_checkpoint*: Checkpoint
|
||||||
## Previous epoch snapshot
|
## Previous epoch snapshot
|
||||||
|
|
||||||
current_justified_checkpoint*: Checkpoint
|
current_justified_checkpoint*: Checkpoint
|
||||||
finalized_checkpoint*: Checkpoint
|
finalized_checkpoint*: Checkpoint
|
||||||
|
@ -167,13 +170,13 @@ type
|
||||||
## is formed.
|
## is formed.
|
||||||
|
|
||||||
slot*: Slot
|
slot*: Slot
|
||||||
proposer_index*: uint64
|
proposer_index*: uint64 # `ValidatorIndex` after validation
|
||||||
|
|
||||||
parent_root*: Eth2Digest ##\
|
parent_root*: Eth2Digest
|
||||||
## Root hash of the previous block
|
## Root hash of the previous block
|
||||||
|
|
||||||
state_root*: Eth2Digest ##\
|
state_root*: Eth2Digest
|
||||||
## The state root, _after_ this block has been processed
|
## The state root, _after_ this block has been processed
|
||||||
|
|
||||||
body*: BeaconBlockBody
|
body*: BeaconBlockBody
|
||||||
|
|
||||||
|
@ -181,13 +184,13 @@ type
|
||||||
## A BeaconBlock that contains verified signatures
|
## A BeaconBlock that contains verified signatures
|
||||||
## but that has not been verified for state transition
|
## but that has not been verified for state transition
|
||||||
slot*: Slot
|
slot*: Slot
|
||||||
proposer_index*: uint64
|
proposer_index*: uint64 # `ValidatorIndex` after validation
|
||||||
|
|
||||||
parent_root*: Eth2Digest ##\
|
parent_root*: Eth2Digest
|
||||||
## Root hash of the previous block
|
## Root hash of the previous block
|
||||||
|
|
||||||
state_root*: Eth2Digest ##\
|
state_root*: Eth2Digest
|
||||||
## The state root, _after_ this block has been processed
|
## The state root, _after_ this block has been processed
|
||||||
|
|
||||||
body*: SigVerifiedBeaconBlockBody
|
body*: SigVerifiedBeaconBlockBody
|
||||||
|
|
||||||
|
@ -209,19 +212,19 @@ type
|
||||||
## then, the type must be manually kept compatible with its untrusted
|
## then, the type must be manually kept compatible with its untrusted
|
||||||
## cousin.
|
## cousin.
|
||||||
slot*: Slot
|
slot*: Slot
|
||||||
proposer_index*: uint64
|
proposer_index*: uint64 # `ValidatorIndex` after validation
|
||||||
parent_root*: Eth2Digest ##\
|
parent_root*: Eth2Digest
|
||||||
state_root*: Eth2Digest ##\
|
state_root*: Eth2Digest
|
||||||
body*: TrustedBeaconBlockBody
|
body*: TrustedBeaconBlockBody
|
||||||
|
|
||||||
# https://github.com/ethereum/consensus-specs/blob/v1.1.7/specs/merge/beacon-chain.md#beaconblockbody
|
# https://github.com/ethereum/consensus-specs/blob/v1.1.7/specs/merge/beacon-chain.md#beaconblockbody
|
||||||
BeaconBlockBody* = object
|
BeaconBlockBody* = object
|
||||||
randao_reveal*: ValidatorSig
|
randao_reveal*: ValidatorSig
|
||||||
eth1_data*: Eth1Data ##\
|
eth1_data*: Eth1Data
|
||||||
## Eth1 data vote
|
## Eth1 data vote
|
||||||
|
|
||||||
graffiti*: GraffitiBytes ##\
|
graffiti*: GraffitiBytes
|
||||||
## Arbitrary data
|
## Arbitrary data
|
||||||
|
|
||||||
# Operations
|
# Operations
|
||||||
proposer_slashings*: List[ProposerSlashing, Limit MAX_PROPOSER_SLASHINGS]
|
proposer_slashings*: List[ProposerSlashing, Limit MAX_PROPOSER_SLASHINGS]
|
||||||
|
@ -247,11 +250,11 @@ type
|
||||||
##
|
##
|
||||||
## The block state transition has NOT been verified
|
## The block state transition has NOT been verified
|
||||||
randao_reveal*: ValidatorSig
|
randao_reveal*: ValidatorSig
|
||||||
eth1_data*: Eth1Data ##\
|
eth1_data*: Eth1Data
|
||||||
## Eth1 data vote
|
## Eth1 data vote
|
||||||
|
|
||||||
graffiti*: GraffitiBytes ##\
|
graffiti*: GraffitiBytes
|
||||||
## Arbitrary data
|
## Arbitrary data
|
||||||
|
|
||||||
# Operations
|
# Operations
|
||||||
proposer_slashings*: List[ProposerSlashing, Limit MAX_PROPOSER_SLASHINGS]
|
proposer_slashings*: List[ProposerSlashing, Limit MAX_PROPOSER_SLASHINGS]
|
||||||
|
@ -267,11 +270,11 @@ type
|
||||||
TrustedBeaconBlockBody* = object
|
TrustedBeaconBlockBody* = object
|
||||||
## A full verified block
|
## A full verified block
|
||||||
randao_reveal*: TrustedSig
|
randao_reveal*: TrustedSig
|
||||||
eth1_data*: Eth1Data ##\
|
eth1_data*: Eth1Data
|
||||||
## Eth1 data vote
|
## Eth1 data vote
|
||||||
|
|
||||||
graffiti*: GraffitiBytes ##\
|
graffiti*: GraffitiBytes
|
||||||
## Arbitrary data
|
## Arbitrary data
|
||||||
|
|
||||||
# Operations
|
# Operations
|
||||||
proposer_slashings*: List[TrustedProposerSlashing, Limit MAX_PROPOSER_SLASHINGS]
|
proposer_slashings*: List[TrustedProposerSlashing, Limit MAX_PROPOSER_SLASHINGS]
|
||||||
|
|
|
@ -5,17 +5,8 @@
|
||||||
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
# * 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.
|
# 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
|
# Types specific to phase0 (ie known to have changed across hard forks) - see
|
||||||
# serialization and spec updates.
|
# `base` for types and guidelines common across forks
|
||||||
#
|
|
||||||
# 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
|
|
||||||
|
|
||||||
# TODO Careful, not nil analysis is broken / incomplete and the semantics will
|
# TODO Careful, not nil analysis is broken / incomplete and the semantics will
|
||||||
# likely change in future versions of the language:
|
# likely change in future versions of the language:
|
||||||
|
@ -40,11 +31,11 @@ type
|
||||||
fork*: Fork
|
fork*: Fork
|
||||||
|
|
||||||
# History
|
# History
|
||||||
latest_block_header*: BeaconBlockHeader ##\
|
latest_block_header*: BeaconBlockHeader
|
||||||
## `latest_block_header.state_root == ZERO_HASH` temporarily
|
## `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
|
## Needed to process attestations, older to newer
|
||||||
|
|
||||||
state_roots*: HashArray[Limit SLOTS_PER_HISTORICAL_ROOT, Eth2Digest]
|
state_roots*: HashArray[Limit SLOTS_PER_HISTORICAL_ROOT, Eth2Digest]
|
||||||
historical_roots*: HashList[Eth2Digest, Limit HISTORICAL_ROOTS_LIMIT]
|
historical_roots*: HashList[Eth2Digest, Limit HISTORICAL_ROOTS_LIMIT]
|
||||||
|
@ -57,14 +48,14 @@ type
|
||||||
|
|
||||||
# Registry
|
# Registry
|
||||||
validators*: HashList[Validator, Limit VALIDATOR_REGISTRY_LIMIT]
|
validators*: HashList[Validator, Limit VALIDATOR_REGISTRY_LIMIT]
|
||||||
balances*: HashList[uint64, Limit VALIDATOR_REGISTRY_LIMIT]
|
balances*: HashList[Gwei, Limit VALIDATOR_REGISTRY_LIMIT]
|
||||||
|
|
||||||
# Randomness
|
# Randomness
|
||||||
randao_mixes*: HashArray[Limit EPOCHS_PER_HISTORICAL_VECTOR, Eth2Digest]
|
randao_mixes*: HashArray[Limit EPOCHS_PER_HISTORICAL_VECTOR, Eth2Digest]
|
||||||
|
|
||||||
# Slashings
|
# Slashings
|
||||||
slashings*: HashArray[Limit EPOCHS_PER_SLASHINGS_VECTOR, uint64] ##\
|
slashings*: HashArray[Limit EPOCHS_PER_SLASHINGS_VECTOR, Gwei]
|
||||||
## Per-epoch sums of slashed effective balances
|
## Per-epoch sums of slashed effective balances
|
||||||
|
|
||||||
# Attestations
|
# Attestations
|
||||||
previous_epoch_attestations*:
|
previous_epoch_attestations*:
|
||||||
|
@ -75,8 +66,8 @@ type
|
||||||
# Finality
|
# Finality
|
||||||
justification_bits*: JustificationBits
|
justification_bits*: JustificationBits
|
||||||
|
|
||||||
previous_justified_checkpoint*: Checkpoint ##\
|
previous_justified_checkpoint*: Checkpoint
|
||||||
## Previous epoch snapshot
|
## Previous epoch snapshot
|
||||||
|
|
||||||
current_justified_checkpoint*: Checkpoint
|
current_justified_checkpoint*: Checkpoint
|
||||||
finalized_checkpoint*: Checkpoint
|
finalized_checkpoint*: Checkpoint
|
||||||
|
@ -100,13 +91,13 @@ type
|
||||||
## is formed.
|
## is formed.
|
||||||
|
|
||||||
slot*: Slot
|
slot*: Slot
|
||||||
proposer_index*: uint64
|
proposer_index*: uint64 # `ValidatorIndex` after validation
|
||||||
|
|
||||||
parent_root*: Eth2Digest ##\
|
parent_root*: Eth2Digest
|
||||||
## Root hash of the previous block
|
## Root hash of the previous block
|
||||||
|
|
||||||
state_root*: Eth2Digest ##\
|
state_root*: Eth2Digest
|
||||||
## The state root, _after_ this block has been processed
|
## The state root, _after_ this block has been processed
|
||||||
|
|
||||||
body*: BeaconBlockBody
|
body*: BeaconBlockBody
|
||||||
|
|
||||||
|
@ -119,13 +110,13 @@ type
|
||||||
## A BeaconBlock that contains verified signatures
|
## A BeaconBlock that contains verified signatures
|
||||||
## but that has not been verified for state transition
|
## but that has not been verified for state transition
|
||||||
slot*: Slot
|
slot*: Slot
|
||||||
proposer_index*: uint64
|
proposer_index*: uint64 # `ValidatorIndex` after validation
|
||||||
|
|
||||||
parent_root*: Eth2Digest ##\
|
parent_root*: Eth2Digest
|
||||||
## Root hash of the previous block
|
## Root hash of the previous block
|
||||||
|
|
||||||
state_root*: Eth2Digest ##\
|
state_root*: Eth2Digest
|
||||||
## The state root, _after_ this block has been processed
|
## The state root, _after_ this block has been processed
|
||||||
|
|
||||||
body*: SigVerifiedBeaconBlockBody
|
body*: SigVerifiedBeaconBlockBody
|
||||||
|
|
||||||
|
@ -147,9 +138,9 @@ type
|
||||||
## then, the type must be manually kept compatible with its untrusted
|
## then, the type must be manually kept compatible with its untrusted
|
||||||
## cousin.
|
## cousin.
|
||||||
slot*: Slot
|
slot*: Slot
|
||||||
proposer_index*: uint64
|
proposer_index*: uint64 # `ValidatorIndex` after validation
|
||||||
parent_root*: Eth2Digest ##\
|
parent_root*: Eth2Digest
|
||||||
state_root*: Eth2Digest ##\
|
state_root*: Eth2Digest
|
||||||
body*: TrustedBeaconBlockBody
|
body*: TrustedBeaconBlockBody
|
||||||
|
|
||||||
# https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/beacon-chain.md#beaconblockbody
|
# 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
|
# 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):
|
iterator get_active_validator_indices*(state: ForkyBeaconState, epoch: Epoch):
|
||||||
ValidatorIndex =
|
ValidatorIndex =
|
||||||
for idx in 0..<state.validators.len:
|
for vidx in state.validators.vindices:
|
||||||
if is_active_validator(state.validators[idx], epoch):
|
if is_active_validator(state.validators[vidx], epoch):
|
||||||
yield idx.ValidatorIndex
|
yield vidx
|
||||||
|
|
||||||
func get_active_validator_indices*(state: ForkyBeaconState, epoch: Epoch):
|
func get_active_validator_indices*(state: ForkyBeaconState, epoch: Epoch):
|
||||||
seq[ValidatorIndex] =
|
seq[ValidatorIndex] =
|
||||||
## Return the sequence of active validator indices at ``epoch``.
|
## Return the sequence of active validator indices at ``epoch``.
|
||||||
var res = newSeqOfCap[ValidatorIndex](state.validators.len)
|
var res = newSeqOfCap[ValidatorIndex](state.validators.len)
|
||||||
for idx in get_active_validator_indices(state, epoch):
|
for vidx in get_active_validator_indices(state, epoch):
|
||||||
res.add idx
|
res.add vidx
|
||||||
res
|
res
|
||||||
|
|
||||||
func get_active_validator_indices_len*(state: ForkyBeaconState, epoch: Epoch):
|
func get_active_validator_indices_len*(state: ForkyBeaconState, epoch: Epoch):
|
||||||
uint64 =
|
uint64 =
|
||||||
for idx in 0..<state.validators.len:
|
for vidx in state.validators.vindices:
|
||||||
if is_active_validator(state.validators[idx], epoch):
|
if is_active_validator(state.validators[vidx], epoch):
|
||||||
inc result
|
inc result
|
||||||
|
|
||||||
func get_active_validator_indices_len*(
|
func get_active_validator_indices_len*(
|
||||||
|
|
|
@ -45,7 +45,7 @@ func process_block_header*(
|
||||||
if proposer_index.isNone:
|
if proposer_index.isNone:
|
||||||
return err("process_block_header: proposer missing")
|
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")
|
return err("process_block_header: proposer index incorrect")
|
||||||
|
|
||||||
# Verify that the parent matches
|
# Verify that the parent matches
|
||||||
|
@ -126,16 +126,12 @@ func is_slashable_validator(validator: Validator, epoch: Epoch): bool =
|
||||||
proc check_proposer_slashing*(
|
proc check_proposer_slashing*(
|
||||||
state: ForkyBeaconState, proposer_slashing: SomeProposerSlashing,
|
state: ForkyBeaconState, proposer_slashing: SomeProposerSlashing,
|
||||||
flags: UpdateFlags):
|
flags: UpdateFlags):
|
||||||
Result[void, cstring] =
|
Result[ValidatorIndex, cstring] =
|
||||||
|
|
||||||
let
|
let
|
||||||
header_1 = proposer_slashing.signed_header_1.message
|
header_1 = proposer_slashing.signed_header_1.message
|
||||||
header_2 = proposer_slashing.signed_header_2.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
|
# Verify header slots match
|
||||||
if not (header_1.slot == header_2.slot):
|
if not (header_1.slot == header_2.slot):
|
||||||
return err("check_proposer_slashing: slot mismatch")
|
return err("check_proposer_slashing: slot mismatch")
|
||||||
|
@ -149,6 +145,9 @@ proc check_proposer_slashing*(
|
||||||
return err("check_proposer_slashing: headers not different")
|
return err("check_proposer_slashing: headers not different")
|
||||||
|
|
||||||
# Verify the proposer is slashable
|
# 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]
|
let proposer = unsafeAddr state.validators.asSeq()[header_1.proposer_index]
|
||||||
if not is_slashable_validator(proposer[], get_current_epoch(state)):
|
if not is_slashable_validator(proposer[], get_current_epoch(state)):
|
||||||
return err("check_proposer_slashing: slashed proposer")
|
return err("check_proposer_slashing: slashed proposer")
|
||||||
|
@ -163,11 +162,12 @@ proc check_proposer_slashing*(
|
||||||
signed_header.signature):
|
signed_header.signature):
|
||||||
return err("check_proposer_slashing: invalid signature")
|
return err("check_proposer_slashing: invalid signature")
|
||||||
|
|
||||||
ok()
|
# Verified above against state.validators
|
||||||
|
ValidatorIndex.init(header_1.proposer_index)
|
||||||
|
|
||||||
proc check_proposer_slashing*(
|
proc check_proposer_slashing*(
|
||||||
state: var ForkedHashedBeaconState; proposer_slashing: SomeProposerSlashing;
|
state: var ForkedHashedBeaconState; proposer_slashing: SomeProposerSlashing;
|
||||||
flags: UpdateFlags): Result[void, cstring] =
|
flags: UpdateFlags): Result[ValidatorIndex, cstring] =
|
||||||
withState(state):
|
withState(state):
|
||||||
check_proposer_slashing(state.data, proposer_slashing, flags)
|
check_proposer_slashing(state.data, proposer_slashing, flags)
|
||||||
|
|
||||||
|
@ -177,11 +177,8 @@ proc process_proposer_slashing*(
|
||||||
proposer_slashing: SomeProposerSlashing, flags: UpdateFlags,
|
proposer_slashing: SomeProposerSlashing, flags: UpdateFlags,
|
||||||
cache: var StateCache):
|
cache: var StateCache):
|
||||||
Result[void, cstring] =
|
Result[void, cstring] =
|
||||||
? check_proposer_slashing(state, proposer_slashing, flags)
|
let proposer_index = ? check_proposer_slashing(state, proposer_slashing, flags)
|
||||||
slash_validator(
|
slash_validator(cfg, state, proposer_index, cache)
|
||||||
cfg, state,
|
|
||||||
proposer_slashing.signed_header_1.message.proposer_index.ValidatorIndex,
|
|
||||||
cache)
|
|
||||||
ok()
|
ok()
|
||||||
|
|
||||||
# https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/beacon-chain.md#is_slashable_attestation_data
|
# 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):
|
system.cmp):
|
||||||
if is_slashable_validator(
|
if is_slashable_validator(
|
||||||
state.validators.asSeq()[index], get_current_epoch(state)):
|
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:
|
if slashed_indices.len == 0:
|
||||||
return err("Attester slashing: Trying to slash participant(s) twice")
|
return err("Attester slashing: Trying to slash participant(s) twice")
|
||||||
|
|
||||||
|
@ -244,27 +243,24 @@ proc process_attester_slashing*(
|
||||||
flags: UpdateFlags,
|
flags: UpdateFlags,
|
||||||
cache: var StateCache
|
cache: var StateCache
|
||||||
): Result[void, cstring] =
|
): Result[void, cstring] =
|
||||||
let attester_slashing_validity =
|
let slashed_attesters =
|
||||||
check_attester_slashing(state, attester_slashing, flags)
|
? check_attester_slashing(state, attester_slashing, flags)
|
||||||
|
|
||||||
if attester_slashing_validity.isErr:
|
for index in slashed_attesters:
|
||||||
return err(attester_slashing_validity.error)
|
|
||||||
|
|
||||||
for index in attester_slashing_validity.value:
|
|
||||||
slash_validator(cfg, state, index, cache)
|
slash_validator(cfg, state, index, cache)
|
||||||
|
|
||||||
ok()
|
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
|
# 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
|
# 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
|
# 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.
|
# 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
|
# Once we have a reusable, long-lived cache, this should be revisited
|
||||||
for i in 0 ..< state.validators.len:
|
for vidx in state.validators.vindices:
|
||||||
if state.validators.asSeq[i].pubkey == pubkey:
|
if state.validators.asSeq[vidx].pubkey == pubkey:
|
||||||
return i
|
return Opt[ValidatorIndex].ok(vidx)
|
||||||
-1
|
|
||||||
|
|
||||||
proc process_deposit*(cfg: RuntimeConfig,
|
proc process_deposit*(cfg: RuntimeConfig,
|
||||||
state: var ForkyBeaconState,
|
state: var ForkyBeaconState,
|
||||||
|
@ -290,9 +286,9 @@ proc process_deposit*(cfg: RuntimeConfig,
|
||||||
amount = deposit.data.amount
|
amount = deposit.data.amount
|
||||||
index = findValidatorIndex(state, pubkey)
|
index = findValidatorIndex(state, pubkey)
|
||||||
|
|
||||||
if index != -1:
|
if index.isSome():
|
||||||
# Increase balance by deposit amount
|
# Increase balance by deposit amount
|
||||||
increase_balance(state, index.ValidatorIndex, amount)
|
increase_balance(state, index.get(), amount)
|
||||||
else:
|
else:
|
||||||
# Verify the deposit signature (proof of possession) which is not checked
|
# Verify the deposit signature (proof of possession) which is not checked
|
||||||
# by the deposit contract
|
# by the deposit contract
|
||||||
|
@ -327,11 +323,10 @@ proc check_voluntary_exit*(
|
||||||
cfg: RuntimeConfig,
|
cfg: RuntimeConfig,
|
||||||
state: ForkyBeaconState,
|
state: ForkyBeaconState,
|
||||||
signed_voluntary_exit: SomeSignedVoluntaryExit,
|
signed_voluntary_exit: SomeSignedVoluntaryExit,
|
||||||
flags: UpdateFlags): Result[void, cstring] =
|
flags: UpdateFlags): Result[ValidatorIndex, cstring] =
|
||||||
|
|
||||||
let voluntary_exit = signed_voluntary_exit.message
|
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:
|
if voluntary_exit.validator_index >= state.validators.lenu64:
|
||||||
return err("Exit: invalid validator index")
|
return err("Exit: invalid validator index")
|
||||||
|
|
||||||
|
@ -362,23 +357,13 @@ proc check_voluntary_exit*(
|
||||||
validator[].pubkey, signed_voluntary_exit.signature):
|
validator[].pubkey, signed_voluntary_exit.signature):
|
||||||
return err("Exit: invalid signature")
|
return err("Exit: invalid signature")
|
||||||
|
|
||||||
# Initiate exit
|
# Checked above
|
||||||
debug "Exit: checking voluntary exit (validator_leaving)",
|
ValidatorIndex.init(voluntary_exit.validator_index)
|
||||||
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()
|
|
||||||
|
|
||||||
proc check_voluntary_exit*(
|
proc check_voluntary_exit*(
|
||||||
cfg: RuntimeConfig, state: ForkedHashedBeaconState;
|
cfg: RuntimeConfig, state: ForkedHashedBeaconState;
|
||||||
signed_voluntary_exit: SomeSignedVoluntaryExit;
|
signed_voluntary_exit: SomeSignedVoluntaryExit;
|
||||||
flags: UpdateFlags): Result[void, cstring] =
|
flags: UpdateFlags): Result[ValidatorIndex, cstring] =
|
||||||
withState(state):
|
withState(state):
|
||||||
check_voluntary_exit(cfg, state.data, signed_voluntary_exit, flags)
|
check_voluntary_exit(cfg, state.data, signed_voluntary_exit, flags)
|
||||||
|
|
||||||
|
@ -389,10 +374,9 @@ proc process_voluntary_exit*(
|
||||||
signed_voluntary_exit: SomeSignedVoluntaryExit,
|
signed_voluntary_exit: SomeSignedVoluntaryExit,
|
||||||
flags: UpdateFlags,
|
flags: UpdateFlags,
|
||||||
cache: var StateCache): Result[void, cstring] =
|
cache: var StateCache): Result[void, cstring] =
|
||||||
? check_voluntary_exit(cfg, state, signed_voluntary_exit, flags)
|
let exited_validator =
|
||||||
initiate_validator_exit(
|
? check_voluntary_exit(cfg, state, signed_voluntary_exit, flags)
|
||||||
cfg, state, signed_voluntary_exit.message.validator_index.ValidatorIndex,
|
initiate_validator_exit(cfg, state, exited_validator, cache)
|
||||||
cache)
|
|
||||||
ok()
|
ok()
|
||||||
|
|
||||||
# https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/beacon-chain.md#operations
|
# 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)
|
info, flag_index)
|
||||||
active_increments = get_active_increments(info)
|
active_increments = get_active_increments(info)
|
||||||
|
|
||||||
for index in 0 ..< state.validators.len:
|
for vidx in state.validators.vindices:
|
||||||
if not is_eligible_validator(info.validators[index]):
|
if not is_eligible_validator(info.validators[vidx]):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
template vidx: ValidatorIndex = index.ValidatorIndex
|
|
||||||
let base_reward = get_base_reward_increment(state, vidx, base_reward_per_increment)
|
let base_reward = get_base_reward_increment(state, vidx, base_reward_per_increment)
|
||||||
yield
|
yield
|
||||||
if is_unslashed_participating_index(
|
if is_unslashed_participating_index(
|
||||||
|
@ -705,16 +704,15 @@ iterator get_inactivity_penalty_deltas*(
|
||||||
cfg.INACTIVITY_SCORE_BIAS * INACTIVITY_PENALTY_QUOTIENT_ALTAIR
|
cfg.INACTIVITY_SCORE_BIAS * INACTIVITY_PENALTY_QUOTIENT_ALTAIR
|
||||||
previous_epoch = get_previous_epoch(state)
|
previous_epoch = get_previous_epoch(state)
|
||||||
|
|
||||||
for index in 0 ..< state.validators.len:
|
for vidx in state.validators.vindices:
|
||||||
if not is_eligible_validator(info.validators[index]):
|
if not is_eligible_validator(info.validators[vidx]):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
template vidx: untyped = index.ValidatorIndex
|
|
||||||
if not is_unslashed_participating_index(
|
if not is_unslashed_participating_index(
|
||||||
state, TIMELY_TARGET_FLAG_INDEX, previous_epoch, vidx):
|
state, TIMELY_TARGET_FLAG_INDEX, previous_epoch, vidx):
|
||||||
let
|
let
|
||||||
penalty_numerator = state.validators[index].effective_balance *
|
penalty_numerator = state.validators[vidx].effective_balance *
|
||||||
state.inactivity_scores[index]
|
state.inactivity_scores[vidx]
|
||||||
yield (vidx, Gwei(penalty_numerator div penalty_denominator))
|
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
|
# 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
|
cfg.INACTIVITY_SCORE_BIAS * INACTIVITY_PENALTY_QUOTIENT_BELLATRIX
|
||||||
previous_epoch = get_previous_epoch(state)
|
previous_epoch = get_previous_epoch(state)
|
||||||
|
|
||||||
for index in 0 ..< state.validators.len:
|
for vidx in state.validators.vindices:
|
||||||
if not is_eligible_validator(info.validators[index]):
|
if not is_eligible_validator(info.validators[vidx]):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
template vidx: untyped = index.ValidatorIndex
|
|
||||||
if not is_unslashed_participating_index(
|
if not is_unslashed_participating_index(
|
||||||
state, TIMELY_TARGET_FLAG_INDEX, previous_epoch, vidx):
|
state, TIMELY_TARGET_FLAG_INDEX, previous_epoch, vidx):
|
||||||
let
|
let
|
||||||
penalty_numerator = state.validators[index].effective_balance *
|
penalty_numerator = state.validators[vidx].effective_balance *
|
||||||
state.inactivity_scores[index]
|
state.inactivity_scores[vidx]
|
||||||
yield (vidx, Gwei(penalty_numerator div penalty_denominator))
|
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
|
# 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
|
# the current epoch, 1 + MAX_SEED_LOOKAHEAD epochs ahead. Thus caches
|
||||||
# remain valid for this epoch through though this function along with
|
# remain valid for this epoch through though this function along with
|
||||||
# the rest of the epoch transition.
|
# the rest of the epoch transition.
|
||||||
for index in 0..<state.validators.len():
|
for vidx in state.validators.vindices:
|
||||||
if is_eligible_for_activation_queue(state.validators.asSeq()[index]):
|
if is_eligible_for_activation_queue(state.validators.asSeq()[vidx]):
|
||||||
state.validators[index].activation_eligibility_epoch =
|
state.validators[vidx].activation_eligibility_epoch =
|
||||||
get_current_epoch(state) + 1
|
get_current_epoch(state) + 1
|
||||||
|
|
||||||
if is_active_validator(state.validators.asSeq()[index], get_current_epoch(state)) and
|
if is_active_validator(state.validators.asSeq()[vidx], get_current_epoch(state)) and
|
||||||
state.validators.asSeq()[index].effective_balance <= cfg.EJECTION_BALANCE:
|
state.validators.asSeq()[vidx].effective_balance <= cfg.EJECTION_BALANCE:
|
||||||
initiate_validator_exit(cfg, state, index.ValidatorIndex, cache)
|
initiate_validator_exit(cfg, state, vidx, cache)
|
||||||
|
|
||||||
## Queue validators eligible for activation and not dequeued for activation
|
## Queue validators eligible for activation and not dequeued for activation
|
||||||
var activation_queue : seq[tuple[a: Epoch, b: int]] = @[]
|
var activation_queue : seq[tuple[a: Epoch, b: ValidatorIndex]] = @[]
|
||||||
for index in 0..<state.validators.len():
|
for vidx in state.validators.vindices:
|
||||||
let validator = unsafeAddr state.validators.asSeq()[index]
|
let validator = unsafeAddr state.validators.asSeq()[vidx]
|
||||||
if is_eligible_for_activation(state, validator[]):
|
if is_eligible_for_activation(state, validator[]):
|
||||||
activation_queue.add (
|
activation_queue.add (
|
||||||
validator[].activation_eligibility_epoch, index)
|
validator[].activation_eligibility_epoch, vidx)
|
||||||
|
|
||||||
activation_queue.sort(system.cmp)
|
activation_queue.sort(system.cmp)
|
||||||
|
|
||||||
|
@ -845,8 +842,8 @@ func process_registry_updates*(
|
||||||
if i.uint64 >= churn_limit:
|
if i.uint64 >= churn_limit:
|
||||||
break
|
break
|
||||||
let
|
let
|
||||||
(_, index) = epoch_and_index
|
(_, vidx) = epoch_and_index
|
||||||
state.validators[index].activation_epoch =
|
state.validators[vidx].activation_epoch =
|
||||||
compute_activation_exit_epoch(get_current_epoch(state))
|
compute_activation_exit_epoch(get_current_epoch(state))
|
||||||
|
|
||||||
# https://github.com/ethereum/consensus-specs/blob/v1.1.10/specs/phase0/beacon-chain.md#slashings
|
# 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(
|
adjusted_total_slashing_balance = get_adjusted_total_slashing_balance(
|
||||||
state, total_balance)
|
state, total_balance)
|
||||||
|
|
||||||
for index in 0..<state.validators.len:
|
for vidx in state.validators.vindices:
|
||||||
let validator = unsafeAddr state.validators.asSeq()[index]
|
let validator = unsafeAddr state.validators.asSeq()[vidx]
|
||||||
if slashing_penalty_applies(validator[], epoch):
|
if slashing_penalty_applies(validator[], epoch):
|
||||||
let penalty = validator[].get_slashing_penalty(
|
let penalty = validator[].get_slashing_penalty(
|
||||||
adjusted_total_slashing_balance, total_balance)
|
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
|
# 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) =
|
func process_eth1_data_reset*(state: var ForkyBeaconState) =
|
||||||
|
|
|
@ -41,7 +41,7 @@ type
|
||||||
currentEpochParticipation: EpochParticipationFlags
|
currentEpochParticipation: EpochParticipationFlags
|
||||||
previousEpochParticipation: EpochParticipationFlags
|
previousEpochParticipation: EpochParticipationFlags
|
||||||
|
|
||||||
PubkeyToIndexTable = Table[ValidatorPubKey, int]
|
PubkeyToIndexTable = Table[ValidatorPubKey, ValidatorIndex]
|
||||||
|
|
||||||
AuxiliaryState* = object
|
AuxiliaryState* = object
|
||||||
epochParticipationFlags: ParticipationFlags
|
epochParticipationFlags: ParticipationFlags
|
||||||
|
@ -340,12 +340,13 @@ proc collectFromDeposits(
|
||||||
let pubkey = deposit.data.pubkey
|
let pubkey = deposit.data.pubkey
|
||||||
let amount = deposit.data.amount
|
let amount = deposit.data.amount
|
||||||
var index = findValidatorIndex(state.data, pubkey)
|
var index = findValidatorIndex(state.data, pubkey)
|
||||||
if index == -1:
|
if index.isNone:
|
||||||
index = pubkeyToIndex.getOrDefault(pubkey, -1)
|
if pubkey in pubkeyToIndex:
|
||||||
if index != -1:
|
index = Opt[ValidatorIndex].ok(pubkeyToIndex[pubkey])
|
||||||
rewardsAndPenalties[index].deposits += amount
|
if index.isSome:
|
||||||
|
rewardsAndPenalties[index.get()].deposits += amount
|
||||||
elif verify_deposit_signature(cfg, deposit.data):
|
elif verify_deposit_signature(cfg, deposit.data):
|
||||||
pubkeyToIndex[pubkey] = rewardsAndPenalties.len
|
pubkeyToIndex[pubkey] = ValidatorIndex(rewardsAndPenalties.len)
|
||||||
rewardsAndPenalties.add(
|
rewardsAndPenalties.add(
|
||||||
RewardsAndPenalties(deposits: amount))
|
RewardsAndPenalties(deposits: amount))
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue