Skip to content

feat(loadtest): per-transaction calldata from stdin for contract-call mode - #1022

Merged
praetoriansentry merged 1 commit into
mainfrom
feat/loadtest-calldata-stdin
Sep 28, 2026
Merged

praetoriansentry merged 1 commit into
mainfrom
feat/loadtest-calldata-stdin

Conversation

@praetoriansentry

@praetoriansentry praetoriansentry commented Sep 25, 2026 •

Copy link
Copy Markdown
Member

Description

--calldata and --calldata-file send the same payload in every contract-call transaction. This adds --calldata-stdin and --calldata-size so each transaction gets its own calldata from a shell pipeline.

  • Stdin is read as raw bytes and cut into --calldata-size chunks. Each transaction carries the next chunk. A trailing partial chunk is discarded with a warning.
  • The test stops cleanly at EOF or at the request count, whichever comes first. A read error on stdin ends the run non-zero so a dead producer is not mistaken for a clean finish.
  • New mode.InputReserver interface. The runner reserves the input before taking a nonce, so an exhausted stream never leaves a reserved nonce unsent (which would stall the final-block wait). Once a request holds a nonce it always runs to completion.
  • Validation: requires contract-call as the sole mode; mutually exclusive with --calldata, --calldata-file, and --reverse-nonce-order; rejects a terminal on stdin; caps chunk size at 1 MiB.
  • No selector prefix is added. Callers prepend one in the input if the target needs it.

Not breaking. No new dependencies (golang.org/x/term was already direct).

Pre-existing gap noticed while here, not changed: the uniswapv3 subcommand never calls cfg.Validate(). The new flags are local to the parent command so it cannot reach them.

Jira / Linear Tickets

  • N/A

Testing

  • go test -race ./loadtest/... ./cmd/..., golangci-lint, go vet, typos, go run docutil/*.go all clean.
  • New unit tests: validation table (TestValidateCalldataStdin), chunk feeder (exact multiple, partial tail, empty input, read error, cancellation while blocked, reader exit when buffer full, 16 concurrent consumers with -race), contract-call mode (per-chunk calldata via reserve + execute, unreserved execute fails, stale feeder cleared on Init, legacy vs dynamic fee tx build).
  • Against local anvil, EOA target, --gas-limit 100000:
    • 25 chunks, --concurrency 8 --requests 100: exactly 25 txs sent, stopped at EOF, final-block wait completed, 0 errors, exit 0.
    • 25 chunks, --concurrency 8 --requests 3: exactly 24 txs sent (request cap wins).
    • --output-raw-tx-only: decoded raw txs carry different input per chunk.
    • 35 bytes at size 30: one tx sent, warning about 5 stray bytes.
    • < /dev/null: 0 txs, clean stop.
    • Pseudo-terminal on stdin (via script): rejected with the intended message.
    • --reverse-nonce-order and --calldata-size 2000000: rejected at validation.

🤖 Generated with Claude Code

… mode

Add --calldata-stdin and --calldata-size to contract-call mode. Stdin is
read as raw bytes and cut into fixed-size chunks, and each transaction
carries the next chunk as its calldata, so a shell pipeline can generate a
distinct payload per transaction. The test stops cleanly when stdin reaches
EOF or when the request count is reached, whichever comes first.

Modes that draw from an external stream implement mode.InputReserver. The
runner reserves the input before taking a nonce, so an exhausted stream
never leaves a reserved nonce unsent, and once a request holds a nonce it
always runs to completion. A read error on stdin ends the run non-zero
instead of being mistaken for EOF.

The flag requires contract-call to be the sole mode, is mutually exclusive
with --calldata, --calldata-file and --reverse-nonce-order, rejects a
terminal on stdin, and caps the chunk size at 1 MiB.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@praetoriansentry
praetoriansentry merged commit 75a9288 into main Sep 28, 2026
15 checks passed
@praetoriansentry
praetoriansentry deleted the feat/loadtest-calldata-stdin branch September 28, 2026 12:39
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.

2 participants