commit 15b404b9de8d0a992f9f07b6c599e1fb1f2e6310
parent 9e1931587d52d67108f0ec6c3a81b7474a763f7d
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:
3 files changed, 55 insertions(+), 3 deletions(-)
diff --git a/gui/qt/history_list.py b/gui/qt/history_list.py
@@ -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))
@@ -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))
diff --git a/gui/qt/main_window.py b/gui/qt/main_window.py
@@ -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)
diff --git a/lib/wallet.py b/lib/wallet.py
@@ -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') == '':