Severity: S3 (fragile boundary, not a silent double-send today)
Location: allways/cli/swap_commands/swap.py:434-449, allways/chain_providers/bitcoin.py:605-634
Node-mode BTC send uses sendtoaddress (or rpc_send_from_address), returns None on any RPC exception. The outer retry at swap.py:452-456 prompts the user and retries. If the first broadcast actually landed but the RPC response was lost, Core's wallet locks the UTXOs — so retry's listunspent won't re-select them — and the retry either fails ("insufficient") or picks a different UTXO set producing a different tx. Lightweight mode with Blockstream has no such wallet-level UTXO locking.
Fix: After any send exception, query mempool/chain for a tx matching (sender, recipient, amount) before retrying.
Severity: S3 (fragile boundary, not a silent double-send today)
Location: allways/cli/swap_commands/swap.py:434-449, allways/chain_providers/bitcoin.py:605-634
Node-mode BTC send uses
sendtoaddress(orrpc_send_from_address), returnsNoneon any RPC exception. The outer retry at swap.py:452-456 prompts the user and retries. If the first broadcast actually landed but the RPC response was lost, Core's wallet locks the UTXOs — so retry'slistunspentwon't re-select them — and the retry either fails ("insufficient") or picks a different UTXO set producing a different tx. Lightweight mode with Blockstream has no such wallet-level UTXO locking.Fix: After any send exception, query mempool/chain for a tx matching
(sender, recipient, amount)before retrying.