From 6d5262da7f4cb2076ec317adcbeffbfca7f0c107 Mon Sep 17 00:00:00 2001 From: Vid Kersic Date: Fri, 12 Jan 2024 18:32:58 +0100 Subject: [PATCH 1/2] chore: move hash inside user operation --- .github/workflows/ci.yml | 2 +- Cargo.lock | 39 +- bin/silius/src/bundler.rs | 18 +- bin/silius/src/cli/args.rs | 4 +- crates/bundler/src/bundler.rs | 17 +- crates/contracts/src/utils.rs | 10 +- crates/grpc/src/proto.rs | 93 ++++ crates/grpc/src/protos/types/types.proto | 15 + crates/grpc/src/protos/uopool/uopool.proto | 2 +- crates/grpc/src/uopool.rs | 6 +- crates/mempool/Cargo.toml | 1 - crates/mempool/src/builder.rs | 11 +- crates/mempool/src/database/mempool.rs | 32 +- crates/mempool/src/database/tables.rs | 6 +- crates/mempool/src/database/utils.rs | 4 +- crates/mempool/src/memory/mempool.rs | 43 +- crates/mempool/src/mempool.rs | 34 +- crates/mempool/src/uopool.rs | 45 +- crates/mempool/src/utils.rs | 92 ++-- .../validate/simulation_trace/code_hashes.rs | 8 +- crates/mempool/src/validate/validator.rs | 6 +- crates/p2p/src/network.rs | 7 +- crates/p2p/src/request_response/models.rs | 4 +- crates/primitives/Cargo.toml | 2 +- crates/primitives/src/lib.rs | 2 +- crates/primitives/src/p2p.rs | 11 +- crates/primitives/src/reputation.rs | 13 +- crates/primitives/src/user_operation/hash.rs | 69 +++ .../mod.rs} | 422 ++++++------------ .../primitives/src/user_operation/request.rs | 106 +++++ crates/primitives/src/wallet.rs | 41 +- crates/rpc/src/debug.rs | 14 +- crates/rpc/src/debug_api.rs | 7 +- crates/rpc/src/eth.rs | 53 ++- crates/rpc/src/eth_api.rs | 14 +- examples/simple-account/examples/create.rs | 21 +- examples/simple-account/examples/transfer.rs | 21 +- examples/storage/examples/memory.rs | 4 +- .../user-operation/examples/user_operation.rs | 10 +- rust-toolchain.toml | 2 +- tests/src/common/mod.rs | 6 +- tests/src/estimate_gas_tests.rs | 8 +- tests/src/simulation_tests.rs | 26 +- 43 files changed, 753 insertions(+), 598 deletions(-) create mode 100644 crates/primitives/src/user_operation/hash.rs rename crates/primitives/src/{user_operation.rs => user_operation/mod.rs} (72%) create mode 100644 crates/primitives/src/user_operation/request.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 99949f81..f0d4ef79 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,7 +23,7 @@ jobs: - uses: actions/checkout@v4 - name: Setup Rust toolchain (stable) - uses: dtolnay/rust-toolchain@stable + uses: dtolnay/rust-toolchain@1.73.0 with: components: clippy diff --git a/Cargo.lock b/Cargo.lock index 08d47384..5617ae3d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1706,24 +1706,12 @@ version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f0042ff8246a363dbe77d2ceedb073339e85a804b9a47636c6e016a9a32c05f" dependencies = [ - "enum-ordinalize 3.1.15", + "enum-ordinalize", "proc-macro2", "quote", "syn 1.0.109", ] -[[package]] -name = "educe" -version = "0.5.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e93898956a40b4e5aadd52d57674426a14b0c5598ac5305b93b8560101083065" -dependencies = [ - "enum-ordinalize 4.3.0", - "proc-macro2", - "quote", - "syn 2.0.43", -] - [[package]] name = "either" version = "1.9.0" @@ -1835,26 +1823,6 @@ dependencies = [ "syn 2.0.43", ] -[[package]] -name = "enum-ordinalize" -version = "4.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea0dcfa4e54eeb516fe454635a95753ddd39acda650ce703031c6973e315dd5" -dependencies = [ - "enum-ordinalize-derive", -] - -[[package]] -name = "enum-ordinalize-derive" -version = "4.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d28318a75d4aead5c4db25382e8ef717932d0346600cacae6357eb5941bc5ff" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.43", -] - [[package]] name = "enumn" version = "0.1.12" @@ -5987,7 +5955,7 @@ dependencies = [ "cipher 0.4.4", "ctr 0.9.2", "digest 0.10.7", - "educe 0.4.23", + "educe", "futures", "generic-array", "hmac 0.12.1", @@ -6996,7 +6964,6 @@ dependencies = [ "alloy-chains", "async-trait", "bin-layout", - "educe 0.5.9", "enumset", "ethers 2.0.8", "eyre", @@ -7052,7 +7019,7 @@ version = "0.3.0-alpha" dependencies = [ "alloy-chains", "async-stream", - "educe 0.5.9", + "derive_more", "ethers 2.0.8", "expanded-pathbuf", "eyre", diff --git a/bin/silius/src/bundler.rs b/bin/silius/src/bundler.rs index 99f77f53..58e42fd4 100644 --- a/bin/silius/src/bundler.rs +++ b/bin/silius/src/bundler.rs @@ -31,7 +31,7 @@ use silius_primitives::{ provider::BlockStream, reputation::ReputationEntry, simulation::CodeHash, - UserOperation, UserOperationHash, Wallet, + UserOperationHash, UserOperationSigned, Wallet, }; use silius_rpc::{ debug_api::{DebugApiServer, DebugApiServerImpl}, @@ -105,17 +105,17 @@ where eth_client_version, ); - let chain_id = eth_client.get_chainid().await?; - let chain_conn = Chain::from(chain_id.as_u64()); + let chain_id = eth_client.get_chainid().await?.as_u64(); + let chain_conn = Chain::from(chain_id); let wallet: Wallet; if args.send_bundle_mode == SendStrategy::Flashbots { - wallet = Wallet::from_file(args.mnemonic_file.into(), &chain_id, true) + wallet = Wallet::from_file(args.mnemonic_file.into(), chain_id, true) .map_err(|error| eyre::format_err!("Could not load mnemonic file: {}", error))?; info!("Wallet Signer {:?}", wallet.signer); info!("Flashbots Signer {:?}", wallet.flashbots_signer); } else { - wallet = Wallet::from_file(args.mnemonic_file.into(), &chain_id, false) + wallet = Wallet::from_file(args.mnemonic_file.into(), chain_id, false) .map_err(|error| eyre::format_err!("Could not load mnemonic file: {}", error))?; info!("{:?}", wallet.signer); } @@ -189,7 +189,7 @@ where args.min_priority_fee_per_gas, ); let mempool = Mempool::new( - Arc::new(RwLock::new(HashMap::::default())), + Arc::new(RwLock::new(HashMap::::default())), Arc::new(RwLock::new(HashMap::>::default())), Arc::new(RwLock::new(HashMap::>::default())), Arc::new(RwLock::new(HashMap::>::default())), @@ -283,7 +283,7 @@ where args.min_priority_fee_per_gas, ); let mempool = Mempool::new( - Arc::new(RwLock::new(HashMap::::default())), + Arc::new(RwLock::new(HashMap::::default())), Arc::new(RwLock::new(HashMap::>::default())), Arc::new(RwLock::new(HashMap::>::default())), Arc::new(RwLock::new(HashMap::>::default())), @@ -472,11 +472,11 @@ pub fn create_wallet(args: CreateWalletArgs) -> eyre::Result<()> { let path = unwrap_path_or_home(args.output_path)?; if args.flashbots_key { - let wallet = Wallet::build_random(path, &args.chain_id, true)?; + let wallet = Wallet::build_random(path, args.chain_id, true)?; info!("Wallet signer {:?}", wallet.signer); info!("Flashbots signer {:?}", wallet.flashbots_signer); } else { - let wallet = Wallet::build_random(path, &args.chain_id, false)?; + let wallet = Wallet::build_random(path, args.chain_id, false)?; info!("Wallet signer {:?}", wallet.signer); } diff --git a/bin/silius/src/cli/args.rs b/bin/silius/src/cli/args.rs index 5bb13027..6a2856ad 100644 --- a/bin/silius/src/cli/args.rs +++ b/bin/silius/src/cli/args.rs @@ -228,8 +228,8 @@ pub struct CreateWalletArgs { pub output_path: Option, /// The chain id. - #[clap(long, value_parser=parse_u256, default_value="1")] - pub chain_id: U256, + #[clap(long, default_value = "1")] + pub chain_id: u64, /// Whether to create a Flashbots key. #[clap(long, default_value_t = false)] diff --git a/crates/bundler/src/bundler.rs b/crates/bundler/src/bundler.rs index fb065f00..d00916c1 100644 --- a/crates/bundler/src/bundler.rs +++ b/crates/bundler/src/bundler.rs @@ -161,9 +161,7 @@ where info!( "Creating a new bundle with {} user operations: {:?}", uos.len(), - uos.iter() - .map(|uo| uo.hash(&self.entry_point, &self.chain.id().into())) - .collect::>() + uos.iter().map(|uo| uo.hash).collect::>() ); trace!("Bundle content: {uos:?}"); @@ -178,6 +176,7 @@ where /// /// # Arguments /// * `client` - A provider that implements [Middleware](Middleware) trait + /// * `uos` - Vector of [UserOperations](UserOperation) /// /// # Returns /// * `TypedTransaction` - A [TypedTransaction](TypedTransaction) @@ -201,8 +200,12 @@ where self.beneficiary }; - let mut tx: TypedTransaction = - ep.handle_ops(uos.clone().into_iter().map(Into::into).collect(), beneficiary).tx; + let mut tx: TypedTransaction = ep + .handle_ops( + uos.clone().into_iter().map(|uo| uo.user_operation.into()).collect(), + beneficiary, + ) + .tx; match self.chain.id() { // Mumbai @@ -521,7 +524,7 @@ mod test { let eth_client = Arc::new(Provider::::connect(anvil.ws_endpoint()).await?); let ep_address = "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789".parse::
()?; - let wallet = Wallet::from_phrase(KEY_PHRASE, &anvil.chain_id().into(), true)?; + let wallet = Wallet::from_phrase(KEY_PHRASE, anvil.chain_id(), true)?; // Create a bundler and connect to the Anvil let bundler = Bundler::new( @@ -559,7 +562,7 @@ mod test { "{}/.silius/0x129D197b2a989C6798601A49D89a4AEC822A17a3", std::env::var("HOME").unwrap() ); - let wallet = Wallet::from_file(dir.into(), &U256::from(5), true)?; + let wallet = Wallet::from_file(dir.into(), 5, true)?; let bundler = Bundler::new( wallet.clone(), diff --git a/crates/contracts/src/utils.rs b/crates/contracts/src/utils.rs index 09405d4c..4e03eb3c 100644 --- a/crates/contracts/src/utils.rs +++ b/crates/contracts/src/utils.rs @@ -1,9 +1,9 @@ use crate::gen::entry_point_api::{self, EntryPointAPICalls}; use ethers::{abi::AbiDecode, types::Bytes}; -use silius_primitives::UserOperation; +use silius_primitives::UserOperationSigned; -impl From for entry_point_api::UserOperation { - fn from(uo: UserOperation) -> Self { +impl From for entry_point_api::UserOperation { + fn from(uo: UserOperationSigned) -> Self { Self { sender: uo.sender, nonce: uo.nonce, @@ -20,7 +20,7 @@ impl From for entry_point_api::UserOperation { } } -impl From for UserOperation { +impl From for UserOperationSigned { fn from(uo: entry_point_api::UserOperation) -> Self { Self { sender: uo.sender, @@ -38,7 +38,7 @@ impl From for UserOperation { } } -pub fn parse_from_input_data(data: Bytes) -> Option> { +pub fn parse_from_input_data(data: Bytes) -> Option> { EntryPointAPICalls::decode(data).ok().and_then(|call| match call { EntryPointAPICalls::HandleOps(ops) => { Some(ops.ops.into_iter().map(|op| op.into()).collect()) diff --git a/crates/grpc/src/proto.rs b/crates/grpc/src/proto.rs index 8d5b3443..3d75ae8b 100644 --- a/crates/grpc/src/proto.rs +++ b/crates/grpc/src/proto.rs @@ -96,6 +96,7 @@ pub mod types { impl From for UserOperation { fn from(user_operation: silius_primitives::UserOperation) -> Self { Self { + hash: Some(user_operation.hash.into()), sender: Some(user_operation.sender.into()), nonce: Some(user_operation.nonce.into()), init_code: prost::bytes::Bytes::copy_from_slice(user_operation.init_code.as_ref()), @@ -115,6 +116,98 @@ pub mod types { impl From for silius_primitives::UserOperation { fn from(user_operation: UserOperation) -> Self { + Self { + hash: { + if let Some(hash) = user_operation.hash { + hash.into() + } else { + UserOperationHash::default() + } + }, + user_operation: silius_primitives::UserOperationSigned { + sender: { + if let Some(sender) = user_operation.sender { + sender.into() + } else { + Address::zero() + } + }, + nonce: { + if let Some(nonce) = user_operation.nonce { + nonce.into() + } else { + U256::zero() + } + }, + init_code: user_operation.init_code.into(), + call_data: user_operation.call_data.into(), + call_gas_limit: { + if let Some(call_gas_limit) = user_operation.call_gas_limit { + call_gas_limit.into() + } else { + U256::zero() + } + }, + verification_gas_limit: { + if let Some(verification_gas_limit) = user_operation.verification_gas_limit + { + verification_gas_limit.into() + } else { + U256::zero() + } + }, + pre_verification_gas: { + if let Some(pre_verification_gas) = user_operation.pre_verification_gas { + pre_verification_gas.into() + } else { + U256::zero() + } + }, + max_fee_per_gas: { + if let Some(max_fee_per_gas) = user_operation.max_fee_per_gas { + max_fee_per_gas.into() + } else { + U256::zero() + } + }, + max_priority_fee_per_gas: { + if let Some(max_priority_fee_per_gas) = + user_operation.max_priority_fee_per_gas + { + max_priority_fee_per_gas.into() + } else { + U256::zero() + } + }, + paymaster_and_data: user_operation.paymaster_and_data.into(), + signature: user_operation.signature.into(), + }, + } + } + } + + impl From for UserOperationSigned { + fn from(user_operation: silius_primitives::UserOperationSigned) -> Self { + Self { + sender: Some(user_operation.sender.into()), + nonce: Some(user_operation.nonce.into()), + init_code: prost::bytes::Bytes::copy_from_slice(user_operation.init_code.as_ref()), + call_data: prost::bytes::Bytes::copy_from_slice(user_operation.call_data.as_ref()), + call_gas_limit: Some(user_operation.call_gas_limit.into()), + verification_gas_limit: Some(user_operation.verification_gas_limit.into()), + pre_verification_gas: Some(user_operation.pre_verification_gas.into()), + max_fee_per_gas: Some(user_operation.max_fee_per_gas.into()), + max_priority_fee_per_gas: Some(user_operation.max_priority_fee_per_gas.into()), + paymaster_and_data: prost::bytes::Bytes::copy_from_slice( + user_operation.paymaster_and_data.as_ref(), + ), + signature: prost::bytes::Bytes::copy_from_slice(user_operation.signature.as_ref()), + } + } + } + + impl From for silius_primitives::UserOperationSigned { + fn from(user_operation: UserOperationSigned) -> Self { Self { sender: { if let Some(sender) = user_operation.sender { diff --git a/crates/grpc/src/protos/types/types.proto b/crates/grpc/src/protos/types/types.proto index 260f1c27..2ff3c894 100644 --- a/crates/grpc/src/protos/types/types.proto +++ b/crates/grpc/src/protos/types/types.proto @@ -26,6 +26,21 @@ message SupportedEntryPoint { } message UserOperation { + types.H256 hash = 1; + types.H160 sender = 2; + PbU256 nonce = 3; + bytes init_code = 4; + bytes call_data = 5; + PbU256 call_gas_limit = 6; + PbU256 verification_gas_limit = 7; + PbU256 pre_verification_gas = 8; + PbU256 max_fee_per_gas = 9; + PbU256 max_priority_fee_per_gas = 10; + bytes paymaster_and_data = 11; + bytes signature = 12; +} + +message UserOperationSigned { types.H160 sender = 1; PbU256 nonce = 2; bytes init_code = 3; diff --git a/crates/grpc/src/protos/uopool/uopool.proto b/crates/grpc/src/protos/uopool/uopool.proto index 4a2f04fb..42a01619 100644 --- a/crates/grpc/src/protos/uopool/uopool.proto +++ b/crates/grpc/src/protos/uopool/uopool.proto @@ -83,7 +83,7 @@ message UserOperationHashRequest{ } message GetUserOperationByHashResponse{ - types.UserOperation user_operation = 1; + types.UserOperationSigned user_operation = 1; types.H160 entry_point = 2; types.H256 transaction_hash = 3; types.H256 block_hash = 4; diff --git a/crates/grpc/src/uopool.rs b/crates/grpc/src/uopool.rs index 61aa0902..7d75b9a6 100644 --- a/crates/grpc/src/uopool.rs +++ b/crates/grpc/src/uopool.rs @@ -92,7 +92,7 @@ where &self, ep: &Address, ) -> tonic::Result> { - let m_id = mempool_id(ep, &U256::from(self.chain.id())); + let m_id = mempool_id(ep, self.chain.id()); self.uopools .read() .get(&m_id) @@ -425,7 +425,7 @@ where let mut entrypoint_channels: EntrypointChannels = Vec::new(); for (ep, block_stream) in eps.into_iter().zip(block_streams.into_iter()) { - let id = mempool_id(&ep, &U256::from(chain.id())); + let id = mempool_id(&ep, chain.id()); let (waiting_to_pub_sd, waiting_to_pub_rv) = unbounded::<(UserOperation, U256)>(); let uo_builder = UoPoolBuilder::new( eth_client.clone(), @@ -531,7 +531,7 @@ where }); } else { for (ep, block_stream) in eps.into_iter().zip(block_streams.into_iter()) { - let id = mempool_id(&ep, &U256::from(chain.id())); + let id = mempool_id(&ep, chain.id()); let uo_builder = UoPoolBuilder::new( eth_client.clone(), ep, diff --git a/crates/mempool/Cargo.toml b/crates/mempool/Cargo.toml index cd11a88c..613ca14c 100644 --- a/crates/mempool/Cargo.toml +++ b/crates/mempool/Cargo.toml @@ -35,7 +35,6 @@ tokio = { workspace = true } # misc bin-layout = "7.1.0" -educe = { version = "0.5.9", features = ["Debug", "Default"] } enumset = "1.1.3" eyre = { workspace = true } page_size = "0.6.0" diff --git a/crates/mempool/src/builder.rs b/crates/mempool/src/builder.rs index 3f0a8627..87c930ae 100644 --- a/crates/mempool/src/builder.rs +++ b/crates/mempool/src/builder.rs @@ -16,7 +16,7 @@ use eyre::format_err; use futures::channel::mpsc::UnboundedSender; use futures_util::StreamExt; use silius_contracts::EntryPoint; -use silius_primitives::{get_address, provider::BlockStream, UserOperation}; +use silius_primitives::{get_address, provider::BlockStream, UserOperation, UserOperationSigned}; use std::{sync::Arc, time::Duration}; use tracing::warn; @@ -93,18 +93,13 @@ where if let Some(txs) = txs { for tx in txs { if tx.to == Some(uopool.entry_point.address()) { - let dec: Result<(Vec, Address), _> = + let dec: Result<(Vec, Address), _> = uopool.entry_point.entry_point_api().decode("handleOps", tx.input); if let Ok((uos, _)) = dec { uopool.remove_user_operations( uos.iter() - .map(|uo| { - uo.hash( - &uopool.entry_point.address(), - &uopool.chain.id().into(), - ) - }) + .map(|uo| uo.hash(&uopool.entry_point.address(), uopool.chain.id())) .collect(), ); diff --git a/crates/mempool/src/database/mempool.rs b/crates/mempool/src/database/mempool.rs index abd2ad17..5b6ef817 100644 --- a/crates/mempool/src/database/mempool.rs +++ b/crates/mempool/src/database/mempool.rs @@ -2,8 +2,8 @@ use super::{ env::DatabaseError, tables::{CodeHashes, UserOperations, UserOperationsByEntity, UserOperationsBySender}, utils::{ - WrapAddress, WrapCodeHash, WrapCodeHashVec, WrapUserOpSet, WrapUserOperation, - WrapUserOperationHash, + WrapAddress, WrapCodeHash, WrapCodeHashVec, WrapUserOpSet, WrapUserOperationHash, + WrapUserOperationSigned, }, DatabaseTable, }; @@ -14,7 +14,7 @@ use crate::{ }, MempoolErrorKind, }; -use ethers::types::{Address, U256}; +use ethers::types::Address; use reth_db::{ cursor::DbCursorRO, database::Database, @@ -24,19 +24,13 @@ use reth_db::{ use silius_primitives::{simulation::CodeHash, UserOperation, UserOperationHash}; impl AddRemoveUserOp for DatabaseTable { - fn add( - &mut self, - uo: UserOperation, - ep: &Address, - chain_id: &U256, - ) -> Result { - let hash = uo.hash(ep, chain_id); + fn add(&mut self, uo: UserOperation) -> Result { let tx = self.env.tx_mut()?; - let uo_hash_wrap: WrapUserOperationHash = hash.into(); - let uo_wrap: WrapUserOperation = uo.into(); + let uo_hash_wrap: WrapUserOperationHash = uo.hash.into(); + let uo_wrap: WrapUserOperationSigned = uo.user_operation.into(); tx.put::(uo_hash_wrap, uo_wrap)?; tx.commit()?; - Ok(hash) + Ok(uo.hash) } fn remove_by_uo_hash(&mut self, uo_hash: &UserOperationHash) -> Result { @@ -110,7 +104,7 @@ impl UserOperationOp for DatabaseTable { let res = tx.get::(uo_hash_wrap)?; tx.commit()?; - Ok(res.map(|uo| uo.into())) + Ok(res.map(|uo| UserOperation::from_user_operation_signed(*uo_hash, uo.into()))) } fn get_sorted(&self) -> Result, MempoolErrorKind> { @@ -120,7 +114,11 @@ impl UserOperationOp for DatabaseTable { let mut cursor = tx.cursor_read::()?; let mut uos: Vec = cursor .walk(Some(WrapUserOperationHash::default()))? - .map(|a| a.map(|(_, uo)| uo.into())) + .map(|a| { + a.map(|(hash, uo)| { + UserOperation::from_user_operation_signed(hash.into(), uo.into()) + }) + }) .collect::, _>>()?; uos.sort_by(|a, b| { if a.max_priority_fee_per_gas != b.max_priority_fee_per_gas { @@ -138,8 +136,8 @@ impl UserOperationOp for DatabaseTable { let tx = self.env.tx()?; let mut c = tx.cursor_read::()?; let mut res = Vec::new(); - while let Some((_, uo)) = c.next()? { - res.push(uo.into()) + while let Some((hash, uo)) = c.next()? { + res.push(UserOperation::from_user_operation_signed(hash.into(), uo.into())) } Ok(res) diff --git a/crates/mempool/src/database/tables.rs b/crates/mempool/src/database/tables.rs index b00cd5a3..2f800ff2 100644 --- a/crates/mempool/src/database/tables.rs +++ b/crates/mempool/src/database/tables.rs @@ -1,12 +1,12 @@ use super::utils::{ - WrapAddress, WrapCodeHashVec, WrapReputationEntry, WrapUserOpSet, WrapUserOperation, - WrapUserOperationHash, + WrapAddress, WrapCodeHashVec, WrapReputationEntry, WrapUserOpSet, WrapUserOperationHash, + WrapUserOperationSigned, }; use reth_db::{table, TableType}; table!( /// Stores the user operations - ( UserOperations ) WrapUserOperationHash | WrapUserOperation + ( UserOperations ) WrapUserOperationHash | WrapUserOperationSigned ); table!( diff --git a/crates/mempool/src/database/utils.rs b/crates/mempool/src/database/utils.rs index e6c62f8c..95e46617 100644 --- a/crates/mempool/src/database/utils.rs +++ b/crates/mempool/src/database/utils.rs @@ -7,7 +7,7 @@ use ethers::{ use reth_db::table::{Compress, Decode, Decompress, Encode}; use serde::{Deserialize, Serialize}; use silius_primitives::{ - reputation::ReputationEntry, simulation::CodeHash, UserOperation, UserOperationHash, + reputation::ReputationEntry, simulation::CodeHash, UserOperationHash, UserOperationSigned, }; use std::{collections::HashSet, fmt::Debug}; @@ -108,7 +108,7 @@ construct_wrap_hash!(Address, WrapAddress, 20); construct_wrap_hash!(UserOperationHash, WrapUserOperationHash, 32); construct_wrap_struct!(CodeHash, WrapCodeHash); -construct_wrap_struct!(UserOperation, WrapUserOperation); +construct_wrap_struct!(UserOperationSigned, WrapUserOperationSigned); construct_wrap_struct!(ReputationEntry, WrapReputationEntry); impl<'de> Decoder<'de> for WrapUserOperationHash { diff --git a/crates/mempool/src/memory/mempool.rs b/crates/mempool/src/memory/mempool.rs index 0ae48c67..2884ef04 100644 --- a/crates/mempool/src/memory/mempool.rs +++ b/crates/mempool/src/memory/mempool.rs @@ -5,21 +5,16 @@ use crate::{ }, MempoolErrorKind, }; -use ethers::types::{Address, U256}; -use silius_primitives::{simulation::CodeHash, UserOperation, UserOperationHash}; +use ethers::types::Address; +use silius_primitives::{ + simulation::CodeHash, UserOperation, UserOperationHash, UserOperationSigned, +}; use std::collections::{HashMap, HashSet}; -impl AddRemoveUserOp for HashMap { - fn add( - &mut self, - uo: UserOperation, - ep: &Address, - chain_id: &U256, - ) -> Result { - let uo_hash = uo.hash(ep, chain_id); - self.insert(uo_hash, uo); - - Ok(uo_hash) +impl AddRemoveUserOp for HashMap { + fn add(&mut self, uo: UserOperation) -> Result { + self.insert(uo.hash, uo.user_operation); + Ok(uo.hash) } fn remove_by_uo_hash(&mut self, uo_hash: &UserOperationHash) -> Result { @@ -33,16 +28,23 @@ impl AddRemoveUserOp for HashMap { } } -impl UserOperationOp for HashMap { +impl UserOperationOp for HashMap { fn get_by_uo_hash( &self, uo_hash: &UserOperationHash, ) -> Result, MempoolErrorKind> { - Ok(self.get(uo_hash).cloned()) + if let Some(uo) = self.get(uo_hash) { + Ok(Some(UserOperation::from_user_operation_signed(*uo_hash, uo.clone()))) + } else { + Ok(None) + } } fn get_sorted(&self) -> Result, MempoolErrorKind> { - let mut uos: Vec = self.values().cloned().collect(); + let mut uos: Vec = self + .iter() + .map(|(hash, uo)| UserOperation::from_user_operation_signed(*hash, uo.clone())) + .collect(); uos.sort_by(|a, b| { if a.max_priority_fee_per_gas != b.max_priority_fee_per_gas { b.max_priority_fee_per_gas.cmp(&a.max_priority_fee_per_gas) @@ -54,7 +56,10 @@ impl UserOperationOp for HashMap { } fn get_all(&self) -> Result, MempoolErrorKind> { - Ok(self.values().cloned().collect()) + Ok(self + .iter() + .map(|(hash, uo)| UserOperation::from_user_operation_signed(*hash, uo.clone())) + .collect()) } } @@ -136,7 +141,7 @@ impl ClearOp for HashMap> { } } -impl ClearOp for HashMap { +impl ClearOp for HashMap { fn clear(&mut self) { self.clear() } @@ -157,7 +162,7 @@ mod tests { #[tokio::test] async fn memory_mempool() { let mempool = Mempool::new( - HashMap::::default(), + HashMap::::default(), HashMap::>::default(), HashMap::>::default(), HashMap::>::default(), diff --git a/crates/mempool/src/mempool.rs b/crates/mempool/src/mempool.rs index 6b2842c9..c998e424 100644 --- a/crates/mempool/src/mempool.rs +++ b/crates/mempool/src/mempool.rs @@ -10,9 +10,10 @@ use std::sync::Arc; pub type MempoolId = H256; -pub fn mempool_id(ep: &Address, chain_id: &U256) -> MempoolId { +pub fn mempool_id(ep: &Address, chain_id: u64) -> MempoolId { H256::from_slice( - keccak256([to_checksum(ep, None).encode(), chain_id.encode()].concat()).as_slice(), + keccak256([to_checksum(ep, None).encode(), U256::from(chain_id).encode()].concat()) + .as_slice(), ) } @@ -22,18 +23,11 @@ pub trait AddRemoveUserOp { /// /// # Arguments /// * `uo` - The [UserOperation](UserOperation) to add - /// * `ep` - The [Address](Address) of the endpoint - /// * `chain_id` - The [EIP-155](https://eips.ethereum.org/EIPS/eip-155) Chain ID. /// /// # Returns /// * `Ok(UserOperationHash)` - The hash of the [UserOperation](UserOperation) that was added /// * `Err(MempoolErrorKind)` - If the [UserOperation](UserOperation) could not be added - fn add( - &mut self, - uo: UserOperation, - ep: &Address, - chain_id: &U256, - ) -> Result; + fn add(&mut self, uo: UserOperation) -> Result; /// Removes a [UserOperation](UserOperation) by its hash /// /// # Arguments @@ -47,13 +41,8 @@ pub trait AddRemoveUserOp { } impl AddRemoveUserOp for Arc> { - fn add( - &mut self, - uo: UserOperation, - ep: &Address, - chain_id: &U256, - ) -> Result { - self.write().add(uo, ep, chain_id) + fn add(&mut self, uo: UserOperation) -> Result { + self.write().add(uo) } fn remove_by_uo_hash(&mut self, uo_hash: &UserOperationHash) -> Result { @@ -389,15 +378,10 @@ where user_operations_code_hashes, } } - pub fn add( - &mut self, - uo: UserOperation, - ep: &Address, - chain_id: &U256, - ) -> Result { - let uo_hash = uo.hash(ep, chain_id); + pub fn add(&mut self, uo: UserOperation) -> Result { let (sender, factory, paymaster) = uo.get_entities(); - self.user_operations.add(uo, ep, chain_id)?; + let uo_hash = uo.hash; + self.user_operations.add(uo)?; self.user_operations_by_sender.add(&sender, uo_hash)?; if let Some(factory) = factory { self.user_operations_by_entity.add(&factory, uo_hash)?; diff --git a/crates/mempool/src/uopool.rs b/crates/mempool/src/uopool.rs index 015d343a..54334023 100644 --- a/crates/mempool/src/uopool.rs +++ b/crates/mempool/src/uopool.rs @@ -97,7 +97,7 @@ where p2p_channel: Option>, ) -> Self { Self { - id: mempool_id(&entry_point.address(), &chain.id().into()), + id: mempool_id(&entry_point.address(), chain.id()), entry_point, validator, mempool, @@ -220,10 +220,7 @@ where { self.remove_user_operation_by_entity(&address); } - return Err(MempoolError { - hash: uo.hash(&self.entry_point.address(), &self.chain.id().into()), - kind: err.into(), - }); + return Err(MempoolError { hash: uo.hash, kind: err.into() }); } }; @@ -234,7 +231,7 @@ where sd.unbounded_send((uo.clone(), res.verified_block)) .expect("Failed to send user operation to publish channel") }; - match self.mempool.add(uo.clone(), &self.entry_point.address(), &self.chain.id().into()) { + match self.mempool.add(uo.clone()) { Ok(uo_hash) => { // TODO: find better way to do it atomically if let Some(code_hashes) = res.code_hashes { @@ -263,10 +260,7 @@ where Ok(uo_hash) } - Err(e) => Err(MempoolError { - hash: uo.hash(&self.entry_point.address(), &self.chain.id().into()), - kind: e, - }), + Err(e) => Err(MempoolError { hash: uo.hash, kind: e }), } } @@ -310,8 +304,6 @@ where continue; } - let uo_hash = uo.hash(&self.entry_point.address(), &self.chain.id().into()); - let p_opt = get_address(&uo.paymaster_and_data.0); let f_opt = get_address(&uo.init_code.0); @@ -329,9 +321,10 @@ where match (p_st, f_st) { (Status::BANNED, _) | (_, Status::BANNED) => { - self.mempool.remove(&uo_hash).map_err(|err| { + self.mempool.remove(&uo.hash).map_err(|err| { format_err!( - "Removing a banned user operation {uo_hash:?} failed with error: {err:?}", + "Removing a banned user operation {:?} failed with error: {err:?}", + uo.hash, ) })?; continue; @@ -355,7 +348,7 @@ where UserOperationValidatorMode::SimulationTrace, ) .await; - debug!("Second validation for userop {:?} result: {:?}", uo_hash, val_out); + debug!("Second validation for userop {:?} result: {:?}", uo.hash, val_out); match val_out { Ok(val_out) => { @@ -406,9 +399,9 @@ where gas_total = gas_total_new; } Err(_) => { - self.mempool.remove(&uo_hash).map_err(|err| { + self.mempool.remove(&uo.hash).map_err(|err| { format_err!( - "Removing a user operation {uo_hash:?} with 2nd failed simulation failed with error: {err:?}", + "Removing a user operation {:?} with 2nd failed simulation failed with error: {err:?}", uo.hash, ) })?; continue; @@ -459,33 +452,27 @@ where UserOperationValidatorMode::SimulationTrace.into(), ) .await - .map_err(|err| MempoolError { - hash: uo.hash(&self.entry_point.address(), &self.chain.id().into()), - kind: err.into(), - })?; + .map_err(|err| MempoolError { hash: uo.hash, kind: err.into() })?; - match self.entry_point.simulate_execution(uo.clone()).await { + match self.entry_point.simulate_execution(uo.user_operation.clone()).await { Ok(_) => {} Err(err) => { return Err(MempoolError { - hash: uo.hash(&self.entry_point.address(), &self.chain.id().into()), + hash: uo.hash, kind: SimulationError::Execution { inner: err.to_string() }.into(), }) } } - let exec_res = match self.entry_point.simulate_handle_op(uo.clone()).await { + let exec_res = match self.entry_point.simulate_handle_op(uo.user_operation.clone()).await { Ok(res) => res, Err(err) => { - return Err(MempoolError { - hash: uo.hash(&self.entry_point.address(), &self.chain.id().into()), - kind: SimulationError::from(err).into(), - }) + return Err(MempoolError { hash: uo.hash, kind: SimulationError::from(err).into() }) } }; let base_fee_per_gas = self.base_fee_per_gas().await.map_err(|err| MempoolError { - hash: uo.hash(&self.entry_point.address(), &self.chain.id().into()), + hash: uo.hash, kind: MempoolErrorKind::Provider { inner: err.to_string() }, })?; let call_gas_limit = calculate_call_gas_limit( diff --git a/crates/mempool/src/utils.rs b/crates/mempool/src/utils.rs index 96dbcbe6..53b1ef2b 100644 --- a/crates/mempool/src/utils.rs +++ b/crates/mempool/src/utils.rs @@ -1,5 +1,5 @@ use ethers::types::{Address, H256, U256}; -use silius_primitives::{simulation::CodeHash, UserOperation}; +use silius_primitives::{simulation::CodeHash, UserOperationSigned}; use std::{collections::HashMap, ops::Deref}; pub fn equal_code_hashes(hashes: &Vec, hashes_prev: &Vec) -> bool { @@ -50,16 +50,16 @@ impl Default for Overhead { } impl Overhead { - /// Calculates the pre-verification gas of a [UserOperation](UserOperation) - /// The function first packs the [UserOperation](UserOperation) by calling the + /// Calculates the pre-verification gas of a [UserOperation](UserOperationSigned) + /// The function first packs the [UserOperation](UserOperationSigned) by calling the /// [pack](UserOperation::pack) method, then extracts the call data for gas calculation. /// /// # Arguments - /// `uo` - The [UserOperation](UserOperation) to calculate the pre-verification gas for + /// `uo` - The [UserOperation](UserOperationSigned) to calculate the pre-verification gas for /// /// # Returns - /// The pre-verification gas of the [UserOperation](UserOperation) - pub fn calculate_pre_verification_gas(&self, uo: &UserOperation) -> U256 { + /// The pre-verification gas of the [UserOperation](UserOperationSigned) + pub fn calculate_pre_verification_gas(&self, uo: &UserOperationSigned) -> U256 { let uo_pack = uo.pack(); let call_data = uo_pack.deref().iter().fold(U256::zero(), |acc, &x| { @@ -157,13 +157,13 @@ pub mod tests { use ethers::types::{Address, Bytes, H256, U256}; use silius_primitives::{ reputation::{ReputationEntry, Status}, - UserOperation, UserOperationHash, + UserOperation, UserOperationHash, UserOperationSigned, }; #[test] fn pre_verification_gas_calculation() { let gas_oh = Overhead::default(); - let uo = UserOperation { + let uo = UserOperationSigned { sender: "0xAB7e2cbFcFb6A5F33A75aD745C3E5fB48d689B54".parse().unwrap(), nonce: U256::zero(), init_code: "0xe19e9755942bb0bd0cccce25b1742596b8a8250b3bf2c3e70000000000000000000000001d9a2cb3638c2fc8bf9c01d088b79e75cd188b17000000000000000000000000789d9058feecf1948af429793e7f1eb4a75db2220000000000000000000000000000000000000000000000000000000000000000".parse().unwrap(), @@ -183,7 +183,7 @@ pub mod tests { #[test] fn pre_verification_gas_calculation_with_large_user_operation() { let gas_oh = Overhead::default(); - let uo = UserOperation { + let uo = UserOperationSigned { sender: "0xAB7e2cbFcFb6A5F33A75aD745C3E5fB48d689B54".parse().unwrap(), nonce: U256::max_value(), init_code: Bytes::from(vec![255; 1024]), // Large init_code @@ -211,7 +211,7 @@ pub mod tests { bundle_size: U256::from(1), sig_size: U256::from(65), }; - let uo = UserOperation { + let uo = UserOperationSigned { sender: "0xAB7e2cbFcFb6A5F33A75aD745C3E5fB48d689B54".parse().unwrap(), nonce: U256::max_value(), init_code: Bytes::from(vec![255; 1024]), // Large init_code @@ -242,7 +242,7 @@ pub mod tests { sig_size: U256::max_value(), }; - let uo = UserOperation { + let uo = UserOperationSigned { sender: Address::default(), nonce: U256::max_value(), init_code: Bytes::from(vec![255; 1024]), // Large init_code @@ -310,42 +310,58 @@ pub mod tests { Z: UserOperationCodeHashAct, { let ep = Address::random(); - let chain_id = U256::from(5); + let chain_id = 5_u64; let senders = vec![Address::random(), Address::random(), Address::random()]; - let mut uo: UserOperation; + let mut uo: UserOperationSigned; let mut uo_hash: UserOperationHash = Default::default(); for i in 0..2 { - uo = UserOperation { + uo = UserOperationSigned { sender: senders[0], nonce: U256::from(i), - ..UserOperation::random() + ..UserOperationSigned::random() }; - uo_hash = mempool.add(uo.clone(), &ep, &chain_id).unwrap(); + uo_hash = uo.hash(&ep, chain_id); - assert_eq!(mempool.get(&uo_hash).unwrap().unwrap(), uo); + assert_eq!( + mempool + .add(UserOperation::from_user_operation_signed(uo_hash, uo.clone())) + .unwrap(), + uo_hash + ); + assert_eq!(mempool.get(&uo_hash).unwrap().unwrap().user_operation, uo); - uo = UserOperation { + uo = UserOperationSigned { sender: senders[1], nonce: U256::from(i), - ..UserOperation::random() + ..UserOperationSigned::random() }; + uo_hash = uo.hash(&ep, chain_id); - uo_hash = mempool.add(uo.clone(), &ep, &chain_id).unwrap(); - - assert_eq!(mempool.get(&uo_hash).unwrap().unwrap(), uo); + assert_eq!( + mempool + .add(UserOperation::from_user_operation_signed(uo_hash, uo.clone())) + .unwrap(), + uo_hash + ); + assert_eq!(mempool.get(&uo_hash).unwrap().unwrap().user_operation, uo); } for i in 0..3 { - uo = UserOperation { + uo = UserOperationSigned { sender: senders[2], nonce: U256::from(i), - ..UserOperation::random() + ..UserOperationSigned::random() }; + uo_hash = uo.hash(&ep, chain_id); - uo_hash = mempool.add(uo.clone(), &ep, &chain_id).unwrap(); - - assert_eq!(mempool.get(&uo_hash).unwrap().unwrap(), uo); + assert_eq!( + mempool + .add(UserOperation::from_user_operation_signed(uo_hash, uo.clone())) + .unwrap(), + uo_hash + ); + assert_eq!(mempool.get(&uo_hash).unwrap().unwrap().user_operation, uo); } assert_eq!(mempool.get_all().unwrap().len(), 7); @@ -366,14 +382,20 @@ pub mod tests { assert_eq!(mempool.get_all_by_sender(&senders[0]).len(), 0); for i in 0..3 { - uo = UserOperation { + uo = UserOperationSigned { sender: senders[2], nonce: U256::from(i), max_priority_fee_per_gas: U256::from(i + 1), - ..UserOperation::random() + ..UserOperationSigned::random() }; + uo_hash = uo.hash(&ep, chain_id); - mempool.add(uo.clone(), &ep, &chain_id).unwrap(); + assert_eq!( + mempool + .add(UserOperation::from_user_operation_signed(uo_hash, uo.clone())) + .unwrap(), + uo_hash + ); } let sorted = mempool.get_sorted().unwrap(); @@ -383,13 +405,17 @@ pub mod tests { assert_eq!(sorted.len(), 3); assert_eq!(mempool.clear(), ()); - let uo = UserOperation { + uo = UserOperationSigned { sender: Address::random(), nonce: U256::from(0), max_priority_fee_per_gas: U256::from(1), - ..UserOperation::random() + ..UserOperationSigned::random() }; - let uo_hash = mempool.add(uo.clone(), &ep, &chain_id).unwrap(); + uo_hash = uo.hash(&ep, chain_id); + assert_eq!( + mempool.add(UserOperation::from_user_operation_signed(uo_hash, uo.clone())).unwrap(), + uo_hash + ); let code_hashes = vec![CodeHash { address: Address::random(), hash: H256::random() }]; mempool.set_code_hashes(&uo_hash, code_hashes.clone()).unwrap(); diff --git a/crates/mempool/src/validate/simulation_trace/code_hashes.rs b/crates/mempool/src/validate/simulation_trace/code_hashes.rs index 25ddb5e7..14e8c851 100644 --- a/crates/mempool/src/validate/simulation_trace/code_hashes.rs +++ b/crates/mempool/src/validate/simulation_trace/code_hashes.rs @@ -100,17 +100,15 @@ impl SimulationTraceCheck for CodeHashes { let hashes: &mut Vec = &mut vec![]; self.get_code_hashes(addrs, hashes, &helper.entry_point.eth_client()).await?; - let uo_hash = uo.hash(&helper.entry_point.address(), &helper.chain.id().into()); - - match mempool.has_code_hashes(&uo_hash) { + match mempool.has_code_hashes(&uo.hash) { Ok(true) => { // 2nd simulation let hashes_prev = mempool - .get_code_hashes(&uo_hash) + .get_code_hashes(&uo.hash) .map_err(|err| SimulationError::Other { inner: err.to_string() })?; debug!( "Veryfing {:?} code hashes in 2nd simulation: {:?} vs {:?}", - uo_hash, hashes, hashes_prev + uo.hash, hashes, hashes_prev ); if !equal_code_hashes(hashes, &hashes_prev) { return Err(SimulationError::CodeHashes {}); diff --git a/crates/mempool/src/validate/validator.rs b/crates/mempool/src/validate/validator.rs index be0e4da1..549e62b9 100644 --- a/crates/mempool/src/validate/validator.rs +++ b/crates/mempool/src/validate/validator.rs @@ -174,7 +174,7 @@ where &self, uo: &UserOperation, ) -> Result { - match self.entry_point.simulate_validation(uo.clone()).await { + match self.entry_point.simulate_validation(uo.user_operation.clone()).await { Ok(res) => Ok(res), Err(err) => Err(match err { EntryPointError::FailedOp(op) => SimulationError::Validation { inner: op.reason }, @@ -198,7 +198,7 @@ where &self, uo: &UserOperation, ) -> Result { - match self.entry_point.simulate_validation_trace(uo.clone()).await { + match self.entry_point.simulate_validation_trace(uo.user_operation.clone()).await { Ok(trace) => Ok(trace), Err(err) => Err(match err { EntryPointError::FailedOp(op) => SimulationError::Validation { inner: op.reason }, @@ -260,7 +260,7 @@ where } if let Some(uo) = mempool.get_prev_by_sender(uo) { - out.prev_hash = Some(uo.hash(&self.entry_point.address(), &self.chain.id().into())); + out.prev_hash = Some(uo.hash); } debug!("Simulate user operation from {:?}", uo.sender); let sim_res = self.simulate_validation(uo).await?; diff --git a/crates/p2p/src/network.rs b/crates/p2p/src/network.rs index 59545299..0b2be1fe 100644 --- a/crates/p2p/src/network.rs +++ b/crates/p2p/src/network.rs @@ -156,11 +156,14 @@ impl Network { return None; } }; - self.entrypoint_channels.iter().find_map(|(_, ep, _, new_coming_uos_ch)| { + self.entrypoint_channels.iter().find_map(|(chain, ep, _, new_coming_uos_ch)| { if *ep == userops.entrypoint_address() { for user_op in userops.clone().user_operations().into_iter() { new_coming_uos_ch - .unbounded_send(user_op) + .unbounded_send(UserOperation::from_user_operation_signed( + user_op.hash(ep, chain.id()), + user_op, + )) .expect("new useop channel should be open all the time"); } Some(()) diff --git a/crates/p2p/src/request_response/models.rs b/crates/p2p/src/request_response/models.rs index 208a7cdb..8a79b6ec 100644 --- a/crates/p2p/src/request_response/models.rs +++ b/crates/p2p/src/request_response/models.rs @@ -1,5 +1,5 @@ use crate::config::Metadata; -use silius_primitives::UserOperation; +use silius_primitives::UserOperationSigned; use ssz_rs::{List, Serialize, Vector}; use std::io; @@ -169,7 +169,7 @@ pub struct PooledUserOpHashes { #[derive(ssz_rs_derive::Serializable, Clone, Debug, PartialEq, Default)] pub struct PooledUserOpsByHash { - hashes: List, + hashes: List, } #[derive(Clone, Default)] diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index f7075518..74071986 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -26,7 +26,7 @@ futures-util = { workspace = true } tokio = { workspace = true } # misc -educe = { version = "0.5.9", features = ["Debug", "Default"] } +derive_more = "0.99.17" expanded-pathbuf = { workspace = true } eyre = { workspace = true } lazy_static = { workspace = true } diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index e3e64e7b..cf37ec8b 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -19,7 +19,7 @@ pub use mempool::Mode as UoPoolMode; pub use p2p::{PooledUserOps, UserOperationsWithEntryPoint}; pub use user_operation::{ UserOperation, UserOperationByHash, UserOperationGasEstimation, UserOperationHash, - UserOperationPartial, UserOperationReceipt, + UserOperationReceipt, UserOperationRequest, UserOperationSigned, }; pub use utils::get_address; pub use wallet::Wallet; diff --git a/crates/primitives/src/p2p.rs b/crates/primitives/src/p2p.rs index 2b14cd8f..db934f61 100644 --- a/crates/primitives/src/p2p.rs +++ b/crates/primitives/src/p2p.rs @@ -1,6 +1,6 @@ //! P2P primitives -use crate::UserOperation; +use crate::{UserOperation, UserOperationSigned}; use alloy_chains::Chain; use ethers::types::{Address, U256 as EthersU256}; use ssz_rs::{List, Vector, U256}; @@ -13,7 +13,7 @@ pub struct UserOperationsWithEntryPoint { entrypoint_contract: Vector, verified_at_block_hash: U256, chain_id: U256, - user_operations: List, + user_operations: List, } impl UserOperationsWithEntryPoint { @@ -29,18 +29,19 @@ impl UserOperationsWithEntryPoint { let mut buf: [u8; 32] = [0; 32]; chain_id.to_little_endian(&mut buf); let chain_id = U256::from_bytes_le(buf); + let uos: Vec = user_operations.into_iter().map(Into::into).collect(); Self { entrypoint_contract: >::try_from(entrypoint_address.as_bytes().to_vec()) .expect("entrypoint address is valid"), verified_at_block_hash, chain_id, // FIXME: should have a bound check here or return Err - user_operations: >::try_from(user_operations) + user_operations: >::try_from(uos) .expect("Too many user operations"), } } - pub fn user_operations(self) -> Vec { + pub fn user_operations(self) -> Vec { self.user_operations.to_vec() } @@ -58,5 +59,5 @@ impl UserOperationsWithEntryPoint { pub struct PooledUserOps { mempool_id: Vector, more_flag: u64, - user_operations: List, + user_operations: List, } diff --git a/crates/primitives/src/reputation.rs b/crates/primitives/src/reputation.rs index c2ea4d6b..d780067f 100644 --- a/crates/primitives/src/reputation.rs +++ b/crates/primitives/src/reputation.rs @@ -1,7 +1,6 @@ //! Primitives for reputation use super::utils::{as_checksum_addr, as_hex_string, as_u64}; -use educe::Educe; use ethers::{ prelude::{EthAbiCodec, EthAbiType}, types::{Address, U256}, @@ -11,8 +10,7 @@ use serde::{Deserialize, Serialize}; pub type ReputationStatus = u64; /// All possible reputation statuses -#[derive(Default, Clone, Educe, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] -#[educe(Debug)] +#[derive(Default, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] pub enum Status { #[default] @@ -46,7 +44,7 @@ impl From for Status { #[derive( Default, Clone, - Educe, + Debug, Eq, PartialEq, PartialOrd, @@ -56,7 +54,6 @@ impl From for Status { EthAbiCodec, EthAbiType, )] -#[educe(Debug)] pub struct ReputationEntry { pub address: Address, #[serde(rename = "opsSeen", serialize_with = "as_hex_string")] @@ -74,8 +71,7 @@ impl ReputationEntry { } /// Stake info -#[derive(Clone, Copy, Default, Educe, Eq, PartialEq, Serialize, Deserialize)] -#[educe(Debug)] +#[derive(Clone, Copy, Default, Debug, Eq, PartialEq, Serialize, Deserialize)] pub struct StakeInfo { #[serde(rename = "addr", serialize_with = "as_checksum_addr")] pub address: Address, @@ -92,8 +88,7 @@ impl StakeInfo { } /// Stake info response for RPC -#[derive(Clone, Copy, Default, Educe, Eq, PartialEq, Serialize, Deserialize)] -#[educe(Debug)] +#[derive(Clone, Copy, Default, Debug, Eq, PartialEq, Serialize, Deserialize)] pub struct StakeInfoResponse { #[serde(rename = "stakeInfo")] pub stake_info: StakeInfo, diff --git a/crates/primitives/src/user_operation/hash.rs b/crates/primitives/src/user_operation/hash.rs new file mode 100644 index 00000000..2775aa99 --- /dev/null +++ b/crates/primitives/src/user_operation/hash.rs @@ -0,0 +1,69 @@ +//! User operation hash related types and helpers + +use ethers::types::H256; +use rustc_hex::FromHexError; +use serde::{Deserialize, Serialize}; +use std::str::FromStr; + +/// User operation hash +#[derive( + Eq, Hash, PartialEq, Debug, Serialize, Deserialize, Clone, Copy, Default, PartialOrd, Ord, +)] +pub struct UserOperationHash(pub H256); + +impl From for UserOperationHash { + fn from(value: H256) -> Self { + Self(value) + } +} + +impl From for H256 { + fn from(value: UserOperationHash) -> Self { + value.0 + } +} + +impl From<[u8; 32]> for UserOperationHash { + fn from(value: [u8; 32]) -> Self { + Self(H256::from_slice(&value)) + } +} + +impl FromStr for UserOperationHash { + type Err = FromHexError; + fn from_str(s: &str) -> Result { + H256::from_str(s).map(|h| h.into()) + } +} + +impl UserOperationHash { + #[inline] + pub const fn as_fixed_bytes(&self) -> &[u8; 32] { + &self.0 .0 + } + + #[inline] + pub fn as_bytes_mut(&mut self) -> &mut [u8] { + &mut self.0 .0 + } + + #[inline] + pub const fn repeat_byte(byte: u8) -> UserOperationHash { + UserOperationHash(H256([byte; 32])) + } + + #[inline] + pub const fn zero() -> UserOperationHash { + UserOperationHash::repeat_byte(0u8) + } + + pub fn assign_from_slice(&mut self, src: &[u8]) { + self.as_bytes_mut().copy_from_slice(src); + } + + pub fn from_slice(src: &[u8]) -> Self { + let mut ret = Self::zero(); + ret.assign_from_slice(src); + ret + } +} diff --git a/crates/primitives/src/user_operation.rs b/crates/primitives/src/user_operation/mod.rs similarity index 72% rename from crates/primitives/src/user_operation.rs rename to crates/primitives/src/user_operation/mod.rs index e8c5cdc7..eb08b738 100644 --- a/crates/primitives/src/user_operation.rs +++ b/crates/primitives/src/user_operation/mod.rs @@ -1,36 +1,65 @@ //! Basic transaction type for account abstraction (ERC-4337) -use super::utils::{as_checksum_addr, as_checksum_bytes, get_address}; +mod hash; +mod request; + +use crate::{get_address, utils::as_checksum_addr}; +use derive_more::{AsRef, Deref}; use ethers::{ abi::AbiEncode, - prelude::{EthAbiCodec, EthAbiType}, + contract::{EthAbiCodec, EthAbiType}, types::{Address, Bytes, Log, TransactionReceipt, H256, U256, U64}, utils::keccak256, }; -use rustc_hex::FromHexError; +pub use hash::UserOperationHash; +pub use request::UserOperationRequest; use serde::{Deserialize, Serialize}; use ssz_rs::List; -use std::{ops::Deref, slice::Windows, str::FromStr}; +use std::{cmp::Ord, ops::Deref, slice::Windows}; -/// This could be increased if we found bigger bytes, the propper value is not sure right now. -const MAXIMUM_SSZ_BYTES_LENGTH: usize = 1024; +/// User operation with hash +#[derive(AsRef, Deref, Debug, Clone)] +pub struct UserOperation { + /// Hash of the user operation + pub hash: UserOperationHash, + + /// Raw user operation + #[deref] + #[as_ref] + pub user_operation: UserOperationSigned, +} + +impl UserOperation { + pub fn from_user_operation_signed( + hash: UserOperationHash, + user_operation: UserOperationSigned, + ) -> Self { + Self { hash, user_operation } + } +} -/// Transaction type for account abstraction (ERC-4337) +impl From for UserOperationSigned { + fn from(value: UserOperation) -> Self { + value.user_operation + } +} + +/// User operation #[derive( + Default, Clone, Debug, - Default, + Ord, + PartialOrd, PartialEq, Eq, - PartialOrd, - Ord, - Serialize, - Deserialize, EthAbiCodec, EthAbiType, + Serialize, + Deserialize, )] #[serde(rename_all = "camelCase")] -pub struct UserOperation { +pub struct UserOperationSigned { /// Sender of the user operation #[serde(serialize_with = "as_checksum_addr")] pub sender: Address, @@ -39,7 +68,6 @@ pub struct UserOperation { pub nonce: U256, /// Init code for the account (needed if account not yet deployed and needs to be created) - #[serde(serialize_with = "as_checksum_bytes")] pub init_code: Bytes, /// The data that is passed to the sender during the main execution call @@ -69,7 +97,39 @@ pub struct UserOperation { pub signature: Bytes, } -impl UserOperation { +/// User operation without signature (helper for packing user operation) +#[derive(EthAbiCodec, EthAbiType)] +struct UserOperationNoSignature { + pub sender: Address, + pub nonce: U256, + pub init_code: H256, + pub call_data: H256, + pub call_gas_limit: U256, + pub verification_gas_limit: U256, + pub pre_verification_gas: U256, + pub max_fee_per_gas: U256, + pub max_priority_fee_per_gas: U256, + pub paymaster_and_data: H256, +} + +impl From for UserOperationNoSignature { + fn from(value: UserOperationSigned) -> Self { + Self { + sender: value.sender, + nonce: value.nonce, + init_code: keccak256(value.init_code.deref()).into(), + call_data: keccak256(value.call_data.deref()).into(), + call_gas_limit: value.call_gas_limit, + verification_gas_limit: value.verification_gas_limit, + pre_verification_gas: value.pre_verification_gas, + max_fee_per_gas: value.max_fee_per_gas, + max_priority_fee_per_gas: value.max_priority_fee_per_gas, + paymaster_and_data: keccak256(value.paymaster_and_data.deref()).into(), + } + } +} + +impl UserOperationSigned { /// Packs the user operation into bytes pub fn pack(&self) -> Bytes { self.clone().encode().into() @@ -77,18 +137,18 @@ impl UserOperation { /// Packs the user operation without signature to bytes (used for calculating the hash) pub fn pack_without_signature(&self) -> Bytes { - let user_operation_packed = UserOperationUnsigned::from(self.clone()); + let user_operation_packed = UserOperationNoSignature::from(self.clone()); user_operation_packed.encode().into() } /// Calculates the hash of the user operation - pub fn hash(&self, entry_point: &Address, chain_id: &U256) -> UserOperationHash { + pub fn hash(&self, entry_point: &Address, chain_id: u64) -> UserOperationHash { H256::from_slice( keccak256( [ keccak256(self.pack_without_signature().deref()).to_vec(), entry_point.encode(), - chain_id.encode(), + U256::from(chain_id).encode(), ] .concat(), ) @@ -176,7 +236,7 @@ impl UserOperation { /// Creates random user operation (for testing purposes) #[cfg(feature = "test-utils")] pub fn random() -> Self { - UserOperation::default() + UserOperationSigned::default() .sender(Address::random()) .verification_gas_limit(100_000.into()) .pre_verification_gas(21_000.into()) @@ -184,242 +244,8 @@ impl UserOperation { } } -/// User operation hash -#[derive( - Eq, Hash, PartialEq, Debug, Serialize, Deserialize, Clone, Copy, Default, PartialOrd, Ord, -)] -pub struct UserOperationHash(pub H256); - -impl From for UserOperationHash { - fn from(value: H256) -> Self { - Self(value) - } -} - -impl From for H256 { - fn from(value: UserOperationHash) -> Self { - value.0 - } -} - -impl From<[u8; 32]> for UserOperationHash { - fn from(value: [u8; 32]) -> Self { - Self(H256::from_slice(&value)) - } -} - -impl FromStr for UserOperationHash { - type Err = FromHexError; - fn from_str(s: &str) -> Result { - H256::from_str(s).map(|h| h.into()) - } -} - -impl UserOperationHash { - #[inline] - pub const fn as_fixed_bytes(&self) -> &[u8; 32] { - &self.0 .0 - } - - #[inline] - pub fn as_bytes_mut(&mut self) -> &mut [u8] { - &mut self.0 .0 - } - - #[inline] - pub const fn repeat_byte(byte: u8) -> UserOperationHash { - UserOperationHash(H256([byte; 32])) - } - - #[inline] - pub const fn zero() -> UserOperationHash { - UserOperationHash::repeat_byte(0u8) - } - - pub fn assign_from_slice(&mut self, src: &[u8]) { - self.as_bytes_mut().copy_from_slice(src); - } - - pub fn from_slice(src: &[u8]) -> Self { - let mut ret = Self::zero(); - ret.assign_from_slice(src); - ret - } -} - -/// User operation without signature -#[derive(EthAbiCodec, EthAbiType)] -pub struct UserOperationUnsigned { - pub sender: Address, - pub nonce: U256, - pub init_code: H256, - pub call_data: H256, - pub call_gas_limit: U256, - pub verification_gas_limit: U256, - pub pre_verification_gas: U256, - pub max_fee_per_gas: U256, - pub max_priority_fee_per_gas: U256, - pub paymaster_and_data: H256, -} - -impl From for UserOperationUnsigned { - fn from(value: UserOperation) -> Self { - Self { - sender: value.sender, - nonce: value.nonce, - init_code: keccak256(value.init_code.deref()).into(), - call_data: keccak256(value.call_data.deref()).into(), - call_gas_limit: value.call_gas_limit, - verification_gas_limit: value.verification_gas_limit, - pre_verification_gas: value.pre_verification_gas, - max_fee_per_gas: value.max_fee_per_gas, - max_priority_fee_per_gas: value.max_priority_fee_per_gas, - paymaster_and_data: keccak256(value.paymaster_and_data.deref()).into(), - } - } -} - -/// Receipt of the user operation (returned from the RPC endpoint eth_getUserOperationReceipt) -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct UserOperationReceipt { - #[serde(rename = "userOpHash")] - pub user_operation_hash: UserOperationHash, - #[serde(serialize_with = "as_checksum_addr")] - pub sender: Address, - pub nonce: U256, - #[serde(skip_serializing_if = "Option::is_none")] - pub paymaster: Option
, - pub actual_gas_cost: U256, - pub actual_gas_used: U256, - pub success: bool, - pub reason: String, - pub logs: Vec, - #[serde(rename = "receipt")] - pub tx_receipt: TransactionReceipt, -} - -/// Struct that is returned from the RPC endpoint eth_getUserOperationByHash -#[derive(Clone, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct UserOperationByHash { - pub user_operation: UserOperation, - #[serde(serialize_with = "as_checksum_addr")] - pub entry_point: Address, - pub transaction_hash: H256, - pub block_hash: H256, - pub block_number: U64, -} - -/// User operation with all fields being optional -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct UserOperationPartial { - pub sender: Option
, - pub nonce: Option, - pub init_code: Option, - pub call_data: Option, - pub call_gas_limit: Option, - pub verification_gas_limit: Option, - pub pre_verification_gas: Option, - pub max_fee_per_gas: Option, - pub max_priority_fee_per_gas: Option, - pub paymaster_and_data: Option, - pub signature: Option, -} - -impl From for UserOperation { - fn from(user_operation: UserOperationPartial) -> Self { - Self { - sender: { - if let Some(sender) = user_operation.sender { - sender - } else { - Address::zero() - } - }, - nonce: { - if let Some(nonce) = user_operation.nonce { - nonce - } else { - U256::zero() - } - }, - init_code: { - if let Some(init_code) = user_operation.init_code { - init_code - } else { - Bytes::default() - } - }, - call_data: { - if let Some(call_data) = user_operation.call_data { - call_data - } else { - Bytes::default() - } - }, - call_gas_limit: { - if let Some(call_gas_limit) = user_operation.call_gas_limit { - call_gas_limit - } else { - U256::zero() - } - }, - verification_gas_limit: { - if let Some(verification_gas_limit) = user_operation.verification_gas_limit { - verification_gas_limit - } else { - U256::zero() - } - }, - pre_verification_gas: { - if let Some(pre_verification_gas) = user_operation.pre_verification_gas { - pre_verification_gas - } else { - U256::zero() - } - }, - max_fee_per_gas: { - if let Some(max_fee_per_gas) = user_operation.max_fee_per_gas { - max_fee_per_gas - } else { - U256::zero() - } - }, - max_priority_fee_per_gas: { - if let Some(max_priority_fee_per_gas) = user_operation.max_priority_fee_per_gas { - max_priority_fee_per_gas - } else { - U256::zero() - } - }, - paymaster_and_data: { - if let Some(paymaster_and_data) = user_operation.paymaster_and_data { - paymaster_and_data - } else { - Bytes::default() - } - }, - signature: { - if let Some(signature) = user_operation.signature { - signature - } else { - Bytes::default() - } - }, - } - } -} - -/// Gas estimations for user operation (returned from the RPC endpoint eth_estimateUserOperationGas) -#[derive(Clone, Debug, Serialize, Deserialize)] -#[serde(rename_all = "camelCase")] -pub struct UserOperationGasEstimation { - pub pre_verification_gas: U256, - pub verification_gas_limit: U256, - pub call_gas_limit: U256, -} +/// This could be increased if we found bigger bytes, not sure about the proper value right now. +const MAXIMUM_SSZ_BYTES_LENGTH: usize = 1024; fn btyes_to_list( value: &Bytes, @@ -429,7 +255,7 @@ fn btyes_to_list( .map_err(|(data, _)| ssz_rs::SerializeError::MaximumEncodedLengthReached(data.len())) } -impl ssz_rs::Serialize for UserOperation { +impl ssz_rs::Serialize for UserOperationSigned { fn serialize(&self, buffer: &mut Vec) -> Result { let mut serializer = ssz_rs::__internal::Serializer::default(); serializer.with_element(&self.sender.0)?; @@ -483,7 +309,7 @@ fn ssz_unpack_bytes( Ok((bytes_data, end - start)) } -impl ssz_rs::Deserialize for UserOperation { +impl ssz_rs::Deserialize for UserOperationSigned { fn deserialize(encoding: &[u8]) -> Result where Self: Sized, @@ -584,7 +410,7 @@ impl ssz_rs::Deserialize for UserOperation { } } -impl ssz_rs::Serializable for UserOperation { +impl ssz_rs::Serializable for UserOperationSigned { fn is_variable_size() -> bool { true } @@ -593,26 +419,68 @@ impl ssz_rs::Serializable for UserOperation { 0 } } + +/// Receipt of the user operation (returned from the RPC endpoint eth_getUserOperationReceipt) +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct UserOperationReceipt { + #[serde(rename = "userOpHash")] + pub user_operation_hash: UserOperationHash, + #[serde(serialize_with = "as_checksum_addr")] + pub sender: Address, + pub nonce: U256, + #[serde(skip_serializing_if = "Option::is_none")] + pub paymaster: Option
, + pub actual_gas_cost: U256, + pub actual_gas_used: U256, + pub success: bool, + pub reason: String, + pub logs: Vec, + #[serde(rename = "receipt")] + pub tx_receipt: TransactionReceipt, +} + +/// Struct that is returned from the RPC endpoint eth_getUserOperationByHash +#[derive(Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct UserOperationByHash { + pub user_operation: UserOperationSigned, + #[serde(serialize_with = "as_checksum_addr")] + pub entry_point: Address, + pub transaction_hash: H256, + pub block_hash: H256, + pub block_number: U64, +} + +/// Gas estimations for user operation (returned from the RPC endpoint eth_estimateUserOperationGas) +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct UserOperationGasEstimation { + pub pre_verification_gas: U256, + pub verification_gas_limit: U256, + pub call_gas_limit: U256, +} + #[cfg(test)] mod tests { - use super::*; + use std::str::FromStr; #[test] - fn user_operation_pack() { + fn user_operation_signed_pack() { let uos = vec![ - UserOperation::default().verification_gas_limit(100_000.into()).pre_verification_gas(21_000.into()).max_priority_fee_per_gas(1_000_000_000.into()), - UserOperation::default().sender("0x9c5754De1443984659E1b3a8d1931D83475ba29C".parse().unwrap()).call_gas_limit(200_000.into()).verification_gas_limit(100_000.into()).pre_verification_gas(21_000.into()).max_fee_per_gas(3_000_000_000_u64.into()).max_priority_fee_per_gas(1_000_000_000.into()).signature("0x7cb39607585dee8e297d0d7a669ad8c5e43975220b6773c10a138deadbc8ec864981de4b9b3c735288a217115fb33f8326a61ddabc60a534e3b5536515c70f931c".parse().unwrap()), + UserOperationSigned::default().verification_gas_limit(100_000.into()).pre_verification_gas(21_000.into()).max_priority_fee_per_gas(1_000_000_000.into()), + UserOperationSigned::default().sender("0x9c5754De1443984659E1b3a8d1931D83475ba29C".parse().unwrap()).call_gas_limit(200_000.into()).verification_gas_limit(100_000.into()).pre_verification_gas(21_000.into()).max_fee_per_gas(3_000_000_000_u64.into()).max_priority_fee_per_gas(1_000_000_000.into()).signature("0x7cb39607585dee8e297d0d7a669ad8c5e43975220b6773c10a138deadbc8ec864981de4b9b3c735288a217115fb33f8326a61ddabc60a534e3b5536515c70f931c".parse().unwrap()), ]; assert_eq!(uos[0].pack(), "0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000186a000000000000000000000000000000000000000000000000000000000000052080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003b9aca0000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000".parse::().unwrap()); assert_eq!(uos[1].pack(), "0x0000000000000000000000009c5754de1443984659e1b3a8d1931d83475ba29c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000030d4000000000000000000000000000000000000000000000000000000000000186a0000000000000000000000000000000000000000000000000000000000000520800000000000000000000000000000000000000000000000000000000b2d05e00000000000000000000000000000000000000000000000000000000003b9aca0000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000417cb39607585dee8e297d0d7a669ad8c5e43975220b6773c10a138deadbc8ec864981de4b9b3c735288a217115fb33f8326a61ddabc60a534e3b5536515c70f931c00000000000000000000000000000000000000000000000000000000000000".parse::().unwrap()); } #[test] - fn user_operation_pack_without_signature() { + fn user_operation_signed_pack_without_signature() { let uos = vec![ - UserOperation::default().verification_gas_limit(100_000.into()).pre_verification_gas(21_000.into()).max_priority_fee_per_gas(1_000_000_000.into()), - UserOperation { + UserOperationSigned::default().verification_gas_limit(100_000.into()).pre_verification_gas(21_000.into()).max_priority_fee_per_gas(1_000_000_000.into()), + UserOperationSigned { sender: "0x9c5754De1443984659E1b3a8d1931D83475ba29C".parse().unwrap(), nonce: 1.into(), init_code: Bytes::default(), @@ -631,10 +499,10 @@ mod tests { } #[test] - fn user_operation_hash() { + fn user_operation_signed_hash() { let uos = vec![ - UserOperation::default().verification_gas_limit(100_000.into()).pre_verification_gas(21_000.into()).max_priority_fee_per_gas(1_000_000_000.into()), - UserOperation { + UserOperationSigned::default().verification_gas_limit(100_000.into()).pre_verification_gas(21_000.into()).max_priority_fee_per_gas(1_000_000_000.into()), + UserOperationSigned { sender: "0x9c5754De1443984659E1b3a8d1931D83475ba29C".parse().unwrap(), nonce: U256::zero(), init_code: "0x9406cc6185a346906296840746125a0e449764545fbfb9cf000000000000000000000000ce0fefa6f7979c4c9b5373e0f5105b7259092c6d0000000000000000000000000000000000000000000000000000000000000000".parse().unwrap(), @@ -649,20 +517,14 @@ mod tests { }, ]; assert_eq!( - uos[0].hash( - &"0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789".parse().unwrap(), - &80_001.into() - ), + uos[0].hash(&"0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789".parse().unwrap(), 80_001), "0x95418c07086df02ff6bc9e8bdc150b380cb761beecc098630440bcec6e862702" .parse::() .unwrap() .into() ); assert_eq!( - uos[1].hash( - &"0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789".parse().unwrap(), - &80_001.into() - ), + uos[1].hash(&"0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789".parse().unwrap(), 80_001), "0x7c1b8c9df49a9e09ecef0f0fe6841d895850d29820f9a4b494097764085dcd7e" .parse::() .unwrap() @@ -671,8 +533,8 @@ mod tests { } #[test] - fn user_operation_ssz() { - let uo = UserOperation { + fn user_operation_signed_ssz() { + let uo = UserOperationSigned { sender: "0x1F9090AAE28B8A3DCEADF281B0F12828E676C326".parse().unwrap(), nonce: 100.into(), init_code: "0x9406cc6185a346906296840746125a0e449764545fbfb9cf000000000000000000000000ce0fefa6f7979c4c9b5373e0f5105b7259092c6d0000000000000000000000000000000000000000000000000000000000000000".parse().unwrap(), @@ -688,11 +550,11 @@ mod tests { let mut encoded = Vec::new(); ssz_rs::Serialize::serialize(&uo, &mut encoded).unwrap(); // generated by python codes - let expected_encode =Bytes::from_str("1f9090aae28b8a3dceadf281b0f12828e676c3266400000000000000000000000000000000000000000000000000000000000000e40000003c010000a086010000000000000000000000000000000000000000000000000000000000f483050000000000000000000000000000000000000000000000000000000000b4af000000000000000000000000000000000000000000000000000000000000dea5076500000000000000000000000000000000000000000000000000000000c0a5076500000000000000000000000000000000000000000000000000000000c0010000c10100009406cc6185a346906296840746125a0e449764545fbfb9cf000000000000000000000000ce0fefa6f7979c4c9b5373e0f5105b7259092c6d0000000000000000000000000000000000000000000000000000000000000000b61d27f60000000000000000000000009c5754de1443984659e1b3a8d1931d83475ba29c00000000000000000000000000000000000000000000000000005af3107a4000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000001febfd4657afe1f1c05c1ec65f3f9cc992a3ac083c424454ba61eab93152195e1400d74df01fc9fa53caadcb83a891d478b713016bcc0c64307c1ad3d7ea2e2d921b").unwrap().to_vec(); + let expected_encode = Bytes::from_str("1f9090aae28b8a3dceadf281b0f12828e676c3266400000000000000000000000000000000000000000000000000000000000000e40000003c010000a086010000000000000000000000000000000000000000000000000000000000f483050000000000000000000000000000000000000000000000000000000000b4af000000000000000000000000000000000000000000000000000000000000dea5076500000000000000000000000000000000000000000000000000000000c0a5076500000000000000000000000000000000000000000000000000000000c0010000c10100009406cc6185a346906296840746125a0e449764545fbfb9cf000000000000000000000000ce0fefa6f7979c4c9b5373e0f5105b7259092c6d0000000000000000000000000000000000000000000000000000000000000000b61d27f60000000000000000000000009c5754de1443984659e1b3a8d1931d83475ba29c00000000000000000000000000000000000000000000000000005af3107a4000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000001febfd4657afe1f1c05c1ec65f3f9cc992a3ac083c424454ba61eab93152195e1400d74df01fc9fa53caadcb83a891d478b713016bcc0c64307c1ad3d7ea2e2d921b").unwrap().to_vec(); assert_eq!(encoded, expected_encode); let uo_decode = - ::deserialize(&expected_encode).unwrap(); + ::deserialize(&expected_encode).unwrap(); assert_eq!(uo_decode.sender, uo.sender); assert_eq!(uo_decode.nonce, uo.nonce); diff --git a/crates/primitives/src/user_operation/request.rs b/crates/primitives/src/user_operation/request.rs new file mode 100644 index 00000000..aaa4ed19 --- /dev/null +++ b/crates/primitives/src/user_operation/request.rs @@ -0,0 +1,106 @@ +//! User operation request (optional fields) + +use super::UserOperationSigned; +use crate::utils::{as_checksum_addr, as_checksum_bytes}; +use ethers::types::{Address, Bytes, U256}; +use serde::{Deserialize, Serialize}; + +/// User operation with all fields being optional +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct UserOperationRequest { + #[serde(default = "Address::zero", serialize_with = "as_checksum_addr")] + pub sender: Address, + #[serde(default)] + pub nonce: U256, + #[serde(default, serialize_with = "as_checksum_bytes")] + pub init_code: Bytes, + #[serde(default)] + pub call_data: Bytes, + #[serde(default)] + pub call_gas_limit: Option, + #[serde(default)] + pub verification_gas_limit: Option, + #[serde(default)] + pub pre_verification_gas: Option, + #[serde(default)] + pub max_fee_per_gas: Option, + #[serde(default)] + pub max_priority_fee_per_gas: Option, + #[serde(default)] + pub paymaster_and_data: Bytes, + #[serde(default)] + pub signature: Option, +} + +impl From for UserOperationSigned { + fn from(user_operation: UserOperationRequest) -> Self { + Self { + sender: user_operation.sender, + nonce: user_operation.nonce, + init_code: user_operation.init_code, + call_data: user_operation.call_data, + call_gas_limit: { + if let Some(call_gas_limit) = user_operation.call_gas_limit { + call_gas_limit + } else { + U256::zero() + } + }, + verification_gas_limit: { + if let Some(verification_gas_limit) = user_operation.verification_gas_limit { + verification_gas_limit + } else { + U256::zero() + } + }, + pre_verification_gas: { + if let Some(pre_verification_gas) = user_operation.pre_verification_gas { + pre_verification_gas + } else { + U256::zero() + } + }, + max_fee_per_gas: { + if let Some(max_fee_per_gas) = user_operation.max_fee_per_gas { + max_fee_per_gas + } else { + U256::zero() + } + }, + max_priority_fee_per_gas: { + if let Some(max_priority_fee_per_gas) = user_operation.max_priority_fee_per_gas { + max_priority_fee_per_gas + } else { + U256::zero() + } + }, + paymaster_and_data: user_operation.paymaster_and_data, + signature: { + if let Some(signature) = user_operation.signature { + signature + } else { + Bytes::default() + } + }, + } + } +} + +impl From for UserOperationRequest { + fn from(user_operation: UserOperationSigned) -> Self { + Self { + sender: user_operation.sender, + nonce: user_operation.nonce, + init_code: user_operation.init_code, + call_data: user_operation.call_data, + call_gas_limit: Some(user_operation.call_gas_limit), + verification_gas_limit: Some(user_operation.verification_gas_limit), + pre_verification_gas: Some(user_operation.pre_verification_gas), + max_fee_per_gas: Some(user_operation.max_fee_per_gas), + max_priority_fee_per_gas: Some(user_operation.max_priority_fee_per_gas), + paymaster_and_data: user_operation.paymaster_and_data, + signature: Some(user_operation.signature), + } + } +} diff --git a/crates/primitives/src/wallet.rs b/crates/primitives/src/wallet.rs index 7c2fd6bd..4fa9f7d5 100644 --- a/crates/primitives/src/wallet.rs +++ b/crates/primitives/src/wallet.rs @@ -1,10 +1,10 @@ //! Wrapper around an ethers wallet with an optional field for Flashbots bundle identifier -use crate::UserOperation; +use crate::{UserOperation, UserOperationSigned}; use ethers::{ prelude::{k256::ecdsa::SigningKey, rand}, signers::{coins_bip39::English, MnemonicBuilder, Signer}, - types::{Address, U256}, + types::Address, }; use expanded_pathbuf::ExpandedPathBuf; use std::fs; @@ -32,7 +32,7 @@ impl Wallet { /// * `Self` - A new `Wallet` instance pub fn build_random( path: ExpandedPathBuf, - chain_id: &U256, + chain_id: u64, flashbots_key: bool, ) -> eyre::Result { let mut rng = rand::thread_rng(); @@ -57,11 +57,11 @@ impl Wallet { .build()?; Ok(Self { - signer: wallet.with_chain_id(chain_id.as_u64()), - flashbots_signer: Some(flashbots_wallet.with_chain_id(chain_id.as_u64())), + signer: wallet.with_chain_id(chain_id), + flashbots_signer: Some(flashbots_wallet.with_chain_id(chain_id)), }) } else { - Ok(Self { signer: wallet.with_chain_id(chain_id.as_u64()), flashbots_signer: None }) + Ok(Self { signer: wallet.with_chain_id(chain_id), flashbots_signer: None }) } } @@ -78,7 +78,7 @@ impl Wallet { /// * `Self` - A new `Wallet` instance pub fn from_file( path: ExpandedPathBuf, - chain_id: &U256, + chain_id: u64, flashbots_key: bool, ) -> eyre::Result { let wallet_builder = MnemonicBuilder::::default().phrase(path.to_path_buf()); @@ -96,11 +96,11 @@ impl Wallet { .build()?; Ok(Self { - signer: wallet.with_chain_id(chain_id.as_u64()), - flashbots_signer: Some(flashbots_wallet.with_chain_id(chain_id.as_u64())), + signer: wallet.with_chain_id(chain_id), + flashbots_signer: Some(flashbots_wallet.with_chain_id(chain_id)), }) } else { - Ok(Self { signer: wallet.with_chain_id(chain_id.as_u64()), flashbots_signer: None }) + Ok(Self { signer: wallet.with_chain_id(chain_id), flashbots_signer: None }) } } @@ -115,7 +115,7 @@ impl Wallet { /// /// # Returns /// * `Self` - A new `Wallet` instance - pub fn from_phrase(phrase: &str, chain_id: &U256, flashbots_key: bool) -> eyre::Result { + pub fn from_phrase(phrase: &str, chain_id: u64, flashbots_key: bool) -> eyre::Result { let wallet_builder = MnemonicBuilder::::default().phrase(phrase); let wallet = wallet_builder @@ -130,31 +130,34 @@ impl Wallet { .expect("Failed to derive wallet") .build()?; Ok(Self { - signer: wallet.with_chain_id(chain_id.as_u64()), - flashbots_signer: Some(flashbots_wallet.with_chain_id(chain_id.as_u64())), + signer: wallet.with_chain_id(chain_id), + flashbots_signer: Some(flashbots_wallet.with_chain_id(chain_id)), }) } else { - Ok(Self { signer: wallet.with_chain_id(chain_id.as_u64()), flashbots_signer: None }) + Ok(Self { signer: wallet.with_chain_id(chain_id), flashbots_signer: None }) } } /// Signs the user operation /// /// # Arguments - /// * `uo` - The [UserOperation](UserOperation) to be signed + /// * `uo` - The [UserOperationSigned](UserOperationSigned) to be signed /// * `ep` - The entry point contract address /// * `chain_id` - The chain id of the blockchain network to be used /// /// # Returns /// * `UserOperation` - The signed [UserOperation](UserOperation) - pub async fn sign_uo( + pub async fn sign_user_operation( &self, - uo: &UserOperation, + uo: &UserOperationSigned, ep: &Address, - chain_id: &U256, + chain_id: u64, ) -> eyre::Result { let h = uo.hash(ep, chain_id); let sig = self.signer.sign_message(h.0.as_bytes()).await?; - Ok(UserOperation { signature: sig.to_vec().into(), ..uo.clone() }) + Ok(UserOperation { + hash: h, + user_operation: UserOperationSigned { signature: sig.to_vec().into(), ..uo.clone() }, + }) } } diff --git a/crates/rpc/src/debug.rs b/crates/rpc/src/debug.rs index a0a39861..7d219794 100644 --- a/crates/rpc/src/debug.rs +++ b/crates/rpc/src/debug.rs @@ -16,7 +16,7 @@ use silius_grpc::{ use silius_primitives::{ constants::bundler::BUNDLE_INTERVAL, reputation::{ReputationEntry, StakeInfoResponse}, - BundlerMode, UserOperation, + BundlerMode, UserOperation, UserOperationRequest, }; use tonic::Request; @@ -77,21 +77,25 @@ impl DebugApiServer for DebugApiServerImpl { } /// Sending an [GetAllRequest](GetAllRequest) to the UoPool gRPC server - /// to get all of the [UserOperation](UserOperation) in the mempool. + /// to get all of the [UserOperation](UserOperationRequest) in the mempool. /// /// # Arguments /// * `entry_point: Address` - The address of the entry point. /// /// # Returns - /// * `RpcResult>` - An array of [UserOperation](UserOperation) - async fn dump_mempool(&self, ep: Address) -> RpcResult> { + /// * `RpcResult>` - An array of [UserOperation](UserOperationRequest) + async fn dump_mempool(&self, ep: Address) -> RpcResult> { let mut uopool_grpc_client = self.uopool_grpc_client.clone(); let req = Request::new(GetAllRequest { ep: Some(ep.into()) }); let res = uopool_grpc_client.get_all(req).await.map_err(JsonRpcError::from)?.into_inner(); - let mut uos: Vec = res.uos.iter().map(|uo| uo.clone().into()).collect(); + let mut uos: Vec = res + .uos + .iter() + .map(|uo| UserOperation::from(uo.clone()).user_operation.into()) + .collect(); uos.sort_by(|a, b| a.nonce.cmp(&b.nonce)); Ok(uos) } diff --git a/crates/rpc/src/debug_api.rs b/crates/rpc/src/debug_api.rs index fc1e6155..ea452788 100644 --- a/crates/rpc/src/debug_api.rs +++ b/crates/rpc/src/debug_api.rs @@ -4,7 +4,7 @@ use jsonrpsee::{core::RpcResult, proc_macros::rpc}; use serde::{Deserialize, Serialize}; use silius_primitives::{ reputation::{ReputationEntry, StakeInfoResponse}, - BundlerMode, UserOperation, + BundlerMode, UserOperationRequest, }; #[derive(Clone, Copy, Serialize, Deserialize)] @@ -46,9 +46,10 @@ pub trait DebugApi { /// * `entry_point: Address` - The address of the entry point. /// /// # Returns - /// * `RpcResult>` - A vector of [UserOperations](UserOperation) returned + /// * `RpcResult>` - A vector of [UserOperations](UserOperationRequest) + /// returned #[method(name = "dumpMempool")] - async fn dump_mempool(&self, entry_point: Address) -> RpcResult>; + async fn dump_mempool(&self, entry_point: Address) -> RpcResult>; /// Set the reputations for the given array of [ReputationEntry](ReputationEntry) /// diff --git a/crates/rpc/src/eth.rs b/crates/rpc/src/eth.rs index 78c585a1..f6e0cb5f 100644 --- a/crates/rpc/src/eth.rs +++ b/crates/rpc/src/eth.rs @@ -12,7 +12,7 @@ use silius_grpc::{ use silius_mempool::MempoolError; use silius_primitives::{ UserOperation, UserOperationByHash, UserOperationGasEstimation, UserOperationHash, - UserOperationPartial, UserOperationReceipt, + UserOperationReceipt, UserOperationRequest, UserOperationSigned, }; use std::str::FromStr; use tonic::Request; @@ -61,19 +61,33 @@ impl EthApiServer for EthApiServerImpl { /// Send a user operation via the [AddRequest](AddRequest). /// /// # Arguments - /// * `user_operation: UserOperation` - The user operation to be sent. + /// * `user_operation: UserOperationRequest` - The user operation to be sent. /// * `entry_point: Address` - The address of the entry point. /// /// # Returns /// * `RpcResult` - The hash of the sent user operation. async fn send_user_operation( &self, - uo: UserOperation, + uo: UserOperationRequest, ep: Address, ) -> RpcResult { let mut uopool_grpc_client = self.uopool_grpc_client.clone(); - let req = Request::new(AddRequest { uo: Some(uo.into()), ep: Some(ep.into()) }); + let res = uopool_grpc_client + .get_chain_id(Request::new(())) + .await + .map_err(JsonRpcError::from)? + .into_inner(); + + let uo: UserOperationSigned = uo.into(); + + let req = Request::new(AddRequest { + uo: Some( + UserOperation::from_user_operation_signed(uo.hash(&ep, res.chain_id), uo.clone()) + .into(), + ), + ep: Some(ep.into()), + }); let res = uopool_grpc_client.add(req).await.map_err(JsonRpcError::from)?.into_inner(); @@ -89,13 +103,13 @@ impl EthApiServer for EthApiServerImpl { .0) } - /// Estimate the gas required for a [UserOperation](UserOperation) via the + /// Estimate the gas required for a [UserOperation](UserOperationRequest) via the /// [EstimateUserOperationGasRequest](EstimateUserOperationGasRequest). This allows you to /// gauge the computational cost of the operation. See [How ERC-4337 Gas Estimation Works](https://www.alchemy.com/blog/erc-4337-gas-estimation). /// /// # Arguments - /// * `user_operation: [UserOperationPartial](UserOperationPartial)` - The partial user - /// operation for which to estimate the gas. + /// * `user_operation: [UserOperation](UserOperationRequest)` - User operation for which to + /// estimate the gas. /// * `entry_point: Address` - The address of the entry point. /// /// # Returns @@ -103,15 +117,30 @@ impl EthApiServer for EthApiServerImpl { /// [UserOperationGasEstimation](UserOperationGasEstimation) for the user operation. async fn estimate_user_operation_gas( &self, - uo: UserOperationPartial, + uo: UserOperationRequest, ep: Address, ) -> RpcResult { let mut uopool_grpc_client = self.uopool_grpc_client.clone(); - let req = Request::new(EstimateUserOperationGasRequest { - uo: Some(UserOperation::from(uo).into()), - ep: Some(ep.into()), - }); + let res = uopool_grpc_client + .get_chain_id(Request::new(())) + .await + .map_err(JsonRpcError::from)? + .into_inner(); + + let uo: UserOperationSigned = uo.into(); + + let req: Request = + Request::new(EstimateUserOperationGasRequest { + uo: Some( + UserOperation::from_user_operation_signed( + uo.hash(&ep, res.chain_id), + uo.clone(), + ) + .into(), + ), + ep: Some(ep.into()), + }); let res = uopool_grpc_client .estimate_user_operation_gas(req) diff --git a/crates/rpc/src/eth_api.rs b/crates/rpc/src/eth_api.rs index 03862251..70c09daf 100644 --- a/crates/rpc/src/eth_api.rs +++ b/crates/rpc/src/eth_api.rs @@ -2,8 +2,8 @@ pub use crate::eth::EthApiServerImpl; use ethers::types::{Address, U64}; use jsonrpsee::{core::RpcResult, proc_macros::rpc}; use silius_primitives::{ - UserOperation, UserOperationByHash, UserOperationGasEstimation, UserOperationHash, - UserOperationPartial, UserOperationReceipt, + UserOperationByHash, UserOperationGasEstimation, UserOperationHash, UserOperationReceipt, + UserOperationRequest, }; /// The ERC-4337 `eth` namespace RPC methods trait @@ -27,7 +27,7 @@ pub trait EthApi { /// Send a [UserOperation](UserOperation). /// /// # Arguments - /// * `user_operation: UserOperation` - The [UserOperation](UserOperation) to be sent. + /// * `user_operation: UserOperation` - The [UserOperation](UserOperationRequest) to be sent. /// * `entry_point: Address` - The address of the entry point. /// /// # Returns @@ -35,7 +35,7 @@ pub trait EthApi { #[method(name = "sendUserOperation")] async fn send_user_operation( &self, - user_operation: UserOperation, + user_operation: UserOperationRequest, entry_point: Address, ) -> RpcResult; @@ -44,8 +44,8 @@ pub trait EthApi { /// See [How ERC-4337 Gas Estimation Works](https://www.alchemy.com/blog/erc-4337-gas-estimation). /// /// # Arguments - /// * `user_operation: [UserOperationPartial](UserOperationPartial)` - A [partial user - /// operation](UserOperationPartial) for which to estimate the gas. + /// * `user_operation: [UserOperation](UserOperationRequest)` - User operation for which to + /// estimate the gas. /// * `entry_point: Address` - The address of the entry point. /// /// # Returns @@ -54,7 +54,7 @@ pub trait EthApi { #[method(name = "estimateUserOperationGas")] async fn estimate_user_operation_gas( &self, - user_operation: UserOperationPartial, + user_operation: UserOperationRequest, entry_point: Address, ) -> RpcResult; diff --git a/examples/simple-account/examples/create.rs b/examples/simple-account/examples/create.rs index 33d08458..d30fcc4e 100644 --- a/examples/simple-account/examples/create.rs +++ b/examples/simple-account/examples/create.rs @@ -8,7 +8,7 @@ use examples_simple_account::{ simple_account::SimpleAccountExecute, EstimateResult, Request, Response, }; use reqwest; -use silius_primitives::{constants::entry_point::ADDRESS, UserOperation, Wallet as UoWallet}; +use silius_primitives::{constants::entry_point::ADDRESS, UserOperationSigned, Wallet as UoWallet}; use silius_tests::common::gen::SimpleAccountFactory; use std::{env, sync::Arc, time::Duration}; @@ -59,7 +59,7 @@ async fn main() -> eyre::Result<()> { let execution = SimpleAccountExecute::new(Address::zero(), U256::from(0), Bytes::default()); println!("{:}", Bytes::from(execution.encode())); - let user_op = UserOperation { + let user_op = UserOperationSigned { sender: address, nonce, init_code: Bytes::from(init_code), @@ -72,12 +72,12 @@ async fn main() -> eyre::Result<()> { paymaster_and_data: Bytes::new(), signature: Bytes::default(), }; - let uo_wallet = UoWallet::from_phrase(seed_phrase.as_str(), &chain_id.into(), false)?; + let uo_wallet = UoWallet::from_phrase(seed_phrase.as_str(), chain_id, false)?; let user_op = uo_wallet - .sign_uo(&user_op, &ADDRESS.to_string().parse::
()?, &chain_id.into()) + .sign_user_operation(&user_op, &ADDRESS.to_string().parse::
()?, chain_id) .await?; - let value = serde_json::to_value(&user_op).unwrap(); + let value = serde_json::to_value(&user_op.user_operation).unwrap(); let req_body = Request { jsonrpc: "2.0".into(), @@ -98,23 +98,26 @@ async fn main() -> eyre::Result<()> { let v = serde_json::from_str::>(&res)?; println!("json: {:?}", v); - let user_op = UserOperation { + let user_op = UserOperationSigned { pre_verification_gas: v.result.pre_verification_gas.saturating_add(U256::from(1000)), verification_gas_limit: v.result.verification_gas_limit, call_gas_limit: v.result.call_gas_limit.saturating_add(U256::from(2000)), max_priority_fee_per_gas: priority_fee, max_fee_per_gas: gas_price, - ..user_op + ..user_op.user_operation }; let user_op = uo_wallet - .sign_uo(&user_op, &ADDRESS.to_string().parse::
()?, &chain_id.into()) + .sign_user_operation(&user_op, &ADDRESS.to_string().parse::
()?, chain_id) .await?; let send_body = Request { jsonrpc: "2.0".into(), id: 1, method: "eth_sendUserOperation".into(), - params: vec![serde_json::to_value(&user_op).unwrap(), ADDRESS.to_string().into()], + params: vec![ + serde_json::to_value(&user_op.user_operation).unwrap(), + ADDRESS.to_string().into(), + ], }; let post = reqwest::Client::builder() .build()? diff --git a/examples/simple-account/examples/transfer.rs b/examples/simple-account/examples/transfer.rs index b929c3fa..95f11bbf 100644 --- a/examples/simple-account/examples/transfer.rs +++ b/examples/simple-account/examples/transfer.rs @@ -10,7 +10,7 @@ use examples_simple_account::{ }; use reqwest; use silius_contracts::EntryPoint; -use silius_primitives::{constants::entry_point::ADDRESS, UserOperation, Wallet as UoWallet}; +use silius_primitives::{constants::entry_point::ADDRESS, UserOperationSigned, Wallet as UoWallet}; use silius_tests::common::gen::SimpleAccountFactory; use std::{env, sync::Arc, time::Duration}; @@ -59,7 +59,7 @@ async fn main() -> eyre::Result<()> { let execution = SimpleAccountExecute::new(address, parse_ether(TRANSFER_VALUE)?, Bytes::default()); - let user_op = UserOperation { + let user_op = UserOperationSigned { sender: address, nonce, init_code: Bytes::default(), @@ -72,12 +72,12 @@ async fn main() -> eyre::Result<()> { paymaster_and_data: Bytes::new(), signature: Bytes::default(), }; - let uo_wallet = UoWallet::from_phrase(seed_phrase.as_str(), &chain_id.into(), false)?; + let uo_wallet = UoWallet::from_phrase(seed_phrase.as_str(), chain_id, false)?; let user_op = uo_wallet - .sign_uo(&user_op, &ADDRESS.to_string().parse::
()?, &chain_id.into()) + .sign_user_operation(&user_op, &ADDRESS.to_string().parse::
()?, chain_id) .await?; - let value = serde_json::to_value(&user_op).unwrap(); + let value = serde_json::to_value(&user_op.user_operation).unwrap(); let req_body = Request { jsonrpc: "2.0".into(), @@ -98,23 +98,26 @@ async fn main() -> eyre::Result<()> { let v = serde_json::from_str::>(&res)?; println!("json: {:?}", v); - let user_op = UserOperation { + let user_op = UserOperationSigned { pre_verification_gas: v.result.pre_verification_gas.saturating_add(U256::from(1000)), verification_gas_limit: v.result.verification_gas_limit.saturating_mul(U256::from(2)), call_gas_limit: v.result.call_gas_limit.saturating_mul(U256::from(2)), max_priority_fee_per_gas: priority_fee, max_fee_per_gas: gas_price, - ..user_op + ..user_op.user_operation }; let user_op = uo_wallet - .sign_uo(&user_op, &ADDRESS.to_string().parse::
()?, &chain_id.into()) + .sign_user_operation(&user_op, &ADDRESS.to_string().parse::
()?, chain_id) .await?; let send_body = Request { jsonrpc: "2.0".into(), id: 1, method: "eth_sendUserOperation".into(), - params: vec![serde_json::to_value(&user_op).unwrap(), ADDRESS.to_string().into()], + params: vec![ + serde_json::to_value(&user_op.user_operation).unwrap(), + ADDRESS.to_string().into(), + ], }; let post = reqwest::Client::builder().build()?.post(bundler_url).json(&send_body).send().await?; diff --git a/examples/storage/examples/memory.rs b/examples/storage/examples/memory.rs index 22f3e730..d50a0726 100644 --- a/examples/storage/examples/memory.rs +++ b/examples/storage/examples/memory.rs @@ -13,7 +13,7 @@ use silius_primitives::{ provider::create_http_provider, reputation::ReputationEntry, simulation::CodeHash, - UserOperation, UserOperationHash, + UserOperationHash, UserOperationSigned, }; use std::{ collections::{HashMap, HashSet}, @@ -33,7 +33,7 @@ async fn main() -> eyre::Result<()> { let chain = Chain::dev(); let entry_point = EntryPoint::new(provider.clone(), ep); let mempool = Mempool::new( - Arc::new(RwLock::new(HashMap::::default())), + Arc::new(RwLock::new(HashMap::::default())), Arc::new(RwLock::new(HashMap::>::default())), Arc::new(RwLock::new(HashMap::>::default())), Arc::new(RwLock::new(HashMap::>::default())), diff --git a/examples/user-operation/examples/user_operation.rs b/examples/user-operation/examples/user_operation.rs index 9a9b7ca9..3edead22 100644 --- a/examples/user-operation/examples/user_operation.rs +++ b/examples/user-operation/examples/user_operation.rs @@ -1,5 +1,5 @@ use ethers::types::Address; -use silius_primitives::{UserOperation, Wallet}; +use silius_primitives::{UserOperationSigned, Wallet}; use std::str::FromStr; pub const MNEMONIC_PHRASE: &str = "test test test test test test test test test test test junk"; @@ -9,20 +9,20 @@ pub const ENTRY_POINT: &str = "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"; #[tokio::main] async fn main() -> eyre::Result<()> { // create wallet - let wallet = Wallet::from_phrase(MNEMONIC_PHRASE, &CHAIN_ID.into(), false)?; + let wallet = Wallet::from_phrase(MNEMONIC_PHRASE, CHAIN_ID, false)?; println!("Wallet address: {:?}", wallet.signer); // create simple user operation - let uo = UserOperation::default().verification_gas_limit(50_000.into()); + let uo = UserOperationSigned::default().verification_gas_limit(50_000.into()); println!("User operation: {:?}", uo); // calculate user operation hash - let uo_hash = uo.hash(&Address::from_str(ENTRY_POINT).unwrap(), &CHAIN_ID.into()); + let uo_hash = uo.hash(&Address::from_str(ENTRY_POINT).unwrap(), CHAIN_ID); println!("User operation hash: {:?}", uo_hash); // sign user operation let uo_signed = - wallet.sign_uo(&uo, &Address::from_str(ENTRY_POINT).unwrap(), &CHAIN_ID.into()).await?; + wallet.sign_user_operation(&uo, &Address::from_str(ENTRY_POINT).unwrap(), CHAIN_ID).await?; println!("User operation signed: {:?}", uo_signed); Ok(()) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 292fe499..8142c301 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,2 +1,2 @@ [toolchain] -channel = "stable" +channel = "1.73.0" diff --git a/tests/src/common/mod.rs b/tests/src/common/mod.rs index 10058caf..583906e4 100644 --- a/tests/src/common/mod.rs +++ b/tests/src/common/mod.rs @@ -16,7 +16,7 @@ use silius_mempool::{ UserOperationsByEntity, UserOperationsBySender, WriteMap, }; use silius_primitives::{ - reputation::ReputationEntry, simulation::CodeHash, UserOperation, UserOperationHash, + reputation::ReputationEntry, simulation::CodeHash, UserOperationHash, UserOperationSigned, }; use std::{ collections::{HashMap, HashSet}, @@ -188,7 +188,7 @@ pub fn setup_database_mempool_reputation() -> ( #[allow(clippy::type_complexity)] pub fn setup_memory_mempool_reputation() -> ( Mempool< - Arc>>, + Arc>>, Arc>>>, Arc>>>, Arc>>>, @@ -199,7 +199,7 @@ pub fn setup_memory_mempool_reputation() -> ( >, ) { let mempool = Mempool::new( - Arc::new(RwLock::new(HashMap::::default())), + Arc::new(RwLock::new(HashMap::::default())), Arc::new(RwLock::new(HashMap::>::default())), Arc::new(RwLock::new(HashMap::>::default())), Arc::new(RwLock::new(HashMap::>::default())), diff --git a/tests/src/estimate_gas_tests.rs b/tests/src/estimate_gas_tests.rs index 4f5427d6..0e5e2561 100644 --- a/tests/src/estimate_gas_tests.rs +++ b/tests/src/estimate_gas_tests.rs @@ -13,7 +13,7 @@ use ethers::{ }; use silius_contracts::EntryPoint; use silius_mempool::{validate::validator::new_canonical, UoPool}; -use silius_primitives::{UserOperation, Wallet as UoWallet}; +use silius_primitives::{UserOperationSigned, Wallet as UoWallet}; use std::sync::Arc; async fn setup_basic() -> eyre::Result<( @@ -92,7 +92,7 @@ macro_rules! estimate_gas_with_init_code { let (_gas_price, priority_fee) = client.estimate_eip1559_fees(None).await?; let nonce = client.get_transaction_count(address, None).await?; - let user_op = UserOperation { + let user_op = UserOperationSigned { sender: address, nonce, init_code: Bytes::from(init_code), @@ -106,9 +106,9 @@ macro_rules! estimate_gas_with_init_code { signature: Bytes::default(), }; - let uo_wallet = UoWallet::from_phrase(SEED_PHRASE, &chain_id.into(), false)?; + let uo_wallet = UoWallet::from_phrase(SEED_PHRASE, chain_id, false)?; let user_op = - uo_wallet.sign_uo(&user_op, &entry_point.address, &chain_id.into()).await?; + uo_wallet.sign_user_operation(&user_op, &entry_point.address, chain_id).await?; let _ = uopool.estimate_user_operation_gas(&user_op).await.expect("estimate done"); Ok(()) diff --git a/tests/src/simulation_tests.rs b/tests/src/simulation_tests.rs index eaf52199..cfed3979 100644 --- a/tests/src/simulation_tests.rs +++ b/tests/src/simulation_tests.rs @@ -34,7 +34,7 @@ use silius_primitives::{ constants::validation::entities::{FACTORY, PAYMASTER, SENDER}, reputation::ReputationEntry, simulation::CodeHash, - UserOperation, UserOperationHash, + UserOperation, UserOperationHash, UserOperationSigned, }; use std::{ collections::{HashMap, HashSet}, @@ -54,6 +54,7 @@ where { pub client: Arc, pub _geth: GethInstance, + pub chain_id: u64, pub entry_point: DeployedContract>, pub paymaster: DeployedContract>, pub opcodes_factory: DeployedContract>, @@ -76,7 +77,7 @@ type DatabaseContext = TestContext< type MemoryContext = TestContext< ClientType, - Arc>>, + Arc>>, Arc>>>, Arc>>>, Arc>>>, @@ -156,6 +157,7 @@ async fn setup_database() -> eyre::Result { Ok(DatabaseContext { client: client.clone(), _geth, + chain_id, entry_point: ep, paymaster, opcodes_factory, @@ -179,6 +181,7 @@ async fn setup_memory() -> eyre::Result { Ok(MemoryContext { client: client.clone(), _geth, + chain_id, entry_point: ep, paymaster, opcodes_factory, @@ -227,7 +230,7 @@ async fn create_test_user_operation( init_code: Bytes, init_func: Bytes, factory_address: Address, -) -> eyre::Result +) -> eyre::Result where M: Middleware + 'static, T: UserOperationAct, @@ -262,7 +265,7 @@ where let sender = Address::from_slice(address); - Ok(UserOperation { + Ok(UserOperationSigned { sender, nonce: U256::zero(), init_code, @@ -281,7 +284,7 @@ fn existing_storage_account_user_operation( context: &TestContext, validate_rule: String, pm_rule: String, -) -> UserOperation +) -> UserOperationSigned where M: Middleware + 'static, T: UserOperationAct, @@ -296,7 +299,7 @@ where paymaster_and_data.extend_from_slice(pm_rule.as_bytes()); let sig = Bytes::from(validate_rule.as_bytes().to_vec()); - UserOperation { + UserOperationSigned { sender: context.storage_account.address, nonce: U256::zero(), init_code: Bytes::default(), @@ -313,7 +316,7 @@ where async fn validate( context: &TestContext, - uo: UserOperation, + uo: UserOperationSigned, ) -> Result where M: Middleware + 'static, @@ -327,7 +330,10 @@ where context .validator .validate_user_operation( - &uo, + &UserOperation::from_user_operation_signed( + uo.hash(&context.entry_point.address, context.chain_id), + uo.clone(), + ), &context.mempool, &context.reputation, UserOperationValidatorMode::Simulation | UserOperationValidatorMode::SimulationTrace, @@ -663,7 +669,7 @@ macro_rules! fail_with_unstaked_paymaster_returning_context { paymaster_and_data.extend_from_slice(pm.address.as_bytes()); paymaster_and_data.extend_from_slice("postOp-context".as_bytes()); - let uo = UserOperation { + let uo = UserOperationSigned { sender: acct.address, nonce: U256::zero(), init_code: Bytes::default(), @@ -705,7 +711,7 @@ macro_rules! fail_with_validation_recursively_calls_handle_ops { let acct = deploy_test_recursion_account(c.client.clone(), c.entry_point.address) .await .expect("deploy succeed"); - let uo = UserOperation { + let uo = UserOperationSigned { sender: acct.address, nonce: U256::zero(), init_code: Bytes::default(), From 5bb841749aeb1f157c1cf08535913134b4c613cd Mon Sep 17 00:00:00 2001 From: Vid Kersic Date: Tue, 16 Jan 2024 23:16:20 +0100 Subject: [PATCH 2/2] chore: update grpc user operation --- Makefile | 4 +- crates/grpc/src/proto.rs | 109 +++++++---------------- crates/grpc/src/protos/types/types.proto | 12 +-- 3 files changed, 35 insertions(+), 90 deletions(-) diff --git a/Makefile b/Makefile index 8d0077d5..c0c7b418 100644 --- a/Makefile +++ b/Makefile @@ -20,10 +20,10 @@ run-silius-create-wallet: cargo run --release -- create-wallet --output-path ${HOME}/.silius run-silius-debug: - cargo run --release -- node --eth-client-address ws://127.0.0.1:8546 --mnemonic-file ${HOME}/.silius/0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 --beneficiary 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 --entry-points 0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789 --http --ws --http.api eth,debug,web3 --ws.api eth,debug,web3 --poll-interval 5 + cargo run --release -- node --eth-client-address ws://127.0.0.1:8546 --mnemonic-file ${HOME}/.silius/0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 --beneficiary 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 --entry-points 0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789 --http --ws --http.api eth,debug,web3 --ws.api eth,debug,web3 run-silius-debug-mode: - cargo run --profile debug-fast -- node --verbosity 4 --eth-client-address ws://127.0.0.1:8546 --mnemonic-file /home/vid/.silius/0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 --beneficiary 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 --entry-points 0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789 --http --ws --http.api eth,debug,web3 --ws.api eth,debug,web3 --poll-interval 5 + cargo run --profile debug-fast -- node --verbosity 4 --eth-client-address ws://127.0.0.1:8546 --mnemonic-file /home/vid/.silius/0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 --beneficiary 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 --entry-points 0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789 --http --ws --http.api eth,debug,web3 --ws.api eth,debug,web3 fetch-thirdparty: git submodule update --init diff --git a/crates/grpc/src/proto.rs b/crates/grpc/src/proto.rs index 3d75ae8b..d0796ba0 100644 --- a/crates/grpc/src/proto.rs +++ b/crates/grpc/src/proto.rs @@ -1,10 +1,8 @@ // Code adapted from: https://github.com/ledgerwatch/interfaces/blob/master/src/lib.rs#L1 pub mod types { - use arrayref::array_ref; use ethers::types::{Address, Bloom, U256}; use prost::bytes::Buf; - use silius_primitives::{reputation::Status, UserOperationHash}; use std::str::FromStr; tonic::include_proto!("types"); @@ -73,7 +71,7 @@ pub mod types { } } - impl From for UserOperationHash { + impl From for silius_primitives::UserOperationHash { fn from(val: H256) -> Self { Self::from(ethers::types::H256::from(val)) } @@ -97,19 +95,27 @@ pub mod types { fn from(user_operation: silius_primitives::UserOperation) -> Self { Self { hash: Some(user_operation.hash.into()), - sender: Some(user_operation.sender.into()), - nonce: Some(user_operation.nonce.into()), - init_code: prost::bytes::Bytes::copy_from_slice(user_operation.init_code.as_ref()), - call_data: prost::bytes::Bytes::copy_from_slice(user_operation.call_data.as_ref()), - call_gas_limit: Some(user_operation.call_gas_limit.into()), - verification_gas_limit: Some(user_operation.verification_gas_limit.into()), - pre_verification_gas: Some(user_operation.pre_verification_gas.into()), - max_fee_per_gas: Some(user_operation.max_fee_per_gas.into()), - max_priority_fee_per_gas: Some(user_operation.max_priority_fee_per_gas.into()), - paymaster_and_data: prost::bytes::Bytes::copy_from_slice( - user_operation.paymaster_and_data.as_ref(), - ), - signature: prost::bytes::Bytes::copy_from_slice(user_operation.signature.as_ref()), + uo: Some(UserOperationSigned { + sender: Some(user_operation.sender.into()), + nonce: Some(user_operation.nonce.into()), + init_code: prost::bytes::Bytes::copy_from_slice( + user_operation.init_code.as_ref(), + ), + call_data: prost::bytes::Bytes::copy_from_slice( + user_operation.call_data.as_ref(), + ), + call_gas_limit: Some(user_operation.call_gas_limit.into()), + verification_gas_limit: Some(user_operation.verification_gas_limit.into()), + pre_verification_gas: Some(user_operation.pre_verification_gas.into()), + max_fee_per_gas: Some(user_operation.max_fee_per_gas.into()), + max_priority_fee_per_gas: Some(user_operation.max_priority_fee_per_gas.into()), + paymaster_and_data: prost::bytes::Bytes::copy_from_slice( + user_operation.paymaster_and_data.as_ref(), + ), + signature: prost::bytes::Bytes::copy_from_slice( + user_operation.signature.as_ref(), + ), + }), } } } @@ -121,66 +127,15 @@ pub mod types { if let Some(hash) = user_operation.hash { hash.into() } else { - UserOperationHash::default() + silius_primitives::UserOperationHash::default() } }, - user_operation: silius_primitives::UserOperationSigned { - sender: { - if let Some(sender) = user_operation.sender { - sender.into() - } else { - Address::zero() - } - }, - nonce: { - if let Some(nonce) = user_operation.nonce { - nonce.into() - } else { - U256::zero() - } - }, - init_code: user_operation.init_code.into(), - call_data: user_operation.call_data.into(), - call_gas_limit: { - if let Some(call_gas_limit) = user_operation.call_gas_limit { - call_gas_limit.into() - } else { - U256::zero() - } - }, - verification_gas_limit: { - if let Some(verification_gas_limit) = user_operation.verification_gas_limit - { - verification_gas_limit.into() - } else { - U256::zero() - } - }, - pre_verification_gas: { - if let Some(pre_verification_gas) = user_operation.pre_verification_gas { - pre_verification_gas.into() - } else { - U256::zero() - } - }, - max_fee_per_gas: { - if let Some(max_fee_per_gas) = user_operation.max_fee_per_gas { - max_fee_per_gas.into() - } else { - U256::zero() - } - }, - max_priority_fee_per_gas: { - if let Some(max_priority_fee_per_gas) = - user_operation.max_priority_fee_per_gas - { - max_priority_fee_per_gas.into() - } else { - U256::zero() - } - }, - paymaster_and_data: user_operation.paymaster_and_data.into(), - signature: user_operation.signature.into(), + user_operation: { + if let Some(uo) = user_operation.uo { + uo.into() + } else { + silius_primitives::UserOperationSigned::default() + } }, } } @@ -273,7 +228,7 @@ pub mod types { addr: Some(reputation_entry.address.into()), uo_seen: reputation_entry.uo_seen, uo_included: reputation_entry.uo_included, - stat: match Status::from(reputation_entry.status) { + stat: match silius_primitives::reputation::Status::from(reputation_entry.status) { silius_primitives::reputation::Status::OK => ReputationStatus::Ok, silius_primitives::reputation::Status::THROTTLED => ReputationStatus::Throttled, silius_primitives::reputation::Status::BANNED => ReputationStatus::Banned, @@ -401,8 +356,8 @@ pub mod types { } } - impl From for H256 { - fn from(value: UserOperationHash) -> Self { + impl From for H256 { + fn from(value: silius_primitives::UserOperationHash) -> Self { Self::from(value.0) } } diff --git a/crates/grpc/src/protos/types/types.proto b/crates/grpc/src/protos/types/types.proto index 2ff3c894..6593420d 100644 --- a/crates/grpc/src/protos/types/types.proto +++ b/crates/grpc/src/protos/types/types.proto @@ -27,17 +27,7 @@ message SupportedEntryPoint { message UserOperation { types.H256 hash = 1; - types.H160 sender = 2; - PbU256 nonce = 3; - bytes init_code = 4; - bytes call_data = 5; - PbU256 call_gas_limit = 6; - PbU256 verification_gas_limit = 7; - PbU256 pre_verification_gas = 8; - PbU256 max_fee_per_gas = 9; - PbU256 max_priority_fee_per_gas = 10; - bytes paymaster_and_data = 11; - bytes signature = 12; + UserOperationSigned uo = 2; } message UserOperationSigned {