refactor: dedupe the R2 witness adapters and hoist the jsonrpsee mock scaffolding - #192
Open
flyq wants to merge 3 commits into
Open
refactor: dedupe the R2 witness adapters and hoist the jsonrpsee mock scaffolding#192flyq wants to merge 3 commits into
flyq wants to merge 3 commits into
Conversation
The two binaries' r2_witness.rs were near-verbatim copies on everything below the decode: the failure taxonomy (enum, KINDS, kind()), the BackoffPolicy->RetryPacing conversion, both constructors, the target accessors, and the kinds_cover_all_fetch_kinds guard test - two guards protecting the same surface from the same drift. That shared core now lives in stateless_common::r2_witness (R2WitnessError + a R2WitnessTransport wrapper; the custom-domain constructor takes each binary's version-observer as a callback), and the adapters keep only what genuinely differs: light-vs-full decode, deadline handling, retry budgets, pacing pauses, and metrics hooks. The error union carries DecodeTimeout for both consumers; a binary whose fetches pass no deadline (the validator) never produces it, so its pre-registered decode_timeout series stays at zero - the same status as its existing structurally-unreachable deadline kind, now documented on the shared type. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…tils Three crates hand-rolled the same mock-upstream scaffold: the ephemeral jsonrpsee server bootstrap, the minimal RPC header shapes, and the hex block-number parse. They now live in stateless_test_utils::mock_rpc (serve / serve_with_config, header_stub / consistent_header, parse_hex_u64), following the mock_r2 precedent; the specialized mocks (scripted witness sources, counting endpoints, the validator's fixture-backed node) stay with the tests that script them, built on the shared scaffold. Both header shapes are kept deliberately: divergence-chain tests must serve prescribed hashes (header_stub), while verify_hash = true paths need the real hash_slow() (consistent_header) - collapsing them would break one consumer or the other. serve_with_config carries the response-size cap the validator's multi-MB witness fixtures need. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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. |
The first pass hoisted the shared core but left both binaries with byte-identical delegation shells over it: four forwarding constructors and seven accessor hops, re-creating one level up the duplication the hoist set out to remove. Transport construction now happens at the two wiring sites that already own the flags and the metrics plumbing (main.rs / app.rs), which log the configured target straight off the transport; each adapter keeps a one-line `new(transport)` and its own fetch-decode-pace logic. app.rs's builder is renamed to build_r2_transport for what it now returns. The duplicated blocking-decode mapping goes the same way: a shared decode_on_blocking_pool takes the decode fn and an optional deadline (Some bounds it for the request path, None lets the pipeline's decode finish), so the shared error type's Decode/DecodeTimeout/DecodePanicked variants are constructed once, in the module that defines them. Both construction-error tests move to the shared module, where one copy covers both binaries and both targets — the rationale that already moved kinds_cover_all_fetch_kinds. The cap-wiring test keeps its discriminating assertion, now reading the cap off the transport the builder returns. Also from the review: jsonrpsee becomes an optional test-utils dependency behind a mock-rpc feature, so stateless-core and stateless-r2 — dev dependents with no other jsonrpsee edge — no longer compile the server stack for tests that never mock an upstream; is_missing's doc drops the one-consumer framing; and the consistent_header re-export hop is gone. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Troublor
approved these changes
Aug 28, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two queued deduplications, in one PR:
r2_witness.rs— the failure taxonomy (R2WitnessError,KINDS,kind()), theBackoffPolicy → RetryPacingconversion, transport construction, the target accessors, the blocking-decode error mapping, and thekinds_cover_all_fetch_kindsguard test (two copies protecting the same surface from the same drift) — now lives once instateless_common::r2_witness. Transport construction happens at the two wiring sites that already own the flags and the metrics plumbing (main.rs/app.rs), which log the configured target straight off the transport; each adapter keeps a one-linenew(transport)plus what genuinely differs: light-vs-full decode, deadline handling, retry budgets (3 vs 9), pacing pauses, and metrics hooks. Newstateless-common → stateless-r2dependency edge; acyclic,stateless-r2stays a leaf crate.stateless_test_utils::mock_rpcnow providesserve/serve_with_config,header_stub/consistent_header, andparse_hex_u64— themock_r2precedent — and the specialized mocks instateless-common,debug-trace-server, and the validator's integration tests build on it instead of re-rolling it. It sits behind an opt-inmock-rpcfeature sostateless-coreandstateless-r2— dev dependents with no other jsonrpsee edge — do not compile the server stack for tests that never mock an upstream.Behavior is pinned by the existing tests
This is a refactor; the discriminating tests are the ones that already existed, unchanged and green: the validator's pacing-pause tests (deterministic throttle + max-backoff pause before surfacing), both adapters'
MAX_ATTEMPTStests, the construction-rejection tests on both targets, the custom-domain bare-key/unsigned-GET assertions, and the cap-wiring test from #191 (the_r2_cap_not_the_rpc_one_reaches_the_transport, still asserting on both target arms that--witness-max-concurrent-requests 16 --r2-max-concurrent-requests 48yields a cap of 48, so reverting either arm's wiring fails by value). Three per-binary tests collapse into one shared copy each — the KINDS guard and the two construction-rejection tests — which is the workspace count going 478 → 477.The one observable change
The shared error union carries
DecodeTimeoutfor both consumers, so the validator now pre-registers anr2_witness_errors_total{kind="decode_timeout"}series it can never produce (its fetches pass no deadline) — the same structurally-unreachable status as its existingdeadlinekind, now documented on the shared type.Two deliberate deviations from the original dedup notes
header_stubis not unified onto the self-consistenthash_slow()shape: the divergence-bisection tests must serve prescribed hashes (two chains differing only by hash), which a number-derived hash cannot express. Both shapes ship, each documenting whichverify_hashpaths accept it.serve_with_configexists alongsideservebecause the validator's fixture-backed mock needsmax_response_body_sizeabove jsonrpsee's default for its multi-MB witness payloads.Testing
cargo test --workspace: 477 passed, 0 failed.fmt/clippy --all-targets --all-features/cargo sortclean;cargo check -p stateless-test-utilsstandalone (the crate's own build constraint) clean, andcargo tree -p stateless-core --edges dev -i jsonrpseenow resolves to nothing.🤖 Generated with Claude Code