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..b185abb 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 @@ -237,13 +241,14 @@ impl DbOps for SqliteDb { 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 +327,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 +335,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..9dc529d 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)] diff --git a/src/generator/mod.rs b/src/generator/mod.rs index ddd1e70..c8ad566 100644 --- a/src/generator/mod.rs +++ b/src/generator/mod.rs @@ -31,14 +31,19 @@ impl NamedTxRequest { pub fn with_name(name: &str, tx: TransactionRequest) -> Self { Self { name: Some(name.to_string()), + kind: None, tx, } } + + pub fn set_kind(&mut self, kind: String) { + self.kind = Some(kind); + } } impl From for NamedTxRequest { fn from(tx: TransactionRequest) -> Self { - Self { name: None, tx } + Self { name: None, kind: None, tx } } } @@ -127,6 +132,7 @@ where 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 +194,9 @@ where let mut step = step.to_owned(); step.args = Some(args); - let tx: NamedTxRequest = templater - .template_function_call(&step, &placeholder_map)? - .into(); + let mut tx: NamedTxRequest = NamedTxRequest::from(templater + .template_function_call(&step, &placeholder_map)?); + tx.set_kind(step.kind.to_owned().unwrap_or("default".to_string())); 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..33f7071 100644 --- a/src/generator/types.rs +++ b/src/generator/types.rs @@ -13,6 +13,7 @@ pub type RpcProvider = RootProvider>; #[derive(Clone, Debug)] pub struct NamedTxRequest { pub name: Option, + pub kind: Option, pub tx: TransactionRequest, } @@ -30,6 +31,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..dfc8789 100644 --- a/src/spammer/blockwise.rs +++ b/src/spammer/blockwise.rs @@ -216,6 +216,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..207f710 100644 --- a/src/spammer/mod.rs +++ b/src/spammer/mod.rs @@ -71,12 +71,16 @@ impl OnTxSent for LogCallback { .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_hash, start_timestamp, kind) .await .expect("failed to cache run tx"); } diff --git a/src/spammer/tx_actor.rs b/src/spammer/tx_actor.rs index cf9b161..5e37d7f 100644 --- a/src/spammer/tx_actor.rs +++ b/src/spammer/tx_actor.rs @@ -13,6 +13,7 @@ enum TxActorMessage { SentRunTx { tx_hash: TxHash, start_timestamp: usize, + kind: Option, on_receipt: oneshot::Sender<()>, }, FlushCache { @@ -36,13 +37,15 @@ where 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) -> Self { Self { tx_hash, start_timestamp, + kind, } } } @@ -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::>(); @@ -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..85cb607 100644 --- a/src/test_scenario.rs +++ b/src/test_scenario.rs @@ -90,7 +90,7 @@ where .wallet(wallet) .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 @@ -110,7 +110,10 @@ where .await .expect("failed to send tx"); let receipt = res.get_receipt().await.expect("failed to get receipt"); - println!("contract address: {:?}", receipt.contract_address); + println!("contract address: {:?}", receipt.contract_address + .as_ref() + .map(|a| a.encode_hex()) + .unwrap_or("".to_string())); let contract_address = receipt.contract_address; db.insert_named_txs( NamedTx::new( @@ -152,7 +155,7 @@ where .wallet(wallet) .on_http(self.rpc_url.to_owned()); - println!("running setup: {:?}", tx_req.name); + println!("running setup: {:?}", tx_req.name.as_ref().unwrap_or(&"".to_string())); let handle = tokio::task::spawn(async move { let chain_id = provider .get_chain_id() @@ -266,6 +269,7 @@ pub mod tests { ] .into(), fuzz: None, + kind: None, }, FunctionCallDefinition { to: "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D".to_owned(), @@ -280,6 +284,7 @@ pub mod tests { ] .into(), fuzz: None, + kind: None, }, ]) } @@ -303,6 +308,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..7ab2c12 100644 --- a/testfile/src/lib.rs +++ b/testfile/src/lib.rs @@ -105,7 +105,7 @@ pub mod tests { use alloy::{ hex::ToHexExt, node_bindings::{Anvil, AnvilInstance}, - primitives::{Address, U256}, + primitives::Address, signers::local::PrivateKeySigner, }; use contender_core::{ @@ -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()); @@ -270,20 +274,17 @@ pub mod tests { let spam = test_file.spam.unwrap(); assert_eq!( - env.get("feeToSetter").unwrap(), - "f39Fd6e51aad88F6F4ce6aB8827279cffFb92266" + 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[1].fuzz.as_ref().unwrap()[0].min, - Some(U256::from(100000)) - ); + 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"