# aqua

![aqua](src/assets/aqua.png)

A load testing tool for benchmarking Solana nodes over RPC.

## What It Measures

For each RPC method, aqua captures:
- **Throughput** - successful requests per second
- **Latency** - mean, p50, p90, p95, p99, min, max
- **Error rate** - success/failure ratio

Compare performance across different node clients (Solana Labs vs Jito vs Firedancer), configurations, or RPC providers.

## Installation

**From source:**
```bash
git clone https://github.com/Not-Sarthak/aqua.git
cd aqua
cargo build --release
```
Binary available at `./target/release/aqua`

**From cargo:**
```bash
cargo install aqua
```

## Quick Start

```bash
# Basic test - getSlot at 10, 50, 100 rps for 5 seconds each
aqua run getSlot https://api.mainnet-beta.solana.com -r 10 50 100 -d 5

# Compare two providers
aqua run getSlot helius=https://mainnet.helius-rpc.com quicknode=https://your-quicknode-url -r 50 100 -d 10

# Stress test with increasing load
aqua run getSlot https://api.mainnet-beta.solana.com --mode stress
```

## Test Modes

| Mode | Description | Default Rates |
|------|-------------|---------------|
| `stress` | Gradually increasing load | 10, 50, 100, 200, 500, 1000 rps |
| `spike` | Sudden load spike | 10, 1000, 10 rps |
| `soak` | Sustained load over time | 100 rps for 5 minutes |

## Available Tests

```bash
aqua ls  # List all available tests
```

Method names support multiple formats: `getSlot`, `sol_getSlot`, or `slot`.

| Category | Methods |
|----------|---------|
| **Slot/Block** | `getSlot`, `getBlock`, `getBlockHeight`, `getBlockTime`, `getBlocks`, `getBlockCommitment`, `getBlockProduction` |
| **Account** | `getBalance`, `getAccountInfo`, `getMultipleAccounts`, `getProgramAccounts`, `getLargestAccounts` |
| **Token** | `getTokenAccountBalance`, `getTokenAccountsByOwner`, `getTokenLargestAccounts`, `getTokenSupply` |
| **Transaction** | `getTransaction`, `getSignaturesForAddress`, `getSignatureStatuses`, `simulateTransaction`, `sendTransaction` |
| **Cluster** | `getHealth`, `getVersion`, `getClusterNodes`, `getEpochInfo`, `getEpochSchedule`, `getLeaderSchedule`, `getVoteAccounts` |
| **Misc** | `getLatestBlockhash`, `getSupply`, `getInflationRate`, `getRecentPrioritizationFees`, `getGenesisHash`, `getIdentity` |

## Usage

### Run Options

```bash
aqua run <TEST> <NODES>... [OPTIONS]
```

| Option | Description |
|--------|-------------|
| `-r, --rates <N>...` | Request rates in rps (e.g., `-r 10 50 100`) |
| `-d, --duration <N>` | Duration in seconds for each rate |
| `-o, --output <DIR>` | Output directory for results |
| `-q, --quiet` | Suppress verbose output |
| `-e, --equality` | Run differential test |
| `--mode <MODE>` | Test mode: stress, spike, soak |
| `--network <NET>` | Network: mainnet-beta, testnet, devnet |
| `--dry` | Dry run (don't execute) |
| `--html` | Generate HTML report with charts |
| `--deep-check` | Validate RPC responses for errors |
| `--save-raw-output` | Save raw RPC responses |
| `--equality-samples <N>` | Samples for equality test (default: 10) |
| `-s, --seed <N>` | Random seed for reproducibility |

### Examples

```bash
# test getHealth with multiple rates
aqua run getHealth https://api.mainnet-beta.solana.com -r 10 50 100 200 -d 5

# test getBalance on devnet
aqua run getBalance https://api.devnet.solana.com --network devnet -r 20 -d 10

# dry run
aqua run getSlot https://api.mainnet-beta.solana.com --dry

# deep check with raw output
aqua run getSlot https://api.mainnet-beta.solana.com -r 10 -d 5 --deep-check --save-raw-output

# generate HTML report with charts
aqua run getSlot https://api.mainnet-beta.solana.com -r 10 50 100 -d 5 --html
```

### Print Results

```bash
aqua print <TEST_DIR>
aqua print <TEST_DIR_1> <TEST_DIR_2>  # Multiple tests
```

### Generate Reports

```bash
aqua report <TEST_DIR> --html              # Generate HTML report with charts
aqua report <TEST_DIR_1> <TEST_DIR_2>      # Combine multiple tests
```

## Differential Testing

Compare responses between nodes to validate correctness:

```bash
# compare two nodes
aqua run getSlot node1=https://api.mainnet-beta.solana.com node2=https://your-node -e

# single node validation
aqua run getSlot https://api.mainnet-beta.solana.com -e --equality-samples 20
```

## Geyser Benchmark

Compare latency between two Geyser gRPC endpoints (e.g., Yellowstone):

```bash
aqua benchmark \
  --endpoint1 https://geyser1.example.com \
  --endpoint2 https://geyser2.example.com \
  --slots 1000

# with auth tokens
aqua benchmark \
  --endpoint1 https://geyser1.example.com \
  --endpoint2 https://geyser2.example.com \
  --token1 your-token-1 \
  --token2 your-token-2 \
  --slots 500 \
  --output results.json
```

| Option | Description |
|--------|-------------|
| `--endpoint1` | First Geyser endpoint URL |
| `--endpoint2` | Second Geyser endpoint URL |
| `--token1` | Auth token for endpoint 1 |
| `--token2` | Auth token for endpoint 2 |
| `--slots` | Number of slots to collect (default: 100) |
| `--with-accounts` | Subscribe to account updates |
| `--account-owner` | Filter accounts by owner pubkey |
| `--timeout` | Connection timeout in seconds (default: 30) |
| `--ping-samples` | Number of ping samples (default: 10) |
| `--config` | Path to TOML config file |
| `-o, --output` | Save results to JSON file |
| `-q, --quiet` | Suppress verbose output |

## Output

Each test creates these files in the output directory:
- `test.json` - test parameters
- `results.json` - full metrics
- `report.html` - interactive charts (with `--html`)
- `raw_output.json` - raw responses (with `--save-raw-output`)

### Metrics

| Metric | Description |
|--------|-------------|
| `target_rate` | Target rps |
| `actual_rate` | Achieved rps |
| `throughput` | Successful rps |
| `success` | Success rate (0.0 - 1.0) |
| `mean` | Mean latency |
| `p50, p90, p95, p99` | Percentile latencies |
| `min, max` | Min/max latency |

## Contributing

```bash
git clone https://github.com/Not-Sarthak/aqua.git
cd aqua
cargo build
cargo test
```

Issues and PRs welcome.

## License

MIT
