feat(sdk)!: verify document proofs against the wire request and share DPNS/DashPay document assembly - #4619
Conversation
…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.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Team Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
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. Comment |
|
🕓 Queued for automated review — 42nd in line, estimated start in ~68 h (commit 42419d2)
|
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 indash-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 sharedplatform-query-wiredecoders the server uses, v0 through drive's ownWhereClause::from_components/OrderClause::from_components(the same parsersquery_documents_v0runs).RequestedDocuments: FromProof<GetDocumentsRequest>— the same request-driven pattern as the otherFromProof<GetXRequest>impls inproof.rs— resolves the contract through theContextProvider, lowers viaDriveDocumentQuery::from_typed_clausesunderDriveConfig::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 existingFromProof<DriveDocumentQuery>. A newtype becauseDocumentsalready has a blanketFromProof<Q: TryInto<DriveDocumentQuery>>.prove=false, aggregateSELECT,GROUP BY,HAVING,OFFSET,chained,sub_queries, a v1limit = Some(0)(the server'svalidate_and_routerejects it; only v0's0means default), a limit aboveu16::MAX, a wire version outside the serveddocument_querybounds, a contract the provider does not know. Time-range selections resolve against the quorum-signed response time viaresolve_time_range_bucket_clause, as the SDK'sFromProof<DocumentQuery>does.Pure document builders, in
dash-platform-queries:dpns_usernames::{build_dpns_preorder_document, build_dpns_domain_document, salted_domain_hash}anddashpay::build_contact_request_document, the assembly halves ofdash-sdk'sregister_dpns_name/create_contact_requestas pure functions over caller-supplied entropy, salt and ciphertexts.dash-sdk's networked flows now call them.convert_to_homograph_safe_chars(the crate'sto_ascii_lowercasecopy, which differed from the data trigger on non-ASCII input, is replaced by a re-export); the preorder commitment usesdpp::util::hash::hash_double; property names come from thedpns-contract/dashpay-contractconstants; the DashPay byte bounds are read from the contract schema'sDocumentPropertyTypesizes. The domain builder rejects a label the contract pattern refuses before a preorder is paid for.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 sameInvalidDocumentTransitionIdErrorDrive would return after the nonce bump.dash-sdk's privateensure_entropy_matches_document_idis removed.How Has This Been Tested?
tests/vectors_document_request.rsin 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-querieslib 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 warningsandcargo macheteclean on all touched crates.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::ContactRequestResultnow carries the assembleddocumentplusentropyinstead ofid/owner_id/properties. No consumer outsiders-sdkuses it.register_dpns_namenow rejects a label the DPNS contract pattern refuses locally instead of after the preorder is broadcast.Checklist:
For repository code-owners and collaborators only