diff --git a/beacon_chain/spec/beaconstate.nim b/beacon_chain/spec/beaconstate.nim index 886460a55..2236f89b5 100644 --- a/beacon_chain/spec/beaconstate.nim +++ b/beacon_chain/spec/beaconstate.nim @@ -19,7 +19,7 @@ import export extras, phase0, altair, merge -# https://github.com/ethereum/eth2.0-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#is_valid_merkle_branch +# https://github.com/ethereum/consensus-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#is_valid_merkle_branch func is_valid_merkle_branch*(leaf: Eth2Digest, branch: openArray[Eth2Digest], depth: int, index: uint64, root: Eth2Digest): bool {.nbench.}= @@ -39,7 +39,7 @@ func is_valid_merkle_branch*(leaf: Eth2Digest, branch: openArray[Eth2Digest], value = eth2digest(buf) value == root -# https://github.com/ethereum/eth2.0-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#increase_balance +# https://github.com/ethereum/consensus-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#increase_balance func increase_balance*(balance: var Gwei, delta: Gwei) = balance += delta @@ -49,7 +49,7 @@ func increase_balance*( if delta != 0: # avoid dirtying the balance cache if not needed increase_balance(state.balances[index], delta) -# https://github.com/ethereum/eth2.0-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#decrease_balance +# https://github.com/ethereum/consensus-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#decrease_balance func decrease_balance*(balance: var Gwei, delta: Gwei) = balance = if delta > balance: @@ -64,8 +64,8 @@ func decrease_balance*( if delta != 0: # avoid dirtying the balance cache if not needed decrease_balance(state.balances[index], delta) -# https://github.com/ethereum/eth2.0-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#deposits -# https://github.com/ethereum/eth2.0-specs/blob/v1.1.0-alpha.6/specs/altair/beacon-chain.md#modified-process_deposit +# https://github.com/ethereum/consensus-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#deposits +# https://github.com/ethereum/consensus-specs/blob/v1.1.0-beta.2/specs/altair/beacon-chain.md#modified-process_deposit func get_validator_from_deposit*(deposit: DepositData): Validator = let @@ -83,13 +83,13 @@ func get_validator_from_deposit*(deposit: DepositData): effective_balance: effective_balance ) -# https://github.com/ethereum/eth2.0-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#compute_activation_exit_epoch +# https://github.com/ethereum/consensus-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#compute_activation_exit_epoch func compute_activation_exit_epoch(epoch: Epoch): Epoch = ## Return the epoch during which validator activations and exits initiated in ## ``epoch`` take effect. epoch + 1 + MAX_SEED_LOOKAHEAD -# https://github.com/ethereum/eth2.0-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#get_validator_churn_limit +# https://github.com/ethereum/consensus-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#get_validator_churn_limit func get_validator_churn_limit( cfg: RuntimeConfig, state: SomeBeaconState, cache: var StateCache): uint64 = @@ -99,7 +99,7 @@ func get_validator_churn_limit( count_active_validators( state, state.get_current_epoch(), cache) div cfg.CHURN_LIMIT_QUOTIENT) -# https://github.com/ethereum/eth2.0-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#initiate_validator_exit +# https://github.com/ethereum/consensus-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#initiate_validator_exit func initiate_validator_exit*(cfg: RuntimeConfig, state: var SomeBeaconState, index: ValidatorIndex, cache: var StateCache) = ## Initiate the exit of the validator with index ``index``. @@ -139,8 +139,8 @@ func initiate_validator_exit*(cfg: RuntimeConfig, state: var SomeBeaconState, validator.withdrawable_epoch = validator.exit_epoch + cfg.MIN_VALIDATOR_WITHDRAWABILITY_DELAY -# https://github.com/ethereum/eth2.0-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#slash_validator -# https://github.com/ethereum/eth2.0-specs/blob/v1.1.0-alpha.6/specs/altair/beacon-chain.md#modified-slash_validator +# https://github.com/ethereum/consensus-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#slash_validator +# https://github.com/ethereum/consensus-specs/blob/v1.1.0-beta.2/specs/altair/beacon-chain.md#modified-slash_validator proc slash_validator*( cfg: RuntimeConfig, state: var SomeBeaconState, slashed_index: ValidatorIndex, cache: var StateCache) = @@ -216,7 +216,7 @@ func altairFork*(cfg: RuntimeConfig): Fork = current_version: cfg.ALTAIR_FORK_VERSION, epoch: cfg.ALTAIR_FORK_EPOCH) -# https://github.com/ethereum/eth2.0-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#genesis +# https://github.com/ethereum/consensus-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#genesis proc initialize_beacon_state_from_eth1*( cfg: RuntimeConfig, eth1_block_hash: Eth2Digest, @@ -314,7 +314,7 @@ proc initialize_hashed_beacon_state_from_eth1*( phase0.HashedBeaconState( data: genesisState[], root: hash_tree_root(genesisState[])) -# https://github.com/ethereum/eth2.0-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#genesis-block +# https://github.com/ethereum/consensus-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#genesis-block func get_initial_beacon_block*(state: phase0.BeaconState): phase0.TrustedSignedBeaconBlock = # The genesis block is implicitly trusted @@ -326,7 +326,7 @@ func get_initial_beacon_block*(state: phase0.BeaconState): phase0.TrustedSignedBeaconBlock( message: message, root: hash_tree_root(message)) -# https://github.com/ethereum/eth2.0-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#get_block_root_at_slot +# https://github.com/ethereum/consensus-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#get_block_root_at_slot func get_block_root_at_slot*(state: SomeBeaconState, slot: Slot): Eth2Digest = ## Return the block root at a recent ``slot``. @@ -339,27 +339,29 @@ func get_block_root_at_slot*(state: SomeBeaconState, doAssert slot < state.slot state.block_roots[slot mod SLOTS_PER_HISTORICAL_ROOT] -# https://github.com/ethereum/eth2.0-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#get_block_root +# https://github.com/ethereum/consensus-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#get_block_root func get_block_root*(state: SomeBeaconState, epoch: Epoch): Eth2Digest = ## Return the block root at the start of a recent ``epoch``. get_block_root_at_slot(state, compute_start_slot_at_epoch(epoch)) -# https://github.com/ethereum/eth2.0-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#get_total_balance -func get_total_balance*(state: SomeBeaconState, validators: auto): Gwei = +# https://github.com/ethereum/consensus-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#get_total_balance +template get_total_balance*( + state: SomeBeaconState, validator_indices: untyped): Gwei = ## Return the combined effective balance of the ``indices``. ## ``EFFECTIVE_BALANCE_INCREMENT`` Gwei minimum to avoid divisions by zero. ## Math safe up to ~10B ETH, afterwhich this overflows uint64. - max(EFFECTIVE_BALANCE_INCREMENT, - foldl(validators, a + state.validators[b].effective_balance, 0'u64) - ) + var res = 0.Gwei + for validator_index in validator_indices: + res += state.validators.asSeq()[validator_index].effective_balance + max(EFFECTIVE_BALANCE_INCREMENT, res) -# https://github.com/ethereum/eth2.0-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#is_eligible_for_activation_queue +# https://github.com/ethereum/consensus-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#is_eligible_for_activation_queue func is_eligible_for_activation_queue(validator: Validator): bool = ## Check if ``validator`` is eligible to be placed into the activation queue. validator.activation_eligibility_epoch == FAR_FUTURE_EPOCH and validator.effective_balance == MAX_EFFECTIVE_BALANCE -# https://github.com/ethereum/eth2.0-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#is_eligible_for_activation +# https://github.com/ethereum/consensus-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#is_eligible_for_activation func is_eligible_for_activation(state: SomeBeaconState, validator: Validator): bool = ## Check if ``validator`` is eligible for activation. @@ -369,7 +371,7 @@ func is_eligible_for_activation(state: SomeBeaconState, validator: Validator): # Has not yet been activated validator.activation_epoch == FAR_FUTURE_EPOCH -# https://github.com/ethereum/eth2.0-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#registry-updates +# https://github.com/ethereum/consensus-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#registry-updates proc process_registry_updates*( cfg: RuntimeConfig, state: var SomeBeaconState, cache: var StateCache) {.nbench.} = ## Process activation eligibility and ejections @@ -419,7 +421,7 @@ proc process_registry_updates*( state.validators[index].activation_epoch = compute_activation_exit_epoch(get_current_epoch(state)) -# https://github.com/ethereum/eth2.0-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#is_valid_indexed_attestation +# https://github.com/ethereum/consensus-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#is_valid_indexed_attestation proc is_valid_indexed_attestation*( state: SomeBeaconState, indexed_attestation: SomeIndexedAttestation, flags: UpdateFlags): Result[void, cstring] = @@ -457,7 +459,7 @@ proc is_valid_indexed_attestation*( ok() -# https://github.com/ethereum/eth2.0-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#get_attesting_indices +# https://github.com/ethereum/consensus-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#get_attesting_indices func get_attesting_indices*(state: SomeBeaconState, data: AttestationData, bits: CommitteeValidatorsBits, @@ -509,8 +511,8 @@ proc is_valid_indexed_attestation*( # Attestation validation # ------------------------------------------------------------------------------------------ -# https://github.com/ethereum/eth2.0-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#attestations -# https://github.com/ethereum/eth2.0-specs/blob/v1.0.1/specs/phase0/p2p-interface.md#beacon_attestation_subnet_id +# https://github.com/ethereum/consensus-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#attestations +# https://github.com/ethereum/consensus-specs/blob/v1.0.1/specs/phase0/p2p-interface.md#beacon_attestation_subnet_id func check_attestation_slot_target*(data: AttestationData): Result[void, cstring] = if not (data.target.epoch == compute_epoch_at_slot(data.slot)): @@ -549,7 +551,7 @@ func check_attestation_index( ok() -# https://github.com/ethereum/eth2.0-specs/blob/v1.1.0-alpha.7/specs/altair/beacon-chain.md#get_attestation_participation_flag_indices +# https://github.com/ethereum/consensus-specs/blob/v1.1.0-beta.2/specs/altair/beacon-chain.md#get_attestation_participation_flag_indices func get_attestation_participation_flag_indices(state: altair.BeaconState, data: AttestationData, inclusion_delay: uint64): seq[int] = @@ -583,7 +585,7 @@ func get_attestation_participation_flag_indices(state: altair.BeaconState, # TODO these duplicate some stuff in state_transition_epoch which uses TotalBalances # better to centralize around that if feasible -# https://github.com/ethereum/eth2.0-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#get_total_active_balance +# https://github.com/ethereum/consensus-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#get_total_active_balance func get_total_active_balance*(state: SomeBeaconState, cache: var StateCache): Gwei = ## Return the combined effective balance of the active validators. # Note: ``get_total_balance`` returns ``EFFECTIVE_BALANCE_INCREMENT`` Gwei @@ -594,13 +596,13 @@ func get_total_active_balance*(state: SomeBeaconState, cache: var StateCache): G get_total_balance( state, cache.get_shuffled_active_validator_indices(state, epoch)) -# https://github.com/ethereum/eth2.0-specs/blob/v1.1.0-alpha.8/specs/altair/beacon-chain.md#get_base_reward_per_increment +# https://github.com/ethereum/consensus-specs/blob/v1.1.0-beta.2/specs/altair/beacon-chain.md#get_base_reward_per_increment func get_base_reward_per_increment*( state: altair.BeaconState, cache: var StateCache): Gwei = EFFECTIVE_BALANCE_INCREMENT * BASE_REWARD_FACTOR div integer_squareroot(get_total_active_balance(state, cache)) -# https://github.com/ethereum/eth2.0-specs/blob/v1.1.0-alpha.8/specs/altair/beacon-chain.md#get_base_reward +# https://github.com/ethereum/consensus-specs/blob/v1.1.0-beta.2/specs/altair/beacon-chain.md#get_base_reward func get_base_reward( state: altair.BeaconState, index: ValidatorIndex, base_reward_per_increment: Gwei): Gwei = @@ -610,7 +612,7 @@ func get_base_reward( state.validators[index].effective_balance div EFFECTIVE_BALANCE_INCREMENT increments * base_reward_per_increment -# https://github.com/ethereum/eth2.0-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#attestations +# https://github.com/ethereum/consensus-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#attestations proc check_attestation*( state: SomeBeaconState, attestation: SomeAttestation, flags: UpdateFlags, cache: var StateCache): Result[void, cstring] = @@ -717,7 +719,7 @@ proc process_attestation*( ok() -# https://github.com/ethereum/eth2.0-specs/blob/v1.1.0-alpha.7/specs/altair/beacon-chain.md#get_next_sync_committee_indices +# https://github.com/ethereum/consensus-specs/blob/v1.1.0-alpha.7/specs/altair/beacon-chain.md#get_next_sync_committee_indices func get_next_sync_committee_indices(state: altair.BeaconState): seq[ValidatorIndex] = ## Return the sequence of sync committee indices (which may include @@ -749,7 +751,7 @@ func get_next_sync_committee_indices(state: altair.BeaconState): i += 1'u64 sync_committee_indices -# https://github.com/ethereum/eth2.0-specs/blob/v1.1.0-alpha.7/specs/altair/beacon-chain.md#get_next_sync_committee +# https://github.com/ethereum/consensus-specs/blob/v1.1.0-alpha.7/specs/altair/beacon-chain.md#get_next_sync_committee proc get_next_sync_committee*(state: altair.BeaconState): SyncCommittee = ## Return the *next* sync committee for a given ``state``. let indices = get_next_sync_committee_indices(state) @@ -771,7 +773,7 @@ proc get_next_sync_committee*(state: altair.BeaconState): SyncCommittee = res.aggregate_pubkey = finish(attestersAgg).toPubKey() res -# https://github.com/ethereum/eth2.0-specs/blob/v1.1.0-alpha.6/specs/altair/fork.md#upgrading-the-state +# https://github.com/ethereum/consensus-specs/blob/v1.1.0-beta.2/specs/altair/fork.md#upgrading-the-state func translate_participation( state: var altair.BeaconState, pending_attestations: openArray[phase0.PendingAttestation]) = diff --git a/beacon_chain/spec/helpers.nim b/beacon_chain/spec/helpers.nim index 7d44dfff1..8795f7471 100644 --- a/beacon_chain/spec/helpers.nim +++ b/beacon_chain/spec/helpers.nim @@ -23,7 +23,7 @@ import export phase0, altair, eth2_merkleization, ssz_codec -# https://github.com/ethereum/eth2.0-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#integer_squareroot +# https://github.com/ethereum/consensus-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#integer_squareroot func integer_squareroot*(n: SomeInteger): SomeInteger = ## Return the largest integer ``x`` such that ``x**2 <= n``. doAssert n >= 0'u64 @@ -36,7 +36,7 @@ func integer_squareroot*(n: SomeInteger): SomeInteger = y = (x + n div x) div 2 x -# https://github.com/ethereum/eth2.0-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#compute_epoch_at_slot +# https://github.com/ethereum/consensus-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#compute_epoch_at_slot func compute_epoch_at_slot*(slot: Slot|uint64): Epoch = ## Return the epoch number at ``slot``. (slot div SLOTS_PER_EPOCH).Epoch @@ -53,24 +53,30 @@ template syncCommitteePeriod*(epoch: Epoch): uint64 = template syncCommitteePeriod*(slot: Slot): uint64 = epoch(slot) div EPOCHS_PER_SYNC_COMMITTEE_PERIOD -# https://github.com/ethereum/eth2.0-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#compute_start_slot_at_epoch +# https://github.com/ethereum/consensus-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#compute_start_slot_at_epoch func compute_start_slot_at_epoch*(epoch: Epoch): Slot = ## Return the start slot of ``epoch``. (epoch * SLOTS_PER_EPOCH).Slot -# https://github.com/ethereum/eth2.0-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#is_active_validator +# https://github.com/ethereum/consensus-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#is_active_validator func is_active_validator*(validator: Validator, epoch: Epoch): bool = ## Check if ``validator`` is active validator.activation_epoch <= epoch and epoch < validator.exit_epoch -# https://github.com/ethereum/eth2.0-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#get_active_validator_indices +# https://github.com/ethereum/consensus-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#get_active_validator_indices +iterator get_active_validator_indices*(state: SomeBeaconState, epoch: Epoch): + ValidatorIndex = + for idx in 0..= GENESIS_SLOT, $state.slot compute_epoch_at_slot(state.slot) -# https://github.com/ethereum/eth2.0-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#get_randao_mix +# https://github.com/ethereum/consensus-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#get_randao_mix func get_randao_mix*(state: SomeBeaconState, epoch: Epoch): Eth2Digest = ## Returns the randao mix at a recent ``epoch``. state.randao_mixes[epoch mod EPOCHS_PER_HISTORICAL_VECTOR] @@ -109,7 +115,7 @@ func uint_to_bytes4*(x: uint64): array[4, byte] = result[2] = ((x shr 16) and 0xff).byte result[3] = ((x shr 24) and 0xff).byte -# https://github.com/ethereum/eth2.0-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#compute_fork_data_root +# https://github.com/ethereum/consensus-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#compute_fork_data_root func compute_fork_data_root(current_version: Version, genesis_validators_root: Eth2Digest): Eth2Digest = ## Return the 32-byte fork data root for the ``current_version`` and @@ -121,7 +127,7 @@ func compute_fork_data_root(current_version: Version, genesis_validators_root: genesis_validators_root )) -# https://github.com/ethereum/eth2.0-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#compute_fork_digest +# https://github.com/ethereum/consensus-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#compute_fork_digest func compute_fork_digest*(current_version: Version, genesis_validators_root: Eth2Digest): ForkDigest = ## Return the 4-byte fork digest for the ``current_version`` and @@ -132,7 +138,7 @@ func compute_fork_digest*(current_version: Version, compute_fork_data_root( current_version, genesis_validators_root).data.toOpenArray(0, 3) -# https://github.com/ethereum/eth2.0-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#compute_domain +# https://github.com/ethereum/consensus-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#compute_domain func compute_domain*( domain_type: DomainType, fork_version: Version, @@ -143,7 +149,7 @@ func compute_domain*( result[0..3] = uint_to_bytes4(domain_type.uint64) result[4..31] = fork_data_root.data.toOpenArray(0, 27) -# https://github.com/ethereum/eth2.0-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#get_domain +# https://github.com/ethereum/consensus-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#get_domain func get_domain*( fork: Fork, domain_type: DomainType, @@ -164,7 +170,7 @@ func get_domain*( ## of a message. get_domain(state.fork, domain_type, epoch, state.genesis_validators_root) -# https://github.com/ethereum/eth2.0-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#compute_signing_root +# https://github.com/ethereum/consensus-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#compute_signing_root func compute_signing_root*(ssz_object: auto, domain: Eth2Domain): Eth2Digest = ## Return the signing root of an object by calculating the root of the ## object-domain tree. @@ -174,7 +180,7 @@ func compute_signing_root*(ssz_object: auto, domain: Eth2Domain): Eth2Digest = ) hash_tree_root(domain_wrapped_object) -# https://github.com/ethereum/eth2.0-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#get_seed +# https://github.com/ethereum/consensus-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#get_seed func get_seed*(state: SomeBeaconState, epoch: Epoch, domain_type: DomainType): Eth2Digest = ## Return the seed at ``epoch``. @@ -192,12 +198,12 @@ func get_seed*(state: SomeBeaconState, epoch: Epoch, domain_type: DomainType): epoch + EPOCHS_PER_HISTORICAL_VECTOR - MIN_SEED_LOOKAHEAD - 1).data eth2digest(seed_input) -# https://github.com/ethereum/eth2.0-specs/blob/v1.1.0-alpha.8/specs/altair/beacon-chain.md#add_flag +# https://github.com/ethereum/consensus-specs/blob/v1.1.0-beta.2/specs/altair/beacon-chain.md#add_flag func add_flag*(flags: ParticipationFlags, flag_index: int): ParticipationFlags = let flag = ParticipationFlags(1'u8 shl flag_index) flags or flag -# https://github.com/ethereum/eth2.0-specs/blob/v1.1.0-alpha.8/specs/altair/beacon-chain.md#has_flag +# https://github.com/ethereum/consensus-specs/blob/v1.1.0-beta.2/specs/altair/beacon-chain.md#has_flag func has_flag*(flags: ParticipationFlags, flag_index: int): bool = let flag = ParticipationFlags(1'u8 shl flag_index) (flags and flag) == flag diff --git a/beacon_chain/spec/state_transition_epoch.nim b/beacon_chain/spec/state_transition_epoch.nim index 6d2b80a19..e066618fd 100644 --- a/beacon_chain/spec/state_transition_epoch.nim +++ b/beacon_chain/spec/state_transition_epoch.nim @@ -21,7 +21,7 @@ {.push raises: [Defect].} import - std/[math, sequtils, sets, tables, algorithm], + std/[math, sets, tables, algorithm], stew/[bitops2], chronicles, ../extras, ./datatypes/[phase0, altair], @@ -160,27 +160,63 @@ func is_eligible_validator*(validator: RewardStatus): bool = # -------------------------------------------------------- # https://github.com/ethereum/consensus-specs/blob/v1.1.0-beta.2/specs/altair/beacon-chain.md#get_unslashed_participating_indices +iterator get_unslashed_participating_indices( + state: altair.BeaconState, flag_index: int, epoch: Epoch): + ValidatorIndex = + ## Return the set of validator indices that are both active and unslashed for + ## the given ``flag_index`` and ``epoch``. + doAssert epoch in [get_previous_epoch(state), get_current_epoch(state)] + let epoch_participation = + if epoch == get_current_epoch(state): + unsafeAddr state.current_epoch_participation + else: + unsafeAddr state.previous_epoch_participation + + for validator_index in get_active_validator_indices(state, epoch): + if has_flag(epoch_participation[][validator_index], flag_index) and + not state.validators[validator_index].slashed: + yield validator_index + func get_unslashed_participating_indices( state: altair.BeaconState, flag_index: int, epoch: Epoch): HashSet[ValidatorIndex] = ## Return the set of validator indices that are both active and unslashed for ## the given ``flag_index`` and ``epoch``. - doAssert epoch in [get_previous_epoch(state), get_current_epoch(state)] - let - epoch_participation = - if epoch == get_current_epoch(state): - state.current_epoch_participation - else: - state.previous_epoch_participation - - # TODO use cached version, or similar - active_validator_indices = get_active_validator_indices(state, epoch) - var res: HashSet[ValidatorIndex] - for validator_index in active_validator_indices: - if has_flag(epoch_participation[validator_index], flag_index) and - not state.validators[validator_index].slashed: - res.incl validator_index + for validator_index in get_unslashed_participating_indices( + state, flag_index, epoch): + res.incl validator_index + res + +# For the first couple of beacon chain years there are likely to be more +# active validators than any other sort. As Ethereum matures, this won't +# continue to hold, and alternative optimization can be pursued. +iterator get_slashed_or_nonparticipating_indices( + state: altair.BeaconState, flag_index: int, epoch: Epoch): + ValidatorIndex = + ## Return the set of validator indices that are both active and unslashed for + ## the given ``flag_index`` and ``epoch``. + doAssert epoch in [get_previous_epoch(state), get_current_epoch(state)] + let epoch_participation = + if epoch == get_current_epoch(state): + unsafeAddr state.current_epoch_participation + else: + unsafeAddr state.previous_epoch_participation + + for validator_index in get_active_validator_indices(state, epoch): + if not has_flag(epoch_participation[][validator_index], flag_index) or + state.validators[validator_index].slashed: + yield validator_index + +func get_slashed_or_nonparticipating_indices( + state: altair.BeaconState, flag_index: int, epoch: Epoch): + HashSet[ValidatorIndex] = + ## Return the set of validator indices that are both active and unslashed for + ## the given ``flag_index`` and ``epoch``. + var res: HashSet[ValidatorIndex] + for validator_index in get_slashed_or_nonparticipating_indices( + state, flag_index, epoch): + res.incl validator_index res # https://github.com/ethereum/consensus-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#justification-and-finalization @@ -383,15 +419,19 @@ proc process_justification_and_finalization*(state: var altair.BeaconState, # result in modifying this stub. if get_current_epoch(state) <= GENESIS_EPOCH + 1: return + + # These ultimately differ from phase0 only in these lines, with the phase 0 + # version effectively embedding weigh_justification_and_finalization(), for + # historical reasons. + # https://github.com/ethereum/consensus-specs/blob/v1.1.0-beta.2/specs/phase0/beacon-chain.md#justification-and-finalization let - # these ultimately differ from phase0 only in these lines - # https://github.com/ethereum/consensus-specs/blob/v1.1.0-beta.2/specs/phase0/beacon-chain.md#justification-and-finalization - previous_indices = get_unslashed_participating_indices( - state, TIMELY_TARGET_FLAG_INDEX, get_previous_epoch(state)) - current_indices = get_unslashed_participating_indices( - state, TIMELY_TARGET_FLAG_INDEX, get_current_epoch(state)) - previous_target_balance = get_total_balance(state, previous_indices) - current_target_balance = get_total_balance(state, current_indices) + previous_target_balance = get_total_balance(state, + get_unslashed_participating_indices( + state, TIMELY_TARGET_FLAG_INDEX, get_previous_epoch(state))) + current_target_balance = get_total_balance(state, + get_unslashed_participating_indices( + state, TIMELY_TARGET_FLAG_INDEX, get_current_epoch(state))) + weigh_justification_and_finalization( state, total_active_balance, previous_target_balance, current_target_balance, flags) @@ -401,7 +441,7 @@ func get_base_reward_sqrt*(state: phase0.BeaconState, index: ValidatorIndex, total_balance_sqrt: auto): Gwei = # Spec function recalculates total_balance every time, which creates an # O(n^2) situation. - let effective_balance = state.validators[index].effective_balance + let effective_balance = state.validators.asSeq()[index].effective_balance effective_balance * BASE_REWARD_FACTOR div total_balance_sqrt div BASE_REWARDS_PER_EPOCH @@ -622,11 +662,13 @@ iterator get_inactivity_penalty_deltas(cfg: RuntimeConfig, state: altair.BeaconS ## Return the inactivity penalty deltas by considering timely target ## participation flags and inactivity scores. let - previous_epoch = get_previous_epoch(state) - matching_target_indices = - get_unslashed_participating_indices(state, TIMELY_TARGET_FLAG_INDEX, previous_epoch) penalty_denominator = cfg.INACTIVITY_SCORE_BIAS * INACTIVITY_PENALTY_QUOTIENT_ALTAIR + previous_epoch = get_previous_epoch(state) + + # This is the set-complement of what the spec calls matching_target_indices + nontarget_indices = get_slashed_or_nonparticipating_indices( + state, TIMELY_TARGET_FLAG_INDEX, previous_epoch) for index in 0 ..< state.validators.len: # get_eligible_validator_indices() @@ -636,7 +678,7 @@ iterator get_inactivity_penalty_deltas(cfg: RuntimeConfig, state: altair.BeaconS continue template vidx: untyped = index.ValidatorIndex - if not (vidx in matching_target_indices): + if vidx in nontarget_indices: let penalty_numerator = state.validators[index].effective_balance * state.inactivity_scores[index] @@ -660,8 +702,10 @@ func process_rewards_and_penalties( # update the raw list directly state.balances.clearCache() for idx, v in rewards.statuses: - increase_balance(state.balances.asSeq()[idx], v.delta.rewards) - decrease_balance(state.balances.asSeq()[idx], v.delta.penalties) + var balance = state.balances.asSeq()[idx] + increase_balance(balance, v.delta.rewards) + decrease_balance(balance, v.delta.penalties) + state.balances.asSeq()[idx] = balance # https://github.com/ethereum/consensus-specs/blob/v1.1.0-beta.2/specs/altair/beacon-chain.md#rewards-and-penalties func process_rewards_and_penalties( @@ -690,9 +734,12 @@ func process_rewards_and_penalties( for validator_index, penalty in get_inactivity_penalty_deltas(cfg, state): penalties[validator_index] += penalty + state.balances.clearCache() for index in 0 ..< len(state.validators): - increase_balance(state, ValidatorIndex(index), rewards[index]) - decrease_balance(state, ValidatorIndex(index), penalties[index]) + var balance = state.balances.asSeq()[index] + increase_balance(balance, rewards[index]) + decrease_balance(balance, penalties[index]) + state.balances.asSeq()[index] = balance # https://github.com/ethereum/consensus-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#slashings # https://github.com/ethereum/consensus-specs/blob/v1.1.0-beta.2/specs/altair/beacon-chain.md#slashings @@ -818,24 +865,27 @@ func process_inactivity_updates*(cfg: RuntimeConfig, state: var altair.BeaconSta # TODO actually implement get_eligible_validator_indices() as an iterator let previous_epoch = get_previous_epoch(state) # get_eligible_validator_indices() - unslashed_participating_indices = - get_unslashed_participating_indices( + slashed_or_nonparticipating_indices = + get_slashed_or_nonparticipating_indices( state, TIMELY_TARGET_FLAG_INDEX, get_previous_epoch(state)) + let not_in_inactivity_leak = not is_in_inactivity_leak(state) for index in 0'u64 ..< state.validators.lenu64: # get_eligible_validator_indices() - let v = state.validators[index] + let v = state.validators.asSeq()[index] if not (is_active_validator(v, previous_epoch) or (v.slashed and previous_epoch + 1 < v.withdrawable_epoch)): continue # Increase the inactivity score of inactive validators - if index.ValidatorIndex in unslashed_participating_indices: - state.inactivity_scores[index] -= min(1'u64, state.inactivity_scores[index]) + var inactivity_score = state.inactivity_scores[index] + if index.ValidatorIndex notin slashed_or_nonparticipating_indices: + inactivity_score -= min(1'u64, inactivity_score) else: - state.inactivity_scores[index] += cfg.INACTIVITY_SCORE_BIAS + inactivity_score += cfg.INACTIVITY_SCORE_BIAS # Decrease the inactivity score of all eligible validators during a # leak-free epoch - if not is_in_inactivity_leak(state): - state.inactivity_scores[index] -= min(INACTIVITY_SCORE_RECOVERY_RATE.uint64, state.inactivity_scores[index]) + if not_in_inactivity_leak: + inactivity_score -= min(INACTIVITY_SCORE_RECOVERY_RATE.uint64, inactivity_score) + state.inactivity_scores[index] = inactivity_score # https://github.com/ethereum/consensus-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#epoch-processing proc process_epoch*(