Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(sim): check total gas limit after estimation #739

Merged
merged 1 commit into from
Jun 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading