tadd LN gossip sync progress estimate indicator to Qt GUI - electrum - Electrum Bitcoin wallet HTML git clone https://git.parazyd.org/electrum DIR Log DIR Files DIR Refs DIR Submodules --- DIR commit 67d24bf12981a730b7b95c369360ccbec5ed29c9 DIR parent fd56fb918982c7afbf96267e03d7cfb2ca7fc858 HTML Author: SomberNight <somber.night@protonmail.com> Date: Sat, 29 Feb 2020 20:02:45 +0100 add LN gossip sync progress estimate indicator to Qt GUI Diffstat: M electrum/channel_db.py | 24 ++++++++++++++++++------ M electrum/gui/qt/main_window.py | 18 +++++++++++++++++- M electrum/lnpeer.py | 2 +- M electrum/lnworker.py | 15 ++++++++++++++- 4 files changed, 50 insertions(+), 9 deletions(-) --- DIR diff --git a/electrum/channel_db.py b/electrum/channel_db.py t@@ -271,6 +271,7 @@ class ChannelDB(SqlDB): self.num_channels = len(self._channels) self.num_policies = len(self._policies) self.network.trigger_callback('channel_db', self.num_nodes, self.num_channels, self.num_policies) + self.network.trigger_callback('ln_gossip_sync_progress') def get_channel_ids(self): with self.lock: t@@ -590,19 +591,30 @@ class ChannelDB(SqlDB): self._channels_for_node[channel_info.node2_id].add(channel_info.short_channel_id) self.logger.info(f'load data {len(self._channels)} {len(self._policies)} {len(self._channels_for_node)}') self.update_counts() - self.logger.info(f'semi-orphaned channels: {self.get_num_incomplete_channels()}') + (nchans_with_0p, nchans_with_1p, nchans_with_2p) = self.get_num_channels_partitioned_by_policy_count() + self.logger.info(f'num_channels_partitioned_by_policy_count. ' + f'0p: {nchans_with_0p}, 1p: {nchans_with_1p}, 2p: {nchans_with_2p}') self.data_loaded.set() - def get_num_incomplete_channels(self) -> int: - found = set() + def get_num_channels_partitioned_by_policy_count(self) -> Tuple[int, int, int]: + chans_with_zero_policies = set() + chans_with_one_policies = set() + chans_with_two_policies = set() with self.lock: _channels = self._channels.copy() for short_channel_id, ci in _channels.items(): p1 = self.get_policy_for_node(short_channel_id, ci.node1_id) p2 = self.get_policy_for_node(short_channel_id, ci.node2_id) - if p1 is None or p2 is not None: - found.add(short_channel_id) - return len(found) + if p1 is not None and p2 is not None: + chans_with_two_policies.add(short_channel_id) + elif p1 is None and p2 is None: + chans_with_zero_policies.add(short_channel_id) + else: + chans_with_one_policies.add(short_channel_id) + nchans_with_0p = len(chans_with_zero_policies) + nchans_with_1p = len(chans_with_one_policies) + nchans_with_2p = len(chans_with_two_policies) + return nchans_with_0p, nchans_with_1p, nchans_with_2p def get_policy_for_node(self, short_channel_id: bytes, node_id: bytes, *, my_channels: Dict[ShortChannelID, 'Channel'] = None) -> Optional['Policy']: DIR diff --git a/electrum/gui/qt/main_window.py b/electrum/gui/qt/main_window.py t@@ -265,7 +265,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger): 'new_transaction', 'status', 'banner', 'verified', 'fee', 'fee_histogram', 'on_quotes', 'on_history', 'channel', 'channels_updated', - 'invoice_status', 'request_status'] + 'invoice_status', 'request_status', 'ln_gossip_sync_progress'] # To avoid leaking references to "self" that prevent the # window from being GC-ed when closed, callbacks should be # methods of this class only, and specifically not be t@@ -430,6 +430,8 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger): pass elif event == 'fee_histogram': self.history_model.on_fee_histogram() + elif event == 'ln_gossip_sync_progress': + self.update_lightning_icon() else: self.logger.info(f"unexpected network event: {event} {args}") t@@ -2085,6 +2087,20 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, Logger): self.coincontrol_label.setText(msg) self.coincontrol_sb.setVisible(True) + def update_lightning_icon(self): # TODO rate-limit? + self.lightning_button.setMaximumWidth(25 + 4 * char_width_in_lineedit()) + cur, total = self.network.lngossip.get_sync_progress_estimate() + # self.logger.debug(f"updating lngossip sync progress estimate: cur={cur}, total={total}") + if cur is None or total is None: + progress_str = "??%" + else: + if total > 0: + progress_percent = 100 * cur // total + else: + progress_percent = 0 + progress_str = f"{progress_percent}%" + self.lightning_button.setText(progress_str) + def update_lock_icon(self): icon = read_QIcon("lock.png") if self.wallet.has_password() else read_QIcon("unlock.png") self.password_button.setIcon(icon) DIR diff --git a/electrum/lnpeer.py b/electrum/lnpeer.py t@@ -125,7 +125,7 @@ class Peer(Logger): self.initialized.set_result(True) def is_initialized(self): - return self.initialized.done() and self.initialized.result() == True + return self.initialized.done() and self.initialized.result() is True async def initialize(self): if isinstance(self.transport, LNTransport): DIR diff --git a/electrum/lnworker.py b/electrum/lnworker.py t@@ -198,7 +198,7 @@ class LNWorker(Logger): def peer_closed(self, peer: Peer) -> None: self.peers.pop(peer.pubkey) - def num_peers(self): + def num_peers(self) -> int: return sum([p.is_initialized() for p in self.peers.values()]) def start_network(self, network: 'Network'): t@@ -359,14 +359,27 @@ class LNGossip(LNWorker): self.unknown_ids.update(new) self.network.trigger_callback('unknown_channels', len(self.unknown_ids)) self.network.trigger_callback('gossip_peers', self.num_peers()) + self.network.trigger_callback('ln_gossip_sync_progress') def get_ids_to_query(self): N = 500 l = list(self.unknown_ids) self.unknown_ids = set(l[N:]) self.network.trigger_callback('unknown_channels', len(self.unknown_ids)) + self.network.trigger_callback('ln_gossip_sync_progress') return l[0:N] + def get_sync_progress_estimate(self) -> Tuple[Optional[int], Optional[int]]: + if self.num_peers() == 0: + return None, None + num_db_channels = self.channel_db.num_channels + nchans_with_0p, nchans_with_1p, nchans_with_2p = self.channel_db.get_num_channels_partitioned_by_policy_count() + # some channels will never have two policies (only one is in gossip?...) + # so if we have at least 1 policy for a channel, we consider that channel "complete" here + current_est = num_db_channels - nchans_with_0p + total_est = len(self.unknown_ids) + num_db_channels + return current_est, total_est + class LNWallet(LNWorker):