Repository navigation
DIP: App Connect, wallet sign-in and transition signing for Platform applications - #181
PastaPastaPasta wants to merge 10 commits into
Conversation
…ansition Signing Defines the dash-key: and dash-st: URI protocols for wallet-based QR code login to Dash Platform applications. Covers ECDH key exchange, AES-256-GCM encryption, BIP32+HKDF key derivation, the loginKeyResponse contract schema, and first-time key registration flow.
|
Need the big picture first? Review this PR in Change Stack to see what changed before going file by file. Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
Walkthrough
Pre-merge checks:
|
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
dip-pasta-yappr.md (2)
262-265: Consider clarifying concurrent request handling.The keyIndex logic states that if no document exists,
keyIndexis0. If a wallet processes multiple key exchange requests for the same identity and contract concurrently (before the first document is created), both could usekeyIndex0, potentially creating a race condition. Consider adding guidance on how wallets should handle this scenario (e.g., serialize requests per identity+contract or retry on conflict).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@dip-pasta-yappr.md` around lines 262 - 265, Clarify concurrent-request behavior for keyIndex/loginKeyResponse: explain that when no loginKeyResponse exists wallets must handle races by serializing requests per identity+contract or using a create-if-not-exists atomic operation and retry-on-conflict; reference the keyIndex and loginKeyResponse documents and state a recommended strategy (e.g., queue/serialize per identity+contract, use optimistic concurrency/version checks on loginKeyResponse, or retry on conflict) so two concurrent requests cannot both assume keyIndex = 0 and create duplicate keys.
391-401: Consider exponential backoff for revision conflict retries.The document lifecycle specifies retrying up to 3 times with a fixed 500ms delay on revision conflicts. Consider recommending exponential backoff (e.g., 500ms, 1000ms, 2000ms) to reduce contention under high load while maintaining the same maximum retry count.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@dip-pasta-yappr.md` around lines 391 - 401, The retry logic for handling revision conflicts when updating a loginKeyResponse document should use exponential backoff instead of a fixed 500ms delay: update the retry loop that currently retries up to 3 times with a fixed 500ms pause to instead wait 500ms, then 1000ms, then 2000ms between attempts (keeping the same max retry count), and continue to re-query the loginKeyResponse document on each retry and preserve the existing update/create flow and revision bump behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@dip-pasta-yappr.md`:
- Line 36: The Table of Contents link fragment "#supported-encodings" is broken
because the actual header is "## Encoding"; update either the TOC entry or the
heading so they match—e.g., change the TOC item "* [Supported
Encodings](`#supported-encodings`)" to "* [Encoding](`#encoding`)" or rename the
header "## Encoding" to "## Supported Encodings" so the fragment and header text
are consistent.
---
Nitpick comments:
In `@dip-pasta-yappr.md`:
- Around line 262-265: Clarify concurrent-request behavior for
keyIndex/loginKeyResponse: explain that when no loginKeyResponse exists wallets
must handle races by serializing requests per identity+contract or using a
create-if-not-exists atomic operation and retry-on-conflict; reference the
keyIndex and loginKeyResponse documents and state a recommended strategy (e.g.,
queue/serialize per identity+contract, use optimistic concurrency/version checks
on loginKeyResponse, or retry on conflict) so two concurrent requests cannot
both assume keyIndex = 0 and create duplicate keys.
- Around line 391-401: The retry logic for handling revision conflicts when
updating a loginKeyResponse document should use exponential backoff instead of a
fixed 500ms delay: update the retry loop that currently retries up to 3 times
with a fixed 500ms pause to instead wait 500ms, then 1000ms, then 2000ms between
attempts (keeping the same max retry count), and continue to re-query the
loginKeyResponse document on each retry and preserve the existing update/create
flow and revision bump behavior.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 6edfd7c4-83bf-45c9-b579-c785373b1631
📒 Files selected for processing (1)
dip-pasta-yappr.md
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9482c77c3f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| | ---------- | ----- | ----------- | | ||
| | 9' | Feature purpose (per [DIP-0009](https://github.com/dashpay/dips/blob/master/dip-0009.md)) | | | ||
| | coin_type' | `5` (mainnet) or `1` (testnet/devnet/regtest) | Per [BIP-0044](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki) | | ||
| | 21' | Key exchange feature index | | |
| | --------- | -------- | ----------- | | ||
| | `n` | Yes | Network identifier (`m`, `t`, `d`, or `r`) | | ||
| | `v` | Yes | Protocol version; must be `1` | | ||
| | `id` | No | Base58-encoded identity identifier hint (32 bytes); wallet should pre-select this identity | |
There was a problem hiding this comment.
Extraneous word?
| | `id` | No | Base58-encoded identity identifier hint (32 bytes); wallet should pre-select this identity | | |
| | `id` | No | Base58-encoded identity identifier (32 bytes); wallet should pre-select this identity | |
- Fix broken ToC link: rename heading to 'Supported Encodings' to match ToC entry - Make Binary Payload Layout a subsection of URI Format and reorder above Network Identifiers - Simplify BIP32 path table by removing empty Description column - Add hyperlink to Network Identifiers from dash-st component table - Remove extraneous 'hint' from id parameter description - Remove unnecessary 'Read Only' rows from key registration tables
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@dip-pasta-masternode-shares.md`:
- Around line 15-28: The Table of Contents entries use two spaces after numeric
list markers which violates markdownlint MD030; update each TOC line (e.g., "1.
[Abstract](`#abstract`)", "4. [Specification](`#specification`)" and the nested
items like "4. 1. [Terminology](`#terminology`)") to use a single space after
the period (e.g., "1. [Abstract](`#abstract`)"), and apply the same single-space
fix to the remaining TOC entries indicated in the comment (including lines
29-32) so all numbered list markers conform to MD030.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 4d7df077-48e0-48c6-b473-46f573c1b5eb
📒 Files selected for processing (1)
dip-pasta-masternode-shares.md
| 1. [Abstract](#abstract) | ||
| 2. [Motivation](#motivation) | ||
| 3. [Prior Work](#prior-work) | ||
| 4. [Specification](#specification) | ||
| 1. [Terminology](#terminology) | ||
| 2. [Provider Transaction Version](#provider-transaction-version) | ||
| 3. [Masternode Payout Share](#masternode-payout-share) | ||
| 4. [Registering a Masternode](#registering-a-masternode) | ||
| 5. [Updating Masternode Payout Shares](#updating-masternode-payout-shares) | ||
| 6. [Masternode List State](#masternode-list-state) | ||
| 7. [Masternode Reward Payments](#masternode-reward-payments) | ||
| 8. [Validation Rules](#validation-rules) | ||
| 9. [RPC and JSON Interfaces](#rpc-and-json-interfaces) | ||
| 10. [Special Transaction Filtering](#special-transaction-filtering) |
There was a problem hiding this comment.
Fix list marker spacing to pass markdownlint.
The Table of Contents uses 2 spaces after numbered list markers, but markdownlint rule MD030 expects exactly 1 space. This will cause the GitHub Actions lint check to fail.
📝 Proposed fix for list marker spacing
-1. [Abstract](`#abstract`)
-2. [Motivation](`#motivation`)
-3. [Prior Work](`#prior-work`)
-4. [Specification](`#specification`)
- 1. [Terminology](`#terminology`)
- 2. [Provider Transaction Version](`#provider-transaction-version`)
- 3. [Masternode Payout Share](`#masternode-payout-share`)
- 4. [Registering a Masternode](`#registering-a-masternode`)
- 5. [Updating Masternode Payout Shares](`#updating-masternode-payout-shares`)
- 6. [Masternode List State](`#masternode-list-state`)
- 7. [Masternode Reward Payments](`#masternode-reward-payments`)
- 8. [Validation Rules](`#validation-rules`)
- 9. [RPC and JSON Interfaces](`#rpc-and-json-interfaces`)
- 10. [Special Transaction Filtering](`#special-transaction-filtering`)
+1. [Abstract](`#abstract`)
+2. [Motivation](`#motivation`)
+3. [Prior Work](`#prior-work`)
+4. [Specification](`#specification`)
+ 1. [Terminology](`#terminology`)
+ 2. [Provider Transaction Version](`#provider-transaction-version`)
+ 3. [Masternode Payout Share](`#masternode-payout-share`)
+ 4. [Registering a Masternode](`#registering-a-masternode`)
+ 5. [Updating Masternode Payout Shares](`#updating-masternode-payout-shares`)
+ 6. [Masternode List State](`#masternode-list-state`)
+ 7. [Masternode Reward Payments](`#masternode-reward-payments`)
+ 8. [Validation Rules](`#validation-rules`)
+ 9. [RPC and JSON Interfaces](`#rpc-and-json-interfaces`)
+ 10. [Special Transaction Filtering](`#special-transaction-filtering`)Apply the same 1-space correction to the remaining TOC entries (lines 29-32).
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| 1. [Abstract](#abstract) | |
| 2. [Motivation](#motivation) | |
| 3. [Prior Work](#prior-work) | |
| 4. [Specification](#specification) | |
| 1. [Terminology](#terminology) | |
| 2. [Provider Transaction Version](#provider-transaction-version) | |
| 3. [Masternode Payout Share](#masternode-payout-share) | |
| 4. [Registering a Masternode](#registering-a-masternode) | |
| 5. [Updating Masternode Payout Shares](#updating-masternode-payout-shares) | |
| 6. [Masternode List State](#masternode-list-state) | |
| 7. [Masternode Reward Payments](#masternode-reward-payments) | |
| 8. [Validation Rules](#validation-rules) | |
| 9. [RPC and JSON Interfaces](#rpc-and-json-interfaces) | |
| 10. [Special Transaction Filtering](#special-transaction-filtering) | |
| 1. [Abstract](`#abstract`) | |
| 2. [Motivation](`#motivation`) | |
| 3. [Prior Work](`#prior-work`) | |
| 4. [Specification](`#specification`) | |
| 1. [Terminology](`#terminology`) | |
| 2. [Provider Transaction Version](`#provider-transaction-version`) | |
| 3. [Masternode Payout Share](`#masternode-payout-share`) | |
| 4. [Registering a Masternode](`#registering-a-masternode`) | |
| 5. [Updating Masternode Payout Shares](`#updating-masternode-payout-shares`) | |
| 6. [Masternode List State](`#masternode-list-state`) | |
| 7. [Masternode Reward Payments](`#masternode-reward-payments`) | |
| 8. [Validation Rules](`#validation-rules`) | |
| 9. [RPC and JSON Interfaces](`#rpc-and-json-interfaces`) | |
| 10. [Special Transaction Filtering](`#special-transaction-filtering`) |
🧰 Tools
🪛 GitHub Check: lint
[failure] 24-24: Spaces after list markers
dip-pasta-masternode-shares.md:24:5 MD030/list-marker-space Spaces after list markers [Expected: 1; Actual: 2] https://github.com/DavidAnson/markdownlint/blob/v0.38.0/doc/md030.md
[failure] 23-23: Spaces after list markers
dip-pasta-masternode-shares.md:23:5 MD030/list-marker-space Spaces after list markers [Expected: 1; Actual: 2] https://github.com/DavidAnson/markdownlint/blob/v0.38.0/doc/md030.md
[failure] 22-22: Spaces after list markers
dip-pasta-masternode-shares.md:22:5 MD030/list-marker-space Spaces after list markers [Expected: 1; Actual: 2] https://github.com/DavidAnson/markdownlint/blob/v0.38.0/doc/md030.md
[failure] 21-21: Spaces after list markers
dip-pasta-masternode-shares.md:21:5 MD030/list-marker-space Spaces after list markers [Expected: 1; Actual: 2] https://github.com/DavidAnson/markdownlint/blob/v0.38.0/doc/md030.md
[failure] 20-20: Spaces after list markers
dip-pasta-masternode-shares.md:20:5 MD030/list-marker-space Spaces after list markers [Expected: 1; Actual: 2] https://github.com/DavidAnson/markdownlint/blob/v0.38.0/doc/md030.md
[failure] 19-19: Spaces after list markers
dip-pasta-masternode-shares.md:19:5 MD030/list-marker-space Spaces after list markers [Expected: 1; Actual: 2] https://github.com/DavidAnson/markdownlint/blob/v0.38.0/doc/md030.md
[failure] 18-18: Spaces after list markers
dip-pasta-masternode-shares.md:18:1 MD030/list-marker-space Spaces after list markers [Expected: 1; Actual: 2] https://github.com/DavidAnson/markdownlint/blob/v0.38.0/doc/md030.md
[failure] 17-17: Spaces after list markers
dip-pasta-masternode-shares.md:17:1 MD030/list-marker-space Spaces after list markers [Expected: 1; Actual: 2] https://github.com/DavidAnson/markdownlint/blob/v0.38.0/doc/md030.md
[failure] 16-16: Spaces after list markers
dip-pasta-masternode-shares.md:16:1 MD030/list-marker-space Spaces after list markers [Expected: 1; Actual: 2] https://github.com/DavidAnson/markdownlint/blob/v0.38.0/doc/md030.md
[failure] 15-15: Spaces after list markers
dip-pasta-masternode-shares.md:15:1 MD030/list-marker-space Spaces after list markers [Expected: 1; Actual: 2] https://github.com/DavidAnson/markdownlint/blob/v0.38.0/doc/md030.md
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@dip-pasta-masternode-shares.md` around lines 15 - 28, The Table of Contents
entries use two spaces after numeric list markers which violates markdownlint
MD030; update each TOC line (e.g., "1. [Abstract](`#abstract`)", "4.
[Specification](`#specification`)" and the nested items like "4. 1.
[Terminology](`#terminology`)") to use a single space after the period (e.g., "1.
[Abstract](`#abstract`)"), and apply the same single-space fix to the remaining
TOC entries indicated in the comment (including lines 29-32) so all numbered
list markers conform to MD030.
|
@PastaPastaPasta those last 4 commits don't belong on this branch? |
The dash-key:/dash-st: protocol this draft described was replaced in September: the wallet now derives and registers every key itself (bounded, budgeted, expiring) and returns them through the App Connect system contract. Rewrite the draft as that protocol: dashpay://connect and dashpay://sign, request v2, DIP-13 6'/7' derivation, per-contract encryption keys, the response envelope, app verification and test vectors. Also drop the masternode reward shares draft, which was committed to this branch by mistake and now lives in DIP-26. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Encryption keys gain a final rotation' level on the DIP-13 7' path. Rotation is a wallet-side user action, possible only when a contract's requirement is 2 (Platform never frees a unique slot). Apps must check that a key is still current before encrypting to it. - Define "current key", and let a wallet send a current key it holds at any path it derives on, so keys from earlier wallets keep working. - CRITICAL is allowed on any binding kind; wallets warn before granting. - Encryption bindings may name system contracts that opt in at contract level (DashPay does not: its requirement is on contactRequest only). - Wallets may support a subset at first and must refuse the rest as unsupported, never grant less. - Rewrite the TLV section as "Fields", with the request vector as an example; add derivation vectors for the new path. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The App Connect DIP (dashpay/dips#181, e9b904e) and the DIP-13 paths (dashpay/dips#191) are now written, and the profile contradicted them in several places. The profile now describes Yappr's side and points to the DIP for the protocol; the rules it still restates match the DIP. - Carriers: dashpay://connect is the protocol carrier and required on mainnet; dash-key: v2 is testnet/devnet only; dash-key: v1 and dash-st: are obsolete. Point to the DIP's dashpay://connect vector. - Encryption bindings may name any contract with a requirement of 0 or 2 (not DashPay), not only appId. Yappr naming its anchor is its choice. - Encryption path gains the rotation level; add the current-key rules, the wallet-side rotation, the app's current-key check and the legacy 8-level keys as current keys at another path. - CRITICAL or HIGH on any binding kind; wallets today grant CRITICAL on contractGroup only. System contracts MUST NOT be appId. - Return link and brand-map rules follow the DIP's SHOULD/MUST wording. - Approval sheet: encryption-binding line, CRITICAL warning for any CRITICAL key, full app id one step away, label as the app's claim. - sign: add the scope rule and the describe-or-show-fields rule; fix the status table. - Wallets may ship a subset and refuse the rest as unsupported. Also fix the "no rotation" statements in WALLET_INTEGRATION.md, DM_V5.md and mobile/tools/README.md, and the ENC_BINDING note in APP_CONNECT_CONTRACTS.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The App Connect DIP (dashpay/dips#181, e9b904e) and the DIP-13 paths (dashpay/dips#191) are now written, and the profile contradicted them in several places. The profile now describes Yappr's side and points to the DIP for the protocol; the rules it still restates match the DIP. - Carriers: dashpay://connect is the protocol carrier and required on mainnet; dash-key: v2 is testnet/devnet only; dash-key: v1 and dash-st: are obsolete. Point to the DIP's dashpay://connect vector. - Encryption bindings may name any contract with a requirement of 0 or 2 (not DashPay), not only appId. Yappr naming its anchor is its choice. - Encryption path gains the rotation level; add the current-key rules, the wallet-side rotation, the app's current-key check and the legacy 8-level keys as current keys at another path. - CRITICAL or HIGH on any binding kind; wallets today grant CRITICAL on contractGroup only. System contracts MUST NOT be appId. - Return link and brand-map rules follow the DIP's SHOULD/MUST wording. - Approval sheet: encryption-binding line, CRITICAL warning for any CRITICAL key, full app id one step away, label as the app's claim. - sign: add the scope rule and the describe-or-show-fields rule; fix the status table. - Wallets may ship a subset and refuse the rest as unsupported. Also fix the "no rotation" statements in WALLET_INTEGRATION.md, DM_V5.md and mobile/tools/README.md, and the ENC_BINDING note in APP_CONNECT_CONTRACTS.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ption paths (#1250) The application encryption path is about to become permanent on mainnet. DIP-13 (dashpay/dips#191, used by App Connect in dashpay/dips#181) ends it with a hardened 31-bit rotation level so an identity can replace the encryption key for a contract: m/9'/coin'/5'/7'/0'/identityId'/contractId'/purpose'/rotation', serialised as ser32(rotation + 2^31), with rotation 0' first. `DerivationPath::application_encryption_path` now takes `rotation: u32` and returns `Result`, failing with `Error::InvalidChildNumber` if the rotation does not fit in 31 bits. Migration: callers pass the rotation (0 for a first key) and handle the `Result`. The old 8-level layout moved to `application_encryption_path_without_rotation`, which wallets derive only to recognise keys that pre-DIP wallets registered on testnet and devnet. The pinned vectors now cover the rotation keys and keep the legacy pins against the renamed function. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Under requirement 2 the key Platform records as current can be disabled while an older key is still enabled. Rule 2 applies whenever no key is current, so say that instead of "all of them disabled". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The application encryption path ends with a hardened 31-bit rotation level (dashpay/dips#191, used by the App Connect DIP, dashpay/dips#181): m/9'/coin'/5'/7'/0'/identityId'/contractId'/purpose'/rotation' with rotation 0' first. Without it the path becomes permanent with the first mainnet key and a compromised or retired key could never be replaced. platform-mobile: application_encryption_private_key_from_seed takes a rotation (below 2^31, an error otherwise) and appends it as a hardened level. The 8-level path without rotation stays available as application_encryption_legacy_private_key_from_seed, only to recognise keys that wallets registered on testnet/devnet before the rotation level existed. Tests pin the DIP vectors for both paths (abandon x11 about, mainnet). Bindings: Ferment output for dash-sdk-bindings and dash-sdk-android now has the new signature and the legacy function (regenerated with cargo build of dash-sdk-bindings; the Android copy carries the same two functions), plus a SWIG rename for applicationEncryptionLegacyPrivateKeyFromSeed. Kotlin: ApplicationKeyDerivation.encryption gains a required rotation parameter, so callers must choose it; new keys use 0. The pre-rotation path is the explicitly named legacyEncryptionWithoutRotation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The application encryption path ends with a hardened 31-bit rotation level (dashpay/dips#191, used by the App Connect DIP, dashpay/dips#181): m/9'/coin'/5'/7'/0'/identityId'/contractId'/purpose'/rotation' with rotation 0' first. Without it the path becomes permanent with the first mainnet key and a compromised or retired key could never be replaced. platform-mobile: application_encryption_private_key_from_seed takes a rotation (below 2^31, an error otherwise) and appends it as a hardened level. The 8-level path without rotation stays available as application_encryption_legacy_private_key_from_seed, only to recognise keys that wallets registered on testnet/devnet before the rotation level existed. Tests pin the DIP vectors for both paths (abandon x11 about, mainnet). Bindings: Ferment output for dash-sdk-bindings and dash-sdk-android now has the new signature and the legacy function (regenerated with cargo build of dash-sdk-bindings; the Android copy carries the same two functions), plus a SWIG rename for applicationEncryptionLegacyPrivateKeyFromSeed. Kotlin: ApplicationKeyDerivation.encryption gains a required rotation parameter, so callers must choose it; new keys use 0. The pre-rotation path is the explicitly named legacyEncryptionWithoutRotation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The application encryption path ends with a hardened 31-bit rotation level (dashpay/dips#191, used by the App Connect DIP, dashpay/dips#181): m/9'/coin'/5'/7'/0'/identityId'/contractId'/purpose'/rotation' with rotation 0' first. Without it the path becomes permanent with the first mainnet key and a compromised or retired key could never be replaced. platform-mobile: application_encryption_private_key_from_seed takes a rotation (below 2^31, an error otherwise) and appends it as a hardened level. The 8-level path without rotation stays available as application_encryption_legacy_private_key_from_seed, only to recognise keys that wallets registered on testnet/devnet before the rotation level existed. Tests pin the DIP vectors for both paths (abandon x11 about, mainnet). Bindings: Ferment output for dash-sdk-bindings and dash-sdk-android now has the new signature and the legacy function (regenerated with cargo build of dash-sdk-bindings; the Android copy carries the same two functions), plus a SWIG rename for applicationEncryptionLegacyPrivateKeyFromSeed. Kotlin: ApplicationKeyDerivation.encryption gains a required rotation parameter, so callers must choose it; new keys use 0. The pre-rotation path is the explicitly named legacyEncryptionWithoutRotation. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: HashEngineering <hashengineeringsolutions@gmail.com>
* feat(connect): accept App Connect requests on dashpay://connect The App Connect DIP (dashpay/dips#181) never allows the dash-key: carrier on mainnet and moves requests to the dashpay:// scheme the wallet already uses for invitations. The Android wallet only knew dash-key:, so a dashpay://connect link from a scan or a tap did nothing. Accept the same request v2 body on dashpay://connect?v=2&n=..&r=..[&cb=..] and hand the decoded bytes to the existing AppConnectParser, so both carriers are processed identically. r is decoded by a strict canonical base64url decoder (java.util.Base64 tolerates padding and trailing bits), with the DIP's size limits checked before decoding. Scheme and host are compared case-insensitively, a repeated v/n/r/cb is invalid, and any dashpay verb the wallet does not implement says the link needs a newer wallet. dashpay://invite still goes to the invitation handler. MainActivity gets a separate dashpay + host connect intent filter; a host on the dash-key/dash-st filter would stop those host-less URIs matching. dash-key:/dash-st: behaviour, mainnet gating and key derivation are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(connect): report a dashpay:// link with userinfo or a port as invalid dashpay://user@connect?... read 'user' as the verb and told the user to update the wallet. Userinfo or a port makes a malformed link, so it is now refused as an invalid connection link before the verb is looked at. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* feat(dashpay): contested username fee follows the Platform protocol version (MO-1067) Platform protocol version 14 (v4.2) lowers the prefunded voting balance a contested username pays from 0.2 to 0.1 DASH, so creating an identity with a contested name drops from 0.25 to 0.15 DASH. ContestedUsernameFees replaces the DASH_PAY_FEE_CONTESTED(_NAME) constants and derives both fees from the protocol version learned via the new PlatformRepo.refreshProtocolVersion() (dash-sdk 4.2.0-SNAPSHOT). The version is refreshed when the username/invite screens open and right before CreateIdentityService sizes the asset lock. Until it is known, the higher pre-v14 fees apply: over-funding only leaves identity credits, while under-funding would make the registration fail. The "You need to have 0.25 DASH" requirement text was hardcoded in every locale; it now takes the fee as an argument (and shows the non-contested fee for non-contested names). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(connect): remove second QR registration path * chore: update Platform SDK to 5.0.0-SNAPSHOT * fix: refresh username affordability and amounts when protocol fees change * fix: refresh invitation labels without delaying non-contested invites * Use DIP-13 DashConnect session key derivation * feat: configure wallet devnet flavor for Sakura * fix(dashpay): dark mode text in the create username confirmation dialog OffsetDialog parented from Theme.Design.Light.BottomSheetDialog with no values-night override, so android:textColorPrimary inside every offset bottom sheet stayed the light theme near-black. The sheet background already follows the night palette via @color/background_secondary, which left any view that relies on the theme for its colour dark-on-dark. Most TextViews escaped this because their style sets android:textColor="@color/content_primary", a colour resource that does have a night variant. The confirmation message and the "I accept" label in dialog_confirm_username_request carried no style at all, fell through to the theme, and rendered black on the dark sheet. Base.OffsetDialog now holds the parent and is overridden in values-night with Theme.Design.BottomSheetDialog. The two Material themes carry identical items and differ only in their AppCompat palette, so nothing structural about the sheet changes. Keeping every <item> on OffsetDialog means the two configurations cannot drift apart - duplicating the style per configuration is what invites this bug back. No layout in the app reads theme-attribute colours and none of the 36 offset sheet layouts hardcodes a light background, so there is no white-on-white inverse of this bug to introduce. The theme fix also covers the unstyled CheckBox tint in the same dialog and the same class of bug in the other offset sheets. The two TextViews additionally get style="@style/Body2", matching the convention every sibling dialog already follows. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(ci): point devnet distribution at the real Firebase app The devnet app id matched no app in dash-wallet-firebase, so every QA upload for that flavor failed the build and then 404'd: App Distribution halted because it had a problem uploading the APK. (Invalid request, status_code: 404) The flavor builds as org.dash.wallet.devnet, whose app is 1:1039839682638:android:fdff563e24b69c5fea631f. Fixed in the workflow too, which carried the same bad id and would have failed the same way. Note that org.dash.dashpay.devnet is a second, older app with the same "DashPay [devnet]" display name, left over from before the applicationId changed. It is not the one we distribute to. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: prevent crashes if google-service.json is missing * revert: devnet flavor app id * docs: update sakura-devnet.md * chore: update dashj to 24.0.0-SNAPSHOT * revert: use 22.0.5 dashj * feat: add PV14 App Connect v2 wallet flow * fix: prevent DashConnect only on mainnet * fix: reuse wallet dialogs and select Sakura quorums * fix: reconcile Connect cancellation and restore approval state Revoke an attempted request before reporting it as declined, retain uncertain outcomes for retry, and freeze approved terms. Revoke confirmed auth keys without blocking on unrelated pending registrations and recognize irrecoverable key ID conflicts. Use a restorable approval dialog owned by the connections fragment, with no denial on composition disposal. Cover approval/revocation errors, key-state policy, and saved-state recreation in focused regressions. * feat: approve dash-st token purchases and handle operation links Keep one-QR login and add a separate explicit token-purchase approval sheet. Validate and display identity, token denomination, quantity and price ceiling, then rebuild through the SDK rather than signing untrusted bytes. Persist scoped submission receipts to prevent uncertain debit retries, retain approval through recreation, and route dash-key and dash-st through Android browsable links and all wallet scanners. Requires kotlin-platform purchase bridge commit 25c83e7. Validation: 74 wallet tests passed with 3 existing flavor skips; 17 JNI/identity tests and 4 Rust parser tests passed. Devnet APK built and native library matched the updated AAR. * fix: confirm Connect grants and restore reconnect actions * fix(connect): preserve revocation after key limit and storage changes * fix: address Connect grant recovery and signing review findings * fix: require wallet unlock for Connect approvals * fix: batch Connect revocation and retain recovery audits * fix: permanently revoke expired Connect keys * fix: separate request revocation from app disconnect confirmation * Fix Connect authentication and cold-start lifecycle blockers * Trigger PR synchronization * fix: prevent DashConnect on 32-bit devices * fix: reject unsupported Connect requests before identity initialization * fix: refresh DashConnect status at expiry and on resume * fix: preserve responses for independent app connections * fix: reject unverified group grants and redact QR errors * fix: reject disabled Connect networks before identity initialization * fix: use dashj 24.0.0-SNAPSHOT * fix: fail closed on grant storage and require explicit signing-key setup * fix: invalidate Connect signing authorization when the wallet locks * fix: reject legacy DashConnect login requests before granting access * Restore Yappr group grants with verified scope and renewed consent The owner-only bridge led to a blanket group rejection that broke Yappr's CRITICAL login grants. Consume the full proof-verified SDK snapshot, disclose current and future scope and administrators, persist approved scope, and recheck before registration and response publication. Refresh consent explicitly when configuration changes while retaining recovery and revocation controls. Pin SDK 9fdfe17, include regression tests, and document the rationale and Pixel 9 test evidence. * Require renewed group-scope consent when recovering Connect grants Decode the persisted approved scope and compare it with the current verified authorization scope across process restarts. Changed or missing prior metadata requires a visible unchecked acknowledgement; direct session approval enforces it before secrets and binds the plan to the saved record. Preserve frozen grants, revocation and error recovery, reset consent on refresh, and leave malformed metadata fail-closed. Cover prepared/pending/confirmed/active recovery and document test limits. * Invalidate Connect signing authority when its UI leaves the foreground Cancel sensitive jobs and invalidate captured authorization on pause, stop, destroy or screen-off. Require an interactive unlocked device, preserve uncertain-outcome recovery and require a fresh explicit action after return. Cover lifecycle wiring on API 28/34 and all sensitive operation types. Also correct the 2 DASH preset, keep QR names explicitly unverified, refresh state-change timestamps and export workflow support email only after external SDK builds. * Refresh Connect timestamps when revocation confirmation changes A disabled grant can move from request-only revocation to confirmed app-wide disconnect without changing its state string. Refresh the status timestamp on confirmation changes and preserve it for identical repeated settlements. * Pin the published DashJ build and complete Connect status follow-ups Use the exact timestamped DashJ 24 publication across direct and transitive dependencies and pin the corresponding SDK revision. Derive expiry status dates from the final auth-key expiry while retaining later state changes. Retain failed approval status and offer the established ac=failed callback only after explicit end-recovery and confirmed revocation; preserve scanner and uncertain-outcome safeguards. * Keep unavailable Connect scope distinct from an empty grant list Propagate wallet and identity scope failures. Keep records loading during pending identity initialization, report failures as unavailable, and restart observation on initialization or identity changes. Clear stale rows while reloading and retain Retry without initiating identity setup. Cover real repository/ViewModel cold start, failure, recovery, identity loss and valid empty results. * Serialize Connect availability and connection-row updates Keep state writes inside the same cancellable identity observation instead of forwarding buffered lists to a separate READY consumer. Check the collecting coroutine before updates and error handling so cancelled observers cannot overwrite newer refresh state. Reproduce emit-then-fail and cover initialization changes and late cancelled-observer failures. * Reload missing Connect identity metadata on explicit Retry Route Retry through the existing identity loader after failed or successful-but-empty initialization. Use the injected application scope so screen closure cannot strand the shared loader, coalesce pending retries, and preserve read-only automatic refresh. Cover actual storage rereads, cached missing identity recovery, startup/healthy guards, duplicate taps and ViewModel disposal. * Settle cancelled identity loads without swallowing cancellation Publish an ordinary retryable interrupted-load failure before rethrowing cancellation. Serialize metadata initialization owners so cancelled or queued attempts cannot overwrite a newer result, and check cancellation after storage reads. Cover owner cancellation, queue ordering, reload and real owner-to-ViewModel Retry recovery without key registration. * Preserve selected grant expiry and fresh consent editing * Reject Unicode formatting characters in Connect labels * Normalize group consent and restore trusted quick sends * Keep Connect recovery reachable without username metadata * Restore saved grant selection before recovery expiry checks * Reload partial identity metadata on explicit Connect Retry * Avoid reading partial identity IDs during metadata reload * Share Connections state ownership and verify pinned DashJ bytes * Gate connection revoke toggle on wallet and record availability * Distinguish confirmed no identity from unavailable Connect records * Distinguish purchase preflight cancellation from native handoff * Consume canonical purchase IDs and pin nonce-preserving SDK * Run native purchase replay regressions in SDK CI * Pin wallet Platform SDK to 5.0.1 * Pin final regenerated Platform SDK bindings * Pin SDK CI publication fix * Fix DashConnect confirmation recovery and simplify connection names Poll exact response confirmation up to three times with backoff and redacted outcome diagnostics. Refresh persisted approval terms after failures, block stale retries when refresh fails, and retain group-consent and revocation checks. Show the friendly app name alone on connection cards per MO-1072 testing feedback. Include regression coverage and build code 11090202. Verified with 260 DashConnect tests against the local 5.0.0 SDK and successful response confirmation on emulator-5556. * feat(connect): accept App Connect requests on dashpay://connect (#1656) * feat(connect): accept App Connect requests on dashpay://connect The App Connect DIP (dashpay/dips#181) never allows the dash-key: carrier on mainnet and moves requests to the dashpay:// scheme the wallet already uses for invitations. The Android wallet only knew dash-key:, so a dashpay://connect link from a scan or a tap did nothing. Accept the same request v2 body on dashpay://connect?v=2&n=..&r=..[&cb=..] and hand the decoded bytes to the existing AppConnectParser, so both carriers are processed identically. r is decoded by a strict canonical base64url decoder (java.util.Base64 tolerates padding and trailing bits), with the DIP's size limits checked before decoding. Scheme and host are compared case-insensitively, a repeated v/n/r/cb is invalid, and any dashpay verb the wallet does not implement says the link needs a newer wallet. dashpay://invite still goes to the invitation handler. MainActivity gets a separate dashpay + host connect intent filter; a host on the dash-key/dash-st filter would stop those host-less URIs matching. dash-key:/dash-st: behaviour, mainnet gating and key derivation are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(connect): report a dashpay:// link with userinfo or a port as invalid dashpay://user@connect?... read 'user' as the verb and told the user to update the wallet. Userinfo or a port makes a malformed link, so it is now refused as an invalid connection link before the verb is looked at. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * Specify initial encryption rotation for Platform SDK 5.0.1 Pass rotation 0 explicitly to the updated application encryption derivation API. Verified merged wallet compilation and all 288 DashConnect tests against SDK 5.0.1. Pixel emulator dashpay://connect testing confirmed response recovery on the second read after backoff. * Block key allocation while other scoped registrations are unresolved Check every other grant in the wallet/identity/network scope before deriving or allocating new keys and saving a prepared record. Do not narrow the settlement check to the same app or connection; exclude only the request being recovered. Keep revocation targeting unchanged. Add nine regression tests covering cross-app and cross-connection pending grants, scope and current-request exclusions, settled versus unresolved states, storage failure, and approval ordering before record persistence. All 297 testnet DashConnect tests pass against SDK 5.0.1. * Pin Platform SDK ValueMap header-order fix Use bb3eeae17cccfbd7d70ae1d077770e19e8b36ebc so CI declares ValueMap before generated header use regardless of definition order. The SDK fix passed reordered-header regression checks, a clean native C++ rebuild and seven JNI Value tests. Platform fork/revision remain unchanged. * Consume published Platform SDK 5.0.1 instead of building it in wallet CI Remove the native SDK bootstrap script, composite action and source revision pin, remove its calls from all three wallet workflows, and drop the isolated repository override. Resolve dash-sdk-java, dash-sdk-kotlin (dpp) and dash-sdk-android 5.0.1 from the published Maven artifacts. Verified testnet compilation and all 297 DashConnect tests with SDK resolution restricted to Maven Central, valid workflow YAML, and required Connect/purchase JNI exports in both published Android ABIs. --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: PastaPastaPasta <6443210+PastaPastaPasta@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Synced with the shipped implementation: Platform v5.0.3 🤖 Posted autonomously by Claude on behalf of pasta. |
…on (#41) * feat: support 4.2-dev * feat: upgrade to v4.2-dev.8 * feat: expose the network's Platform protocol version Add get_protocol_version_with_sdk and refresh_protocol_version_with_sdk exports (fermented bindings regenerated) and DapiClient.getProtocolVersion() / refreshProtocolVersion() wrappers. refresh issues a proven getEpochsInfo query and ratchets dash-sdk up to the version the network runs, so wallets can pick fees that depend on the activated protocol version (e.g. the contested username prefunded voting balance, 0.2 -> 0.1 DASH in v14). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * ci: skip retired Android SDK tools package * feat: upgrade to Platform 5.0 and support App Connect Expose bounded V1 identity keys, DIP-13 application key derivation, remaining budgets, and explicit devnet configuration across Rust, JNI, and Kotlin. Fix V1 native enum widths and index-only response confirmation using the submitted PV14 nonce-derived document ID. Add regression coverage and build against HashEngineering/platform ferment-5.0. * feat: expose safe wallet token-purchase requests Decode one exact unsigned direct-purchase batch, verify token identity and denomination, and rebuild approved values with a fresh nonce and an unbound CRITICAL wallet key. Reject unsupported operations and hidden fee or group changes. Add native decoder and JNI boundary regression tests. Treat purchase proof results as affected-state snapshots rather than execution confirmation. * fix: provision wallet signing keys and prove current application key index * fix: propagate data contract query and conversion failures * fix: require opt-in critical key migration and use DashJ 24 * fix: guard identity updates and wallet signing with caller authorization * Expose complete proof-verified contract group scope for Android Replace the owner-only bridge limitation that forced the wallet to block Yappr's group-bound CRITICAL grants. Verify all member pages at one block, expose admins and each member kind, and retain explicit errors instead of partial scope. Order Android native builds after SWIG generation to prevent stale JNI wrappers. Document the rationale and verified test coverage. * Pin DashJ 24 to the published timestamped build Use 24.0.0-20261024.220449-1 instead of the moving SNAPSHOT alias. Record the publicly resolved JAR checksum and snapshot retention caveat; native SDK code is unchanged. * Preserve purchase operation nonces and expose canonical replay IDs * Upgrade Platform SDK integration to 5.0.1 * Regenerate Android Ferment bindings for Platform 5.0.1 * Use unsigned local publication in SDK CI * chore: set dash-sdk-bindings to 5.0.1 * feat: add the DIP-13 rotation level to application encryption keys (#42) The application encryption path ends with a hardened 31-bit rotation level (dashpay/dips#191, used by the App Connect DIP, dashpay/dips#181): m/9'/coin'/5'/7'/0'/identityId'/contractId'/purpose'/rotation' with rotation 0' first. Without it the path becomes permanent with the first mainnet key and a compromised or retired key could never be replaced. platform-mobile: application_encryption_private_key_from_seed takes a rotation (below 2^31, an error otherwise) and appends it as a hardened level. The 8-level path without rotation stays available as application_encryption_legacy_private_key_from_seed, only to recognise keys that wallets registered on testnet/devnet before the rotation level existed. Tests pin the DIP vectors for both paths (abandon x11 about, mainnet). Bindings: Ferment output for dash-sdk-bindings and dash-sdk-android now has the new signature and the legacy function (regenerated with cargo build of dash-sdk-bindings; the Android copy carries the same two functions), plus a SWIG rename for applicationEncryptionLegacyPrivateKeyFromSeed. Kotlin: ApplicationKeyDerivation.encryption gains a required rotation parameter, so callers must choose it; new keys use 0. The pre-rotation path is the explicitly named legacyEncryptionWithoutRotation. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: HashEngineering <hashengineeringsolutions@gmail.com> * fix: sync bindings lockfile with 5.0.1 * Forward declare ValueMap independently of generated header order Declare platform_value_value_map_ValueMap before including Ferment's generated header. This fixes the CI compile failure when Value is emitted before ValueMap without changing ABI or Platform versions. Add a native header-order regression. Verified the original and reordered generated headers, a clean C++ rebuild, and seven JNI ValueTest tests. * chore: prepare 5.0.1 Maven Central release --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: PastaPastaPasta <6443210+PastaPastaPasta@users.noreply.github.com>
…#1670, #1674) (#1687) * chore: remove CrowdNode integration (#1664) * chore: remove CrowdNode integration (MO-1114) Remove the CrowdNode integration module and every reference to it: staking entry points (Explore menu item, Staking shortcut, StakingActivity), CrowdNode validations and messaging, the leftover-balance check on sends (LeftoverBalanceException, checkSendingConditions, MinimumBalanceDialog), and CrowdNode transaction grouping in wallet history. Upgrade handling for users who had CrowdNode: - Room onOpen callback deletes cached crowdnode rows from tx_group_cache and tx_display_cache so history is rebuilt (no schema version bump) - the saved Staking shortcut id (10) is ignored and refilled - obsolete crowdnode Configuration keys, the crowdnode DataStore file and the pending CrowdNode.WORK job are removed on startup ServiceName.CrowdNode is kept so existing transaction metadata still reads. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: rebuild history without waiting for sync; keep scan_qr translations Clear both transaction caches when they hold CrowdNode rows instead of only deleting those rows, so history is rebuilt from the wallet on load rather than after sync completes. Move the scan_qr translations from the removed CrowdNode module to common, and drop the now-unused button_show_qr. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test: use JUnit assertions in DatabaseMigrationTest Kotlin assert is a no-op unless assertions are enabled at runtime, so the migration and CrowdNode cache cleanup tests could pass without checking anything. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix: index-friendly CrowdNode cache probe; restore group details heading Probe tx_group_cache by groupId, which the primary key indexes, so the startup check no longer scans the whole cache for wallets without CrowdNode. The CoinJoin group details sheet showed the explainer text as both heading and body. Restore the "Why do I see all these transactions?" heading it had before, moved from the removed CrowdNode module with its translations. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: drop unused analytics from ExploreEntryViewModel logEvent lost its last callers with the CrowdNode Staking entry. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: drop orphaned CrowdNode test fixtures and stale APY KDoc TransactionWrapperHelperTest lost its only test with the CrowdNode grouping; BlockchainStateProvider no longer provides Masternode APY. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * feat(connect): update DashConnect to v2 one-QR flow (#1604) * feat(dashpay): contested username fee follows the Platform protocol version (MO-1067) Platform protocol version 14 (v4.2) lowers the prefunded voting balance a contested username pays from 0.2 to 0.1 DASH, so creating an identity with a contested name drops from 0.25 to 0.15 DASH. ContestedUsernameFees replaces the DASH_PAY_FEE_CONTESTED(_NAME) constants and derives both fees from the protocol version learned via the new PlatformRepo.refreshProtocolVersion() (dash-sdk 4.2.0-SNAPSHOT). The version is refreshed when the username/invite screens open and right before CreateIdentityService sizes the asset lock. Until it is known, the higher pre-v14 fees apply: over-funding only leaves identity credits, while under-funding would make the registration fail. The "You need to have 0.25 DASH" requirement text was hardcoded in every locale; it now takes the fee as an argument (and shows the non-contested fee for non-contested names). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(connect): remove second QR registration path * chore: update Platform SDK to 5.0.0-SNAPSHOT * fix: refresh username affordability and amounts when protocol fees change * fix: refresh invitation labels without delaying non-contested invites * Use DIP-13 DashConnect session key derivation * feat: configure wallet devnet flavor for Sakura * fix(dashpay): dark mode text in the create username confirmation dialog OffsetDialog parented from Theme.Design.Light.BottomSheetDialog with no values-night override, so android:textColorPrimary inside every offset bottom sheet stayed the light theme near-black. The sheet background already follows the night palette via @color/background_secondary, which left any view that relies on the theme for its colour dark-on-dark. Most TextViews escaped this because their style sets android:textColor="@color/content_primary", a colour resource that does have a night variant. The confirmation message and the "I accept" label in dialog_confirm_username_request carried no style at all, fell through to the theme, and rendered black on the dark sheet. Base.OffsetDialog now holds the parent and is overridden in values-night with Theme.Design.BottomSheetDialog. The two Material themes carry identical items and differ only in their AppCompat palette, so nothing structural about the sheet changes. Keeping every <item> on OffsetDialog means the two configurations cannot drift apart - duplicating the style per configuration is what invites this bug back. No layout in the app reads theme-attribute colours and none of the 36 offset sheet layouts hardcodes a light background, so there is no white-on-white inverse of this bug to introduce. The theme fix also covers the unstyled CheckBox tint in the same dialog and the same class of bug in the other offset sheets. The two TextViews additionally get style="@style/Body2", matching the convention every sibling dialog already follows. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(ci): point devnet distribution at the real Firebase app The devnet app id matched no app in dash-wallet-firebase, so every QA upload for that flavor failed the build and then 404'd: App Distribution halted because it had a problem uploading the APK. (Invalid request, status_code: 404) The flavor builds as org.dash.wallet.devnet, whose app is 1:1039839682638:android:fdff563e24b69c5fea631f. Fixed in the workflow too, which carried the same bad id and would have failed the same way. Note that org.dash.dashpay.devnet is a second, older app with the same "DashPay [devnet]" display name, left over from before the applicationId changed. It is not the one we distribute to. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: prevent crashes if google-service.json is missing * revert: devnet flavor app id * docs: update sakura-devnet.md * chore: update dashj to 24.0.0-SNAPSHOT * revert: use 22.0.5 dashj * feat: add PV14 App Connect v2 wallet flow * fix: prevent DashConnect only on mainnet * fix: reuse wallet dialogs and select Sakura quorums * fix: reconcile Connect cancellation and restore approval state Revoke an attempted request before reporting it as declined, retain uncertain outcomes for retry, and freeze approved terms. Revoke confirmed auth keys without blocking on unrelated pending registrations and recognize irrecoverable key ID conflicts. Use a restorable approval dialog owned by the connections fragment, with no denial on composition disposal. Cover approval/revocation errors, key-state policy, and saved-state recreation in focused regressions. * feat: approve dash-st token purchases and handle operation links Keep one-QR login and add a separate explicit token-purchase approval sheet. Validate and display identity, token denomination, quantity and price ceiling, then rebuild through the SDK rather than signing untrusted bytes. Persist scoped submission receipts to prevent uncertain debit retries, retain approval through recreation, and route dash-key and dash-st through Android browsable links and all wallet scanners. Requires kotlin-platform purchase bridge commit 25c83e7. Validation: 74 wallet tests passed with 3 existing flavor skips; 17 JNI/identity tests and 4 Rust parser tests passed. Devnet APK built and native library matched the updated AAR. * fix: confirm Connect grants and restore reconnect actions * fix(connect): preserve revocation after key limit and storage changes * fix: address Connect grant recovery and signing review findings * fix: require wallet unlock for Connect approvals * fix: batch Connect revocation and retain recovery audits * fix: permanently revoke expired Connect keys * fix: separate request revocation from app disconnect confirmation * Fix Connect authentication and cold-start lifecycle blockers * Trigger PR synchronization * fix: prevent DashConnect on 32-bit devices * fix: reject unsupported Connect requests before identity initialization * fix: refresh DashConnect status at expiry and on resume * fix: preserve responses for independent app connections * fix: reject unverified group grants and redact QR errors * fix: reject disabled Connect networks before identity initialization * fix: use dashj 24.0.0-SNAPSHOT * fix: fail closed on grant storage and require explicit signing-key setup * fix: invalidate Connect signing authorization when the wallet locks * fix: reject legacy DashConnect login requests before granting access * Restore Yappr group grants with verified scope and renewed consent The owner-only bridge led to a blanket group rejection that broke Yappr's CRITICAL login grants. Consume the full proof-verified SDK snapshot, disclose current and future scope and administrators, persist approved scope, and recheck before registration and response publication. Refresh consent explicitly when configuration changes while retaining recovery and revocation controls. Pin SDK 9fdfe17, include regression tests, and document the rationale and Pixel 9 test evidence. * Require renewed group-scope consent when recovering Connect grants Decode the persisted approved scope and compare it with the current verified authorization scope across process restarts. Changed or missing prior metadata requires a visible unchecked acknowledgement; direct session approval enforces it before secrets and binds the plan to the saved record. Preserve frozen grants, revocation and error recovery, reset consent on refresh, and leave malformed metadata fail-closed. Cover prepared/pending/confirmed/active recovery and document test limits. * Invalidate Connect signing authority when its UI leaves the foreground Cancel sensitive jobs and invalidate captured authorization on pause, stop, destroy or screen-off. Require an interactive unlocked device, preserve uncertain-outcome recovery and require a fresh explicit action after return. Cover lifecycle wiring on API 28/34 and all sensitive operation types. Also correct the 2 DASH preset, keep QR names explicitly unverified, refresh state-change timestamps and export workflow support email only after external SDK builds. * Refresh Connect timestamps when revocation confirmation changes A disabled grant can move from request-only revocation to confirmed app-wide disconnect without changing its state string. Refresh the status timestamp on confirmation changes and preserve it for identical repeated settlements. * Pin the published DashJ build and complete Connect status follow-ups Use the exact timestamped DashJ 24 publication across direct and transitive dependencies and pin the corresponding SDK revision. Derive expiry status dates from the final auth-key expiry while retaining later state changes. Retain failed approval status and offer the established ac=failed callback only after explicit end-recovery and confirmed revocation; preserve scanner and uncertain-outcome safeguards. * Keep unavailable Connect scope distinct from an empty grant list Propagate wallet and identity scope failures. Keep records loading during pending identity initialization, report failures as unavailable, and restart observation on initialization or identity changes. Clear stale rows while reloading and retain Retry without initiating identity setup. Cover real repository/ViewModel cold start, failure, recovery, identity loss and valid empty results. * Serialize Connect availability and connection-row updates Keep state writes inside the same cancellable identity observation instead of forwarding buffered lists to a separate READY consumer. Check the collecting coroutine before updates and error handling so cancelled observers cannot overwrite newer refresh state. Reproduce emit-then-fail and cover initialization changes and late cancelled-observer failures. * Reload missing Connect identity metadata on explicit Retry Route Retry through the existing identity loader after failed or successful-but-empty initialization. Use the injected application scope so screen closure cannot strand the shared loader, coalesce pending retries, and preserve read-only automatic refresh. Cover actual storage rereads, cached missing identity recovery, startup/healthy guards, duplicate taps and ViewModel disposal. * Settle cancelled identity loads without swallowing cancellation Publish an ordinary retryable interrupted-load failure before rethrowing cancellation. Serialize metadata initialization owners so cancelled or queued attempts cannot overwrite a newer result, and check cancellation after storage reads. Cover owner cancellation, queue ordering, reload and real owner-to-ViewModel Retry recovery without key registration. * Preserve selected grant expiry and fresh consent editing * Reject Unicode formatting characters in Connect labels * Normalize group consent and restore trusted quick sends * Keep Connect recovery reachable without username metadata * Restore saved grant selection before recovery expiry checks * Reload partial identity metadata on explicit Connect Retry * Avoid reading partial identity IDs during metadata reload * Share Connections state ownership and verify pinned DashJ bytes * Gate connection revoke toggle on wallet and record availability * Distinguish confirmed no identity from unavailable Connect records * Distinguish purchase preflight cancellation from native handoff * Consume canonical purchase IDs and pin nonce-preserving SDK * Run native purchase replay regressions in SDK CI * Pin wallet Platform SDK to 5.0.1 * Pin final regenerated Platform SDK bindings * Pin SDK CI publication fix * Fix DashConnect confirmation recovery and simplify connection names Poll exact response confirmation up to three times with backoff and redacted outcome diagnostics. Refresh persisted approval terms after failures, block stale retries when refresh fails, and retain group-consent and revocation checks. Show the friendly app name alone on connection cards per MO-1072 testing feedback. Include regression coverage and build code 11090202. Verified with 260 DashConnect tests against the local 5.0.0 SDK and successful response confirmation on emulator-5556. * feat(connect): accept App Connect requests on dashpay://connect (#1656) * feat(connect): accept App Connect requests on dashpay://connect The App Connect DIP (dashpay/dips#181) never allows the dash-key: carrier on mainnet and moves requests to the dashpay:// scheme the wallet already uses for invitations. The Android wallet only knew dash-key:, so a dashpay://connect link from a scan or a tap did nothing. Accept the same request v2 body on dashpay://connect?v=2&n=..&r=..[&cb=..] and hand the decoded bytes to the existing AppConnectParser, so both carriers are processed identically. r is decoded by a strict canonical base64url decoder (java.util.Base64 tolerates padding and trailing bits), with the DIP's size limits checked before decoding. Scheme and host are compared case-insensitively, a repeated v/n/r/cb is invalid, and any dashpay verb the wallet does not implement says the link needs a newer wallet. dashpay://invite still goes to the invitation handler. MainActivity gets a separate dashpay + host connect intent filter; a host on the dash-key/dash-st filter would stop those host-less URIs matching. dash-key:/dash-st: behaviour, mainnet gating and key derivation are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(connect): report a dashpay:// link with userinfo or a port as invalid dashpay://user@connect?... read 'user' as the verb and told the user to update the wallet. Userinfo or a port makes a malformed link, so it is now refused as an invalid connection link before the verb is looked at. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * Specify initial encryption rotation for Platform SDK 5.0.1 Pass rotation 0 explicitly to the updated application encryption derivation API. Verified merged wallet compilation and all 288 DashConnect tests against SDK 5.0.1. Pixel emulator dashpay://connect testing confirmed response recovery on the second read after backoff. * Block key allocation while other scoped registrations are unresolved Check every other grant in the wallet/identity/network scope before deriving or allocating new keys and saving a prepared record. Do not narrow the settlement check to the same app or connection; exclude only the request being recovered. Keep revocation targeting unchanged. Add nine regression tests covering cross-app and cross-connection pending grants, scope and current-request exclusions, settled versus unresolved states, storage failure, and approval ordering before record persistence. All 297 testnet DashConnect tests pass against SDK 5.0.1. * Pin Platform SDK ValueMap header-order fix Use bb3eeae17cccfbd7d70ae1d077770e19e8b36ebc so CI declares ValueMap before generated header use regardless of definition order. The SDK fix passed reordered-header regression checks, a clean native C++ rebuild and seven JNI Value tests. Platform fork/revision remain unchanged. * Consume published Platform SDK 5.0.1 instead of building it in wallet CI Remove the native SDK bootstrap script, composite action and source revision pin, remove its calls from all three wallet workflows, and drop the isolated repository override. Resolve dash-sdk-java, dash-sdk-kotlin (dpp) and dash-sdk-android 5.0.1 from the published Maven artifacts. Verified testnet compilation and all 297 DashConnect tests with SDK resolution restricted to Maven Central, valid workflow YAML, and required Connect/purchase JNI exports in both published Android ABIs. --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: PastaPastaPasta <6443210+PastaPastaPasta@users.noreply.github.com> * feat(connect): redesign the approve sheet and Connections page (#1662) * feat(connect): redesign the approve connection sheet Follow the new DashPay Android designs for the App Connect approval: - Reorder the sheet: app name and website, a first-connection warning, request details, identity funding limit and expiration, then collapsible "able to do" / "will NOT have access to" cards. - Describe each grant in plain language instead of raw grant text, and approve every requested grant (the per-grant checkboxes are gone). - Replace the dropdowns with funding limit (0.1/0.5/2/5 DASH) and expiration (30/90/365 days) picker sheets. The sheet opens on the smallest presets at or above the request, so it grants exactly what was asked until the user lowers it. - Add info sheets for identity funding limits and spendable Dash. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(connect): keep approve buttons on screen and fix sheet text Found while checking the redesigned sheet on a staging build: - Pin Approve/Deny below a body that scrolls within the visible part of the expanded sheet, instead of a fixed 720dp cap that left a blank gap; the top spacer stays outside the scroll so cards no longer slide under the drag indicator. - Hide the website row when no website was resolved rather than repeating the unverified app label beside the globe. - Escape paragraph breaks in the new strings (raw newlines were folded into spaces) and avoid a possessive on the quoted app label. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(connect): scroll the approve sheet instead of dismissing it on drag down The approve sheet's Compose content scrolled inside a View-based bottom sheet that could not see that scroll, so dragging down to re-read the terms dismissed the sheet, which denies the request. Connect the scroll through nested-scroll interop so the sheet only collapses once the content is back at the top. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(connect): tabbed Connections page with connection details sheet Follow the DashPay Android Connections designs (Figma 40042:59884): - Active / Disconnected / Expired tabs once a connection exists, and a + button in the top bar that opens the scanner like Scan QR. Pending connections stay under Active; local-only records under Disconnected. - Add an EXPIRED status for connections whose keys all expired without being revoked; they stay enabled on Platform until disconnected. - Rows show the app name and website; tapping one opens a details sheet (status, IDs, funding limit and expiry, what the app could and could not do) built from the latest recorded grant, with Disconnect for active and expired connections and Remove from list for local-only records. The old per-key text moves into a collapsed Key details card. - Share the sheet cards between the approve and details sheets. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(connect): Reconnect button and disconnect date on the disconnected sheet Reconnect opens the scanner, since a new login needs the app's QR. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(connect): warn about DashPay profile keys no connection holds Connections now lists enabled keys bound to the DashPay profile that no local grant record holds, e.g. after a wallet restore or from another wallet with the same identity. Dismiss hides those keys on this device until another one appears; Revoke disables them with a master-key identity update, re-checking first that each key is still unlinked. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(connect): drop the Key details card from the connection sheet It is not in the design, and key info is out of scope for the MVP. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(connect): address pre-review findings on the Connections redesign - Describe every login that may still authorize access, not only the newest one. - Show Expired only when no login is still registering or awaiting a confirmed revoke. - Keep the details sheet open while the list reloads on resume. - Cancel an earlier details load when the sheet is reopened or closed. - Warn that removing an older connection does not revoke its Platform access. - Catch a failed save of dismissed profile keys and show the warning again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(connect): show each key's current Platform limits on the details sheet A key's budget and expiry can change on Platform without changing the key, so the sheet reads the identity and describes each recorded key as Platform holds it now. A key no longer on the identity keeps its recorded terms. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(connect): expose collapsible card headers as buttons with expanded state Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(connect): cover shared DashPay profile key listing, dismissal and revocation Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(connect): confirm on Platform that a connection expired before listing it Another device can extend a key's expiry on Platform, so the expiry saved at approval is only a hint. When a connection's saved expiry has passed, the list reads the identity once and keeps the connection Active if a recorded key is still enabled with a later expiry, scheduling the next refresh from it. If Platform cannot be read, the connection shows as pending, never Expired. Live connections are still listed without a Platform read. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(connect): keep the details selection while connection records reload A reload empties the list until it finishes, so a missing row proves nothing yet; the sheet shows a spinner and closes only if a finished read no longer has the connection. This also survives the dialog being recreated mid-reload. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(connect): give info buttons a button role and a topic-specific label Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(connect): always show Connections in Tools when Connect is supported Connections now offers to create a username, so a wallet without one no longer needs the entry hidden. Only the Platform and Connect flags decide. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(connect): offer to create a username on Connections Without a username, Connections shows the design's intro (Figma 40042:60147) with a Create username button that starts the same flow as Join DashPay. A request opened from the main scanner, the camera or a link lands on this screen instead of an error toast. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(connect): offer to buy credits when the identity cannot pay to approve Approve reads the identity balance first. When it is empty, nothing is submitted, the request stays open, and the sheet offers Buy credits, the same top-up the wallet uses before contact requests and profile edits. An unreadable balance or a recovering approval goes ahead as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(connect): one funding limit per connection, split between its keys Per MO-1117, the approve sheet sets one funding limit and one expiry for the whole connection: - The limit starts at the total the app requests (Yappr: 0.5 + 0.01 DASH), capped at 5 DASH, and the user may go above or below it. - It is split between the keys: a DashPay profile key gets 0.002 DASH (half the limit under 0.004 DASH) and the app's own key gets the rest. - One expiry for all keys: the request, capped at a year, 90 days if none. - Set custom limit opens a keypad sheet (Figma 40103:38833) that refuses 0 and anything above 5 DASH; Set date opens the system date picker. - Consent checks only the wallet's maximums; a recovering approval keeps the limits it was prepared with. - The approve and details sheets show the keys' budgets together. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(connect): show when a connection was made and the funding left on its keys Per MO-1117 (no designs; the wallet's existing patterns): - Details show a Connected date. A record keeps connectedAt from the first time it became active; older records use when they became active. - Details show the funding left on each enabled key (the app's own key and the DashPay profile key), read from Platform; an unreadable one shows as unavailable. - Disconnect asks for confirmation first, since it revokes the app's keys on Platform for good. Removing an older connection from the list does not. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(connect): reword what the app's own key may do with tokens "Act as you in Yappr until <date>, including buying and sending its tokens. It can spend up to <limit> DASH of your identity balance on fees and token purchases, but can send any amount of your Yappr tokens." Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(connect): close the pre-review findings on limits, expiry and consent - Judge a connection expired by Platform time, and count any live key of the app the wallet has no record of (e.g. registered on another device). - A recovering approval keeps an unlimited budget an earlier version saved. - Connection terms leave out keys a renewal disabled; a key without an expiry leaves the connection without one. - The details sheet is bound to its connection's wallet scope, and reads the username before the scope-checked terms. - The Buy credits prompt shows only while the wallet is unlocked. - The approve sheet shows what each contract-group key covers, including members added later. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * revert(connect): drop the contract-group scope lines from the approve sheet The disclosure needs a design before it goes on the sheet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(connect): keep a disconnected connection's terms when its encryption key survives A disconnect disables the authentication keys and keeps encryption keys, so leaving out disabled keys left only the encryption key and the sheet showed "No limit". Disabled keys are left out only while an authentication key is still enabled. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * feat(connect): run DashConnect on mainnet, gated on Platform protocol 14 (#1670) * feat(connect): run DashConnect on mainnet, gated on Platform protocol 14 DashConnect is built into the prod flavour (SUPPORTS_CONNECT = true) and no longer refuses production builds or mainnet. Instead every entry point and operation requires the network to run Platform protocol version 14, which brings key funding limits and expiry, contract-group bounds, remaining-budget queries and the AppConnect response contract. - ConnectAvailability: build support plus the protocol version the wallet already records (PlatformRepo.refreshProtocolVersion, which only goes up). - Tools shows Connections only once the network is known to be new enough. - The Connections screen and every repository operation check it, and report DashConnect as unavailable below version 14. - A QR's network selector is matched to the wallet's network, so a mainnet wallet accepts n=m (it was compared with devnet). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(connect): mainnet key derivation and a retryable protocol check - Application keys are derived with the wallet's network, so a mainnet wallet uses the mainnet coin type (both were derived as testnet). - Tools reads the protocol version again on every resume until Connections is available, so an offline start or a network upgrade is noticed. - A protocol version that could not be read is not reported as too old: the Connections screen stays retryable and a request says to try again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(connect): count only a Platform version the network reported A failed refreshProtocolVersion() does not return 0: the SDK keeps its per-network starting version (13 on mainnet and testnet) and returns that. Offline after a fresh launch, the gate read 13 as the network's answer and called a protocol 14 network too old, removing the Connections screen's Retry. The version now counts only after a proven query succeeds. PlatformRepo.readProtocolVersion() reads Platform time for the identity, whose verified response sets the SDK's version, and returns null when the network does not answer. ConnectAvailability keeps the highest version read this way; Tools and the repository ask through it. The session's checks run after its proven identity read instead of after a refresh. A wallet without a username still gets the username prompt: the Connections screen reads the version once the identity is known. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * test(connect): pin the network flag of approval key derivation Approval derives authentication and encryption keys with isMainnet true on mainnet and false elsewhere; going back to a hard-coded value now fails this test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(connect): drop the stale prod note from the QR routing comments Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(connect): read the Platform version without a wallet identity The proven query behind the version read asked for the wallet's own identity, so a wallet without a username never learned the version and Tools kept the Connections entry hidden, the way to create a username. The query now asks for an identity no wallet owns: its proven absence carries the same verified metadata, so any wallet can read the version. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(tools): keep Connections shown while the username loads The username coroutine copied the state before its suspending read and wrote that copy back afterwards, so an availability update in between was undone. With the version already known, a later Tools visit hid the Connections entry for good. Every writer now goes through update {}. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(connect): answer with the version kept when probes overlap The cache kept the highest version read, but each probe answered with its own. An older probe that read 13 and finished after a newer one read 14 reported the network too old, which hid the Connections screen while Tools showed the entry. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(connect): check the Platform version before offering a username The screen skipped the version check for a wallet without a username, because the version used to be read with the wallet's identity. Now that any wallet can read it, the screen checks it first, so a network too old for DashConnect is not offered a username to use it with. A restored process does not replay the link, so the screen cannot rely on the link check. Requests check the version before waiting for the identity too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(connect): keep a confirmed version when an overlapping probe fails A probe that failed answered "unknown" before looking at the version kept, so after another probe had confirmed protocol 14 it still made the caller reject an incoming link or mark the records unavailable. A failed probe now answers with the kept version too, and is unknown only when no probe has confirmed one. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * feat: v11.9.2 with updated translations and core 24 support (#1674) * feat(connect): translate DashConnect strings into DE, PL, IT, ES, PT-BR, RU and FR Add strings_app_connect.xml for the seven locales and the three DashConnect strings in strings-dashpay.xml and strings.xml. Translated locally, not pulled from Transifex; strings_app_connect.xml is not in .tx/config yet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: v11.9.2 release notes Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: v11.9.2 (11090205) and pin dashj 24.0.0 checksums Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: update dashj to 24.0.0 * chore: drop Gradle dependency verification for dashj-core The checksums pinned a locally built 24.0.0 whose JAR and module metadata differ in packaging from the Maven Central release, which failed CI. The release is immutable, so the verification file and the non-changing override it needed are removed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: verify dashj-core 24.0.0 against the Maven Central release Restores gradle/verification-metadata.xml, scoped to dashj-core, with the SHA-256 hashes of the Maven Central 24.0.0 JAR, POM, module metadata, sources and javadoc. The version force pins coordinates, not bytes, and Maven Local is searched first, so the hashes are what keeps a different local publication out of the build. docs/app-connect-v2.md: describe the release pin and its verification, the dashpay://connect carrier, and drop the stale SDK-build CI note. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * fix(merge): keep the DashConnect audit through the registry-driven wipe Reset Wallet clears every live config through BaseConfig.clearAllLiveInstances() before deleting datastore files. It called clearAll() directly, bypassing DashConnectConfig.onWalletWiped(), which keeps grants and unknown purchase receipts for a same-seed restore, so the audit was lost before either preservation step ran. The registry now applies each config's own wipe policy; onWalletWiped() defaults to clearAll(), so every other config is unchanged. DashConnectConfigTest now runs the production order (registry clear, then file deletion). DatabaseMigrationTest includes the 21-to-22 migration. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: drop WalletDataAdapter's unused seam-lock registry, fix registry KDoc Both left over from removing lockOutputsPayingTo with CrowdNode (PastaClaw on #1687). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: PastaPastaPasta <6443210+PastaPastaPasta@users.noreply.github.com> Co-authored-by: QA Integration <eric.britten@dash.org>
This draft described
dash-key:/dash-st:. In that protocol the wallet hands the app one login secret, the app derives and registers its own keys, and a first login takes two QR scans. That design was replaced in September, and the wallets now implement its successor without a spec in this repo. This PR rewrites the draft as that successor: App Connect.The wallet derives and registers every key itself. Each key is bounded to the app's contracts and carries a credit budget and an expiry, all enforced by Platform. The wallet returns the private keys encrypted in a
loginKeyResponsedocument of the App Connect system contract (H8F9mP1BM55TE1ShsxPZHzhyinaMdY9bMmP85mkDhcJJ, protocol version 14).What the DIP covers:
dashpay://connectanddashpay://sign(extending DIP-15'sdashpay:scheme).dash-key:v2 is a transitional carrier, never on mainnet.6', with the leafhash256(appEphPub ‖ u8(bindingIndex));7': one current key per contract and purpose, on any contract that opts in at contract level, with arotation'level so a user can replace a key (where the contract's requirement allows it);sign: accepts every transition type signed by an identity key, describes it, and signs it with an unbound key.unsupported), and test vectors (dip-pasta-app-connect/vectors.json, plus the derivation vectors pinned in rust-dashcore).The derivation paths themselves, including the rotation level, are in #191.
The masternode reward shares draft was committed to this branch by mistake and is removed here. It now lives in DIP-26. The old file
dip-pasta-yappr.mdis replaced bydip-pasta-app-connect.md.Implementations:
platform-authsrc/app-connect/(vendored in Yappr);🤖 Generated with Claude Code