refactor(drive-abci): extract v1 document-query wire decoders into platform-query-wire - #4464
Draft
PastaPastaPasta wants to merge 1 commit into
Draft
refactor(drive-abci): extract v1 document-query wire decoders into platform-query-wire#4464PastaPastaPasta wants to merge 1 commit into
PastaPastaPasta wants to merge 1 commit into
Conversation
…atform-query-wire
Pure move: the wire-proto -> drive-type decoders for the v1 getDocuments surface now live in a new packages/rs-platform-query-wire micro-crate (crate name platform-query-wire) behind a neutral DecodeError { InvalidArgument, Unsupported }, and drive-abci's v1/conversions.rs becomes a thin adapter mapping DecodeError onto its QueryError surface with the exact same message strings (including the aggregate ORDER BY arm, which maps Unsupported onto QuerySyntaxError::Unsupported). No behavior change to server request decoding.
The decode of a wire request into a rich query is an equivalence contract at a trust boundary: a client-side proof verifier must interpret a request exactly as the server does, or a proof could verify against a different query than the server answered. Hosting the shared decoders in a neutral crate lets both rs-drive-abci and (in a follow-up) the client-side SDK decoders run the same functions, without making the consensus server depend on SDK-branded code: the new crate's dependencies (dapi-grpc platform+client without transport, dpp, drive/verify, thiserror) are a strict subset of what drive-abci already carries.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Issue being fixed or feature implemented
The problem this solves is not an SDK problem — it's a duplication problem at a security boundary.
Why doesn't the existing SDK already need this? Because the SDK never decodes a request. It builds the rich
DocumentQueryobject in-process, encodes it to wire bytes on the way out, and verifies the returned proof against the same in-memory object it still holds (FromProof<DocumentQuery>takes the rich query, not bytes). One process, one object — the bytes→query direction never runs client-side. It has only ever existed in the server, the one party that receives requests as bytes:rs-drive-abci/src/query/document_query/v1/conversions.rs(~380 lines), unreachable by any client.What changes now: Dash Core's DashPay GUI (#4389) is the first client that holds its own request only as wire bytes — its transport lives in C++, and bytes are what cross the FFI. Its verification entry point therefore has to reconstruct the query from those bytes, running the server's decode direction client-side for the first time. (Binding verification to the bytes actually sent, rather than to a parallel in-memory object, is also the point — the proof then attests to exactly what went on the wire.) The same will hold for any future embedder with its own transport: block explorers, wallet tooling.
And that reconstruction must match the server's interpretation exactly — a proof only protects the client if the client rebuilds the query precisely as an honest server would have executed it. Since the decode lives inside drive-abci, unreachable by clients, #4389 is forced to carry a byte-for-byte copy of those 380 lines with a comment saying "keep this in lockstep with the server" — because there is currently nowhere shared to put the code.
Hand-maintained copies of security-critical logic rot in a specific, nasty way here: if the server's decode gains a new operator, clause type, or changed edge-case handling and the client copy isn't updated in the same breath, the two sides now interpret the same request bytes differently. The failure isn't a crash — it's a client whose "verified" answer corresponds to a subtly different query than the one the server answered. Drift is silent until it's exploitable or produces wrong data.
Why a new crate? Every existing home is wrong for one side or the other:
dapi-grpcshouldn't know drive's query model;rs-drivedeliberately carries nodapi-grpcdependency;dash-platform-queries,dash-sdk) would pull client-flavored dependencies into the consensus server's build graph — an earlier draft inside refactor(sdk): shared wire-request decode and pure DPNS/DashPay document builders #4389 tried exactly that and was rejected for inverting the layering.Hence this micro-crate:
platform-query-wire, containing only the wire→drive decode, with dependencies that are a strict subset of what drive-abci already builds (dapi-grpc,dpp,drive,thiserror— no proof verifier, no context provider, no async runtime, no transport). The server consumes it through a thin error-mapping adapter with zero behavior change; a small follow-up deletes #4389's client-side copy and points it here. From then on, server and verifiers compile against the same functions and this class of drift is structurally impossible instead of a standing review obligation.This PR is fully independent and standalone — it does not stack on or depend on #4389, and nothing regresses if it merges before or after it. It exists now, rather than later, because the first client copy is being introduced right now; landing the shared home early keeps that copy's lifetime short.
What was done?
packages/rs-platform-query-wire(crate nameplatform-query-wire, matching thepackages/rs-platform-*→platform-*convention). Scope: decode only — no transport, no proof verification, no async runtime.dapi-grpc(default-features = false,platform+client—client = ["platform"]adds no dependencies and is required because the generated message types are only emitted under theclient/servercfgs on native targets; thetransportfeature stays off),dpp(default-features = false),drive(default-features = false,verify),thiserror. drive-abci already enables dapi-grpc'sserverfeature (a superset ofclient) and a superset of thedpp/drivefeatures, so the server's build graph gains no new dapi-grpc features and no new third-party dependencies.v1/conversions.rsintoplatform_query_wire::proto_conversionsverbatim (byte-identical bodies including every error message string), behind a neutralDecodeError { InvalidArgument(String), Unsupported(String) }.v1/conversions.rsis now a thin adapter mappingDecodeErrorback onto itsQueryErrorsurface with identical message strings:InvalidArgument(msg)→QueryError::InvalidArgument(msg), and the aggregate ORDER BY armUnsupported(msg)→QueryError::Query(QuerySyntaxError::Unsupported(msg)). No server behavior change.Cargo.tomlmembers,Cargo.lock, the four Dockerfile workspace COPY lists, and the three.github/package-filtersfiles (with the new crate added to drive-abci's filter, since it is now a drive-abci dependency).check-features/the nightly per-feature matrix (the crate has no features; the check-features harness panics on crates without a[features]table) nor to the transport-free CI assertion step (that step is specific todash-platform-queries/drive-proof-verifier).How Has This Been Tested?
conversions.rs(atv4.2-dev) against the new crate'sproto_conversions.rswith comments stripped and the declared transforms normalized (error-type rename, the aggregate ORDER BY arm neutralization, visibility qualifiers,#[allow(dead_code)]removal, rustfmt re-wrapping of two signatures). Result: normalized bodies are identical — every function body and error message string is unchanged.cargo check -p platform-query-wire -p drive-abci --locked— passes, no new warnings (the only warning is the pre-existing workspace-widedashcoredefault-featuresmanifest note).cargo test -p drive-abci --locked document_query— 93 passed, 0 failed (1 ignored), covering the v1 decode paths end to end including the error-message assertions inv1/tests.rs.cargo test -p platform-query-wire --locked— passes (the moved code's tests live in drive-abci and exercise the shared decoders through the adapter).cargo clippy -p platform-query-wire -p drive-abci --all-targets --locked— no new warnings.cargo fmt -p platform-query-wire -p drive-abci -- --check— clean.platform-query-wireentry defined before its use in drive-abci's list).Breaking Changes
None. The server's request decoding, error variants, and error message strings are byte-for-byte unchanged. The new crate is additive.
Checklist:
For repository code-owners and collaborators only