From f46e72c3aedb7d53536947ca5a2b4ff75e7e00d5 Mon Sep 17 00:00:00 2001 From: zeroXbrock <2791467+zeroXbrock@users.noreply.github.com> Date: Tue, 29 Oct 2024 23:09:08 -0700 Subject: [PATCH 01/19] [unfinished] refactor Generator trait to process bundles (ExecutionPayload) --- cli/testfile.toml | 10 ++ src/generator/mod.rs | 264 ++++++++++++++++++------------------- src/generator/named_txs.rs | 95 +++++++++++++ src/generator/types.rs | 31 +++-- 4 files changed, 253 insertions(+), 147 deletions(-) create mode 100644 cli/testfile.toml create mode 100644 src/generator/named_txs.rs diff --git a/cli/testfile.toml b/cli/testfile.toml new file mode 100644 index 0000000..d9a7ea4 --- /dev/null +++ b/cli/testfile.toml @@ -0,0 +1,10 @@ +[[create]] +bytecode = "0x6080604052348015600f57600080fd5b506104f48061001f6000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c806369f86ec8146100515780639402c0041461005b578063a329e8de1461006e578063fb0e722b14610081575b600080fd5b61005961009f565b005b6100596100693660046101a5565b6100aa565b61005961007c36600461025e565b6100df565b610089610101565b604051610096919061029b565b60405180910390f35b5b60325a116100a057565b6000816040516020016100be929190610308565b604051602081830303815290604052600090816100db91906103dd565b5050565b60005b6100ed60db8361049c565b8110156100db5760008190556001016100e2565b6000805461010e906102ce565b80601f016020809104026020016040519081016040528092919081815260200182805461013a906102ce565b80156101875780601f1061015c57610100808354040283529160200191610187565b820191906000526020600020905b81548152906001019060200180831161016a57829003601f168201915b505050505081565b634e487b7160e01b600052604160045260246000fd5b6000602082840312156101b757600080fd5b813567ffffffffffffffff8111156101ce57600080fd5b8201601f810184136101df57600080fd5b803567ffffffffffffffff8111156101f9576101f961018f565b604051601f8201601f19908116603f0116810167ffffffffffffffff811182821017156102285761022861018f565b60405281815282820160200186101561024057600080fd5b81602084016020830137600091810160200191909152949350505050565b60006020828403121561027057600080fd5b5035919050565b60005b8381101561029257818101518382015260200161027a565b50506000910152565b60208152600082518060208401526102ba816040850160208701610277565b601f01601f19169190910160400192915050565b600181811c908216806102e257607f821691505b60208210810361030257634e487b7160e01b600052602260045260246000fd5b50919050565b6000808454610316816102ce565b60018216801561032d576001811461034257610372565b60ff1983168652811515820286019350610372565b87600052602060002060005b8381101561036a5781548882015260019091019060200161034e565b505081860193505b5050508351610385818360208801610277565b01949350505050565b601f8211156103d857806000526020600020601f840160051c810160208510156103b55750805b601f840160051c820191505b818110156103d557600081556001016103c1565b50505b505050565b815167ffffffffffffffff8111156103f7576103f761018f565b61040b8161040584546102ce565b8461038e565b6020601f82116001811461043f57600083156104275750848201515b600019600385901b1c1916600184901b1784556103d5565b600084815260208120601f198516915b8281101561046f578785015182556020948501946001909201910161044f565b508482101561048d5786840151600019600387901b60f8161c191681555b50505050600190811b01905550565b6000826104b957634e487b7160e01b600052601260045260246000fd5b50049056fea26469706673582212207301a1db30de4d24440f5ed9a7dc130e8bc19f454451a8422ba4f1308087182964736f6c634300081b0033" +name = "SpamMe" +from = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" + +[[spam]] +to = "{SpamMe}" +from = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" +signature = "consumeGas(uint256)" +args = ["50000"] diff --git a/src/generator/mod.rs b/src/generator/mod.rs index a7aec03..46ed881 100644 --- a/src/generator/mod.rs +++ b/src/generator/mod.rs @@ -9,100 +9,48 @@ use crate::{ Result, }; use alloy::primitives::U256; -use alloy::rpc::types::TransactionRequest; use async_trait::async_trait; +use named_txs::ExecutionPayload; +pub use named_txs::NamedTxRequestBuilder; pub use seeder::rand_seed::RandSeed; use std::{collections::HashMap, fmt::Debug, hash::Hash}; +use types::SpamRequest; pub use types::{CallbackResult, NamedTxRequest, PlanType}; +/// Defines named tx requests, which are used to store transaction requests with optional names and kinds. +/// Used for tracking transactions in a test scenario. +pub mod named_txs; + /// Generates values for fuzzed parameters. /// Contains the Seeder trait and an implementation. pub mod seeder; + /// Provides templating for transaction requests, etc. /// Contains the Templater trait and an implementation. pub mod templater; + /// Contains types used by the generator module. pub mod types; + /// Utility functions used in the generator module. pub mod util; -/// Syntactical sugar for creating a [`NamedTxRequest`]. -/// -/// This is useful for imperatively assigning optional fields to a tx. -/// It is _not_ useful when you're dynamically assigning these fields (i.e. you have an Option to check first). -/// -/// ### Example: -/// ``` -/// use alloy::rpc::types::TransactionRequest; -/// # use contender_core::generator::NamedTxRequestBuilder; -/// -/// let tx_req = TransactionRequest::default(); -/// let named_tx_req = NamedTxRequestBuilder::new(tx_req) -/// .with_name("unique_tx_name") -/// .with_kind("tx_kind") -/// .build(); -/// assert_eq!(named_tx_req.name, Some("unique_tx_name".to_owned())); -/// assert_eq!(named_tx_req.kind, Some("tx_kind".to_owned())); -/// ``` -pub struct NamedTxRequestBuilder { - name: Option, - kind: Option, - tx: TransactionRequest, -} - -impl NamedTxRequestBuilder { - pub fn new(tx: TransactionRequest) -> Self { - Self { - name: None, - kind: None, - tx, - } - } - - pub fn with_name(&mut self, name: &str) -> &mut Self { - self.name = Some(name.to_owned()); - self - } - - pub fn with_kind(&mut self, kind: &str) -> &mut Self { - self.kind = Some(kind.to_owned()); - self - } - - pub fn build(&self) -> NamedTxRequest { - NamedTxRequest::new( - self.tx.to_owned(), - self.name.to_owned(), - self.kind.to_owned(), - ) - } -} - -impl NamedTxRequest { - pub fn new(tx: TransactionRequest, name: Option, kind: Option) -> Self { - Self { name, kind, tx } - } -} - -impl From for NamedTxRequest { - fn from(tx: TransactionRequest) -> Self { - Self { - name: None, - kind: None, - tx, - } - } -} - pub trait PlanConfig where K: Eq + Hash + Debug + Send + Sync, { + /// Get \[\[env]] variables from the plan configuration. fn get_env(&self) -> Result>; + + /// Get contract-creation steps from the plan configuration. fn get_create_steps(&self) -> Result>; + + /// Get setup transactions from the plan configuration. fn get_setup_steps(&self) -> Result>; - fn get_spam_steps(&self) -> Result>; + + /// Get spam step templates from the plan configuration. + fn get_spam_steps(&self) -> Result>; } #[async_trait] @@ -117,7 +65,8 @@ where fn get_db(&self) -> &D; fn get_fuzz_seeder(&self) -> &impl Seeder; - fn get_fuzz_map( + /// Generates a map of N=`num_values` fuzzed values for each parameter in `fuzz_args`. + fn create_fuzz_map( &self, num_values: usize, fuzz_args: &[FuzzParam], @@ -137,7 +86,7 @@ where async fn load_txs CallbackResult>( &self, plan_type: PlanType, - ) -> Result> { + ) -> Result> { let conf = self.get_plan_conf(); let env = conf.get_env().unwrap_or_default(); let db = self.get_db(); @@ -148,7 +97,8 @@ where placeholder_map.insert(key.to_owned(), value.to_owned()); } - let mut txs = vec![]; + let mut txs: Vec = vec![]; + match plan_type { PlanType::Create(on_create_step) => { let create_steps = conf.get_create_steps()?; @@ -169,7 +119,7 @@ where ContenderError::with_err(e, "join error; callback crashed") })?; } - txs.push(tx); + txs.push(tx.into()); } } PlanType::Setup(on_setup_step) => { @@ -191,7 +141,7 @@ where ContenderError::with_err(e, "join error; callback crashed") })?; } - txs.push(tx); + txs.push(tx.into()); } } PlanType::Spam(num_txs, on_spam_setup) => { @@ -199,82 +149,120 @@ where let num_steps = spam_steps.len(); // round num_txs up to the nearest multiple of num_steps to prevent missed steps let num_txs = num_txs + (num_txs % num_steps); + let mut placeholder_map = HashMap::::new(); + let mut canonical_fuzz_map = HashMap::>::new(); for step in spam_steps.iter() { - // find templates from fn call - templater.find_fncall_placeholders(step, db, &mut placeholder_map)?; - let fn_args = step.args.to_owned().unwrap_or_default(); + // finds fuzzed values for a function call definition and populates `canonical_fuzz_map` with fuzzy values. + let mut find_fuzz = |req: &FunctionCallDefinition| { + let fuzz_args = req.fuzz.to_owned().unwrap_or(vec![]); + let fuzz_map = self.create_fuzz_map(num_txs, &fuzz_args); // this may create more values than needed, but it's fine + canonical_fuzz_map.extend(fuzz_map); + }; - // parse fn signature, used to check for fuzzed args later (to make sure they're named) - let func = alloy::json_abi::Function::parse(&step.signature).map_err(|e| { - ContenderError::with_err(e, "failed to parse function name") - })?; + // finds placeholders in a function call definition and populates `placeholder_map` and `canonical_fuzz_map` with injectable values. + let mut lookup_tx_placeholders = |tx: &FunctionCallDefinition| { + templater.find_fncall_placeholders(tx, db, &mut placeholder_map); + find_fuzz(tx); + }; - // pre-generate fuzzy values for each fuzzed parameter - let fuzz_args = step.fuzz.to_owned().unwrap_or(vec![]); - let fuzz_map = self.get_fuzz_map(num_txs / num_steps, &fuzz_args); - - // generate spam txs; split total amount by number of spam steps - for i in 0..(num_txs / num_steps) { - // check args for fuzz params first - let mut args = Vec::new(); - for j in 0..fn_args.len() { - let maybe_fuzz = || { - let input_def = func.inputs[j].to_string(); - // there's probably a better way to do this, but I haven't found it - let arg_namedefs = - input_def.split_ascii_whitespace().collect::>(); - if arg_namedefs.len() < 2 { - // can't fuzz unnamed params - return None; - } - let arg_name = arg_namedefs[1]; - if fuzz_map.contains_key(arg_name) { - return Some( - fuzz_map.get(arg_name).expect("this should never happen") - [i] - .to_string(), - ); - } - None - }; - - // !!! args with template values will be overwritten by the fuzzer if it's enabled for this arg - let val = maybe_fuzz().unwrap_or(fn_args[j].to_owned()); - args.push(val); + // populate maps for each step + match step { + SpamRequest::Single(tx) => { + lookup_tx_placeholders(tx); + } + SpamRequest::Bundle(req) => { + for tx in req.txs.iter() { + lookup_tx_placeholders(tx); + } } - let mut step = step.to_owned(); - step.args = Some(args); + }; + } - let tx = NamedTxRequest::new( - templater.template_function_call(&step, &placeholder_map)?, - None, - step.kind, - ); + for i in 0..(num_txs / num_steps) { + for step in spam_steps.iter() { + // converts a FunctionCallDefinition to a NamedTxRequest (filling in fuzzable args), + // returns a callback handle and the processed tx request + let process_tx = |req| { + let args = get_fuzzed_args(req, &canonical_fuzz_map, i); + let mut req = req.to_owned(); + req.args = Some(args); + let tx = NamedTxRequest::new( + templater.template_function_call(&req, &placeholder_map)?, + None, + req.kind.to_owned(), + ); + return Ok((on_spam_setup(tx.to_owned())?, tx)); + }; - let handle = on_spam_setup(tx.to_owned())?; - if let Some(handle) = handle { - handle - .await - .map_err(|e| ContenderError::with_err(e, "error from callback"))?; + match step { + SpamRequest::Single(req) => { + let (handle, tx) = process_tx(req)?; + if let Some(handle) = handle { + handle.await.map_err(|e| { + ContenderError::with_err(e, "error from callback") + })?; + } + txs.push(tx.into()); + } + SpamRequest::Bundle(req) => { + let mut bundle_txs = vec![]; + for tx in req.txs.iter() { + let (handle, txr) = process_tx(tx)?; + if let Some(handle) = handle { + handle.await.map_err(|e| { + ContenderError::with_err(e, "error from callback") + })?; + } + bundle_txs.push(txr); + } + txs.push(bundle_txs.into()); + } } - txs.push(tx); } } - - // interleave spam txs to evenly distribute various calls - // this may create contention if different senders are specified for each call - let chunksize = txs.len() / num_steps; - let mut new_templates = vec![]; - for i in 0..txs.len() { - let chunk_idx = chunksize * (i % num_steps); - let idx = (i / num_steps) + chunk_idx; - new_templates.push(txs[idx].to_owned()); - } - return Ok(new_templates); } } Ok(txs) } } + +/// For the given function call definition, return the fuzzy arguments for the given fuzz index. +fn get_fuzzed_args( + tx: &FunctionCallDefinition, + fuzz_map: &HashMap>, + fuzz_idx: usize, +) -> Vec { + // let mut args = Vec::new(); + let func = + alloy::json_abi::Function::parse(&tx.signature).expect("failed to parse function name"); + let tx_args = tx.args.as_deref().unwrap_or_default(); + tx_args + .iter() + .enumerate() + .map(|(idx, arg)| { + let maybe_fuzz = || { + let input_def = func.inputs[idx].to_string(); + // there's probably a better way to do this, but I haven't found it + // we're looking for something like "uint256 arg_name" in input_def + let arg_namedefs = input_def.split_ascii_whitespace().collect::>(); + if arg_namedefs.len() < 2 { + // can't fuzz unnamed params + return None; + } + let arg_name = arg_namedefs[1]; + if fuzz_map.contains_key(arg_name) { + return Some( + fuzz_map.get(arg_name).expect("this should never happen")[fuzz_idx] + .to_string(), + ); + } + None + }; + + // !!! args with template values will be overwritten by the fuzzer if it's enabled for this arg + maybe_fuzz().unwrap_or(arg.to_owned()) + }) + .collect() +} diff --git a/src/generator/named_txs.rs b/src/generator/named_txs.rs new file mode 100644 index 0000000..5a6e903 --- /dev/null +++ b/src/generator/named_txs.rs @@ -0,0 +1,95 @@ +use alloy::rpc::types::TransactionRequest; + +/// Wrapper for [`TransactionRequest`](alloy::rpc::types::TransactionRequest) that includes optional name and kind fields. +#[derive(Clone, Debug)] +pub struct NamedTxRequest { + pub name: Option, + pub kind: Option, + pub tx: TransactionRequest, +} + +/// Syntactical sugar for creating a [`NamedTxRequest`]. +/// +/// This is useful for imperatively assigning optional fields to a tx. +/// It is _not_ useful when you're dynamically assigning these fields (i.e. you have an Option to check first). +/// +/// ### Example: +/// ``` +/// use alloy::rpc::types::TransactionRequest; +/// # use contender_core::generator::NamedTxRequestBuilder; +/// +/// let tx_req = TransactionRequest::default(); +/// let named_tx_req = NamedTxRequestBuilder::new(tx_req) +/// .with_name("unique_tx_name") +/// .with_kind("tx_kind") +/// .build(); +/// assert_eq!(named_tx_req.name, Some("unique_tx_name".to_owned())); +/// assert_eq!(named_tx_req.kind, Some("tx_kind".to_owned())); +/// ``` +pub struct NamedTxRequestBuilder { + name: Option, + kind: Option, + tx: TransactionRequest, +} + +#[derive(Clone, Debug)] +pub enum ExecutionPayload { + Tx(NamedTxRequest), + Bundle(Vec), +} + +impl From for ExecutionPayload { + fn from(tx: NamedTxRequest) -> Self { + Self::Tx(tx) + } +} + +impl From> for ExecutionPayload { + fn from(txs: Vec) -> Self { + Self::Bundle(txs) + } +} + +impl NamedTxRequestBuilder { + pub fn new(tx: TransactionRequest) -> Self { + Self { + name: None, + kind: None, + tx, + } + } + + pub fn with_name(&mut self, name: &str) -> &mut Self { + self.name = Some(name.to_owned()); + self + } + + pub fn with_kind(&mut self, kind: &str) -> &mut Self { + self.kind = Some(kind.to_owned()); + self + } + + pub fn build(&self) -> NamedTxRequest { + NamedTxRequest::new( + self.tx.to_owned(), + self.name.to_owned(), + self.kind.to_owned(), + ) + } +} + +impl NamedTxRequest { + pub fn new(tx: TransactionRequest, name: Option, kind: Option) -> Self { + Self { name, kind, tx } + } +} + +impl From for NamedTxRequest { + fn from(tx: TransactionRequest) -> Self { + Self { + name: None, + kind: None, + tx, + } + } +} diff --git a/src/generator/types.rs b/src/generator/types.rs index 23bc81e..8055e8e 100644 --- a/src/generator/types.rs +++ b/src/generator/types.rs @@ -1,24 +1,24 @@ +use super::named_txs::ExecutionPayload; use alloy::{ network::AnyNetwork, primitives::U256, providers::RootProvider, - rpc::types::TransactionRequest, transports::http::{Client, Http}, }; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use tokio::task::JoinHandle; +// -- re-exports +pub use crate::generator::named_txs::NamedTxRequest; + +// -- convenience pub type EthProvider = RootProvider>; pub type AnyProvider = RootProvider, AnyNetwork>; -#[derive(Clone, Debug)] -pub struct NamedTxRequest { - pub name: Option, - pub kind: Option, - pub tx: TransactionRequest, -} +// -- core types for test scenarios +/// User-facing definition of a function call to be executed. #[derive(Clone, Deserialize, Debug, Serialize)] pub struct FunctionCallDefinition { /// Address of the contract to call. @@ -34,7 +34,20 @@ pub struct FunctionCallDefinition { /// Parameters to fuzz during the test. pub fuzz: Option>, /// Optional type of the spam transaction for categorization. - pub kind: Option + pub kind: Option, +} + +/// User-facing definition of a function call to be executed. +#[derive(Clone, Deserialize, Debug, Serialize)] +pub struct BundleCallDefinition { + #[serde(rename = "tx")] + pub txs: Vec, +} + +/// +pub enum SpamRequest { + Single(FunctionCallDefinition), + Bundle(BundleCallDefinition), } #[derive(Clone, Deserialize, Debug, Serialize)] @@ -62,7 +75,7 @@ pub struct Plan { pub env: HashMap, pub create_steps: Vec, pub setup_steps: Vec, - pub spam_steps: Vec, + pub spam_steps: Vec, } pub type CallbackResult = crate::Result>>; From bcefd6502e8c4fc10b296e06f82c0b2ac5584f1f Mon Sep 17 00:00:00 2001 From: zeroXbrock <2791467+zeroXbrock@users.noreply.github.com> Date: Wed, 30 Oct 2024 03:04:51 -0700 Subject: [PATCH 02/19] introduce new ExecutionPayload & ExecutionRequest, DRY & simplify --- src/generator/named_txs.rs | 6 +- src/generator/types.rs | 4 +- src/spammer/blockwise.rs | 260 ++++++++++++++++++++++++------------- src/spammer/mod.rs | 11 +- 4 files changed, 185 insertions(+), 96 deletions(-) diff --git a/src/generator/named_txs.rs b/src/generator/named_txs.rs index 5a6e903..72ad365 100644 --- a/src/generator/named_txs.rs +++ b/src/generator/named_txs.rs @@ -33,18 +33,18 @@ pub struct NamedTxRequestBuilder { } #[derive(Clone, Debug)] -pub enum ExecutionPayload { +pub enum ExecutionRequest { Tx(NamedTxRequest), Bundle(Vec), } -impl From for ExecutionPayload { +impl From for ExecutionRequest { fn from(tx: NamedTxRequest) -> Self { Self::Tx(tx) } } -impl From> for ExecutionPayload { +impl From> for ExecutionRequest { fn from(txs: Vec) -> Self { Self::Bundle(txs) } diff --git a/src/generator/types.rs b/src/generator/types.rs index 8055e8e..acc490a 100644 --- a/src/generator/types.rs +++ b/src/generator/types.rs @@ -1,4 +1,4 @@ -use super::named_txs::ExecutionPayload; +use super::named_txs::ExecutionRequest; use alloy::{ network::AnyNetwork, primitives::U256, @@ -75,7 +75,7 @@ pub struct Plan { pub env: HashMap, pub create_steps: Vec, pub setup_steps: Vec, - pub spam_steps: Vec, + pub spam_steps: Vec, } pub type CallbackResult = crate::Result>>; diff --git a/src/spammer/blockwise.rs b/src/spammer/blockwise.rs index c4ce362..2665d61 100644 --- a/src/spammer/blockwise.rs +++ b/src/spammer/blockwise.rs @@ -1,15 +1,19 @@ use crate::db::DbOps; use crate::error::ContenderError; +use crate::generator::named_txs::ExecutionRequest; use crate::generator::seeder::Seeder; use crate::generator::templater::Templater; use crate::generator::types::{AnyProvider, EthProvider, PlanType}; -use crate::generator::{Generator, PlanConfig}; +use crate::generator::{Generator, NamedTxRequest, PlanConfig}; +use crate::spammer::ExecutionPayload; use crate::test_scenario::TestScenario; use crate::Result; use alloy::hex::ToHexExt; -use alloy::network::{AnyNetwork, TransactionBuilder}; -use alloy::primitives::FixedBytes; +use alloy::network::{AnyNetwork, EthereumWallet, NetworkWallet, TransactionBuilder}; +use alloy::primitives::{Address, FixedBytes}; use alloy::providers::{Provider, ProviderBuilder}; +use alloy::rpc::client::ReqwestClient; +use alloy::rpc::types::TransactionRequest; use futures::StreamExt; use std::collections::HashMap; use std::ops::Deref; @@ -31,6 +35,8 @@ where rpc_client: AnyProvider, eth_client: Arc, callback_handler: Arc, + nonces: HashMap, + gas_limits: HashMap, u128>, } impl BlockwiseSpammer @@ -40,7 +46,7 @@ where S: Seeder + Send + Sync, P: PlanConfig + Templater + Send + Sync, { - pub fn new(scenario: TestScenario, callback_handler: F) -> Self { + pub async fn new(scenario: TestScenario, callback_handler: F) -> Self { let rpc_client = ProviderBuilder::new() .network::() .on_http(scenario.rpc_url.to_owned()); @@ -52,17 +58,90 @@ where )); let callback_handler = Arc::new(callback_handler); + // get nonce for each signer and put it into a hashmap + let mut nonces = HashMap::new(); + for (addr, _) in scenario.wallet_map.iter() { + let nonce = eth_client + .get_transaction_count(*addr) + .await + .expect("failed to retrieve nonce"); + nonces.insert(*addr, nonce); + } + + // track gas limits for each function signature + let gas_limits = HashMap::, u128>::new(); + Self { scenario, rpc_client, eth_client, callback_handler, msg_handler, + nonces, + gas_limits, + } + } + + async fn prepare_tx_req( + &mut self, + tx_req: &TransactionRequest, + gas_price: u128, + chain_id: u64, + ) -> Result<(TransactionRequest, EthereumWallet)> { + let from = tx_req.from.expect("missing from address"); + let nonce = self + .nonces + .get(&from) + .expect("failed to get nonce") + .to_owned(); + /* + Increment nonce assuming the tx will succeed. + Note: if any tx fails, txs with higher nonces will also fail. + However, we'll get a fresh nonce next block. + */ + self.nonces.insert(from.to_owned(), nonce + 1); + let fn_sig = FixedBytes::<4>::from_slice( + tx_req + .input + .input + .to_owned() + .map(|b| b.split_at(4).0.to_owned()) + .expect("invalid function call") + .as_slice(), + ); + if !self.gas_limits.contains_key(fn_sig.as_slice()) { + let gas_limit = self + .eth_client + .estimate_gas(&tx_req.to_owned()) + .await + .map_err(|e| ContenderError::with_err(e, "failed to estimate gas"))?; + self.gas_limits.insert(fn_sig, gas_limit); } + // query hashmaps for gaslimit & signer of this tx + let gas_limit = self + .gas_limits + .get(&fn_sig) + .expect("failed to get gas limit") + .to_owned(); + let signer = self + .scenario + .wallet_map + .get(&from) + .expect("failed to create signer") + .to_owned(); + + let full_tx = tx_req + .clone() + .with_nonce(nonce) + .with_gas_price(gas_price) + .with_chain_id(chain_id) + .with_gas_limit(gas_limit); + + Ok((full_tx, signer)) } pub async fn spam_rpc( - &self, + &mut self, txs_per_block: usize, num_blocks: usize, run_id: Option, @@ -98,18 +177,18 @@ where .take(num_blocks); let mut tasks = vec![]; - let mut gas_limits = HashMap::, u128>::new(); + // let mut gas_limits = HashMap::, u128>::new(); - // get nonce for each signer and put it into a hashmap - let mut nonces = HashMap::new(); - for (addr, _) in self.scenario.wallet_map.iter() { - let nonce = self - .rpc_client - .get_transaction_count(*addr) - .await - .map_err(|_| ContenderError::SpamError("failed to get nonce", None))?; - nonces.insert(*addr, nonce); - } + // // get nonce for each signer and put it into a hashmap + // let mut nonces = HashMap::new(); + // for (addr, _) in self.scenario.wallet_map.iter() { + // let nonce = self + // .rpc_client + // .get_transaction_count(*addr) + // .await + // .map_err(|_| ContenderError::SpamError("failed to get nonce", None))?; + // nonces.insert(*addr, nonce); + // } while let Some(block_hash) = stream.next().await { let block_txs = tx_req_chunks[block_offset].clone(); @@ -132,99 +211,99 @@ where for (idx, tx) in block_txs.into_iter().enumerate() { let gas_price = gas_price + (idx as u128 * 1e9 as u128); - let tx_req = tx.tx.to_owned(); - println!( - "sending tx. from={} to={} input={}", - tx_req.from.map(|s| s.encode_hex()).unwrap_or_default(), - tx_req - .to - .map(|s| s.to().map(|s| *s)) - .flatten() - .map(|s| s.encode_hex()) - .unwrap_or_default(), - tx_req - .input - .input - .as_ref() - .map(|s| s.encode_hex()) - .unwrap_or_default(), - ); - - let from = &tx_req.from.expect("missing from address"); - let nonce = nonces.get(from).expect("failed to get nonce").to_owned(); - /* - Increment nonce assuming the tx will succeed. - Note: if any tx fails, txs with higher nonces will also fail. - However, we'll get a fresh nonce next block. - */ - nonces.insert(from.to_owned(), nonce + 1); - - let fn_sig = FixedBytes::<4>::from_slice( - tx_req - .input - .input - .to_owned() - .map(|b| b.split_at(4).0.to_owned()) - .expect("invalid function call") - .as_slice(), - ); - - if !gas_limits.contains_key(fn_sig.as_slice()) { - let gas_limit = self - .eth_client - .estimate_gas(&tx.tx.to_owned()) - .await - .map_err(|e| ContenderError::with_err(e, "failed to estimate gas"))?; - gas_limits.insert(fn_sig, gas_limit); - } - // clone muh Arcs let eth_client = self.eth_client.clone(); let callback_handler = self.callback_handler.clone(); - - // query hashmaps for gaslimit & signer of this tx - let gas_limit = gas_limits - .get(&fn_sig) - .expect("failed to get gas limit") - .to_owned(); - let signer = self - .scenario - .wallet_map - .get(from) - .expect("failed to create signer") - .to_owned(); - - // build, sign, and send tx in a new task (green thread) let tx_handler = self.msg_handler.clone(); - let tx_kind = tx.kind.to_owned(); - tasks.push(task::spawn(async move { - let provider = ProviderBuilder::new() - .wallet(signer) - .on_provider(eth_client); - let full_tx = tx_req - .clone() - .with_nonce(nonce) - .with_gas_price(gas_price) - .with_chain_id(chain_id) - .with_gas_limit(gas_limit); + // prepare tx/bundle with nonce, gas price, signatures, etc + let payload = match tx { + ExecutionRequest::Bundle(reqs) => { + // prepare each tx in the bundle (increment nonce, set gas price, etc) + let mut bundle_txs = vec![]; + for req in reqs.iter() { + let tx_req = req.tx; + let (tx_req, signer) = self + .prepare_tx_req(&tx_req, gas_price, chain_id) + .await + .map_err(|e| ContenderError::with_err(e, "failed to prepare tx"))?; + + // sign tx + let tx_envelope = tx_req.build(&signer).await.map_err(|e| { + ContenderError::with_err(e, "bad request: failed to build tx") + })?; + bundle_txs.push(tx_envelope); + } + // TODO: call eth_sendBundle with signed txs + ExecutionPayload::SignedTxBundle(bundle_txs) + } + ExecutionRequest::Tx(req) => { + let tx_req = req.tx; + println!( + "sending tx. from={} to={} input={}", + tx_req.from.map(|s| s.encode_hex()).unwrap_or_default(), + tx_req + .to + .map(|s| s.to().map(|s| *s)) + .flatten() + .map(|s| s.encode_hex()) + .unwrap_or_default(), + tx_req + .input + .input + .as_ref() + .map(|s| s.encode_hex()) + .unwrap_or_default(), + ); + + let (tx_req, signer) = self + .prepare_tx_req(&tx_req, gas_price, chain_id) + .await + .map_err(|e| ContenderError::with_err(e, "failed to prepare tx"))?; + + // sign tx + let tx_envelope = tx_req.build(&signer).await.map_err(|e| { + ContenderError::with_err(e, "bad request: failed to build tx") + })?; + ExecutionPayload::SignedTx(tx_envelope) + } + }; + + // build, sign, and send tx/bundle in a new task (green thread) + tasks.push(task::spawn(async move { let start_timestamp = std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .expect("failed to get timestamp") .as_millis() as usize; + + match payload { + ExecutionPayload::SignedTx(tx) => { + let provider = ProviderBuilder::new().on_provider(eth_client); + provider + .send_tx_envelope(tx) + .await + .expect("RPC error: failed to send tx"); + } + ExecutionPayload::SignedTxBundle(txs) => { + let bprovider = ReqwestClient::new_http(self.scenario.rpc_url); + bprovider.request("eth_sendBundle", (txs,)) + } + } let res = provider - .send_transaction(full_tx) + .send_transaction(tx_req) .await .expect("failed to send tx"); + let mut extra = HashMap::new(); extra.insert("start_timestamp".to_owned(), start_timestamp.to_string()); - if let Some(kind) = tx_kind { + if let Some(kind) = req.kind.to_owned() { extra.insert("kind".to_owned(), kind); } + let maybe_handle = callback_handler.on_tx_sent( res.into_inner(), - tx, + req, extra.into(), Some(tx_handler), ); @@ -234,6 +313,7 @@ where // ignore None values so we don't attempt to await them })); } + println!("new block: {block_hash}"); if let Some(run_id) = run_id { // write this block's txs to DB diff --git a/src/spammer/mod.rs b/src/spammer/mod.rs index 3a8b29a..9552bb8 100644 --- a/src/spammer/mod.rs +++ b/src/spammer/mod.rs @@ -4,7 +4,10 @@ pub mod tx_actor; pub mod util; use crate::generator::{types::AnyProvider, NamedTxRequest}; -use alloy::providers::PendingTransactionConfig; +use alloy::{ + consensus::TxEnvelope, network::EthereumWallet, providers::PendingTransactionConfig, + rpc::types::TransactionRequest, +}; use std::{collections::HashMap, sync::Arc}; use tokio::task::JoinHandle; use tx_actor::TxActorHandle; @@ -85,3 +88,9 @@ impl OnTxSent for LogCallback { Some(handle) } } + +#[derive(Debug)] +pub enum ExecutionPayload { + SignedTx(TxEnvelope), + SignedTxBundle(Vec), +} From 6645ee94869d96145b035e832673c438cdafdc43 Mon Sep 17 00:00:00 2001 From: zeroXbrock <2791467+zeroXbrock@users.noreply.github.com> Date: Wed, 30 Oct 2024 03:59:22 -0700 Subject: [PATCH 03/19] [still untested] (mostly) fix compiler errors, need to implement bundles in testfile --- src/generator/mod.rs | 6 ++-- src/spammer/blockwise.rs | 70 +++++++++++++++++++--------------------- src/spammer/mod.rs | 10 +++--- src/spammer/timed.rs | 63 ++++++++++++++++++++++-------------- src/spammer/util.rs | 2 +- src/test_scenario.rs | 46 ++++++++++++++------------ testfile/src/lib.rs | 36 +++++++++++++++++---- testfile/src/types.rs | 2 +- 8 files changed, 136 insertions(+), 99 deletions(-) diff --git a/src/generator/mod.rs b/src/generator/mod.rs index 46ed881..74c26b3 100644 --- a/src/generator/mod.rs +++ b/src/generator/mod.rs @@ -10,7 +10,7 @@ use crate::{ }; use alloy::primitives::U256; use async_trait::async_trait; -use named_txs::ExecutionPayload; +use named_txs::ExecutionRequest; pub use named_txs::NamedTxRequestBuilder; pub use seeder::rand_seed::RandSeed; use std::{collections::HashMap, fmt::Debug, hash::Hash}; @@ -86,7 +86,7 @@ where async fn load_txs CallbackResult>( &self, plan_type: PlanType, - ) -> Result> { + ) -> Result> { let conf = self.get_plan_conf(); let env = conf.get_env().unwrap_or_default(); let db = self.get_db(); @@ -97,7 +97,7 @@ where placeholder_map.insert(key.to_owned(), value.to_owned()); } - let mut txs: Vec = vec![]; + let mut txs: Vec = vec![]; match plan_type { PlanType::Create(on_create_step) => { diff --git a/src/spammer/blockwise.rs b/src/spammer/blockwise.rs index 2665d61..609e0bf 100644 --- a/src/spammer/blockwise.rs +++ b/src/spammer/blockwise.rs @@ -9,11 +9,12 @@ use crate::spammer::ExecutionPayload; use crate::test_scenario::TestScenario; use crate::Result; use alloy::hex::ToHexExt; -use alloy::network::{AnyNetwork, EthereumWallet, NetworkWallet, TransactionBuilder}; +use alloy::network::{AnyNetwork, Ethereum, EthereumWallet, NetworkWallet, TransactionBuilder}; use alloy::primitives::{Address, FixedBytes}; -use alloy::providers::{Provider, ProviderBuilder}; +use alloy::providers::{PendingTransactionBuilder, Provider, ProviderBuilder}; use alloy::rpc::client::ReqwestClient; use alloy::rpc::types::TransactionRequest; +use alloy::transports::http::{Client, Http}; use futures::StreamExt; use std::collections::HashMap; use std::ops::Deref; @@ -222,7 +223,7 @@ where // prepare each tx in the bundle (increment nonce, set gas price, etc) let mut bundle_txs = vec![]; for req in reqs.iter() { - let tx_req = req.tx; + let tx_req = req.tx.to_owned(); let (tx_req, signer) = self .prepare_tx_req(&tx_req, gas_price, chain_id) .await @@ -235,10 +236,10 @@ where bundle_txs.push(tx_envelope); } // TODO: call eth_sendBundle with signed txs - ExecutionPayload::SignedTxBundle(bundle_txs) + ExecutionPayload::SignedTxBundle(bundle_txs, reqs) } ExecutionRequest::Tx(req) => { - let tx_req = req.tx; + let tx_req = req.tx.to_owned(); println!( "sending tx. from={} to={} input={}", tx_req.from.map(|s| s.encode_hex()).unwrap_or_default(), @@ -266,51 +267,48 @@ where ContenderError::with_err(e, "bad request: failed to build tx") })?; - ExecutionPayload::SignedTx(tx_envelope) + ExecutionPayload::SignedTx(tx_envelope, req) } }; // build, sign, and send tx/bundle in a new task (green thread) tasks.push(task::spawn(async move { + let provider = ProviderBuilder::new().on_provider(eth_client); let start_timestamp = std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .expect("failed to get timestamp") .as_millis() as usize; - match payload { - ExecutionPayload::SignedTx(tx) => { - let provider = ProviderBuilder::new().on_provider(eth_client); - provider - .send_tx_envelope(tx) + let mut extra = HashMap::new(); + extra.insert("start_timestamp".to_owned(), start_timestamp.to_string()); + + // triggers & awaits callback for every individual tx (including txs in a bundle) + let handles = match payload { + ExecutionPayload::SignedTx(signed_tx, req) => { + let res = provider + .send_tx_envelope(signed_tx) .await .expect("RPC error: failed to send tx"); + let maybe_handle = callback_handler.on_tx_sent( + res.into_inner(), + &req.to_owned(), + extra.clone().into(), + Some(tx_handler), + ); + vec![maybe_handle] } - ExecutionPayload::SignedTxBundle(txs) => { - let bprovider = ReqwestClient::new_http(self.scenario.rpc_url); - bprovider.request("eth_sendBundle", (txs,)) + ExecutionPayload::SignedTxBundle(signed_txs, reqs) => { + // let res = provider.send_bundle(txs) + // let handles = report_sent_req(start_timestamp, res); + // TODO: implement send_bundle + todo!(); + } + }; + for handle in handles { + if let Some(handle) = handle { + handle.await.expect("callback task failed"); } } - let res = provider - .send_transaction(tx_req) - .await - .expect("failed to send tx"); - - let mut extra = HashMap::new(); - extra.insert("start_timestamp".to_owned(), start_timestamp.to_string()); - if let Some(kind) = req.kind.to_owned() { - extra.insert("kind".to_owned(), kind); - } - - let maybe_handle = callback_handler.on_tx_sent( - res.into_inner(), - req, - extra.into(), - Some(tx_handler), - ); - if let Some(handle) = maybe_handle { - handle.await.expect("callback task failed"); - } - // ignore None values so we don't attempt to await them })); } @@ -378,7 +376,7 @@ mod tests { &get_test_signers(), ); let callback_handler = MockCallback; - let spammer = BlockwiseSpammer::new(scenario, callback_handler); + let mut spammer = BlockwiseSpammer::new(scenario, callback_handler).await; let result = spammer.spam_rpc(10, 3, None).await; println!("{:?}", result); diff --git a/src/spammer/mod.rs b/src/spammer/mod.rs index 9552bb8..9f3f3b7 100644 --- a/src/spammer/mod.rs +++ b/src/spammer/mod.rs @@ -23,7 +23,7 @@ where fn on_tx_sent( &self, tx_response: PendingTransactionConfig, - req: NamedTxRequest, + req: &NamedTxRequest, extra: Option>, tx_handler: Option>, ) -> Option>; @@ -51,7 +51,7 @@ impl OnTxSent for NilCallback { fn on_tx_sent( &self, _tx_res: PendingTransactionConfig, - _req: NamedTxRequest, + _req: &NamedTxRequest, _extra: Option>, _tx_handler: Option>, ) -> Option> { @@ -64,7 +64,7 @@ impl OnTxSent for LogCallback { fn on_tx_sent( &self, tx_response: PendingTransactionConfig, - _req: NamedTxRequest, + _req: &NamedTxRequest, extra: Option>, tx_actor: Option>, ) -> Option> { @@ -91,6 +91,6 @@ impl OnTxSent for LogCallback { #[derive(Debug)] pub enum ExecutionPayload { - SignedTx(TxEnvelope), - SignedTxBundle(Vec), + SignedTx(TxEnvelope, NamedTxRequest), + SignedTxBundle(Vec, Vec), } diff --git a/src/spammer/timed.rs b/src/spammer/timed.rs index 7e4e135..8c5fe91 100644 --- a/src/spammer/timed.rs +++ b/src/spammer/timed.rs @@ -1,6 +1,7 @@ use crate::{ db::DbOps, generator::{ + named_txs::ExecutionRequest, seeder::Seeder, templater::Templater, types::{EthProvider, PlanType}, @@ -60,31 +61,43 @@ where // send tx to the RPC asynchrononsly tasks.push(task::spawn(async move { - let tx_req = &tx.tx; - println!( - "sending tx. from={} to={} input={}", - tx_req.from.map(|s| s.encode_hex()).unwrap_or_default(), - tx_req - .to - .map(|s| s.to().map(|s| *s)) - .flatten() - .map(|s| s.encode_hex()) - .unwrap_or_default(), - tx_req - .input - .input - .as_ref() - .map(|s| s.encode_hex()) - .unwrap_or_default(), - ); - let res = rpc_client - .send_transaction(tx_req.to_owned()) - .await - .expect("failed to send tx"); - let maybe_handle = callback_handler.on_tx_sent(res.into_inner(), tx, None, None); - if let Some(handle) = maybe_handle { - handle.await.expect("failed to join task handle"); - } // ignore None values so we don't attempt to await them + let handles = match tx { + ExecutionRequest::Tx(req) => { + let tx_req = &req.tx; + println!( + "sending tx. from={} to={} input={}", + tx_req.from.map(|s| s.encode_hex()).unwrap_or_default(), + tx_req + .to + .map(|s| s.to().map(|s| *s)) + .flatten() + .map(|s| s.encode_hex()) + .unwrap_or_default(), + tx_req + .input + .input + .as_ref() + .map(|s| s.encode_hex()) + .unwrap_or_default(), + ); + let res = rpc_client + .send_transaction(tx_req.to_owned()) + .await + .expect("failed to send tx"); + let maybe_handle = + callback_handler.on_tx_sent(res.into_inner(), &req, None, None); + vec![maybe_handle] + } + ExecutionRequest::Bundle(reqs) => { + todo!(); + } + }; + + for handle in handles { + if let Some(handle) = handle { + handle.await.expect("failed to join task handle"); + } // ignore None values so we don't attempt to await them + } })); // sleep for interval diff --git a/src/spammer/util.rs b/src/spammer/util.rs index 5d90ee0..2080406 100644 --- a/src/spammer/util.rs +++ b/src/spammer/util.rs @@ -15,7 +15,7 @@ pub mod test { fn on_tx_sent( &self, _tx_res: PendingTransactionConfig, - _req: NamedTxRequest, + _req: &NamedTxRequest, _extra: Option>, _tx_handler: Option>, ) -> Option> { diff --git a/src/test_scenario.rs b/src/test_scenario.rs index 6086203..c95ad74 100644 --- a/src/test_scenario.rs +++ b/src/test_scenario.rs @@ -256,7 +256,9 @@ where pub mod tests { use crate::db::MockDb; use crate::generator::templater::Templater; - use crate::generator::types::{CreateDefinition, FunctionCallDefinition, FuzzParam}; + use crate::generator::types::{ + CreateDefinition, FunctionCallDefinition, FuzzParam, SpamRequest, + }; use crate::generator::{types::PlanType, util::test::spawn_anvil, RandSeed}; use crate::generator::{Generator, PlanConfig}; use crate::spammer::util::test::get_test_signers; @@ -323,26 +325,28 @@ pub mod tests { ]) } - fn get_spam_steps(&self) -> Result> { - let fn_call = |data: &str, from_addr: &str| FunctionCallDefinition { - to: "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D".to_owned(), - from: from_addr.to_owned(), - value: None, - signature: "swap(uint256 x, uint256 y, address a, bytes b)".to_owned(), - args: vec![ - "1".to_owned(), - "2".to_owned(), - Address::repeat_byte(0x11).encode_hex(), - data.to_owned(), - ] - .into(), - fuzz: vec![FuzzParam { - param: "x".to_string(), - min: None, - max: None, - }] - .into(), - kind: None, + fn get_spam_steps(&self) -> Result> { + let fn_call = |data: &str, from_addr: &str| { + SpamRequest::Single(FunctionCallDefinition { + to: "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D".to_owned(), + from: from_addr.to_owned(), + value: None, + signature: "swap(uint256 x, uint256 y, address a, bytes b)".to_owned(), + args: vec![ + "1".to_owned(), + "2".to_owned(), + Address::repeat_byte(0x11).encode_hex(), + data.to_owned(), + ] + .into(), + fuzz: vec![FuzzParam { + param: "x".to_string(), + min: None, + max: None, + }] + .into(), + kind: None, + }) }; Ok(vec![ fn_call("0xbeef", "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"), diff --git a/testfile/src/lib.rs b/testfile/src/lib.rs index 496ab27..9aeaa6f 100644 --- a/testfile/src/lib.rs +++ b/testfile/src/lib.rs @@ -7,7 +7,7 @@ use contender_core::{ error::ContenderError, generator::{ templater::Templater, - types::{CreateDefinition, FunctionCallDefinition}, + types::{CreateDefinition, FunctionCallDefinition, SpamRequest}, PlanConfig, }, }; @@ -35,7 +35,7 @@ impl TestConfig { } impl PlanConfig for TestConfig { - fn get_spam_steps(&self) -> Result, ContenderError> { + fn get_spam_steps(&self) -> Result, ContenderError> { Ok(self.spam.to_owned().unwrap_or_default()) } @@ -111,6 +111,7 @@ pub mod tests { use contender_core::{ db::MockDb, generator::{ + named_txs::ExecutionRequest, types::{CreateDefinition, FunctionCallDefinition, FuzzParam, PlanType}, Generator, RandSeed, }, @@ -334,8 +335,15 @@ pub mod tests { .await .unwrap(); assert_eq!(spam_txs.len(), 10); - let data = spam_txs[0].tx.input.input.to_owned().unwrap().to_string(); - assert_eq!(data, "0x022c0d9f00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000111111111111111111111111111111111111111100000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002dead000000000000000000000000000000000000000000000000000000000000"); + match &spam_txs[0] { + ExecutionRequest::Tx(req) => { + let data = req.tx.input.input.to_owned().unwrap().to_string(); + assert_eq!(data, "0x022c0d9f00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000111111111111111111111111111111111111111100000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000002dead000000000000000000000000000000000000000000000000000000000000"); + } + _ => { + panic!("expected ExecutionRequest::Tx"); + } + } } #[tokio::test] @@ -370,9 +378,23 @@ pub mod tests { .unwrap(); assert_eq!(spam_txs_1.len(), spam_txs_2.len()); for i in 0..spam_txs_1.len() { - let data1 = spam_txs_1[i].tx.input.input.to_owned().unwrap().to_string(); - let data2 = spam_txs_2[i].tx.input.input.to_owned().unwrap().to_string(); - assert_eq!(data1, data2); + match &spam_txs_1[i] { + ExecutionRequest::Tx(req) => { + let data1 = req.tx.input.input.to_owned().unwrap().to_string(); + match &spam_txs_2[i] { + ExecutionRequest::Tx(req) => { + let data2 = req.tx.input.input.to_owned().unwrap().to_string(); + assert_eq!(data1, data2); + } + _ => { + panic!("expected ExecutionRequest::Tx"); + } + } + } + _ => { + panic!("expected ExecutionRequest::Tx"); + } + } } } } diff --git a/testfile/src/types.rs b/testfile/src/types.rs index 3a1309d..578c85b 100644 --- a/testfile/src/types.rs +++ b/testfile/src/types.rs @@ -15,5 +15,5 @@ pub struct TestConfig { pub setup: Option>, /// Function to call in spam txs. - pub spam: Option>, + pub spam: Option>, // TODO: figure out how to implement BundleCallDefinition alongside FunctionCallDefinition } From d4ab0ab6bc1bd4dfd377db2c6234a0e6434ab88c Mon Sep 17 00:00:00 2001 From: zeroXbrock <2791467+zeroXbrock@users.noreply.github.com> Date: Wed, 30 Oct 2024 04:47:01 -0700 Subject: [PATCH 04/19] accept bundles or txs in testconfig --- cli/src/main.rs | 22 +++++-- src/generator/types.rs | 3 +- testfile/src/lib.rs | 127 +++++++++++++++++++++++++---------------- testfile/src/types.rs | 4 +- 4 files changed, 100 insertions(+), 56 deletions(-) diff --git a/cli/src/main.rs b/cli/src/main.rs index 7458ec6..6f0bdb4 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -14,7 +14,7 @@ use commands::{ContenderCli, ContenderSubcommand}; use contender_core::{ db::{DbOps, RunTx}, generator::{ - types::{AnyProvider, FunctionCallDefinition}, + types::{AnyProvider, FunctionCallDefinition, SpamRequest}, RandSeed, }, spammer::{BlockwiseSpammer, LogCallback, NilCallback, TimedSpammer}, @@ -117,7 +117,21 @@ async fn main() -> Result<(), Box> { .spam .as_ref() .expect("No spam function calls found in testfile"); - check_private_keys(spam, &signers); + + // distill all FunctionCallDefinitions from the spam requests + let mut fn_calls = vec![]; + for s in spam { + match s { + SpamRequest::Single(fn_call) => { + fn_calls.push(fn_call.to_owned()); + } + SpamRequest::Bundle(bundle) => { + fn_calls.extend(bundle.txs.iter().map(|s| s.to_owned())); + } + } + } + + check_private_keys(&fn_calls, &signers); check_balances(&signers, min_balance, &rpc_client).await; if txs_per_block.is_some() && txs_per_second.is_some() { @@ -138,14 +152,14 @@ async fn main() -> Result<(), Box> { .expect("Time went backwards") .as_millis(); let run_id = DB.insert_run(timestamp as u64, txs_per_block * duration)?; - let spammer = BlockwiseSpammer::new(scenario, cback); + let mut spammer = BlockwiseSpammer::new(scenario, cback).await; spammer .spam_rpc(txs_per_block, duration, Some(run_id.into())) .await?; println!("Saved run. run_id = {}", run_id); } SpamCallbackType::Nil(cback) => { - let spammer = BlockwiseSpammer::new(scenario, cback); + let mut spammer = BlockwiseSpammer::new(scenario, cback).await; spammer.spam_rpc(txs_per_block, duration, None).await?; } }; diff --git a/src/generator/types.rs b/src/generator/types.rs index acc490a..2344867 100644 --- a/src/generator/types.rs +++ b/src/generator/types.rs @@ -44,7 +44,8 @@ pub struct BundleCallDefinition { pub txs: Vec, } -/// +/// Definition of a spam request template. +#[derive(Clone, Deserialize, Debug, Serialize)] pub enum SpamRequest { Single(FunctionCallDefinition), Bundle(BundleCallDefinition), diff --git a/testfile/src/lib.rs b/testfile/src/lib.rs index 9aeaa6f..197cdf4 100644 --- a/testfile/src/lib.rs +++ b/testfile/src/lib.rs @@ -112,7 +112,7 @@ pub mod tests { db::MockDb, generator::{ named_txs::ExecutionRequest, - types::{CreateDefinition, FunctionCallDefinition, FuzzParam, PlanType}, + types::{CreateDefinition, FunctionCallDefinition, FuzzParam, PlanType, SpamRequest}, Generator, RandSeed, }, test_scenario::TestScenario, @@ -138,49 +138,52 @@ pub mod tests { } pub fn get_testconfig() -> TestConfig { + let fncall = FunctionCallDefinition { + to: "0x7a250d5630B4cF539739dF2C5dAcb4c659F248DD".to_owned(), + from: "0x7a250d5630B4cF539739dF2C5dAcb4c659F248DD".to_owned(), + signature: "swap(uint256 x, uint256 y, address a, bytes b)".to_owned(), + args: vec![ + "1".to_owned(), + "2".to_owned(), + Address::repeat_byte(0x11).encode_hex(), + "0xdead".to_owned(), + ] + .into(), + fuzz: None, + value: None, + kind: None, + }; + TestConfig { env: None, create: None, setup: None, - spam: vec![FunctionCallDefinition { - to: "0x7a250d5630B4cF539739dF2C5dAcb4c659F248DD".to_owned(), - from: "0x7a250d5630B4cF539739dF2C5dAcb4c659F248DD".to_owned(), + spam: vec![SpamRequest::Single(fncall)].into(), + } + } + + pub fn get_fuzzy_testconfig() -> TestConfig { + let fn_call = |data: &str, from_addr: &str| { + SpamRequest::Single(FunctionCallDefinition { + to: "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D".to_owned(), + from: from_addr.to_owned(), + value: None, signature: "swap(uint256 x, uint256 y, address a, bytes b)".to_owned(), args: vec![ "1".to_owned(), "2".to_owned(), Address::repeat_byte(0x11).encode_hex(), - "0xdead".to_owned(), + data.to_owned(), ] .into(), - fuzz: None, - value: None, kind: None, - }] - .into(), - } - } - - pub fn get_fuzzy_testconfig() -> TestConfig { - let fn_call = |data: &str, from_addr: &str| FunctionCallDefinition { - to: "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D".to_owned(), - from: from_addr.to_owned(), - value: None, - signature: "swap(uint256 x, uint256 y, address a, bytes b)".to_owned(), - args: vec![ - "1".to_owned(), - "2".to_owned(), - Address::repeat_byte(0x11).encode_hex(), - data.to_owned(), - ] - .into(), - kind: None, - fuzz: vec![FuzzParam { - param: "x".to_string(), - min: None, - max: None, - }] - .into(), + fuzz: vec![FuzzParam { + param: "x".to_string(), + min: None, + max: None, + }] + .into(), + }) }; TestConfig { env: None, @@ -275,19 +278,26 @@ pub mod tests { let spam = test_file.spam.unwrap(); assert_eq!(env.get("env1").unwrap(), "env1"); - assert_eq!( - spam[0].from, - "0x70997970C51812dc3A010C7d01b50e0d17dc79C8".to_owned() - ); - assert_eq!(setup.len(), 1); - assert_eq!(setup[0].value, Some("1234".to_owned())); - assert_eq!(spam[0].fuzz.as_ref().unwrap()[0].param, "amountIn"); - assert_eq!(spam[0].fuzz.as_ref().unwrap()[0].min, Some(U256::from(1))); - assert_eq!( - spam[0].fuzz.as_ref().unwrap()[0].max, - Some(U256::from(100_000_000_000_000_000_u64)) - ); - assert_eq!(spam[0].kind, Some("test".to_owned())); + match spam[0] { + SpamRequest::Single(ref fncall) => { + assert_eq!( + fncall.from, + "0x70997970C51812dc3A010C7d01b50e0d17dc79C8".to_owned() + ); + assert_eq!(setup.len(), 1); + assert_eq!(setup[0].value, Some("1234".to_owned())); + assert_eq!(fncall.fuzz.as_ref().unwrap()[0].param, "amountIn"); + assert_eq!(fncall.fuzz.as_ref().unwrap()[0].min, Some(U256::from(1))); + assert_eq!( + fncall.fuzz.as_ref().unwrap()[0].max, + Some(U256::from(100_000_000_000_000_000_u64)) + ); + assert_eq!(fncall.kind, Some("test".to_owned())); + } + _ => { + panic!("expected SpamRequest::Single"); + } + } } fn print_testconfig(cfg: &str) { @@ -304,10 +314,29 @@ pub mod tests { cfg.save_toml("cargotest.toml").unwrap(); let test_file2 = TestConfig::from_file("cargotest.toml").unwrap(); let spam = cfg.clone().spam.unwrap(); - let args = spam[0].args.as_ref().unwrap(); - assert_eq!(spam[0].to, test_file2.spam.unwrap()[0].to); - assert_eq!(args[0], "1"); - assert_eq!(args[1], "2"); + match &spam[0] { + SpamRequest::Single(req) => { + let args = req.args.as_ref().unwrap(); + match &test_file2.spam.unwrap()[0] { + SpamRequest::Single(req2) => { + let args2 = req2.args.as_ref().unwrap(); + assert_eq!(req.from, req2.from); + assert_eq!(req.to, req2.to); + assert_eq!(args[0], args2[0]); + assert_eq!(args[1], args2[1]); + } + _ => { + panic!("expected SpamRequest::Single"); + } + } + // assert_eq!(req.to, test_file2.spam.unwrap()[0].to); + // assert_eq!(args[0], "1"); + // assert_eq!(args[1], "2"); + } + _ => { + panic!("expected SpamRequest::Single"); + } + } fs::remove_file("cargotest.toml").unwrap(); } diff --git a/testfile/src/types.rs b/testfile/src/types.rs index 578c85b..becd3de 100644 --- a/testfile/src/types.rs +++ b/testfile/src/types.rs @@ -1,4 +1,4 @@ -use contender_core::generator::types::{CreateDefinition, FunctionCallDefinition}; +use contender_core::generator::types::{CreateDefinition, FunctionCallDefinition, SpamRequest}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -15,5 +15,5 @@ pub struct TestConfig { pub setup: Option>, /// Function to call in spam txs. - pub spam: Option>, // TODO: figure out how to implement BundleCallDefinition alongside FunctionCallDefinition + pub spam: Option>, // TODO: figure out how to implement BundleCallDefinition alongside FunctionCallDefinition } From 9caec2b980c40c871bbcb8ccae780264fafa77bd Mon Sep 17 00:00:00 2001 From: zeroXbrock <2791467+zeroXbrock@users.noreply.github.com> Date: Fri, 1 Nov 2024 13:08:00 -0700 Subject: [PATCH 05/19] move error module to single file --- src/{error/mod.rs => error.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/{error/mod.rs => error.rs} (100%) diff --git a/src/error/mod.rs b/src/error.rs similarity index 100% rename from src/error/mod.rs rename to src/error.rs From d45ff3ac7bdb85d10ffceab905c5906a20b9611d Mon Sep 17 00:00:00 2001 From: zeroXbrock <2791467+zeroXbrock@users.noreply.github.com> Date: Sat, 2 Nov 2024 02:12:33 -0700 Subject: [PATCH 06/19] [wip] add primitive sendBundle functionality to blockwise timer (bundles not landing) --- Cargo.lock | 672 ++++++++++++++++++++++++++++++++++----- Cargo.toml | 6 +- cli/src/main.rs | 36 ++- cli/testfile.toml | 30 +- src/bundle_provider.rs | 76 +++++ src/generator/mod.rs | 4 +- src/generator/types.rs | 7 +- src/lib.rs | 1 + src/spammer/blockwise.rs | 87 +++-- src/spammer/timed.rs | 15 +- src/test_scenario.rs | 6 +- testfile/src/lib.rs | 43 ++- testfile/testConfig.toml | 9 +- 13 files changed, 855 insertions(+), 137 deletions(-) create mode 100644 src/bundle_provider.rs diff --git a/Cargo.lock b/Cargo.lock index 10e9c4c..8f9e9e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -52,7 +52,7 @@ dependencies = [ "alloy-pubsub", "alloy-rpc-client", "alloy-rpc-types", - "alloy-serde", + "alloy-serde 0.3.6", "alloy-signer", "alloy-signer-local", "alloy-transport", @@ -80,7 +80,7 @@ dependencies = [ "alloy-eips", "alloy-primitives", "alloy-rlp", - "alloy-serde", + "alloy-serde 0.3.6", "c-kzg", "serde", ] @@ -108,21 +108,22 @@ dependencies = [ [[package]] name = "alloy-core" -version = "0.8.3" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88b095eb0533144b4497e84a9cc3e44a5c2e3754a3983c0376a55a2f9183a53e" +checksum = "b72bf30967a232bec83809bea1623031f6285a013096229330c68c406192a4ca" dependencies = [ "alloy-dyn-abi", "alloy-json-abi", "alloy-primitives", + "alloy-rlp", "alloy-sol-types", ] [[package]] name = "alloy-dyn-abi" -version = "0.8.3" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4004925bff5ba0a11739ae84dbb6601a981ea692f3bd45b626935ee90a6b8471" +checksum = "f5228b189b18b85761340dc9eaac0141148a8503657b36f9bc3a869413d987ca" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -168,7 +169,7 @@ dependencies = [ "alloy-eip7702", "alloy-primitives", "alloy-rlp", - "alloy-serde", + "alloy-serde 0.3.6", "c-kzg", "derive_more", "once_cell", @@ -183,15 +184,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a7a18afb0b318616b6b2b0e2e7ac5529d32a966c673b48091c9919e284e6aca" dependencies = [ "alloy-primitives", - "alloy-serde", + "alloy-serde 0.3.6", "serde", ] [[package]] name = "alloy-json-abi" -version = "0.8.3" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9996daf962fd0a90d3c93b388033228865953b92de7bb1959b891d78750a4091" +checksum = "31a0f0d51db8a1a30a4d98a9f90e090a94c8f44cb4d9eafc7e03aa6d00aae984" dependencies = [ "alloy-primitives", "alloy-sol-type-parser", @@ -225,7 +226,7 @@ dependencies = [ "alloy-network-primitives", "alloy-primitives", "alloy-rpc-types-eth", - "alloy-serde", + "alloy-serde 0.3.6", "alloy-signer", "alloy-sol-types", "async-trait", @@ -242,7 +243,7 @@ checksum = "94ad40869867ed2d9cd3842b1e800889e5b49e6b92da346e93862b4a741bedf3" dependencies = [ "alloy-eips", "alloy-primitives", - "alloy-serde", + "alloy-serde 0.3.6", "serde", ] @@ -265,23 +266,29 @@ dependencies = [ [[package]] name = "alloy-primitives" -version = "0.8.3" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "411aff151f2a73124ee473708e82ed51b2535f68928b6a1caa8bc1246ae6f7cd" +checksum = "8edae627382349b56cd6a7a2106f4fd69b243a9233e560c55c2e03cabb7e1d3c" dependencies = [ "alloy-rlp", "bytes", "cfg-if", "const-hex", "derive_more", + "foldhash", + "hashbrown 0.15.0", "hex-literal", + "indexmap", "itoa", "k256", "keccak-asm", + "paste", "proptest", "rand", "ruint", + "rustc-hash", "serde", + "sha3", "tiny-keccak", ] @@ -363,7 +370,7 @@ checksum = "4d0f2d905ebd295e7effec65e5f6868d153936130ae718352771de3e7d03c75c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.86", ] [[package]] @@ -399,7 +406,8 @@ checksum = "64333d639f2a0cf73491813c629a405744e16343a4bc5640931be707c345ecc5" dependencies = [ "alloy-rpc-types-engine", "alloy-rpc-types-eth", - "alloy-serde", + "alloy-rpc-types-mev", + "alloy-serde 0.3.6", "serde", ] @@ -410,7 +418,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d25cb45ad7c0930dd62eecf164d2afe4c3d2dd2c82af85680ad1f118e1e5cb83" dependencies = [ "alloy-primitives", - "alloy-serde", + "alloy-serde 0.3.6", "serde", ] @@ -438,16 +446,29 @@ dependencies = [ "alloy-network-primitives", "alloy-primitives", "alloy-rlp", - "alloy-serde", + "alloy-serde 0.3.6", "alloy-sol-types", "cfg-if", "derive_more", - "hashbrown", + "hashbrown 0.14.5", "itertools 0.13.0", "serde", "serde_json", ] +[[package]] +name = "alloy-rpc-types-mev" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "922d92389e5022650c4c60ffd2f9b2467c3f853764f0f74ff16a23106f9017d5" +dependencies = [ + "alloy-eips", + "alloy-primitives", + "alloy-serde 0.3.6", + "serde", + "serde_json", +] + [[package]] name = "alloy-serde" version = "0.3.6" @@ -459,6 +480,17 @@ dependencies = [ "serde_json", ] +[[package]] +name = "alloy-serde" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "028e72eaa9703e4882344983cfe7636ce06d8cce104a78ea62fd19b46659efc4" +dependencies = [ + "alloy-primitives", + "serde", + "serde_json", +] + [[package]] name = "alloy-signer" version = "0.3.6" @@ -491,23 +523,23 @@ dependencies = [ [[package]] name = "alloy-sol-macro" -version = "0.8.3" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0458ccb02a564228fcd76efb8eb5a520521a8347becde37b402afec9a1b83859" +checksum = "841eabaa4710f719fddbc24c95d386eae313f07e6da4babc25830ee37945be0c" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.86", ] [[package]] name = "alloy-sol-macro-expander" -version = "0.8.3" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bc65475025fc1e84bf86fc840f04f63fcccdcf3cf12053c99918e4054dfbc69" +checksum = "6672337f19d837b9f7073c45853aeb528ed9f7dd6a4154ce683e9e5cb7794014" dependencies = [ "alloy-json-abi", "alloy-sol-macro-input", @@ -517,16 +549,16 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.86", "syn-solidity", "tiny-keccak", ] [[package]] name = "alloy-sol-macro-input" -version = "0.8.3" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed10f0715a0b69fde3236ff3b9ae5f6f7c97db5a387747100070d3016b9266b" +checksum = "0dff37dd20bfb118b777c96eda83b2067f4226d2644c5cfa00187b3bc01770ba" dependencies = [ "alloy-json-abi", "const-hex", @@ -535,15 +567,15 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.77", + "syn 2.0.86", "syn-solidity", ] [[package]] name = "alloy-sol-type-parser" -version = "0.8.3" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3edae8ea1de519ccba896b6834dec874230f72fe695ff3c9c118e90ec7cff783" +checksum = "5b853d42292dbb159671a3edae3b2750277ff130f32b726fe07dc2b17aa6f2b5" dependencies = [ "serde", "winnow", @@ -551,9 +583,9 @@ dependencies = [ [[package]] name = "alloy-sol-types" -version = "0.8.3" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1eb88e4da0a1b697ed6a9f811fdba223cf4d5c21410804fd1707836af73a462b" +checksum = "aa828bb1b9a6dc52208fbb18084fb9ce2c30facc2bfda6a5d922349b4990354f" dependencies = [ "alloy-json-abi", "alloy-primitives", @@ -831,7 +863,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.86", ] [[package]] @@ -842,7 +874,7 @@ checksum = "a27b8a3a6e1a44fa4c8baf1f653e4172e81486d4941f2237e20dc2d0cf4ddff1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.86", ] [[package]] @@ -856,6 +888,12 @@ dependencies = [ "rustc_version 0.4.1", ] +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "auto_impl" version = "1.2.0" @@ -864,7 +902,7 @@ checksum = "3c87f3f15e7794432337fc718554eaa4dc8f04c9677a950ffe366f20a162ae42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.86", ] [[package]] @@ -1017,6 +1055,12 @@ dependencies = [ "shlex", ] +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + [[package]] name = "cfg-if" version = "1.0.0" @@ -1054,7 +1098,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.86", ] [[package]] @@ -1069,6 +1113,16 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + [[package]] name = "const-hex" version = "1.12.0" @@ -1093,9 +1147,11 @@ name = "contender" version = "0.1.0" dependencies = [ "alloy", + "alloy-serde 0.5.4", "async-trait", "eyre", "futures", + "jsonrpsee", "rand", "serde", "tokio", @@ -1226,7 +1282,7 @@ checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" dependencies = [ "cfg-if", "crossbeam-utils", - "hashbrown", + "hashbrown 0.14.5", "lock_api", "once_cell", "parking_lot_core", @@ -1276,7 +1332,7 @@ checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.86", "unicode-xid", ] @@ -1435,6 +1491,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2" + [[package]] name = "foreign-types" version = "0.3.2" @@ -1482,9 +1544,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", @@ -1492,9 +1554,9 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" @@ -1509,38 +1571,48 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-macro" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.86", ] [[package]] name = "futures-sink" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.30" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-timer" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" +dependencies = [ + "gloo-timers", + "send_wrapper 0.4.0", +] [[package]] name = "futures-util" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-channel", "futures-core", @@ -1594,6 +1666,52 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[package]] +name = "gloo-net" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06f627b1a58ca3d42b45d6104bf1e1a03799df472df00988b6ba21accc10580" +dependencies = [ + "futures-channel", + "futures-core", + "futures-sink", + "gloo-utils", + "http", + "js-sys", + "pin-project", + "serde", + "serde_json", + "thiserror", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "gloo-timers" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "gloo-utils" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" +dependencies = [ + "js-sys", + "serde", + "serde_json", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "group" version = "0.13.0" @@ -1605,6 +1723,25 @@ dependencies = [ "subtle", ] +[[package]] +name = "h2" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "hashbrown" version = "0.14.5" @@ -1616,13 +1753,23 @@ dependencies = [ "serde", ] +[[package]] +name = "hashbrown" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" +dependencies = [ + "foldhash", + "serde", +] + [[package]] name = "hashlink" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" dependencies = [ - "hashbrown", + "hashbrown 0.14.5", ] [[package]] @@ -1701,6 +1848,12 @@ version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + [[package]] name = "hyper" version = "1.4.1" @@ -1710,9 +1863,11 @@ dependencies = [ "bytes", "futures-channel", "futures-util", + "h2", "http", "http-body", "httparse", + "httpdate", "itoa", "pin-project-lite", "smallvec", @@ -1720,6 +1875,24 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-rustls" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" +dependencies = [ + "futures-util", + "http", + "hyper", + "hyper-util", + "log", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + [[package]] name = "hyper-tls" version = "0.6.0" @@ -1799,7 +1972,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.14.5", + "serde", ] [[package]] @@ -1853,6 +2027,26 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +[[package]] +name = "jni" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec" +dependencies = [ + "cesu8", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + [[package]] name = "js-sys" version = "0.3.70" @@ -1862,6 +2056,177 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "jsonrpsee" +version = "0.24.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5c71d8c1a731cc4227c2f698d377e7848ca12c8a48866fc5e6951c43a4db843" +dependencies = [ + "jsonrpsee-client-transport", + "jsonrpsee-core", + "jsonrpsee-http-client", + "jsonrpsee-proc-macros", + "jsonrpsee-server", + "jsonrpsee-types", + "jsonrpsee-wasm-client", + "jsonrpsee-ws-client", + "tokio", + "tracing", +] + +[[package]] +name = "jsonrpsee-client-transport" +version = "0.24.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "548125b159ba1314104f5bb5f38519e03a41862786aa3925cf349aae9cdd546e" +dependencies = [ + "base64", + "futures-channel", + "futures-util", + "gloo-net", + "http", + "jsonrpsee-core", + "pin-project", + "rustls", + "rustls-pki-types", + "rustls-platform-verifier", + "soketto", + "thiserror", + "tokio", + "tokio-rustls", + "tokio-util", + "tracing", + "url", +] + +[[package]] +name = "jsonrpsee-core" +version = "0.24.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2882f6f8acb9fdaec7cefc4fd607119a9bd709831df7d7672a1d3b644628280" +dependencies = [ + "async-trait", + "bytes", + "futures-timer", + "futures-util", + "http", + "http-body", + "http-body-util", + "jsonrpsee-types", + "parking_lot", + "pin-project", + "rand", + "rustc-hash", + "serde", + "serde_json", + "thiserror", + "tokio", + "tokio-stream", + "tracing", + "wasm-bindgen-futures", +] + +[[package]] +name = "jsonrpsee-http-client" +version = "0.24.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3638bc4617f96675973253b3a45006933bde93c2fd8a6170b33c777cc389e5b" +dependencies = [ + "async-trait", + "base64", + "http-body", + "hyper", + "hyper-rustls", + "hyper-util", + "jsonrpsee-core", + "jsonrpsee-types", + "rustls", + "rustls-platform-verifier", + "serde", + "serde_json", + "thiserror", + "tokio", + "tower 0.4.13", + "tracing", + "url", +] + +[[package]] +name = "jsonrpsee-proc-macros" +version = "0.24.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06c01ae0007548e73412c08e2285ffe5d723195bf268bce67b1b77c3bb2a14d" +dependencies = [ + "heck", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.86", +] + +[[package]] +name = "jsonrpsee-server" +version = "0.24.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82ad8ddc14be1d4290cd68046e7d1d37acd408efed6d3ca08aefcc3ad6da069c" +dependencies = [ + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "jsonrpsee-core", + "jsonrpsee-types", + "pin-project", + "route-recognizer", + "serde", + "serde_json", + "soketto", + "thiserror", + "tokio", + "tokio-stream", + "tokio-util", + "tower 0.4.13", + "tracing", +] + +[[package]] +name = "jsonrpsee-types" +version = "0.24.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a178c60086f24cc35bb82f57c651d0d25d99c4742b4d335de04e97fa1f08a8a1" +dependencies = [ + "http", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "jsonrpsee-wasm-client" +version = "0.24.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a01cd500915d24ab28ca17527e23901ef1be6d659a2322451e1045532516c25" +dependencies = [ + "jsonrpsee-client-transport", + "jsonrpsee-core", + "jsonrpsee-types", +] + +[[package]] +name = "jsonrpsee-ws-client" +version = "0.24.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fe322e0896d0955a3ebdd5bf813571c53fea29edd713bc315b76620b327e86d" +dependencies = [ + "http", + "jsonrpsee-client-transport", + "jsonrpsee-core", + "jsonrpsee-types", + "url", +] + [[package]] name = "k256" version = "0.13.3" @@ -1875,6 +2240,15 @@ dependencies = [ "sha2", ] +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + [[package]] name = "keccak-asm" version = "0.1.4" @@ -1941,7 +2315,7 @@ version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37ee39891760e7d94734f6f63fedc29a2e4a152f836120753a72503f09fcf904" dependencies = [ - "hashbrown", + "hashbrown 0.14.5", ] [[package]] @@ -2050,7 +2424,7 @@ checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.86", ] [[package]] @@ -2091,7 +2465,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.86", ] [[package]] @@ -2211,7 +2585,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.86", ] [[package]] @@ -2290,7 +2664,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.86", ] [[package]] @@ -2374,6 +2748,7 @@ dependencies = [ "libc", "rand_chacha", "rand_core", + "serde", ] [[package]] @@ -2499,6 +2874,12 @@ dependencies = [ "rustc-hex", ] +[[package]] +name = "route-recognizer" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afab94fb28594581f62d981211a9a4d53cc8130bbcbbb89a0440d9b8e81a7746" + [[package]] name = "ruint" version = "1.12.3" @@ -2549,6 +2930,12 @@ version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +[[package]] +name = "rustc-hash" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" + [[package]] name = "rustc-hex" version = "2.1.0" @@ -2592,6 +2979,7 @@ version = "0.23.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8" dependencies = [ + "log", "once_cell", "ring", "rustls-pki-types", @@ -2600,6 +2988,19 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rustls-native-certs" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "rustls-pki-types", + "schannel", + "security-framework", +] + [[package]] name = "rustls-pemfile" version = "2.1.3" @@ -2616,6 +3017,33 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0" +[[package]] +name = "rustls-platform-verifier" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afbb878bdfdf63a336a5e63561b1835e7a8c91524f51621db870169eac84b490" +dependencies = [ + "core-foundation", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework", + "security-framework-sys", + "webpki-roots", + "winapi", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" + [[package]] name = "rustls-webpki" version = "0.102.8" @@ -2651,6 +3079,15 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "schannel" version = "0.1.24" @@ -2699,6 +3136,7 @@ dependencies = [ "core-foundation", "core-foundation-sys", "libc", + "num-bigint", "security-framework-sys", ] @@ -2736,6 +3174,12 @@ dependencies = [ "pest", ] +[[package]] +name = "send_wrapper" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0" + [[package]] name = "send_wrapper" version = "0.6.0" @@ -2759,7 +3203,7 @@ checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.86", ] [[package]] @@ -2817,6 +3261,16 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest 0.10.7", + "keccak", +] + [[package]] name = "sha3-asm" version = "0.1.4" @@ -2835,9 +3289,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signature" -version = "2.2.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +checksum = "8fe458c98333f9c8152221191a77e2a44e8325d0193484af2e9421a53019e57d" dependencies = [ "digest 0.10.7", "rand_core", @@ -2868,6 +3322,22 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "soketto" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37468c595637c10857701c990f93a40ce0e357cedb0953d1c26c8d8027f9bb53" +dependencies = [ + "base64", + "bytes", + "futures", + "http", + "httparse", + "log", + "rand", + "sha1", +] + [[package]] name = "spin" version = "0.9.8" @@ -2915,7 +3385,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.77", + "syn 2.0.86", ] [[package]] @@ -2937,9 +3407,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.77" +version = "2.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "e89275301d38033efb81a6e60e3497e734dfcc62571f2854bf4b16690398824c" dependencies = [ "proc-macro2", "quote", @@ -2948,14 +3418,14 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "0.8.3" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b95156f8b577cb59dc0b1df15c6f29a10afc5f8a7ac9786b0b5c68c19149278" +checksum = "16320d4a2021ba1a32470b3759676114a918885e9800e68ad60f2c67969fba62" dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.86", ] [[package]] @@ -3009,7 +3479,7 @@ checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.86", ] [[package]] @@ -3047,9 +3517,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.40.0" +version = "1.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +checksum = "145f3413504347a2be84393cc8a7d2fb4d863b375909ea59f2158261aa258bbb" dependencies = [ "backtrace", "bytes", @@ -3069,7 +3539,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.86", ] [[package]] @@ -3129,6 +3599,7 @@ checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" dependencies = [ "bytes", "futures-core", + "futures-io", "futures-sink", "pin-project-lite", "tokio", @@ -3181,6 +3652,7 @@ dependencies = [ "tokio", "tower-layer", "tower-service", + "tracing", ] [[package]] @@ -3215,6 +3687,7 @@ version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ + "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -3228,7 +3701,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.86", ] [[package]] @@ -3389,6 +3862,16 @@ dependencies = [ "libc", ] +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "want" version = "0.3.1" @@ -3426,7 +3909,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.86", "wasm-bindgen-shared", ] @@ -3460,7 +3943,7 @@ checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.86", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3496,6 +3979,37 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-registry" version = "0.2.0" @@ -3629,7 +4143,7 @@ dependencies = [ "log", "pharos", "rustc_version 0.4.1", - "send_wrapper", + "send_wrapper 0.6.0", "thiserror", "wasm-bindgen", "wasm-bindgen-futures", @@ -3663,7 +4177,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.86", ] [[package]] @@ -3683,5 +4197,5 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.77", + "syn 2.0.86", ] diff --git a/Cargo.toml b/Cargo.toml index 62fdcd0..50166cd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,13 +9,15 @@ name = "contender_core" path = "src/lib.rs" [dependencies] -alloy = { workspace = true, features = ["full", "node-bindings"] } +alloy = { workspace = true, features = ["full", "node-bindings", "rpc-types-mev"] } eyre = "0.6.12" rand = "0.8.5" -serde = { workspace = true } +serde = { workspace = true, features = ["derive"] } futures = "0.3.30" async-trait = "0.1.82" tokio = { workspace = true } +jsonrpsee = { version = "0.24", features = ["full", "server-core"] } +alloy-serde = "0.5.4" [workspace] members = ["cli", "sqlite_db", "testfile"] diff --git a/cli/src/main.rs b/cli/src/main.rs index 6f0bdb4..bbcc7d1 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -32,6 +32,10 @@ static DB: LazyLock = std::sync::LazyLock::new(|| { SqliteDb::from_file("contender.db").expect("failed to open contender.db") }); +fn rbuilder_url() -> Url { + "http://localhost:3000".parse().expect("invalid url") +} + fn get_signers_with_defaults(private_keys: Option>) -> Vec { if private_keys.is_none() { println!("No private keys provided. Using default private keys."); @@ -77,13 +81,17 @@ async fn main() -> Result<(), Box> { .map(|key| PrivateKeySigner::from_str(&key).expect("invalid private key")) .collect::>(); let signers = get_signers_with_defaults(private_keys); - check_private_keys(&testconfig.setup.to_owned().unwrap_or(vec![]), &signers); + check_private_keys( + &testconfig.setup.to_owned().unwrap_or(vec![]), + signers.as_slice(), + ); check_balances(&user_signers, min_balance, &rpc_client).await; let scenario = TestScenario::new( testconfig.to_owned(), Arc::new(DB.clone()), url, + Some(rbuilder_url()), // TODO: replace this with user-provided url RandSeed::new(), &signers, ); @@ -122,7 +130,7 @@ async fn main() -> Result<(), Box> { let mut fn_calls = vec![]; for s in spam { match s { - SpamRequest::Single(fn_call) => { + SpamRequest::Tx(fn_call) => { fn_calls.push(fn_call.to_owned()); } SpamRequest::Bundle(bundle) => { @@ -131,8 +139,8 @@ async fn main() -> Result<(), Box> { } } - check_private_keys(&fn_calls, &signers); - check_balances(&signers, min_balance, &rpc_client).await; + check_private_keys(&fn_calls, signers.as_slice()); + check_balances(signers.as_slice(), min_balance, &rpc_client).await; if txs_per_block.is_some() && txs_per_second.is_some() { panic!("Cannot set both --txs-per-block and --txs-per-second"); @@ -142,8 +150,14 @@ async fn main() -> Result<(), Box> { } if let Some(txs_per_block) = txs_per_block { - let scenario = - TestScenario::new(testconfig, DB.clone().into(), url, rand_seed, &signers); + let scenario = TestScenario::new( + testconfig, + DB.clone().into(), + url, + Some(rbuilder_url()), + rand_seed, + &signers, + ); println!("Blockwise spamming with {} txs per block", txs_per_block); match spam_callback_default(!disable_reports, Arc::new(rpc_client).into()).await { SpamCallbackType::Log(cback) => { @@ -168,8 +182,14 @@ async fn main() -> Result<(), Box> { // NOTE: private keys are not currently used for timed spamming. // Timed spamming only works with unlocked accounts, because it uses the `eth_sendTransaction` RPC method. - let scenario = - TestScenario::new(testconfig, DB.clone().into(), url, rand_seed, &signers); + let scenario = TestScenario::new( + testconfig, + DB.clone().into(), + url, + Some(rbuilder_url()), + rand_seed, + &signers, + ); let tps = txs_per_second.unwrap_or(10); println!("Timed spamming with {} txs per second", tps); let spammer = TimedSpammer::new(scenario, NilCallback::new()); diff --git a/cli/testfile.toml b/cli/testfile.toml index d9a7ea4..0c3a5d5 100644 --- a/cli/testfile.toml +++ b/cli/testfile.toml @@ -3,8 +3,36 @@ bytecode = "0x6080604052348015600f57600080fd5b506104f48061001f6000396000f3fe6080 name = "SpamMe" from = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" +# # spam single tx +# [[spam]] + +# [spam.tx] +# to = "{SpamMe}" +# from = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" +# signature = "consumeGas(uint256)" +# args = ["150000"] + +# # same tx from alt address +# [[spam]] + +# [spam.tx] +# to = "{SpamMe}" +# from = "0x70997970C51812dc3A010C7d01b50e0d17dc79C8" +# signature = "consumeGas(uint256)" +# args = ["500000"] + + +# spam bundle [[spam]] + +[[spam.bundle.tx]] to = "{SpamMe}" from = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" signature = "consumeGas(uint256)" -args = ["50000"] +args = ["151000"] + +[[spam.bundle.tx]] +to = "{SpamMe}" +from = "0x70997970C51812dc3A010C7d01b50e0d17dc79C8" +signature = "consumeGas(uint256)" +args = ["152000"] diff --git a/src/bundle_provider.rs b/src/bundle_provider.rs new file mode 100644 index 0000000..3fce71d --- /dev/null +++ b/src/bundle_provider.rs @@ -0,0 +1,76 @@ +use alloy::network::EthereumWallet; +use alloy::primitives::{Bytes, B256}; +use alloy::signers::local::PrivateKeySigner; +use alloy::transports::http::reqwest::Url; +use jsonrpsee::{core::client::ClientT, rpc_params}; +use serde::{Deserialize, Serialize}; + +pub struct BundleClient { + url: Url, + _wallet: EthereumWallet, // TODO: use to sign payload for auth header +} + +impl BundleClient { + pub fn new(url: String, auth_signer: PrivateKeySigner) -> Self { + let wallet = EthereumWallet::new(auth_signer); + Self { + url: url.parse().expect("invalid bundle RPC URL"), + _wallet: wallet, + } + } + + pub async fn send_bundle(&self, bundle: EthSendBundle) -> Result<(), String> { + // TODO: make this more efficient + let client = jsonrpsee::http_client::HttpClient::builder() + // .set_headers(HeaderMap::from_iter(vec![( + // HeaderName::from_str("X-Flashbots-Signature").unwrap(), + // HeaderValue::from_str("test").unwrap(), + // )])) + .build(self.url.clone()) + .expect("failed to connect to RPC provider"); + + let res: Result = client.request("eth_sendBundle", rpc_params![bundle]).await; + println!("sent bundle {:?}", res); + + Ok(()) + } +} + +// testing: +#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct EthSendBundle { + /// A list of hex-encoded signed transactions + pub txs: Vec, + /// hex-encoded block number for which this bundle is valid + #[serde(with = "alloy_serde::quantity")] + pub block_number: u64, + /// unix timestamp when this bundle becomes active + #[serde( + default, + with = "alloy_serde::quantity::opt", + skip_serializing_if = "Option::is_none" + )] + pub min_timestamp: Option, + /// unix timestamp how long this bundle stays valid + #[serde( + default, + with = "alloy_serde::quantity::opt", + skip_serializing_if = "Option::is_none" + )] + pub max_timestamp: Option, + /// list of hashes of possibly reverting txs + #[serde( + default + // this doesn't work on rbuilder: + // , skip_serializing_if = "Vec::is_empty" + )] + pub reverting_tx_hashes: Vec, + /// UUID that can be used to cancel/replace this bundle + #[serde( + default, + rename = "replacementUuid", + skip_serializing_if = "Option::is_none" + )] + pub replacement_uuid: Option, +} diff --git a/src/generator/mod.rs b/src/generator/mod.rs index 74c26b3..84f95b5 100644 --- a/src/generator/mod.rs +++ b/src/generator/mod.rs @@ -168,7 +168,7 @@ where // populate maps for each step match step { - SpamRequest::Single(tx) => { + SpamRequest::Tx(tx) => { lookup_tx_placeholders(tx); } SpamRequest::Bundle(req) => { @@ -196,7 +196,7 @@ where }; match step { - SpamRequest::Single(req) => { + SpamRequest::Tx(req) => { let (handle, tx) = process_tx(req)?; if let Some(handle) = handle { handle.await.map_err(|e| { diff --git a/src/generator/types.rs b/src/generator/types.rs index 2344867..b06f092 100644 --- a/src/generator/types.rs +++ b/src/generator/types.rs @@ -15,6 +15,8 @@ pub use crate::generator::named_txs::NamedTxRequest; // -- convenience pub type EthProvider = RootProvider>; pub type AnyProvider = RootProvider, AnyNetwork>; +// TODO: associate this with whatever we come up with using ethers +// pub type BuilderProvider; // -- core types for test scenarios @@ -45,9 +47,12 @@ pub struct BundleCallDefinition { } /// Definition of a spam request template. +/// TestConfig uses this for TOML parsing. #[derive(Clone, Deserialize, Debug, Serialize)] pub enum SpamRequest { - Single(FunctionCallDefinition), + #[serde(rename = "tx")] + Tx(FunctionCallDefinition), + #[serde(rename = "bundle")] Bundle(BundleCallDefinition), } diff --git a/src/lib.rs b/src/lib.rs index c94a68c..d84c061 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,4 @@ +pub mod bundle_provider; pub mod db; pub mod error; pub mod generator; diff --git a/src/spammer/blockwise.rs b/src/spammer/blockwise.rs index 609e0bf..17cf548 100644 --- a/src/spammer/blockwise.rs +++ b/src/spammer/blockwise.rs @@ -1,3 +1,4 @@ +use crate::bundle_provider::{BundleClient, EthSendBundle}; use crate::db::DbOps; use crate::error::ContenderError; use crate::generator::named_txs::ExecutionRequest; @@ -8,12 +9,18 @@ use crate::generator::{Generator, NamedTxRequest, PlanConfig}; use crate::spammer::ExecutionPayload; use crate::test_scenario::TestScenario; use crate::Result; +use alloy::consensus::BlobTransactionSidecar; +use alloy::eips::eip2718::Encodable2718; use alloy::hex::ToHexExt; -use alloy::network::{AnyNetwork, Ethereum, EthereumWallet, NetworkWallet, TransactionBuilder}; +use alloy::network::{ + AnyNetwork, Ethereum, EthereumWallet, NetworkWallet, TransactionBuilder, TransactionBuilder4844, +}; use alloy::primitives::{Address, FixedBytes}; -use alloy::providers::{PendingTransactionBuilder, Provider, ProviderBuilder}; +use alloy::providers::{ + PendingTransactionBuilder, PendingTransactionConfig, Provider, ProviderBuilder, +}; use alloy::rpc::client::ReqwestClient; -use alloy::rpc::types::TransactionRequest; +use alloy::rpc::types::{Transaction, TransactionRequest}; use alloy::transports::http::{Client, Http}; use futures::StreamExt; use std::collections::HashMap; @@ -35,6 +42,7 @@ where msg_handler: Arc, rpc_client: AnyProvider, eth_client: Arc, + bundle_client: Option>, callback_handler: Arc, nonces: HashMap, gas_limits: HashMap, u128>, @@ -52,6 +60,12 @@ where .network::() .on_http(scenario.rpc_url.to_owned()); let eth_client = Arc::new(ProviderBuilder::new().on_http(scenario.rpc_url.to_owned())); + // TODO: parameterize auth_signer (u get random auth signer until then) + let auth_signer = alloy::signers::local::LocalSigner::random(); + let bundle_client = scenario + .builder_rpc_url + .to_owned() + .map(|url| Arc::new(BundleClient::new(url.into(), auth_signer))); let msg_handler = Arc::new(TxActorHandle::new( 12, scenario.db.clone(), @@ -76,6 +90,7 @@ where scenario, rpc_client, eth_client, + bundle_client, callback_handler, msg_handler, nonces, @@ -134,7 +149,10 @@ where let full_tx = tx_req .clone() .with_nonce(nonce) - .with_gas_price(gas_price) + .with_max_fee_per_gas(gas_price + (gas_price / 5)) + .with_max_priority_fee_per_gas(gas_price / 5) + .with_max_fee_per_blob_gas(gas_price + (gas_price / 5)) + .with_blob_sidecar(BlobTransactionSidecar::new(vec![], vec![], vec![])) .with_chain_id(chain_id) .with_gas_limit(gas_limit); @@ -178,18 +196,6 @@ where .take(num_blocks); let mut tasks = vec![]; - // let mut gas_limits = HashMap::, u128>::new(); - - // // get nonce for each signer and put it into a hashmap - // let mut nonces = HashMap::new(); - // for (addr, _) in self.scenario.wallet_map.iter() { - // let nonce = self - // .rpc_client - // .get_transaction_count(*addr) - // .await - // .map_err(|_| ContenderError::SpamError("failed to get nonce", None))?; - // nonces.insert(*addr, nonce); - // } while let Some(block_hash) = stream.next().await { let block_txs = tx_req_chunks[block_offset].clone(); @@ -225,7 +231,7 @@ where for req in reqs.iter() { let tx_req = req.tx.to_owned(); let (tx_req, signer) = self - .prepare_tx_req(&tx_req, gas_price, chain_id) + .prepare_tx_req(&tx_req, gas_price + (gas_price / 3), chain_id) .await .map_err(|e| ContenderError::with_err(e, "failed to prepare tx"))?; @@ -233,6 +239,7 @@ where let tx_envelope = tx_req.build(&signer).await.map_err(|e| { ContenderError::with_err(e, "bad request: failed to build tx") })?; + println!("signed {:?}", tx_envelope.tx_hash()); bundle_txs.push(tx_envelope); } // TODO: call eth_sendBundle with signed txs @@ -271,6 +278,8 @@ where } }; + let bundle_client = self.bundle_client.clone(); + // build, sign, and send tx/bundle in a new task (green thread) tasks.push(task::spawn(async move { let provider = ProviderBuilder::new().on_provider(eth_client); @@ -298,10 +307,43 @@ where vec![maybe_handle] } ExecutionPayload::SignedTxBundle(signed_txs, reqs) => { - // let res = provider.send_bundle(txs) - // let handles = report_sent_req(start_timestamp, res); - // TODO: implement send_bundle - todo!(); + let mut bundle_txs = vec![]; + for tx in &signed_txs { + println!("sending tx: {:?}", tx); + let mut raw_tx = vec![]; + tx.encode_2718(&mut raw_tx); + bundle_txs.push(raw_tx); + } + let rpc_bundle = EthSendBundle { + txs: bundle_txs.into_iter().map(|tx| tx.into()).collect(), + block_number: last_block_number, + min_timestamp: None, + max_timestamp: None, + reverting_tx_hashes: vec![], + replacement_uuid: None, + }; + if let Some(bundle_client) = bundle_client { + // send 12 bundles at a time, targeting each next block + for i in 1..13 { + let mut rpc_bundle = rpc_bundle.clone(); + rpc_bundle.block_number = last_block_number + i as u64; + println!("FINNA SEND A BUNDLE!"); + let res = bundle_client.send_bundle(rpc_bundle).await; + println!("{:?}", res); + } + } + + let mut tx_handles = vec![]; + for (tx, req) in signed_txs.into_iter().zip(&reqs) { + let maybe_handle = callback_handler.on_tx_sent( + PendingTransactionConfig::new(tx.tx_hash().to_owned()), + &req, + extra.clone().into(), + Some(tx_handler.clone()), + ); + tx_handles.push(maybe_handle); + } + tx_handles } }; for handle in handles { @@ -372,8 +414,9 @@ mod tests { MockConfig, MockDb.into(), anvil.endpoint_url(), + None, seed, - &get_test_signers(), + get_test_signers().as_slice(), ); let callback_handler = MockCallback; let mut spammer = BlockwiseSpammer::new(scenario, callback_handler).await; diff --git a/src/spammer/timed.rs b/src/spammer/timed.rs index 8c5fe91..3723512 100644 --- a/src/spammer/timed.rs +++ b/src/spammer/timed.rs @@ -1,15 +1,11 @@ use crate::{ - db::DbOps, - generator::{ + bundle_provider::BundleClient, db::DbOps, generator::{ named_txs::ExecutionRequest, seeder::Seeder, templater::Templater, types::{EthProvider, PlanType}, Generator, PlanConfig, - }, - spammer::OnTxSent, - test_scenario::TestScenario, - Result, + }, spammer::OnTxSent, test_scenario::TestScenario, Result }; use alloy::hex::ToHexExt; use alloy::providers::Provider; @@ -27,6 +23,7 @@ where scenario: TestScenario, rpc_client: Arc, callback_handler: Arc, + bundle_client: Option, } impl TimedSpammer @@ -38,10 +35,16 @@ where { pub fn new(scenario: TestScenario, callback_handler: F) -> Self { let rpc_client = ProviderBuilder::new().on_http(scenario.rpc_url.to_owned()); + // TODO: parameterize auth_signer (u get random auth signer until then) + let auth_signer = alloy::signers::local::LocalSigner::random(); + let bundle_client = scenario.builder_rpc_url.to_owned().map(|url| { + BundleClient::new(url.into(), auth_signer) + }); Self { scenario, rpc_client: Arc::new(rpc_client), callback_handler: Arc::new(callback_handler), + bundle_client, } } diff --git a/src/test_scenario.rs b/src/test_scenario.rs index c95ad74..6e849fb 100644 --- a/src/test_scenario.rs +++ b/src/test_scenario.rs @@ -22,6 +22,7 @@ where pub config: P, pub db: Arc, pub rpc_url: Url, + pub builder_rpc_url: Option, pub rand_seed: S, pub wallet_map: HashMap, } @@ -36,6 +37,7 @@ where config: P, db: Arc, rpc_url: Url, + builder_rpc_url: Option, rand_seed: S, signers: &[PrivateKeySigner], ) -> Self { @@ -52,6 +54,7 @@ where config, db: db.clone(), rpc_url, + builder_rpc_url, rand_seed, wallet_map, } @@ -327,7 +330,7 @@ pub mod tests { fn get_spam_steps(&self) -> Result> { let fn_call = |data: &str, from_addr: &str| { - SpamRequest::Single(FunctionCallDefinition { + SpamRequest::Tx(FunctionCallDefinition { to: "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D".to_owned(), from: from_addr.to_owned(), value: None, @@ -392,6 +395,7 @@ pub mod tests { MockConfig, MockDb.into(), anvil.endpoint_url(), + None, seed, &signers, ) diff --git a/testfile/src/lib.rs b/testfile/src/lib.rs index 197cdf4..5aab5a8 100644 --- a/testfile/src/lib.rs +++ b/testfile/src/lib.rs @@ -112,7 +112,7 @@ pub mod tests { db::MockDb, generator::{ named_txs::ExecutionRequest, - types::{CreateDefinition, FunctionCallDefinition, FuzzParam, PlanType, SpamRequest}, + types::{BundleCallDefinition, CreateDefinition, FunctionCallDefinition, FuzzParam, PlanType, SpamRequest}, Generator, RandSeed, }, test_scenario::TestScenario, @@ -158,13 +158,13 @@ pub mod tests { env: None, create: None, setup: None, - spam: vec![SpamRequest::Single(fncall)].into(), + spam: vec![SpamRequest::Tx(fncall)].into(), } } pub fn get_fuzzy_testconfig() -> TestConfig { let fn_call = |data: &str, from_addr: &str| { - SpamRequest::Single(FunctionCallDefinition { + FunctionCallDefinition { to: "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D".to_owned(), from: from_addr.to_owned(), value: None, @@ -183,16 +183,23 @@ pub mod tests { max: None, }] .into(), - }) + } }; TestConfig { env: None, create: None, setup: None, spam: vec![ - fn_call("0xbeef", "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"), - fn_call("0xea75", "0x70997970C51812dc3A010C7d01b50e0d17dc79C8"), - fn_call("0xf00d", "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC"), + SpamRequest::Tx(fn_call("0xbeef", "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266")), + SpamRequest::Tx(fn_call("0xea75", "0x70997970C51812dc3A010C7d01b50e0d17dc79C8")), + SpamRequest::Tx(fn_call("0xf00d", "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC")), + SpamRequest::Bundle(BundleCallDefinition { + txs: vec![ + fn_call("0xbeef", "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"), + fn_call("0xea75", "0x70997970C51812dc3A010C7d01b50e0d17dc79C8"), + fn_call("0xf00d", "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC"), + ] + }) ] .into(), } @@ -279,7 +286,7 @@ pub mod tests { assert_eq!(env.get("env1").unwrap(), "env1"); match spam[0] { - SpamRequest::Single(ref fncall) => { + SpamRequest::Tx(ref fncall) => { assert_eq!( fncall.from, "0x70997970C51812dc3A010C7d01b50e0d17dc79C8".to_owned() @@ -315,10 +322,10 @@ pub mod tests { let test_file2 = TestConfig::from_file("cargotest.toml").unwrap(); let spam = cfg.clone().spam.unwrap(); match &spam[0] { - SpamRequest::Single(req) => { + SpamRequest::Tx(req) => { let args = req.args.as_ref().unwrap(); match &test_file2.spam.unwrap()[0] { - SpamRequest::Single(req2) => { + SpamRequest::Tx(req2) => { let args2 = req2.args.as_ref().unwrap(); assert_eq!(req.from, req2.from); assert_eq!(req.to, req2.to); @@ -349,6 +356,7 @@ pub mod tests { test_file, MockDb.into(), anvil.endpoint_url(), + None, seed, &get_test_signers(), ); @@ -385,6 +393,7 @@ pub mod tests { test_file.clone(), MockDb.into(), anvil.endpoint_url(), + None, seed.to_owned(), &signers, ); @@ -392,6 +401,7 @@ pub mod tests { test_file, MockDb.into(), anvil.endpoint_url(), + None, seed, &signers, ); @@ -420,8 +430,17 @@ pub mod tests { } } } - _ => { - panic!("expected ExecutionRequest::Tx"); + ExecutionRequest::Bundle(reqs) => { + let data1 = reqs[0].tx.input.input.to_owned().unwrap().to_string(); + match &spam_txs_2[i] { + ExecutionRequest::Bundle(reqs) => { + let data2 = reqs[0].tx.input.input.to_owned().unwrap().to_string(); + assert_eq!(data1, data2); + } + _ => { + panic!("expected ExecutionRequest::Bundle"); + } + } } } } diff --git a/testfile/testConfig.toml b/testfile/testConfig.toml index 5371354..4046fe1 100644 --- a/testfile/testConfig.toml +++ b/testfile/testConfig.toml @@ -8,9 +8,11 @@ from = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" signature = "function deposit() public payable" value = "1234" - -### the spam step will be repeated +# the spam step will be repeated [[spam]] + +# specify a single tx to spam +[spam.tx] kind = "test" to = "0xE46CcF40134e7ad524529B25Ce04e39BC2B51cDc" from = "0x70997970C51812dc3A010C7d01b50e0d17dc79C8" @@ -20,7 +22,8 @@ args = [ "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", ] -[[spam.fuzz]] +# each tx can have multiple fuzzed params +[[spam.tx.fuzz]] param = "amountIn" min = "1" max = "100000000000000000" From abb226c8301d39e67969cc6e8a8e0e8f65be6323 Mon Sep 17 00:00:00 2001 From: zeroXbrock <2791467+zeroXbrock@users.noreply.github.com> Date: Wed, 6 Nov 2024 13:21:40 -0800 Subject: [PATCH 07/19] working sendBundle scenario from blockwise spammer --- Cargo.lock | 5 +++-- Cargo.toml | 1 + cli/testfile.toml | 21 ++++++------------- src/bundle_provider.rs | 45 +++++++++++++++++++++------------------- src/generator/mod.rs | 14 +++++++------ src/spammer/blockwise.rs | 32 +++++++++------------------- src/spammer/mod.rs | 5 +---- src/spammer/timed.rs | 20 ++++++++---------- 8 files changed, 62 insertions(+), 81 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8f9e9e0..f5dc59a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1154,6 +1154,7 @@ dependencies = [ "jsonrpsee", "rand", "serde", + "serde_json", "tokio", ] @@ -3208,9 +3209,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.128" +version = "1.0.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" dependencies = [ "itoa", "memchr", diff --git a/Cargo.toml b/Cargo.toml index 50166cd..811968a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ async-trait = "0.1.82" tokio = { workspace = true } jsonrpsee = { version = "0.24", features = ["full", "server-core"] } alloy-serde = "0.5.4" +serde_json = "1.0.132" [workspace] members = ["cli", "sqlite_db", "testfile"] diff --git a/cli/testfile.toml b/cli/testfile.toml index 0c3a5d5..51a1063 100644 --- a/cli/testfile.toml +++ b/cli/testfile.toml @@ -1,25 +1,16 @@ [[create]] -bytecode = "0x6080604052348015600f57600080fd5b506104f48061001f6000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c806369f86ec8146100515780639402c0041461005b578063a329e8de1461006e578063fb0e722b14610081575b600080fd5b61005961009f565b005b6100596100693660046101a5565b6100aa565b61005961007c36600461025e565b6100df565b610089610101565b604051610096919061029b565b60405180910390f35b5b60325a116100a057565b6000816040516020016100be929190610308565b604051602081830303815290604052600090816100db91906103dd565b5050565b60005b6100ed60db8361049c565b8110156100db5760008190556001016100e2565b6000805461010e906102ce565b80601f016020809104026020016040519081016040528092919081815260200182805461013a906102ce565b80156101875780601f1061015c57610100808354040283529160200191610187565b820191906000526020600020905b81548152906001019060200180831161016a57829003601f168201915b505050505081565b634e487b7160e01b600052604160045260246000fd5b6000602082840312156101b757600080fd5b813567ffffffffffffffff8111156101ce57600080fd5b8201601f810184136101df57600080fd5b803567ffffffffffffffff8111156101f9576101f961018f565b604051601f8201601f19908116603f0116810167ffffffffffffffff811182821017156102285761022861018f565b60405281815282820160200186101561024057600080fd5b81602084016020830137600091810160200191909152949350505050565b60006020828403121561027057600080fd5b5035919050565b60005b8381101561029257818101518382015260200161027a565b50506000910152565b60208152600082518060208401526102ba816040850160208701610277565b601f01601f19169190910160400192915050565b600181811c908216806102e257607f821691505b60208210810361030257634e487b7160e01b600052602260045260246000fd5b50919050565b6000808454610316816102ce565b60018216801561032d576001811461034257610372565b60ff1983168652811515820286019350610372565b87600052602060002060005b8381101561036a5781548882015260019091019060200161034e565b505081860193505b5050508351610385818360208801610277565b01949350505050565b601f8211156103d857806000526020600020601f840160051c810160208510156103b55750805b601f840160051c820191505b818110156103d557600081556001016103c1565b50505b505050565b815167ffffffffffffffff8111156103f7576103f761018f565b61040b8161040584546102ce565b8461038e565b6020601f82116001811461043f57600083156104275750848201515b600019600385901b1c1916600184901b1784556103d5565b600084815260208120601f198516915b8281101561046f578785015182556020948501946001909201910161044f565b508482101561048d5786840151600019600387901b60f8161c191681555b50505050600190811b01905550565b6000826104b957634e487b7160e01b600052601260045260246000fd5b50049056fea26469706673582212207301a1db30de4d24440f5ed9a7dc130e8bc19f454451a8422ba4f1308087182964736f6c634300081b0033" +bytecode = "0x6080604052348015600f57600080fd5b5061055d8061001f6000396000f3fe60806040526004361061004a5760003560e01c806369f86ec81461004f5780639402c00414610066578063a329e8de14610086578063c5eeaf17146100a6578063fb0e722b146100ae575b600080fd5b34801561005b57600080fd5b506100646100d9565b005b34801561007257600080fd5b5061006461008136600461020e565b6100e4565b34801561009257600080fd5b506100646100a13660046102c7565b610119565b61006461013b565b3480156100ba57600080fd5b506100c361016a565b6040516100d09190610304565b60405180910390f35b5b60325a116100da57565b6000816040516020016100f8929190610371565b604051602081830303815290604052600090816101159190610446565b5050565b60005b61012760db83610505565b81101561011557600081905560010161011c565b60405141903480156108fc02916000818181858888f19350505050158015610167573d6000803e3d6000fd5b50565b6000805461017790610337565b80601f01602080910402602001604051908101604052809291908181526020018280546101a390610337565b80156101f05780601f106101c5576101008083540402835291602001916101f0565b820191906000526020600020905b8154815290600101906020018083116101d357829003601f168201915b505050505081565b634e487b7160e01b600052604160045260246000fd5b60006020828403121561022057600080fd5b813567ffffffffffffffff81111561023757600080fd5b8201601f8101841361024857600080fd5b803567ffffffffffffffff811115610262576102626101f8565b604051601f8201601f19908116603f0116810167ffffffffffffffff81118282101715610291576102916101f8565b6040528181528282016020018610156102a957600080fd5b81602084016020830137600091810160200191909152949350505050565b6000602082840312156102d957600080fd5b5035919050565b60005b838110156102fb5781810151838201526020016102e3565b50506000910152565b60208152600082518060208401526103238160408501602087016102e0565b601f01601f19169190910160400192915050565b600181811c9082168061034b57607f821691505b60208210810361036b57634e487b7160e01b600052602260045260246000fd5b50919050565b600080845461037f81610337565b60018216801561039657600181146103ab576103db565b60ff19831686528115158202860193506103db565b87600052602060002060005b838110156103d3578154888201526001909101906020016103b7565b505081860193505b50505083516103ee8183602088016102e0565b01949350505050565b601f82111561044157806000526020600020601f840160051c8101602085101561041e5750805b601f840160051c820191505b8181101561043e576000815560010161042a565b50505b505050565b815167ffffffffffffffff811115610460576104606101f8565b6104748161046e8454610337565b846103f7565b6020601f8211600181146104a857600083156104905750848201515b600019600385901b1c1916600184901b17845561043e565b600084815260208120601f198516915b828110156104d857878501518255602094850194600190920191016104b8565b50848210156104f65786840151600019600387901b60f8161c191681555b50505050600190811b01905550565b60008261052257634e487b7160e01b600052601260045260246000fd5b50049056fea2646970667358221220e6a2a27582535e97477f8641207332fe4acf27138934be685090cb3acd25033164736f6c634300081b0033" name = "SpamMe" from = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" -# # spam single tx -# [[spam]] - -# [spam.tx] -# to = "{SpamMe}" -# from = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" -# signature = "consumeGas(uint256)" -# args = ["150000"] - -# # same tx from alt address +# spam single tx # [[spam]] # [spam.tx] # to = "{SpamMe}" # from = "0x70997970C51812dc3A010C7d01b50e0d17dc79C8" # signature = "consumeGas(uint256)" -# args = ["500000"] +# args = ["150000"] # spam bundle @@ -29,10 +20,10 @@ from = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" to = "{SpamMe}" from = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" signature = "consumeGas(uint256)" -args = ["151000"] +args = ["510000"] [[spam.bundle.tx]] to = "{SpamMe}" from = "0x70997970C51812dc3A010C7d01b50e0d17dc79C8" -signature = "consumeGas(uint256)" -args = ["152000"] +signature = "tipCoinbase()" +value = "100000000000000000" diff --git a/src/bundle_provider.rs b/src/bundle_provider.rs index 3fce71d..f8bdb02 100644 --- a/src/bundle_provider.rs +++ b/src/bundle_provider.rs @@ -1,36 +1,39 @@ -use alloy::network::EthereumWallet; use alloy::primitives::{Bytes, B256}; -use alloy::signers::local::PrivateKeySigner; -use alloy::transports::http::reqwest::Url; +use jsonrpsee::http_client::HttpClient; use jsonrpsee::{core::client::ClientT, rpc_params}; use serde::{Deserialize, Serialize}; pub struct BundleClient { - url: Url, - _wallet: EthereumWallet, // TODO: use to sign payload for auth header + client: HttpClient, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct EthSendBundleResponse { + pub bundle_hash: B256, } impl BundleClient { - pub fn new(url: String, auth_signer: PrivateKeySigner) -> Self { - let wallet = EthereumWallet::new(auth_signer); - Self { - url: url.parse().expect("invalid bundle RPC URL"), - _wallet: wallet, - } + pub fn new(url: String) -> Self { + let client = HttpClient::builder() + .build(url) + .expect("failed to connect to RPC provider"); + Self { client } } pub async fn send_bundle(&self, bundle: EthSendBundle) -> Result<(), String> { - // TODO: make this more efficient - let client = jsonrpsee::http_client::HttpClient::builder() - // .set_headers(HeaderMap::from_iter(vec![( - // HeaderName::from_str("X-Flashbots-Signature").unwrap(), - // HeaderValue::from_str("test").unwrap(), - // )])) - .build(self.url.clone()) - .expect("failed to connect to RPC provider"); + // Result contents optional because some endpoints don't return this response + let res: Result, _> = self + .client + .request("eth_sendBundle", rpc_params![bundle]) + .await; - let res: Result = client.request("eth_sendBundle", rpc_params![bundle]).await; - println!("sent bundle {:?}", res); + if let Ok(res) = res { + if let Some(res) = res { + println!("sent bundle {:?}", res); + } else { + println!("sent bundle, no response"); + } + } Ok(()) } diff --git a/src/generator/mod.rs b/src/generator/mod.rs index 84f95b5..1e62d1e 100644 --- a/src/generator/mod.rs +++ b/src/generator/mod.rs @@ -71,16 +71,14 @@ where num_values: usize, fuzz_args: &[FuzzParam], ) -> HashMap> { - let mut fuzz_map = HashMap::>::new(); let seed = self.get_fuzz_seeder(); - for fuzz in fuzz_args { + HashMap::>::from_iter(fuzz_args.iter().map(|fuzz| { let values: Vec = seed .seed_values(num_values, fuzz.min, fuzz.max) .map(|v| v.as_u256()) .collect(); - fuzz_map.insert(fuzz.param.to_owned(), values); - } - fuzz_map + (fuzz.param.to_owned(), values) + })) } async fn load_txs CallbackResult>( @@ -162,7 +160,11 @@ where // finds placeholders in a function call definition and populates `placeholder_map` and `canonical_fuzz_map` with injectable values. let mut lookup_tx_placeholders = |tx: &FunctionCallDefinition| { - templater.find_fncall_placeholders(tx, db, &mut placeholder_map); + let res = templater.find_fncall_placeholders(tx, db, &mut placeholder_map); + if let Err(e) = res { + eprintln!("error finding placeholders: {}", e); + return; + } find_fuzz(tx); }; diff --git a/src/spammer/blockwise.rs b/src/spammer/blockwise.rs index 17cf548..1c21bd0 100644 --- a/src/spammer/blockwise.rs +++ b/src/spammer/blockwise.rs @@ -5,23 +5,16 @@ use crate::generator::named_txs::ExecutionRequest; use crate::generator::seeder::Seeder; use crate::generator::templater::Templater; use crate::generator::types::{AnyProvider, EthProvider, PlanType}; -use crate::generator::{Generator, NamedTxRequest, PlanConfig}; +use crate::generator::{Generator, PlanConfig}; use crate::spammer::ExecutionPayload; use crate::test_scenario::TestScenario; use crate::Result; -use alloy::consensus::BlobTransactionSidecar; use alloy::eips::eip2718::Encodable2718; use alloy::hex::ToHexExt; -use alloy::network::{ - AnyNetwork, Ethereum, EthereumWallet, NetworkWallet, TransactionBuilder, TransactionBuilder4844, -}; +use alloy::network::{AnyNetwork, EthereumWallet, TransactionBuilder}; use alloy::primitives::{Address, FixedBytes}; -use alloy::providers::{ - PendingTransactionBuilder, PendingTransactionConfig, Provider, ProviderBuilder, -}; -use alloy::rpc::client::ReqwestClient; -use alloy::rpc::types::{Transaction, TransactionRequest}; -use alloy::transports::http::{Client, Http}; +use alloy::providers::{PendingTransactionConfig, Provider, ProviderBuilder}; +use alloy::rpc::types::TransactionRequest; use futures::StreamExt; use std::collections::HashMap; use std::ops::Deref; @@ -60,12 +53,10 @@ where .network::() .on_http(scenario.rpc_url.to_owned()); let eth_client = Arc::new(ProviderBuilder::new().on_http(scenario.rpc_url.to_owned())); - // TODO: parameterize auth_signer (u get random auth signer until then) - let auth_signer = alloy::signers::local::LocalSigner::random(); let bundle_client = scenario .builder_rpc_url .to_owned() - .map(|url| Arc::new(BundleClient::new(url.into(), auth_signer))); + .map(|url| Arc::new(BundleClient::new(url.into()))); let msg_handler = Arc::new(TxActorHandle::new( 12, scenario.db.clone(), @@ -150,11 +141,9 @@ where .clone() .with_nonce(nonce) .with_max_fee_per_gas(gas_price + (gas_price / 5)) - .with_max_priority_fee_per_gas(gas_price / 5) - .with_max_fee_per_blob_gas(gas_price + (gas_price / 5)) - .with_blob_sidecar(BlobTransactionSidecar::new(vec![], vec![], vec![])) + .with_max_priority_fee_per_gas(gas_price) .with_chain_id(chain_id) - .with_gas_limit(gas_limit); + .with_gas_limit(gas_limit + (gas_limit / 4)); Ok((full_tx, signer)) } @@ -231,7 +220,7 @@ where for req in reqs.iter() { let tx_req = req.tx.to_owned(); let (tx_req, signer) = self - .prepare_tx_req(&tx_req, gas_price + (gas_price / 3), chain_id) + .prepare_tx_req(&tx_req, gas_price, chain_id) .await .map_err(|e| ContenderError::with_err(e, "failed to prepare tx"))?; @@ -324,12 +313,11 @@ where }; if let Some(bundle_client) = bundle_client { // send 12 bundles at a time, targeting each next block - for i in 1..13 { + for i in 1..6 { let mut rpc_bundle = rpc_bundle.clone(); rpc_bundle.block_number = last_block_number + i as u64; - println!("FINNA SEND A BUNDLE!"); let res = bundle_client.send_bundle(rpc_bundle).await; - println!("{:?}", res); + println!("sent bundle {:?}", res); } } diff --git a/src/spammer/mod.rs b/src/spammer/mod.rs index 9f3f3b7..ae2fd01 100644 --- a/src/spammer/mod.rs +++ b/src/spammer/mod.rs @@ -4,10 +4,7 @@ pub mod tx_actor; pub mod util; use crate::generator::{types::AnyProvider, NamedTxRequest}; -use alloy::{ - consensus::TxEnvelope, network::EthereumWallet, providers::PendingTransactionConfig, - rpc::types::TransactionRequest, -}; +use alloy::{consensus::TxEnvelope, providers::PendingTransactionConfig}; use std::{collections::HashMap, sync::Arc}; use tokio::task::JoinHandle; use tx_actor::TxActorHandle; diff --git a/src/spammer/timed.rs b/src/spammer/timed.rs index 3723512..f8af06e 100644 --- a/src/spammer/timed.rs +++ b/src/spammer/timed.rs @@ -1,11 +1,15 @@ use crate::{ - bundle_provider::BundleClient, db::DbOps, generator::{ + db::DbOps, + generator::{ named_txs::ExecutionRequest, seeder::Seeder, templater::Templater, types::{EthProvider, PlanType}, Generator, PlanConfig, - }, spammer::OnTxSent, test_scenario::TestScenario, Result + }, + spammer::OnTxSent, + test_scenario::TestScenario, + Result, }; use alloy::hex::ToHexExt; use alloy::providers::Provider; @@ -23,7 +27,6 @@ where scenario: TestScenario, rpc_client: Arc, callback_handler: Arc, - bundle_client: Option, } impl TimedSpammer @@ -35,16 +38,10 @@ where { pub fn new(scenario: TestScenario, callback_handler: F) -> Self { let rpc_client = ProviderBuilder::new().on_http(scenario.rpc_url.to_owned()); - // TODO: parameterize auth_signer (u get random auth signer until then) - let auth_signer = alloy::signers::local::LocalSigner::random(); - let bundle_client = scenario.builder_rpc_url.to_owned().map(|url| { - BundleClient::new(url.into(), auth_signer) - }); Self { scenario, rpc_client: Arc::new(rpc_client), callback_handler: Arc::new(callback_handler), - bundle_client, } } @@ -91,8 +88,9 @@ where callback_handler.on_tx_sent(res.into_inner(), &req, None, None); vec![maybe_handle] } - ExecutionRequest::Bundle(reqs) => { - todo!(); + ExecutionRequest::Bundle(_) => { + eprintln!("bundles are not supported in timed spammer. Please try the blockwise spammer (--tpb)"); + vec![] } }; From 1ed33862be0bd124e8eaeed02ddb8a39a3b28a7b Mon Sep 17 00:00:00 2001 From: zeroXbrock <2791467+zeroXbrock@users.noreply.github.com> Date: Wed, 6 Nov 2024 14:15:06 -0800 Subject: [PATCH 08/19] cleanup --- cli/testfile.toml | 6 ++-- src/bundle_provider.rs | 1 + src/generator/types.rs | 2 -- src/spammer/blockwise.rs | 1 - testfile/src/lib.rs | 67 ++++++++++++++++++++++------------------ 5 files changed, 41 insertions(+), 36 deletions(-) diff --git a/cli/testfile.toml b/cli/testfile.toml index 51a1063..f94a877 100644 --- a/cli/testfile.toml +++ b/cli/testfile.toml @@ -8,7 +8,7 @@ from = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" # [spam.tx] # to = "{SpamMe}" -# from = "0x70997970C51812dc3A010C7d01b50e0d17dc79C8" +# from = "0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f" # signature = "consumeGas(uint256)" # args = ["150000"] @@ -18,12 +18,12 @@ from = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" [[spam.bundle.tx]] to = "{SpamMe}" -from = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" +from = "0xa0Ee7A142d267C1f36714E4a8F75612F20a79720" signature = "consumeGas(uint256)" args = ["510000"] [[spam.bundle.tx]] to = "{SpamMe}" -from = "0x70997970C51812dc3A010C7d01b50e0d17dc79C8" +from = "0x14dC79964da2C08b23698B3D3cc7Ca32193d9955" signature = "tipCoinbase()" value = "100000000000000000" diff --git a/src/bundle_provider.rs b/src/bundle_provider.rs index f8bdb02..1346254 100644 --- a/src/bundle_provider.rs +++ b/src/bundle_provider.rs @@ -8,6 +8,7 @@ pub struct BundleClient { } #[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] pub struct EthSendBundleResponse { pub bundle_hash: B256, } diff --git a/src/generator/types.rs b/src/generator/types.rs index b06f092..a2eb1be 100644 --- a/src/generator/types.rs +++ b/src/generator/types.rs @@ -15,8 +15,6 @@ pub use crate::generator::named_txs::NamedTxRequest; // -- convenience pub type EthProvider = RootProvider>; pub type AnyProvider = RootProvider, AnyNetwork>; -// TODO: associate this with whatever we come up with using ethers -// pub type BuilderProvider; // -- core types for test scenarios diff --git a/src/spammer/blockwise.rs b/src/spammer/blockwise.rs index 1c21bd0..e119c07 100644 --- a/src/spammer/blockwise.rs +++ b/src/spammer/blockwise.rs @@ -231,7 +231,6 @@ where println!("signed {:?}", tx_envelope.tx_hash()); bundle_txs.push(tx_envelope); } - // TODO: call eth_sendBundle with signed txs ExecutionPayload::SignedTxBundle(bundle_txs, reqs) } ExecutionRequest::Tx(req) => { diff --git a/testfile/src/lib.rs b/testfile/src/lib.rs index 5aab5a8..2ad7684 100644 --- a/testfile/src/lib.rs +++ b/testfile/src/lib.rs @@ -112,7 +112,10 @@ pub mod tests { db::MockDb, generator::{ named_txs::ExecutionRequest, - types::{BundleCallDefinition, CreateDefinition, FunctionCallDefinition, FuzzParam, PlanType, SpamRequest}, + types::{ + BundleCallDefinition, CreateDefinition, FunctionCallDefinition, FuzzParam, + PlanType, SpamRequest, + }, Generator, RandSeed, }, test_scenario::TestScenario, @@ -163,43 +166,50 @@ pub mod tests { } pub fn get_fuzzy_testconfig() -> TestConfig { - let fn_call = |data: &str, from_addr: &str| { - FunctionCallDefinition { - to: "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D".to_owned(), - from: from_addr.to_owned(), - value: None, - signature: "swap(uint256 x, uint256 y, address a, bytes b)".to_owned(), - args: vec![ - "1".to_owned(), - "2".to_owned(), - Address::repeat_byte(0x11).encode_hex(), - data.to_owned(), - ] - .into(), - kind: None, - fuzz: vec![FuzzParam { - param: "x".to_string(), - min: None, - max: None, - }] - .into(), - } + let fn_call = |data: &str, from_addr: &str| FunctionCallDefinition { + to: "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D".to_owned(), + from: from_addr.to_owned(), + value: None, + signature: "swap(uint256 x, uint256 y, address a, bytes b)".to_owned(), + args: vec![ + "1".to_owned(), + "2".to_owned(), + Address::repeat_byte(0x11).encode_hex(), + data.to_owned(), + ] + .into(), + kind: None, + fuzz: vec![FuzzParam { + param: "x".to_string(), + min: None, + max: None, + }] + .into(), }; TestConfig { env: None, create: None, setup: None, spam: vec![ - SpamRequest::Tx(fn_call("0xbeef", "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266")), - SpamRequest::Tx(fn_call("0xea75", "0x70997970C51812dc3A010C7d01b50e0d17dc79C8")), - SpamRequest::Tx(fn_call("0xf00d", "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC")), + SpamRequest::Tx(fn_call( + "0xbeef", + "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266", + )), + SpamRequest::Tx(fn_call( + "0xea75", + "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", + )), + SpamRequest::Tx(fn_call( + "0xf00d", + "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC", + )), SpamRequest::Bundle(BundleCallDefinition { txs: vec![ fn_call("0xbeef", "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"), fn_call("0xea75", "0x70997970C51812dc3A010C7d01b50e0d17dc79C8"), fn_call("0xf00d", "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC"), - ] - }) + ], + }), ] .into(), } @@ -336,9 +346,6 @@ pub mod tests { panic!("expected SpamRequest::Single"); } } - // assert_eq!(req.to, test_file2.spam.unwrap()[0].to); - // assert_eq!(args[0], "1"); - // assert_eq!(args[1], "2"); } _ => { panic!("expected SpamRequest::Single"); From ce9518889c57e28ea600e2a80a99f43f77695896 Mon Sep 17 00:00:00 2001 From: zeroXbrock <2791467+zeroXbrock@users.noreply.github.com> Date: Thu, 7 Nov 2024 16:10:33 -0800 Subject: [PATCH 09/19] pass builder url as flag --- cli/src/commands.rs | 4 ++++ cli/src/main.rs | 11 ++++------- cli/testfile.toml | 2 +- src/spammer/blockwise.rs | 2 ++ 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/cli/src/commands.rs b/cli/src/commands.rs index fc2bd94..ed41996 100644 --- a/cli/src/commands.rs +++ b/cli/src/commands.rs @@ -25,6 +25,10 @@ pub enum ContenderSubcommand { /// The HTTP JSON-RPC URL to spam with requests. rpc_url: String, + /// The builder HTTP JSON-RPC URL to use for spamming (must support `eth_sendBundle`). + #[arg(short, long, long_help = "")] + builder_url: Option, + /// The number of txs to send per second using the timed spammer. This is the default spammer. /// May not be set if `txs_per_block` is set. #[arg(long, long_help = "Number of txs to send per second. Must not be set if --txs-per-block is set.", visible_aliases = &["tps"])] diff --git a/cli/src/main.rs b/cli/src/main.rs index bbcc7d1..ec11724 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -32,10 +32,6 @@ static DB: LazyLock = std::sync::LazyLock::new(|| { SqliteDb::from_file("contender.db").expect("failed to open contender.db") }); -fn rbuilder_url() -> Url { - "http://localhost:3000".parse().expect("invalid url") -} - fn get_signers_with_defaults(private_keys: Option>) -> Vec { if private_keys.is_none() { println!("No private keys provided. Using default private keys."); @@ -91,7 +87,7 @@ async fn main() -> Result<(), Box> { testconfig.to_owned(), Arc::new(DB.clone()), url, - Some(rbuilder_url()), // TODO: replace this with user-provided url + None, RandSeed::new(), &signers, ); @@ -103,6 +99,7 @@ async fn main() -> Result<(), Box> { ContenderSubcommand::Spam { testfile, rpc_url, + builder_url, txs_per_block, txs_per_second, duration, @@ -154,7 +151,7 @@ async fn main() -> Result<(), Box> { testconfig, DB.clone().into(), url, - Some(rbuilder_url()), + builder_url.map(|url| Url::parse(&url).expect("Invalid builder URL")), rand_seed, &signers, ); @@ -186,7 +183,7 @@ async fn main() -> Result<(), Box> { testconfig, DB.clone().into(), url, - Some(rbuilder_url()), + None, rand_seed, &signers, ); diff --git a/cli/testfile.toml b/cli/testfile.toml index f94a877..07d8d54 100644 --- a/cli/testfile.toml +++ b/cli/testfile.toml @@ -1,5 +1,5 @@ [[create]] -bytecode = "0x6080604052348015600f57600080fd5b5061055d8061001f6000396000f3fe60806040526004361061004a5760003560e01c806369f86ec81461004f5780639402c00414610066578063a329e8de14610086578063c5eeaf17146100a6578063fb0e722b146100ae575b600080fd5b34801561005b57600080fd5b506100646100d9565b005b34801561007257600080fd5b5061006461008136600461020e565b6100e4565b34801561009257600080fd5b506100646100a13660046102c7565b610119565b61006461013b565b3480156100ba57600080fd5b506100c361016a565b6040516100d09190610304565b60405180910390f35b5b60325a116100da57565b6000816040516020016100f8929190610371565b604051602081830303815290604052600090816101159190610446565b5050565b60005b61012760db83610505565b81101561011557600081905560010161011c565b60405141903480156108fc02916000818181858888f19350505050158015610167573d6000803e3d6000fd5b50565b6000805461017790610337565b80601f01602080910402602001604051908101604052809291908181526020018280546101a390610337565b80156101f05780601f106101c5576101008083540402835291602001916101f0565b820191906000526020600020905b8154815290600101906020018083116101d357829003601f168201915b505050505081565b634e487b7160e01b600052604160045260246000fd5b60006020828403121561022057600080fd5b813567ffffffffffffffff81111561023757600080fd5b8201601f8101841361024857600080fd5b803567ffffffffffffffff811115610262576102626101f8565b604051601f8201601f19908116603f0116810167ffffffffffffffff81118282101715610291576102916101f8565b6040528181528282016020018610156102a957600080fd5b81602084016020830137600091810160200191909152949350505050565b6000602082840312156102d957600080fd5b5035919050565b60005b838110156102fb5781810151838201526020016102e3565b50506000910152565b60208152600082518060208401526103238160408501602087016102e0565b601f01601f19169190910160400192915050565b600181811c9082168061034b57607f821691505b60208210810361036b57634e487b7160e01b600052602260045260246000fd5b50919050565b600080845461037f81610337565b60018216801561039657600181146103ab576103db565b60ff19831686528115158202860193506103db565b87600052602060002060005b838110156103d3578154888201526001909101906020016103b7565b505081860193505b50505083516103ee8183602088016102e0565b01949350505050565b601f82111561044157806000526020600020601f840160051c8101602085101561041e5750805b601f840160051c820191505b8181101561043e576000815560010161042a565b50505b505050565b815167ffffffffffffffff811115610460576104606101f8565b6104748161046e8454610337565b846103f7565b6020601f8211600181146104a857600083156104905750848201515b600019600385901b1c1916600184901b17845561043e565b600084815260208120601f198516915b828110156104d857878501518255602094850194600190920191016104b8565b50848210156104f65786840151600019600387901b60f8161c191681555b50505050600190811b01905550565b60008261052257634e487b7160e01b600052601260045260246000fd5b50049056fea2646970667358221220e6a2a27582535e97477f8641207332fe4acf27138934be685090cb3acd25033164736f6c634300081b0033" +bytecode = "0x6080604052348015600f57600080fd5b506105d98061001f6000396000f3fe60806040526004361061004a5760003560e01c806369f86ec81461004f5780639402c00414610066578063a329e8de14610086578063c5eeaf17146100a6578063fb0e722b146100ae575b600080fd5b34801561005b57600080fd5b506100646100d9565b005b34801561007257600080fd5b5061006461008136600461028b565b6100e4565b34801561009257600080fd5b506100646100a1366004610344565b610119565b6100646101b8565b3480156100ba57600080fd5b506100c36101e7565b6040516100d09190610381565b60405180910390f35b5b60325a116100da57565b6000816040516020016100f89291906103ee565b6040516020818303038152906040526000908161011591906104c2565b5050565b6000610127610bb883610581565b905060005b818110156101b357604080516000808252602082018084527f77dcd57beb1f0f2e28ea0f01df187f9912d3a78de5e0bd8abf37307a7e9b75969052918101829052606081018290526080810182905260019060a0016020604051602081039080840390855afa1580156101a3573d6000803e3d6000fd5b50506001909201915061012c9050565b505050565b60405141903480156108fc02916000818181858888f193505050501580156101e4573d6000803e3d6000fd5b50565b600080546101f4906103b4565b80601f0160208091040260200160405190810160405280929190818152602001828054610220906103b4565b801561026d5780601f106102425761010080835404028352916020019161026d565b820191906000526020600020905b81548152906001019060200180831161025057829003601f168201915b505050505081565b634e487b7160e01b600052604160045260246000fd5b60006020828403121561029d57600080fd5b813567ffffffffffffffff8111156102b457600080fd5b8201601f810184136102c557600080fd5b803567ffffffffffffffff8111156102df576102df610275565b604051601f8201601f19908116603f0116810167ffffffffffffffff8111828210171561030e5761030e610275565b60405281815282820160200186101561032657600080fd5b81602084016020830137600091810160200191909152949350505050565b60006020828403121561035657600080fd5b5035919050565b60005b83811015610378578181015183820152602001610360565b50506000910152565b60208152600082518060208401526103a081604085016020870161035d565b601f01601f19169190910160400192915050565b600181811c908216806103c857607f821691505b6020821081036103e857634e487b7160e01b600052602260045260246000fd5b50919050565b60008084546103fc816103b4565b600182168015610413576001811461042857610458565b60ff1983168652811515820286019350610458565b87600052602060002060005b8381101561045057815488820152600190910190602001610434565b505081860193505b505050835161046b81836020880161035d565b01949350505050565b601f8211156101b357806000526020600020601f840160051c8101602085101561049b5750805b601f840160051c820191505b818110156104bb57600081556001016104a7565b5050505050565b815167ffffffffffffffff8111156104dc576104dc610275565b6104f0816104ea84546103b4565b84610474565b6020601f821160018114610524576000831561050c5750848201515b600019600385901b1c1916600184901b1784556104bb565b600084815260208120601f198516915b828110156105545787850151825560209485019460019092019101610534565b50848210156105725786840151600019600387901b60f8161c191681555b50505050600190811b01905550565b60008261059e57634e487b7160e01b600052601260045260246000fd5b50049056fea264697066735822122017dbdf9f8f4e3b3118550d844730b189060b0f0f80002772fa9094ff4a416c5364736f6c634300081b0033" name = "SpamMe" from = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" diff --git a/src/spammer/blockwise.rs b/src/spammer/blockwise.rs index e119c07..2dfea0c 100644 --- a/src/spammer/blockwise.rs +++ b/src/spammer/blockwise.rs @@ -318,6 +318,8 @@ where let res = bundle_client.send_bundle(rpc_bundle).await; println!("sent bundle {:?}", res); } + } else { + panic!("no bundle client provided. Please add the `--builder-url` flag"); } let mut tx_handles = vec![]; From ddf0cd152416fe3a993b85badc03ef3e8a04eb22 Mon Sep 17 00:00:00 2001 From: zeroXbrock <2791467+zeroXbrock@users.noreply.github.com> Date: Thu, 7 Nov 2024 16:16:23 -0800 Subject: [PATCH 10/19] better bundle block assignments --- src/spammer/blockwise.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/spammer/blockwise.rs b/src/spammer/blockwise.rs index 2dfea0c..7d0b10c 100644 --- a/src/spammer/blockwise.rs +++ b/src/spammer/blockwise.rs @@ -311,8 +311,8 @@ where replacement_uuid: None, }; if let Some(bundle_client) = bundle_client { - // send 12 bundles at a time, targeting each next block - for i in 1..6 { + // send `num_blocks` bundles at a time, targeting each successive block + for i in 1..num_blocks { let mut rpc_bundle = rpc_bundle.clone(); rpc_bundle.block_number = last_block_number + i as u64; let res = bundle_client.send_bundle(rpc_bundle).await; From fa89b384eced4cc8c676f0697f6ea604e3bd7f25 Mon Sep 17 00:00:00 2001 From: zeroXbrock <2791467+zeroXbrock@users.noreply.github.com> Date: Fri, 8 Nov 2024 15:42:22 -0800 Subject: [PATCH 11/19] dupe target contract & send both bundles & mempool txs --- cli/testfile.toml | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/cli/testfile.toml b/cli/testfile.toml index 07d8d54..a96fd7c 100644 --- a/cli/testfile.toml +++ b/cli/testfile.toml @@ -3,27 +3,32 @@ bytecode = "0x6080604052348015600f57600080fd5b506105d98061001f6000396000f3fe6080 name = "SpamMe" from = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" +[[create]] +bytecode = "0x6080604052348015600f57600080fd5b506105d98061001f6000396000f3fe60806040526004361061004a5760003560e01c806369f86ec81461004f5780639402c00414610066578063a329e8de14610086578063c5eeaf17146100a6578063fb0e722b146100ae575b600080fd5b34801561005b57600080fd5b506100646100d9565b005b34801561007257600080fd5b5061006461008136600461028b565b6100e4565b34801561009257600080fd5b506100646100a1366004610344565b610119565b6100646101b8565b3480156100ba57600080fd5b506100c36101e7565b6040516100d09190610381565b60405180910390f35b5b60325a116100da57565b6000816040516020016100f89291906103ee565b6040516020818303038152906040526000908161011591906104c2565b5050565b6000610127610bb883610581565b905060005b818110156101b357604080516000808252602082018084527f77dcd57beb1f0f2e28ea0f01df187f9912d3a78de5e0bd8abf37307a7e9b75969052918101829052606081018290526080810182905260019060a0016020604051602081039080840390855afa1580156101a3573d6000803e3d6000fd5b50506001909201915061012c9050565b505050565b60405141903480156108fc02916000818181858888f193505050501580156101e4573d6000803e3d6000fd5b50565b600080546101f4906103b4565b80601f0160208091040260200160405190810160405280929190818152602001828054610220906103b4565b801561026d5780601f106102425761010080835404028352916020019161026d565b820191906000526020600020905b81548152906001019060200180831161025057829003601f168201915b505050505081565b634e487b7160e01b600052604160045260246000fd5b60006020828403121561029d57600080fd5b813567ffffffffffffffff8111156102b457600080fd5b8201601f810184136102c557600080fd5b803567ffffffffffffffff8111156102df576102df610275565b604051601f8201601f19908116603f0116810167ffffffffffffffff8111828210171561030e5761030e610275565b60405281815282820160200186101561032657600080fd5b81602084016020830137600091810160200191909152949350505050565b60006020828403121561035657600080fd5b5035919050565b60005b83811015610378578181015183820152602001610360565b50506000910152565b60208152600082518060208401526103a081604085016020870161035d565b601f01601f19169190910160400192915050565b600181811c908216806103c857607f821691505b6020821081036103e857634e487b7160e01b600052602260045260246000fd5b50919050565b60008084546103fc816103b4565b600182168015610413576001811461042857610458565b60ff1983168652811515820286019350610458565b87600052602060002060005b8381101561045057815488820152600190910190602001610434565b505081860193505b505050835161046b81836020880161035d565b01949350505050565b601f8211156101b357806000526020600020601f840160051c8101602085101561049b5750805b601f840160051c820191505b818110156104bb57600081556001016104a7565b5050505050565b815167ffffffffffffffff8111156104dc576104dc610275565b6104f0816104ea84546103b4565b84610474565b6020601f821160018114610524576000831561050c5750848201515b600019600385901b1c1916600184901b1784556104bb565b600084815260208120601f198516915b828110156105545787850151825560209485019460019092019101610534565b50848210156105725786840151600019600387901b60f8161c191681555b50505050600190811b01905550565b60008261059e57634e487b7160e01b600052601260045260246000fd5b50049056fea264697066735822122017dbdf9f8f4e3b3118550d844730b189060b0f0f80002772fa9094ff4a416c5364736f6c634300081b0033" +name = "SpamMe2" +from = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" + # spam single tx -# [[spam]] +[[spam]] -# [spam.tx] -# to = "{SpamMe}" -# from = "0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f" -# signature = "consumeGas(uint256)" -# args = ["150000"] +[spam.tx] +to = "{SpamMe}" +from = "0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f" +signature = "consumeGas(uint256)" +args = ["150000"] # spam bundle [[spam]] [[spam.bundle.tx]] -to = "{SpamMe}" +to = "{SpamMe2}" from = "0xa0Ee7A142d267C1f36714E4a8F75612F20a79720" signature = "consumeGas(uint256)" args = ["510000"] [[spam.bundle.tx]] -to = "{SpamMe}" +to = "{SpamMe2}" from = "0x14dC79964da2C08b23698B3D3cc7Ca32193d9955" signature = "tipCoinbase()" value = "100000000000000000" From aa124cc30bf733674f965dbeeb9faa973d30e7f6 Mon Sep 17 00:00:00 2001 From: zeroXbrock <2791467+zeroXbrock@users.noreply.github.com> Date: Wed, 13 Nov 2024 12:40:28 -0800 Subject: [PATCH 12/19] use same EOA for both txs in each bundle * unlocks many bundles per block in rbuilder --- cli/testfile.toml | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/cli/testfile.toml b/cli/testfile.toml index a96fd7c..4b02079 100644 --- a/cli/testfile.toml +++ b/cli/testfile.toml @@ -1,34 +1,46 @@ [[create]] -bytecode = "0x6080604052348015600f57600080fd5b506105d98061001f6000396000f3fe60806040526004361061004a5760003560e01c806369f86ec81461004f5780639402c00414610066578063a329e8de14610086578063c5eeaf17146100a6578063fb0e722b146100ae575b600080fd5b34801561005b57600080fd5b506100646100d9565b005b34801561007257600080fd5b5061006461008136600461028b565b6100e4565b34801561009257600080fd5b506100646100a1366004610344565b610119565b6100646101b8565b3480156100ba57600080fd5b506100c36101e7565b6040516100d09190610381565b60405180910390f35b5b60325a116100da57565b6000816040516020016100f89291906103ee565b6040516020818303038152906040526000908161011591906104c2565b5050565b6000610127610bb883610581565b905060005b818110156101b357604080516000808252602082018084527f77dcd57beb1f0f2e28ea0f01df187f9912d3a78de5e0bd8abf37307a7e9b75969052918101829052606081018290526080810182905260019060a0016020604051602081039080840390855afa1580156101a3573d6000803e3d6000fd5b50506001909201915061012c9050565b505050565b60405141903480156108fc02916000818181858888f193505050501580156101e4573d6000803e3d6000fd5b50565b600080546101f4906103b4565b80601f0160208091040260200160405190810160405280929190818152602001828054610220906103b4565b801561026d5780601f106102425761010080835404028352916020019161026d565b820191906000526020600020905b81548152906001019060200180831161025057829003601f168201915b505050505081565b634e487b7160e01b600052604160045260246000fd5b60006020828403121561029d57600080fd5b813567ffffffffffffffff8111156102b457600080fd5b8201601f810184136102c557600080fd5b803567ffffffffffffffff8111156102df576102df610275565b604051601f8201601f19908116603f0116810167ffffffffffffffff8111828210171561030e5761030e610275565b60405281815282820160200186101561032657600080fd5b81602084016020830137600091810160200191909152949350505050565b60006020828403121561035657600080fd5b5035919050565b60005b83811015610378578181015183820152602001610360565b50506000910152565b60208152600082518060208401526103a081604085016020870161035d565b601f01601f19169190910160400192915050565b600181811c908216806103c857607f821691505b6020821081036103e857634e487b7160e01b600052602260045260246000fd5b50919050565b60008084546103fc816103b4565b600182168015610413576001811461042857610458565b60ff1983168652811515820286019350610458565b87600052602060002060005b8381101561045057815488820152600190910190602001610434565b505081860193505b505050835161046b81836020880161035d565b01949350505050565b601f8211156101b357806000526020600020601f840160051c8101602085101561049b5750805b601f840160051c820191505b818110156104bb57600081556001016104a7565b5050505050565b815167ffffffffffffffff8111156104dc576104dc610275565b6104f0816104ea84546103b4565b84610474565b6020601f821160018114610524576000831561050c5750848201515b600019600385901b1c1916600184901b1784556104bb565b600084815260208120601f198516915b828110156105545787850151825560209485019460019092019101610534565b50848210156105725786840151600019600387901b60f8161c191681555b50505050600190811b01905550565b60008261059e57634e487b7160e01b600052601260045260246000fd5b50049056fea264697066735822122017dbdf9f8f4e3b3118550d844730b189060b0f0f80002772fa9094ff4a416c5364736f6c634300081b0033" +bytecode = "0x6080604052348015600f57600080fd5b506105668061001f6000396000f3fe60806040526004361061004a5760003560e01c806369f86ec81461004f5780639402c00414610066578063a329e8de14610086578063c5eeaf17146100a6578063fb0e722b146100ae575b600080fd5b34801561005b57600080fd5b506100646100d9565b005b34801561007257600080fd5b50610064610081366004610218565b6100e4565b34801561009257600080fd5b506100646100a13660046102d1565b610119565b610064610145565b3480156100ba57600080fd5b506100c3610174565b6040516100d0919061030e565b60405180910390f35b5b60325a116100da57565b6000816040516020016100f892919061037b565b60405160208183030381529060405260009081610115919061044f565b5050565b600061012660d98361050e565b905060005b8181101561014057600160008190550161012b565b505050565b60405141903480156108fc02916000818181858888f19350505050158015610171573d6000803e3d6000fd5b50565b6000805461018190610341565b80601f01602080910402602001604051908101604052809291908181526020018280546101ad90610341565b80156101fa5780601f106101cf576101008083540402835291602001916101fa565b820191906000526020600020905b8154815290600101906020018083116101dd57829003601f168201915b505050505081565b634e487b7160e01b600052604160045260246000fd5b60006020828403121561022a57600080fd5b813567ffffffffffffffff81111561024157600080fd5b8201601f8101841361025257600080fd5b803567ffffffffffffffff81111561026c5761026c610202565b604051601f8201601f19908116603f0116810167ffffffffffffffff8111828210171561029b5761029b610202565b6040528181528282016020018610156102b357600080fd5b81602084016020830137600091810160200191909152949350505050565b6000602082840312156102e357600080fd5b5035919050565b60005b838110156103055781810151838201526020016102ed565b50506000910152565b602081526000825180602084015261032d8160408501602087016102ea565b601f01601f19169190910160400192915050565b600181811c9082168061035557607f821691505b60208210810361037557634e487b7160e01b600052602260045260246000fd5b50919050565b600080845461038981610341565b6001821680156103a057600181146103b5576103e5565b60ff19831686528115158202860193506103e5565b87600052602060002060005b838110156103dd578154888201526001909101906020016103c1565b505081860193505b50505083516103f88183602088016102ea565b01949350505050565b601f82111561014057806000526020600020601f840160051c810160208510156104285750805b601f840160051c820191505b818110156104485760008155600101610434565b5050505050565b815167ffffffffffffffff81111561046957610469610202565b61047d816104778454610341565b84610401565b6020601f8211600181146104b157600083156104995750848201515b600019600385901b1c1916600184901b178455610448565b600084815260208120601f198516915b828110156104e157878501518255602094850194600190920191016104c1565b50848210156104ff5786840151600019600387901b60f8161c191681555b50505050600190811b01905550565b60008261052b57634e487b7160e01b600052601260045260246000fd5b50049056fea264697066735822122043ea7522db98264cdc5157a0f2d3f9fc75e28c6078f917dfe9a946bf9b21af7f64736f6c634300081b0033" name = "SpamMe" from = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" -[[create]] -bytecode = "0x6080604052348015600f57600080fd5b506105d98061001f6000396000f3fe60806040526004361061004a5760003560e01c806369f86ec81461004f5780639402c00414610066578063a329e8de14610086578063c5eeaf17146100a6578063fb0e722b146100ae575b600080fd5b34801561005b57600080fd5b506100646100d9565b005b34801561007257600080fd5b5061006461008136600461028b565b6100e4565b34801561009257600080fd5b506100646100a1366004610344565b610119565b6100646101b8565b3480156100ba57600080fd5b506100c36101e7565b6040516100d09190610381565b60405180910390f35b5b60325a116100da57565b6000816040516020016100f89291906103ee565b6040516020818303038152906040526000908161011591906104c2565b5050565b6000610127610bb883610581565b905060005b818110156101b357604080516000808252602082018084527f77dcd57beb1f0f2e28ea0f01df187f9912d3a78de5e0bd8abf37307a7e9b75969052918101829052606081018290526080810182905260019060a0016020604051602081039080840390855afa1580156101a3573d6000803e3d6000fd5b50506001909201915061012c9050565b505050565b60405141903480156108fc02916000818181858888f193505050501580156101e4573d6000803e3d6000fd5b50565b600080546101f4906103b4565b80601f0160208091040260200160405190810160405280929190818152602001828054610220906103b4565b801561026d5780601f106102425761010080835404028352916020019161026d565b820191906000526020600020905b81548152906001019060200180831161025057829003601f168201915b505050505081565b634e487b7160e01b600052604160045260246000fd5b60006020828403121561029d57600080fd5b813567ffffffffffffffff8111156102b457600080fd5b8201601f810184136102c557600080fd5b803567ffffffffffffffff8111156102df576102df610275565b604051601f8201601f19908116603f0116810167ffffffffffffffff8111828210171561030e5761030e610275565b60405281815282820160200186101561032657600080fd5b81602084016020830137600091810160200191909152949350505050565b60006020828403121561035657600080fd5b5035919050565b60005b83811015610378578181015183820152602001610360565b50506000910152565b60208152600082518060208401526103a081604085016020870161035d565b601f01601f19169190910160400192915050565b600181811c908216806103c857607f821691505b6020821081036103e857634e487b7160e01b600052602260045260246000fd5b50919050565b60008084546103fc816103b4565b600182168015610413576001811461042857610458565b60ff1983168652811515820286019350610458565b87600052602060002060005b8381101561045057815488820152600190910190602001610434565b505081860193505b505050835161046b81836020880161035d565b01949350505050565b601f8211156101b357806000526020600020601f840160051c8101602085101561049b5750805b601f840160051c820191505b818110156104bb57600081556001016104a7565b5050505050565b815167ffffffffffffffff8111156104dc576104dc610275565b6104f0816104ea84546103b4565b84610474565b6020601f821160018114610524576000831561050c5750848201515b600019600385901b1c1916600184901b1784556104bb565b600084815260208120601f198516915b828110156105545787850151825560209485019460019092019101610534565b50848210156105725786840151600019600387901b60f8161c191681555b50505050600190811b01905550565b60008261059e57634e487b7160e01b600052601260045260246000fd5b50049056fea264697066735822122017dbdf9f8f4e3b3118550d844730b189060b0f0f80002772fa9094ff4a416c5364736f6c634300081b0033" -name = "SpamMe2" -from = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" # spam single tx +# [[spam]] + +# [spam.tx] +# to = "{SpamMe}" +# from = "0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f" +# signature = "consumeGas(uint256)" +# args = ["150000"] + + +# spam bundle [[spam]] -[spam.tx] +[[spam.bundle.tx]] to = "{SpamMe}" -from = "0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f" +from = "0x90F79bf6EB2c4f870365E785982E1f101E93b906" signature = "consumeGas(uint256)" -args = ["150000"] +args = ["510000"] -# spam bundle +[[spam.bundle.tx]] +to = "{SpamMe}" +from = "0x90F79bf6EB2c4f870365E785982E1f101E93b906" +signature = "tipCoinbase()" +value = "10000000000000000" + [[spam]] [[spam.bundle.tx]] -to = "{SpamMe2}" +to = "{SpamMe}" from = "0xa0Ee7A142d267C1f36714E4a8F75612F20a79720" signature = "consumeGas(uint256)" args = ["510000"] + [[spam.bundle.tx]] -to = "{SpamMe2}" -from = "0x14dC79964da2C08b23698B3D3cc7Ca32193d9955" +to = "{SpamMe}" +from = "0xa0Ee7A142d267C1f36714E4a8F75612F20a79720" signature = "tipCoinbase()" -value = "100000000000000000" +value = "10000000000000000" \ No newline at end of file From 7d379f0575c5de50bd5461a044afeb7169aa66d5 Mon Sep 17 00:00:00 2001 From: zeroXbrock <2791467+zeroXbrock@users.noreply.github.com> Date: Wed, 13 Nov 2024 13:35:25 -0800 Subject: [PATCH 13/19] target an extra 5 blocks when sending bundles --- src/spammer/blockwise.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/spammer/blockwise.rs b/src/spammer/blockwise.rs index 7d0b10c..e31271e 100644 --- a/src/spammer/blockwise.rs +++ b/src/spammer/blockwise.rs @@ -24,6 +24,9 @@ use tokio::task; use super::tx_actor::TxActorHandle; use super::OnTxSent; +/// Defines the number of blocks to target with a single bundle. +const BUNDLE_BLOCK_TOLERANCE: usize = 5; + pub struct BlockwiseSpammer where D: DbOps + Send + Sync + 'static, @@ -312,7 +315,7 @@ where }; if let Some(bundle_client) = bundle_client { // send `num_blocks` bundles at a time, targeting each successive block - for i in 1..num_blocks { + for i in 1..(num_blocks + BUNDLE_BLOCK_TOLERANCE) { let mut rpc_bundle = rpc_bundle.clone(); rpc_bundle.block_number = last_block_number + i as u64; let res = bundle_client.send_bundle(rpc_bundle).await; From 95e998641ac5056c585b05d44eae4675c314e4be Mon Sep 17 00:00:00 2001 From: zeroXbrock <2791467+zeroXbrock@users.noreply.github.com> Date: Wed, 13 Nov 2024 13:40:30 -0800 Subject: [PATCH 14/19] clean up core deps --- Cargo.lock | 197 +---------------------------------------------------- Cargo.toml | 5 +- 2 files changed, 4 insertions(+), 198 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f5dc59a..808e4f6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1599,16 +1599,6 @@ version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" -[[package]] -name = "futures-timer" -version = "3.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" -dependencies = [ - "gloo-timers", - "send_wrapper 0.4.0", -] - [[package]] name = "futures-util" version = "0.3.31" @@ -1667,52 +1657,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" -[[package]] -name = "gloo-net" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06f627b1a58ca3d42b45d6104bf1e1a03799df472df00988b6ba21accc10580" -dependencies = [ - "futures-channel", - "futures-core", - "futures-sink", - "gloo-utils", - "http", - "js-sys", - "pin-project", - "serde", - "serde_json", - "thiserror", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "gloo-timers" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" -dependencies = [ - "futures-channel", - "futures-core", - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "gloo-utils" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b5555354113b18c547c1d3a98fbf7fb32a9ff4f6fa112ce823a21641a0ba3aa" -dependencies = [ - "js-sys", - "serde", - "serde_json", - "wasm-bindgen", - "web-sys", -] - [[package]] name = "group" version = "0.13.0" @@ -1849,12 +1793,6 @@ version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" - [[package]] name = "hyper" version = "1.4.1" @@ -1868,7 +1806,6 @@ dependencies = [ "http", "http-body", "httparse", - "httpdate", "itoa", "pin-project-lite", "smallvec", @@ -2063,41 +2000,9 @@ version = "0.24.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5c71d8c1a731cc4227c2f698d377e7848ca12c8a48866fc5e6951c43a4db843" dependencies = [ - "jsonrpsee-client-transport", "jsonrpsee-core", "jsonrpsee-http-client", - "jsonrpsee-proc-macros", - "jsonrpsee-server", "jsonrpsee-types", - "jsonrpsee-wasm-client", - "jsonrpsee-ws-client", - "tokio", - "tracing", -] - -[[package]] -name = "jsonrpsee-client-transport" -version = "0.24.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "548125b159ba1314104f5bb5f38519e03a41862786aa3925cf349aae9cdd546e" -dependencies = [ - "base64", - "futures-channel", - "futures-util", - "gloo-net", - "http", - "jsonrpsee-core", - "pin-project", - "rustls", - "rustls-pki-types", - "rustls-platform-verifier", - "soketto", - "thiserror", - "tokio", - "tokio-rustls", - "tokio-util", - "tracing", - "url", ] [[package]] @@ -2108,23 +2013,16 @@ checksum = "f2882f6f8acb9fdaec7cefc4fd607119a9bd709831df7d7672a1d3b644628280" dependencies = [ "async-trait", "bytes", - "futures-timer", "futures-util", "http", "http-body", "http-body-util", "jsonrpsee-types", - "parking_lot", - "pin-project", - "rand", - "rustc-hash", "serde", "serde_json", "thiserror", "tokio", - "tokio-stream", "tracing", - "wasm-bindgen-futures", ] [[package]] @@ -2152,46 +2050,6 @@ dependencies = [ "url", ] -[[package]] -name = "jsonrpsee-proc-macros" -version = "0.24.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06c01ae0007548e73412c08e2285ffe5d723195bf268bce67b1b77c3bb2a14d" -dependencies = [ - "heck", - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.86", -] - -[[package]] -name = "jsonrpsee-server" -version = "0.24.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82ad8ddc14be1d4290cd68046e7d1d37acd408efed6d3ca08aefcc3ad6da069c" -dependencies = [ - "futures-util", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-util", - "jsonrpsee-core", - "jsonrpsee-types", - "pin-project", - "route-recognizer", - "serde", - "serde_json", - "soketto", - "thiserror", - "tokio", - "tokio-stream", - "tokio-util", - "tower 0.4.13", - "tracing", -] - [[package]] name = "jsonrpsee-types" version = "0.24.7" @@ -2204,30 +2062,6 @@ dependencies = [ "thiserror", ] -[[package]] -name = "jsonrpsee-wasm-client" -version = "0.24.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a01cd500915d24ab28ca17527e23901ef1be6d659a2322451e1045532516c25" -dependencies = [ - "jsonrpsee-client-transport", - "jsonrpsee-core", - "jsonrpsee-types", -] - -[[package]] -name = "jsonrpsee-ws-client" -version = "0.24.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fe322e0896d0955a3ebdd5bf813571c53fea29edd713bc315b76620b327e86d" -dependencies = [ - "http", - "jsonrpsee-client-transport", - "jsonrpsee-core", - "jsonrpsee-types", - "url", -] - [[package]] name = "k256" version = "0.13.3" @@ -2875,12 +2709,6 @@ dependencies = [ "rustc-hex", ] -[[package]] -name = "route-recognizer" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afab94fb28594581f62d981211a9a4d53cc8130bbcbbb89a0440d9b8e81a7746" - [[package]] name = "ruint" version = "1.12.3" @@ -3175,12 +3003,6 @@ dependencies = [ "pest", ] -[[package]] -name = "send_wrapper" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f638d531eccd6e23b980caf34876660d38e265409d8e99b397ab71eb3612fad0" - [[package]] name = "send_wrapper" version = "0.6.0" @@ -3323,22 +3145,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "soketto" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37468c595637c10857701c990f93a40ce0e357cedb0953d1c26c8d8027f9bb53" -dependencies = [ - "base64", - "bytes", - "futures", - "http", - "httparse", - "log", - "rand", - "sha1", -] - [[package]] name = "spin" version = "0.9.8" @@ -3600,7 +3406,6 @@ checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" dependencies = [ "bytes", "futures-core", - "futures-io", "futures-sink", "pin-project-lite", "tokio", @@ -4144,7 +3949,7 @@ dependencies = [ "log", "pharos", "rustc_version 0.4.1", - "send_wrapper 0.6.0", + "send_wrapper", "thiserror", "wasm-bindgen", "wasm-bindgen-futures", diff --git a/Cargo.toml b/Cargo.toml index 811968a..b87d6e8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,13 +10,13 @@ path = "src/lib.rs" [dependencies] alloy = { workspace = true, features = ["full", "node-bindings", "rpc-types-mev"] } -eyre = "0.6.12" +eyre = { workspace = true } rand = "0.8.5" serde = { workspace = true, features = ["derive"] } futures = "0.3.30" async-trait = "0.1.82" tokio = { workspace = true } -jsonrpsee = { version = "0.24", features = ["full", "server-core"] } +jsonrpsee = { version = "0.24", features = ["http-client", "client-core"] } alloy-serde = "0.5.4" serde_json = "1.0.132" @@ -24,6 +24,7 @@ serde_json = "1.0.132" members = ["cli", "sqlite_db", "testfile"] [workspace.dependencies] +eyre = "0.6.12" tokio = { version = "1.40.0" } alloy = { version = "0.3.6" } serde = "1.0.209" From 8ff7881f511f213752f6028ca6acca220dbc60d4 Mon Sep 17 00:00:00 2001 From: zeroXbrock <2791467+zeroXbrock@users.noreply.github.com> Date: Wed, 13 Nov 2024 15:01:56 -0800 Subject: [PATCH 15/19] add working mempool txs w/ bundles in testfile.toml --- cli/testfile.toml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cli/testfile.toml b/cli/testfile.toml index 4b02079..c2cb664 100644 --- a/cli/testfile.toml +++ b/cli/testfile.toml @@ -5,13 +5,13 @@ from = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" # spam single tx -# [[spam]] +[[spam]] -# [spam.tx] -# to = "{SpamMe}" -# from = "0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f" -# signature = "consumeGas(uint256)" -# args = ["150000"] +[spam.tx] +to = "0x90F79bf6EB2c4f870365E785982E1f101E93b906" +from = "0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f" +signature = "transfer()" +value = "100000000000000" # spam bundle From ff129531f1221123baa4e364f9d275e658fa9152 Mon Sep 17 00:00:00 2001 From: zeroXbrock <2791467+zeroXbrock@users.noreply.github.com> Date: Wed, 13 Nov 2024 16:24:45 -0800 Subject: [PATCH 16/19] add alt sendBundle, improve sendBundle log --- src/bundle_provider.rs | 17 +++++++++++++++++ src/spammer/blockwise.rs | 7 +++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/bundle_provider.rs b/src/bundle_provider.rs index 1346254..77512d4 100644 --- a/src/bundle_provider.rs +++ b/src/bundle_provider.rs @@ -78,3 +78,20 @@ pub struct EthSendBundle { )] pub replacement_uuid: Option, } + +impl EthSendBundle { + pub fn new_basic(txs: Vec, block_number: u64) -> Self { + Self { + txs, + block_number, + min_timestamp: None, + max_timestamp: None, + reverting_tx_hashes: Vec::new(), + replacement_uuid: None, + } + } + + pub async fn send_to_builder(&self, client: &BundleClient) -> Result<(), String> { + client.send_bundle(self.clone()).await + } +} diff --git a/src/spammer/blockwise.rs b/src/spammer/blockwise.rs index e31271e..1f93b25 100644 --- a/src/spammer/blockwise.rs +++ b/src/spammer/blockwise.rs @@ -314,12 +314,15 @@ where replacement_uuid: None, }; if let Some(bundle_client) = bundle_client { + println!("spamming bundle: {:?}", rpc_bundle); // send `num_blocks` bundles at a time, targeting each successive block for i in 1..(num_blocks + BUNDLE_BLOCK_TOLERANCE) { let mut rpc_bundle = rpc_bundle.clone(); rpc_bundle.block_number = last_block_number + i as u64; - let res = bundle_client.send_bundle(rpc_bundle).await; - println!("sent bundle {:?}", res); + let res = rpc_bundle.send_to_builder(&bundle_client).await; + if let Err(e) = res { + eprintln!("failed to send bundle: {:?}", e); + } } } else { panic!("no bundle client provided. Please add the `--builder-url` flag"); From 30148a23276361a106b66f806519a8bc1910913d Mon Sep 17 00:00:00 2001 From: zeroXbrock <2791467+zeroXbrock@users.noreply.github.com> Date: Wed, 13 Nov 2024 16:30:33 -0800 Subject: [PATCH 17/19] panic if bundle detected when builder-url not provided --- src/spammer/blockwise.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/spammer/blockwise.rs b/src/spammer/blockwise.rs index 1f93b25..3db35e1 100644 --- a/src/spammer/blockwise.rs +++ b/src/spammer/blockwise.rs @@ -218,6 +218,13 @@ where // prepare tx/bundle with nonce, gas price, signatures, etc let payload = match tx { ExecutionRequest::Bundle(reqs) => { + if self.bundle_client.is_none() { + return Err(ContenderError::SpamError( + "Bundle client not found. Add the `--builder-url` flag to send bundles.", + None, + )); + } + // prepare each tx in the bundle (increment nonce, set gas price, etc) let mut bundle_txs = vec![]; for req in reqs.iter() { From 4467d34f7629abff9d8018af2fd2b409c710add9 Mon Sep 17 00:00:00 2001 From: zeroXbrock <2791467+zeroXbrock@users.noreply.github.com> Date: Wed, 13 Nov 2024 16:31:19 -0800 Subject: [PATCH 18/19] remove unnecessary log --- src/spammer/blockwise.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/spammer/blockwise.rs b/src/spammer/blockwise.rs index 3db35e1..abd0381 100644 --- a/src/spammer/blockwise.rs +++ b/src/spammer/blockwise.rs @@ -238,7 +238,7 @@ where let tx_envelope = tx_req.build(&signer).await.map_err(|e| { ContenderError::with_err(e, "bad request: failed to build tx") })?; - println!("signed {:?}", tx_envelope.tx_hash()); + bundle_txs.push(tx_envelope); } ExecutionPayload::SignedTxBundle(bundle_txs, reqs) From 2d0fd138ea9ac92725faf1731f82fbaadc7eebdf Mon Sep 17 00:00:00 2001 From: zeroXbrock <2791467+zeroXbrock@users.noreply.github.com> Date: Wed, 13 Nov 2024 16:41:28 -0800 Subject: [PATCH 19/19] cleanup logs, add doc comment --- cli/src/commands.rs | 8 ++++++-- src/bundle_provider.rs | 10 ++-------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/cli/src/commands.rs b/cli/src/commands.rs index ed41996..0687ded 100644 --- a/cli/src/commands.rs +++ b/cli/src/commands.rs @@ -25,8 +25,12 @@ pub enum ContenderSubcommand { /// The HTTP JSON-RPC URL to spam with requests. rpc_url: String, - /// The builder HTTP JSON-RPC URL to use for spamming (must support `eth_sendBundle`). - #[arg(short, long, long_help = "")] + /// HTTP JSON-RPC URL to use for bundle spamming (must support `eth_sendBundle`). + #[arg( + short, + long, + long_help = "HTTP JSON-RPC URL to use for bundle spamming (must support `eth_sendBundle`)" + )] builder_url: Option, /// The number of txs to send per second using the timed spammer. This is the default spammer. diff --git a/src/bundle_provider.rs b/src/bundle_provider.rs index 77512d4..d38692b 100644 --- a/src/bundle_provider.rs +++ b/src/bundle_provider.rs @@ -27,20 +27,14 @@ impl BundleClient { .client .request("eth_sendBundle", rpc_params![bundle]) .await; - - if let Ok(res) = res { - if let Some(res) = res { - println!("sent bundle {:?}", res); - } else { - println!("sent bundle, no response"); - } + if let Err(e) = res { + return Err(format!("Failed to send bundle: {:?}", e)); } Ok(()) } } -// testing: #[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct EthSendBundle {