refactor: behavior-preserving cleanups and unused-API removals - #194
Conversation
Deletes the second sources of truth and the dead parameters/accessors the /simplify review found, with no observable change on canonical-block paths. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CHVpMMX9N69sUNbuKgpBVY
Claude review status
🛠️ Review did not finish Attempted This round did not publish: MODEL_ACTION_FAILED in phase review_retry. Anything listed below is from the last round that did. Re-run the workflow or push a new commit to try again. |
Codecov Report✅ All modified and coverable lines are covered by tests. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f3e23b61af
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
- BackoffPolicy is RetryPacing re-exported under the old name; both hand-written adapters between the two identical pairs go (RetryPacing gains `const fn new`) - BlockMeta::try_from_header dedupes the three strict anchor-init projections (validator app, trace-server main, validator integration test) - run_with_signals reads RpcClient::reports_validation() instead of a parallel bool - the hardfork order test pins into_vec against the mega_mainnet_hardforks ladder (the cross-check the deleted reorder loop did at runtime), one literal fewer - install_prometheus_exporter sits behind a `prometheus-exporter` feature the two binaries enable, so library consumers do not build the exporter - mock-server handlers use `params.parse()?` (already -32602) instead of re-wrapping - BackoffSchedule drops Copy; task_to_block / WitnessExternalEnv / hardfork docs trimmed Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HvmqYNUsaoGSQFhkyyZ3eG
`recover_gaps` runs after every fetch completion and checked in-flight membership by scanning `task_to_block.values()` — O(max_in_flight²) per completion once the outstanding window fills behind a stalled block. Keying the same single map by block (`in_flight: HashMap<u64, Id>`) makes that check and the success/failure removals O(1) again; the task id is only needed to map a panicked task's `JoinError` back to its block, so that rare path scans instead. Answers the Codex P2 on `crates/stateless-core/src/pipeline/fetcher.rs`. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The header→meta constructors were exercised only from the binaries, so the stateless-core coverage job (core tests alone, `--no-default-features`) saw the new block at 0% and codecov flagged 17 missing patch lines. Two unit tests pin both constructors: the sealed-block projection, the zero default for a missing `withdrawals_root`, and the strict variant's `None`. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
vincent-k2026
left a comment
There was a problem hiding this comment.
Right split of #170: second sources of truth and dead parameters go, hardfork order moves from a runtime reorder to a test-time cross-check. No correctness/security blocker.
What's good
ChainStoreno longer extendsContractStore— the pipeline mock andStubBlockStorestubs go with it, and mega-reth can drop the "intentionally inert" impl on the next pin bump.BlockMeta::from_header(tip: missingwithdrawals_root→ zero) vstry_from_header(anchor:None, each site keeps its own reject error) — defaulting and rejecting are not collapsed into one constructor.- Fetcher
in_flight: HashMap<u64, Id>fixes the Codex P2 O(n²)recover_gapsat the source, without bringing the mirror set back. BackoffPolicyas aRetryPacingre-export plusRetryPacing::schedule(): jitter / max / 1 ms floor live in one place; RPC and R2 GET no longer duplicate the arithmetic;stateless-r2stays free of upward deps.mega_mainnet_hardforks()kept as the membership oracle (#190 already consumes it); only the reorder loop goes. Matches main having moved.test_mega_hardforks_iterate_in_activation_orderis the deleted runtime cross-check, moved to test time. The claimed Rex5/Rex6 swap going red is the right shape.set_anchor_blocknow goes through productionreset_to_anchor; the mocketh_getHeaderByNumberparse().unwrap()isparse()?.- Breaking surface is explicit (
kvs(),BLOB_GASPRICE_UPDATE_FRACTION,ChainStoresupertrait). mega-reth is pinned atv2.0.14, so nothing breaks until the coordinated bump.
Blocking (correctness / security)
None.
Non-blocking / follow-up
- The order test filters
forks_iter()by ladder names, so an extra fork ininto_vecis silently dropped. Omitting it from the ladder fails; adding it only tointo_vecdoes not.assert_eq!(mega_order.len(), expected.len())(or comparing the MegaETH prefix without a filter) would make that symmetric. Today it is a comment: insert in both places. /metricsloses the permanently-zeroeth_getTransactionByHashseries, and the exporter-install error string becomes the shared "Prometheus exporter" wording. Disclosed in the PR body; dashboards that keyed on the old name need to know.workers.rs→runner.rsis behavior-preserving and the docs were updated; it is a rename, not an unused-API removal, so greps for the old path will wobble once.install_prometheus_exporter'sexpect("valid bucket config")is hoisted as-is from the two binaries, not newly introduced. Startup-only, buckets are static — fine.
…r membership `test_mega_hardforks_iterate_in_activation_order` filtered `forks_iter()` by ladder membership, which made it one-directional: a fork added to the ladder alone failed, but a fork added to `into_vec` alone was filtered out of the comparison and passed silently. `from_genesis` concatenates the MegaETH forks ahead of the Optimism/Ethereum ones, so they are the leading `ladder.len()` entries. Taking that prefix instead of filtering makes the check symmetric — both omissions now fail. Mutation-checked both ways: swapping Rex5/Rex6 in `into_vec` fails, and dropping a fork from the ladder alone fails where the filtered version passed. Answers item 1 of vincent-k2026's review of #194. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Thanks — item 1 holds and is fixed; 2–4 need no code change. 1. Order test was one-directional. Confirmed: Mutation-checked both directions on the same tree:
The second row is exactly the blind spot you named. The doc on 2. 3. 4. |
RealiCZ
left a comment
There was a problem hiding this comment.
Two follow-ups on the hardfork section, both inline. Also for the "Breaking at the next tag" list: BackoffPolicy gaining Copy means any .clone() on it in mega-reth becomes a clippy::clone_on_copy warning, which this PR itself had to remove here; worth a grep before the pin bump.
…:VARIANTS The order test compared a prefix, so a fork appended to `into_vec` that the hand-written ladder lacked still passed. Both chain-spec tests now read `MegaHardfork::VARIANTS` as the membership-and-order oracle, which also lets the duplicate `mega_mainnet_hardforks()` ladder go. The `into_vec` doc no longer claims insertion order is consensus-relevant: fork selection goes through `mega_fork_activation`, a name-keyed `ChainHardforks` lookup, so the tests guard agreement with mega-evm's declaration, nothing more. Addresses RealiCZ's review on #194 (threads r3940123812 and r3940123815). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ws3jKHqLMPS6iNfF7RECWn
|
On the |
RealiCZ
left a comment
There was a problem hiding this comment.
f43fdfe closes both hardfork threads: the order test now pins into_vec against MegaHardfork::VARIANTS (symmetric on a missing, extra, duplicated, or misplaced entry), the membership test iterates VARIANTS through the same name-keyed lookup fork selection uses, the hand-written ladder is gone, and the into_vec doc states what the tests actually guard. The two tests fail in complementary ways on a mega-evm bump that brings a new fork, so the next pin bump cannot leave it unscheduled or unmapped unnoticed.
PR #194 was squash-merged as 2675337; this branch carried its pre-squash snapshot, which had drifted from the final form during review. Every conflicted file is untouched by this PR's own commit, so each was resolved to main's version. The merge result differs from main by exactly this PR's diff (executor.rs + chain_sync.rs). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0196vFR5tf9XpXMe3CuDyuiv
…code PR #195 was squash-merged as 7ca2253; this branch carried the pre-squash snapshots of #194 and #195, which had drifted from their final form during review. Ten of the twelve conflicted files are untouched by this PR's own commit and were resolved to main's version. The other two (executor.rs, rpc_client.rs) were taken from main with this PR's hunks re-applied on top: #195's final form inlined `execute_transactions` back into `replay_block`, so the `gas_used` change now lives there. The merge result differs from main by exactly this PR's diff. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Resolves what main's squash-merges of #194, #195 and #196 left conflicting against this branch's own copies of that work. Every file outside this PR's two commits is taken from main verbatim, so the branch keeps only the reviewed form of those refactors and the PR diff narrows to the perf work. - verify_block_integrity: main's #196 form wins outright. It is this branch's "encode each transaction once" rewrite plus the per-transaction keccak check and its forged-hash test, so the PR no longer carries that hunk at all. The blocking-pool wrapper around it is unchanged. - executor.rs: main's inlined replay body, with this branch's `execution_result.gas_used` + `debug_assert_eq!` re-applied on top; the `execute_transactions` extraction is dropped, as it was during #194's review. - rpc_client.rs test imports: union of main's `TestFixtures` and this branch's `consistent_header`. The merged tree is origin/main plus exactly the deltas of e62e3e1 and 432e550. Cargo.lock is untouched; check, clippy, fmt, sort and the full test suite are green.
Summary
PR 1/6 of the #170 split. Everything here is behavior-preserving on canonical-block paths, plus the public-API removals that had no consumer left. If a bisect ever lands on this PR, that is a strong signal something in it was not behavior-preserving — which is the whole point of separating it from B–F.
Responds to vincent's review of #170: "Suggest splitting into a genuinely behavior-preserving cleanup plus a handful of small, individually-motivated changes." Every item he listed under "Worth keeping, whatever the split" is in here.
What's in it
Second sources of truth removed
ChainStoredrops theContractStoresupertrait — nothing consumed contracts through it. The forced stubs on the pipeline mock and the trace server'sStubBlockStorego with it.BlockMeta::from_header/try_from_headerreplace six hand-rolled header→meta projections: the three tip observations (validator + two in the trace server) default a missingwithdrawals_rootto zero, and the three anchor-init sites (app.rs, the trace server'smain.rs, the validator integration test) go through the strict variant, which returnsNoneso each keeps its own reject error — that path must reject, not default.FetcherState::in_flight_blocksdeleted: it mirroredtask_to_block.values(). Rather than scan those values per completion (O(max_in_flight²), Codex's P2), the one remaining map is keyed by block —in_flight: HashMap<u64, Id>— sorecover_gapsand the success/failure paths stay O(1); the task id is only needed to map a panicked task'sJoinErrorback to its block, a scan on that rare path.ChainSpec::from_genesisis gone, and so is themega_mainnet_hardforks()ladder: both were copies of mega-evm'sMegaHardforkdeclaration, which the chain-spec tests now read directly asMegaHardfork::VARIANTS. The "unknown fork" append branch was unreachable —into_veconly ever produces those ten.BackoffSchedule(stepped fromRetryPacing::schedule()) collects the jittered-doubling arithmetic (jitter,maxcap, 1 ms floor) that was duplicated between the RPC round-robin loop and the R2 GET loop, comments and all. Both consume it — andBackoffPolicyitself is now a re-export ofRetryPacing: the two were field-for-field identical{initial, max}pairs with a hand-written adapter between them (r2_witness.rs::pacing), which goes too.MockServerState::block_by_number_hex/block_by_hash/invalid_params— which also fixes a drifted bareunwrap()on malformedeth_getHeaderByNumberparams.Dead parameters, accessors, and backdoors
handle_transient_restart's_reason: Stringparameter and the upstream string construction that fed it.ValidatorFetcher::on_remote_heightfn-pointer injection (the only value ever passed wasmetrics::set_remote_chain_height).run_with_signalsno longer takes the report endpoint (anOption<String>it only ever.is_some()'d); it readsRpcClient::reports_validation(), since the client is already built from the same flag — two inputs that had to agree became one.LightWitnessExecutor::kvs()— no consumer, here or in mega-reth.#[cfg(test)] ValidatorDB::set_anchor_block— the roundtrip test now goes through the productionreset_to_anchorpath instead of a test-only shortcut around the helper layer.WitnessExternalEnv::new/from_light_witnessshare onefrom_metadata_kvsbody (the metadata scan was duplicated verbatim); a doubled doc block oncreate_evm_envremoved.Reuse
BLOB_BASE_FEE_UPDATE_FRACTION_CANCUNinstead of a hand-copied consensus constant. Verified equal: revm-primitives defines it as3_338_477, the local constant was3338477.install_prometheus_exporterhoisted intostateless_common::metricsbehind aprometheus-exporterfeature both binaries enable; themetrics-exporter-prometheusdep moves from the two bins into common as an optional dep, so mega-reth (which consumesstateless-commonand pins the exporter at 0.16) does not build a second copy for a function it never calls.collect_code_hashesinstead of open-codingiter_code_hashes().collect::<HashSet<_>>().workers.rs→runner.rs(it runs the pipeline; the workers live in core), docs updated.The invariant that lost its only enforcement
vincent: "Removing
mega_mainnet_hardforks()and the explicit reordering loop makesMegaethGenesisHardforks::into_vec's literal the single source of activation order. That is the right direction… But nothing tests the order."test_mega_hardforks_iterate_in_activation_ordernow pinsinto_vecend-to-end throughfrom_genesisagainst mega-evm's own declaration rather than a local ladder: it schedules everyMegaHardfork::VARIANTSentry (genesis field derived from the variant name) and asserts the scheduled MegaETH forks equalVARIANTS, so a variantinto_veclacks, duplicates, or misplaces fails at either end of the ladder — a hand-written expected list or a prefix compare both let a trailing omission through, and new forks are only ever appended. Mutation-checked on the final tree: dropping Rex6 frominto_vecfails, swapping Rex5/Rex6 fails, duplicating Rex6 fails.mainnet_genesis_schedules_every_canonical_hardforkiteratesVARIANTSthe same way, through the same name-keyedChainHardforks::forklookup fork selection uses at runtime, which is what let the hand-written ladder go. One correction to the earlier framing: insertion order is not consensus-relevant. mega-evm resolves the active fork throughmega_fork_activation, a name-keyed map lookup onChainHardforks, and nothing outside the tests iteratesforks_iter(), so the deleted reorder loop was already inert for consensus; what the tests guard is that the local literal and mega-evm's declaration agree in membership and order, so a fork the executor supports cannot go unscheduled unnoticed.Deviations from the #170 material
One, forced by main moving 9 PRs since
e1b2ede:BackoffSchedulelives instateless-r2, notstateless-common. feat(stateless-r2): custom-domain R2 target with HTTP/2, keeping the raw S3 endpoint path #187/refactor: dedupe the R2 witness adapters and hoist the jsonrpsee mock scaffolding #192 moved the R2 GET loop intostateless-r2, andstateless-commondepends onstateless-r2— so common is no longer a home both consumers can reach, andstateless-r2must stay free of upward dependencies (itsRetryPacingdoc says so explicitly). The schedule therefore sits next toRetryPacingasRetryPacing::schedule(), andBackoffPolicyisRetryPacingre-exported under the namestateless-common's API uses (RetryPacinggained theconst fn newthatBackoffPolicyhad; every existing constructor, struct-literal, and field-access form compiles unchanged). For mega-reth this is an addition tostateless-r2's surface, not a break.Observable changes (behavior-preserving ≠ byte-identical output)
/metricsloses theeth_getTransactionByHashcounter series. It was pre-registered but permanently zero — that method is only ever called by the trace server.collect_code_hashesreturns a sorted, deduplicatedVecwhere the open-codedHashSetpath was unordered. No consumer depended on the order; the cache lookup and the RPC batch are both order-insensitive.Breaking at the next tag
mega-reth pins
stateless-coreatv2.0.14, so nothing breaks until the bump — which needs coordination:LightWitnessExecutor::kvs()removed.BLOB_GASPRICE_UPDATE_FRACTIONremoved; use revm'sBLOB_BASE_FEE_UPDATE_FRACTION_CANCUN(same value, verified — it is a consensus parameter).ChainStore: ContractStoresupertrait dropped. Downstream payoff: mega-reth'sMegaValidatorChainStorecan delete theContractStoreimpl its own comment calls "intentionally inert".stateless_common::BackoffPolicyis now a re-export ofstateless_r2::fetch::RetryPacing— source-compatible (same field names,new, and it gainsCopy); only a trait impl naming the type would notice, and mega-reth has none.Copyalso turns any.clone()on it into aclippy::clone_on_copywarning (this PR dropped two of its own on the retry field); the mega-reth branch carrying the validator crates has noBackoffPolicyreference today, but grep before the pin bump.mega_mainnet_hardforks()removed — the refactor: apply simplify-review cleanups across core, common, and the binaries #170 deletion, restored now that its one consumer readsMegaHardfork::VARIANTS. No consumer in mega-reth.Testing
cargo fmt --all --check,cargo clippy --workspace --all-targets --all-features(0 warnings),cargo sort --check, full workspace suite 478 passed / 0 failed, andcargo test -p stateless-core --no-default-features --lib --no-run(the no-std alias path) all clean.Notes
PR 1 of 6, stacked: B (EIP-3155 writer deletion) → C (verify/gas_used) → D (blocking work) → E (hot-path allocations) → F (empty witness endpoints). Each targets its predecessor and stands on its own.