Skip to content

Conversation

@scharissis
Copy link
Contributor

@scharissis scharissis commented Dec 9, 2025

Description

  • Add campaign meta-scenarios with new contender campaign subcommand, supporting staged mixes and shorthand single-stage form.
  • Persist campaign metadata (campaign_id, campaign_name, stage_name, scenario_name) per run; each stage/scenario gets its own run_id grouped under a campaign.
  • Extend reporting with contender report --campaign [<campaign_id>] to aggregate metrics, emit campaign HTML/JSON, and link to per-run reports; default to latest campaign when none provided.
  • Add Handlebars-based campaign report template plus per-run report tweaks to display campaign context.
  • Include builtin scenario support inside campaigns, preflight validation, and example campaign files/docs; update DB schema version to 6 and changelog.

Motivation

We wish to run multiple scenarios in parallel without having to resort to hacks (such as running multiple contender instances simultaneously).

Solution

Summary

A campaign is a meta-scenario that sets up several existing scenarios once, then runs them in parallel across one or more stages with specified traffic shares. Each (stage, scenario) spam run gets its own run_id; all runs are grouped under a single campaign_id for reporting.

Components

  • New contender campaign <config_file> subcommand to run composite traffic.
  • Campaign config defines:
    • Scenarios to set up (run once before spamming).
    • Global spam defaults (mode tps/tpb, duration, seed, etc.).
    • Stages with name, duration, rate (tps/tpb), and a scenario mix (share_pct per scenario).
    • Shorthand: if no [[spam.stage]], a single implicit steady stage is built from [spam] + [[spam.mix]] + spam.duration.
  • Execution:
    • Each scenario spammer runs with its own run_id.
    • All run_ids share a campaign_id (uuid)
  • Reporting:
    • Maintain standard per-run reports by run_id
    • Add campaign reports by campaign_id that generate aggregate stats as well as link to per-run HTML

Flow

  1. Parse campaign config.
  2. Run setup for each referenced scenario (once).
  3. Normalize stages (explicit or shorthand), compute rates and traffic shares.
  4. Launch parallel spammers per (stage, scenario), each with its own run_id, all under one campaign_id.
  5. Reporting: per-run reports unchanged; optional campaign report aggregates and links runs.

Testing

Testing this is non-trivial and I encourage reviewers to double-check this all for themselves.

  • cargo test
  • cargo clippy --all --all-targets --all-features -- -D warnings
  • Manual:
    • Spun up an OP Stack devnet
    • contender campaign ./campaigns/staged-example.toml -r $RPC_URL -p $PKEY
    • contender report --campaign
    • Checked report stats/numbers
    • Observed metrics (see attached chart for one example)
Screenshot 2025-12-09 at 14 37 12 Screenshot 2025-12-09 at 14 40 19

Campaign Report HTML:
campaign-1cab16ed-1356-4000-a0e3-83012b20f15b.html

PR Checklist

  • Added Tests
  • Added Documentation
  • Ran cargo +nightly clippy --workspace --lib --examples --tests --benches --all-features --locked --fix
  • Ran cargo fmt --all
  • Note breaking changes in PR description, if applicable
  • update changelogs
    • Update CHANGELOG.md in each affected crate
    • add a high-level description in the root changelog

Metadata

Closes #145

@zeroXbrock
Copy link
Member

this is amazing! Thank you so much for putting this together. I made a handful of changes here -- simplified the CLI interfaces a bit, sharpened up the error handling, and then fixed some bugs related to the new PR in main that I just merged.

I set up my PR to target your branch so you can see the specific changes. Feel free to review it and make any changes you like, then when it's merged here I'll merge this PR into contender. Looking forward to adding this exciting feature!

@scharissis
Copy link
Contributor Author

this is amazing! Thank you so much for putting this together. I made a handful of changes here -- simplified the CLI interfaces a bit, sharpened up the error handling, and then fixed some bugs related to the new PR in main that I just merged.

I set up my PR to target your branch so you can see the specific changes. Feel free to review it and make any changes you like, then when it's merged here I'll merge this PR into contender. Looking forward to adding this exciting feature!

Hey @zeroXbrock - great to hear!
Because I didn't have the permissions to push new commits to your branch I've done the same and opened a new PR with some suggested improvements to your latest. If you're happy with that lets start merging/collapsing them. :)

scharissis and others added 5 commits December 17, 2025 12:34
* fix: nonce race condition

When multiple transactions from the same address failed with nonce errors, each adjustment read stale nonce values from the map, causing incorrect calculations. Now accumulates all nonce adjustments per address (summing +1, -1, etc.) before applying the total adjustment once, ensuring correctness even with concurrent failures.

* fix: panic-inducing calls

Replaced panic-inducing .expect(from) calls with safe if let Some(from) pattern matching that logs warnings instead of crashing when a transaction's from field is missing.

* fix: error handling

Replaced silent let _ = sender.send(...) calls with error handling that logs warnings when channel sends fail, making debugging easier and exposing receiver drop issues.

* improved stage rate validation

Added validation in validate_stage_rates to check rate * duration >= spam_len, matching the runtime check in spam execution and catching configuration errors early with helpful error messages.

* improved rate distribution

Implemented three-pass rate distribution that ensures mixes with non-zero share_pct get at least rate 1 by redistributing from larger allocations, preventing unexpected zero rates due to rounding.
@zeroXbrock zeroXbrock requested a review from sukoneck as a code owner December 18, 2025 01:28
@zeroXbrock zeroXbrock merged commit 490b411 into flashbots:main Dec 18, 2025
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

composite scenario execution

2 participants