tadd CPFP (child pays for parent) dialog to the Qt GUI - electrum - Electrum Bitcoin wallet HTML git clone https://git.parazyd.org/electrum DIR Log DIR Files DIR Refs DIR Submodules --- DIR commit 15b404b9de8d0a992f9f07b6c599e1fb1f2e6310 DIR parent 9e1931587d52d67108f0ec6c3a81b7474a763f7d HTML Author: ThomasV <thomasv@electrum.org> Date: Wed, 1 Feb 2017 12:26:30 +0100 add CPFP (child pays for parent) dialog to the Qt GUI Diffstat: M gui/qt/history_list.py | 12 +++++++++--- M gui/qt/main_window.py | 27 +++++++++++++++++++++++++++ M lib/wallet.py | 19 +++++++++++++++++++ 3 files changed, 55 insertions(+), 3 deletions(-) --- DIR diff --git a/gui/qt/history_list.py b/gui/qt/history_list.py t@@ -142,7 +142,7 @@ class HistoryList(MyTreeWidget): height, conf, timestamp = self.wallet.get_tx_height(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 height <=0 and tx and not tx.is_final() + is_unconfirmed = height <= 0 menu = QMenu() menu.addAction(_("Copy %s")%column_title, lambda: self.parent.app.clipboard().setText(column_data)) t@@ -150,8 +150,14 @@ class HistoryList(MyTreeWidget): menu.addAction(_("Edit %s")%column_title, lambda: self.editItem(item, column)) menu.addAction(_("Details"), lambda: self.parent.show_transaction(tx)) - if rbf: - menu.addAction(_("Increase fee"), lambda: self.parent.bump_fee_dialog(tx)) + if is_unconfirmed and tx: + rbf = is_mine and not tx.is_final() + if rbf: + menu.addAction(_("Increase fee"), lambda: self.parent.bump_fee_dialog(tx)) + else: + child_tx = self.wallet.cpfp(tx, 0) + if child_tx: + menu.addAction(_("Child pays for parent"), lambda: self.parent.cpfp(tx, child_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@@ -2767,6 +2767,33 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError): vbox.addLayout(Buttons(CloseButton(d))) d.exec_() + def cpfp(self, parent_tx, new_tx): + total_size = parent_tx.estimated_size() + new_tx.estimated_size() + d = WindowModalDialog(self, _('Child Pays for Parent')) + vbox = QVBoxLayout(d) + vbox.addWidget(QLabel(_('Total size') + ': %d bytes'% total_size)) + max_fee = new_tx.output_value() + vbox.addWidget(QLabel(_('Max fee') + ': %s'% self.format_amount(max_fee) + ' ' + self.base_unit())) + vbox.addWidget(QLabel(_('Child fee' + ':'))) + fee_e = BTCAmountEdit(self.get_decimal_point) + fee = self.config.fee_per_kb() * total_size / 1000 + fee_e.setAmount(fee) + vbox.addWidget(fee_e) + def on_rate(dyn, pos, fee_rate): + fee = fee_rate * total_size / 1000 + fee_e.setAmount(min(max_fee, fee)) + fee_slider = FeeSlider(self, self.config, on_rate) + vbox.addWidget(fee_slider) + vbox.addLayout(Buttons(CancelButton(d), OkButton(d))) + if not d.exec_(): + return + fee = fee_e.get_amount() + if fee > max_fee: + self.show_error(_('Max fee exceeded')) + return + new_tx = self.wallet.cpfp(parent_tx, fee) + new_tx.set_sequence(0) + self.show_transaction(new_tx) def bump_fee_dialog(self, tx): is_relevant, is_mine, v, fee = self.wallet.get_wallet_delta(tx) DIR diff --git a/lib/wallet.py b/lib/wallet.py t@@ -1042,6 +1042,25 @@ class Abstract_Wallet(PrintError): raise BaseException(_('Cannot bump fee: cound not find suitable outputs')) return Transaction.from_io(inputs, outputs) + def cpfp(self, tx, fee): + txid = tx.txid() + for i, o in enumerate(tx.outputs()): + otype, address, value = o + if otype == TYPE_ADDRESS and self.is_mine(address): + break + else: + return + coins = self.get_addr_utxo(address) + for item in coins: + if item['prevout_hash'] == txid and item['prevout_n'] == i: + break + else: + return + self.add_input_info(item) + inputs = [item] + outputs = [(TYPE_ADDRESS, address, value - fee)] + return Transaction.from_io(inputs, outputs) + def add_input_info(self, txin): # Add address for utxo that are in wallet if txin.get('scriptSig') == '':