feat: back up hardware wallet names - #1173
Open
jvsena42 wants to merge 18 commits into
Open
Conversation
jvsena42
marked this pull request as ready for review
August 21, 2026 10:34
Greptile SummaryThe PR backs up custom hardware-wallet names alongside metadata and lets users preserve names and tags when removing a wallet.
Confidence Score: 5/5The PR appears safe to merge. The previously reported pending-name loss is fixed because the adopted label and pending-name deletion now occur in one DataStore update, so a failed write retains the pending copy for a later retry; no blocking failure remains.
|
| Filename | Overview |
|---|---|
| app/src/main/java/to/bitkit/data/HwWalletStore.kt | Adds pending-name persistence and atomically consumes a pending name when saving the device record that adopts it. |
| app/src/main/java/to/bitkit/repositories/TrezorRepo.kt | Applies restored or retained names during pairing and uses the atomic store operation to prevent loss on persistence failure. |
| app/src/main/java/to/bitkit/repositories/BackupRepo.kt | Includes hardware-wallet names in metadata snapshots and restores them as pending names before devices are paired. |
| app/src/main/java/to/bitkit/repositories/HwWalletRepo.kt | Coordinates removal with optional preservation of wallet-scoped names and tag metadata. |
| app/src/main/java/to/bitkit/repositories/ActivityRepo.kt | Adds wallet-scoped tag metadata snapshots combining stored and rendered hardware-wallet metadata. |
Sequence Diagram
sequenceDiagram
participant Backup as Metadata Backup
participant Store as HwWalletStore
participant Trezor as TrezorRepo
participant Device as Paired Device
Backup->>Store: Restore names into pendingNames
Device->>Trezor: Pair wallet identity
Trezor->>Store: Read pending name by walletId
Trezor->>Store: Atomically save device label and consume pending name
Store-->>Trezor: Durable updated snapshot
Reviews (2): Last reviewed commit: "fix: make delete pending name and persis..." | Re-trigger Greptile
jvsena42
marked this pull request as draft
August 21, 2026 10:37
jvsena42
marked this pull request as ready for review
August 21, 2026 12:26
…t was never renamed
…persisted activities
…device, so the store never publishes a device list and a name set that disagree
10 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR:
Follow-up to #1163, which closed #1046 by making hardware wallet tags survive backup and restore.
Description
#1163 made hardware wallet tags survive a restore: they ride the metadata backup as pre-activity metadata keyed by the Core-derived wallet id, and Core re-attaches them once the device watcher recreates the activities. Two gaps were left behind.
The name itself was never backed up. It lives only in the local device store, so restoring on a new phone and pairing the Trezor again brought the tags back but not the name the user chose. Nothing even marked a backup as required when a name changed, so a rename was invisible to the backup system entirely.
The second gap is the more damaging one. Removing a hardware wallet destroyed its tags in the backup, silently. Core deletes a wallet's activities, its activity tags and its pre-activity metadata in one cascade, which is both of the sources the metadata envelope draws hardware tags from, and the deletion then signals that tags changed, so a metadata envelope without them was uploaded within seconds. The dialog said only that funds are safe and coins will not be deleted; it never mentioned the tags. A full wallet wipe was never affected, because wiping suppresses uploads and leaves the stored backup intact.
The name travels on the same key the tags already use. Core's wallet id derives from a device's account extended public keys, is stable across installs and platforms, and does not depend on the order of those keys, so a name keyed by it survives a restore exactly as well as the tags do.
The name of a paired wallet still lives on its device entry, which is untouched. What is new is a small pending map for the two moments when no device entry exists: a name restored from a backup before the device has been paired again, and a name kept when a wallet was removed. Pairing takes the entry over and consumes it in the same write that stores the entry, so the name is never in both places or in neither, and a failed save leaves it pending rather than losing it.
Removal takes the name and tag snapshot before the cascade and writes the tags back after it, carrying the name in the same store write that forgets the device entries. Splitting those two writes published a device list that still held the wallet being removed, and a watcher reconcile reading it restarted that wallet's watcher, which silently re-created the activities the removal had just deleted. The two kinds of data are kept independently: a wallet that was never renamed still keeps its tags, which is the more common shape by far. Turning the toggle off is the current behaviour made explicit: the tags go, and any name kept from an earlier removal of the same wallet is dropped too.
The watcher cleanup that removes activity scopes without a known device now reads its input under the same lock as the removal, and only considers wallets that still have activities to clear. Core drops a wallet's tag metadata along with its activities whether or not any matched, so cleaning up a wallet that has none is not a no-op: it takes the metadata a removal deliberately kept.
Two failure points are treated differently on purpose, because the cascade sits between them. Failing to read the tags happens before anything is deleted, so the wallet is untouched and the removal is refused with a message naming the way through — retry, or remove without keeping the data. Failing to write them back happens after the activities are already gone and the watchers already stopped, where there is nothing to roll back to and reporting a failed removal would be false, so that one is logged and the removal completes.
One behaviour falls out of this for free: re-pairing a wallet that was removed with the toggle on restores its tags with no restore flow involved at all, because the pre-activity metadata rows survived locally and Core re-attaches them as the watcher recreates the activities.
The envelope is shared with iOS, whose model mirrors it field for field. Swift ignores unknown keys when decoding, so an older iOS build restores this envelope without trouble, but it drops the new field whenever it re-uploads, and it re-uploads often. A user running both apps on one seed would lose their backed-up names until the iOS side ships. Unlike the tags in #1163, which rode a field iOS already knew, this is a new field, so a companion iOS change is needed. A passthrough that decodes and re-encodes the field, with no UI, is enough to close the window.
Preview
backup-enabled.mp4
backup-disabled.mp4
restore-wallet.mp4
QA Notes
Manual Tests
regression:Disconnect and reconnect the device without renaming → Settings → Data Backups: Tags is not re-uploaded.regression:Remove dialog → cancel: the wallet stays paired with its name and tags.Automated Checks
BackupRepoTest.kt, including that a failed name read fails the backup without uploading, that an envelope written before this field never clears stored names, that a failed name write still records the category as restored, and that a rename triggers a metadata backup while a reconnect does not.HwWalletRepoTest.kt, pinning that the tag rewrite follows Core's cascade and that the kept name rides the write that forgets the device, plus the toggle-off branch, the default, a wallet keeping its tags without ever having been renamed, a removal surviving a failed rewrite, an unreadable-tags failure leaving the wallet entirely untouched, and a removal deleting a wallet's activities exactly once.TrezorRepoTest.kt, covering adopting and consuming a pending name in a single store write, preferring a name set locally, and leaving another identity's name alone.ActivityRepoTest.kt. It unions the stored and the rendered metadata, because the cascade drops both and only the rendered half can be rebuilt from activity tags.HwWalletViewModelTest.kt, including that it returns to its default when the dialog is reopened, and that an unreadable-tags failure is reported with its own message rather than the generic retry one.just compile,just testandjust lintpass, with no new detekt findings.