Draft: spam-stream subcommand for streaming tx specs (relayer use case)#589
Draft
jelias2 wants to merge 2 commits into
Draft
Draft: spam-stream subcommand for streaming tx specs (relayer use case)#589jelias2 wants to merge 2 commits into
jelias2 wants to merge 2 commits into
Conversation
Supersedes flashbots#581. Adds an `access_list` field to FunctionCallDefinition that accepts {placeholder} strings in `address` and `storageKeys`, resolved during the loose-to-strict conversion via the existing templater + DB map. Coexists with max_priority_fee_per_gas (flashbots#580) and alloy 2.0 (flashbots#561).
Reads newline-delimited JSON FunctionCallDefinitions from stdin or a file and spams them via the existing TestScenario pipeline. Reuses agent pools, rate limiting, nonce management, and receipt tracking. See docs/stream-mode.md for the design note and scope.
a171692 to
3c13137
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a new
spam-streamsubcommand that reads newline-delimited JSON tx specs from stdin (or a file) and spams them through the existingTestScenariopipeline. Each spec is aFunctionCallDefinition— the same schema as scenario TOML[[spam.tx]]— soaccess_list,signature/args,gas_limit,value, andfrom_poolall work without any new schema.This is a Draft PR for design feedback. It compiles, runs end-to-end against a real devnet, and lands tx receipts via the regular tx_actor flush loop. It is deliberately small: no bundle support, no fuzzing, no recorded
spam_runsentry, no integration with--rpc-batch-size/--send-raw-tx-sync. Seedocs/stream-mode.mdfor the architecture note and scope list.Motivation: a generic streaming primitive
Today, contender spam is generator-driven: a static scenario describes what to send, contender cycles through it. That's the right shape for synthetic throughput tests but not for cases where what to send is computed at runtime by something other than contender.
Stream mode lets any upstream process pipe specs into contender and reuse the existing agent pools, rate limiting, signer/nonce management, gas-price caching, receipt tracking, and Prometheus latency metrics. The upstream owns what to send; contender owns how to send it efficiently.
Use cases
Cross-chain / bridge / relayer workflows — the motivating case. Watch chain A for an event, compute a tx (with access list) for chain B, emit it. Works for OP-stack interop, Hyperlane, LayerZero, native rollup bridges, any "receive-and-forward" pattern. Also fits MEV relayers replaying captured bundles and AA bundlers feeding pre-signed UserOperations.
Tx replay
External generators
Operations
Differential testing
Composition with existing contender features: keep
[[create]]and[[setup]]static in a scenario, deploy/fund once, then stream the dynamic spam phase.CLI
Flags:
-r/--rpc-url,-p/--priv-key,--from,--from-pool(defaultexecutors),--pool-size(default 10),--tps(default 0 = drain as fast as the stream emits),--min-balance,--seed,--skip-funding.Stream format
Newline-delimited JSON, one
FunctionCallDefinitionper line. Empty lines and#-prefixed lines are ignored; malformed JSON logs a warning and the loop continues.{ "to": "0x4200000000000000000000000000000000000022", "signature": "validateMessage(bytes32)", "args": ["0x0102030405060708091011121314151617181920212223242526272829303132"], "access_list": [ { "address": "0x4200000000000000000000000000000000000022", "storageKeys": ["0x0100000000000000000000000000000000000000000000000000000000000000"] } ], "gas_limit": 200000 }Private keys never appear in the stream. Producers describe txs; contender signs with an agent from its own pool (derived from
--seed, funded from--priv-keyat startup). A compromised producer can spam txs but can't drain accounts beyond what the pre-funded agents hold.Architecture
All new code lives in
crates/cli/src/commands/spam_stream.rs. Nocontender_corechanges. The flow:A no-op one-step
TestConfigis constructed soAgentPools::build_agent_storeproduces a pool with the requested name and size. The decoy spam step itself is never executed; we bypassload_txsentirely.We don't reuse
TimedSpammer/BlockwiseSpammerbecause theiron_spamloops pull from a pre-loadedVec<Vec<ExecutionRequest>>viaget_spam_tx_chunks. Stream mode is fundamentally stream-shaped. Adding a genericSpamSourceabstraction across the existing spammers would be a much larger change; see open questions below.Dependency on #588
This PR depends on #588 (access_list field + placeholder resolution). The interop relayer use case needs access lists on executing-message calls, and one of the primary justifications for stream mode is that those access lists are computed per-message upstream.
Validation
Smoke test against
interop-bench-2-0:Tx
0x8742f5d94cec761fd927ddcbe1cfcad7ba45e352a81cfeb87277780523ed3646landed in block 131011, status0x1.Test plan
cargo fmt --checkcleanWhat's deferred (follow-up work)
[[spam.bundle]]) support--rpc-batch-size/--send-raw-tx-syncintegrationspam_runsTimedSpammer/BlockwiseSpammerto share aSpamSourcetraitOpen design questions
contender_core? The prototype keeps everything incli/. Moving it intocorewould letcampaignsconsume a stream too — but the existingSpammertrait wants aVec<Vec<ExecutionRequest>>upfront. Natural refactor is a newSpamSourcetrait thatTimedSpammer/BlockwiseSpammercould also adopt.FunctionCallDefinition. A tagged envelope ({"v":1,"tx":{...}}) would give us room to add per-line metadata (e.g. correlation IDs back to the upstream event) without breaking compatibility.--tps 0. Drain-as-fast currently sends one tx at a time per loop iteration, bounded by the pool size only via nonce contention. Should it explicitly usepool_sizeparallel workers?AgentPools::build_agent_storeto accept an explicit pool list. Worth doing now or in a follow-up?