Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions consensus/state_processing/src/per_epoch_processing/altair.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ pub fn process_epoch<E: EthSpec>(
// without loss of correctness.
let current_epoch_progressive_balances = state.progressive_balances_cache().clone();
let current_epoch_total_active_balance = state.get_total_active_balance()?;
let participation_summary =
process_epoch_single_pass(state, spec, SinglePassConfig::default())?;
let epoch_result = process_epoch_single_pass(state, spec, SinglePassConfig::default())?;
let participation_summary = epoch_result.summary;

// Reset eth1 data votes.
process_eth1_data_reset(state)?;
Expand All @@ -79,6 +79,13 @@ pub fn process_epoch<E: EthSpec>(

// Rotate the epoch caches to suit the epoch transition.
state.advance_caches()?;

// Install the lookahead committee cache (built during PTC window processing) as the Next
// cache. After advance_caches, the lookahead epoch becomes the Next relative epoch.
if let Some(cache) = epoch_result.lookahead_committee_cache {
state.set_committee_cache(RelativeEpoch::Next, cache)?;
}

update_progressive_balances_on_epoch_transition(state, spec)?;

Ok(EpochProcessingSummary::Altair {
Expand Down
76 changes: 72 additions & 4 deletions consensus/state_processing/src/per_epoch_processing/single_pass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@ use milhouse::{Cow, List, Vector};
use safe_arith::{SafeArith, SafeArithIter};
use std::cmp::{max, min};
use std::collections::{BTreeSet, HashMap};
use std::sync::Arc;
use tracing::instrument;
use typenum::Unsigned;
use types::{
ActivationQueue, BeaconState, BeaconStateError, BuilderPendingPayment, ChainSpec, Checkpoint,
DepositData, Epoch, EthSpec, ExitCache, ForkName, ParticipationFlags, PendingDeposit,
ProgressiveBalancesCache, RelativeEpoch, Validator,
CommitteeCache, DepositData, Epoch, EthSpec, ExitCache, ForkName, ParticipationFlags,
PendingDeposit, ProgressiveBalancesCache, RelativeEpoch, Validator,
consts::altair::{
NUM_FLAG_INDICES, PARTICIPATION_FLAG_WEIGHTS, TIMELY_HEAD_FLAG_INDEX,
TIMELY_TARGET_FLAG_INDEX, WEIGHT_DENOMINATOR,
Expand All @@ -34,6 +35,7 @@ pub struct SinglePassConfig {
pub effective_balance_updates: bool,
pub proposer_lookahead: bool,
pub builder_pending_payments: bool,
pub ptc_window: bool,
}

impl Default for SinglePassConfig {
Expand All @@ -54,6 +56,7 @@ impl SinglePassConfig {
effective_balance_updates: true,
proposer_lookahead: true,
builder_pending_payments: true,
ptc_window: true,
}
}

Expand All @@ -68,6 +71,7 @@ impl SinglePassConfig {
effective_balance_updates: false,
proposer_lookahead: false,
builder_pending_payments: false,
ptc_window: false,
}
}
}
Expand Down Expand Up @@ -139,12 +143,20 @@ impl ValidatorInfo {
}
}

/// Result of single-pass epoch processing.
pub struct SinglePassEpochResult<E: EthSpec> {
pub summary: ParticipationEpochSummary<E>,
/// Committee cache for the lookahead epoch, built during PTC window processing.
/// Can be installed as the Next committee cache after `advance_caches`.
pub lookahead_committee_cache: Option<Arc<CommitteeCache>>,
}

#[instrument(skip_all)]
pub fn process_epoch_single_pass<E: EthSpec>(
state: &mut BeaconState<E>,
spec: &ChainSpec,
conf: SinglePassConfig,
) -> Result<ParticipationEpochSummary<E>, Error> {
) -> Result<SinglePassEpochResult<E>, Error> {
initialize_epoch_cache(state, spec)?;
initialize_progressive_balances_cache(state, spec)?;
state.build_exit_cache(spec)?;
Expand Down Expand Up @@ -479,7 +491,16 @@ pub fn process_epoch_single_pass<E: EthSpec>(
process_proposer_lookahead(state, spec)?;
}

Ok(summary)
let lookahead_committee_cache = if conf.ptc_window && fork_name.gloas_enabled() {
Some(process_ptc_window(state, spec)?)
} else {
None
};

Ok(SinglePassEpochResult {
summary,
lookahead_committee_cache,
})
}

// TOOO(EIP-7917): use balances cache
Expand Down Expand Up @@ -512,6 +533,53 @@ pub fn process_proposer_lookahead<E: EthSpec>(
Ok(())
}

/// Process the PTC window, returning the committee cache built for the lookahead epoch.
///
/// The returned cache can be injected into the state's Next committee cache slot after
/// `advance_caches` is called during the epoch transition, avoiding redundant recomputation.
pub fn process_ptc_window<E: EthSpec>(
state: &mut BeaconState<E>,
spec: &ChainSpec,
) -> Result<Arc<CommitteeCache>, Error> {
let slots_per_epoch = E::slots_per_epoch() as usize;

// Convert Vector -> List to use tree-efficient pop_front.
let ptc_window = state.ptc_window()?.clone();
let mut window: List<_, E::PtcWindowLength> = List::from(ptc_window);

// Drop the oldest epoch from the front (reuses shared tree nodes).
window
.pop_front(slots_per_epoch)
.map_err(|e| Error::BeaconStateError(BeaconStateError::MilhouseError(e)))?;

// Compute PTC for the new lookahead epoch
let next_epoch = state
.current_epoch()
.safe_add(spec.min_seed_lookahead.as_u64())?
.safe_add(1)?;
let start_slot = next_epoch.start_slot(E::slots_per_epoch());

// Build a committee cache for the lookahead epoch (beyond the normal Next bound)
let committee_cache = state.initialize_committee_cache_for_lookahead(next_epoch, spec)?;

for i in 0..slots_per_epoch {
let slot = start_slot.safe_add(i as u64)?;
let ptc = state.compute_ptc_with_cache(slot, &committee_cache, spec)?;
let ptc_u64: Vec<u64> = ptc.into_iter().map(|v| v as u64).collect();
let entry = ssz_types::FixedVector::new(ptc_u64)
.map_err(|e| Error::BeaconStateError(BeaconStateError::SszTypesError(e)))?;
window
.push(entry)
.map_err(|e| Error::BeaconStateError(BeaconStateError::MilhouseError(e)))?;
}

// Convert List back to Vector.
*state.ptc_window_mut()? = Vector::try_from(window)
.map_err(|e| Error::BeaconStateError(BeaconStateError::MilhouseError(e)))?;

Ok(committee_cache)
}

/// Calculate the quorum threshold for builder payments based on total active balance.
fn get_builder_payment_quorum_threshold<E: EthSpec>(
state_ctxt: &StateContext,
Expand Down
43 changes: 39 additions & 4 deletions consensus/state_processing/src/upgrade/gloas.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ use crate::per_block_processing::{
is_valid_deposit_signature, process_operations::apply_deposit_for_builder,
};
use milhouse::{List, Vector};
use safe_arith::SafeArith;
use ssz_types::BitVector;
use ssz_types::FixedVector;
use std::collections::HashSet;
use std::mem;
use typenum::Unsigned;
Expand Down Expand Up @@ -102,13 +104,11 @@ pub fn upgrade_state_to_gloas<E: EthSpec>(
vec![0xFFu8; E::SlotsPerHistoricalRoot::to_usize() / 8].into(),
)
.map_err(|_| Error::InvalidBitfield)?,
builder_pending_payments: Vector::new(vec![
BuilderPendingPayment::default();
E::builder_pending_payments_limit()
])?,
builder_pending_payments: Vector::from_elem(BuilderPendingPayment::default())?,
builder_pending_withdrawals: List::default(), // Empty list initially,
latest_block_hash: pre.latest_execution_payload_header.block_hash,
payload_expected_withdrawals: List::default(),
ptc_window: Vector::from_elem(FixedVector::from_elem(0))?, // placeholder, will be initialized below
// Caches
total_active_balance: pre.total_active_balance,
progressive_balances_cache: mem::take(&mut pre.progressive_balances_cache),
Expand All @@ -120,10 +120,45 @@ pub fn upgrade_state_to_gloas<E: EthSpec>(
});
// [New in Gloas:EIP7732]
onboard_builders_from_pending_deposits(&mut post, spec)?;
initialize_ptc_window(&mut post, spec)?;

Ok(post)
}

/// Initialize the `ptc_window` field in the beacon state at fork transition.
///
/// The window contains:
/// - One epoch of empty entries (previous epoch)
/// - Computed PTC for the current epoch through `1 + MIN_SEED_LOOKAHEAD` epochs
fn initialize_ptc_window<E: EthSpec>(
state: &mut BeaconState<E>,
spec: &ChainSpec,
) -> Result<(), Error> {
let slots_per_epoch = E::slots_per_epoch() as usize;

let empty_previous_epoch = vec![FixedVector::<u64, E::PTCSize>::from_elem(0); slots_per_epoch];
let mut ptcs = empty_previous_epoch;

// Compute PTC for current epoch + lookahead epochs
let current_epoch = state.current_epoch();
for e in 0..=spec.min_seed_lookahead.as_u64() {
let epoch = current_epoch.safe_add(e)?;
let committee_cache = state.initialize_committee_cache_for_lookahead(epoch, spec)?;
let start_slot = epoch.start_slot(E::slots_per_epoch());
for i in 0..slots_per_epoch {
let slot = start_slot.safe_add(i as u64)?;
let ptc = state.compute_ptc_with_cache(slot, &committee_cache, spec)?;
let ptc_u64: Vec<u64> = ptc.into_iter().map(|v| v as u64).collect();
let entry = FixedVector::new(ptc_u64)?;
ptcs.push(entry);
}
}

*state.ptc_window_mut()? = Vector::new(ptcs)?;

Ok(())
}

/// Applies any pending deposit for builders, effectively onboarding builders at the fork.
fn onboard_builders_from_pending_deposits<E: EthSpec>(
state: &mut BeaconState<E>,
Expand Down
20 changes: 17 additions & 3 deletions consensus/types/src/core/eth_spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ use std::{
use safe_arith::{ArithError, SafeArith};
use serde::{Deserialize, Serialize};
use typenum::{
U0, U1, U2, U4, U8, U16, U17, U32, U64, U128, U256, U512, U625, U1024, U2048, U4096, U8192,
U16384, U65536, U131072, U262144, U1048576, U16777216, U33554432, U134217728, U1073741824,
U1099511627776, UInt, Unsigned, bit::B0,
U0, U1, U2, U4, U8, U16, U17, U24, U32, U48, U64, U96, U128, U256, U512, U625, U1024, U2048,
U4096, U8192, U16384, U65536, U131072, U262144, U1048576, U16777216, U33554432, U134217728,
U1073741824, U1099511627776, UInt, Unsigned, bit::B0,
};

use crate::core::{ChainSpec, Epoch};
Expand Down Expand Up @@ -176,6 +176,7 @@ pub trait EthSpec: 'static + Default + Sync + Send + Clone + Debug + PartialEq +
* New in Gloas
*/
type PTCSize: Unsigned + Clone + Sync + Send + Debug + PartialEq;
type PtcWindowLength: Unsigned + Clone + Sync + Send + Debug + PartialEq;
type MaxPayloadAttestations: Unsigned + Clone + Sync + Send + Debug + PartialEq;
type BuilderPendingPaymentsLimit: Unsigned + Clone + Sync + Send + Debug + PartialEq;
type BuilderPendingWithdrawalsLimit: Unsigned + Clone + Sync + Send + Debug + PartialEq;
Expand Down Expand Up @@ -428,6 +429,11 @@ pub trait EthSpec: 'static + Default + Sync + Send + Clone + Debug + PartialEq +
Self::PTCSize::to_usize()
}

/// Returns the `PtcWindowLength` constant for this specification.
fn ptc_window_length() -> usize {
Self::PtcWindowLength::to_usize()
}

/// Returns the `MaxPayloadAttestations` constant for this specification.
fn max_payload_attestations() -> usize {
Self::MaxPayloadAttestations::to_usize()
Expand Down Expand Up @@ -515,6 +521,7 @@ impl EthSpec for MainnetEthSpec {
type MaxWithdrawalRequestsPerPayload = U16;
type MaxPendingDepositsPerEpoch = U16;
type PTCSize = U512;
type PtcWindowLength = U96; // (2 + MIN_SEED_LOOKAHEAD) * SLOTS_PER_EPOCH
type MaxPayloadAttestations = U4;
type MaxBuildersPerWithdrawalsSweep = U16384;

Expand Down Expand Up @@ -561,6 +568,7 @@ impl EthSpec for MinimalEthSpec {
type ProposerLookaheadSlots = U16; // Derived from (MIN_SEED_LOOKAHEAD + 1) * SLOTS_PER_EPOCH
type BuilderPendingPaymentsLimit = U16; // 2 * SLOTS_PER_EPOCH = 2 * 8 = 16
type PTCSize = U2;
type PtcWindowLength = U24; // (2 + MIN_SEED_LOOKAHEAD) * SLOTS_PER_EPOCH
type MaxBuildersPerWithdrawalsSweep = U16;

params_from_eth_spec!(MainnetEthSpec {
Expand Down Expand Up @@ -668,6 +676,7 @@ impl EthSpec for GnosisEthSpec {
type ProposerLookaheadSlots = U32; // Derived from (MIN_SEED_LOOKAHEAD + 1) * SLOTS_PER_EPOCH
type BuilderRegistryLimit = U1099511627776;
type PTCSize = U512;
type PtcWindowLength = U48; // (2 + MIN_SEED_LOOKAHEAD) * SLOTS_PER_EPOCH
type MaxPayloadAttestations = U2;
type MaxBuildersPerWithdrawalsSweep = U16384;

Expand All @@ -694,6 +703,11 @@ mod test {
E::proposer_lookahead_slots(),
(spec.min_seed_lookahead.as_usize() + 1) * E::slots_per_epoch() as usize
);
assert_eq!(
E::ptc_window_length(),
(spec.min_seed_lookahead.as_usize() + 2) * E::slots_per_epoch() as usize,
"PtcWindowLength must equal (2 + MIN_SEED_LOOKAHEAD) * SLOTS_PER_EPOCH"
);
}

#[test]
Expand Down
Loading
Loading