URI: 
       tsupport Replace-By-Fee in GUI - electrum - Electrum Bitcoin wallet
  HTML git clone https://git.parazyd.org/electrum
   DIR Log
   DIR Files
   DIR Refs
   DIR Submodules
       ---
   DIR commit bc1bef60a0e9c01dedc37368f9a64ae9621a7d35
   DIR parent 8c42c6d39f26b5b680be5f6b7c1c8ac4a1075609
  HTML Author: ThomasV <thomasv@electrum.org>
       Date:   Fri, 20 May 2016 10:38:48 +0200
       
       support Replace-By-Fee in GUI
       
       Diffstat:
         M gui/qt/history_widget.py            |      13 +++++++++----
         M gui/qt/main_window.py               |      46 ++++++++++++++++++++++++++-----
         M lib/wallet.py                       |      23 +++++++++++++++++++++--
       
       3 files changed, 69 insertions(+), 13 deletions(-)
       ---
   DIR diff --git a/gui/qt/history_widget.py b/gui/qt/history_widget.py
       t@@ -117,11 +117,16 @@ class HistoryWidget(MyTreeWidget):
                if not tx_hash:
                    return
                tx_URL = block_explorer_URL(self.config, 'tx', tx_hash)
       -        if not tx_URL:
       -            return
       +        conf, timestamp = self.wallet.get_confirmations(tx_hash)
       +        tx = self.wallet.transactions.get(tx_hash)
       +        is_relevant, is_mine, v, fee = self.wallet.get_wallet_delta(tx)
       +        rbf = is_mine and (conf == 0) and tx and not tx.is_final()
                menu = QMenu()
                menu.addAction(_("Copy ID to Clipboard"), lambda: self.parent.app.clipboard().setText(tx_hash))
       -        menu.addAction(_("Details"), lambda: self.parent.show_transaction(self.wallet.transactions.get(tx_hash)))
       +        menu.addAction(_("Details"), lambda: self.parent.show_transaction(tx))
                menu.addAction(_("Edit description"), lambda: self.editItem(item, self.editable_columns[0]))
       -        menu.addAction(_("View on block explorer"), lambda: webbrowser.open(tx_URL))
       +        if rbf:
       +            menu.addAction(_("Increase fee"), lambda: self.parent.bump_fee_dialog(tx))
       +        if tx_URL:
       +            menu.addAction(_("View on block explorer"), lambda: webbrowser.open(tx_URL))
                menu.exec_(self.viewport().mapToGlobal(position))
   DIR diff --git a/gui/qt/main_window.py b/gui/qt/main_window.py
       t@@ -2785,6 +2785,19 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
                qr_combo.currentIndexChanged.connect(on_video_device)
                gui_widgets.append((qr_label, qr_combo))
        
       +        rbf_cb = QCheckBox(_('Enable Replace-By-Fee'))
       +        rbf_cb.setChecked(self.wallet.use_rbf)
       +        if not self.config.is_modifiable('use_rbf'):
       +            rbf_cb.setEnabled(False)
       +        def on_rbf(x):
       +            rbf_result = x == Qt.Checked
       +            if self.wallet.use_rbf != rbf_result:
       +                self.wallet.use_rbf = rbf_result
       +                self.wallet.storage.put('use_rbf', self.wallet.use_rbf)
       +        rbf_cb.stateChanged.connect(on_rbf)
       +        rbf_cb.setToolTip(_('Enable RBF'))
       +        tx_widgets.append((rbf_cb, None))
       +
                usechange_cb = QCheckBox(_('Use change addresses'))
                usechange_cb.setChecked(self.wallet.use_change)
                if not self.config.is_modifiable('use_change'): usechange_cb.setEnabled(False)
       t@@ -2796,6 +2809,7 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
                        multiple_cb.setEnabled(self.wallet.use_change)
                usechange_cb.stateChanged.connect(on_usechange)
                usechange_cb.setToolTip(_('Using change addresses makes it more difficult for other people to track your transactions.'))
       +        tx_widgets.append((usechange_cb, None))
        
                def on_multiple(x):
                    multiple = x == Qt.Checked
       t@@ -2812,7 +2826,6 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
                ]))
                multiple_cb.setChecked(multiple_change)
                multiple_cb.stateChanged.connect(on_multiple)
       -        tx_widgets.append((usechange_cb, None))
                tx_widgets.append((multiple_cb, None))
        
                showtx_cb = QCheckBox(_('View transaction before signing'))
       t@@ -2973,20 +2986,14 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
        
            def show_account_details(self, k):
                account = self.wallet.accounts[k]
       -
                d = WindowModalDialog(self, _('Account Details'))
       -
                vbox = QVBoxLayout(d)
                name = self.wallet.get_account_name(k)
                label = QLabel('Name: ' + name)
                vbox.addWidget(label)
       -
                vbox.addWidget(QLabel(_('Address type') + ': ' + account.get_type()))
       -
                vbox.addWidget(QLabel(_('Derivation') + ': ' + k))
       -
                vbox.addWidget(QLabel(_('Master Public Key:')))
       -
                text = QTextEdit()
                text.setReadOnly(True)
                text.setMaximumHeight(170)
       t@@ -2995,3 +3002,28 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
                text.setText(mpk_text)
                vbox.addLayout(Buttons(CloseButton(d)))
                d.exec_()
       +
       +    def bump_fee_dialog(self, tx):
       +        is_relevant, is_mine, v, fee = self.wallet.get_wallet_delta(tx)
       +        fee = -fee
       +        d = WindowModalDialog(self, _('Bump Fee'))
       +        vbox = QVBoxLayout(d)
       +        vbox.addWidget(QLabel(_('Current fee') + ': %s'% self.format_amount(fee) + ' ' + self.base_unit()))
       +        vbox.addWidget(QLabel(_('New Fee') + ': '))
       +        e = BTCAmountEdit(self.get_decimal_point)
       +        e.setAmount(fee + self.wallet.relayfee())
       +        vbox.addWidget(e)
       +        vbox.addLayout(Buttons(CancelButton(d), OkButton(d)))
       +        if not d.exec_():
       +            return
       +        new_fee = e.get_amount()
       +        delta = new_fee - fee
       +        if delta < 0:
       +            self.show_error("fee too low")
       +            return
       +        try:
       +            new_tx = self.wallet.bump_fee(tx, delta)
       +        except BaseException as e:
       +            self.show_error(e)
       +            return
       +        self.show_transaction(new_tx)
   DIR diff --git a/lib/wallet.py b/lib/wallet.py
       t@@ -172,15 +172,17 @@ class Abstract_Wallet(PrintError):
                self.gap_limit_for_change = 6 # constant
                # saved fields
                self.seed_version          = storage.get('seed_version', NEW_SEED_VERSION)
       -        self.use_change            = storage.get('use_change',True)
       +        self.use_change            = storage.get('use_change', True)
                self.multiple_change       = storage.get('multiple_change', False)
                self.use_encryption        = storage.get('use_encryption', False)
       +        self.use_rbf               = storage.get('use_rbf', False)
                self.seed                  = storage.get('seed', '')               # encrypted
                self.labels                = storage.get('labels', {})
                self.frozen_addresses      = set(storage.get('frozen_addresses',[]))
                self.stored_height         = storage.get('stored_height', 0)       # last known height (for offline mode)
                self.history               = storage.get('addr_history',{})        # address -> list(txid, height)
        
       +
                # imported_keys is deprecated. The GUI should call convert_imported_keys
                self.imported_keys = self.storage.get('imported_keys',{})
        
       t@@ -988,6 +990,7 @@ class Abstract_Wallet(PrintError):
        
            def add_input_info(self, txin):
                address = txin['address']
       +        txin['sequence'] = 0 if self.use_rbf else 0xffffffff
                account_id, sequence = self.get_address_index(address)
                account = self.accounts[account_id]
                redeemScript = account.redeem_script(*sequence)
       t@@ -998,7 +1001,6 @@ class Abstract_Wallet(PrintError):
                txin['pubkeys'] = list(pubkeys)
                txin['x_pubkeys'] = list(x_pubkeys)
                txin['signatures'] = [None] * len(pubkeys)
       -
                if redeemScript:
                    txin['redeemScript'] = redeemScript
                    txin['num_sig'] = account.m
       t@@ -1176,6 +1178,23 @@ class Abstract_Wallet(PrintError):
                        age = tx_age
                return age > age_limit
        
       +    def bump_fee(self, tx, delta):
       +        if tx.is_final():
       +            raise BaseException("cannot bump fee: transaction is final")
       +        inputs = copy.deepcopy(tx.inputs())
       +        outputs = copy.deepcopy(tx.outputs())
       +        for txin in inputs:
       +            txin['signatures'] = [None] * len(txin['signatures'])
       +        for i, o in enumerate(outputs):
       +            otype, address, value = o
       +            if self.is_mine(address) and value >= delta:
       +                outputs[i] = otype, address, value - delta
       +                break
       +        else:
       +            raise BaseException("cannot bump fee")
       +        new_tx = Transaction.from_io(inputs, outputs)
       +        return new_tx
       +
            def can_sign(self, tx):
                if self.is_watching_only():
                    return False