Skip to content

fix(spammer): reject rates above limiter resolution - #350

Open
Kewe63 wants to merge 1 commit into
circlefin:mainfrom
Kewe63:fix-349-reject-oversized-spammer-rate
Open

fix(spammer): reject rates above limiter resolution#350
Kewe63 wants to merge 1 commit into
circlefin:mainfrom
Kewe63:fix-349-reject-oversized-spammer-rate

Conversation

@Kewe63

@Kewe63 Kewe63 commented Sep 4, 2026

Copy link
Copy Markdown

Summary

Fixes #349

Reject spammer rates above the highest distinct per-second rate supported by the governor limiter, and replace the limiter's integer-conversion panics with normal propagated errors.

The fix enforces the same upper bound at the CLI, configuration, and rate-limiter layers. It also covers both normal and resumed spammer construction paths.


Problem

The spammer previously exposed --rate as an unbounded positive u64:

clap::value_parser!(u64).range(1..)

Config::validate() did not enforce an upper bound. The value eventually reached RateLimiter::new(), which narrowed it with:

u32::try_from(tps).expect("TPS must fit in u32")

A rate greater than u32::MAX therefore passed CLI and configuration validation, then panicked with:

TPS must fit in u32: TryFromIntError(())

Implementation review also found that u32::MAX is not the true semantic limit. governor 0.8.1 computes its per-token interval as:

1_000_000_000 nanoseconds / rate

Rates above 1_000_000_000 round this interval down to zero nanoseconds, after which the GCRA implementation clamps it to one nanosecond. Such values fit in u32, but cannot be represented as distinct requested rates.

The correct maximum for this path is therefore 1_000_000_000 TPS.


Changes

  • Define a single MAX_TPS value of 1_000_000_000 at the rate-limiter boundary.
  • Limit the Clap --rate parser to 1..=MAX_TPS.
  • Make Config::validate() enforce the same maximum for non-CLI callers.
  • Change RateLimiter::new() to return Result<Self> instead of panicking on invalid rate or burst values.
  • Propagate rate-limiter construction errors through both:
    • normal Spammer::new() construction;
    • resumed Spammer::new_resuming() construction.
  • Add boundary and regression tests at the CLI, configuration, and limiter layers.

Regression Coverage

The new tests verify that:

  • 1_000_000_000 is accepted by the CLI;
  • 1_000_000_001 is rejected by the CLI;
  • Config::validate() accepts the supported maximum;
  • Config::validate() rejects the first unsupported value;
  • RateLimiter::new() accepts the supported maximum;
  • RateLimiter::new() returns an error for the first unsupported value instead of panicking.

The rate limiter is fallible independently of CLI/config validation, preventing unvalidated library callers from reaching the old panic path.


RED Verification

Before the fix, focused tests against current main demonstrated all three layers of the bug:

CLI:
--rate accepted a value the rate limiter cannot represent

Config:
rate limiter only supports rates through u32::MAX

RateLimiter:
TPS must fit in u32: TryFromIntError(())

The CLI, configuration, and limiter regression tests all failed on the previous implementation.

A separate boundary probe confirmed that u32::MAX did not panic during construction; review of governor's interval calculation then identified 1_000_000_000 as the actual distinct-rate limit.


How to Test

Focused rejection tests:

cargo +1.94.0 test -p spammer supported_max -- --nocapture

Result:

CLI/config/limiter rejection tests: 3 passed; 0 failed

Supported-boundary tests:

cargo +1.94.0 test -p spammer max_supported_rate -- --nocapture

Result:

configuration/limiter maximum tests: 2 passed; 0 failed

Complete package:

cargo +1.94.0 test -p spammer

Results:

library tests: 76 passed; 0 failed
binary tests: 11 passed; 0 failed
doc tests: 0 failed

Additional checks:

cargo +1.94.0 clippy -p spammer --all-targets -- -D warnings
cargo +1.94.0 fmt -p spammer -- --check
git diff --check

All checks passed.

The built CLI was also invoked with both 1_000_000_001 and 4_294_967_296. Both values now produce a normal Clap validation error with exit code 2. Neither invocation emits panic text or reaches network initialization.

Local verification used Rust 1.94.0 because the local pinned 1.93.0 installation has a cargo-clippy component conflict. CI should provide the authoritative pinned-toolchain result.


Scope and Risk

The change is limited to spammer rate validation and construction:

crates/spammer/src/cli.rs
crates/spammer/src/config.rs
crates/spammer/src/main.rs
crates/spammer/src/rate_limiter.rs
crates/spammer/src/spammer.rs

It does not change:

  • behavior for rates from 1 through 1_000_000_000;
  • transaction generation or sending;
  • WebSocket behavior;
  • account or nonce handling;
  • consensus or protocol behavior;
  • dependency versions.

Rates above 1_000_000_000 were previously accepted, but governor could not represent them as distinct rates because of nanosecond resolution. They are now rejected explicitly instead of being rounded or eventually panicking.


Duplicate Check

Open and closed issues and pull requests were searched using the issue number, panic text, rate-limiter symbols, type boundary, and supported-rate wording. No duplicate PR or existing implementation was found.


Checklist

  • Bug reproduced on current main
  • Regression tests confirmed failing before the fix
  • Actual limiter resolution boundary verified from governor 0.8.1 source
  • CLI and internal configuration enforce the same bound
  • Rate limiter no longer panics on invalid rates
  • Normal and resume paths propagate construction errors
  • Focused and complete package tests pass
  • Formatting and Clippy checks pass
  • Real CLI error path verified
  • No unrelated files changed
  • Independent follow-up review approved the corrected boundary
  • Follows Conventional Commits

Impact

Type: 🐛 Bug fix

Fixes: #349

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.

Spammer should reject rates that exceed the rate limiter's supported range

1 participant