Skip to content

feat(sdk): transport-free verification and query core for embedders - #4335

Draft
PastaPastaPasta wants to merge 7 commits into
v4.2-devfrom
feat/transport-free-embedder-core
Draft

feat(sdk): transport-free verification and query core for embedders#4335
PastaPastaPasta wants to merge 7 commits into
v4.2-devfrom
feat/transport-free-embedder-core

Conversation

@PastaPastaPasta

@PastaPastaPasta PastaPastaPasta commented Aug 7, 2026

Copy link
Copy Markdown
Member

Issue being fixed or feature implemented

Embedders that bring their own transport and trust context cannot currently consume Platform's query and proof-verification layer without depending on the networked SDK or reconstructing Drive query shapes themselves.

Dash Core is the concrete consumer: it already owns endpoint selection, transport, locally synchronized LLMQ keys, and wallet key custody. It needs Platform's canonical request construction, response decoding, and proof verification without adopting rs-dapi-client or tonic's native channel/TLS stack.

What was done?

This branch is rebased onto current v4.2-dev, which includes the proof-vector corpus from #4345. It contains corrected #4344 as its prerequisite followed by six focused commits:

  1. feat(sdk): add transport feature to dapi-grpc for types-only consumers #4344: opt-in native transport for dapi-grpc

    • generated types and transport-generic client stubs remain in the default feature set;
    • native channel/TLS support is behind transport;
    • native rs-dapi-client and server builds opt in at their target-specific boundaries;
    • default dash-sdk and wasm-sdk wasm32 graphs remain transport-free.
  2. Extract dash-platform-queries

    • moves query types, wire encoding, aggregate proof helpers, DPNS helpers, and transition validation into a reusable crate;
    • dash-sdk consumes it and preserves historical re-export paths.
  3. Share document request decoding

    • moves v1 proto-to-query decoding into one shared implementation used by both drive-abci and clients;
    • adds DocumentQuery::try_from_request;
    • adds request-driven verify_documents_response entry points for embedders.
  4. Extract pure DPNS and DashPay builders

    • adds transport-free preorder/domain and contact-request document assembly;
    • callers supply entropy, salt, and encrypted material, so key custody remains outside this crate.
  5. Cover transport-free cuts in CI

    • checks standalone dapi-grpc, drive-proof-verifier, and dash-platform-queries graphs;
    • rejects hyper/rustls/tower leakage in native proof verification;
    • rejects hyper/rustls/tower/mio leakage in default wasm SDK graphs;
    • adds both verification crates to nightly per-feature checks.
  6. Document the embedder path

    • documents the precise boundary: no rs-dapi-client or tonic native channel/TLS stack;
    • shared generated types and context-provider utilities remain dependencies.
  7. Harden validation seams

    • matches DPNS builder validation to the contract's consensus schema while preserving the stricter existing client policy separately;
    • rejects aggregate projections at the plain-documents verifier entry point;
    • documents client/server validation scope.

Once #4344 lands, this branch can be rebased to drop that prerequisite commit without changing the remaining series.

How Has This Been Tested?

Local validation on the final rebased revision e8e1961fe54fcdcb8eae55b7c2d84bcdf44004a8:

  • cargo fmt --check --all
  • workspace-wide clippy with all targets/features and warnings denied
  • cargo machete
  • wallet dependency-closure check
  • dash-platform-queries: 36 unit tests + 9 wire-roundtrip integration tests
  • drive-proof-verifier --features mocks: 261 unit tests + all 19 test(sdk): add proof-vector regression corpus for drive-proof-verifier #4345 fixture tests
  • native dapi-grpc, rs-dapi-client, dash-sdk, verifier, and query-core checks
  • transport feature-tree and dependency-leak assertions for native and wasm32 graphs

The exact published revision was then pinned from a clean Dash Core #67 checkout:

  • all 31 dash-platform-ffi tests passed (6 decoder, 14 proof, 11 signing/transition)
  • dashrust built successfully with the platform feature

Breaking Changes

Existing dash-sdk paths remain re-exported.

Two narrow Rust source changes remain:

  • the unused QuerySettings.request_settings field is removed;
  • DocumentQuery::new_with_data_contract_id is provided by the DocumentQuerySdk extension trait and requires that trait in scope.

External native consumers depending directly on dapi-grpc and calling generated connect() methods must explicitly enable features = ["transport"]. Native SDK/server consumers retain transport through their normal dependencies, and wasm consumers can use the default feature set safely.

Checklist:

  • I have performed a self-review of my own code
  • I have commented code where non-obvious invariants require explanation
  • I have added or updated relevant tests
  • I have updated the documentation
  • I have assigned this pull request to a milestone

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: eda9aade-6b05-49b2-9183-b99ae72ba068

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Comment @coderabbitai help to get the list of available commands.

dapi-grpc unconditionally built tonic with its native transport stack
(channel + TLS roots) on non-wasm targets, so any consumer of the message
types or proof-verification layers (drive-proof-verifier) dragged
hyper/rustls and the tokio networking stack into its dependency tree even
when it never opens a connection. The wasm target already proves the crate
works with codegen-only tonic.

Add an opt-in 'transport' cargo feature carrying tonic's
channel/transport/tls features, mirroring the client/server feature split
tenderdash-proto already has. It is deliberately NOT a default feature:
cargo features are not target-scoped, so a default-on transport would force
tonic's transport stack onto wasm32 consumers riding defaults, where it
does not build. build.rs drives tonic-build's build_transport from
CARGO_FEATURE_TRANSPORT (never on wasm32). Native networked consumers
enable it explicitly: rs-dapi-client (target-scoped to non-wasm), dash-sdk
(default feature, so SDK users are unchanged), and drive-abci via server
(which now implies transport). wasm-sdk and other wasm consumers need no
changes.

drive-proof-verifier needs no changes and its standalone tree drops from
407 to 339 crates: hyper, h2, rustls, ring, tower and the rest of the
transport stack disappear; what remains of tonic's codegen core is a
sync-only tokio slice via tokio-stream.

Types-only consumption is simply the default; embedders with their own
transport depend on:
  dapi-grpc = { default-features = false, features = ["platform", "client"] }
(default-features = false remains advisable for wasm and keeps the feature
set explicit.)
…ueries

Split rs-sdk per the maintainer guidance to refactor rather than duplicate: the query-building, wire-encoding, and proof-decoding core that a transport-free embedder needs now lives in a new packages/dash-platform-queries crate, and rs-sdk depends on it and re-exports every moved item at its old path, so no rs-sdk consumer changes imports.

Moved out of rs-sdk: DocumentQuery and its wire encoders (document_query.rs), the count/sum/average/ranked proof helpers and their FromProof aggregate views (DocumentCount, DocumentSum, DocumentAverage, DocumentSplitCounts, DocumentSplitSums, DocumentSplitAverages, DocumentRankedEntries), DocumentHistoryQuery, block_info_from_metadata, QuerySettings, FinalizedEpochQuery, ensure_valid_state_transition_structure, and the DPNS username helpers (convert_to_homograph_safe_chars, is_valid_username, is_contested_username). Sdk-bound pieces stay behind: the contract-fetching DocumentQuery constructor (now the DocumentQuerySdk extension trait), the Query<GetDocumentsRequest> encoder impl, the Fetch bindings for the aggregate views, and the Query impls for FinalizedEpochQuery.

QuerySettings loses its request_settings field: it was documented dead weight (not consulted by any encoder) and was the only rs-dapi-client tie in the moved struct. Sdk::query_settings and the few test construction sites were updated accordingly.

The new crate has its own small thiserror enum (Config/Drive/Protocol); rs-sdk converts it via From, so existing ? call sites keep compiling. wasm-sdk gains the matching From impl for WasmSdkError, routed through SdkError so the mapping is unchanged.

Coherence fallout: with DocumentQuery now foreign to rs-sdk, the blanket 'impl Query<T> for T where T: TransportRequest' would conflict with the explicit identity impl for DocumentQuery. The blanket is now additionally bounded by a local, explicitly-implemented WireQuery marker covering every wire request proto (list mirrors rs-dapi-client's TransportRequest impls); rustc can then prove the impl sets disjoint. The new crate's dependency tree is transport-free: no rs-dapi-client, hyper, rustls, or tonic transport.
…d client code

The proto-to-domain decoding for document queries existed only server-side
(rs-drive-abci's v1 conversions), so a client verifying a documents proof had
to reconstruct the query shape by hand and could silently drift from what the
server actually proves. Move the decode logic into
dash-platform-queries::documents::proto_conversions with a neutral error
type; drive-abci's conversions module becomes a thin mapping onto its
QueryError surface with identical error message strings.

On top of the shared decoder, DocumentQuery::try_from_request(request,
contract) reconstructs the rich query from the wire request (both request
versions), and verify_documents_response(...) /
verify_documents_response_with_provider_contract(...) give embedders a
request-driven verification entry point that delegates to the existing
FromProof machinery, resolving the contract explicitly or via
ContextProvider::get_data_contract.

Round-trip tests cover encode-decode equality for representative queries in
both wire versions plus malformed-clause rejection; drive-abci's
document_query unit tests pass unchanged (76 cases).
Move the document *content* assembly of rs-sdk's networked DPNS and DashPay flows into transport-free functions in dash-platform-queries, so offline/embedder consumers and rs-sdk share one implementation:

- build_dpns_preorder_and_domain_documents assembles the preorder and domain documents exactly as register_dpns_name did: both ids from the same entropy via generate_document_id_v0, saltedDomainHash = sha256d(salt || normalized_label + ".dash"), and the full domain property map (parentDomainName/normalizedParentDomainName, label, normalizedLabel, preorderSalt, records.identity, subdomainRules.allowSubdomains=false). It additionally rejects labels failing is_valid_username up front - previously only enforced by rs-sdk-ffi and platform consensus - so register_dpns_name now fails locally on an invalid label instead of after a network round-trip.

- build_contact_request_document assembles the DIP-15 contactRequest id and property map from already-derived crypto material (encrypted xpub/label bytes, key indices, entropy). ECDH, encryption, the 69-byte compact-xpub check, key purpose checks, and recipient fetching stay in rs-sdk; the ciphertext size validations (96-byte xpub, 48-80-byte label, 38-102-byte autoAcceptProof) moved into the builder, with validate_auto_accept_proof also called early in create_contact_request to keep the pre-fetch fail-fast.

- ensure_entropy_matches_document_id and prepare_document_for_transition moved from put_document.rs into dash_platform_queries::transition::put_document; rs-sdk re-exports and keeps calling them.

Entropy/salt generation and all networking remain in rs-sdk. Builder validation errors surface through the new dash_platform_queries::Error::InvalidInput variant, which rs-sdk maps back to Error::Generic with the exact pre-move messages. New unit tests in dash-platform-queries pin a DPNS known vector (document ids and property maps for fixed label/entropy/salt), mirror the entropy-derives-id relation for contact requests, and cover the negative validation paths.
Feature unification hides transport-stack regressions in whole-workspace
builds, so add a PR-time step checking the standalone graphs (types-only
dapi-grpc, drive-proof-verifier, dash-platform-queries) and failing if
hyper, rustls, or tower leaks into drive-proof-verifier's tree. Add both
verification crates to the nightly per-feature check matrix and to the
check-features tool's crate list.
dapi-grpc gets a crate-level feature table (including the new transport
feature and the types-only build recipe), dash-platform-queries gets a
README describing who the crate is for and what lives in it, and rs-sdk's
README points transport-free embedders at the split crate.
…er seams

Review follow-ups on the transport-free series:

- The DPNS document builder validated labels with is_valid_username, whose
  consecutive-hyphen rejection is stricter than the DPNS contract's schema
  pattern - consensus accepts names like ab--cd. Split the check: new
  is_consensus_valid_label matches the contract pattern exactly and gates
  the builder (so dash-sdk's register_dpns_name no longer refuses
  consensus-valid labels), while is_valid_username keeps the stricter
  policy for its existing FFI/wasm gates and now documents the difference.
- verify_documents_response rejects aggregate projections (COUNT/SUM/AVG)
  up front with a pointer to the aggregate proof helpers, instead of
  surfacing an opaque low-level proof error; try_from_request documents
  that it mirrors the server's wire-shape decode, not validate_and_route
  business rules.
- The CI transport-leak guard also asserts wasm-sdk's wasm32 tree stays
  free of the native transport stack.
- The proof-vector corpus gains a README with an explicit coverage matrix:
  the four documents-family cases pin query shape and clean decode failure
  but stop before the BLS check (placeholder payloads in the fixture
  state); identity, contested, and quorum-sig families run the full
  pipeline. This corrects the corpus commit's broader claim.
@PastaPastaPasta
PastaPastaPasta force-pushed the feat/transport-free-embedder-core branch from db332fe to e8e1961 Compare August 10, 2026 16:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant