Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
2 changes: 1 addition & 1 deletion utils/client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ where
// Strip out all transactions that are not deposits.
attributes.transactions = attributes.transactions.map(|txs| {
txs.into_iter()
.filter(|tx| (!tx.is_empty() && tx[0] == OpTxType::Deposit as u8))
.filter(|tx| !tx.is_empty() && tx[0] == OpTxType::Deposit as u8)
.collect::<Vec<_>>()
});

Expand Down
5 changes: 5 additions & 0 deletions validity/src/prom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ pub enum ValidityGauge {
message = "Number of aggregation proof request errors"
)]
AggProofRequestErrorCount,
#[strum(
serialize = "succinct_agg_proof_validation_error_count",
message = "Number of aggregation proof validation errors"
)]
AggProofValidationErrorCount,
#[strum(
serialize = "succinct_relay_agg_proof_error_count",
message = "Number of relay aggregation proof errors"
Expand Down
131 changes: 130 additions & 1 deletion validity/src/proposer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -602,7 +602,38 @@ where
.await?;

if let Some(unreq_agg_request) = unreq_agg_request {
return Ok(Some(unreq_agg_request));
// Fetch consecutive range proofs from the database associated with the aggregation
// proof request.
let range_proofs = self
.proof_requester
.db_client
.get_consecutive_complete_range_proofs(
unreq_agg_request.start_block,
unreq_agg_request.end_block,
&self.program_config.commitments,
self.requester_config.l1_chain_id,
self.requester_config.l2_chain_id,
)
.await?;

// Validate the aggregation proof request
match self.validate_aggregation_request(&range_proofs, &unreq_agg_request).await {
true => {
debug!(
"Aggregation request validated successfully: start_block={}, end_block={}",
unreq_agg_request.start_block, unreq_agg_request.end_block
);
return Ok(Some(unreq_agg_request));
}
false => {
debug!(
"Aggregation request validation failed, moving to range proofs: start_block={}, end_block={}",
unreq_agg_request.start_block, unreq_agg_request.end_block
);
ValidityGauge::AggProofValidationErrorCount.increment(1.0);
// Validation failed, continue to try fetching range proofs
}
}
}

let unreq_range_request = self
Expand All @@ -623,6 +654,104 @@ where
Ok(None)
}

/// Validates an aggregation proof request by checking that:
/// 1. There are no gaps between consecutive range proofs
/// 2. There are no duplicate/overlapping range proofs
/// 3. The range proofs cover the entire block range
pub async fn validate_aggregation_request(
&self,
range_proofs: &[OPSuccinctRequest],
agg_request: &OPSuccinctRequest,
) -> bool {
debug!(
"Validating aggregation proof request: start_block={}, end_block={}",
agg_request.start_block, agg_request.end_block
);

// Log all constituent range proofs
for (i, proof) in range_proofs.iter().enumerate() {
debug!(
"Range proof {}: start_block={}, end_block={}",
i, proof.start_block, proof.end_block
);
}

// If no range proofs found, validation fails
Copy link
Contributor

Choose a reason for hiding this comment

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

if range_proofs.is_empty() {
warn!(
start_block = ?agg_request.start_block,
end_block = ?agg_request.end_block,
commitments = ?self.program_config.commitments,
"No consecutive span proof range found for request"
);
return false;
}

let first_range_proof_request =
range_proofs.first().expect("Range proofs should not be empty");

let last_range_proof_request =
range_proofs.last().expect("Range proofs should not be empty");

if first_range_proof_request.start_block != agg_request.start_block {
warn!(
expected_start_block = ?agg_request.start_block,
actual_start_block = ?first_range_proof_request.start_block,
commitments = ?self.program_config.commitments,
"Range proofs start block does not match aggregation request"
);

return false;
}

if last_range_proof_request.end_block != agg_request.end_block {
warn!(
expected_end_block = ?agg_request.end_block,
actual_end_block = ?last_range_proof_request.end_block,
commitments = ?self.program_config.commitments,
"Range proofs end block does not match aggregation request"
);
return false;
}

// Check for gaps and duplicates / overlaps between consecutive proofs
for i in 1..range_proofs.len() {
let prev_proof = &range_proofs[i - 1];
let curr_proof = &range_proofs[i];

// Check for gap
if prev_proof.end_block != curr_proof.start_block {
debug!(
"Gap detected: proof {} ends at {} but proof {} starts at {}",
i - 1,
prev_proof.end_block,
i,
curr_proof.start_block
);
return false;
}

// Check for overlap (duplicate blocks)
if prev_proof.end_block > curr_proof.start_block {
debug!(
"Overlap detected: proof {} ends at {} but proof {} starts at {}",
i - 1,
prev_proof.end_block,
i,
curr_proof.start_block
);
return false;
}
}

// All validation checks passed
debug!(
"Aggregation request validated successfully with {} consecutive range proofs",
range_proofs.len()
);
true
}

/// Relay all completed aggregation proofs to the contract.
#[tracing::instrument(name = "proposer.submit_agg_proofs", skip(self))]
async fn submit_agg_proofs(&self) -> Result<()> {
Expand Down
Loading