From 839c2fa46dbf4059202d0b2595a8e569691f6d41 Mon Sep 17 00:00:00 2001 From: Janus Date: Fri, 29 Jun 2018 16:22:32 +0100 Subject: [PATCH] ln: do not add_peer in open_channel, but add_peer in gui when opening channel to unknown peer --- gui/qt/channels_list.py | 79 ++++++++++++++++++++++++++++++++++++----- lib/lnbase.py | 3 ++ lib/lnworker.py | 22 ++++++------ 3 files changed, 84 insertions(+), 20 deletions(-) diff --git a/gui/qt/channels_list.py b/gui/qt/channels_list.py index 94c59a7f..457b0ac0 100644 --- a/gui/qt/channels_list.py +++ b/gui/qt/channels_list.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from PyQt5 import QtCore, QtWidgets from PyQt5.QtWidgets import * +import concurrent.futures from electrum.util import inv_dict, bh2u, bfh from electrum.i18n import _ @@ -84,7 +85,7 @@ class ChannelsList(MyTreeWidget): push_amt_inp.setAmount(0) h.addWidget(QLabel(_('Your Node ID')), 0, 0) h.addWidget(local_nodeid, 0, 1) - h.addWidget(QLabel(_('Remote Node ID')), 1, 0) + h.addWidget(QLabel(_('Remote Node ID or connection string')), 1, 0) h.addWidget(remote_nodeid, 1, 1) h.addWidget(QLabel('Local amount'), 2, 0) h.addWidget(local_amt_inp, 2, 1) @@ -94,20 +95,80 @@ class ChannelsList(MyTreeWidget): vbox.addLayout(Buttons(CancelButton(d), OkButton(d))) if not d.exec_(): return - nodeid_hex = str(remote_nodeid.text()) - local_amt = local_amt_inp.get_amount() - push_amt = push_amt_inp.get_amount() + connect_contents = str(remote_nodeid.text()) + rest = None + try: + nodeid_hex, rest = connect_contents.split("@") + except ValueError: + nodeid_hex = connect_contents try: node_id = bfh(nodeid_hex) + assert len(node_id) == 33 except: - self.parent.show_error(_('Invalid node ID')) + self.parent.show_error(_('Invalid node ID, must be 33 bytes and hexadecimal')) return - if node_id not in self.parent.wallet.lnworker.peers and node_id not in self.parent.network.lightning_nodes: + + local_amt = local_amt_inp.get_amount() + push_amt = push_amt_inp.get_amount() + + if local_amt < 200000: + self.parent.show_error(_('You must specify an decent amount (>=2 mBTC) for ' + \ + 'the initial local balance of the channel.')) + return + + known = node_id in self.parent.network.lightning_nodes + connected = node_id in self.parent.wallet.lnworker.peers + + open_channel_info = (node_id, local_amt, push_amt) + + if connected: + peer = self.parent.wallet.lnworker.peers[node_id] + if not peer.initialized.done(): + self.parent.show_error(_('Peer not initialized')) + return + self.gui_open_channel(None, peer, open_channel_info) + return + + if rest is not None: # perfer ip/port from input field + try: + host, port = rest.split(":") + except ValueError: + self.parent.show_error(_('Connection strings must be in @: format')) + + elif known: # then use config file + node = self.network.lightning_nodes.get(node_id) + host, port = node['addresses'][0] + else: self.parent.show_error(_('Unknown node:') + ' ' + nodeid_hex) return - assert local_amt >= 200000 - assert local_amt >= push_amt - self.main_window.protect(self.open_channel, (node_id, local_amt, push_amt)) + + try: + int(port) + except: + self.parent.show_error(_('Port number must be decimal')) + return + + peer, coro = self.parent.wallet.lnworker.add_peer(host, port, node_id, aiosafe=False) + q = QtCore.QTimer() + q.singleShot(3000, lambda: self.gui_open_channel(coro, peer, open_channel_info)) + + def gui_open_channel(self, coro, peer, open_channel_info): + if coro is not None: + try: + raise coro.exception(timeout=0) + except concurrent.futures.TimeoutError: + pass + except concurrent.futures.CancelledError: + print("Ignoring CancelledError, probably shutting down since main_loop cancelled...") + return + except Exception as e: + self.parent.show_error(_('LN main loop threw:') + ' ' + str(e)) + return + + if not peer.initialized.done(): + self.parent.show_error(_('Peer not initialized within 3 seconds')) + return + self.main_window.protect(self.open_channel, open_channel_info) def open_channel(self, *args, **kwargs): self.parent.wallet.lnworker.open_channel(*args, **kwargs) diff --git a/lib/lnbase.py b/lib/lnbase.py index 34890985..f9115d15 100644 --- a/lib/lnbase.py +++ b/lib/lnbase.py @@ -479,6 +479,9 @@ class Peer(PrintError): @aiosafe async def main_loop(self): + return await self._main_loop() + + async def _main_loop(self): self.reader, self.writer = await asyncio.open_connection(self.host, self.port) await self.handshake() # send init diff --git a/lib/lnworker.py b/lib/lnworker.py index ae924f86..4caded6d 100644 --- a/lib/lnworker.py +++ b/lib/lnworker.py @@ -52,11 +52,16 @@ class LNWorker(PrintError): assert type(node_id) is bytes return {x: y for (x, y) in self.channels.items() if y.node_id == node_id} - def add_peer(self, host, port, node_id): + def add_peer(self, host, port, node_id, aiosafe=True): peer = Peer(self, host, int(port), node_id, request_initial_sync=self.config.get("request_initial_sync", True)) - self.network.futures.append(asyncio.run_coroutine_threadsafe(peer.main_loop(), asyncio.get_event_loop())) + if not aiosafe: + method = peer._main_loop + else: + method = peer.main_loop + coro = asyncio.run_coroutine_threadsafe(method(), asyncio.get_event_loop()) + self.network.futures.append(coro) self.peers[node_id] = peer - self.lock = threading.Lock() + return peer, coro def save_channel(self, openchannel): assert type(openchannel) is HTLCStateMachine @@ -115,15 +120,10 @@ class LNWorker(PrintError): conf = self.wallet.get_tx_height(chan.funding_outpoint.txid)[1] peer.on_network_update(chan, conf) - async def _open_channel_coroutine(self, node_id, amount_sat, push_sat, password): - if node_id not in self.peers: - node = self.network.lightning_nodes.get(node_id) - if node is None: - return "node not found, peers available are: " + str(self.network.lightning_nodes.keys()) - host, port = node['addresses'][0] - self.add_peer(host, port, node_id) + async def _open_channel_coroutine(self, node_id, local_amount_sat, push_sat, password): + peer = self.peers[node_id] - openingchannel = await peer.channel_establishment_flow(self.wallet, self.config, password, amount_sat, push_sat * 1000, temp_channel_id=os.urandom(32)) + openingchannel = await peer.channel_establishment_flow(self.wallet, self.config, password, local_amount_sat + push_sat, push_sat * 1000, temp_channel_id=os.urandom(32)) self.save_channel(openingchannel) self.on_channels_updated()