Skip to content

feat(sdk)!: verify document proofs against the wire request and share DPNS/DashPay document assembly - #4619

Open
PastaPastaPasta wants to merge 2 commits into
refactor/platform-query-wire-and-dpp-boundsfrom
feat/request-driven-document-verification
Open

feat(sdk)!: verify document proofs against the wire request and share DPNS/DashPay document assembly#4619
PastaPastaPasta wants to merge 2 commits into
refactor/platform-query-wire-and-dpp-boundsfrom
feat/request-driven-document-verification

Conversation

@PastaPastaPasta

Copy link
Copy Markdown
Member

Issue being fixed or feature implemented

Second of three PRs replacing the transport-free-embedder series; stacked on #4618 (the first two commits are that PR, review from feat(drive-proof-verifier) onward). Supersedes #4478, #4389 and #4433.

Transport-free embedders (Dash Core's platform GUI via the C++ crate in the next PR) hold their DAPI request only as protobuf bytes and must (a) verify a proved document response against exactly that request and (b) assemble DPNS/DashPay documents without reimplementing SDK logic in C++. The earlier PRs did this with two free verify_documents_response* functions in dash-platform-queries, a local copy of the server's CBOR clause decode, an ASCII-only copy of dpp's homograph normalization, and hard-coded DashPay byte bounds. This PR does the same job using the shapes the codebase already has.

What was done?

Request-driven document verification, in drive-proof-verifier (src/proof/document_request.rs):

  • DocumentWireQuery: TryFromRequest<GetDocumentsRequest> decodes either wire version: v1 through the shared platform-query-wire decoders the server uses, v0 through drive's own WhereClause::from_components / OrderClause::from_components (the same parsers query_documents_v0 runs).
  • RequestedDocuments: FromProof<GetDocumentsRequest> — the same request-driven pattern as the other FromProof<GetXRequest> impls in proof.rs — resolves the contract through the ContextProvider, lowers via DriveDocumentQuery::from_typed_clauses under DriveConfig::default() and the response's platform version (so limit semantics and clause grouping are the server's, not a client mirror), and delegates to the existing FromProof<DriveDocumentQuery>. A newtype because Documents already has a blanket FromProof<Q: TryInto<DriveDocumentQuery>>.
  • Request shapes no honest server answers with a plain proved document set are refused before any proof machinery runs: prove=false, aggregate SELECT, GROUP BY, HAVING, OFFSET, chained, sub_queries, a v1 limit = Some(0) (the server's validate_and_route rejects it; only v0's 0 means default), a limit above u16::MAX, a wire version outside the served document_query bounds, a contract the provider does not know. Time-range selections resolve against the quorum-signed response time via resolve_time_range_bucket_clause, as the SDK's FromProof<DocumentQuery> does.

Pure document builders, in dash-platform-queries:

  • dpns_usernames::{build_dpns_preorder_document, build_dpns_domain_document, salted_domain_hash} and dashpay::build_contact_request_document, the assembly halves of dash-sdk's register_dpns_name / create_contact_request as pure functions over caller-supplied entropy, salt and ciphertexts. dash-sdk's networked flows now call them.
  • They reuse what exists: normalization is dpp's consensus convert_to_homograph_safe_chars (the crate's to_ascii_lowercase copy, which differed from the data trigger on non-ASCII input, is replaced by a re-export); the preorder commitment uses dpp::util::hash::hash_double; property names come from the dpns-contract / dashpay-contract constants; the DashPay byte bounds are read from the contract schema's DocumentPropertyType sizes. The domain builder rejects a label the contract pattern refuses before a preorder is paid for.
  • The entropy/document-id consistency check moves into dpp's DocumentCreateTransitionV0::from_document, so every create-transition caller (SDK, wasm, FFI, embedders) inherits it instead of remembering an opt-in helper; it fails with the same InvalidDocumentTransitionIdError Drive would return after the nonce bump. dash-sdk's private ensure_entropy_matches_document_id is removed.

How Has This Been Tested?

  • New tests/vectors_document_request.rs in drive-proof-verifier (14 tests): the four documents fixtures of the proof-vector corpus replayed from synthesized v1 wire requests lower to the exact query the fixture proof was generated for (root hash pinned), a v0 CBOR request lowers to the same query, and each shape gate (prove=false, aggregate select, GROUP BY/HAVING/OFFSET, chained, v1 limit 0, limit above cap, unknown contract, cursor inclusion semantics, wire-version gate) is pinned. The rest of the crate's suite: 297 tests pass.
  • dash-platform-queries lib tests: 46 pass, including builder tests against the real DPNS/DashPay system contracts (preorder commitment matches the domain document's salt+label, id derivation, label-pattern rejection, schema byte-bound enforcement).
  • dpp: from_document_refuses_an_id_the_entropy_does_not_derive; the batch/document-create suites pass (323).
  • dash-sdk --lib: 185 pass. Clippy -D warnings and cargo machete clean on all touched crates.
  • Not done here: a live-network run of register_dpns_name / send_contact_request; the on-wire behaviour is byte-identical by construction (assembly moved, not changed) and the unit tests pin the property maps.

Breaking Changes

API shape on unreleased v4.2-dev (not on crates.io): dash_sdk::platform::dashpay::ContactRequestResult now carries the assembled document plus entropy instead of id / owner_id / properties. No consumer outside rs-sdk uses it. register_dpns_name now rejects a label the DPNS contract pattern refuses locally instead of after the preorder is broadcast.

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests
  • I have added "!" to the title and described breaking changes in the corresponding section if my code contains any
  • I have made corresponding changes to the documentation if needed

For repository code-owners and collaborators only

  • I have assigned this pull request to a milestone

…equest

Adds FromProof<GetDocumentsRequest> for a new RequestedDocuments newtype, plus the DocumentWireQuery it decodes through (TryFromRequest, like every other request-driven verifier in this crate). Embedders that own their transport (the Dash Core platform GUI, explorers) hold their request only as protobuf bytes, so the verifier must rebuild the query the same way the server did: v1 clauses decode through the shared platform-query-wire decoder rs-drive-abci uses, v0 CBOR clauses through drive's from_components parsers, and both lower through DriveDocumentQuery::from_typed_clauses under the default DriveConfig and the response's platform version. Request shapes the server never answers with a plain proved document set (aggregate SELECT, GROUP BY, HAVING, OFFSET, chained, prove=false, an unserved wire version) are refused up front; the data contract is resolved through the ContextProvider so a server cannot supply the schema.

RequestedDocuments is a newtype because Documents already carries a blanket FromProof<Q: TryInto<DriveDocumentQuery>> that coherence will not let a concrete GetDocumentsRequest impl sit beside.

Tests replay the existing proof-vector corpus from wire requests (v1 for all four document fixtures, v0 CBOR for one) and pin every shape gate.
dash-platform-queries gains build_dpns_preorder_document / build_dpns_domain_document / salted_domain_hash (dpns_usernames) and build_contact_request_document (new dashpay module): the document-assembly halves of dash-sdk's register_dpns_name and create_contact_request as pure functions that take caller-supplied entropy, salt and ciphertexts and touch no network or randomness. dash-sdk's networked flows now call them, so an embedder that assembles its own transitions (the Dash Core platform GUI) and the SDK share one implementation.

The builders lean on what the codebase already has rather than re-deriving it: normalization is dpp's consensus convert_to_homograph_safe_chars (the crate's ASCII-only copy, whose non-ASCII behaviour differed from the data trigger's, is replaced by a re-export); the preorder commitment uses dpp::util::hash::hash_double; property names come from the dpns-contract / dashpay-contract constants; and the DashPay byte-array bounds (96 / 48-80 / 38-102) are read from the contract schema instead of being hard-coded. The DPNS domain builder rejects a label the contract's pattern refuses before a preorder is paid for.

The entropy/document-id consistency check moves into dpp's DocumentCreateTransitionV0::from_document, where every create-transition caller (SDK, wasm, FFI, embedders) inherits it instead of having to remember an opt-in helper; it refuses with the same InvalidDocumentTransitionIdError Drive would return after the nonce bump. dash-sdk's private ensure_entropy_matches_document_id and its tests are removed in favour of that.

API shape (unreleased v4.2-dev): ContactRequestResult now carries the assembled document plus entropy instead of id/owner_id/properties; send_contact_request no longer hand-rebuilds a DocumentV0.
@coderabbitai

coderabbitai Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

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: Team

Run ID: bc45f2d4-2419-4130-95ab-87a7bdc19ee4

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

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@thepastaclaw

thepastaclaw commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

🕓 Queued for automated review — 42nd in line, estimated start in ~68 h (commit 42419d2)
Estimated review time once started: ~3 h (two-phase automated review; median of recent runs).

  • Request priority review — tick this box and the review moves to the front of the queue.

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.

2 participants