URI: 
       tsubmarine swaps: - use lnwatcher callback - add gui button - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit f8b736c908e155c3c6fcc35416e8d651aa69ea66
   DIR parent 1e67e55303987aa643018a314c1245be597286ef
  HTML Author: ThomasV <thomasv@electrum.org>
       Date:   Wed, 20 May 2020 13:26:58 +0200
       
       submarine swaps:
        - use lnwatcher callback
        - add gui button
       
       Diffstat:
         M electrum/gui/qt/channels_list.py    |      22 ++++++++++++++++++++++
         M electrum/submarine_swaps.py         |      48 +++++++++++---------------------
       
       2 files changed, 38 insertions(+), 32 deletions(-)
       ---
   DIR diff --git a/electrum/gui/qt/channels_list.py b/electrum/gui/qt/channels_list.py
       t@@ -276,9 +276,11 @@ class ChannelsList(MyTreeView):
                self.can_send_label = QLabel('')
                h.addWidget(self.can_send_label)
                h.addStretch()
       +        self.swap_button = EnterButton(_('Swap'), self.swap_dialog)
                self.new_channel_button = EnterButton(_('Open Channel'), self.new_channel_dialog)
                self.new_channel_button.setEnabled(self.parent.wallet.has_lightning())
                h.addWidget(self.new_channel_button)
       +        h.addWidget(self.swap_button)
                return h
        
            def statistics_dialog(self):
       t@@ -365,3 +367,23 @@ class ChannelsList(MyTreeView):
                if not connect_str or not funding_sat:
                    return
                self.parent.open_channel(connect_str, funding_sat, 0)
       +
       +    def swap_dialog(self):
       +        lnworker = self.parent.wallet.lnworker
       +        d = WindowModalDialog(self.parent, _('Reverse Submarine Swap'))
       +        vbox = QVBoxLayout(d)
       +        amount_e = BTCAmountEdit(self.parent.get_decimal_point)
       +        h = QGridLayout()
       +        h.addWidget(QLabel('Amount'), 3, 0)
       +        h.addWidget(amount_e, 3, 1)
       +        vbox.addLayout(h)
       +        ok_button = OkButton(d)
       +        ok_button.setDefault(True)
       +        vbox.addLayout(Buttons(CancelButton(d), ok_button))
       +        if not d.exec_():
       +            return
       +        from electrum.submarine_swaps import reverse_swap
       +        import asyncio
       +        amount_sat = amount_e.get_amount()
       +        coro = reverse_swap(amount_sat, self.parent.wallet, self.parent.network)
       +        fut = asyncio.run_coroutine_threadsafe(coro, self.parent.network.asyncio_loop)
   DIR diff --git a/electrum/submarine_swaps.py b/electrum/submarine_swaps.py
       t@@ -10,9 +10,6 @@ from .transaction import Transaction
        from .util import log_exceptions
        
        
       -# todo:
       -#  - integrate with lnwatcher
       -#  - forward swaps
        
        API_URL = 'http://ecdsa.org:9001'
        
       t@@ -36,10 +33,8 @@ WITNESS_TEMPLATE_SWAP = [
        ]
        
        
       -def create_claim_tx(txid, index, witness_script, preimage, privkey:bytes, amount_sat, address, fee):
       +def create_claim_tx(txin, witness_script, preimage, privkey:bytes, amount_sat, address, fee):
            pubkey = ECPrivkey(privkey).get_public_key_bytes(compressed=True)
       -    prevout = TxOutpoint(txid=bytes.fromhex(txid), out_idx=index)
       -    txin = PartialTxInput(prevout=prevout)
            txin.script_type = 'p2wsh'
            txin.script_sig = b''
            txin.pubkeys = [pubkey]
       t@@ -59,29 +54,14 @@ def create_claim_tx(txid, index, witness_script, preimage, privkey:bytes, amount
        async def _claim_swap(lnworker, lockup_address, redeem_script, preimage, privkey, onchain_amount, address):
            # add address to lnwatcher
            lnwatcher = lnworker.lnwatcher
       -    lnwatcher.add_address(lockup_address)
       -    while True:
       -        h = lnwatcher.get_address_history(lockup_address)
       -        if not h:
       -            await asyncio.sleep(1)
       -            continue
       -        for txid, height in h:
       -            tx = lnwatcher.db.get_transaction(txid)
       -            # find relevant output
       -            for i, o in enumerate(tx.outputs()):
       -                if o.address == lockup_address:
       -                    break
       -            else:
       -                continue
       -            # create claim tx
       -            fee = lnwatcher.config.estimate_fee(136, allow_fallback_to_static_rates=True)
       -            tx = create_claim_tx(txid, i, redeem_script, preimage, privkey, onchain_amount, address, fee)
       -            try:
       -                await lnwatcher.network.broadcast_transaction(tx)
       -                break
       -            except:
       -                continue
       +    utxos = lnwatcher.get_addr_utxo(lockup_address)
       +    for txin in list(utxos.values()):
       +        fee = lnwatcher.config.estimate_fee(136, allow_fallback_to_static_rates=True)
       +        tx = create_claim_tx(txin, redeem_script, preimage, privkey, onchain_amount, address, fee)
       +        await lnwatcher.network.broadcast_transaction(tx)
        
       +
       +@log_exceptions
        async def claim_swap(key, wallet):
            lnworker = wallet.lnworker
            address = wallet.get_unused_address()
       t@@ -92,7 +72,10 @@ async def claim_swap(key, wallet):
            lockup_address = data['lockupAddress']
            preimage = bytes.fromhex(data['preimage'])
            privkey = bytes.fromhex(data['privkey'])
       -    await _claim_swap(lnworker, lockup_address, redeem_script, preimage, privkey, onchain_amount, address)
       +    callback = lambda: _claim_swap(lnworker, lockup_address, redeem_script, preimage, privkey, onchain_amount, address)
       +    lnworker.lnwatcher.add_callback(lockup_address, callback)
       +    return True
       +
        
        @log_exceptions
        async def reverse_swap(amount_sat, wallet: 'Abstract_Wallet', network: 'Network'):
       t@@ -141,10 +124,11 @@ async def reverse_swap(amount_sat, wallet: 'Abstract_Wallet', network: 'Network'
            # save data to wallet file
            swaps = wallet.db.get_dict('submarine_swaps')
            swaps[response_id] = data
       -    # add to lnwatcher
       -    f = asyncio.ensure_future(_claim_swap(lnworker, lockup_address, redeem_script, preimage, privkey, onchain_amount, address))
       +    # add callback to lnwatcher
       +    callback = lambda: _claim_swap(lnworker, lockup_address, redeem_script, preimage, privkey, onchain_amount, address)
       +    lnworker.lnwatcher.add_callback(lockup_address, callback)
            # initiate payment.
       -    success, log = await lnworker._pay(invoice, attempts=1)
       +    success, log = await lnworker._pay(invoice, attempts=5)
            # discard data; this should be done by lnwatcher
            if success:
                swaps.pop(response_id)