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
24 changes: 22 additions & 2 deletions crates/blockchain/src/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,26 @@ pub fn on_gossip_attestation(
pub fn on_gossip_aggregated_attestation(
store: &mut Store,
aggregated: SignedAggregatedAttestation,
) -> Result<(), StoreError> {
on_gossip_aggregated_attestation_core(store, aggregated, true)
}

/// Process a gossiped aggregated attestation WITHOUT verifying its proof.
///
/// Only for spec tests whose fixtures carry mocked (placeholder) proofs
/// (`proofSetting == 0`); production paths must use
/// [`on_gossip_aggregated_attestation`].
pub fn on_gossip_aggregated_attestation_without_verification(
store: &mut Store,
aggregated: SignedAggregatedAttestation,
) -> Result<(), StoreError> {
on_gossip_aggregated_attestation_core(store, aggregated, false)
}
Comment on lines +404 to +409

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

There should be a better way to do this.


fn on_gossip_aggregated_attestation_core(
store: &mut Store,
aggregated: SignedAggregatedAttestation,
verify: bool,
) -> Result<(), StoreError> {
validate_attestation_data(store, &aggregated.data)
.inspect_err(|_| metrics::inc_attestations_invalid())?;
Expand Down Expand Up @@ -420,16 +440,16 @@ pub fn on_gossip_aggregated_attestation(
let data_root = hashed.root();
let slot: u32 = aggregated.data.slot.try_into().expect("slot exceeds u32");

{
if verify {
let _timing = metrics::time_pq_sig_aggregated_signatures_verification();
ethlambda_crypto::verify_aggregated_signature(
&aggregated.proof.proof,
pubkeys,
&data_root,
slot,
)
.map_err(StoreError::AggregateVerificationFailed)?;
}
.map_err(StoreError::AggregateVerificationFailed)?;

// Read stats before moving the proof into the store.
let num_participants = aggregated.proof.participants.count_ones();
Expand Down
23 changes: 18 additions & 5 deletions crates/blockchain/tests/forkchoice_spectests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ fn run(path: &Path) -> datatest_stable::Result<()> {
}
println!("Running test: {}", name);

// Mocked-proof vectors (`proofSetting == 0`) carry placeholder
// aggregation proofs that must not be cryptographically verified.
let proofs_are_mocked = test.proofs_are_mocked();

// Initialize store from anchor state/block.
//
// Fixtures whose `steps` is empty are "anchor rejection" cases (e.g.
Expand Down Expand Up @@ -93,11 +97,14 @@ fn run(path: &Path) -> datatest_stable::Result<()> {

let signed_block = block_data.to_blank_signed_block();

let block_time_ms =
genesis_time * 1000 + signed_block.message.slot * MILLISECONDS_PER_SLOT;

// Advance time to the block's slot unless the test delivers
// the block ahead of the store clock.
// NOTE: the has_proposal argument is set to true, following the spec
store::on_tick(&mut store, block_time_ms, true);
if step.tick_to_slot {
let block_time_ms =
genesis_time * 1000 + signed_block.message.slot * MILLISECONDS_PER_SLOT;
store::on_tick(&mut store, block_time_ms, true);
}
let result = store::on_block_without_verification(&mut store, signed_block);
let import_ok = result.is_ok();
assert_step_outcome(step_idx, step.valid, result)?;
Expand Down Expand Up @@ -184,7 +191,13 @@ fn run(path: &Path) -> datatest_stable::Result<()> {
TypeOneMultiSignature::new(proof_fixture.participants.into(), proof_data);
let aggregated = SignedAggregatedAttestation { data, proof };

let result = store::on_gossip_aggregated_attestation(&mut store, aggregated);
let result = if proofs_are_mocked {
store::on_gossip_aggregated_attestation_without_verification(
&mut store, aggregated,
)
} else {
store::on_gossip_aggregated_attestation(&mut store, aggregated)
};
assert_step_outcome(step_idx, step.valid, result)?;
}
other => {
Expand Down
22 changes: 22 additions & 0 deletions crates/common/test-fixtures/src/fork_choice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,30 @@ pub struct ForkChoiceTest {
#[serde(rename = "anchorBlock")]
pub anchor_block: Block,
pub steps: Vec<ForkChoiceStep>,
/// Aggregation proof regime: 0 = mocked (placeholder bytes, must not be
/// verified), 1 = real and must verify, 2 = real and must fail
/// verification. Older fixtures lack the field and carry real proofs.
#[serde(rename = "proofSetting", default = "default_proof_setting")]
pub proof_setting: u8,
#[serde(rename = "maxSlot")]
#[allow(dead_code)]
pub max_slot: u64,
#[serde(rename = "_info")]
pub info: TestInfo,
}

fn default_proof_setting() -> u8 {
1
}

impl ForkChoiceTest {
/// Whether the vector's aggregation proofs are placeholders that must
/// not be cryptographically verified (`proofSetting == 0`).
pub fn proofs_are_mocked(&self) -> bool {
self.proof_setting == 0
}
}

// ============================================================================
// Step Types
// ============================================================================
Expand All @@ -76,6 +93,11 @@ pub struct ForkChoiceStep {
pub has_proposal: Option<bool>,
#[serde(rename = "isAggregator")]
pub is_aggregator: Option<bool>,
/// Whether the harness must advance the store clock to the block's slot
/// before delivering a `block` step. Early-arrival tests set this to
/// `false` to deliver the block ahead of the store clock.
#[serde(rename = "tickToSlot", default = "default_true")]
pub tick_to_slot: bool,
}

fn default_true() -> bool {
Expand Down
2 changes: 1 addition & 1 deletion crates/common/test-fixtures/src/state_transition.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@ use serde::Deserialize;
pub struct StateTransitionRunRequest {
pub pre: TestState,
pub blocks: Vec<Block>,
#[serde(default, rename = "expectException")]
#[serde(default, rename = "expectException", alias = "rejectionReason")]
pub expect_exception: Option<String>,
}
5 changes: 4 additions & 1 deletion crates/common/test-fixtures/src/verify_signatures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@ pub struct VerifySignaturesTest {
pub anchor_state: TestState,
#[serde(rename = "signedBlock")]
pub signed_block: TestSignedBlock,
#[serde(rename = "expectException")]
/// Expected rejection, when present. Newer fixtures name this field
/// `rejectionReason` (leanSpec replaced `expectException`); both
/// spellings are accepted.
#[serde(default, rename = "expectException", alias = "rejectionReason")]
pub expect_exception: Option<String>,
#[serde(rename = "_info")]
#[allow(dead_code)]
Expand Down
2 changes: 1 addition & 1 deletion crates/common/types/tests/ssz_spectests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use ethlambda_types::primitives::HashTreeRoot;
mod ssz_types;
use ssz_types::{SszTestCase, SszTestVector, decode_hex, decode_hex_h256};

const SUPPORTED_FIXTURE_FORMAT: &str = "ssz";
const SUPPORTED_FIXTURE_FORMAT: &str = "ssz_test";

fn run(path: &Path) -> datatest_stable::Result<()> {
let tests = SszTestVector::from_file(path)?;
Expand Down
11 changes: 7 additions & 4 deletions crates/net/rpc/src/test_driver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -356,10 +356,13 @@ fn apply_step(store: &mut Store, step: ForkChoiceStep) -> Result<(), String> {
.ok_or_else(|| "block step missing block data".to_string())?;
let signed_block = block_data.to_blank_signed_block();
// Match the spec-test runner: advance time to the block's slot
// before importing so the future-slot guard doesn't reject it.
let block_time_ms = store.config().genesis_time * 1000
+ signed_block.message.slot * MILLISECONDS_PER_SLOT;
store::on_tick(store, block_time_ms, true);
// before importing, unless the step delivers the block ahead of
// the store clock.
if step.tick_to_slot {
let block_time_ms = store.config().genesis_time * 1000
+ signed_block.message.slot * MILLISECONDS_PER_SLOT;
store::on_tick(store, block_time_ms, true);
}
store::on_block_without_verification(store, signed_block).map_err(|e| e.to_string())
}
"attestation" => {
Expand Down