URI: 
       tconfig: distinguish knowing mempool is empty vs not having mempool_fees - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit ea22d0073ea5f61d53449a7afd072c3fd78003e8
   DIR parent 2232955a237f8fc1d6fa63696018961dece8c9c3
  HTML Author: SomberNight <somber.night@protonmail.com>
       Date:   Tue, 27 Oct 2020 18:55:39 +0100
       
       config: distinguish knowing mempool is empty vs not having mempool_fees
       
       config.mempool_fees is now [] if server claims mempool is ~empty,
       and None if no valid histogram has been received from server.
       (previously it used to be [] in both cases)
       
       Diffstat:
         M electrum/gui/kivy/uix/dialogs/tx_d… |       2 +-
         M electrum/gui/qt/transaction_dialog… |       2 +-
         M electrum/simple_config.py           |      29 ++++++++++++++++++-----------
         M electrum/tests/test_simple_config.… |       4 ++++
         M electrum/wallet.py                  |       2 +-
       
       5 files changed, 25 insertions(+), 14 deletions(-)
       ---
   DIR diff --git a/electrum/gui/kivy/uix/dialogs/tx_dialog.py b/electrum/gui/kivy/uix/dialogs/tx_dialog.py
       t@@ -156,7 +156,7 @@ class TxDialog(Factory.Popup):
                if tx_mined_status.timestamp:
                    self.date_label = _('Date')
                    self.date_str = datetime.fromtimestamp(tx_mined_status.timestamp).isoformat(' ')[:-3]
       -        elif exp_n:
       +        elif exp_n is not None:
                    self.date_label = _('Mempool depth')
                    self.date_str = _('{} from tip').format('%.2f MB'%(exp_n/1000000))
                else:
   DIR diff --git a/electrum/gui/qt/transaction_dialog.py b/electrum/gui/qt/transaction_dialog.py
       t@@ -430,7 +430,7 @@ class BaseTxDialog(QDialog, MessageBoxMixin):
                    time_str = datetime.datetime.fromtimestamp(tx_mined_status.timestamp).isoformat(' ')[:-3]
                    self.date_label.setText(_("Date: {}").format(time_str))
                    self.date_label.show()
       -        elif exp_n:
       +        elif exp_n is not None:
                    text = '%.2f MB'%(exp_n/1000000)
                    self.date_label.setText(_('Position in mempool: {} from tip').format(text))
                    self.date_label.show()
   DIR diff --git a/electrum/simple_config.py b/electrum/simple_config.py
       t@@ -65,7 +65,7 @@ class SimpleConfig(Logger):
                # a thread-safe way.
                self.lock = threading.RLock()
        
       -        self.mempool_fees = []  # type: Sequence[Tuple[Union[float, int], int]]
       +        self.mempool_fees = None  # type: Optional[Sequence[Tuple[Union[float, int], int]]]
                self.fee_estimates = {}
                self.fee_estimates_last_updated = {}
                self.last_time_fee_estimates_requested = 0  # zero ensures immediate fees
       t@@ -345,11 +345,13 @@ class SimpleConfig(Logger):
                        fee = int(fee)
                return fee
        
       -    def fee_to_depth(self, target_fee: Real) -> int:
       +    def fee_to_depth(self, target_fee: Real) -> Optional[int]:
                """For a given sat/vbyte fee, returns an estimate of how deep
                it would be in the current mempool in vbytes.
                Pessimistic == overestimates the depth.
                """
       +        if self.mempool_fees is None:
       +            return None
                depth = 0
                for fee, s in self.mempool_fees:
                    depth += s
       t@@ -357,16 +359,18 @@ class SimpleConfig(Logger):
                        break
                return depth
        
       -    def depth_to_fee(self, slider_pos) -> int:
       +    def depth_to_fee(self, slider_pos) -> Optional[int]:
                """Returns fee in sat/kbyte."""
                target = self.depth_target(slider_pos)
                return self.depth_target_to_fee(target)
        
            @impose_hard_limits_on_fee
       -    def depth_target_to_fee(self, target: int) -> int:
       +    def depth_target_to_fee(self, target: int) -> Optional[int]:
                """Returns fee in sat/kbyte.
                target: desired mempool depth in vbytes
                """
       +        if self.mempool_fees is None:
       +            return None
                depth = 0
                for fee, s in self.mempool_fees:
                    depth += s
       t@@ -381,7 +385,7 @@ class SimpleConfig(Logger):
                # convert to sat/kbyte
                return int(fee * 1000)
        
       -    def depth_target(self, slider_pos):
       +    def depth_target(self, slider_pos) -> int:
                slider_pos = max(slider_pos, 0)
                slider_pos = min(slider_pos, len(FEE_DEPTH_TARGETS)-1)
                return FEE_DEPTH_TARGETS[slider_pos]
       t@@ -400,8 +404,11 @@ class SimpleConfig(Logger):
                    min_target = -1
                return min_target
        
       -    def depth_tooltip(self, depth):
       -        return "%.1f MB from tip"%(depth/1000000)
       +    def depth_tooltip(self, depth: Optional[int]) -> str:
       +        """Returns text tooltip for given mempool depth (in vbytes)."""
       +        if depth is None:
       +            return "unknown from tip"
       +        return "%.1f MB from tip" % (depth/1_000_000)
        
            def eta_tooltip(self, x):
                if x < 0:
       t@@ -460,7 +467,7 @@ class SimpleConfig(Logger):
                maxp = len(FEE_ETA_TARGETS)  # not (-1) to have "next block"
                return min(maxp, self.get('fee_level', 2))
        
       -    def get_fee_slider(self, dyn, mempool):
       +    def get_fee_slider(self, dyn, mempool) -> Tuple[int, int, Optional[int]]:
                if dyn:
                    if mempool:
                        pos = self.get_depth_level()
       t@@ -479,7 +486,7 @@ class SimpleConfig(Logger):
            def static_fee(self, i):
                return FEERATE_STATIC_VALUES[i]
        
       -    def static_fee_index(self, value):
       +    def static_fee_index(self, value) -> int:
                if value is None:
                    raise TypeError('static fee cannot be None')
                dist = list(map(lambda x: abs(x - value), FEERATE_STATIC_VALUES))
       t@@ -488,8 +495,8 @@ class SimpleConfig(Logger):
            def has_fee_etas(self):
                return len(self.fee_estimates) == 4
        
       -    def has_fee_mempool(self):
       -        return bool(self.mempool_fees)
       +    def has_fee_mempool(self) -> bool:
       +        return self.mempool_fees is not None
        
            def has_dynamic_fees_ready(self):
                if self.use_mempool_fees():
   DIR diff --git a/electrum/tests/test_simple_config.py b/electrum/tests/test_simple_config.py
       t@@ -131,6 +131,10 @@ class Test_SimpleConfig(ElectrumTestCase):
                self.assertEqual( 2 * 1000, config.depth_target_to_fee(10 ** 6))
                self.assertEqual( 2 * 1000, config.depth_target_to_fee(10 ** 7))
                self.assertEqual( 1 * 1000, config.depth_target_to_fee(10 ** 8))
       +        config.mempool_fees = []
       +        self.assertEqual(1 * 1000, config.depth_target_to_fee(10 ** 5))
       +        config.mempool_fees = None
       +        self.assertEqual(None, config.depth_target_to_fee(10 ** 5))
        
            def test_fee_to_depth(self):
                config = SimpleConfig(self.options)
   DIR diff --git a/electrum/wallet.py b/electrum/wallet.py
       t@@ -1050,7 +1050,7 @@ class Abstract_Wallet(AddressSynchronizer, ABC):
                    if fee is not None and height in (TX_HEIGHT_UNCONF_PARENT, TX_HEIGHT_UNCONFIRMED) \
                       and self.config.has_fee_mempool():
                        exp_n = self.config.fee_to_depth(fee_per_byte)
       -                if exp_n:
       +                if exp_n is not None:
                            extra.append('%.2f MB'%(exp_n/1000000))
                    if height == TX_HEIGHT_LOCAL:
                        status = 3