electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

commit f8b736c908e155c3c6fcc35416e8d651aa69ea66
parent 1e67e55303987aa643018a314c1245be597286ef
Author: ThomasV <thomasv@electrum.org>
Date:   Wed, 20 May 2020 13:26:58 +0200

submarine swaps:
 - use lnwatcher callback
 - add gui button

Diffstat:
Melectrum/gui/qt/channels_list.py | 22++++++++++++++++++++++
Melectrum/submarine_swaps.py | 48++++++++++++++++--------------------------------
2 files changed, 38 insertions(+), 32 deletions(-)

diff --git a/electrum/gui/qt/channels_list.py b/electrum/gui/qt/channels_list.py @@ -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): @@ -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) diff --git a/electrum/submarine_swaps.py b/electrum/submarine_swaps.py @@ -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' @@ -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] @@ -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() @@ -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'): @@ -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)