Skip to content
Open
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
13 changes: 9 additions & 4 deletions lean_client/fork_choice/src/handlers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -409,10 +409,13 @@ pub fn on_attestation(
/// Verifies the aggregated XMSS proof against participant public keys and stores
/// it in `latest_new_aggregated_payloads`. At interval 3, these are merged with
/// `latest_known_aggregated_payloads` (from blocks) to compute safe target.
///
/// `verify_proof` gates only the XMSS SNARK check.
#[inline]
pub fn on_aggregated_attestation(
store: &mut Store,
signed_aggregated_attestation: SignedAggregatedAttestation,
verify_proof: bool,
) -> Result<()> {
// Structure: { data: AttestationData, proof: AggregatedSignatureProof }
let attestation_data = signed_aggregated_attestation.data.clone();
Expand Down Expand Up @@ -472,9 +475,11 @@ pub fn on_aggregated_attestation(
})
.collect::<Result<Vec<_>>>()?;

proof
.verify(public_keys, data_root, attestation_data.slot.0 as u32)
.context("aggregated attestation proof verification failed")?;
if verify_proof {
proof
.verify(public_keys, data_root, attestation_data.slot.0 as u32)
.context("aggregated attestation proof verification failed")?;
}

let attestation_slot = attestation_data.slot;
for vid in &validator_ids {
Expand Down Expand Up @@ -796,7 +801,7 @@ pub fn apply_verified_block(
.remove(&block_root)
.unwrap_or_default();
for signed_agg in pending_agg {
if let Err(err) = on_aggregated_attestation(store, signed_agg) {
if let Err(err) = on_aggregated_attestation(store, signed_agg, true) {
warn!(%err, "Pending aggregated attestation retry failed after block arrival");
}
}
Expand Down
29 changes: 20 additions & 9 deletions lean_client/http_api/src/test_driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ use spec_test_fixtures::{
use ssz::SszHash;
use xmss::{AggregatedSignature, Signature};

const MOCK_AGGREGATION_PROOF_MARKER: &[u8] = b"MOCKED-AGGREGATION-PROOF";

/// Shared state for test-driver routes. Carries a writable handle to the
/// fork-choice store plus the `BlockCache` that `on_block` requires.
///
Expand Down Expand Up @@ -412,8 +414,8 @@ fn apply_step(
// `Checks` steps still see a snapshot.
return Ok(());
};
let signed = build_signed_aggregated_attestation(step)?;
on_aggregated_attestation(store, signed).map_err(|err| err.to_string())
let (signed, verify_proof) = build_signed_aggregated_attestation(step)?;
on_aggregated_attestation(store, signed, verify_proof).map_err(|err| err.to_string())
}
ForkChoiceStep::Checks { .. } => {
// Pure-assertion step. The simulator validates against the
Expand Down Expand Up @@ -468,20 +470,29 @@ fn hex_root(root: &ssz::H256) -> String {
/// string (the harness cannot re-aggregate without the signers' private
/// keys), so we decode it directly into an [`AggregatedSignature`] and
/// wrap with the participants bitfield from the same payload.
///
/// Returns the attestation and whether its proof should be XMSS-verified —
/// `false` when the proof is the mocked sentinel.
fn build_signed_aggregated_attestation(
step: GossipAggregatedAttestationStep,
) -> Result<SignedAggregatedAttestation, String> {
) -> Result<(SignedAggregatedAttestation, bool), String> {
let proof_hex = step.proof.proof.data.trim_start_matches("0x");
let proof_bytes = hex::decode(proof_hex)
.map_err(|err| format!("invalid hex in aggregate proof_data: {err}"))?;
let verify_proof = !proof_bytes
.windows(MOCK_AGGREGATION_PROOF_MARKER.len())
.any(|window| window == MOCK_AGGREGATION_PROOF_MARKER);
let proof_data = AggregatedSignature::new(&proof_bytes)
.map_err(|err| format!("failed to construct aggregated signature: {err}"))?;

Ok(SignedAggregatedAttestation {
data: step.data.into(),
proof: AggregatedSignatureProof {
participants: step.proof.participants.into(),
proof_data,
Ok((
SignedAggregatedAttestation {
data: step.data.into(),
proof: AggregatedSignatureProof {
participants: step.proof.participants.into(),
proof_data,
},
},
})
verify_proof,
))
}
Loading