Repository navigation
feat(dashpay): sign in to an app with one DashConnect QR from protocol 14 - #1202
romchornyi wants to merge 6 commits into
Conversation
…l 14 From protocol version 14 the wallet answers a `dash-key:` request by itself: it derives the request's session key (DIP-13 application session authentication, leaf `hash256` of the app's ephemeral key), registers it on the identity as an AUTHENTICATION / HIGH / ECDSA_HASH160 key bound to the requested contract with a budget and an expiry, and only then publishes the encrypted key to the App Connect system contract. The app no longer sends a `dash-st:` key registration. Earlier responses for the same app are deleted by their values first; the published values are recorded for that. Below protocol 14 the two-QR flow runs unchanged. Testnet's SDK is pinned to protocol 13, so testnet stays on it until that pin moves. Also ports the `dash-st:` parser to the SDK's `ParsedStateTransition` summary (dashpay/platform#4844): a key registration maps as before, and a batch is accepted only as a lone, complete token direct purchase. Needs dashpay/platform#4844 and the delete-by-values entry point. The budget and lifetime are placeholders. Mainnet is still gated off, the approval sheet and disconnect are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ed off Turning a connection off only changed its local status, so the app kept a key that could still sign. For a connection made by the one-QR login the wallet now disables that session key on the identity and deletes the published response by its values before the row is downgraded; a failure leaves the row active and is shown to the user. A two-QR connection stays local-only: that login derives the same key every time and Platform never re-adds a disabled key, so disabling it would lock the app out for good. `disconnect(id:)` now throws, and the view model keeps a second tap and inbound requests out while the identity update is in flight. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review pass on the one-QR login and its disconnect: - A later login to the same app deleted the earlier record together with its key id, so that key stayed live and could no longer be found. The record now outlives its response (marked as deleted) until a disconnect disables the key. - The grant and the connection row are recorded once the key is on the identity, before the publish, so a publish with an unknown outcome still leaves a key the user can see and disable. Approving the same QR again reuses the recorded values and does nothing when already published. - A connection remembers which login made it. Turning a one-QR connection off removes the row instead of parking it in the two-QR "approved" state, and refuses, rather than reporting success, when its records are missing. - A scanned `dash-st:` key registration keeps the budget and expiry it carried instead of being rebuilt as an unlimited key. - Disconnect has its own in-flight flag, does not overlap an approval or a resolving request, and re-resolves the wallet context after the PIN prompt. Grant records are read, changed and written in one step. - The protocol-version query runs off the cooperative pool. - The two logins share one `AppConnect.seal` instead of two copies of the encrypt-and-wipe sequence, and the session private key is the sole owner of its buffer so the wipe reaches it. Still open, marked in code: key state is read from the SDK's local view of the identity (TODO(app-connect-stale-keys)); the approval sheet still shows the two-QR text. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…dentity view Second review pass on the one-QR login: - A grant was looked up in the store by whole-value equality, and its date does not survive the store's millisecond round trip bit for bit, so about half of all publishes never recorded their document id. Records are now matched on what identifies a grant: identity, app, key id, request hash. - A granted key id missing from the wallet's local view of the identity was treated as gone, after which the records and the row were dropped as if the key had been disabled. Platform never removes a key, so a missing id now stops the disconnect with an error. - The one-QR approval re-resolves the wallet context after the PIN prompt and refuses to continue for a different identity. - The grant store decodes row by row and writes back rows it cannot read instead of overwriting them. - Two doc comments corrected to say what the code does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: true
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
🕓 Review not started yet because this PR is a draft.
Commit 09a28c5. Normal review starts when eligible; priority review starts as soon as a slot is available. |
|
Thanks for this, Roman. Yappr is lined up as an early App Connect consumer, and we've written up the request profile we need. We'd like to agree it with you before the one-QR flow hardens around a single key: Profile: https://github.com/PastaPastaPasta/yappr/blob/staging/docs/APP_CONNECT_PROFILE.md Why one What the profile asks for:
Two questions for you:
Happy to adjust the format. The vectors are generated and checked by our tests, so wallet implementations can verify against them. 🤖 Posted autonomously by Claude on behalf of pasta. |
|
Thanks, I read the profile and the vectors. Short answers first:
Before I start on v2, four things I need settled:
What I can do meanwhile without those answers: the v2 request decoder and the new HKDF info / AAD, checked against your vectors. For testing: is there a build or a deployment that shows a v2 QR on devnet sakura? As of today 🤖 Generated with Claude Code |
The wallet decided whether to add or disable a session key from the SDK's local copy of the identity, which is only replaced when an identity update returns success. It now reloads the identity before both decisions. A publish or a delete of the response can land without confirming. A refused publish is now looked up and adopted with the values that are on Platform, a failed delete is checked against Platform, and a disconnect no longer drops the record of a response whose publish never confirmed. Also: - a response deleted by a later login is published again when its own request comes back, instead of being reported as already published; - a connection stays while the app holds a key on another identity of the wallet, and a disconnect from an identity that gave it none says so; - a login does not start when the grant store cannot save; - records left by a disconnect are retried by the next login with the same identity. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…record of A reinstalled wallet has no grant records, so disconnect left the app's earlier session keys live. Disconnect now also takes the live keys on the active identity that the wallet's key store shows bound to the app's contract and that are shaped as this login registers them (AUTHENTICATION, HIGH, ECDSA_HASH160, with a budget and an expiry). - Stops with an error when a live session key cannot be placed, or when a stored key of the app's is missing from the fetched key list. - Disables at most 10 keys per identity update, the consensus limit. - A one-QR row with no record no longer fails; the identity is checked. Not covered: the response behind a key with no record stays on Platform, and a key on a non-active identity with no record is not seen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Issue being fixed or feature implemented
DashConnect v2 (one-QR login) for iOS. Today a first login takes two scans:
dash-key:(the wallet publishes an encrypted login key) and thendash-st:(the app hands back an identity update that registers the keys). From protocol version 14 the wallet does it in one approval and publishes its answer to the App Connect system contract (H8F9mP1BM55TE1ShsxPZHzhyinaMdY9bMmP85mkDhcJJ, dashpay/platform#4869), which has the same id on every network — this is what makes the feature possible on mainnet.Supersedes the draft #1133 (a per-network id for the old response contract is no longer needed).
Draft — not ready for review. It does not build against
develop's SDK (see "Depends on").What was done?
approveAppConnectLogin, newAppConnectLogin.swift). The wallet derives the request's session key (DIP-13 application session authentication, leafhash256of the app's ephemeral key, viaderiveConnectKey), registers it on the identity as AUTHENTICATION / HIGH / ECDSA_HASH160 bound to the contract named in the QR, with a budget and an expiry, and only then publishes the encrypted key as a three-fieldloginKeyResponse. Earlier responses for the same app are deleted by their values first.dash-st:parser ported to the SDK'sParsedStateTransitionsummary (feat(sdk)!: key limits, DIP-14 sub-feature derivation and decode-any-kind for DashPay Connect platform#4844): a key registration maps as before and now keeps its budget and expiry; a batch is accepted only as a lone, complete token direct purchase.How Has This Been Tested?
yap.pr/devnet: the first approval registered the session key (contract-bound, budget, expiry) and published the response. Both were read back from a node: the key viagetIdentityKeys, the response viagetDocumentsonH8F9…by the request hash.09a28c551: login → disconnect → login → disconnect, each step read back from a node. Disconnect setdisabledAton the session key and the response was gone fromH8F9…. The last disconnect also disabled two live session keys from earlier installs that the wallet had no record of (one identity update for all three).yap.pr/devnetstill queries its own key-exchange contract (contractId+appEphemeralPubKeyHash) and does not readH8F9….Depends on
deriveConnectKeyand the reshapedParsedStateTransition. Open.countPresent(present onv5.0-devonly). Without it the SDK fails to parse such an app contract, bans each answering node, and has no DAPI addresses left for about 40 seconds.Known gaps
TODO(app-connect-limits)).SDK.documentListis main-actor isolated and blocks), so the sheet freezes for that round trip. The two-QR lookup has the same shape.ConnectionsViewModel, the mock data source, the data source's network assert).Breaking Changes
None for users.
DashConnectDataSource.disconnect(id:)now throws.Checklist:
For repository code-owners and collaborators only
🤖 Generated with Claude Code