Skip to content

Commit

Permalink
feat(sim): check total gas limit after estimation
Browse files Browse the repository at this point in the history
  • Loading branch information
dancoombs committed Jun 24, 2024
1 parent 3de6345 commit 4d79ad3
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 6 deletions.
1 change: 1 addition & 0 deletions bin/rundler/src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,7 @@ impl TryFrom<&CommonArgs> for EstimationSettings {
max_call_gas,
max_paymaster_verification_gas: value.max_verification_gas,
max_paymaster_post_op_gas: max_call_gas,
max_total_execution_gas: value.max_bundle_gas,
max_simulate_handle_ops_gas: value.max_simulate_handle_ops_gas,
verification_estimation_gas_fee: value.verification_estimation_gas_fee,
})
Expand Down
3 changes: 3 additions & 0 deletions crates/rpc/src/eth/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,9 @@ impl From<GasEstimationError> for EthRpcError {
error @ GasEstimationError::GasUsedTooLarge => {
Self::EntryPointValidationRejected(error.to_string())
}
error @ GasEstimationError::GasTotalTooLarge(_, _) => {
Self::InvalidParams(error.to_string())
}
error @ GasEstimationError::GasFieldTooLarge(_, _) => {
Self::InvalidParams(error.to_string())
}
Expand Down
5 changes: 5 additions & 0 deletions crates/sim/src/estimation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ pub enum GasEstimationError {
/// Supplied gas was too large
#[error("{0} cannot be larger than {1}")]
GasFieldTooLarge(&'static str, u64),
/// The total amount of gas used by the UO is greater than allowed
#[error("total gas used by the user operation {0} is greater than the allowed limit: {1}")]
GasTotalTooLarge(u64, u64),
/// Other error
#[error(transparent)]
Other(#[from] anyhow::Error),
Expand Down Expand Up @@ -86,6 +89,8 @@ pub struct Settings {
pub max_paymaster_verification_gas: u64,
/// The maximum amount of gas that can be used for the paymaster post op step of a user operation
pub max_paymaster_post_op_gas: u64,
/// The maximum amount of total execution gas to check after estimation
pub max_total_execution_gas: u64,
/// The maximum amount of gas that can be used in a call to `simulateHandleOps`
pub max_simulate_handle_ops_gas: u64,
/// The gas fee to use during verification gas estimation, required to be held by the fee-payer
Expand Down
59 changes: 59 additions & 0 deletions crates/sim/src/estimation/v0_6.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,19 @@ where
let verification_gas_limit = verification_gas_limit?;
let call_gas_limit = call_gas_limit?;

// Verify total gas limit
let mut op_with_gas = full_op;
op_with_gas.verification_gas_limit = verification_gas_limit;
op_with_gas.call_gas_limit = call_gas_limit;
let gas_limit =
gas::user_operation_execution_gas_limit(&self.chain_spec, &op_with_gas, true);
if gas_limit > self.settings.max_total_execution_gas.into() {
return Err(GasEstimationError::GasTotalTooLarge(
gas_limit.as_u64(),
self.settings.max_total_execution_gas,
));
}

Ok(GasEstimate {
pre_verification_gas,
verification_gas_limit,
Expand Down Expand Up @@ -524,6 +537,7 @@ mod tests {
max_call_gas: TEST_MAX_GAS_LIMITS,
max_paymaster_verification_gas: TEST_MAX_GAS_LIMITS,
max_paymaster_post_op_gas: TEST_MAX_GAS_LIMITS,
max_total_execution_gas: TEST_MAX_GAS_LIMITS,
max_simulate_handle_ops_gas: TEST_MAX_GAS_LIMITS,
verification_estimation_gas_fee: 1_000_000_000_000,
};
Expand Down Expand Up @@ -617,6 +631,7 @@ mod tests {
max_call_gas: 10000000000,
max_paymaster_verification_gas: 10000000000,
max_paymaster_post_op_gas: 10000000000,
max_total_execution_gas: 10000000000,
max_simulate_handle_ops_gas: 100000000,
verification_estimation_gas_fee: 1_000_000_000_000,
};
Expand Down Expand Up @@ -684,6 +699,7 @@ mod tests {
max_call_gas: 10000000000,
max_paymaster_verification_gas: 10000000000,
max_paymaster_post_op_gas: 10000000000,
max_total_execution_gas: 10000000000,
max_simulate_handle_ops_gas: 100000000,
verification_estimation_gas_fee: 1_000_000_000_000,
};
Expand Down Expand Up @@ -1266,6 +1282,7 @@ mod tests {
max_call_gas: 10,
max_paymaster_post_op_gas: 10,
max_paymaster_verification_gas: 10,
max_total_execution_gas: 10,
max_simulate_handle_ops_gas: 10,
verification_estimation_gas_fee: 1_000_000_000_000,
};
Expand Down Expand Up @@ -1428,6 +1445,48 @@ mod tests {
));
}

#[tokio::test]
async fn test_total_limit() {
let (mut entry, mut provider) = create_base_config();

provider
.expect_get_latest_block_hash_and_number()
.returning(|| Ok((H256::zero(), U64::zero())));

entry
.expect_call_spoofed_simulate_op()
.returning(move |_a, _b, _c, _d, _e, _f| {
Ok(Ok(ExecutionResult {
target_result: TestCallGasResult {
success: true,
gas_used: 0.into(),
revert_data: Bytes::new(),
}
.encode()
.into(),
target_success: true,
..Default::default()
}))
});

let (estimator, _) = create_estimator(entry, provider);

let mut optional_op = demo_user_op_optional_gas(Some(U256::from(10000)));
optional_op.call_gas_limit = Some(TEST_MAX_GAS_LIMITS.into());
optional_op.verification_gas_limit = Some(TEST_MAX_GAS_LIMITS.into());

let err = estimator
.estimate_op_gas(optional_op.clone(), spoof::state())
.await
.err()
.unwrap();

assert!(matches!(
err,
GasEstimationError::GasTotalTooLarge(_, TEST_MAX_GAS_LIMITS)
))
}

#[test]
fn test_proxy_target_offset() {
let proxy_target_bytes = hex::decode(PROXY_IMPLEMENTATION_ADDRESS_MARKER).unwrap();
Expand Down
85 changes: 79 additions & 6 deletions crates/sim/src/estimation/v0_7.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,17 +110,32 @@ where
);
tracing::debug!("gas estimation took {}ms", timer.elapsed().as_millis());

let verification_gas_limit = verification_gas_limit?.into();
let paymaster_verification_gas_limit = paymaster_verification_gas_limit?.into();
let call_gas_limit = call_gas_limit?.into();
let verification_gas_limit = verification_gas_limit?;
let paymaster_verification_gas_limit = paymaster_verification_gas_limit?;
let call_gas_limit = call_gas_limit?;

// check the total gas limit
let mut op_with_gas = full_op;
op_with_gas.pre_verification_gas = pre_verification_gas;
op_with_gas.call_gas_limit = call_gas_limit;
op_with_gas.verification_gas_limit = verification_gas_limit;
op_with_gas.paymaster_verification_gas_limit = paymaster_verification_gas_limit;
let gas_limit =
gas::user_operation_execution_gas_limit(&self.chain_spec, &op_with_gas, true);
if gas_limit > self.settings.max_total_execution_gas.into() {
return Err(GasEstimationError::GasTotalTooLarge(
gas_limit.as_u64(),
self.settings.max_total_execution_gas,
));
}

Ok(GasEstimate {
pre_verification_gas,
call_gas_limit,
verification_gas_limit,
call_gas_limit: call_gas_limit.into(),
verification_gas_limit: verification_gas_limit.into(),
paymaster_verification_gas_limit: op
.paymaster
.map(|_| paymaster_verification_gas_limit),
.map(|_| paymaster_verification_gas_limit.into()),
})
}
}
Expand Down Expand Up @@ -563,6 +578,7 @@ mod tests {
max_call_gas: TEST_MAX_GAS_LIMITS,
max_paymaster_verification_gas: TEST_MAX_GAS_LIMITS,
max_paymaster_post_op_gas: TEST_MAX_GAS_LIMITS,
max_total_execution_gas: TEST_MAX_GAS_LIMITS,
max_simulate_handle_ops_gas: TEST_MAX_GAS_LIMITS,
verification_estimation_gas_fee: 1_000_000_000_000,
};
Expand Down Expand Up @@ -799,6 +815,63 @@ mod tests {
));
}

#[tokio::test]
async fn test_total_limit() {
let (mut entry, mut provider) = create_base_config();

entry
.expect_call_spoofed_simulate_op()
.returning(move |_a, _b, _c, _d, _e, _f| {
Ok(Ok(ExecutionResult {
target_result: TestCallGasResult {
success: true,
gas_used: TEST_MAX_GAS_LIMITS.into(),
revert_data: Bytes::new(),
}
.encode()
.into(),
target_success: true,
..Default::default()
}))
});
provider
.expect_get_latest_block_hash_and_number()
.returning(|| Ok((H256::zero(), U64::zero())));

let (estimator, _) = create_estimator(entry, provider);

let optional_op = UserOperationOptionalGas {
sender: Address::zero(),
nonce: U256::zero(),
call_data: Bytes::new(),
call_gas_limit: Some(TEST_MAX_GAS_LIMITS.into()),
verification_gas_limit: Some(TEST_MAX_GAS_LIMITS.into()),
pre_verification_gas: Some(TEST_MAX_GAS_LIMITS.into()),
max_fee_per_gas: None,
max_priority_fee_per_gas: None,
signature: Bytes::new(),

paymaster: None,
paymaster_data: Bytes::new(),
paymaster_verification_gas_limit: Some(TEST_MAX_GAS_LIMITS.into()),
paymaster_post_op_gas_limit: Some(TEST_MAX_GAS_LIMITS.into()),

factory: None,
factory_data: Bytes::new(),
};

let estimation = estimator
.estimate_op_gas(optional_op, spoof::state())
.await
.err()
.unwrap();

assert!(matches!(
estimation,
GasEstimationError::GasTotalTooLarge(_, TEST_MAX_GAS_LIMITS)
));
}

#[test]
fn test_proxy_target_offset() {
let proxy_target_bytes = hex::decode(PROXY_IMPLEMENTATION_ADDRESS_MARKER).unwrap();
Expand Down

0 comments on commit 4d79ad3

Please sign in to comment.