Merge branch 'unstable' of https://github.com/sigp/lighthouse into gloas-block-and-bid-production

This commit is contained in:
Eitan Seri- Levi
2026-02-11 14:53:39 -08:00
20 changed files with 583 additions and 65 deletions

View File

@@ -55,9 +55,21 @@ use crate::{
};
pub const CACHED_EPOCHS: usize = 3;
// Pre-electra WS calculations are not supported. On mainnet, pre-electra epochs are outside the weak subjectivity
// period. The default pre-electra WS value is set to 256 to allow for `basic-sim``, `fallback-sim`` test case `revert_minority_fork_on_resume`
// to pass. 256 is a small enough number to trigger the WS safety check pre-electra on mainnet.
pub const DEFAULT_PRE_ELECTRA_WS_PERIOD: u64 = 256;
const MAX_RANDOM_BYTE: u64 = (1 << 8) - 1;
const MAX_RANDOM_VALUE: u64 = (1 << 16) - 1;
// `SAFETY_DECAY` is defined as the maximum percentage tolerable loss in the one-third
// safety margin of FFG finality. Thus, any attack exploiting the Weak Subjectivity Period has
// a safety margin of at least `1/3 - SAFETY_DECAY/100`.
// Spec: https://github.com/ethereum/consensus-specs/blob/1937aff86b41b5171a9bc3972515986f1bbbf303/specs/phase0/weak-subjectivity.md?plain=1#L50-L71
const SAFETY_DECAY: u64 = 10;
pub type Validators<E> = List<Validator, <E as EthSpec>::ValidatorRegistryLimit>;
pub type Balances<E> = List<u64, <E as EthSpec>::ValidatorRegistryLimit>;
@@ -3007,6 +3019,26 @@ impl<E: EthSpec> BeaconState<E> {
Ok(())
}
/// Returns the weak subjectivity period for `self`
pub fn compute_weak_subjectivity_period(
&self,
spec: &ChainSpec,
) -> Result<Epoch, BeaconStateError> {
let total_active_balance = self.get_total_active_balance()?;
let fork_name = self.fork_name_unchecked();
if fork_name.electra_enabled() {
let balance_churn_limit = self.get_balance_churn_limit(spec)?;
compute_weak_subjectivity_period_electra(
total_active_balance,
balance_churn_limit,
spec,
)
} else {
Ok(Epoch::new(DEFAULT_PRE_ELECTRA_WS_PERIOD))
}
}
/// Get the payload timeliness committee for the given `slot`.
///
/// Requires the committee cache to be initialized.
@@ -3382,3 +3414,75 @@ impl<'de, E: EthSpec> ContextDeserialize<'de, ForkName> for BeaconState<E> {
))
}
}
/// Spec: https://github.com/ethereum/consensus-specs/blob/1937aff86b41b5171a9bc3972515986f1bbbf303/specs/electra/weak-subjectivity.md?plain=1#L30
pub fn compute_weak_subjectivity_period_electra(
total_active_balance: u64,
balance_churn_limit: u64,
spec: &ChainSpec,
) -> Result<Epoch, BeaconStateError> {
let epochs_for_validator_set_churn = SAFETY_DECAY
.safe_mul(total_active_balance)?
.safe_div(balance_churn_limit.safe_mul(200)?)?;
let ws_period = spec
.min_validator_withdrawability_delay
.safe_add(epochs_for_validator_set_churn)?;
Ok(ws_period)
}
#[cfg(test)]
mod weak_subjectivity_tests {
use crate::state::beacon_state::compute_weak_subjectivity_period_electra;
use crate::{ChainSpec, Epoch, EthSpec, MainnetEthSpec};
const GWEI_PER_ETH: u64 = 1_000_000_000;
#[test]
fn test_compute_weak_subjectivity_period_electra() {
let mut spec = MainnetEthSpec::default_spec();
spec.altair_fork_epoch = Some(Epoch::new(0));
spec.bellatrix_fork_epoch = Some(Epoch::new(0));
spec.capella_fork_epoch = Some(Epoch::new(0));
spec.deneb_fork_epoch = Some(Epoch::new(0));
spec.electra_fork_epoch = Some(Epoch::new(0));
// A table of some expected values:
// https://github.com/ethereum/consensus-specs/blob/1937aff86b41b5171a9bc3972515986f1bbbf303/specs/electra/weak-subjectivity.md?plain=1#L44-L54
// (total_active_balance, expected_ws_period)
let expected_values: Vec<(u64, u64)> = vec![
(1_048_576 * GWEI_PER_ETH, 665),
(2_097_152 * GWEI_PER_ETH, 1_075),
(4_194_304 * GWEI_PER_ETH, 1_894),
(8_388_608 * GWEI_PER_ETH, 3_532),
(16_777_216 * GWEI_PER_ETH, 3_532),
(33_554_432 * GWEI_PER_ETH, 3_532),
// This value cross referenced w/
// beacon_chain/tests/tests.rs:test_compute_weak_subjectivity_period
(1536 * GWEI_PER_ETH, 256),
];
for (total_active_balance, expected_ws_period) in expected_values {
let balance_churn_limit = get_balance_churn_limit(total_active_balance, &spec);
let calculated_ws_period = compute_weak_subjectivity_period_electra(
total_active_balance,
balance_churn_limit,
&spec,
)
.unwrap();
assert_eq!(calculated_ws_period, expected_ws_period);
}
}
// caclulate the balance_churn_limit without dealing with states
// and without initializing the active balance cache
fn get_balance_churn_limit(total_active_balance: u64, spec: &ChainSpec) -> u64 {
let churn = std::cmp::max(
spec.min_per_epoch_churn_limit_electra,
total_active_balance / spec.churn_limit_quotient,
);
churn - (churn % spec.effective_balance_increment)
}
}

View File

@@ -17,7 +17,7 @@ pub use balance::Balance;
pub use beacon_state::{
BeaconState, BeaconStateAltair, BeaconStateBase, BeaconStateBellatrix, BeaconStateCapella,
BeaconStateDeneb, BeaconStateElectra, BeaconStateError, BeaconStateFulu, BeaconStateGloas,
BeaconStateHash, BeaconStateRef, CACHED_EPOCHS,
BeaconStateHash, BeaconStateRef, CACHED_EPOCHS, DEFAULT_PRE_ELECTRA_WS_PERIOD,
};
pub use committee_cache::{
CommitteeCache, compute_committee_index_in_epoch, compute_committee_range_in_epoch,