You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Dash-QT has long lacked first-class Dash Platform support: usernames (DPNS), DashPay profiles, and contact-to-contact payments — the feature set of the DashPay mobile wallets — without coupling Dash Core to Platform.
A complete, working implementation exists and has been E2E-tested against live testnet Platform (usernames registered, profile published, contact request sent/accepted, payment to a contact-derived address received): PastaPastaPasta#67 is the canonical reference implementation. It is far too large to review as one PR, so it is being landed as a train of small, individually reviewable PRs across two repos (dashpay/platform and dashpay/dash), tracked here. This issue is the living map: where we are, where we're going, and the design decisions made along the way.
History: the original reference was PastaPastaPasta#49 (~17.9k lines of pure C++, including hand-ported GroveDB proof verification, protobuf/bincode codecs, and vendored BLAKE3). On 2026-08-07/08 the architecture pivoted (see decisions log): the hand-written C++ reimplementations were replaced with the canonical Platform Rust crates, and Platform itself was refactored upstream to be embeddable transport-free. #49 remains open only as provenance; #67 supersedes it.
Canonical reference implementation — security-hardened, full testnet E2E proven; rebases onto #7580 once it merges
Architecture (current)
Compile-time optional GUI feature: --enable-platform-gui. dashd and dash-cli link zero Rust in any configuration; default developer builds do no Rust work at all. For the final v24 release, dash-qt release builds ship with Platform GUI enabled (decision 2026-08-12), so Guix reproducibility with the flag on is a release gate.
Rust (canonical crates, not reimplementations): proof verification, response decoding, and state-transition/document construction are done by the real dashpay/platform crates (dpp, drive verify, grovedb verify), pinned by revision and bridged via cxx (rust/platform — an interim location, see End state). Built with panic = "unwind" so malformed bytes from remote nodes surface as catchable C++ exceptions rather than aborting dash-qt.
C++ keeps what Core should own: gRPC-Web/HTTP1.1/TLS (mbedtls) transport; evonode endpoint selection, ban/backoff and cross-node retry from the locally synced deterministic masternode list; wallet signing seams; Qt GUI.
No trusted intermediary: every query uses prove=true. Verification runs through the upstream FromProof machinery with a ContextProvider fed locally synced LLMQ Platform quorum keys from C++ — the trust anchor is unchanged from the original design; only where the check executes moved. Platform quorum signatures are checked in Rust against local keys, never against a remote key service.
Additive wallet/node seams: DIP-13/14/15 key derivation, sign-only interfaces (raw keys never cross the FFI — Rust computes signable digests, C++ signs), asset-lock transaction creation, opaque per-wallet platform data records, DIP-15 friendship receiving-keychain import. The contact's own receiving chain is deliberately never imported — its scriptPubKeys must not be IsMine, or payments to the contact decompose as payments-to-self; contact destinations are derived statelessly from the stored xpub.
Where everything should live when the train is done:
No first-party Rust wrapper code maintained in dashpay/dash. All wrapper/FFI logic lives upstream in dashpay/platform; Core pins a released upstream revision plus a thin bridge. The in-repo rust/platform crate is explicitly interim. The mechanism — a new minimal rs-platform-primitives-ffi package vs. adapting the existing rs-sdk-ffi/ContextProviderCallbacks surface — will be decided by building a target-state E2E prototype, not by RFC debate.
Request construction moves to Rust (dash-platform-queries): today C++ still hand-builds some request bytes (protobuf field encoding, CBOR where-clauses, contested-resource bincode). That drift surface goes away when Core consumes the upstream query core.
Seed-only recovery of Platform state is implemented (see Release gates).
Core's pin points at merged, released upstream code — not a draft branch.
build: enable Platform GUI in release dash-qt builds; Guix reproducibility with the flag on across all release targets
Release gates (v24)
Guix build testing: deterministic Guix builds with Platform GUI enabled (Rust toolchain, vendored crates, mbedtls, protoc all through depends/Guix, offline). Required because release builds ship enabled.
Seed-only recovery implemented and covered by functional tests (full-backup restore, mnemonic-only restore, recovery at each identity-registration stage). Current state: all key material is seed-derivable; identity record, contact state, pay cursors, and the random preorder salt are not — that gap is what this gate closes.
Malformed-input fuzzing of the verify_*/decode_* bridge surface (attacker-controlled bytes from remote evonodes).
DAPI transport authentication decision: TLS to evonodes is currently unauthenticated by design (reads are proof-gated; broadcast parsing has been hardened). With the GUI on by default in release builds, the certificate-pinning/authentication story needs an explicit decision before final v24.
Source distribution completeness: flag-on builds from release tarballs (known: platform/recoveryplan.h missing from the reference branch's dist tree).
Timeline (v24)
RC1 target: Thu/Fri Aug 20–21 (feature cutoff ~Aug 18). Bar for RC1: the full train merged, compile-time flag available.
Final v24: Platform GUI enabled by default in release dash-qt builds.
Critical path note: seed-only recovery is the only item on the RC1 path that is not yet implemented somewhere; it starts now.
Decisions log
2026-08-01 — IsMine watch-only guard deferred. Extracted as fix(wallet): report public-only descriptors in signing wallets as watch-only #7510, then closed: nothing in the series consumes it — the friendship receiving chain imports as a private descriptor and the contact's chain is never imported. Returns alongside any future xpub-only import work.
2026-08-01 — DIP-14 compatibility-mode semantics.feat: add DIP-14 256-bit child key derivation (Derive256) #7511 treats sub-2^32 indexes as raw BIP32 index space (hardened flag folded into the high bit on the private side; public side rejects high-bit indexes as BIP32 does). The DIP's Compatibility section and published test vectors require this interpretation (vector 2 only reproduces under it).
2026-08-01 — No QWizard. Registration flow is QDialog + QStackedWidget because depends/Guix Qt is built with -no-feature-wizard.
2026-08-07 — Rust crates over C++ reimplementations (the pivot). ~6,300 lines of hand-written security-critical C++ (GroveDB verifier port, bincode/DPP codecs, vendored BLAKE3) would have had to track consensus-critical upstream behavior byte-for-byte by hand, forever. Replaced with the canonical crates behind a cxx bridge (+498/−6,306 on the reference branch), with PR Update README.md #49's own fixtures as a byte-exact conformance harness. Supersedes the earlier "vendor BLAKE3 / port GroveDB" plan (waves 1/3 of the original train).
2026-08-08 — Refactor upstream instead of duplicating (per Platform maintainer feedback). Rather than carrying wrapper code in Core, Platform itself was made embeddable: dapi-grpc grew a transport feature (feat(sdk): add transport feature to dapi-grpc for types-only consumers platform#4344 — Cargo features aren't target-scoped, so transport must be opt-in or it forces tonic's stack onto wasm consumers), rs-sdk is being split (dash-platform-queries), and verification runs through upstream FromProof + ContextProvider over locally synced LLMQ keys — which let Core delete its own quorum-sig verification C++.
2026-08-08 — panic=unwind for the bridge.verify_*/decode_* parse attacker-controlled bytes deep inside a large dependency tree that cannot be exhaustively audited for panics; unwind converts residual panics into catchable C++ exceptions instead of aborting dash-qt. dashd is unaffected (links no Rust).
2026-08-12 — Platform GUI ships enabled in final v24 release dash-qt builds. DashPay must be usable by end users in v24, not a compile-time easter egg. RC1 bar is the full train merged; release enablement (incl. Guix) follows as its own gated step.
2026-08-12 — Seed-only recovery is pre-merge for the wallet wave, in v24. "Your recovery phrase restores your wallet" must hold for Platform state before the wallet wave completes.
2026-08-12 — FFI end state: upstream, mechanism TBD. Wrapper logic moves to dashpay/platform and Core pins a released revision; whether that is a new minimal primitives-FFI package or an adaptation of the existing rs-sdk-ffi surface will be settled by a target-state E2E prototype.
Status detail (2026-08-12)
build: add optional Rust and CXX bridge support #7580: CI green, all automated-review threads resolved, mergeable — awaiting human review. One pre-review fix is queued: macOS deployment-target propagation into Rust artifacts (Rust currently falls back to the host SDK version instead of OSX_MIN_VERSION), plus trimming speculative cross-target entries.
What / why
Dash-QT has long lacked first-class Dash Platform support: usernames (DPNS), DashPay profiles, and contact-to-contact payments — the feature set of the DashPay mobile wallets — without coupling Dash Core to Platform.
A complete, working implementation exists and has been E2E-tested against live testnet Platform (usernames registered, profile published, contact request sent/accepted, payment to a contact-derived address received): PastaPastaPasta#67 is the canonical reference implementation. It is far too large to review as one PR, so it is being landed as a train of small, individually reviewable PRs across two repos (dashpay/platform and dashpay/dash), tracked here. This issue is the living map: where we are, where we're going, and the design decisions made along the way.
Where we are (2026-08-12)
Derive256)dapi-grpctransportfeature — verifier dependency tree drops 407 → 339 crates; no tokio/hyper/rustls in embedder graphsrs-drive-proof-verifierArchitecture (current)
--enable-platform-gui. dashd and dash-cli link zero Rust in any configuration; default developer builds do no Rust work at all. For the final v24 release, dash-qt release builds ship with Platform GUI enabled (decision 2026-08-12), so Guix reproducibility with the flag on is a release gate.verify, grovedbverify), pinned by revision and bridged via cxx (rust/platform— an interim location, see End state). Built withpanic = "unwind"so malformed bytes from remote nodes surface as catchable C++ exceptions rather than aborting dash-qt.prove=true. Verification runs through the upstreamFromProofmachinery with aContextProviderfed locally synced LLMQ Platform quorum keys from C++ — the trust anchor is unchanged from the original design; only where the check executes moved. Platform quorum signatures are checked in Rust against local keys, never against a remote key service.End state (target)
Where everything should live when the train is done:
rust/platformcrate is explicitly interim. The mechanism — a new minimalrs-platform-primitives-ffipackage vs. adapting the existingrs-sdk-ffi/ContextProviderCallbackssurface — will be decided by building a target-state E2E prototype, not by RFC debate.dash-platform-queries): today C++ still hand-builds some request bytes (protobuf field encoding, CBOR where-clauses, contested-resource bincode). That drift surface goes away when Core consumes the upstream query core.Plan — dashpay/platform track
transportcargo feature for types-only consumers — feat(sdk): add transport feature to dapi-grpc for types-only consumers platform#4344 (merged 2026-08-11)dash-platform-queriescrate split from rs-sdk (transport-free query core: DocumentQuery, aggregate proof helpers, DPNS free functions) — refactor(sdk)!: extract transport-free query core into dash-platform-queries platform#4388 (open 2026-08-12)GetDocumentsRequest → DocumentQuerydecode + pure DPNS/DashPay document builders — refactor(sdk): shared wire-request decode and pure DPNS/DashPay document builders platform#4389 (open 2026-08-12, stacked on refactor(sdk)!: extract transport-free query core into dash-platform-queries platform#4388)rs-platform-primitives-ffivs. adaptrs-sdk-ffi) → implementationvsss-rs branch="main", MSRV policyTENDERDASH_DIRdepends source)Plan — dashpay/dash train
Each PR carries its own build-system, lint, and CI increments.
Foundations
--enable-rust, depends toolchain/vendoring,linux64_rustCI lane)Wallet/node seams (additive; stubbed when the flag is off)
Platform client (linked only into dash-qt)
GUI
Release enablement
Release gates (v24)
verify_*/decode_*bridge surface (attacker-controlled bytes from remote evonodes).platform/recoveryplan.hmissing from the reference branch's dist tree).Timeline (v24)
Decisions log
-no-feature-wizard.transportfeature (feat(sdk): add transport feature to dapi-grpc for types-only consumers platform#4344 — Cargo features aren't target-scoped, so transport must be opt-in or it forces tonic's stack onto wasm consumers), rs-sdk is being split (dash-platform-queries), and verification runs through upstreamFromProof+ContextProviderover locally synced LLMQ keys — which let Core delete its own quorum-sig verification C++.verify_*/decode_*parse attacker-controlled bytes deep inside a large dependency tree that cannot be exhaustively audited for panics; unwind converts residual panics into catchable C++ exceptions instead of aborting dash-qt. dashd is unaffected (links no Rust).libbitcoin_query, switch to Drogon as an optional dependency, add helper to directly serve disk contents #7220 pins Trantor withTLS=none(cannot reach https DAPI at all); Drogon's client cannot cancel in-flight requests and parses untrusted lengths unboundedly. Core's transport keeps its 32 MiB cap, absolute deadlines, and cancellation. A clean swap seam exists atGrpcWebUnary()if this is ever revisited.Status detail (2026-08-12)
OSX_MIN_VERSION), plus trimming speculative cross-target entries.dash-platform-queriessplit.