From 23da1225ed87bb2677abc7a575cfa75c54f280f7 Mon Sep 17 00:00:00 2001 From: JP <36560907+0xfourzerofour@users.noreply.github.com> Date: Tue, 18 Jun 2024 16:17:14 -0400 Subject: [PATCH] feat(tracer): add timeout of 10s to tracer (#730) --- Cargo.lock | 7 +++++ bin/rundler/Cargo.toml | 1 + bin/rundler/src/cli/builder.rs | 2 +- bin/rundler/src/cli/mod.rs | 28 ++++++++++++++---- bin/rundler/src/cli/pool.rs | 2 +- crates/builder/src/task.rs | 8 ++--- crates/pool/src/task.rs | 18 ++++++++---- crates/sim/src/simulation/context.rs | 4 +-- crates/sim/src/simulation/mod.rs | 8 ++++- crates/sim/src/simulation/simulator.rs | 10 +++---- crates/sim/src/simulation/v0_6/context.rs | 36 ++++++++++++----------- crates/sim/src/simulation/v0_6/tracer.rs | 16 +++++++--- crates/sim/src/simulation/v0_7/context.rs | 11 +++++-- crates/sim/src/simulation/v0_7/tracer.rs | 16 +++++++--- docs/cli.md | 2 ++ 15 files changed, 116 insertions(+), 53 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0d67859fc..041db3126 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2016,6 +2016,12 @@ dependencies = [ "web-sys", ] +[[package]] +name = "go-parse-duration" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "558b88954871f5e5b2af0e62e2e176c8bde7a6c2c4ed41b13d138d96da2e2cbd" + [[package]] name = "group" version = "0.13.0" @@ -4027,6 +4033,7 @@ dependencies = [ "config", "dotenv", "ethers", + "go-parse-duration", "itertools 0.12.1", "metrics", "metrics-exporter-prometheus", diff --git a/bin/rundler/Cargo.toml b/bin/rundler/Cargo.toml index 08e0e4476..5fc29f88f 100644 --- a/bin/rundler/Cargo.toml +++ b/bin/rundler/Cargo.toml @@ -27,6 +27,7 @@ dotenv = "0.15.0" ethers.workspace = true itertools = "0.12.1" metrics = "0.22.1" +go-parse-duration = "0.1" metrics-exporter-prometheus = { version = "0.13.1", default-features = false, features = ["http-listener"] } metrics-process = "1.2.1" metrics-util = "0.16.2" diff --git a/bin/rundler/src/cli/builder.rs b/bin/rundler/src/cli/builder.rs index 2a5d73c0d..0cd5bed81 100644 --- a/bin/rundler/src/cli/builder.rs +++ b/bin/rundler/src/cli/builder.rs @@ -286,7 +286,7 @@ impl BuilderArgs { priority_fee_mode, sender_args, eth_poll_interval: Duration::from_millis(common.eth_poll_interval_millis), - sim_settings: common.into(), + sim_settings: common.try_into()?, max_blocks_to_wait_for_mine: self.max_blocks_to_wait_for_mine, replacement_fee_percent_increase: self.replacement_fee_percent_increase, max_fee_increases: self.max_fee_increases, diff --git a/bin/rundler/src/cli/mod.rs b/bin/rundler/src/cli/mod.rs index 9e34f3054..9902dcf25 100644 --- a/bin/rundler/src/cli/mod.rs +++ b/bin/rundler/src/cli/mod.rs @@ -11,7 +11,7 @@ // You should have received a copy of the GNU General Public License along with Rundler. // If not, see https://www.gnu.org/licenses/. -use anyhow::Context; +use anyhow::{bail, Context}; use clap::{builder::PossibleValuesParser, Args, Parser, Subcommand}; mod builder; @@ -163,6 +163,17 @@ pub struct CommonArgs { )] min_unstake_delay: u32, + /// String representation of the timeout of a custom tracer in a format that is parsable by the + /// `ParseDuration` function on the ethereum node. See Docs: https://pkg.go.dev/time#ParseDuration + #[arg( + long = "tracer_timeout", + name = "tracer_timeout", + env = "TRACER_TIMEOUT", + default_value = "15s", + global = true + )] + tracer_timeout: String, + /// Amount of blocks to search when calling eth_getUserOperationByHash. /// Defaults from 0 to latest block #[arg( @@ -357,14 +368,21 @@ impl TryFrom<&CommonArgs> for PrecheckSettings { } } -impl From<&CommonArgs> for SimulationSettings { - fn from(value: &CommonArgs) -> Self { - Self::new( +impl TryFrom<&CommonArgs> for SimulationSettings { + type Error = anyhow::Error; + + fn try_from(value: &CommonArgs) -> Result { + if go_parse_duration::parse_duration(&value.tracer_timeout).is_err() { + bail!("Invalid value for tracer_timeout, must be parsable by the ParseDuration function. See docs https://pkg.go.dev/time#ParseDuration") + } + + Ok(Self::new( value.min_unstake_delay, value.min_stake_value, value.max_simulate_handle_ops_gas, value.max_verification_gas, - ) + value.tracer_timeout.clone(), + )) } } diff --git a/bin/rundler/src/cli/pool.rs b/bin/rundler/src/cli/pool.rs index 0f1728c08..53c6cf992 100644 --- a/bin/rundler/src/cli/pool.rs +++ b/bin/rundler/src/cli/pool.rs @@ -195,7 +195,7 @@ impl PoolArgs { blocklist: blocklist.clone(), allowlist: allowlist.clone(), precheck_settings: common.try_into()?, - sim_settings: common.into(), + sim_settings: common.try_into()?, throttled_entity_mempool_count: self.throttled_entity_mempool_count, throttled_entity_live_blocks: self.throttled_entity_live_blocks, paymaster_tracking_enabled: self.paymaster_tracking_enabled, diff --git a/crates/builder/src/task.rs b/crates/builder/src/task.rs index 91c41190b..27f7dbf01 100644 --- a/crates/builder/src/task.rs +++ b/crates/builder/src/task.rs @@ -267,7 +267,7 @@ where UnsafeSimulator::new( Arc::clone(&provider), ep_v0_6.clone(), - self.args.sim_settings, + self.args.sim_settings.clone(), ), ) .await? @@ -279,7 +279,7 @@ where simulation::new_v0_6_simulator( Arc::clone(&provider), ep_v0_6.clone(), - self.args.sim_settings, + self.args.sim_settings.clone(), ep.mempool_configs.clone(), ), ) @@ -316,7 +316,7 @@ where UnsafeSimulator::new( Arc::clone(&provider), ep_v0_7.clone(), - self.args.sim_settings, + self.args.sim_settings.clone(), ), ) .await? @@ -328,7 +328,7 @@ where simulation::new_v0_7_simulator( Arc::clone(&provider), ep_v0_7.clone(), - self.args.sim_settings, + self.args.sim_settings.clone(), ep.mempool_configs.clone(), ), ) diff --git a/crates/pool/src/task.rs b/crates/pool/src/task.rs index 78e17b86e..bfe905384 100644 --- a/crates/pool/src/task.rs +++ b/crates/pool/src/task.rs @@ -196,8 +196,11 @@ impl PoolTask { ); if unsafe_mode { - let simulator = - UnsafeSimulator::new(Arc::clone(&provider), ep.clone(), pool_config.sim_settings); + let simulator = UnsafeSimulator::new( + Arc::clone(&provider), + ep.clone(), + pool_config.sim_settings.clone(), + ); Self::create_mempool( chain_spec, pool_config, @@ -210,7 +213,7 @@ impl PoolTask { let simulator = simulation::new_v0_6_simulator( Arc::clone(&provider), ep.clone(), - pool_config.sim_settings, + pool_config.sim_settings.clone(), pool_config.mempool_channel_configs.clone(), ); Self::create_mempool( @@ -239,8 +242,11 @@ impl PoolTask { ); if unsafe_mode { - let simulator = - UnsafeSimulator::new(Arc::clone(&provider), ep.clone(), pool_config.sim_settings); + let simulator = UnsafeSimulator::new( + Arc::clone(&provider), + ep.clone(), + pool_config.sim_settings.clone(), + ); Self::create_mempool( chain_spec, pool_config, @@ -253,7 +259,7 @@ impl PoolTask { let simulator = simulation::new_v0_7_simulator( Arc::clone(&provider), ep.clone(), - pool_config.sim_settings, + pool_config.sim_settings.clone(), pool_config.mempool_channel_configs.clone(), ); Self::create_mempool( diff --git a/crates/sim/src/simulation/context.rs b/crates/sim/src/simulation/context.rs index c0e4b9cf0..d739a3199 100644 --- a/crates/sim/src/simulation/context.rs +++ b/crates/sim/src/simulation/context.rs @@ -124,7 +124,7 @@ pub(crate) fn infos_from_validation_output( sender_address: Address, paymaster_address: Option
, entry_point_out: &ValidationOutput, - sim_settings: Settings, + sim_settings: &Settings, ) -> EntityInfos { let mut ei = EntityInfos::default(); ei.set_sender( @@ -153,7 +153,7 @@ pub(crate) fn infos_from_validation_output( ei } -pub(crate) fn is_staked(info: StakeInfo, sim_settings: Settings) -> bool { +pub(crate) fn is_staked(info: StakeInfo, sim_settings: &Settings) -> bool { info.stake >= sim_settings.min_stake_value.into() && info.unstake_delay_sec >= sim_settings.min_unstake_delay.into() } diff --git a/crates/sim/src/simulation/mod.rs b/crates/sim/src/simulation/mod.rs index 9a963fe24..eb035d488 100644 --- a/crates/sim/src/simulation/mod.rs +++ b/crates/sim/src/simulation/mod.rs @@ -150,7 +150,7 @@ pub trait Simulator: Send + Sync + 'static { } /// Simulation Settings -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Clone)] pub struct Settings { /// The minimum amount of time that a staked entity must have configured as /// their unstake delay on the entry point contract in order to be considered staked. @@ -162,6 +162,9 @@ pub struct Settings { pub max_simulate_handle_ops_gas: u64, /// The maximum amount of verification gas that can be used during the simulation call pub max_verification_gas: u64, + /// The max duration of the custom javascript tracer. Must be in a format parseable by the + /// ParseDuration function on an ethereum node. See Docs: https://pkg.go.dev/time#ParseDuration + pub tracer_timeout: String, } impl Settings { @@ -171,12 +174,14 @@ impl Settings { min_stake_value: u128, max_simulate_handle_ops_gas: u64, max_verification_gas: u64, + tracer_timeout: String, ) -> Self { Self { min_unstake_delay, min_stake_value, max_simulate_handle_ops_gas, max_verification_gas, + tracer_timeout, } } } @@ -192,6 +197,7 @@ impl Default for Settings { // 550 million gas: currently the defaults for Alchemy eth_call max_simulate_handle_ops_gas: 550_000_000, max_verification_gas: 5_000_000, + tracer_timeout: "10s".to_string(), } } } diff --git a/crates/sim/src/simulation/simulator.rs b/crates/sim/src/simulation/simulator.rs index eef9f9201..e818b5257 100644 --- a/crates/sim/src/simulation/simulator.rs +++ b/crates/sim/src/simulation/simulator.rs @@ -63,7 +63,7 @@ where SimulatorImpl::new( provider.clone(), entry_point.clone(), - ValidationContextProviderV0_6::new(provider, entry_point, sim_settings), + ValidationContextProviderV0_6::new(provider, entry_point, sim_settings.clone()), sim_settings, mempool_configs, ) @@ -86,7 +86,7 @@ where SimulatorImpl::new( provider.clone(), entry_point.clone(), - ValidationContextProviderV0_7::new(provider, entry_point, sim_settings), + ValidationContextProviderV0_7::new(provider, entry_point, sim_settings.clone()), sim_settings, mempool_configs, ) @@ -345,7 +345,7 @@ where } if let Some(aggregator_info) = entry_point_out.aggregator_info { - if !context::is_staked(aggregator_info.stake_info, self.sim_settings) { + if !context::is_staked(aggregator_info.stake_info, &self.sim_settings) { // [EREP-040] violations.push(SimulationViolation::UnstakedAggregator) } @@ -514,7 +514,7 @@ where sender_info, .. } = entry_point_out; - let account_is_staked = context::is_staked(sender_info, self.sim_settings); + let account_is_staked = context::is_staked(sender_info, &self.sim_settings); let ValidationReturnInfo { pre_op_gas, valid_after, @@ -798,7 +798,7 @@ mod tests { paymaster_info: StakeInfo::from((U256::default(), U256::default())), aggregator_info: None, }, - Settings::default(), + &Settings::default(), ), tracer_out, entry_point_out: ValidationOutput { diff --git a/crates/sim/src/simulation/v0_6/context.rs b/crates/sim/src/simulation/v0_6/context.rs index e5e015b40..3cd621671 100644 --- a/crates/sim/src/simulation/v0_6/context.rs +++ b/crates/sim/src/simulation/v0_6/context.rs @@ -55,7 +55,7 @@ where let paymaster_address = op.paymaster(); let tracer_out = self .simulate_validation_tracer - .trace_simulate_validation(op.clone(), block_id, self.sim_settings.max_verification_gas) + .trace_simulate_validation(op.clone(), block_id) .await?; let num_phases = tracer_out.phases.len() as u32; // Check if there are too many phases here, then check too few at the @@ -107,7 +107,7 @@ where sender_address, paymaster_address, &entry_point_out, - self.sim_settings, + &self.sim_settings, ); let associated_addresses = tracer_out.associated_slots_by_address.addresses(); @@ -181,7 +181,12 @@ where /// Creates a new `ValidationContextProvider` for entry point v0.6 with the given provider and entry point. pub(crate) fn new(provider: Arc

, entry_point: E, sim_settings: SimulationSettings) -> Self { Self { - simulate_validation_tracer: SimulateValidationTracerImpl::new(provider, entry_point), + simulate_validation_tracer: SimulateValidationTracerImpl::new( + provider, + entry_point, + sim_settings.max_verification_gas, + sim_settings.tracer_timeout.clone(), + ), sim_settings, } } @@ -275,7 +280,6 @@ mod tests { &self, op: UserOperation, block_id: BlockId, - max_validation_gas: u64, ) -> anyhow::Result; } } @@ -284,19 +288,17 @@ mod tests { async fn test_create_context_two_phases_unintended_revert() { let mut tracer = MockTracer::new(); - tracer - .expect_trace_simulate_validation() - .returning(|_, _, _| { - let mut tracer_output = get_test_tracer_output(); - tracer_output.revert_data = Some(hex::encode( - FailedOp { - op_index: U256::from(100), - reason: "AA23 reverted (or OOG)".to_string(), - } - .encode(), - )); - Ok(tracer_output) - }); + tracer.expect_trace_simulate_validation().returning(|_, _| { + let mut tracer_output = get_test_tracer_output(); + tracer_output.revert_data = Some(hex::encode( + FailedOp { + op_index: U256::from(100), + reason: "AA23 reverted (or OOG)".to_string(), + } + .encode(), + )); + Ok(tracer_output) + }); let user_operation = UserOperation { sender: Address::from_str("b856dbd4fa1a79a46d426f537455e7d3e79ab7c4").unwrap(), diff --git a/crates/sim/src/simulation/v0_6/tracer.rs b/crates/sim/src/simulation/v0_6/tracer.rs index 154856667..b5f8aad2f 100644 --- a/crates/sim/src/simulation/v0_6/tracer.rs +++ b/crates/sim/src/simulation/v0_6/tracer.rs @@ -43,7 +43,6 @@ pub(super) trait SimulateValidationTracer: Send + Sync + 'static { &self, op: UserOperation, block_id: BlockId, - max_validation_gas: u64, ) -> anyhow::Result; } @@ -52,6 +51,8 @@ pub(super) trait SimulateValidationTracer: Send + Sync + 'static { pub(crate) struct SimulateValidationTracerImpl { provider: Arc

, entry_point: E, + max_validation_gas: u64, + tracer_timeout: String, } /// Runs the bundler's custom tracer on the entry point's `simulateValidation` @@ -67,11 +68,10 @@ where &self, op: UserOperation, block_id: BlockId, - max_validation_gas: u64, ) -> anyhow::Result { let (tx, state_override) = self .entry_point - .get_tracer_simulate_validation_call(op, max_validation_gas); + .get_tracer_simulate_validation_call(op, self.max_validation_gas); TracerOutput::try_from( self.provider @@ -83,6 +83,7 @@ where tracer: Some(GethDebugTracerType::JsTracer( validation_tracer_js().to_string(), )), + timeout: Some(self.tracer_timeout.clone()), ..Default::default() }, state_overrides: Some(state_override), @@ -95,10 +96,17 @@ where impl SimulateValidationTracerImpl { /// Creates a new instance of the bundler's custom tracer. - pub(crate) fn new(provider: Arc

, entry_point: E) -> Self { + pub(crate) fn new( + provider: Arc

, + entry_point: E, + max_validation_gas: u64, + tracer_timeout: String, + ) -> Self { Self { provider, entry_point, + max_validation_gas, + tracer_timeout, } } } diff --git a/crates/sim/src/simulation/v0_7/context.rs b/crates/sim/src/simulation/v0_7/context.rs index 98dbcc711..348f843c2 100644 --- a/crates/sim/src/simulation/v0_7/context.rs +++ b/crates/sim/src/simulation/v0_7/context.rs @@ -101,7 +101,7 @@ where ) -> Result, ViolationError> { let tracer_out = self .simulate_validation_tracer - .trace_simulate_validation(op.clone(), block_id, self.sim_settings.max_verification_gas) + .trace_simulate_validation(op.clone(), block_id) .await?; let call_stack = self.parse_call_stack(tracer_out.calls.clone())?; @@ -133,7 +133,7 @@ where op.sender(), op.paymaster(), &entry_point_out, - self.sim_settings, + &self.sim_settings, ); let mut tracer_out = self.parse_tracer_out(&op, tracer_out)?; @@ -489,7 +489,12 @@ where pub(crate) fn new(provider: Arc

, entry_point: E, sim_settings: SimulationSettings) -> Self { Self { entry_point_address: entry_point.address(), - simulate_validation_tracer: SimulateValidationTracerImpl::new(provider, entry_point), + simulate_validation_tracer: SimulateValidationTracerImpl::new( + provider, + entry_point, + sim_settings.max_verification_gas, + sim_settings.tracer_timeout.clone(), + ), sim_settings, } } diff --git a/crates/sim/src/simulation/v0_7/tracer.rs b/crates/sim/src/simulation/v0_7/tracer.rs index 545d86c5b..2eb10545c 100644 --- a/crates/sim/src/simulation/v0_7/tracer.rs +++ b/crates/sim/src/simulation/v0_7/tracer.rs @@ -125,7 +125,6 @@ pub(super) trait SimulateValidationTracer: Send + Sync + 'static { &self, op: UserOperation, block_id: BlockId, - max_validation_gas: u64, ) -> anyhow::Result; } @@ -134,6 +133,8 @@ pub(super) trait SimulateValidationTracer: Send + Sync + 'static { pub(crate) struct SimulateValidationTracerImpl { provider: Arc

, entry_point: E, + max_validation_gas: u64, + tracer_timeout: String, } /// Runs the bundler's custom tracer on the entry point's `simulateValidation` @@ -149,11 +150,10 @@ where &self, op: UserOperation, block_id: BlockId, - max_validation_gas: u64, ) -> anyhow::Result { let (tx, state_override) = self .entry_point - .get_tracer_simulate_validation_call(op, max_validation_gas); + .get_tracer_simulate_validation_call(op, self.max_validation_gas); let out = self .provider @@ -165,6 +165,7 @@ where tracer: Some(GethDebugTracerType::JsTracer( validation_tracer_js().to_string(), )), + timeout: Some(self.tracer_timeout.clone()), ..Default::default() }, state_overrides: Some(state_override), @@ -178,10 +179,17 @@ where impl SimulateValidationTracerImpl { /// Creates a new instance of the bundler's custom tracer. - pub(crate) fn new(provider: Arc

, entry_point: E) -> Self { + pub(crate) fn new( + provider: Arc

, + entry_point: E, + max_validation_gas: u64, + tracer_timeout: String, + ) -> Self { Self { provider, entry_point, + max_validation_gas, + tracer_timeout, } } } diff --git a/docs/cli.md b/docs/cli.md index 6249d3b02..3ac1402b7 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -73,6 +73,8 @@ See [chain spec](./architecture/chain_spec.md) for a detailed description of cha - env: *DISABLE_ENTRY_POINT_V0_7* - `--num_builders_v0_7`: The number of bundle builders to run on entry point v0.7 (default: `1`) - env: *NUM_BUILDERS_V0_7* +- `--tracer_timeout`: The timeout used for custom javascript tracers, the string must be in a valid parseable format that can be used in the `ParseDuration` function on an ethereum node. See Docs [Here](https://pkg.go.dev/time#ParseDuration). (default: `15s`) + - env: *TRACER_TIMEOUT* ## Metrics Options