diff --git a/Cargo.lock b/Cargo.lock index 09ddab32..350c4496 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,15 +18,15 @@ version = "0.1.0" dependencies = [ "anyhow", "async-trait", - "bytes", "clap", "dirs", "educe", "ethereum-interfaces", - "ethereum-types", "ethers", "expanded-pathbuf", + "hex", "jsonrpsee", + "parking_lot 0.12.1", "prost", "prost-build", "protobuf-src", diff --git a/Cargo.toml b/Cargo.toml index 33f05729..9812ac37 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,15 +9,15 @@ default-run = "bundler" [dependencies] anyhow = "1" async-trait = "0.1" -bytes = { version = "1", features = ["serde"] } clap = { version = "4", features = ["derive"] } dirs = "4.0" educe = { version = "0.4", features = ["Debug", "Default"] } ethereum-interfaces = { git = "https://github.com/ledgerwatch/interfaces" } -ethereum-types = { version = "0.14", features = ["codec"] } ethers = "1.0.0" expanded-pathbuf = "0.1" +hex = { version = "0.4.3", default-features = false, features = ["std"] } jsonrpsee = { version = "0.16", features = ["server", "macros"] } +parking_lot = "0.12" prost = "0.11" ron = "0.8" serde = "1" diff --git a/Makefile b/Makefile index 7661010e..8c8f0f4a 100644 --- a/Makefile +++ b/Makefile @@ -13,5 +13,8 @@ run-create-wallet: cargo-fmt: cargo fmt --all +cargo-test: + cargo test + fetch-thirdparty: git submodule update --init \ No newline at end of file diff --git a/README.md b/README.md index 55516c69..9d2e03c0 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ For more information: https://hackmd.io/@Vid201/aa-bundler-rust ## Prerequisites -1. [install solc](https://docs.soliditylang.org/en/v0.8.17/installing-solidity.html) >=0.8.12 +1. [solc](https://docs.soliditylang.org/en/v0.8.17/installing-solidity.html) >=0.8.12 ## How to run? diff --git a/src/rpc/eth.rs b/src/rpc/eth.rs index 9106216e..50598c4a 100644 --- a/src/rpc/eth.rs +++ b/src/rpc/eth.rs @@ -1,6 +1,6 @@ use crate::{rpc::eth_api::EthApiServer, types::user_operation::UserOperation}; use async_trait::async_trait; -use ethereum_types::{Address, U64}; +use ethers::types::{Address, U64}; use jsonrpsee::{core::RpcResult, tracing::info}; pub struct EthApiServerImpl { diff --git a/src/rpc/eth_api.rs b/src/rpc/eth_api.rs index 77511835..9e30550a 100644 --- a/src/rpc/eth_api.rs +++ b/src/rpc/eth_api.rs @@ -1,4 +1,4 @@ -use ethereum_types::{Address, U64}; +use ethers::types::{Address, U64}; use jsonrpsee::{core::RpcResult, proc_macros::rpc}; use crate::types::user_operation::UserOperation; diff --git a/src/types/user_operation.rs b/src/types/user_operation.rs index bb3768b7..c0101c61 100644 --- a/src/types/user_operation.rs +++ b/src/types/user_operation.rs @@ -1,8 +1,13 @@ -use bytes::Bytes; -use ethereum_types::{Address, U256}; +use ethers::abi::AbiEncode; +use ethers::prelude::{EthAbiCodec, EthAbiType}; +use ethers::types::{Address, Bytes, H256, U256}; +use ethers::utils::keccak256; use serde::{Deserialize, Serialize}; +use std::str::FromStr; -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub type UserOperationHash = H256; + +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, EthAbiCodec, EthAbiType)] #[serde(rename_all = "camelCase")] pub struct UserOperation { pub sender: Address, @@ -17,3 +22,161 @@ pub struct UserOperation { pub paymaster_and_data: Bytes, pub signature: Bytes, } + +impl UserOperation { + pub fn pack(&self) -> Vec { + self.clone().encode_hex().into_bytes() + } + + pub fn pack_for_signature(&self) -> Vec { + let mut encoded = String::from("0x"); + let packed = hex::encode( + UserOperation { + signature: Bytes::from_str("0x").unwrap(), + ..self.clone() + } + .encode(), + ); + encoded.push_str(&packed[..packed.len() - 64]); + encoded.into_bytes() + } + + pub fn hash(&self, entry_point_address: Address, chain_id: U256) -> UserOperationHash { + H256::from_slice( + keccak256( + [ + keccak256(hex::decode(&self.pack_for_signature()[2..]).unwrap()).to_vec(), + entry_point_address.encode(), + chain_id.encode(), + ] + .concat(), + ) + .as_slice(), + ) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn user_operation_pack() { + let user_operations = vec![ + UserOperation { + sender: Address::zero(), + nonce: U256::zero(), + init_code: Bytes::default(), + call_data: Bytes::default(), + call_gas_limit: U256::zero(), + verification_gas_limit: U256::from(100000), + pre_verification_gas: U256::from(21000), + max_fee_per_gas: U256::zero(), + max_priority_fee_per_gas: U256::from(1e9 as u64), + paymaster_and_data: Bytes::default(), + signature: Bytes::default(), + }, + UserOperation { + sender: "0x663F3ad617193148711d28f5334eE4Ed07016602".parse().unwrap(), + nonce: U256::zero(), + init_code: Bytes::default(), + call_data: Bytes::default(), + call_gas_limit: U256::from(200000), + verification_gas_limit: U256::from(100000), + pre_verification_gas: U256::from(21000), + max_fee_per_gas: U256::from(3000000000 as u64), + max_priority_fee_per_gas: U256::from(1000000000), + paymaster_and_data: Bytes::default(), + signature: Bytes::from_str("0x7cb39607585dee8e297d0d7a669ad8c5e43975220b6773c10a138deadbc8ec864981de4b9b3c735288a217115fb33f8326a61ddabc60a534e3b5536515c70f931c").unwrap(), + }, + ]; + assert_eq!(user_operations[0].pack(), String::from("0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000186a000000000000000000000000000000000000000000000000000000000000052080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003b9aca0000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").into_bytes()); + assert_eq!(user_operations[1].pack(), String::from("0x000000000000000000000000663f3ad617193148711d28f5334ee4ed070166020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000030d4000000000000000000000000000000000000000000000000000000000000186a0000000000000000000000000000000000000000000000000000000000000520800000000000000000000000000000000000000000000000000000000b2d05e00000000000000000000000000000000000000000000000000000000003b9aca0000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000417cb39607585dee8e297d0d7a669ad8c5e43975220b6773c10a138deadbc8ec864981de4b9b3c735288a217115fb33f8326a61ddabc60a534e3b5536515c70f931c00000000000000000000000000000000000000000000000000000000000000").into_bytes()); + } + + #[test] + fn user_operation_pack_for_signature() { + let user_operations = vec![ + UserOperation { + sender: Address::zero(), + nonce: U256::zero(), + init_code: Bytes::default(), + call_data: Bytes::default(), + call_gas_limit: U256::zero(), + verification_gas_limit: U256::from(100000), + pre_verification_gas: U256::from(21000), + max_fee_per_gas: U256::zero(), + max_priority_fee_per_gas: U256::from(1e9 as u64), + paymaster_and_data: Bytes::default(), + signature: Bytes::default(), + }, + UserOperation { + sender: "0x663F3ad617193148711d28f5334eE4Ed07016602".parse().unwrap(), + nonce: U256::zero(), + init_code: Bytes::default(), + call_data: Bytes::default(), + call_gas_limit: U256::from(200000), + verification_gas_limit: U256::from(100000), + pre_verification_gas: U256::from(21000), + max_fee_per_gas: U256::from(3000000000 as u64), + max_priority_fee_per_gas: U256::from(1000000000), + paymaster_and_data: Bytes::default(), + signature: Bytes::from_str("0x7cb39607585dee8e297d0d7a669ad8c5e43975220b6773c10a138deadbc8ec864981de4b9b3c735288a217115fb33f8326a61ddabc60a534e3b5536515c70f931c").unwrap(), + }, + ]; + assert_eq!(user_operations[0].pack_for_signature(), String::from("0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000180000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000186a000000000000000000000000000000000000000000000000000000000000052080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003b9aca0000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").into_bytes()); + assert_eq!(user_operations[1].pack_for_signature(), String::from("0x000000000000000000000000663f3ad617193148711d28f5334ee4ed070166020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001800000000000000000000000000000000000000000000000000000000000030d4000000000000000000000000000000000000000000000000000000000000186a0000000000000000000000000000000000000000000000000000000000000520800000000000000000000000000000000000000000000000000000000b2d05e00000000000000000000000000000000000000000000000000000000003b9aca0000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").into_bytes()); + } + + #[test] + fn user_operation_hash() { + let user_operations = vec![ + UserOperation { + sender: Address::zero(), + nonce: U256::zero(), + init_code: Bytes::default(), + call_data: Bytes::default(), + call_gas_limit: U256::zero(), + verification_gas_limit: U256::from(100000), + pre_verification_gas: U256::from(21000), + max_fee_per_gas: U256::zero(), + max_priority_fee_per_gas: U256::from(1e9 as u64), + paymaster_and_data: Bytes::default(), + signature: Bytes::default(), + }, + UserOperation { + sender: "0x663F3ad617193148711d28f5334eE4Ed07016602".parse().unwrap(), + nonce: U256::zero(), + init_code: Bytes::default(), + call_data: Bytes::default(), + call_gas_limit: U256::from(200000), + verification_gas_limit: U256::from(100000), + pre_verification_gas: U256::from(21000), + max_fee_per_gas: U256::from(3000000000 as u64), + max_priority_fee_per_gas: U256::from(1000000000), + paymaster_and_data: Bytes::default(), + signature: Bytes::from_str("0x7cb39607585dee8e297d0d7a669ad8c5e43975220b6773c10a138deadbc8ec864981de4b9b3c735288a217115fb33f8326a61ddabc60a534e3b5536515c70f931c").unwrap(), + }, + ]; + assert_eq!( + user_operations[0].hash( + "0x2DF1592238420ecFe7f2431360e224707e77fA0E" + .parse() + .unwrap(), + U256::from(1) + ), + H256::from_str("0x42e145138104ec4124367ea3f7994833071b2011927290f6844d593e05011279") + .unwrap() + ); + assert_eq!( + user_operations[1].hash( + "0x2DF1592238420ecFe7f2431360e224707e77fA0E" + .parse() + .unwrap(), + U256::from(1) + ), + H256::from_str("0x583c8fcba470fd9da514f9482ccd31c299b0161a36b365aab353a6bfebaa0bb2") + .unwrap() + ); + } +} diff --git a/src/uopool/mod.rs b/src/uopool/mod.rs index ff3df529..f0131168 100644 --- a/src/uopool/mod.rs +++ b/src/uopool/mod.rs @@ -1,13 +1,31 @@ -use crate::uopool::{server::server::uo_pool_server::UoPoolServer, services::UoPoolService}; +use crate::{ + types::user_operation::{UserOperation, UserOperationHash}, + uopool::{server::server::uo_pool_server::UoPoolServer, services::UoPoolService}, +}; use anyhow::Result; use clap::Parser; use educe::Educe; use jsonrpsee::tracing::info; -use std::{net::SocketAddr, time::Duration}; +use parking_lot::RwLock; +use std::{collections::HashMap, net::SocketAddr, sync::Arc, time::Duration}; pub mod server; pub mod services; +#[derive(Educe)] +#[educe(Debug)] +pub struct UserOperationPool { + pub pool: Arc>>, +} + +impl UserOperationPool { + pub fn new() -> Self { + Self { + pool: Default::default(), + } + } +} + #[derive(Educe, Parser)] #[educe(Debug)] pub struct Opts { @@ -18,7 +36,7 @@ pub struct Opts { pub async fn run(opts: Opts) -> Result<()> { tokio::spawn(async move { let mut builder = tonic::transport::Server::builder(); - let svc = UoPoolServer::new(UoPoolService::new()); + let svc = UoPoolServer::new(UoPoolService::new(Arc::new(UserOperationPool::new()))); info!( "UoPool gRPC server starting on {}", diff --git a/src/uopool/services/uopool.rs b/src/uopool/services/uopool.rs index d6410258..66565a62 100644 --- a/src/uopool/services/uopool.rs +++ b/src/uopool/services/uopool.rs @@ -1,15 +1,22 @@ -use crate::uopool::server::server::{ - uo_pool_server::UoPool, AddRequest, AddResponse, AllRequest, AllResponse, RemoveRequest, - RemoveResponse, +use std::sync::Arc; + +use crate::uopool::{ + server::server::{ + uo_pool_server::UoPool, AddRequest, AddResponse, AllRequest, AllResponse, RemoveRequest, + RemoveResponse, + }, + UserOperationPool, }; use async_trait::async_trait; use tonic::Response; -pub struct UoPoolService {} +pub struct UoPoolService { + uo_pool: Arc, +} impl UoPoolService { - pub fn new() -> Self { - Self {} + pub fn new(uo_pool: Arc) -> Self { + Self { uo_pool } } }