Skip to content

Commit c47fffa

Browse files
Wrap the entire proof submission in a result to catch all errors
1 parent f475f08 commit c47fffa

1 file changed

Lines changed: 169 additions & 127 deletions

File tree

  • aggregation_mode/proof_aggregator/src/backend

aggregation_mode/proof_aggregator/src/backend/mod.rs

Lines changed: 169 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -336,143 +336,179 @@ impl ProofAggregator {
336336

337337
let max_retries = self.config.max_bump_retries;
338338
let retry_interval = Duration::from_secs(self.config.bump_retry_interval_seconds);
339-
let base_bump_percentage = self.config.base_bump_percentage; // e.g., 10
340-
let retry_attempt_percentage = self.config.retry_attempt_percentage; // e.g., 5
339+
let base_bump_percentage = self.config.base_bump_percentage;
340+
let retry_attempt_percentage = self.config.retry_attempt_percentage;
341341

342-
for attempt in 0..max_retries {
343-
let mut tx_req = match aggregated_proof {
344-
AlignedProof::SP1(proof) => self
345-
.proof_aggregation_service
346-
.verifyAggregationSP1(
347-
blob_versioned_hash.into(),
348-
proof.proof_with_pub_values.public_values.to_vec().into(),
349-
proof.proof_with_pub_values.bytes().into(),
350-
self.sp1_chunk_aggregator_vk_hash_bytes.into(),
351-
)
352-
.sidecar(blob.clone())
353-
.into_transaction_request(),
354-
AlignedProof::Risc0(proof) => {
355-
let encoded_seal = encode_seal(&proof.receipt)
356-
.map_err(|e| {
357-
AggregatedProofSubmissionError::Risc0EncodingSeal(e.to_string())
358-
})
359-
.map_err(RetryError::Permanent)?;
360-
self.proof_aggregation_service
361-
.verifyAggregationRisc0(
362-
blob_versioned_hash.into(),
363-
encoded_seal.into(),
364-
proof.receipt.journal.bytes.clone().into(),
365-
self.risc0_chunk_aggregator_image_id_bytes.into(),
366-
)
367-
.sidecar(blob.clone())
368-
.into_transaction_request()
369-
}
370-
};
371-
372-
// Increase gas price/fees for retries before filling
373-
if attempt > 0 {
374-
tx_req = self
375-
.update_gas_fees(
376-
base_bump_percentage,
377-
retry_attempt_percentage,
378-
attempt as u64,
379-
tx_req,
380-
)
381-
.await?;
382-
}
383-
384-
let provider = self.proof_aggregation_service.provider();
342+
let mut last_error: Option<AggregatedProofSubmissionError> = None;
385343

386-
let envelope = provider
387-
.fill(tx_req)
388-
.await
389-
.map_err(|err| {
390-
AggregatedProofSubmissionError::SendVerifyAggregatedProofTransaction(
391-
err.to_string(),
392-
)
393-
})
394-
.map_err(RetryError::Transient)?
395-
.try_into_envelope()
396-
.map_err(|err| {
397-
AggregatedProofSubmissionError::SendVerifyAggregatedProofTransaction(
398-
err.to_string(),
399-
)
400-
})
401-
.map_err(RetryError::Transient)?;
402-
403-
let tx: EthereumTxEnvelope<TxEip4844WithSidecar<BlobTransactionSidecarEip7594>> =
404-
envelope
405-
.try_into_pooled()
406-
.map_err(|err| {
407-
AggregatedProofSubmissionError::SendVerifyAggregatedProofTransaction(
408-
err.to_string(),
409-
)
410-
})
411-
.map_err(RetryError::Transient)?
412-
.try_map_eip4844(|tx| {
413-
tx.try_map_sidecar(|sidecar| {
414-
sidecar.try_into_7594(EnvKzgSettings::Default.get())
415-
})
416-
})
417-
.map_err(|err| {
418-
AggregatedProofSubmissionError::SendVerifyAggregatedProofTransaction(
419-
err.to_string(),
420-
)
421-
})
422-
.map_err(RetryError::Transient)?;
423-
424-
let encoded_tx = tx.encoded_2718();
425-
let pending_tx = provider
426-
.send_raw_transaction(&encoded_tx)
427-
.await
428-
.map_err(|err| {
429-
AggregatedProofSubmissionError::SendVerifyAggregatedProofTransaction(
430-
err.to_string(),
431-
)
432-
})
433-
.map_err(RetryError::Transient)?;
344+
for attempt in 0..max_retries {
345+
info!("Transaction attempt {} of {}", attempt + 1, max_retries);
434346

435-
// Wait for the receipt with timeout
436-
let receipt_result =
437-
tokio::time::timeout(retry_interval, pending_tx.get_receipt()).await;
347+
// Wrap the entire transaction submission in a result to catch all errors
348+
let attempt_result = self
349+
.try_submit_transaction(
350+
&blob,
351+
blob_versioned_hash,
352+
aggregated_proof,
353+
base_bump_percentage,
354+
retry_attempt_percentage,
355+
attempt as u64,
356+
retry_interval,
357+
)
358+
.await;
438359

439-
match receipt_result {
440-
Ok(Ok(receipt)) => {
360+
match attempt_result {
361+
Ok(receipt) => {
441362
info!(
442363
"Transaction confirmed successfully on attempt {}",
443364
attempt + 1
444365
);
445366
return Ok(receipt);
446367
}
447-
Ok(Err(err)) => {
448-
warn!("Error getting receipt on attempt {}: {}", attempt + 1, err);
449-
if attempt == max_retries - 1 {
450-
return Err(RetryError::Transient(
451-
AggregatedProofSubmissionError::SendVerifyAggregatedProofTransaction(
452-
err.to_string(),
453-
),
454-
));
455-
}
456-
}
457-
Err(_) => {
458-
warn!("Transaction not confirmed after {} seconds on attempt {}, retrying with higher fee...",
459-
retry_interval.as_secs(), attempt + 1);
460-
if attempt == max_retries - 1 {
461-
return Err(RetryError::Transient(
462-
AggregatedProofSubmissionError::SendVerifyAggregatedProofTransaction(
463-
"Transaction timeout after all retries".to_string(),
464-
),
465-
));
368+
Err(err) => {
369+
warn!("Attempt {} failed: {:?}", attempt + 1, err);
370+
last_error = Some(err);
371+
372+
if attempt < max_retries - 1 {
373+
info!("Retrying with bumped gas fees...");
374+
375+
tokio::time::sleep(Duration::from_millis(500)).await;
376+
} else {
377+
warn!("Max retries ({}) exceeded", max_retries);
466378
}
467379
}
468380
}
469381
}
470382

471-
Err(RetryError::Transient(
383+
// If we exhausted all retries, return the last error
384+
Err(RetryError::Transient(last_error.unwrap_or_else(|| {
472385
AggregatedProofSubmissionError::SendVerifyAggregatedProofTransaction(
473-
"Max retries exceeded".to_string(),
386+
"Max retries exceeded with no error details".to_string(),
387+
)
388+
})))
389+
}
390+
391+
async fn try_submit_transaction(
392+
&self,
393+
blob: &BlobTransactionSidecar,
394+
blob_versioned_hash: [u8; 32],
395+
aggregated_proof: &AlignedProof,
396+
base_bump_percentage: u64,
397+
retry_attempt_percentage: u64,
398+
attempt: u64,
399+
retry_interval: Duration,
400+
) -> Result<TransactionReceipt, AggregatedProofSubmissionError> {
401+
// Build the transaction request
402+
let mut tx_req = match aggregated_proof {
403+
AlignedProof::SP1(proof) => self
404+
.proof_aggregation_service
405+
.verifyAggregationSP1(
406+
blob_versioned_hash.into(),
407+
proof.proof_with_pub_values.public_values.to_vec().into(),
408+
proof.proof_with_pub_values.bytes().into(),
409+
self.sp1_chunk_aggregator_vk_hash_bytes.into(),
410+
)
411+
.sidecar(blob.clone())
412+
.into_transaction_request(),
413+
AlignedProof::Risc0(proof) => {
414+
let encoded_seal = encode_seal(&proof.receipt).map_err(|e| {
415+
AggregatedProofSubmissionError::Risc0EncodingSeal(e.to_string())
416+
})?;
417+
self.proof_aggregation_service
418+
.verifyAggregationRisc0(
419+
blob_versioned_hash.into(),
420+
encoded_seal.into(),
421+
proof.receipt.journal.bytes.clone().into(),
422+
self.risc0_chunk_aggregator_image_id_bytes.into(),
423+
)
424+
.sidecar(blob.clone())
425+
.into_transaction_request()
426+
}
427+
};
428+
429+
// Apply gas fee bump for retries
430+
if attempt > 0 {
431+
tx_req = self
432+
.apply_gas_fee_bump(
433+
base_bump_percentage,
434+
retry_attempt_percentage,
435+
attempt,
436+
tx_req,
437+
)
438+
.await?;
439+
}
440+
441+
let provider = self.proof_aggregation_service.provider();
442+
443+
// Fill the transaction
444+
let envelope = provider
445+
.fill(tx_req)
446+
.await
447+
.map_err(|err| {
448+
AggregatedProofSubmissionError::SendVerifyAggregatedProofTransaction(format!(
449+
"Failed to fill transaction: {}",
450+
err
451+
))
452+
})?
453+
.try_into_envelope()
454+
.map_err(|err| {
455+
AggregatedProofSubmissionError::SendVerifyAggregatedProofTransaction(format!(
456+
"Failed to convert to envelope: {}",
457+
err
458+
))
459+
})?;
460+
461+
// Convert to EIP-4844 transaction
462+
let tx: EthereumTxEnvelope<TxEip4844WithSidecar<BlobTransactionSidecarEip7594>> = envelope
463+
.try_into_pooled()
464+
.map_err(|err| {
465+
AggregatedProofSubmissionError::SendVerifyAggregatedProofTransaction(format!(
466+
"Failed to pool transaction: {}",
467+
err
468+
))
469+
})?
470+
.try_map_eip4844(|tx| {
471+
tx.try_map_sidecar(|sidecar| sidecar.try_into_7594(EnvKzgSettings::Default.get()))
472+
})
473+
.map_err(|err| {
474+
AggregatedProofSubmissionError::SendVerifyAggregatedProofTransaction(format!(
475+
"Failed to convert to EIP-7594: {}",
476+
err
477+
))
478+
})?;
479+
480+
// Send the transaction
481+
let encoded_tx = tx.encoded_2718();
482+
let pending_tx = provider
483+
.send_raw_transaction(&encoded_tx)
484+
.await
485+
.map_err(|err| {
486+
AggregatedProofSubmissionError::SendVerifyAggregatedProofTransaction(format!(
487+
"Failed to send raw transaction: {}",
488+
err
489+
))
490+
})?;
491+
492+
info!("Transaction sent, waiting for confirmation...");
493+
494+
// Wait for the receipt with timeout
495+
let receipt_result = tokio::time::timeout(retry_interval, pending_tx.get_receipt()).await;
496+
497+
match receipt_result {
498+
Ok(Ok(receipt)) => Ok(receipt),
499+
Ok(Err(err)) => Err(
500+
AggregatedProofSubmissionError::SendVerifyAggregatedProofTransaction(format!(
501+
"Error getting receipt: {}",
502+
err
503+
)),
504+
),
505+
Err(_) => Err(
506+
AggregatedProofSubmissionError::SendVerifyAggregatedProofTransaction(format!(
507+
"Transaction timeout after {} seconds",
508+
retry_interval.as_secs()
509+
)),
474510
),
475-
))
511+
}
476512
}
477513

478514
// Updates the gas fees of a `TransactionRequest` for retry attempts by applying a linear fee
@@ -487,30 +523,36 @@ impl ProofAggregator {
487523
// - `attempt = 3` → 10% + (3 * 5%) = 25% bump
488524
//
489525
// The bumped price is: current_gas_price * (1 + total_bump_percentage / 100)
490-
async fn update_gas_fees(
526+
async fn apply_gas_fee_bump(
491527
&self,
492528
base_bump_percentage: u64,
493529
retry_attempt_percentage: u64,
494530
attempt: u64,
495531
tx_req: alloy::rpc::types::TransactionRequest,
496-
) -> Result<alloy::rpc::types::TransactionRequest, RetryError<AggregatedProofSubmissionError>>
497-
{
532+
) -> Result<alloy::rpc::types::TransactionRequest, AggregatedProofSubmissionError> {
498533
let provider = self.proof_aggregation_service.provider();
499534

500535
// Calculate total bump percentage: base + (retry_count * retry_attempt)
501536
let incremental_retry_percentage = retry_attempt_percentage * attempt;
502537
let total_bump_percentage = base_bump_percentage + incremental_retry_percentage;
503538

539+
info!(
540+
"Applying {}% gas fee bump for attempt {}",
541+
total_bump_percentage,
542+
attempt + 1
543+
);
544+
504545
let mut current_tx_req = tx_req.clone();
505546

506547
if current_tx_req.max_fee_per_gas.is_none() {
507-
let current_gas_price = provider.get_gas_price().await.map_err(|e| {
508-
RetryError::Transient(AggregatedProofSubmissionError::GasPriceError(e.to_string()))
509-
})?;
548+
let current_gas_price = provider
549+
.get_gas_price()
550+
.await
551+
.map_err(|e| AggregatedProofSubmissionError::GasPriceError(e.to_string()))?;
510552

511553
let new_max_fee =
512554
Self::calculate_bumped_price(current_gas_price, total_bump_percentage);
513-
let new_priority_fee = new_max_fee / 10; // 10% of max fee
555+
let new_priority_fee = new_max_fee / 10;
514556

515557
current_tx_req = current_tx_req
516558
.with_max_fee_per_gas(new_max_fee)

0 commit comments

Comments
 (0)