Repository navigation
fix(dashpay): keep contacts hidden and edits safe when saved contact details cannot be read - #1139
Claudius-Maginificent wants to merge 10 commits into
Conversation
… cannot be read When the contacts list could not read the saved contact details as a whole (the wallet is locked, or its password prompt was cancelled), it treated every contact as having none. Hidden contacts showed as active, nothing said why, and a note typed over the empty guess replaced the real note once a save's own read succeeded. The list now carries the failure as TaskError::DashPayContactDetailsUnread. - A contact whose details would come from the failed read is listed hidden and labelled. Details this device holds still place a contact. - The Contacts tab shows the error's message: what happened and to unlock the wallet, then open Contacts again. - Save note and Unhide are refused until a read succeeds. - The contact details screen opens its editor only after its details read succeeds, not after it fails. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012ujJpB9DBhvtbjhYB3aG9Q
…ts-read-failure Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012ujJpB9DBhvtbjhYB3aG9Q
…hen contacts cannot be read - The contact details screen no longer takes nickname, note and hidden flag from a contacts list whose saved details were not read. Those are guesses, and Edit starts from these fields. - A locked wallet or a cancelled password prompt reports DashPayContactDetailsLocked, which asks the user to unlock. Any other cause reports DashPayContactDetailsUnread, which asks them to open the Contacts tab again. The cause is logged. - The per-contact note and the disabled-save tooltip now say what to do. - Doc comments that ended up on the wrong items are moved back. - New tests cover the hub's record-and-clear wiring, including a stale load, and the disabled note editor. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012ujJpB9DBhvtbjhYB3aG9Q
…ts-read-failure Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012ujJpB9DBhvtbjhYB3aG9Q
…r saw The UI gate kept the hub from saving over unread details, but the backend, the authoritative layer, accepted any update. ContactInfoUpdate now carries the details the user saw (opened). save_contact_details refuses with TaskError::DashPayContactDetailsChanged, writing nothing, when a field the update replaces holds another value by the time of the save. Fields the update keeps may change freely. - The hub's note save and unhide pass the listed details. The contact details screen passes the details its editor opened from. - The backend-e2e step reads the details before it edits them. - A refused save reloads what the user edits from: the hub reloads the contacts list, and the details screen reads the contact's details again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012ujJpB9DBhvtbjhYB3aG9Q
…ed from - The hub's note save now carries the note its draft started from. It used to carry the listed note, which a reload can change under an unsaved edit, so a save over a note changed on another device passed the check. - A refused save restarts the hub's note draft from the reloaded note. It closes the contact details screen's editor while the details are read again. Before, every later save from the old baseline was refused. - changed_since_opened compares texts as they are stored: surrounding whitespace dropped, and an empty text the same as none. - Tests assert the real baselines and cover the hidden flag, a nickname replace, blank versus no text, and kept fields that moved. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012ujJpB9DBhvtbjhYB3aG9Q
|
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 defd206. Normal review starts when eligible; priority review starts as soon as a slot is available. |
…ts-read-failure Conflicts resolved: - save_contact_details: both checks are kept. The opened-details check runs first, then the unreadable-baseline check. - TaskError: DashPayContactDetailsUnread, DashPayContactDetailsLocked and DashPayContactDetailsChanged from this branch, and DashPayContactDetailsUnreadable from #1102. - unhide_task: #1102's replacement for unreadable details, built with this branch's opened-details API. replace_all is no longer test-only, since unhide uses it. - Tests from both sides are kept and adapted to the opened-details API. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012ujJpB9DBhvtbjhYB3aG9Q
…ts-read-failure Conflicts resolved: - ContactInfoUpdate: changed_since_opened from this branch, and keeps_text and unhides_only from #1102. - unhide_task: #1102's single visibility update (the backend decides what unreadable details it replaces), built with this branch's opened API. - Tests from both sides are kept. #1102's failed-publish and unreadable-baseline tests are adapted to the opened API. The unreadable-baseline test's ContactData gains details_unknown. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012ujJpB9DBhvtbjhYB3aG9Q
- A stale view of unreadable details is refused as changed, and nothing is written. Swapping the two checks fails this test. - An unhide sent while the list showed unreadable details, after they became readable, keeps them if they are still hidden. If they aren't hidden, it is refused as changed. - save_contact_details' # Errors names both refusals and their order. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012ujJpB9DBhvtbjhYB3aG9Q
…fix/dashpay-contacts-read-failure Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012ujJpB9DBhvtbjhYB3aG9Q
|
Blocked by the stack: this PR is based on #1102, which waits on dashpay/platform#5546 (DashPay contact details signed with HASH160 keys, DET's default identity key type). It can land only after that upstream PR merges, DET bumps its Platform pin past it, and #1102 merges; then retarget this branch to v1.0-dev. 🤖 Co-authored by Claudius the Magnificent AI Agent |
TL;DR
When DET cannot read your saved contact details at all, for example because the wallet is locked or you cancelled the password prompt, the Contacts tab now says so. Before this PR, hidden contacts showed up as active with no explanation. They now stay hidden. Saving notes and unhiding are paused until a read succeeds, so a note typed over an empty guess can no longer replace the note you actually saved. Existing data needs no migration.
What changed
The read failure is kept.
LoadContactsused to log a failed read of the published DIP-0015 documents or of the legacy-key documents and carry on as if there were no details. Either failure now produces a typed error, with the original error as its#[source], and the cause is logged:TaskError::DashPayContactDetailsLockedwhen the wallet is locked or its password prompt was cancelled.TaskError::DashPayContactDetailsUnreadfor any other cause.The error travels on
DashPayContactsWithInfo { details_error }.Hidden contacts stay hidden. A contact whose details would have come from the failed read gets the new
ContactData.details_unknownand is listed hidden. Details this device already holds from platform-wallet still decide where a contact goes. The rule isdetails_unknown(); it reuseslegacy_details_shown(), so it follows the same precedence as the shown details.The list explains itself. The Contacts tab shows the error's message under its header. It asks the user to unlock only when the wallet is locked:
Under each such contact, the hidden section says "This contact's saved details have not been read, so it stays hidden for now. Open the Contacts tab again to retry." Switching to another hub tab and back reloads the list.
Edits wait for a read. The note editor, Save note and Unhide are disabled while the error is set, with a tooltip that says why.
dispatch_note_saveanddispatch_unhidealso refuse. A list that reads the details clears the error.The contact details screen. Its editor used to open after its own details read failed, starting from this device's copy. It now opens only after the read succeeds; until then Edit is disabled and its tooltip says why. It also no longer takes the nickname, note or hidden flag from a contacts list whose saved details were not read, so a read it already made stays the baseline for Edit.
The backend enforces it too.
ContactInfoUpdatecarriesopened, the details the user saw.save_contact_detailsrefuses with the newTaskError::DashPayContactDetailsChangedand writes nothing when a field the update replaces holds another value by the time of the save. Message: "This contact's details changed since you opened them, so nothing was saved. Check the latest details, then make your change again." Fields the update keeps may change freely.TaskError::DashPayContactDetailsUnreadable) comes after it. A stale view therefore always gets the reload, even over unreadable details. Each refusal writes nothing.Why the save path needed this
Every save already re-reads its baseline, and a save fails if that read fails. Fields the user does not touch are preserved, so the hidden flag survives a note save. The gap was the edited field:
Blocking the save in the UI closes that gap at its source. The backend check closes it for any caller.
Follow-ups
contactInfodocument cannot be tied to them stay outside this rule. That happens when the document's key cannot be derived or its encrypted contact id does not open, so nothing tells whose document it is. Those documents are skipped as before.Testing
details_are_unknown_only_when_they_rest_on_a_failed_readnothing_is_saved_while_the_saved_details_are_unreada_failed_profile_read_keeps_the_editor_closed_until_the_details_read_ends, updated: a failed details read now keeps the editor closed, and a read that succeeds opens it.a_details_read_error_names_the_action_that_fits_its_causean_unread_contacts_list_keeps_the_details_this_screen_reada_save_over_details_that_changed_writes_nothing(run red first),a_save_over_changed_details_reads_them_again,a_save_over_changed_details_reloads_the_contacts.a_stale_view_of_unreadable_details_is_refused_as_changedpins the check order; swapping the two checks makes it fail.an_unhide_over_details_that_became_readable_keeps_themcovers both outcomes of a stale unhide.unread_details_pause_saving_until_a_newer_list_reads_themcovers record, clear, and a stale load that must not pause saving again.unread_saved_details_are_explained_and_block_the_unhide. It checks the notice, the label and the disabled Unhide, in both states. Removing the notice and the Unhide gate makes it fail. New kittestunread_saved_details_disable_the_note_editor: a note typed while the details are unread is not saved.cargo test --all-features --lib -- dashpay contact(360 passed),cargo test --all-features --test kittest(523 passed) andcargo clippy --all-features --all-targets -- -D warningspass.cargo fmt --allis clean.🤖 Co-authored by Claudius the Magnificent AI Agent
🤖 Generated with Claude Code
https://claude.ai/code/session_012ujJpB9DBhvtbjhYB3aG9Q