From 092deec1488e2cf17236390f886d7d3731f420b6 Mon Sep 17 00:00:00 2001 From: zeroXbrock <2791467+zeroXbrock@users.noreply.github.com> Date: Thu, 14 Nov 2024 13:48:45 -0800 Subject: [PATCH 1/7] idiomatic workspace structure --- Cargo.lock | 32 +++++------ Cargo.toml | 57 ++++++++++++------- cli/Cargo.toml | 15 ----- {cli => crates/cli}/.gitignore | 0 {cli => crates/cli}/Cargo.lock | 0 crates/cli/Cargo.toml | 19 +++++++ {cli => crates/cli}/src/commands.rs | 0 {cli => crates/cli}/src/main.rs | 0 {cli => crates/cli}/testfile.toml | 0 {cli => crates/cli}/univ2ConfigTest.toml | 0 crates/core/Cargo.toml | 21 +++++++ {src => crates/core/src}/bundle_provider.rs | 0 {src => crates/core/src}/db/mod.rs | 0 {src => crates/core/src}/error.rs | 0 {src => crates/core/src}/generator/mod.rs | 0 .../core/src}/generator/named_txs.rs | 0 .../core/src}/generator/seeder/mod.rs | 0 .../core/src}/generator/seeder/rand_seed.rs | 0 .../core/src}/generator/templater.rs | 0 {src => crates/core/src}/generator/types.rs | 0 {src => crates/core/src}/generator/util.rs | 0 {src => crates/core/src}/lib.rs | 0 {src => crates/core/src}/spammer/blockwise.rs | 0 {src => crates/core/src}/spammer/mod.rs | 0 {src => crates/core/src}/spammer/timed.rs | 0 {src => crates/core/src}/spammer/tx_actor.rs | 0 {src => crates/core/src}/spammer/util.rs | 0 {src => crates/core/src}/test_scenario.rs | 0 {sqlite_db => crates/sqlite_db}/Cargo.toml | 8 +-- {sqlite_db => crates/sqlite_db}/src/lib.rs | 0 {testfile => crates/testfile}/Cargo.toml | 4 +- {testfile => crates/testfile}/src/lib.rs | 0 {testfile => crates/testfile}/src/types.rs | 0 {testfile => crates/testfile}/testConfig.toml | 0 34 files changed, 100 insertions(+), 56 deletions(-) delete mode 100644 cli/Cargo.toml rename {cli => crates/cli}/.gitignore (100%) rename {cli => crates/cli}/Cargo.lock (100%) create mode 100644 crates/cli/Cargo.toml rename {cli => crates/cli}/src/commands.rs (100%) rename {cli => crates/cli}/src/main.rs (100%) rename {cli => crates/cli}/testfile.toml (100%) rename {cli => crates/cli}/univ2ConfigTest.toml (100%) create mode 100644 crates/core/Cargo.toml rename {src => crates/core/src}/bundle_provider.rs (100%) rename {src => crates/core/src}/db/mod.rs (100%) rename {src => crates/core/src}/error.rs (100%) rename {src => crates/core/src}/generator/mod.rs (100%) rename {src => crates/core/src}/generator/named_txs.rs (100%) rename {src => crates/core/src}/generator/seeder/mod.rs (100%) rename {src => crates/core/src}/generator/seeder/rand_seed.rs (100%) rename {src => crates/core/src}/generator/templater.rs (100%) rename {src => crates/core/src}/generator/types.rs (100%) rename {src => crates/core/src}/generator/util.rs (100%) rename {src => crates/core/src}/lib.rs (100%) rename {src => crates/core/src}/spammer/blockwise.rs (100%) rename {src => crates/core/src}/spammer/mod.rs (100%) rename {src => crates/core/src}/spammer/timed.rs (100%) rename {src => crates/core/src}/spammer/tx_actor.rs (100%) rename {src => crates/core/src}/spammer/util.rs (100%) rename {src => crates/core/src}/test_scenario.rs (100%) rename {sqlite_db => crates/sqlite_db}/Cargo.toml (53%) rename {sqlite_db => crates/sqlite_db}/src/lib.rs (100%) rename {testfile => crates/testfile}/Cargo.toml (73%) rename {testfile => crates/testfile}/src/lib.rs (100%) rename {testfile => crates/testfile}/src/types.rs (100%) rename {testfile => crates/testfile}/testConfig.toml (100%) diff --git a/Cargo.lock b/Cargo.lock index 808e4f6..7e3beac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1143,32 +1143,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] -name = "contender" +name = "contender_cli" version = "0.1.0" dependencies = [ "alloy", - "alloy-serde 0.5.4", - "async-trait", - "eyre", - "futures", - "jsonrpsee", - "rand", + "clap", + "contender_core", + "contender_sqlite", + "contender_testfile", + "csv", "serde", - "serde_json", "tokio", ] [[package]] -name = "contender_cli" +name = "contender_core" version = "0.1.0" dependencies = [ "alloy", - "clap", - "contender", - "contender_sqlite", - "contender_testfile", - "csv", + "alloy-serde 0.5.4", + "async-trait", + "eyre", + "futures", + "jsonrpsee", + "rand", "serde", + "serde_json", "tokio", ] @@ -1177,7 +1177,7 @@ name = "contender_sqlite" version = "0.1.0" dependencies = [ "alloy", - "contender", + "contender_core", "r2d2", "r2d2_sqlite", "rusqlite", @@ -1189,7 +1189,7 @@ name = "contender_testfile" version = "0.1.0" dependencies = [ "alloy", - "contender", + "contender_core", "serde", "tokio", "toml", diff --git a/Cargo.toml b/Cargo.toml index b87d6e8..8018d30 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,30 +1,49 @@ -[package] -name = "contender" +[workspace] +members = [ + "crates/cli/", + "crates/core/", + "crates/sqlite_db/", + "crates/testfile/" +] + +resolver = "2" + +[workspace.package] +# name = "contender" version = "0.1.0" edition = "2021" -authors = ["Brock Smedley"] +rust-version = "1.80" +authors = ["Flashbots"] +license = "MIT OR Apache-2.0" +homepage = "https://github.com/flashbots/contender" +repository = "https://github.com/flashbots/contender" + +[workspace.dependencies] +contender_core = { path = "crates/core/" } +contender_sqlite = { path = "crates/sqlite_db/" } +contender_testfile = { path = "crates/testfile/" } + +eyre = "0.6.12" +tokio = { version = "1.40.0" } +alloy = { version = "0.3.6" } +serde = "1.0.209" -[lib] -name = "contender_core" -path = "src/lib.rs" +## cli +clap = { version = "4.5.16" } +csv = "1.3.0" -[dependencies] -alloy = { workspace = true, features = ["full", "node-bindings", "rpc-types-mev"] } -eyre = { workspace = true } +## core 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 = ["http-client", "client-core"] } +jsonrpsee = { version = "0.24" } alloy-serde = "0.5.4" serde_json = "1.0.132" -[workspace] -members = ["cli", "sqlite_db", "testfile"] +## sqlite +r2d2_sqlite = "0.25.0" +rusqlite = "0.32.1" +r2d2 = "0.8.10" -[workspace.dependencies] -eyre = "0.6.12" -tokio = { version = "1.40.0" } -alloy = { version = "0.3.6" } -serde = "1.0.209" +## testfile +toml = "0.8.19" diff --git a/cli/Cargo.toml b/cli/Cargo.toml deleted file mode 100644 index 7187fc7..0000000 --- a/cli/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "contender_cli" -version = "0.1.0" -edition = "2021" - -[dependencies] -tokio = { workspace = true, features = ["rt-multi-thread"] } -serde = { workspace = true } -contender = { path = "../" } -contender_sqlite = { path = "../sqlite_db" } -contender_testfile = { path = "../testfile" } - -clap = { version = "4.5.16", features = ["derive"] } -alloy = { version = "0.3.6", features = ["full", "node-bindings"] } -csv = "1.3.0" diff --git a/cli/.gitignore b/crates/cli/.gitignore similarity index 100% rename from cli/.gitignore rename to crates/cli/.gitignore diff --git a/cli/Cargo.lock b/crates/cli/Cargo.lock similarity index 100% rename from cli/Cargo.lock rename to crates/cli/Cargo.lock diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml new file mode 100644 index 0000000..eb2a826 --- /dev/null +++ b/crates/cli/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "contender_cli" +version = "0.1.0" +edition = "2021" + +[[bin]] +name = "contender" +path = "src/main.rs" + +[dependencies] +tokio = { workspace = true, features = ["rt-multi-thread"] } +serde = { workspace = true } +contender_core = { workspace = true } +contender_sqlite = { workspace = true } +contender_testfile = { workspace = true } + +clap = { workspace = true, features = ["derive"] } +alloy = { workspace = true, features = ["full", "node-bindings"] } +csv = { workspace = true } diff --git a/cli/src/commands.rs b/crates/cli/src/commands.rs similarity index 100% rename from cli/src/commands.rs rename to crates/cli/src/commands.rs diff --git a/cli/src/main.rs b/crates/cli/src/main.rs similarity index 100% rename from cli/src/main.rs rename to crates/cli/src/main.rs diff --git a/cli/testfile.toml b/crates/cli/testfile.toml similarity index 100% rename from cli/testfile.toml rename to crates/cli/testfile.toml diff --git a/cli/univ2ConfigTest.toml b/crates/cli/univ2ConfigTest.toml similarity index 100% rename from cli/univ2ConfigTest.toml rename to crates/cli/univ2ConfigTest.toml diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml new file mode 100644 index 0000000..d62fb55 --- /dev/null +++ b/crates/core/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "contender_core" +version = "0.1.0" +edition = "2021" +authors = ["Brock Smedley"] + +[lib] +name = "contender_core" +path = "src/lib.rs" + +[dependencies] +alloy = { workspace = true, features = ["full", "node-bindings", "rpc-types-mev"] } +eyre = { workspace = true } +rand = { workspace = true } +serde = { workspace = true, features = ["derive"] } +futures = { workspace = true } +async-trait = { workspace = true } +tokio = { workspace = true } +jsonrpsee = { workspace = true, features = ["http-client", "client-core"] } +alloy-serde = { workspace = true } +serde_json = { workspace = true } diff --git a/src/bundle_provider.rs b/crates/core/src/bundle_provider.rs similarity index 100% rename from src/bundle_provider.rs rename to crates/core/src/bundle_provider.rs diff --git a/src/db/mod.rs b/crates/core/src/db/mod.rs similarity index 100% rename from src/db/mod.rs rename to crates/core/src/db/mod.rs diff --git a/src/error.rs b/crates/core/src/error.rs similarity index 100% rename from src/error.rs rename to crates/core/src/error.rs diff --git a/src/generator/mod.rs b/crates/core/src/generator/mod.rs similarity index 100% rename from src/generator/mod.rs rename to crates/core/src/generator/mod.rs diff --git a/src/generator/named_txs.rs b/crates/core/src/generator/named_txs.rs similarity index 100% rename from src/generator/named_txs.rs rename to crates/core/src/generator/named_txs.rs diff --git a/src/generator/seeder/mod.rs b/crates/core/src/generator/seeder/mod.rs similarity index 100% rename from src/generator/seeder/mod.rs rename to crates/core/src/generator/seeder/mod.rs diff --git a/src/generator/seeder/rand_seed.rs b/crates/core/src/generator/seeder/rand_seed.rs similarity index 100% rename from src/generator/seeder/rand_seed.rs rename to crates/core/src/generator/seeder/rand_seed.rs diff --git a/src/generator/templater.rs b/crates/core/src/generator/templater.rs similarity index 100% rename from src/generator/templater.rs rename to crates/core/src/generator/templater.rs diff --git a/src/generator/types.rs b/crates/core/src/generator/types.rs similarity index 100% rename from src/generator/types.rs rename to crates/core/src/generator/types.rs diff --git a/src/generator/util.rs b/crates/core/src/generator/util.rs similarity index 100% rename from src/generator/util.rs rename to crates/core/src/generator/util.rs diff --git a/src/lib.rs b/crates/core/src/lib.rs similarity index 100% rename from src/lib.rs rename to crates/core/src/lib.rs diff --git a/src/spammer/blockwise.rs b/crates/core/src/spammer/blockwise.rs similarity index 100% rename from src/spammer/blockwise.rs rename to crates/core/src/spammer/blockwise.rs diff --git a/src/spammer/mod.rs b/crates/core/src/spammer/mod.rs similarity index 100% rename from src/spammer/mod.rs rename to crates/core/src/spammer/mod.rs diff --git a/src/spammer/timed.rs b/crates/core/src/spammer/timed.rs similarity index 100% rename from src/spammer/timed.rs rename to crates/core/src/spammer/timed.rs diff --git a/src/spammer/tx_actor.rs b/crates/core/src/spammer/tx_actor.rs similarity index 100% rename from src/spammer/tx_actor.rs rename to crates/core/src/spammer/tx_actor.rs diff --git a/src/spammer/util.rs b/crates/core/src/spammer/util.rs similarity index 100% rename from src/spammer/util.rs rename to crates/core/src/spammer/util.rs diff --git a/src/test_scenario.rs b/crates/core/src/test_scenario.rs similarity index 100% rename from src/test_scenario.rs rename to crates/core/src/test_scenario.rs diff --git a/sqlite_db/Cargo.toml b/crates/sqlite_db/Cargo.toml similarity index 53% rename from sqlite_db/Cargo.toml rename to crates/sqlite_db/Cargo.toml index 1deb8cc..c0d0beb 100644 --- a/sqlite_db/Cargo.toml +++ b/crates/sqlite_db/Cargo.toml @@ -4,9 +4,9 @@ version = "0.1.0" edition = "2021" [dependencies] -contender = { path = "../" } -r2d2_sqlite = "0.25.0" -rusqlite = "0.32.1" -r2d2 = "0.8.10" +contender_core = { workspace = true } +r2d2_sqlite = {workspace = true} +rusqlite = {workspace = true} +r2d2 = {workspace = true} alloy = { workspace = true } serde = { workspace = true } \ No newline at end of file diff --git a/sqlite_db/src/lib.rs b/crates/sqlite_db/src/lib.rs similarity index 100% rename from sqlite_db/src/lib.rs rename to crates/sqlite_db/src/lib.rs diff --git a/testfile/Cargo.toml b/crates/testfile/Cargo.toml similarity index 73% rename from testfile/Cargo.toml rename to crates/testfile/Cargo.toml index c5d5996..d898b7a 100644 --- a/testfile/Cargo.toml +++ b/crates/testfile/Cargo.toml @@ -4,8 +4,8 @@ version = "0.1.0" edition = "2021" [dependencies] -toml = "0.8.19" +toml = {workspace = true} alloy = { workspace = true } serde = { workspace = true } tokio = { workspace = true } -contender = { path = "../" } +contender_core = { workspace = true } diff --git a/testfile/src/lib.rs b/crates/testfile/src/lib.rs similarity index 100% rename from testfile/src/lib.rs rename to crates/testfile/src/lib.rs diff --git a/testfile/src/types.rs b/crates/testfile/src/types.rs similarity index 100% rename from testfile/src/types.rs rename to crates/testfile/src/types.rs diff --git a/testfile/testConfig.toml b/crates/testfile/testConfig.toml similarity index 100% rename from testfile/testConfig.toml rename to crates/testfile/testConfig.toml From 6a1f9b56175148a2316243a35af3e6a6de5cfcfa Mon Sep 17 00:00:00 2001 From: zeroXbrock <2791467+zeroXbrock@users.noreply.github.com> Date: Thu, 14 Nov 2024 14:02:22 -0800 Subject: [PATCH 2/7] update contender_core import code in README --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8a2770a..91af804 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,7 @@ Contender is a high-performance Ethereum network spammer and testing tool design To install the Contender CLI, you need to have the [Rust toolchain](https://rustup.rs/) installed on your system. Then install from github: ```bash -cargo install --git https://github.com/flashbots/contender --bin contender_cli -alias contender=contender_cli +cargo install --git https://github.com/flashbots/contender --bin contender ``` ## Usage @@ -46,7 +45,7 @@ To use Contender as a library in your Rust project, add the crates you need to y ```toml [dependencies] ... -contender = { git = "https://github.com/flashbots/contender" } +contender_core = { git = "https://github.com/flashbots/contender" } contender_sqlite = { git = "https://github.com/flashbots/contender" } contender_testfile = { git = "https://github.com/flashbots/contender" } # not necessarily required, but recommended: From e0c93c2155b64eeeea99fc38dbacaf52f9d0a72d Mon Sep 17 00:00:00 2001 From: zeroXbrock <2791467+zeroXbrock@users.noreply.github.com> Date: Thu, 14 Nov 2024 14:15:26 -0800 Subject: [PATCH 3/7] move testfiles into scenarios/ --- .../testfile/testConfig.toml => scenarios/simple-test.toml | 0 {crates/cli => scenarios}/testfile.toml | 6 +++++- {crates/cli => scenarios}/univ2ConfigTest.toml | 0 3 files changed, 5 insertions(+), 1 deletion(-) rename crates/testfile/testConfig.toml => scenarios/simple-test.toml (100%) rename {crates/cli => scenarios}/testfile.toml (96%) rename {crates/cli => scenarios}/univ2ConfigTest.toml (100%) diff --git a/crates/testfile/testConfig.toml b/scenarios/simple-test.toml similarity index 100% rename from crates/testfile/testConfig.toml rename to scenarios/simple-test.toml diff --git a/crates/cli/testfile.toml b/scenarios/testfile.toml similarity index 96% rename from crates/cli/testfile.toml rename to scenarios/testfile.toml index c2cb664..59f662e 100644 --- a/crates/cli/testfile.toml +++ b/scenarios/testfile.toml @@ -35,9 +35,13 @@ value = "10000000000000000" [[spam.bundle.tx]] to = "{SpamMe}" from = "0xa0Ee7A142d267C1f36714E4a8F75612F20a79720" -signature = "consumeGas(uint256)" +signature = "consumeGas(uint256 gasAmount)" args = ["510000"] +[[spam.bundle.tx.fuzz]] +param = "gasAmount" +min = "100000" +max = "500000" [[spam.bundle.tx]] to = "{SpamMe}" diff --git a/crates/cli/univ2ConfigTest.toml b/scenarios/univ2ConfigTest.toml similarity index 100% rename from crates/cli/univ2ConfigTest.toml rename to scenarios/univ2ConfigTest.toml From b3f2515a331c1c0085d0bcd0ad39ce2a39ea2336 Mon Sep 17 00:00:00 2001 From: zeroXbrock <2791467+zeroXbrock@users.noreply.github.com> Date: Thu, 14 Nov 2024 15:36:35 -0800 Subject: [PATCH 4/7] allow tx 'value' field to be fuzzed --- README.md | 16 +++- crates/core/src/generator/mod.rs | 91 ++++++++++++++++--- crates/core/src/generator/types.rs | 4 +- crates/core/src/test_scenario.rs | 3 +- crates/testfile/src/lib.rs | 8 +- .../testfile/testConfig.toml | 0 scenarios/{testfile.toml => spamMe.toml} | 4 + 7 files changed, 106 insertions(+), 20 deletions(-) rename scenarios/simple-test.toml => crates/testfile/testConfig.toml (100%) rename scenarios/{testfile.toml => spamMe.toml} (97%) diff --git a/README.md b/README.md index 91af804..4bc1db8 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,9 @@ async fn main() -> Result<(), Box> { ## Configuration -Contender uses TOML files for test configuration. The key directives are: +Contender uses TOML files for test configuration. Single brackets `[]` indicate the item may only be specified once. Double brackets `[[]]` indicate an array, which allows the directive to be specified multiple times. + +The key directives are: - `[env]`: Defines environment variables that can be used throughout the configuration. @@ -106,9 +108,17 @@ Contender uses TOML files for test configuration. The key directives are: - `[[spam]]`: Describes the transactions to be repeatedly sent during the spam test. These form the core of the network stress test. -- `[[spam.fuzz]]`: (Sub-directive of `spam`) Configures fuzzing parameters for specific fields in spam transactions, allowing for randomized inputs within defined ranges. + - Spam directives can send bundles or single txs. + + - `[[spam.bundle.tx]]` defines transactions in a bundle + + - `[spam.tx]` defines a single transaction + + - Each tx directive can include various fields such as `to`, `from`, `signature`, `args`, and `value` to specify the details of the transactions or contract interactions. + + - `[[spam.bundle.tx.fuzz]]` or `[[spam.tx.fuzz]]`: Configures fuzzing parameters for specific fields in spam transactions, allowing for randomized inputs or ETH values within defined ranges. -Each directive can include various fields such as `to`, `from`, `signature`, `args`, and `value` to specify the details of the transactions or contract interactions. +See [scenarios/](./scenarios/) for examples. ## Architecture diff --git a/crates/core/src/generator/mod.rs b/crates/core/src/generator/mod.rs index 1e62d1e..7e21585 100644 --- a/crates/core/src/generator/mod.rs +++ b/crates/core/src/generator/mod.rs @@ -10,6 +10,7 @@ use crate::{ }; use alloy::primitives::U256; use async_trait::async_trait; +use core::panic; use named_txs::ExecutionRequest; pub use named_txs::NamedTxRequestBuilder; pub use seeder::rand_seed::RandSeed; @@ -36,6 +37,8 @@ pub mod types; /// Utility functions used in the generator module. pub mod util; +const VALUE_KEY: &str = "__tx_value_contender__"; + pub trait PlanConfig where K: Eq + Hash + Debug + Send + Sync, @@ -53,6 +56,31 @@ where fn get_spam_steps(&self) -> Result>; } +fn parse_map_key(fuzz: FuzzParam) -> Result { + if fuzz.param.is_none() && fuzz.value.is_none() { + return Err(ContenderError::SpamError( + "fuzz must specify one of `param` or `value`", + None, + )); + } + + let key = if let Some(param) = &fuzz.param { + param.to_owned() + } else if let Some(value) = fuzz.value { + if !value { + return Err(ContenderError::SpamError( + "fuzz.value is false, but no param is specified", + None, + )); + } + VALUE_KEY.to_owned() + } else { + panic!("this should never happen"); + }; + + Ok(key) +} + #[async_trait] pub trait Generator where @@ -70,15 +98,21 @@ where &self, num_values: usize, fuzz_args: &[FuzzParam], - ) -> HashMap> { + ) -> Result>> { let seed = self.get_fuzz_seeder(); - 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.param.to_owned(), values) - })) + let mut map = HashMap::>::new(); + + for fuzz in fuzz_args.iter() { + let key = parse_map_key(fuzz.to_owned())?; + map.insert( + key, + seed.seed_values(num_values, fuzz.min, fuzz.max) + .map(|v| v.as_u256()) + .collect(), + ); + } + + Ok(map) } async fn load_txs CallbackResult>( @@ -154,8 +188,9 @@ where // 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 + 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); + Ok(()) }; // finds placeholders in a function call definition and populates `placeholder_map` and `canonical_fuzz_map` with injectable values. @@ -163,19 +198,23 @@ where let res = templater.find_fncall_placeholders(tx, db, &mut placeholder_map); if let Err(e) = res { eprintln!("error finding placeholders: {}", e); - return; + return Err(ContenderError::SpamError( + "failed to find placeholder value", + Some(e.to_string()), + )); } - find_fuzz(tx); + find_fuzz(tx)?; + Ok(()) }; // populate maps for each step match step { SpamRequest::Tx(tx) => { - lookup_tx_placeholders(tx); + lookup_tx_placeholders(tx)?; } SpamRequest::Bundle(req) => { for tx in req.txs.iter() { - lookup_tx_placeholders(tx); + lookup_tx_placeholders(tx)?; } } }; @@ -187,8 +226,12 @@ where // returns a callback handle and the processed tx request let process_tx = |req| { let args = get_fuzzed_args(req, &canonical_fuzz_map, i); + let fuzz_tx_value = get_fuzzed_tx_value(req, &canonical_fuzz_map, i); let mut req = req.to_owned(); req.args = Some(args); + if fuzz_tx_value.is_some() { + req.value = fuzz_tx_value; + } let tx = NamedTxRequest::new( templater.template_function_call(&req, &placeholder_map)?, None, @@ -268,3 +311,25 @@ fn get_fuzzed_args( }) .collect() } + +fn get_fuzzed_tx_value( + tx: &FunctionCallDefinition, + fuzz_map: &HashMap>, + fuzz_idx: usize, +) -> Option { + if let Some(fuzz) = &tx.fuzz { + for fuzz_param in fuzz { + if let Some(value) = fuzz_param.value { + if value { + return Some( + fuzz_map + .get(VALUE_KEY) + .expect("value fuzzer was not initialized")[fuzz_idx] + .to_string(), + ); + } + } + } + } + None +} diff --git a/crates/core/src/generator/types.rs b/crates/core/src/generator/types.rs index a2eb1be..fe18c1e 100644 --- a/crates/core/src/generator/types.rs +++ b/crates/core/src/generator/types.rs @@ -67,7 +67,9 @@ pub struct CreateDefinition { #[derive(Clone, Deserialize, Debug, Serialize)] pub struct FuzzParam { /// Name of the parameter to fuzz. - pub param: String, + pub param: Option, + /// Fuzz the `value` field of the tx (ETH sent with the tx). + pub value: Option, /// Minimum value fuzzer will use. pub min: Option, /// Maximum value fuzzer will use. diff --git a/crates/core/src/test_scenario.rs b/crates/core/src/test_scenario.rs index 6e849fb..cd84413 100644 --- a/crates/core/src/test_scenario.rs +++ b/crates/core/src/test_scenario.rs @@ -343,7 +343,8 @@ pub mod tests { ] .into(), fuzz: vec![FuzzParam { - param: "x".to_string(), + param: Some("x".to_string()), + value: None, min: None, max: None, }] diff --git a/crates/testfile/src/lib.rs b/crates/testfile/src/lib.rs index 2ad7684..3dd5c3f 100644 --- a/crates/testfile/src/lib.rs +++ b/crates/testfile/src/lib.rs @@ -180,7 +180,8 @@ pub mod tests { .into(), kind: None, fuzz: vec![FuzzParam { - param: "x".to_string(), + param: Some("x".to_string()), + value: None, min: None, max: None, }] @@ -303,7 +304,10 @@ pub mod tests { ); 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].param.to_owned().unwrap(), + "amountIn" + ); assert_eq!(fncall.fuzz.as_ref().unwrap()[0].min, Some(U256::from(1))); assert_eq!( fncall.fuzz.as_ref().unwrap()[0].max, diff --git a/scenarios/simple-test.toml b/crates/testfile/testConfig.toml similarity index 100% rename from scenarios/simple-test.toml rename to crates/testfile/testConfig.toml diff --git a/scenarios/testfile.toml b/scenarios/spamMe.toml similarity index 97% rename from scenarios/testfile.toml rename to scenarios/spamMe.toml index 59f662e..14f9775 100644 --- a/scenarios/testfile.toml +++ b/scenarios/spamMe.toml @@ -13,6 +13,10 @@ from = "0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f" signature = "transfer()" value = "100000000000000" +[[spam.tx.fuzz]] +value = true +min = "10000000000000" +max = "1000000000000000" # spam bundle [[spam]] From 98c4f2eaf8025ed8e5bcf647170397843240efe1 Mon Sep 17 00:00:00 2001 From: zeroXbrock <2791467+zeroXbrock@users.noreply.github.com> Date: Thu, 14 Nov 2024 15:51:31 -0800 Subject: [PATCH 5/7] cleanup, remove unneeded field in example config --- crates/core/src/spammer/blockwise.rs | 37 ++++++++++++++-------------- scenarios/spamMe.toml | 1 - 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/crates/core/src/spammer/blockwise.rs b/crates/core/src/spammer/blockwise.rs index abd0381..e69fb84 100644 --- a/crates/core/src/spammer/blockwise.rs +++ b/crates/core/src/spammer/blockwise.rs @@ -9,6 +9,7 @@ use crate::generator::{Generator, PlanConfig}; use crate::spammer::ExecutionPayload; use crate::test_scenario::TestScenario; use crate::Result; +use alloy::consensus::Transaction; use alloy::eips::eip2718::Encodable2718; use alloy::hex::ToHexExt; use alloy::network::{AnyNetwork, EthereumWallet, TransactionBuilder}; @@ -245,22 +246,6 @@ where } ExecutionRequest::Tx(req) => { let tx_req = req.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 (tx_req, signer) = self .prepare_tx_req(&tx_req, gas_price, chain_id) @@ -268,10 +253,27 @@ where .map_err(|e| ContenderError::with_err(e, "failed to prepare tx"))?; // sign tx - let tx_envelope = tx_req.build(&signer).await.map_err(|e| { + let tx_envelope = tx_req.to_owned().build(&signer).await.map_err(|e| { ContenderError::with_err(e, "bad request: failed to build tx") })?; + println!( + "sending tx {} from={} to={:?} input={} value={}", + tx_envelope.tx_hash(), + tx_req.from.map(|s| s.encode_hex()).unwrap_or_default(), + tx_envelope.to().to(), + tx_req + .input + .input + .as_ref() + .map(|s| s.encode_hex()) + .unwrap_or_default(), + tx_req + .value + .map(|s| s.to_string()) + .unwrap_or_else(|| "0".to_owned()) + ); + ExecutionPayload::SignedTx(tx_envelope, req) } }; @@ -307,7 +309,6 @@ where ExecutionPayload::SignedTxBundle(signed_txs, reqs) => { 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); diff --git a/scenarios/spamMe.toml b/scenarios/spamMe.toml index 14f9775..0c8e0fe 100644 --- a/scenarios/spamMe.toml +++ b/scenarios/spamMe.toml @@ -11,7 +11,6 @@ from = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266" to = "0x90F79bf6EB2c4f870365E785982E1f101E93b906" from = "0x23618e81E3f5cdF7f54C3d65f7FBc0aBf5B21E8f" signature = "transfer()" -value = "100000000000000" [[spam.tx.fuzz]] value = true From 171cf6c277ac442396c144d5aba222bed5434202 Mon Sep 17 00:00:00 2001 From: zeroXbrock <2791467+zeroXbrock@users.noreply.github.com> Date: Thu, 14 Nov 2024 15:56:49 -0800 Subject: [PATCH 6/7] remove unused import --- crates/core/src/generator/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/core/src/generator/mod.rs b/crates/core/src/generator/mod.rs index 7e21585..cfcd53f 100644 --- a/crates/core/src/generator/mod.rs +++ b/crates/core/src/generator/mod.rs @@ -10,7 +10,6 @@ use crate::{ }; use alloy::primitives::U256; use async_trait::async_trait; -use core::panic; use named_txs::ExecutionRequest; pub use named_txs::NamedTxRequestBuilder; pub use seeder::rand_seed::RandSeed; From 3677be8ea48d33d2c30d7a7b2736182d807a5a0b Mon Sep 17 00:00:00 2001 From: zeroXbrock <2791467+zeroXbrock@users.noreply.github.com> Date: Thu, 14 Nov 2024 16:03:25 -0800 Subject: [PATCH 7/7] remove errant panic, improve logs for bad config --- crates/core/src/generator/mod.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/crates/core/src/generator/mod.rs b/crates/core/src/generator/mod.rs index cfcd53f..9cc2662 100644 --- a/crates/core/src/generator/mod.rs +++ b/crates/core/src/generator/mod.rs @@ -58,7 +58,13 @@ where fn parse_map_key(fuzz: FuzzParam) -> Result { if fuzz.param.is_none() && fuzz.value.is_none() { return Err(ContenderError::SpamError( - "fuzz must specify one of `param` or `value`", + "fuzz must specify either `param` or `value`", + None, + )); + } + if fuzz.param.is_some() && fuzz.value.is_some() { + return Err(ContenderError::SpamError( + "fuzz cannot specify both `param` and `value`; choose one per fuzz directive", None, )); } @@ -74,7 +80,7 @@ fn parse_map_key(fuzz: FuzzParam) -> Result { } VALUE_KEY.to_owned() } else { - panic!("this should never happen"); + return Err(ContenderError::SpamError("this should never happen", None)); }; Ok(key)