Add Migrate UTXOs feature to Tools menu#1983
Conversation
Implements sparrowwallet#1981 — migrate UTXOs individually between wallets creating one transaction per UTXO to preserve privacy. Two-phase dialog with setup (destination, fee strategy, UTXO selection) and sign/broadcast management with persistence, JSON export/import, and batch broadcast. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
I think a feature like that would be useful to have. I did not try it, but there are some things that need to be considered:
|
- Fee column editable in manage table (double-click on unsigned rows) - Fix fee column editability in setup table (wrong column index) - Filter BIP47 payment code wallets from destination list - Broadcast All: single confirmation, no intermediate dialogs - Export/Import: JSON format, no success dialogs - Hide Close button (use window X), remove button bar padding - Single dialog instance (re-focus if already open) - Double-click destination address to show QR code - Right-click "Verify address on device" for HW wallets Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
If each transaction has to be signed individually (at every startup, at different times), it severely limits the usefulness of the Migration Wizard. A later "Broadcast all signed" action likely negates the previously achieved privacy due to a mempool observer. In my opinion, the advantages compared to normally spending a UTXO are then marginal, and I would rather resort to the familiar manual Send dialog. The workflow there is well-known, I can skip the intermediate "broadcast later" step, and I can adjust the transaction fee to the actual current fee market. |
|
Thanks for the feedback! 1. Signing individually is impracticalCurrently transactions are signed one by one via USB. Possible improvement: Batch signing via USB. 2. Broadcast All negates privacyIt's a convenience option. Transactions can also be broadcast individually — each row has its own "Broadcast" button. The dialog persists state between sessions so it can be done over hours or days. We're working on an option for Broadcast All to emit each transaction at randomized intervals, so that each tx ends up in a different block. 3. nLocktime reveals simultaneous signingValid point. Possible improvement: Set nLocktime to 0, as other wallets do. We're reviewing all the options we could develop to address this. 4. Marginal advantages over manual Send
5.Fee marketFees can be adjusted at any time in the Manage phase: right-click a signed transaction → "Reset to unsigned", double-click the fee column to set the new rate, then re-sign. This lets you adapt to the current fee market without leaving the wizard. |
…pport Sign All opens each unsigned PSBT sequentially in a tab for signing, auto-advancing to the next after each signature. Cancels gracefully if user closes tab without signing. Also: resolveDestWallet for persistence/import, HW_AIRGAPPED support, JSON-only import, uniform row height in both tables. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
DeviceDisplayAddressDialog uses HWI USB enumeration, so airgapped devices would never be found. The check only applies to HW_USB and SW_WATCH. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Each PSBT gets a future nLocktime (currentHeight+5, then +random 1-5 from previous). Transactions auto-broadcast via NewBlockEvent when their target block is reached. New CONFIRMED status tracks when txs enter a block. Migration completes when all are confirmed. Simplified manage phase: removed Import/Export/Broadcast All buttons. Sign All + Clear only. Renamed Action column to Status with Scheduled, Unconfirmed, and Confirmed states. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The StatusCell column was redundant — the Action/Status column already conveys all states (Sign Tx, Scheduled, Unconfirmed, Confirmed, Retry). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Copy source UTXO labels to destination wallet nodes (stripping received/change/sent suffixes that Sparrow re-adds automatically) - Fix checkConfirmations to also check destWallet transactions - Run checkConfirmations on migration state load (catches confirmations that occurred while Sparrow was closed) - Listen to WalletHistoryChangedEvent for timely confirmation updates (NewBlockEvent fires before wallet transactions are updated) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
I don't want to seem ungrateful, but the problem with these AI written PRs is that instead of being either small incremental updates or large, carefully designed changes they tend to be fairly invasive, making major assumptions without consideration for the overall integrity of the application. For example, there is the addition of a "migrations" folder which contains unencrypted PSBTs, breaking expected user privacy. The wallet storage is also now no longer atomic - it resides in two different files rather than one. This is a major architectural change, made simply to satisfy a particular feature. If I was to merge this, I'd have to manage this complexity forever onwards. Another problem with AI written PRs is that they happily ditch the original design goals. For example, I could go on - the interface is un-Sparrow-like in appearance and the code formatting doesn't match. But the real issue that is putting server requirements onto a client is always going to create issues, and I think we'd be better off waiting for broadcast pool, which is gradually coming along. |
- Labels: copy exact source label to dest UTXO and BlockTransaction directly (no WalletNode, no suffix manipulation). Applied on WalletHistoryChangedEvent and on migration state load. - Resume broadcast: when app reopens with pending signed txs above current block height, broadcast one at a time waiting for confirmation before the next (broadcastNextReady). - Normal flow unchanged: each tx broadcasts at its own target block. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Fair points, Craig. We tried to offer a practical idea in response to a real need that many users face when migrating wallets — moving from singlesig to multisig, from a wallet without passphrase to one with passphrase, etc. Right now there's nothing practical to help the user avoid common mistakes during this process: address reuse, choosing the wrong destination address, accidentally consolidating UTXOs, or broadcasting everything at once. The reality is that most users will damage their privacy when migrating, simply because the process is manual and error-prone. Our thinking was that migration should be as automated as possible — the user picks the destination wallet, signs, and the system handles the rest: unique addresses, no consolidation, staggered timing. No room for human error beyond those two decisions. We understand the architectural concerns and the preference to wait for broadcast pool. Just wanted to put the idea out there. |
…fx Form - Remove 5 unused methods (~220 lines): broadcastTransaction(), exportAllPsbts(), importSignedPsbts(), readPsbtFiles(), broadcastAllSigned() - Externalize inline styles to migrate-utxos.css (padding, button-bar, fee text field alignment, status classes, centered column headers) - Replace manual HBox labels with tornadofx Form/Fieldset/Field pattern to match Sparrow dialog conventions (PrivateKeySweep, MessageSign, etc.) - Move styleClass from scene root hack to dialogPane directly - Move setStageIcon() to constructor top (Sparrow convention) - Clean up imports: order, group events wildcard, remove unused - Remove ALIGN_RIGHT/ALIGN_CENTER string constants Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove all auto-broadcast, scheduling, and persistence logic from MigrateUtxosDialog. Sparrow now only creates PSBTs with staggered nLocktime, signs them, and sends via standard Electrum broadcast. A Broadcast Pool proxy (semillabitcoin/broadcast-pool) intercepts the broadcast, detects future locktime, and handles scheduling, encryption at rest, rebroadcast, and confirmation tracking. Removed: NewBlockEvent listener, broadcastNextReady, autoBroadcast, checkConfirmations, checkMigrationComplete, broadcastTransactionSilent, JSON persistence (saveMigrationState/loadMigrationState), BROADCAST and CONFIRMED status states, gson/io/nio imports. Added: Send All button (appears after all signed), sendAllSigned loop, SENT status, auto-close dialog and navigate to dest wallet on success. Resolves Craig's concerns: no unencrypted PSBTs on disk, no non-atomic storage, no background broadcasting in wallet. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Labels were not applied to destination wallet because the dialog closed (and unregistered from EventManager) before the wallet received the transactions. Now registerLabelPropagator() creates an anonymous listener that stays alive after close, applies labels as txs arrive, and self-unregisters when all labels are applied. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>









Add "Migrate UTXOs" feature (Tools menu)
Implements #1981 — Migrate UTXOs individually between wallets, creating one transaction per UTXO to preserve privacy.
Problem
When a user needs to move funds between wallets — for example migrating from a wallet without a passphrase to one with a passphrase, or moving funds to a multisig wallet — sending all UTXOs in a single transaction links them together on-chain, destroying privacy. There is currently no built-in way to migrate UTXOs individually.
Solution
A new dialog under Tools → Migrate UTXOs that creates separate PSBTs for each selected UTXO, each sending to a unique address in the destination wallet. The entire flow is managed in a two-phase modeless dialog.
Flow
Phase 1 — Setup
Phase 2 — Sign & Broadcast
Table showing all migration transactions with columns: UTXO, Dest. Address, Value, Fee, Label, Status, Action.
The Fee column is editable — Double-click to change the fee rate on any unsigned transaction.
Status flow: Unsigned → Signed → Broadcast (or Failed)
Per-row actions (Action column):
Right-click context menu:
Double-click on a destination address shows a QR code for scanning with airgapped devices (Passport, Keystone, Jade, etc.)
Double-click on any other column in a signed/broadcast/failed row opens transaction details (read-only view).
Toolbar buttons:
Persistence
Migration state is automatically saved to
~/.sparrow[/testnet4]/migrations/<walletName>.json. If you close the dialog or Sparrow and reopen, the migration resumes where it was left off. Completed migrations (all broadcast) are auto-cleaned on next open.Privacy considerations
Files changed
MigrateUtxosDialog.java(new) — Full dialog implementationapp.fxml— Menu item under ToolsAppController.java— Handler method with single-instance enforcement