double altair epoch slot processing speed (#2811)

* approximately double altair epoch processing speed

* don't redundantly clear state balances cache

* only invalidate altair state balances cache once in process_rewards_and_penalties()
This commit is contained in:
tersec 2021-08-24 20:09:03 +00:00 committed by GitHub
parent ba06f13942
commit 98547e0c6b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 152 additions and 94 deletions

View File

@ -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]) =

View File

@ -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..<state.validators.len:
if is_active_validator(state.validators[idx], epoch):
yield idx.ValidatorIndex
func get_active_validator_indices*(state: SomeBeaconState, epoch: Epoch):
seq[ValidatorIndex] =
## Return the sequence of active validator indices at ``epoch``.
result = newSeqOfCap[ValidatorIndex](state.validators.len)
for idx in 0..<state.validators.len:
if is_active_validator(state.validators[idx], epoch):
result.add idx.ValidatorIndex
var res = newSeqOfCap[ValidatorIndex](state.validators.len)
for idx in get_active_validator_indices(state, epoch):
res.add idx.ValidatorIndex
res
func get_active_validator_indices_len*(state: SomeBeaconState, epoch: Epoch):
uint64 =
@ -78,13 +84,13 @@ func get_active_validator_indices_len*(state: SomeBeaconState, epoch: Epoch):
if is_active_validator(state.validators[idx], epoch):
inc result
# https://github.com/ethereum/eth2.0-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#get_current_epoch
# https://github.com/ethereum/consensus-specs/blob/v1.0.1/specs/phase0/beacon-chain.md#get_current_epoch
func get_current_epoch*(state: SomeBeaconState): Epoch =
## Return the current epoch.
doAssert state.slot >= 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

View File

@ -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*(