diff --git a/crates/chain-orchestrator/src/lib.rs b/crates/chain-orchestrator/src/lib.rs index c898163a..99d59d3c 100644 --- a/crates/chain-orchestrator/src/lib.rs +++ b/crates/chain-orchestrator/src/lib.rs @@ -47,6 +47,9 @@ pub use error::ChainOrchestratorError; mod metrics; pub use metrics::{ChainOrchestratorItem, ChainOrchestratorMetrics}; +mod retry; +pub use retry::Retry; + /// The mask used to mask the L1 message queue hash. const L1_MESSAGE_QUEUE_HASH_MASK: B256 = b256!("ffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000"); @@ -211,9 +214,14 @@ impl< let database = ctx.database.clone(); let block_info: L2BlockInfoWithL1Messages = (&block_with_peer.block).into(); Self::do_handle_block_from_peer(ctx, block_with_peer).await?; - let tx = database.tx_mut().await?; - tx.update_l1_messages_with_l2_block(block_info.clone()).await?; - tx.commit().await?; + Retry::default() + .retry("update_l1_messages_with_l2_block", || async { + let tx = database.tx_mut().await?; + tx.update_l1_messages_with_l2_block(block_info.clone()).await?; + tx.commit().await?; + Ok::<_, ChainOrchestratorError>(()) + }) + .await?; Ok(ChainOrchestratorEvent::L2ChainCommitted(block_info, None, true)) } @@ -253,9 +261,14 @@ impl< tracing::trace!(target: "scroll::chain_orchestrator", number = ?(optimistic_headers.first().expect("chain can not be empty").number - 1), "fetching block"); let parent_hash = optimistic_headers.first().expect("chain can not be empty").parent_hash; - let header = network_client - .get_header(BlockHashOrNumber::Hash(parent_hash)) - .await? + let header = Retry::default() + .retry("network_client_get_header", || async { + let header = + network_client.get_header(BlockHashOrNumber::Hash(parent_hash)).await?; + Ok::<_, ChainOrchestratorError>(header) + }) + .await?; + let header = header .into_data() .ok_or(ChainOrchestratorError::MissingBlockHeader { hash: parent_hash })?; optimistic_headers.push_front(header); @@ -292,10 +305,14 @@ impl< let mut received_chain_headers = VecDeque::from(vec![received_block.header.clone()]); // We should never have a re-org that is deeper than the current safe head. - let tx = database.tx().await?; - let (latest_safe_block, _) = - tx.get_latest_safe_l2_info().await?.expect("safe block must exist"); - drop(tx); + let (latest_safe_block, _) = Retry::default() + .retry("get_latest_safe_l2_info", || async { + let tx = database.tx().await?; + let (latest_safe_block, batch_info) = + tx.get_latest_safe_l2_info().await?.expect("safe block must exist"); + Ok::<_, ChainOrchestratorError>((latest_safe_block, batch_info)) + }) + .await?; // We search for the re-org index in the in-memory chain. const BATCH_FETCH_SIZE: usize = 50; @@ -383,12 +400,20 @@ impl< } tracing::trace!(target: "scroll::chain_orchestrator", number = ?(received_chain_headers.front().expect("chain can not be empty").number - 1), "fetching block"); - if let Some(header) = network_client - .get_header(BlockHashOrNumber::Hash( - received_chain_headers.front().expect("chain can not be empty").parent_hash, - )) + if let Some(header) = Retry::default() + .retry("network_client_get_header", || async { + let header = network_client + .get_header(BlockHashOrNumber::Hash( + received_chain_headers + .front() + .expect("chain can not be empty") + .parent_hash, + )) + .await? + .into_data(); + Ok::<_, ChainOrchestratorError>(header) + }) .await? - .into_data() { received_chain_headers.push_front(header.clone()); } else { @@ -455,11 +480,16 @@ impl< ChainOrchestratorItem::InsertConsolidatedL2Blocks, Box::pin(async move { let head = block_infos.last().expect("block info must not be empty").clone(); - let tx = database.tx_mut().await?; - for block in block_infos { - tx.insert_block(block, batch_info).await?; - } - tx.commit().await?; + Retry::default() + .retry("insert_block", || async { + let tx = database.tx_mut().await?; + for block in block_infos.clone() { + tx.insert_block(block, batch_info).await?; + } + tx.commit().await?; + Ok::<_, ChainOrchestratorError>(()) + }) + .await?; Result::<_, ChainOrchestratorError>::Ok(Some( ChainOrchestratorEvent::L2ConsolidatedBlockCommitted(head), )) @@ -503,9 +533,14 @@ impl< // Insert the blocks into the database. let head = block_info.last().expect("block info must not be empty").clone(); - let tx = database.tx_mut().await?; - tx.update_l1_messages_from_l2_blocks(block_info).await?; - tx.commit().await?; + Retry::default() + .retry("update_l1_messages_from_l2_blocks", || async { + let tx = database.tx_mut().await?; + tx.update_l1_messages_from_l2_blocks(block_info.clone()).await?; + tx.commit().await?; + Ok::<_, ChainOrchestratorError>(()) + }) + .await?; Result::<_, ChainOrchestratorError>::Ok(Some( ChainOrchestratorEvent::L2ChainCommitted(head, None, consolidated), @@ -589,10 +624,25 @@ impl< l2_client: Arc

, current_chain: Arc>, ) -> Result, ChainOrchestratorError> { - let txn = database.tx_mut().await?; let UnwindResult { l1_block_number, queue_index, l2_head_block_number, l2_safe_block_info } = - txn.unwind(chain_spec.genesis_hash(), l1_block_number).await?; - txn.commit().await?; + Retry::default() + .retry("unwind", || async { + let txn = database.tx_mut().await?; + let UnwindResult { + l1_block_number, + queue_index, + l2_head_block_number, + l2_safe_block_info, + } = txn.unwind(chain_spec.genesis_hash(), l1_block_number).await?; + txn.commit().await?; + Ok::<_, ChainOrchestratorError>(UnwindResult { + l1_block_number, + queue_index, + l2_head_block_number, + l2_safe_block_info, + }) + }) + .await?; let l2_head_block_info = if let Some(block_number) = l2_head_block_number { // Fetch the block hash of the new L2 head block. let block_hash = l2_client @@ -623,16 +673,23 @@ impl< block_number: u64, l1_block_number: Arc, ) -> Result, ChainOrchestratorError> { - let tx = database.tx_mut().await?; + let finalized_batches = Retry::default() + .retry("handle_finalized", || async { + let tx = database.tx_mut().await?; + + // Set the latest finalized L1 block in the database. + tx.set_latest_finalized_l1_block_number(block_number).await?; - // Set the latest finalized L1 block in the database. - tx.set_latest_finalized_l1_block_number(block_number).await?; + // Get all unprocessed batches that have been finalized by this L1 block + // finalization. + let finalized_batches = + tx.fetch_and_update_unprocessed_finalized_batches(block_number).await?; - // Get all unprocessed batches that have been finalized by this L1 block finalization. - let finalized_batches = - tx.fetch_and_update_unprocessed_finalized_batches(block_number).await?; + tx.commit().await?; - tx.commit().await?; + Ok::<_, ChainOrchestratorError>(finalized_batches) + }) + .await?; // Update the chain orchestrator L1 block number. l1_block_number.store(block_number, Ordering::Relaxed); @@ -654,17 +711,24 @@ impl< let l1_message = L1MessageEnvelope::new(l1_message, l1_block_number, None, queue_hash); // Perform a consistency check to ensure the previous L1 message exists in the database. - let tx = database.tx_mut().await?; - if l1_message.transaction.queue_index > 0 && - tx.get_l1_message_by_index(l1_message.transaction.queue_index - 1).await?.is_none() - { - return Err(ChainOrchestratorError::L1MessageQueueGap( - l1_message.transaction.queue_index, - )) - } + Retry::default() + .retry("handle_l1_message", || async { + let tx = database.tx_mut().await?; + if l1_message.transaction.queue_index > 0 && + tx.get_l1_message_by_index(l1_message.transaction.queue_index - 1) + .await? + .is_none() + { + return Err(ChainOrchestratorError::L1MessageQueueGap( + l1_message.transaction.queue_index, + )) + } - tx.insert_l1_message(l1_message).await?; - tx.commit().await?; + tx.insert_l1_message(l1_message.clone()).await?; + tx.commit().await?; + Ok::<_, ChainOrchestratorError>(()) + }) + .await?; Ok(Some(event)) } @@ -673,36 +737,44 @@ impl< database: Arc, batch: BatchCommitData, ) -> Result, ChainOrchestratorError> { - let tx = database.tx_mut().await?; - let prev_batch_index = batch.index - 1; + let event = Retry::default() + .retry("handle_batch_commit", || async { + let tx = database.tx_mut().await?; + let batch_clone = batch.clone(); + let prev_batch_index = batch_clone.clone().index - 1; - // Perform a consistency check to ensure the previous commit batch exists in the database. - if tx.get_batch_by_index(prev_batch_index).await?.is_none() { - return Err(ChainOrchestratorError::BatchCommitGap(batch.index)) - } + // Perform a consistency check to ensure the previous commit batch exists in the + // database. + if tx.get_batch_by_index(prev_batch_index).await?.is_none() { + return Err(ChainOrchestratorError::BatchCommitGap(batch_clone.index)) + } - // remove any batches with an index greater than the previous batch. - let affected = tx.delete_batches_gt_batch_index(prev_batch_index).await?; + // remove any batches with an index greater than the previous batch. + let affected = tx.delete_batches_gt_batch_index(prev_batch_index).await?; - // handle the case of a batch revert. - let new_safe_head = if affected > 0 { - tx.delete_l2_blocks_gt_batch_index(prev_batch_index).await?; - tx.get_highest_block_for_batch_index(prev_batch_index).await? - } else { - None - }; + // handle the case of a batch revert. + let new_safe_head = if affected > 0 { + tx.delete_l2_blocks_gt_batch_index(prev_batch_index).await?; + tx.get_highest_block_for_batch_index(prev_batch_index).await? + } else { + None + }; - let event = ChainOrchestratorEvent::BatchCommitIndexed { - batch_info: BatchInfo::new(batch.index, batch.hash), - l1_block_number: batch.block_number, - safe_head: new_safe_head, - }; + let event = ChainOrchestratorEvent::BatchCommitIndexed { + batch_info: BatchInfo::new(batch_clone.index, batch_clone.hash), + l1_block_number: batch.block_number, + safe_head: new_safe_head, + }; - // insert the batch and commit the transaction. - tx.insert_batch(batch).await?; - tx.commit().await?; + // insert the batch and commit the transaction. + tx.insert_batch(batch_clone).await?; + tx.commit().await?; - Ok(Some(event)) + Ok::<_, ChainOrchestratorError>(Some(event)) + }) + .await?; + + Ok(event) } /// Handles a batch finalization event by updating the batch input in the database. @@ -712,22 +784,31 @@ impl< block_number: u64, finalized_block_number: Arc, ) -> Result, ChainOrchestratorError> { - let tx = database.tx_mut().await?; - - // finalize all batches up to `batch_index`. - tx.finalize_batches_up_to_index(batch_index, block_number).await?; - - // Get all unprocessed batches that have been finalized by this L1 block finalization. - let finalized_block_number = finalized_block_number.load(Ordering::Relaxed); - if finalized_block_number >= block_number { - let finalized_batches = - tx.fetch_and_update_unprocessed_finalized_batches(finalized_block_number).await?; - tx.commit().await?; - return Ok(Some(ChainOrchestratorEvent::BatchFinalized(block_number, finalized_batches))) - } + Retry::default() + .retry("handle_batch_finalization", || async { + let tx = database.tx_mut().await?; - tx.commit().await?; - Ok(None) + // finalize all batches up to `batch_index`. + tx.finalize_batches_up_to_index(batch_index, block_number).await?; + + // Get all unprocessed batches that have been finalized by this L1 block + // finalization. + let finalized_block_number = finalized_block_number.load(Ordering::Relaxed); + if finalized_block_number >= block_number { + let finalized_batches = tx + .fetch_and_update_unprocessed_finalized_batches(finalized_block_number) + .await?; + tx.commit().await?; + return Ok(Some(ChainOrchestratorEvent::BatchFinalized( + block_number, + finalized_batches, + ))) + } + + tx.commit().await?; + Ok::<_, ChainOrchestratorError>(None) + }) + .await } } @@ -749,9 +830,12 @@ async fn compute_l1_message_queue_hash( Some(keccak256(input) & L1_MESSAGE_QUEUE_HASH_MASK) } else if l1_message.queue_index > l1_v2_message_queue_start_index { let index = l1_message.queue_index - 1; - let tx = database.tx().await?; - let mut input = tx - .get_l1_message_by_index(index) + let mut input = Retry::default() + .retry("get_l1_message_by_index", || async { + let tx = database.tx().await?; + let input = tx.get_l1_message_by_index(index).await?; + Ok::<_, ChainOrchestratorError>(input) + }) .await? .map(|m| m.queue_hash) .ok_or(DatabaseError::L1MessageNotFound(L1MessageStart::Index(index)))? @@ -772,10 +856,21 @@ async fn init_chain_from_db + 'static>( ) -> Result, ChainOrchestratorError> { let blocks = { let mut blocks = Vec::with_capacity(chain_buffer_size); - let tx = database.tx().await?; - let blocks_stream = tx.get_l2_blocks().await?.take(chain_buffer_size); + let tx = Retry::default() + .retry("get_l2_blocks_new_tx", || async { + let tx = database.tx().await?; + Ok::<_, ChainOrchestratorError>(tx) + }) + .await?; + let blocks_stream = Retry::default() + .retry("get_l2_blocks", || async { + let stream = tx.get_l2_blocks().await?; + Ok::<_, ChainOrchestratorError>(stream) + }) + .await? + .take(chain_buffer_size); pin_mut!(blocks_stream); - while let Some(block_info) = blocks_stream.try_next().await? { + while let Some(block_info) = blocks_stream.as_mut().try_next().await? { let header = l2_client .get_block_by_hash(block_info.hash) .await? @@ -869,9 +964,13 @@ async fn consolidate_chain + 'static>( // Fetch the safe head from the database. We use this as a trust anchor to reconcile the chain // back to. - let tx = database.tx().await?; - let safe_head = tx.get_latest_safe_l2_info().await?.expect("safe head must exist").0; - drop(tx); + let safe_head = Retry::default() + .retry("get_latest_safe_l2_info", || async { + let tx = database.tx().await?; + let safe_head = tx.get_latest_safe_l2_info().await?.expect("safe head must exist").0; + Ok::<_, ChainOrchestratorError>(safe_head) + }) + .await?; // If the in-memory chain contains the safe head, we check if the safe hash from the // database (L1 consolidation) matches the in-memory value. If it does not match, we return an @@ -970,14 +1069,31 @@ async fn validate_l1_messages( // TODO: instead of using `l1_message_hashes.first().map(|tx| L1MessageStart::Hash(*tx))` to // determine the start of the L1 message stream, we should use a more robust method to determine // the start of the L1 message stream. - let tx = database.tx().await?; - let l1_message_stream = - tx.get_l1_messages(l1_message_hashes.first().map(|tx| L1MessageStart::Hash(*tx))).await?; + let tx = Retry::default() + .retry("get_l1_messages_new_tx", || async { + let tx = database.tx().await?; + Ok::<_, ChainOrchestratorError>(tx) + }) + .await?; + let l1_message_stream = Retry::default() + .retry("get_l1_messages", || async { + let messages = tx + .get_l1_messages(l1_message_hashes.first().map(|tx| L1MessageStart::Hash(*tx))) + .await?; + Ok::<_, ChainOrchestratorError>(messages) + }) + .await?; pin_mut!(l1_message_stream); for message_hash in l1_message_hashes { // Get the expected L1 message from the database. - let expected_hash = l1_message_stream.next().await.unwrap().unwrap().transaction.tx_hash(); + let expected_hash = l1_message_stream + .as_mut() + .next() + .await + .map(|m| m.map(|msg| msg.transaction.tx_hash())) + .transpose()? + .ok_or(ChainOrchestratorError::L1MessageNotFound(L1MessageStart::Hash(message_hash)))?; // If the received and expected L1 messages do not match return an error. if message_hash != expected_hash { diff --git a/crates/chain-orchestrator/src/retry.rs b/crates/chain-orchestrator/src/retry.rs new file mode 100644 index 00000000..35a7fa9d --- /dev/null +++ b/crates/chain-orchestrator/src/retry.rs @@ -0,0 +1,187 @@ +//! Configurable retry mechanism for database, network, and other fallible operations. + +use std::time::Duration; + +/// A type used for retrying transient failures in operations. +#[derive(Debug, Clone)] +pub struct Retry { + /// Maximum number of retry attempts. None means infinite retries + pub max_retries: Option, + /// Initial delay between retries in milliseconds + pub initial_delay_ms: u64, + /// Whether to use exponential backoff + pub exponential_backoff: bool, +} + +impl Default for Retry { + fn default() -> Self { + Self { max_retries: None, initial_delay_ms: 50, exponential_backoff: false } + } +} + +impl Retry { + /// Creates a new [`Retry`] with the specified parameters. + pub const fn new( + max_retries: Option, + initial_delay_ms: u64, + exponential_backoff: bool, + ) -> Self { + Self { max_retries, initial_delay_ms, exponential_backoff } + } + + /// Retry an asynchronous operation with the configured retry strategy. + pub async fn retry(&self, operation_name: &str, operation: F) -> Result + where + F: Fn() -> Fut, + Fut: std::future::Future>, + E: std::fmt::Debug + CanRetry, + { + let mut attempt: usize = 0; + + loop { + match operation().await { + Ok(result) => return Ok(result), + Err(error) => { + // If the error is not retryable, return immediately. + if !error.can_retry() { + return Err(error); + } + + if let Some(max_retries) = self.max_retries { + if attempt >= max_retries { + return Err(error); + } + } + + attempt += 1; + tracing::debug!( + target: "scroll::chain_orchestrator", + operation = operation_name, + error = ?error, + attempt = attempt, + "Retrying operation" + ); + + // Calculate delay for next retry + let delay_ms = if self.exponential_backoff { + self.initial_delay_ms * 2_u64.pow(attempt as u32 - 1) + } else { + self.initial_delay_ms + }; + + tokio::time::sleep(Duration::from_millis(delay_ms)).await; + } + } + } + } +} + +/// A trait for errors that can indicate whether an operation can be retried. +pub trait CanRetry { + fn can_retry(&self) -> bool; +} + +// Centralized retry classification impls +impl CanRetry for scroll_db::DatabaseError { + fn can_retry(&self) -> bool { + matches!(self, Self::DatabaseError(_) | Self::SqlxError(_)) + } +} + +impl CanRetry for crate::error::ChainOrchestratorError { + fn can_retry(&self) -> bool { + match self { + Self::DatabaseError(db) => db.can_retry(), + Self::NetworkRequestError(_) | Self::RpcError(_) => true, + _ => false, + } + } +} + +#[cfg(test)] +mod tests { + use super::{CanRetry, Retry}; + use std::cell::RefCell; + + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + struct TestErr; + impl CanRetry for TestErr { + fn can_retry(&self) -> bool { + true + } + } + + #[tokio::test] + async fn test_retry_success_on_first_attempt() { + let attempt = RefCell::new(0); + let retry = Retry::new(Some(3), 10, false); + let result = retry + .retry("test_operation", || { + *attempt.borrow_mut() += 1; + async move { Ok::(42) } + }) + .await; + + assert_eq!(result, Ok(42)); + assert_eq!(*attempt.borrow(), 1); + } + + #[tokio::test] + async fn test_retry_success_after_failures() { + let attempt = RefCell::new(0); + let retry = Retry::new(Some(5), 10, false); + let result = retry + .retry("test_operation", || { + *attempt.borrow_mut() += 1; + let current_attempt = *attempt.borrow(); + async move { + if current_attempt < 3 { + Err::(TestErr) + } else { + Ok(42) + } + } + }) + .await; + + assert_eq!(result, Ok(42)); + assert_eq!(*attempt.borrow(), 3); + } + + #[tokio::test] + async fn test_retry_exhausted() { + let attempt = RefCell::new(0); + let retry = Retry::new(Some(2), 10, false); + let result = retry + .retry("test_operation", || { + *attempt.borrow_mut() += 1; + async move { Err::(TestErr) } + }) + .await; + + assert_eq!(result, Err(TestErr)); + assert_eq!(*attempt.borrow(), 3); // 1 initial + 2 retries + } + + #[tokio::test] + async fn test_retry_with_defaults() { + let attempt = RefCell::new(0); + let retry = Retry::default(); + let result = retry + .retry("test_retry_with_defaults", || { + *attempt.borrow_mut() += 1; + let current_attempt = *attempt.borrow(); + async move { + if current_attempt < 2 { + Err::(TestErr) + } else { + Ok(42) + } + } + }) + .await; + + assert_eq!(result, Ok(42)); + assert_eq!(*attempt.borrow(), 2); + } +} diff --git a/crates/database/db/src/error.rs b/crates/database/db/src/error.rs index 127ea0bb..0320a03f 100644 --- a/crates/database/db/src/error.rs +++ b/crates/database/db/src/error.rs @@ -1,6 +1,4 @@ use super::L1MessageStart; -use alloy_eips::BlockId; -use alloy_primitives::B256; use sea_orm::sqlx::Error as SqlxError; /// The error type for database operations. @@ -9,19 +7,13 @@ pub enum DatabaseError { /// A database error occurred. #[error("database error: {0}")] DatabaseError(#[from] sea_orm::DbErr), - /// A batch was not found in the database. - #[error("batch with hash [{0}] not found in database")] - BatchNotFound(B256), - /// The block was not found in database. - #[error("no block for id {0}")] - BlockNotFound(BlockId), + /// An error occurred at the sqlx level. + #[error("A sqlx error occurred: {0}")] + SqlxError(#[from] SqlxError), /// A generic error occurred. #[error("parse signature error: {0}")] ParseSignatureError(String), /// The L1 message was not found in database. #[error("L1 message at index [{0}] not found in database")] L1MessageNotFound(L1MessageStart), - /// An error occurred at the sqlx level. - #[error("A sqlx error occurred: {0}")] - SqlxError(#[from] SqlxError), } diff --git a/crates/node/src/args.rs b/crates/node/src/args.rs index 3b828f58..ab386b92 100644 --- a/crates/node/src/args.rs +++ b/crates/node/src/args.rs @@ -199,11 +199,21 @@ impl ScrollRollupNodeConfig { ProviderBuilder::new().connect_client(client) }); - // Get a provider to the execution layer. - let l2_provider = rpc_server_handles - .rpc - .new_http_provider_for() - .expect("failed to create payload provider"); + // Init a retry provider to the execution layer. + let retry_layer = RetryBackoffLayer::new( + constants::L2_PROVIDER_MAX_RETRIES, + constants::L2_PROVIDER_INITIAL_BACKOFF, + constants::PROVIDER_COMPUTE_UNITS_PER_SECOND, + ); + let client = RpcClient::builder().layer(retry_layer).http( + rpc_server_handles + .rpc + .http_url() + .expect("failed to get l2 rpc url") + .parse() + .expect("invalid l2 rpc url"), + ); + let l2_provider = ProviderBuilder::<_, _, Scroll>::default().connect_client(client); let l2_provider = Arc::new(l2_provider); // Fetch the database from the hydrated config. @@ -578,10 +588,10 @@ pub struct L1ProviderArgs { #[arg(long = "l1.cups", id = "l1_compute_units_per_second", value_name = "L1_COMPUTE_UNITS_PER_SECOND", default_value_t = constants::PROVIDER_COMPUTE_UNITS_PER_SECOND)] pub compute_units_per_second: u64, /// The max amount of retries for the provider. - #[arg(long = "l1.max-retries", id = "l1_max_retries", value_name = "L1_MAX_RETRIES", default_value_t = constants::PROVIDER_MAX_RETRIES)] + #[arg(long = "l1.max-retries", id = "l1_max_retries", value_name = "L1_MAX_RETRIES", default_value_t = constants::L1_PROVIDER_MAX_RETRIES)] pub max_retries: u32, /// The initial backoff for the provider. - #[arg(long = "l1.initial-backoff", id = "l1_initial_backoff", value_name = "L1_INITIAL_BACKOFF", default_value_t = constants::PROVIDER_INITIAL_BACKOFF)] + #[arg(long = "l1.initial-backoff", id = "l1_initial_backoff", value_name = "L1_INITIAL_BACKOFF", default_value_t = constants::L1_PROVIDER_INITIAL_BACKOFF)] pub initial_backoff: u64, } @@ -608,10 +618,10 @@ pub struct BlobProviderArgs { #[arg(long = "blob.cups", id = "blob_compute_units_per_second", value_name = "BLOB_COMPUTE_UNITS_PER_SECOND", default_value_t = constants::PROVIDER_COMPUTE_UNITS_PER_SECOND)] pub compute_units_per_second: u64, /// The max amount of retries for the provider. - #[arg(long = "blob.max-retries", id = "blob_max_retries", value_name = "BLOB_MAX_RETRIES", default_value_t = constants::PROVIDER_MAX_RETRIES)] + #[arg(long = "blob.max-retries", id = "blob_max_retries", value_name = "BLOB_MAX_RETRIES", default_value_t = constants::L1_PROVIDER_MAX_RETRIES)] pub max_retries: u32, /// The initial backoff for the provider. - #[arg(long = "blob.initial-backoff", id = "blob_initial_backoff", value_name = "BLOB_INITIAL_BACKOFF", default_value_t = constants::PROVIDER_INITIAL_BACKOFF)] + #[arg(long = "blob.initial-backoff", id = "blob_initial_backoff", value_name = "BLOB_INITIAL_BACKOFF", default_value_t = constants::L1_PROVIDER_INITIAL_BACKOFF)] pub initial_backoff: u64, } diff --git a/crates/node/src/constants.rs b/crates/node/src/constants.rs index 3611aaa3..48425bbf 100644 --- a/crates/node/src/constants.rs +++ b/crates/node/src/constants.rs @@ -3,10 +3,16 @@ use alloy_primitives::{address, Address, U128}; /// The max retries for the L1 provider. -pub(crate) const PROVIDER_MAX_RETRIES: u32 = 10; +pub(crate) const L1_PROVIDER_MAX_RETRIES: u32 = 10; /// The initial backoff for the L1 provider. -pub(crate) const PROVIDER_INITIAL_BACKOFF: u64 = 100; +pub(crate) const L1_PROVIDER_INITIAL_BACKOFF: u64 = 100; + +/// The max retries for the L2 provider. +pub(crate) const L2_PROVIDER_MAX_RETRIES: u32 = u32::MAX; + +/// The initial backoff for the L2 provider. +pub(crate) const L2_PROVIDER_INITIAL_BACKOFF: u64 = 50; /// The default provider compute units per second. pub(crate) const PROVIDER_COMPUTE_UNITS_PER_SECOND: u64 = 10000;