diff --git a/Cargo.lock b/Cargo.lock index 7b7991c3d..4c0ebb1f5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -873,6 +873,12 @@ dependencies = [ "serde", ] +[[package]] +name = "ascii" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" + [[package]] name = "async-channel" version = "1.9.0" @@ -1727,6 +1733,12 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "chunked_transfer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e4de3bc4ea267985becf712dc6d9eed8b04c953b3fcfb339ebc87acd9804901" + [[package]] name = "clang-sys" version = "1.8.1" @@ -4791,6 +4803,7 @@ dependencies = [ "serde", "serde_json", "sscanf", + "tiny_http", "tokio", "tokio-metrics", "tokio-rustls 0.26.1", @@ -4942,6 +4955,7 @@ dependencies = [ "rundler-types", "rundler-utils", "thiserror 1.0.69", + "tiny_http", "tokio", "tower 0.4.13", "tracing", @@ -5940,6 +5954,18 @@ dependencies = [ "crunchy", ] +[[package]] +name = "tiny_http" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389915df6413a2e74fb181895f933386023c71110878cd0825588928e64cdc82" +dependencies = [ + "ascii", + "chunked_transfer", + "httpdate", + "log", +] + [[package]] name = "tinystr" version = "0.7.6" diff --git a/Cargo.toml b/Cargo.toml index d9ecdda1e..7ca038099 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -94,3 +94,4 @@ tower-http = { version = "0.6.2", features = ["cors"] } tracing = "0.1.40" strum = { version = "0.26.3", features = ["derive"] } url = "2.5.2" +tiny_http = "0.12.0" diff --git a/bin/rundler/Cargo.toml b/bin/rundler/Cargo.toml index f8665bc15..ac2adf83a 100644 --- a/bin/rundler/Cargo.toml +++ b/bin/rundler/Cargo.toml @@ -41,6 +41,7 @@ reth-tasks.workspace = true serde.workspace = true serde_json.workspace = true sscanf = "0.4.2" +tiny_http.workspace = true tokio = { workspace = true, features = ["macros", "rt-multi-thread", "signal", "sync"] } tokio-metrics = "0.3.1" tokio-rustls = "0.26.0" diff --git a/bin/rundler/src/cli/builder.rs b/bin/rundler/src/cli/builder.rs index 6376361ed..0e5f485a1 100644 --- a/bin/rundler/src/cli/builder.rs +++ b/bin/rundler/src/cli/builder.rs @@ -336,6 +336,8 @@ impl BuilderArgs { let da_gas_tracking_enabled = super::lint_da_gas_tracking(common.da_gas_tracking_enabled, &chain_spec); + let provider_client_timeout_seconds = common.provider_client_timeout_seconds; + Ok(BuilderTaskArgs { entry_points, chain_spec, @@ -358,6 +360,7 @@ impl BuilderArgs { max_replacement_underpriced_blocks: self.max_replacement_underpriced_blocks, remote_address, da_gas_tracking_enabled, + provider_client_timeout_seconds, }) } diff --git a/bin/rundler/src/cli/mod.rs b/bin/rundler/src/cli/mod.rs index 577ee8ad7..959c37773 100644 --- a/bin/rundler/src/cli/mod.rs +++ b/bin/rundler/src/cli/mod.rs @@ -346,6 +346,14 @@ pub struct CommonArgs { default_value = "false" )] pub da_gas_tracking_enabled: bool, + + #[arg( + long = "provider_client_timeout_seconds", + name = "provider_client_timeout_seconds", + env = "PROVIDER_CLIENT_TIMEOUT_SECONDS", + default_value = "10" + )] + pub provider_client_timeout_seconds: u64, } const SIMULATION_GAS_OVERHEAD: u64 = 100_000; @@ -594,6 +602,7 @@ pub fn construct_providers( ) -> anyhow::Result { let provider = Arc::new(rundler_provider::new_alloy_provider( args.node_http.as_ref().context("must provide node_http")?, + args.provider_client_timeout_seconds, )?); let (da_gas_oracle, da_gas_oracle_sync) = rundler_provider::new_alloy_da_gas_oracle(chain_spec, provider.clone()); diff --git a/crates/builder/src/sender/mod.rs b/crates/builder/src/sender/mod.rs index 9c94c8961..60a97a3e5 100644 --- a/crates/builder/src/sender/mod.rs +++ b/crates/builder/src/sender/mod.rs @@ -171,12 +171,17 @@ impl TransactionSenderArgs { self, rpc_url: &str, signer: S, + provider_client_timeout_seconds: u64, ) -> std::result::Result, SenderConstructorErrors> { - let provider = rundler_provider::new_alloy_evm_provider(rpc_url)?; + let provider = + rundler_provider::new_alloy_evm_provider(rpc_url, provider_client_timeout_seconds)?; let sender = match self { Self::Raw(args) => { - let submitter = rundler_provider::new_alloy_evm_provider(&args.submit_url)?; + let submitter = rundler_provider::new_alloy_evm_provider( + &args.submit_url, + provider_client_timeout_seconds, + )?; if args.use_submit_for_status { TransactionSenderEnum::Raw(RawTransactionSender::new( diff --git a/crates/builder/src/task.rs b/crates/builder/src/task.rs index b0d6e5230..8f8c22ce2 100644 --- a/crates/builder/src/task.rs +++ b/crates/builder/src/task.rs @@ -89,6 +89,8 @@ pub struct Args { pub entry_points: Vec, /// Enable DA tracking pub da_gas_tracking_enabled: bool, + /// Provider client timeout + pub provider_client_timeout_seconds: u64, } /// Builder settings for an entrypoint @@ -355,11 +357,11 @@ where da_gas_tracking_enabled: self.args.da_gas_tracking_enabled, }; - let transaction_sender = self - .args - .sender_args - .clone() - .into_sender(&self.args.rpc_url, signer)?; + let transaction_sender = self.args.sender_args.clone().into_sender( + &self.args.rpc_url, + signer, + self.args.provider_client_timeout_seconds, + )?; let tracker_settings = transaction_tracker::Settings { replacement_fee_percent_increase: self.args.replacement_fee_percent_increase, diff --git a/crates/provider/Cargo.toml b/crates/provider/Cargo.toml index a3b521057..be07752dc 100644 --- a/crates/provider/Cargo.toml +++ b/crates/provider/Cargo.toml @@ -52,4 +52,5 @@ alloy-node-bindings = "0.4.2" alloy-provider = { workspace = true, features = ["debug-api", "anvil-node"] } alloy-sol-macro.workspace = true rundler-provider = { workspace = true, features = ["test-utils"] } +tiny_http.workspace = true tokio.workspace = true diff --git a/crates/provider/src/alloy/mod.rs b/crates/provider/src/alloy/mod.rs index 97d114662..673e9eb91 100644 --- a/crates/provider/src/alloy/mod.rs +++ b/crates/provider/src/alloy/mod.rs @@ -34,14 +34,18 @@ pub(crate) mod metrics; mod provider_timeout; /// Create a new alloy evm provider from a given RPC URL -pub fn new_alloy_evm_provider(rpc_url: &str) -> anyhow::Result { - let provider = new_alloy_provider(rpc_url)?; +pub fn new_alloy_evm_provider( + rpc_url: &str, + provider_client_timeout_seconds: u64, +) -> anyhow::Result { + let provider = new_alloy_provider(rpc_url, provider_client_timeout_seconds)?; Ok(AlloyEvmProvider::new(provider)) } /// Create a new alloy provider from a given RPC URL pub fn new_alloy_provider( rpc_url: &str, + provider_client_timeout_seconds: u64, ) -> anyhow::Result< impl AlloyProvider>>>> + Clone, @@ -51,7 +55,8 @@ pub fn new_alloy_provider( // TODO: make this configurable: use a large number for CUPS for now let retry_layer = alloy_transport::layers::RetryBackoffLayer::new(10, 500, 1_000_000); // add a timeout layer here. - let timeout_layer = ProviderTimeoutLayer::new(Duration::from_secs(10)); + let timeout_layer = + ProviderTimeoutLayer::new(Duration::from_secs(provider_client_timeout_seconds)); let client = ClientBuilder::default() .layer(retry_layer) .layer(metric_layer) @@ -60,3 +65,45 @@ pub fn new_alloy_provider( let provider = ProviderBuilder::new().on_client(client); Ok(provider) } + +#[cfg(test)] +mod tests { + use std::{ + thread::{self, sleep}, + time::Duration, + }; + + use alloy_provider::Provider; + use tiny_http::{Response, Server}; + + use crate::new_alloy_provider; + fn setup() { + let server = Server::http("0.0.0.0:8000").unwrap(); + for request in server.incoming_requests() { + sleep(Duration::from_secs(10)); + let _ = request.respond(Response::from_string( + "{\"jsonrpc\": \"2.0\", \"id\": 1, \"result\": \"0x146b6d7\"}", + )); + } + } + #[tokio::test] + async fn test_timeout() { + thread::spawn(move || { + setup(); + }); + { + // Wait 11 seconds and get result + let provider = new_alloy_provider("http://localhost:8000", 11) + .expect("can not initialize provider"); + let x = provider.get_block_number().await; + assert!(x.is_ok()); + } + { + // Wait 9 seconds and timeout form client side + let provider = new_alloy_provider("http://localhost:8000", 9) + .expect("can not initialize provider"); + let x = provider.get_block_number().await; + assert!(x.is_err()); + } + } +}