diff --git a/cli/src/main.rs b/cli/src/main.rs index a7961b9..3641c15 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -1,6 +1,7 @@ mod commands; use alloy::{ + network::AnyNetwork, primitives::{ utils::{format_ether, parse_ether}, Address, U256, @@ -13,7 +14,7 @@ use commands::{ContenderCli, ContenderSubcommand}; use contender_core::{ db::{DbOps, RunTx}, generator::{ - types::{FunctionCallDefinition, RpcProvider}, + types::{AnyProvider, FunctionCallDefinition}, RandSeed, }, spammer::{BlockwiseSpammer, LogCallback, NilCallback, TimedSpammer}, @@ -63,7 +64,9 @@ async fn main() -> Result<(), Box> { min_balance, } => { let url = Url::parse(rpc_url.as_ref()).expect("Invalid RPC URL"); - let rpc_client = ProviderBuilder::new().on_http(url.to_owned()); + let rpc_client = ProviderBuilder::new() + .network::() + .on_http(url.to_owned()); let testconfig: TestConfig = TestConfig::from_file(&testfile)?; let min_balance = parse_ether(&min_balance)?; @@ -101,7 +104,9 @@ async fn main() -> Result<(), Box> { let testconfig = TestConfig::from_file(&testfile)?; let rand_seed = seed.map(|s| RandSeed::from_str(&s)).unwrap_or_default(); let url = Url::parse(rpc_url.as_ref()).expect("Invalid RPC URL"); - let rpc_client = ProviderBuilder::new().on_http(url.to_owned()); + let rpc_client = ProviderBuilder::new() + .network::() + .on_http(url.to_owned()); let duration = duration.unwrap_or_default(); let min_balance = parse_ether(&min_balance)?; @@ -223,7 +228,7 @@ const DEFAULT_PRV_KEYS: [&str; 10] = [ async fn spam_callback_default( log_txs: bool, - rpc_client: Option>, + rpc_client: Option>, ) -> SpamCallbackType { if let Some(rpc_client) = rpc_client { if log_txs { @@ -236,7 +241,7 @@ async fn spam_callback_default( async fn check_balances( prv_keys: &[PrivateKeySigner], min_balance: U256, - rpc_client: &RpcProvider, + rpc_client: &AnyProvider, ) { for prv_key in prv_keys { let address = prv_key.address(); diff --git a/cli/univ2ConfigTest.toml b/cli/univ2ConfigTest.toml index 49a6fc0..4301d42 100644 --- a/cli/univ2ConfigTest.toml +++ b/cli/univ2ConfigTest.toml @@ -141,6 +141,7 @@ args = [ ### the spam step will be repeated [[spam]] +kind = "uniswap_v2" to = "{uniRouterV2}" from = "0x70997970C51812dc3A010C7d01b50e0d17dc79C8" signature = "swapExactTokensForTokens(uint256 amountIn, uint256 amountOutMin, address[] path, address to, uint256 deadline) external returns (uint256[] memory)" @@ -158,6 +159,7 @@ min = "1" max = "100000000000000000" [[spam]] +kind = "uniswap_v2" to = "{uniRouterV2}" from = "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC" signature = "swapExactTokensForTokens(uint256 amountIn, uint256 amountOutMin, address[] path, address to, uint256 deadline) external returns (uint256[] memory)" @@ -172,4 +174,4 @@ args = [ [[spam.fuzz]] param = "amountIn" min = "100000" -max = "10000000000" \ No newline at end of file +max = "10000000000" diff --git a/sqlite_db/src/lib.rs b/sqlite_db/src/lib.rs index faf4ea1..a25859a 100644 --- a/sqlite_db/src/lib.rs +++ b/sqlite_db/src/lib.rs @@ -95,6 +95,7 @@ struct RunTxRow { end_timestamp: usize, block_number: u64, gas_used: String, + kind: Option, } impl RunTxRow { @@ -106,6 +107,7 @@ impl RunTxRow { end_timestamp: row.get(3)?, block_number: row.get(4)?, gas_used: row.get(5)?, + kind: row.get(6)?, }) } } @@ -119,6 +121,7 @@ impl From for RunTx { end_timestamp: row.end_timestamp, block_number: row.block_number, gas_used: row.gas_used.parse().expect("invalid gas_used parameter"), + kind: row.kind, } } } @@ -151,6 +154,7 @@ impl DbOps for SqliteDb { end_timestamp INTEGER NOT NULL, block_number INTEGER NOT NULL, gas_used TEXT NOT NULL, + kind TEXT, FOREIGN KEY(run_id) REFERENCES runs(runid) )", params![], @@ -178,7 +182,7 @@ impl DbOps for SqliteDb { fn get_run_txs(&self, run_id: u64) -> Result> { let pool = self.get_pool()?; let mut stmt = pool - .prepare("SELECT run_id, tx_hash, start_timestamp, end_timestamp, block_number, gas_used FROM run_txs WHERE run_id = ?1") + .prepare("SELECT run_id, tx_hash, start_timestamp, end_timestamp, block_number, gas_used, kind FROM run_txs WHERE run_id = ?1") .map_err(|e| ContenderError::with_err(e, "failed to prepare statement"))?; let rows = stmt @@ -233,17 +237,39 @@ impl DbOps for SqliteDb { Ok(res.into()) } + fn get_named_tx_by_address(&self, address: &Address) -> Result { + let pool = self.get_pool()?; + let mut stmt = pool + .prepare( + "SELECT name, tx_hash, contract_address FROM named_txs WHERE contract_address = ?1 ORDER BY id DESC LIMIT 1", + ) + .map_err(|e| ContenderError::with_err(e, "failed to prepare statement"))?; + + let row = stmt + .query_map(params![address.encode_hex()], |row| { + NamedTxRow::from_row(row) + }) + .map_err(|e| ContenderError::with_err(e, "failed to map row"))?; + 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()) + } + fn insert_run_txs(&self, run_id: u64, run_txs: Vec) -> Result<()> { let pool = self.get_pool()?; let stmts = run_txs.iter().map(|tx| { format!( - "INSERT INTO run_txs (run_id, tx_hash, start_timestamp, end_timestamp, block_number, gas_used) VALUES ({}, '{}', {}, {}, {}, '{}');", + "INSERT INTO run_txs (run_id, tx_hash, start_timestamp, end_timestamp, block_number, gas_used, kind) VALUES ({}, '{}', {}, {}, {}, '{}', '{}');", run_id, tx.tx_hash.encode_hex(), tx.start_timestamp, tx.end_timestamp, tx.block_number, - tx.gas_used.to_string() + tx.gas_used.to_string(), + tx.kind.to_owned().unwrap_or("NULL".to_string()), ) }); pool.execute_batch(&format!( @@ -322,6 +348,7 @@ mod tests { end_timestamp: 200, block_number: 1, gas_used: 100, + kind: Some("test".to_string()), }, RunTx { tx_hash: TxHash::from_slice(&[1u8; 32]), @@ -329,6 +356,7 @@ mod tests { end_timestamp: 300, block_number: 2, gas_used: 200, + kind: Some("test".to_string()), }, ]; db.insert_run_txs(run_id as u64, run_txs).unwrap(); diff --git a/src/db/mod.rs b/src/db/mod.rs index 8b5bc67..1fd19ff 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -12,6 +12,7 @@ pub struct RunTx { pub end_timestamp: usize, pub block_number: u64, pub gas_used: u128, + pub kind: Option, } #[derive(Debug, Serialize, Clone)] @@ -49,6 +50,8 @@ pub trait DbOps { fn get_named_tx(&self, name: &str) -> 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>; @@ -77,6 +80,14 @@ impl DbOps for MockDb { 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(()) } diff --git a/src/generator/mod.rs b/src/generator/mod.rs index ddd1e70..51f13fc 100644 --- a/src/generator/mod.rs +++ b/src/generator/mod.rs @@ -27,18 +27,71 @@ pub mod types; /// Utility functions used in the generator module. pub mod util; -impl NamedTxRequest { - pub fn with_name(name: &str, tx: TransactionRequest) -> Self { +/// 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: Some(name.to_string()), + 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, tx } + Self { + name: None, + kind: None, + tx, + } } } @@ -103,11 +156,13 @@ where // lookup placeholder values in DB & update map before templating templater.find_placeholder_values(&step.bytecode, &mut placeholder_map, db)?; - // create txs with template values - let tx = NamedTxRequest::with_name( - &step.name, + // create tx with template values + let tx = NamedTxRequestBuilder::new( templater.template_contract_deploy(step, &placeholder_map)?, - ); + ) + .with_name(&step.name) + .build(); + let handle = on_create_step(tx.to_owned())?; if let Some(handle) = handle { handle.await.map_err(|e| { @@ -123,10 +178,11 @@ where // lookup placeholders in DB & update map before templating templater.find_fncall_placeholders(step, db, &mut placeholder_map)?; - // create txs with template values + // setup tx with template values let tx: NamedTxRequest = templater .template_function_call(step, &placeholder_map)? .into(); + let handle = on_setup_step(tx.to_owned())?; if let Some(handle) = handle { handle.await.map_err(|e| { @@ -188,9 +244,12 @@ where let mut step = step.to_owned(); step.args = Some(args); - let tx: NamedTxRequest = templater - .template_function_call(&step, &placeholder_map)? - .into(); + let tx = NamedTxRequest::new( + templater.template_function_call(&step, &placeholder_map)?, + None, + step.kind, + ); + let handle = on_spam_setup(tx.to_owned())?; if let Some(handle) = handle { handle diff --git a/src/generator/types.rs b/src/generator/types.rs index a7835b7..23bc81e 100644 --- a/src/generator/types.rs +++ b/src/generator/types.rs @@ -1,4 +1,5 @@ use alloy::{ + network::AnyNetwork, primitives::U256, providers::RootProvider, rpc::types::TransactionRequest, @@ -8,11 +9,13 @@ use serde::{Deserialize, Serialize}; use std::collections::HashMap; use tokio::task::JoinHandle; -pub type RpcProvider = RootProvider>; +pub type EthProvider = RootProvider>; +pub type AnyProvider = RootProvider, AnyNetwork>; #[derive(Clone, Debug)] pub struct NamedTxRequest { pub name: Option, + pub kind: Option, pub tx: TransactionRequest, } @@ -30,6 +33,8 @@ pub struct FunctionCallDefinition { pub value: Option, /// Parameters to fuzz during the test. pub fuzz: Option>, + /// Optional type of the spam transaction for categorization. + pub kind: Option } #[derive(Clone, Deserialize, Debug, Serialize)] diff --git a/src/spammer/blockwise.rs b/src/spammer/blockwise.rs index 8c575a0..2c5fe50 100644 --- a/src/spammer/blockwise.rs +++ b/src/spammer/blockwise.rs @@ -2,12 +2,12 @@ use crate::db::DbOps; use crate::error::ContenderError; use crate::generator::seeder::Seeder; use crate::generator::templater::Templater; -use crate::generator::types::{PlanType, RpcProvider}; +use crate::generator::types::{AnyProvider, EthProvider, PlanType}; use crate::generator::{Generator, PlanConfig}; use crate::test_scenario::TestScenario; use crate::Result; use alloy::hex::ToHexExt; -use alloy::network::TransactionBuilder; +use alloy::network::{AnyNetwork, TransactionBuilder}; use alloy::primitives::FixedBytes; use alloy::providers::{Provider, ProviderBuilder}; use futures::StreamExt; @@ -28,7 +28,8 @@ where { scenario: TestScenario, msg_handler: Arc, - rpc_client: Arc, + rpc_client: AnyProvider, + eth_client: Arc, callback_handler: Arc, } @@ -40,17 +41,22 @@ where P: PlanConfig + Templater + Send + Sync, { pub fn new(scenario: TestScenario, callback_handler: F) -> Self { - let rpc_client = Arc::new(ProviderBuilder::new().on_http(scenario.rpc_url.to_owned())); + let rpc_client = ProviderBuilder::new() + .network::() + .on_http(scenario.rpc_url.to_owned()); + let eth_client = Arc::new(ProviderBuilder::new().on_http(scenario.rpc_url.to_owned())); let msg_handler = Arc::new(TxActorHandle::new( 12, scenario.db.clone(), - rpc_client.clone(), + Arc::new(rpc_client.to_owned()), )); + let callback_handler = Arc::new(callback_handler); Self { scenario, rpc_client, - callback_handler: Arc::new(callback_handler), + eth_client, + callback_handler, msg_handler, } } @@ -165,7 +171,7 @@ where if !gas_limits.contains_key(fn_sig.as_slice()) { let gas_limit = self - .rpc_client + .eth_client .estimate_gas(&tx.tx.to_owned()) .await .map_err(|e| ContenderError::with_err(e, "failed to estimate gas"))?; @@ -173,7 +179,7 @@ where } // clone muh Arcs - let rpc_client = self.rpc_client.clone(); + let eth_client = self.eth_client.clone(); let callback_handler = self.callback_handler.clone(); // query hashmaps for gaslimit & signer of this tx @@ -193,7 +199,7 @@ where tasks.push(task::spawn(async move { let provider = ProviderBuilder::new() .wallet(signer) - .on_provider(rpc_client); + .on_provider(eth_client); let full_tx = tx_req .clone() @@ -216,6 +222,9 @@ where HashMap::from_iter([( "start_timestamp".to_owned(), start_timestamp.to_string(), + ), ( + "kind".to_owned(), + String::from("spam"), )]) .into(), Some(tx_handler), diff --git a/src/spammer/mod.rs b/src/spammer/mod.rs index 2212a35..3a8b29a 100644 --- a/src/spammer/mod.rs +++ b/src/spammer/mod.rs @@ -3,8 +3,8 @@ pub mod timed; pub mod tx_actor; pub mod util; -use crate::generator::{types::RpcProvider, NamedTxRequest}; -use alloy::providers::{PendingTransactionBuilder, PendingTransactionConfig}; +use crate::generator::{types::AnyProvider, NamedTxRequest}; +use alloy::providers::PendingTransactionConfig; use std::{collections::HashMap, sync::Arc}; use tokio::task::JoinHandle; use tx_actor::TxActorHandle; @@ -35,11 +35,11 @@ impl NilCallback { } pub struct LogCallback { - pub rpc_provider: Arc, + pub rpc_provider: Arc, } impl LogCallback { - pub fn new(rpc_provider: Arc) -> Self { + pub fn new(rpc_provider: Arc) -> Self { Self { rpc_provider } } } @@ -65,18 +65,19 @@ impl OnTxSent for LogCallback { extra: Option>, tx_actor: Option>, ) -> Option> { - let rpc = self.rpc_provider.clone(); let start_timestamp = extra .as_ref() .map(|e| e.get("start_timestamp").map(|t| t.parse::())) .flatten()? .unwrap_or(0); + let kind = extra + .as_ref() + .map(|e| e.get("kind").map(|k| k.to_string())) + .flatten(); let handle = tokio::task::spawn(async move { - let res = PendingTransactionBuilder::from_config(&rpc, tx_response); - let tx_hash = res.tx_hash(); if let Some(tx_actor) = tx_actor { tx_actor - .cache_run_tx(*tx_hash, start_timestamp) + .cache_run_tx(*tx_response.tx_hash(), start_timestamp, kind) .await .expect("failed to cache run tx"); } diff --git a/src/spammer/timed.rs b/src/spammer/timed.rs index 260df37..7e4e135 100644 --- a/src/spammer/timed.rs +++ b/src/spammer/timed.rs @@ -3,7 +3,7 @@ use crate::{ generator::{ seeder::Seeder, templater::Templater, - types::{PlanType, RpcProvider}, + types::{EthProvider, PlanType}, Generator, PlanConfig, }, spammer::OnTxSent, @@ -24,7 +24,7 @@ where P: PlanConfig + Templater + Send + Sync, { scenario: TestScenario, - rpc_client: Arc, + rpc_client: Arc, callback_handler: Arc, } diff --git a/src/spammer/tx_actor.rs b/src/spammer/tx_actor.rs index cf9b161..d70c724 100644 --- a/src/spammer/tx_actor.rs +++ b/src/spammer/tx_actor.rs @@ -6,13 +6,14 @@ use tokio::sync::{mpsc, oneshot}; use crate::{ db::{DbOps, RunTx}, error::ContenderError, - generator::types::RpcProvider, + generator::types::AnyProvider, }; enum TxActorMessage { SentRunTx { tx_hash: TxHash, start_timestamp: usize, + kind: Option, on_receipt: oneshot::Sender<()>, }, FlushCache { @@ -29,20 +30,22 @@ where receiver: mpsc::Receiver, db: Arc, cache: Vec, - rpc: Arc, + rpc: Arc, } #[derive(Clone, PartialEq)] pub struct PendingRunTx { tx_hash: TxHash, start_timestamp: usize, + kind: Option, } impl PendingRunTx { - pub fn new(tx_hash: TxHash, start_timestamp: usize) -> Self { + pub fn new(tx_hash: TxHash, start_timestamp: usize, kind: Option<&str>) -> Self { Self { tx_hash, start_timestamp, + kind: kind.map(|s| s.to_owned()), } } } @@ -54,7 +57,7 @@ where pub fn new( receiver: mpsc::Receiver, db: Arc, - rpc: Arc, + rpc: Arc, ) -> Self { Self { receiver, @@ -72,11 +75,13 @@ where TxActorMessage::SentRunTx { tx_hash, start_timestamp, + kind, on_receipt, } => { let run_tx = PendingRunTx { tx_hash, start_timestamp, + kind, }; self.cache.push(run_tx.to_owned()); on_receipt.send(()).map_err(|_| { @@ -147,6 +152,7 @@ where end_timestamp: target_block.header.timestamp as usize, block_number: target_block.header.number, gas_used: receipt.gas_used, + kind: pending_tx.kind, } }) .collect::>(); @@ -176,7 +182,7 @@ impl TxActorHandle { pub fn new( bufsize: usize, db: Arc, - rpc: Arc, + rpc: Arc, ) -> Self { let (sender, receiver) = mpsc::channel(bufsize); let mut actor = TxActor::new(receiver, db, rpc); @@ -190,12 +196,14 @@ impl TxActorHandle { &self, tx_hash: TxHash, start_timestamp: usize, + kind: Option, ) -> Result<(), Box> { let (sender, receiver) = oneshot::channel(); self.sender .send(TxActorMessage::SentRunTx { tx_hash, start_timestamp, + kind, on_receipt: sender, }) .await?; diff --git a/src/test_scenario.rs b/src/test_scenario.rs index 27abff2..6086203 100644 --- a/src/test_scenario.rs +++ b/src/test_scenario.rs @@ -1,6 +1,7 @@ use crate::db::{DbOps, NamedTx}; use crate::error::ContenderError; use crate::generator::templater::Templater; +use crate::generator::NamedTxRequest; use crate::generator::{seeder::Seeder, types::PlanType, Generator, PlanConfig}; use alloy::hex::ToHexExt; use alloy::network::{EthereumWallet, TransactionBuilder}; @@ -79,21 +80,24 @@ where "failed to get 'from' address", None, ))?; - let wallet = self + let wallet_conf = self .wallet_map .get(from) .expect(&format!("couldn't find wallet for 'from' address {}", from)) .to_owned(); - let provider = ProviderBuilder::new() + let wallet = ProviderBuilder::new() // simple_nonce_management is unperformant but it's OK bc we're just deploying .with_simple_nonce_management() - .wallet(wallet) + .wallet(wallet_conf) .on_http(self.rpc_url.to_owned()); - println!("deploying contract: {:?}", tx_req.name); + println!( + "deploying contract: {:?}", + tx_req.name.as_ref().unwrap_or(&"".to_string()) + ); let handle = tokio::task::spawn(async move { // estimate gas limit - let gas_limit = provider + let gas_limit = wallet .estimate_gas(&tx_req.tx) .await .expect("failed to estimate gas"); @@ -105,18 +109,20 @@ where .with_chain_id(chain_id) .with_gas_limit(gas_limit); - let res = provider + let res = wallet .send_transaction(tx) .await .expect("failed to send tx"); let receipt = res.get_receipt().await.expect("failed to get receipt"); - println!("contract address: {:?}", receipt.contract_address); - let contract_address = receipt.contract_address; + println!( + "contract address: {}", + receipt.contract_address.unwrap_or_default() + ); db.insert_named_txs( NamedTx::new( tx_req.name.unwrap_or_default(), receipt.transaction_hash, - contract_address, + receipt.contract_address, ) .into(), ) @@ -132,12 +138,13 @@ where pub async fn run_setup(&self) -> Result<(), ContenderError> { self.load_txs(PlanType::Setup(|tx_req| { /* callback */ + println!("{}", self.format_setup_log(&tx_req)); + // copy data/refs from self before spawning the task let from = tx_req.tx.from.as_ref().ok_or(ContenderError::SetupError( "failed to get 'from' address", None, ))?; - println!("from: {:?}", from); let wallet = self .wallet_map .get(from) @@ -147,22 +154,19 @@ where ))? .to_owned(); let db = self.db.clone(); - let provider = ProviderBuilder::new() - .with_simple_nonce_management() - .wallet(wallet) - .on_http(self.rpc_url.to_owned()); - - println!("running setup: {:?}", tx_req.name); + let rpc_url = self.rpc_url.clone(); let handle = tokio::task::spawn(async move { - let chain_id = provider - .get_chain_id() - .await - .expect("failed to get chain id"); - let gas_price = provider + let wallet = ProviderBuilder::new() + .with_simple_nonce_management() + .wallet(wallet) + .on_http(rpc_url); + + let chain_id = wallet.get_chain_id().await.expect("failed to get chain id"); + let gas_price = wallet .get_gas_price() .await .expect("failed to get gas price"); - let gas_limit = provider + let gas_limit = wallet .estimate_gas(&tx_req.tx) .await .expect("failed to estimate gas"); @@ -171,10 +175,12 @@ where .with_gas_price(gas_price) .with_chain_id(chain_id) .with_gas_limit(gas_limit); - let res = provider + let res = wallet .send_transaction(tx) .await .expect("failed to send tx"); + + // get receipt using provider (not wallet) to allow any receipt type (support non-eth chains) let receipt = res.get_receipt().await.expect("failed to get receipt"); if let Some(name) = tx_req.name { db.insert_named_txs( @@ -190,6 +196,37 @@ where Ok(()) } + + fn format_setup_log(&self, tx_req: &NamedTxRequest) -> String { + let to_address = tx_req.tx.to.unwrap_or_default(); + let to_address = to_address.to(); + + // 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() + }); + + format!( + "running setup: from={} to={} {}", + tx_req + .tx + .from + .as_ref() + .map(|a| a.encode_hex()) + .unwrap_or_default(), + if let Some(to) = to_name { + to + } else { + to_address.map(|a| a.encode_hex()).unwrap_or_default() + }, + if let Some(kind) = &tx_req.kind { + format!("kind={}", kind) + } else { + "".to_string() + }, + ) + } } impl Generator for TestScenario @@ -266,6 +303,7 @@ pub mod tests { ] .into(), fuzz: None, + kind: None, }, FunctionCallDefinition { to: "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D".to_owned(), @@ -280,6 +318,7 @@ pub mod tests { ] .into(), fuzz: None, + kind: None, }, ]) } @@ -303,6 +342,7 @@ pub mod tests { max: None, }] .into(), + kind: None, }; Ok(vec![ fn_call("0xbeef", "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"), diff --git a/testfile/src/lib.rs b/testfile/src/lib.rs index f254a09..496ab27 100644 --- a/testfile/src/lib.rs +++ b/testfile/src/lib.rs @@ -154,6 +154,7 @@ pub mod tests { .into(), fuzz: None, value: None, + kind: None, }] .into(), } @@ -172,6 +173,7 @@ pub mod tests { data.to_owned(), ] .into(), + kind: None, fuzz: vec![FuzzParam { param: "x".to_string(), min: None, @@ -210,6 +212,7 @@ pub mod tests { "0xdead".to_owned(), ] .into(), + kind: None, fuzz: None, }, FunctionCallDefinition { @@ -224,6 +227,7 @@ pub mod tests { "0xbeef".to_owned(), ] .into(), + kind: None, fuzz: None, }, ] @@ -261,7 +265,7 @@ pub mod tests { #[test] fn parses_testconfig_toml() { - let test_file = TestConfig::from_file("../cli/univ2ConfigTest.toml").unwrap(); + let test_file = TestConfig::from_file("testConfig.toml").unwrap(); assert!(test_file.env.is_some()); assert!(test_file.setup.is_some()); assert!(test_file.spam.is_some()); @@ -269,21 +273,20 @@ pub mod tests { let setup = test_file.setup.unwrap(); let spam = test_file.spam.unwrap(); - assert_eq!( - env.get("feeToSetter").unwrap(), - "f39Fd6e51aad88F6F4ce6aB8827279cffFb92266" - ); + assert_eq!(env.get("env1").unwrap(), "env1"); assert_eq!( spam[0].from, "0x70997970C51812dc3A010C7d01b50e0d17dc79C8".to_owned() ); - assert_eq!(setup.len(), 11); - assert_eq!(setup[0].value, Some("10000000000000000000".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[1].fuzz.as_ref().unwrap()[0].min, - Some(U256::from(100000)) + 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())); } fn print_testconfig(cfg: &str) { diff --git a/testfile/testConfig.toml b/testfile/testConfig.toml new file mode 100644 index 0000000..5371354 --- /dev/null +++ b/testfile/testConfig.toml @@ -0,0 +1,26 @@ +[env] +env1 = "env1" +env2 = "env2" + +[[setup]] +to = "0xE46CcF40134e7ad524529B25Ce04e39BC2B51cDc" +from = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" +signature = "function deposit() public payable" +value = "1234" + + +### the spam step will be repeated +[[spam]] +kind = "test" +to = "0xE46CcF40134e7ad524529B25Ce04e39BC2B51cDc" +from = "0x70997970C51812dc3A010C7d01b50e0d17dc79C8" +signature = "test(uint256 amountIn, address to) external returns (uint256[] memory)" +args = [ + "1000000000000000000", + "0x70997970C51812dc3A010C7d01b50e0d17dc79C8", +] + +[[spam.fuzz]] +param = "amountIn" +min = "1" +max = "100000000000000000"