From 094cbe7d3d42f7fb548f74f75188c347f4ee56cb Mon Sep 17 00:00:00 2001 From: bomanaps Date: Thu, 19 Mar 2026 11:32:52 +0100 Subject: [PATCH] Gate attestations until justified checkpoint advances from anchor --- lean_client/fork_choice/src/handlers.rs | 1 + lean_client/fork_choice/src/store.rs | 8 ++++++++ lean_client/src/main.rs | 16 ++++++++++++++-- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/lean_client/fork_choice/src/handlers.rs b/lean_client/fork_choice/src/handlers.rs index 26a56c6..43a354d 100644 --- a/lean_client/fork_choice/src/handlers.rs +++ b/lean_client/fork_choice/src/handlers.rs @@ -502,6 +502,7 @@ fn process_block_internal( "Store justified checkpoint updated!" ); store.latest_justified = new_state.latest_justified.clone(); + store.justified_ever_updated = true; METRICS.get().map(|metrics| { let Some(slot) = new_state.latest_justified.slot.0.try_into().ok() else { warn!("unable to set latest_justified slot in metrics"); diff --git a/lean_client/fork_choice/src/store.rs b/lean_client/fork_choice/src/store.rs index c9d165d..cfb2d7e 100644 --- a/lean_client/fork_choice/src/store.rs +++ b/lean_client/fork_choice/src/store.rs @@ -33,6 +33,13 @@ pub struct Store { pub latest_finalized: Checkpoint, + /// Set to `true` the first time `on_block` drives a justified checkpoint + /// update beyond the initial anchor value. Validator duties (attestation, + /// block proposal) must not run while this is `false` — the store's + /// `latest_justified` is still the placeholder anchor checkpoint and using + /// it as an attestation source would produce wrong source checkpoints. + pub justified_ever_updated: bool, + pub blocks: HashMap, pub states: HashMap, @@ -209,6 +216,7 @@ pub fn get_forkchoice_store( safe_target: block_root, latest_justified, latest_finalized, + justified_ever_updated: false, blocks: [(block_root, block)].into(), states: [(block_root, anchor_state)].into(), latest_known_attestations: HashMap::new(), diff --git a/lean_client/src/main.rs b/lean_client/src/main.rs index 15a1937..ff72d6d 100644 --- a/lean_client/src/main.rs +++ b/lean_client/src/main.rs @@ -997,8 +997,20 @@ async fn main() -> Result<()> { let _ = sender.send(result); } ValidatorChainMessage::BuildAttestationData { slot, sender } => { - let result = store.read().produce_attestation_data(slot); - let _ = sender.send(result); + let store_read = store.read(); + if !store_read.justified_ever_updated { + warn!( + slot = slot.0, + "Skipping attestation: justified checkpoint has not yet \ + advanced from anchor — node is not ready to attest" + ); + let _ = sender.send(Err(anyhow::anyhow!( + "not ready: justified checkpoint has not advanced from anchor value" + ))); + } else { + let result = store_read.produce_attestation_data(slot); + let _ = sender.send(result); + } } } }