Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,18 @@ path = "src/lib.rs"
name = "reth-bsc"
path = "src/main.rs"

[[bin]]
name = "miner-bench"
path = "src/bench_main.rs"
required-features = ["bench-test"]

[dependencies]
reth = { git = "https://github.com/bnb-chain/reth.git", branch = "develop" }
reth-cli = { git = "https://github.com/bnb-chain/reth.git", branch = "develop" }
reth-cli-commands = { git = "https://github.com/bnb-chain/reth.git", branch = "develop" }
reth-basic-payload-builder = { git = "https://github.com/bnb-chain/reth.git", branch = "develop" }
reth-db = { git = "https://github.com/bnb-chain/reth.git", branch = "develop" }
reth-db-common = { git = "https://github.com/bnb-chain/reth.git", branch = "develop" }
reth-engine-local = { git = "https://github.com/bnb-chain/reth.git", branch = "develop" }
reth-engine-tree = { git = "https://github.com/bnb-chain/reth.git", branch = "develop" }
reth-node-builder = { git = "https://github.com/bnb-chain/reth.git", branch = "develop" }
Expand Down
88 changes: 88 additions & 0 deletions src/bench/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Miner Benchmark

This directory contains the `miner-bench` CLI, which is a local benchmark harness for
measuring block building, state-root computation, and post-build persistence costs.

## What It Measures

The main supported command on this branch is `run`, which executes a direct block-building
microbenchmark:

- initializes a temporary MDBX database from genesis
- optionally enables trieDB before genesis
- builds a setup block that deploys and distributes an ERC20 token
- executes a configurable number of benchmark blocks with synthetic ERC20 transfers
- finalizes the block
- persists block/state changes and reports timing breakdowns

The reported persistence bucket is real local work. It includes:

- block insert
- state write
- triedb flush
- database commit

## Supported Commands

### `run`

This is the supported benchmark mode on this branch.

Example:

```bash
cargo run --features bench-test --bin miner-bench -- run \
--num-blocks 3 \
--txs-per-block 500 \
--funded-accounts 500 \
--background-accounts 1000000 \
--storage-slots-per-account 10 \
--triedb \
--output benchmark.csv
```

### `payload-job-run`

This command is currently unavailable on this branch. The upstream refactor removed the
`BscPayloadJob` / wait-slice scheduling APIs that the original payload-job benchmark depended on.
The command remains reserved so the CLI shape is explicit, but it returns an error if invoked.

## trieDB Flag

The benchmark CLI uses `--triedb`.

That is the benchmark-side equivalent of running the node with:

```bash
reth-bsc node --statedb.triedb
```

When `--triedb` is enabled, the benchmark initializes the global triedb manager before genesis so
the builder/finalization flow runs against triedb-backed state logic on this branch.

## Branch-Specific Caveats

- `--chain-difflayers` is ignored by the direct benchmark on this branch.
The current builder API does not expose difflayer-returning finalize hooks here.
- The direct benchmark is still a microbenchmark, not a full node simulation.
- Transactions are pre-recovered during pool generation so the measured execution loop reflects
block-building work more closely than signer recovery overhead.
- The workload is synthetic ERC20 traffic, not a live heterogeneous mempool.

## Interpreting Results

Useful high-level buckets:

- `tx_execution_us`: aggregate transaction execution time
- `execute_only_us`: time spent inside `execute_transaction()`
- `finish_us`: post-execution finalize work, including state-root computation
- `commit_us`: aggregate persistence work
- `insert_block_us`, `write_state_us`, `triedb_flush_us`, `provider_commit_us`:
persistence subphases

In practice on this branch:

- `finish_us` is the relevant builder/finalize number
- `commit_us` is the relevant post-build persistence number
- the benchmark is useful for tuning builder/root/persistence behavior
- it should not be described as a full real-miner simulation
205 changes: 205 additions & 0 deletions src/bench/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
use alloy_primitives::B256;
use clap::{Parser, Subcommand};
use std::path::PathBuf;

/// Hardcoded validator private keys (from local node-deploy keystores)
const VALIDATOR_KEY_0: &str = "937f86f4a49cafcf81a2595c5e7afd08b875b42bf05a18aa5ebc64a0af584000";
const VALIDATOR_KEY_1: &str = "ac24b6aeb63fc825b2866a5ad628c42c1c5222c56c1c9f2cedfffd95d96c75a0";
const VALIDATOR_KEY_2: &str = "c73e6841e8e422048a8eafb0e8a2e62059b5d4fe9195b87d49e9b6c1c635549f";

/// Hardcoded genesis-funded deployer account private key
const DEPLOYER_KEY: &str = "59ba8068eb256d520179e903f43dacf6d8d57d72bd306e1bd603fdb8c8da10e8";

/// Default genesis path (local copy in testing/)
const DEFAULT_GENESIS: &str = "testing/genesis_local.json";

#[derive(Parser, Debug)]
#[command(name = "miner-bench", about = "BSC execution/state-root microbenchmark")]
pub struct Cli {
#[command(subcommand)]
pub command: Commands,
}

#[derive(Subcommand, Debug)]
pub enum Commands {
/// Run the block-execution microbenchmark (direct pipeline)
Run(RunArgs),
/// Reserved payload-job benchmark command (currently unavailable on this branch)
PayloadJobRun(PayloadJobRunArgs),
/// Compare two benchmark CSV outputs
Compare(CompareArgs),
}

#[derive(Parser, Debug)]
pub struct RunArgs {
/// Path to genesis JSON file
#[arg(long, default_value = DEFAULT_GENESIS)]
pub genesis: PathBuf,

/// Number of blocks to mine
#[arg(long, default_value = "100")]
pub num_blocks: usize,

/// Number of transactions per block
#[arg(long, default_value = "200")]
pub txs_per_block: usize,

/// Number of funded accounts for tx generation
#[arg(long, default_value = "500")]
pub funded_accounts: usize,

/// Number of extra "background" accounts to inflate the state trie (no txs, just state bulk)
#[arg(long, default_value = "0")]
pub background_accounts: usize,

/// Number of storage slots per background account (simulates token balances, approvals, etc.)
#[arg(long, default_value = "5")]
pub storage_slots_per_account: usize,

/// Enable difflayer chain (unsupported on this branch; ignored by the direct benchmark)
#[arg(long, default_value = "false")]
pub chain_difflayers: bool,

/// Enable trieDB for state root calculation
#[arg(long, default_value = "false")]
pub triedb: bool,

/// Output CSV file path
#[arg(long, default_value = "benchmark.csv")]
pub output: PathBuf,

/// Label for this benchmark run
#[arg(long, default_value = "default")]
pub label: String,
}

#[derive(Parser, Debug)]
pub struct PayloadJobRunArgs {
/// Path to genesis JSON file
#[arg(long, default_value = DEFAULT_GENESIS)]
pub genesis: PathBuf,

/// Number of payload-job iterations to run
#[arg(long, default_value = "20")]
pub iterations: usize,

/// Number of transactions to populate in the pool per iteration
#[arg(long, default_value = "200")]
pub txs_per_iteration: usize,

/// Number of funded accounts for tx generation
#[arg(long, default_value = "500")]
pub funded_accounts: usize,

/// Number of extra background accounts to inflate the state trie
#[arg(long, default_value = "0")]
pub background_accounts: usize,

/// Number of storage slots per background account
#[arg(long, default_value = "5")]
pub storage_slots_per_account: usize,

/// Enable difflayer chaining between payload-job iterations
#[arg(long, default_value = "false")]
pub chain_difflayers: bool,

/// Enable trieDB for payload finalization
#[arg(long, default_value = "false")]
pub triedb: bool,

// --- Wait-Slice Parameters ---
/// DELAY_LEFT_OVER: ms reserved for finalization (default: 120)
#[arg(long, default_value = "120")]
pub delay_left_over_ms: u64,

/// TIME_MULTIPLIER: retry threshold multiplier (default: 2)
#[arg(long, default_value = "2")]
pub time_multiplier: u32,

/// Grace period past expected_end_timestamp_ms in ms (default: 150)
#[arg(long, default_value = "150")]
pub grace_period_ms: u128,

/// Max wait-slice duration per iteration in ms (default: 50)
#[arg(long, default_value = "50")]
pub max_wait_slice_ms: u64,

/// Override mining delay in ms (default: use parlia computation).
/// Set to e.g. 330 to simulate a realistic BSC block period.
#[arg(long)]
pub mining_delay_ms: Option<u64>,

/// Percentage of txs to pre-load before starting the job (0-100, default: 100).
/// Remaining txs are drip-fed mid-job to trigger retries and concurrent builds.
#[arg(long, default_value = "100")]
pub initial_tx_pct: u32,

/// Delay in ms before starting to drip-feed remaining txs (default: 50).
#[arg(long, default_value = "50")]
pub tx_drip_delay_ms: u64,

/// Interval in ms between drip-feed batches (default: 20).
#[arg(long, default_value = "20")]
pub tx_drip_interval_ms: u64,

/// Output CSV file
#[arg(long, default_value = "payload_job_benchmark.csv")]
pub output: PathBuf,

/// Label for this run
#[arg(long, default_value = "default")]
pub label: String,
}

#[derive(Parser, Debug)]
pub struct CompareArgs {
/// Baseline CSV file
#[arg(long)]
pub baseline: PathBuf,

/// Optimized CSV file
#[arg(long)]
pub optimized: PathBuf,
}

pub struct BenchConfig {
pub genesis_path: PathBuf,
pub private_keys: Vec<B256>,
pub deployer_key: B256,
pub num_blocks: usize,
pub txs_per_block: usize,
pub funded_accounts: usize,
pub background_accounts: usize,
pub storage_slots_per_account: usize,
pub chain_difflayers: bool,
pub triedb: bool,
pub output_csv: PathBuf,
pub label: String,
}

fn parse_key(hex: &str) -> B256 {
hex.parse::<B256>().expect("hardcoded key must be valid")
}

impl BenchConfig {
pub fn from_run_args(args: RunArgs) -> eyre::Result<Self> {
Ok(Self {
genesis_path: args.genesis,
private_keys: vec![
parse_key(VALIDATOR_KEY_0),
parse_key(VALIDATOR_KEY_1),
parse_key(VALIDATOR_KEY_2),
],
deployer_key: parse_key(DEPLOYER_KEY),
num_blocks: args.num_blocks,
txs_per_block: args.txs_per_block,
funded_accounts: args.funded_accounts,
background_accounts: args.background_accounts,
storage_slots_per_account: args.storage_slots_per_account,
chain_difflayers: args.chain_difflayers,
triedb: args.triedb,
output_csv: args.output,
label: args.label,
})
}
}
Loading
Loading