Skip to content

Commit

Permalink
fix(provider): use state overrides for gas used, not ctor method
Browse files Browse the repository at this point in the history
  • Loading branch information
dancoombs committed May 10, 2024
1 parent 58f31f4 commit c1dc6a9
Show file tree
Hide file tree
Showing 17 changed files with 189 additions and 103 deletions.
2 changes: 1 addition & 1 deletion bin/rundler/chain_specs/arbitrum.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ l1_gas_oracle_contract_type = "ARBITRUM_NITRO"
l1_gas_oracle_contract_address = "0x00000000000000000000000000000000000000C8"

supports_eip1559 = false
max_bundle_size_bytes = 95000
max_transaction_size_bytes = 95000
2 changes: 1 addition & 1 deletion bin/rundler/chain_specs/base.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ include_l1_gas_in_gas_limit = false

priority_fee_oracle_type = "USAGE_BASED"
min_max_priority_fee_per_gas = "0x0186A0" # 100_000
max_bundle_size_bytes = 130000
max_transaction_size_bytes = 130000
2 changes: 1 addition & 1 deletion bin/rundler/chain_specs/optimism.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ include_l1_gas_in_gas_limit = false

priority_fee_oracle_type = "USAGE_BASED"
min_max_priority_fee_per_gas = "0x0186A0" # 100_000
max_bundle_size_bytes = 90000
max_transaction_size_bytes = 90000
2 changes: 1 addition & 1 deletion bin/rundler/chain_specs/polygon.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ id = 137
priority_fee_oracle_type = "USAGE_BASED"
min_max_priority_fee_per_gas = "0x06FC23AC00" # 30_000_000_000
bloxroute_enabled = true
max_bundle_size_bytes = 300000
max_transaction_size_bytes = 300000
23 changes: 4 additions & 19 deletions crates/builder/src/bundle_proposer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ use rundler_types::{
chain::ChainSpec,
pool::{Pool, PoolOperation, SimulationViolation},
Entity, EntityInfo, EntityInfos, EntityType, EntityUpdate, EntityUpdateType, GasFees,
Timestamp, UserOperation, UserOperationVariant, UserOpsPerAggregator,
Timestamp, UserOperation, UserOperationVariant, UserOpsPerAggregator, BUNDLE_BYTE_OVERHEAD,
USER_OP_OFFSET_WORD_SIZE,
};
use rundler_utils::{emit::WithEntryPoint, math};
use tokio::{sync::broadcast, try_join};
Expand All @@ -53,19 +54,6 @@ const TIME_RANGE_BUFFER: Duration = Duration::from_secs(60);
/// Extra buffer percent to add on the bundle transaction gas estimate to be sure it will be enough
const BUNDLE_TRANSACTION_GAS_OVERHEAD_PERCENT: u64 = 5;

/// Overhead for bytes required for each bundle
/// 4 bytes for function signature
/// 32 bytes for user op array offset
/// 32 bytes for beneficiary
/// 32 bytes for array count
/// Ontop of this offset there needs to be another 32 bytes for each
/// user operation in the bundle to store its offset within the array
const BUNDLE_BYTE_OVERHEAD: u64 = 4 + 32 + 32 + 32;

/// Size of word that stores offset of user op location
/// within handleOps `ops` array
const USER_OP_OFFSET_WORD_SIZE: u64 = 32;

#[derive(Debug)]
pub(crate) struct Bundle<UO: UserOperation> {
pub(crate) ops_per_aggregator: Vec<UserOpsPerAggregator<UO>>,
Expand Down Expand Up @@ -442,15 +430,12 @@ where
continue;
}

let op_size_bytes: u64 = op
.abi_encoded_size()
.try_into()
.expect("User operation size should fit within u64");
let op_size_bytes: usize = op.abi_encoded_size();

let op_size_with_offset_word = op_size_bytes.saturating_add(USER_OP_OFFSET_WORD_SIZE);

if op_size_with_offset_word.saturating_add(constructed_bundle_size)
>= self.settings.chain_spec.max_bundle_size_bytes
>= self.settings.chain_spec.max_transaction_size_bytes
{
continue;
}
Expand Down
25 changes: 25 additions & 0 deletions crates/provider/src/ethers/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ use ethers::{
},
};
use reqwest::Url;
use rundler_types::contracts::utils::get_gas_used::{
GasUsedResult, GetGasUsed, GETGASUSED_DEPLOYED_BYTECODE,
};
use serde::{de::DeserializeOwned, Serialize};

use super::metrics_middleware::MetricsMiddleware;
Expand Down Expand Up @@ -182,6 +185,28 @@ impl<C: JsonRpcClient + 'static> Provider for EthersProvider<C> {
async fn get_transaction_count(&self, address: Address) -> ProviderResult<U256> {
Ok(Middleware::get_transaction_count(self, address, None).await?)
}

async fn get_gas_used(
self: &Arc<Self>,
target: Address,
value: U256,
data: Bytes,
mut state_overrides: spoof::State,
) -> ProviderResult<GasUsedResult> {
let helper_addr = Address::random();
let helper = GetGasUsed::new(helper_addr, Arc::clone(self));

state_overrides
.account(helper_addr)
.code(GETGASUSED_DEPLOYED_BYTECODE.clone());

Ok(helper
.get_gas(target, value, data)
.call_raw()
.state(&state_overrides)
.await
.context("should get gas used")?)
}
}

impl From<EthersProviderError> for ProviderError {
Expand Down
12 changes: 11 additions & 1 deletion crates/provider/src/traits/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

//! Trait for interacting with chain data and contracts.

use std::fmt::Debug;
use std::{fmt::Debug, sync::Arc};

use ethers::{
abi::{AbiDecode, AbiEncode},
Expand All @@ -25,6 +25,7 @@ use ethers::{
};
#[cfg(feature = "test-utils")]
use mockall::automock;
use rundler_types::contracts::utils::get_gas_used::GasUsedResult;
use serde::{de::DeserializeOwned, Serialize};

use super::error::ProviderError;
Expand Down Expand Up @@ -127,4 +128,13 @@ pub trait Provider: Send + Sync + Debug + 'static {

/// Get the logs matching a filter
async fn get_logs(&self, filter: &Filter) -> ProviderResult<Vec<Log>>;

/// Measures the gas used by a call to target with value and data.
async fn get_gas_used(
self: &Arc<Self>,
target: Address,
value: U256,
data: Bytes,
state_overrides: spoof::State,
) -> ProviderResult<GasUsedResult>;
}
24 changes: 21 additions & 3 deletions crates/rpc/src/eth/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ use ethers::{
utils::to_checksum,
};
use futures_util::future;
use rundler_types::{chain::ChainSpec, pool::Pool, UserOperationOptionalGas, UserOperationVariant};
use rundler_types::{
chain::ChainSpec, pool::Pool, UserOperation, UserOperationOptionalGas, UserOperationVariant,
};
use rundler_utils::log::LogOnError;
use tracing::Level;

Expand Down Expand Up @@ -67,6 +69,14 @@ where
op: UserOperationVariant,
entry_point: Address,
) -> EthResult<H256> {
let bundle_size = op.single_uo_bundle_size_bytes();
if bundle_size > self.chain_spec.max_transaction_size_bytes {
return Err(EthRpcError::InvalidParams(format!(
"User operation in bundle size {} exceeds max transaction size {}",
bundle_size, self.chain_spec.max_transaction_size_bytes
)));
}

self.router.check_and_get_route(&entry_point, &op)?;

self.pool
Expand All @@ -78,12 +88,20 @@ where

pub(crate) async fn estimate_user_operation_gas(
&self,
uo: UserOperationOptionalGas,
op: UserOperationOptionalGas,
entry_point: Address,
state_override: Option<spoof::State>,
) -> EthResult<RpcGasEstimate> {
let bundle_size = op.single_uo_bundle_size_bytes();
if bundle_size > self.chain_spec.max_transaction_size_bytes {
return Err(EthRpcError::InvalidParams(format!(
"User operation in bundle size {} exceeds max transaction size {}",
bundle_size, self.chain_spec.max_transaction_size_bytes
)));
}

self.router
.estimate_gas(&entry_point, uo, state_override)
.estimate_gas(&entry_point, op, state_override)
.await
}

Expand Down
7 changes: 7 additions & 0 deletions crates/rpc/src/task.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,13 @@ where
.set_logger(RpcMetricsLogger)
.set_middleware(service_builder)
.max_connections(self.args.max_connections)
// Set max request body size to 2x the max transaction size as none of our
// APIs should require more than that.
.max_request_body_size(
(self.args.chain_spec.max_transaction_size_bytes * 2)
.try_into()
.expect("max_transaction_size_bytes * 2 overflowed u32"),
)
.http_only()
.build(addr)
.await?;
Expand Down
24 changes: 13 additions & 11 deletions crates/sim/src/estimation/estimate_verification_gas.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{ops::Deref, sync::Arc};
use std::sync::Arc;

use anyhow::{anyhow, Context};
use async_trait::async_trait;
Expand All @@ -7,7 +7,7 @@ use rundler_provider::{EntryPoint, Provider, SimulateOpCallData, SimulationProvi
use rundler_types::{chain::ChainSpec, UserOperation};

use super::Settings;
use crate::{utils, GasEstimationError};
use crate::GasEstimationError;

/// Gas estimation will stop when the binary search bounds are within
/// `GAS_ESTIMATION_ERROR_MARGIN` of each other.
Expand Down Expand Up @@ -111,15 +111,17 @@ where
} = self
.entry_point
.get_simulate_op_call_data(initial_op, state_override);
let gas_used = utils::get_gas_used(
self.provider.deref(),
self.entry_point.address(),
U256::zero(),
call_data,
&spoofed_state,
)
.await
.context("failed to run initial guess")?;
let gas_used = self
.provider
.get_gas_used(
self.entry_point.address(),
U256::zero(),
call_data,
spoofed_state.clone(),
)
.await
.context("failed to run initial guess")?;

if gas_used.success {
if self.entry_point.simulation_should_revert() {
Err(anyhow!(
Expand Down
60 changes: 29 additions & 31 deletions crates/sim/src/estimation/v0_6.rs
Original file line number Diff line number Diff line change
Expand Up @@ -698,15 +698,15 @@ mod tests {
}))
});

provider.expect_call_constructor().returning(
move |_a, _b: (Address, U256, Bytes), _c, _d| {
provider
.expect_get_gas_used()
.returning(move |_a, _b, _c, _d| {
Ok(GasUsedResult {
gas_used: gas_usage * 2,
success: false,
result: Bytes::new(),
})
},
);
});

let (estimator, _) = create_estimator(entry, provider);
let optional_op = demo_user_op_optional_gas(Some(U256::from(10000)));
Expand Down Expand Up @@ -757,15 +757,15 @@ mod tests {

// this gas used number is larger than a u64 max number so we need to
// check for this overflow
provider.expect_call_constructor().returning(
move |_a, _b: (Address, U256, Bytes), _c, _d| {
provider
.expect_get_gas_used()
.returning(move |_a, _b, _c, _d| {
Ok(GasUsedResult {
gas_used: U256::from(18446744073709551616_u128),
success: false,
result: Bytes::new(),
})
},
);
});

let (estimator, _) = create_estimator(entry, provider);
let optional_op = demo_user_op_optional_gas(Some(U256::from(10000)));
Expand Down Expand Up @@ -814,15 +814,15 @@ mod tests {

// the success field should not be true as the
// call should always revert
provider.expect_call_constructor().returning(
move |_a, _b: (Address, U256, Bytes), _c, _d| {
provider
.expect_get_gas_used()
.returning(move |_a, _b, _c, _d| {
Ok(GasUsedResult {
gas_used: U256::from(20000),
success: true,
result: Bytes::new(),
})
},
);
});

let (estimator, _) = create_estimator(entry, provider);
let optional_op = demo_user_op_optional_gas(Some(U256::from(10000)));
Expand Down Expand Up @@ -861,15 +861,15 @@ mod tests {
}))
});

provider.expect_call_constructor().returning(
move |_a, _b: (Address, U256, Bytes), _c, _d| {
provider
.expect_get_gas_used()
.returning(move |_a, _b, _c, _d| {
Ok(GasUsedResult {
gas_used: U256::from(20000),
success: false,
result: Bytes::new(),
})
},
);
});

let (estimator, _) = create_estimator(entry, provider);
let optional_op = demo_user_op_optional_gas(Some(U256::from(10000)));
Expand Down Expand Up @@ -903,15 +903,15 @@ mod tests {
.expect_call_spoofed_simulate_op()
.returning(|_a, _b, _c, _d, _e, _f| Err(anyhow!("Invalid spoof error")));

provider.expect_call_constructor().returning(
move |_a, _b: (Address, U256, Bytes), _c, _d| {
provider
.expect_get_gas_used()
.returning(move |_a, _b, _c, _d| {
Ok(GasUsedResult {
gas_used: U256::from(20000),
success: false,
result: Bytes::new(),
})
},
);
});

let (estimator, _) = create_estimator(entry, provider);
let optional_op = demo_user_op_optional_gas(Some(U256::from(10000)));
Expand Down Expand Up @@ -956,13 +956,11 @@ mod tests {
}))
});

provider.expect_call_constructor().returning(
move |_a, _b: (Address, U256, Bytes), _c, _d| {
let ret: Result<GasUsedResult, anyhow::Error> =
Err(anyhow::anyhow!("This should always revert"));
ret
},
);
provider
.expect_get_gas_used()
.returning(move |_a, _b, _c, _d| {
Err(anyhow::anyhow!("This should always revert").into())
});

let (estimator, _) = create_estimator(entry, provider);
let optional_op = demo_user_op_optional_gas(Some(U256::from(10000)));
Expand Down Expand Up @@ -1146,15 +1144,15 @@ mod tests {
provider
.expect_get_latest_block_hash_and_number()
.returning(|| Ok((H256::zero(), U64::zero())));
provider.expect_call_constructor().returning(
move |_a, _b: (Address, U256, Bytes), _c, _d| {
provider
.expect_get_gas_used()
.returning(move |_a, _b, _c, _d| {
Ok(GasUsedResult {
gas_used: gas_usage,
success: false,
result: Bytes::new(),
})
},
);
});

provider.expect_get_base_fee().returning(|| Ok(TEST_FEE));
provider
Expand Down
Loading

0 comments on commit c1dc6a9

Please sign in to comment.