Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
448f36d
Add initial weak subjectivity check
eserilev Apr 22, 2025
48ee262
Add ignore-ws-check flag and changes based on feedback
eserilev Apr 23, 2025
6ac8d99
Add pre-electra calculations
eserilev Apr 23, 2025
c2b9f9f
fix ci
eserilev Apr 23, 2025
1727167
Added tests
eserilev Apr 24, 2025
e6fdd4d
added better test coverage
eserilev Apr 24, 2025
1086b50
update comment
eserilev Apr 24, 2025
c995e6a
update comment
eserilev Apr 24, 2025
54228e0
Resolve merge conflicts
eserilev Oct 14, 2025
b5adc67
Merge branch 'unstable' into weak-subjectivity-startup-check
michaelsproul Nov 19, 2025
8cb1a78
Merge conflicts
eserilev Nov 26, 2025
c09954a
Fix messaging
eserilev Dec 1, 2025
83f6ac4
simlplify cli text
eserilev Dec 1, 2025
3d919f0
Remove pre-electra calc
eserilev Dec 2, 2025
c524e98
Merge branch 'unstable' into weak-subjectivity-startup-check
eserilev Dec 2, 2025
5db3205
Use test spec in InvalidPayloadRig
eserilev Dec 3, 2025
a2886a4
Merge branch 'weak-subjectivity-startup-check' of https://github.com/…
eserilev Dec 3, 2025
bc60964
WS pre-electra set to 5
eserilev Dec 3, 2025
5bb7d46
Fix test
eserilev Dec 3, 2025
d808c07
Fi
eserilev Dec 3, 2025
73e6eb6
Merge conflicts
eserilev Dec 8, 2025
5243aaf
fix
eserilev Dec 8, 2025
ccfbb8a
Fix merge conflicts
eserilev Jan 14, 2026
490ce0c
Merge branch 'unstable' of https://github.com/sigp/lighthouse into we…
eserilev Jan 14, 2026
2ba18d3
Fix
eserilev Jan 14, 2026
d4b3d34
merge sigp/unstable into weak-subjectivity-startup-check
dapplion Feb 10, 2026
0b25b1d
remove accidentally committed .claude/plans file
dapplion Feb 10, 2026
8a62893
Merge branch 'unstable' into weak-subjectivity-startup-check
eserilev Feb 10, 2026
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
18 changes: 18 additions & 0 deletions beacon_node/beacon_chain/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -816,6 +816,24 @@ where
));
}

// Check if the store is within the weak subjectivity period
if let Some(ws_checkpoint) = self.chain_config.weak_subjectivity_checkpoint {
let Ok(finalized_block) = fork_choice.get_finalized_block() else {
panic!("TODO(ws)")
};
if !store.is_within_weak_subjectivity_period(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just use the head_snapshot.beacon_state to compute the weak subjectivity period. You don't need to fetch the head state's finalize state. For WS sync, the head state = the weak subjectivity checkpoint state. You don't need to fetch any more data:

  • compute ws_period from head state
  • get current epoch from clock
  • check current_epoch <= head_state.epoch + ws_period

No need to involve the store here

ws_checkpoint,
finalized_block.slot,
finalized_block.state_root,
head_snapshot
.beacon_state
.slot()
.epoch(E::slots_per_epoch()),
) {
panic!("TODO(ws)")
}
};

let validator_pubkey_cache = self
.validator_pubkey_cache
.map(|mut validator_pubkey_cache| {
Expand Down
7 changes: 1 addition & 6 deletions beacon_node/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,9 @@ pub type ProductionClient<E> = Client<
>,
>;

/// The beacon node `Client` that will be used in production.
/// The beacon node `Client` that is used in production.
///
/// Generic over some `EthSpec`.
///
/// ## Notes:
///
/// Despite being titled `Production...`, this code is not ready for production. The name
/// demonstrates an intention, not a promise.
pub struct ProductionBeaconNode<E: EthSpec>(ProductionClient<E>);

impl<E: EthSpec> ProductionBeaconNode<E> {
Expand Down
29 changes: 29 additions & 0 deletions beacon_node/store/src/hot_cold_store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2944,6 +2944,35 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> HotColdDB<E, Hot, Cold>

Ok(())
}

pub fn is_within_weak_subjectivity_period(
&self,
ws_checkpoint: Checkpoint,
fc_slot: Slot,
fc_state_root: Hash256,
current_epoch: Epoch,
) -> bool {
let Ok(Some(finalized_state)) = self.get_state(&fc_state_root, Some(fc_slot), true) else {
// we cant get a finalized state from the db, we should force exit here?
return true;
};

if finalized_state.latest_block_header().state_root != ws_checkpoint.root {
return true;
}

let finalized_epoch = finalized_state.slot().epoch(E::slots_per_epoch());

if finalized_epoch != ws_checkpoint.epoch {
return false;
}

let Ok(ws_period) = finalized_state.compute_weak_subjectivity_period(&self.spec) else {
// TODO(ws) failed to calculate ws period, log and continue?
return true;
};
current_epoch <= finalized_epoch + ws_period
}
}

/// Advance the split point of the store, copying new finalized states to the freezer.
Expand Down
24 changes: 24 additions & 0 deletions consensus/types/src/beacon_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2505,6 +2505,30 @@ impl<E: EthSpec> BeaconState<E> {

Ok(())
}

/// Returns the weak subjectivity period for `self`. This computation takes into
/// account the effect of validator set churn (bounded by `get_balance_churn_limit()` per epoch)
/// A detailed calculation can be found at:
/// https://notes.ethereum.org/@CarlBeek/electra_weak_subjectivity
pub fn compute_weak_subjectivity_period(&self, spec: &ChainSpec) -> Result<Epoch, Error> {
// `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
// TODO(ws) move this to config
const SAFETY_DECAY: u64 = 10;

let total_active_balance = self.get_total_active_balance()?;
let balance_churn_limit = self.get_balance_churn_limit(spec)?;
let epochs_for_validator_set_churn = SAFETY_DECAY
.safe_mul(total_active_balance)?
.safe_div(balance_churn_limit.safe_mul(200)?)?;
let weak_subjectivity_period = spec
.min_validator_withdrawability_delay
.safe_mul(epochs_for_validator_set_churn)?;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

@eserilev eserilev Apr 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm using the updated forumla for electra

https://github.com/ethereum/consensus-specs/blob/dev/specs/electra/weak-subjectivity.md#modified-compute_weak_subjectivity_period

I think the calc is simpler now that top ups are part of activation churn

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah right, please link it

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah but we probably need to support the pre-electra calculation as well


Ok(weak_subjectivity_period)
}
}

impl<E: EthSpec> BeaconState<E> {
Expand Down
Loading