Repository navigation
Commit a562eda
feat(qt): DashPay contact payments, seed recovery and identity details
Send to username: the recipient field of the send dialog accepts a DPNS label (the Base58 entry validator would silently drop 0, I, O and l), resolves it through a proved read, and replaces it with the next unused DIP-15 payment address derived from the contact's stored xpub. A lookup discloses what was typed to an evonode, so while typing only a label that cannot be the start of a Dash address (one with a character outside Base58, or a hyphen) is looked up; a label that could still become an address is looked up once the entry is left. The status shown is the DPNS label the proof verified, as registered rather than in its homograph-safe normalized form, together with the derived address; a profile display name is never presented as the destination. The address is reserved when it resolves, labelled for transaction history, and its cursor advances only once the payment was committed; a commit the mempool refuses (TransactionCommitFailed) releases the reservation. Paying a contact starts on the Send tab: the send entry's @ button opens a contact picker (first contact selected, Enter pays), and the DashPay tab has no pay controls of its own; a connected contact's row and tooltip point there. Leaving the recipient field looks a username up through a FocusOut event filter as well as editingFinished, which a field losing focus with an intermediate text does not report. While network activity is off the @ button is disabled and a lookup says DashPay is paused rather than that Platform can't be reached.
Seed-only recovery rebuilds the local Platform state of a wallet restored from its recovery phrase: it probes identity indexes by MASTER key hash with a gap of five, where only a proven absence counts (a failed probe ends the session incomplete and the next start retries), restores index 0 as REGISTERED with the lexicographically first of its names or as an identity awaiting a username, and records the identity's signing, encryption and decryption key ids after checking each against the key the wallet derives there, so an identity registered by another wallet from the same seed with a different key layout still works and one whose keys this wallet does not hold is reported rather than restored as an identity that could never sign. Until the probe has concluded, and while a locked wallet keeps it from running, no new registration may start: it would burn an asset lock on an identity Platform refuses as a duplicate. Established contacts (a request proved in both directions) are restored by decrypting their requests, the friendship keychains are imported with birth times taken from our own on-chain requests (never the sender-authored document time), the outbound payment cursors are rebuilt from wallet history and a rescan starts. A contact skipped for a reason a later run can resolve (an unanswered page, a re-locked wallet) keeps the contact phase owed under a platform/recovery-pending record that the next start resumes. Every paged read is one request per step, continued from the cursor, under a flow-level cap.
The network-changed and record-version wipes end in recovery through this path.
Tests (test_dash-qt): recovery treats only proven absence as absence, never re-probes after a session ends and opens registration only then; an identity with foreign keys is refused and a locked wallet defers the probe to its unlock while blocking registration; an owed contact phase resumes on the next start without a second identity scan; the restored identity, name across two pages continued by cursor and key ids; mutual-only contacts with the birth time of our own request; the send entry looking up only what cannot be an address prefix while typing (and on leaving the entry otherwise), showing the DPNS label and derived address (never the profile name or the normalized label) with the cursor advancing exactly once on commit; and the recipient entry validator.
An Identity details dialog shows the username, the identity id in Base58 (hex in its tooltip and context menu), its state, when this wallet registered it (not shown for a restored identity, whose record was written when it was found), its balance on Dash Platform in the display unit, its profile, and behind Show keys its keys with the ones this wallet holds marked, in cards sharing one label column. It reads once when opened; a failed read leaves its values at a dash instead of Loading… and offers Try again, and while DashPay is paused it reads nothing and says it shows only what the wallet knows. Its Copy buttons are secondary so Close is its one filled button. Contact errors and the picker's empty state say to add contacts on the DashPay tab. A recovery check that gets no answer ends in a notice with Try again instead of checking forever, and a locked wallet is offered Unlock wallet…, which unlocks it for that scan only and locks it again when the scan ends or after two minutes (a monotonic timer), whichever comes first. The scan derives the MASTER key hashes it probes before its first request; a scan whose unlock ran out goes on with its reads, and an identity it finds is not concluded on (its keys cannot be compared) until the next unlock, while a contact it cannot decrypt stays owed. The Send tab shows a username's lookup state on a line under the recipient; the @ button's glyph is sized through GUIUtil::setFont, and its Alt+C shortcut belongs to the entry being edited, so a second recipient does not make it ambiguous. The DashPay page gives its primary action the initial focus (the state card's action, else the empty state's Add contact…, else the contacts table), and Identity details… sits next to Edit profile… on the right of the header. A contact row whose reply Platform is confirming cannot be accepted again meanwhile. The contact picker hides its Profile name column when nobody has a profile and shares the text colour and selection of the contacts and search tables in both themes. Tests: the identity details after failed reads with no Network, Funding, Revision, Refresh or Copy all details and the keys behind Show keys (identityDetailsAfterFailedReads), Created only for an identity this wallet registered (identityDetailsCreatedOnlyWhenRegisteredHere), no reads while paused (identityDetailsPausedShowsLocalOnly), a connected contact's row pointing to Send with no action of its own (answeredRequestEstablishesContact) and the recovery unlock lasting only for the scan, with an identity found after it ran out left for the next unlock (recoveryUnlockLastsForTheScan).
Recovery restores each contact from its newest incoming request (the one DashPay acts on, ContactFlow::NewestPerSender) and remembers every request this identity sent, answered or not, under contact/out/ with the time of the first one: that is the keychain's birth time and what tells Add contact a request was already sent. Past payments the rebuilt cursor finds in wallet history to a restored contact's payment addresses are labelled as payments to them, as a send from here labels them; only scripts the wallet holds a transaction for are labelled, and a label the user gave the address stays. The Identity details dialog no longer shows the normalized Stored as form of the username. Tests: recoveryRestoresIdentityAndPagesContacts also covers an unanswered request we sent being remembered (and one never sent not being), and a past payment to a restored contact labelled with the cursor continuing after it.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>1 parent 7004cfe commit a562eda
27 files changed
Lines changed: 4537 additions & 1497 deletions
File tree
- src
- qt
- forms
- platform
- res/css
- test
- test/lint
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
125 | 125 | | |
126 | 126 | | |
127 | 127 | | |
| 128 | + | |
128 | 129 | | |
129 | 130 | | |
130 | 131 | | |
131 | 132 | | |
| 133 | + | |
132 | 134 | | |
133 | 135 | | |
134 | 136 | | |
| 137 | + | |
135 | 138 | | |
136 | 139 | | |
137 | 140 | | |
| |||
243 | 246 | | |
244 | 247 | | |
245 | 248 | | |
| 249 | + | |
246 | 250 | | |
247 | 251 | | |
248 | 252 | | |
249 | 253 | | |
| 254 | + | |
250 | 255 | | |
251 | 256 | | |
252 | 257 | | |
| 258 | + | |
253 | 259 | | |
254 | 260 | | |
255 | 261 | | |
| |||
391 | 397 | | |
392 | 398 | | |
393 | 399 | | |
| 400 | + | |
394 | 401 | | |
395 | 402 | | |
396 | 403 | | |
397 | 404 | | |
| 405 | + | |
398 | 406 | | |
399 | 407 | | |
400 | 408 | | |
| 409 | + | |
401 | 410 | | |
402 | 411 | | |
403 | 412 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
22 | 22 | | |
23 | 23 | | |
24 | 24 | | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
25 | 50 | | |
26 | 51 | | |
27 | 52 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
23 | 23 | | |
24 | 24 | | |
25 | 25 | | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
26 | 46 | | |
27 | 47 | | |
28 | 48 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
54 | 54 | | |
55 | 55 | | |
56 | 56 | | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
57 | 76 | | |
58 | 77 | | |
59 | 78 | | |
| |||
110 | 129 | | |
111 | 130 | | |
112 | 131 | | |
113 | | - | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
114 | 146 | | |
115 | 147 | | |
116 | 148 | | |
| |||
123 | 155 | | |
124 | 156 | | |
125 | 157 | | |
126 | | - | |
| 158 | + | |
127 | 159 | | |
128 | 160 | | |
129 | 161 | | |
| |||
133 | 165 | | |
134 | 166 | | |
135 | 167 | | |
136 | | - | |
| 168 | + | |
137 | 169 | | |
138 | 170 | | |
139 | 171 | | |
| |||
146 | 178 | | |
147 | 179 | | |
148 | 180 | | |
149 | | - | |
| 181 | + | |
150 | 182 | | |
151 | 183 | | |
152 | 184 | | |
| |||
174 | 206 | | |
175 | 207 | | |
176 | 208 | | |
177 | | - | |
| 209 | + | |
178 | 210 | | |
179 | 211 | | |
180 | 212 | | |
| |||
184 | 216 | | |
185 | 217 | | |
186 | 218 | | |
187 | | - | |
| 219 | + | |
188 | 220 | | |
189 | 221 | | |
190 | 222 | | |
| |||
194 | 226 | | |
195 | 227 | | |
196 | 228 | | |
197 | | - | |
| 229 | + | |
198 | 230 | | |
199 | 231 | | |
200 | 232 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
0 commit comments