diff --git a/crates/executor/src/estimate.rs b/crates/executor/src/estimate.rs index fd070da001..f9e63296f2 100644 --- a/crates/executor/src/estimate.rs +++ b/crates/executor/src/estimate.rs @@ -1,6 +1,7 @@ use blockifier::transaction::objects::TransactionExecutionInfo; use blockifier::transaction::transaction_execution::Transaction; use starknet_api::transaction::fields::GasVectorComputationMode; +use util::percentage::PercentageInt; use super::error::TransactionExecutionError; use super::execution_state::ExecutionState; @@ -15,6 +16,7 @@ use crate::transaction::{ pub fn estimate( execution_state: ExecutionState<'_>, transactions: Vec, + epsilon: PercentageInt, ) -> Result, TransactionExecutionError> { let block_number = execution_state.header.number; @@ -44,6 +46,7 @@ pub fn estimate( tx_index, &mut state, &block_context, + epsilon, )? } else { execute_transaction(&tx, tx_index, &mut state, &block_context)? diff --git a/crates/executor/src/simulate.rs b/crates/executor/src/simulate.rs index fed70a18ed..54d783e4de 100644 --- a/crates/executor/src/simulate.rs +++ b/crates/executor/src/simulate.rs @@ -20,6 +20,7 @@ use pathfinder_common::{ TransactionHash, }; use starknet_api::transaction::fields::GasVectorComputationMode; +use util::percentage::PercentageInt; use super::error::TransactionExecutionError; use super::execution_state::ExecutionState; @@ -86,6 +87,7 @@ impl Default for TraceCache { pub fn simulate( execution_state: ExecutionState<'_>, transactions: Vec, + epsilon: PercentageInt, ) -> Result, TransactionExecutionError> { let block_number = execution_state.header.number; @@ -116,6 +118,7 @@ pub fn simulate( tx_index, &mut tx_state, &block_context, + epsilon, )? } else { execute_transaction(&tx, tx_index, &mut tx_state, &block_context)? diff --git a/crates/executor/src/transaction.rs b/crates/executor/src/transaction.rs index 2d39626309..3b9ed2a6b4 100644 --- a/crates/executor/src/transaction.rs +++ b/crates/executor/src/transaction.rs @@ -141,6 +141,7 @@ pub(crate) fn find_l2_gas_limit_and_execute_transaction( tx_index: usize, state: &mut S, block_context: &blockifier::context::BlockContext, + epsilon: PercentageInt, ) -> Result where S: UpdatableState, @@ -162,7 +163,6 @@ where }; let GasAmount(l2_gas_consumed) = tx_info.receipt.gas.l2_gas; - let epsilon = PercentageInt::new(10); // Add a buffer (in terms of %) to the actual L2 gas fee. let l2_gas_adjusted = GasAmount(l2_gas_consumed.saturating_add(epsilon.of(l2_gas_consumed))); diff --git a/crates/pathfinder/examples/re_execute.rs b/crates/pathfinder/examples/re_execute.rs index 805bc196df..c29866424f 100644 --- a/crates/pathfinder/examples/re_execute.rs +++ b/crates/pathfinder/examples/re_execute.rs @@ -8,6 +8,7 @@ use pathfinder_executor::ExecutionState; use pathfinder_rpc::context::{ETH_FEE_TOKEN_ADDRESS, STRK_FEE_TOKEN_ADDRESS}; use pathfinder_storage::{BlockId, Storage}; use rayon::prelude::*; +use util::percentage::PercentageInt; // The Cairo VM allocates felts on the stack, so during execution it's making // a huge number of allocations. We get roughly two times better execution @@ -157,7 +158,7 @@ fn execute(storage: &mut Storage, chain_id: ChainId, work: Work) { } }; - match pathfinder_executor::simulate(execution_state, transactions) { + match pathfinder_executor::simulate(execution_state, transactions, PercentageInt::new(10)) { Ok(simulations) => { for (simulation, (receipt, transaction)) in simulations .iter() diff --git a/crates/pathfinder/src/bin/pathfinder/config.rs b/crates/pathfinder/src/bin/pathfinder/config.rs index af1d563995..8f8de9d9e3 100644 --- a/crates/pathfinder/src/bin/pathfinder/config.rs +++ b/crates/pathfinder/src/bin/pathfinder/config.rs @@ -14,6 +14,7 @@ use pathfinder_common::AllowedOrigins; use pathfinder_executor::VersionedConstants; use pathfinder_storage::JournalMode; use reqwest::Url; +use util::percentage::PercentageInt; #[derive(Parser)] #[command(name = "Pathfinder")] @@ -324,6 +325,17 @@ This should only be enabled for debugging purposes as it adds substantial proces default_value = "10" )] shutdown_grace_period: std::num::NonZeroU64, + + #[arg( + long = "rpc.fee-estimation-epsilon", + value_name = "Percentage", + long_help = "Acceptable overhead to add on top of consumed L2 gas (g) during fee estimation (`estimateFee` and `simulate` RPC methods). \ + Setting a lower value gives a more precise fee estimation (in terms of L2 gas) but runs a higher risk of having to resort to a binary search if the initial L2 gas limit (`g + (g * EPSILON/100)`) is insufficient.", + env = "PATHFINDER_RPC_FEE_ESTIMATION_EPSILON", + default_value = "10", + value_parser = parse_fee_estimation_epsilon + )] + fee_estimation_epsilon: PercentageInt, } #[derive(clap::ValueEnum, Debug, Clone, Copy, PartialEq)] @@ -372,6 +384,14 @@ fn parse_state_tries(s: &str) -> Result { } } +fn parse_fee_estimation_epsilon(s: &str) -> Result { + let value: u8 = s + .parse() + .map_err(|_| "Expected a number between 0 and 100".to_string())?; + + Ok(PercentageInt::new(value)) +} + #[derive(clap::Args)] struct NetworkCli { #[arg( @@ -735,6 +755,7 @@ pub struct Config { pub feeder_gateway_fetch_concurrency: NonZeroUsize, pub fetch_casm_from_fgw: bool, pub shutdown_grace_period: Duration, + pub fee_estimation_epsilon: PercentageInt, } pub struct Ethereum { @@ -1030,6 +1051,7 @@ impl Config { .map(parse_versioned_constants_or_exit), fetch_casm_from_fgw: cli.fetch_casm_from_fgw, shutdown_grace_period: Duration::from_secs(cli.shutdown_grace_period.get()), + fee_estimation_epsilon: cli.fee_estimation_epsilon, } } } diff --git a/crates/pathfinder/src/bin/pathfinder/main.rs b/crates/pathfinder/src/bin/pathfinder/main.rs index 119a44e3c3..e021cb26a6 100644 --- a/crates/pathfinder/src/bin/pathfinder/main.rs +++ b/crates/pathfinder/src/bin/pathfinder/main.rs @@ -226,6 +226,7 @@ Hint: This is usually caused by exceeding the file descriptor limit of your syst get_events_max_blocks_to_scan: config.get_events_max_blocks_to_scan, get_events_max_uncached_event_filters_to_load: config .get_events_max_uncached_event_filters_to_load, + fee_estimation_epsilon: config.fee_estimation_epsilon, custom_versioned_constants: config.custom_versioned_constants.take(), }; diff --git a/crates/rpc/src/context.rs b/crates/rpc/src/context.rs index 96c180c2fe..5d3e76edd5 100644 --- a/crates/rpc/src/context.rs +++ b/crates/rpc/src/context.rs @@ -6,6 +6,7 @@ use pathfinder_ethereum::EthereumClient; use pathfinder_executor::{TraceCache, VersionedConstants}; use pathfinder_storage::Storage; use primitive_types::{H160, H256}; +use util::percentage::PercentageInt; pub use crate::jsonrpc::websocket::WebsocketContext; use crate::jsonrpc::Notifications; @@ -68,6 +69,7 @@ pub struct RpcConfig { pub batch_concurrency_limit: NonZeroUsize, pub get_events_max_blocks_to_scan: NonZeroUsize, pub get_events_max_uncached_event_filters_to_load: NonZeroUsize, + pub fee_estimation_epsilon: PercentageInt, pub custom_versioned_constants: Option, } @@ -169,6 +171,7 @@ impl RpcContext { batch_concurrency_limit: NonZeroUsize::new(8).unwrap(), get_events_max_blocks_to_scan: NonZeroUsize::new(1000).unwrap(), get_events_max_uncached_event_filters_to_load: NonZeroUsize::new(1000).unwrap(), + fee_estimation_epsilon: PercentageInt::new(10), custom_versioned_constants: None, }; diff --git a/crates/rpc/src/jsonrpc/router/subscription.rs b/crates/rpc/src/jsonrpc/router/subscription.rs index b34376581f..6513d67cb5 100644 --- a/crates/rpc/src/jsonrpc/router/subscription.rs +++ b/crates/rpc/src/jsonrpc/router/subscription.rs @@ -1027,6 +1027,7 @@ mod tests { batch_concurrency_limit: 1.try_into().unwrap(), get_events_max_blocks_to_scan: 1.try_into().unwrap(), get_events_max_uncached_event_filters_to_load: 1.try_into().unwrap(), + fee_estimation_epsilon: Default::default(), custom_versioned_constants: None, }, }; diff --git a/crates/rpc/src/method/estimate_fee.rs b/crates/rpc/src/method/estimate_fee.rs index 53c9ce2f1f..947880a269 100644 --- a/crates/rpc/src/method/estimate_fee.rs +++ b/crates/rpc/src/method/estimate_fee.rs @@ -104,7 +104,11 @@ pub async fn estimate_fee(context: RpcContext, input: Input) -> Result, _>>()?; - let result = pathfinder_executor::estimate(state, transactions)?; + let result = pathfinder_executor::estimate( + state, + transactions, + context.config.fee_estimation_epsilon, + )?; Ok::<_, EstimateFeeError>(result) }) diff --git a/crates/rpc/src/method/estimate_message_fee.rs b/crates/rpc/src/method/estimate_message_fee.rs index 15c77eeb2b..e77334c32d 100644 --- a/crates/rpc/src/method/estimate_message_fee.rs +++ b/crates/rpc/src/method/estimate_message_fee.rs @@ -110,7 +110,11 @@ pub async fn estimate_message_fee( let transaction = create_executor_transaction(input, context.chain_id)?; - let result = pathfinder_executor::estimate(state, vec![transaction])?; + let result = pathfinder_executor::estimate( + state, + vec![transaction], + context.config.fee_estimation_epsilon, + )?; Ok::<_, EstimateMessageFeeError>(result) }) diff --git a/crates/rpc/src/method/simulate_transactions.rs b/crates/rpc/src/method/simulate_transactions.rs index 1e92813090..3479e90d02 100644 --- a/crates/rpc/src/method/simulate_transactions.rs +++ b/crates/rpc/src/method/simulate_transactions.rs @@ -100,7 +100,11 @@ pub async fn simulate_transactions( }) .collect::, _>>()?; - let txs = pathfinder_executor::simulate(state, transactions)?; + let txs = pathfinder_executor::simulate( + state, + transactions, + context.config.fee_estimation_epsilon, + )?; Ok(Output(txs)) }) .await diff --git a/crates/rpc/src/method/subscribe_events.rs b/crates/rpc/src/method/subscribe_events.rs index c3dee172d0..4079caaee5 100644 --- a/crates/rpc/src/method/subscribe_events.rs +++ b/crates/rpc/src/method/subscribe_events.rs @@ -743,6 +743,7 @@ mod tests { batch_concurrency_limit: 64.try_into().unwrap(), get_events_max_blocks_to_scan: 1024.try_into().unwrap(), get_events_max_uncached_event_filters_to_load: 1.try_into().unwrap(), + fee_estimation_epsilon: Default::default(), custom_versioned_constants: None, }, }; diff --git a/crates/rpc/src/method/subscribe_new_heads.rs b/crates/rpc/src/method/subscribe_new_heads.rs index 35c8160c43..2d679f854a 100644 --- a/crates/rpc/src/method/subscribe_new_heads.rs +++ b/crates/rpc/src/method/subscribe_new_heads.rs @@ -544,6 +544,7 @@ mod tests { batch_concurrency_limit: 1.try_into().unwrap(), get_events_max_blocks_to_scan: 1.try_into().unwrap(), get_events_max_uncached_event_filters_to_load: 1.try_into().unwrap(), + fee_estimation_epsilon: Default::default(), custom_versioned_constants: None, }, }; diff --git a/crates/rpc/src/method/subscribe_pending_transactions.rs b/crates/rpc/src/method/subscribe_pending_transactions.rs index 28a0833d14..b1e505aa2b 100644 --- a/crates/rpc/src/method/subscribe_pending_transactions.rs +++ b/crates/rpc/src/method/subscribe_pending_transactions.rs @@ -494,6 +494,7 @@ mod tests { batch_concurrency_limit: 1.try_into().unwrap(), get_events_max_blocks_to_scan: 1.try_into().unwrap(), get_events_max_uncached_event_filters_to_load: 1.try_into().unwrap(), + fee_estimation_epsilon: Default::default(), custom_versioned_constants: None, }, }; diff --git a/crates/rpc/src/method/subscribe_transaction_status.rs b/crates/rpc/src/method/subscribe_transaction_status.rs index 2ed76b0c7b..15026e7cda 100644 --- a/crates/rpc/src/method/subscribe_transaction_status.rs +++ b/crates/rpc/src/method/subscribe_transaction_status.rs @@ -1156,6 +1156,7 @@ mod tests { batch_concurrency_limit: 1.try_into().unwrap(), get_events_max_blocks_to_scan: 1.try_into().unwrap(), get_events_max_uncached_event_filters_to_load: 1.try_into().unwrap(), + fee_estimation_epsilon: Default::default(), custom_versioned_constants: None, }, };