Skip to content

feat(rs-sdk): DocumentCountQuery should honor SdkBuilder::with_proofs(false) (no-proof decoder) #3630

@QuantumExplorer

Description

@QuantumExplorer

Expected Behavior

`SdkBuilder::with_proofs(false)` should govern every fetch surface. Callers who deliberately opt out of proofs (for cost or latency) should reach the unified `GetDocumentsCount` endpoint's no-proof modes (`Total` / `PerInValue` / `RangeNoProof`) via the same `Fetch` API the proof modes use.

Current Behavior

`DocumentCountQuery::TryFrom<...> for GetDocumentsCountRequest` hardcodes `prove: true` (`document_count_query.rs:206`). The blanket `impl Query for T` in `query.rs:119-124` does emit a `tracing::warn!` when `prove=false`, but the request still ships with `prove: true` on the wire. Effect:

  • Callers using `with_proofs(false)` get a fully-proven response with only a log-line indication of the divergence (the warning text says "ensure data is trusted," which is itself misleading since the data IS verified).
  • The server's no-proof execution paths — `Total` (O(1) reads on documents_countable contracts), `PerInValue` (one O(1) read per In value), `RangeNoProof` (per-In aggregate fan-out, O(|In| × log n)), and `RangeNoProof` distinct (O(limit × log n)) — are unreachable from the SDK.

PR #3623's review thread surfaced this as a deferred suggestion. The current state ships intentionally as "loud no-op + tracked follow-up."

Root cause (structural)

`Fetch::Request = DocumentCountQuery` and `FromProof::Request = DocumentCountQuery` — the same type implements both `TransportRequest` (for the gRPC transport) and serves as the input to the proof-decoder. The blanket `Query for T` thus owns the `prove` flag at `Fetch::fetch_with_metadata_and_proof`'s call to `query.query(sdk.prove())` (`fetch.rs:161`), and Rust's coherence rules block shadowing the blanket with a specific `impl Query for DocumentCountQuery` that could intercept `prove=false`.

The two structural fixes that don't fight coherence:

  1. Split the transport vs. domain types. Stop implementing `TransportRequest` on `DocumentCountQuery`; implement `Query for DocumentCountQuery` instead. The custom `Query` impl owns the conversion + prove threading. Tricky because `FromProof::Request = DocumentCountQuery` carries the loaded `DataContract` context that `GetDocumentsCountRequest` doesn't have, and `Fetch::Request: IntoFromProof::Request` requires that conversion to roundtrip. Solvable, but invasive.

  2. Add a no-proof decoder seam. Implement a parallel `Fetch`-shaped trait (or extend `Fetch`) that decodes the wire response's `Counts(...)` / `Entries(...)` variants directly without going through `FromProof`. `DocumentCount::fetch_unverified` and `DocumentSplitCounts::fetch_unverified` as named entry points, gated on `sdk.prove() == false`.

Possible Solution

(2) is the cleaner shape. Concretely:

  • Add a new SDK trait `FetchUnverified` (or method on `Fetch`) that takes `(sdk, query)` and returns the deserialized no-proof response directly.
  • For `DocumentCount`: `Fetch::fetch` switches on `sdk.prove()` — `true` → existing `FromProof` path; `false` → new no-proof decoder that extracts the `aggregate_count: u64` from the response's `Counts` oneof.
  • For `DocumentSplitCounts`: same switch, no-proof path extracts `entries: Vec` from the response's `Entries` oneof, builds `Vec`.
  • Wire the `prove` flag through to `TryFrom for GetDocumentsCountRequest` (probably by passing it as a field on `DocumentCountQuery` or via a fresh conversion method that takes it explicitly).

Once both decoders exist, the blanket `Query for T` warning becomes accurate (it really is unverified) and `with_proofs(false)` reaches the cheaper backend variants the server already supports.

Alternatives Considered

  • Hard-error on `prove=false`. The reviewer offered this as an alternative to the no-proof decoder. Cleaner than silent-ignore, but breaks callers who deliberately set `with_proofs(false)` SDK-wide for other queries and now have to special-case count queries.
  • Keep silent-ignore, accept the divergence. Status quo. Documented in `document_count_query.rs:185-206` as a no-op. Reviewer's "milder framing" framing accepts this as the current shipped state, but flags it as a real gap.

Additional Context

  • PR feat(platform)!: verifiable, bounded count queries on a unified endpoint #3623 (this is the follow-up of) ships the server-side unified count endpoint with all no-proof modes operational. The SDK gap is purely client-side.
  • Cross-language SDKs (wasm-sdk, rs-sdk-ffi) already expose `return_distinct_counts_in_range` and related knobs but route through the same proof path; they'd benefit from a no-proof entry point too.
  • Pre-release scope means breaking the existing `Fetch`-routes-to-FromProof contract is on the table; post-release this would want a platform-version gate.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions