mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-02 16:21:42 +00:00
## Proposed Changes Cache the total active balance for the current epoch in the `BeaconState`. Computing this value takes around 1ms, and this was negatively impacting block processing times on Prater, particularly when reconstructing states. With a large number of attestations in each block, I saw the `process_attestations` function taking 150ms, which means that reconstructing hot states can take up to 4.65s (31 * 150ms), and reconstructing freezer states can take up to 307s (2047 * 150ms). I opted to add the cache to the beacon state rather than computing the total active balance at the start of state processing and threading it through. Although this would be simpler in a way, it would waste time, particularly during block replay, as the total active balance doesn't change for the duration of an epoch. So we save ~32ms for hot states, and up to 8.1s for freezer states (using `--slots-per-restore-point 8192`).
124 lines
5.0 KiB
Rust
124 lines
5.0 KiB
Rust
use crate::common::{get_attestation_participation_flag_indices, get_attesting_indices};
|
|
use std::mem;
|
|
use std::sync::Arc;
|
|
use types::{
|
|
BeaconState, BeaconStateAltair, BeaconStateError as Error, ChainSpec, EthSpec, Fork,
|
|
ParticipationFlags, PendingAttestation, RelativeEpoch, SyncCommittee, VariableList,
|
|
};
|
|
|
|
/// Translate the participation information from the epoch prior to the fork into Altair's format.
|
|
pub fn translate_participation<E: EthSpec>(
|
|
state: &mut BeaconState<E>,
|
|
pending_attestations: &VariableList<PendingAttestation<E>, E::MaxPendingAttestations>,
|
|
spec: &ChainSpec,
|
|
) -> Result<(), Error> {
|
|
// Previous epoch committee cache is required for `get_attesting_indices`.
|
|
state.build_committee_cache(RelativeEpoch::Previous, spec)?;
|
|
|
|
for attestation in pending_attestations {
|
|
let data = &attestation.data;
|
|
let inclusion_delay = attestation.inclusion_delay;
|
|
|
|
// Translate attestation inclusion info to flag indices.
|
|
let participation_flag_indices =
|
|
get_attestation_participation_flag_indices(state, data, inclusion_delay, spec)?;
|
|
|
|
// Apply flags to all attesting validators.
|
|
let committee = state.get_beacon_committee(data.slot, data.index)?;
|
|
let attesting_indices =
|
|
get_attesting_indices::<E>(committee.committee, &attestation.aggregation_bits)?;
|
|
let epoch_participation = state.previous_epoch_participation_mut()?;
|
|
|
|
for index in attesting_indices {
|
|
for flag_index in &participation_flag_indices {
|
|
epoch_participation
|
|
.get_mut(index)
|
|
.ok_or(Error::UnknownValidator(index))?
|
|
.add_flag(*flag_index)?;
|
|
}
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
/// Transform a `Base` state into an `Altair` state.
|
|
pub fn upgrade_to_altair<E: EthSpec>(
|
|
pre_state: &mut BeaconState<E>,
|
|
spec: &ChainSpec,
|
|
) -> Result<(), Error> {
|
|
let epoch = pre_state.current_epoch();
|
|
let pre = pre_state.as_base_mut()?;
|
|
|
|
let default_epoch_participation =
|
|
VariableList::new(vec![ParticipationFlags::default(); pre.validators.len()])?;
|
|
let inactivity_scores = VariableList::new(vec![0; pre.validators.len()])?;
|
|
|
|
let temp_sync_committee = Arc::new(SyncCommittee::temporary()?);
|
|
|
|
// Where possible, use something like `mem::take` to move fields from behind the &mut
|
|
// reference. For other fields that don't have a good default value, use `clone`.
|
|
//
|
|
// Fixed size vectors get cloned because replacing them would require the same size
|
|
// allocation as cloning.
|
|
let mut post = BeaconState::Altair(BeaconStateAltair {
|
|
// Versioning
|
|
genesis_time: pre.genesis_time,
|
|
genesis_validators_root: pre.genesis_validators_root,
|
|
slot: pre.slot,
|
|
fork: Fork {
|
|
previous_version: pre.fork.current_version,
|
|
current_version: spec.altair_fork_version,
|
|
epoch,
|
|
},
|
|
// History
|
|
latest_block_header: pre.latest_block_header.clone(),
|
|
block_roots: pre.block_roots.clone(),
|
|
state_roots: pre.state_roots.clone(),
|
|
historical_roots: mem::take(&mut pre.historical_roots),
|
|
// Eth1
|
|
eth1_data: pre.eth1_data.clone(),
|
|
eth1_data_votes: mem::take(&mut pre.eth1_data_votes),
|
|
eth1_deposit_index: pre.eth1_deposit_index,
|
|
// Registry
|
|
validators: mem::take(&mut pre.validators),
|
|
balances: mem::take(&mut pre.balances),
|
|
// Randomness
|
|
randao_mixes: pre.randao_mixes.clone(),
|
|
// Slashings
|
|
slashings: pre.slashings.clone(),
|
|
// `Participation
|
|
previous_epoch_participation: default_epoch_participation.clone(),
|
|
current_epoch_participation: default_epoch_participation,
|
|
// Finality
|
|
justification_bits: pre.justification_bits.clone(),
|
|
previous_justified_checkpoint: pre.previous_justified_checkpoint,
|
|
current_justified_checkpoint: pre.current_justified_checkpoint,
|
|
finalized_checkpoint: pre.finalized_checkpoint,
|
|
// Inactivity
|
|
inactivity_scores,
|
|
// Sync committees
|
|
current_sync_committee: temp_sync_committee.clone(), // not read
|
|
next_sync_committee: temp_sync_committee, // not read
|
|
// Caches
|
|
total_active_balance: pre.total_active_balance,
|
|
committee_caches: mem::take(&mut pre.committee_caches),
|
|
pubkey_cache: mem::take(&mut pre.pubkey_cache),
|
|
exit_cache: mem::take(&mut pre.exit_cache),
|
|
tree_hash_cache: mem::take(&mut pre.tree_hash_cache),
|
|
});
|
|
|
|
// Fill in previous epoch participation from the pre state's pending attestations.
|
|
translate_participation(&mut post, &pre.previous_epoch_attestations, spec)?;
|
|
|
|
// Fill in sync committees
|
|
// Note: A duplicate committee is assigned for the current and next committee at the fork
|
|
// boundary
|
|
let sync_committee = Arc::new(post.get_next_sync_committee(spec)?);
|
|
*post.current_sync_committee_mut()? = sync_committee.clone();
|
|
*post.next_sync_committee_mut()? = sync_committee;
|
|
|
|
*pre_state = post;
|
|
|
|
Ok(())
|
|
}
|