fix: allow a witness-less RpcClient and type the unsatisfiable-range failure - #199
Conversation
…failure R2 mode stops handing the data endpoints to the client as placeholder witness endpoints, and an unsatisfiable witness provider range fails one request as `WitnessFetchError::NoProviderInRange` instead of panicking the process. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CHVpMMX9N69sUNbuKgpBVY
Claude review status
✅ Review clean Last reviewed: head New this round: 0 finding(s), 0 question(s) · Resolved this round: 0 · Open questions: 0 |
|
This PR has no labels. Given the diff (replacing an |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ae09a4f9a2
ℹ️ 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".
| WitnessFetchError::NoProviderInRange { skip, configured } => unreachable!( | ||
| "full-range witness fetch on a client with no witness providers \ | ||
| (skip={skip}, configured={configured})" | ||
| ), |
There was a problem hiding this comment.
Return NoProviderInRange from every witness fetch API
When RpcClient is constructed with the now-supported empty witness list, both get_witness_with_deadline and get_witness_light_with_deadline pass NoProviderInRange to this unreachable!, while their unbounded wrappers and get_witness_light_first_provider_only panic through expect. This is reachable for the validator's fallback-less R2 configuration and contradicts the constructor documentation that a witness call returns the typed error; an accidental call therefore still takes down its task/process instead of failing structurally. Propagate WitnessFetchError through all witness-fetch APIs, or otherwise make the witness-less state impossible for APIs that cannot return it.
AGENTS.md reference: AGENTS.md:L154-L154
Useful? React with 👍 / 👎.
Summary
PR 6/6 of the #170 split, stacked on #198. The validator's R2 witness mode stops handing the data endpoints to
RpcClientas placeholder witness endpoints, and the caller-contractassert!that guarded the witness provider range becomes a typed error.vincent's review of #170: "It is also strictly better than the old 'hand it the data endpoints as a placeholder' behavior, which silently pointed witness calls at the wrong endpoints. Still worth being deliberate about
assert!vs a typed error in a validator that is expected to stay up."The change
RpcClient::new_with_configaccepts an emptywitness_apis. In--witness-source r2the client now carries no witness providers at all, instead of a copy of--rpc-endpoint.witness_round_robin'sassert!becomesWitnessFetchError::NoProviderInRange { skip, configured }, following the shapeCodeFetchErroralready uses in this file (#[error(transparent)] Deadline(#[from] RpcDeadlineExceeded)plus the domain variant).Being deliberate about it
One variant covers both failures, and that is the point.
skip..lenis unsatisfiable either because a caller'sskipran past the configured endpoints (a routing bug) or because there are no endpoints at all (0..0, the witness-less deployment). Both are wiring failures — neither is a transport condition — so both leave as the same typed error.Why not keep the
assert!. A routing bug in the trace server's witness-skip computation would take the process down on a request path. As a typed error it fails one request, and the reason label says which.Why an empty witness list is nevertheless not a new runtime hazard. Both binaries already reject the configuration at startup: the validator errors on
--witness-source rpcwith no--witness-endpoint(app.rs), and the trace server's clap marks--witness-endpointrequired_unless_present = "witness_generator_endpoint". Relaxing the constructor loses no startup diagnostic — it only stops forcing R2 mode to lie about where witnesses come from.What deliberately did not change. Only
get_witness_light_with_deadline_from— the one method taking a caller-computed range — returnsWitnessFetchError. The full-range methods (get_witness_with_deadline,get_witness_light_with_deadline) keepRpcDeadlineExceeded; they pass0..len, where the range variant is unreachable, and threading the enum through them would have rippled into the trace server's error classification for no behavioural gain.get_witness_light_first_provider_onlystays infallible with a pinned0..1, its.expectnaming the startup contract — the same idiom as the.expect("None deadline cannot time out")beside it.Trace-server classification
From<WitnessFetchError> for DataProviderErrorsendsDeadlinedown the existing method-based path andNoProviderInRangetoInternal. That distinction matters operationally:Timeout { Witness }feeds thedeadline_witnesserror reason, whose entire value is meaning "an upstream witness fetch ran out of budget" — a wiring bug landing there would page for the wrong incident.witness_range_failure_is_internal_not_a_witness_timeoutpins both halves.Testing
cargo fmt --all --check,cargo clippy --workspace --all-targets --all-features(0 warnings),cargo sort --check, full workspace suite 483 passed / 0 failed,cargo test -p stateless-core --no-default-features --lib --no-runclean.New:
witness_fetch_out_of_range_returns_a_typed_error(both the out-of-rangeskipand the witness-less client) andwitness_range_failure_is_internal_not_a_witness_timeout; the constructor test now pins that an empty witness list constructs.test_witness_fetch_skip_of_all_providers_panicsis removed — superseded by the typed-error test over the same call. Both new tests were mutation-checked (disabling the range check, and misrouting the variant toTimeout { Witness }, each kill their test).Notes
Closes the last of the #170 split.
TODO-A-2in the internal ledger is satisfied by the typed error.