Compare commits

..

8 Commits

Author SHA1 Message Date
Dan Cline
956a31c149 Merge branch 'main' into dan/static-file-split 2026-03-31 18:32:18 -04:00
Dan Cline
832ac79b8e perf: reuse jar cursor in account/storage changeset split
Same optimization as transactions/receipts — reuse one cursor per jar
file instead of creating a new jar + cursor per block. Reduces cursor
creations from ~24.5M to ~50 for changeset segments.

Amp-Thread-ID: https://ampcode.com/threads/T-019c9c40-05f9-72c0-b6a0-7f8c32db6c6f
Co-authored-by: Amp <amp@ampcode.com>
2026-02-26 19:01:45 -05:00
Dan Cline
47681f785d perf: reuse jar cursor across blocks in static file split
Instead of creating a new StaticFileJarProvider + StaticFileCursor for
every single transaction (billions on mainnet), reuse one cursor per
jar file. The cursor is only replaced when the next block's transactions
fall outside the current jar's tx range.

This eliminates billions of Vec<u8>::with_capacity allocations and
BTreeMap lookups, reducing cursor creations from ~2.8B to ~50 (one
per jar file).

Amp-Thread-ID: https://ampcode.com/threads/T-019c9c40-05f9-72c0-b6a0-7f8c32db6c6f
Co-authored-by: Amp <amp@ampcode.com>
2026-02-26 18:58:23 -05:00
Dan Cline
cb47b195fd perf: bulk-load block body indices for static file split
Instead of holding a DB read transaction open and seeking per-block
throughout the entire split operation, load all BlockBodyIndices into
memory upfront with a single sequential walk_range scan. The table is
<1G so this is safe, and the loaded map is shared across the
transactions, receipts, and transaction senders segments.

Amp-Thread-ID: https://ampcode.com/threads/T-019c9c40-05f9-72c0-b6a0-7f8c32db6c6f
Co-authored-by: Amp <amp@ampcode.com>
2026-02-26 18:42:14 -05:00
Dan Cline
5a9bbbe72b fix: disable long read tx safety in static file split command
The split_transactions, split_receipts, and split_transaction_senders
methods hold a read transaction open for the entire block range iteration,
which can trigger the long-lived read transaction timeout warning.

Since the split command runs offline against a non-progressing database,
disable the safety check to match the pattern used in other CLI commands
(db checksum, db list, db diff, etc.).

Amp-Thread-ID: https://ampcode.com/threads/T-019c9c40-05f9-72c0-b6a0-7f8c32db6c6f
Co-authored-by: Amp <amp@ampcode.com>
2026-02-26 18:25:57 -05:00
Dan Cline
749719c053 chore: regenerate CLI docs after rebase
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 15:16:48 -05:00
Georgios Konstantopoulos
cf12b2cb7f fix: update changeset offset API and generate CLI docs
- Update split_account_changesets and split_storage_changesets to use
  the new read_changeset_offset() method instead of the removed
  changeset_offset() on SegmentHeader
- Generate CLI docs for the new static-files split command

Amp-Thread-ID: https://ampcode.com/threads/T-019c6eb5-dde6-75ed-bf8c-b520a95d9fd4
Co-authored-by: Amp <amp@ampcode.com>
2026-02-25 15:16:48 -05:00
Dan Cline
8122fdf0af chore: static file split cli command 2026-02-25 15:16:48 -05:00
321 changed files with 5399 additions and 5698 deletions

View File

@@ -0,0 +1,4 @@
---
---
Added site-level meta description for SEO.

View File

@@ -0,0 +1,5 @@
---
reth-transaction-pool: patch
---
Renamed and documented validation methods for clarity. `validate_one_no_state` and `validate_one_against_state` are now public methods `validate_stateless` and `validate_stateful` with improved documentation explaining their respective validation phases.

View File

@@ -0,0 +1,10 @@
---
reth-engine-primitives: patch
reth-engine-tree: patch
reth-node-core: patch
reth-trie-parallel: minor
---
Removed legacy proof calculation system and V2-specific configuration flags.
Removed the legacy (non-V2) proof calculation code paths, simplified multiproof task architecture by removing the dual-mode system, and cleaned up V2-specific CLI flags (`--engine.disable-proof-v2`, `--engine.disable-trie-cache`) that are no longer needed. The codebase now exclusively uses V2 proofs with the sparse trie cache.

View File

@@ -0,0 +1,5 @@
---
reth-trie-sparse: patch
---
Refactored sparse trie node state tracking to use RLP nodes instead of hashes. Replaced `Option<B256>` hash fields with `SparseNodeState` enum that tracks either dirty nodes or cached RLP nodes with optional database storage flags. Added debug assertions to validate leaf path lengths and improved pruning logic to use node paths directly instead of path-hash tuples.

20
.changelog/config.toml Normal file
View File

@@ -0,0 +1,20 @@
# Changelogs configuration for reth
# https://github.com/wevm/changelogs
# How to bump packages that depend on changed packages
dependent_bump = "patch"
[changelog]
# Generate per-crate changelogs (vs single root changelog)
format = "per-crate"
# Fixed groups: all always share the same version
# reth binaries share version
[[fixed]]
members = ["reth"]
# Packages to ignore (internal/test-only crates)
ignore = [
"reth-testing-utils",
"reth-bench",
]

View File

@@ -0,0 +1,5 @@
---
reth-trie-sparse: patch
---
Fixed a bug in `merge_subtrie_updates` where source insertions did not cancel destination removals (and vice versa), causing inconsistent trie updates accumulated across multiple `root()` calls without intermediate `take_updates()`. Added a test covering the cross-cancellation behavior.

View File

@@ -0,0 +1,5 @@
---
reth-transaction-pool: minor
---
Added support for optional custom stateless and stateful validation hooks in `EthTransactionValidator` via `set_additional_stateless_validation` and `set_additional_stateful_validation` methods. Also implemented a manual `Debug` impl to handle the non-`Debug` function pointer fields.

View File

@@ -0,0 +1,17 @@
---
reth: minor
reth-cli-commands: minor
reth-e2e-test-utils: minor
reth-ethereum-cli: minor
reth-node-core: minor
reth-optimism-bin: minor
reth-optimism-cli: minor
reth-prune: patch
reth-stages: patch
reth-storage-api: minor
reth-storage-db-api: minor
reth-storage-db-common: patch
reth-storage-provider: patch
---
Introduced `--storage.v2` flag to control storage mode defaults, replacing the `edge` feature flag with `rocksdb` feature. The new flag enables v2 storage settings (static files + RocksDB routing) while individual `--static-files.*` and `--rocksdb.*` flags can still override defaults. Updated feature gates from `edge` to `rocksdb` across all affected crates.

View File

@@ -0,0 +1,5 @@
---
reth-tasks: patch
---
Added panic handler to all rayon thread pools that logs panics via `tracing::error` instead of aborting the process.

View File

@@ -0,0 +1,5 @@
---
reth: patch
---
Removed Windows platform support from the codebase, including the Windows cross-compilation Dockerfile, build targets in Cross.toml and Makefile, and Windows-specific options in the bug report template.

View File

@@ -0,0 +1,5 @@
---
reth-network: minor
---
Added reason label to backed_off_peers metric. The metric now tracks backed off peers by reason (too_many_peers, graceful_close, connection_error) to improve observability.

View File

@@ -0,0 +1,5 @@
---
reth-network-types: patch
---
Increased default maximum concurrent outbound dials from 15 to 30.

View File

@@ -0,0 +1,6 @@
---
reth-trie-common: minor
reth-trie: minor
---
Added `contains_range` method to `PrefixSet` for checking if any key falls within a half-open range. Added prefix set support to `ProofCalculator` via `with_prefix_set`, enabling stale cached hash invalidation and branch collapse detection when keys are inserted or removed; propagated storage prefix sets through `SyncAccountValueEncoder`.

View File

@@ -0,0 +1,5 @@
---
reth-trie-sparse: patch
---
Refactored arena trie internals by adding a `BranchChildIdx::sibling()` helper, deduplicating `Index`/`NodeArena` type aliases, and replacing `is_empty()` with a `drop_root()` method. Fixed a bug where `cursor.pop()` was called before checking if the leaf was the root node, which could cause incorrect dirty-state propagation.

View File

@@ -0,0 +1,5 @@
---
reth-trie-sparse: patch
---
Added recording of `SetRoot` operation in `ParallelSparseTrie::set_root` when the `trie-debug` feature is enabled.

View File

@@ -0,0 +1,5 @@
---
reth-trie-sparse: minor
---
Fixed a bug in `ArenaParallelSparseTrie` where subtrie updates that would completely empty a subtrie were incorrectly dispatched to parallel workers instead of being processed inline, preventing correct branch collapse detection when blinded siblings are present. Refactored the `SparseTrie` test suite to accept a `fn() -> T` factory instead of requiring `T: Default`, enabling a new `arena_parallel_sparse_trie_always_parallel` test variant that exercises all tests with parallelism thresholds set to 1. Added `test_branch_collapse_multi_empty_subtries_blinded_remaining` to cover the case where removing multiple revealed leaves empties their subtries and leaves a single blinded sibling requiring a proof.

View File

@@ -0,0 +1,5 @@
---
reth-trie-sparse-parallel: patch
---
Fixed parallel sparse trie to skip revealing disconnected leaves by checking parent branch reachability before inserting leaf nodes.

View File

@@ -0,0 +1,5 @@
---
ef-tests: patch
---
Removed reth-stateless crate and stateless validation from ef-tests.

View File

@@ -0,0 +1,6 @@
---
reth-rpc-convert: minor
reth-storage-rpc-provider: minor
---
Replaced the separate `TryFromBlockResponse`, `TryFromReceiptResponse`, and `TryFromTransactionResponse` traits with a unified `RpcResponseConverter` trait and default `EthRpcConverter` implementation. Removed the `op-alloy-network` dependency and refactored `RpcBlockchainProvider` to store a dynamic converter instance instead of relying on per-type trait bounds.

View File

@@ -0,0 +1,6 @@
---
reth-engine-tree: patch
reth-trie-sparse-parallel: patch
---
Added tracing spans and debug logs to sparse trie operations for better observability during parallel state root computation.

View File

@@ -0,0 +1,6 @@
---
reth-exex: patch
reth-exex-types: patch
---
Added configurable backfill thresholds to ExEx notifications stream and added regression tests for state provider parity between pipeline and backfill execution paths.

View File

@@ -0,0 +1,5 @@
---
reth-payload-builder: minor
---
Added observability metrics for payload resolve latency and new payload job creation latency to the payload builder service.

View File

@@ -0,0 +1,4 @@
---
---
Added WebSocket subscription integration tests for eth_subscribe.

View File

@@ -0,0 +1,5 @@
---
reth-transaction-pool: minor
---
Added `consensus_ref` method to `PoolTransaction` trait for borrowing consensus transactions without cloning.

View File

@@ -0,0 +1,4 @@
---
---
Improved nightly Docker build failure Slack notification with more detailed formatting and context.

View File

@@ -0,0 +1,5 @@
---
reth-trie-sparse: patch
---
Fixed another branch collapse edge case where `check_subtrie_collapse_needs_proof` incorrectly compared removal count against total update count (including `Touched` entries), causing it to skip proof requests for blinded siblings and panic when the subtrie emptied. Added a regression test covering the removals + `Touched` + blinded sibling scenario.

View File

@@ -0,0 +1,10 @@
---
reth-chain-state: minor
reth-engine-primitives: minor
reth-engine-tree: minor
reth-node-core: minor
reth-node-events: minor
reth: patch
---
Added configurable slow block logging (`--engine.slow-block-threshold`) that emits a structured `warn!` log with detailed timing, state-operation counts, and cache hit-rate metrics for blocks whose total processing time exceeds the threshold. Introduced `ExecutionTimingStats`, `CacheStats`, `StateProviderStats`, and `SlowBlockInfo` types to carry execution statistics from block validation through persistence, and refactored `PersistenceResult` to carry commit duration alongside the last persisted block.

View File

@@ -0,0 +1,5 @@
---
reth-cli-commands: patch
---
Added `snapshot_api_url` field to `DownloadDefaults` so downstream projects can override the snapshot discovery API endpoint. Previously, `discover_manifest_url`, `fetch_snapshot_api_entries`, and `print_snapshot_listing` used a hardcoded `snapshots.reth.rs` URL that bypassed the `DownloadDefaults` override mechanism.

View File

@@ -0,0 +1,5 @@
---
reth-rpc-eth-types: patch
---
Updated `eth_simulateV1` revert error code from `-32000` to `3` to be consistent with `eth_call`, per [execution-apis#748](https://github.com/ethereum/execution-apis/pull/748).

View File

@@ -0,0 +1,5 @@
---
reth-engine-tree: patch
---
Reordered cache size calculations in `ExecutionCache::new` to group related operations together.

View File

@@ -0,0 +1,7 @@
---
reth: patch
reth-cli-commands: patch
reth-node-core: patch
---
Removed experimental ress protocol support for stateless Ethereum nodes.

View File

@@ -0,0 +1,6 @@
---
reth-trie-db: minor
reth-engine-tree: minor
---
Added `PendingChangeset` and `PendingChangesetGuard` to `ChangesetCache` so concurrent readers wait for an in-progress computation instead of falling back to the expensive DB-based path. The guard automatically cancels the pending entry on drop (e.g. task panic), ensuring waiters always make progress.

View File

@@ -0,0 +1,5 @@
---
reth-engine-tree: patch
---
Added sub-phase timing histograms to the sparse trie event loop, tracking channel wait, proof coalescing, multiproof reveal, and trie update durations separately.

View File

@@ -0,0 +1,6 @@
---
reth-primitives-traits: major
reth-downloaders: patch
---
Removed the local `size` module from `reth-primitives-traits` and replaced it with `alloy_consensus::InMemorySize`. Simplified `SignedTransaction` to a blanket impl covering all types satisfying the required bounds, removing `is_system_tx`, `auto_impl` attributes, and explicit impls for `EthereumTxEnvelope` and OP types. Updated import paths in `reth-downloaders` accordingly.

View File

@@ -0,0 +1,13 @@
---
reth-primitives-traits: minor
reth-engine-local: patch
reth-evm: patch
reth-node-builder: patch
reth-payload-primitives: patch
reth-rpc-convert: patch
reth-rpc-eth-api: patch
reth-db-api: patch
reth-db: patch
---
Removed the unused `Extended` type and `op` feature (including `op-alloy-consensus` dependency) from `reth-primitives-traits`. Updated all dependent crates to remove the now-unnecessary `reth-primitives-traits/op` feature flag propagation.

View File

@@ -0,0 +1,5 @@
---
reth-node-builder: patch
---
Removed biased select in engine service loop to allow fair scheduling of shutdown requests alongside event processing.

View File

@@ -0,0 +1,5 @@
---
reth-transaction-pool: patch
---
Fixed swapped arguments in `blob_tx_priority` function calls, correcting the parameter order to match the function signature.

View File

@@ -0,0 +1,4 @@
---
---
Improved documentation overview page with better structure and clarity.

View File

@@ -0,0 +1,5 @@
---
reth: patch
---
Re-enabled changelog workflow to run automatically on pull requests.

View File

@@ -0,0 +1,5 @@
---
reth-node-events: patch
---
Updated consensus engine log message to be more accurate about received updates.

View File

@@ -0,0 +1,8 @@
---
reth-engine-primitives: minor
reth-engine-tree: minor
reth-node-core: minor
reth-trie-parallel: minor
---
Added `--engine.proof-jitter` CLI option behind the `trie-debug` feature flag. When set, each proof worker sleeps for a random duration up to the specified value before starting proof computation, useful for stress-testing timing-sensitive proof logic.

View File

@@ -0,0 +1,6 @@
---
reth-rpc-eth-api: minor
reth-rpc-server-types: minor
---
Added `eth_getStorageValues` RPC method for batch storage slot retrieval across multiple addresses.

View File

@@ -0,0 +1,6 @@
---
reth-chainspec: minor
reth-network-peers: minor
---
Removed OP stack bootnodes from default chain configurations and network peers module.

View File

@@ -0,0 +1,5 @@
---
reth-rpc-convert: patch
---
Updated `alloy-evm` dependency to git revision `9bc2dba` and adapted `TxEnvConverter` impl to the updated `TryIntoTxEnv` trait signature that now includes a `Spec` generic parameter.

View File

@@ -0,0 +1,6 @@
---
reth-trie: patch
reth-trie-sparse: patch
---
Refactored test harness for sparse trie tests by extracting `TrieTestHarness` into a shared `reth-trie` test utility, replacing duplicated inline harness code across multiple test modules. Updated `proof_v2` return type to include an optional root hash, and converted `original_root` and `storage` from public fields to accessor methods.

View File

@@ -0,0 +1,5 @@
---
reth-trie: patch
---
Removed the local `increment_and_strip_trailing_zeros` function and `PATH_ALL_ZEROS` static in `proof_v2`, replacing them with the equivalent `Nibbles::next_without_prefix` and `Nibbles::is_zeroes` builtins. Also replaced manual `.get()` calls on `state_mask`/`hash_mask` with direct field access and switched to `Nibbles::unpack_array` over the unsafe `unpack_unchecked`.

View File

@@ -0,0 +1,9 @@
---
reth-network-api: minor
reth-network-types: minor
reth-network: minor
reth-node-core: minor
reth: minor
---
Added optional ENR fork ID enforcement to filter out peers from incompatible networks during peer discovery, controlled by the `--enforce-enr-fork-id` CLI flag.

View File

@@ -0,0 +1,5 @@
---
reth-primitives: patch
---
Moved feature-referenced dependencies from dev-dependencies to optional dependencies to ensure they are available when their corresponding features are enabled.

View File

@@ -0,0 +1,5 @@
---
reth-engine-tree: patch
---
Added idle-time pre-computation of account trie upper hashes in the sparse trie payload processor when no pending proof results are available.

View File

@@ -0,0 +1,6 @@
---
reth-engine-local: patch
reth-node-builder: patch
---
Removed the `op` feature flag and `OpPayloadAttributes` `PayloadAttributesBuilder` implementation from `reth-engine-local`, along with the `op-alloy-rpc-types-engine` dependency. Updated `reth-node-builder` to no longer enable the removed `op` feature on `reth-engine-local`.

View File

@@ -0,0 +1,5 @@
---
reth-engine-tree: patch
---
Fixed `compare_trie_updates` to return `bool` indicating whether differences were found, and updated the caller to properly use the return value instead of treating all successful comparisons as having no differences.

View File

@@ -0,0 +1,7 @@
---
reth-cli-commands: minor
reth-node-core: minor
reth: patch
---
Made v2 storage the default for all new databases, deprecating the `--storage.v2` flag to a hidden no-op kept for backwards compatibility. Updated CLI reference docs to remove the now-hidden flag from all command help pages.

View File

@@ -0,0 +1,5 @@
---
reth-node-core: minor
---
Added `with_dev_block_time` helper method to `NodeConfig` for configuring dev miner block production interval.

View File

@@ -0,0 +1,5 @@
---
reth-transaction-pool: minor
---
Added `IntoIter: Send` bounds to `validate_transactions` and `validate_transactions_with_origin` in the `TransactionValidator` trait, avoiding unnecessary `Vec` collects. Simplified default `validate_transactions_with_origin` to delegate to `validate_transactions`.

View File

@@ -0,0 +1,5 @@
---
reth-db-api: patch
---
Changed `StoredNibblesSubKey` encoding to use a stack-allocated `[u8; 65]` array instead of a heap-allocated `Vec<u8>`, avoiding unnecessary heap allocation.

View File

@@ -0,0 +1,7 @@
---
reth-engine-tree: patch
reth-trie-sparse: patch
reth-tasks: patch
---
Offloaded deallocation of expensive proof node buffers to a persistent background thread (`Runtime::spawn_drop`) to avoid blocking state root computation or lock-holding code.

View File

@@ -0,0 +1,5 @@
---
reth-storage-api: patch
---
Added `Arc` to `auto_impl` derive for storage-api traits to support automatic `Arc` wrapper implementations.

View File

@@ -0,0 +1,8 @@
---
reth: patch
reth-engine-tree: patch
reth-node-builder: patch
reth-trie-sparse: minor
---
Added `trie-debug` feature for recording sparse trie mutations to aid in debugging state root mismatches.

View File

@@ -0,0 +1,6 @@
---
reth-static-file-types: patch
reth-provider: patch
---
Move changeset offsets from segment header to external `.csoff` sidecar file for incremental writes and crash recovery.

View File

@@ -0,0 +1,5 @@
---
reth-provider: patch
---
Removed unused staging types from ProviderFactoryBuilder.

View File

@@ -0,0 +1,6 @@
---
reth-trie-sparse: patch
reth-engine-tree: patch
---
Removed the `skip_proof_node_filtering` flag, `revealed_account_paths`/`revealed_paths` tracking, and the `filter_revealed_v2_proof_nodes` function from the sparse trie implementation. Also removed the corresponding skipped-nodes metrics, simplifying the proof node reveal path to always pass nodes directly to the sparse trie without pre-filtering.

View File

@@ -0,0 +1,5 @@
---
reth-trie-sparse: minor
---
Added a comprehensive generic `SparseTrie` test suite covering `set_root`, `reveal_nodes`, `update_leaves`, `root`, `take_updates`, `commit_updates`, `prune`, `wipe`/`clear`, `get_leaf_value`, `find_leaf`, `size_hint`, and integration lifecycle scenarios. Tests are stamped out for all concrete `SparseTrie` implementations via a macro.

View File

@@ -0,0 +1,5 @@
---
reth-provider: patch
---
Fixed sender pruning during block reorg to skip when sender_recovery is fully pruned, preventing a fatal crash when no sender data exists in static files.

View File

@@ -0,0 +1,6 @@
---
reth-payload-primitives: patch
reth-engine-local: patch
---
Removed the `op` feature and `op-alloy-rpc-types-engine` dependency from `reth-payload-primitives`, along with the `ExecutionPayload` impl for `OpExecutionData`. Updated `reth-engine-local` to drop the corresponding feature flag dependency.

View File

@@ -0,0 +1,5 @@
---
reth-engine-tree: patch
---
Downgraded per-transaction prewarm span from `debug_span!` to `trace_span!` to reduce noise in debug-level logging.

View File

@@ -0,0 +1,7 @@
---
reth-network-types: minor
reth-network: minor
reth-node-core: patch
---
Added `PersistedPeerInfo` struct to persist richer peer metadata (kind, fork ID, reputation) to disk. Updated `PeersConfig::with_basic_nodes_from_file` to support both the new `PersistedPeerInfo` format and the legacy `Vec<NodeRecord>` format with automatic conversion, and updated `write_peers_to_file` to exclude backed-off and banned peers.

View File

@@ -0,0 +1,5 @@
---
reth: patch
---
Added automated changelog generation infrastructure using wevm/changelogs-rs with Claude Code integration. Configured per-crate changelog format with fixed version groups for reth binaries and exclusions for internal test utilities.

View File

@@ -0,0 +1,5 @@
---
reth-trie-sparse: minor
---
Removed `SerialSparseTrie` from the workspace, consolidating on `ParallelSparseTrie` as the single sparse trie implementation in `reth-trie-sparse`.

View File

@@ -0,0 +1,5 @@
---
reth-cli-commands: minor
---
Added `reth_version` field to `SnapshotManifest` to record the Reth version that produced a snapshot. The field is optional and populated automatically during manifest generation.

View File

@@ -0,0 +1,5 @@
---
reth-network: minor
---
Added `fork_id` as a tiebreaker in peer selection when reputations are equal, preferring peers with a discovered `fork_id` as it indicates fork compatibility. Added a test to verify the tiebreaker behavior.

View File

@@ -0,0 +1,5 @@
---
reth-trie-sparse: patch
---
Fixed a panic in `ParallelSparseTrie::reveal_nodes` when a boundary node's upper parent is absent or non-branch (e.g. when an upper extension crosses the boundary). The code now skips gracefully instead of unwrapping. Added a regression test covering this case.

View File

@@ -0,0 +1,5 @@
---
reth-trie-sparse: patch
---
Fixed a bug where trie nodes could appear in both `updated_nodes` and `removed_nodes` simultaneously by removing entries from `removed_nodes` when a node is inserted as updated.

View File

@@ -0,0 +1,8 @@
---
reth-trie-common: minor
reth-trie: minor
reth-trie-parallel: minor
reth-engine-tree: patch
---
Moved `ProofV2Target`, `MultiProofTargetsV2`, and `ChunkedMultiProofTargetsV2` from `reth-trie-parallel::targets_v2` into a new `reth-trie-common::target_v2` module, making these types available at a lower level without pulling in the full parallel trie crate. Added a `multiproof_v2` method to `Proof` in `reth-trie` that generates a state multiproof using the V2 proof calculator with synchronous account value encoding.

View File

@@ -0,0 +1,14 @@
---
reth-engine-tree: patch
reth-evm-ethereum: patch
reth-evm: patch
reth-primitives-traits: patch
reth-revm: patch
reth-rpc-eth-api: patch
reth-rpc-eth-types: patch
reth-provider: patch
example-custom-evm: patch
example-precompile-cache: patch
---
Bumped revm to v35.0.0, revm-inspectors to 0.35.0, and alloy-evm to 0.29.0. Updated call sites throughout the codebase to align with the new APIs, including `ExecutionResult` field changes (`gas_used``gas.used()`/`gas.final_refunded()`), removal of `.without_state_clear()`, updated `EthPrecompiles::new(spec)` constructor, and updated `block_hashes.lowest()` access.

View File

@@ -0,0 +1,9 @@
---
reth-trie-sparse: minor
reth-engine-primitives: minor
reth-engine-tree: minor
reth-node-core: minor
reth-trie-common: patch
---
Added an arena-based sparse trie implementation (`ArenaParallelSparseTrie`) using `slotmap` arena allocation for node storage, enabling parallel subtrie mutation without per-node hashing overhead. Added `ConfigurableSparseTrie` enum to switch between the arena and hash-map implementations, and a `--engine.enable-arena-sparse-trie` CLI flag to opt in at runtime.

View File

@@ -0,0 +1,5 @@
---
reth: patch
---
Updated Alloy dependencies from 1.5.2 to 1.6.1.

View File

@@ -0,0 +1,4 @@
---
---
Expanded CLI integration tests with subcommand help coverage, config TOML validation, genesis JSON validation, and send transaction round-trip test for dev mode.

View File

@@ -0,0 +1,6 @@
---
reth-engine-local: minor
reth-node-builder: minor
---
Added trigger-based `MiningMode` variant that allows blocks to be built on-demand via custom streams, and exposed `with_mining_mode` method on `DebugNodeLauncherFuture` to override default mining configuration.

View File

@@ -0,0 +1,5 @@
---
reth-network: minor
---
Added direction labels to `closed_sessions` and `pending_session_failures` metrics. Operators can now distinguish session closures and failures by direction (`active`, `incoming_pending`, `outgoing_pending` for closed sessions; `inbound`, `outbound` for pending session failures).

View File

@@ -0,0 +1,4 @@
---
---
Moved Kurtosis CI failure notifications to the hive Slack channel.

View File

@@ -0,0 +1,7 @@
---
reth-rpc-api: minor
reth-rpc-builder: patch
reth-rpc: minor
---
Added `subscribeFinalizedChainNotifications` RPC endpoint that buffers committed chain notifications and emits them once a new finalized block is received.

View File

@@ -0,0 +1,5 @@
---
reth-transaction-pool: patch
---
Fixed a bug where transactions from the same sender were added to the pending subpool out of nonce order. Ensured `process_updates` runs before `add_new_transaction` so that lower-nonce promotions are enqueued before the newly inserted higher-nonce transaction, preserving correct ordering for live `BestTransactions` iterators.

View File

@@ -0,0 +1,5 @@
---
reth-transaction-pool: minor
---
Added `TransactionValidationTaskExecutor::spawn` as a dedicated constructor that encapsulates spawning validation tasks on a runtime, and refactored `EthTransactionValidatorBuilder::build_with_tasks` to use it.

View File

@@ -0,0 +1,7 @@
---
reth-engine-primitives: patch
reth-engine-tree: patch
reth-node-core: patch
---
Removed `--engine.enable-arena-sparse-trie` CLI flag and made the arena-based sparse trie the default implementation. The hash-map-based `ParallelSparseTrie` variant is no longer selectable.

View File

@@ -0,0 +1,7 @@
---
reth-trie: major
reth-trie-db: major
reth-provider: minor
---
Added `MaskedTrieCursorFactory` and `MaskedTrieCursor` to handle prefix-set-based hash invalidation at the cursor layer, replacing the `DatabaseTrieWitness` trait abstraction. Removed `with_prefix_sets_mut` from `TrieWitness` and deleted `DatabaseTrieWitness` — callers should now wrap their cursor factory with `MaskedTrieCursorFactory` to apply prefix sets during witness/proof computation.

View File

@@ -0,0 +1,8 @@
---
reth-engine-primitives: minor
reth-engine-tree: major
reth-node-core: minor
reth-cli-commands: minor
---
Added persistence backpressure to the engine tree: when the canonical-minus-persisted block gap exceeds a configurable threshold (`--engine.persistence-backpressure-threshold`, default 16), the engine loop stalls on the persistence receiver instead of processing new incoming messages. Added CLI argument, cross-field validation, metrics (`backpressure_active`, `backpressure_stall_duration`), and tests.

View File

@@ -0,0 +1,6 @@
---
reth-trie: minor
reth-trie-parallel: minor
---
Added `root_node` and `storage_root_node` methods to proof calculators for efficient root-only calculations. These methods directly return the root node without requiring dummy targets, replacing the previous workaround of passing fake targets to proof generation.

View File

@@ -0,0 +1,5 @@
---
reth-trie: patch
---
Fixed a potential panic in `ProofCalculator` by clearing internal computation state (`branch_stack`, `child_stack`, `branch_path`, etc.) after errors, preventing stale state from causing `usize` underflow panics when the calculator is reused. Added a test verifying correct behavior after simulated mid-computation errors.

View File

@@ -20,6 +20,11 @@
# include dist directory, where the reth binary is located after compilation
!/dist
# include PGO build helper used by Dockerfile.depot
!/.github
!/.github/scripts
!/.github/scripts/build_pgo_bolt.sh
# include licenses
!LICENSE-*

View File

@@ -16,7 +16,7 @@
#
# Required: mc (MinIO client) with a configured alias
# Optional env: BENCH_BIG_BLOCKS (true/false) — build reth-bb instead of reth
set -euxo pipefail
set -euo pipefail
MC="mc"
MODE="$1"
@@ -73,14 +73,14 @@ case "$MODE" in
mkdir -p "${SOURCE_DIR}/target/profiling"
CACHE_VALID=false
if $MC stat --no-list "${BUCKET}/${NODE_BIN}" &>/dev/null; then
if $MC stat "${BUCKET}/${NODE_BIN}" &>/dev/null; then
echo "Cache hit for baseline (${COMMIT}), downloading ${NODE_BIN}..."
if $MC cp "${BUCKET}/${NODE_BIN}" "${SOURCE_DIR}/target/profiling/${NODE_BIN}" && \
chmod +x "${SOURCE_DIR}/target/profiling/${NODE_BIN}" && \
verify_binary "${SOURCE_DIR}/target/profiling/${NODE_BIN}" "${COMMIT}"; then
$MC cp "${BUCKET}/${NODE_BIN}" "${SOURCE_DIR}/target/profiling/${NODE_BIN}"
chmod +x "${SOURCE_DIR}/target/profiling/${NODE_BIN}"
if verify_binary "${SOURCE_DIR}/target/profiling/${NODE_BIN}" "${COMMIT}"; then
CACHE_VALID=true
else
echo "Cached baseline binary is stale or download failed, rebuilding..."
echo "Cached baseline binary is stale, rebuilding..."
fi
fi
if [ "$CACHE_VALID" = false ]; then
@@ -105,16 +105,16 @@ case "$MODE" in
BUCKET="minio/reth-binaries/${BRANCH_SHA}${BUILD_SUFFIX}"
CACHE_VALID=false
if $MC stat --no-list "${BUCKET}/${NODE_BIN}" &>/dev/null && $MC stat --no-list "${BUCKET}/reth-bench" &>/dev/null; then
if $MC stat "${BUCKET}/${NODE_BIN}" &>/dev/null && $MC stat "${BUCKET}/reth-bench" &>/dev/null; then
echo "Cache hit for ${BRANCH_SHA}, downloading binaries..."
mkdir -p "${SOURCE_DIR}/target/profiling"
if $MC cp "${BUCKET}/${NODE_BIN}" "${SOURCE_DIR}/target/profiling/${NODE_BIN}" && \
$MC cp "${BUCKET}/reth-bench" /home/ubuntu/.cargo/bin/reth-bench && \
chmod +x "${SOURCE_DIR}/target/profiling/${NODE_BIN}" /home/ubuntu/.cargo/bin/reth-bench && \
verify_binary "${SOURCE_DIR}/target/profiling/${NODE_BIN}" "${COMMIT}"; then
$MC cp "${BUCKET}/${NODE_BIN}" "${SOURCE_DIR}/target/profiling/${NODE_BIN}"
$MC cp "${BUCKET}/reth-bench" /home/ubuntu/.cargo/bin/reth-bench
chmod +x "${SOURCE_DIR}/target/profiling/${NODE_BIN}" /home/ubuntu/.cargo/bin/reth-bench
if verify_binary "${SOURCE_DIR}/target/profiling/${NODE_BIN}" "${COMMIT}"; then
CACHE_VALID=true
else
echo "Cached feature binary is stale or download failed, rebuilding..."
echo "Cached feature binary is stale, rebuilding..."
fi
fi
if [ "$CACHE_VALID" = false ]; then

View File

@@ -26,7 +26,7 @@
#
# The script delegates to the existing bench-reth-*.sh scripts in the reth
# repo for the actual build, snapshot, and run steps.
set -euxo pipefail
set -euo pipefail
# ── PATH ──────────────────────────────────────────────────────────────
# Ensure cargo and user-local bins (mc, uv) are visible
@@ -399,9 +399,12 @@ upload_tracy() {
# ── Step 6: Pre-flight cleanup ───────────────────────────────────────
echo "▸ Pre-flight cleanup..."
pkill -f bench-metrics-proxy 2>/dev/null || true
sudo systemctl stop "${RETH_SCOPE:-reth-bench.scope}" 2>/dev/null || true
sudo systemctl reset-failed "${RETH_SCOPE:-reth-bench.scope}" 2>/dev/null || true
sudo schelk recover -y --kill || sudo schelk full-recover -y || true
sudo pkill -9 reth 2>/dev/null || true
sleep 1
if mountpoint -q "$SCHELK_MOUNT" 2>/dev/null; then
sudo umount -l "$SCHELK_MOUNT" 2>/dev/null || true
sudo schelk recover -y 2>/dev/null || true
fi
echo
# ── Step 7: Interleaved benchmark runs (B-F-F-B) ────────────────────

View File

@@ -7,14 +7,13 @@
#
# Required env: SCHELK_MOUNT, BENCH_RPC_URL, BENCH_BLOCKS, BENCH_WARMUP_BLOCKS
# Optional env: BENCH_BIG_BLOCKS (true/false), BENCH_WORK_DIR (for big blocks path)
# BENCH_BAL (false/true/feature/baseline; only used with big blocks)
# BENCH_RETH_NEW_PAYLOAD (true/false, default true)
# BENCH_WAIT_TIME (duration like 500ms, default empty)
# BENCH_BASELINE_ARGS (extra reth node args for baseline runs)
# BENCH_FEATURE_ARGS (extra reth node args for feature runs)
# BENCH_OTLP_TRACES_ENDPOINT (OTLP HTTP endpoint for traces, e.g. https://host/insert/opentelemetry/v1/traces)
# BENCH_OTLP_LOGS_ENDPOINT (OTLP HTTP endpoint for logs, e.g. https://host/insert/opentelemetry/v1/logs)
# BENCH_OTLP_DISABLED (true to skip OTLP export even if endpoints are set)
set -euxo pipefail
set -euo pipefail
LABEL="$1"
BINARY="$2"
@@ -27,8 +26,6 @@ DATADIR="$SCHELK_MOUNT/$DATADIR_NAME"
mkdir -p "$OUTPUT_DIR"
LOG="${OUTPUT_DIR}/node.log"
RETH_SCOPE="${RETH_SCOPE:-reth-bench.scope}"
cleanup() {
kill "$TAIL_PID" 2>/dev/null || true
# Stop tracy-capture first (SIGINT makes it disconnect and flush to disk)
@@ -49,7 +46,7 @@ cleanup() {
fi
wait "$TRACY_PID" 2>/dev/null || true
fi
if sudo systemctl is-active "$RETH_SCOPE" >/dev/null 2>&1; then
if [ -n "${RETH_PID:-}" ] && sudo kill -0 "$RETH_PID" 2>/dev/null; then
if [ "${BENCH_SAMPLY:-false}" = "true" ]; then
# Send SIGINT to the inner reth process by exact name (not -f which
# would also match samply's cmdline containing "reth"). Samply will
@@ -67,28 +64,31 @@ cleanup() {
echo "Samply still running after 120s, sending SIGTERM..."
sudo pkill -x samply 2>/dev/null || true
fi
else
sudo kill "$RETH_PID"
for i in $(seq 1 30); do
sudo kill -0 "$RETH_PID" 2>/dev/null || break
sleep 1
done
fi
# Stop the entire systemd scope — kills all processes in the cgroup.
# This is reliable regardless of process reparenting or PID wrapper issues.
sudo systemctl stop "$RETH_SCOPE" 2>/dev/null || true
sudo kill -9 "$RETH_PID" 2>/dev/null || true
sleep 1
fi
sudo systemctl reset-failed "$RETH_SCOPE" 2>/dev/null || true
# Fix ownership of reth-created files (reth runs as root)
sudo chown -R "$(id -un):$(id -gn)" "$OUTPUT_DIR" 2>/dev/null || true
# Let schelk recover the mounted volume in place so dm-era can restore only
# the changed blocks and clean up its own state.
sudo schelk recover -y --kill || true
if mountpoint -q "$SCHELK_MOUNT"; then
sudo umount -l "$SCHELK_MOUNT" || true
sudo schelk recover -y || true
fi
}
TAIL_PID=
TRACY_PID=
trap cleanup EXIT
# Clean up stale state from a previous cancelled run.
# Stop any leftover reth process in the scope, then recover schelk state.
sudo systemctl stop "$RETH_SCOPE" 2>/dev/null || true
sudo systemctl reset-failed "$RETH_SCOPE" 2>/dev/null || true
sudo schelk recover -y --kill || true
# Clean up stale schelk state from a previous cancelled run.
# If schelk thinks it's still mounted (e.g. a cancelled run skipped cleanup),
# recover first to reset state.
sudo schelk recover -y -k || true
# Mount
sudo schelk mount -y
@@ -149,13 +149,11 @@ if [ -n "${BENCH_METRICS_ADDR:-}" ]; then
fi
# OTLP traces and logs export
if [ "${BENCH_OTLP_DISABLED:-false}" != "true" ]; then
if [ -n "${BENCH_OTLP_TRACES_ENDPOINT:-}" ]; then
RETH_ARGS+=(--tracing-otlp="${BENCH_OTLP_TRACES_ENDPOINT}" --tracing-otlp.service-name=reth-bench)
fi
if [ -n "${BENCH_OTLP_LOGS_ENDPOINT:-}" ]; then
RETH_ARGS+=(--logs-otlp="${BENCH_OTLP_LOGS_ENDPOINT}" --logs-otlp.filter=debug)
fi
if [ -n "${BENCH_OTLP_TRACES_ENDPOINT:-}" ]; then
RETH_ARGS+=(--tracing-otlp="${BENCH_OTLP_TRACES_ENDPOINT}" --tracing-otlp.service-name=reth-bench)
fi
if [ -n "${BENCH_OTLP_LOGS_ENDPOINT:-}" ]; then
RETH_ARGS+=(--logs-otlp="${BENCH_OTLP_LOGS_ENDPOINT}" --logs-otlp.filter=debug)
fi
# Tracy profiling: add --log.tracy flags and set environment
@@ -182,19 +180,19 @@ echo "Memory limit: $(( MEM_LIMIT / 1024 / 1024 ))MB (95% of $(( TOTAL_MEM_KB /
if [ "${BENCH_SAMPLY:-false}" = "true" ]; then
RETH_ARGS+=(--log.samply)
SAMPLY="$(which samply)"
sudo systemd-run --quiet --scope --collect --unit="$RETH_SCOPE" \
-p MemoryMax="$MEM_LIMIT" -p AllowedCPUs="$RETH_CPUS" \
sudo systemd-run --scope -p MemoryMax="$MEM_LIMIT" -p AllowedCPUs="$RETH_CPUS" \
env "${SUDO_ENV[@]}" nice -n -20 \
"$SAMPLY" record --save-only --presymbolicate --rate 10000 \
--output "$OUTPUT_DIR/samply-profile.json.gz" \
-- "$BINARY" "${RETH_ARGS[@]}" \
> "$LOG" 2>&1 &
else
sudo systemd-run --quiet --scope --collect --unit="$RETH_SCOPE" \
-p MemoryMax="$MEM_LIMIT" -p AllowedCPUs="$RETH_CPUS" \
sudo systemd-run --scope -p MemoryMax="$MEM_LIMIT" -p AllowedCPUs="$RETH_CPUS" \
env "${SUDO_ENV[@]}" nice -n -20 "$BINARY" "${RETH_ARGS[@]}" \
> "$LOG" 2>&1 &
fi
RETH_PID=$!
stdbuf -oL tail -f "$LOG" | sed -u "s/^/[reth] /" &
TAIL_PID=$!
@@ -242,7 +240,10 @@ fi
BENCH_NICE="sudo nice -n -20 sudo -u $(id -un)"
# Build optional flags
EXTRA_BENCH_ARGS=(--reth-new-payload)
EXTRA_BENCH_ARGS=()
if [ "${BENCH_RETH_NEW_PAYLOAD:-true}" != "false" ]; then
EXTRA_BENCH_ARGS+=(--reth-new-payload --wait-for-persistence)
fi
if [ -n "${BENCH_WAIT_TIME:-}" ]; then
EXTRA_BENCH_ARGS+=(--wait-time "$BENCH_WAIT_TIME")
fi
@@ -250,47 +251,8 @@ fi
if [ "$BIG_BLOCKS" = "true" ]; then
# Big blocks mode: replay pre-generated payloads
BIG_BLOCKS_DIR="${BENCH_BIG_BLOCKS_DIR:-${BENCH_WORK_DIR}/big-blocks}"
BENCH_BAL_MODE="${BENCH_BAL:-false}"
BB_BENCH_ARGS=(--reth-new-payload)
if [ -n "${BENCH_WAIT_TIME:-}" ]; then
BB_BENCH_ARGS+=(--wait-time "$BENCH_WAIT_TIME")
fi
case "$BENCH_BAL_MODE" in
false)
;;
true)
BB_BENCH_ARGS+=(--bal)
;;
baseline)
if [[ "$LABEL" == baseline* ]]; then
BB_BENCH_ARGS+=(--bal)
fi
;;
feature)
if [[ "$LABEL" == feature* ]]; then
BB_BENCH_ARGS+=(--bal)
fi
;;
*)
echo "::error::Unknown BENCH_BAL value: $BENCH_BAL_MODE"
exit 1
;;
esac
# Warmup
WARMUP="${BENCH_WARMUP_BLOCKS:-50}"
if [ "$WARMUP" -gt 0 ] 2>/dev/null; then
echo "Running big blocks warmup (${WARMUP} payloads)..."
$BENCH_NICE "$RETH_BENCH" replay-payloads \
"${BB_BENCH_ARGS[@]}" \
--count "$WARMUP" \
--payload-dir "$BIG_BLOCKS_DIR/payloads" \
--engine-rpc-url http://127.0.0.1:8551 \
--jwt-secret "$DATADIR/jwt.hex" 2>&1 | sed -u "s/^/[bench] /"
fi
# Start tracy-capture after warmup so profile only covers the benchmark
# Start tracy-capture so profile only covers the benchmark
if [ "${BENCH_TRACY:-off}" != "off" ]; then
echo "Starting tracy-capture..."
tracy-capture -f -o "$OUTPUT_DIR/tracy-profile.tracy" &
@@ -298,19 +260,18 @@ if [ "$BIG_BLOCKS" = "true" ]; then
sleep 0.5 # give tracy-capture time to connect
fi
# Benchmark — skip warmup payloads so they aren't measured
BB_SKIP=0
if [ "$WARMUP" -gt 0 ] 2>/dev/null; then
BB_SKIP="$WARMUP"
BB_BENCH_ARGS=(--reth-new-payload --wait-for-persistence)
if [ -n "${BENCH_WAIT_TIME:-}" ]; then
BB_BENCH_ARGS+=(--wait-time "$BENCH_WAIT_TIME")
fi
# Limit number of payloads if blocks count is specified
if [ "${BENCH_BLOCKS:-0}" -gt 0 ] 2>/dev/null; then
BB_BENCH_ARGS+=(--count "$BENCH_BLOCKS")
fi
echo "Running big blocks benchmark (replay-payloads, skip=${BB_SKIP})..."
echo "Running big blocks benchmark (replay-payloads)..."
$BENCH_NICE "$RETH_BENCH" replay-payloads \
"${BB_BENCH_ARGS[@]}" \
--skip "$BB_SKIP" \
--payload-dir "$BIG_BLOCKS_DIR/payloads" \
--engine-rpc-url http://127.0.0.1:8551 \
--jwt-secret "$DATADIR/jwt.hex" \

View File

@@ -18,7 +18,7 @@
# BENCH_JOB_URL link to the Actions job
# BENCH_ACTOR user who triggered the benchmark
# BENCH_CONFIG config summary line
set -euxo pipefail
set -euo pipefail
MC="mc"
BUCKET="minio/reth-snapshots"
@@ -77,8 +77,8 @@ trap 'rm -f -- "$MANIFEST_TMP"' EXIT
echo "$MANIFEST_CONTENT" \
| jq --arg base "$BASE_URL" '.base_url = $base' > "$MANIFEST_TMP"
# Prepare mount. If a previous run left the volume mounted, recover first.
sudo schelk recover -y --kill || true
# Prepare mount
mountpoint -q "$SCHELK_MOUNT" && sudo schelk recover -y || true
sudo schelk mount -y
sudo rm -rf "$DATADIR"
sudo mkdir -p "$DATADIR"

View File

@@ -290,14 +290,6 @@ def fmt_s(v: float) -> str:
return f"{v:.2f}s"
def display_bal_mode(bal_mode: str | None) -> str | None:
if not bal_mode or bal_mode == "false":
return None
if bal_mode == "both":
return "true"
return bal_mode
def significance(pct: float, ci_pct: float, lower_is_better: bool) -> str:
"""Return significance label: 'good', 'bad', or 'neutral'."""
significant = abs(pct) > ci_pct
@@ -359,9 +351,6 @@ def generate_comparison_table(
feature_name: str,
feature_sha: str,
big_blocks: bool = False,
warmup_blocks: str | None = None,
wait_time: str | None = None,
bal_mode: str | None = None,
) -> str:
"""Generate a markdown comparison table between baseline and feature."""
n = paired["blocks"]
@@ -402,16 +391,8 @@ def generate_comparison_table(
f"| Mgas/s | {fmt_mgas(run1['mean_mgas_s'])} | {fmt_mgas(run2['mean_mgas_s'])} | {change_str(gas_pct, mgas_ci_pct, lower_is_better=False)} |",
f"| Wall Clock | {fmt_s(run1['wall_clock_s'])} | {fmt_s(run2['wall_clock_s'])} | {change_str(wall_pct, wall_ci_pct, lower_is_better=True)} |",
"",
f"*{n} {'big blocks' if big_blocks else 'blocks'}*",
]
meta_parts = [f"{n} {'big blocks' if big_blocks else 'blocks'}"]
if warmup_blocks:
meta_parts.append(f"{warmup_blocks} warmup")
if wait_time:
meta_parts.append(f"wait time: {wait_time}")
display_mode = display_bal_mode(bal_mode)
if big_blocks and display_mode:
meta_parts.append(f"BAL: {display_mode}")
lines.append(f"*{', '.join(meta_parts)}*")
return "\n".join(lines)
@@ -491,9 +472,6 @@ def main():
parser.add_argument("--feature-ref", "--branch-sha", "--feature-sha", default=None, help="Feature commit SHA")
parser.add_argument("--behind-baseline", "--behind-main", type=int, default=0, help="Commits behind baseline")
parser.add_argument("--big-blocks", action="store_true", default=False, help="Big blocks mode")
parser.add_argument("--warmup-blocks", default=None, help="Number of warmup blocks")
parser.add_argument("--wait-time", default=None, help="Wait time interval used between blocks")
parser.add_argument("--bal-mode", default=None, help="BAL mode (true, feature, baseline)")
parser.add_argument("--grafana-url", default=None, help="Grafana dashboard URL for this benchmark run")
args = parser.parse_args()
@@ -533,7 +511,6 @@ def main():
baseline_name = args.baseline_name or "baseline"
feature_name = args.feature_name or "feature"
feature_sha = args.feature_ref or "unknown"
bal_mode = display_bal_mode(args.bal_mode)
comparison_table = generate_comparison_table(
baseline_stats,
@@ -545,9 +522,6 @@ def main():
feature_name=feature_name,
feature_sha=feature_sha,
big_blocks=args.big_blocks,
warmup_blocks=args.warmup_blocks,
wait_time=args.wait_time,
bal_mode=bal_mode,
)
print(f"Generated comparison ({paired_stats['n']} paired blocks, "
f"mean diff {paired_stats['mean_diff_ms']:+.3f}ms ± {paired_stats['ci_ms']:.3f}ms)")
@@ -579,9 +553,6 @@ def main():
summary = {
"blocks": paired_stats["blocks"],
"big_blocks": args.big_blocks,
"warmup_blocks": args.warmup_blocks,
"wait_time": args.wait_time,
"bal_mode": bal_mode,
"baseline": {
"name": baseline_name,
"ref": baseline_ref,

View File

@@ -2,20 +2,17 @@
#
# Resolves baseline and feature refs for scheduled benchmark runs.
#
# Supports three modes:
# Supports two modes:
# nightly — Queries the latest successful scheduled docker.yml run via
# GitHub API to find the nightly Docker image commit. Compares
# with the last successful feature ref to detect staleness.
# hourly — Compares origin/main HEAD against the last successfully
# benchmarked commit (falls back to HEAD~1 on first run).
# Checks for in-progress sibling runs to avoid overlap.
# release — Compares the latest GitHub release tag against the current
# nightly Docker build. Baseline is the release tag commit,
# feature is the nightly commit.
#
# Usage: bench-scheduled-refs.sh <force> <mode>
# force — "true" to run even if no new commit (bypass skip logic)
# mode — "nightly", "hourly", or "release"
# mode — "nightly" or "hourly"
#
# Outputs (via GITHUB_OUTPUT):
# baseline-ref — commit SHA for baseline
@@ -24,15 +21,13 @@
# is-stale — "true" if latest nightly build is >24h old (nightly only)
# stale-age-hours — age of the nightly build in hours (nightly only)
# nightly-created — ISO timestamp of the nightly build (nightly only)
# release-tag — release tag name (release mode only, e.g. "v2.0.0")
#
# Reads:
# state/nightly-last-feature-ref (nightly, from decofe/reth-bench-charts repo)
# state/hourly-last-feature-ref (hourly, from decofe/reth-bench-charts repo)
# state/release-last-feature-ref (release, from decofe/reth-bench-charts repo)
# .nightly-state/last-feature-ref (nightly, from GH Actions cache)
# .hourly-state/last-feature-ref (hourly, from GH Actions cache)
#
# Requires: gh (GitHub CLI), jq, date, git (hourly mode), curl, DEREK_TOKEN env
set -euxo pipefail
# Requires: gh (GitHub CLI), jq, date, git (hourly mode)
set -euo pipefail
FORCE="${1:-false}"
MODE="${2:-nightly}"
@@ -47,7 +42,7 @@ if [ "$MODE" = "hourly" ]; then
# --- Step 1: Resolve feature ref from git ---
echo "::group::Resolving hourly refs from git"
git fetch origin main --depth=2 --quiet
git fetch origin main --quiet
FEATURE_REF=$(git rev-parse origin/main)
echo "Feature (HEAD): $FEATURE_REF"
echo "::endgroup::"
@@ -74,15 +69,15 @@ if [ "$MODE" = "hourly" ]; then
fi
echo "::endgroup::"
# --- Step 3: Read last successful feature ref from charts repo ---
echo "::group::Reading persisted state"
# --- Step 3: Read last successful feature ref from cache ---
echo "::group::Reading cached state"
LAST_FEATURE_REF=""
STATE_URL="https://raw.githubusercontent.com/decofe/reth-bench-charts/state/state/hourly-last-feature-ref"
if RAW=$(curl -sfL -H "Authorization: token ${DEREK_TOKEN}" "$STATE_URL"); then
LAST_FEATURE_REF=$(echo "$RAW" | tr -d '[:space:]')
STATE_FILE=".hourly-state/last-feature-ref"
if [ -f "$STATE_FILE" ]; then
LAST_FEATURE_REF=$(tr -d '[:space:]' < "$STATE_FILE")
echo "Previous feature ref: $LAST_FEATURE_REF"
else
echo "No persisted state found (first run)"
echo "No cached state found (first run)"
fi
echo "::endgroup::"
@@ -126,106 +121,6 @@ if [ "$MODE" = "hourly" ]; then
exit 0
fi
# ==========================================================================
# Release mode: compare latest GitHub release tag vs current nightly build
# ==========================================================================
if [ "$MODE" = "release" ]; then
# --- Step 1: Resolve feature ref from latest nightly Docker build ---
echo "::group::Querying latest nightly docker build"
RUNS_JSON=$(gh run list \
-R "$REPO" \
--workflow=docker.yml \
--event=schedule \
--status=completed \
--limit 5 \
--json headSha,createdAt,conclusion)
LATEST=$(echo "$RUNS_JSON" | jq -r '[.[] | select(.conclusion == "success")] | first // empty')
if [ -z "$LATEST" ]; then
echo "::error::No successful scheduled docker.yml run found in the last 5 runs"
exit 1
fi
FEATURE_REF=$(echo "$LATEST" | jq -r '.headSha')
echo "Nightly commit (feature): $FEATURE_REF"
echo "::endgroup::"
# --- Step 2: Resolve baseline ref from latest GitHub release ---
echo "::group::Resolving latest release tag"
RELEASE_JSON=$(gh release view --repo "$REPO" --json tagName,targetCommitish,publishedAt 2>/dev/null || echo "{}")
RELEASE_TAG=$(echo "$RELEASE_JSON" | jq -r '.tagName // empty')
if [ -z "$RELEASE_TAG" ]; then
echo "::error::No release found on $REPO"
exit 1
fi
# Resolve the tag to a commit SHA
BASELINE_REF=$(gh api "repos/$REPO/git/ref/tags/$RELEASE_TAG" --jq '.object.sha' 2>/dev/null || true)
# If tag points to an annotated tag object, dereference to the commit
if [ -n "$BASELINE_REF" ]; then
OBJ_TYPE=$(gh api "repos/$REPO/git/tags/$BASELINE_REF" --jq '.object.type' 2>/dev/null || echo "commit")
if [ "$OBJ_TYPE" = "commit" ]; then
BASELINE_REF=$(gh api "repos/$REPO/git/tags/$BASELINE_REF" --jq '.object.sha' 2>/dev/null || echo "$BASELINE_REF")
fi
fi
if [ -z "$BASELINE_REF" ]; then
echo "::error::Could not resolve release tag $RELEASE_TAG to a commit"
exit 1
fi
echo "Release tag: $RELEASE_TAG"
echo "Release commit (baseline): $BASELINE_REF"
echo "::endgroup::"
# --- Step 3: Read last successful feature ref from charts repo ---
echo "::group::Reading persisted state"
LAST_FEATURE_REF=""
STATE_URL="https://raw.githubusercontent.com/decofe/reth-bench-charts/state/state/release-last-feature-ref"
if RAW=$(curl -sfL -H "Authorization: token ${DEREK_TOKEN}" "$STATE_URL"); then
LAST_FEATURE_REF=$(echo "$RAW" | tr -d '[:space:]')
echo "Previous feature ref: $LAST_FEATURE_REF"
else
echo "No persisted state found (first run)"
fi
echo "::endgroup::"
# --- Step 4: Skip logic ---
echo "::group::Resolving skip logic"
SHOULD_SKIP="false"
if [ -n "$LAST_FEATURE_REF" ] && [ "$LAST_FEATURE_REF" = "$FEATURE_REF" ]; then
if [ "$FORCE" = "true" ] || [ "$FORCE" = "--force" ]; then
echo "No new nightly, but force=true — running anyway"
else
SHOULD_SKIP="true"
echo "No new nightly since last release regression run — will skip"
fi
else
echo "New nightly detected or first run"
fi
echo "Baseline: $BASELINE_REF ($RELEASE_TAG)"
echo "Feature: $FEATURE_REF"
echo "Skip: $SHOULD_SKIP"
echo "::endgroup::"
# --- Step 5: Write outputs ---
{
echo "baseline-ref=$BASELINE_REF"
echo "feature-ref=$FEATURE_REF"
echo "should-skip=$SHOULD_SKIP"
echo "is-stale=false"
echo "stale-age-hours=0"
echo "nightly-created="
echo "long-running=false"
echo "release-tag=$RELEASE_TAG"
} >> "$GITHUB_OUTPUT"
exit 0
fi
# ==========================================================================
# Nightly mode: query latest Docker nightly build (original logic)
# ==========================================================================
@@ -278,15 +173,15 @@ else
fi
echo "::endgroup::"
# --- Step 3: Read last successful feature ref from charts repo ---
echo "::group::Reading persisted state"
# --- Step 3: Read last successful feature ref from cache ---
echo "::group::Reading cached state"
LAST_FEATURE_REF=""
STATE_URL="https://raw.githubusercontent.com/decofe/reth-bench-charts/state/state/nightly-last-feature-ref"
if RAW=$(curl -sfL -H "Authorization: token ${DEREK_TOKEN}" "$STATE_URL"); then
LAST_FEATURE_REF=$(echo "$RAW" | tr -d '[:space:]')
STATE_FILE=".nightly-state/last-feature-ref"
if [ -f "$STATE_FILE" ]; then
LAST_FEATURE_REF=$(tr -d '[:space:]' < "$STATE_FILE")
echo "Previous feature ref: $LAST_FEATURE_REF"
else
echo "No persisted state found (first run)"
echo "No cached state found (first run)"
fi
echo "::endgroup::"

View File

@@ -1,150 +0,0 @@
#!/usr/bin/env python3
"""Upload bench-scheduled summary.json results to ClickHouse.
Reads the summary JSON produced by bench-reth-summary.py and inserts a row
into the bench_dual_comparisons table so the PM dashboard can display results.
Usage:
bench-upload-clickhouse.py \
--summary <summary.json> \
--workflow-name <name> \
--chain <chain>
Environment variables:
CLICKHOUSE_HOST ClickHouse host URL
CLICKHOUSE_USER ClickHouse username
CLICKHOUSE_PASSWORD ClickHouse password
CLICKHOUSE_DATABASE ClickHouse database (default: "default")
"""
import argparse
import json
import os
import sys
import urllib.request
import urllib.error
def main():
parser = argparse.ArgumentParser(description="Upload benchmark results to ClickHouse")
parser.add_argument("--summary", required=True, help="Path to summary.json")
parser.add_argument("--workflow-name", required=True, help="Workflow name for ClickHouse")
parser.add_argument("--chain", default="mainnet", help="Chain name")
parser.add_argument("--grafana-url", default="", help="Grafana dashboard URL")
parser.add_argument("--github-diff-url", default="", help="GitHub diff URL")
parser.add_argument("--job-url", default="", help="CI job URL")
args = parser.parse_args()
ch_host = os.environ.get("CLICKHOUSE_HOST", "")
ch_user = os.environ.get("CLICKHOUSE_USER", "")
ch_password = os.environ.get("CLICKHOUSE_PASSWORD", "")
ch_database = os.environ.get("CLICKHOUSE_DATABASE", "default")
ch_table = "bench_dual_comparisons"
if not ch_host or not ch_user or not ch_password:
print("Missing ClickHouse credentials, skipping upload", file=sys.stderr)
sys.exit(0)
with open(args.summary) as f:
summary = json.load(f)
baseline = summary["baseline"]
feature = summary["feature"]
b_stats = baseline["stats"]
f_stats = feature["stats"]
changes = summary["changes"]
blocks = summary["blocks"]
# Extract wait time data
wait_times = summary.get("wait_times", {})
def wait_mean(field):
wt = wait_times.get(field, {})
b = wt.get("baseline", {}).get("mean_ms", 0.0)
f = wt.get("feature", {}).get("mean_ms", 0.0)
return b, f
b_persist, f_persist = wait_mean("persistence_wait_us")
b_exec_cache, f_exec_cache = wait_mean("execution_cache_wait_us")
b_sparse, f_sparse = wait_mean("sparse_trie_wait_us")
# gas_per_second: summary uses mean_mgas_s (Mgas/s), ClickHouse stores gas/s
b_gas_per_second = b_stats["mean_mgas_s"] * 1_000_000
f_gas_per_second = f_stats["mean_mgas_s"] * 1_000_000
mean_change = changes.get("mean", {}).get("pct", 0.0)
gas_change = changes.get("mgas_s", {}).get("pct", 0.0)
latency_improved = 1 if mean_change < 0 else 0
throughput_improved = 1 if gas_change > 0 else 0
big_blocks = "true" if summary.get("big_blocks", False) else "false"
warmup_blocks = summary.get("warmup_blocks", 0) or 0
def esc(s):
return str(s).replace("'", "\\'")
insert = f"""
INSERT INTO {ch_database}.{ch_table} (
workflow_name, chain,
baseline_ref, baseline_commit,
feature_ref, feature_commit,
blocks,
baseline_total_latency_ms, baseline_gas_per_second,
baseline_latency_mean_ms, baseline_latency_median_ms,
baseline_latency_p90_ms, baseline_latency_p99_ms,
feature_total_latency_ms, feature_gas_per_second,
feature_latency_mean_ms, feature_latency_median_ms,
feature_latency_p90_ms, feature_latency_p99_ms,
mean_latency_change_percent, gas_per_second_change_percent,
latency_improved, throughput_improved,
warmup_blocks, big_blocks,
grafana_benchmark_url, github_diff_url, argo_workflow_url,
baseline_persistence_wait_mean_ms, baseline_execution_cache_wait_mean_ms,
baseline_sparse_trie_wait_mean_ms,
feature_persistence_wait_mean_ms, feature_execution_cache_wait_mean_ms,
feature_sparse_trie_wait_mean_ms
) VALUES (
'{esc(args.workflow_name)}', '{esc(args.chain)}',
'{esc(baseline["ref"])}', '{esc(baseline["ref"])}',
'{esc(feature["ref"])}', '{esc(feature["ref"])}',
{blocks},
{b_stats.get("wall_clock_s", 0) * 1000}, {b_gas_per_second},
{b_stats["mean_ms"]}, {b_stats["p50_ms"]},
{b_stats["p90_ms"]}, {b_stats["p99_ms"]},
{f_stats.get("wall_clock_s", 0) * 1000}, {f_gas_per_second},
{f_stats["mean_ms"]}, {f_stats["p50_ms"]},
{f_stats["p90_ms"]}, {f_stats["p99_ms"]},
{mean_change}, {gas_change},
{latency_improved}, {throughput_improved},
{warmup_blocks}, '{big_blocks}',
'{esc(args.grafana_url)}', '{esc(args.github_diff_url)}', '{esc(args.job_url)}',
{b_persist}, {b_exec_cache}, {b_sparse},
{f_persist}, {f_exec_cache}, {f_sparse}
);
"""
# Build ClickHouse HTTP URL (credentials via headers, never in URL)
host = ch_host.rstrip("/")
if not host.startswith("http"):
host = f"https://{host}:8443"
url = f"{host}/?database={ch_database}"
req = urllib.request.Request(url, data=insert.encode("utf-8"), method="POST")
req.add_header("Content-Type", "text/plain")
req.add_header("X-ClickHouse-User", ch_user)
req.add_header("X-ClickHouse-Key", ch_password)
try:
with urllib.request.urlopen(req) as resp:
body = resp.read().decode("utf-8")
if body.strip():
print(f"ClickHouse response: {body}")
print(f"Successfully uploaded benchmark results to ClickHouse ({args.workflow_name})")
except urllib.error.HTTPError as e:
body = e.read().decode("utf-8")
print(f"ClickHouse upload failed ({e.code}): {body}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()

View File

@@ -39,25 +39,10 @@ function loadSamplyUrls(workDir) {
return urls;
}
function balModeLabel(mode) {
switch (mode) {
case 'true':
case 'feature':
case 'baseline':
return mode;
case 'both':
return 'true';
default:
return '';
}
}
function blocksLabel(summary) {
const parts = [];
if (summary.big_blocks) {
parts.push({ key: 'Big Blocks', value: summary.blocks });
const balMode = balModeLabel(summary.bal_mode || summary.bal || process.env.BENCH_BAL || 'false');
if (balMode) parts.push({ key: 'BAL', value: balMode });
} else {
const warmup = summary.warmup_blocks || process.env.BENCH_WARMUP_BLOCKS || '';
if (warmup) parts.push({ key: 'Warmup', value: warmup });
@@ -65,7 +50,6 @@ function blocksLabel(summary) {
}
const cores = process.env.BENCH_CORES || '0';
if (cores !== '0') parts.push({ key: 'Cores', value: cores });
if (summary.wait_time) parts.push({ key: 'Wait time', value: summary.wait_time });
return parts;
}

414
.github/scripts/build_pgo_bolt.sh vendored Executable file
View File

@@ -0,0 +1,414 @@
#!/usr/bin/env bash
#
# Full PGO+BOLT optimized build for reth using real reth-bench workloads.
#
# Phases:
# 1. Build PGO-instrumented reth, run reth-bench → collect PGO profiles
# 2. Build BOLT-instrumented reth (with PGO), run reth-bench → collect BOLT profiles
# 3. Build final PGO+BOLT optimized binary
#
# Required environment variables:
# DATADIR - Path to reth datadir (must already contain chain data)
# RPC_URL - Source RPC URL for reth-bench to fetch payloads from
#
# Optional environment variables:
# PGO_BLOCKS - Number of blocks for PGO profiling (default: 20)
# BOLT_BLOCKS - Number of blocks for BOLT profiling (default: 20)
# SKIP_BOLT - Temporarily skip BOLT phases (default: false)
# STRIP_SYMBOLS - Strip debug symbols from output binary (default: true)
# COLLECT_PGO_ONLY - Stop after producing merged.profdata (default: false)
# PGO_PROFDATA - Path to pre-collected merged.profdata (optional)
# PROFILE - Cargo profile (default: maxperf-symbols)
# FEATURES - Cargo features (default: jemalloc,asm-keccak,min-debug-logs)
# TARGET - Target triple (default: auto-detected)
# EXTRA_RUSTFLAGS - Additional RUSTFLAGS (e.g. -C target-cpu=x86-64-v3)
#
# Output:
# target/$PROFILE_DIR/reth — final optimized binary
set -euo pipefail
gha_section_start() {
local title="$1"
if [ -n "${GITHUB_ACTIONS:-}" ]; then
echo "::group::$title"
else
echo ""
echo "=== $title ==="
fi
}
gha_section_end() {
if [ -n "${GITHUB_ACTIONS:-}" ]; then
echo "::endgroup::"
fi
}
cd "$(dirname "$0")/../.."
# ── Configuration ──────────────────────────────────────────────────────────────
PGO_BLOCKS="${PGO_BLOCKS:-20}"
BOLT_BLOCKS="${BOLT_BLOCKS:-20}"
SKIP_BOLT="${SKIP_BOLT:-false}"
STRIP_SYMBOLS="${STRIP_SYMBOLS:-true}"
COLLECT_PGO_ONLY="${COLLECT_PGO_ONLY:-false}"
PROFILE="${PROFILE:-maxperf-symbols}"
FEATURES="${FEATURES:-jemalloc,asm-keccak,min-debug-logs}"
TARGET="${TARGET:-$(rustc -Vv | grep host | cut -d' ' -f2)}"
BASE_RUSTFLAGS="${RUSTFLAGS:-}"
EXTRA_RUSTFLAGS="${EXTRA_RUSTFLAGS:-}"
COMBINED_RUSTFLAGS="$BASE_RUSTFLAGS $EXTRA_RUSTFLAGS"
PGO_PROFDATA="${PGO_PROFDATA:-}"
DATADIR="${DATADIR:-}"
RPC_URL="${RPC_URL:-}"
SKIP_BOLT_BOOL=false
if [[ "${SKIP_BOLT,,}" == "true" || "$SKIP_BOLT" == "1" ]]; then
SKIP_BOLT_BOOL=true
fi
STRIP_SYMBOLS_BOOL=false
if [[ "${STRIP_SYMBOLS,,}" == "true" || "$STRIP_SYMBOLS" == "1" ]]; then
STRIP_SYMBOLS_BOOL=true
fi
COLLECT_PGO_ONLY_BOOL=false
if [[ "${COLLECT_PGO_ONLY,,}" == "true" || "$COLLECT_PGO_ONLY" == "1" ]]; then
COLLECT_PGO_ONLY_BOOL=true
fi
USE_PRECOLLECTED_PGO=false
if [ -n "$PGO_PROFDATA" ]; then
if [ ! -f "$PGO_PROFDATA" ]; then
echo "error: PGO_PROFDATA points to a missing file: $PGO_PROFDATA"
exit 1
fi
USE_PRECOLLECTED_PGO=true
fi
NEEDS_BENCH_WORKLOAD=true
if [ "$USE_PRECOLLECTED_PGO" = true ] && [ "$SKIP_BOLT_BOOL" = true ]; then
NEEDS_BENCH_WORKLOAD=false
fi
if [ "$NEEDS_BENCH_WORKLOAD" = true ]; then
: "${DATADIR:?DATADIR must be set to the reth data directory}"
: "${RPC_URL:?RPC_URL must be set}"
fi
if [[ "$PROFILE" == dev ]]; then
PROFILE_DIR=debug
else
PROFILE_DIR=$PROFILE
fi
MANIFEST_PATH="bin/reth"
LLVM_VERSION=$(rustc -Vv | grep -oP 'LLVM version: \K\d+')
PGO_DIR="$PWD/target/pgo-profiles"
BOLT_DIR="$PWD/target/bolt-profiles"
CARGO_ARGS=(--profile "$PROFILE" --features "$FEATURES" --manifest-path "$MANIFEST_PATH/Cargo.toml" --bin "reth" --locked)
# Enable debug symbols for BOLT (requires symbols to reorder code).
# Strip them at the end.
PROFILE_UPPER=$(echo "$PROFILE" | tr '[:lower:]-' '[:upper:]_')
export "CARGO_PROFILE_${PROFILE_UPPER}_STRIP=debuginfo"
gha_section_start "Full PGO+BOLT Build"
echo "Binary: reth"
echo "Manifest: $MANIFEST_PATH"
echo "Target: $TARGET"
echo "Profile: $PROFILE"
echo "Features: $FEATURES"
echo "LLVM: $LLVM_VERSION"
echo "PGO blocks: $PGO_BLOCKS"
echo "BOLT blocks: $BOLT_BLOCKS"
echo "Skip BOLT: $SKIP_BOLT"
echo "Strip symbols: $STRIP_SYMBOLS"
echo "Collect only: $COLLECT_PGO_ONLY"
echo "PGO profdata: ${PGO_PROFDATA:-<collect with reth-bench>}"
echo "RUSTFLAGS: ${BASE_RUSTFLAGS:-<unset>}"
echo "EXTRA_RUSTFLAGS: ${EXTRA_RUSTFLAGS:-<unset>}"
if [ "$NEEDS_BENCH_WORKLOAD" = true ]; then
echo "Datadir: $DATADIR"
echo "RPC URL: $RPC_URL"
else
echo "Datadir: <not required>"
echo "RPC URL: <not required>"
fi
gha_section_end
# ── Prerequisites ──────────────────────────────────────────────────────────────
gha_section_start "Installing prerequisites"
rustup component add llvm-tools-preview
LLVM_PROFDATA=$(find "$(rustc --print sysroot)" -name llvm-profdata -type f | head -1)
if [ -z "$LLVM_PROFDATA" ]; then
echo "error: llvm-profdata not found"
exit 1
fi
install_bolt() {
if command -v llvm-bolt &>/dev/null; then
echo "BOLT already installed"
return
fi
echo "Installing BOLT from apt.llvm.org..."
wget -qO- https://apt.llvm.org/llvm-snapshot.gpg.key | sudo tee /etc/apt/trusted.gpg.d/apt.llvm.org.asc >/dev/null
CODENAME=$(lsb_release -cs)
echo "deb http://apt.llvm.org/$CODENAME/ llvm-toolchain-$CODENAME-$LLVM_VERSION main" | sudo tee /etc/apt/sources.list.d/llvm.list >/dev/null
sudo apt-get update -qq
sudo apt-get install -y -qq "bolt-$LLVM_VERSION"
sudo ln -sf "/usr/bin/llvm-bolt-$LLVM_VERSION" /usr/local/bin/llvm-bolt
sudo ln -sf "/usr/bin/merge-fdata-$LLVM_VERSION" /usr/local/bin/merge-fdata
}
if [ "$SKIP_BOLT_BOOL" = true ]; then
echo "Skipping BOLT installation (SKIP_BOLT=$SKIP_BOLT)"
else
install_bolt
fi
gha_section_end
if [ "$NEEDS_BENCH_WORKLOAD" = true ]; then
# Build reth-bench once (non-instrumented) — reused for both phases.
gha_section_start "Building reth-bench"
RUSTFLAGS="$COMBINED_RUSTFLAGS" \
cargo build --profile "$PROFILE" --features "$FEATURES" \
--manifest-path bin/reth-bench/Cargo.toml --bin reth-bench --locked
RETH_BENCH_BIN="$(find target -name reth-bench -type f -executable | head -1)"
echo "reth-bench: $RETH_BENCH_BIN"
gha_section_end
else
gha_section_start "Building reth-bench"
echo "Skipping reth-bench build (pre-collected PGO with SKIP_BOLT=true)"
gha_section_end
fi
# ── Helpers ────────────────────────────────────────────────────────────────────
RETH_PID=
cleanup() {
if [ -n "${RETH_PID:-}" ] && kill -0 "$RETH_PID" 2>/dev/null; then
echo "Stopping reth (pid $RETH_PID)..."
sudo kill "$RETH_PID" 2>/dev/null || true
for i in $(seq 1 60); do
sudo kill -0 "$RETH_PID" 2>/dev/null || break
if [ $((i % 10)) -eq 0 ]; then
echo " waiting... (${i}s)"
fi
sleep 1
done
sudo kill -9 "$RETH_PID" 2>/dev/null || true
fi
}
trap cleanup EXIT
# Start reth, wait for RPC, run reth-bench, then stop reth.
# Arguments: $1 = reth binary path, $2 = number of blocks, $3 = log label
run_bench_workload() {
local reth_bin="$1" blocks="$2" label="$3"
local http_port=8545 authrpc_port=8551
echo "--- Starting reth ($label) ---"
sudo "$reth_bin" node \
--datadir "$DATADIR" \
--log.file.directory "/tmp/reth-${label}-logs" \
--engine.accept-execution-requests-hash \
--http --http.port "$http_port" \
--authrpc.port "$authrpc_port" \
--disable-discovery --no-persist-peers \
> "/tmp/reth-${label}.log" 2>&1 &
RETH_PID=$!
echo "Waiting for reth RPC..."
for i in $(seq 1 120); do
if curl -sf "http://127.0.0.1:$http_port" -X POST \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' \
> /dev/null 2>&1; then
echo "reth is ready after ${i}s"
break
fi
if [ "$i" -eq 120 ]; then
echo "error: reth failed to start within 120s"
cat "/tmp/reth-${label}.log"
exit 1
fi
sleep 1
done
echo "Running reth-bench ($blocks blocks)..."
"$RETH_BENCH_BIN" new-payload-fcu \
--rpc-url "$RPC_URL" \
--engine-rpc-url "http://127.0.0.1:$authrpc_port" \
--jwt-secret "$DATADIR/jwt.hex" \
--advance "$blocks" \
--reth-new-payload 2>&1 | sed -u "s/^/[$label] /"
echo "Stopping reth ($label)..."
sudo kill "$RETH_PID" 2>/dev/null || true
for i in $(seq 1 60); do
sudo kill -0 "$RETH_PID" 2>/dev/null || break
sleep 1
done
sudo kill -9 "$RETH_PID" 2>/dev/null || true
RETH_PID=
}
publish_binary() {
local source_bin="$1"
for out in "target/$TARGET/$PROFILE_DIR" "target/$PROFILE_DIR"; do
local destination="$out/reth"
mkdir -p "$out"
# Skip copying when source and destination resolve to the same inode.
if [ -e "$destination" ] && [ "$source_bin" -ef "$destination" ]; then
continue
fi
cp "$source_bin" "$destination"
done
}
if [ "$USE_PRECOLLECTED_PGO" = true ]; then
gha_section_start "Phase 1: Using Pre-Collected PGO Profile"
rm -rf "$PGO_DIR"
mkdir -p "$PGO_DIR"
cp "$PGO_PROFDATA" "$PGO_DIR/merged.profdata"
echo "Using pre-collected profile: $PGO_PROFDATA"
echo "PGO profile: $PGO_DIR/merged.profdata ($(ls -lh "$PGO_DIR/merged.profdata" | awk '{print $5}'))"
gha_section_end
else
# ── Phase 1: PGO profile collection ───────────────────────────────────────
gha_section_start "Phase 1: PGO Profile Collection"
rm -rf "$PGO_DIR"
mkdir -p "$PGO_DIR"
echo "Building PGO-instrumented binary..."
RUSTFLAGS="-Cprofile-generate=$PGO_DIR -Crelocation-model=pic $COMBINED_RUSTFLAGS" \
cargo build "${CARGO_ARGS[@]}" --target "$TARGET"
PGO_RETH_BIN="$PWD/target/$TARGET/$PROFILE_DIR/reth"
echo "Instrumented binary: $PGO_RETH_BIN ($(ls -lh "$PGO_RETH_BIN" | awk '{print $5}'))"
run_bench_workload "$PGO_RETH_BIN" "$PGO_BLOCKS" "pgo"
# Fix ownership if reth ran as root.
sudo chown -R "$(id -un):$(id -gn)" "$PGO_DIR" 2>/dev/null || true
# Merge PGO profiles.
echo "Merging PGO profiles..."
PROFRAW_COUNT=$(find "$PGO_DIR" -name '*.profraw' | wc -l)
echo "Found $PROFRAW_COUNT .profraw files"
if [ "$PROFRAW_COUNT" -eq 0 ]; then
echo "error: no .profraw files — instrumented binary did not produce profiles"
exit 1
fi
"$LLVM_PROFDATA" merge -o "$PGO_DIR/merged.profdata" "$PGO_DIR"/*.profraw
echo "PGO profile: $PGO_DIR/merged.profdata ($(ls -lh "$PGO_DIR/merged.profdata" | awk '{print $5}'))"
gha_section_end
fi
if [ "$COLLECT_PGO_ONLY_BOOL" = true ]; then
gha_section_start "PGO Collection Complete"
echo "COLLECT_PGO_ONLY=true, skipping PGO/BOLT optimized binary build"
echo "Profile: $PGO_DIR/merged.profdata"
gha_section_end
exit 0
fi
if [ "$SKIP_BOLT_BOOL" = true ]; then
gha_section_start "BOLT Phase Skipped"
echo "SKIP_BOLT=$SKIP_BOLT, building PGO-only binary"
echo "Building PGO-optimized binary..."
RUSTFLAGS="-Cprofile-use=$PGO_DIR/merged.profdata $COMBINED_RUSTFLAGS" \
cargo build "${CARGO_ARGS[@]}" --target "$TARGET"
BUILT_BIN="$PWD/target/$TARGET/$PROFILE_DIR/reth"
if [ "$STRIP_SYMBOLS_BOOL" = true ]; then
echo "Stripping debug symbols..."
strip "$BUILT_BIN"
else
echo "Skipping strip (STRIP_SYMBOLS=$STRIP_SYMBOLS)"
fi
publish_binary "$BUILT_BIN"
gha_section_end
else
# ── Phase 2: BOLT profile collection (with PGO) ──────────────────────────
gha_section_start "Phase 2: BOLT Profile Collection (with PGO)"
rm -rf "$BOLT_DIR"
mkdir -p "$BOLT_DIR"
echo "Building BOLT-instrumented binary with PGO..."
# --emit-relocs preserves relocation entries in the binary, required by llvm-bolt -instrument
RUSTFLAGS="-Cprofile-use=$PGO_DIR/merged.profdata -Clink-arg=-Wl,--emit-relocs $COMBINED_RUSTFLAGS" \
cargo build "${CARGO_ARGS[@]}" --target "$TARGET"
# Instrument with BOLT
BUILT_BIN="$PWD/target/$TARGET/$PROFILE_DIR/reth"
BOLT_INSTRUMENTED_BIN="$BUILT_BIN-bolt-instrumented"
echo "Instrumenting binary with BOLT..."
# --skip-funcs: skip compiler-generated drop_in_place functions that BOLT can't handle
# as split functions in relocation mode (triggered by --emit-relocs)
llvm-bolt "$BUILT_BIN" \
-instrument \
--instrumentation-file-append-pid \
--instrumentation-file="$BOLT_DIR/prof" \
--skip-funcs='.*drop_in_place.*' \
-o "$BOLT_INSTRUMENTED_BIN"
echo "BOLT-instrumented binary: $BOLT_INSTRUMENTED_BIN ($(ls -lh "$BOLT_INSTRUMENTED_BIN" | awk '{print $5}'))"
run_bench_workload "$BOLT_INSTRUMENTED_BIN" "$BOLT_BLOCKS" "bolt"
# Fix ownership for BOLT profiles
sudo chown -R "$(id -un):$(id -gn)" "$BOLT_DIR" 2>/dev/null || true
# Merge BOLT profiles
echo "Merging BOLT profiles..."
FDATA_COUNT=$(find "$BOLT_DIR" -name '*.fdata' | wc -l)
echo "Found $FDATA_COUNT .fdata files"
if [ "$FDATA_COUNT" -eq 0 ]; then
echo "error: no .fdata files — BOLT-instrumented binary did not produce profiles"
exit 1
fi
merge-fdata "$BOLT_DIR"/*.fdata > "$BOLT_DIR/merged.fdata"
echo "BOLT profile: $BOLT_DIR/merged.fdata ($(ls -lh "$BOLT_DIR/merged.fdata" | awk '{print $5}'))"
gha_section_end
# ── Phase 3: Final optimized build ───────────────────────────────────────
gha_section_start "Phase 3: Final PGO+BOLT Optimized Build"
echo "Building PGO-optimized binary..."
# --emit-relocs preserves relocation entries in the binary, required by llvm-bolt for code reordering
RUSTFLAGS="-Cprofile-use=$PGO_DIR/merged.profdata -Clink-arg=-Wl,--emit-relocs $COMBINED_RUSTFLAGS" \
cargo build "${CARGO_ARGS[@]}" --target "$TARGET"
BUILT_BIN="$PWD/target/$TARGET/$PROFILE_DIR/reth"
OPTIMIZED_BIN="$BUILT_BIN-bolt-optimized"
echo "Optimizing with BOLT..."
llvm-bolt "$BUILT_BIN" \
-o "$OPTIMIZED_BIN" \
--data "$BOLT_DIR/merged.fdata" \
-reorder-blocks=ext-tsp \
-reorder-functions=cdsort \
-split-functions \
-split-all-cold \
-dyno-stats \
-icf=1 \
-use-gnu-stack \
--skip-funcs='.*drop_in_place.*'
if [ "$STRIP_SYMBOLS_BOOL" = true ]; then
echo "Stripping debug symbols..."
strip "$OPTIMIZED_BIN"
else
echo "Skipping strip (STRIP_SYMBOLS=$STRIP_SYMBOLS)"
fi
publish_binary "$OPTIMIZED_BIN"
gha_section_end
fi
gha_section_start "Build Complete"
ls -lh "target/$PROFILE_DIR/reth"
echo "Output: target/$PROFILE_DIR/reth"
gha_section_end

View File

@@ -1,5 +1,5 @@
#!/usr/bin/env bash
set -uxo pipefail
set -uo pipefail
crates_to_check=(
reth-network-peers

View File

@@ -1,5 +1,5 @@
#!/usr/bin/env bash
set -uxo pipefail
set -uo pipefail
readarray -t crates < <(
cargo metadata --format-version=1 --no-deps | jq -r '.packages[].name' | grep '^reth' | sort

Some files were not shown because too many files have changed in this diff Show More