Skip to content

feat(qt): back Dash Platform GUI internals with real grovedb/drive/dpp crates - #67

Open
PastaPastaPasta wants to merge 276 commits into
developfrom
platform-gui-rust
Open

feat(qt): back Dash Platform GUI internals with real grovedb/drive/dpp crates#67
PastaPastaPasta wants to merge 276 commits into
developfrom
platform-gui-rust

Conversation

@PastaPastaPasta

@PastaPastaPasta PastaPastaPasta commented Aug 7, 2026

Copy link
Copy Markdown
Owner

What this is

The composite reference implementation of Dash Platform usernames, profiles, and DashPay contacts in dash-qt, assembled from the current train foundations (tracked in dashpay#7512). It proves the whole path end-to-end — Platform crates, Core build, wallet seams, GUI — while the individual train PRs are reviewed and merged. It is an integration branch, not a merge candidate.

Branch structure (bottom-up)

  1. Base: build: add optional Rust and CXX bridge support dashpay/dash#7580 (feat/optional-rust-components) — opt-in Rust + cxxbridge build foundation, validated-host target set, and Guix-baseline coverage.
  2. DIP-14 Derive256: feat: add DIP-14 256-bit child key derivation (Derive256) dashpay/dash#7511 (merged) — cherry-picked prerequisite until the base catches up with develop.
  3. Wallet seams: feat(wallet): add Platform key provider, data records and DIP-15 friendship keychain seams dashpay/dash#7581 (dashpay/wallet-seams) — DIP-13/14/15 key provider, opaque Platform data records, fail-closed Platform-seed pinning, and friendship-keychain import with preserved descriptor state. Includes the cross-implementation derivation vector against rust-dashcore's key-wallet for mobile parity.
  4. Platform client + GUI — the feat(qt): back Dash Platform GUI internals with real grovedb/drive/dpp crates #67 line of work: C++ mbedTLS/gRPC-Web transport with proof-gated reads and Qt identity/username/profile/contact flows.
  5. Platform-owned Rust/CXX implementation: feat(sdk): add transport-free CXX bindings dashpay/platform#4416 — the transport-free DPP/Drive implementation, CXX schema, signer interface, generated headers, and static archive now live in the Platform repository.

Platform ownership and Core integration

--enable-platform-gui no longer builds an in-tree rust/platform crate. Core's depends system pins Platform dashpay#4416 commit df4fdb68559ef57d50624b7f0841594aef8647e5, vendors its standalone Cargo dependency graph, builds it offline, and installs this interface into the target prefix:

  • include/dash/platform/ffi.h
  • include/dash/platform/signer.h
  • include/dash/platform/src/lib.rs.h
  • include/rust/cxx.h
  • lib/libdash_platform_cxx.a

Core includes and links only that installed interface. Its duplicated Platform Rust crate, generated bridge build, and signer header have been removed. The Platform archive is linked only into dash-qt and the Platform-gated test/fuzz binaries; dashd, dash-cli, dash-tx, wallet libraries, and default builds remain free of Platform code.

Core's independent chirp smoke component remains available through --enable-rust. --enable-rust and --enable-platform-gui are intentionally mutually exclusive because separately built Rust static archives each contain Rust and CXX runtime symbols; linking both into one binary is non-portable and produced duplicate runtime definitions.

Platform dashpay#4416 is currently stacked on dashpay#4389 and assumes Platform dashpay#4388/dashpay#4389 merge. After that merge, dashpay#4416 should be rebased/retargeted to v4.2-dev; if its commit changes, this branch's depends pin and source hash must be refreshed together.

Validation

  • Platform Backport v18 vijay batch3 dashpay/dash#4416: 31 Rust tests, clippy, release standalone build/install, and a C++ installed-interface smoke test pass.
  • Core depends: a full PLATFORM_GUI=1 dependency build succeeds from the pinned Platform source and vendored archive.
  • Core Platform GUI: configure's real C++ header/archive link probe passes; a full native build links dash-qt, unit tests, Qt tests, fuzz, and other targets without duplicate Rust/CXX runtime definitions.
  • Core-only Rust: a separate --enable-rust --enable-online-rust --disable-platform-gui full build passes.
  • Platform-gated tests: platform_drive_tests (5), platform_dpp_tests (9), and platformkeys_tests (23) pass.
  • All 11 Platform fuzz targets are present; representative decode and proof-verification targets pass empty-input smoke runs.
  • Full repository lint passes locally (Python style lint skipped because flake8 is unavailable).

Known follow-ups

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 278 files, which is 178 over the limit of 100.

To get a review, reduce the PR to 100 files or fewer by splitting it into smaller PRs or changing its base branch.

Upgrade to a paid plan to raise the limit.

This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f8048cdd-4e7f-44e2-b25d-24bf1a4abeb4

📥 Commits

Reviewing files that changed from the base of the PR and between e5e5997 and 126bc9a.

⛔ Files ignored due to path filters (2)
  • Cargo.lock is excluded by !**/*.lock
  • depends/patches/native_cxxbridge/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (278)
  • .cargo/config.toml.offline
  • .github/workflows/build-depends.yml
  • .github/workflows/build-src.yml
  • .github/workflows/build.yml
  • .github/workflows/cache-depends-sources.yml
  • .github/workflows/guix-build.yml
  • .gitignore
  • Cargo.toml
  • Makefile.am
  • ci/dash/matrix.sh
  • ci/test/00_setup_env_native_asan.sh
  • ci/test/00_setup_env_native_platform_gui.sh
  • ci/test/00_setup_env_native_rust.sh
  • ci/test/04_install.sh
  • configure.ac
  • contrib/containers/guix/scripts/guix-start
  • contrib/devtools/cargo-vendor-git-sources.sh
  • contrib/devtools/update-native-cxxbridge.py
  • contrib/devtools/update-rust-hashes.py
  • contrib/guix/guix-build
  • contrib/guix/libexec/build.sh
  • contrib/guix/manifest.scm
  • depends/Makefile
  • depends/README.md
  • depends/config.site.in
  • depends/funcs.mk
  • depends/packages/mbedtls.mk
  • depends/packages/native_cxxbridge.mk
  • depends/packages/native_protobuf.mk
  • depends/packages/native_rust.mk
  • depends/packages/packages.mk
  • depends/packages/platform_cxx.mk
  • depends/packages/rust_stdlib.mk
  • depends/packages/rustcxx.mk
  • depends/packages/tenderdash_sources.mk
  • depends/patches/native_cxxbridge/cargo-config.toml
  • depends/patches/native_rust/fix-elf-interpreter.sh
  • depends/patches/platform_cxx/cargo-config.toml
  • doc/README.md
  • doc/design/assumeutxo.md
  • doc/design/platform-rust-scope.md
  • doc/release-notes-25122.md
  • doc/release-notes-25504.md
  • doc/release-notes-7595.md
  • doc/release-notes-7600.md
  • doc/rust.md
  • rust-toolchain.toml
  • rust/Makefile.am
  • rust/Makefile.chirp.include
  • rust/Makefile.common.include
  • rust/Makefile.libs.include
  • rust/chirp/Cargo.toml
  • rust/chirp/build.rs
  • rust/chirp/src/lib.rs
  • rust/dashrust/Cargo.toml
  • rust/dashrust/src/lib.rs
  • src/Makefile.am
  • src/Makefile.bench.include
  • src/Makefile.qt.include
  • src/Makefile.qttest.include
  • src/Makefile.test.include
  • src/bench/load_external.cpp
  • src/bls/bls.cpp
  • src/chain.h
  • src/coinjoin/client.cpp
  • src/coinjoin/coinjoin.cpp
  • src/coinjoin/coinjoin.h
  • src/coinjoin/server.cpp
  • src/coinjoin/server.h
  • src/evo/assetlocktx.h
  • src/evo/chainhelper.cpp
  • src/evo/chainhelper.h
  • src/evo/core_write.cpp
  • src/evo/deterministicmns.cpp
  • src/evo/deterministicmns.h
  • src/evo/evodb.cpp
  • src/evo/evodb.h
  • src/evo/providertx.cpp
  • src/evo/providertx.h
  • src/evo/providertx_service.cpp
  • src/evo/providertx_service.h
  • src/evo/smldiff.cpp
  • src/evo/specialtxman.cpp
  • src/governance/core_write.cpp
  • src/init.cpp
  • src/interfaces/chain.h
  • src/interfaces/node.h
  • src/interfaces/providertx.h
  • src/interfaces/wallet.h
  • src/llmq/blockprocessor.cpp
  • src/llmq/core_write.cpp
  • src/llmq/dkgsessionhandler.cpp
  • src/llmq/dkgsessionhandler.h
  • src/llmq/net_dkg.cpp
  • src/llmq/net_dkg.h
  • src/llmq/net_quorum.cpp
  • src/llmq/snapshot.cpp
  • src/masternode/payments.cpp
  • src/masternode/payments.h
  • src/node/blockstorage.cpp
  • src/node/blockstorage.h
  • src/node/chainstate.cpp
  • src/node/chainstate.h
  • src/node/interfaces.cpp
  • src/node/utxo_snapshot.cpp
  • src/node/utxo_snapshot.h
  • src/platform/README.md
  • src/platform/client.h
  • src/platform/dpp/document.cpp
  • src/platform/dpp/document.h
  • src/platform/dpp/identity.cpp
  • src/platform/dpp/identity.h
  • src/platform/dpp/statetransitions.cpp
  • src/platform/drive/queries.cpp
  • src/platform/drive/queries.h
  • src/platform/params.cpp
  • src/platform/params.h
  • src/platform/statetransitions.h
  • src/platform/transport/cbor.h
  • src/platform/transport/client.cpp
  • src/platform/transport/endpoint_retry.h
  • src/platform/transport/freshness.h
  • src/platform/transport/grpcweb.cpp
  • src/platform/transport/grpcweb.h
  • src/platform/transport/protobuf.cpp
  • src/platform/transport/protobuf.h
  • src/platform/transport/tls.cpp
  • src/platform/transport/tls.h
  • src/platform/types.h
  • src/platform/walletrecords.cpp
  • src/platform/walletrecords.h
  • src/qt/bitcoinaddressvalidator.cpp
  • src/qt/bitcoinaddressvalidator.h
  • src/qt/bitcoingui.cpp
  • src/qt/bitcoingui.h
  • src/qt/forms/optionsdialog.ui
  • src/qt/forms/sendcoinsentry.ui
  • src/qt/optionsdialog.cpp
  • src/qt/optionsmodel.cpp
  • src/qt/optionsmodel.h
  • src/qt/platform/contactflow.cpp
  • src/qt/platform/contactflow.h
  • src/qt/platform/contactpickerdialog.cpp
  • src/qt/platform/contactpickerdialog.h
  • src/qt/platform/contactsmodel.cpp
  • src/qt/platform/contactsmodel.h
  • src/qt/platform/contactspage.cpp
  • src/qt/platform/contactspage.h
  • src/qt/platform/createusernamewizard.cpp
  • src/qt/platform/createusernamewizard.h
  • src/qt/platform/identityflow.cpp
  • src/qt/platform/identityflow.h
  • src/qt/platform/platformpage.cpp
  • src/qt/platform/platformpage.h
  • src/qt/platform/platformrecovery.cpp
  • src/qt/platform/platformrecovery.h
  • src/qt/platform/platformservice.cpp
  • src/qt/platform/platformservice.h
  • src/qt/platform/profiledialog.cpp
  • src/qt/platform/profiledialog.h
  • src/qt/platform/usernamesearchdialog.cpp
  • src/qt/platform/usernamesearchdialog.h
  • src/qt/sendcoinsdialog.cpp
  • src/qt/sendcoinsdialog.h
  • src/qt/sendcoinsentry.cpp
  • src/qt/sendcoinsentry.h
  • src/qt/test/providertransactiontests.cpp
  • src/qt/test/providertransactiontests.h
  • src/qt/test/test_main.cpp
  • src/qt/test/uritests.cpp
  • src/qt/transactiondesc.cpp
  • src/qt/transactionfilterproxy.h
  • src/qt/transactionrecord.cpp
  • src/qt/transactionrecord.h
  • src/qt/transactiontablemodel.cpp
  • src/qt/transactionview.cpp
  • src/qt/walletframe.cpp
  • src/qt/walletframe.h
  • src/qt/walletmodel.cpp
  • src/qt/walletmodel.h
  • src/qt/walletview.cpp
  • src/qt/walletview.h
  • src/rpc/blockchain.cpp
  • src/rpc/client.cpp
  • src/rpc/coinjoin.cpp
  • src/rpc/evo.cpp
  • src/rpc/evo_util.cpp
  • src/rpc/evo_util.h
  • src/rpc/governance.cpp
  • src/rpc/masternode.cpp
  • src/rpc/mempool.cpp
  • src/rpc/mining.cpp
  • src/rpc/net.cpp
  • src/rpc/node.cpp
  • src/rpc/quorums.cpp
  • src/rpc/rawtransaction.cpp
  • src/rpc/util.cpp
  • src/rpc/util.h
  • src/test/blockmanager_tests.cpp
  • src/test/coinjoin_inouts_tests.cpp
  • src/test/coinstatsindex_tests.cpp
  • src/test/data/platform/dpp_identity_vectors.json
  • src/test/data/platform/dpp_st_vectors.json
  • src/test/data/platform/drive_query_vectors.json
  • src/test/data/platform/quorum_sig_vectors.json
  • src/test/evo_assetlocks_tests.cpp
  • src/test/evo_cbtx_tests.cpp
  • src/test/evo_db_tests.cpp
  • src/test/evo_deterministicmns_tests.cpp
  • src/test/evo_netinfo_tests.cpp
  • src/test/fuzz/dkg_message_framing.cpp
  • src/test/fuzz/load_external_block_file.cpp
  • src/test/fuzz/platform_bridge.cpp
  • src/test/interfaces_tests.cpp
  • src/test/llmq_dkg_tests.cpp
  • src/test/masternode_payments_tests.cpp
  • src/test/platform_client_tests.cpp
  • src/test/platform_dpp_tests.cpp
  • src/test/platform_drive_tests.cpp
  • src/test/system_tests.cpp
  • src/test/util/chainstate.h
  • src/test/util/llmq_tests.h
  • src/test/util/setup_common.cpp
  • src/test/util/setup_common.h
  • src/test/util_tests.cpp
  • src/test/validation_block_tests.cpp
  • src/test/validation_chainstate_tests.cpp
  • src/test/validation_chainstatemanager_tests.cpp
  • src/util/fs_helpers.cpp
  • src/util/fs_helpers.h
  • src/util/ranges_set.cpp
  • src/validation.cpp
  • src/validation.h
  • src/wallet/coinselection.cpp
  • src/wallet/coinselection.h
  • src/wallet/interfaces.cpp
  • src/wallet/platformkeys.cpp
  • src/wallet/platformkeys.h
  • src/wallet/platformseed.cpp
  • src/wallet/platformseed.h
  • src/wallet/receive.cpp
  • src/wallet/receive.h
  • src/wallet/rpc/backup.cpp
  • src/wallet/rpc/coins.cpp
  • src/wallet/rpc/spend.cpp
  • src/wallet/rpc/transactions.cpp
  • src/wallet/rpc/util.cpp
  • src/wallet/rpc/util.h
  • src/wallet/rpc/wallet.cpp
  • src/wallet/spend.cpp
  • src/wallet/test/availablecoins_tests.cpp
  • src/wallet/test/coinselector_tests.cpp
  • src/wallet/test/fuzz/coinselection.cpp
  • src/wallet/test/platformkeys_tests.cpp
  • src/wallet/test/wallet_tests.cpp
  • src/wallet/test/walletdb_tests.cpp
  • src/wallet/test/walletload_tests.cpp
  • src/wallet/transaction.h
  • src/wallet/wallet.cpp
  • src/wallet/wallet.h
  • src/wallet/walletdb.cpp
  • src/wallet/walletdb.h
  • test/functional/feature_asset_locks.py
  • test/functional/feature_llmq_connections.py
  • test/functional/feature_llmq_dkg_intake.py
  • test/functional/feature_protx_version.py
  • test/functional/mempool_expiry.py
  • test/functional/p2p_quorum_data.py
  • test/functional/test_framework/messages.py
  • test/functional/wallet_basic.py
  • test/functional/wallet_dash_rpcs.py
  • test/functional/wallet_groups.py
  • test/functional/wallet_listreceivedby.py
  • test/functional/wallet_listsinceblock.py
  • test/functional/wallet_resendwallettransactions.py
  • test/lint/lint-circular-dependencies.py
  • test/sanitizer_suppressions/lsan
  • test/util/data/non-backported.txt

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


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.

cargo vendor handles git dependencies, but consuming the vendor directory
offline requires per-git-source replacement stanzas in .cargo/config.toml,
which the build system previously did not generate (only crates.io was
redirected). Derive the stanzas deterministically from Cargo.lock with
contrib/devtools/cargo-vendor-git-sources.sh — output verified byte-identical
to what cargo vendor itself prints — and append them when generating the
offline config. Git dependencies are unavoidable for upcoming components:
crates.io copies of the dashpay/platform crates are stale or name-squatted.
PastaPastaPasta and others added 21 commits August 11, 2026 12:23
…d JSON

fc892c3 rpc: Fail to return undocumented or misdocumented JSON (MarcoFalke)
f4bc4a7 rpc: Add m_skip_type_check to RPCResult (MarcoFalke)

Pull request description:

  This avoids documentation shortcomings such as the ones fixed in commit e7b6272, 138d55e, 577bd51, f8c84e0, 0ee9a00, 13f4185, or faecb2e

ACKs for top commit:
  fanquake:
    ACK fc892c3 - tested that this catches issue, i.e bitcoin#24691:

Tree-SHA512: 9d0d7e6291bfc6f67541a4ff746d374ad8751fefcff6d103d8621c0298b190ab1d209ce96cfc3a0d4a6a5460a9f9bb790eb96027b16e5ff91f2512e40c92ca84

Co-authored-by: fanquake <fanquake@gmail.com>
… JSON-RPC interface

Includes 6a1a66c rpc: render Type::ANY in help text instead of aborting

RPCResult::Type::ANY triggers NONFATAL_UNREACHABLE() in ToSections(),
which crashes the help() RPC when a command uses Type::ANY in a
nested result field.

Previously this was never hit because Type::ANY was only used as a
top-level alternate result type, filtered out before ToSections() is
called.

getopenrpcinfo() will use this result type, so render it like other
types allowing it to be used in nested result definitions like schema.

Co-authored-by: will <will@256k1.dev>
PastaPastaPasta and others added 10 commits August 17, 2026 17:55
Cargo-compiled build scripts link libgcc_s.so.1 without an RPATH; in the Guix container GUIX_LD_WRAPPER_DISABLE_RPATH is set and the loader has no default search path providing it, so they fail to start. Set LD_LIBRARY_PATH in CARGO_ENV to the rustc sysroot lib directory, where the depends toolchain stages libgcc_s.so.1 and libz.so.1.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…nces

libcxxbridge and libdashrust reference each other: C++ bridge wrappers call Rust-implemented externs while Rust code calls generated C++ shims like the WalletSigner one. With a single-pass linker, binaries that never call a bridge's C++ wrappers directly (dashd with the platform bridge) leave the shim object unextracted, and references introduced later by libdashrust fail with undefined platform_ffi cxxbridge symbols. List both archives twice in RUST_COMPONENT_LIBS so a second scan converges.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add a workflow_dispatch trigger with a 'hosts' input (default x86_64-linux-gnu) that narrows the build matrix to a single host, and a 'platform_gui' boolean input that passes PLATFORM_GUI=1 into the guix-start invocation so dispatch runs can build the Platform GUI with vendored Rust crates. Also include the native_cxxbridge and top-level Cargo.lock files in the depends-sources cache key so stale source caches do not hide vendored crate changes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The upstream slices were rebased after dropping an out-of-scope dapi-grpc doc hunk from dashpay/platform#4388, which moved the head of the stacked dashpay#4389 branch this pin tracks.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The upstream query-core series was rebased onto the latest v4.2-dev, which absorbed the new having-range aggregate modules into the extracted crate.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Follows the upstream re-export of DocumentQuerySdk alongside DocumentQuery and the restack it produced.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Picks up the request-binding gate in verify_documents_response (rejects requests the server would not have proved) and the QuerySettings/block-info compatibility restorations upstream.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Track dashpay/platform refactor/document-query-decode-builders at 3285b1c99bca after its restack; over-cap document query limits now fail as Error::Drive(InvalidLimit) with pre-proof rejection of limits above 100 and out-of-bounds wire versions. No wrapper adaptations needed: the FFI suite passes unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
walletdb.cpp now reads platformkeys::SEED_ID_RECORD to tell seed-pin damage apart from opaque Platform cache damage, closing a wallet/platformseed -> wallet/scriptpubkeyman -> wallet/walletdb include cycle. The cycle exists on the wallet-seams foundation head itself; whitelist it here until the foundation PR breaks it properly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
wallet-seams removed the unused label parameter from importFriendshipKeychains; the receiving chain is labeled explicitly through setAddressBook instead.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Only a malformed reserved platform/seed-id record is fatal wallet corruption: silently dropping it could unpin the Platform seed and let a multi-seed wallet sign under another identity. All other Platform data records are opaque cache/metadata, so a damaged one now follows the wallet's noncritical-record policy instead of preventing the wallet from opening. The reserved record key moves to a shared constant, and walletdb_tests pins both sides of the policy.
GetPlatformSeed accepted any nonempty stored mnemonic, but CMnemonic::ToSeed hashes arbitrary strings, so a corrupt mnemonic record would silently derive an unrelated Platform key universe. Require the phrase to pass BIP39 validation before it can become a seed candidate; an invalid phrase is skipped like an unreadable one, so a pinned wallet fails closed. platformkeys_tests covers both the rejected invalid phrase and the unchanged valid-mnemonic path.
platform#4389 gained DPNS salt-contract docs, pub(crate) narrowing of the singular proto decoders, and aggregate-cap boundary tests. No FFI-surface changes; rust/platform suite passes 31/31 against the new rev.
PastaPastaPasta added a commit to dashpay/dash that referenced this pull request Aug 18, 2026
…eys and islocks via node interface

ae11a9d test: cover Platform endpoint, quorum and islock node interface seams (pasta)
340be85 test: move mined-commitment EvoDB helpers into llmq test utils (pasta)
047aa52 refactor: expose masternode Platform endpoints, quorum keys and islocks via node interface (pasta)

Pull request description:

  ## Issue being fixed or feature implemented

  Part of the Dash Platform GUI train (#7512). dash-qt's Platform integration needs three pieces of node state that today have no `interfaces::Node` surface: evonode Platform HTTPS endpoints (to select DAPI gateways from the locally synced masternode list), locally retained Platform-quorum public keys (to verify Platform quorum signatures against local LLMQ data instead of trusting a remote key service), and InstantSend locks by txid (to build asset-lock proofs).

  ## What was done?

  Three read-only additions, compiled unconditionally (no Platform types, no feature gate):

  - `MnEntry::getPlatformHTTPSAddrs()` — the `PLATFORM_HTTPS` entries from the extended address list; empty for non-evo masternodes.
  - `LLMQ::getPlatformQuorums(uint8_t llmq_type)` — quorum hash, serialized basic-scheme BLS public key, and height for locally retained quorums, scanning `max(signingActiveQuorumCount, keepOldKeys)` so verification keeps working for proofs signed by recently rotated-out quorums.
  - `LLMQ::getInstantSendLock(const uint256& txid)` — serialized islock or empty.

  These are exports of already-public node state (`netInfo->GetEntries`, `qman->ScanQuorums`, `isman->GetInstantSendLockByTxid`); no consensus surface, no key material, no behavior change for existing code.

  Reference implementation using these seams: PastaPastaPasta#67.

  ## How Has This Been Tested?

  Compiles cleanly (`node/libbitcoin_node_a-interfaces.o` on macOS arm64 against depends). No other implementers of these interface classes exist in-tree (verified across src/test, src/qt/test, src/interfaces, src/ipc), so the pure-virtual additions require no stubs.

  ## Breaking Changes

  None.

Top commit has no ACKs.

Tree-SHA512: 417892131a3284e5edd522eded75ab647e005456f8553003697538ac3ef0273519350d01db2759c5e8d9f1e228a98bcde1c7135e45a6b080ee931eb125505f15
CheckAssetLockTx rejects payload version 2 until DEPLOYMENT_V24 applies to the block that would contain the transaction, but the wallet always stamped CURRENT_VERSION on the payloads it built. Every asset lock created before activation is therefore refused at acceptance with bad-assetlocktx-version-2; the regtest tests miss it because they activate v24 up front.

Derive the payload version from the chain's activation state. Version 1 stays valid after activation, so a transaction built just before the boundary and mined after it remains acceptable. Version 1 admits only P2PKH credit outputs, so reject the other script types up front with an explanation rather than letting the mempool refuse them.
commitTransaction discarded the result of the mempool submission, so a funding transaction the node refused still moved the wizard into FUNDING_SENT. checkFundingLock only ever looked for an islock, a chainlock, a confirmation or an explicit abandon, none of which can arrive for a transaction that was never accepted, so the flow waited forever behind a phantom pending balance and a persisted record resumed straight back into that dead wait.

Return the rejection reason from commitTransaction and roll the flow back when it is set. During the wait, treat unconfirmed-and-absent-from-the-mempool as recoverable first: rebroadcast, since a restart can lose the broadcast without the transaction being invalid. Only once the node keeps refusing it does the flow abandon the transaction, releasing its inputs, and fail.
dashpay/platform#4389 was rebased onto its parent PR's amended documentation commit; the crate contents are unchanged apart from doc comments.
…nsaction

setWalletUTXO holds every unspent output the wallet owns and is what AvailableCoins() walks. AddToSpends() drops an outpoint from it as soon as some wallet transaction spends it, but nothing ever puts it back when that transaction stops spending it, so abandoning a transaction left its inputs invisible to coin selection for the rest of the session. Balances recovered immediately, because those are recomputed from the transaction states, which made the coins look present while every attempt to spend them failed for lack of funds. Restarting cleared it, since the set is rebuilt from scratch at load.

Restore the outpoints a transaction consumed whenever its state changes and they are no longer spent. This covers abandonment as well as a transaction being conflicted away by a competing one, and is a no-op when the transaction still spends them.
Every verify_*/decode_* argument is a byte string a DAPI node chose, parsed by rs-dpp and drive-proof-verifier before anything about it has been verified. The adapters convert Rust panics into C++ exceptions and catch them, so what these targets look for is an input that gets past that: a crash, an uncaught panic, or a verification that fails without saying why.

Eleven targets cover the five decoders and the six proved-query verifiers, built only when the bridge itself is (--enable-platform-gui). The verifiers run against a deliberately fake quorum key, which keeps the fuzzer working on protobuf decoding, GroveDB proof replay and document deserialization rather than stopping at the signature check. Documents that survive verification are fed to the decoders, since proof verification says the node did not invent them, not that they are well formed.
DashPay is descriptor-wallet-only by decision: the contact-payment path already hard-requires descriptor wallets (importFriendshipKeychains), so serving a legacy wallet's HD chain seed only let users build a Platform key universe they could never finish using. GetPlatformSeed now refuses non-descriptor wallets outright, which also closes off signPlatformDigest, ECDH and friendship derivation for them.
Nothing in the DashPay GUI checked the wallet type, so a legacy-wallet user could fund and broadcast an asset lock, register an identity and claim a username - spending real DASH - before hitting the descriptor requirement on their first contact accept. The tab now detects a non-descriptor wallet up front and shows a migrate-this-wallet message instead of creating the PlatformService, which makes the wizard, the recovery probe and every contact flow unreachable before any spend.
PastaPastaPasta added a commit to dashpay/dash that referenced this pull request Aug 19, 2026
…d DIP-15 friendship keychain seams

b2a3c40 fix(wallet): derive Platform keys from descriptor root (pasta)
939ee20 refactor(wallet): avoid serializing friendship xprv (pasta)
81ba952 refactor(wallet): confine Platform derivation to key managers (pasta)
449da14 wallet: make the Platform seed provider descriptor-wallet-only (pasta)
6a92a1c fix(wallet): validate mnemonics before deriving the platform seed (pasta)
d58facb fix(wallet): keep damaged Platform cache records noncritical on load (pasta)
d5e41f4 refactor(wallet): drop the unused friendship import label parameter (pasta)
b1da3d4 fix(wallet): treat corrupt platform data records as wallet corruption (pasta)
3f954b4 fix(wallet): reject unknown platform key types before derivation (pasta)
80a46ec fix(wallet): skip empty-mnemonic descriptors in platform seed selection (pasta)
b78f3cf chore(wallet): annotate m_platform_data locking, add missing includes (pasta)
47c0751 test(wallet): pin friendship derivation against rust-dashcore key-wallet (pasta)
3a430b5 fix(wallet): require a full unlock before serving the legacy platform seed (pasta)
31637d7 fix(wallet): fail platform seed selection on a malformed seed pin (pasta)
005459f fix(wallet): drop platform data from memory only after the database erase succeeds (pasta)
2cd5bcd fix(wallet): preserve friendship descriptor state on re-import (pasta)
d583a42 fix(wallet): fail platform seed selection when the pinned seed is unavailable (pasta)
a67dbc2 fix(wallet): validate parent pubkey before DIP-14 public derivation (pasta)
01c8a8a feat(wallet): add DIP-15 friendship keychain import and platform key provider seams (pasta)
c6fe429 feat(wallet): add generic per-wallet Platform data records (pasta)
3b8fc64 feat(wallet): add Platform (DIP-9/13/14/15) key derivation helpers (pasta)

Pull request description:

  ## Issue being fixed or feature implemented
  Part of the Dash Platform GUI PR train tracked in #7512 (the tracking issue's body still describes an older architecture; the current reference implementation is PastaPastaPasta#67). This PR extracts the **wallet-layer Platform seams**: pure C++ wallet code with **no Rust/FFI dependency**, so it can be reviewed and merged in parallel with the build-system PR #7580.

  Builds on the DIP-14 `Derive256` primitives merged in #7511.

  ## What was done?
  Three seams:

  **1. Platform key derivation helpers (`src/wallet/platformkeys.{h,cpp}`)**
  Pure BIP32/DIP-14 path math, independent of Platform documents/contracts/network:
  - DIP-9 feature-purpose paths; DIP-13 identity authentication and funding paths; DIP-15 friendship keychain paths whose two 256-bit identity components are deliberately non-hardened (enabling watch-only xpub derivation).
  - Private and public (neutered) derivation walkers over mixed 31-bit/256-bit paths. Private derivation starts from a BIP32 extended private key; a mnemonic is only one possible way to create that key and is not required by these primitives.
  - ECDH shared secrets via the libsecp256k1 ECDH KDF (SHA256 of the compressed shared point), matching dashj's `Secp256k1ECDHAgreement` used for DashPay contact request encryption. The secp256k1 subtree is now configured with `--enable-module-ecdh` (previously disabled).
  - Descriptor helpers expose a descriptor's depth-zero root xpub and recover the matching root xprv from its signing provider without reconstructing a BIP39 seed.

  **2. Generic per-wallet Platform data records (walletdb)**
  A string-keyed, opaque key/value store in the wallet database (`DBKeys::PLATFORM_DATA`): `WalletBatch::{Write,Erase}PlatformData`, `CWallet::{Load,Write,Get}PlatformData` (prefix queries), the `ReadKeyValue` load path, and `interfaces::Wallet::{write,get}PlatformData`. Records persist in the wallet database and travel with backups.

  **These records are opaque to the wallet by design.** The wallet stores and returns bytes; interpretation lives entirely with the Platform client layers. They are not consulted for Platform key-source selection or key derivation.

  **3. DIP-15 friendship keychain import + Platform key provider (`interfaces::Wallet`)**
  - `getPlatformPubKey` / `signPlatformDigest` / `platformECDHSecret`: DIP-13 identity authentication and funding keys derived on demand inside the active descriptor key manager; root private key material never crosses the interface.
  - `ensureFriendshipReceivingKeychain`: derives the wallet's **own** DIP-15 receiving chain, imports it idempotently as a ranged private descriptor, and returns its public chain in one wallet-locked operation. The stored descriptor uses the xpub plus its private key rather than serializing the friendship xprv.
  - `DeriveFriendshipPaymentDestination`: derives contact payment destinations statelessly from a contact's stored friendship xpub, without touching any wallet keypool.
  - Platform derivation is descriptor-wallet-only. A compatible active key manager must expose a genuine depth-zero descriptor root and hold its matching private key. When multiple active managers support Platform derivation, their complete root xpubs (including chain code) must agree. Root-xprv-only descriptor imports are supported; child xprvs, watch-only wallets, external signers, and legacy wallets are rejected.

  **Design invariant (please review against it):** the contact's own receiving chain is deliberately **never** imported. If its scriptPubKeys became `IsMine`, payments to the contact would decompose as payments-to-self and the contact's outputs would be counted as our own coins. Payment destinations for a contact are instead derived statelessly from their xpub. `friendship_contact_chain_is_not_ours` pins this (`ISMINE_NO` for both the reversed-id chain and a genuinely foreign contact xpub).

  ### Adaptations relative to the reference branch
  - **De-gated:** on the reference branch this code sat behind `--enable-platform-gui`. That flag does not exist on `develop`, so the extracted code compiles and is tested **unconditionally** (like #7511). The `ENABLE_PLATFORM_GUI` ifdefs and their `#else` stubs were removed, and the secp256k1 ECDH module is enabled unconditionally in `configure.ac`.
  - **Trimmed out of scope** (arrive with later PRs in the train): the asset-lock creation seam (`createAssetLockTransaction`), `startRescanFromHeight`, `wallet/rpc/platform.cpp`, and everything Qt/GUI or Rust/FFI.
  - The DIP-14 test vectors appear here again on top of #7511's `dip14_tests`: that suite pins the raw `CKey::Derive256` primitives, while `platformkeys_tests` pins the same vectors through the new `Path`/`DeriveExtKey` walker (mixed 31-bit/256-bit paths). The duplication is deliberate.
  - New files are listed in `test/util/data/non-backported.txt` so Dash-specific lint (cppcheck, clang-format-diff) covers them.

  ## How Has This Been Tested?
  Built with autotools on macOS (aarch64, depends prefix) from a clean tree.

  New/extended unit tests, all passing:
  - `platformkeys_tests` (22 cases): DIP-14 vectors 1-4 from dashpay/dips dip-0014.md through the path walker; public/private derivation consistency including hardened-step rejection; ECDH symmetry; descriptor root-source agreement; root-xprv-only derivation without a mnemonic; child-xprv rejection; chain-code-sensitive source identity; invalid mnemonic metadata not redirecting derivation; own friendship chain `ISMINE_SPENDABLE` with coins visible to `AvailableCoins`; contact chain `ISMINE_NO`; recovery rederivation of authentication keys, friendship xpubs/destinations, ECDH secrets and compact signatures; import-after-restore making pre-loss payments spendable; import idempotency without script-pub-key-manager duplication.
  - `descriptor_tests`: extraction of a unique root extended public key and recovery of its matching root extended private key from the signing provider, including ambiguity and mismatch rejection.
  - `walletdb_tests`: Platform data record write/prefix-query/erase and the `ReadKeyValue` load path.

  Also run locally: `dip14_tests` (sanity anchor for #7511 interplay) plus `wallet_tests`, `scriptpubkeyman_tests`, `ismine_tests`, `spend_tests`, `availablecoins_tests`, `coinselector_tests`, `descriptor_tests` — all green. `test/lint/all-lint.py` passes.

  ## Breaking Changes
  None. New wallet records are additive and ignored-by-absence; no existing serialization changes. Enabling the secp256k1 ECDH module only adds symbols to the static subtree library.

  ## Checklist:
  - [x] I have performed a self-review of my own code
  - [x] I have commented my code, particularly in hard-to-understand areas
  - [x] I have added or updated relevant unit/integration/functional/e2e tests
  - [ ] I have made corresponding changes to the documentation
  - [ ] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_

Top commit has no ACKs.

Tree-SHA512: 7194093d963190e7a70c16eceba6d01a2664d688b4fe7bcddb3ada4dc08a234fc9d20d35791f6996a2fa4a700d82d26b0f123138a2325ccdd8d461405a067b77
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.

4 participants