electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

commit 2cf479525043203675815341ea3fca8b83d210f4
parent 352cdd7f5ab923288b460bf6904efca82f0aaf37
Author: ThomasV <thomasv@electrum.org>
Date:   Tue, 31 May 2016 20:26:09 +0200

dynamic fees: define fee levels using expected confirmation times

Diffstat:
Mgui/kivy/uix/dialogs/fee_dialog.py | 18++++++------------
Mgui/qt/main_window.py | 11++++++-----
Mlib/network.py | 27++++++++++++++++++++++-----
Mlib/util.py | 2+-
Mlib/wallet.py | 18+++++++-----------
5 files changed, 42 insertions(+), 34 deletions(-)

diff --git a/gui/kivy/uix/dialogs/fee_dialog.py b/gui/kivy/uix/dialogs/fee_dialog.py @@ -34,14 +34,6 @@ Builder.load_string(''' CheckBox: id: dynfees on_active: root.on_checkbox(self.active) - BoxLayout: - orientation: 'horizontal' - size_hint: 1, None - Label: - id: reco - font_size: '6pt' - text_size: self.size - text: '' Widget: size_hint: 1, 1 BoxLayout: @@ -77,9 +69,6 @@ class FeeDialog(Factory.Popup): self.update_slider() self.update_text() - if self.app.network and self.app.network.fee: - self.ids.reco.text = _('Recommended fee for inclusion in the next two blocks') + ': ' + self.app.format_amount_and_units(self.app.network.fee) +'/kb' - def update_text(self): self.ids.fee_per_kb.text = self.get_fee_text() @@ -96,7 +85,12 @@ class FeeDialog(Factory.Popup): def get_fee_text(self): if self.ids.dynfees.active: - return fee_levels[self.fee_level] + ' (%d%%)'% (100 * (self.fee_level + 1)/3) + tooltip = fee_levels[self.fee_level] + if self.app.network: + dynfee = self.app.network.dynfee(self.fee_level) + if dynfee: + tooltip += '\n' + (self.app.format_amount_and_units(dynfee)) + '/kB' + return tooltip else: return self.app.format_amount_and_units(self.static_fee) + '/kB' diff --git a/gui/qt/main_window.py b/gui/qt/main_window.py @@ -925,7 +925,10 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): def slider_moved(): from electrum.util import fee_levels i = self.fee_slider.sliderPosition() - tooltip = fee_levels[i] + ' (%d%%)'% (100 * (i + 1)/3) + tooltip = fee_levels[i] + dynfee = self.network.dynfee(i) + if dynfee: + tooltip += '\n' + self.format_amount(dynfee) + ' ' + self.base_unit() + '/kB' QToolTip.showText(QCursor.pos(), tooltip, self.fee_slider) def slider_released(): self.config.set_key('fee_level', self.fee_slider.sliderPosition(), False) @@ -933,7 +936,6 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): self.spend_max() else: self.update_fee() - self.fee_slider.valueChanged.connect(slider_moved) self.fee_slider.sliderReleased.connect(slider_released) self.fee_slider.setValue(self.config.get('fee_level', 2)) @@ -2376,12 +2378,11 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): dynfee_cb.setToolTip(_("Use a fee per kB value recommended by the server.")) fee_widgets.append((dynfee_cb, None)) def update_feeperkb(): - fee_e.setAmount(self.wallet.fee_per_kb(self.config)) + fee_e.setAmount(self.config.get('fee_per_kb', bitcoin.RECOMMENDED_FEE)) b = self.config.get('dynamic_fees', False) fee_e.setEnabled(not b) def on_dynfee(x): - dynfee = x == Qt.Checked - self.config.set_key('dynamic_fees', dynfee) + self.config.set_key('dynamic_fees', x == Qt.Checked) update_feeperkb() self.update_fee_edit() dynfee_cb.stateChanged.connect(on_dynfee) diff --git a/lib/network.py b/lib/network.py @@ -42,6 +42,8 @@ from interface import Connection, Interface from blockchain import Blockchain from version import ELECTRUM_VERSION, PROTOCOL_VERSION +FEE_TARGETS = [25, 10, 5, 2] + DEFAULT_PORTS = {'t':'50001', 's':'50002', 'h':'8081', 'g':'8082'} DEFAULT_SERVERS = { @@ -190,7 +192,7 @@ class Network(util.DaemonThread): self.banner = '' self.donation_address = '' - self.fee = None + self.fee_estimates = {} self.relay_fee = None self.heights = {} self.merkle_roots = {} @@ -314,7 +316,8 @@ class Network(util.DaemonThread): self.queue_request('server.banner', []) self.queue_request('server.donation_address', []) self.queue_request('server.peers.subscribe', []) - self.queue_request('blockchain.estimatefee', [2]) + for i in FEE_TARGETS: + self.queue_request('blockchain.estimatefee', [i]) self.queue_request('blockchain.relayfee', []) def get_status_value(self, key): @@ -323,7 +326,7 @@ class Network(util.DaemonThread): elif key == 'banner': value = self.banner elif key == 'fee': - value = self.fee + value = self.fee_estimates elif key == 'updated': value = (self.get_local_height(), self.get_server_height()) elif key == 'servers': @@ -332,6 +335,20 @@ class Network(util.DaemonThread): value = self.get_interfaces() return value + def dynfee(self, i): + from bitcoin import RECOMMENDED_FEE + if i < 4: + j = FEE_TARGETS[i] + fee = self.fee_estimates.get(j) + else: + assert i == 4 + fee = self.fee_estimates.get(2) + if fee is not None: + fee += fee/2 + if fee is not None: + fee = min(10*RECOMMENDED_FEE, fee) + return fee + def notify(self, key): if key in ['status', 'updated']: self.trigger_callback(key) @@ -514,8 +531,8 @@ class Network(util.DaemonThread): self.donation_address = result elif method == 'blockchain.estimatefee': if error is None: - self.fee = int(result * COIN) - self.print_error("recommended fee", self.fee) + i = params[0] + self.fee_estimates[i] = int(result * COIN) self.notify('fee') elif method == 'blockchain.relayfee': if error is None: diff --git a/lib/util.py b/lib/util.py @@ -36,7 +36,7 @@ import threading from i18n import _ base_units = {'BTC':8, 'mBTC':5, 'uBTC':2} -fee_levels = [_('Very low'), _('Low'), _('Normal'), _('High'), _('Very high')] +fee_levels = [_('Within 25 blocks'), _('Within 10 blocks'), _('Within 5 blocks'), _('Within 2 blocks'), _('In the next block')] def normalize_version(v): return [int(x) for x in re.sub(r'(\.0+)*$','', v).split(".")] diff --git a/lib/wallet.py b/lib/wallet.py @@ -909,15 +909,11 @@ class Abstract_Wallet(PrintError): def fee_per_kb(self, config): b = config.get('dynamic_fees') - F = config.get('fee_per_kb', bitcoin.RECOMMENDED_FEE) - if b and self.network and self.network.fee: - i = config.get('fee_level', 2) - fee = self.network.fee*(i+1)/3 - fee = max(fee, self.relayfee()) - fee = min(10*bitcoin.RECOMMENDED_FEE, fee) - return fee + i = config.get('fee_level', 2) + if b and self.network and self.network.dynfee(i): + return self.network.dynfee(i) else: - return F + return config.get('fee_per_kb', bitcoin.RECOMMENDED_FEE) def get_tx_status(self, tx_hash, height, conf, timestamp): from util import format_time @@ -925,10 +921,10 @@ class Abstract_Wallet(PrintError): tx = self.transactions.get(tx_hash) is_final = tx and tx.is_final() fee = self.tx_fees.get(tx_hash) - if fee and self.network and self.network.fee: + if fee and self.network and self.network.dynfee(25): size = len(tx.raw)/2 - network_fee = int(self.network.fee * size / 1000) - is_lowfee = fee < network_fee * 0.25 + low_fee = int(self.network.dynfee(25)*size/1000) + is_lowfee = fee < low_fee * 0.5 else: is_lowfee = False if not is_final: