diff --git a/.cargo/audit.toml b/.cargo/audit.toml index 7266adc5..f29b16a4 100644 --- a/.cargo/audit.toml +++ b/.cargo/audit.toml @@ -18,4 +18,9 @@ ignore = [ # `lru` IterMut unsoundness; transitive dep via reth's `discv5` and `ratatui`. # Neither crate calls `iter_mut()` on the LruCache, so the affected code path is never hit. "RUSTSEC-2026-0002", + + # `rand` 0.8.5 unsoundness when `log` + `thread_rng` features are both enabled and a custom + # logger calls `rand::rng()` during reseeding. Our 0.8.5 (transitive via alloy-consensus) + # has neither feature enabled; our 0.9.4 is already patched. + "RUSTSEC-2026-0097", ] diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md index d2aacee2..d50cf2ca 100644 --- a/.claude/CLAUDE.md +++ b/.claude/CLAUDE.md @@ -10,13 +10,32 @@ Single-crate Rust application (not a workspace) that builds Signet rollup blocks make # Debug build make release # Optimized release build make run # Run zenith-builder-example binary -make test # Run all tests +make test # Run unit tests +make test-all # Run unit + integration tests (requires network) make fmt # Format code make clippy # Lint with warnings denied ``` Always lint before committing. The Makefile provides shortcuts (`make fmt`, `make clippy`, `make test`) +### Pre-push Checks (enforced by Claude hook) + +A Claude hook in `.claude/settings.json` runs `.claude/hooks/pre-push.sh` +before every `git push`. The push is blocked if any check fails. The checks: + +- `make fmt` +- `make clippy` (runs clippy with `-D warnings`) +- `RUSTDOCFLAGS="-D warnings" cargo doc --no-deps` + +Clippy and doc warnings are hard failures. + +### Running Individual Tests + +```bash +cargo test test_name # Run specific unit test by name +cargo test --features test-utils --test test_file_name # Run a specific integration test +``` + ## Architecture Five actor tasks communicate via tokio channels: @@ -41,7 +60,7 @@ src/ service.rs - Axum /healthcheck endpoint macros.rs - span_scoped!, span_debug/info/warn/error!, res/opt_unwrap_or_continue! utils.rs - Signature extraction, gas population helpers - test_utils.rs - setup_test_config, new_signed_tx, test_block_env helpers + test_utils/ - Test harness (cfg-gated via test-utils feature) tasks/ mod.rs - Module re-exports env.rs - EnvTask, SimEnv, Environment types @@ -125,6 +144,25 @@ src/ - Fresh branches off `main` for PRs. Descriptive branch names. - AI-authored GitHub comments must include `**[Claude Code]**` header. +## Testing + +### Integration Tests + +Integration tests live in `tests/` and are gated behind the `test-utils` Cargo feature via `required-features` in `Cargo.toml`. They are not compiled by `cargo test` alone — use `make test-all` or `cargo test --features test-utils` to build and run them. They require network access (real RPC endpoints or Anvil). + +### Simulation Harness (Offline Tests) + +`src/test_utils/` provides a testing harness for offline simulation testing, gated with `#[cfg(any(test, feature = "test-utils"))]`: + +- `TestDbBuilder` - Create in-memory EVM state +- `TestSimEnvBuilder` - Create `RollupEnv`/`HostEnv` without RPC +- `TestBlockBuildBuilder` - Build blocks with `BlockBuild` +- `basic_scenario()`, `gas_limit_scenario()` - Pre-configured test scenarios + +## Workflow + +After completing a set of changes, always run `make fmt` and `make clippy` and fix any issues before committing. + ## Local Development For local SDK development, uncomment the `[patch.crates-io]` section in Cargo.toml to point to local signet-sdk paths. diff --git a/.claude/hooks/pre-push.sh b/.claude/hooks/pre-push.sh new file mode 100755 index 00000000..189df81c --- /dev/null +++ b/.claude/hooks/pre-push.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Consume stdin (hook protocol) +cat > /dev/null + +cd "$(dirname "$0")/../.." + +echo "Running pre-push checks..." >&2 + +if ! make fmt 2>&1; then + echo "Format check failed. Run 'make fmt' to fix." >&2 + exit 2 +fi + +if ! make clippy 2>&1; then + echo "Clippy failed." >&2 + exit 2 +fi + +if ! RUSTDOCFLAGS="-D warnings" cargo doc --no-deps 2>&1; then + echo "Doc check failed." >&2 + exit 2 +fi + +echo "All pre-push checks passed." >&2 +exit 0 diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 00000000..cc3faa1e --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,17 @@ +{ + "hooks": { + "PreToolUse": [ + { + "matcher": "Bash", + "hooks": [ + { + "type": "command", + "if": "Bash(git push *)", + "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/pre-push.sh", + "timeout": 600 + } + ] + } + ] + } +} diff --git a/.github/workflows/rust-ci.yml b/.github/workflows/rust-ci.yml index 2665cf6d..1da3ef16 100644 --- a/.github/workflows/rust-ci.yml +++ b/.github/workflows/rust-ci.yml @@ -10,3 +10,4 @@ jobs: uses: init4tech/actions/.github/workflows/rust-base.yml@main with: install-foundry: true + test-all-features: false diff --git a/.gitignore b/.gitignore index 2aab8832..c2857905 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,6 @@ target/ # VSCode debug launcher .vscode/launch.json +# Claude configs .claude/*.local.* -.claude/settings.json CLAUDE.local.md diff --git a/Cargo.lock b/Cargo.lock index f52111e4..c868afab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -89,6 +89,15 @@ dependencies = [ "alloc-no-stdlib", ] +[[package]] +name = "alloca" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7d05ea6aea7e9e64d25b9156ba2fee3fdd659e34e41063cd2fc7cd020d7f4" +dependencies = [ + "cc", +] + [[package]] name = "allocator-api2" version = "0.2.21" @@ -126,9 +135,9 @@ dependencies = [ [[package]] name = "alloy-chains" -version = "0.2.32" +version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9247f0a399ef71aeb68f497b2b8fb348014f742b50d3b83b1e00dfe1b7d64b3d" +checksum = "84e0378e959aa6a885897522080a990e80eb317f1e9a222a604492ea50e13096" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -157,7 +166,7 @@ dependencies = [ "either", "k256", "once_cell", - "rand 0.8.5", + "rand 0.8.6", "secp256k1 0.30.0", "serde", "serde_json", @@ -245,7 +254,7 @@ dependencies = [ "alloy-rlp", "arbitrary", "crc", - "rand 0.8.5", + "rand 0.8.6", "serde", "thiserror 2.0.18", ] @@ -260,7 +269,7 @@ dependencies = [ "alloy-rlp", "arbitrary", "borsh", - "rand 0.8.5", + "rand 0.8.6", "serde", ] @@ -275,7 +284,7 @@ dependencies = [ "arbitrary", "borsh", "k256", - "rand 0.8.5", + "rand 0.8.6", "serde", "serde_with", "thiserror 2.0.18", @@ -283,14 +292,15 @@ dependencies = [ [[package]] name = "alloy-eip7928" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8222b1d88f9a6d03be84b0f5e76bb60cd83991b43ad8ab6477f0e4a7809b98d" +checksum = "407510740da514b694fecb44d8b3cebdc60d448f70cc5d24743e8ba273448a6e" dependencies = [ "alloy-primitives", "alloy-rlp", "arbitrary", "borsh", + "once_cell", "serde", ] @@ -464,7 +474,7 @@ dependencies = [ "alloy-signer-local", "k256", "libc", - "rand 0.8.5", + "rand 0.8.6", "serde_json", "tempfile", "thiserror 2.0.18", @@ -488,14 +498,14 @@ dependencies = [ "foldhash 0.2.0", "getrandom 0.4.2", "hashbrown 0.16.1", - "indexmap 2.13.0", + "indexmap 2.14.0", "itoa", "k256", "keccak-asm", "paste", "proptest", "proptest-derive", - "rand 0.9.2", + "rand 0.9.4", "rapidhash", "ruint", "rustc-hash", @@ -576,9 +586,9 @@ dependencies = [ [[package]] name = "alloy-rlp" -version = "0.3.13" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e93e50f64a77ad9c5470bf2ad0ca02f228da70c792a8f06634801e202579f35e" +checksum = "dc90b1e703d3c03f4ff7f48e82dd0bc1c8211ab7d079cd836a06fcfeb06651cb" dependencies = [ "alloy-rlp-derive", "arrayvec", @@ -587,9 +597,9 @@ dependencies = [ [[package]] name = "alloy-rlp-derive" -version = "0.3.13" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce8849c74c9ca0f5a03da1c865e3eb6f768df816e67dd3721a398a8a7e398011" +checksum = "f36834a5c0a2fa56e171bf256c34d70fca07d0c0031583edea1c4946b7889c9e" dependencies = [ "proc-macro2", "quote", @@ -724,7 +734,7 @@ dependencies = [ "ethereum_ssz 0.9.1", "ethereum_ssz_derive 0.9.1", "jsonwebtoken", - "rand 0.8.5", + "rand 0.8.6", "serde", "strum", ] @@ -852,7 +862,7 @@ dependencies = [ "coins-bip32", "coins-bip39", "k256", - "rand 0.8.5", + "rand 0.8.6", "thiserror 2.0.18", "zeroize", ] @@ -881,7 +891,7 @@ dependencies = [ "alloy-sol-macro-input", "const-hex", "heck", - "indexmap 2.13.0", + "indexmap 2.14.0", "proc-macro-error2", "proc-macro2", "quote", @@ -1047,6 +1057,12 @@ dependencies = [ "libc", ] +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + [[package]] name = "anstream" version = "1.0.0" @@ -1393,7 +1409,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1df2c09229cbc5a028b1d70e00fdb2acee28b1055dfb5ca73eea49c5a25c4e7c" dependencies = [ "num-traits", - "rand 0.8.5", + "rand 0.8.6", ] [[package]] @@ -1403,7 +1419,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94893f1e0c6eeab764ade8dc4c0db24caf4fe7cbbaafc0eba0a9030f447b5185" dependencies = [ "num-traits", - "rand 0.8.5", + "rand 0.8.6", ] [[package]] @@ -1413,7 +1429,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "246a225cc6131e9ee4f24619af0f19d67761fff15d7ccc22e42b80846e69449a" dependencies = [ "num-traits", - "rand 0.8.5", + "rand 0.8.6", ] [[package]] @@ -1439,9 +1455,9 @@ checksum = "4858a9d740c5007a9069007c3b4e91152d0506f13c1b31dd49051fd537656156" [[package]] name = "async-compression" -version = "0.4.41" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0f9ee0f6e02ffd7ad5816e9464499fba7b3effd01123b515c41d1697c43dad1" +checksum = "e79b3f8a79cccc2898f31920fc69f304859b3bd567490f75ebf51ae1c792a9ac" dependencies = [ "compression-codecs", "compression-core", @@ -1565,9 +1581,9 @@ dependencies = [ [[package]] name = "aws-lc-rs" -version = "1.16.2" +version = "1.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a054912289d18629dc78375ba2c3726a3afe3ff71b4edba9dedfca0e3446d1fc" +checksum = "0ec6fb3fe69024a75fa7e1bfb48aa6cf59706a101658ea01bfd33b2b248a038f" dependencies = [ "aws-lc-sys", "zeroize", @@ -1575,9 +1591,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.39.0" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa7e52a4c5c547c741610a2c6f123f3881e409b714cd27e6798ef020c514f0a" +checksum = "f50037ee5e1e41e7b8f9d161680a725bd1626cb6f8c7e901f91f942850852fe7" dependencies = [ "cc", "cmake", @@ -1891,9 +1907,9 @@ dependencies = [ [[package]] name = "axum" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b52af3cb4058c895d37317bb27508dccc8e5f2d39454016b297bf4a400597b8" +checksum = "31b698c5f9a010f6573133b09e0de5408834d0c82f8d7475a89fc1867a71cd90" dependencies = [ "axum-core 0.5.6", "bytes", @@ -2050,7 +2066,7 @@ version = "0.72.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "cexpr", "clang-sys", "itertools 0.13.0", @@ -2101,9 +2117,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.11.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" +checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" dependencies = [ "serde_core", ] @@ -2153,28 +2169,28 @@ dependencies = [ [[package]] name = "boa_ast" -version = "0.21.0" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc119a5ad34c3f459062a96907f53358989b173d104258891bb74f95d93747e8" +checksum = "6339a700715bda376f5ea65c76e8fe8fc880930d8b0638cea68e7f3da6538e0a" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "boa_interner", "boa_macros", "boa_string", - "indexmap 2.13.0", + "indexmap 2.14.0", "num-bigint", "rustc-hash", ] [[package]] name = "boa_engine" -version = "0.21.0" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e637ec52ea66d76b0ca86180c259d6c7bb6e6a6e14b2f36b85099306d8b00cc3" +checksum = "1521be326f8a5c8887e95d4ce7f002917a002a23f7b93b9a6a2bf50ed4157824" dependencies = [ "aligned-vec", "arrayvec", - "bitflags 2.11.0", + "bitflags 2.11.1", "boa_ast", "boa_gc", "boa_interner", @@ -2193,7 +2209,7 @@ dependencies = [ "futures-lite", "hashbrown 0.16.1", "icu_normalizer", - "indexmap 2.13.0", + "indexmap 2.14.0", "intrusive-collections", "itertools 0.14.0", "num-bigint", @@ -2202,7 +2218,7 @@ dependencies = [ "num_enum", "paste", "portable-atomic", - "rand 0.9.2", + "rand 0.9.4", "regress", "rustc-hash", "ryu-js", @@ -2220,9 +2236,9 @@ dependencies = [ [[package]] name = "boa_gc" -version = "0.21.0" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1179f690cbfcbe5364cceee5f1cb577265bb6f07b0be6f210aabe270adcf9da" +checksum = "17323a98cf2e631afacf1a6d659c1212c48a68bacfa85afab0a66ade80582e51" dependencies = [ "boa_macros", "boa_string", @@ -2232,14 +2248,14 @@ dependencies = [ [[package]] name = "boa_interner" -version = "0.21.0" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9626505d33dc63d349662437297df1d3afd9d5fc4a2b3ad34e5e1ce879a78848" +checksum = "20510b8b02bcde9b0a01cf34c0c308c56156503d1d91cdab4c8cfbd292b747ea" dependencies = [ "boa_gc", "boa_macros", "hashbrown 0.16.1", - "indexmap 2.13.0", + "indexmap 2.14.0", "once_cell", "phf", "rustc-hash", @@ -2248,9 +2264,9 @@ dependencies = [ [[package]] name = "boa_macros" -version = "0.21.0" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f36418a46544b152632c141b0a0b7a453cd69ca150caeef83aee9e2f4b48b7d" +checksum = "5822cb4f146d243060e588bc5a5f2e709683fdad3d7111f42c48e6b5c921d23d" dependencies = [ "cfg-if", "cow-utils", @@ -2262,11 +2278,11 @@ dependencies = [ [[package]] name = "boa_parser" -version = "0.21.0" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02f99bf5b684f0de946378fcfe5f38c3a0fbd51cbf83a0f39ff773a0e218541f" +checksum = "35bd957fa9fa93e3a001a8aba5a5cd40c2bbfde486378be4c4b472fd304aaddb" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "boa_ast", "boa_interner", "boa_macros", @@ -2280,9 +2296,9 @@ dependencies = [ [[package]] name = "boa_string" -version = "0.21.0" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45ce9d7aa5563a2e14eab111e2ae1a06a69a812f6c0c3d843196c9d03fbef440" +checksum = "ca2da1d7f4a76fd9040788a122f0d807910800a7b86f5952e9244848c36511de" dependencies = [ "fast-float2", "itoa", @@ -2358,13 +2374,14 @@ dependencies = [ [[package]] name = "builder" -version = "1.0.0-rc.7" +version = "1.0.0" dependencies = [ "alloy", "alloy-chains", "alloy-hardforks 0.4.7", "axum 0.7.9", "backon", + "criterion", "eyre", "futures-util", "git-version", @@ -2374,6 +2391,7 @@ dependencies = [ "reqwest", "reth-chainspec", "signet-block-processor", + "signet-bundle", "signet-constants", "signet-genesis", "signet-sim", @@ -2498,12 +2516,18 @@ checksum = "ef987d17b0a113becdd19d3d0022d04d7ef41f9efe4f3fb63ac44ba61df3ade9" dependencies = [ "camino", "cargo-platform", - "semver 1.0.27", + "semver 1.0.28", "serde", "serde_json", "thiserror 2.0.18", ] +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + [[package]] name = "castaway" version = "0.2.4" @@ -2515,9 +2539,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.57" +version = "1.2.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a0dd1ca384932ff3641c8718a02769f1698e7563dc6974ffd03346116310423" +checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d" dependencies = [ "find-msvc-tools", "jobserver", @@ -2566,6 +2590,33 @@ dependencies = [ "windows-link", ] +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + [[package]] name = "cipher" version = "0.4.4" @@ -2589,9 +2640,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.6.0" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" dependencies = [ "clap_builder", "clap_derive", @@ -2611,9 +2662,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.6.0" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1110bd8a634a1ab8cb04345d8d878267d57c3cf1b38d91b71af6686408bbca6a" +checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" dependencies = [ "heck", "proc-macro2", @@ -2629,9 +2680,9 @@ checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" [[package]] name = "cmake" -version = "0.1.57" +version = "0.1.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d" +checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" dependencies = [ "cc", ] @@ -2663,7 +2714,7 @@ dependencies = [ "hmac", "once_cell", "pbkdf2", - "rand 0.8.5", + "rand 0.8.6", "sha2", "thiserror 1.0.69", ] @@ -2730,9 +2781,9 @@ dependencies = [ [[package]] name = "compression-codecs" -version = "0.4.37" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb7b51a7d9c967fc26773061ba86150f19c50c0d65c887cb1fbe295fd16619b7" +checksum = "ce2548391e9c1929c21bf6aa2680af86fe4c1b33e6cea9ac1cfeec0bd11218cf" dependencies = [ "brotli", "compression-core", @@ -2744,9 +2795,9 @@ dependencies = [ [[package]] name = "compression-core" -version = "0.4.31" +version = "0.4.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75984efb6ed102a0d42db99afb6c1948f0380d1d91808d5529916e6c08b49d8d" +checksum = "cc14f565cf027a105f7a44ccf9e5b424348421a1d8952a8fc9d499d313107789" [[package]] name = "concat-kdf" @@ -2783,11 +2834,12 @@ checksum = "2f421161cb492475f1661ddc9815a745a1c894592070661180fdec3d4872e9c3" [[package]] name = "const_format" -version = "0.2.35" +version = "0.2.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7faa7469a93a566e9ccc1c73fe783b4a65c274c5ace346038dca9c39fe0030ad" +checksum = "4481a617ad9a412be3b97c5d403fef8ed023103368908b9c50af598ff467cc1e" dependencies = [ "const_format_proc_macros", + "konst", ] [[package]] @@ -2836,15 +2888,6 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" -[[package]] -name = "core2" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505" -dependencies = [ - "memchr", -] - [[package]] name = "cow-utils" version = "0.1.3" @@ -2871,9 +2914,9 @@ dependencies = [ [[package]] name = "crc-catalog" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" +checksum = "217698eaf96b4a3f0bc4f3662aaa55bdf913cd54d7204591faa790070c6d0853" [[package]] name = "crc32fast" @@ -2884,6 +2927,42 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "criterion" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "950046b2aa2492f9a536f5f4f9a3de7b9e2476e575e05bd6c333371add4d98f3" +dependencies = [ + "alloca", + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "itertools 0.13.0", + "num-traits", + "oorandom", + "page_size", + "plotters", + "rayon", + "regex", + "serde", + "serde_json", + "tinytemplate", + "tokio", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8d80a2f4f5b554395e47b5d8305bc3d27813bacb73493eb1001e8f76dae29ea" +dependencies = [ + "cast", + "itertools 0.13.0", +] + [[package]] name = "critical-section" version = "1.2.0" @@ -2930,7 +3009,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "crossterm_winapi", "derive_more", "document-features", @@ -3138,15 +3217,15 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" +checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" [[package]] name = "data-encoding-macro" -version = "0.1.19" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8142a83c17aa9461d637e649271eae18bf2edd00e91f2e105df36c3c16355bdb" +checksum = "3259c913752a86488b501ed8680446a5ed2d5aeac6e596cb23ba3800768ea32c" dependencies = [ "data-encoding", "data-encoding-macro-internal", @@ -3154,9 +3233,9 @@ dependencies = [ [[package]] name = "data-encoding-macro-internal" -version = "0.1.17" +version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ab67060fc6b8ef687992d439ca0fa36e7ed17e9a0b16b25b601e8757df720de" +checksum = "ccc2776f0c61eca1ca32528f85548abd1a4be8fb53d1b21c013e4f18da1e7090" dependencies = [ "data-encoding", "syn 2.0.117", @@ -3378,7 +3457,7 @@ dependencies = [ "more-asserts", "multiaddr", "parking_lot", - "rand 0.8.5", + "rand 0.8.6", "smallvec", "socket2", "tokio", @@ -3548,7 +3627,7 @@ dependencies = [ "hex", "k256", "log", - "rand 0.8.5", + "rand 0.8.6", "secp256k1 0.30.0", "serde", "sha3", @@ -3664,9 +3743,9 @@ dependencies = [ [[package]] name = "ethereum_ssz" -version = "0.10.1" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2128a84f7a3850d54ee343334e3392cca61f9f6aa9441eec481b9394b43c238b" +checksum = "368a4a4e4273b0135111fe9464e35465067766a8f664615b5a86338b73864407" dependencies = [ "alloy-primitives", "ethereum_serde_utils", @@ -3691,9 +3770,9 @@ dependencies = [ [[package]] name = "ethereum_ssz_derive" -version = "0.10.1" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd596f91cff004fc8d02be44c21c0f9b93140a04b66027ae052f5f8e05b48eba" +checksum = "f2cd82c68120c89361e1a457245cf212f7d9f541bffaffed530c8f2d54a160b2" dependencies = [ "darling 0.23.0", "proc-macro2", @@ -3701,6 +3780,17 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "evmap" +version = "11.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8874945f036109c72242964c1174cf99434e30cfa45bf45fedc983f50046f8" +dependencies = [ + "hashbag", + "left-right", + "smallvec", +] + [[package]] name = "eyre" version = "0.6.12" @@ -3719,9 +3809,9 @@ checksum = "f8eb564c5c7423d25c886fb561d1e4ee69f72354d16918afa32c08811f6b6a55" [[package]] name = "fastrand" -version = "2.3.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" [[package]] name = "fastrlp" @@ -3806,7 +3896,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "835c052cb0c08c1acf6ffd71c022172e18723949c8282f2b9f27efbc51e64534" dependencies = [ "byteorder", - "rand 0.8.5", + "rand 0.8.6", "rustc-hex", "static_assertions", ] @@ -4067,6 +4157,21 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42012b0f064e01aa58b545fe3727f90f7dd4020f4a3ea735b50344965f5a57e9" +[[package]] +name = "generator" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52f04ae4152da20c76fe800fa48659201d5cf627c5149ca0b707b69d7eef6cf9" +dependencies = [ + "cc", + "cfg-if", + "libc", + "log", + "rustversion", + "windows-link", + "windows-result", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -4154,7 +4259,7 @@ version = "0.20.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b88256088d75a56f8ecfa070513a775dd9107f6530ef14919dac831af9cfe2b" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "libc", "libgit2-sys", "log", @@ -4248,13 +4353,30 @@ dependencies = [ "futures-core", "futures-sink", "http 1.4.0", - "indexmap 2.13.0", + "indexmap 2.14.0", "slab", "tokio", "tokio-util", "tracing", ] +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "zerocopy", +] + +[[package]] +name = "hashbag" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7040a10f52cba493ddb09926e15d10a9d8a28043708a405931fe4c6f19fac064" + [[package]] name = "hashbrown" version = "0.12.3" @@ -4296,6 +4418,12 @@ dependencies = [ "serde_core", ] +[[package]] +name = "hashbrown" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" + [[package]] name = "hashlink" version = "0.11.0" @@ -4358,7 +4486,7 @@ dependencies = [ "idna", "ipnet", "once_cell", - "rand 0.9.2", + "rand 0.9.4", "ring", "serde", "thiserror 2.0.18", @@ -4381,7 +4509,7 @@ dependencies = [ "moka", "once_cell", "parking_lot", - "rand 0.9.2", + "rand 0.9.4", "resolv-conf", "serde", "smallvec", @@ -4505,9 +4633,9 @@ dependencies = [ [[package]] name = "hyper" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" dependencies = [ "atomic-waker", "bytes", @@ -4520,7 +4648,6 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "pin-utils", "smallvec", "tokio", "want", @@ -4528,9 +4655,9 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.7" +version = "0.27.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" dependencies = [ "http 1.4.0", "hyper", @@ -4538,11 +4665,10 @@ dependencies = [ "log", "rustls", "rustls-native-certs", - "rustls-pki-types", "tokio", "tokio-rustls", "tower-service", - "webpki-roots 1.0.6", + "webpki-roots 1.0.7", ] [[package]] @@ -4638,9 +4764,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" dependencies = [ "displaydoc", "litemap", @@ -4697,9 +4823,9 @@ checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" [[package]] name = "icu_provider" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" dependencies = [ "displaydoc", "icu_locale_core", @@ -4813,13 +4939,13 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.13.0" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "arbitrary", "equivalent", - "hashbrown 0.16.1", + "hashbrown 0.17.0", "serde", "serde_core", ] @@ -4835,15 +4961,15 @@ dependencies = [ [[package]] name = "init4-bin-base" -version = "0.18.0-rc.13" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "054d4b927194b1e73c85945a93254b19772fee2c627b190c9ae7c72da0b40eaf" +checksum = "f94c64278d765503d752783c7a1f1052f0d41cff51cd1fd258958d94a9a03010" dependencies = [ "alloy", "async-trait", "aws-config", "aws-sdk-kms", - "axum 0.8.8", + "axum 0.8.9", "chrono", "eyre", "futures-util", @@ -4888,7 +5014,7 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd5b3eaf1a28b758ac0faa5a4254e8ab2705605496f1b1f3fbbc3988ad73d199" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "inotify-sys", "libc", ] @@ -4927,9 +5053,9 @@ dependencies = [ [[package]] name = "interprocess" -version = "2.4.0" +version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6be5e5c847dbdb44564bd85294740d031f4f8aeb3464e5375ef7141f7538db69" +checksum = "069323743400cb7ab06a8fe5c1ed911d36b6919ec531661d034c89083629595b" dependencies = [ "doctest-file", "futures-core", @@ -4937,7 +5063,7 @@ dependencies = [ "recvmsg", "tokio", "widestring", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -4951,14 +5077,15 @@ dependencies = [ [[package]] name = "ipconfig" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d72a21f6a71a6c4c3160e095e8925861f5119dd26ef71acee1b9146f74f76c8" +checksum = "4d40460c0ce33d6ce4b0630ad68ff63d6661961c48b6dba35e5a4d81cfb48222" dependencies = [ "socket2", "widestring", + "windows-registry", + "windows-result", "windows-sys 0.61.2", - "winreg", ] [[package]] @@ -4969,9 +5096,9 @@ checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" [[package]] name = "iri-string" -version = "0.7.11" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8e7418f59cc01c88316161279a7f665217ae316b388e58a0d10e29f54f1e5eb" +checksum = "25e659a4bb38e810ebc252e53b5814ff908a8c58c2a9ce2fae1bbec24cbf4e20" dependencies = [ "memchr", "serde", @@ -5072,10 +5199,12 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.91" +version = "0.3.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" +checksum = "a1840c94c045fbcf8ba2812c95db44499f7c64910a912551aaaa541decebcacf" dependencies = [ + "cfg-if", + "futures-util", "once_cell", "wasm-bindgen", ] @@ -5139,7 +5268,7 @@ dependencies = [ "jsonrpsee-types", "parking_lot", "pin-project", - "rand 0.9.2", + "rand 0.9.4", "rustc-hash", "serde", "serde_json", @@ -5304,14 +5433,29 @@ dependencies = [ [[package]] name = "keccak-asm" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b646a74e746cd25045aa0fd42f4f7f78aa6d119380182c7e63a5593c4ab8df6f" +checksum = "fa468878266ad91431012b3e5ef1bf9b170eab22883503a318d46857afa4579a" dependencies = [ "digest 0.10.7", "sha3-asm", ] +[[package]] +name = "konst" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128133ed7824fcd73d6e7b17957c5eb7bacb885649bd8c69708b2331a10bcefb" +dependencies = [ + "konst_macro_rules", +] + +[[package]] +name = "konst_macro_rules" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4933f3f57a8e9d9da04db23fb153356ecaf00cbd14aee46279c33dc80925c37" + [[package]] name = "kqueue" version = "1.1.1" @@ -5344,11 +5488,22 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" +[[package]] +name = "left-right" +version = "0.11.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f0c21e4c8ff95f487fb34e6f9182875f42c84cef966d29216bf115d9bba835a" +dependencies = [ + "crossbeam-utils", + "loom", + "slab", +] + [[package]] name = "libc" -version = "0.2.183" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "libgit2-sys" @@ -5410,14 +5565,14 @@ dependencies = [ [[package]] name = "libredox" -version = "0.1.14" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1744e39d1d6a9948f4f388969627434e31128196de472883b39f148769bfe30a" +checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "libc", "plain", - "redox_syscall 0.7.3", + "redox_syscall 0.7.4", ] [[package]] @@ -5438,9 +5593,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.25" +version = "1.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52f4c29e2a68ac30c9087e1b772dc9f44a2b66ed44edf2266cf2be9b03dafc1" +checksum = "fc3a226e576f50782b3305c5ccf458698f92798987f551c6a02efe8276721e22" dependencies = [ "cc", "libc", @@ -5450,11 +5605,11 @@ dependencies = [ [[package]] name = "line-clipping" -version = "0.3.5" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f4de44e98ddbf09375cbf4d17714d18f39195f4f4894e8524501726fd9a8a4a" +checksum = "3f50e8f47623268b5407192d26876c4d7f89d686ca130fdc53bced4814cd29f8" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", ] [[package]] @@ -5481,9 +5636,9 @@ checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "litemap" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" [[package]] name = "litrs" @@ -5507,11 +5662,24 @@ version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +[[package]] +name = "loom" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "419e0dc8046cb947daa77eb95ae174acfbddb7673b4151f56d1eed8e93fbfaca" +dependencies = [ + "cfg-if", + "generator", + "scoped-tls", + "tracing", + "tracing-subscriber 0.3.23", +] + [[package]] name = "lru" -version = "0.16.3" +version = "0.16.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1dc47f592c06f33f8e3aea9591776ec7c9f9e4124778ff8a3c3b87159f7e593" +checksum = "7f66e8d5d03f609abc3a39e6f08e4164ebf1447a732906d39eb9b99b7919ef39" dependencies = [ "hashbrown 0.16.1", ] @@ -5631,12 +5799,12 @@ dependencies = [ [[package]] name = "metrics" -version = "0.24.3" +version = "0.24.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d5312e9ba3771cfa961b585728215e3d972c950a3eed9252aa093d6301277e8" +checksum = "ff56c2e7dce6bd462e3b8919986a617027481b1dcc703175b58cf9dd98a2f071" dependencies = [ - "ahash", "portable-atomic", + "rapidhash", ] [[package]] @@ -5652,16 +5820,17 @@ dependencies = [ [[package]] name = "metrics-exporter-prometheus" -version = "0.18.1" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3589659543c04c7dc5526ec858591015b87cd8746583b51b48ef4353f99dbcda" +checksum = "5c0ca2990f7f78a72c4000ddce186db7d1b700477426563ee851c95ea3c0d0c4" dependencies = [ "base64 0.22.1", + "evmap", "http-body-util", "hyper", "hyper-rustls", "hyper-util", - "indexmap 2.13.0", + "indexmap 2.14.0", "ipnet", "metrics", "metrics-util", @@ -5690,17 +5859,18 @@ dependencies = [ [[package]] name = "metrics-util" -version = "0.20.1" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdfb1365fea27e6dd9dc1dbc19f570198bc86914533ad639dae939635f096be4" +checksum = "55ff5c12b797ebf094dc7c1d87e905efc0329cba332f96d51db03875441012b5" dependencies = [ "crossbeam-epoch", "crossbeam-utils", "hashbrown 0.16.1", "metrics", "quanta", - "rand 0.9.2", + "rand 0.9.4", "rand_xoshiro", + "rapidhash", "sketches-ddsketch", ] @@ -5738,9 +5908,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" dependencies = [ "libc", "log", @@ -5825,11 +5995,10 @@ dependencies = [ [[package]] name = "multihash" -version = "0.19.3" +version = "0.19.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b430e7953c29dd6a09afc29ff0bb69c6e306329ee6794700aee27b76a1aea8d" +checksum = "577c63b00ad74d57e8c9aa870b5fccebf2fd64a308a5aee9f1bb88e4aea19447" dependencies = [ - "core2", "unsigned-varint", ] @@ -5866,7 +6035,7 @@ version = "8.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "fsevent-sys", "inotify", "kqueue", @@ -5884,7 +6053,7 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42b8cfee0e339a0337359f3c88165702ac6e600dc01c0cc9579a92d62b08477a" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", ] [[package]] @@ -5941,9 +6110,9 @@ dependencies = [ [[package]] name = "num-conv" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" +checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" [[package]] name = "num-integer" @@ -6052,7 +6221,7 @@ dependencies = [ "chrono", "getrandom 0.2.17", "http 1.4.0", - "rand 0.8.5", + "rand 0.8.6", "reqwest", "serde", "serde_json", @@ -6068,7 +6237,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", ] [[package]] @@ -6097,6 +6266,12 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" +[[package]] +name = "oorandom" +version = "11.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" + [[package]] name = "op-alloy" version = "0.23.1" @@ -6220,11 +6395,11 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openssl" -version = "0.10.76" +version = "0.10.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "951c002c75e16ea2c65b8c7e4d3d51d5530d8dfa7d060b4776828c88cfb18ecf" +checksum = "f38c4372413cdaaf3cc79dd92d29d7d9f5ab09b51b10dded508fb90bb70b9222" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "cfg-if", "foreign-types", "libc", @@ -6252,18 +6427,18 @@ checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" [[package]] name = "openssl-src" -version = "300.5.5+3.5.5" +version = "300.6.0+3.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f1787d533e03597a7934fd0a765f0d28e94ecc5fb7789f8053b1e699a56f709" +checksum = "a8e8cbfd3a4a8c8f089147fd7aaa33cf8c7450c4d09f8f80698a0cf093abeff4" dependencies = [ "cc", ] [[package]] name = "openssl-sys" -version = "0.9.112" +version = "0.9.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d55af3b3e226502be1526dfdba67ab0e9c96fc293004e79576b2b9edb0dbdb" +checksum = "13ce1245cd07fcc4cfdb438f7507b0c7e4f3849a69fd84d52374c66d83741bb6" dependencies = [ "cc", "libc", @@ -6422,7 +6597,7 @@ dependencies = [ "futures-util", "opentelemetry 0.30.0", "percent-encoding", - "rand 0.9.2", + "rand 0.9.4", "serde_json", "thiserror 2.0.18", ] @@ -6438,7 +6613,7 @@ dependencies = [ "futures-util", "opentelemetry 0.31.0", "percent-encoding", - "rand 0.9.2", + "rand 0.9.4", "thiserror 2.0.18", ] @@ -6673,9 +6848,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" [[package]] name = "plain" @@ -6683,6 +6858,34 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" +[[package]] +name = "plotters" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" + +[[package]] +name = "plotters-svg" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" +dependencies = [ + "plotters-backend", +] + [[package]] name = "polyval" version = "0.6.2" @@ -6703,9 +6906,9 @@ checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" [[package]] name = "potential_utf" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" dependencies = [ "zerovec", ] @@ -6811,7 +7014,7 @@ version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25485360a54d6861439d60facef26de713b1e126bf015ec8f98239467a2b82f7" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "chrono", "flate2", "procfs-core", @@ -6824,7 +7027,7 @@ version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6401bf7b6af22f78b563665d15a22e9aef27775b79b149a66ca022468a4e405" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "chrono", "hex", ] @@ -6837,9 +7040,9 @@ checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" dependencies = [ "bit-set", "bit-vec", - "bitflags 2.11.0", + "bitflags 2.11.1", "num-traits", - "rand 0.9.2", + "rand 0.9.4", "rand_chacha 0.9.0", "rand_xorshift", "regex-syntax", @@ -6964,7 +7167,7 @@ dependencies = [ "bytes", "getrandom 0.3.4", "lru-slab", - "rand 0.9.2", + "rand 0.9.4", "ring", "rustc-hash", "rustls", @@ -7019,9 +7222,9 @@ checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" [[package]] name = "rand" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" dependencies = [ "libc", "rand_chacha 0.3.1", @@ -7031,9 +7234,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.2" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.5", @@ -7103,7 +7306,7 @@ version = "4.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e48930979c155e2f33aa36ab3119b5ee81332beb6482199a8ecd6029b80b59" dependencies = [ - "rand 0.9.2", + "rand 0.9.4", "rustversion", ] @@ -7125,7 +7328,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ef8dea09a92caaf73bff7adb70b76162e5937524058a7e5bff37869cbbec293" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "compact_str", "hashbrown 0.16.1", "indoc", @@ -7157,7 +7360,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7dbfa023cd4e604c2553483820c5fe8aa9d71a42eea5aa77c6e7f35756612db" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "hashbrown 0.16.1", "indoc", "instability", @@ -7176,14 +7379,14 @@ version = "11.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", ] [[package]] name = "rayon" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" dependencies = [ "either", "rayon-core", @@ -7211,16 +7414,16 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", ] [[package]] name = "redox_syscall" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce70a74e890531977d37e532c34d45e9055d2409ed08ddba14529471ed0be16" +checksum = "f450ad9c3b1da563fb6948a8e0fb0fb9269711c9c73d9ea1de5058c79c8d643a" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", ] [[package]] @@ -7356,7 +7559,7 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots 1.0.6", + "webpki-roots 1.0.7", ] [[package]] @@ -7441,7 +7644,7 @@ dependencies = [ "metrics", "parking_lot", "pin-project", - "rand 0.9.2", + "rand 0.9.4", "rayon", "reth-chainspec", "reth-errors", @@ -7593,7 +7796,7 @@ dependencies = [ "cfg-if", "eyre", "libc", - "rand 0.8.5", + "rand 0.8.6", "reth-fs-util", "secp256k1 0.30.0", "serde", @@ -7802,7 +8005,7 @@ dependencies = [ "enr", "itertools 0.14.0", "parking_lot", - "rand 0.8.5", + "rand 0.8.6", "reth-ethereum-forks", "reth-net-banlist", "reth-net-nat", @@ -7829,7 +8032,7 @@ dependencies = [ "futures", "itertools 0.14.0", "metrics", - "rand 0.9.2", + "rand 0.9.4", "reth-chainspec", "reth-ethereum-forks", "reth-metrics", @@ -7912,7 +8115,7 @@ dependencies = [ "futures", "hmac", "pin-project", - "rand 0.8.5", + "rand 0.8.6", "reth-network-peers", "secp256k1 0.30.0", "sha2", @@ -8082,8 +8285,8 @@ dependencies = [ "alloy-eips", "alloy-primitives", "alloy-rlp", - "ethereum_ssz 0.10.1", - "ethereum_ssz_derive 0.10.1", + "ethereum_ssz 0.10.3", + "ethereum_ssz_derive 0.10.3", "snap", "thiserror 2.0.18", ] @@ -8505,7 +8708,7 @@ name = "reth-libmdbx" version = "1.11.3" source = "git+https://github.com/paradigmxyz/reth?tag=v1.11.3#d6324d63e27ef6b7c49cdc9b1977c1b808234c7b" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "byteorder", "dashmap", "derive_more", @@ -8579,8 +8782,8 @@ dependencies = [ "metrics", "parking_lot", "pin-project", - "rand 0.8.5", - "rand 0.9.2", + "rand 0.8.6", + "rand 0.9.4", "rayon", "reth-chainspec", "reth-consensus", @@ -8818,7 +9021,7 @@ dependencies = [ "futures", "humantime", "ipnet", - "rand 0.9.2", + "rand 0.9.4", "reth-chainspec", "reth-cli-util", "reth-config", @@ -9452,7 +9655,7 @@ dependencies = [ "jsonrpsee-core", "jsonrpsee-types", "metrics", - "rand 0.9.2", + "rand 0.9.4", "reqwest", "reth-chain-state", "reth-chainspec", @@ -9746,12 +9949,12 @@ dependencies = [ "alloy-rlp", "aquamarine", "auto_impl", - "bitflags 2.11.0", + "bitflags 2.11.1", "futures-util", "metrics", "parking_lot", "pin-project", - "rand 0.9.2", + "rand 0.9.4", "reth-chain-state", "reth-chainspec", "reth-eth-wire-types", @@ -10108,7 +10311,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "311720d4f0f239b041375e7ddafdbd20032a33b7bae718562ea188e188ed9fd3" dependencies = [ "alloy-eip7928", - "bitflags 2.11.0", + "bitflags 2.11.1", "revm-bytecode", "revm-primitives", "serde", @@ -10246,8 +10449,8 @@ dependencies = [ "parity-scale-codec", "primitive-types", "proptest", - "rand 0.8.5", - "rand 0.9.2", + "rand 0.8.6", + "rand 0.9.4", "rlp", "ruint-macro", "serde_core", @@ -10263,11 +10466,11 @@ checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" [[package]] name = "rustc-hash" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" dependencies = [ - "rand 0.8.5", + "rand 0.8.6", ] [[package]] @@ -10300,7 +10503,7 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ - "semver 1.0.27", + "semver 1.0.28", ] [[package]] @@ -10309,7 +10512,7 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "errno", "libc", "linux-raw-sys", @@ -10318,9 +10521,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.37" +version = "0.23.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" +checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" dependencies = [ "aws-lc-rs", "log", @@ -10346,9 +10549,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.14.0" +version = "1.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" +checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" dependencies = [ "web-time", "zeroize", @@ -10383,9 +10586,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] name = "rustls-webpki" -version = "0.103.10" +version = "0.103.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" dependencies = [ "aws-lc-rs", "ring", @@ -10476,6 +10679,12 @@ dependencies = [ "hashbrown 0.13.2", ] +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + [[package]] name = "scopeguard" version = "1.2.0" @@ -10504,7 +10713,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b50c5943d326858130af85e049f2661ba3c78b26589b8ab98e65e80ae44a1252" dependencies = [ "bitcoin_hashes", - "rand 0.8.5", + "rand 0.8.6", "secp256k1-sys 0.10.1", "serde", ] @@ -10516,7 +10725,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c3c81b43dc2d8877c216a3fccf76677ee1ebccd429566d3e67447290d0c42b2" dependencies = [ "bitcoin_hashes", - "rand 0.9.2", + "rand 0.9.4", "secp256k1-sys 0.11.0", ] @@ -10544,7 +10753,7 @@ version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "core-foundation 0.10.1", "core-foundation-sys", "libc", @@ -10581,9 +10790,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" dependencies = [ "serde", "serde_core", @@ -10652,7 +10861,7 @@ version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ - "indexmap 2.13.0", + "indexmap 2.14.0", "itoa", "memchr", "serde", @@ -10673,9 +10882,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "876ac351060d4f882bb1032b6369eb0aef79ad9df1ea8bc404874d8cc3d0cd98" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" dependencies = [ "serde_core", ] @@ -10702,7 +10911,7 @@ dependencies = [ "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.13.0", + "indexmap 2.14.0", "schemars 0.9.0", "schemars 1.2.1", "serde_core", @@ -10757,9 +10966,9 @@ dependencies = [ [[package]] name = "sha3" -version = "0.10.8" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +checksum = "77fd7028345d415a4034cf8777cd4f8ab1851274233b45f84e3d955502d93874" dependencies = [ "digest 0.10.7", "keccak", @@ -10767,9 +10976,9 @@ dependencies = [ [[package]] name = "sha3-asm" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b31139435f327c93c6038ed350ae4588e2c70a13d50599509fee6349967ba35a" +checksum = "59cbb88c189d6352cc8ae96a39d19c7ecad8f7330b29461187f2587fdc2988d5" dependencies = [ "cc", "cfg-if", @@ -10888,9 +11097,9 @@ dependencies = [ [[package]] name = "signet-bundle" -version = "0.16.0-rc.16" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9bd62b011781950089d31f742d90b3d62ba00f374db0dfe0d3ba1f1358b45c" +checksum = "dafb04a643cee29180ce8ae2067c81691417bc6ca6ac350146652a3f84576685" dependencies = [ "alloy", "serde", @@ -10904,9 +11113,9 @@ dependencies = [ [[package]] name = "signet-constants" -version = "0.16.0-rc.16" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9a4bb305179fc7461eb5cbee914344067ef03dccc267b20dfc1eaea9db1459" +checksum = "f4360f5a5706c7a9d6ddea3b3d8ab7721f7341c5242bc7f3857967f28090568d" dependencies = [ "alloy", "serde", @@ -10940,12 +11149,12 @@ dependencies = [ [[package]] name = "signet-evm" -version = "0.16.0-rc.16" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55caa1277f82e8854b9daf275d20deb8a78d57fa1babdcdeed8dd2976dfca18c" +checksum = "05cd7eb4c1a23f8b3742084283c690d04fda828f8c7aaa232b0d0a8d89861267" dependencies = [ "alloy", - "bitflags 2.11.0", + "bitflags 2.11.1", "signet-extract", "signet-journal", "signet-types", @@ -10957,9 +11166,9 @@ dependencies = [ [[package]] name = "signet-extract" -version = "0.16.0-rc.16" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a1e5c76e9c86d5e3cc8d321e41022cf2a6658e6346119fd4c29f894d8513a1c" +checksum = "6f23779af433acdbac5fb7a5e1489f5a1caab72f5337e2ee640c030a3bdcfb6b" dependencies = [ "alloy", "signet-types", @@ -10982,9 +11191,9 @@ dependencies = [ [[package]] name = "signet-journal" -version = "0.16.0-rc.16" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89519bce0c6d3143593cdab0501852741f66b641d048d65ec87cb095a803bd0a" +checksum = "f4c3c665f734b1cb68ee2a0263032fd171a3737927b4793d11377b8e917f05e7" dependencies = [ "alloy", "futures-util", @@ -11010,9 +11219,9 @@ dependencies = [ [[package]] name = "signet-sim" -version = "0.16.0-rc.16" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f6fe279a15c70233025257536f405c1f4c502525ef533c852f918933e0b5084" +checksum = "8002d4b0a83bdab1568d77de5790a70a1bd2c8d58bb43649299b7b319b5f67e4" dependencies = [ "alloy", "lru", @@ -11029,9 +11238,9 @@ dependencies = [ [[package]] name = "signet-tx-cache" -version = "0.16.0-rc.16" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d95529a7f5a351bf5daee91a4c25a317fcb815b0fc4a26aa7099629d619eb51b" +checksum = "ba06dc92c72b877f042ba141c7ee553c68aab1c6e077a3ba1adb2eae17fdaff4" dependencies = [ "alloy", "futures-util", @@ -11048,9 +11257,9 @@ dependencies = [ [[package]] name = "signet-types" -version = "0.16.0-rc.16" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10dff727bb17387f043999bca9dafef6c0ba8e10e8d7c5f5fd8e53b708ea959" +checksum = "9ad0d5db88367e5f2433e1cdd74017cf399dd985906be905032cba1ad314c30f" dependencies = [ "alloy", "chrono", @@ -11062,9 +11271,9 @@ dependencies = [ [[package]] name = "signet-zenith" -version = "0.16.0-rc.16" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7c96ff57c3b9d32a452d96cbf0bd852c987688721c84877ec6218e1ceed87ed" +checksum = "4cafc152356fad0f4461af607af6e4151a3a577b55007a60ce5587969127a6e5" dependencies = [ "alloy", "alloy-core", @@ -11074,9 +11283,9 @@ dependencies = [ [[package]] name = "simd-adler32" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" [[package]] name = "simple_asn1" @@ -11155,7 +11364,7 @@ dependencies = [ "http 1.4.0", "httparse", "log", - "rand 0.8.5", + "rand 0.8.6", "sha1", ] @@ -11214,6 +11423,12 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +[[package]] +name = "symlink" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7973cce6668464ea31f176d85b13c7ab3bba2cb3b77a2ed26abd7801688010a" + [[package]] name = "syn" version = "1.0.109" @@ -11288,7 +11503,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -11347,9 +11562,9 @@ dependencies = [ [[package]] name = "thin-vec" -version = "0.2.14" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "144f754d318415ac792f9d69fc87abbbfc043ce2ef041c60f16ad828f638717d" +checksum = "b0f7e269b48f0a7dd0146680fa24b50cc67fc0373f086a5b2f99bd084639b482" [[package]] name = "thiserror" @@ -11448,7 +11663,6 @@ checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" dependencies = [ "deranged", "itoa", - "js-sys", "libc", "num-conv", "num_threads", @@ -11476,15 +11690,25 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" dependencies = [ "displaydoc", "serde_core", "zerovec", ] +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "tinyvec" version = "1.11.0" @@ -11502,9 +11726,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.50.0" +version = "1.52.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" +checksum = "b67dee974fe86fd92cc45b7a95fdd2f99a36a6d7b0d431a231178d3d670bbcc6" dependencies = [ "bytes", "libc", @@ -11519,9 +11743,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.6.1" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", @@ -11609,7 +11833,7 @@ version = "0.9.12+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" dependencies = [ - "indexmap 2.13.0", + "indexmap 2.14.0", "serde_core", "serde_spanned", "toml_datetime 0.7.5+spec-1.1.0", @@ -11629,39 +11853,39 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "1.1.0+spec-1.1.0" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97251a7c317e03ad83774a8752a7e81fb6067740609f75ea2b585b569a59198f" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" dependencies = [ "serde_core", ] [[package]] name = "toml_edit" -version = "0.25.8+spec-1.1.0" +version = "0.25.11+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16bff38f1d86c47f9ff0647e6838d7bb362522bdf44006c7068c2b1e606f1f3c" +checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b" dependencies = [ - "indexmap 2.13.0", - "toml_datetime 1.1.0+spec-1.1.0", + "indexmap 2.14.0", + "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", - "winnow 1.0.0", + "winnow 1.0.2", ] [[package]] name = "toml_parser" -version = "1.1.0+spec-1.1.0" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2334f11ee363607eb04df9b8fc8a13ca1715a72ba8662a26ac285c98aabb4011" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ - "winnow 1.0.0", + "winnow 1.0.2", ] [[package]] name = "toml_writer" -version = "1.1.0+spec-1.1.0" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d282ade6016312faf3e41e57ebbba0c073e4056dab1232ab1cb624199648f8ed" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" [[package]] name = "tonic" @@ -11730,7 +11954,7 @@ dependencies = [ "futures-core", "futures-util", "hdrhistogram", - "indexmap 2.13.0", + "indexmap 2.14.0", "pin-project-lite", "slab", "sync_wrapper", @@ -11749,7 +11973,7 @@ checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ "async-compression", "base64 0.22.1", - "bitflags 2.11.0", + "bitflags 2.11.1", "bytes", "futures-core", "futures-util", @@ -11798,11 +12022,12 @@ dependencies = [ [[package]] name = "tracing-appender" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "786d480bce6247ab75f005b14ae1624ad978d3029d9113f0a22fa1ac773faeaf" +checksum = "050686193eb999b4bb3bc2acfa891a13da00f79734704c4b8b4ef1a10b368a3c" dependencies = [ "crossbeam-channel", + "symlink", "thiserror 2.0.18", "time", "tracing-subscriber 0.3.23", @@ -11990,9 +12215,9 @@ dependencies = [ [[package]] name = "trevm" -version = "0.34.1" +version = "0.34.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904819c7508930edfc3c1ea7e47871aa8c6a4e0c913de5c56b7d73006845c5ae" +checksum = "bb4a80f4985c7b8aefee9fe1ba48b6b37c617f8b5ffd88f67adbfef78d77c894" dependencies = [ "alloy", "dashmap", @@ -12019,7 +12244,7 @@ dependencies = [ "http 1.4.0", "httparse", "log", - "rand 0.9.2", + "rand 0.9.4", "rustls", "rustls-pki-types", "sha1", @@ -12038,7 +12263,7 @@ dependencies = [ "http 1.4.0", "httparse", "log", - "rand 0.9.2", + "rand 0.9.4", "sha1", "thiserror 2.0.18", "utf-8", @@ -12052,9 +12277,9 @@ checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" [[package]] name = "typenum" -version = "1.19.0" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" [[package]] name = "ucd-trie" @@ -12106,9 +12331,9 @@ checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "unicode-segmentation" -version = "1.12.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" [[package]] name = "unicode-truncate" @@ -12200,9 +12425,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.22.0" +version = "1.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a68d3c8f01c0cfa54a75291d83601161799e4a89a39e0929f4b0354d88757a37" +checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76" dependencies = [ "getrandom 0.4.2", "js-sys", @@ -12311,11 +12536,11 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.2+wasi-0.2.9" +version = "1.0.3+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" dependencies = [ - "wit-bindgen", + "wit-bindgen 0.57.1", ] [[package]] @@ -12324,14 +12549,14 @@ version = "0.4.0+wasi-0.3.0-rc-2026-01-06" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" dependencies = [ - "wit-bindgen", + "wit-bindgen 0.51.0", ] [[package]] name = "wasm-bindgen" -version = "0.2.114" +version = "0.2.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" +checksum = "df52b6d9b87e0c74c9edfa1eb2d9bf85e5d63515474513aa50fa181b3c4f5db1" dependencies = [ "cfg-if", "once_cell", @@ -12342,23 +12567,19 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.64" +version = "0.4.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9c5522b3a28661442748e09d40924dfb9ca614b21c00d3fd135720e48b67db8" +checksum = "af934872acec734c2d80e6617bbb5ff4f12b052dd8e6332b0817bce889516084" dependencies = [ - "cfg-if", - "futures-util", "js-sys", - "once_cell", "wasm-bindgen", - "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.114" +version = "0.2.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" +checksum = "78b1041f495fb322e64aca85f5756b2172e35cd459376e67f2a6c9dffcedb103" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -12366,9 +12587,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.114" +version = "0.2.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" +checksum = "9dcd0ff20416988a18ac686d4d4d0f6aae9ebf08a389ff5d29012b05af2a1b41" dependencies = [ "bumpalo", "proc-macro2", @@ -12379,9 +12600,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.114" +version = "0.2.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" +checksum = "49757b3c82ebf16c57d69365a142940b384176c24df52a087fb748e2085359ea" dependencies = [ "unicode-ident", ] @@ -12403,7 +12624,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" dependencies = [ "anyhow", - "indexmap 2.13.0", + "indexmap 2.14.0", "wasm-encoder", "wasmparser", ] @@ -12427,10 +12648,10 @@ version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "hashbrown 0.15.5", - "indexmap 2.13.0", - "semver 1.0.27", + "indexmap 2.14.0", + "semver 1.0.28", ] [[package]] @@ -12449,9 +12670,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.91" +version = "0.3.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9" +checksum = "2eadbac71025cd7b0834f20d1fe8472e8495821b4e9801eb0a60bd1f19827602" dependencies = [ "js-sys", "wasm-bindgen", @@ -12473,14 +12694,14 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75c7f0ef91146ebfb530314f5f1d24528d7f0767efbfd31dce919275413e393e" dependencies = [ - "webpki-root-certs 1.0.6", + "webpki-root-certs 1.0.7", ] [[package]] name = "webpki-root-certs" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca" +checksum = "f31141ce3fc3e300ae89b78c0dd67f9708061d1d2eda54b8209346fd6be9a92c" dependencies = [ "rustls-pki-types", ] @@ -12491,14 +12712,14 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "webpki-roots 1.0.6", + "webpki-roots 1.0.7", ] [[package]] name = "webpki-roots" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" +checksum = "52f5ee44c96cf55f1b349600768e3ece3a8f26010c05265ab73f945bb1a2eb9d" dependencies = [ "rustls-pki-types", ] @@ -12903,23 +13124,13 @@ dependencies = [ [[package]] name = "winnow" -version = "1.0.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a90e88e4667264a994d34e6d1ab2d26d398dcdca8b7f52bec8668957517fc7d8" +checksum = "2ee1708bef14716a11bae175f579062d4554d95be2c6829f518df847b7b3fdd0" dependencies = [ "memchr", ] -[[package]] -name = "winreg" -version = "0.55.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb5a765337c50e9ec252c2069be9bf91c7df47afb103b642ba3a53bf8101be97" -dependencies = [ - "cfg-if", - "windows-sys 0.59.0", -] - [[package]] name = "wit-bindgen" version = "0.51.0" @@ -12929,6 +13140,12 @@ dependencies = [ "wit-bindgen-rust-macro", ] +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + [[package]] name = "wit-bindgen-core" version = "0.51.0" @@ -12948,7 +13165,7 @@ checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" dependencies = [ "anyhow", "heck", - "indexmap 2.13.0", + "indexmap 2.14.0", "prettyplease", "syn 2.0.117", "wasm-metadata", @@ -12978,8 +13195,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", - "bitflags 2.11.0", - "indexmap 2.13.0", + "bitflags 2.11.1", + "indexmap 2.14.0", "log", "serde", "serde_derive", @@ -12998,9 +13215,9 @@ checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" dependencies = [ "anyhow", "id-arena", - "indexmap 2.13.0", + "indexmap 2.14.0", "log", - "semver 1.0.27", + "semver 1.0.28", "serde", "serde_derive", "serde_json", @@ -13016,9 +13233,9 @@ checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" [[package]] name = "writeable" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" [[package]] name = "ws_stream_wasm" @@ -13078,9 +13295,9 @@ checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" [[package]] name = "yoke" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" dependencies = [ "stable_deref_trait", "yoke-derive", @@ -13089,9 +13306,9 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", @@ -13101,18 +13318,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.47" +version = "0.8.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efbb2a062be311f2ba113ce66f697a4dc589f85e78a4aea276200804cea0ed87" +checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.47" +version = "0.8.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e8bc7269b54418e7aeeef514aa68f8690b8c0489a06b0136e5f57c4c5ccab89" +checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" dependencies = [ "proc-macro2", "quote", @@ -13121,18 +13338,18 @@ dependencies = [ [[package]] name = "zerofrom" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", @@ -13162,20 +13379,21 @@ dependencies = [ [[package]] name = "zerotrie" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" dependencies = [ "displaydoc", "yoke", "zerofrom", + "zerovec", ] [[package]] name = "zerovec" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" dependencies = [ "serde", "yoke", @@ -13185,9 +13403,9 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index e9bcdb35..79bdf6f0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "builder" -version = "1.0.0-rc.8" +version = "1.0.0" description = "signet builder example" edition = "2024" @@ -10,6 +10,9 @@ license = "Apache-2.0 OR MIT" homepage = "https://github.com/init4tech/builder" repository = "https://github.com/init4tech/builder" +[features] +test-utils = [] + [lib] name = "builder" @@ -20,11 +23,11 @@ path = "bin/builder.rs" [dependencies] init4-bin-base = { version = "0.18.0-rc.13", features = ["perms", "aws", "pylon"] } -signet-constants = { version = "0.16.0-rc.16" } -signet-sim = { version = "0.16.0-rc.16" } -signet-tx-cache = { version = "0.16.0-rc.16" } -signet-types = { version = "0.16.0-rc.16" } -signet-zenith = { version = "0.16.0-rc.16" } +signet-constants = { version = "0.16.0-rc.17" } +signet-sim = { version = "0.16.0-rc.17" } +signet-tx-cache = { version = "0.16.0-rc.17" } +signet-types = { version = "0.16.0-rc.17" } +signet-zenith = { version = "0.16.0-rc.17" } signet-block-processor = { git = "https://github.com/init4tech/node-components", tag = "v0.16.0-rc.10" } signet-genesis = { git = "https://github.com/init4tech/node-components", tag = "v0.16.0-rc.10" } @@ -63,6 +66,14 @@ url = "2.5.4" [dev-dependencies] alloy-hardforks = "0.4.0" alloy-chains = "0.2" +criterion = { version = "0.8.2", features = ["async_tokio"] } +signet-bundle = "0.16.0-rc.11" + +[[bench]] +name = "sim" +path = "benches/sim/main.rs" +harness = false +required-features = ["test-utils"] # comment / uncomment for local dev # [patch.crates-io] diff --git a/Makefile b/Makefile index db935850..0ce5a089 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ PROFILE ?= dev # override with `make PROFILE=release build` CLIPPY_FLAGS ?= $(TARGETS) $(FEATURES) --workspace --profile dev -- -D warnings FMT_FLAGS ?= --all -.PHONY: build release run test clean fmt clippy default +.PHONY: build release run test test-all clean fmt clippy default default: build @@ -34,6 +34,10 @@ run: test: $(CARGO) test +# NB: requires auth and server configuration +test-all: + $(CARGO) test --features test-utils + clean: $(CARGO) clean diff --git a/README.md b/README.md index 169fca72..83052075 100644 --- a/README.md +++ b/README.md @@ -166,6 +166,23 @@ The previous header's basefee is tracked through the build loop and used for gas ## ✅ Testing +### Unit Tests + +```bash +make test +``` + +### Integration Tests + +Integration tests require network access (RPC endpoints or Anvil) and are gated behind the `test-utils` Cargo feature. They are not compiled by default. + +```bash +make test-all # Run all tests (unit + integration) +cargo test --features test-utils --test block_builder_test # Run a specific integration test +``` + +### Deployment Verification + 1. Build the Docker image: ```bash diff --git a/benches/sim/bundles.rs b/benches/sim/bundles.rs new file mode 100644 index 00000000..e065126c --- /dev/null +++ b/benches/sim/bundles.rs @@ -0,0 +1,111 @@ +//! Benchmarks for bundle simulation: varying counts, DB latency, and concurrency. + +use crate::fixture::{Fixture, LATENCIES, set_up_bundle_sim, set_up_sender_distribution_sim}; +use builder::test_utils::setup_test_config; +use criterion::{BatchSize, Bencher, BenchmarkId, Criterion}; +use std::time::Duration; + +pub fn bench_bundle_counts(criterion: &mut Criterion) { + const LATENCY: Duration = Duration::ZERO; + const CONCURRENCY: usize = 4; + + setup_test_config(); + let runtime = tokio::runtime::Runtime::new().unwrap(); + + let mut group = criterion.benchmark_group("sim_varying_simple_bundle_counts_no_db_latency"); + group.sample_size(10); + + for bundle_count in [1, 10, 100, 1000] { + group.bench_with_input( + BenchmarkId::new("bundle_count", bundle_count), + &bundle_count, + |bench: &mut Bencher, &bundle_count| { + bench.to_async(&runtime).iter_batched( + || set_up_bundle_sim(bundle_count, LATENCY, CONCURRENCY), + Fixture::run, + BatchSize::PerIteration, + ); + }, + ); + } + group.finish(); +} + +pub fn bench_bundle_db_latency(criterion: &mut Criterion) { + const BUNDLE_COUNT: usize = 10; + const CONCURRENCY: usize = 4; + + setup_test_config(); + let runtime = tokio::runtime::Runtime::new().unwrap(); + + let mut group = + criterion.benchmark_group(format!("sim_{BUNDLE_COUNT}_simple_bundles_varying_db_latency")); + group.sample_size(10); + + for (label, latency) in LATENCIES { + group.bench_with_input(BenchmarkId::new("latency", label), &latency, |bench, &lat| { + bench.to_async(&runtime).iter_batched( + || set_up_bundle_sim(BUNDLE_COUNT, lat, CONCURRENCY), + Fixture::run, + BatchSize::PerIteration, + ); + }); + } + group.finish(); +} + +pub fn bench_bundle_concurrency(criterion: &mut Criterion) { + const BUNDLE_COUNT: usize = 100; + const LATENCY: Duration = Duration::ZERO; + + setup_test_config(); + let runtime = tokio::runtime::Runtime::new().unwrap(); + + let mut group = criterion.benchmark_group(format!( + "sim_{BUNDLE_COUNT}_simple_bundles_no_db_latency_varying_concurrency" + )); + group.sample_size(10); + + for concurrency in [1, 2, 4, 8, 16] { + group.bench_with_input( + BenchmarkId::new("thread_count", concurrency), + &concurrency, + |bench, &conc| { + bench.to_async(&runtime).iter_batched( + || set_up_bundle_sim(BUNDLE_COUNT, LATENCY, conc), + Fixture::run, + BatchSize::PerIteration, + ); + }, + ); + } + group.finish(); +} + +pub fn bench_bundle_sender_distribution(criterion: &mut Criterion) { + const BUNDLE_COUNT: usize = 100; + const CONCURRENCY: usize = 4; + + setup_test_config(); + let runtime = tokio::runtime::Runtime::new().unwrap(); + + let mut group = criterion.benchmark_group(format!( + "sim_{BUNDLE_COUNT}_simple_bundles_no_db_latency_varying_sender_count" + )); + group.sample_size(10); + + for num_senders in [1, 5, 20, 100] { + group.bench_with_input( + BenchmarkId::new("sender_count", num_senders), + &num_senders, + |bench, &senders| { + bench.to_async(&runtime).iter_batched( + || set_up_sender_distribution_sim(BUNDLE_COUNT, senders, CONCURRENCY), + Fixture::run, + BatchSize::PerIteration, + ); + }, + ); + } + group.finish(); +} diff --git a/benches/sim/complex_bundles.rs b/benches/sim/complex_bundles.rs new file mode 100644 index 00000000..24ba2653 --- /dev/null +++ b/benches/sim/complex_bundles.rs @@ -0,0 +1,61 @@ +//! Benchmarks for complex bundle shapes: multi-tx bundles and bundles with host transactions. + +use crate::fixture::{Fixture, set_up_host_tx_bundle_sim, set_up_multi_tx_bundle_sim}; +use builder::test_utils::setup_test_config; +use criterion::{BatchSize, BenchmarkId, Criterion}; + +pub fn bench_multi_tx_bundles(criterion: &mut Criterion) { + const BUNDLE_COUNT: usize = 100; + const CONCURRENCY: usize = 4; + + setup_test_config(); + let runtime = tokio::runtime::Runtime::new().unwrap(); + + let mut group = criterion.benchmark_group(format!( + "sim_{BUNDLE_COUNT}_bundles_no_db_latency_varying_txs_per_bundle" + )); + group.sample_size(10); + + for txs_per_bundle in [1, 3, 5, 10] { + group.bench_with_input( + BenchmarkId::new("txs_per_bundle", txs_per_bundle), + &txs_per_bundle, + |bench, &tpb| { + bench.to_async(&runtime).iter_batched( + || set_up_multi_tx_bundle_sim(BUNDLE_COUNT, tpb, CONCURRENCY), + Fixture::run, + BatchSize::PerIteration, + ); + }, + ); + } + group.finish(); +} + +pub fn bench_host_tx_bundles(criterion: &mut Criterion) { + const BUNDLE_COUNT: usize = 100; + const CONCURRENCY: usize = 4; + + setup_test_config(); + let runtime = tokio::runtime::Runtime::new().unwrap(); + + let mut group = criterion.benchmark_group(format!( + "sim_{BUNDLE_COUNT}_bundles_no_db_latency_varying_host_txs_per_bundle" + )); + group.sample_size(10); + + for host_txs in [0, 1, 3, 5] { + group.bench_with_input( + BenchmarkId::new("host_txs_per_bundle", host_txs), + &host_txs, + |bench, &htpb| { + bench.to_async(&runtime).iter_batched( + || set_up_host_tx_bundle_sim(BUNDLE_COUNT, htpb, CONCURRENCY), + Fixture::run, + BatchSize::PerIteration, + ); + }, + ); + } + group.finish(); +} diff --git a/benches/sim/failed_bundles.rs b/benches/sim/failed_bundles.rs new file mode 100644 index 00000000..324adfac --- /dev/null +++ b/benches/sim/failed_bundles.rs @@ -0,0 +1,53 @@ +//! Benchmarks for bundles that fail: preflight validation failures and EVM execution reverts. + +use crate::fixture::{ + Fixture, LATENCIES, set_up_execution_failure_sim, set_up_preflight_failure_sim, +}; +use builder::test_utils::setup_test_config; +use criterion::{BatchSize, BenchmarkId, Criterion}; + +pub fn bench_preflight_failure(criterion: &mut Criterion) { + const COUNT: usize = 10; + const CONCURRENCY: usize = 4; + + setup_test_config(); + let runtime = tokio::runtime::Runtime::new().unwrap(); + + let mut group = + criterion.benchmark_group(format!("sim_{COUNT}_simple_bundles_all_failing_preflight")); + group.sample_size(10); + + for (label, latency) in LATENCIES { + group.bench_with_input(BenchmarkId::new("latency", label), &latency, |bench, &lat| { + bench.to_async(&runtime).iter_batched( + || set_up_preflight_failure_sim(COUNT, lat, CONCURRENCY), + Fixture::run, + BatchSize::PerIteration, + ); + }); + } + group.finish(); +} + +pub fn bench_execution_failure(criterion: &mut Criterion) { + const COUNT: usize = 10; + const CONCURRENCY: usize = 4; + + setup_test_config(); + let runtime = tokio::runtime::Runtime::new().unwrap(); + + let mut group = + criterion.benchmark_group(format!("sim_{COUNT}_simple_bundles_all_failing_execution")); + group.sample_size(10); + + for (label, latency) in LATENCIES { + group.bench_with_input(BenchmarkId::new("latency", label), &latency, |bench, &lat| { + bench.to_async(&runtime).iter_batched( + || set_up_execution_failure_sim(COUNT, lat, CONCURRENCY), + Fixture::run, + BatchSize::PerIteration, + ); + }); + } + group.finish(); +} diff --git a/benches/sim/fixture.rs b/benches/sim/fixture.rs new file mode 100644 index 00000000..472dce26 --- /dev/null +++ b/benches/sim/fixture.rs @@ -0,0 +1,429 @@ +//! Shared fixtures and setup functions for simulation benchmarks. + +use alloy::{ + primitives::{Address, Bytes, U256}, + serde::OtherFields, + signers::local::PrivateKeySigner, +}; +use builder::test_utils::{ + DEFAULT_BALANCE, DEFAULT_BASEFEE, TestDb, TestDbBuilder, TestHostEnv, TestRollupEnv, + TestSimEnvBuilder, TestStateSource, create_call_tx, create_transfer_tx, + scenarios_test_block_env, +}; +use signet_bundle::RecoveredBundle; +use signet_sim::{SharedSimEnv, SimCache}; +use std::time::Duration; +use tokio::time::Instant; +use trevm::revm::bytecode::Bytecode; +use trevm::revm::inspector::NoOpInspector; + +pub const BLOCK_NUMBER: u64 = 100; +pub const BLOCK_TIMESTAMP: u64 = 1_700_000_000; +pub const RU_CHAIN_ID: u64 = 88888; +pub const DEFAULT_PRIORITY_FEE: u128 = 10_000_000_000; +pub const MAX_GAS: u64 = 3_000_000_000; +pub const MAX_HOST_GAS: u64 = 24_000_000; +pub const REVERTING_CONTRACT: Address = Address::repeat_byte(0xBB); +/// Latency values based on real-world RPC provider benchmarks: +/// - 0ms: baseline, no network simulation +/// - 50ms: good provider, same-region (p50 for top providers) +/// - 200ms: cross-region or average provider +pub const LATENCIES: [(&str, Duration); 3] = [ + ("0ms", Duration::ZERO), + ("50ms", Duration::from_millis(50)), + ("200ms", Duration::from_millis(200)), +]; + +/// Type alias for the shared simulation environment used in benchmarks. +pub type BenchSimEnv = SharedSimEnv; + +/// Everything needed to run a benchmark iteration: the simulation environment +/// plus state sources for preflight validity checks. +pub struct Fixture { + env: BenchSimEnv, + rollup_source: TestStateSource, + host_source: TestStateSource, +} + +impl Fixture { + fn new(envs: Envs, concurrency: usize, cache: SimCache) -> Self { + let Envs { rollup_env, host_env, rollup_source, host_source } = envs; + // Set a deadline far in the future so that `sim_round()` never short-circuits. + let finish_by = Instant::now() + Duration::from_secs(3600); + let env = SharedSimEnv::new(rollup_env, host_env, finish_by, concurrency, cache); + Fixture { env, rollup_source, host_source } + } + + /// Drain all items from the sim env via repeated `sim_round()` calls. + pub async fn run(self) { + let Fixture { mut env, rollup_source, host_source } = self; + while env.sim_round(MAX_GAS, MAX_HOST_GAS, &rollup_source, &host_source).await.is_some() {} + } +} + +struct Envs { + rollup_env: TestRollupEnv, + host_env: TestHostEnv, + rollup_source: TestStateSource, + host_source: TestStateSource, +} + +impl Envs { + /// Build simulation environments and state sources from a shared database. + fn new(db: TestDb) -> Self { + let block_env = + scenarios_test_block_env(BLOCK_NUMBER, DEFAULT_BASEFEE, BLOCK_TIMESTAMP, MAX_GAS); + + let sim_env = TestSimEnvBuilder::new() + .with_rollup_db(db.clone()) + .with_host_db(db.clone()) + .with_block_env(block_env); + + let rollup_source = TestStateSource::new(db.clone()); + let host_source = TestStateSource::new(db); + let (rollup_env, host_env) = sim_env.build(); + Envs { rollup_env, host_env, rollup_source, host_source } + } +} + +/// Generate `count` random funded signers. +fn generate_signers(count: usize) -> Vec { + (0..count).map(|_| PrivateKeySigner::random()).collect() +} + +/// Create a `TestDbBuilder` with accounts funded from the given signers. +fn fund_accounts(signers: &[PrivateKeySigner]) -> TestDbBuilder { + let balance = U256::from(DEFAULT_BALANCE); + let mut builder = TestDbBuilder::new(); + for signer in signers { + builder = builder.with_account(signer.address(), balance, 0); + } + builder +} + +/// Create a `RecoveredBundle` with one transfer transaction. +fn make_bundle( + signer: &PrivateKeySigner, + to: Address, + nonce: u64, + uuid: String, + max_priority_fee: u128, +) -> RecoveredBundle { + let tx = + create_transfer_tx(signer, to, U256::from(1_000u64), nonce, RU_CHAIN_ID, max_priority_fee) + .unwrap(); + + RecoveredBundle::new_unchecked( + vec![tx], + vec![], + BLOCK_NUMBER, + Some(BLOCK_TIMESTAMP - 100), + Some(BLOCK_TIMESTAMP + 100), + vec![], + Some(uuid), + vec![], + None, + None, + vec![], + OtherFields::default(), + ) +} + +/// Build a [`SharedSimEnv`] and state sources with bundles in the cache. +/// +/// All databases use the same latency to model production RPC round-trips +/// (both simulation environments and preflight state sources hit the RPC). +pub fn set_up_bundle_sim(count: usize, latency: Duration, concurrency: usize) -> Fixture { + let signers = generate_signers(count); + let db = fund_accounts(&signers).with_latency(latency).build(); + let envs = Envs::new(db); + + let cache = SimCache::with_capacity(count); + let recipient = Address::repeat_byte(0xAA); + let bundles: Vec = signers + .iter() + .enumerate() + .map(|(idx, signer)| { + make_bundle(signer, recipient, 0, format!("bench-{idx}"), DEFAULT_PRIORITY_FEE) + }) + .collect(); + cache.add_bundles(bundles, DEFAULT_BASEFEE); + + Fixture::new(envs, concurrency, cache) +} + +/// Build a [`SharedSimEnv`] and state sources with standalone txs in the cache. +pub fn set_up_tx_sim(count: usize, latency: Duration, concurrency: usize) -> Fixture { + let signers = generate_signers(count); + let db = fund_accounts(&signers).with_latency(latency).build(); + let envs = Envs::new(db); + + let cache = SimCache::with_capacity(count); + let recipient = Address::repeat_byte(0xAA); + for signer in &signers { + let tx = create_transfer_tx( + signer, + recipient, + U256::from(1_000u64), + 0, + RU_CHAIN_ID, + DEFAULT_PRIORITY_FEE, + ) + .unwrap(); + cache.add_tx(tx, DEFAULT_BASEFEE); + } + + Fixture::new(envs, concurrency, cache) +} + +/// Build a [`SharedSimEnv`] with bundles that all fail preflight validation. +/// +/// Accounts are funded with nonce 1, but all bundles use nonce 0. Since +/// `state_nonce > tx_nonce`, every item is marked `Never` and removed from the +/// cache on the first `sim_round()` call. +pub fn set_up_preflight_failure_sim( + count: usize, + latency: Duration, + concurrency: usize, +) -> Fixture { + let signers = generate_signers(count); + + let balance = U256::from(DEFAULT_BALANCE); + let mut db_builder = TestDbBuilder::new(); + for signer in &signers { + db_builder = db_builder.with_account(signer.address(), balance, 1); + } + let db = db_builder.with_latency(latency).build(); + let envs = Envs::new(db); + + let cache = SimCache::with_capacity(count); + let recipient = Address::repeat_byte(0xAA); + let bundles: Vec = signers + .iter() + .enumerate() + .map(|(idx, signer)| { + make_bundle(signer, recipient, 0, format!("bench-{idx}"), DEFAULT_PRIORITY_FEE) + }) + .collect(); + cache.add_bundles(bundles, DEFAULT_BASEFEE); + + Fixture::new(envs, concurrency, cache) +} + +/// Build a [`SharedSimEnv`] with bundles that pass preflight but revert during +/// EVM execution. +/// +/// A contract at [`REVERTING_CONTRACT`] contains `PUSH0 PUSH0 REVERT` (always +/// reverts with empty data). All bundles call this contract, so they pass +/// preflight (valid nonce + sufficient balance) but fail during simulation. +pub fn set_up_execution_failure_sim( + count: usize, + latency: Duration, + concurrency: usize, +) -> Fixture { + let signers = generate_signers(count); + + // PUSH0 PUSH0 REVERT — always reverts with empty returndata. + let revert_bytecode = Bytecode::new_raw(Bytes::from_static(&[0x5F, 0x5F, 0xFD])); + + let balance = U256::from(DEFAULT_BALANCE); + let mut db_builder = TestDbBuilder::new().with_contract(REVERTING_CONTRACT, revert_bytecode); + for signer in &signers { + db_builder = db_builder.with_account(signer.address(), balance, 0); + } + let db = db_builder.with_latency(latency).build(); + let envs = Envs::new(db); + + let cache = SimCache::with_capacity(count); + let bundles: Vec = signers + .iter() + .enumerate() + .map(|(idx, signer)| { + let tx = create_call_tx( + signer, + REVERTING_CONTRACT, + Bytes::new(), + U256::ZERO, + 0, + RU_CHAIN_ID, + 50_000, + DEFAULT_PRIORITY_FEE, + ) + .unwrap(); + RecoveredBundle::new_unchecked( + vec![tx], + vec![], + BLOCK_NUMBER, + Some(BLOCK_TIMESTAMP - 100), + Some(BLOCK_TIMESTAMP + 100), + vec![], + Some(format!("bench-{idx}")), + vec![], + None, + None, + vec![], + OtherFields::default(), + ) + }) + .collect(); + cache.add_bundles(bundles, DEFAULT_BASEFEE); + + Fixture::new(envs, concurrency, cache) +} + +/// Build a [`SharedSimEnv`] with `bundle_count` bundles spread across `num_senders` signers. +/// +/// Each signer sends `bundle_count / num_senders` bundles with incrementing nonces. +pub fn set_up_sender_distribution_sim( + bundle_count: usize, + num_senders: usize, + concurrency: usize, +) -> Fixture { + let signers = generate_signers(num_senders); + let bundles_per_sender = bundle_count / num_senders; + + let balance = U256::from(DEFAULT_BALANCE); + let mut db_builder = TestDbBuilder::new(); + for signer in &signers { + db_builder = db_builder.with_account(signer.address(), balance, 0); + } + let db = db_builder.build(); + let envs = Envs::new(db); + + let cache = SimCache::with_capacity(bundle_count); + let recipient = Address::repeat_byte(0xAA); + let bundles: Vec = signers + .iter() + .enumerate() + .flat_map(|(sender_idx, signer)| { + (0..bundles_per_sender).map(move |nonce| { + make_bundle( + signer, + recipient, + nonce as u64, + format!("bench-{sender_idx}-{nonce}"), + DEFAULT_PRIORITY_FEE, + ) + }) + }) + .collect(); + cache.add_bundles(bundles, DEFAULT_BASEFEE); + + Fixture::new(envs, concurrency, cache) +} + +/// Build a [`SharedSimEnv`] with bundles containing multiple rollup transactions each. +/// +/// Each bundle has `txs_per_bundle` transfer transactions from the same sender with +/// incrementing nonces. This measures the cost of simulating larger bundles where +/// all transactions must succeed atomically. +pub fn set_up_multi_tx_bundle_sim( + bundle_count: usize, + txs_per_bundle: usize, + concurrency: usize, +) -> Fixture { + let signers = generate_signers(bundle_count); + let db = fund_accounts(&signers).build(); + let envs = Envs::new(db); + + let cache = SimCache::with_capacity(bundle_count); + let recipient = Address::repeat_byte(0xAA); + let bundles: Vec = signers + .iter() + .enumerate() + .map(|(idx, signer)| { + let txs: Vec<_> = (0..txs_per_bundle) + .map(|nonce| { + create_transfer_tx( + signer, + recipient, + U256::from(1_000u64), + nonce as u64, + RU_CHAIN_ID, + DEFAULT_PRIORITY_FEE, + ) + .unwrap() + }) + .collect(); + RecoveredBundle::new_unchecked( + txs, + vec![], + BLOCK_NUMBER, + Some(BLOCK_TIMESTAMP - 100), + Some(BLOCK_TIMESTAMP + 100), + vec![], + Some(format!("bench-{idx}")), + vec![], + None, + None, + vec![], + OtherFields::default(), + ) + }) + .collect(); + cache.add_bundles(bundles, DEFAULT_BASEFEE); + + Fixture::new(envs, concurrency, cache) +} + +/// Build a [`SharedSimEnv`] with bundles that include host-chain transactions. +/// +/// Each bundle has one rollup transfer plus `host_txs_per_bundle` host-chain transfers. +/// This measures the overhead of bundles that carry cross-chain transactions. +pub fn set_up_host_tx_bundle_sim( + bundle_count: usize, + host_txs_per_bundle: usize, + concurrency: usize, +) -> Fixture { + let signers = generate_signers(bundle_count); + let db = fund_accounts(&signers).build(); + let envs = Envs::new(db); + + let cache = SimCache::with_capacity(bundle_count); + let recipient = Address::repeat_byte(0xAA); + let bundles: Vec = signers + .iter() + .enumerate() + .map(|(idx, signer)| { + let rollup_tx = create_transfer_tx( + signer, + recipient, + U256::from(1_000u64), + 0, + RU_CHAIN_ID, + DEFAULT_PRIORITY_FEE, + ) + .unwrap(); + let host_txs: Vec<_> = (0..host_txs_per_bundle) + .map(|nonce| { + create_transfer_tx( + signer, + recipient, + U256::from(1_000u64), + (nonce + 1) as u64, + RU_CHAIN_ID, + DEFAULT_PRIORITY_FEE, + ) + .unwrap() + }) + .collect(); + RecoveredBundle::new_unchecked( + vec![rollup_tx], + host_txs, + BLOCK_NUMBER, + Some(BLOCK_TIMESTAMP - 100), + Some(BLOCK_TIMESTAMP + 100), + vec![], + Some(format!("bench-{idx}")), + vec![], + None, + None, + vec![], + OtherFields::default(), + ) + }) + .collect(); + cache.add_bundles(bundles, DEFAULT_BASEFEE); + + Fixture::new(envs, concurrency, cache) +} diff --git a/benches/sim/main.rs b/benches/sim/main.rs new file mode 100644 index 00000000..01aba87f --- /dev/null +++ b/benches/sim/main.rs @@ -0,0 +1,22 @@ +mod bundles; +mod complex_bundles; +mod failed_bundles; +mod fixture; +mod txs; + +use criterion::{criterion_group, criterion_main}; + +criterion_group!( + benches, + bundles::bench_bundle_counts, + bundles::bench_bundle_db_latency, + bundles::bench_bundle_concurrency, + bundles::bench_bundle_sender_distribution, + complex_bundles::bench_multi_tx_bundles, + complex_bundles::bench_host_tx_bundles, + txs::bench_tx_counts, + txs::bench_tx_db_latency, + failed_bundles::bench_preflight_failure, + failed_bundles::bench_execution_failure, +); +criterion_main!(benches); diff --git a/benches/sim/txs.rs b/benches/sim/txs.rs new file mode 100644 index 00000000..d124818c --- /dev/null +++ b/benches/sim/txs.rs @@ -0,0 +1,55 @@ +//! Benchmarks for standalone transaction simulation: varying counts and DB latency. + +use crate::fixture::{Fixture, LATENCIES, set_up_tx_sim}; +use builder::test_utils::setup_test_config; +use criterion::{BatchSize, Bencher, BenchmarkId, Criterion}; +use std::time::Duration; + +pub fn bench_tx_counts(criterion: &mut Criterion) { + const LATENCY: Duration = Duration::ZERO; + const CONCURRENCY: usize = 4; + + setup_test_config(); + let runtime = tokio::runtime::Runtime::new().unwrap(); + + let mut group = criterion.benchmark_group("sim_varying_tx_counts_with_no_db_latency"); + group.sample_size(10); + + for tx_count in [1, 10, 100, 1000] { + group.bench_with_input( + BenchmarkId::new("tx_count", tx_count), + &tx_count, + |bench: &mut Bencher, &tx_count| { + bench.to_async(&runtime).iter_batched( + || set_up_tx_sim(tx_count, LATENCY, CONCURRENCY), + Fixture::run, + BatchSize::PerIteration, + ); + }, + ); + } + group.finish(); +} + +pub fn bench_tx_db_latency(criterion: &mut Criterion) { + const TX_COUNT: usize = 10; + const CONCURRENCY: usize = 4; + + setup_test_config(); + let runtime = tokio::runtime::Runtime::new().unwrap(); + + let mut group = + criterion.benchmark_group(format!("sim_{TX_COUNT}_txs_with_varying_db_latency")); + group.sample_size(10); + + for (label, latency) in LATENCIES { + group.bench_with_input(BenchmarkId::new("latency", label), &latency, |bench, &lat| { + bench.to_async(&runtime).iter_batched( + || set_up_tx_sim(TX_COUNT, lat, CONCURRENCY), + Fixture::run, + BatchSize::PerIteration, + ); + }); + } + group.finish(); +} diff --git a/bin/builder.rs b/bin/builder.rs index 77573233..e9e3ee3e 100644 --- a/bin/builder.rs +++ b/bin/builder.rs @@ -58,6 +58,7 @@ async fn main() -> eyre::Result<()> { } let config = builder::config_from_env(); + builder::init_metrics(); let init_span_guard = info_span!("builder initialization").entered(); info!(pkg_version = PKG_VERSION, git_commit = GIT_COMMIT, "starting builder"); diff --git a/src/lib.rs b/src/lib.rs index dfb7245d..ef8e73f0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,6 +16,9 @@ #[macro_use] mod macros; +/// Centralized metrics for the builder. +pub(crate) mod metrics; + /// Configuration for the Builder binary. pub mod config; @@ -31,7 +34,7 @@ pub mod tasks; /// Utilities. pub mod utils; -/// Test utilitites +#[cfg(any(test, feature = "test-utils"))] pub mod test_utils; // Anonymous imports suppress warnings about unused crate dependencies. @@ -72,6 +75,12 @@ pub fn config() -> &'static config::BuilderConfig { &CONFIG_AND_GUARD.get().expect("Builder config not initialized").config } +/// Register all metric descriptions. Call once at startup after the metrics +/// recorder is installed. +pub fn init_metrics() { + metrics::init(); +} + /// Get a reference to the Signet system constants from the global Builder /// configuration. /// diff --git a/src/metrics.rs b/src/metrics.rs new file mode 100644 index 00000000..14c57dbd --- /dev/null +++ b/src/metrics.rs @@ -0,0 +1,405 @@ +//! Centralized metrics for the builder. +//! +//! All metrics are defined and described here so that callers never need to +//! use bare `counter!` / `histogram!` macros directly. This prevents +//! metric-name typos and provides a single place to survey every metric the +//! builder emits. +use init4_bin_base::deps::metrics::{counter, describe_counter, describe_histogram, histogram}; +use std::sync::LazyLock; + +// --------------------------------------------------------------------------- +// Metric names and help text +// --------------------------------------------------------------------------- + +// -- Block building -- + +const BUILT_BLOCKS: &str = "signet.builder.built_blocks"; +const BUILT_BLOCKS_HELP: &str = "Number of blocks built by the simulator."; + +const BUILT_BLOCKS_TX_COUNT: &str = "signet.builder.built_blocks.tx_count"; +const BUILT_BLOCKS_TX_COUNT_HELP: &str = "Transaction count per built block."; + +// -- Cache polling -- + +const TX_NONCE_STALE: &str = "signet.builder.cache.tx_nonce_stale"; +const TX_NONCE_STALE_HELP: &str = "Transactions dropped due to stale nonce."; + +const TX_POLL_COUNT: &str = "signet.builder.cache.tx_poll_count"; +const TX_POLL_COUNT_HELP: &str = "Transaction cache poll attempts."; + +const TX_POLL_ERRORS: &str = "signet.builder.cache.tx_poll_errors"; +const TX_POLL_ERRORS_HELP: &str = "Transaction cache poll errors."; + +const TXS_FETCHED: &str = "signet.builder.cache.txs_fetched"; +const TXS_FETCHED_HELP: &str = "Transactions fetched per poll cycle."; + +const BUNDLE_POLL_COUNT: &str = "signet.builder.cache.bundle_poll_count"; +const BUNDLE_POLL_COUNT_HELP: &str = "Bundle cache poll attempts."; + +const BUNDLE_POLL_ERRORS: &str = "signet.builder.cache.bundle_poll_errors"; +const BUNDLE_POLL_ERRORS_HELP: &str = "Bundle cache poll errors."; + +const BUNDLES_FETCHED: &str = "signet.builder.cache.bundles_fetched"; +const BUNDLES_FETCHED_HELP: &str = "Bundles fetched per poll cycle."; + +// -- Cache ingestion -- + +const CACHE_CLEANS: &str = "signet.builder.cache.cache_cleans"; +const CACHE_CLEANS_HELP: &str = "Cache clean operations triggered by new block environments."; + +const BUNDLES_SKIPPED: &str = "signet.builder.cache.bundles_skipped"; +const BUNDLES_SKIPPED_HELP: &str = "Bundles skipped because they target a past block."; + +const BUNDLE_ADD_ERRORS: &str = "signet.builder.cache.bundle_add_errors"; +const BUNDLE_ADD_ERRORS_HELP: &str = "Errors adding bundles to the simulation cache."; + +const BUNDLES_INGESTED: &str = "signet.builder.cache.bundles_ingested"; +const BUNDLES_INGESTED_HELP: &str = "Bundles successfully ingested into the simulation cache."; + +const TXS_INGESTED: &str = "signet.builder.cache.txs_ingested"; +const TXS_INGESTED_HELP: &str = "Transactions successfully ingested into the simulation cache."; + +const TX_RECOVER_FAILURES: &str = "signet.builder.cache.tx_recover_failures"; +const TX_RECOVER_FAILURES_HELP: &str = "Transaction signature recovery failures."; + +// -- Tx monitoring -- + +const TX_MINE_TIME: &str = "signet.builder.tx_mine_time"; +const TX_MINE_TIME_HELP: &str = "Time in milliseconds for a submitted transaction to be mined."; + +const TX_SUCCEEDED: &str = "signet.builder.tx_succeeded"; +const TX_SUCCEEDED_HELP: &str = "Submitted transactions that succeeded on-chain."; + +const TX_REVERTED: &str = "signet.builder.tx_reverted"; +const TX_REVERTED_HELP: &str = "Submitted transactions that reverted on-chain."; + +const TX_NOT_MINED: &str = "signet.builder.tx_not_mined"; +const TX_NOT_MINED_HELP: &str = "Submitted transactions that were not mined within the timeout."; + +const RPC_ERROR: &str = "signet.builder.rpc_error"; +const RPC_ERROR_HELP: &str = "RPC errors encountered while watching submitted transactions."; + +// -- Quincey signing -- + +const QUINCEY_SIGNATURES: &str = "signet.builder.quincey_signatures"; +const QUINCEY_SIGNATURES_HELP: &str = "Successful Quincey signature responses."; + +const QUINCEY_SIGNATURE_FAILURES: &str = "signet.builder.quincey_signature_failures"; +const QUINCEY_SIGNATURE_FAILURES_HELP: &str = "Failed Quincey signature requests."; + +// -- Submit -- + +const TRANSACTIONS_PREPARED: &str = "signet.builder.submit.transactions_prepared"; +const TRANSACTIONS_PREPARED_HELP: &str = "Signed rollup block transactions ready for submission."; + +const EMPTY_BLOCKS: &str = "signet.builder.submit.empty_blocks"; +const EMPTY_BLOCKS_HELP: &str = "Empty blocks skipped during submission."; + +const BUNDLE_PREP_FAILURES: &str = "signet.builder.submit.bundle_prep_failures"; +const BUNDLE_PREP_FAILURES_HELP: &str = "Bundle preparation errors."; + +const RELAY_SUBMISSIONS: &str = "signet.builder.submit.relay_submissions"; +const RELAY_SUBMISSIONS_HELP: &str = "Relay submission attempts (incremented per relay)."; + +const DEADLINE_MISSED_BEFORE_SEND: &str = "signet.builder.submit.deadline_missed_before_send"; +const DEADLINE_MISSED_BEFORE_SEND_HELP: &str = + "Submissions that started after the slot deadline had already passed."; + +const RELAY_SUCCESSES: &str = "signet.builder.submit.relay_successes"; +const RELAY_SUCCESSES_HELP: &str = "Per-relay successful bundle submissions."; + +const RELAY_FAILURES: &str = "signet.builder.submit.relay_failures"; +const RELAY_FAILURES_HELP: &str = "Per-relay bundle submission errors."; + +const RELAY_TIMEOUTS: &str = "signet.builder.submit.relay_timeouts"; +const RELAY_TIMEOUTS_HELP: &str = "Per-relay bundle submission deadline timeouts."; + +const ALL_RELAYS_FAILED: &str = "signet.builder.submit.all_relays_failed"; +const ALL_RELAYS_FAILED_HELP: &str = "Bundles where no relay accepted the submission."; + +const BUNDLES_SUBMITTED: &str = "signet.builder.submit.bundles_submitted"; +const BUNDLES_SUBMITTED_HELP: &str = "Bundles accepted by at least one relay."; + +const DEADLINE_MISSED: &str = "signet.builder.submit.deadline_missed"; +const DEADLINE_MISSED_HELP: &str = "Bundles accepted by relays but after the slot deadline."; + +const DEADLINE_MET: &str = "signet.builder.submit.deadline_met"; +const DEADLINE_MET_HELP: &str = "Bundles accepted by relays within the slot deadline."; + +// -- Pylon -- + +const PYLON_SUBMISSION_FAILURES: &str = "signet.builder.pylon.submission_failures"; +const PYLON_SUBMISSION_FAILURES_HELP: &str = "Pylon sidecar submission errors."; + +const PYLON_SIDECARS_SUBMITTED: &str = "signet.builder.pylon.sidecars_submitted"; +const PYLON_SIDECARS_SUBMITTED_HELP: &str = "Successful Pylon sidecar submissions."; + +// --------------------------------------------------------------------------- +// Lazy description registration +// --------------------------------------------------------------------------- + +static DESCRIPTIONS: LazyLock<()> = LazyLock::new(|| { + // Block building + describe_counter!(BUILT_BLOCKS, BUILT_BLOCKS_HELP); + describe_histogram!(BUILT_BLOCKS_TX_COUNT, BUILT_BLOCKS_TX_COUNT_HELP); + + // Cache polling + describe_counter!(TX_NONCE_STALE, TX_NONCE_STALE_HELP); + describe_counter!(TX_POLL_COUNT, TX_POLL_COUNT_HELP); + describe_counter!(TX_POLL_ERRORS, TX_POLL_ERRORS_HELP); + describe_histogram!(TXS_FETCHED, TXS_FETCHED_HELP); + describe_counter!(BUNDLE_POLL_COUNT, BUNDLE_POLL_COUNT_HELP); + describe_counter!(BUNDLE_POLL_ERRORS, BUNDLE_POLL_ERRORS_HELP); + describe_histogram!(BUNDLES_FETCHED, BUNDLES_FETCHED_HELP); + + // Cache ingestion + describe_counter!(CACHE_CLEANS, CACHE_CLEANS_HELP); + describe_counter!(BUNDLES_SKIPPED, BUNDLES_SKIPPED_HELP); + describe_counter!(BUNDLE_ADD_ERRORS, BUNDLE_ADD_ERRORS_HELP); + describe_counter!(BUNDLES_INGESTED, BUNDLES_INGESTED_HELP); + describe_counter!(TXS_INGESTED, TXS_INGESTED_HELP); + describe_counter!(TX_RECOVER_FAILURES, TX_RECOVER_FAILURES_HELP); + + // Tx monitoring + describe_histogram!(TX_MINE_TIME, TX_MINE_TIME_HELP); + describe_counter!(TX_SUCCEEDED, TX_SUCCEEDED_HELP); + describe_counter!(TX_REVERTED, TX_REVERTED_HELP); + describe_counter!(TX_NOT_MINED, TX_NOT_MINED_HELP); + describe_counter!(RPC_ERROR, RPC_ERROR_HELP); + + // Quincey signing + describe_counter!(QUINCEY_SIGNATURES, QUINCEY_SIGNATURES_HELP); + describe_counter!(QUINCEY_SIGNATURE_FAILURES, QUINCEY_SIGNATURE_FAILURES_HELP); + + // Submit + describe_counter!(TRANSACTIONS_PREPARED, TRANSACTIONS_PREPARED_HELP); + describe_counter!(EMPTY_BLOCKS, EMPTY_BLOCKS_HELP); + describe_counter!(BUNDLE_PREP_FAILURES, BUNDLE_PREP_FAILURES_HELP); + describe_counter!(RELAY_SUBMISSIONS, RELAY_SUBMISSIONS_HELP); + describe_counter!(DEADLINE_MISSED_BEFORE_SEND, DEADLINE_MISSED_BEFORE_SEND_HELP); + describe_counter!(RELAY_SUCCESSES, RELAY_SUCCESSES_HELP); + describe_counter!(RELAY_FAILURES, RELAY_FAILURES_HELP); + describe_counter!(RELAY_TIMEOUTS, RELAY_TIMEOUTS_HELP); + describe_counter!(ALL_RELAYS_FAILED, ALL_RELAYS_FAILED_HELP); + describe_counter!(BUNDLES_SUBMITTED, BUNDLES_SUBMITTED_HELP); + describe_counter!(DEADLINE_MISSED, DEADLINE_MISSED_HELP); + describe_counter!(DEADLINE_MET, DEADLINE_MET_HELP); + + // Pylon + describe_counter!(PYLON_SUBMISSION_FAILURES, PYLON_SUBMISSION_FAILURES_HELP); + describe_counter!(PYLON_SIDECARS_SUBMITTED, PYLON_SIDECARS_SUBMITTED_HELP); +}); + +/// Register all metric descriptions. Call once at startup (e.g. after +/// initializing the metrics recorder). +pub(crate) fn init() { + LazyLock::force(&DESCRIPTIONS); +} + +// --------------------------------------------------------------------------- +// Public API -- Block building +// --------------------------------------------------------------------------- + +/// Increment the built-blocks counter. +pub(crate) fn inc_built_blocks() { + counter!(BUILT_BLOCKS).increment(1); +} + +/// Record the transaction count of a built block. +pub(crate) fn record_built_block_tx_count(count: usize) { + histogram!(BUILT_BLOCKS_TX_COUNT).record(count as f64); +} + +// --------------------------------------------------------------------------- +// Public API -- Cache polling +// --------------------------------------------------------------------------- + +/// Increment the stale-nonce transaction counter. +pub(crate) fn inc_tx_nonce_stale() { + counter!(TX_NONCE_STALE).increment(1); +} + +/// Increment the transaction poll attempt counter. +pub(crate) fn inc_tx_poll_count() { + counter!(TX_POLL_COUNT).increment(1); +} + +/// Increment the transaction poll error counter. +pub(crate) fn inc_tx_poll_errors() { + counter!(TX_POLL_ERRORS).increment(1); +} + +/// Record the number of transactions fetched in a poll cycle. +pub(crate) fn record_txs_fetched(count: usize) { + histogram!(TXS_FETCHED).record(count as f64); +} + +/// Increment the bundle poll attempt counter. +pub(crate) fn inc_bundle_poll_count() { + counter!(BUNDLE_POLL_COUNT).increment(1); +} + +/// Increment the bundle poll error counter. +pub(crate) fn inc_bundle_poll_errors() { + counter!(BUNDLE_POLL_ERRORS).increment(1); +} + +/// Record the number of bundles fetched in a poll cycle. +pub(crate) fn record_bundles_fetched(count: usize) { + histogram!(BUNDLES_FETCHED).record(count as f64); +} + +// --------------------------------------------------------------------------- +// Public API -- Cache ingestion +// --------------------------------------------------------------------------- + +/// Increment the cache-clean counter. +pub(crate) fn inc_cache_cleans() { + counter!(CACHE_CLEANS).increment(1); +} + +/// Increment the skipped-bundle counter (bundle targets a past block). +pub(crate) fn inc_bundles_skipped() { + counter!(BUNDLES_SKIPPED).increment(1); +} + +/// Increment the bundle-add-error counter. +pub(crate) fn inc_bundle_add_errors() { + counter!(BUNDLE_ADD_ERRORS).increment(1); +} + +/// Increment the bundles-ingested counter. +pub(crate) fn inc_bundles_ingested() { + counter!(BUNDLES_INGESTED).increment(1); +} + +/// Increment the transactions-ingested counter. +pub(crate) fn inc_txs_ingested() { + counter!(TXS_INGESTED).increment(1); +} + +/// Increment the transaction signature recovery failure counter. +pub(crate) fn inc_tx_recover_failures() { + counter!(TX_RECOVER_FAILURES).increment(1); +} + +// --------------------------------------------------------------------------- +// Public API -- Tx monitoring +// --------------------------------------------------------------------------- + +/// Record the time for a submitted transaction to be mined. +pub(crate) fn record_tx_mine_time(duration: std::time::Duration) { + histogram!(TX_MINE_TIME).record(duration.as_millis() as f64); +} + +/// Increment the succeeded-transaction counter. +pub(crate) fn inc_tx_succeeded() { + counter!(TX_SUCCEEDED).increment(1); +} + +/// Increment the reverted-transaction counter. +pub(crate) fn inc_tx_reverted() { + counter!(TX_REVERTED).increment(1); +} + +/// Increment the not-mined-transaction counter. +pub(crate) fn inc_tx_not_mined() { + counter!(TX_NOT_MINED).increment(1); +} + +/// Increment the RPC error counter. +pub(crate) fn inc_rpc_error() { + counter!(RPC_ERROR).increment(1); +} + +// --------------------------------------------------------------------------- +// Public API -- Quincey signing +// --------------------------------------------------------------------------- + +/// Increment the successful Quincey signature counter. +pub(crate) fn inc_quincey_signatures() { + counter!(QUINCEY_SIGNATURES).increment(1); +} + +/// Increment the failed Quincey signature counter. +pub(crate) fn inc_quincey_signature_failures() { + counter!(QUINCEY_SIGNATURE_FAILURES).increment(1); +} + +// --------------------------------------------------------------------------- +// Public API -- Submit +// --------------------------------------------------------------------------- + +/// Increment the transactions-prepared counter. +pub(crate) fn inc_transactions_prepared() { + counter!(TRANSACTIONS_PREPARED).increment(1); +} + +/// Increment the empty-blocks counter. +pub(crate) fn inc_empty_blocks() { + counter!(EMPTY_BLOCKS).increment(1); +} + +/// Increment the bundle-prep-failures counter. +pub(crate) fn inc_bundle_prep_failures() { + counter!(BUNDLE_PREP_FAILURES).increment(1); +} + +/// Increment the relay submission attempt counter by `count`. +pub(crate) fn inc_relay_submissions(count: usize) { + counter!(RELAY_SUBMISSIONS).increment(count as u64); +} + +/// Increment the deadline-missed-before-send counter. +pub(crate) fn inc_deadline_missed_before_send() { + counter!(DEADLINE_MISSED_BEFORE_SEND).increment(1); +} + +/// Increment the per-relay success counter. +pub(crate) fn inc_relay_successes() { + counter!(RELAY_SUCCESSES).increment(1); +} + +/// Increment the per-relay failure counter. +pub(crate) fn inc_relay_failures() { + counter!(RELAY_FAILURES).increment(1); +} + +/// Increment the per-relay timeout counter. +pub(crate) fn inc_relay_timeouts() { + counter!(RELAY_TIMEOUTS).increment(1); +} + +/// Increment the all-relays-failed counter. +pub(crate) fn inc_all_relays_failed() { + counter!(ALL_RELAYS_FAILED).increment(1); +} + +/// Increment the bundles-submitted counter. +pub(crate) fn inc_bundles_submitted() { + counter!(BUNDLES_SUBMITTED).increment(1); +} + +/// Increment the deadline-missed counter. +pub(crate) fn inc_deadline_missed() { + counter!(DEADLINE_MISSED).increment(1); +} + +/// Increment the deadline-met counter. +pub(crate) fn inc_deadline_met() { + counter!(DEADLINE_MET).increment(1); +} + +// --------------------------------------------------------------------------- +// Public API -- Pylon +// --------------------------------------------------------------------------- + +/// Increment the Pylon submission failure counter. +pub(crate) fn inc_pylon_submission_failures() { + counter!(PYLON_SUBMISSION_FAILURES).increment(1); +} + +/// Increment the Pylon sidecars-submitted counter. +pub(crate) fn inc_pylon_sidecars_submitted() { + counter!(PYLON_SIDECARS_SUBMITTED).increment(1); +} diff --git a/src/tasks/block/sim.rs b/src/tasks/block/sim.rs index 4fd32e91..f6edc5c1 100644 --- a/src/tasks/block/sim.rs +++ b/src/tasks/block/sim.rs @@ -6,10 +6,7 @@ use crate::{ tasks::env::SimEnv, }; use alloy::{consensus::Header, eips::Encodable2718, primitives::Bytes}; -use init4_bin_base::{ - deps::metrics::{counter, histogram}, - utils::calc::SlotCalculator, -}; +use init4_bin_base::utils::calc::SlotCalculator; use signet_sim::{BlockBuild, BuiltBlock, SimCache}; use signet_types::constants::SignetSystemConstants; use std::time::{Duration, Instant}; @@ -168,8 +165,8 @@ impl SimulatorTask { block_number = built_block.block_number(), "block simulation completed", ); - counter!("signet.builder.built_blocks").increment(1); - histogram!("signet.builder.built_blocks.tx_count").record(built_block.tx_count() as u32); + crate::metrics::inc_built_blocks(); + crate::metrics::record_built_block_tx_count(built_block.tx_count()); Ok(built_block) } diff --git a/src/tasks/cache/bundle.rs b/src/tasks/cache/bundle.rs index 8b6a4416..4397046c 100644 --- a/src/tasks/cache/bundle.rs +++ b/src/tasks/cache/bundle.rs @@ -1,17 +1,15 @@ //! Bundler service responsible for fetching bundles and sending them to the simulator. use crate::config::BuilderConfig; use futures_util::{TryFutureExt, TryStreamExt}; -use init4_bin_base::{ - deps::metrics::{counter, histogram}, - perms::tx_cache::{BuilderTxCache, BuilderTxCacheError}, -}; +use init4_bin_base::perms::tx_cache::{BuilderTxCache, BuilderTxCacheError}; +use signet_sim::{ProviderStateSource, SimItemValidity, check_bundle_tx_list}; use signet_tx_cache::{TxCacheError, types::CachedBundle}; use tokio::{ sync::mpsc::{UnboundedReceiver, UnboundedSender, unbounded_channel}, task::JoinHandle, time::{self, Duration}, }; -use tracing::{Instrument, debug, trace, trace_span, warn}; +use tracing::{Instrument, debug_span, trace, trace_span, warn}; /// Poll interval for the bundle poller in milliseconds. const POLL_INTERVAL_MS: u64 = 1000; @@ -59,6 +57,63 @@ impl BundlePoller { self.tx_cache.stream_bundles().try_collect().await } + /// Spawns a tokio task to check the validity of all host transactions in a + /// bundle before sending it to the cache task via the outbound channel. + /// + /// Uses [`check_bundle_tx_list`] from `signet-sim` to validate host tx nonces + /// and balance against the host chain. Drops bundles that are not currently valid. + fn spawn_check_bundle_nonces(bundle: CachedBundle, outbound: UnboundedSender) { + let span = debug_span!("check_bundle_nonces", bundle_id = %bundle.id); + tokio::spawn(async move { + let recovered = match bundle.bundle.try_to_recovered() { + Ok(recovered) => recovered, + Err(error) => { + span_debug!(span, ?error, "Failed to recover bundle, dropping"); + return; + } + }; + + if recovered.host_txs().is_empty() { + let _ = outbound.send(bundle).inspect_err(|_| { + span_debug!(span, "Outbound channel closed"); + }); + return; + } + + // Check if the receiver is still alive before doing expensive nonce validation over + // the network. + if outbound.is_closed() { + span_debug!(span, "Outbound channel closed, skipping nonce validation"); + return; + } + + let Ok(host_provider) = + crate::config().connect_host_provider().instrument(span.clone()).await + else { + span_debug!(span, "Failed to connect to host provider, dropping bundle"); + return; + }; + + let source = ProviderStateSource(host_provider); + match check_bundle_tx_list(recovered.host_tx_reqs(), &source) + .instrument(span.clone()) + .await + { + Ok(SimItemValidity::Now) | Ok(SimItemValidity::Future) => { + let _ = outbound.send(bundle).inspect_err(|_| { + span_debug!(span, "Outbound channel closed"); + }); + } + Ok(SimItemValidity::Never) => { + span_debug!(span, "Dropping bundle: host txs will never be valid"); + } + Err(error) => { + span_debug!(span, %error, "Failed to check bundle validity, dropping"); + } + } + }); + } + async fn task_future(self, outbound: UnboundedSender) { loop { let span = trace_span!("BundlePoller::loop", url = %self.config.tx_pool_url); @@ -70,7 +125,7 @@ impl BundlePoller { break; } - counter!("signet.builder.cache.bundle_poll_count").increment(1); + crate::metrics::inc_bundle_poll_count(); let Ok(bundles) = self .check_bundle_cache() .inspect_err(|error| match error { @@ -78,7 +133,7 @@ impl BundlePoller { trace!("Not our slot to fetch bundles"); } _ => { - counter!("signet.builder.cache.bundle_poll_errors").increment(1); + crate::metrics::inc_bundle_poll_errors(); warn!(%error, "Failed to fetch bundles from tx-cache"); } }) @@ -91,13 +146,10 @@ impl BundlePoller { { let _guard = span.entered(); - histogram!("signet.builder.cache.bundles_fetched").record(bundles.len() as f64); + crate::metrics::record_bundles_fetched(bundles.len()); trace!(count = bundles.len(), "fetched bundles from tx-cache"); for bundle in bundles { - if let Err(err) = outbound.send(bundle) { - debug!(?err, "Failed to send bundle - channel is dropped"); - break; - } + Self::spawn_check_bundle_nonces(bundle, outbound.clone()); } } diff --git a/src/tasks/cache/task.rs b/src/tasks/cache/task.rs index aec1a22e..927ff50d 100644 --- a/src/tasks/cache/task.rs +++ b/src/tasks/cache/task.rs @@ -1,6 +1,5 @@ use crate::tasks::{cache::tx::ReceivedTx, env::SimEnv}; use alloy::consensus::transaction::SignerRecoverable; -use init4_bin_base::deps::metrics::counter; use signet_sim::SimCache; use signet_tx_cache::types::CachedBundle; use tokio::{ @@ -65,7 +64,7 @@ impl CacheTask { cache.clean( sim_env.number.to(), sim_env.timestamp.to() ); - counter!("signet.builder.cache.cache_cleans").increment(1); + crate::metrics::inc_cache_cleans(); } } Some(bundle) = self.bundles.recv() => { @@ -85,7 +84,7 @@ impl CacheTask { %bundle.id, "skipping bundle insert" ); - counter!("signet.builder.cache.bundles_skipped").increment(1); + crate::metrics::inc_bundles_skipped(); summary.bundles_skipped_stale += 1; continue; } @@ -93,11 +92,11 @@ impl CacheTask { let res = cache.add_bundle(bundle.bundle, basefee); // Skip bundles that fail to be added to the cache if let Err(e) = res { - counter!("signet.builder.cache.bundle_add_errors").increment(1); + crate::metrics::inc_bundle_add_errors(); debug!(?e, "Failed to add bundle to cache"); continue; } - counter!("signet.builder.cache.bundles_ingested").increment(1); + crate::metrics::inc_bundles_ingested(); summary.bundles_accepted += 1; } Some(received_txn) = self.txns.recv() => { @@ -110,11 +109,11 @@ impl CacheTask { match txn.try_into_recovered() { Ok(recovered_tx) => { cache.add_tx(recovered_tx, basefee); - counter!("signet.builder.cache.txs_ingested").increment(1); + crate::metrics::inc_txs_ingested(); summary.txs_accepted += 1; } Err(_) => { - counter!("signet.builder.cache.tx_recover_failures").increment(1); + crate::metrics::inc_tx_recover_failures(); debug!("Failed to recover transaction signature"); } } diff --git a/src/tasks/cache/tx.rs b/src/tasks/cache/tx.rs index def47090..25a481d9 100644 --- a/src/tasks/cache/tx.rs +++ b/src/tasks/cache/tx.rs @@ -5,7 +5,6 @@ use alloy::{ providers::Provider, }; use futures_util::{TryFutureExt, TryStreamExt}; -use init4_bin_base::deps::metrics::{counter, histogram}; use signet_tx_cache::{TxCache, TxCacheError}; use std::time::Duration; use tokio::{sync::mpsc, task::JoinHandle, time}; @@ -82,7 +81,7 @@ impl TxPoller { }; if tx.nonce() < tx_count { - counter!("signet.builder.cache.tx_nonce_stale").increment(1); + crate::metrics::inc_tx_nonce_stale(); if outbound.send(ReceivedTx::StaleNonce).is_err() { span_warn!(span, "Outbound channel closed, stopping NonceChecker task."); } @@ -112,18 +111,18 @@ impl TxPoller { break; } - counter!("signet.builder.cache.tx_poll_count").increment(1); + crate::metrics::inc_tx_poll_count(); if let Ok(transactions) = self .check_tx_cache() .inspect_err(|error| { - counter!("signet.builder.cache.tx_poll_errors").increment(1); + crate::metrics::inc_tx_poll_errors(); debug!(%error, "Error fetching transactions"); }) .instrument(span.clone()) .await { let _guard = span.entered(); - histogram!("signet.builder.cache.txs_fetched").record(transactions.len() as f64); + crate::metrics::record_txs_fetched(transactions.len()); trace!(count = transactions.len(), "found transactions"); for tx in transactions.into_iter() { self.spawn_check_nonce(tx, outbound.clone()); diff --git a/src/tasks/metrics.rs b/src/tasks/metrics.rs index 699978c8..702c2deb 100644 --- a/src/tasks/metrics.rs +++ b/src/tasks/metrics.rs @@ -3,7 +3,6 @@ use alloy::{ primitives::TxHash, providers::{PendingTransactionBuilder, PendingTransactionError, Provider as _, WatchTxError}, }; -use init4_bin_base::deps::metrics::{counter, histogram}; use std::time::{Duration, Instant}; use tokio::{sync::mpsc, task::JoinHandle}; use tracing::{Instrument, debug, error, info_span}; @@ -50,24 +49,24 @@ impl MetricsTask { Ok(receipt) => { // record how long it took to mine the transaction // potential improvement: use the block timestamp to calculate the time elapsed - histogram!("metrics.tx_mine_time").record(start.elapsed().as_millis() as f64); + crate::metrics::record_tx_mine_time(start.elapsed()); // log whether the transaction reverted if receipt.status() { - counter!("metrics.tx_succeeded").increment(1); + crate::metrics::inc_tx_succeeded(); debug!("tx succeeded"); } else { - counter!("metrics.tx_reverted").increment(1); + crate::metrics::inc_tx_reverted(); debug!("tx reverted"); } } Err(PendingTransactionError::TxWatcher(WatchTxError::Timeout)) => { // log that the transaction timed out - counter!("metrics.tx_not_mined").increment(1); + crate::metrics::inc_tx_not_mined(); debug!("tx not mined"); } Err(e) => { - counter!("metrics.rpc_error").increment(1); + crate::metrics::inc_rpc_error(); error!(error = ?e, "rpc error"); } } diff --git a/src/tasks/submit/prep.rs b/src/tasks/submit/prep.rs index 7e97ecda..265c1cdb 100644 --- a/src/tasks/submit/prep.rs +++ b/src/tasks/submit/prep.rs @@ -14,7 +14,6 @@ use alloy::{ sol_types::SolCall, }; use futures_util::FutureExt; -use init4_bin_base::deps::metrics::counter; use signet_sim::BuiltBlock; use signet_types::{SignRequest, SignResponse}; use signet_zenith::Zenith; @@ -83,9 +82,9 @@ impl<'a> SubmitPrep<'a> { self.quincey .get_signature(sig_request) .await - .inspect(|_| counter!("signet.builder.quincey_signatures").increment(1)) + .inspect(|_| crate::metrics::inc_quincey_signatures()) .inspect_err(|err| { - counter!("signet.builder.quincey_signature_failures").increment(1); + crate::metrics::inc_quincey_signature_failures(); if let QuinceyError::NotOurSlot = err { warn!("Quincey indicated not our slot to sign"); } else { diff --git a/src/tasks/submit/task.rs b/src/tasks/submit/task.rs index a0911155..6721a27b 100644 --- a/src/tasks/submit/task.rs +++ b/src/tasks/submit/task.rs @@ -31,7 +31,7 @@ use alloy::{ rpc::types::mev::EthSendBundle, }; use futures_util::stream::{FuturesUnordered, StreamExt}; -use init4_bin_base::{deps::metrics::counter, utils::signer::LocalOrAws}; +use init4_bin_base::utils::signer::LocalOrAws; use std::{ sync::Arc, time::{Duration, Instant}, @@ -159,7 +159,7 @@ impl SubmitTask { /// Tracks the outbound transaction hash and increments submission metrics. fn track_outbound_tx(&self, envelope: &TxEnvelope) { - counter!("signet.builder.submit.transactions_prepared").increment(1); + crate::metrics::inc_transactions_prepared(); let hash = *envelope.tx_hash(); if self.outbound.send(hash).is_err() { debug!("outbound channel closed, could not track tx hash"); @@ -189,7 +189,7 @@ impl SubmitTask { let deadline = self.calculate_submit_deadline(); if sim_result.block.is_empty() { - counter!("signet.builder.submit.empty_blocks").increment(1); + crate::metrics::inc_empty_blocks(); span_debug!(span, "received empty block - skipping"); continue; } @@ -198,7 +198,7 @@ impl SubmitTask { let bundle = match self.prepare(&sim_result).instrument(span.clone()).await { Ok(bundle) => bundle, Err(error) => { - counter!("signet.builder.submit.bundle_prep_failures").increment(1); + crate::metrics::inc_bundle_prep_failures(); span_debug!(span, %error, "bundle preparation failed"); continue; } @@ -333,13 +333,13 @@ impl Submission { /// results from faster relays. async fn submit_to_relays(&self) -> Vec { let n_relays = self.relays.len(); - counter!("signet.builder.submit.relay_submissions").increment(n_relays as u64); + crate::metrics::inc_relay_submissions(n_relays); let now = Instant::now(); let timeout_dur = if now > self.deadline { let late_by = now.duration_since(self.deadline); warn!(?late_by, "submission started AFTER deadline; attempting anyway"); - counter!("signet.builder.submit.deadline_missed_before_send").increment(1); + crate::metrics::inc_deadline_missed_before_send(); LATE_SUBMISSION_TIMEOUT } else { self.deadline.duration_since(now) @@ -375,33 +375,33 @@ impl Submission { for outcome in outcomes { match outcome { RelayOutcome::Success => { - counter!("signet.builder.submit.relay_successes").increment(1); + crate::metrics::inc_relay_successes(); successes += 1; } RelayOutcome::Failure => { - counter!("signet.builder.submit.relay_failures").increment(1); + crate::metrics::inc_relay_failures(); failures += 1; } RelayOutcome::Timeout => { - counter!("signet.builder.submit.relay_timeouts").increment(1); + crate::metrics::inc_relay_timeouts(); timeouts += 1; } } } if successes == 0 { - counter!("signet.builder.submit.all_relays_failed").increment(1); + crate::metrics::inc_all_relays_failed(); error!( failures, timeouts, n_relays, "all relay submissions failed - bundle may not land" ); } else { - counter!("signet.builder.submit.bundles_submitted").increment(1); + crate::metrics::inc_bundles_submitted(); if Instant::now() > self.deadline { - counter!("signet.builder.submit.deadline_missed").increment(1); + crate::metrics::inc_deadline_missed(); warn!(successes, failures, timeouts, "bundle accepted by relays AFTER deadline"); } else { - counter!("signet.builder.submit.deadline_met").increment(1); + crate::metrics::inc_deadline_met(); info!( successes, failures, timeouts, n_relays, "bundle submitted to relays within deadline" @@ -416,12 +416,12 @@ impl Submission { /// submission failed or timed out. async fn submit_to_pylon(&self) { if let Err(err) = self.pylon.post_blob_tx(self.block_tx.clone()).await { - counter!("signet.builder.pylon.submission_failures").increment(1); + crate::metrics::inc_pylon_submission_failures(); warn!(%err, "pylon submission failed"); return; } - counter!("signet.builder.pylon.sidecars_submitted").increment(1); + crate::metrics::inc_pylon_sidecars_submitted(); debug!("posted sidecar to pylon"); } } diff --git a/src/test_utils/block.rs b/src/test_utils/block.rs new file mode 100644 index 00000000..872439a5 --- /dev/null +++ b/src/test_utils/block.rs @@ -0,0 +1,186 @@ +//! Test utilities for block building and simulation. +//! This module provides builders for creating `BlockBuild` instances +//! for testing block simulation. + +use super::{ + db::{TestDb, TestStateSource}, + env::{TestHostEnv, TestRollupEnv, TestSimEnvBuilder}, +}; +use signet_sim::{BlockBuild, BuiltBlock, SimCache}; +use std::time::Duration; +use tokio::time::Instant; +use trevm::revm::inspector::NoOpInspector; + +/// Test block builder type using in-memory databases. +pub type TestBlockBuild = + BlockBuild; + +/// Builder for creating test `BlockBuild` instances. +/// Configures all the parameters needed for block simulation +/// and provides sensible defaults for testing scenarios. +#[derive(Debug)] +pub struct TestBlockBuildBuilder { + /// The test environment configuration for the block build. + env: TestBlockBuildEnv, + /// The simulation cache to use for the block build. + sim_cache: SimCache, + /// The duration from now until the block build should finish. + deadline_duration: Duration, + /// The concurrency limit for parallel simulation. + concurrency_limit: usize, + /// The maximum gas limit for the rollup block. + max_gas: u64, + /// The maximum gas limit for host transactions. + max_host_gas: u64, +} + +/// Internal enum to manage the environment configuration for the block build. +#[derive(Debug)] +#[allow(clippy::large_enum_variant)] +enum TestBlockBuildEnv { + /// A builder that will create the environments when `into_block_build()` is called. + Builder(TestSimEnvBuilder), + /// A pair of already built environments to use directly. + Built { rollup: TestRollupEnv, host: TestHostEnv }, +} + +impl Default for TestBlockBuildBuilder { + fn default() -> Self { + Self::new() + } +} + +impl TestBlockBuildBuilder { + /// Create a new test block build builder with test-focused defaults. + /// Default values: + /// - Deadline: 2 seconds + /// - Concurrency limit: 4 + /// - Max gas: 3,000,000,000 (3 billion) + /// - Max host gas: 24,000,000 + pub fn new() -> Self { + Self { + // Default to building fresh test environments unless the caller injects a pair. + env: TestBlockBuildEnv::Builder(TestSimEnvBuilder::new()), + sim_cache: SimCache::new(), + deadline_duration: Duration::from_secs(2), + concurrency_limit: 4, + max_gas: 3_000_000_000, + max_host_gas: 24_000_000, + } + } + + /// Set the simulation environment builder. + /// The environments will be built from this builder when `into_block_build()` is called. + pub fn with_sim_env_builder(mut self, builder: TestSimEnvBuilder) -> Self { + self.env = TestBlockBuildEnv::Builder(builder); + self + } + + /// Set both environments directly so the block build uses a consistent pair. + pub fn with_envs(mut self, rollup: TestRollupEnv, host: TestHostEnv) -> Self { + self.env = TestBlockBuildEnv::Built { rollup, host }; + self + } + + /// Set the simulation cache. + pub fn with_cache(mut self, cache: SimCache) -> Self { + self.sim_cache = cache; + self + } + + /// Set the deadline duration from now. + pub const fn with_deadline(mut self, duration: Duration) -> Self { + self.deadline_duration = duration; + self + } + + /// Set the concurrency limit for parallel simulation. + pub const fn with_concurrency(mut self, limit: usize) -> Self { + self.concurrency_limit = limit; + self + } + + /// Set the maximum gas limit for the rollup block. + pub const fn with_max_gas(mut self, gas: u64) -> Self { + self.max_gas = gas; + self + } + + /// Set the maximum gas limit for host transactions. + pub const fn with_max_host_gas(mut self, gas: u64) -> Self { + self.max_host_gas = gas; + self + } + + /// Build the test `BlockBuild` instance. + /// This creates a `BlockBuild` ready for simulation. + /// Call `.build().await` on the result to execute the simulation and get a `BuiltBlock`. + pub fn build(self) -> TestBlockBuild { + let (rollup_env, host_env, rollup_db, host_db) = match self.env { + TestBlockBuildEnv::Builder(builder) => { + let rollup_db = builder.rollup_db(); + let host_db = builder.host_db(); + let (rollup, host) = builder.build(); + (rollup, host, rollup_db, host_db) + } + TestBlockBuildEnv::Built { rollup, host } => { + let rollup_db = rollup.db().db.clone(); + let host_db = host.db().db.clone(); + (rollup, host, rollup_db, host_db) + } + }; + + let ru_state_source = TestStateSource::new(rollup_db); + let host_state_source = TestStateSource::new(host_db); + + // Convert the relative deadline into the absolute instant expected by `BlockBuild`. + let finish_by = Instant::now() + self.deadline_duration; + + BlockBuild::new( + rollup_env, + host_env, + finish_by, + self.concurrency_limit, + self.sim_cache, + self.max_gas, + self.max_host_gas, + ru_state_source, + host_state_source, + ) + } +} + +/// Convenience function to quickly build a block with a cache and optional configuration. +/// This is useful for simple test cases where you just want to simulate +/// some transactions quickly. +pub async fn quick_build_block(cache: SimCache, deadline: Duration) -> BuiltBlock { + TestBlockBuildBuilder::new().with_cache(cache).with_deadline(deadline).build().build().await +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_block_build_builder_defaults() { + let builder = TestBlockBuildBuilder::new(); + assert_eq!(builder.deadline_duration, Duration::from_secs(2)); + assert_eq!(builder.concurrency_limit, 4); + assert_eq!(builder.max_gas, 3_000_000_000); + assert_eq!(builder.max_host_gas, 24_000_000); + } + + #[test] + fn test_block_build_builder_custom_values() { + let builder = TestBlockBuildBuilder::new() + .with_deadline(Duration::from_secs(5)) + .with_concurrency(8) + .with_max_gas(1_000_000_000) + .with_max_host_gas(10_000_000); + + assert_eq!(builder.deadline_duration, Duration::from_secs(5)); + assert_eq!(builder.concurrency_limit, 8); + assert_eq!(builder.max_gas, 1_000_000_000); + assert_eq!(builder.max_host_gas, 10_000_000); + } +} diff --git a/src/test_utils/db.rs b/src/test_utils/db.rs new file mode 100644 index 00000000..90bfe6ef --- /dev/null +++ b/src/test_utils/db.rs @@ -0,0 +1,186 @@ +//! Test database utilities for in-memory EVM state. +//! This module provides an in-memory database implementation that can be used +//! for testing block simulation without requiring network access. + +use alloy::primitives::{Address, B256, U256}; +use signet_sim::{AcctInfo, StateSource}; +use std::time::Duration; +use trevm::revm::{ + bytecode::Bytecode, + database::{CacheDB, EmptyDB}, + database_interface::DatabaseRef, + primitives::{StorageKey, StorageValue}, + state::AccountInfo, +}; + +/// Mirrors the production `CacheDB` stack: the outer [`CacheDB`] starts +/// empty and caches on the mutable `Database` path, while the inner [`LatencyDb`] +/// holds all state in-memory and applies configurable sleep on every read to +/// simulate RPC round-trip cost. +pub type TestDb = CacheDB; + +/// A [`StateSource`] for testing backed by an in-memory [`TestDb`]. +/// Returns actual account info (nonce, balance) from the database, +/// which is required for preflight validity checks during simulation. +#[derive(Debug, Clone)] +pub struct TestStateSource { + db: TestDb, +} + +impl TestStateSource { + /// Create a new `TestStateSource` backed by the given database. + pub const fn new(db: TestDb) -> Self { + Self { db } + } +} + +impl StateSource for TestStateSource { + type Error = std::convert::Infallible; + + async fn account_details(&self, address: &Address) -> Result { + match self.db.basic_ref(*address) { + Ok(Some(info)) => Ok(AcctInfo { + nonce: info.nonce, + balance: info.balance, + has_code: info.code_hash != trevm::revm::primitives::KECCAK_EMPTY, + }), + Ok(None) => Ok(AcctInfo { nonce: 0, balance: U256::ZERO, has_code: false }), + Err(_) => unreachable!("error type is infallible"), + } + } +} + +/// Builder for creating pre-populated test databases. +/// Use this builder to set up blockchain state (accounts, contracts, storage) +/// before running simulations. +#[derive(Debug)] +pub struct TestDbBuilder { + latency_db: LatencyDb, +} + +impl Default for TestDbBuilder { + fn default() -> Self { + Self::new() + } +} + +impl TestDbBuilder { + /// Create a new empty test database builder. + pub fn new() -> Self { + Self { latency_db: LatencyDb::default() } + } + + /// Add an account with the specified balance and nonce. + /// + /// # Arguments + /// + /// * `address` - The account address + /// * `balance` - The account balance in wei + /// * `nonce` - The account nonce (transaction count) + pub fn with_account(mut self, address: Address, balance: U256, nonce: u64) -> Self { + self.latency_db + .in_mem_db + .insert_account_info(address, AccountInfo { balance, nonce, ..Default::default() }); + self + } + + /// Set a storage slot for an account. + /// + /// # Arguments + /// + /// * `address` - The account address + /// * `slot` - The storage slot index + /// * `value` - The value to store + pub fn with_storage(mut self, address: Address, slot: U256, value: U256) -> Self { + // Ensure the account exists before setting storage + if !self.latency_db.in_mem_db.cache.accounts.contains_key(&address) { + self.latency_db.in_mem_db.insert_account_info(address, AccountInfo::default()); + } + let _ = self.latency_db.in_mem_db.insert_account_storage(address, slot, value); + self + } + + /// Insert a block hash for a specific block number. + /// + /// This is useful for testing contracts that use the BLOCKHASH opcode. + /// + /// # Arguments + /// + /// * `number` - The block number + /// * `hash` - The block hash + pub fn with_block_hash(mut self, number: u64, hash: B256) -> Self { + self.latency_db.in_mem_db.cache.block_hashes.insert(U256::from(number), hash); + self + } + + /// Add a contract account with the specified bytecode. + pub fn with_contract(mut self, address: Address, bytecode: Bytecode) -> Self { + self.latency_db.in_mem_db.insert_account_info( + address, + AccountInfo { code: Some(bytecode), ..Default::default() }, + ); + self + } + + /// Apply the given latency to every call to the wrapped, in-memory DB. + pub const fn with_latency(mut self, latency: Duration) -> Self { + self.latency_db.latency = Some(latency); + self + } + + /// Build the test database. + pub fn build(self) -> TestDb { + CacheDB::new(self.latency_db) + } +} + +/// In-memory database with configurable per-access latency. +/// +/// Holds all state (accounts, storage, block hashes) in plain hash maps and +/// calls [`std::thread::sleep`] before every read through [`DatabaseRef`]. +/// This simulates production conditions where the backing store (e.g. `AlloyDB`) +/// sends an RPC for each state lookup. +/// +/// Intended to be wrapped in a [`CacheDB`] so that the `CacheDB` provides real +/// mutable-path caching while this type represents the slow backing store behind it. +#[derive(Debug, Clone, Default)] +pub struct LatencyDb { + in_mem_db: CacheDB, + latency: Option, +} + +impl LatencyDb { + fn sleep(&self) { + if let Some(latency) = self.latency { + std::thread::sleep(latency); + } + } +} + +impl DatabaseRef for LatencyDb { + type Error = core::convert::Infallible; + + fn basic_ref(&self, address: Address) -> Result, Self::Error> { + self.sleep(); + Ok(self.in_mem_db.basic_ref(address).unwrap()) + } + + fn code_by_hash_ref(&self, code_hash: B256) -> Result { + self.sleep(); + Ok(self.in_mem_db.code_by_hash_ref(code_hash).unwrap()) + } + + fn storage_ref( + &self, + address: Address, + index: StorageKey, + ) -> Result { + self.sleep(); + Ok(self.in_mem_db.storage_ref(address, index).unwrap()) + } + + fn block_hash_ref(&self, number: u64) -> Result { + self.sleep(); + Ok(self.in_mem_db.block_hash_ref(number).unwrap()) + } +} diff --git a/src/test_utils/env.rs b/src/test_utils/env.rs new file mode 100644 index 00000000..730d1ddb --- /dev/null +++ b/src/test_utils/env.rs @@ -0,0 +1,180 @@ +//! Test simulation environment utilities. +//! This module provides builders for creating `RollupEnv` and `HostEnv` +//! instances with in-memory databases for offline testing. + +use super::db::{TestDb, TestDbBuilder}; +use crate::tasks::block::cfg::SignetCfgEnv; +use alloy::primitives::{Address, B256, U256}; +use signet_constants::SignetSystemConstants; +use signet_sim::{HostEnv, RollupEnv}; +use trevm::revm::{ + context::BlockEnv, context_interface::block::BlobExcessGasAndPrice, inspector::NoOpInspector, +}; + +/// Test rollup environment using in-memory database. +pub type TestRollupEnv = RollupEnv; + +/// Test host environment using in-memory database. +pub type TestHostEnv = HostEnv; + +/// Builder for creating test simulation environments. +/// This allows you to configure both rollup and host environments +/// with pre-populated in-memory databases for testing block simulation +/// without network access. +#[derive(Debug)] +pub struct TestSimEnvBuilder { + /// The in-memory database for the rollup environment. + rollup_db: TestDb, + /// The in-memory database for the host environment. + host_db: TestDb, + /// The block environment configuration for the rollup. + rollup_block_env: BlockEnv, + /// The block environment configuration for the host. + host_block_env: BlockEnv, + /// The unified system constants to use for both environments. + constants: SignetSystemConstants, +} + +impl Default for TestSimEnvBuilder { + fn default() -> Self { + Self::new() + } +} + +impl TestSimEnvBuilder { + /// Create a new builder with default Parmigiana constants and empty databases. + pub fn new() -> Self { + // Share one default block template so the rollup and host builders start aligned. + let default_block_env = Self::default_block_env(); + Self { + rollup_db: TestDbBuilder::new().build(), + host_db: TestDbBuilder::new().build(), + rollup_block_env: default_block_env.clone(), + host_block_env: default_block_env, + constants: SignetSystemConstants::parmigiana(), + } + } + + /// Create a default block environment suitable for testing. + fn default_block_env() -> BlockEnv { + BlockEnv { + number: U256::from(100u64), + beneficiary: Address::repeat_byte(0x01), + timestamp: U256::from(1_700_000_000u64), + gas_limit: 3_000_000_000, + basefee: 1_000_000_000, // 1 gwei + difficulty: U256::ZERO, + prevrandao: Some(B256::random()), + blob_excess_gas_and_price: Some(BlobExcessGasAndPrice { + excess_blob_gas: 0, + blob_gasprice: 0, + }), + } + } + + /// Set the rollup database. + pub fn with_rollup_db(mut self, db: TestDb) -> Self { + self.rollup_db = db; + self + } + + /// Set the host database. + pub fn with_host_db(mut self, db: TestDb) -> Self { + self.host_db = db; + self + } + + /// Set the rollup block environment. + pub const fn with_rollup_block_env(mut self, env: BlockEnv) -> Self { + self.rollup_block_env = env; + self + } + + /// Set the host block environment. + pub const fn with_host_block_env(mut self, env: BlockEnv) -> Self { + self.host_block_env = env; + self + } + + /// Set both rollup and host block environments to the same value. + pub fn with_block_env(mut self, env: BlockEnv) -> Self { + self.rollup_block_env = env.clone(); + self.host_block_env = env; + self + } + + /// Set the system constants. + pub const fn with_constants(mut self, constants: SignetSystemConstants) -> Self { + self.constants = constants; + self + } + + /// Build the test RollupEnv. + pub fn build_rollup_env(&self) -> TestRollupEnv { + let timestamp = self.rollup_block_env.timestamp.to::(); + let cfg = SignetCfgEnv::new(self.constants.ru_chain_id(), timestamp); + RollupEnv::new(self.rollup_db.clone(), self.constants.clone(), &cfg, &self.rollup_block_env) + } + + /// Build the test HostEnv. + pub fn build_host_env(&self) -> TestHostEnv { + let timestamp = self.host_block_env.timestamp.to::(); + let cfg = SignetCfgEnv::new(self.constants.host_chain_id(), timestamp); + HostEnv::new(self.host_db.clone(), self.constants.clone(), &cfg, &self.host_block_env) + } + + /// Get a clone of the rollup database. + pub fn rollup_db(&self) -> TestDb { + self.rollup_db.clone() + } + + /// Get a clone of the host database. + pub fn host_db(&self) -> TestDb { + self.host_db.clone() + } + + /// Build both environments as a tuple. + pub fn build(self) -> (TestRollupEnv, TestHostEnv) { + let rollup = self.build_rollup_env(); + let host = self.build_host_env(); + (rollup, host) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_sim_env_builder_creates_environments() { + // The default builder should produce both environments without any extra setup. + let builder = TestSimEnvBuilder::new(); + let (rollup_env, host_env) = builder.build(); + + // Verify environments were created (we can't easily inspect internals, + // but we can verify they don't panic during creation) + let _ = rollup_env; + let _ = host_env; + } + + #[test] + fn test_sim_env_builder_with_custom_block_env() { + // Override the shared block env to verify both environments accept custom settings. + let custom_env = BlockEnv { + number: U256::from(500u64), + beneficiary: Address::repeat_byte(0x42), + timestamp: U256::from(1800000000u64), + gas_limit: 5_000_000_000, + basefee: 2_000_000_000, + difficulty: U256::ZERO, + prevrandao: Some(B256::repeat_byte(0x11)), + blob_excess_gas_and_price: None, + }; + + let builder = TestSimEnvBuilder::new().with_block_env(custom_env); + let (rollup_env, host_env) = builder.build(); + + let _ = rollup_env; + let _ = host_env; + } +} diff --git a/src/test_utils.rs b/src/test_utils/mod.rs similarity index 85% rename from src/test_utils.rs rename to src/test_utils/mod.rs index 1a2c9e53..d8cae7c8 100644 --- a/src/test_utils.rs +++ b/src/test_utils/mod.rs @@ -1,4 +1,25 @@ //! Test utilities for testing builder tasks +//! +//! This module provides utilities for testing the block builder without +//! requiring network access or full chain state setup. +#![allow(dead_code, unreachable_pub, unused_imports)] + +mod block; +mod db; +mod env; +mod scenarios; +mod tx; + +// Re-export test harness components +pub use block::{TestBlockBuild, TestBlockBuildBuilder, quick_build_block}; +pub use db::{LatencyDb, TestDb, TestDbBuilder, TestStateSource}; +pub use env::{TestHostEnv, TestRollupEnv, TestSimEnvBuilder}; +pub use scenarios::{ + DEFAULT_BALANCE, DEFAULT_BASEFEE, basic_scenario, custom_funded_scenario, funded_test_db, + gas_limit_scenario, priority_ordering_scenario, test_block_env as scenarios_test_block_env, +}; +pub use tx::{TestAccounts, create_call_tx, create_transfer_tx}; + use crate::config::BuilderConfig; use alloy::{ consensus::{SignableTransaction, TxEip1559, TxEnvelope}, @@ -20,7 +41,6 @@ use init4_bin_base::{ }, }; use signet_constants::SignetSystemConstants; -use std::env; use trevm::revm::{context::BlockEnv, context_interface::block::BlobExcessGasAndPrice}; /// Set up a block builder with test values @@ -40,7 +60,7 @@ pub fn setup_test_config() -> &'static BuilderConfig { submit_endpoints: vec!["https://relay-sepolia.flashbots.net:443".to_string()], quincey_url: "http://localhost:8080".into(), sequencer_key: None, - builder_key: env::var("SEPOLIA_ETH_PRIV_KEY") + builder_key: std::env::var("SEPOLIA_ETH_PRIV_KEY") .unwrap_or_else(|_| B256::repeat_byte(0x42).to_string()), builder_port: 8080, builder_rewards_address: Address::default(), diff --git a/src/test_utils/scenarios.rs b/src/test_utils/scenarios.rs new file mode 100644 index 00000000..4eec47a6 --- /dev/null +++ b/src/test_utils/scenarios.rs @@ -0,0 +1,187 @@ +//! Pre-configured test scenarios. +//! This module provides ready-to-use test scenarios that configure databases, +//! environments, and builders for common testing needs. + +use super::{ + block::TestBlockBuildBuilder, db::TestDbBuilder, env::TestSimEnvBuilder, tx::TestAccounts, +}; +use alloy::primitives::{Address, B256, U256}; +use signet_sim::SimCache; +use trevm::revm::{context::BlockEnv, context_interface::block::BlobExcessGasAndPrice}; + +/// Default test balance: 100 ETH in wei. +pub const DEFAULT_BALANCE: u128 = 100_000_000_000_000_000_000; + +/// Default basefee: 1 gwei. +pub const DEFAULT_BASEFEE: u64 = 1_000_000_000; + +/// Create a test database with pre-funded accounts. +/// +/// # Arguments +/// +/// * `accounts` - The test accounts to fund +/// * `balance` - The balance to give each account +pub fn funded_test_db(accounts: &TestAccounts, balance: U256) -> TestDbBuilder { + TestDbBuilder::new() + .with_account(accounts.alice_address(), balance, 0) + .with_account(accounts.bob_address(), balance, 0) + .with_account(accounts.charlie_address(), balance, 0) +} + +/// Create a standard test block environment. +/// +/// # Arguments +/// +/// * `block_number` - The block number +/// * `basefee` - The base fee per gas +/// * `timestamp` - The block timestamp +/// * `gas_limit` - The block gas limit +pub fn test_block_env(block_number: u64, basefee: u64, timestamp: u64, gas_limit: u64) -> BlockEnv { + BlockEnv { + number: U256::from(block_number), + beneficiary: Address::repeat_byte(0x01), + timestamp: U256::from(timestamp), + gas_limit, + basefee, + difficulty: U256::ZERO, + prevrandao: Some(B256::random()), + blob_excess_gas_and_price: Some(BlobExcessGasAndPrice { + excess_blob_gas: 0, + blob_gasprice: 0, + }), + } +} + +/// Basic multi-transaction block building scenario. +/// +/// Creates a scenario with: +/// - Three pre-funded test accounts (100 ETH each) +/// - Default block environment +/// - Empty SimCache ready for transactions +/// +/// # Returns +/// +/// A tuple of (builder, accounts, cache) ready for adding transactions and building. +pub fn basic_scenario() -> (TestBlockBuildBuilder, TestAccounts, SimCache) { + let accounts = TestAccounts::new(); + let balance = U256::from(DEFAULT_BALANCE); + + let rollup_db = funded_test_db(&accounts, balance).build(); + let host_db = funded_test_db(&accounts, balance).build(); + + let block_env = test_block_env(100, DEFAULT_BASEFEE, 1700000000, 3_000_000_000); + + let sim_env = TestSimEnvBuilder::new() + .with_rollup_db(rollup_db) + .with_host_db(host_db) + .with_block_env(block_env); + + let cache = SimCache::new(); + + let builder = TestBlockBuildBuilder::new().with_sim_env_builder(sim_env); + + (builder, accounts, cache) +} + +/// Scenario for testing transaction priority ordering. +/// +/// Similar to `basic_scenario` but with a longer deadline to ensure +/// all transactions can be processed and ordered. +/// +/// # Returns +/// +/// A tuple of (builder, accounts, cache) configured for priority testing. +pub fn priority_ordering_scenario() -> (TestBlockBuildBuilder, TestAccounts, SimCache) { + let (builder, accounts, cache) = basic_scenario(); + + // Use a longer deadline for ordering tests + let builder = builder.with_deadline(std::time::Duration::from_secs(5)); + + (builder, accounts, cache) +} + +/// Scenario for testing gas limit enforcement. +/// +/// Creates a scenario with a configurable gas limit to test that +/// block building respects the maximum gas constraint. +/// +/// # Arguments +/// +/// * `max_gas` - The maximum gas limit for the block +/// +/// # Returns +/// +/// A tuple of (builder, accounts, cache) with the specified gas limit. +pub fn gas_limit_scenario(max_gas: u64) -> (TestBlockBuildBuilder, TestAccounts, SimCache) { + let (builder, accounts, cache) = basic_scenario(); + + let builder = builder.with_max_gas(max_gas); + + (builder, accounts, cache) +} + +/// Scenario with custom funded addresses. +/// +/// Use this when you need specific addresses to be funded +/// (e.g., for testing contract interactions). +/// +/// # Arguments +/// +/// * `addresses` - List of (address, balance, nonce) tuples to fund +/// +/// # Returns +/// +/// A TestBlockBuildBuilder configured with the funded accounts. +pub fn custom_funded_scenario( + addresses: &[(Address, U256, u64)], +) -> (TestBlockBuildBuilder, SimCache) { + let mut db_builder = TestDbBuilder::new(); + + for (addr, balance, nonce) in addresses { + db_builder = db_builder.with_account(*addr, *balance, *nonce); + } + + let db = db_builder.build(); + + let sim_env = TestSimEnvBuilder::new().with_rollup_db(db.clone()).with_host_db(db); + + let cache = SimCache::new(); + let builder = TestBlockBuildBuilder::new().with_sim_env_builder(sim_env); + + (builder, cache) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_basic_scenario_creates_funded_accounts() { + let (_, accounts, _) = basic_scenario(); + + // Verify accounts are created with unique addresses + let addresses = accounts.addresses(); + assert_eq!(addresses.len(), 3); + assert_ne!(addresses[0], addresses[1]); + } + + #[test] + fn test_gas_limit_scenario_creates_builder() { + // Verify gas limit scenario creates a valid builder + let (_, _, _) = gas_limit_scenario(100_000); + // Builder creation succeeds (gas limit is applied internally) + } + + #[test] + fn test_custom_funded_scenario() { + let addr1 = Address::repeat_byte(0x11); + let addr2 = Address::repeat_byte(0x22); + + let (_, _) = custom_funded_scenario(&[ + (addr1, U256::from(1000u64), 5), + (addr2, U256::from(2000u64), 10), + ]); + + // Scenario creation succeeds + } +} diff --git a/src/test_utils/tx.rs b/src/test_utils/tx.rs new file mode 100644 index 00000000..eeb856ad --- /dev/null +++ b/src/test_utils/tx.rs @@ -0,0 +1,193 @@ +//! Test transaction utilities. +//! This module provides helpers for creating test transactions and accounts +//! for use in simulation tests. + +use alloy::{ + consensus::{ + SignableTransaction, TxEip1559, TxEnvelope, transaction::Recovered, + transaction::SignerRecoverable, + }, + primitives::{Address, TxKind, U256}, + signers::{SignerSync, local::PrivateKeySigner}, +}; +use eyre::Result; + +/// Pre-funded test accounts for simulation testing. +/// These accounts can be used to create and sign test transactions. +/// Use `TestDbBuilder` to fund these accounts in the test database. +#[derive(Debug, Clone)] +pub struct TestAccounts { + /// First test account (Alice) + pub alice: PrivateKeySigner, + /// Second test account (Bob) + pub bob: PrivateKeySigner, + /// Third test account (Charlie) + pub charlie: PrivateKeySigner, +} + +impl Default for TestAccounts { + fn default() -> Self { + Self::new() + } +} + +impl TestAccounts { + /// Create new random test accounts. + pub fn new() -> Self { + Self { + alice: PrivateKeySigner::random(), + bob: PrivateKeySigner::random(), + charlie: PrivateKeySigner::random(), + } + } + + /// Get all account addresses. + pub fn addresses(&self) -> Vec
{ + vec![self.alice.address(), self.bob.address(), self.charlie.address()] + } + + /// Get Alice's address. + pub const fn alice_address(&self) -> Address { + self.alice.address() + } + + /// Get Bob's address. + pub const fn bob_address(&self) -> Address { + self.bob.address() + } + + /// Get Charlie's address. + pub const fn charlie_address(&self) -> Address { + self.charlie.address() + } +} + +/// Create a signed EIP-1559 transfer transaction. +/// +/// # Arguments +/// +/// * `signer` - The account signing the transaction +/// * `to` - The recipient address +/// * `value` - The amount of wei to transfer +/// * `nonce` - The sender's nonce (transaction count) +/// * `chain_id` - The chain ID (e.g., signet_constants::parmigiana::RU_CHAIN_ID) +/// * `max_priority_fee_per_gas` - The priority fee (tip) per gas unit +/// +/// # Returns +/// +/// A recovered transaction envelope ready to be added to a SimCache. +pub fn create_transfer_tx( + signer: &PrivateKeySigner, + to: Address, + value: U256, + nonce: u64, + chain_id: u64, + max_priority_fee_per_gas: u128, +) -> Result> { + let tx = TxEip1559 { + chain_id, + nonce, + max_fee_per_gas: 100_000_000_000, // 100 gwei max fee + max_priority_fee_per_gas, + gas_limit: 21_000, // Standard transfer gas + to: TxKind::Call(to), + value, + ..Default::default() + }; + + let signature = signer.sign_hash_sync(&tx.signature_hash())?; + let signed = TxEnvelope::Eip1559(tx.into_signed(signature)); + let recovered = signed.try_into_recovered()?; + Ok(recovered) +} + +/// Create a signed EIP-1559 contract call transaction. +/// +/// # Arguments +/// +/// * `signer` - The account signing the transaction +/// * `to` - The contract address +/// * `input` - The calldata for the contract call +/// * `value` - The amount of wei to send with the call +/// * `nonce` - The sender's nonce +/// * `chain_id` - The chain ID +/// * `gas_limit` - The gas limit for the transaction +/// * `max_priority_fee_per_gas` - The priority fee per gas unit +#[allow(clippy::too_many_arguments)] +pub fn create_call_tx( + signer: &PrivateKeySigner, + to: Address, + input: alloy::primitives::Bytes, + value: U256, + nonce: u64, + chain_id: u64, + gas_limit: u64, + max_priority_fee_per_gas: u128, +) -> Result> { + let tx = TxEip1559 { + chain_id, + nonce, + max_fee_per_gas: 100_000_000_000, + max_priority_fee_per_gas, + gas_limit, + to: TxKind::Call(to), + value, + input, + ..Default::default() + }; + + let signature = signer.sign_hash_sync(&tx.signature_hash())?; + let signed = TxEnvelope::Eip1559(tx.into_signed(signature)); + let recovered = signed.try_into_recovered()?; + Ok(recovered) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_accounts_creates_unique_addresses() { + let accounts = TestAccounts::new(); + let addresses = accounts.addresses(); + + assert_eq!(addresses.len(), 3); + assert_ne!(addresses[0], addresses[1]); + assert_ne!(addresses[1], addresses[2]); + assert_ne!(addresses[0], addresses[2]); + } + + #[test] + fn test_create_transfer_tx_succeeds() { + let accounts = TestAccounts::new(); + let tx = create_transfer_tx( + &accounts.alice, + accounts.bob_address(), + U256::from(1_000_000_000_000_000_000u128), // 1 ETH + 0, + 1, // Mainnet chain ID for testing + 10_000, + ); + + assert!(tx.is_ok()); + let recovered = tx.unwrap(); + assert_eq!(recovered.signer(), accounts.alice_address()); + } + + #[test] + fn test_create_call_tx_succeeds() { + let accounts = TestAccounts::new(); + let tx = create_call_tx( + &accounts.alice, + accounts.bob_address(), + alloy::primitives::Bytes::from_static(&[0x12, 0x34]), + U256::ZERO, + 0, + 1, + 50_000, + 10_000, + ); + + assert!(tx.is_ok()); + } +} diff --git a/tests/block_builder_test.rs b/tests/block_builder_test.rs index 38db7772..7992912e 100644 --- a/tests/block_builder_test.rs +++ b/tests/block_builder_test.rs @@ -1,6 +1,6 @@ -//! Tests for the block building task. - +#![cfg(feature = "test-utils")] #![recursion_limit = "256"] +//! Tests for the block building task. use alloy::{ consensus::transaction::SignerRecoverable, eips::BlockId, node_bindings::Anvil, @@ -21,7 +21,6 @@ use std::time::{Duration, Instant}; /// This test sets up a simulated environment using Anvil, creates a block builder, /// and verifies that the block builder can successfully build a block containing /// transactions from multiple senders. -#[ignore = "integration test"] #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn test_handle_build() { setup_logging(); diff --git a/tests/bundle_load_test.rs b/tests/bundle_load_test.rs new file mode 100644 index 00000000..e5c515dc --- /dev/null +++ b/tests/bundle_load_test.rs @@ -0,0 +1,314 @@ +#![cfg(feature = "test-utils")] +//! Load tests for bundle simulation. +//! +//! These tests exercise the block building loop with high volumes of bundles +//! and transactions to verify correctness and deadline compliance under stress. + +use alloy::{ + primitives::{Address, U256}, + serde::OtherFields, + signers::local::PrivateKeySigner, +}; +use builder::test_utils::{ + DEFAULT_BALANCE, DEFAULT_BASEFEE, TestBlockBuildBuilder, TestDbBuilder, TestSimEnvBuilder, + create_transfer_tx, scenarios_test_block_env, +}; +use signet_bundle::RecoveredBundle; +use signet_sim::{BuiltBlock, SimCache}; +use std::collections::HashSet; +use std::time::Duration; + +/// Block number used for all test environments and bundles. +const BLOCK_NUMBER: u64 = 100; + +/// Block timestamp used for all test environments and bundles. +const BLOCK_TIMESTAMP: u64 = 1_700_000_000; + +/// Parmigiana rollup chain ID. +const RU_CHAIN_ID: u64 = 88888; + +/// Default max priority fee used for transfer bundles in tests. +const DEFAULT_PRIORITY_FEE: u128 = 10_000_000_000; + +/// Generate N random funded signers and a database builder with all of them funded. +fn generate_funded_accounts(n: usize) -> (Vec, TestDbBuilder) { + let signers: Vec = (0..n).map(|_| PrivateKeySigner::random()).collect(); + let balance = U256::from(DEFAULT_BALANCE); + + let mut db_builder = TestDbBuilder::new(); + for signer in &signers { + db_builder = db_builder.with_account(signer.address(), balance, 0); + } + + (signers, db_builder) +} + +/// Create a `RecoveredBundle` with one transfer transaction. +fn make_bundle( + signer: &PrivateKeySigner, + to: Address, + uuid: String, + max_priority_fee: u128, +) -> RecoveredBundle { + let tx = create_transfer_tx(signer, to, U256::from(1_000u64), 0, RU_CHAIN_ID, max_priority_fee) + .unwrap(); + + RecoveredBundle::new_unchecked( + vec![tx], + vec![], + BLOCK_NUMBER, + Some(BLOCK_TIMESTAMP - 100), + Some(BLOCK_TIMESTAMP + 100), + vec![], + Some(uuid), + vec![], + None, + None, + vec![], + OtherFields::default(), + ) +} + +/// Build a `TestBlockBuildBuilder` from a pre-funded db builder. +fn build_env(db_builder: TestDbBuilder) -> TestBlockBuildBuilder { + let db = db_builder.build(); + let block_env = + scenarios_test_block_env(BLOCK_NUMBER, DEFAULT_BASEFEE, BLOCK_TIMESTAMP, 3_000_000_000); + let sim_env = TestSimEnvBuilder::new() + .with_rollup_db(db.clone()) + .with_host_db(db) + .with_block_env(block_env); + TestBlockBuildBuilder::new().with_sim_env_builder(sim_env) +} + +/// 50 bundles each containing 1 transfer tx. Verify block builds and includes txs. +#[tokio::test(flavor = "multi_thread", worker_threads = 4)] +async fn test_load_many_bundles() { + let count = 50; + let (signers, db_builder) = generate_funded_accounts(count); + let recipient = Address::repeat_byte(0xAA); + + let cache = SimCache::with_capacity(count); + let bundles: Vec = signers + .iter() + .enumerate() + .map(|(i, signer)| { + make_bundle(signer, recipient, format!("bundle-{i}"), DEFAULT_PRIORITY_FEE) + }) + .collect(); + + cache.add_bundles(bundles, DEFAULT_BASEFEE); + assert_eq!(cache.len(), count); + + let builder = build_env(db_builder).with_cache(cache).with_deadline(Duration::from_secs(5)); + let built: BuiltBlock = builder.build().build().await; + + assert!(built.tx_count() > 0, "expected transactions in built block, got 0"); + assert_eq!( + built.tx_count(), + count, + "expected all {count} bundle txs to be included, got {}", + built.tx_count() + ); +} + +/// 5k bundles each containing 1 transfer tx. Verify block builds with non-zero count of included txs. +#[tokio::test(flavor = "multi_thread", worker_threads = 4)] +async fn test_load_5k_bundles() { + let count = 5_000; + let (signers, db_builder) = generate_funded_accounts(count); + let recipient = Address::repeat_byte(0xAA); + + let cache = SimCache::with_capacity(count); + let bundles: Vec = signers + .iter() + .enumerate() + .map(|(i, signer)| { + // Keep ranks distinct to avoid pathological cache insertion cost at high volume. + make_bundle(signer, recipient, format!("bundle-{i}"), DEFAULT_PRIORITY_FEE + i as u128) + }) + .collect(); + + cache.add_bundles(bundles, DEFAULT_BASEFEE); + assert_eq!(cache.len(), count); + + let builder = build_env(db_builder).with_cache(cache).with_deadline(Duration::from_secs(12)); + let built: BuiltBlock = builder.build().build().await; + + assert!(built.tx_count() > 0, "expected transactions in built block, got 0"); +} + +/// 30 bundles + 30 standalone txs. Verify both types land in the built block. +#[tokio::test(flavor = "multi_thread", worker_threads = 4)] +async fn test_load_bundles_and_txs_mixed() { + let bundle_count = 30; + let tx_count = 30; + let total = bundle_count + tx_count; + + let (signers, db_builder) = generate_funded_accounts(total); + let recipient = Address::repeat_byte(0xBB); + + let cache = SimCache::with_capacity(total); + + let bundles: Vec = signers[..bundle_count] + .iter() + .enumerate() + .map(|(i, signer)| { + make_bundle(signer, recipient, format!("mix-bundle-{i}"), DEFAULT_PRIORITY_FEE) + }) + .collect(); + cache.add_bundles(bundles, DEFAULT_BASEFEE); + + for signer in &signers[bundle_count..] { + let tx = create_transfer_tx( + signer, + recipient, + U256::from(1_000u64), + 0, + RU_CHAIN_ID, + 10_000_000_000, + ) + .unwrap(); + cache.add_tx(tx, DEFAULT_BASEFEE); + } + + assert_eq!(cache.len(), total); + + let builder = build_env(db_builder).with_cache(cache).with_deadline(Duration::from_secs(5)); + let built: BuiltBlock = builder.build().build().await; + + assert!(built.tx_count() > 0, "expected transactions in built block"); + assert_eq!( + built.tx_count(), + total, + "expected all {total} items included, got {}", + built.tx_count() + ); +} + +/// Many bundles with a constrained gas limit. Verify gas cap is respected. +#[tokio::test(flavor = "multi_thread", worker_threads = 4)] +async fn test_load_saturate_gas_limit() { + let count = 50; + let (signers, db_builder) = generate_funded_accounts(count); + let recipient = Address::repeat_byte(0xCC); + + let cache = SimCache::with_capacity(count); + let bundles: Vec = signers + .iter() + .enumerate() + .map(|(i, signer)| { + make_bundle(signer, recipient, format!("gas-bundle-{i}"), DEFAULT_PRIORITY_FEE) + }) + .collect(); + cache.add_bundles(bundles, DEFAULT_BASEFEE); + + // Each transfer costs 21,000 gas. Allow room for ~10 transfers. + let max_gas: u64 = 21_000 * 10; + + let builder = build_env(db_builder) + .with_cache(cache) + .with_deadline(Duration::from_secs(5)) + .with_max_gas(max_gas); + let built: BuiltBlock = builder.build().build().await; + + assert!( + built.tx_count() <= 10, + "expected at most 10 txs within gas limit, got {}", + built.tx_count() + ); + assert!(built.tx_count() > 0, "expected at least some txs to be included"); +} + +/// Many bundles with a tight deadline. Verify block completes within time. +#[tokio::test(flavor = "multi_thread", worker_threads = 4)] +async fn test_load_deadline_pressure() { + let count = 1000; + let (signers, db_builder) = generate_funded_accounts(count); + let recipient = Address::repeat_byte(0xDD); + + let cache = SimCache::with_capacity(count); + let bundles: Vec = signers + .iter() + .enumerate() + .map(|(i, signer)| { + make_bundle(signer, recipient, format!("deadline-bundle-{i}"), DEFAULT_PRIORITY_FEE) + }) + .collect(); + cache.add_bundles(bundles, DEFAULT_BASEFEE); + + let deadline = Duration::from_millis(500); + let start = std::time::Instant::now(); + + let builder = build_env(db_builder).with_cache(cache).with_deadline(deadline); + let built: BuiltBlock = builder.build().build().await; + + let elapsed = start.elapsed(); + + assert!(built.tx_count() > 0, "expected at least some txs under deadline pressure"); + assert!( + built.tx_count() < count, + "expected fewer than {count} txs under deadline pressure, got {}", + built.tx_count() + ); + + // Should complete within a reasonable margin of the deadline. + assert!(elapsed < deadline * 3, "block build took {elapsed:?}, expected within ~{deadline:?}"); +} + +/// Gas-constrained block: verify the builder selects the highest-fee bundles first. +/// +/// 10 low-fee bundles + 10 high-fee bundles, with a gas cap that can only fit 10. +/// Every included transaction must originate from a high-fee sender. +#[tokio::test(flavor = "multi_thread", worker_threads = 4)] +async fn test_load_fee_priority_ordering() { + let low_count = 10usize; + let high_count = 10usize; + let total = low_count + high_count; + + let (signers, db_builder) = generate_funded_accounts(total); + let recipient = Address::repeat_byte(0xEE); + + let cache = SimCache::with_capacity(total); + + let low_fee = DEFAULT_PRIORITY_FEE; + let high_fee = 90_000_000_000u128; // 90 Gwei — valid (<= max_fee_per_gas=100 Gwei), 9× above low_fee + + let low_fee_senders: HashSet
= + signers[..low_count].iter().map(|s| s.address()).collect(); + + let bundles: Vec = signers + .iter() + .enumerate() + .map(|(i, signer)| { + let fee = if i < low_count { low_fee } else { high_fee }; + make_bundle(signer, recipient, format!("priority-bundle-{i}"), fee) + }) + .collect(); + + cache.add_bundles(bundles, DEFAULT_BASEFEE); + + // Gas limit exactly fits the 10 high-fee bundles (21,000 gas each). + let max_gas: u64 = 21_000 * high_count as u64; + + let builder = build_env(db_builder) + .with_cache(cache) + .with_deadline(Duration::from_secs(5)) + .with_max_gas(max_gas); + let built: BuiltBlock = builder.build().build().await; + + assert_eq!( + built.tx_count(), + high_count, + "expected exactly {high_count} txs, got {}", + built.tx_count() + ); + + for tx in built.transactions() { + assert!( + !low_fee_senders.contains(&tx.signer()), + "low-fee sender {} was included instead of a high-fee sender", + tx.signer() + ); + } +} diff --git a/tests/bundle_poller_test.rs b/tests/bundle_poller_test.rs index 440e0572..9acfe483 100644 --- a/tests/bundle_poller_test.rs +++ b/tests/bundle_poller_test.rs @@ -1,7 +1,8 @@ +#![cfg(feature = "test-utils")] + use builder::test_utils::{setup_logging, setup_test_config}; use eyre::Result; -#[ignore = "integration test"] #[tokio::test] async fn test_bundle_poller_roundtrip() -> Result<()> { setup_logging(); diff --git a/tests/cache.rs b/tests/cache.rs index 9173ad08..d37cd7e9 100644 --- a/tests/cache.rs +++ b/tests/cache.rs @@ -1,3 +1,4 @@ +#![cfg(feature = "test-utils")] #![recursion_limit = "256"] use builder::{ @@ -7,7 +8,6 @@ use builder::{ use init4_bin_base::deps::tracing::warn; use std::time::Duration; -#[ignore = "integration test. This test will take >12 seconds to run, and requires Authz configuration env vars."] #[tokio::test] async fn test_bundle_poller_roundtrip() -> eyre::Result<()> { setup_logging(); diff --git a/tests/env.rs b/tests/env.rs index 89fa6a04..06f78f05 100644 --- a/tests/env.rs +++ b/tests/env.rs @@ -1,3 +1,4 @@ +#![cfg(feature = "test-utils")] #![recursion_limit = "256"] use builder::{ @@ -5,7 +6,6 @@ use builder::{ test_utils::{setup_logging, setup_test_config}, }; -#[ignore = "integration test. This test will take between 0 and 12 seconds to run."] #[tokio::test] async fn test_bundle_poller_roundtrip() -> eyre::Result<()> { setup_logging(); diff --git a/tests/tx_poller_test.rs b/tests/tx_poller_test.rs index bca37e3c..d607d8c6 100644 --- a/tests/tx_poller_test.rs +++ b/tests/tx_poller_test.rs @@ -1,3 +1,5 @@ +#![cfg(feature = "test-utils")] + use alloy::{primitives::U256, signers::local::PrivateKeySigner}; use builder::{ tasks::cache::TxPoller, @@ -5,7 +7,6 @@ use builder::{ }; use eyre::{Ok, Result}; -#[ignore = "integration test"] #[tokio::test] async fn test_tx_roundtrip() -> Result<()> { setup_logging();