Single-pass epoch processing and optimised block processing (#5279)

* Single-pass epoch processing (#4483, #4573)

Co-authored-by: Michael Sproul <michael@sigmaprime.io>

* Delete unused epoch processing code (#5170)

* Delete unused epoch processing code

* Compare total deltas

* Remove unnecessary apply_pending

* cargo fmt

* Remove newline

* Use epoch cache in block packing (#5223)

* Remove progressive balances mode (#5224)

* inline inactivity_penalty_quotient_for_state

* drop previous_epoch_total_active_balance

* fc lint

* spec compliant process_sync_aggregate (#15)

* spec compliant process_sync_aggregate

* Update consensus/state_processing/src/per_block_processing/altair/sync_committee.rs

Co-authored-by: Michael Sproul <micsproul@gmail.com>

---------

Co-authored-by: Michael Sproul <micsproul@gmail.com>

* Delete the participation cache (#16)

* update help

* Fix op_pool tests

* Fix fork choice tests

* Merge remote-tracking branch 'sigp/unstable' into epoch-single-pass

* Simplify exit cache (#5280)

* Fix clippy on exit cache

* Clean up single-pass a bit (#5282)

* Address Mark's review of single-pass (#5386)

* Merge remote-tracking branch 'origin/unstable' into epoch-single-pass

* Address Sean's review comments (#5414)

* Address most of Sean's review comments

* Simplify total balance cache building

* Clean up unused junk

* Merge remote-tracking branch 'origin/unstable' into epoch-single-pass

* More self-review

* Merge remote-tracking branch 'origin/unstable' into epoch-single-pass

* Merge branch 'unstable' into epoch-single-pass

* Fix imports for beta compiler

* Fix tests, probably
This commit is contained in:
Michael Sproul
2024-04-05 00:14:36 +11:00
committed by GitHub
parent f4cdcea7b1
commit feb531f85b
81 changed files with 2545 additions and 1316 deletions

View File

@@ -4,7 +4,9 @@ use crate::{signature_sets::sync_aggregate_signature_set, VerifySignatures};
use safe_arith::SafeArith;
use std::borrow::Cow;
use types::consts::altair::{PROPOSER_WEIGHT, SYNC_REWARD_WEIGHT, WEIGHT_DENOMINATOR};
use types::{BeaconState, ChainSpec, EthSpec, PublicKeyBytes, SyncAggregate, Unsigned};
use types::{
BeaconState, BeaconStateError, ChainSpec, EthSpec, PublicKeyBytes, SyncAggregate, Unsigned,
};
pub fn process_sync_aggregate<E: EthSpec>(
state: &mut BeaconState<E>,
@@ -47,18 +49,34 @@ pub fn process_sync_aggregate<E: EthSpec>(
// Apply participant and proposer rewards
let committee_indices = state.get_sync_committee_indices(&current_sync_committee)?;
let proposer_index = proposer_index as usize;
let mut proposer_balance = *state
.balances()
.get(proposer_index)
.ok_or(BeaconStateError::BalancesOutOfBounds(proposer_index))?;
for (participant_index, participation_bit) in committee_indices
.into_iter()
.zip(aggregate.sync_committee_bits.iter())
{
if participation_bit {
increase_balance(state, participant_index, participant_reward)?;
increase_balance(state, proposer_index as usize, proposer_reward)?;
// Accumulate proposer rewards in a temp var in case the proposer has very low balance, is
// part of the sync committee, does not participate and its penalties saturate.
if participant_index == proposer_index {
proposer_balance.safe_add_assign(participant_reward)?;
} else {
increase_balance(state, participant_index, participant_reward)?;
}
proposer_balance.safe_add_assign(proposer_reward)?;
} else if participant_index == proposer_index {
proposer_balance = proposer_balance.saturating_sub(participant_reward);
} else {
decrease_balance(state, participant_index, participant_reward)?;
}
}
*state.get_balance_mut(proposer_index)? = proposer_balance;
Ok(())
}

View File

@@ -1,8 +1,6 @@
use super::signature_sets::Error as SignatureSetError;
use crate::per_epoch_processing::altair::participation_cache;
use crate::ContextError;
use merkle_proof::MerkleTreeError;
use participation_cache::Error as ParticipationCacheError;
use safe_arith::ArithError;
use ssz::DecodeError;
use types::*;
@@ -84,12 +82,12 @@ pub enum BlockProcessingError {
},
ExecutionInvalid,
ConsensusContext(ContextError),
EpochCacheError(EpochCacheError),
WithdrawalsRootMismatch {
expected: Hash256,
found: Hash256,
},
WithdrawalCredentialsInvalid,
ParticipationCacheError(ParticipationCacheError),
}
impl From<BeaconStateError> for BlockProcessingError {
@@ -134,6 +132,12 @@ impl From<ContextError> for BlockProcessingError {
}
}
impl From<EpochCacheError> for BlockProcessingError {
fn from(e: EpochCacheError) -> Self {
BlockProcessingError::EpochCacheError(e)
}
}
impl From<BlockOperationError<HeaderInvalid>> for BlockProcessingError {
fn from(e: BlockOperationError<HeaderInvalid>) -> BlockProcessingError {
match e {
@@ -147,12 +151,6 @@ impl From<BlockOperationError<HeaderInvalid>> for BlockProcessingError {
}
}
impl From<ParticipationCacheError> for BlockProcessingError {
fn from(e: ParticipationCacheError) -> Self {
BlockProcessingError::ParticipationCacheError(e)
}
}
/// A conversion that consumes `self` and adds an `index` variable to resulting struct.
///
/// Used here to allow converting an error into an upstream error that points to the object that
@@ -328,9 +326,11 @@ pub enum AttestationInvalid {
///
/// `is_current` is `true` if the attestation was compared to the
/// `state.current_justified_checkpoint`, `false` if compared to `state.previous_justified_checkpoint`.
///
/// Checkpoints have been boxed to keep the error size down and prevent clippy failures.
WrongJustifiedCheckpoint {
state: Checkpoint,
attestation: Checkpoint,
state: Box<Checkpoint>,
attestation: Box<Checkpoint>,
is_current: bool,
},
/// The aggregation bitfield length is not the smallest possible size to represent the committee.

View File

@@ -1,6 +1,5 @@
use super::*;
use crate::common::{
altair::{get_base_reward, BaseRewardPerIncrement},
get_attestation_participation_flag_indices, increase_balance, initiate_validator_exit,
slash_validator,
};
@@ -54,8 +53,12 @@ pub mod base {
ctxt: &mut ConsensusContext<E>,
spec: &ChainSpec,
) -> Result<(), BlockProcessingError> {
// Ensure the previous epoch cache exists.
// Ensure required caches are all built. These should be no-ops during regular operation.
state.build_committee_cache(RelativeEpoch::Current, spec)?;
state.build_committee_cache(RelativeEpoch::Previous, spec)?;
initialize_epoch_cache(state, spec)?;
initialize_progressive_balances_cache(state, spec)?;
state.build_slashings_cache()?;
let proposer_index = ctxt.get_proposer_index(state, spec)?;
@@ -97,7 +100,6 @@ pub mod base {
pub mod altair_deneb {
use super::*;
use crate::common::update_progressive_balances_cache::update_progressive_balances_on_attestation;
use types::consts::altair::TIMELY_TARGET_FLAG_INDEX;
pub fn process_attestations<E: EthSpec>(
state: &mut BeaconState<E>,
@@ -122,10 +124,9 @@ pub mod altair_deneb {
verify_signatures: VerifySignatures,
spec: &ChainSpec,
) -> Result<(), BlockProcessingError> {
state.build_committee_cache(RelativeEpoch::Previous, spec)?;
state.build_committee_cache(RelativeEpoch::Current, spec)?;
let proposer_index = ctxt.get_proposer_index(state, spec)?;
let previous_epoch = ctxt.previous_epoch;
let current_epoch = ctxt.current_epoch;
let attesting_indices = &verify_attestation_for_block_inclusion(
state,
@@ -144,32 +145,36 @@ pub mod altair_deneb {
get_attestation_participation_flag_indices(state, data, inclusion_delay, spec)?;
// Update epoch participation flags.
let total_active_balance = state.get_total_active_balance()?;
let base_reward_per_increment = BaseRewardPerIncrement::new(total_active_balance, spec)?;
let mut proposer_reward_numerator = 0;
for index in attesting_indices {
let index = *index as usize;
let validator_effective_balance = state.epoch_cache().get_effective_balance(index)?;
let validator_slashed = state.slashings_cache().is_slashed(index);
for (flag_index, &weight) in PARTICIPATION_FLAG_WEIGHTS.iter().enumerate() {
let epoch_participation = state.get_epoch_participation_mut(data.target.epoch)?;
let validator_participation = epoch_participation
.get_mut(index)
.ok_or(BeaconStateError::ParticipationOutOfBounds(index))?;
let epoch_participation = state.get_epoch_participation_mut(
data.target.epoch,
previous_epoch,
current_epoch,
)?;
if participation_flag_indices.contains(&flag_index)
&& !validator_participation.has_flag(flag_index)?
{
validator_participation.add_flag(flag_index)?;
proposer_reward_numerator.safe_add_assign(
get_base_reward(state, index, base_reward_per_increment, spec)?
.safe_mul(weight)?,
)?;
if participation_flag_indices.contains(&flag_index) {
let validator_participation = epoch_participation
.get_mut(index)
.ok_or(BeaconStateError::ParticipationOutOfBounds(index))?;
if !validator_participation.has_flag(flag_index)? {
validator_participation.add_flag(flag_index)?;
proposer_reward_numerator
.safe_add_assign(state.get_base_reward(index)?.safe_mul(weight)?)?;
if flag_index == TIMELY_TARGET_FLAG_INDEX {
update_progressive_balances_on_attestation(
state,
data.target.epoch,
index,
flag_index,
validator_effective_balance,
validator_slashed,
)?;
}
}
@@ -197,6 +202,8 @@ pub fn process_proposer_slashings<E: EthSpec>(
ctxt: &mut ConsensusContext<E>,
spec: &ChainSpec,
) -> Result<(), BlockProcessingError> {
state.build_slashings_cache()?;
// Verify and apply proposer slashings in series.
// We have to verify in series because an invalid block may contain multiple slashings
// for the same validator, and we need to correctly detect and reject that.
@@ -230,6 +237,8 @@ pub fn process_attester_slashings<E: EthSpec>(
ctxt: &mut ConsensusContext<E>,
spec: &ChainSpec,
) -> Result<(), BlockProcessingError> {
state.build_slashings_cache()?;
for (i, attester_slashing) in attester_slashings.iter().enumerate() {
let slashable_indices =
verify_attester_slashing(state, attester_slashing, verify_signatures, spec)

View File

@@ -451,8 +451,8 @@ async fn invalid_attestation_wrong_justified_checkpoint() {
Err(BlockProcessingError::AttestationInvalid {
index: 0,
reason: AttestationInvalid::WrongJustifiedCheckpoint {
state: old_justified_checkpoint,
attestation: new_justified_checkpoint,
state: Box::new(old_justified_checkpoint),
attestation: Box::new(new_justified_checkpoint),
is_current: true,
}
})

View File

@@ -102,8 +102,8 @@ fn verify_casper_ffg_vote<E: EthSpec>(
verify!(
data.source == state.current_justified_checkpoint(),
Invalid::WrongJustifiedCheckpoint {
state: state.current_justified_checkpoint(),
attestation: data.source,
state: Box::new(state.current_justified_checkpoint()),
attestation: Box::new(data.source),
is_current: true,
}
);
@@ -112,8 +112,8 @@ fn verify_casper_ffg_vote<E: EthSpec>(
verify!(
data.source == state.previous_justified_checkpoint(),
Invalid::WrongJustifiedCheckpoint {
state: state.previous_justified_checkpoint(),
attestation: data.source,
state: Box::new(state.previous_justified_checkpoint()),
attestation: Box::new(data.source),
is_current: false,
}
);