diff --git a/electrum/address_synchronizer.py b/electrum/address_synchronizer.py index fe9a35d86..9ece423bf 100644 --- a/electrum/address_synchronizer.py +++ b/electrum/address_synchronizer.py @@ -241,6 +241,9 @@ class AddressSynchronizer(Logger): conflicting_txns -= {tx_hash} return conflicting_txns + def get_transaction(self, txid: str) -> Transaction: + return self.db.get_transaction(txid) + def add_transaction(self, tx: Transaction, *, allow_unrelated=False) -> bool: """ Returns whether the tx was successfully added to the wallet history. diff --git a/electrum/gui/qt/settings_dialog.py b/electrum/gui/qt/settings_dialog.py index 69eb63e65..b63220f97 100644 --- a/electrum/gui/qt/settings_dialog.py +++ b/electrum/gui/qt/settings_dialog.py @@ -147,6 +147,18 @@ class SettingsDialog(WindowModalDialog): util.trigger_callback('channels_updated', self.wallet) trampoline_cb.stateChanged.connect(on_trampoline_checked) + help_instant_swaps = ' '.join([ + _("If this option is checked, your client will complete reverse swaps before the funding transaction is confirmed."), + _("Note you are at risk of losing the funds in the swap, if the funding transaction never confirms.") + ]) + instant_swaps_cb = QCheckBox(_("Allow instant swaps")) + instant_swaps_cb.setToolTip(messages.to_rtf(help_instant_swaps)) + trampoline_cb.setChecked(not bool(self.config.get('allow_instant_swaps', False))) + def on_instant_swaps_checked(allow_instant_swaps): + self.config.set_key('allow_instant_swaps', bool(allow_instant_swaps)) + instant_swaps_cb.stateChanged.connect(on_instant_swaps_checked) + lightning_widgets.append((instant_swaps_cb, None)) + help_remote_wt = ' '.join([ _("A watchtower is a daemon that watches your channels and prevents the other party from stealing funds by broadcasting an old state."), _("If you have private a watchtower, enter its URL here."), diff --git a/electrum/lnworker.py b/electrum/lnworker.py index 3ae2ed4cd..e682730af 100644 --- a/electrum/lnworker.py +++ b/electrum/lnworker.py @@ -908,7 +908,10 @@ class LNWallet(LNWorker): amount_msat = 0 label = 'Reverse swap' if swap.is_reverse else 'Forward swap' delta = current_height - swap.locktime - if not swap.is_redeemed and swap.spending_txid is None and delta < 0: + tx_height = self.lnwatcher.get_tx_height(swap.funding_txid) + if swap.is_reverse and tx_height.height <=0: + label += ' (%s)' % _('waiting for funding tx confirmation') + if not swap.is_reverse and not swap.is_redeemed and swap.spending_txid is None and delta < 0: label += f' (refundable in {-delta} blocks)' # fixme: only if unspent out[txid] = { 'txid': txid, diff --git a/electrum/submarine_swaps.py b/electrum/submarine_swaps.py index 778891526..67004608a 100644 --- a/electrum/submarine_swaps.py +++ b/electrum/submarine_swaps.py @@ -20,6 +20,7 @@ from .logging import Logger from .lnutil import hex_to_bytes from .json_db import StoredObject from . import constants +from .address_synchronizer import TX_HEIGHT_LOCAL if TYPE_CHECKING: from .network import Network @@ -191,10 +192,17 @@ class SwapManager(Logger): spent_height = txin.spent_height if spent_height is not None: swap.spending_txid = txin.spent_txid - if spent_height > 0 and current_height - spent_height > REDEEM_AFTER_DOUBLE_SPENT_DELAY: - self.logger.info(f'stop watching swap {swap.lockup_address}') - self.lnwatcher.remove_callback(swap.lockup_address) - swap.is_redeemed = True + if spent_height > 0: + if current_height - spent_height > REDEEM_AFTER_DOUBLE_SPENT_DELAY: + self.logger.info(f'stop watching swap {swap.lockup_address}') + self.lnwatcher.remove_callback(swap.lockup_address) + swap.is_redeemed = True + elif spent_height == TX_HEIGHT_LOCAL: + if txin.block_height > 0 or self.wallet.config.get('allow_instant_swaps', False): + tx = self.lnwatcher.get_transaction(txin.spent_txid) + self.logger.info(f'broadcasting tx {txin.spent_txid}') + await self.network.broadcast_transaction(tx) + # already in mempool continue if not swap.is_reverse and delta < 0: # too early for refund @@ -221,7 +229,9 @@ class SwapManager(Logger): locktime=locktime, ) self.sign_tx(tx, swap) - await self.network.broadcast_transaction(tx) + self.logger.info(f'adding claim tx {tx.txid()}') + self.wallet.add_transaction(tx) + self.lnwatcher.add_transaction(tx) def get_claim_fee(self): return self.wallet.config.estimate_fee(136, allow_fallback_to_static_rates=True)