feat: add reth-bb binary with multi-segment big block execution support#23140
Conversation
|
✅ Benchmark complete! View job Benchmark Results
20 big blocks Wait Time BreakdownPersistence Wait
Trie Cache Update Wait
Execution Cache Update Wait
ChartsGrafana Dashboard |
…VM env swapping Adds env_switches parameter to reth_newPayload that allows swapping the EVM block environment at specific transaction boundaries during block execution. This enables packing multiple virtual blocks into a single big-block while retaining per-block post-execution semantics (withdrawals, rewards, system calls) at each switch point. At each switch boundary the executor is finalized (applying post-execution changes), the DB is reclaimed, a new EVM+executor is created with the switched env on the same State<DB>, and execution continues. Results (receipts, gas, requests) are aggregated across all segments. Amp-Thread-ID: https://ampcode.com/threads/T-019d0bcd-e703-720a-90ec-e8e8abc41ac3 Co-authored-by: Amp <amp@ampcode.com>
Rewrites generate-big-block as a pure data assembly tool that no longer requires a running reth node. Instead of collecting transactions and calling testing_buildBlockV1, it fetches N consecutive blocks from an RPC, merges their transactions into a single payload, and records env_switches at each block boundary. The new on-disk format (BigBlockPayload) contains the merged ExecutionData and Vec<(usize, ExecutionData)> env_switches. During replay, reth_newPayload uses env_switches to swap the EVM environment at each block boundary, executing multiple virtual blocks as one. Changes: - generate_big_block.rs: Remove engine API, JWT, testing RPC, retry logic, pipelining. New CLI: --rpc-url, --from-block, --count, --output-dir. Saves BigBlockPayload JSON files. - replay_payloads.rs: Load BigBlockPayload format (with fallback to legacy ExecutionPayloadEnvelopeV4). Pass env_switches through reth_newPayload. Detect big_block_*.json filenames. - mod.rs: Re-export BigBlockPayload, update subcommand docs. - helpers.rs: Suppress dead_code warning on parse_gas_limit. Amp-Thread-ID: https://ampcode.com/threads/T-019d0c31-527f-7231-8936-8adf974c4464 Co-authored-by: Amp <amp@ampcode.com>
|
Treats B256::ZERO block_hash as a sentinel meaning 'skip hash validation'. Used by artificial merged blocks (big blocks with env_switches) that have modified headers and intentionally don't carry a valid block hash. Amp-Thread-ID: https://ampcode.com/threads/T-019d0c62-bc03-753e-92ca-30e702563567 Co-authored-by: Amp <amp@ampcode.com>
Blob transactions (EIP-4844, type 3) are removed from merged payloads since their versioned hashes cannot be reconciled across multiple blocks. The sidecar's versioned_hashes is cleared to match. Amp-Thread-ID: https://ampcode.com/threads/T-019d0c62-bc03-753e-92ca-30e702563567 Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-019d0c62-bc03-753e-92ca-30e702563567 Co-authored-by: Amp <amp@ampcode.com>
excess_blob_gas is validated against the parent block's excess_blob_gas and blob_gas_used. Zeroing it causes validation failure since the parent has a non-zero excess_blob_gas. Only blob_gas_used should be zeroed. Amp-Thread-ID: https://ampcode.com/threads/T-019d0c62-bc03-753e-92ca-30e702563567 Co-authored-by: Amp <amp@ampcode.com>
Receipt cumulative gas counters reset at each env_switch segment boundary, causing gas_used and state_root validation to fail. These checks are not meaningful for artificial merged big blocks used in benchmarking. Amp-Thread-ID: https://ampcode.com/threads/T-019d0c62-bc03-753e-92ca-30e702563567 Co-authored-by: Amp <amp@ampcode.com>
… segments When switching executors at env_switch boundaries: 1. Reset last_sent_len so receipts from the new executor are sent 2. Use senders.len() as the global tx index since executor.receipts() resets per segment while senders accumulates across all segments Amp-Thread-ID: https://ampcode.com/threads/T-019d0c62-bc03-753e-92ca-30e702563567 Co-authored-by: Amp <amp@ampcode.com>
The node rejects forkchoice updates with a zero head_block_hash. Big blocks use B256::ZERO as a sentinel for artificial blocks. Amp-Thread-ID: https://ampcode.com/threads/T-019d0c62-bc03-753e-92ca-30e702563567 Co-authored-by: Amp <amp@ampcode.com>
…ck hash - Replace full post-execution validation skip with selective skip: state root, header, parent, and hashed state checks are always performed; only receipt-based checks (gas_used, receipts_root, logs_bloom) are skipped for env_switches blocks since receipt cumulative gas counters reset at segment boundaries. - Always check state root matches header (remove has_env_switches guard). - Compute real block hash in generate-big-block instead of B256::ZERO, enabling proper FCU and sequential big block replay. - Remove zero-hash sentinel in payload validator (no longer needed). - Add fallback hash computation in replay for legacy zero-hash files. - Always run FCU (remove zero-hash skip). Amp-Thread-ID: https://ampcode.com/threads/T-019d0f93-3635-770d-8e0f-28598d8c3142 Co-authored-by: Amp <amp@ampcode.com>
…ipping Stripping blob transactions from big blocks changes the execution state, causing state root mismatches. Instead, keep all transactions (including blobs) and merge the versioned hashes from all constituent blocks into the base sidecar. Sum blob_gas_used across all blocks. Amp-Thread-ID: https://ampcode.com/threads/T-019d0f93-3635-770d-8e0f-28598d8c3142 Co-authored-by: Amp <amp@ampcode.com>
switch_envs now carries the full ExecutionData per segment so that context_for_payload() is called with the correct block data at each env_switch boundary instead of reusing the base block's context. This ensures each segment gets the right parent_beacon_block_root and withdrawals. Amp-Thread-ID: https://ampcode.com/threads/T-019d108b-03b4-771f-8120-60167805a390 Co-authored-by: Amp <amp@ampcode.com>
All generated payloads now include a computed block hash. Remove the code that detected B256::ZERO hashes and recomputed them locally. Amp-Thread-ID: https://ampcode.com/threads/T-019d108b-03b4-771f-8120-60167805a390 Co-authored-by: Amp <amp@ampcode.com>
Add --num-big-blocks flag to generate-big-block. When generating multiple sequential big blocks, each block's parent_hash is set to the previous big block's computed hash, enabling sequential replay via replay-payloads. Amp-Thread-ID: https://ampcode.com/threads/T-019d108b-03b4-771f-8120-60167805a390 Co-authored-by: Amp <amp@ampcode.com>
Skip validate_header_against_parent for env_switch blocks since merged big blocks span multiple real block numbers and the parent number check would fail. Amp-Thread-ID: https://ampcode.com/threads/T-019d108b-03b4-771f-8120-60167805a390 Co-authored-by: Amp <amp@ampcode.com>
Three bugs fixed in multi-segment big block execution: 1. State hook not re-attached at segment boundaries: new executors created after env_switches lacked state hooks, so the sparse trie task only received segment 0's state changes. 2. Initial segment context: when env_switch at tx_idx=0 carries the original base block's ExecutionData, use it for both the EVM env and execution context (parent_hash, withdrawals) instead of the merged payload. 3. Missing virtual block hashes: BLOCKHASH opcodes in segments 1+ returned zero for virtual block numbers not yet in the DB. Fixed by seeding each finished segment's block hash into the State's block_hashes cache before creating the next segment's EVM. Co-Authored-By: Brian Picciano <933154+mediocregopher@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d1105-12e2-754d-b61d-ae7d8c4fd407 Co-authored-by: Amp <amp@ampcode.com>
…dation - Use non-finishing state hooks for intermediate segments so the sparse trie task doesn't finalize when segment 0's executor is dropped. Only the last segment's hook triggers FinishedStateUpdates. - Skip full header validation for env_switches blocks since the merged header has aggregated values (blob_gas_used, gas_limit) that exceed single-block consensus limits. - Fix double-counting of base block's blob data in generate_big_block by skipping env_switch[0] (the base clone) when merging. Co-Authored-By: Brian Picciano <933154+mediocregopher@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d1105-12e2-754d-b61d-ae7d8c4fd407 Co-authored-by: Amp <amp@ampcode.com>
…ob support Add support for replaying chains of sequential big blocks (each merging N real blocks). Key changes: - Sequential block numbering (from_block + idx) so make_canonical can walk the chain without gaps - prior_block_hashes plumbed through the full pipeline (generate → replay → RPC → engine tree → payload_validator) so BLOCKHASH returns correct hashes for blocks merged into earlier big blocks - Merge blob versioned hashes and sum blob_gas_used across constituent blocks; skip validate_block_pre_execution for env_switches to avoid per-block blob gas limit checks - Downgrade noisy info! logs to debug! in payload_validator Tested: 10 sequential ~1 Ggas hoodi big blocks (10 Ggas total) at 0.78 Ggas/s execution throughput. Co-Authored-By: Brian Picciano <933154+mediocregopher@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d116f-b9c2-7160-94a1-120d413fdf3b Co-authored-by: Amp <amp@ampcode.com>
Flatten prior_block_hashes from a separate RPC parameter into the EnvSwitch struct. This removes redundant Option wrapping across the RPC API, engine message, and payload validator layers. - Move prior_block_hashes field into EnvSwitch - Remove dedicated RPC parameter and message field - Simplify generate/replay to set the field on each EnvSwitch - Downgrade noisy payload_validator logs to debug Amp-Thread-ID: https://ampcode.com/threads/T-019d0bb3-8248-7638-8d0c-e8bb90cbce6a Co-authored-by: Amp <amp@ampcode.com>
…ble 4844 validation Add --chain flag to generate-big-block to resolve correct BlobParams per chain/timestamp. Derive excess_blob_gas for sequential big blocks using next_block_excess_blob_gas_osaka so validate_against_parent_4844 passes. Remove the max_blob_count_override guard that was skipping 4844 parent validation in EthBeaconConsensus. Add --testing.max-blob-count CLI flag and wire it through to EthBeaconConsensus for standalone header checks. Co-Authored-By: Brian Picciano <933154+mediocregopher@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d1a9d-6d47-745a-aa3f-41ad190ac0d8 Co-authored-by: Amp <amp@ampcode.com>
… big blocks The merged header's blob_gas_used matches the concatenated body transactions, so validate_cancun_gas passes. The max_blob_count limit is already handled by --testing.max-blob-count in validate_header. Co-Authored-By: Brian Picciano <933154+mediocregopher@users.noreply.github.com>
…big blocks The generator now fetches receipts from RPC for each constituent block, fixes up cumulative_gas_used across segment boundaries, and computes the correct merged receipts_root and logs_bloom for the big block header. The validator now checks gas_used via output.result.gas_used directly (bypassing the per-receipt cumulative gas check). receipts_root and logs_bloom validation remain skipped because the validator's own receipts still have per-segment cumulative gas counters. Co-Authored-By: Brian Picciano <933154+mediocregopher@users.noreply.github.com>
The prewarm system clones BbEvmConfig (sharing the staged_plan via Arc) and calls evm_with_env() on a background thread, which was consuming the plan before the main execution thread could. Without a plan, no segment boundaries were applied and transactions exceeded the first segment's gas limit. Move plan ownership to BbBlockExecutorFactory, which is only accessed during executor creation on the main execution path. This eliminates the race and also removes BbEvm and BbEvmFactory entirely — they were identity wrappers over EthEvm/EthEvmFactory with no remaining purpose. Amp-Thread-ID: https://ampcode.com/threads/T-019d3efe-1368-7679-86fc-0587fc12a648 Co-authored-by: Amp <amp@ampcode.com>
|
❌ Benchmark failed while running baseline benchmark (1/2). View logs |
Big block re-execution produces subtly different gas usage than the original blocks due to segment boundary handling (EIP-2935/4788 system calls between segments change storage slot access patterns, affecting gas costs for subsequent transactions). This causes gas_used and receipts_root mismatches against the synthetic big block header. Skip all post-execution consensus checks (gas_used, receipt_root, logs_bloom, requests_hash) for reth-bb since it's a benchmarking tool where these validations aren't meaningful. Amp-Thread-ID: https://ampcode.com/threads/T-019d3f25-1f7f-70a3-9492-6b7aaa6e137b Co-authored-by: Amp <amp@ampcode.com>
This reverts commit 8ac2a8e.
Temporary debug logging to diagnose gas_used mismatch in big block execution. Logs per-segment gas_used, cumulative gas_used_offset, and last receipt's cumulative_gas_used at each boundary and at final finish(). Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d3f25-1f7f-70a3-9492-6b7aaa6e137b
The State::block_hashes ring buffer (256 slots, block_number % 256) causes slot collisions when big blocks span >256 original blocks. Evicted entries fall back to the DB which returns B256::ZERO for non-persisted original block numbers, changing contract execution paths and gas consumption. Fix by reseeding the ring buffer at each segment boundary with only the 256 hashes relevant to that segment's BLOCKHASH window. A BlockHashSeeder function pointer is injected from ConfigureEvm:: create_executor (where State<DB> is concrete) into the generic BbBlockExecutor, avoiding trait bound changes on DB. Amp-Thread-ID: https://ampcode.com/threads/T-019d3ffd-d434-766c-ae32-3219bdedbbce Co-authored-by: Amp <amp@ampcode.com>
|
✅ Benchmark complete! View job Benchmark Results
100 big blocks Wait Time BreakdownPersistence Wait
Trie Cache Update Wait
Execution Cache Update Wait
ChartsGrafana Dashboard |
Amp-Thread-ID: https://ampcode.com/threads/T-019d4355-3b3e-779a-b512-746a2c916915 Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-019d4355-3b3e-779a-b512-746a2c916915 Co-authored-by: Amp <amp@ampcode.com>
| let mut new_inner = EthBlockExecutor::new(evm, new_ctx, spec, receipt_builder); | ||
|
|
||
| // Carry forward receipts from prior segments. | ||
| new_inner.receipts = result.receipts; |
There was a problem hiding this comment.
could we override new_inner.gas_used here to avoid dealing with offsets for cumulative gas?
There was a problem hiding this comment.
I believe we can't because then we'd have to raise the gas-limit on the inner EVM, and that throws off the GASLIMIT opcode. We could disable gas limit checking on the EVM I think, but this worked with less skipped checks
| // Use create_executor_with_seeder to inject a concrete seeder that | ||
| // can reseed State::block_hashes at segment boundaries. The seeder | ||
| // is a function pointer that knows the concrete State<DB> type, | ||
| // allowing the generic BbBlockExecutor to reseed without additional | ||
| // trait bounds on DB. | ||
| self.executor_factory.create_executor_with_seeder( | ||
| evm, | ||
| ctx, | ||
| Some(seed_state_block_hashes::<DB>), | ||
| ) | ||
| } |
There was a problem hiding this comment.
ah it's a bit unfortunate that we rely on State<DB> type here
i guess one way to get rid of it would be to wrap the db into some kind of wrapper that would overwrite the blockhashes but probably ok to just do this for now
There was a problem hiding this comment.
I think I had tried that in a previous iteration but there was a reason it couldn't work, idr now
| /// Block number → real block hash for blocks covered by previous big blocks in a sequence. | ||
| /// When replaying chained big blocks, the BLOCKHASH opcode needs real hashes for blocks | ||
| /// that were merged into earlier big blocks (and thus not individually persisted). | ||
| pub prior_block_hashes: Vec<(u64, alloy_primitives::B256)>, |
There was a problem hiding this comment.
Nit: it would be great to distinguish between different types of blocks in these docs. You also used segments somewhere, maybe that's a better term here.
Automated nightly update of reth dependencies from `paradigmxyz/reth` main branch. ## Upstream reth changes [`7f4a9a0...f8efc76`](paradigmxyz/reth@7f4a9a0...f8efc76) 🔗 Amp thread: https://ampcode.com/threads/T-019d473e-4c63-7539-bda6-d72354aae810 **Engine** - Share execution cache and sparse trie pipeline with payload builder ([#23242](paradigmxyz/reth#23242), [#23246](paradigmxyz/reth#23246)) - Add backpressure, take 2 ([#23280](paradigmxyz/reth#23280)) - Add method to get payload resolve future ([#23256](paradigmxyz/reth#23256)) - Return -38003 for FCUv2 payloadAttributes mismatch ([#22924](paradigmxyz/reth#22924)) - Fix double decrement in account cache size ([#23249](paradigmxyz/reth#23249)) **Trie** - Call root before prune ([#23243](paradigmxyz/reth#23243)) - Use Entry API in `MultiProofTargets::extend_inner` ([#23247](paradigmxyz/reth#23247)) - Record trie cursor metrics ([#23252](paradigmxyz/reth#23252)) - Add `SparseStateTrie::update_account_stateless` for stateless validation ([#23272](paradigmxyz/reth#23272)) **RPC** - Integrate `reth-rpc-traits` and remove `IntoRpcTx` ([#23288](paradigmxyz/reth#23288)) - Relax rpc converter impls ([#23254](paradigmxyz/reth#23254)) **Net** - Prefer peer-reported block number in session activation ([#23275](paradigmxyz/reth#23275)) - Retry block subscription on initial connection failure ([#23233](paradigmxyz/reth#23233)) - Resolve DNS for ExternalAddr in `external_addr_with` ([#23269](paradigmxyz/reth#23269)) **Payload / Refactor** - Remove OP `ExecutionPayload` impl and op feature from payload-primitives ([#23253](paradigmxyz/reth#23253)) - Remove OP `PayloadAttributesBuilder` impl and op feature from engine-local ([#23255](paradigmxyz/reth#23255)) - Remove changeset count APIs from storage ([#23310](paradigmxyz/reth#23310)) **DB / Storage** - Add `create_test_provider_factory_with_chain_spec_and_db_args` ([#23270](paradigmxyz/reth#23270)) - Add `reth-bb` binary with multi-segment big block execution support ([#23140](paradigmxyz/reth#23140)) - Add era type override functionality to `EraClient` ([#23307](paradigmxyz/reth#23307)) - Make snapshot API URL overridable ([#23303](paradigmxyz/reth#23303)) **Perf** - Use `FastInstant` for remaining metrics timing ([#23265](paradigmxyz/reth#23265)) **Bench / Testing** - Add hourly main regression bench ([#23219](paradigmxyz/reth#23219)) - Add regression test for parked basefee ancestor handling ([#23277](paradigmxyz/reth#23277)) **CLI** - Add more WARN logging before download retries ([#23258](paradigmxyz/reth#23258)) - Use `HeaderTy` for stage dump headers ([#23274](paradigmxyz/reth#23274)) **Deps** - Bump alloy 1.8.2, alloy-evm, Lighthouse v8.1.3 ([#23241](paradigmxyz/reth#23241), [#23289](paradigmxyz/reth#23289), [#23239](paradigmxyz/reth#23239)) - Weekly `cargo update` ([#23267](paradigmxyz/reth#23267)) **Grafana** - Add sparse trie idle metrics to overview ([#23302](paradigmxyz/reth#23302)) ## Migrations 🔗 Amp thread: https://ampcode.com/threads/T-019d473e-8aba-7283-b729-057391d70bc6 - **Reth dependency bump**: All `reth-*` crates updated from rev `7f4a9a0` to `f8efc76` - **alloy-evm version bump**: `0.29.2` → `0.30.0`; `revm-inspectors` `0.36.1` → `0.36.0` - **`TransactionEnv` → `TransactionEnvMut`**: Trait moved from `reth_evm::TransactionEnv` to `alloy_evm::TransactionEnvMut`; `nonce()` getter removed from the trait (now accessed via `revm::context::Transaction::nonce()` instead) - **`FromConsensusHeader` re-export flattened**: Import path changed from `reth_rpc_convert::transaction::FromConsensusHeader` to `reth_rpc_convert::FromConsensusHeader` - **`TxResult::into_result` added**: New required method on the `TxResult` trait to consume and return `ResultAndState` - **`BuildArguments::new` signature expanded**: Now takes two additional `None` parameters (likely cached/prebuilt payload fields) - **`BuildArguments` destructure**: Uses `..` rest pattern to ignore new fields - **`BlockBuilder::finish` signature change**: Now takes an additional parameter (`None` — likely an optional requests list) - **`snapshot_api_url` field added**: New required field on download URL config struct [GitHub Workflow](https://github.com/tempoxyz/tempo/actions/runs/23831300439) --------- Co-authored-by: Arsenii Kulikov <klkvrr@gmail.com> Co-authored-by: Arsenii Kulikov <62447812+klkvr@users.noreply.github.com>






Adds
reth-bb, a modified reth node that executes "big block" payloads — merged multi-block payloads used for high-gas benchmarking. Thereth_newPayloadendpoint accepts optionalBigBlockDatacontaining per-segment EVM environments and prior block hashes, enabling correct execution across segment boundaries (system calls, withdrawals, BLOCKHASH resolution, gas accounting).Also rewrites
generate-big-blockin reth-bench to directly merge consecutive RPC blocks (instead of going throughtesting_buildBlockV1), producing self-contained payload files with environment switches, correct receipts roots, and chained basefee/blob gas derivation. Thereplay-payloadscommand loads the new format and passesBigBlockDataviareth_newPayload.Supporting changes:
BeaconEngineMessage::ForkchoiceUpdatednow carriesEngineApiMessageVersionso the version propagates through the engine pipelineEthBeaconConsensusgainsskip_blob_gas_used_checkandskip_requests_hash_checkflags (used only byreth-bb)--testing.skip-gas-limit-ramp-checkCLI flag from the main reth binary (no longer needed)reth-bbwhenbig-blocksmode is selected