High-performance load testing tool for EVM-compatible testnets
load-blaster is a Rust-based load testing tool designed to stress-test EVM-compatible testnets with high transaction throughput and blob data rates.
| Metric | Target Range | Notes |
|---|---|---|
| TPS (transactions/second) | 10k - 100k | Regular EIP-1559 transactions |
| Blob throughput | 16 - 100 MB/s | EIP-4844 blob transactions |
| Latency P99 | < 5s | Submission to inclusion |
- Distributed coordinator/worker architecture (future)
- Smart contract interaction (just transfers)
- Cross-chain testing
┌─────────────────────────────────────────────────────────────────┐
│ load-blaster │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │
│ │ AccountPool │ │ Generator │ │ RateController │ │
│ │ - HD wallet │ │ - EIP-1559 │ │ - target_tps │ │
│ │ - Nonce mgmt │ │ - EIP-4844 │ │ - target_blob_mbps │ │
│ │ - Backpress. │ │ - BlobRing │ │ - governor limiter │ │
│ └──────┬───────┘ └──────┬───────┘ └──────────┬───────────┘ │
│ │ │ │ │
│ ┌──────▼─────────────────▼──────────────────────▼───────────┐ │
│ │ RpcClientPool │ │
│ │ - HTTP/1.1 keep-alive │ │
│ │ - JSON-RPC batching (eth_sendRawTransaction[]) │ │
│ │ - Round-robin endpoint selection │ │
│ └────────────────────────┬──────────────────────────────────┘ │
│ │ │
│ ┌────────────────────────▼──────────────────────────────────┐ │
│ │ Metrics │ │
│ │ - Prometheus exporter (:9091) │ │
│ │ - Console reporter │ │
│ │ - Confirmation sampling │ │
│ └───────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
│
▼
Target RPC nodes
- AccountPool provides next available account (with backpressure)
- Generator creates signed transaction (EIP-1559 or EIP-4844 with blobs)
- RateController enforces target TPS / blob MB/s
- RpcClientPool batches and sends transactions
- Metrics tracks submitted/errors/latency
Purpose: Manage test accounts with HD derivation and per-account backpressure.
Requirements:
- Derive accounts from mnemonic (BIP-39 / BIP-44)
- Track pending transactions per account
- Enforce max pending limit (default: 16, matches reth txpool)
- Support 1000+ accounts for high TPS
Interface:
pub struct AccountPool {
pub fn new(mnemonic: &str, count: usize, max_pending: u32) -> Result<Self>;
pub fn acquire(&self) -> Option<AccountHandle>; // None = backpressure
pub fn release(&self, handle: AccountHandle);
pub fn get_nonce(&self, index: usize) -> u64;
pub fn increment_nonce(&self, index: usize);
}Purpose: Create signed transactions (EIP-1559 and EIP-4844).
Requirements:
- Reuse patterns from
ultramarine/crates/utils/src/tx.rs - EIP-4844: Use ring buffer of pre-computed blobs (64 blobs, ~50MB)
- KZG proofs computed once at startup
- Support configurable blobs_per_tx (1-32)
Interface:
pub struct Generator {
pub fn new(chain_id: u64, blobs_per_tx: usize) -> Result<Self>;
pub async fn create_eip1559(&self, signer: &Signer, nonce: u64, to: Address) -> Result<Bytes>;
pub async fn create_eip4844(&self, signer: &Signer, nonce: u64, to: Address) -> Result<Bytes>;
}Purpose: Efficient transaction submission with batching.
Requirements:
- Multiple reqwest clients per endpoint (default: 4)
- HTTP/1.1 with keep-alive (not HTTP/2)
- JSON-RPC batch arrays (default: 20 txs per batch)
- Round-robin across endpoints
- Timeout handling (default: 10s)
Interface:
pub struct RpcClientPool {
pub fn new(endpoints: Vec<String>, config: RpcConfig) -> Self;
pub async fn send_batch(&self, txs: Vec<Bytes>) -> Vec<Result<TxHash, RpcError>>;
pub async fn get_nonce(&self, address: Address) -> Result<u64>;
}Purpose: Enforce target TPS and blob throughput.
Requirements:
- Token bucket algorithm (governor crate)
- Separate limiters for regular TPS and blob MB/s
- Calculate blob_tx_rate from target_blob_mbps and blobs_per_tx
- Support burst mode for stress testing
Interface:
pub struct RateController {
pub fn new(target_tps: u64, target_blob_mbps: f64, blobs_per_tx: usize) -> Self;
pub async fn wait_for_regular(&self);
pub async fn wait_for_blob(&self);
pub fn blob_tx_rate(&self) -> f64; // txs/sec for blobs
}Purpose: Observability and reporting.
Requirements:
- Prometheus metrics on configurable port
- Console reporter with 1s interval
- Confirmation sampling (default: 1% of txs)
Metrics:
| Name | Type | Description |
|---|---|---|
load_blaster_txs_submitted_total |
Counter | Total transactions submitted |
load_blaster_txs_errors_total |
Counter | Submission errors (by type) |
load_blaster_blobs_submitted_total |
Counter | Total blobs submitted |
load_blaster_bytes_submitted_total |
Counter | Total bytes submitted |
load_blaster_tps_current |
Gauge | Current TPS (1s window) |
load_blaster_blob_mbps_current |
Gauge | Current blob throughput |
load_blaster_nonce_resyncs_total |
Counter | Nonce resync operations |
load_blaster_confirmation_latency_seconds |
Histogram | Sampled confirmation latency |
[target]
endpoints = ["http://localhost:8545"]
chain_id = 16384
[accounts]
count = 1000
max_pending = 16
[connections]
clients_per_endpoint = 4
keepalive_secs = 30
timeout_secs = 10
batch_size = 20
[blobs]
ring_buffer_size = 6
blobs_per_tx = 6
max_blobs_per_tx = 32
[metrics]
prometheus_addr = "127.0.0.1:9091"
console_interval_secs = 1
confirmation_sample_rate = 0.01
[sanity]
max_tps = 50000
max_blob_mbps = 200[scenario]
name = "example"
[[phases]]
type = "ramp_up" # or "constant", "burst", "ramp_down"
duration = "30s"
from_tps = 1000
to_tps = 10000
from_blob_mbps = 0
to_blob_mbps = 50
[blobs]
blobs_per_tx = 6
[criteria]
min_success_rate = 0.95
max_p99_latency_ms = 5000load-blaster 0.1.0
High-performance load testing for EVM-compatible networks
USAGE:
load-blaster <COMMAND>
COMMANDS:
run Run a load test
accounts Manage test accounts
help Print help
OPTIONS:
-c, --config <FILE> Config file [default: load-blaster.toml]
-v, --verbose Verbose output
load-blaster run [OPTIONS]
OPTIONS:
--target-tps <N> Target regular TPS [default: from config]
--target-blob-mbps <N> Target blob throughput MB/s [default: 0]
--duration <DURATION> Test duration (e.g., 60s, 5m) [default: 60s]
--scenario <FILE> Scenario file (overrides other options)
--blobs-per-tx <N> Blobs per blob transaction [default: 6]
--mnemonic-stdin Read mnemonic from stdin
load-blaster accounts <SUBCOMMAND>
SUBCOMMANDS:
generate Generate addresses for external funding
check Verify accounts are funded
- No keys in repo: Mnemonic loaded from env var or stdin only
- Sanity checks: Prevent impossible TPS/MB/s configurations
- Rate limiting: Respect network capacity, don't DoS nodes
- Graceful backpressure: Don't exhaust account nonces
| Crate | Version | Purpose |
|---|---|---|
| alloy-consensus | 1.0.37 | Transaction types |
| alloy-eips | 1.0.37 | EIP-4844 blob types |
| alloy-signer-local | 1.1.2 | HD wallet signing |
| c-kzg | 1.0 | KZG proof generation |
| tokio | 1.46 | Async runtime |
| reqwest | 0.12 | HTTP client |
| governor | 0.8 | Rate limiting |
| prometheus | 0.13 | Metrics |
| clap | 4.5 | CLI |
| serde | 1.0 | Serialization |
| toml | 0.8 | Config parsing |
- Config parsing and validation
- Rate calculations (blob_tx_rate)
- Ring buffer correctness
- Account pool backpressure
- JSON-RPC batching against mock server
- Transaction generation and signing
- Nonce management
- Local devnet: 1000 txs, verify inclusion
- Remote testnet: 10k TPS sustained load
- Blob stress: 50 MB/s, monitor memory
- Distributed coordinator/worker mode for 100k+ TPS
- Smart contract call load testing
- WebSocket subscription for real-time block monitoring
- Grafana dashboard generator
- Benchmark regression tests in CI