diff --git a/Cargo.lock b/Cargo.lock index b59d0612..a7f74ffd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5279,7 +5279,7 @@ dependencies = [ [[package]] name = "silius" -version = "0.2.0-alpha" +version = "0.3.0-alpha" dependencies = [ "clap", "dirs 4.0.0", @@ -5299,7 +5299,7 @@ dependencies = [ [[package]] name = "silius-bundler" -version = "0.2.0-alpha" +version = "0.3.0-alpha" dependencies = [ "alloy-primitives 0.2.0", "alloy-sol-types 0.2.0", @@ -5323,7 +5323,7 @@ dependencies = [ [[package]] name = "silius-contracts" -version = "0.2.0-alpha" +version = "0.3.0-alpha" dependencies = [ "ethers 2.0.8", "ethers-solc 2.0.9", @@ -5338,7 +5338,7 @@ dependencies = [ [[package]] name = "silius-examples" -version = "0.2.0-alpha" +version = "0.3.0-alpha" dependencies = [ "alloy-primitives 0.3.3", "alloy-sol-types 0.3.2", @@ -5355,7 +5355,7 @@ dependencies = [ [[package]] name = "silius-grpc" -version = "0.2.0-alpha" +version = "0.3.0-alpha" dependencies = [ "arrayref", "async-trait", @@ -5363,6 +5363,7 @@ dependencies = [ "ethers 2.0.8", "expanded-pathbuf", "eyre", + "futures-util", "parking_lot", "prost", "prost-build", @@ -5380,12 +5381,14 @@ dependencies = [ [[package]] name = "silius-primitives" -version = "0.2.0-alpha" +version = "0.3.0-alpha" dependencies = [ + "async-stream", "educe", "ethers 2.0.8", "expanded-pathbuf", "eyre", + "futures-util", "lazy_static", "rustc-hex", "serde", @@ -5400,13 +5403,14 @@ dependencies = [ [[package]] name = "silius-rpc" -version = "0.2.0-alpha" +version = "0.3.0-alpha" dependencies = [ "async-trait", "ethers 2.0.8", "eyre", "git-version", "hyper", + "hyper-tls", "jsonrpsee 0.18.2", "serde", "serde_json", @@ -5420,7 +5424,7 @@ dependencies = [ [[package]] name = "silius-tests" -version = "0.2.0-alpha" +version = "0.3.0-alpha" dependencies = [ "ethers 2.0.8", "eyre", @@ -5433,7 +5437,7 @@ dependencies = [ [[package]] name = "silius-uopool" -version = "0.2.0-alpha" +version = "0.3.0-alpha" dependencies = [ "async-trait", "educe", diff --git a/Cargo.toml b/Cargo.toml index 7ebd1193..64232086 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,19 +14,21 @@ default-members = ["bin/silius"] [workspace.package] authors = ["Vid Kersic "] -version = "0.2.0-alpha" +version = "0.3.0-alpha" edition = "2021" license = "MIT OR Apache-2.0" repository = "https://github.com/Vid201/silius" rust-version = "1.71.1" [workspace.dependencies] +async-stream = "0.3.5" async-trait = "0.1" ethers = { git = "https://github.com/gakonst/ethers-rs", rev = "fa3017715a298728d9fb341933818a5d0d84c2dc", features = [ "ws", ] } expanded-pathbuf = "0.1" eyre = "0.6.8" +futures-util = "0.3.28" parking_lot = "0.12" serde_json = "1" tokio = { version = "1.18", features = ["full"] } diff --git a/bin/silius/src/bundler.rs b/bin/silius/src/bundler.rs index 51654f66..da6f48ba 100644 --- a/bin/silius/src/bundler.rs +++ b/bin/silius/src/bundler.rs @@ -8,7 +8,8 @@ use silius_grpc::{ uopool_service_run, }; use silius_primitives::{ - bundler::SendBundleMode, consts::flashbots_relay_endpoints, Chain, Wallet, + bundler::SendBundleMode, consts::flashbots_relay_endpoints, provider::BlockStream, Chain, + Wallet, }; use silius_rpc::{ debug_api::{DebugApiServer, DebugApiServerImpl}, @@ -25,6 +26,7 @@ pub async fn launch_bundler( common_args: BundlerAndUoPoolArgs, rpc_args: RpcArgs, eth_client: Arc, + block_streams: Vec, ) -> eyre::Result<()> where M: Middleware + Clone + 'static, @@ -32,6 +34,7 @@ where launch_uopool( uopool_args.clone(), eth_client.clone(), + block_streams, common_args.chain.clone(), common_args.entry_points.clone(), ) @@ -131,6 +134,7 @@ where pub async fn launch_uopool( args: UoPoolArgs, eth_client: Arc, + block_streams: Vec, chain: Option, entry_points: Vec
, ) -> eyre::Result<()> @@ -154,6 +158,7 @@ where datadir, entry_points, eth_client, + block_streams, chain_id, args.max_verification_gas, args.min_stake, diff --git a/bin/silius/src/cli/args.rs b/bin/silius/src/cli/args.rs index 5f83c7a2..429b9e57 100644 --- a/bin/silius/src/cli/args.rs +++ b/bin/silius/src/cli/args.rs @@ -102,7 +102,7 @@ pub struct BundlerAndUoPoolArgs { pub eth_client_address: String, /// Chain information. - #[clap(long, default_value= "dev", value_parser = SUPPORTED_CHAINS)] + #[clap(long, value_parser = SUPPORTED_CHAINS)] pub chain: Option, /// Entry point addresses. diff --git a/bin/silius/src/cli/commands.rs b/bin/silius/src/cli/commands.rs index 57799f6e..95d302b0 100644 --- a/bin/silius/src/cli/commands.rs +++ b/bin/silius/src/cli/commands.rs @@ -1,9 +1,9 @@ use super::args::{BundlerAndUoPoolArgs, BundlerArgs, CreateWalletArgs, RpcArgs, UoPoolArgs}; -use crate::{ - bundler::{create_wallet, launch_bundler, launch_bundling, launch_rpc, launch_uopool}, - utils::{create_http_provider, create_ws_provider}, -}; +use crate::bundler::{create_wallet, launch_bundler, launch_bundling, launch_rpc, launch_uopool}; use clap::Parser; +use silius_primitives::provider::{ + create_http_block_streams, create_http_provider, create_ws_block_streams, create_ws_provider, +}; use std::{future::pending, sync::Arc}; /// Start the bundler with all components (bundling component, user operation mempool, RPC server) @@ -30,11 +30,31 @@ impl BundlerCommand { /// Execute the command pub async fn execute(self) -> eyre::Result<()> { if self.common.eth_client_address.clone().starts_with("http") { - let eth_client = Arc::new(create_http_provider(&self.common.eth_client_address)?); - launch_bundler(self.bundler, self.uopool, self.common, self.rpc, eth_client).await?; + let eth_client = Arc::new(create_http_provider(&self.common.eth_client_address).await?); + let block_streams = + create_http_block_streams(eth_client.clone(), self.common.entry_points.len()).await; + launch_bundler( + self.bundler, + self.uopool, + self.common, + self.rpc, + eth_client, + block_streams, + ) + .await?; } else { let eth_client = Arc::new(create_ws_provider(&self.common.eth_client_address).await?); - launch_bundler(self.bundler, self.uopool, self.common, self.rpc, eth_client).await?; + let block_streams = + create_ws_block_streams(eth_client.clone(), self.common.entry_points.len()).await; + launch_bundler( + self.bundler, + self.uopool, + self.common, + self.rpc, + eth_client, + block_streams, + ) + .await?; } pending().await @@ -61,7 +81,7 @@ impl BundlingCommand { /// Execute the command pub async fn execute(self) -> eyre::Result<()> { if self.common.eth_client_address.clone().starts_with("http") { - let eth_client = Arc::new(create_http_provider(&self.common.eth_client_address)?); + let eth_client = Arc::new(create_http_provider(&self.common.eth_client_address).await?); launch_bundling( self.bundler, eth_client, @@ -102,19 +122,25 @@ impl UoPoolCommand { /// Execute the command pub async fn execute(self) -> eyre::Result<()> { if self.common.eth_client_address.clone().starts_with("http") { - let eth_client = Arc::new(create_http_provider(&self.common.eth_client_address)?); + let eth_client = Arc::new(create_http_provider(&self.common.eth_client_address).await?); + let block_streams = + create_http_block_streams(eth_client.clone(), self.common.entry_points.len()).await; launch_uopool( self.uopool, eth_client, + block_streams, self.common.chain, self.common.entry_points, ) .await?; } else { let eth_client = Arc::new(create_ws_provider(&self.common.eth_client_address).await?); + let block_streams = + create_ws_block_streams(eth_client.clone(), self.common.entry_points.len()).await; launch_uopool( self.uopool, eth_client, + block_streams, self.common.chain, self.common.entry_points, ) diff --git a/bin/silius/src/utils.rs b/bin/silius/src/utils.rs index 44b67972..ff34145b 100644 --- a/bin/silius/src/utils.rs +++ b/bin/silius/src/utils.rs @@ -1,8 +1,5 @@ use dirs::home_dir; -use ethers::{ - providers::{Http, Provider, Ws}, - types::{Address, U256}, -}; +use ethers::types::{Address, U256}; use expanded_pathbuf::ExpandedPathBuf; use pin_utils::pin_mut; use silius_primitives::{bundler::SendBundleMode, UoPoolMode}; @@ -67,15 +64,3 @@ where Ok(()) } - -/// Creates ethers provider with HTTP connection -pub fn create_http_provider(addr: &str) -> eyre::Result> { - let provider = Provider::::try_from(addr)?; - Ok(provider) -} - -/// Creates ethers provider with WebSockets connection -pub async fn create_ws_provider(addr: &str) -> eyre::Result> { - let provider = Provider::::connect_with_reconnects(addr, usize::MAX).await?; - Ok(provider) -} diff --git a/crates/bundler/src/bundler.rs b/crates/bundler/src/bundler.rs index bfdc9220..fb82659d 100644 --- a/crates/bundler/src/bundler.rs +++ b/crates/bundler/src/bundler.rs @@ -435,11 +435,11 @@ pub(crate) fn generate_flashbots_middleware( let mut flashbots_middleware = FlashbotsMiddleware::new( eth_client, - Url::parse(relay_endpoint.clone())?, + Url::parse(relay_endpoint)?, bundle_signer.clone(), ); flashbots_middleware.set_simulation_relay( - Url::parse(relay_endpoint.clone()).expect("Failed to parse simulation relay URL"), + Url::parse(relay_endpoint).expect("Failed to parse simulation relay URL"), bundle_signer.clone(), ); diff --git a/crates/contracts/src/entry_point.rs b/crates/contracts/src/entry_point.rs index 43837af9..1116d3ba 100644 --- a/crates/contracts/src/entry_point.rs +++ b/crates/contracts/src/entry_point.rs @@ -391,7 +391,6 @@ mod tests { fn deserialize_error_msg() -> eyre::Result<()> { let err_msg = Bytes::from_str("0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001841413934206761732076616c756573206f766572666c6f770000000000000000")?; let res = EntryPointAPIErrors::decode(err_msg)?; - println!("res: {:?}", res); match res { EntryPointAPIErrors::RevertString(s) => { assert_eq!(s, "AA94 gas values overflow") diff --git a/crates/grpc/Cargo.toml b/crates/grpc/Cargo.toml index 73ae2cd0..fe60ad33 100644 --- a/crates/grpc/Cargo.toml +++ b/crates/grpc/Cargo.toml @@ -17,6 +17,7 @@ dashmap = "5.4.0" ethers = { workspace = true } expanded-pathbuf = { workspace = true } eyre = { workspace = true } +futures-util = { workspace = true } parking_lot = { workspace = true } prost = "0.11" serde_json = { workspace = true } diff --git a/crates/grpc/src/builder.rs b/crates/grpc/src/builder.rs index ae5e7e46..2a22f29b 100644 --- a/crates/grpc/src/builder.rs +++ b/crates/grpc/src/builder.rs @@ -1,19 +1,27 @@ use crate::uopool::{GAS_INCREASE_PERC, MAX_UOS_PER_UNSTAKED_SENDER}; use ethers::{ providers::Middleware, - types::{Address, U256}, + types::{Address, H256, U256}, }; +use eyre::format_err; +use futures_util::StreamExt; use silius_contracts::EntryPoint; use silius_primitives::{ + get_address, + provider::BlockStream, reputation::{ReputationEntry, BAN_SLACK, MIN_INCLUSION_RATE_DENOMINATOR, THROTTLING_SLACK}, - Chain, + Chain, UserOperation, }; use silius_uopool::{ validate::validator::StandardUserOperationValidator, Mempool, MempoolBox, Reputation, ReputationBox, UoPool, VecCh, VecUo, }; -use std::fmt::{Debug, Display}; use std::sync::Arc; +use std::{ + fmt::{Debug, Display}, + time::Duration, +}; +use tracing::warn; pub struct UoPoolBuilder where @@ -37,9 +45,9 @@ where impl UoPoolBuilder where M: Middleware + Clone + 'static, - P: Mempool + Send + Sync, - R: Reputation, Error = E> + Send + Sync, - E: Debug + Display, + P: Mempool + Send + Sync + 'static, + R: Reputation, Error = E> + Send + Sync + 'static, + E: Debug + Display + 'static, { #[allow(clippy::too_many_arguments)] pub fn new( @@ -55,7 +63,10 @@ where mempool: P, reputation: R, ) -> Self { + // sets mempool let mempool = MempoolBox::::new(mempool); + + // sets reputation let mut reputation = ReputationBox::, R, E>::new(reputation); reputation.init( MIN_INCLUSION_RATE_DENOMINATOR, @@ -67,6 +78,7 @@ where for addr in whitelist.iter() { reputation.add_whitelist(addr); } + Self { is_unsafe, eth_client, @@ -82,6 +94,102 @@ where } } + async fn handle_block_update( + hash: H256, + uopool: &mut UoPool, P, R, E>, + ) -> eyre::Result<()> { + let txs = uopool + .entry_point + .eth_client() + .get_block_with_txs(hash) + .await? + .map(|b| b.transactions); + + if let Some(txs) = txs { + for tx in txs { + if tx.to == Some(uopool.entry_point_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(), + ) + }) + .collect(), + ); + + for uo in uos { + // update reputations + uopool + .reputation + .increment_included(&uo.sender) + .map_err(|e| { + format_err!( + "Failed to increment sender reputation: {:?}", + e.to_string() + ) + })?; + + if let Some(addr) = get_address(&uo.paymaster_and_data) { + uopool.reputation.increment_included(&addr).map_err(|e| { + format_err!( + "Failed to increment paymaster reputation: {:?}", + e.to_string() + ) + })?; + } + + if let Some(addr) = get_address(&uo.init_code) { + uopool.reputation.increment_included(&addr).map_err(|e| { + format_err!( + "Failed to increment factory reputation: {:?}", + e.to_string() + ) + })?; + } + } + } + } + } + } + + Ok(()) + } + + pub fn register_block_updates(&self, mut block_stream: BlockStream) { + let mut uopool = self.uopool(); + tokio::spawn(async move { + while let Some(hash) = block_stream.next().await { + if let Ok(hash) = hash { + let h: H256 = hash; + let _ = Self::handle_block_update(h, &mut uopool) + .await + .map_err(|e| warn!("Failed to handle block update: {:?}", e)); + } + } + }); + } + + pub fn register_reputation_updates(&self) { + let mut uopool = self.uopool(); + tokio::spawn(async move { + loop { + let _ = uopool + .reputation + .update_hourly() + .map_err(|e| warn!("Failed to update hourly reputation: {:?}", e)); + tokio::time::sleep(Duration::from_secs(60 * 60)).await; + } + }); + } + pub fn uopool(&self) -> UoPool, P, R, E> { let entry_point = EntryPoint::::new(self.eth_client.clone(), self.entrypoint_addr); diff --git a/crates/grpc/src/bundler.rs b/crates/grpc/src/bundler.rs index 3887628c..04742bba 100644 --- a/crates/grpc/src/bundler.rs +++ b/crates/grpc/src/bundler.rs @@ -1,5 +1,5 @@ use crate::proto::bundler::*; -use crate::proto::uopool::{GetSortedRequest, HandlePastEventRequest}; +use crate::proto::uopool::GetSortedRequest; use crate::uo_pool_client::UoPoolClient; use async_trait::async_trait; use ethers::providers::Middleware; @@ -9,7 +9,7 @@ use silius_bundler::Bundler; use silius_primitives::{bundler::SendBundleMode, Chain, UserOperation, Wallet}; use std::{net::SocketAddr, sync::Arc, time::Duration}; use tonic::{Request, Response, Status}; -use tracing::{error, info, warn}; +use tracing::{error, info}; pub struct BundlerService where @@ -64,8 +64,6 @@ where Self::get_user_operations(&self.uopool_grpc_client, &bundler.entry_point).await?; let tx_hash = bundler.send_next_bundle(&uos).await?; - Self::handle_past_events(&self.uopool_grpc_client, &bundler.entry_point).await?; - tx_hashes.push(tx_hash) } @@ -87,26 +85,6 @@ where is_running(self.running.clone()) } - async fn handle_past_events( - uopool_grpc_client: &UoPoolClient, - ep: &Address, - ) -> eyre::Result<()> { - let req = Request::new(HandlePastEventRequest { - ep: Some((*ep).into()), - }); - - if let Some(e) = uopool_grpc_client - .clone() - .handle_past_events(req) - .await - .err() - { - warn!("Failed to handle past events: {:?}", e) - }; - - Ok(()) - } - pub fn start_bundling(&self, int: u64) { if !self.is_running() { info!("Starting auto bundling"); @@ -137,14 +115,6 @@ where if let Err(e) = bundler_own.send_next_bundle(&bundle).await { error!("Error while sending bundle: {e:?}"); } - if let Err(e) = Self::handle_past_events( - &uopool_grpc_client, - &bundler_own.entry_point, - ) - .await - { - error!("Error while handling past events: {e:?}"); - } } Err(e) => { error!("Error while creating bundle: {e:?}"); diff --git a/crates/grpc/src/protos/uopool/uopool.proto b/crates/grpc/src/protos/uopool/uopool.proto index eabeaab6..15b07606 100644 --- a/crates/grpc/src/protos/uopool/uopool.proto +++ b/crates/grpc/src/protos/uopool/uopool.proto @@ -90,10 +90,6 @@ message GetUserOperationByHashResponse{ uint64 block_number = 5; } -message HandlePastEventRequest{ - types.H160 ep = 1; -} - message GetUserOperationReceiptResponse{ types.H256 user_operation_hash = 1; types.H160 sender = 2; @@ -115,7 +111,6 @@ service UoPool { rpc EstimateUserOperationGas(EstimateUserOperationGasRequest) returns (EstimateUserOperationGasResponse); rpc GetSortedUserOperations(GetSortedRequest) returns (GetSortedResponse); rpc GetUserOperationByHash(UserOperationHashRequest) returns (GetUserOperationByHashResponse); - rpc HandlePastEvents(HandlePastEventRequest) returns (google.protobuf.Empty); rpc GetUserOperationReceipt(UserOperationHashRequest) returns (GetUserOperationReceiptResponse); // debug diff --git a/crates/grpc/src/uopool.rs b/crates/grpc/src/uopool.rs index ce475bb8..83366224 100644 --- a/crates/grpc/src/uopool.rs +++ b/crates/grpc/src/uopool.rs @@ -12,6 +12,7 @@ use ethers::{ }; use expanded_pathbuf::ExpandedPathBuf; use eyre::Result; +use silius_primitives::provider::BlockStream; use silius_primitives::reputation::ReputationEntry; use silius_primitives::{uopool::AddError, Chain, UoPoolMode}; use silius_uopool::{ @@ -24,7 +25,6 @@ use silius_uopool::{ use std::fmt::{Debug, Display}; use std::{net::SocketAddr, sync::Arc, time::Duration}; use tonic::{Code, Request, Response, Status}; -use tracing::warn; pub const MAX_UOS_PER_UNSTAKED_SENDER: usize = 4; pub const GAS_INCREASE_PERC: u64 = 10; @@ -45,9 +45,9 @@ where impl UoPoolService where M: Middleware + Clone + 'static, - P: Mempool + Send + Sync, - R: Reputation, Error = E> + Send + Sync, - E: Debug + Display, + P: Mempool + Send + Sync + 'static, + R: Reputation, Error = E> + Send + Sync + 'static, + E: Debug + Display + 'static, { pub fn new(uopools: Arc>>, chain: Chain) -> Self { Self { uopools, chain } @@ -206,23 +206,6 @@ where })) } - async fn handle_past_events( - &self, - req: Request, - ) -> Result, Status> { - let req = req.into_inner(); - - let ep = parse_addr(req.ep)?; - let mut uopool = self.get_uopool(&ep)?; - - uopool - .handle_past_events() - .await - .map_err(|e| tonic::Status::internal(format!("Failed to handle past events: {e:?}")))?; - - Ok(Response::new(())) - } - async fn get_user_operation_by_hash( &self, req: Request, @@ -347,6 +330,7 @@ pub async fn uopool_service_run( datadir: ExpandedPathBuf, eps: Vec
, eth_client: Arc, + block_streams: Vec, chain: Chain, max_verification_gas: U256, min_stake: U256, @@ -370,7 +354,7 @@ where env.create_tables() .expect("Create mdbx database tables failed"); - for ep in eps { + for (ep, block_stream) in eps.into_iter().zip(block_streams.into_iter()) { let id = mempool_id(&ep, &U256::from(chain.id())); let builder = UoPoolBuilder::new( upool_mode == UoPoolMode::Unsafe, @@ -385,6 +369,10 @@ where DatabaseMempool::new(env.clone()), DatabaseReputation::new(env.clone()), ); + + builder.register_block_updates(block_stream); + builder.register_reputation_updates(); + m_map.insert(id, builder); } @@ -395,19 +383,6 @@ where DBError, >::new(m_map.clone(), chain)); - tokio::spawn(async move { - loop { - m_map.iter_mut().for_each(|m| { - let _ = m - .uopool() - .reputation - .update_hourly() - .map_err(|e| warn!("Failed to update hourly reputation: {:?}", e)); - }); - tokio::time::sleep(Duration::from_secs(60 * 60)).await; - } - }); - builder.add_service(svc).serve(addr).await }); diff --git a/crates/primitives/Cargo.toml b/crates/primitives/Cargo.toml index 507f4393..eaa307ec 100644 --- a/crates/primitives/Cargo.toml +++ b/crates/primitives/Cargo.toml @@ -11,10 +11,12 @@ AA (ERC-4337) bundler commonly used types """ [dependencies] +async-stream = { workspace = true } educe = { version = "0.4", features = ["Debug", "Default"] } ethers = { workspace = true } expanded-pathbuf = { workspace = true } eyre = { workspace = true } +futures-util = { workspace = true } lazy_static = "1.4.0" rustc-hex = "^2.0.1" serde = "1" diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index 1b2c8dd8..04f5edaa 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -3,6 +3,7 @@ pub mod bundler; pub mod chain; pub mod consts; +pub mod provider; pub mod reputation; pub mod sanity; pub mod simulation; diff --git a/crates/primitives/src/provider.rs b/crates/primitives/src/provider.rs new file mode 100644 index 00000000..43d1714a --- /dev/null +++ b/crates/primitives/src/provider.rs @@ -0,0 +1,71 @@ +use async_stream::stream; +use ethers::{ + providers::{Http, Middleware, Provider, Ws}, + types::{Chain, H256}, +}; +use futures_util::{Stream, StreamExt}; +use std::{pin::Pin, sync::Arc, time::Duration}; + +pub type BlockStream = Pin> + Send>>; + +/// Creates ethers provider with HTTP connection +pub async fn create_http_provider(addr: &str) -> eyre::Result> { + let provider = Provider::::try_from(addr)?; + + let chain_id = provider.get_chainid().await?; + + Ok(provider.interval(if chain_id == Chain::Dev.into() { + Duration::from_millis(5u64) + } else { + Duration::from_millis(500u64) + })) +} + +/// Creates ethers provider with WebSockets connection +pub async fn create_ws_provider(addr: &str) -> eyre::Result> { + let provider = Provider::::connect_with_reconnects(addr, usize::MAX).await?; + Ok(provider) +} + +/// Listens for new blocks over HTTP connection +pub async fn create_http_block_stream(provider: Arc>) -> BlockStream { + Box::pin(stream! { + let mut stream = provider.watch_blocks().await?.stream(); + while let Some(hash) = stream.next().await { + yield Ok(hash); + } + }) +} + +/// Create multiple HTTP block streams +pub async fn create_http_block_streams( + provider: Arc>, + n: usize, +) -> Vec { + let mut streams = Vec::new(); + for _ in 0..n { + streams.push(create_http_block_stream(provider.clone()).await); + } + streams +} + +/// Listens for new block over WS connection +pub async fn create_ws_block_stream(provider: Arc>) -> BlockStream { + Box::pin(stream! { + let mut stream = provider.subscribe_blocks().await?; + while let Some(block) = stream.next().await { + if let Some(hash) = block.hash { + yield Ok(hash); + } + } + }) +} + +/// Creates multiple WS block streams +pub async fn create_ws_block_streams(provider: Arc>, n: usize) -> Vec { + let mut streams = Vec::new(); + for _ in 0..n { + streams.push(create_ws_block_stream(provider.clone()).await); + } + streams +} diff --git a/crates/rpc/Cargo.toml b/crates/rpc/Cargo.toml index d7c8484c..a467eec9 100644 --- a/crates/rpc/Cargo.toml +++ b/crates/rpc/Cargo.toml @@ -16,6 +16,7 @@ ethers = { workspace = true } eyre = { workspace = true } git-version = "0.3.5" hyper = { version = "0.14" } +hyper-tls = { version = "0.5.0" } jsonrpsee = { version = "0.18.2", features = ["server", "macros", "client"] } serde = "1" serde_json = { workspace = true } diff --git a/crates/rpc/src/middleware.rs b/crates/rpc/src/middleware.rs index eafa00a8..75dda047 100644 --- a/crates/rpc/src/middleware.rs +++ b/crates/rpc/src/middleware.rs @@ -1,4 +1,5 @@ use hyper::{Body, Request, Response}; +use hyper_tls::HttpsConnector; use jsonrpsee::core::error::Error as JsonRpcError; use jsonrpsee::types::error::{ErrorCode, METHOD_NOT_FOUND_MSG}; use jsonrpsee::types::ErrorObjectOwned; @@ -106,11 +107,18 @@ where if err.error.code() == ErrorCode::MethodNotFound.code() && err.error.message() == METHOD_NOT_FOUND_MSG { - let client = hyper::Client::new(); - let req = Request::post(addr) + let req = Request::post(addr.clone()) .header(hyper::header::CONTENT_TYPE, "application/json") .body(Body::from(req_bb))?; - let res = client.request(req).await?; + + let res = if addr.starts_with("https") { + let https = HttpsConnector::new(); + let client = hyper::Client::builder().build::<_, hyper::Body>(https); + client.request(req).await? + } else { + let client = hyper::Client::new(); + client.request(req).await? + }; return Ok(res); } } diff --git a/crates/uopool/src/memory/mempool.rs b/crates/uopool/src/memory/mempool.rs index 130ceaf3..e8cc084b 100644 --- a/crates/uopool/src/memory/mempool.rs +++ b/crates/uopool/src/memory/mempool.rs @@ -45,7 +45,7 @@ impl Mempool for MemoryMempool { self.user_operations_by_sender .entry(uo.sender) - .or_insert_with(Default::default) + .or_default() .insert(uo_hash); self.user_operations.insert(uo_hash, uo); diff --git a/crates/uopool/src/uopool.rs b/crates/uopool/src/uopool.rs index 452233e6..4debd273 100644 --- a/crates/uopool/src/uopool.rs +++ b/crates/uopool/src/uopool.rs @@ -11,11 +11,11 @@ use crate::{ use ethers::{ prelude::LogMeta, providers::Middleware, - types::{Address, BlockNumber, U256, U64}, + types::{Address, BlockNumber, U256}, }; use eyre::format_err; use silius_contracts::{ - entry_point::{EntryPointAPIEvents, EntryPointErr, UserOperationEventFilter}, + entry_point::{EntryPointErr, UserOperationEventFilter}, utils::parse_from_input_data, EntryPoint, }; @@ -588,48 +588,6 @@ where Err(format_err!("No user operation found")) } - /// Queries the [EntryPoint](EntryPoint) contract for the past events logged that are included in the current block. - /// If [UserOperation](UserOperation) is found, it is removed from the [UserOperationQueue](UserOperationQueue), while simultaneously incrementing the reputation of the sender and paymaster. - /// - /// # Returns - /// `Result<(), eyre::Error>` - None if the query was successful. - pub async fn handle_past_events(&mut self) -> eyre::Result<()> { - let block_num = self.entry_point.eth_client().get_block_number().await?; - let block_st = std::cmp::max( - 1u64, - block_num - .checked_sub(U64::from(LATEST_SCAN_DEPTH)) - .unwrap_or(U64::from(0)) - .as_u64(), - ); - - let filter = self.entry_point.events().from_block(block_st); - let events = filter.query().await?; - - for event in events { - match event { - EntryPointAPIEvents::UserOperationEventFilter(uo_event) => { - self.remove_user_operation(&uo_event.user_op_hash.into()); - self.reputation - .increment_included(&uo_event.sender) - .map_err(|err| eyre::eyre!(err.to_string()))?; - self.reputation - .increment_included(&uo_event.paymaster) - .map_err(|err| eyre::eyre!(err.to_string()))?; - // TODO: include event aggregator - } - EntryPointAPIEvents::AccountDeployedFilter(event) => { - self.reputation - .increment_included(&event.factory) - .map_err(|err| eyre::eyre!(err.to_string()))?; - } - _ => (), - } - } - - Ok(()) - } - /// Removes the [UserOperation](UserOperation) from the [UserOperationQueue](UserOperationQueue) given the [UserOperationHash](UserOperationHash). /// /// # Arguments diff --git a/crates/uopool/src/validate/simulation_trace/storage_access.rs b/crates/uopool/src/validate/simulation_trace/storage_access.rs index 7b344e80..c5999474 100644 --- a/crates/uopool/src/validate/simulation_trace/storage_access.rs +++ b/crates/uopool/src/validate/simulation_trace/storage_access.rs @@ -47,10 +47,7 @@ impl StorageAccess { if kecc.starts_with(&addr_b) { let k = keccak256(kecc.clone()); - slots - .entry(entity.address) - .or_insert(HashSet::new()) - .insert(k.into()); + slots.entry(entity.address).or_default().insert(k.into()); } } } diff --git a/examples/simple_account/create.rs b/examples/simple_account/create.rs index ae58f58d..5e7aa50a 100644 --- a/examples/simple_account/create.rs +++ b/examples/simple_account/create.rs @@ -20,12 +20,15 @@ const CREATE_INDEX: u64 = 2; async fn main() -> eyre::Result<()> { let seed_phrase = env::var("SEED_PHRASE").unwrap(); let bundler_url = env::var("BUNDLER_URL").unwrap(); + + let provider = + Provider::::try_from(bundler_url.as_str())?.interval(Duration::from_millis(10u64)); + let chain_id = provider.get_chainid().await?.as_u64(); + let wallet = MnemonicBuilder::::default() .phrase(seed_phrase.as_str()) .build()?; - let provider = - Provider::::try_from(bundler_url.as_str())?.interval(Duration::from_millis(10u64)); - let client = SignerMiddleware::new(provider.clone(), wallet.clone().with_chain_id(5u64)) + let client = SignerMiddleware::new(provider.clone(), wallet.clone().with_chain_id(chain_id)) .nonce_manager(wallet.address()); let provider = Arc::new(client); @@ -41,6 +44,7 @@ async fn main() -> eyre::Result<()> { .call() .await?; println!("Smart account addresss: {:?}", address); + let nonce = provider.get_transaction_count(address, None).await?; let call = simple_account_factory.create_account(owner_address, U256::from(CREATE_INDEX)); let tx: TypedTransaction = call.tx; @@ -71,12 +75,12 @@ async fn main() -> eyre::Result<()> { paymaster_and_data: Bytes::new(), signature: Bytes::default(), }; - let uo_wallet = UoWallet::from_phrase(seed_phrase.as_str(), &U256::from(5), false)?; + let uo_wallet = UoWallet::from_phrase(seed_phrase.as_str(), &chain_id.into(), false)?; let user_op = uo_wallet .sign_uo( &user_op, &ADDRESS.to_string().parse::
()?, - &U256::from(5), + &chain_id.into(), ) .await?; @@ -116,7 +120,7 @@ async fn main() -> eyre::Result<()> { .sign_uo( &user_op, &ADDRESS.to_string().parse::
()?, - &U256::from(5), + &chain_id.into(), ) .await?; diff --git a/examples/simple_account/deposit.rs b/examples/simple_account/deposit.rs index 188ab700..ab94d330 100644 --- a/examples/simple_account/deposit.rs +++ b/examples/simple_account/deposit.rs @@ -12,18 +12,21 @@ use std::{env, sync::Arc, time::Duration}; // stackup simple account factory const SIMPLE_ACCOUNT_FACTORY: &str = "0x9406Cc6185a346906296840746125a0E44976454"; -const CREATE_INDEX: u64 = 1; +const CREATE_INDEX: u64 = 2; const SEND_VALUE: &str = "0.01"; // ether unit #[tokio::main] async fn main() -> eyre::Result<()> { let seed_phrase = env::var("SEED_PHRASE").unwrap(); let provider_url = env::var("PROVIDER_URL").unwrap(); + + let provider = Provider::::try_from(provider_url)?.interval(Duration::from_millis(10u64)); + let chain_id = provider.get_chainid().await?.as_u64(); + let wallet = MnemonicBuilder::::default() .phrase(seed_phrase.as_str()) .build()?; - let provider = Provider::::try_from(provider_url)?.interval(Duration::from_millis(10u64)); - let client = SignerMiddleware::new(provider.clone(), wallet.clone().with_chain_id(5u64)) + let client = SignerMiddleware::new(provider.clone(), wallet.clone().with_chain_id(chain_id)) .nonce_manager(wallet.address()) .gas_oracle(ProviderOracle::new(provider.clone())); let provider = Arc::new(client); diff --git a/examples/simple_account/transfer.rs b/examples/simple_account/transfer.rs index 9643f09e..445aaed5 100644 --- a/examples/simple_account/transfer.rs +++ b/examples/simple_account/transfer.rs @@ -3,6 +3,7 @@ use ethers::{ providers::{Http, Middleware, Provider}, signers::{coins_bip39::English, MnemonicBuilder, Signer}, types::{Address, Bytes, U256}, + utils::parse_ether, }; use reqwest; use silius_contracts::EntryPoint; @@ -11,23 +12,27 @@ use silius_primitives::consts::entry_point::ADDRESS; use silius_primitives::UserOperation; use silius_primitives::Wallet as UoWallet; use silius_tests::common::gen::SimpleAccountFactory; -use std::{env, str::FromStr, sync::Arc, time::Duration}; +use std::{env, sync::Arc, time::Duration}; // stackup simple account factory const SIMPLE_ACCOUNT_FACTORY: &str = "0x9406Cc6185a346906296840746125a0E44976454"; // 0.6.0 entrypoint address -const CREATE_INDEX: u64 = 1; +const CREATE_INDEX: u64 = 2; +const TRANSFER_VALUE: &str = "0.01"; // ether unit #[tokio::main] async fn main() -> eyre::Result<()> { let seed_phrase = env::var("SEED_PHRASE").unwrap(); let bundler_url = env::var("BUNDLER_URL").unwrap(); + + let provider = + Provider::::try_from(bundler_url.as_str())?.interval(Duration::from_millis(10u64)); + let chain_id = provider.get_chainid().await?.as_u64(); + let wallet = MnemonicBuilder::::default() .phrase(seed_phrase.as_str()) .build()?; - let provider = - Provider::::try_from(bundler_url.as_str())?.interval(Duration::from_millis(10u64)); - let client = SignerMiddleware::new(provider.clone(), wallet.clone().with_chain_id(5u64)) + let client = SignerMiddleware::new(provider.clone(), wallet.clone().with_chain_id(chain_id)) .nonce_manager(wallet.address()); let provider = Arc::new(client); @@ -54,31 +59,28 @@ async fn main() -> eyre::Result<()> { gas_price, priority_fee ); - let execution = SimpleAccountExecute::new( - Address::from_str("0x21A14e061fe67A3060ef238DDD8Bf90c81829F7d")?, - U256::from(1), - Bytes::default(), - ); + let execution = + SimpleAccountExecute::new(address, parse_ether(TRANSFER_VALUE)?, Bytes::default()); let user_op = UserOperation { sender: address, nonce, init_code: Bytes::default(), call_data: Bytes::from(execution.encode()), - call_gas_limit: U256::from(10000u64), - verification_gas_limit: U256::from(10000u64), + call_gas_limit: U256::from(1), + verification_gas_limit: U256::from(1000000u64), pre_verification_gas: U256::from(1u64), max_fee_per_gas: U256::from(1), max_priority_fee_per_gas: priority_fee, paymaster_and_data: Bytes::new(), signature: Bytes::default(), }; - let uo_wallet = UoWallet::from_phrase(seed_phrase.as_str(), &U256::from(5), false)?; + let uo_wallet = UoWallet::from_phrase(seed_phrase.as_str(), &chain_id.into(), false)?; let user_op = uo_wallet .sign_uo( &user_op, &ADDRESS.to_string().parse::
()?, - &U256::from(5), + &chain_id.into(), ) .await?; @@ -108,7 +110,10 @@ async fn main() -> eyre::Result<()> { .result .pre_verification_gas .saturating_add(U256::from(1000)), - verification_gas_limit: v.result.verification_gas_limit, + 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, @@ -118,7 +123,7 @@ async fn main() -> eyre::Result<()> { .sign_uo( &user_op, &ADDRESS.to_string().parse::
()?, - &U256::from(5), + &chain_id.into(), ) .await?;