Browse Source

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)
patch-4
SomberNight 4 years ago
parent
commit
ea22d0073e
No known key found for this signature in database GPG Key ID: B33B5F232C6271E9
  1. 2
      electrum/gui/kivy/uix/dialogs/tx_dialog.py
  2. 2
      electrum/gui/qt/transaction_dialog.py
  3. 29
      electrum/simple_config.py
  4. 4
      electrum/tests/test_simple_config.py
  5. 2
      electrum/wallet.py

2
electrum/gui/kivy/uix/dialogs/tx_dialog.py

@ -156,7 +156,7 @@ class TxDialog(Factory.Popup):
if tx_mined_status.timestamp: if tx_mined_status.timestamp:
self.date_label = _('Date') self.date_label = _('Date')
self.date_str = datetime.fromtimestamp(tx_mined_status.timestamp).isoformat(' ')[:-3] 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_label = _('Mempool depth')
self.date_str = _('{} from tip').format('%.2f MB'%(exp_n/1000000)) self.date_str = _('{} from tip').format('%.2f MB'%(exp_n/1000000))
else: else:

2
electrum/gui/qt/transaction_dialog.py

@ -430,7 +430,7 @@ class BaseTxDialog(QDialog, MessageBoxMixin):
time_str = datetime.datetime.fromtimestamp(tx_mined_status.timestamp).isoformat(' ')[:-3] time_str = datetime.datetime.fromtimestamp(tx_mined_status.timestamp).isoformat(' ')[:-3]
self.date_label.setText(_("Date: {}").format(time_str)) self.date_label.setText(_("Date: {}").format(time_str))
self.date_label.show() self.date_label.show()
elif exp_n: elif exp_n is not None:
text = '%.2f MB'%(exp_n/1000000) text = '%.2f MB'%(exp_n/1000000)
self.date_label.setText(_('Position in mempool: {} from tip').format(text)) self.date_label.setText(_('Position in mempool: {} from tip').format(text))
self.date_label.show() self.date_label.show()

29
electrum/simple_config.py

@ -65,7 +65,7 @@ class SimpleConfig(Logger):
# a thread-safe way. # a thread-safe way.
self.lock = threading.RLock() 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 = {}
self.fee_estimates_last_updated = {} self.fee_estimates_last_updated = {}
self.last_time_fee_estimates_requested = 0 # zero ensures immediate fees self.last_time_fee_estimates_requested = 0 # zero ensures immediate fees
@ -345,11 +345,13 @@ class SimpleConfig(Logger):
fee = int(fee) fee = int(fee)
return 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 """For a given sat/vbyte fee, returns an estimate of how deep
it would be in the current mempool in vbytes. it would be in the current mempool in vbytes.
Pessimistic == overestimates the depth. Pessimistic == overestimates the depth.
""" """
if self.mempool_fees is None:
return None
depth = 0 depth = 0
for fee, s in self.mempool_fees: for fee, s in self.mempool_fees:
depth += s depth += s
@ -357,16 +359,18 @@ class SimpleConfig(Logger):
break break
return depth return depth
def depth_to_fee(self, slider_pos) -> int: def depth_to_fee(self, slider_pos) -> Optional[int]:
"""Returns fee in sat/kbyte.""" """Returns fee in sat/kbyte."""
target = self.depth_target(slider_pos) target = self.depth_target(slider_pos)
return self.depth_target_to_fee(target) return self.depth_target_to_fee(target)
@impose_hard_limits_on_fee @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. """Returns fee in sat/kbyte.
target: desired mempool depth in vbytes target: desired mempool depth in vbytes
""" """
if self.mempool_fees is None:
return None
depth = 0 depth = 0
for fee, s in self.mempool_fees: for fee, s in self.mempool_fees:
depth += s depth += s
@ -381,7 +385,7 @@ class SimpleConfig(Logger):
# convert to sat/kbyte # convert to sat/kbyte
return int(fee * 1000) 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 = max(slider_pos, 0)
slider_pos = min(slider_pos, len(FEE_DEPTH_TARGETS)-1) slider_pos = min(slider_pos, len(FEE_DEPTH_TARGETS)-1)
return FEE_DEPTH_TARGETS[slider_pos] return FEE_DEPTH_TARGETS[slider_pos]
@ -400,8 +404,11 @@ class SimpleConfig(Logger):
min_target = -1 min_target = -1
return min_target return min_target
def depth_tooltip(self, depth): def depth_tooltip(self, depth: Optional[int]) -> str:
return "%.1f MB from tip"%(depth/1000000) """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): def eta_tooltip(self, x):
if x < 0: if x < 0:
@ -460,7 +467,7 @@ class SimpleConfig(Logger):
maxp = len(FEE_ETA_TARGETS) # not (-1) to have "next block" maxp = len(FEE_ETA_TARGETS) # not (-1) to have "next block"
return min(maxp, self.get('fee_level', 2)) 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 dyn:
if mempool: if mempool:
pos = self.get_depth_level() pos = self.get_depth_level()
@ -479,7 +486,7 @@ class SimpleConfig(Logger):
def static_fee(self, i): def static_fee(self, i):
return FEERATE_STATIC_VALUES[i] return FEERATE_STATIC_VALUES[i]
def static_fee_index(self, value): def static_fee_index(self, value) -> int:
if value is None: if value is None:
raise TypeError('static fee cannot be None') raise TypeError('static fee cannot be None')
dist = list(map(lambda x: abs(x - value), FEERATE_STATIC_VALUES)) dist = list(map(lambda x: abs(x - value), FEERATE_STATIC_VALUES))
@ -488,8 +495,8 @@ class SimpleConfig(Logger):
def has_fee_etas(self): def has_fee_etas(self):
return len(self.fee_estimates) == 4 return len(self.fee_estimates) == 4
def has_fee_mempool(self): def has_fee_mempool(self) -> bool:
return bool(self.mempool_fees) return self.mempool_fees is not None
def has_dynamic_fees_ready(self): def has_dynamic_fees_ready(self):
if self.use_mempool_fees(): if self.use_mempool_fees():

4
electrum/tests/test_simple_config.py

@ -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 ** 6))
self.assertEqual( 2 * 1000, config.depth_target_to_fee(10 ** 7)) self.assertEqual( 2 * 1000, config.depth_target_to_fee(10 ** 7))
self.assertEqual( 1 * 1000, config.depth_target_to_fee(10 ** 8)) 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): def test_fee_to_depth(self):
config = SimpleConfig(self.options) config = SimpleConfig(self.options)

2
electrum/wallet.py

@ -1050,7 +1050,7 @@ class Abstract_Wallet(AddressSynchronizer, ABC):
if fee is not None and height in (TX_HEIGHT_UNCONF_PARENT, TX_HEIGHT_UNCONFIRMED) \ if fee is not None and height in (TX_HEIGHT_UNCONF_PARENT, TX_HEIGHT_UNCONFIRMED) \
and self.config.has_fee_mempool(): and self.config.has_fee_mempool():
exp_n = self.config.fee_to_depth(fee_per_byte) 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)) extra.append('%.2f MB'%(exp_n/1000000))
if height == TX_HEIGHT_LOCAL: if height == TX_HEIGHT_LOCAL:
status = 3 status = 3

Loading…
Cancel
Save