diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..2b3d92a --- /dev/null +++ b/.dockerignore @@ -0,0 +1,22 @@ +# Build artifacts +target/ + +# Git +.git/ +.gitignore + +# IDE +.idea/ +.vscode/ +*.swp +*.swo + +# Documentation +*.md +!crates/*/Cargo.toml + +# CI/CD +.github/ + +# Miscellaneous +.DS_Store diff --git a/.github/workflows/sanity.yaml b/.github/workflows/sanity.yaml index cc1950b..4dc6e26 100644 --- a/.github/workflows/sanity.yaml +++ b/.github/workflows/sanity.yaml @@ -19,7 +19,7 @@ jobs: ~/.cargo/registry ~/.cargo/git target - key: ${{ runner.os }}-rust-nightly-${{ hashFiles('**/Cargo.lock') }} + key: ${{ runner.os }}-rust-nightly-fmt-${{ hashFiles('**/Cargo.lock') }} - name: Set up Rust Nightly uses: actions-rs/toolchain@v1 with: @@ -41,7 +41,7 @@ jobs: ~/.cargo/registry ~/.cargo/git target - key: ${{ runner.os }}-rust-stable-${{ hashFiles('**/Cargo.lock') }} + key: ${{ runner.os }}-rust-stable-clippy-all-${{ hashFiles('**/Cargo.lock') }} - name: Set up Rust Stable uses: actions-rs/toolchain@v1 with: @@ -49,7 +49,7 @@ jobs: override: true components: clippy - name: Run Clippy with stable - run: cargo clippy --all-targets --all-features -- -D warnings + run: cargo clippy --workspace --all-targets --all-features -- -D warnings clippy-no-default: name: Clippy (no default features) @@ -63,7 +63,7 @@ jobs: ~/.cargo/registry ~/.cargo/git target - key: ${{ runner.os }}-rust-stable-${{ hashFiles('**/Cargo.lock') }} + key: ${{ runner.os }}-rust-stable-clippy-nodefault-${{ hashFiles('**/Cargo.lock') }} - name: Set up Rust Stable uses: actions-rs/toolchain@v1 with: @@ -71,7 +71,7 @@ jobs: override: true components: clippy - name: Run Clippy with stable - run: cargo clippy --all-targets --no-default-features -- -D warnings + run: cargo clippy --workspace --all-targets --no-default-features -- -D warnings typos: name: Typos @@ -96,14 +96,14 @@ jobs: ~/.cargo/registry ~/.cargo/git target - key: ${{ runner.os }}-rust-stable-${{ hashFiles('**/Cargo.lock') }} + key: ${{ runner.os }}-rust-stable-test-${{ hashFiles('**/Cargo.lock') }} - name: Set up Rust Stable uses: actions-rs/toolchain@v1 with: toolchain: stable override: true - name: Run tests with stable - run: cargo test --all-targets --all-features + run: cargo test --workspace --lib --bins --tests --examples --all-features doc: name: Docs @@ -117,7 +117,7 @@ jobs: ~/.cargo/registry ~/.cargo/git target - key: ${{ runner.os }}-rust-stable-${{ hashFiles('**/Cargo.lock') }} + key: ${{ runner.os }}-rust-stable-doc-${{ hashFiles('**/Cargo.lock') }} - name: Set up Rust Stable uses: actions-rs/toolchain@v1 with: @@ -126,4 +126,4 @@ jobs: - name: Build documentation env: RUSTDOCFLAGS: -D warnings - run: cargo doc --no-deps --document-private-items --all-features --examples \ No newline at end of file + run: cargo doc --workspace --no-deps --document-private-items --all-features --examples \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index a3b61b8..72dc772 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,21 +1,23 @@ -[workspace] -members = [ - ".", # The main rblib library - "src/pipelines/macros", - "src/test_utils/macros", -] -resolver = "2" - [workspace.package] version = "0.4.1" edition = "2024" -rust-version = "1.91" +rust-version = "1.92" license = "MIT" homepage = "https://github.com/flashbots/rblib" repository = "https://github.com/flashbots/rblib" -authors = ["Flashbots ", "Karim Agha "] +authors = ["Flashbots ", "Karim Agha ", "Jules Doumeche "] exclude = [".github/"] +[workspace] +members = [ + "crates/rblib", + "crates/core", + "crates/pipeline", + "crates/pipeline-macros", + "crates/test-utils-macros", +] +resolver = "2" + [workspace.lints.rust] type_alias_bounds = "allow" unreachable_pub = "warn" @@ -33,73 +35,8 @@ cast_precision_loss = "allow" [workspace.metadata.scripts] lint = "cargo clippy --all-targets -- -D warnings" -[package] -name = "rblib" -description = "Modular SDK for building ethereum block builders." -version.workspace = true -edition.workspace = true -rust-version.workspace = true -license.workspace = true -homepage.workspace = true -repository.workspace = true -authors.workspace = true -exclude.workspace = true - -[lib] -doctest = false - -[features] -default = ["optimism", "jemalloc"] - -jemalloc = [ - "dep:tikv-jemallocator", - "reth-origin/jemalloc", - "reth-optimism-cli?/jemalloc", -] - -jemalloc-prof = [ - "jemalloc", - "tikv-jemallocator?/profiling", - "reth-origin/jemalloc-prof", -] - -optimism = [ - "op-alloy", - "op-alloy-rpc-types-engine", - "reth-optimism-node", - "reth-optimism-chainspec", - "reth-optimism-forks", - "reth-optimism-primitives", - "reth-optimism-txpool", - "reth-optimism-rpc", - "reth-optimism-consensus", - "reth-optimism-evm", - "reth-node-builder/op", - "reth-payload-util", - "reth-optimism-cli", - "rblib-tests-macros?/optimism", -] - -test-utils = [ - "nanoid", - "tokio/full", - "alloy-genesis", - "reth-ipc", - "reth-ethereum/test-utils", - "reth-optimism-rpc/client", - "rblib-tests-macros", - "jsonrpsee-core", - "tracing-subscriber", - "ctor", -] - -long-pipelines-syntax = [] - -[dependencies] -# internal dependencies -pipelines-macros = { path = "src/pipelines/macros" } - -# Common dependencies +[workspace.dependencies] +# Common itertools = "0.14" eyre = "0.6" thiserror = "2.0" @@ -118,16 +55,13 @@ metrics = "0.24.0" priority-queue = "2.0.0" rand = "0.9" -# Alloy dependencies -alloy-origin = { version = "1.0.41", package = "alloy", features = [ - "k256", - "rpc-types-mev", -] } +# Alloy +alloy-origin = { version = "1.0.41", package = "alloy", features = ["k256", "rpc-types-mev"] } alloy-evm = "0.23.0" alloy-serde = "1.0.41" alloy-json-rpc = "1.0.41" -# Reth dependencies +# Reth reth-origin = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3", package = "reth" } reth-evm = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" } reth-errors = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" } @@ -137,10 +71,7 @@ reth-rpc-api = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" } reth-rpc-eth-types = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" } reth-basic-payload-builder = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" } reth-ethereum-payload-builder = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" } -reth-ethereum = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3", features = [ - "node", - "evm", -] } +reth-ethereum = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3", features = ["node", "evm"] } reth-network-peers = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" } reth-transaction-pool = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" } reth-payload-builder = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" } @@ -150,7 +81,7 @@ reth-node-api = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" } reth-chainspec = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" } reth-metrics = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" } reth-provider = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" } -reth-ipc = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3", optional = true } +reth-ipc = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" } reth-tracing-otlp = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" } reth-db = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" } reth-eth-wire-types = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" } @@ -158,40 +89,39 @@ reth-network = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" } reth-node-types = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" } reth-db-api = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" } -# Revm dependencies +# Revm revm-database = "9.0" -# Reth-optimism dependencies (optional) -op-alloy = { version = "0.22.4", features = ["full"], optional = true } -op-alloy-rpc-types-engine = { version = "0.22.4", default-features = false, optional = true } +# Reth-optimism +op-alloy = { version = "0.22.4", features = ["full"] } +op-alloy-rpc-types-engine = { version = "0.22.4", default-features = false } op-alloy-flz = "0.13.1" -reth-optimism-node = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3", optional = true } -reth-optimism-chainspec = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3", optional = true } -reth-optimism-forks = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3", optional = true } -reth-optimism-rpc = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3", optional = true } -reth-optimism-txpool = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3", optional = true } -reth-optimism-consensus = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3", optional = true } -reth-optimism-evm = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3", optional = true } -reth-payload-util = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3", optional = true } -reth-optimism-cli = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3", optional = true } -reth-optimism-primitives = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3", optional = true } - -# test-utils -rblib-tests-macros = { path = "src/test_utils/macros", optional = true } -jsonrpsee-core = { version = "0.26.0", optional = true, features = ["client"] } -nanoid = { version = "0.4", optional = true } -alloy-genesis = { version = "1.0", default-features = false, optional = true } -ctor = { version = "0.5", optional = true } -tracing-subscriber = { version = "0.3.20", features = [ - "env-filter", - "json", -], optional = true } - -[target.'cfg(unix)'.dependencies] -tikv-jemallocator = { version = "0.6", optional = true } -[dev-dependencies] -rblib = { path = ".", features = ["test-utils"] } +reth-optimism-node = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" } +reth-optimism-chainspec = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" } +reth-optimism-forks = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" } +reth-optimism-rpc = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" } +reth-optimism-txpool = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" } +reth-optimism-consensus = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" } +reth-optimism-evm = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" } +reth-payload-util = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" } +reth-optimism-cli = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" } +reth-optimism-primitives = { git = "https://github.com/paradigmxyz/reth", tag = "v1.9.3" } -[lints] -workspace = true +# test-utils +criterion = { version = "0.8", features = ["html_reports"] } +gungraun = "0.17.0" +jsonrpsee-core = { version = "0.26.0", features = ["client"] } +nanoid = "0.4" +alloy-genesis = { version = "1.0", default-features = false } +ctor = "0.5" +tracing-subscriber = { version = "0.3.20", features = ["env-filter", "json"] } + +# unix-only allocator +tikv-jemallocator = "0.6" + +# macro +proc-macro2 = "1.0" +quote = "1.0" +syn = { version = "2.0", features = ["full"] } +proc-macro-crate = "3.3.0" diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml new file mode 100644 index 0000000..6cdd1b0 --- /dev/null +++ b/crates/core/Cargo.toml @@ -0,0 +1,152 @@ +[package] +name = "rblib-core" +description = "Modular Payload SDK for building ethereum block builders." +version.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +authors.workspace = true +exclude.workspace = true + +[lints] +workspace = true + +[lib] +doctest = false + +[features] +default = ["optimism", "jemalloc"] +jemalloc = ["dep:tikv-jemallocator", "reth-origin/jemalloc"] +jemalloc-prof = ["jemalloc", "tikv-jemallocator?/profiling", "reth-origin/jemalloc-prof"] + +optimism = [ + "dep:op-alloy", + "dep:op-alloy-rpc-types-engine", + "dep:reth-optimism-node", + "dep:reth-optimism-chainspec", + "dep:reth-optimism-forks", + "dep:reth-optimism-primitives", + "dep:reth-optimism-txpool", + "dep:reth-optimism-rpc", + "dep:reth-optimism-consensus", + "dep:reth-optimism-evm", + "dep:reth-payload-util", + "dep:reth-optimism-cli", + "dep:op-alloy-flz" +] + +test-utils = [ + "reth-ethereum/test-utils", + "dep:alloy-genesis" +] + +[target.'cfg(unix)'.dependencies] +tikv-jemallocator = { workspace = true, optional = true } + +[dependencies] +# Common +itertools.workspace = true +thiserror.workspace = true +tracing.workspace = true +dashmap.workspace = true +uuid.workspace = true +serde.workspace = true +metrics.workspace = true + +# test utils +rand.workspace = true +eyre.workspace = true + +# Alloy +alloy-origin.workspace = true +alloy-evm.workspace = true +alloy-serde.workspace = true + +# Reth +reth-origin.workspace = true +reth-evm.workspace = true +reth-errors.workspace = true +reth-cli.workspace = true +reth-cli-commands.workspace = true +reth-rpc-eth-types.workspace = true +reth-basic-payload-builder.workspace = true +reth-ethereum-payload-builder.workspace = true +reth-ethereum.workspace = true +reth-network-peers.workspace = true +reth-transaction-pool.workspace = true +reth-payload-builder.workspace = true +reth-payload-primitives.workspace = true +reth-node-builder.workspace = true +reth-node-api.workspace = true +reth-chainspec.workspace = true +reth-metrics.workspace = true +reth-provider.workspace = true +reth-tracing-otlp.workspace = true +reth-db.workspace = true +reth-eth-wire-types.workspace = true +reth-network.workspace = true +reth-node-types.workspace = true + +# Revm +revm-database.workspace = true + +# Optimism (optional) +op-alloy = { workspace = true, optional = true } +op-alloy-rpc-types-engine = { workspace = true, optional = true } + +reth-optimism-node = { workspace = true, optional = true } +reth-optimism-chainspec = { workspace = true, optional = true } +reth-optimism-forks = { workspace = true, optional = true } +reth-optimism-rpc = { workspace = true, optional = true } +reth-optimism-txpool = { workspace = true, optional = true } +reth-optimism-consensus = { workspace = true, optional = true } +reth-optimism-evm = { workspace = true, optional = true } +reth-payload-util = { workspace = true, optional = true } +reth-optimism-cli = { workspace = true, optional = true } +reth-optimism-primitives = { workspace = true, optional = true } +op-alloy-flz = { workspace = true, optional = true } + +# test utils +alloy-genesis = { workspace = true, optional = true } + +[dev-dependencies] +rblib-core = { path = ".", features = ["test-utils"] } +criterion = { workspace = true } +gungraun = { workspace = true } + +[[bench]] +name = "apply_criterion" +path = "benches/revm_regression/apply_criterion.rs" +harness = false + +[[bench]] +name = "apply_valgrind" +path = "benches/revm_regression/apply_valgrind.rs" +harness = false + +[[bench]] +name = "revert_criterion" +path = "benches/revm_regression/revert_criterion.rs" +harness = false + +[[bench]] +name = "revert_valgrind" +path = "benches/revm_regression/revert_valgrind.rs" +harness = false + +[[bench]] +name = "traversal_criterion" +path = "benches/revm_regression/traversal_criterion.rs" +harness = false + +[[bench]] +name = "traversal_valgrind" +path = "benches/revm_regression/traversal_valgrind.rs" +harness = false + +[[bench]] +name = "forking" +path = "benches/revm_regression/forking.rs" +harness = false diff --git a/crates/core/bench/Dockerfile b/crates/core/bench/Dockerfile new file mode 100644 index 0000000..3c24c62 --- /dev/null +++ b/crates/core/bench/Dockerfile @@ -0,0 +1,40 @@ +# Dockerfile for running rblib-core benchmarks + +FROM rust:1.92-bookworm + +RUN apt-get update && apt-get install -y \ + valgrind \ + pkg-config \ + libssl-dev \ + clang \ + libclang-dev \ + && rm -rf /var/lib/apt/lists/* + +RUN cargo install gungraun-runner --version 0.17.0 + +WORKDIR /workspace + +# Copy workspace manifests +COPY Cargo.toml Cargo.lock ./ +COPY crates/rblib/Cargo.toml crates/rblib/ +COPY crates/core/Cargo.toml crates/core/ +COPY crates/pipeline/Cargo.toml crates/pipeline/ +COPY crates/pipeline-macros/Cargo.toml crates/pipeline-macros/ +COPY crates/test-utils-macros/Cargo.toml crates/test-utils-macros/ + +# Minimal stubs for workspace members +RUN mkdir -p crates/rblib/src crates/core/src crates/pipeline/src \ + crates/pipeline-macros/src crates/test-utils-macros/src && \ + touch crates/rblib/src/lib.rs crates/core/src/lib.rs crates/pipeline/src/lib.rs \ + crates/pipeline-macros/src/lib.rs crates/test-utils-macros/src/lib.rs + +# Cache dependencies +RUN cargo fetch + +# Copy rblib-core source +COPY crates/core crates/core + +# Build benchmarks +RUN cargo build --release -p rblib-core --features test-utils --benches + +CMD ["cargo", "bench", "-p", "rblib-core"] diff --git a/crates/core/bench/README.md b/crates/core/bench/README.md new file mode 100644 index 0000000..c533a4d --- /dev/null +++ b/crates/core/bench/README.md @@ -0,0 +1,74 @@ +# Benchmarking rblib-core + +## Overview + +The `revm_regression` benchmarks compare checkpoint operations against equivalent direct REVM/BundleState operations to +quantify abstraction overhead. + +### Scenarios + +| Benchmark | What it measures | +|---------------|--------------------------------------------------------------------| +| `apply_*` | Transaction execution: checkpoint chain vs single mutable State | +| `revert_*` | Revert capability: checkpoint fork vs BundleState::revert_latest() | +| `traversal_*` | State lookup cost at varying checkpoint chain depths | +| `forking` | Cost of creating parallel branches from shared checkpoint | + +See [`benches/revm_regression/scenarios.rs`](../benches/revm_regression/scenarios.rs) for detailed +scenario definitions including workloads, approaches compared, and what each benchmark measures. + +### Harnesses + +- **Criterion** (`*_criterion`): Wall-clock timing (cross-platform) +- **Valgrind** (`*_valgrind`): CPU instructions, cache behavior, heap profiling (Linux only) + +## Running Benchmarks + +### macOS (via Docker) + +```bash +# Run all benchmarks in Docker, results copied to target/ +./crates/core/bench/macos.sh +``` + +Docker build: + +```bash +docker build -f crates/core/bench/Dockerfile -t rblib-core-bench . +``` + +### Linux (native) + +```bash +# Criterion only +cargo bench -p rblib-core + +# All including Valgrind (requires valgrind + gungraun-runner) +cargo bench -p rblib-core --bench apply_valgrind --bench revert_valgrind --bench traversal_valgrind +``` + +or a specific benchmark: + +```bash +cargo bench -p rblib-core --bench apply_criterion +``` + +## Results + +### Criterion + +Results in `target/criterion/` with HTML reports. + +### Valgrind (via Gungraun) + +Results in `target/gungraun/`. + +``` +apply_valgrind::tx_execution::single_state tx_10:setup_10() +Instructions: 413062 | 506572 (-18.45%) [-1.23x] + ^ current ^ checkpoint ^ single_state is 18% faster +``` + +- Negative percentage = baseline (revm) uses fewer instructions +- `[-1.23x]` = checkpoint uses 1.23x more instructions + diff --git a/crates/core/bench/macos.sh b/crates/core/bench/macos.sh new file mode 100755 index 0000000..aa36cc3 --- /dev/null +++ b/crates/core/bench/macos.sh @@ -0,0 +1,37 @@ +#!/bin/bash +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +WORKSPACE_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)" +CORE_DIR="$WORKSPACE_ROOT/crates/core" + +cd "$WORKSPACE_ROOT" + +echo "=== Building Docker image ===" +docker build -f crates/core/bench/Dockerfile -t rblib-core-bench . + +echo "" +echo "=== Running Criterion benchmarks ===" +docker run --rm \ + -v "$CORE_DIR/target/criterion:/workspace/crates/core/target/criterion" \ + rblib-core-bench \ + cargo bench -p rblib-core \ + --bench apply_criterion \ + --bench revert_criterion \ + --bench traversal_criterion \ + --bench forking + +echo "" +echo "=== Running Valgrind benchmarks ===" +docker run --rm \ + -v "$CORE_DIR/target/gungraun:/workspace/target/gungraun" \ + rblib-core-bench \ + cargo bench -p rblib-core \ + --bench apply_valgrind \ + --bench revert_valgrind \ + --bench traversal_valgrind + +echo "" +echo "=== Done ===" +echo "Criterion results: crates/core/target/criterion/" +echo "Valgrind results: crates/core/target/gungraun/" diff --git a/crates/core/benches/revm_regression/apply_criterion.rs b/crates/core/benches/revm_regression/apply_criterion.rs new file mode 100644 index 0000000..c5ee5ed --- /dev/null +++ b/crates/core/benches/revm_regression/apply_criterion.rs @@ -0,0 +1,91 @@ +//! Criterion benchmark: `checkpoint.apply()` vs direct `transact_commit` on +//! single State. + +mod scenarios; + +use { + criterion::{ + BenchmarkId, + Criterion, + Throughput, + criterion_group, + criterion_main, + }, + rblib_core::reth::{ + evm::{ConfigureEvm, Evm}, + revm::{ + State, + db::{WrapDatabaseRef, states::bundle_state::BundleRetention}, + }, + }, + scenarios::{APPLY_SAMPLES, ApplySetup, generate_transactions}, +}; + +fn bench_checkpoint_vs_direct(c: &mut Criterion) { + let mut group = c.benchmark_group("tx_execution"); + + for n in APPLY_SAMPLES { + let txs = generate_transactions(n); + let param = format!("{n}_transfers"); + + group.throughput(Throughput::Elements(n)); + + group.bench_with_input( + BenchmarkId::new("checkpoint_chain", ¶m), + &txs, + |b, txs| { + b.iter_with_setup( + || { + let setup = scenarios::apply_setup(n); + let checkpoint = setup.block.start(); + (setup.block, checkpoint, txs.clone()) + }, + |(_block, mut checkpoint, txs)| { + for tx in txs { + checkpoint = checkpoint.apply(tx).unwrap(); + } + checkpoint + }, + ); + }, + ); + + group.bench_with_input( + BenchmarkId::new("single_state", ¶m), + &txs, + |b, txs| { + b.iter_with_setup( + || { + let ApplySetup { block, .. } = scenarios::apply_setup(n); + (block, txs.clone()) + }, + |(block, txs)| { + let base_state = block.start(); + let mut state = State::builder() + .with_database(WrapDatabaseRef(base_state)) + .with_bundle_update() + .build(); + + let evm_config = block.evm_config(); + let evm_env = block.evm_env(); + + for tx in txs { + evm_config + .evm_with_env(&mut state, evm_env.clone()) + .transact_commit(&tx) + .expect("tx failed"); + } + + state.merge_transitions(BundleRetention::Reverts); + state.take_bundle() + }, + ); + }, + ); + } + + group.finish(); +} + +criterion_group!(benches, bench_checkpoint_vs_direct); +criterion_main!(benches); diff --git a/crates/core/benches/revm_regression/apply_valgrind.rs b/crates/core/benches/revm_regression/apply_valgrind.rs new file mode 100644 index 0000000..bbb8c06 --- /dev/null +++ b/crates/core/benches/revm_regression/apply_valgrind.rs @@ -0,0 +1,93 @@ +//! Valgrind benchmark: `checkpoint.apply()` vs direct `transact_commit` on +//! single State. + +mod scenarios; + +use { + gungraun::{ + Callgrind, + Dhat, + EventKind, + FlamegraphConfig, + LibraryBenchmarkConfig, + library_benchmark, + library_benchmark_group, + main, + }, + rblib_core::reth::{ + evm::{ConfigureEvm, Evm}, + revm::{ + State, + db::{WrapDatabaseRef, states::bundle_state::BundleRetention}, + }, + }, + scenarios::{ + ApplySetup, + apply_setup_10, + apply_setup_100, + apply_setup_500, + apply_setup_1000, + }, + std::hint::black_box, +}; + +#[library_benchmark] +#[bench::tx_10(apply_setup_10())] +#[bench::tx_100(apply_setup_100())] +#[bench::tx_500(apply_setup_500())] +#[bench::tx_1000(apply_setup_1000())] +fn checkpoint_chain(setup: ApplySetup) { + let ApplySetup { block, txs } = setup; + let mut checkpoint = block.start(); + + for tx in txs { + checkpoint = checkpoint.apply(tx).unwrap(); + } + + black_box(checkpoint); +} + +#[library_benchmark] +#[bench::tx_10(apply_setup_10())] +#[bench::tx_100(apply_setup_100())] +#[bench::tx_500(apply_setup_500())] +#[bench::tx_1000(apply_setup_1000())] +fn single_state(setup: ApplySetup) { + let ApplySetup { block, txs } = setup; + let base_state = block.start(); + + let mut state = State::builder() + .with_database(WrapDatabaseRef(base_state)) + .with_bundle_update() + .build(); + + let evm_config = block.evm_config(); + let evm_env = block.evm_env(); + + for tx in txs { + evm_config + .evm_with_env(&mut state, evm_env.clone()) + .transact_commit(&tx) + .expect("tx failed"); + } + + state.merge_transitions(BundleRetention::Reverts); + black_box(state.take_bundle()); +} + +library_benchmark_group!( + name = tx_execution; + compare_by_id = true; + benchmarks = checkpoint_chain, single_state +); + +main!( + config = LibraryBenchmarkConfig::default() + .tool( + Callgrind::default() + .soft_limits([(EventKind::Ir, 5.0)]) + .flamegraph(FlamegraphConfig::default()) + ) + .tool(Dhat::default()); + library_benchmark_groups = tx_execution +); diff --git a/crates/core/benches/revm_regression/forking.rs b/crates/core/benches/revm_regression/forking.rs new file mode 100644 index 0000000..4fbe3f1 --- /dev/null +++ b/crates/core/benches/revm_regression/forking.rs @@ -0,0 +1,86 @@ +//! Criterion benchmark: cost of creating and using parallel branches from a +//! shared checkpoint. + +mod scenarios; + +use { + criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}, + rblib_core::reth::primitives::Recovered, + scenarios::{ + BRANCH_COUNTS, + FORK_DEPTHS, + Transaction, + build_base_chain, + generate_branch_txs, + }, +}; + +fn bench_fork_and_apply(c: &mut Criterion) { + let mut group = c.benchmark_group("fork_and_apply"); + + for depth in FORK_DEPTHS { + for num_branches in BRANCH_COUNTS { + let branch_txs: Vec>> = (1..=num_branches) + .map(|signer_idx| { + generate_branch_txs(u32::try_from(signer_idx).unwrap()) + }) + .collect(); + + let fork_point = build_base_chain(depth); + + let id = BenchmarkId::new( + format!("base_{depth}_txs"), + format!("{num_branches}_branches_10tx_each"), + ); + + group.bench_with_input( + id, + &(&fork_point, &branch_txs), + |b, (fork_point, branch_txs)| { + b.iter(|| { + let mut branches = Vec::with_capacity(branch_txs.len()); + + for txs in *branch_txs { + let mut branch = (*fork_point).clone(); + + for tx in txs { + branch = branch.apply(tx.clone()).unwrap(); + } + + branches.push(branch); + } + + branches + }); + }, + ); + } + } + + group.finish(); +} + +fn bench_fork_only(c: &mut Criterion) { + let mut group = c.benchmark_group("clone_10_forks"); + + for depth in FORK_DEPTHS { + let fork_point = build_base_chain(depth); + let param = format!("base_{depth}_txs"); + + group.bench_with_input( + BenchmarkId::from_parameter(¶m), + &fork_point, + |b, fork_point| { + b.iter(|| { + let forks: Vec<_> = (0..10).map(|_| fork_point.clone()).collect(); + forks + }); + }, + ); + } + + group.finish(); +} + +criterion_group!(benches, bench_fork_and_apply, bench_fork_only); +criterion_main!(benches); diff --git a/crates/core/benches/revm_regression/revert_criterion.rs b/crates/core/benches/revm_regression/revert_criterion.rs new file mode 100644 index 0000000..053c2f7 --- /dev/null +++ b/crates/core/benches/revm_regression/revert_criterion.rs @@ -0,0 +1,237 @@ +//! Criterion benchmark: checkpoint revert vs `BundleState::revert_latest()`. + +mod scenarios; + +use { + criterion::{ + BenchmarkId, + Criterion, + Throughput, + criterion_group, + criterion_main, + }, + rblib_core::reth::{ + evm::{ConfigureEvm, Evm}, + revm::{ + State, + db::{ + BundleState, + WrapDatabaseRef, + states::bundle_state::BundleRetention, + }, + }, + }, + scenarios::{ + REVERT_PAIR_COUNTS, + generate_transaction_pairs, + generate_transactions, + }, +}; + +fn bench_apply_revert_continue(c: &mut Criterion) { + let mut group = c.benchmark_group("apply_revert_continue"); + + for num_pairs in REVERT_PAIR_COUNTS { + let pairs = generate_transaction_pairs(num_pairs); + let param = format!("{num_pairs}_pairs"); + + group.throughput(Throughput::Elements(num_pairs)); + + group.bench_with_input( + BenchmarkId::new("checkpoint", ¶m), + &pairs, + |b, pairs| { + b.iter_with_setup( + || { + let setup = scenarios::revert_setup(num_pairs); + let checkpoint = setup.block.start(); + (setup.block, checkpoint, pairs.clone()) + }, + |(_block, mut checkpoint, pairs)| { + for (kept_tx, reverted_tx) in pairs { + checkpoint = checkpoint.apply(kept_tx).unwrap(); + let _discarded = checkpoint.apply(reverted_tx).unwrap(); + } + checkpoint + }, + ); + }, + ); + + group.bench_with_input( + BenchmarkId::new("direct_per_tx_merge", ¶m), + &pairs, + |b, pairs| { + b.iter_with_setup( + || { + let setup = scenarios::revert_setup(num_pairs); + (setup.block, pairs.clone()) + }, + |(block, pairs)| { + let base_state = block.start(); + let evm_config = block.evm_config(); + let evm_env = block.evm_env(); + let mut bundle = BundleState::default(); + + for (kept_tx, reverted_tx) in pairs { + let mut state = State::builder() + .with_database(WrapDatabaseRef(&base_state)) + .with_bundle_update() + .with_bundle_prestate(bundle) + .build(); + + evm_config + .evm_with_env(&mut state, evm_env.clone()) + .transact_commit(&kept_tx) + .expect("tx failed"); + state.merge_transitions(BundleRetention::Reverts); + + evm_config + .evm_with_env(&mut state, evm_env.clone()) + .transact_commit(&reverted_tx) + .expect("tx failed"); + state.merge_transitions(BundleRetention::Reverts); + + bundle = state.take_bundle(); + bundle.revert_latest(); + } + + bundle + }, + ); + }, + ); + + group.bench_with_input( + BenchmarkId::new("direct_batch_baseline", ¶m), + &pairs, + |b, pairs| { + b.iter_with_setup( + || { + let setup = scenarios::revert_setup(num_pairs); + let kept_txs: Vec<_> = + pairs.iter().map(|(kept, _)| kept.clone()).collect(); + (setup.block, kept_txs) + }, + |(block, txs)| { + let base_state = block.start(); + let mut state = State::builder() + .with_database(WrapDatabaseRef(base_state)) + .with_bundle_update() + .build(); + + let evm_config = block.evm_config(); + let evm_env = block.evm_env(); + + for tx in txs { + evm_config + .evm_with_env(&mut state, evm_env.clone()) + .transact_commit(&tx) + .expect("tx failed"); + } + + state.merge_transitions(BundleRetention::Reverts); + state.take_bundle() + }, + ); + }, + ); + } + + group.finish(); +} + +fn bench_apply_then_revert(c: &mut Criterion) { + let mut group = c.benchmark_group("apply_then_revert"); + + let test_cases: [(u64, u64); 4] = [(10, 5), (50, 25), (100, 10), (100, 50)]; + + for (total, to_revert) in test_cases { + let txs = generate_transactions(total); + let param = format!("{total}_apply_{to_revert}_revert"); + let kept = total - to_revert; + + group.throughput(Throughput::Elements(kept)); + + group.bench_with_input( + BenchmarkId::new("checkpoint", ¶m), + &txs, + |b, txs| { + b.iter_with_setup( + || { + let setup = scenarios::apply_setup(total); + let checkpoint = setup.block.start(); + ( + setup.block, + checkpoint, + txs.clone(), + usize::try_from(to_revert).unwrap(), + ) + }, + |(_block, mut checkpoint, txs, to_revert)| { + let mut checkpoints = Vec::with_capacity(txs.len()); + + for tx in txs { + checkpoint = checkpoint.apply(tx).unwrap(); + checkpoints.push(checkpoint.clone()); + } + + let revert_to_idx = checkpoints.len() - to_revert - 1; + checkpoints[revert_to_idx].clone() + }, + ); + }, + ); + + group.bench_with_input( + BenchmarkId::new("direct_per_tx_merge", ¶m), + &txs, + |b, txs| { + b.iter_with_setup( + || { + let setup = scenarios::apply_setup(total); + ( + setup.block, + txs.clone(), + usize::try_from(to_revert).unwrap(), + ) + }, + |(block, txs, to_revert)| { + let base_state = block.start(); + let evm_config = block.evm_config(); + let evm_env = block.evm_env(); + let mut bundle = BundleState::default(); + + for tx in &txs { + let mut state = State::builder() + .with_database(WrapDatabaseRef(&base_state)) + .with_bundle_update() + .with_bundle_prestate(bundle) + .build(); + + evm_config + .evm_with_env(&mut state, evm_env.clone()) + .transact_commit(tx) + .expect("tx failed"); + + state.merge_transitions(BundleRetention::Reverts); + bundle = state.take_bundle(); + } + + bundle.revert(to_revert); + bundle + }, + ); + }, + ); + } + + group.finish(); +} + +criterion_group!( + benches, + bench_apply_revert_continue, + bench_apply_then_revert +); +criterion_main!(benches); diff --git a/crates/core/benches/revm_regression/revert_valgrind.rs b/crates/core/benches/revm_regression/revert_valgrind.rs new file mode 100644 index 0000000..7e77ec6 --- /dev/null +++ b/crates/core/benches/revm_regression/revert_valgrind.rs @@ -0,0 +1,135 @@ +//! Valgrind benchmark: checkpoint revert vs `BundleState::revert_latest()`. + +mod scenarios; + +use { + gungraun::{ + Callgrind, + Dhat, + EventKind, + FlamegraphConfig, + LibraryBenchmarkConfig, + library_benchmark, + library_benchmark_group, + main, + }, + rblib_core::reth::{ + evm::{ConfigureEvm, Evm}, + revm::{ + State, + db::{ + BundleState, + WrapDatabaseRef, + states::bundle_state::BundleRetention, + }, + }, + }, + scenarios::{ + RevertSetup, + revert_setup_5, + revert_setup_25, + revert_setup_100, + revert_setup_250, + }, + std::hint::black_box, +}; + +#[library_benchmark] +#[bench::pairs_5(revert_setup_5())] +#[bench::pairs_25(revert_setup_25())] +#[bench::pairs_100(revert_setup_100())] +#[bench::pairs_250(revert_setup_250())] +fn checkpoint(setup: RevertSetup) { + let RevertSetup { block, pairs } = setup; + let mut checkpoint = block.start(); + + for (kept_tx, reverted_tx) in pairs { + checkpoint = checkpoint.apply(kept_tx).unwrap(); + let _discarded = checkpoint.apply(reverted_tx).unwrap(); + } + + black_box(checkpoint); +} + +#[library_benchmark] +#[bench::pairs_5(revert_setup_5())] +#[bench::pairs_25(revert_setup_25())] +#[bench::pairs_100(revert_setup_100())] +#[bench::pairs_250(revert_setup_250())] +fn direct_per_tx_merge(setup: RevertSetup) { + let RevertSetup { block, pairs } = setup; + let base_state = block.start(); + let evm_config = block.evm_config(); + let evm_env = block.evm_env(); + let mut bundle = BundleState::default(); + + for (kept_tx, reverted_tx) in pairs { + let mut state = State::builder() + .with_database(WrapDatabaseRef(&base_state)) + .with_bundle_update() + .with_bundle_prestate(bundle) + .build(); + + evm_config + .evm_with_env(&mut state, evm_env.clone()) + .transact_commit(&kept_tx) + .expect("tx failed"); + state.merge_transitions(BundleRetention::Reverts); + + evm_config + .evm_with_env(&mut state, evm_env.clone()) + .transact_commit(&reverted_tx) + .expect("tx failed"); + state.merge_transitions(BundleRetention::Reverts); + + bundle = state.take_bundle(); + bundle.revert_latest(); + } + + black_box(bundle); +} + +#[library_benchmark] +#[bench::pairs_5(revert_setup_5())] +#[bench::pairs_25(revert_setup_25())] +#[bench::pairs_100(revert_setup_100())] +#[bench::pairs_250(revert_setup_250())] +fn direct_batch_baseline(setup: RevertSetup) { + let RevertSetup { block, pairs } = setup; + let base_state = block.start(); + + let mut state = State::builder() + .with_database(WrapDatabaseRef(base_state)) + .with_bundle_update() + .build(); + + let evm_config = block.evm_config(); + let evm_env = block.evm_env(); + + for (kept_tx, _) in pairs { + evm_config + .evm_with_env(&mut state, evm_env.clone()) + .transact_commit(&kept_tx) + .expect("tx failed"); + } + + state.merge_transitions(BundleRetention::Reverts); + black_box(state.take_bundle()); +} + +library_benchmark_group!( + name = apply_revert_continue; + compare_by_id = true; + benchmarks = checkpoint, direct_per_tx_merge, direct_batch_baseline +); + +main!( + config = LibraryBenchmarkConfig::default() + .tool( + Callgrind::default() + .soft_limits([(EventKind::Ir, 5.0)]) + .flamegraph(FlamegraphConfig::default()) + ) + .tool(Dhat::default()); + library_benchmark_groups = apply_revert_continue +); diff --git a/crates/core/benches/revm_regression/scenarios.rs b/crates/core/benches/revm_regression/scenarios.rs new file mode 100644 index 0000000..51c8267 --- /dev/null +++ b/crates/core/benches/revm_regression/scenarios.rs @@ -0,0 +1,241 @@ +//! Shared scenario definitions for `revm_regression` benchmarks. +//! +//! This module defines the test scenarios used to measure the cost of +//! abstraction of the Checkpoint system against direct REVM/BundleState usage. +//! +//! # Scenarios +//! +//! ## Apply +//! +//! Measures transaction execution overhead. +//! +//! **Workload**: N simple ETH transfers (21k gas each) from a single funded +//! account to random recipients. +//! +//! **Approaches compared**: +//! - `checkpoint_chain`: Each tx creates a new immutable Checkpoint via +//! `checkpoint.apply(tx)`. State lookups traverse the checkpoint chain. +//! - `single_state`: All txs applied to one mutable `State` via +//! `transact_commit()`. Single `BundleState` accumulates all changes. +//! +//! **What it measures**: Per-transaction overhead of checkpoint creation and +//! the growing cost of state lookups as the chain deepens. +//! +//! ## Revert +//! +//! Measures the cost of maintaining revert capability. +//! +//! **Workload**: Apply-revert-continue pattern. For each pair: apply a "kept" +//! tx, apply a "reverted" tx, then discard the second. +//! +//! **Approaches compared**: +//! - `checkpoint`: Revert by keeping reference to previous checkpoint (O(1)). +//! - `direct_per_tx_merge`: Merge after each tx, use +//! `BundleState::revert_latest()`. +//! - `direct_batch_baseline`: No revert capability, only applies kept txs +//! (baseline). +//! +//! **What it measures**: Cost of revert capability. Checkpoint revert is O(1) +//! but pays traversal cost on subsequent reads. Direct revert is O(accounts) +//! but has O(1) reads. +//! +//! ## Traversal +//! +//! Measures state lookup cost at varying checkpoint chain depths. +//! +//! **Workload**: Build a checkpoint chain of depth N, then perform single +//! account reads. +//! +//! **Read patterns**: +//! - `hot`: Read the signer account (modified in every checkpoint, found at +//! top). +//! - `cold`: Read an untouched account (must traverse entire chain to base +//! state). +//! - `deep`: Read recipient of first tx (found at bottom of chain). +//! +//! **What it measures**: How read latency scales with chain depth. Cold reads +//! show worst-case traversal cost. Hot reads show best-case. +//! +//! ## Forking +//! +//! Measures the cost of creating parallel branches from a shared checkpoint. +//! +//! **Workload**: Build a base chain of depth M, then create N branches from the +//! tip, each applying 10 transactions from a unique signer (no state +//! contention). +//! +//! ```text +//! Base: [0] -> [1] -> ... -> [M] +//! |-> Branch A: 10 txs +//! |-> Branch B: 10 txs +//! |-> Branch C: 10 txs +//! ``` +//! +//! **What it measures**: Cost of checkpoint cloning (Arc efficiency) and +//! whether parallel branches interfere with each other's performance. + +#![allow(dead_code, unreachable_pub)] + +use rblib_core::{ + alloy::primitives::{Address, U256}, + payload::{BlockContext, Checkpoint}, + platform::Ethereum, + reth::primitives::Recovered, + test_utils::{ + BlockContextMocked, + FundedAccounts, + transfer_tx, + transfer_tx_to, + }, +}; + +pub type Transaction = rblib_core::platform::types::Transaction; + +// ============================================================================ +// Apply scenario +// ============================================================================ + +pub const APPLY_SAMPLES: [u64; 4] = [10, 100, 500, 1000]; + +pub struct ApplySetup { + pub block: BlockContext, + pub txs: Vec>, +} + +pub fn generate_transactions(n: u64) -> Vec> { + let signer = FundedAccounts::signer(0); + (0..n) + .map(|nonce| transfer_tx::(&signer, nonce, U256::from(1u64))) + .collect() +} + +pub fn apply_setup(n: u64) -> ApplySetup { + ApplySetup { + block: BlockContext::::mocked(), + txs: generate_transactions(n), + } +} + +pub fn apply_setup_10() -> ApplySetup { + apply_setup(10) +} +pub fn apply_setup_100() -> ApplySetup { + apply_setup(100) +} +pub fn apply_setup_500() -> ApplySetup { + apply_setup(500) +} +pub fn apply_setup_1000() -> ApplySetup { + apply_setup(1000) +} + +// ============================================================================ +// Revert scenario +// ============================================================================ + +pub const REVERT_PAIR_COUNTS: [u64; 4] = [5, 25, 100, 250]; + +pub struct RevertSetup { + pub block: BlockContext, + pub pairs: Vec<(Recovered, Recovered)>, +} + +pub fn generate_transaction_pairs( + num_pairs: u64, +) -> Vec<(Recovered, Recovered)> { + let signer = FundedAccounts::signer(0); + (0..num_pairs) + .map(|i| { + ( + transfer_tx::(&signer, i, U256::from(1u64)), + transfer_tx::(&signer, i + 1, U256::from(1u64)), + ) + }) + .collect() +} + +pub fn revert_setup(num_pairs: u64) -> RevertSetup { + RevertSetup { + block: BlockContext::::mocked(), + pairs: generate_transaction_pairs(num_pairs), + } +} + +pub fn revert_setup_5() -> RevertSetup { + revert_setup(5) +} +pub fn revert_setup_25() -> RevertSetup { + revert_setup(25) +} +pub fn revert_setup_100() -> RevertSetup { + revert_setup(100) +} +pub fn revert_setup_250() -> RevertSetup { + revert_setup(250) +} + +// ============================================================================ +// Traversal scenario +// ============================================================================ + +pub const TRAVERSAL_DEPTHS: [u64; 5] = [10, 50, 100, 500, 1000]; + +pub fn build_checkpoint_chain(n: u64) -> (Checkpoint, Address) { + let block = BlockContext::::mocked(); + let mut checkpoint = block.start(); + let signer = FundedAccounts::signer(0); + let mut first_recipient = Address::ZERO; + + for nonce in 0..n { + let to = Address::random(); + let tx: Recovered = + transfer_tx_to::(&signer, nonce, U256::from(1u64), to); + + if nonce == 0 { + first_recipient = to; + } + checkpoint = checkpoint.apply(tx).unwrap(); + } + + (checkpoint, first_recipient) +} + +// ============================================================================ +// Forking scenario +// ============================================================================ + +pub const FORK_DEPTHS: [u64; 3] = [10, 100, 500]; +pub const BRANCH_COUNTS: [usize; 3] = [2, 5, 10]; +pub const TXS_PER_BRANCH: u64 = 10; + +pub fn build_base_chain(n: u64) -> Checkpoint { + let block = BlockContext::::mocked(); + let mut checkpoint = block.start(); + let signer = FundedAccounts::signer(0); + + for nonce in 0..n { + let tx: Recovered = transfer_tx_to::( + &signer, + nonce, + U256::from(1u64), + Address::random(), + ); + checkpoint = checkpoint.apply(tx).unwrap(); + } + + checkpoint +} + +pub fn generate_branch_txs(signer_index: u32) -> Vec> { + let signer = FundedAccounts::signer(signer_index); + (0..TXS_PER_BRANCH) + .map(|nonce| { + transfer_tx_to::( + &signer, + nonce, + U256::from(1u64), + Address::random(), + ) + }) + .collect() +} diff --git a/crates/core/benches/revm_regression/traversal_criterion.rs b/crates/core/benches/revm_regression/traversal_criterion.rs new file mode 100644 index 0000000..f25cf13 --- /dev/null +++ b/crates/core/benches/revm_regression/traversal_criterion.rs @@ -0,0 +1,78 @@ +//! Criterion benchmark: state lookup cost at varying checkpoint chain depths. +//! +//! Measures how read performance scales with chain depth for hot, cold, and +//! deep accounts. + +mod scenarios; + +use { + criterion::{BenchmarkId, Criterion, criterion_group, criterion_main}, + rblib_core::{ + alloy::primitives::Address, + reth::revm::DatabaseRef, + test_utils::FundedAccounts, + }, + scenarios::{TRAVERSAL_DEPTHS, build_checkpoint_chain}, +}; + +fn bench_hot_read(c: &mut Criterion) { + let mut group = c.benchmark_group("read_hot_account"); + let signer_address = FundedAccounts::address(0); + + for depth in TRAVERSAL_DEPTHS { + let (checkpoint, _) = build_checkpoint_chain(depth); + let param = format!("chain_depth_{depth}"); + + group.bench_with_input( + BenchmarkId::from_parameter(¶m), + &checkpoint, + |b, checkpoint| { + b.iter(|| checkpoint.basic_ref(signer_address)); + }, + ); + } + + group.finish(); +} + +fn bench_cold_read(c: &mut Criterion) { + let mut group = c.benchmark_group("read_untouched_account"); + let untouched_address = Address::repeat_byte(0xDE); + + for depth in TRAVERSAL_DEPTHS { + let (checkpoint, _) = build_checkpoint_chain(depth); + let param = format!("chain_depth_{depth}"); + + group.bench_with_input( + BenchmarkId::from_parameter(¶m), + &checkpoint, + |b, checkpoint| { + b.iter(|| checkpoint.basic_ref(untouched_address)); + }, + ); + } + + group.finish(); +} + +fn bench_deep_read(c: &mut Criterion) { + let mut group = c.benchmark_group("read_deep_account"); + + for depth in TRAVERSAL_DEPTHS { + let (checkpoint, first_recipient) = build_checkpoint_chain(depth); + let param = format!("chain_depth_{depth}"); + + group.bench_with_input( + BenchmarkId::from_parameter(¶m), + &(checkpoint, first_recipient), + |b, (checkpoint, addr)| { + b.iter(|| checkpoint.basic_ref(*addr)); + }, + ); + } + + group.finish(); +} + +criterion_group!(benches, bench_hot_read, bench_cold_read, bench_deep_read); +criterion_main!(benches); diff --git a/crates/core/benches/revm_regression/traversal_valgrind.rs b/crates/core/benches/revm_regression/traversal_valgrind.rs new file mode 100644 index 0000000..5891ff6 --- /dev/null +++ b/crates/core/benches/revm_regression/traversal_valgrind.rs @@ -0,0 +1,99 @@ +//! Valgrind benchmark: state lookup cost at varying checkpoint chain depths. + +#![allow(clippy::needless_pass_by_value)] + +mod scenarios; + +use { + gungraun::{ + Callgrind, + Dhat, + EventKind, + FlamegraphConfig, + LibraryBenchmarkConfig, + library_benchmark, + library_benchmark_group, + main, + }, + rblib_core::{ + alloy::primitives::Address, + payload::Checkpoint, + platform::Ethereum, + reth::revm::DatabaseRef, + test_utils::FundedAccounts, + }, + scenarios::build_checkpoint_chain, + std::hint::black_box, +}; + +struct TraversalSetup { + checkpoint: Checkpoint, + first_recipient: Address, +} + +fn setup(depth: u64) -> TraversalSetup { + let (checkpoint, first_recipient) = build_checkpoint_chain(depth); + TraversalSetup { + checkpoint, + first_recipient, + } +} + +fn setup_10() -> TraversalSetup { + setup(10) +} +fn setup_100() -> TraversalSetup { + setup(100) +} +fn setup_500() -> TraversalSetup { + setup(500) +} +fn setup_1000() -> TraversalSetup { + setup(1000) +} + +#[library_benchmark] +#[bench::depth_10(setup_10())] +#[bench::depth_100(setup_100())] +#[bench::depth_500(setup_500())] +#[bench::depth_1000(setup_1000())] +fn hot_read(setup: TraversalSetup) { + let signer_address = FundedAccounts::address(0); + black_box(setup.checkpoint.basic_ref(signer_address).unwrap()); +} + +#[library_benchmark] +#[bench::depth_10(setup_10())] +#[bench::depth_100(setup_100())] +#[bench::depth_500(setup_500())] +#[bench::depth_1000(setup_1000())] +fn cold_read(setup: TraversalSetup) { + let untouched_address = Address::repeat_byte(0xDE); + black_box(setup.checkpoint.basic_ref(untouched_address).unwrap()); +} + +#[library_benchmark] +#[bench::depth_10(setup_10())] +#[bench::depth_100(setup_100())] +#[bench::depth_500(setup_500())] +#[bench::depth_1000(setup_1000())] +fn deep_read(setup: TraversalSetup) { + black_box(setup.checkpoint.basic_ref(setup.first_recipient).unwrap()); +} + +library_benchmark_group!( + name = chain_traversal; + compare_by_id = true; + benchmarks = hot_read, cold_read, deep_read +); + +main!( + config = LibraryBenchmarkConfig::default() + .tool( + Callgrind::default() + .soft_limits([(EventKind::Ir, 5.0)]) + .flamegraph(FlamegraphConfig::default()) + ) + .tool(Dhat::default()); + library_benchmark_groups = chain_traversal +); diff --git a/src/lib.rs b/crates/core/src/lib.rs similarity index 84% rename from src/lib.rs rename to crates/core/src/lib.rs index 857aff4..bfd658c 100644 --- a/src/lib.rs +++ b/crates/core/src/lib.rs @@ -1,38 +1,25 @@ -// core modules, partially re-exported through `rblib::prelude` -mod payload; -mod pipelines; -mod platform; +// Core payload API and platform abstractions. +pub mod payload; +pub mod platform; -// RBLib Core Public API Prelude -pub mod prelude { - pub use { - super::{payload::*, pipelines::*, platform::*}, - metrics::{Counter, Gauge, Histogram}, - pipelines_macros::MetricsSet, - }; - - pub(crate) use super::Variant; -} +#[cfg(any(test, feature = "test-utils"))] +pub mod test_utils; #[doc(hidden)] pub mod metrics_util { pub use metrics::*; } -/// Order Pool Public API -pub mod pool; - -/// Common steps library -pub mod steps; - -/// Orderpool utils -pub mod orderpool2; - -// Externally available test utilities -#[cfg(any(test, feature = "test-utils"))] -pub mod test_utils; +pub mod prelude { + pub(crate) use super::Variant; + pub use super::{ + metrics_util::{Counter, Gauge, Histogram}, + payload::*, + platform::*, + }; +} -// Reexport reth version that is used by rblib as a convenience for downstream +// Reexport reth version used by rblib as a convenience for downstream // users. Those exports should be enough to get started with a simple node. pub mod reth { pub use reth_origin::*; diff --git a/src/payload/block.rs b/crates/core/src/payload/block.rs similarity index 99% rename from src/payload/block.rs rename to crates/core/src/payload/block.rs index 90d7c75..7b55028 100644 --- a/src/payload/block.rs +++ b/crates/core/src/payload/block.rs @@ -5,6 +5,7 @@ use { api::ConfigureEvm, errors::ProviderError, evm::{block::BlockExecutionError, execute::BlockBuilder}, + payload::builder::PayloadBuilderError, primitives::SealedHeader, providers::{StateProvider, StateProviderBox, StateProviderFactory}, revm::{State, database::StateProviderDatabase}, diff --git a/src/payload/checkpoint.rs b/crates/core/src/payload/checkpoint.rs similarity index 99% rename from src/payload/checkpoint.rs rename to crates/core/src/payload/checkpoint.rs index 48ac8b2..91ff40e 100644 --- a/src/payload/checkpoint.rs +++ b/crates/core/src/payload/checkpoint.rs @@ -8,6 +8,7 @@ use { core::fmt::{Debug, Display}, reth::{ errors::ProviderError, + payload::PayloadBuilderError, primitives::Recovered, revm::{ DatabaseRef, diff --git a/src/payload/exec.rs b/crates/core/src/payload/exec.rs similarity index 77% rename from src/payload/exec.rs rename to crates/core/src/payload/exec.rs index d914d9d..3e728be 100644 --- a/src/payload/exec.rs +++ b/crates/core/src/payload/exec.rs @@ -441,22 +441,13 @@ impl ExecutionResult

{ mod tests { use { super::*, - crate::test_utils::{ - BlockContextMocked, - TestablePlatform, - test_bundle, - test_tx, - test_txs, - }, - rblib_tests_macros::rblib_test, + crate::test_utils::{BlockContextMocked, test_bundle, test_tx, test_txs}, }; - #[rblib_test(Ethereum, Optimism)] - fn test_executable_transaction_returns_single_transaction< - P: TestablePlatform, - >() { - let tx = test_tx::

(0, 0); - let executable = Executable::

::Transaction(tx.clone()); + #[test] + fn test_executable_transaction_returns_single_transaction() { + let tx = test_tx::(0, 0); + let executable = Executable::::Transaction(tx.clone()); assert_eq!(executable.transactions().len(), 1); assert_eq!(executable.transactions()[0], tx); @@ -464,13 +455,10 @@ mod tests { assert!(!executable.is_bundle()); } - #[rblib_test(Ethereum, Optimism)] - fn test_executable_bundle_returns_all_transactions

() - where - P: TestablePlatform>, - { - let (bundle, txs) = test_bundle::

(0, 0); - let executable = Executable::

::Bundle(bundle); + #[test] + fn test_executable_bundle_returns_all_transactions() { + let (bundle, txs) = test_bundle::(0, 0); + let executable = Executable::::Bundle(bundle); assert_eq!(executable.transactions().len(), txs.len()); assert_eq!(executable.transactions(), txs.as_slice()); @@ -478,15 +466,11 @@ mod tests { assert!(executable.is_bundle()); } - #[rblib_test(Ethereum, Optimism)] - fn test_execute_transaction_success

() - where - P: TestablePlatform, - BlockContext

: BlockContextMocked

, - { - let block = BlockContext::

::mocked(); + #[test] + fn test_execute_transaction_success() { + let block = BlockContext::::mocked(); let checkpoint = block.start(); - let tx = test_tx::

(0, 0); + let tx = test_tx::(0, 0); let result = Executable::execute_transaction( tx.clone(), @@ -502,15 +486,11 @@ mod tests { assert!(exec_result.results()[0].is_success()); } - #[rblib_test(Ethereum, Optimism)] - fn test_execute_transaction_produces_state_changes

() - where - P: TestablePlatform, - BlockContext

: BlockContextMocked

, - { - let block = BlockContext::

::mocked(); + #[test] + fn test_execute_transaction_produces_state_changes() { + let block = BlockContext::::mocked(); let checkpoint = block.start(); - let tx = test_tx::

(0, 0); + let tx = test_tx::(0, 0); let result = Executable::execute_transaction( tx, @@ -524,30 +504,22 @@ mod tests { assert!(exec_result.gas_used() > 0); } - #[rblib_test(Ethereum, Optimism)] - fn test_execute_via_execute_method

() - where - P: TestablePlatform, - BlockContext

: BlockContextMocked

, - { - let block = BlockContext::

::mocked(); + #[test] + fn test_execute_via_execute_method() { + let block = BlockContext::::mocked(); let checkpoint = block.start(); - let tx = test_tx::

(0, 0); - let executable = Executable::

::Transaction(tx); + let tx = test_tx::(0, 0); + let executable = Executable::Transaction(tx); let result = executable.execute(&block, &checkpoint, checkpoint.context()); assert_eq!(result.unwrap().results().len(), 1); } - #[rblib_test(Ethereum, Optimism)] - fn test_execute_bundle_all_successful

() - where - P: TestablePlatform>, - BlockContext

: BlockContextMocked

, - { - let block = BlockContext::

::mocked(); + #[test] + fn test_execute_bundle_all_successful() { + let block = BlockContext::::mocked(); let checkpoint = block.start(); - let (bundle, txs) = test_bundle::

(0, 0); + let (bundle, txs) = test_bundle(0, 0); let result = Executable::execute_bundle( bundle, @@ -563,15 +535,11 @@ mod tests { assert_eq!(exec_result.transactions(), txs.as_slice()); } - #[rblib_test(Ethereum, Optimism)] - fn test_execute_bundle_aggregates_gas

() - where - P: TestablePlatform>, - BlockContext

: BlockContextMocked

, - { - let block = BlockContext::

::mocked(); + #[test] + fn test_execute_bundle_aggregates_gas() { + let block = BlockContext::::mocked(); let checkpoint = block.start(); - let (bundle, _) = test_bundle::

(0, 0); + let (bundle, _) = test_bundle(0, 0); let result = Executable::execute_bundle( bundle, @@ -587,18 +555,14 @@ mod tests { assert!(total_gas > 0); } - #[rblib_test(Ethereum, Optimism)] - fn test_execute_bundle_sequential_execution

() - where - P: TestablePlatform>, - BlockContext

: BlockContextMocked

, - { + #[test] + fn test_execute_bundle_sequential_execution() { // Each transaction in a bundle executes on the state from the previous - let block = BlockContext::

::mocked(); + let block = BlockContext::::mocked(); let checkpoint = block.start(); // Use the same account for all transactions to test sequential nonces - let txs = test_txs::

(0, 0, 3); - let (bundle, _) = test_bundle::

(0, 0); + let txs = test_txs::(0, 0, 3); + let (bundle, _) = test_bundle(0, 0); let result = Executable::execute_bundle( bundle, @@ -612,103 +576,87 @@ mod tests { assert!(exec_result.results().iter().all(|r| r.is_success())); } - #[rblib_test(Ethereum, Optimism)] - fn test_into_executable_from_recovered_transaction() { - let tx = test_tx::

(0, 0); - let result: Result, _> = - IntoExecutable::>::try_into_executable(tx.clone()); + #[test] + fn test_into_executable_from_recovered_transaction() { + let tx = test_tx::(0, 0); + let result = + IntoExecutable::>::try_into_executable(tx.clone()); let executable = result.unwrap(); assert!(executable.is_transaction()); assert_eq!(executable.transactions()[0], tx); } - #[rblib_test(Ethereum, Optimism)] - fn test_into_executable_from_bundle

() - where - P: TestablePlatform>, - { - let (bundle, _) = test_bundle::

(0, 0); - let result: Result, _> = - IntoExecutable::>::try_into_executable(bundle); + #[test] + fn test_into_executable_from_bundle() { + let (bundle, _) = test_bundle::(0, 0); + let result = + IntoExecutable::>::try_into_executable(bundle); assert!(result.unwrap().is_bundle()); } - #[rblib_test(Ethereum, Optimism)] - fn test_into_executable_from_executable() { - let tx = test_tx::

(0, 0); - let executable = Executable::

::Transaction(tx); - let result: Result, _> = - IntoExecutable::>::try_into_executable(executable.clone()); + #[test] + fn test_into_executable_from_executable() { + let tx = test_tx::(0, 0); + let executable = Executable::Transaction(tx); + let result = IntoExecutable::>::try_into_executable( + executable.clone(), + ); assert_eq!(result.unwrap(), executable); } - #[rblib_test(Ethereum, Optimism)] - fn test_into_executable_from_checkpoint

() - where - P: TestablePlatform>, - BlockContext

: BlockContextMocked

, - { - let block = BlockContext::

::mocked(); + #[test] + fn test_into_executable_from_checkpoint() { + let block = BlockContext::::mocked(); let checkpoint = block.start(); - let tx = test_tx::

(0, 0); + let tx = test_tx::(0, 0); let checkpoint_with_tx = checkpoint.apply(tx.clone()).unwrap(); - let result: Result, _> = - IntoExecutable::>::try_into_executable(checkpoint_with_tx); + let result = IntoExecutable::>::try_into_executable( + checkpoint_with_tx, + ); let executable = result.unwrap(); assert!(executable.is_transaction()); assert_eq!(executable.transactions()[0], tx); } - #[rblib_test(Ethereum, Optimism)] - fn test_into_executable_from_checkpoint_ref

() - where - P: TestablePlatform>, - BlockContext

: BlockContextMocked

, - { - let block = BlockContext::

::mocked(); + #[test] + fn test_into_executable_from_checkpoint_ref() { + let block = BlockContext::::mocked(); let checkpoint = block.start(); - let tx = test_tx::

(0, 0); + let tx = test_tx::(0, 0); let checkpoint_with_tx = checkpoint.apply(tx.clone()).unwrap(); - let result: Result, _> = - IntoExecutable::>::try_into_executable(&checkpoint_with_tx); + let result = IntoExecutable::>::try_into_executable( + &checkpoint_with_tx, + ); let executable = result.unwrap(); assert!(executable.is_transaction()); assert_eq!(executable.transactions()[0], tx); } - #[rblib_test(Ethereum, Optimism)] - fn test_into_executable_from_barrier_checkpoint_fails

() - where - P: TestablePlatform, - BlockContext

: BlockContextMocked

, - { - let block = BlockContext::

::mocked(); + #[test] + fn test_into_executable_from_barrier_checkpoint_fails() { + let block = BlockContext::::mocked(); let checkpoint = block.start(); let barrier = checkpoint.barrier(); - let result: Result, _> = - IntoExecutable::>::try_into_executable(&barrier); + let result = + IntoExecutable::>::try_into_executable(&barrier); assert!(result.is_err()); } - #[rblib_test(Ethereum, Optimism)] - fn test_execution_result_source

() - where - P: TestablePlatform, - BlockContext

: BlockContextMocked

, - { - let block = BlockContext::

::mocked(); + #[test] + fn test_execution_result_source() { + let block = BlockContext::::mocked(); let checkpoint = block.start(); - let tx = test_tx::

(0, 0); - let executable = Executable::

::Transaction(tx.clone()); + let tx = test_tx::(0, 0); + let executable = Executable::Transaction(tx.clone()); let result = executable .execute(&block, &checkpoint, checkpoint.context()) @@ -720,16 +668,12 @@ mod tests { } } - #[rblib_test(Ethereum, Optimism)] - fn test_execution_result_transactions

() - where - P: TestablePlatform>, - BlockContext

: BlockContextMocked

, - { - let block = BlockContext::

::mocked(); + #[test] + fn test_execution_result_transactions() { + let block = BlockContext::::mocked(); let checkpoint = block.start(); - let (bundle, txs) = test_bundle::

(0, 0); - let executable = Executable::

::Bundle(bundle); + let (bundle, txs) = test_bundle(0, 0); + let executable = Executable::Bundle(bundle); let result = executable .execute(&block, &checkpoint, checkpoint.context()) @@ -738,43 +682,37 @@ mod tests { assert_eq!(result.transactions(), txs.as_slice()); } - #[rblib_test(Ethereum, Optimism)] - fn test_executable_hash_transaction() { - let tx = test_tx::

(0, 0); + #[test] + fn test_executable_hash_transaction() { + let tx = test_tx::(0, 0); let expected_hash = *tx.tx_hash(); - let executable = Executable::

::Transaction(tx); + let executable = Executable::::Transaction(tx); assert_eq!(executable.hash(), expected_hash); } - #[rblib_test(Ethereum, Optimism)] - fn test_executable_hash_bundle

() - where - P: TestablePlatform>, - { - let (bundle, _) = test_bundle::

(0, 0); + #[test] + fn test_executable_hash_bundle() { + let (bundle, _) = test_bundle::(0, 0); let expected_hash = bundle.hash(); - let executable = Executable::

::Bundle(bundle); + let executable = Executable::::Bundle(bundle); assert_eq!(executable.hash(), expected_hash); } - #[rblib_test(Ethereum, Optimism)] - fn test_execution_error_invalid_signature_display() { - let err = ExecutionError::

::InvalidSignature(RecoveryError::new()); + #[test] + fn test_execution_error_invalid_signature_display() { + let err = + ExecutionError::::InvalidSignature(RecoveryError::new()); let display = format!("{err}"); assert!(display.contains("Invalid signature")); } - #[rblib_test(Ethereum, Optimism)] - fn test_execution_result_state_is_bundle_state

() - where - P: TestablePlatform, - BlockContext

: BlockContextMocked

, - { - let block = BlockContext::

::mocked(); + #[test] + fn test_execution_result_state_is_bundle_state() { + let block = BlockContext::::mocked(); let checkpoint = block.start(); - let tx = test_tx::

(0, 0); + let tx = test_tx::(0, 0); let result = Executable::execute_transaction( tx, @@ -788,15 +726,11 @@ mod tests { assert!(!result.state().is_empty()); } - #[rblib_test(Ethereum, Optimism)] - fn test_execution_result_clone

() - where - P: TestablePlatform, - BlockContext

: BlockContextMocked

, - { - let block = BlockContext::

::mocked(); + #[test] + fn test_execution_result_clone() { + let block = BlockContext::::mocked(); let checkpoint = block.start(); - let tx = test_tx::

(0, 0); + let tx = test_tx::(0, 0); let result = Executable::execute_transaction( tx, diff --git a/src/payload/ext/block.rs b/crates/core/src/payload/ext/block.rs similarity index 100% rename from src/payload/ext/block.rs rename to crates/core/src/payload/ext/block.rs diff --git a/src/payload/ext/cached_state.rs b/crates/core/src/payload/ext/cached_state.rs similarity index 100% rename from src/payload/ext/cached_state.rs rename to crates/core/src/payload/ext/cached_state.rs diff --git a/src/payload/ext/checkpoint.rs b/crates/core/src/payload/ext/checkpoint.rs similarity index 100% rename from src/payload/ext/checkpoint.rs rename to crates/core/src/payload/ext/checkpoint.rs diff --git a/src/payload/ext/mod.rs b/crates/core/src/payload/ext/mod.rs similarity index 100% rename from src/payload/ext/mod.rs rename to crates/core/src/payload/ext/mod.rs diff --git a/src/payload/ext/span.rs b/crates/core/src/payload/ext/span.rs similarity index 96% rename from src/payload/ext/span.rs rename to crates/core/src/payload/ext/span.rs index 5f03f3e..b1ea5db 100644 --- a/src/payload/ext/span.rs +++ b/crates/core/src/payload/ext/span.rs @@ -109,14 +109,11 @@ impl SpanExt

for Span

{ #[cfg(test)] mod span_ext_tests { - use { - crate::{ - payload::{Span, SpanExt}, - prelude::{BlockContext, Checkpoint, Ethereum}, - test_utils::{BlockContextMocked, transfer_tx}, - }, + use crate::{ alloy::primitives::U256, - rblib::{alloy, test_utils::FundedAccounts}, + payload::{Span, SpanExt}, + prelude::{BlockContext, Checkpoint, Ethereum}, + test_utils::{BlockContextMocked, FundedAccounts, transfer_tx}, }; #[test] diff --git a/src/payload/mod.rs b/crates/core/src/payload/mod.rs similarity index 100% rename from src/payload/mod.rs rename to crates/core/src/payload/mod.rs diff --git a/src/payload/span.rs b/crates/core/src/payload/span.rs similarity index 100% rename from src/payload/span.rs rename to crates/core/src/payload/span.rs diff --git a/src/platform/bundle/ext.rs b/crates/core/src/platform/bundle/ext.rs similarity index 100% rename from src/platform/bundle/ext.rs rename to crates/core/src/platform/bundle/ext.rs diff --git a/src/platform/bundle/flashbots.rs b/crates/core/src/platform/bundle/flashbots.rs similarity index 100% rename from src/platform/bundle/flashbots.rs rename to crates/core/src/platform/bundle/flashbots.rs diff --git a/src/platform/bundle/mod.rs b/crates/core/src/platform/bundle/mod.rs similarity index 100% rename from src/platform/bundle/mod.rs rename to crates/core/src/platform/bundle/mod.rs diff --git a/src/platform/context.rs b/crates/core/src/platform/context.rs similarity index 100% rename from src/platform/context.rs rename to crates/core/src/platform/context.rs diff --git a/src/platform/ethereum/limits.rs b/crates/core/src/platform/ethereum/limits.rs similarity index 100% rename from src/platform/ethereum/limits.rs rename to crates/core/src/platform/ethereum/limits.rs diff --git a/src/platform/ethereum/mod.rs b/crates/core/src/platform/ethereum/mod.rs similarity index 100% rename from src/platform/ethereum/mod.rs rename to crates/core/src/platform/ethereum/mod.rs diff --git a/src/platform/ethereum/pool.rs b/crates/core/src/platform/ethereum/pool.rs similarity index 100% rename from src/platform/ethereum/pool.rs rename to crates/core/src/platform/ethereum/pool.rs diff --git a/src/platform/limits.rs b/crates/core/src/platform/limits.rs similarity index 100% rename from src/platform/limits.rs rename to crates/core/src/platform/limits.rs diff --git a/src/platform/mod.rs b/crates/core/src/platform/mod.rs similarity index 92% rename from src/platform/mod.rs rename to crates/core/src/platform/mod.rs index f6e3808..b8aa845 100644 --- a/src/platform/mod.rs +++ b/crates/core/src/platform/mod.rs @@ -1,6 +1,6 @@ //! Platform abstraction layer //! -//! This module fines the platform specific extension points for the underlying +//! This module fines the platform-specific extension points for the underlying //! node that executes pipelines. By default, rblib provides implementations of //! the standard Ethereum and Optimism platforms, but it can be extended to //! support other platforms as well. @@ -12,7 +12,10 @@ use { network::Network as AlloyNetwork, signers::Signature, }, - reth::ethereum::primitives::SignedTransaction, + reth::{ + ethereum::primitives::SignedTransaction, + payload::PayloadBuilderError, + }, serde::{Serialize, de::DeserializeOwned}, std::sync::Arc, }; @@ -21,6 +24,7 @@ mod bundle; mod context; mod ethereum; mod limits; +pub mod traits; pub mod types; mod utils; @@ -57,11 +61,11 @@ pub trait Platform: /// Type that configures the essential types of an Ethereum-like node. /// /// Implementations of this trait describe all types that are used by the - /// consensus engine such as transactions, blocks, headers, etc. + /// consensus engine, such as transactions, blocks, headers, etc. /// - /// Two well known implementations of this trait are: - /// - [`EthereumNode`](crate::reth::ethereum::node::EthereumNode) for Ethereum - /// L1 mainnet, + /// Two well-known implementations of this trait are: + /// - [`EthereumNode`](reth::ethereum::node::EthereumNode) for Ethereum L1 + /// mainnet, #[cfg_attr( feature = "optimism", doc = " - [`OpNode`](crate::reth::optimism::node::OpNode) for Optimism \ diff --git a/src/platform/optimism/ext.rs b/crates/core/src/platform/optimism/ext.rs similarity index 100% rename from src/platform/optimism/ext.rs rename to crates/core/src/platform/optimism/ext.rs diff --git a/src/platform/optimism/limits.rs b/crates/core/src/platform/optimism/limits.rs similarity index 100% rename from src/platform/optimism/limits.rs rename to crates/core/src/platform/optimism/limits.rs diff --git a/src/platform/optimism/mod.rs b/crates/core/src/platform/optimism/mod.rs similarity index 100% rename from src/platform/optimism/mod.rs rename to crates/core/src/platform/optimism/mod.rs diff --git a/crates/core/src/platform/traits.rs b/crates/core/src/platform/traits.rs new file mode 100644 index 0000000..59b00dd --- /dev/null +++ b/crates/core/src/platform/traits.rs @@ -0,0 +1,114 @@ +use crate::{ + prelude::*, + reth::{ + api::FullNodeTypes, + evm::ConfigureEvm, + node::builder::NodeTypes, + providers::{ChainSpecProvider, HeaderProvider, StateProviderFactory}, + transaction_pool::{PoolTransaction, TransactionPool}, + }, +}; + +pub trait NodeBounds: FullNodeTypes {} + +impl NodeBounds

for T where + T: FullNodeTypes +{ +} + +/// Bounds for the provider factory required to build payloads. +pub trait ProviderFactoryBounds: + StateProviderFactory + + ChainSpecProvider> + + Send + + Sync +{ +} + +impl ProviderFactoryBounds

for T where + T: StateProviderFactory + + ChainSpecProvider> + + Send + + Sync +{ +} + +pub trait ProviderBounds: + ProviderFactoryBounds

+ + HeaderProvider

> + + Clone + + 'static +{ +} + +impl ProviderBounds

for T where + T: ProviderFactoryBounds

+ + HeaderProvider

> + + Clone + + 'static +{ +} + +pub trait PoolBounds: + TransactionPool + Unpin + 'static +{ +} + +impl PoolBounds

for T where + T: TransactionPool + Unpin + 'static +{ +} + +pub trait PooledTransactionBounds: + PoolTransaction> + Send + Sync + 'static +{ +} + +pub trait EvmConfigBounds: + ConfigureEvm> + Send + Sync +{ +} + +impl EvmConfigBounds

for T where + T: ConfigureEvm> + Send + Sync +{ +} + +pub trait PlatformExecBounds: + Platform< + NodeTypes: NodeTypes< + ChainSpec = types::ChainSpec

, + Primitives = types::Primitives

, + Payload = types::PayloadTypes

, + >, + EvmConfig = types::EvmConfig

, + ExtraLimits = types::ExtraLimits

, + > +{ +} + +impl PlatformExecBounds for P where + T: Platform< + NodeTypes: NodeTypes< + ChainSpec = types::ChainSpec

, + Primitives = types::Primitives

, + Payload = types::PayloadTypes

, + >, + EvmConfig = types::EvmConfig

, + ExtraLimits = types::ExtraLimits

, + > +{ +} + +// equal to `PlatformExecBounds` but with stricter checkpoint context +pub trait PlatformExecCtxBounds: + PlatformExecBounds

+ + Platform> +{ +} + +impl PlatformExecCtxBounds for P where + P: PlatformExecBounds + + Platform> +{ +} diff --git a/src/platform/types.rs b/crates/core/src/platform/types.rs similarity index 100% rename from src/platform/types.rs rename to crates/core/src/platform/types.rs diff --git a/src/platform/utils.rs b/crates/core/src/platform/utils.rs similarity index 100% rename from src/platform/utils.rs rename to crates/core/src/platform/utils.rs diff --git a/src/test_utils/accounts.rs b/crates/core/src/test_utils/accounts.rs similarity index 100% rename from src/test_utils/accounts.rs rename to crates/core/src/test_utils/accounts.rs diff --git a/src/test_utils/exts/mock.rs b/crates/core/src/test_utils/exts/mock.rs similarity index 96% rename from src/test_utils/exts/mock.rs rename to crates/core/src/test_utils/exts/mock.rs index 663b386..1682d39 100644 --- a/src/test_utils/exts/mock.rs +++ b/crates/core/src/test_utils/exts/mock.rs @@ -3,11 +3,8 @@ use { super::*, - crate::test_utils::optimism::{ - DEFAULT_EIP_1559_PARAMS, - DEFAULT_MIN_BASE_FEE, - }, alloy::primitives::{Address, B64, B256}, + optimism_constants::{DEFAULT_EIP_1559_PARAMS, DEFAULT_MIN_BASE_FEE}, reth::{ chainspec::EthChainSpec, ethereum::{ @@ -28,7 +25,7 @@ pub trait PlatformWithTestnet: Platform { /// Blanket implementation for ethereum impl PlatformWithTestnet for Ethereum { fn dev_chainspec() -> Arc> { - LazyLock::force(&crate::reth::chainspec::DEV) + LazyLock::force(&reth::chainspec::DEV) .clone() .with_funded_accounts() } @@ -38,7 +35,7 @@ impl PlatformWithTestnet for Ethereum { #[cfg(feature = "optimism")] impl PlatformWithTestnet for Optimism { fn dev_chainspec() -> Arc> { - LazyLock::force(&crate::reth::optimism::chainspec::OP_DEV) + LazyLock::force(&reth::optimism::chainspec::OP_DEV) .clone() .with_funded_accounts() } diff --git a/src/test_utils/exts/mod.rs b/crates/core/src/test_utils/exts/mod.rs similarity index 100% rename from src/test_utils/exts/mod.rs rename to crates/core/src/test_utils/exts/mod.rs diff --git a/src/test_utils/mock.rs b/crates/core/src/test_utils/mock.rs similarity index 100% rename from src/test_utils/mock.rs rename to crates/core/src/test_utils/mock.rs diff --git a/crates/core/src/test_utils/mod.rs b/crates/core/src/test_utils/mod.rs new file mode 100644 index 0000000..fbddbd2 --- /dev/null +++ b/crates/core/src/test_utils/mod.rs @@ -0,0 +1,52 @@ +//! rblib-core testing infrastructure +//! +//! Those are utilities that provide ways to test platforms and other primitives +//! defined by rblib-core and downstream crates extending and building on top of +//! rblib such as rblib-pipeline. +//! +//! By default, two test platforms are defined for the two platforms maintained +//! by flashbots: +//! - [`Ethereum`] +//! - [`Optimism`] +//! +//! If you want to make your own [`crate::Platform`] type implementation +//! available for testing with those utils, you need to implement the +//! [`TestablePlatform`] trait for it. + +mod accounts; + +mod exts; + +mod mock; + +mod transactions; + +pub use { + accounts::{FundedAccounts, WithFundedAccounts}, + exts::*, + mock::{GenesisProviderFactory, GenesisStateProvider}, + transactions::{ + invalid_tx, + reverting_tx, + test_bundle, + test_tx, + test_txs, + transfer_tx, + transfer_tx_to, + }, +}; + +pub const ONE_ETH: u128 = 1_000_000_000_000_000_000; +pub const TEST_COINBASE: crate::alloy::primitives::Address = // + crate::alloy::primitives::address!( + "0x0000000000000000000000000000000000012345" + ); + +#[cfg(feature = "optimism")] +pub mod optimism_constants { + pub const DEFAULT_DENOMINATOR: u32 = 50; + pub const DEFAULT_ELASTICITY: u32 = 2; + pub const DEFAULT_EIP_1559_PARAMS: u64 = + ((DEFAULT_DENOMINATOR as u64) << 32) | (DEFAULT_ELASTICITY as u64); + pub const DEFAULT_MIN_BASE_FEE: u64 = 0; +} diff --git a/src/test_utils/transactions.rs b/crates/core/src/test_utils/transactions.rs similarity index 93% rename from src/test_utils/transactions.rs rename to crates/core/src/test_utils/transactions.rs index 28b4c17..949b47e 100644 --- a/src/test_utils/transactions.rs +++ b/crates/core/src/test_utils/transactions.rs @@ -49,14 +49,15 @@ pub fn test_bundle>>( } #[allow(clippy::missing_panics_doc)] -pub fn transfer_tx( +pub fn transfer_tx_to( signer: &PrivateKeySigner, nonce: u64, value: U256, + address: Address, ) -> Recovered> { let mut tx = types::TransactionRequest::

::default() .with_nonce(nonce) - .with_to(Address::random()) + .with_to(address) .with_value(value) .with_gas_price(1_000_000_000) .with_gas_limit(21_000) @@ -74,6 +75,15 @@ pub fn transfer_tx( signed_tx.with_signer(signer.address()) } +#[allow(clippy::missing_panics_doc)] +pub fn transfer_tx( + signer: &PrivateKeySigner, + nonce: u64, + value: U256, +) -> Recovered> { + transfer_tx_to::

(signer, nonce, value, Address::random()) +} + /// Create a transaction that will revert when executed #[allow(clippy::missing_panics_doc)] pub fn reverting_tx( diff --git a/src/pipelines/macros/Cargo.toml b/crates/pipeline-macros/Cargo.toml similarity index 60% rename from src/pipelines/macros/Cargo.toml rename to crates/pipeline-macros/Cargo.toml index c8d9d20..d09c902 100644 --- a/src/pipelines/macros/Cargo.toml +++ b/crates/pipeline-macros/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pipelines-macros" -description = "Internal helper macros for the rblib pipelines API." +description = "Internal helper macros for rblib pipelines SDK." version.workspace = true edition.workspace = true rust-version.workspace = true @@ -11,15 +11,17 @@ authors.workspace = true exclude.workspace = true publish = false +[lints] +workspace = true + [lib] +doctest = false proc-macro = true [dependencies] -proc-macro2 = "1.0" -quote = "1.0" -syn = { version = "2.0", features = ["full"] } -proc-macro-crate = "3.3.0" +proc-macro2.workspace = true +quote.workspace = true +syn = { workspace = true, features = ["full"] } +proc-macro-crate.workspace = true -[lints] -workspace = true diff --git a/src/pipelines/macros/src/lib.rs b/crates/pipeline-macros/src/lib.rs similarity index 100% rename from src/pipelines/macros/src/lib.rs rename to crates/pipeline-macros/src/lib.rs diff --git a/src/pipelines/macros/src/metrics/mod.rs b/crates/pipeline-macros/src/metrics/mod.rs similarity index 74% rename from src/pipelines/macros/src/metrics/mod.rs rename to crates/pipeline-macros/src/metrics/mod.rs index 0cb18fa..cbe6a6d 100644 --- a/src/pipelines/macros/src/metrics/mod.rs +++ b/crates/pipeline-macros/src/metrics/mod.rs @@ -8,9 +8,9 @@ fn rblib_path() -> proc_macro2::TokenStream { quote::quote, }; - match crate_name("rblib") { + match crate_name("rblib-pipeline") { Ok(FoundCrate::Itself) => { - // We are inside the rblib crate itself. + // We are inside rblib-pipeline crate itself. quote!(crate) } Ok(FoundCrate::Name(name)) => { @@ -20,9 +20,9 @@ fn rblib_path() -> proc_macro2::TokenStream { quote!(::#ident) } Err(_) => { - // Fallback: assume the crate is available as `::rblib`. + // Fallback: assume the crate is available as `::pipeline`. // Emit a helpful error if that also fails at type-check time. - quote!(::rblib) + quote!(::rblib::pipeline) } } } diff --git a/src/pipelines/macros/src/metrics/set.rs b/crates/pipeline-macros/src/metrics/set.rs similarity index 100% rename from src/pipelines/macros/src/metrics/set.rs rename to crates/pipeline-macros/src/metrics/set.rs diff --git a/src/pipelines/macros/src/variants.rs b/crates/pipeline-macros/src/variants.rs similarity index 100% rename from src/pipelines/macros/src/variants.rs rename to crates/pipeline-macros/src/variants.rs diff --git a/crates/pipeline/Cargo.toml b/crates/pipeline/Cargo.toml new file mode 100644 index 0000000..031f2e3 --- /dev/null +++ b/crates/pipeline/Cargo.toml @@ -0,0 +1,91 @@ +[package] +name = "rblib-pipeline" +description = "Pipeline orchestration and steps for rblib." +version.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +authors.workspace = true +exclude.workspace = true + +[lints] +workspace = true + +[lib] +doctest = false + +[features] +default = ["rblib-core/default", "optimism", "jemalloc"] +optimism = ["rblib-core/optimism"] +jemalloc = ["rblib-core/jemalloc", "reth-optimism-cli?/jemalloc"] +jemalloc-prof = ["rblib-core/jemalloc-prof"] +long-pipelines-syntax = [] + +test-utils = [ + "rblib-core/test-utils", + "dep:nanoid", + "tokio/full", + "reth-optimism-rpc/client", + "dep:rblib-tests-macros", + "dep:jsonrpsee-core", + "dep:tracing-subscriber", + "dep:ctor", + "dep:reth-ipc" +] + +[target.'cfg(unix)'.dependencies] +tikv-jemallocator = { workspace = true, optional = true } + +[dependencies] +rblib-core = { path = "../core", default-features = false } +pipelines-macros = { path = "../pipeline-macros" } +rblib-tests-macros = { path = "../test-utils-macros", optional = true } + +# Common +eyre.workspace = true +derive_more.workspace = true +smallvec.workspace = true +dashmap.workspace = true +uuid.workspace = true +futures.workspace = true +tokio.workspace = true +tokio-stream.workspace = true +serde.workspace = true +jsonrpsee.workspace = true +parking_lot.workspace = true +priority-queue.workspace = true +rand.workspace = true +tracing.workspace = true +metrics.workspace = true + +# Reth +reth-evm.workspace = true +reth-errors.workspace = true +reth-rpc-api.workspace = true +reth-transaction-pool.workspace = true +reth-payload-builder.workspace = true + +# Optimism (optional) +reth-optimism-node = { workspace = true, optional = true } +reth-optimism-rpc = { workspace = true, optional = true } +reth-optimism-cli = { workspace = true, optional = true } + +# test utils +jsonrpsee-core = { workspace = true, optional = true } +nanoid = { workspace = true, optional = true } +alloy-genesis = { workspace = true, optional = true } +ctor = { workspace = true, optional = true } +tracing-subscriber = { workspace = true, optional = true, features = [ + "env-filter", + "json", +] } + +# Optional reth extras +reth-ipc = { workspace = true, optional = true } +reth-ethereum = { workspace = true, optional = true } +reth-node-builder = { workspace = true, optional = true } + +[dev-dependencies] +rblib-pipeline = { path = ".", features = ["test-utils"] } diff --git a/crates/pipeline/src/lib.rs b/crates/pipeline/src/lib.rs new file mode 100644 index 0000000..a516d59 --- /dev/null +++ b/crates/pipeline/src/lib.rs @@ -0,0 +1,36 @@ +// Pipelines, steps, pools, and other orchestration utilities for rblib. +pub use rblib_core::{ + self as core, + Variant, + alloy, + payload, + platform, + reth, + revm, + uuid, +}; + +#[doc(hidden)] +pub mod metrics_util { + pub use rblib_core::metrics_util::*; +} + +pub mod orderpool2; +pub mod pipelines; +pub mod pool; +pub mod steps; + +#[cfg(any(test, feature = "test-utils"))] +pub mod test_utils; + +pub use {orderpool2::*, pipelines::*, pool::*, steps::*}; + +pub mod prelude { + pub(crate) use rblib_core::Variant; + pub use { + super::{orderpool2::*, pipelines::*, pool::*, steps::*}, + metrics::{Counter, Gauge, Histogram}, + pipelines_macros::MetricsSet, + rblib_core::prelude::*, + }; +} diff --git a/src/orderpool2/mod.rs b/crates/pipeline/src/orderpool2/mod.rs similarity index 100% rename from src/orderpool2/mod.rs rename to crates/pipeline/src/orderpool2/mod.rs diff --git a/src/orderpool2/prioritized_pool/mod.rs b/crates/pipeline/src/orderpool2/prioritized_pool/mod.rs similarity index 100% rename from src/orderpool2/prioritized_pool/mod.rs rename to crates/pipeline/src/orderpool2/prioritized_pool/mod.rs diff --git a/src/orderpool2/prioritized_pool/step.rs b/crates/pipeline/src/orderpool2/prioritized_pool/step.rs similarity index 97% rename from src/orderpool2/prioritized_pool/step.rs rename to crates/pipeline/src/orderpool2/prioritized_pool/step.rs index b78a9d8..5072d50 100644 --- a/src/orderpool2/prioritized_pool/step.rs +++ b/crates/pipeline/src/orderpool2/prioritized_pool/step.rs @@ -8,16 +8,16 @@ use { crate::{ alloy::{ consensus::Transaction, + consensus::transaction::TxHashRef, primitives::{Address, B256}, }, orderpool2::{AccountNonce, BundleNonce}, payload::CheckpointExt, prelude::{Bundle, Checkpoint, ControlFlow, Platform, Step, StepContext}, reth, + reth::revm::DatabaseRef, }, parking_lot::Mutex, - reth_ethereum::primitives::transaction::TxHashRef, - reth_evm::revm::DatabaseRef, std::{ marker::{PhantomData, Send, Sync}, sync::Arc, @@ -35,7 +35,7 @@ use crate::{ pub struct BundleWithNonces { bundle: B, nonces: Vec, - phantom: std::marker::PhantomData

, + phantom: PhantomData

, } impl BundleWithNonces diff --git a/src/orderpool2/prioritized_pool/tests.rs b/crates/pipeline/src/orderpool2/prioritized_pool/tests.rs similarity index 100% rename from src/orderpool2/prioritized_pool/tests.rs rename to crates/pipeline/src/orderpool2/prioritized_pool/tests.rs diff --git a/src/orderpool2/sim_tree.rs b/crates/pipeline/src/orderpool2/sim_tree.rs similarity index 100% rename from src/orderpool2/sim_tree.rs rename to crates/pipeline/src/orderpool2/sim_tree.rs diff --git a/src/pipelines/events.rs b/crates/pipeline/src/pipelines/events.rs similarity index 100% rename from src/pipelines/events.rs rename to crates/pipeline/src/pipelines/events.rs diff --git a/src/pipelines/exec/mod.rs b/crates/pipeline/src/pipelines/exec/mod.rs similarity index 100% rename from src/pipelines/exec/mod.rs rename to crates/pipeline/src/pipelines/exec/mod.rs diff --git a/src/pipelines/exec/navi.rs b/crates/pipeline/src/pipelines/exec/navi.rs similarity index 100% rename from src/pipelines/exec/navi.rs rename to crates/pipeline/src/pipelines/exec/navi.rs diff --git a/src/pipelines/exec/scope.rs b/crates/pipeline/src/pipelines/exec/scope.rs similarity index 100% rename from src/pipelines/exec/scope.rs rename to crates/pipeline/src/pipelines/exec/scope.rs diff --git a/src/pipelines/iter.rs b/crates/pipeline/src/pipelines/iter.rs similarity index 100% rename from src/pipelines/iter.rs rename to crates/pipeline/src/pipelines/iter.rs diff --git a/src/pipelines/job.rs b/crates/pipeline/src/pipelines/job.rs similarity index 100% rename from src/pipelines/job.rs rename to crates/pipeline/src/pipelines/job.rs diff --git a/src/pipelines/limits.rs b/crates/pipeline/src/pipelines/limits.rs similarity index 100% rename from src/pipelines/limits.rs rename to crates/pipeline/src/pipelines/limits.rs diff --git a/src/pipelines/metrics.rs b/crates/pipeline/src/pipelines/metrics.rs similarity index 100% rename from src/pipelines/metrics.rs rename to crates/pipeline/src/pipelines/metrics.rs diff --git a/src/pipelines/mod.rs b/crates/pipeline/src/pipelines/mod.rs similarity index 77% rename from src/pipelines/mod.rs rename to crates/pipeline/src/pipelines/mod.rs index b7f1b2d..80aa68c 100644 --- a/src/pipelines/mod.rs +++ b/crates/pipeline/src/pipelines/mod.rs @@ -340,126 +340,6 @@ impl core::fmt::Debug for Pipeline

{ } } -pub mod traits { - use crate::{ - prelude::*, - reth::{ - api::FullNodeTypes, - evm::ConfigureEvm, - node::builder::NodeTypes, - providers::{ChainSpecProvider, HeaderProvider, StateProviderFactory}, - transaction_pool::{PoolTransaction, TransactionPool}, - }, - }; - - pub trait NodeBounds: - FullNodeTypes - { - } - - impl NodeBounds

for T where - T: FullNodeTypes - { - } - - /// Bounds for the provider factory required to build payloads. - pub trait ProviderFactoryBounds: - StateProviderFactory - + ChainSpecProvider> - + Send - + Sync - { - } - - impl ProviderFactoryBounds

for T where - T: StateProviderFactory - + ChainSpecProvider> - + Send - + Sync - { - } - - pub trait ProviderBounds: - ProviderFactoryBounds

- + HeaderProvider

> - + Clone - + 'static - { - } - - impl ProviderBounds

for T where - T: ProviderFactoryBounds

- + HeaderProvider

> - + Clone - + 'static - { - } - - pub trait PoolBounds: - TransactionPool + Unpin + 'static - { - } - - impl PoolBounds

for T where - T: TransactionPool + Unpin + 'static - { - } - - pub trait PooledTransactionBounds: - PoolTransaction> + Send + Sync + 'static - { - } - - pub trait EvmConfigBounds: - ConfigureEvm> + Send + Sync - { - } - - impl EvmConfigBounds

for T where - T: ConfigureEvm> + Send + Sync - { - } - - pub trait PlatformExecBounds: - Platform< - NodeTypes: NodeTypes< - ChainSpec = types::ChainSpec

, - Primitives = types::Primitives

, - Payload = types::PayloadTypes

, - >, - EvmConfig = types::EvmConfig

, - ExtraLimits = types::ExtraLimits

, - > - { - } - - impl PlatformExecBounds for P where - T: Platform< - NodeTypes: NodeTypes< - ChainSpec = types::ChainSpec

, - Primitives = types::Primitives

, - Payload = types::PayloadTypes

, - >, - EvmConfig = types::EvmConfig

, - ExtraLimits = types::ExtraLimits

, - > - { - } - - // equal to `PlatformExecBounds` but with stricter checkpoint context - pub trait PlatformExecCtxBounds: - PlatformExecBounds

- + Platform> - { - } - - impl PlatformExecCtxBounds for P where - P: PlatformExecBounds - + Platform> - { - } -} - // internal utilities #[cfg(any(test, feature = "test-utils"))] pub(crate) use exec::clone_payload_error_lossy; diff --git a/src/pipelines/service.rs b/crates/pipeline/src/pipelines/service.rs similarity index 98% rename from src/pipelines/service.rs rename to crates/pipeline/src/pipelines/service.rs index 4cb2337..8f30ac2 100644 --- a/src/pipelines/service.rs +++ b/crates/pipeline/src/pipelines/service.rs @@ -15,10 +15,13 @@ use { }, node::builder::NodePrimitives, payload::builder::{PayloadBuilderHandle, PayloadBuilderService, *}, - providers::{CanonStateSubscriptions, StateProviderFactory}, + providers::{ + CanonStateNotification, + CanonStateSubscriptions, + StateProviderFactory, + }, }, }, - reth_ethereum::provider::CanonStateNotification, std::sync::Arc, tracing::debug, }; diff --git a/src/pipelines/step/context.rs b/crates/pipeline/src/pipelines/step/context.rs similarity index 100% rename from src/pipelines/step/context.rs rename to crates/pipeline/src/pipelines/step/context.rs diff --git a/src/pipelines/step/instance.rs b/crates/pipeline/src/pipelines/step/instance.rs similarity index 100% rename from src/pipelines/step/instance.rs rename to crates/pipeline/src/pipelines/step/instance.rs diff --git a/src/pipelines/step/metrics.rs b/crates/pipeline/src/pipelines/step/metrics.rs similarity index 100% rename from src/pipelines/step/metrics.rs rename to crates/pipeline/src/pipelines/step/metrics.rs diff --git a/src/pipelines/step/mod.rs b/crates/pipeline/src/pipelines/step/mod.rs similarity index 100% rename from src/pipelines/step/mod.rs rename to crates/pipeline/src/pipelines/step/mod.rs diff --git a/src/pipelines/step/name.rs b/crates/pipeline/src/pipelines/step/name.rs similarity index 100% rename from src/pipelines/step/name.rs rename to crates/pipeline/src/pipelines/step/name.rs diff --git a/src/pipelines/tests/mod.rs b/crates/pipeline/src/pipelines/tests/mod.rs similarity index 100% rename from src/pipelines/tests/mod.rs rename to crates/pipeline/src/pipelines/tests/mod.rs diff --git a/src/pipelines/tests/revert.rs b/crates/pipeline/src/pipelines/tests/revert.rs similarity index 100% rename from src/pipelines/tests/revert.rs rename to crates/pipeline/src/pipelines/tests/revert.rs diff --git a/src/pipelines/tests/smoke.rs b/crates/pipeline/src/pipelines/tests/smoke.rs similarity index 100% rename from src/pipelines/tests/smoke.rs rename to crates/pipeline/src/pipelines/tests/smoke.rs diff --git a/src/pipelines/tests/syntax.rs b/crates/pipeline/src/pipelines/tests/syntax.rs similarity index 98% rename from src/pipelines/tests/syntax.rs rename to crates/pipeline/src/pipelines/tests/syntax.rs index 0e08fef..9bd04b3 100644 --- a/src/pipelines/tests/syntax.rs +++ b/crates/pipeline/src/pipelines/tests/syntax.rs @@ -1,6 +1,6 @@ use { - crate::{prelude::*, steps::*, test_utils::*}, - alloy_origin::signers::local::LocalSigner, + crate::{alloy, prelude::*, steps::*, test_utils::*}, + alloy::signers::local::LocalSigner, tracing::info, }; diff --git a/src/pool/host.rs b/crates/pipeline/src/pool/host.rs similarity index 100% rename from src/pool/host.rs rename to crates/pipeline/src/pool/host.rs diff --git a/src/pool/maintain.rs b/crates/pipeline/src/pool/maintain.rs similarity index 100% rename from src/pool/maintain.rs rename to crates/pipeline/src/pool/maintain.rs diff --git a/src/pool/mod.rs b/crates/pipeline/src/pool/mod.rs similarity index 100% rename from src/pool/mod.rs rename to crates/pipeline/src/pool/mod.rs diff --git a/src/pool/native.rs b/crates/pipeline/src/pool/native.rs similarity index 100% rename from src/pool/native.rs rename to crates/pipeline/src/pool/native.rs diff --git a/src/pool/report.rs b/crates/pipeline/src/pool/report.rs similarity index 100% rename from src/pool/report.rs rename to crates/pipeline/src/pool/report.rs diff --git a/src/pool/rpc.rs b/crates/pipeline/src/pool/rpc.rs similarity index 100% rename from src/pool/rpc.rs rename to crates/pipeline/src/pool/rpc.rs diff --git a/src/pool/select.rs b/crates/pipeline/src/pool/select.rs similarity index 100% rename from src/pool/select.rs rename to crates/pipeline/src/pool/select.rs diff --git a/src/pool/setup.rs b/crates/pipeline/src/pool/setup.rs similarity index 100% rename from src/pool/setup.rs rename to crates/pipeline/src/pool/setup.rs diff --git a/src/pool/step.rs b/crates/pipeline/src/pool/step.rs similarity index 100% rename from src/pool/step.rs rename to crates/pipeline/src/pool/step.rs diff --git a/src/steps/builder.rs b/crates/pipeline/src/steps/builder.rs similarity index 100% rename from src/steps/builder.rs rename to crates/pipeline/src/steps/builder.rs diff --git a/src/steps/combinator/atomic.rs b/crates/pipeline/src/steps/combinator/atomic.rs similarity index 100% rename from src/steps/combinator/atomic.rs rename to crates/pipeline/src/steps/combinator/atomic.rs diff --git a/src/steps/combinator/chain.rs b/crates/pipeline/src/steps/combinator/chain.rs similarity index 100% rename from src/steps/combinator/chain.rs rename to crates/pipeline/src/steps/combinator/chain.rs diff --git a/src/steps/combinator/mod.rs b/crates/pipeline/src/steps/combinator/mod.rs similarity index 100% rename from src/steps/combinator/mod.rs rename to crates/pipeline/src/steps/combinator/mod.rs diff --git a/src/steps/mod.rs b/crates/pipeline/src/steps/mod.rs similarity index 100% rename from src/steps/mod.rs rename to crates/pipeline/src/steps/mod.rs diff --git a/src/steps/optimism.rs b/crates/pipeline/src/steps/optimism.rs similarity index 100% rename from src/steps/optimism.rs rename to crates/pipeline/src/steps/optimism.rs diff --git a/src/steps/ordering/mod.rs b/crates/pipeline/src/steps/ordering/mod.rs similarity index 100% rename from src/steps/ordering/mod.rs rename to crates/pipeline/src/steps/ordering/mod.rs diff --git a/src/steps/ordering/profit.rs b/crates/pipeline/src/steps/ordering/profit.rs similarity index 100% rename from src/steps/ordering/profit.rs rename to crates/pipeline/src/steps/ordering/profit.rs diff --git a/src/steps/ordering/tip.rs b/crates/pipeline/src/steps/ordering/tip.rs similarity index 100% rename from src/steps/ordering/tip.rs rename to crates/pipeline/src/steps/ordering/tip.rs diff --git a/src/steps/revert.rs b/crates/pipeline/src/steps/revert.rs similarity index 100% rename from src/steps/revert.rs rename to crates/pipeline/src/steps/revert.rs diff --git a/src/steps/utils.rs b/crates/pipeline/src/steps/utils.rs similarity index 100% rename from src/steps/utils.rs rename to crates/pipeline/src/steps/utils.rs diff --git a/src/test_utils/ethereum.rs b/crates/pipeline/src/test_utils/ethereum.rs similarity index 98% rename from src/test_utils/ethereum.rs rename to crates/pipeline/src/test_utils/ethereum.rs index 1002133..140674b 100644 --- a/src/test_utils/ethereum.rs +++ b/crates/pipeline/src/test_utils/ethereum.rs @@ -16,10 +16,9 @@ use { node::EthereumAddOns, }, payload::builder::PayloadId, - rpc::types::engine::ForkchoiceState, + rpc::{api::EngineApiClient, types::engine::ForkchoiceState}, }, reth_ipc::client::IpcClientBuilder, - reth_rpc_api::EngineApiClient, }; impl TestNodeFactory for Ethereum { diff --git a/src/test_utils/mod.rs b/crates/pipeline/src/test_utils/mod.rs similarity index 82% rename from src/test_utils/mod.rs rename to crates/pipeline/src/test_utils/mod.rs index 553e388..46f1a77 100644 --- a/src/test_utils/mod.rs +++ b/crates/pipeline/src/test_utils/mod.rs @@ -17,23 +17,16 @@ //! [`Ethereum`]: crate::prelude::Ethereum //! [`Optimism`]: crate::prelude::Optimism -mod accounts; -mod ethereum; -mod exts; -mod mock; mod node; mod platform; mod step; -mod transactions; pub use { crate::fake_step, - accounts::{FundedAccounts, WithFundedAccounts}, ethereum::EthConsensusDriver, - exts::*, - mock::{GenesisProviderFactory, GenesisStateProvider}, node::{ConsensusDriver, LocalNode}, platform::{TestNodeFactory, TestablePlatform}, + rblib_core::test_utils::*, rblib_tests_macros::{assert_is_dyn_safe, if_platform, rblib_test}, step::{ AlwaysBreakStep, @@ -43,28 +36,15 @@ pub use { OneStep, StringEvent, }, - transactions::{ - invalid_tx, - reverting_tx, - test_bundle, - test_tx, - test_txs, - transfer_tx, - }, }; +// Platform specific +mod ethereum; #[cfg(feature = "optimism")] mod optimism; - #[cfg(feature = "optimism")] pub use optimism::OptimismConsensusDriver; -pub const ONE_ETH: u128 = 1_000_000_000_000_000_000; -pub const TEST_COINBASE: crate::alloy::primitives::Address = // - crate::alloy::primitives::address!( - "0x0000000000000000000000000000000000012345" - ); - /// This gets invoked before any tests, when the cargo test framework loads the /// test library. #[cfg(feature = "test-utils")] diff --git a/src/test_utils/node.rs b/crates/pipeline/src/test_utils/node.rs similarity index 100% rename from src/test_utils/node.rs rename to crates/pipeline/src/test_utils/node.rs diff --git a/src/test_utils/optimism.rs b/crates/pipeline/src/test_utils/optimism.rs similarity index 92% rename from src/test_utils/optimism.rs rename to crates/pipeline/src/test_utils/optimism.rs index 4409d51..cada73f 100644 --- a/src/test_utils/optimism.rs +++ b/crates/pipeline/src/test_utils/optimism.rs @@ -10,6 +10,7 @@ use { primitives::B256, providers::Provider, }, + optimism_constants::{DEFAULT_EIP_1559_PARAMS, DEFAULT_MIN_BASE_FEE}, reth::{ ethereum::node::engine::EthPayloadAttributes as PayloadAttributes, node::builder::Node, @@ -21,14 +22,13 @@ use { TX_SET_L1_BLOCK_OP_MAINNET_BLOCK_124665056, }, }, - node::{OpEngineTypes, OpNode, OpPayloadAttributes}, + node::{OpEngineTypes, OpNode, OpPayloadAttributes, args::RollupArgs}, + rpc::OpEngineApiClient, }, payload::builder::PayloadId, rpc::types::{Block, engine::ForkchoiceState}, }, reth_ipc::client::IpcClientBuilder, - reth_optimism_node::args::RollupArgs, - reth_optimism_rpc::OpEngineApiClient, }; impl TestNodeFactory for Optimism { @@ -51,12 +51,6 @@ impl TestNodeFactory for Optimism { } } -pub(super) const DEFAULT_DENOMINATOR: u32 = 50; -pub(super) const DEFAULT_ELASTICITY: u32 = 2; -pub(super) const DEFAULT_EIP_1559_PARAMS: u64 = - ((DEFAULT_DENOMINATOR as u64) << 32) | (DEFAULT_ELASTICITY as u64); -pub(super) const DEFAULT_MIN_BASE_FEE: u64 = 0; - pub struct OptimismConsensusDriver; impl

ConsensusDriver

for OptimismConsensusDriver where diff --git a/src/test_utils/platform.rs b/crates/pipeline/src/test_utils/platform.rs similarity index 100% rename from src/test_utils/platform.rs rename to crates/pipeline/src/test_utils/platform.rs diff --git a/src/test_utils/step.rs b/crates/pipeline/src/test_utils/step.rs similarity index 100% rename from src/test_utils/step.rs rename to crates/pipeline/src/test_utils/step.rs diff --git a/crates/rblib/Cargo.toml b/crates/rblib/Cargo.toml new file mode 100644 index 0000000..8f00964 --- /dev/null +++ b/crates/rblib/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "rblib" +description = "Facade crate for backward compatibility. Alias of rblib-pipeline." +version.workspace = true +edition.workspace = true +rust-version.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +authors.workspace = true +exclude.workspace = true +publish = false + +[lints] +workspace = true + +[lib] +doctest = false + +[features] +default = ["rblib-pipeline/default"] +jemalloc = ["rblib-pipeline/jemalloc"] +jemalloc-prof = ["rblib-pipeline/jemalloc-prof"] +optimism = ["rblib-pipeline/optimism"] +test-utils = ["rblib-pipeline/test-utils"] + +[dependencies] +rblib-pipeline = { path = "../pipeline" } + +[dev-dependencies] +rblib = { path = ".", features = ["test-utils"] } +eyre.workspace = true +serde.workspace = true diff --git a/examples/checkpoints-eth.rs b/crates/rblib/examples/checkpoints-eth.rs similarity index 100% rename from examples/checkpoints-eth.rs rename to crates/rblib/examples/checkpoints-eth.rs diff --git a/examples/checkpoints-op.rs b/crates/rblib/examples/checkpoints-op.rs similarity index 100% rename from examples/checkpoints-op.rs rename to crates/rblib/examples/checkpoints-op.rs diff --git a/examples/custom-platform.rs b/crates/rblib/examples/custom-platform.rs similarity index 99% rename from examples/custom-platform.rs rename to crates/rblib/examples/custom-platform.rs index 44fc7c4..da3cf18 100644 --- a/examples/custom-platform.rs +++ b/crates/rblib/examples/custom-platform.rs @@ -67,7 +67,7 @@ impl Platform for CustomPlatform { impl PlatformWithTestnet for CustomPlatform { fn dev_chainspec() -> Arc> { - LazyLock::force(&crate::reth::optimism::chainspec::OP_DEV) + LazyLock::force(&reth::optimism::chainspec::OP_DEV) .clone() .with_funded_accounts() } diff --git a/examples/node-builder.md b/crates/rblib/examples/node-builder.md similarity index 100% rename from examples/node-builder.md rename to crates/rblib/examples/node-builder.md diff --git a/examples/pipeline-op.rs b/crates/rblib/examples/pipeline-op.rs similarity index 81% rename from examples/pipeline-op.rs rename to crates/rblib/examples/pipeline-op.rs index 3e4e689..2479d0b 100644 --- a/examples/pipeline-op.rs +++ b/crates/rblib/examples/pipeline-op.rs @@ -1,19 +1,14 @@ //! Example of creating a basic block builder for OP stack chains -use { - rblib::{ - pool::{AppendOrders, HostNodeInstaller, OrderPool}, - prelude::*, - steps::{OptimismPrologue, OrderByPriorityFee}, +use rblib::{ + pool::{AppendOrders, HostNodeInstaller, OrderPool}, + prelude::*, + reth::optimism::{ + cli::Cli, + node::{OpAddOns, OpEngineApiBuilder, OpEngineValidatorBuilder, OpNode}, + rpc::OpEthApiBuilder, }, - reth_optimism_cli::Cli, - reth_optimism_node::{ - OpAddOns, - OpEngineApiBuilder, - OpEngineValidatorBuilder, - OpNode, - }, - reth_optimism_rpc::OpEthApiBuilder, + steps::{OptimismPrologue, OrderByPriorityFee}, }; /// Basic block builder diff --git a/crates/rblib/src/lib.rs b/crates/rblib/src/lib.rs new file mode 100644 index 0000000..466e56b --- /dev/null +++ b/crates/rblib/src/lib.rs @@ -0,0 +1,29 @@ +pub use rblib_pipeline::{self as pipeline, core}; + +pub mod prelude { + // rblib_pipeline already include rblib core + pub use rblib_pipeline::prelude::*; +} + +pub mod pool { + pub use rblib_pipeline::pool::*; +} + +pub mod steps { + pub use rblib_pipeline::steps::*; +} + +pub mod reth { + pub use rblib_pipeline::reth::*; +} +pub mod revm { + pub use rblib_pipeline::revm::*; +} +pub mod alloy { + pub use rblib_pipeline::alloy::*; +} + +#[cfg(any(test, feature = "test-utils"))] +pub mod test_utils { + pub use rblib_pipeline::test_utils::*; +} diff --git a/src/test_utils/macros/Cargo.toml b/crates/test-utils-macros/Cargo.toml similarity index 76% rename from src/test_utils/macros/Cargo.toml rename to crates/test-utils-macros/Cargo.toml index d590c46..8da4d61 100644 --- a/src/test_utils/macros/Cargo.toml +++ b/crates/test-utils-macros/Cargo.toml @@ -11,16 +11,17 @@ authors.workspace = true exclude.workspace = true publish = false +[lints] +workspace = true + [lib] doctest = false proc-macro = true [features] default = [] -optimism = [] [dependencies] -syn = "2.0" -quote = "1.0" -proc-macro2 = { version = "1.0" } -paste = "1.0" +proc-macro2.workspace = true +quote.workspace = true +syn = { workspace = true, features = ["full"] } diff --git a/src/test_utils/macros/src/lib.rs b/crates/test-utils-macros/src/lib.rs similarity index 95% rename from src/test_utils/macros/src/lib.rs rename to crates/test-utils-macros/src/lib.rs index 67bfbb9..9dad9f2 100644 --- a/src/test_utils/macros/src/lib.rs +++ b/crates/test-utils-macros/src/lib.rs @@ -47,9 +47,8 @@ pub fn rblib_test(args: TokenStream, input: TokenStream) -> TokenStream { // Preserve return type only when it is `eyre::Result<()>` let generic_fn_output = if returns_eyre_result_unit { - let ty = match output { - syn::ReturnType::Type(_, ty) => ty, - _ => unreachable!(), + let syn::ReturnType::Type(_, ty) = output else { + unreachable!() }; quote! { -> #ty } } else { @@ -116,8 +115,8 @@ pub fn if_platform(input: TokenStream) -> TokenStream { /// dyn safe. /// /// Usage examples: -/// assert_is_dyn_safe!(MyTrait); -/// assert_is_dyn_safe!(MyTrait

, P: SomeBound + AnotherBound); +/// `assert_is_dyn_safe!(MyTrait);` +/// `assert_is_dyn_safe!(MyTrait

, P: SomeBound + AnotherBound);` /// /// [dyn safe]: https://doc.rust-lang.org/reference/items/traits.html#object-safety #[proc_macro] @@ -222,7 +221,7 @@ pub fn assert_is_dyn_safe(input: TokenStream) -> TokenStream { let fallback = Ident::new("trait_is_dyn_safe", Span::call_site()); let ident = last_ident_of_type(&trait_ty).unwrap_or(fallback.clone()); let snake = to_snake_case(&ident.to_string()); - Ident::new(&format!("{}_is_dyn_safe", snake), ident.span()) + Ident::new(&format!("{snake}_is_dyn_safe"), ident.span()) }; // Create a private scope with a type that includes Box>. @@ -246,11 +245,11 @@ struct IfPlatformInput { impl Parse for IfPlatformInput { fn parse(input: ParseStream) -> syn::Result { let platform = input.parse()?; - let _arrow = input.parse()?; + let arrow = input.parse()?; let code = input.parse()?; Ok(IfPlatformInput { platform, - _arrow, + _arrow: arrow, code, }) }