Skip to content

Commit

Permalink
feat: Adds support for v0.7 AA contracts. (#13)
Browse files Browse the repository at this point in the history
* feat: Adds support for v0.7 AA contracts.
Updates protocol version to 0.3 in offchain-RPC calls, updates Python examples.
Other example / SDK repositories will also need to be updated.
Applies some audit recommendations to HCHelper.

* fix: "cargo fmt" cleanup

 Changes to be committed:
	modified:   crates/builder/src/bundle_proposer.rs
	modified:   crates/rpc/src/eth/api.rs
	modified:   crates/rpc/src/eth/router.rs
	modified:   crates/types/src/hybrid_compute.rs
	modified:   crates/types/src/user_operation/mod.rs
	modified:   crates/types/src/user_operation/v0_6.rs
	modified:   crates/types/src/user_operation/v0_7.rs

* fix: Use OpenZeppelin Ownable and ReentrancyGuard for HCHelper
as suggested by audit. The index of the ResponseCache storage
slot is now detected rather
than being hard-coded.
 Changes to be committed:
	modified:   bin/rundler/src/cli/mod.rs
	modified:   crates/rpc/src/eth/api.rs
	modified:   crates/rpc/src/eth/router.rs
	modified:   crates/types/contracts/src/hc0_7/HCHelper.sol
	modified:   crates/types/src/hybrid_compute.rs

* fix: "cargo fmt"
 Changes to be committed:
	modified:   bin/rundler/src/cli/mod.rs
	modified:   crates/rpc/src/eth/api.rs
	modified:   crates/types/src/hybrid_compute.rs

* fix: some pylint cleanup.
 Changes to be committed:
	modified:   aa_utils/__init__.py
	modified:   offchain/offchain_utils.py

* fix: default to v0.7 in deploy-local.py

 Changes to be committed:
	modified:   deploy-local.py

* fix: sync the TestHybrid contract with the documetation.
 Changes to be committed:
	modified:   ../crates/types/contracts/src/hc0_7/TestHybrid.sol

* fix: force the EntryPoint in deployer script, fund ha0 acct directly.
 Changes to be committed:
	modified:   hc_scripts/LocalDeploy_v7.s.sol
  • Loading branch information
mmontour1306 authored Nov 12, 2024
1 parent 9e0afd9 commit 990aaf2
Show file tree
Hide file tree
Showing 52 changed files with 1,913 additions and 327 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 15 additions & 8 deletions bin/rundler/src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ use rundler_rpc::{EthApiSettings, RundlerApiSettings};
use rundler_sim::{
EstimationSettings, PrecheckSettings, PriorityFeeMode, SimulationSettings, MIN_CALL_GAS_LIMIT,
};
use rundler_types::hybrid_compute;
use rundler_types::{contracts::v0_7::hc_helper::HCHelper, hybrid_compute};

/// Main entry point for the CLI
///
Expand All @@ -53,19 +53,26 @@ pub async fn run() -> anyhow::Result<()> {

let cs = chain_spec::resolve_chain_spec(&opt.common.network, &opt.common.chain_spec);
tracing::info!("Chain spec: {:#?}", cs);
let node_http = opt
.common
.node_http
.clone()
.expect("must provide node_http");
let p2 = rundler_provider::new_provider(&node_http, None)?;
let hx = HCHelper::new(opt.common.hc_helper_addr, p2);
let slot_idx = hx
.response_slot()
.await
.expect("Failed to get ResponseSlot");

hybrid_compute::init(
opt.common.hc_helper_addr,
opt.common.hc_sys_account,
opt.common.hc_sys_owner,
opt.common.hc_sys_privkey,
//opt.common.entry_points[0].parse::<Address>().expect("Must provide an entry_point"),
cs.entry_point_address_v0_6,
cs.id,
opt.common
.node_http
.clone()
.expect("Must provide node_http"),
cs.clone(),
node_http,
slot_idx,
);

match opt.command {
Expand Down
24 changes: 18 additions & 6 deletions crates/builder/src/bundle_proposer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,7 @@ where
let mut gas_spent = self.settings.chain_spec.transaction_intrinsic_gas;
let mut cleanup_keys: Vec<H256> = Vec::new();
let mut constructed_bundle_size = BUNDLE_BYTE_OVERHEAD;

for (po, simulation) in ops_with_simulations {
let op = po.clone().uo;
let simulation = match simulation {
Expand Down Expand Up @@ -637,7 +638,11 @@ where
if hc_ent.is_some() {
gas_spent += hc_ent.clone().unwrap().oc_gas;
//println!("HC insert, hc_ent {:?}", hc_ent);
let u_op2: UserOperationVariant = hc_ent.clone().unwrap().user_op.into();
let u_op2: UserOperationVariant = hc_ent
.clone()
.unwrap()
.user_op
.into_variant(&self.settings.chain_spec);

let sim_result = self
.simulator
Expand Down Expand Up @@ -679,16 +684,23 @@ where
.get_nonce(cfg.sys_account, U256::zero())
.await
.unwrap();
let cleanup_op: UserOperationVariant =
hybrid_compute::rr_op(&cfg, c_nonce, cleanup_keys)
.await
.into();
let is_v7 =
self.entry_point.address() == self.settings.chain_spec.entry_point_address_v0_7;
let cleanup_op: UserOperationVariant = hybrid_compute::rr_op(
&cfg,
self.entry_point.address(),
c_nonce,
cleanup_keys,
is_v7,
)
.await
.into_variant(&self.settings.chain_spec);

let cleanup_sim = self
.simulator
.simulate_validation(cleanup_op.clone().into(), None, None)
.await
.expect("Failed to unwrap sim_result"); // FIXME
.expect("Failed to unwrap sim_result");

context
.groups_by_aggregator
Expand Down
10 changes: 5 additions & 5 deletions crates/provider/src/ethers/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,10 +141,10 @@ impl<C: JsonRpcClient + 'static> Provider for EthersProvider<C> {
block_id: Option<BlockId>,
trace_options: GethDebugTracingCallOptions,
) -> ProviderResult<GethTrace> {
println!(
"HC debug_trace_call overrides {:?} tx {:?}",
trace_options.state_overrides, tx
);
//println!(
// "HC debug_trace_call overrides {:?}",
// trace_options.state_overrides
//);
println!("HC will use BlockNumber::Latest instead of {:?}", block_id);
let ret = Middleware::debug_trace_call(
self,
Expand All @@ -153,7 +153,7 @@ impl<C: JsonRpcClient + 'static> Provider for EthersProvider<C> {
trace_options,
)
.await;
println!("HC debug_trace_call ret {:?}", ret);
//println!("HC debug_trace_call ret {:?}", ret);
Ok(ret?)
}

Expand Down
146 changes: 74 additions & 72 deletions crates/rpc/src/eth/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use jsonrpsee::{
};
use rundler_types::{
chain::ChainSpec,
contracts::v0_6::{hc_helper::HCHelper as HH2, simple_account::SimpleAccount},
contracts::{v0_6::simple_account::SimpleAccount, v0_7::hc_helper::HCHelper},
hybrid_compute,
pool::Pool,
UserOperation, UserOperationOptionalGas, UserOperationVariant,
Expand Down Expand Up @@ -156,22 +156,19 @@ where
// max_simulate_handle_ops_gas: 0,
// validation_estimation_gas_fee: 0,
//};
let op6: rundler_types::v0_6::UserOperationOptionalGas = op.clone().into();

let hh = op6
.clone()
.into_user_operation(U256::from(0), U256::from(0))
.hc_hash();
let hh = op.hc_hash();
println!("HC api.rs hh {:?}", hh);
let op_t: UserOperationVariant = op.into_variant(&self.chain_spec);

let ep_addr = hybrid_compute::hc_ep_addr(revert_data);
let ep_addr = hybrid_compute::hc_ha_addr(revert_data);

let n_key: U256 = op6.nonce >> 64;
let at_price = op6.max_priority_fee_per_gas;
//let hc_nonce = context.gas_estimator.entry_point.get_nonce(op6.sender, n_key).await.unwrap();
let n_key: U256 = op_t.nonce() >> 64;
let at_price = Some(op_t.max_priority_fee_per_gas());
//let hc_nonce = context.gas_estimator.entry_point.get_nonce(op_t.sender(), n_key).await.unwrap();
let hc_nonce = self
.router
.get_nonce(&entry_point, op6.sender, n_key)
.get_nonce(&entry_point, op_t.sender(), n_key)
.await
.unwrap();

Expand All @@ -182,11 +179,14 @@ where
.unwrap();
println!(
"HC hc_nonce {:?} err_nonce {:?} op_nonce {:?} n_key {:?}",
hc_nonce, err_nonce, op6.nonce, n_key
hc_nonce,
err_nonce,
op_t.nonce(),
n_key
);
let p2 = rundler_provider::new_provider(&self.hc.node_http, None)?;

let hx = HH2::new(self.hc.helper_addr, p2.clone());
let hx = HCHelper::new(self.hc.helper_addr, p2.clone());
let url = hx.registered_callers(ep_addr).await.expect("url_decode").1;
println!("HC registered_caller url {:?}", url);

Expand All @@ -206,9 +206,10 @@ where
let payload = hex::encode(hybrid_compute::hc_req_payload(revert_data));
let n_bytes: [u8; 32] = (hc_nonce).into();
let src_n = hex::encode(n_bytes);
let src_addr = hex::encode(op6.sender);
let src_addr = hex::encode(op_t.sender());

let oo_n_key: U256 = U256::from_big_endian(op_t.sender().as_fixed_bytes());

let oo_n_key: U256 = U256::from_big_endian(op6.sender.as_fixed_bytes());
let oo_nonce = self
.router
.get_nonce(&entry_point, ep_addr, oo_n_key)
Expand All @@ -222,10 +223,31 @@ where
"Failed to look up HybridAccount owner"
)));
}
const REQ_VERSION: &str = "0.2";
// This version parameter tells the offchain RPC which version of the
// AA contracts are being used. This affects the hashing algorithm needed
// to generate an offchain signature.
const REQ_VERSION_V6: &str = "0.2";
const REQ_VERSION_V7: &str = "0.3";

let is_v7 = match self.router.get_ep_version(&entry_point)? {
rundler_types::EntryPointVersion::V0_7 => true,
rundler_types::EntryPointVersion::V0_6 => false,
rundler_types::EntryPointVersion::Unspecified => {
return Err(EthRpcError::Internal(anyhow::anyhow!(
"HC04: Unknown EntryPoint version"
)))
}
};

let mut params = ObjectParams::new();
let _ = params.insert("ver", REQ_VERSION);
let _ = params.insert(
"ver",
if is_v7 {
REQ_VERSION_V7
} else {
REQ_VERSION_V6
},
);
let _ = params.insert("sk", sk_hex);
let _ = params.insert("src_addr", src_addr);
let _ = params.insert("src_nonce", src_n);
Expand All @@ -250,11 +272,11 @@ where
let resp_hex = resp["response"].as_str().unwrap();
let sig_hex: String = resp["signature"].as_str().unwrap().into();
let hc_res: Bytes = hex::decode(resp_hex).unwrap().into();
//println!("HC api.rs do_op result sk {:?} success {:?} res {:?}", sub_key, op_success, hc_res);

err_hc = hybrid_compute::external_op(
entry_point,
hh,
op6.sender,
op_t.sender(),
hc_nonce,
op_success,
&hc_res,
Expand All @@ -266,6 +288,7 @@ where
&self.hc,
ha_owner.unwrap(),
err_nonce,
is_v7,
)
.await;
} else {
Expand Down Expand Up @@ -329,11 +352,12 @@ where
entry_point,
err_hc.clone(),
sub_key,
op6.sender,
op_t.sender(),
hc_nonce,
err_nonce,
map_key,
&self.hc,
is_v7,
)
.await;
}
Expand All @@ -349,31 +373,19 @@ where
println!("HC api.rs Ok gas result2 = {:?}", result2);
let r3a = result2.unwrap();
match r3a {
RpcGasEstimate::V0_6(abc) => {
r3 = abc;
RpcGasEstimate::V0_6(est) => {
r3 = est;
}
_ => {
return Err(EthRpcError::Internal(anyhow::anyhow!(
"HC04 user_op gas estimation failed"
)));
RpcGasEstimate::V0_7(est) => {
r3 = RpcGasEstimateV0_6 {
pre_verification_gas: est.pre_verification_gas,
call_gas_limit: est.call_gas_limit,
verification_gas_limit: est.verification_gas_limit,
};
}
}

let op_tmp = hybrid_compute::get_hc_ent(hh).unwrap().user_op;
let op_tmp_2: rundler_types::v0_6::UserOperationOptionalGas =
rundler_types::v0_6::UserOperationOptionalGas {
sender: op_tmp.sender,
nonce: op_tmp.nonce,
init_code: op_tmp.init_code,
call_data: op_tmp.call_data,
call_gas_limit: Some(op_tmp.call_gas_limit),
verification_gas_limit: Some(op_tmp.verification_gas_limit),
pre_verification_gas: Some(op_tmp.pre_verification_gas),
max_fee_per_gas: Some(op_tmp.max_fee_per_gas),
max_priority_fee_per_gas: Some(op_tmp.max_priority_fee_per_gas),
paymaster_and_data: op_tmp.paymaster_and_data,
signature: op_tmp.signature,
};
let op_tmp_2 = hybrid_compute::get_hc_ent(hh).unwrap().user_op;

// The op_tmp_2 below specifies a 0 gas price, but we need to estimate the L1 fee at the
// price offered by real userOperation which will be paying for it.
Expand All @@ -382,7 +394,7 @@ where
.router
.estimate_gas(
&entry_point,
rundler_types::UserOperationOptionalGas::V0_6(op_tmp_2.clone()),
op_tmp_2.clone(),
Some(spoof::State::default()),
at_price,
)
Expand All @@ -396,17 +408,17 @@ where
};

let r2: RpcGasEstimateV0_6 = match r2a? {
RpcGasEstimate::V0_6(estimate) => estimate,
_ => {
return Err(EthRpcError::Internal(anyhow::anyhow!(
"HC04 offchain_op gas estimation failed"
)));
}
RpcGasEstimate::V0_6(est) => est,
RpcGasEstimate::V0_7(est) => RpcGasEstimateV0_6 {
pre_verification_gas: est.pre_verification_gas,
call_gas_limit: est.call_gas_limit,
verification_gas_limit: est.verification_gas_limit,
},
};

// The current formula used to estimate gas usage in the offchain_rpc service
// sometimes underestimates the true cost. For now all we can do is error here.
if r2.call_gas_limit > op_tmp_2.call_gas_limit.unwrap() {
if r2.call_gas_limit > op_tmp_2.into_variant(&self.chain_spec).call_gas_limit() {
println!("HC op_tmp_2 failed, call_gas_limit too low");
let msg = "HC04: Offchain call_gas_limit too low".to_string();
return Err(EthRpcError::Internal(anyhow::anyhow!(msg)));
Expand All @@ -421,38 +433,28 @@ where
.get_nonce(&entry_point, self.hc.sys_account, U256::zero())
.await
.unwrap();
let cleanup_op = hybrid_compute::rr_op(&self.hc, c_nonce, cleanup_keys.clone()).await;
let op_tmp_4: rundler_types::v0_6::UserOperationOptionalGas =
rundler_types::v0_6::UserOperationOptionalGas {
sender: cleanup_op.sender,
nonce: cleanup_op.nonce,
init_code: cleanup_op.init_code,
call_data: cleanup_op.call_data,
call_gas_limit: Some(cleanup_op.call_gas_limit),
verification_gas_limit: Some(cleanup_op.verification_gas_limit),
pre_verification_gas: Some(cleanup_op.pre_verification_gas),
max_fee_per_gas: Some(cleanup_op.max_fee_per_gas),
max_priority_fee_per_gas: Some(cleanup_op.max_priority_fee_per_gas),
paymaster_and_data: cleanup_op.paymaster_and_data,
signature: cleanup_op.signature,
};
println!("HC op_tmp_4 {:?} {:?}", op_tmp_4, cleanup_keys);
let cleanup_op =
hybrid_compute::rr_op(&self.hc, entry_point, c_nonce, cleanup_keys.clone(), is_v7)
.await;

println!("HC cleanup_op {:?} {:?}", cleanup_op, cleanup_keys);
let r4a = self
.router
.estimate_gas(
&entry_point,
rundler_types::UserOperationOptionalGas::V0_6(op_tmp_4),
// rundler_types::UserOperationOptionalGas::V0_6(op_tmp_4),
cleanup_op,
Some(spoof::State::default()),
at_price,
)
.await;
let r4: RpcGasEstimateV0_6 = match r4a? {
RpcGasEstimate::V0_6(estimate) => estimate,
_ => {
return Err(EthRpcError::Internal(anyhow::anyhow!(
"HC04 cleanup_op gas estimation failed"
)));
}
RpcGasEstimate::V0_6(est) => est,
RpcGasEstimate::V0_7(est) => RpcGasEstimateV0_6 {
pre_verification_gas: est.pre_verification_gas,
call_gas_limit: est.call_gas_limit,
verification_gas_limit: est.verification_gas_limit,
},
};

let cleanup_gas =
Expand Down
Loading

0 comments on commit 990aaf2

Please sign in to comment.