URI: 
       tallow fractional feerates (#4324) - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit dae187badabf4a8e458a217455e33a8a97c6f2f2
   DIR parent 3337af0734cf17b7510783bf9bdecb8e4c4f6f17
  HTML Author: ghost43 <somber.night@protonmail.com>
       Date:   Wed,  9 May 2018 19:30:18 +0200
       
       allow fractional feerates (#4324)
       
       
       Diffstat:
         M gui/qt/amountedit.py                |      31 ++++++++++++++++++++++++-------
         M gui/qt/main_window.py               |      17 +++++++++--------
         M lib/simple_config.py                |      10 ++++------
         M lib/util.py                         |      16 +++++++++++++++-
       
       4 files changed, 52 insertions(+), 22 deletions(-)
       ---
   DIR diff --git a/gui/qt/amountedit.py b/gui/qt/amountedit.py
       t@@ -1,11 +1,12 @@
        # -*- coding: utf-8 -*-
        
       +from decimal import Decimal
       +
        from PyQt5.QtCore import *
        from PyQt5.QtGui import *
        from PyQt5.QtWidgets import (QLineEdit, QStyle, QStyleOptionFrame)
        
       -from decimal import Decimal
       -from electrum.util import format_satoshis_plain, decimal_point_to_base_unit_name
       +from electrum.util import format_satoshis_plain, decimal_point_to_base_unit_name, FEERATE_PRECISION
        
        
        class MyLineEdit(QLineEdit):
       t@@ -19,7 +20,7 @@ class MyLineEdit(QLineEdit):
        class AmountEdit(MyLineEdit):
            shortcut = pyqtSignal()
        
       -    def __init__(self, base_unit, is_int = False, parent=None):
       +    def __init__(self, base_unit, is_int=False, parent=None):
                QLineEdit.__init__(self, parent)
                # This seems sufficient for hundred-BTC amounts with 8 decimals
                self.setFixedWidth(140)
       t@@ -28,10 +29,14 @@ class AmountEdit(MyLineEdit):
                self.is_int = is_int
                self.is_shortcut = False
                self.help_palette = QPalette()
       +        self.extra_precision = 0
        
            def decimal_point(self):
                return 8
        
       +    def max_precision(self):
       +        return self.decimal_point() + self.extra_precision
       +
            def numbify(self):
                text = self.text().strip()
                if text == '!':
       t@@ -45,7 +50,7 @@ class AmountEdit(MyLineEdit):
                    if '.' in s:
                        p = s.find('.')
                        s = s.replace('.','')
       -                s = s[:p] + '.' + s[p:p+self.decimal_point()]
       +                s = s[:p] + '.' + s[p:p+self.max_precision()]
                self.setText(s)
                # setText sets Modified to False.  Instead we want to remember
                # if updates were because of user modification.
       t@@ -75,7 +80,7 @@ class AmountEdit(MyLineEdit):
        
        class BTCAmountEdit(AmountEdit):
        
       -    def __init__(self, decimal_point, is_int = False, parent=None):
       +    def __init__(self, decimal_point, is_int=False, parent=None):
                AmountEdit.__init__(self, self._base_unit, is_int, parent)
                self.decimal_point = decimal_point
        
       t@@ -87,8 +92,15 @@ class BTCAmountEdit(AmountEdit):
                    x = Decimal(str(self.text()))
                except:
                    return None
       -        p = pow(10, self.decimal_point())
       -        return int( p * x )
       +        # scale it to max allowed precision, make it an int
       +        power = pow(10, self.max_precision())
       +        max_prec_amount = int(power * x)
       +        # if the max precision is simply what unit conversion allows, just return
       +        if self.max_precision() == self.decimal_point():
       +            return max_prec_amount
       +        # otherwise, scale it back to the expected unit
       +        amount = Decimal(max_prec_amount) / pow(10, self.max_precision()-self.decimal_point())
       +        return Decimal(amount) if not self.is_int else int(amount)
        
            def setAmount(self, amount):
                if amount is None:
       t@@ -98,6 +110,11 @@ class BTCAmountEdit(AmountEdit):
        
        
        class FeerateEdit(BTCAmountEdit):
       +
       +    def __init__(self, decimal_point, is_int=False, parent=None):
       +        super().__init__(decimal_point, is_int, parent)
       +        self.extra_precision = FEERATE_PRECISION
       +
            def _base_unit(self):
                return 'sat/byte'
        
   DIR diff --git a/gui/qt/main_window.py b/gui/qt/main_window.py
       t@@ -49,7 +49,7 @@ from electrum.util import (format_time, format_satoshis, format_fee_satoshis,
                                   UserCancelled, NoDynamicFeeEstimates, profiler,
                                   export_meta, import_meta, bh2u, bfh, InvalidPassword,
                                   base_units, base_units_list, base_unit_name_to_decimal_point,
       -                           decimal_point_to_base_unit_name)
       +                           decimal_point_to_base_unit_name, quantize_feerate)
        from electrum import Transaction
        from electrum import util, bitcoin, commands, coinchooser
        from electrum import paymentrequest
       t@@ -1102,7 +1102,8 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
                        self.config.set_key('fee_per_kb', fee_rate, False)
        
                    if fee_rate:
       -                self.feerate_e.setAmount(fee_rate // 1000)
       +                fee_rate = Decimal(fee_rate)
       +                self.feerate_e.setAmount(quantize_feerate(fee_rate / 1000))
                    else:
                        self.feerate_e.setAmount(None)
                    self.fee_e.setModified(False)
       t@@ -1334,12 +1335,12 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
                    if freeze_feerate or self.fee_slider.is_active():
                        displayed_feerate = self.feerate_e.get_amount()
                        if displayed_feerate:
       -                    displayed_feerate = displayed_feerate // 1000
       +                    displayed_feerate = quantize_feerate(displayed_feerate / 1000)
                        else:
                            # fallback to actual fee
       -                    displayed_feerate = fee // size if fee is not None else None
       +                    displayed_feerate = quantize_feerate(fee / size) if fee is not None else None
                            self.feerate_e.setAmount(displayed_feerate)
       -                displayed_fee = displayed_feerate * size if displayed_feerate is not None else None
       +                displayed_fee = round(displayed_feerate * size) if displayed_feerate is not None else None
                        self.fee_e.setAmount(displayed_fee)
                    else:
                        if freeze_fee:
       t@@ -1349,14 +1350,14 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
                            displayed_fee = fee
                            self.fee_e.setAmount(displayed_fee)
                        displayed_fee = displayed_fee if displayed_fee else 0
       -                displayed_feerate = displayed_fee // size if displayed_fee is not None else None
       +                displayed_feerate = quantize_feerate(displayed_fee / size) if displayed_fee is not None else None
                        self.feerate_e.setAmount(displayed_feerate)
        
                    # show/hide fee rounding icon
                    feerounding = (fee - displayed_fee) if fee else 0
       -            self.set_feerounding_text(feerounding)
       +            self.set_feerounding_text(int(feerounding))
                    self.feerounding_icon.setToolTip(self.feerounding_text)
       -            self.feerounding_icon.setVisible(bool(feerounding))
       +            self.feerounding_icon.setVisible(abs(feerounding) >= 1)
        
                    if self.is_max:
                        amount = tx.output_value()
   DIR diff --git a/lib/simple_config.py b/lib/simple_config.py
       t@@ -3,6 +3,7 @@ import threading
        import time
        import os
        import stat
       +from decimal import Decimal
        
        from copy import deepcopy
        
       t@@ -473,12 +474,9 @@ class SimpleConfig(PrintError):
        
            @classmethod
            def estimate_fee_for_feerate(cls, fee_per_kb, size):
       -        # note: We only allow integer sat/byte values atm.
       -        # The GUI for simplicity reasons only displays integer sat/byte,
       -        # and for the sake of consistency, we thus only use integer sat/byte in
       -        # the backend too.
       -        fee_per_byte = int(fee_per_kb / 1000)
       -        return int(fee_per_byte * size)
       +        fee_per_kb = Decimal(fee_per_kb)
       +        fee_per_byte = fee_per_kb / 1000
       +        return round(fee_per_byte * size)
        
            def update_fee_estimates(self, key, value):
                self.fee_estimates[key] = value
   DIR diff --git a/lib/util.py b/lib/util.py
       t@@ -24,6 +24,7 @@ import binascii
        import os, sys, re, json
        from collections import defaultdict
        from datetime import datetime
       +import decimal
        from decimal import Decimal
        import traceback
        import urllib
       t@@ -472,8 +473,21 @@ def format_satoshis(x, num_zeros=0, decimal_point=8, precision=None, is_diff=Fal
                result = " " * (15 - len(result)) + result
            return result
        
       +
       +FEERATE_PRECISION = 1  # num fractional decimal places for sat/byte fee rates
       +_feerate_quanta = Decimal(10) ** (-FEERATE_PRECISION)
       +
       +
        def format_fee_satoshis(fee, num_zeros=0):
       -    return format_satoshis(fee, num_zeros, 0, precision=1)
       +    return format_satoshis(fee, num_zeros, 0, precision=FEERATE_PRECISION)
       +
       +
       +def quantize_feerate(fee):
       +    """Strip sat/byte fee rate of excess precision."""
       +    if fee is None:
       +        return None
       +    return Decimal(fee).quantize(_feerate_quanta, rounding=decimal.ROUND_HALF_DOWN)
       +
        
        def timestamp_to_datetime(timestamp):
            if timestamp is None: