URI: 
       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):