From 892304bfa7ff7688b4643d263f3e180f43790798 Mon Sep 17 00:00:00 2001 From: zeroXbrock <2791467+zeroXbrock@users.noreply.github.com> Date: Tue, 26 Nov 2024 18:03:01 -0800 Subject: [PATCH 01/10] add 'run' command; runs builtin scenarios --- crates/cli/src/commands.rs | 38 +++ crates/cli/src/default_scenarios/bytecode.rs | 1 + crates/cli/src/default_scenarios/mod.rs | 4 + .../cli/src/default_scenarios/testconfig.rs | 68 +++++ crates/cli/src/main.rs | 248 ++++++++++++------ crates/core/src/generator/mod.rs | 2 +- crates/core/src/spammer/spammer_trait.rs | 3 +- crates/core/src/test_scenario.rs | 28 +- 8 files changed, 309 insertions(+), 83 deletions(-) create mode 100644 crates/cli/src/default_scenarios/bytecode.rs create mode 100644 crates/cli/src/default_scenarios/mod.rs create mode 100644 crates/cli/src/default_scenarios/testconfig.rs diff --git a/crates/cli/src/commands.rs b/crates/cli/src/commands.rs index 0687ded..2122ecd 100644 --- a/crates/cli/src/commands.rs +++ b/crates/cli/src/commands.rs @@ -1,5 +1,7 @@ use clap::{Parser, Subcommand}; +use crate::default_scenarios::BuiltinScenario; + #[derive(Parser, Debug)] pub struct ContenderCli { #[command(subcommand)] @@ -143,4 +145,40 @@ May be specified multiple times." )] out_file: Option, }, + + #[command(name = "run", long_about = "Run a builtin scenario.")] + Run { + /// The scenario to run. Currently only `fill-block` is supported. + scenario: BuiltinScenario, + + /// The HTTP JSON-RPC URL to target with the scenario. + rpc_url: String, + + /// The private keys to use for the scenario. + #[arg( + short, + long = "priv-key", + long_help = "Private key used to send all transactions." + )] + private_key: Option, + + /// The time to wait in seconds between sending another tx. + #[arg( + short, + long = "interval", + long_help = "Interval in seconds between each transaction.", + default_value = "12" + )] + interval: usize, + + /// The time to wait in seconds between sending another tx. + #[arg( + short, + long = "duration", + long_help = "Number of txs to send for the whole run.", + default_value = "10" + )] + duration: usize, + // TODO: DRY duplicate args + }, } diff --git a/crates/cli/src/default_scenarios/bytecode.rs b/crates/cli/src/default_scenarios/bytecode.rs new file mode 100644 index 0000000..91c4d56 --- /dev/null +++ b/crates/cli/src/default_scenarios/bytecode.rs @@ -0,0 +1 @@ +pub const SPAM_ME: &str = "0x6080604052348015600f57600080fd5b506105f98061001f6000396000f3fe60806040526004361061004a5760003560e01c806369f86ec81461004f5780639402c00414610066578063a329e8de14610086578063c5eeaf17146100a6578063fb0e722b146100ae575b600080fd5b34801561005b57600080fd5b506100646100d9565b005b34801561007257600080fd5b50610064610081366004610284565b6100e4565b34801561009257600080fd5b506100646100a136600461033d565b610119565b6100646101b1565b3480156100ba57600080fd5b506100c36101e0565b6040516100d0919061037a565b60405180910390f35b5b60325a116100da57565b6000816040516020016100f89291906103e7565b6040516020818303038152906040526000908161011591906104bb565b5050565b6000811161016d5760405162461bcd60e51b815260206004820152601a60248201527f476173206d7573742062652067726561746572207468616e2030000000000000604482015260640160405180910390fd5b6000609561017d610a288461057a565b61018791906105a1565b905080600003610195575060015b60005b818110156101ac5760008055600101610198565b505050565b60405141903480156108fc02916000818181858888f193505050501580156101dd573d6000803e3d6000fd5b50565b600080546101ed906103ad565b80601f0160208091040260200160405190810160405280929190818152602001828054610219906103ad565b80156102665780601f1061023b57610100808354040283529160200191610266565b820191906000526020600020905b81548152906001019060200180831161024957829003601f168201915b505050505081565b634e487b7160e01b600052604160045260246000fd5b60006020828403121561029657600080fd5b813567ffffffffffffffff8111156102ad57600080fd5b8201601f810184136102be57600080fd5b803567ffffffffffffffff8111156102d8576102d861026e565b604051601f8201601f19908116603f0116810167ffffffffffffffff811182821017156103075761030761026e565b60405281815282820160200186101561031f57600080fd5b81602084016020830137600091810160200191909152949350505050565b60006020828403121561034f57600080fd5b5035919050565b60005b83811015610371578181015183820152602001610359565b50506000910152565b6020815260008251806020840152610399816040850160208701610356565b601f01601f19169190910160400192915050565b600181811c908216806103c157607f821691505b6020821081036103e157634e487b7160e01b600052602260045260246000fd5b50919050565b60008084546103f5816103ad565b60018216801561040c576001811461042157610451565b60ff1983168652811515820286019350610451565b87600052602060002060005b838110156104495781548882015260019091019060200161042d565b505081860193505b5050508351610464818360208801610356565b01949350505050565b601f8211156101ac57806000526020600020601f840160051c810160208510156104945750805b601f840160051c820191505b818110156104b457600081556001016104a0565b5050505050565b815167ffffffffffffffff8111156104d5576104d561026e565b6104e9816104e384546103ad565b8461046d565b6020601f82116001811461051d57600083156105055750848201515b600019600385901b1c1916600184901b1784556104b4565b600084815260208120601f198516915b8281101561054d578785015182556020948501946001909201910161052d565b508482101561056b5786840151600019600387901b60f8161c191681555b50505050600190811b01905550565b8181038181111561059b57634e487b7160e01b600052601160045260246000fd5b92915050565b6000826105be57634e487b7160e01b600052601260045260246000fd5b50049056fea264697066735822122045a1a87948aab5d390113cacf93d9eb435038ea2c95e18140c4d0e3e2604afca64736f6c634300081b0033"; diff --git a/crates/cli/src/default_scenarios/mod.rs b/crates/cli/src/default_scenarios/mod.rs new file mode 100644 index 0000000..d05e3a2 --- /dev/null +++ b/crates/cli/src/default_scenarios/mod.rs @@ -0,0 +1,4 @@ +mod bytecode; +mod testconfig; + +pub use testconfig::{BuiltinScenario, BuiltinScenarioConfig}; diff --git a/crates/cli/src/default_scenarios/testconfig.rs b/crates/cli/src/default_scenarios/testconfig.rs new file mode 100644 index 0000000..87b9904 --- /dev/null +++ b/crates/cli/src/default_scenarios/testconfig.rs @@ -0,0 +1,68 @@ +use alloy::primitives::Address; +use contender_core::generator::types::{CreateDefinition, FunctionCallDefinition, SpamRequest}; +use contender_testfile::TestConfig; +use serde::{Deserialize, Serialize}; + +use super::bytecode::SPAM_ME; + +#[derive(Serialize, Deserialize, Debug, Clone, clap::ValueEnum)] +pub enum BuiltinScenario { + FillBlock, +} + +pub enum BuiltinScenarioConfig { + FillBlock { + max_gas_per_block: u64, + num_txs: u64, + sender: Address, + }, +} + +impl BuiltinScenarioConfig { + pub fn fill_block(max_gas_per_block: u64, num_txs: u64, sender: Address) -> Self { + Self::FillBlock { + max_gas_per_block, + num_txs, + sender, + } + } +} + +impl Into for BuiltinScenarioConfig { + fn into(self) -> TestConfig { + match self { + Self::FillBlock { + max_gas_per_block, + num_txs, + sender, + } => { + let gas_per_tx = max_gas_per_block / num_txs; + let spam_txs = (0..num_txs) + .map(|_| { + SpamRequest::Tx(FunctionCallDefinition { + to: "{SpamMe}".to_owned(), + from: Some(sender.to_string()), + signature: "consumeGas(uint256 gas)".to_owned(), + from_pool: None, + args: Some(vec![gas_per_tx.to_string()]), + value: None, + fuzz: None, + kind: Some("fill-block".to_owned()), + }) + }) + .collect::>(); + + TestConfig { + env: None, + create: Some(vec![CreateDefinition { + name: "SpamMe".to_owned(), + bytecode: SPAM_ME.to_owned(), + from: sender.to_string(), + }]), + setup: None, + spam: Some(spam_txs), + } + } + } + } +} diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index c898a92..1a77e84 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -1,4 +1,5 @@ mod commands; +mod default_scenarios; use alloy::{ network::{AnyNetwork, EthereumWallet, TransactionBuilder}, @@ -27,6 +28,7 @@ use contender_core::{ use contender_sqlite::SqliteDb; use contender_testfile::TestConfig; use csv::{Writer, WriterBuilder}; +use default_scenarios::{BuiltinScenario, BuiltinScenarioConfig}; use std::{ str::FromStr, sync::{Arc, LazyLock}, @@ -61,7 +63,7 @@ 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( + check_private_keys_fns( &testconfig.setup.to_owned().unwrap_or_default(), signers.as_slice(), ); @@ -75,7 +77,7 @@ async fn main() -> Result<(), Box> { panic!("Some accounts do not have sufficient balance"); } - let scenario = TestScenario::new( + let mut scenario = TestScenario::new( testconfig.to_owned(), Arc::new(DB.clone()), url, @@ -121,29 +123,8 @@ async fn main() -> Result<(), Box> { .as_ref() .expect("No spam function calls found in testfile"); - // distill all FunctionCallDefinitions from the spam requests - let mut fn_calls = vec![]; // distill all from_pool arguments from the spam requests - let mut from_pools = vec![]; - - for s in spam { - match s { - SpamRequest::Tx(fn_call) => { - fn_calls.push(fn_call.to_owned()); - if let Some(from_pool) = &fn_call.from_pool { - from_pools.push(from_pool); - } - } - SpamRequest::Bundle(bundle) => { - fn_calls.extend(bundle.txs.iter().map(|s| { - if let Some(from_pool) = &s.from_pool { - from_pools.push(from_pool); - } - s.to_owned() - })); - } - } - } + let from_pools = get_from_pools(&testconfig); let mut agents = AgentStore::new(); let signers_per_period = @@ -152,7 +133,7 @@ async fn main() -> Result<(), Box> { let mut all_signers = vec![]; all_signers.extend_from_slice(&user_signers); - for from_pool in from_pools { + for from_pool in &from_pools { if agents.has_agent(from_pool) { continue; } @@ -162,57 +143,17 @@ async fn main() -> Result<(), Box> { agents.add_agent(from_pool, agent); } - check_private_keys(&fn_calls, &all_signers); + check_private_keys(&testconfig, &all_signers); - let insufficient_balance_addrs = find_insufficient_balance_addrs( - &all_signers.iter().map(|s| s.address()).collect::>(), - min_balance, + fund_accounts( &rpc_client, + ð_client, + min_balance, + &all_signers, + &user_signers[0], ) .await?; - let admin_signer = &user_signers[0]; - let mut pending_fund_txs = vec![]; - let admin_nonce = rpc_client - .get_transaction_count(admin_signer.address()) - .await?; - for (idx, address) in insufficient_balance_addrs.iter().enumerate() { - if !is_balance_sufficient(&admin_signer.address(), min_balance, &rpc_client).await? - { - // panic early if admin account runs out of funds - return Err(format!( - "Admin account {} has insufficient balance to fund this account.", - admin_signer.address() - ) - .into()); - } - - let balance = rpc_client.get_balance(*address).await?; - println!( - "Account {} has insufficient balance. (has {}, needed {})", - address, - format_ether(balance), - format_ether(min_balance) - ); - - let fund_amount = min_balance; - pending_fund_txs.push( - fund_account( - admin_signer, - *address, - fund_amount, - ð_client, - Some(admin_nonce + idx as u64), - ) - .await?, - ); - } - - for tx in pending_fund_txs { - let pending = rpc_client.watch_pending_transaction(tx).await?; - println!("funding tx confirmed ({})", pending.await?); - } - if txs_per_block.is_some() && txs_per_second.is_some() { panic!("Cannot set both --txs-per-block and --txs-per-second"); } @@ -251,7 +192,6 @@ async fn main() -> Result<(), Box> { cback.into(), ) .await?; - println!("Saved run. run_id = {}", run_id); } SpamCallbackType::Nil(cback) => { spammer @@ -278,7 +218,6 @@ async fn main() -> Result<(), Box> { spammer .spam_rpc(&mut scenario, tps, duration, Some(run_id), cback.into()) .await?; - println!("Saved run. run_id = {}", run_id); } SpamCallbackType::Nil(cback) => { spammer @@ -319,6 +258,55 @@ async fn main() -> Result<(), Box> { write_run_txs(&mut writer, &txs)?; // TODO: write a macro that lets us generalize the writer param to write_run_txs, then refactor this duplication }; } + ContenderSubcommand::Run { + scenario, + rpc_url, + private_key, + interval, + duration, + } => { + let user_signers = get_signers_with_defaults(private_key.map(|s| vec![s])); + let admin_signer = &user_signers[0]; + let rand_seed = RandSeed::default(); + + let scenario_config = match scenario { + BuiltinScenario::FillBlock => { + BuiltinScenarioConfig::fill_block(25_000_000, 1, admin_signer.address()) + } // TODO: get max_gas_per_block from chain + }; + let testconfig: TestConfig = scenario_config.into(); + check_private_keys(&testconfig, &user_signers); + + let rpc_url = Url::parse(&rpc_url).expect("Invalid RPC URL"); + let mut scenario = TestScenario::new( + testconfig, + DB.clone().into(), + rpc_url.to_owned(), + None, + rand_seed, + &user_signers, + AgentStore::default(), + ) + .await?; + + scenario.deploy_contracts().await?; + scenario.run_setup().await?; + let wait_duration = std::time::Duration::from_secs(interval as u64); + let spammer = TimedSpammer::new(wait_duration); + let timestamp = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .expect("Time went backwards") + .as_millis(); + let run_id = DB.insert_run(timestamp as u64, duration)?; + let callback = LogCallback::new(Arc::new( + ProviderBuilder::new() + .network::() + .on_http(rpc_url), + )); + spammer + .spam_rpc(&mut scenario, 1, duration, Some(run_id), callback.into()) + .await?; + } } Ok(()) } @@ -328,8 +316,36 @@ enum SpamCallbackType { Nil(NilCallback), } +fn check_private_keys(testconfig: &TestConfig, prv_keys: &[PrivateKeySigner]) { + let setup = testconfig.setup.to_owned().unwrap_or_default(); + let spam = testconfig + .spam + .as_ref() + .expect("No spam function calls found in testfile"); + + // distill all FunctionCallDefinitions from the spam requests + let mut fn_calls = vec![]; + + for s in setup { + fn_calls.push(s.to_owned()); + } + + for s in spam { + match s { + SpamRequest::Tx(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_fns(&fn_calls, prv_keys); +} + /// Panics if any of the function calls' `from` addresses do not have a corresponding private key. -fn check_private_keys(fn_calls: &[FunctionCallDefinition], prv_keys: &[PrivateKeySigner]) { +fn check_private_keys_fns(fn_calls: &[FunctionCallDefinition], prv_keys: &[PrivateKeySigner]) { for fn_call in fn_calls { if let Some(from) = &fn_call.from { let address = from.parse::
().expect("invalid 'from' address"); @@ -340,6 +356,33 @@ fn check_private_keys(fn_calls: &[FunctionCallDefinition], prv_keys: &[PrivateKe } } +fn get_from_pools(testconfig: &TestConfig) -> Vec { + let mut from_pools = vec![]; + let spam = testconfig + .spam + .as_ref() + .expect("No spam function calls found in testfile"); + + for s in spam { + match s { + SpamRequest::Tx(fn_call) => { + if let Some(from_pool) = &fn_call.from_pool { + from_pools.push(from_pool.to_owned()); + } + } + SpamRequest::Bundle(bundle) => { + for tx in &bundle.txs { + if let Some(from_pool) = &tx.from_pool { + from_pools.push(from_pool.to_owned()); + } + } + } + } + } + + from_pools +} + const DEFAULT_PRV_KEYS: [&str; 10] = [ "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d", @@ -373,6 +416,63 @@ fn get_signers_with_defaults(private_keys: Option>) -> Vec>() } +async fn fund_accounts( + rpc_client: &AnyProvider, + eth_client: &EthProvider, + min_balance: U256, + all_signers: &[PrivateKeySigner], + admin_signer: &PrivateKeySigner, +) -> Result<(), Box> { + let insufficient_balance_addrs = find_insufficient_balance_addrs( + &all_signers.iter().map(|s| s.address()).collect::>(), + min_balance, + rpc_client, + ) + .await?; + + let mut pending_fund_txs = vec![]; + let admin_nonce = rpc_client + .get_transaction_count(admin_signer.address()) + .await?; + for (idx, address) in insufficient_balance_addrs.iter().enumerate() { + if !is_balance_sufficient(&admin_signer.address(), min_balance, &rpc_client).await? { + // panic early if admin account runs out of funds + return Err(format!( + "Admin account {} has insufficient balance to fund this account.", + admin_signer.address() + ) + .into()); + } + + let balance = rpc_client.get_balance(*address).await?; + println!( + "Account {} has insufficient balance. (has {}, needed {})", + address, + format_ether(balance), + format_ether(min_balance) + ); + + let fund_amount = min_balance; + pending_fund_txs.push( + fund_account( + admin_signer, + *address, + fund_amount, + ð_client, + Some(admin_nonce + idx as u64), + ) + .await?, + ); + } + + for tx in pending_fund_txs { + let pending = rpc_client.watch_pending_transaction(tx).await?; + println!("funding tx confirmed ({})", pending.await?); + } + + Ok(()) +} + async fn fund_account( admin_signer: &PrivateKeySigner, recipient: Address, diff --git a/crates/core/src/generator/mod.rs b/crates/core/src/generator/mod.rs index b8a4eb1..da44743 100644 --- a/crates/core/src/generator/mod.rs +++ b/crates/core/src/generator/mod.rs @@ -15,7 +15,7 @@ use named_txs::ExecutionRequest; pub use named_txs::NamedTxRequestBuilder; pub use seeder::rand_seed::RandSeed; use std::{collections::HashMap, fmt::Debug, hash::Hash}; -use types::{FunctionCallDefinitionStrict, SpamRequest}; +use types::{FunctionCallDefinitionStrict, Plan, SpamRequest}; pub use types::{CallbackResult, NamedTxRequest, PlanType}; diff --git a/crates/core/src/spammer/spammer_trait.rs b/crates/core/src/spammer/spammer_trait.rs index c92367f..2631c68 100644 --- a/crates/core/src/spammer/spammer_trait.rs +++ b/crates/core/src/spammer/spammer_trait.rs @@ -72,7 +72,7 @@ where let mut timeout_counter = 0; if let Some(run_id) = run_id { loop { - if timeout_counter > (num_periods * txs_per_period) { + if timeout_counter > (num_periods * txs_per_period + 1) { println!("quitting due to timeout"); break; } @@ -86,6 +86,7 @@ where } timeout_counter += 1; } + println!("done spamming. run_id={}", run_id); } Ok(()) diff --git a/crates/core/src/test_scenario.rs b/crates/core/src/test_scenario.rs index a41d679..3c01050 100644 --- a/crates/core/src/test_scenario.rs +++ b/crates/core/src/test_scenario.rs @@ -123,10 +123,20 @@ where }) } - pub async fn deploy_contracts( - &self, - // only_with_names: Option<&[impl AsRef]>, - ) -> Result<()> { + pub async fn sync_nonces(&mut self) -> Result<()> { + let all_addrs = self.wallet_map.keys().copied().collect::>(); + for addr in &all_addrs { + let nonce = self + .rpc_client + .get_transaction_count(*addr) + .await + .map_err(|e| ContenderError::with_err(e, "failed to retrieve nonce from RPC"))?; + self.nonces.insert(*addr, nonce); + } + Ok(()) + } + + pub async fn deploy_contracts(&mut self) -> Result<()> { let pub_provider = &self.rpc_client; let gas_price = pub_provider .get_gas_price() @@ -198,10 +208,12 @@ where })) .await?; + self.sync_nonces().await?; + Ok(()) } - pub async fn run_setup(&self) -> Result<()> { + pub async fn run_setup(&mut self) -> Result<()> { self.load_txs(PlanType::Setup(|tx_req| { /* callback */ println!("{}", self.format_setup_log(&tx_req)); @@ -260,6 +272,8 @@ where })) .await?; + self.sync_nonces().await?; + Ok(()) } @@ -762,7 +776,7 @@ pub mod tests { #[tokio::test] async fn scenario_creates_contracts() { let anvil = spawn_anvil(); - let scenario = get_test_scenario(&anvil).await; + let mut scenario = get_test_scenario(&anvil).await; let res = scenario.deploy_contracts().await; assert!(res.is_ok()); } @@ -770,7 +784,7 @@ pub mod tests { #[tokio::test] async fn scenario_runs_setup() { let anvil = spawn_anvil(); - let scenario = get_test_scenario(&anvil).await; + let mut scenario = get_test_scenario(&anvil).await; scenario.deploy_contracts().await.unwrap(); let res = scenario.run_setup().await; println!("{:?}", res); From a93a6fcf5e584bfd8d0a300c579db290cfd03830 Mon Sep 17 00:00:00 2001 From: zeroXbrock <2791467+zeroXbrock@users.noreply.github.com> Date: Tue, 26 Nov 2024 18:04:51 -0800 Subject: [PATCH 02/10] rename file --- crates/cli/src/default_scenarios/mod.rs | 4 ++-- .../cli/src/default_scenarios/{testconfig.rs => runconfig.rs} | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename crates/cli/src/default_scenarios/{testconfig.rs => runconfig.rs} (100%) diff --git a/crates/cli/src/default_scenarios/mod.rs b/crates/cli/src/default_scenarios/mod.rs index d05e3a2..5cbedd1 100644 --- a/crates/cli/src/default_scenarios/mod.rs +++ b/crates/cli/src/default_scenarios/mod.rs @@ -1,4 +1,4 @@ mod bytecode; -mod testconfig; +mod runconfig; -pub use testconfig::{BuiltinScenario, BuiltinScenarioConfig}; +pub use runconfig::{BuiltinScenario, BuiltinScenarioConfig}; diff --git a/crates/cli/src/default_scenarios/testconfig.rs b/crates/cli/src/default_scenarios/runconfig.rs similarity index 100% rename from crates/cli/src/default_scenarios/testconfig.rs rename to crates/cli/src/default_scenarios/runconfig.rs From dcbd560dee8738d3f66a4cdd0e0faccaaa371325 Mon Sep 17 00:00:00 2001 From: zeroXbrock <2791467+zeroXbrock@users.noreply.github.com> Date: Wed, 27 Nov 2024 15:37:27 -0800 Subject: [PATCH 03/10] spam many txs in fill-block --- crates/cli/src/default_scenarios/runconfig.rs | 4 ++-- crates/cli/src/main.rs | 23 ++++++++++++++++--- crates/core/src/generator/mod.rs | 2 +- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/crates/cli/src/default_scenarios/runconfig.rs b/crates/cli/src/default_scenarios/runconfig.rs index 87b9904..a6f1c16 100644 --- a/crates/cli/src/default_scenarios/runconfig.rs +++ b/crates/cli/src/default_scenarios/runconfig.rs @@ -3,7 +3,7 @@ use contender_core::generator::types::{CreateDefinition, FunctionCallDefinition, use contender_testfile::TestConfig; use serde::{Deserialize, Serialize}; -use super::bytecode::SPAM_ME; +use super::bytecode; #[derive(Serialize, Deserialize, Debug, Clone, clap::ValueEnum)] pub enum BuiltinScenario { @@ -56,7 +56,7 @@ impl Into for BuiltinScenarioConfig { env: None, create: Some(vec![CreateDefinition { name: "SpamMe".to_owned(), - bytecode: SPAM_ME.to_owned(), + bytecode: bytecode::SPAM_ME.to_owned(), from: sender.to_string(), }]), setup: None, diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 1a77e84..f8bac2d 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -268,11 +268,19 @@ async fn main() -> Result<(), Box> { let user_signers = get_signers_with_defaults(private_key.map(|s| vec![s])); let admin_signer = &user_signers[0]; let rand_seed = RandSeed::default(); + let txs_per_duration = 150; + // TODO: get max_gas_per_block from chain + let max_gas_per_block = 30_000_000; let scenario_config = match scenario { BuiltinScenario::FillBlock => { - BuiltinScenarioConfig::fill_block(25_000_000, 1, admin_signer.address()) - } // TODO: get max_gas_per_block from chain + // TODO: should we parameterize num_txs? + BuiltinScenarioConfig::fill_block( + max_gas_per_block, + txs_per_duration, + admin_signer.address(), + ) + } }; let testconfig: TestConfig = scenario_config.into(); check_private_keys(&testconfig, &user_signers); @@ -290,6 +298,9 @@ async fn main() -> Result<(), Box> { .await?; scenario.deploy_contracts().await?; + + // TODO: read for existing contract, prompt user to re-deploy if exists + scenario.run_setup().await?; let wait_duration = std::time::Duration::from_secs(interval as u64); let spammer = TimedSpammer::new(wait_duration); @@ -304,7 +315,13 @@ async fn main() -> Result<(), Box> { .on_http(rpc_url), )); spammer - .spam_rpc(&mut scenario, 1, duration, Some(run_id), callback.into()) + .spam_rpc( + &mut scenario, + txs_per_duration as usize, + duration, + Some(run_id), + callback.into(), + ) .await?; } } diff --git a/crates/core/src/generator/mod.rs b/crates/core/src/generator/mod.rs index da44743..b8a4eb1 100644 --- a/crates/core/src/generator/mod.rs +++ b/crates/core/src/generator/mod.rs @@ -15,7 +15,7 @@ use named_txs::ExecutionRequest; pub use named_txs::NamedTxRequestBuilder; pub use seeder::rand_seed::RandSeed; use std::{collections::HashMap, fmt::Debug, hash::Hash}; -use types::{FunctionCallDefinitionStrict, Plan, SpamRequest}; +use types::{FunctionCallDefinitionStrict, SpamRequest}; pub use types::{CallbackResult, NamedTxRequest, PlanType}; From 90bd5eb0335cbfb37d5318cebd7bffdfc105df0d Mon Sep 17 00:00:00 2001 From: zeroXbrock <2791467+zeroXbrock@users.noreply.github.com> Date: Wed, 27 Nov 2024 15:58:09 -0800 Subject: [PATCH 04/10] read block gas limit from rpc --- crates/cli/src/default_scenarios/runconfig.rs | 6 +++--- crates/cli/src/main.rs | 21 ++++++++++++++----- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/crates/cli/src/default_scenarios/runconfig.rs b/crates/cli/src/default_scenarios/runconfig.rs index a6f1c16..98e18eb 100644 --- a/crates/cli/src/default_scenarios/runconfig.rs +++ b/crates/cli/src/default_scenarios/runconfig.rs @@ -12,14 +12,14 @@ pub enum BuiltinScenario { pub enum BuiltinScenarioConfig { FillBlock { - max_gas_per_block: u64, + max_gas_per_block: u128, num_txs: u64, sender: Address, }, } impl BuiltinScenarioConfig { - pub fn fill_block(max_gas_per_block: u64, num_txs: u64, sender: Address) -> Self { + pub fn fill_block(max_gas_per_block: u128, num_txs: u64, sender: Address) -> Self { Self::FillBlock { max_gas_per_block, num_txs, @@ -36,7 +36,7 @@ impl Into for BuiltinScenarioConfig { num_txs, sender, } => { - let gas_per_tx = max_gas_per_block / num_txs; + let gas_per_tx = max_gas_per_block / num_txs as u128; let spam_txs = (0..num_txs) .map(|_| { SpamRequest::Tx(FunctionCallDefinition { diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index f8bac2d..8fca345 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -2,13 +2,14 @@ mod commands; mod default_scenarios; use alloy::{ + eips::BlockId, network::{AnyNetwork, EthereumWallet, TransactionBuilder}, primitives::{ utils::{format_ether, parse_ether}, Address, U256, }, providers::{PendingTransactionConfig, Provider, ProviderBuilder}, - rpc::types::TransactionRequest, + rpc::types::{BlockTransactionsKind, TransactionRequest}, signers::local::PrivateKeySigner, transports::http::reqwest::Url, }; @@ -16,6 +17,7 @@ use commands::{ContenderCli, ContenderSubcommand}; use contender_core::{ agent_controller::{AgentStore, SignerStore}, db::{DbOps, RunTx}, + error::ContenderError, generator::{ types::{AnyProvider, EthProvider, FunctionCallDefinition, SpamRequest}, RandSeed, @@ -268,15 +270,24 @@ async fn main() -> Result<(), Box> { let user_signers = get_signers_with_defaults(private_key.map(|s| vec![s])); let admin_signer = &user_signers[0]; let rand_seed = RandSeed::default(); - let txs_per_duration = 150; - // TODO: get max_gas_per_block from chain - let max_gas_per_block = 30_000_000; + let txs_per_duration = 99; + let provider = ProviderBuilder::new() + .network::() + .on_http(Url::parse(&rpc_url).expect("Invalid RPC URL")); + let block_gas_limit = provider + .get_block(BlockId::latest(), BlockTransactionsKind::Hashes) + .await? + .map(|b| b.header.gas_limit) + .ok_or(ContenderError::SetupError( + "failed getting gas limit from block", + None, + ))?; let scenario_config = match scenario { BuiltinScenario::FillBlock => { // TODO: should we parameterize num_txs? BuiltinScenarioConfig::fill_block( - max_gas_per_block, + block_gas_limit, txs_per_duration, admin_signer.address(), ) From 7e9c8b88b8315c490146603ae94f851b11dac896 Mon Sep 17 00:00:00 2001 From: zeroXbrock <2791467+zeroXbrock@users.noreply.github.com> Date: Wed, 27 Nov 2024 16:35:25 -0800 Subject: [PATCH 05/10] organize db, modify templater return types, prompt user to redeploy on fill-blocks --- crates/cli/src/main.rs | 30 ++++++++++++++-- crates/core/src/db/mock.rs | 48 ++++++++++++++++++++++++++ crates/core/src/{db.rs => db/mod.rs} | 48 ++++---------------------- crates/core/src/generator/templater.rs | 19 ++++++---- crates/core/src/test_scenario.rs | 4 ++- crates/sqlite_db/src/lib.rs | 20 +++++------ 6 files changed, 108 insertions(+), 61 deletions(-) create mode 100644 crates/core/src/db/mock.rs rename crates/core/src/{db.rs => db/mod.rs} (57%) diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 8fca345..5d01cbb 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -308,11 +308,26 @@ async fn main() -> Result<(), Box> { ) .await?; - scenario.deploy_contracts().await?; + let contract_name = "SpamMe"; + let contract_result = DB.clone().get_named_tx(&contract_name)?; + let do_deploy_contracts = if contract_result.is_some() { + let input = prompt_cli(format!( + "{} deployment already detected. Re-deploy? [y/N]", + contract_name + )); + input.to_lowercase() == "y" + } else { + true + }; - // TODO: read for existing contract, prompt user to re-deploy if exists + if do_deploy_contracts { + println!("deploying contracts..."); + scenario.deploy_contracts().await?; + } + println!("running setup..."); scenario.run_setup().await?; + let wait_duration = std::time::Duration::from_secs(interval as u64); let spammer = TimedSpammer::new(wait_duration); let timestamp = std::time::SystemTime::now() @@ -325,6 +340,8 @@ async fn main() -> Result<(), Box> { .network::() .on_http(rpc_url), )); + + println!("starting spammer..."); spammer .spam_rpc( &mut scenario, @@ -344,6 +361,15 @@ enum SpamCallbackType { Nil(NilCallback), } +fn prompt_cli(msg: impl AsRef) -> String { + println!("{}", msg.as_ref()); + let mut input = String::new(); + std::io::stdin() + .read_line(&mut input) + .expect("Failed to read line"); + input.trim().to_owned() +} + fn check_private_keys(testconfig: &TestConfig, prv_keys: &[PrivateKeySigner]) { let setup = testconfig.setup.to_owned().unwrap_or_default(); let spam = testconfig diff --git a/crates/core/src/db/mock.rs b/crates/core/src/db/mock.rs new file mode 100644 index 0000000..dc216b2 --- /dev/null +++ b/crates/core/src/db/mock.rs @@ -0,0 +1,48 @@ +use alloy::primitives::{Address, TxHash}; + +use super::{DbOps, NamedTx, RunTx}; +use crate::Result; + +pub struct MockDb; + +impl DbOps for MockDb { + fn create_tables(&self) -> Result<()> { + Ok(()) + } + + fn insert_run(&self, _timestamp: u64, _tx_count: usize) -> Result { + Ok(0) + } + + fn num_runs(&self) -> Result { + Ok(0) + } + + fn insert_named_txs(&self, _named_txs: Vec) -> Result<()> { + Ok(()) + } + + fn get_named_tx(&self, _name: &str) -> Result> { + Ok(Some(NamedTx::new( + String::default(), + TxHash::default(), + None, + ))) + } + + fn get_named_tx_by_address(&self, address: &Address) -> Result> { + Ok(Some(NamedTx::new( + String::default(), + TxHash::default(), + Some(*address), + ))) + } + + fn insert_run_txs(&self, _run_id: u64, _run_txs: Vec) -> Result<()> { + Ok(()) + } + + fn get_run_txs(&self, _run_id: u64) -> Result> { + Ok(vec![]) + } +} diff --git a/crates/core/src/db.rs b/crates/core/src/db/mod.rs similarity index 57% rename from crates/core/src/db.rs rename to crates/core/src/db/mod.rs index 1fd19ff..349a66d 100644 --- a/crates/core/src/db.rs +++ b/crates/core/src/db/mod.rs @@ -1,8 +1,12 @@ +mod mock; + use alloy::primitives::{Address, TxHash}; use serde::Serialize; use crate::Result; +pub use mock::MockDb; + #[derive(Debug, Serialize, Clone)] pub struct RunTx { pub tx_hash: TxHash, @@ -48,51 +52,11 @@ pub trait DbOps { fn insert_named_txs(&self, named_txs: Vec) -> Result<()>; - fn get_named_tx(&self, name: &str) -> Result; + fn get_named_tx(&self, name: &str) -> Result>; - fn get_named_tx_by_address(&self, address: &Address) -> Result; + fn get_named_tx_by_address(&self, address: &Address) -> Result>; fn insert_run_txs(&self, run_id: u64, run_txs: Vec) -> Result<()>; fn get_run_txs(&self, run_id: u64) -> Result>; } - -pub struct MockDb; - -impl DbOps for MockDb { - fn create_tables(&self) -> Result<()> { - Ok(()) - } - - fn insert_run(&self, _timestamp: u64, _tx_count: usize) -> Result { - Ok(0) - } - - fn num_runs(&self) -> Result { - Ok(0) - } - - fn insert_named_txs(&self, _named_txs: Vec) -> Result<()> { - Ok(()) - } - - fn get_named_tx(&self, _name: &str) -> Result { - Ok(NamedTx::new(String::default(), TxHash::default(), None)) - } - - fn get_named_tx_by_address(&self, address: &Address) -> Result { - Ok(NamedTx::new( - String::default(), - TxHash::default(), - Some(*address), - )) - } - - fn insert_run_txs(&self, _run_id: u64, _run_txs: Vec) -> Result<()> { - Ok(()) - } - - fn get_run_txs(&self, _run_id: u64) -> Result> { - Ok(vec![]) - } -} diff --git a/crates/core/src/generator/templater.rs b/crates/core/src/generator/templater.rs index 0fadd9b..c6cd4a9 100644 --- a/crates/core/src/generator/templater.rs +++ b/crates/core/src/generator/templater.rs @@ -54,12 +54,19 @@ where continue; } - let template_value = db.get_named_tx(&template_key.to_string()).map_err(|e| { - ContenderError::SpamError( - "failed to get placeholder value from DB", - Some(format!("value={:?} ({})", template_key, e)), - ) - })?; + let template_value = db + .get_named_tx(&template_key.to_string()) + .map_err(|e| { + ContenderError::SpamError( + "failed to get placeholder value from DB", + Some(format!("value={:?} ({})", template_key, e)), + ) + })? + .ok_or(ContenderError::SpamError( + "failed to find placeholder value in DB", + Some(template_key.to_string()), + ))?; + placeholder_map.insert( template_key, template_value diff --git a/crates/core/src/test_scenario.rs b/crates/core/src/test_scenario.rs index 3c01050..f9b6d7b 100644 --- a/crates/core/src/test_scenario.rs +++ b/crates/core/src/test_scenario.rs @@ -532,7 +532,9 @@ where // lookup name of contract if it exists let to_name = to_address.map(|a| { let named_tx = self.db.get_named_tx_by_address(a); - named_tx.map(|t| t.name).unwrap_or_default() + named_tx + .map(|t| t.map(|tt| tt.name).unwrap_or_default()) + .unwrap_or_default() }); format!( diff --git a/crates/sqlite_db/src/lib.rs b/crates/sqlite_db/src/lib.rs index 81cf2a3..187e53c 100644 --- a/crates/sqlite_db/src/lib.rs +++ b/crates/sqlite_db/src/lib.rs @@ -218,7 +218,7 @@ impl DbOps for SqliteDb { Ok(()) } - fn get_named_tx(&self, name: &str) -> Result { + fn get_named_tx(&self, name: &str) -> Result> { let pool = self.get_pool()?; let mut stmt = pool .prepare( @@ -232,12 +232,12 @@ impl DbOps for SqliteDb { let res = row .last() .transpose() - .map_err(|e| ContenderError::with_err(e, "no row found"))? - .ok_or(ContenderError::DbError("no existing row", None))?; - Ok(res.into()) + .map_err(|e| ContenderError::with_err(e, "failed to query row"))? + .map(|r| r.into()); + Ok(res) } - fn get_named_tx_by_address(&self, address: &Address) -> Result { + fn get_named_tx_by_address(&self, address: &Address) -> Result> { let pool = self.get_pool()?; let mut stmt = pool .prepare( @@ -249,13 +249,13 @@ impl DbOps for SqliteDb { .query_map(params![address.encode_hex()], |row| { NamedTxRow::from_row(row) }) - .map_err(|e| ContenderError::with_err(e, "failed to map row"))?; + .map_err(|e| ContenderError::with_err(e, "failed to map row query"))?; let res = row .last() .transpose() - .map_err(|e| ContenderError::with_err(e, "no row found"))? - .ok_or(ContenderError::DbError("no existing row", None))?; - Ok(res.into()) + .map_err(|e| ContenderError::with_err(e, "failed to query row"))? + .map(|r| r.into()); + Ok(res) } fn insert_run_txs(&self, run_id: u64, run_txs: Vec) -> Result<()> { @@ -342,7 +342,7 @@ mod tests { .unwrap(); assert_eq!(count, 2); - let res1 = db.get_named_tx(&name1).unwrap(); + let res1 = db.get_named_tx(&name1).unwrap().unwrap(); assert_eq!(res1.name, name1); assert_eq!(res1.tx_hash, tx_hash); assert_eq!(res1.address, contract_address); From 28e6f01a682c8ac2f9095f98ec8c8600d6fbf40b Mon Sep 17 00:00:00 2001 From: zeroXbrock <2791467+zeroXbrock@users.noreply.github.com> Date: Wed, 27 Nov 2024 16:44:54 -0800 Subject: [PATCH 06/10] add termcolor to cli, make prompt orange --- Cargo.lock | 10 ++++++++++ crates/cli/Cargo.toml | 1 + crates/cli/src/main.rs | 19 ++++++++++++++----- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 53c375c..d803f09 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1163,6 +1163,7 @@ dependencies = [ "contender_testfile", "csv", "serde", + "termcolor", "tokio", ] @@ -3262,6 +3263,15 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + [[package]] name = "thiserror" version = "1.0.63" diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index eb2a826..26defc0 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -17,3 +17,4 @@ contender_testfile = { workspace = true } clap = { workspace = true, features = ["derive"] } alloy = { workspace = true, features = ["full", "node-bindings"] } csv = { workspace = true } +termcolor = "1.4.1" diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 5d01cbb..203547e 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -1,6 +1,12 @@ mod commands; mod default_scenarios; +use std::{ + io::Write, + str::FromStr, + sync::{Arc, LazyLock}, +}; + use alloy::{ eips::BlockId, network::{AnyNetwork, EthereumWallet, TransactionBuilder}, @@ -31,10 +37,7 @@ use contender_sqlite::SqliteDb; use contender_testfile::TestConfig; use csv::{Writer, WriterBuilder}; use default_scenarios::{BuiltinScenario, BuiltinScenarioConfig}; -use std::{ - str::FromStr, - sync::{Arc, LazyLock}, -}; +use termcolor::{ColorChoice, ColorSpec, StandardStream, WriteColor}; static DB: LazyLock = std::sync::LazyLock::new(|| { SqliteDb::from_file("contender.db").expect("failed to open contender.db") @@ -362,7 +365,13 @@ enum SpamCallbackType { } fn prompt_cli(msg: impl AsRef) -> String { - println!("{}", msg.as_ref()); + let mut stdout = StandardStream::stdout(ColorChoice::Always); + stdout + .set_color(ColorSpec::new().set_fg(Some(termcolor::Color::Rgb(252, 186, 3)))) + .expect("failed to set stdout color"); + writeln!(&mut stdout, "{}", msg.as_ref()).expect("failed to write to stdout"); + stdout.reset().expect("failed to reset color"); + let mut input = String::new(); std::io::stdin() .read_line(&mut input) From 395afc487c84b311d9f6eb489844e7f13522e78d Mon Sep 17 00:00:00 2001 From: zeroXbrock <2791467+zeroXbrock@users.noreply.github.com> Date: Wed, 27 Nov 2024 17:02:05 -0800 Subject: [PATCH 07/10] add --num-txs to run command --- crates/cli/src/commands.rs | 9 +++++++++ crates/cli/src/main.rs | 15 ++++++--------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/crates/cli/src/commands.rs b/crates/cli/src/commands.rs index 2122ecd..6db20e3 100644 --- a/crates/cli/src/commands.rs +++ b/crates/cli/src/commands.rs @@ -179,6 +179,15 @@ May be specified multiple times." default_value = "10" )] duration: usize, + + /// The number of txs to send for each duration. + #[arg( + short = 'n', + long = "num-txs", + long_help = "Number of txs to send for each duration.", + default_value = "100" + )] + txs_per_duration: usize, // TODO: DRY duplicate args }, } diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 203547e..602b418 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -269,11 +269,11 @@ async fn main() -> Result<(), Box> { private_key, interval, duration, + txs_per_duration, } => { let user_signers = get_signers_with_defaults(private_key.map(|s| vec![s])); let admin_signer = &user_signers[0]; let rand_seed = RandSeed::default(); - let txs_per_duration = 99; let provider = ProviderBuilder::new() .network::() .on_http(Url::parse(&rpc_url).expect("Invalid RPC URL")); @@ -287,14 +287,11 @@ async fn main() -> Result<(), Box> { ))?; let scenario_config = match scenario { - BuiltinScenario::FillBlock => { - // TODO: should we parameterize num_txs? - BuiltinScenarioConfig::fill_block( - block_gas_limit, - txs_per_duration, - admin_signer.address(), - ) - } + BuiltinScenario::FillBlock => BuiltinScenarioConfig::fill_block( + block_gas_limit, + txs_per_duration as u64, + admin_signer.address(), + ), }; let testconfig: TestConfig = scenario_config.into(); check_private_keys(&testconfig, &user_signers); From 25139da8601336cc3c90855ae61bb6f629a32ced Mon Sep 17 00:00:00 2001 From: zeroXbrock <2791467+zeroXbrock@users.noreply.github.com> Date: Wed, 27 Nov 2024 17:10:19 -0800 Subject: [PATCH 08/10] cleanup doc comments, fix num_txs bug in run db --- crates/cli/src/commands.rs | 12 ++++-------- crates/cli/src/main.rs | 2 +- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/crates/cli/src/commands.rs b/crates/cli/src/commands.rs index 6db20e3..3c9465d 100644 --- a/crates/cli/src/commands.rs +++ b/crates/cli/src/commands.rs @@ -148,13 +148,12 @@ May be specified multiple times." #[command(name = "run", long_about = "Run a builtin scenario.")] Run { - /// The scenario to run. Currently only `fill-block` is supported. + /// The scenario to run. scenario: BuiltinScenario, /// The HTTP JSON-RPC URL to target with the scenario. rpc_url: String, - /// The private keys to use for the scenario. #[arg( short, long = "priv-key", @@ -162,29 +161,26 @@ May be specified multiple times." )] private_key: Option, - /// The time to wait in seconds between sending another tx. #[arg( short, long = "interval", - long_help = "Interval in seconds between each transaction.", + long_help = "Interval in seconds between each batch of requests.", default_value = "12" )] interval: usize, - /// The time to wait in seconds between sending another tx. #[arg( short, long = "duration", - long_help = "Number of txs to send for the whole run.", + long_help = "The number of batches of requests to send.", default_value = "10" )] duration: usize, - /// The number of txs to send for each duration. #[arg( short = 'n', long = "num-txs", - long_help = "Number of txs to send for each duration.", + long_help = "The number of txs to send on each elapsed interval.", default_value = "100" )] txs_per_duration: usize, diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 602b418..695d9ee 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -334,7 +334,7 @@ async fn main() -> Result<(), Box> { .duration_since(std::time::UNIX_EPOCH) .expect("Time went backwards") .as_millis(); - let run_id = DB.insert_run(timestamp as u64, duration)?; + let run_id = DB.insert_run(timestamp as u64, duration * txs_per_duration)?; let callback = LogCallback::new(Arc::new( ProviderBuilder::new() .network::() From 779c0ace2e16804ad6147f63f3f42d9ffcd4b273 Mon Sep 17 00:00:00 2001 From: zeroXbrock <2791467+zeroXbrock@users.noreply.github.com> Date: Wed, 27 Nov 2024 17:41:55 -0800 Subject: [PATCH 09/10] replace Into impl with From --- crates/cli/src/default_scenarios/runconfig.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/cli/src/default_scenarios/runconfig.rs b/crates/cli/src/default_scenarios/runconfig.rs index 98e18eb..476d328 100644 --- a/crates/cli/src/default_scenarios/runconfig.rs +++ b/crates/cli/src/default_scenarios/runconfig.rs @@ -28,10 +28,10 @@ impl BuiltinScenarioConfig { } } -impl Into for BuiltinScenarioConfig { - fn into(self) -> TestConfig { - match self { - Self::FillBlock { +impl From for TestConfig { + fn from(scenario: BuiltinScenarioConfig) -> Self { + match scenario { + BuiltinScenarioConfig::FillBlock { max_gas_per_block, num_txs, sender, From 17a84cf5f906fcdc88e0feeaabaeecbebaa0fa58 Mon Sep 17 00:00:00 2001 From: zeroXbrock <2791467+zeroXbrock@users.noreply.github.com> Date: Wed, 27 Nov 2024 17:43:28 -0800 Subject: [PATCH 10/10] chore: make clippy happy --- crates/cli/src/main.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 695d9ee..b6c5c1e 100644 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -309,7 +309,7 @@ async fn main() -> Result<(), Box> { .await?; let contract_name = "SpamMe"; - let contract_result = DB.clone().get_named_tx(&contract_name)?; + let contract_result = DB.clone().get_named_tx(contract_name)?; let do_deploy_contracts = if contract_result.is_some() { let input = prompt_cli(format!( "{} deployment already detected. Re-deploy? [y/N]", @@ -345,7 +345,7 @@ async fn main() -> Result<(), Box> { spammer .spam_rpc( &mut scenario, - txs_per_duration as usize, + txs_per_duration, duration, Some(run_id), callback.into(), @@ -495,7 +495,7 @@ async fn fund_accounts( .get_transaction_count(admin_signer.address()) .await?; for (idx, address) in insufficient_balance_addrs.iter().enumerate() { - if !is_balance_sufficient(&admin_signer.address(), min_balance, &rpc_client).await? { + if !is_balance_sufficient(&admin_signer.address(), min_balance, rpc_client).await? { // panic early if admin account runs out of funds return Err(format!( "Admin account {} has insufficient balance to fund this account.", @@ -518,7 +518,7 @@ async fn fund_accounts( admin_signer, *address, fund_amount, - ð_client, + eth_client, Some(admin_nonce + idx as u64), ) .await?,