[Qt] simple fee bumper with user verification

This commit is contained in:
Jonas Schnelli 2017-02-03 22:04:39 +01:00
parent 7f2b9e0868
commit fbf385cc83
No known key found for this signature in database
GPG Key ID: 1EB776BB03C7922D
6 changed files with 98 additions and 4 deletions

View File

@ -31,8 +31,6 @@
#include <QTextDocument> #include <QTextDocument>
#include <QTimer> #include <QTimer>
#define SEND_CONFIRM_DELAY 3
SendCoinsDialog::SendCoinsDialog(const PlatformStyle *_platformStyle, QWidget *parent) : SendCoinsDialog::SendCoinsDialog(const PlatformStyle *_platformStyle, QWidget *parent) :
QDialog(parent), QDialog(parent),
ui(new Ui::SendCoinsDialog), ui(new Ui::SendCoinsDialog),

View File

@ -100,13 +100,14 @@ Q_SIGNALS:
}; };
#define SEND_CONFIRM_DELAY 3
class SendConfirmationDialog : public QMessageBox class SendConfirmationDialog : public QMessageBox
{ {
Q_OBJECT Q_OBJECT
public: public:
SendConfirmationDialog(const QString &title, const QString &text, int secDelay = 0, QWidget *parent = 0); SendConfirmationDialog(const QString &title, const QString &text, int secDelay = SEND_CONFIRM_DELAY, QWidget *parent = 0);
int exec(); int exec();
private Q_SLOTS: private Q_SLOTS:

View File

@ -11,6 +11,7 @@
#include "guiutil.h" #include "guiutil.h"
#include "optionsmodel.h" #include "optionsmodel.h"
#include "platformstyle.h" #include "platformstyle.h"
#include "sendcoinsdialog.h"
#include "transactiondescdialog.h" #include "transactiondescdialog.h"
#include "transactionfilterproxy.h" #include "transactionfilterproxy.h"
#include "transactionrecord.h" #include "transactionrecord.h"
@ -37,7 +38,7 @@
TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *parent) : TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *parent) :
QWidget(parent), model(0), transactionProxyModel(0), QWidget(parent), model(0), transactionProxyModel(0),
transactionView(0), abandonAction(0), columnResizingFixer(0) transactionView(0), abandonAction(0), bumpFeeAction(0), columnResizingFixer(0)
{ {
// Build filter row // Build filter row
setContentsMargins(0,0,0,0); setContentsMargins(0,0,0,0);
@ -138,6 +139,7 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa
// Actions // Actions
abandonAction = new QAction(tr("Abandon transaction"), this); abandonAction = new QAction(tr("Abandon transaction"), this);
bumpFeeAction = new QAction(tr("Increase transaction fee"), this);
QAction *copyAddressAction = new QAction(tr("Copy address"), this); QAction *copyAddressAction = new QAction(tr("Copy address"), this);
QAction *copyLabelAction = new QAction(tr("Copy label"), this); QAction *copyLabelAction = new QAction(tr("Copy label"), this);
QAction *copyAmountAction = new QAction(tr("Copy amount"), this); QAction *copyAmountAction = new QAction(tr("Copy amount"), this);
@ -156,6 +158,7 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa
contextMenu->addAction(copyTxPlainText); contextMenu->addAction(copyTxPlainText);
contextMenu->addAction(showDetailsAction); contextMenu->addAction(showDetailsAction);
contextMenu->addSeparator(); contextMenu->addSeparator();
contextMenu->addAction(bumpFeeAction);
contextMenu->addAction(abandonAction); contextMenu->addAction(abandonAction);
contextMenu->addAction(editLabelAction); contextMenu->addAction(editLabelAction);
@ -173,6 +176,7 @@ TransactionView::TransactionView(const PlatformStyle *platformStyle, QWidget *pa
connect(view, SIGNAL(doubleClicked(QModelIndex)), this, SIGNAL(doubleClicked(QModelIndex))); connect(view, SIGNAL(doubleClicked(QModelIndex)), this, SIGNAL(doubleClicked(QModelIndex)));
connect(view, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextualMenu(QPoint))); connect(view, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextualMenu(QPoint)));
connect(bumpFeeAction, SIGNAL(triggered()), this, SLOT(bumpFee()));
connect(abandonAction, SIGNAL(triggered()), this, SLOT(abandonTx())); connect(abandonAction, SIGNAL(triggered()), this, SLOT(abandonTx()));
connect(copyAddressAction, SIGNAL(triggered()), this, SLOT(copyAddress())); connect(copyAddressAction, SIGNAL(triggered()), this, SLOT(copyAddress()));
connect(copyLabelAction, SIGNAL(triggered()), this, SLOT(copyLabel())); connect(copyLabelAction, SIGNAL(triggered()), this, SLOT(copyLabel()));
@ -372,6 +376,7 @@ void TransactionView::contextualMenu(const QPoint &point)
uint256 hash; uint256 hash;
hash.SetHex(selection.at(0).data(TransactionTableModel::TxHashRole).toString().toStdString()); hash.SetHex(selection.at(0).data(TransactionTableModel::TxHashRole).toString().toStdString());
abandonAction->setEnabled(model->transactionCanBeAbandoned(hash)); abandonAction->setEnabled(model->transactionCanBeAbandoned(hash));
bumpFeeAction->setEnabled(model->transactionSignalsRBF(hash));
if(index.isValid()) if(index.isValid())
{ {
@ -397,6 +402,21 @@ void TransactionView::abandonTx()
model->getTransactionTableModel()->updateTransaction(hashQStr, CT_UPDATED, false); model->getTransactionTableModel()->updateTransaction(hashQStr, CT_UPDATED, false);
} }
void TransactionView::bumpFee()
{
if(!transactionView || !transactionView->selectionModel())
return;
QModelIndexList selection = transactionView->selectionModel()->selectedRows(0);
// get the hash from the TxHashRole (QVariant / QString)
uint256 hash;
QString hashQStr = selection.at(0).data(TransactionTableModel::TxHashRole).toString();
hash.SetHex(hashQStr.toStdString());
// Bump tx fee over the walletModel
model->bumpFee(hash);
}
void TransactionView::copyAddress() void TransactionView::copyAddress()
{ {
GUIUtil::copyEntryData(transactionView, 0, TransactionTableModel::AddressRole); GUIUtil::copyEntryData(transactionView, 0, TransactionTableModel::AddressRole);

View File

@ -76,6 +76,7 @@ private:
QDateTimeEdit *dateFrom; QDateTimeEdit *dateFrom;
QDateTimeEdit *dateTo; QDateTimeEdit *dateTo;
QAction *abandonAction; QAction *abandonAction;
QAction *bumpFeeAction;
QWidget *createDateRangeWidget(); QWidget *createDateRangeWidget();
@ -99,6 +100,7 @@ private Q_SLOTS:
void openThirdPartyTxUrl(QString url); void openThirdPartyTxUrl(QString url);
void updateWatchOnlyColumn(bool fHaveWatchOnly); void updateWatchOnlyColumn(bool fHaveWatchOnly);
void abandonTx(); void abandonTx();
void bumpFee();
Q_SIGNALS: Q_SIGNALS:
void doubleClicked(const QModelIndex&); void doubleClicked(const QModelIndex&);

View File

@ -8,8 +8,10 @@
#include "consensus/validation.h" #include "consensus/validation.h"
#include "guiconstants.h" #include "guiconstants.h"
#include "guiutil.h" #include "guiutil.h"
#include "optionsmodel.h"
#include "paymentserver.h" #include "paymentserver.h"
#include "recentrequeststablemodel.h" #include "recentrequeststablemodel.h"
#include "sendcoinsdialog.h"
#include "transactiontablemodel.h" #include "transactiontablemodel.h"
#include "base58.h" #include "base58.h"
@ -17,15 +19,18 @@
#include "keystore.h" #include "keystore.h"
#include "validation.h" #include "validation.h"
#include "net.h" // for g_connman #include "net.h" // for g_connman
#include "policy/rbf.h"
#include "sync.h" #include "sync.h"
#include "ui_interface.h" #include "ui_interface.h"
#include "util.h" // for GetBoolArg #include "util.h" // for GetBoolArg
#include "wallet/feebumper.h"
#include "wallet/wallet.h" #include "wallet/wallet.h"
#include "wallet/walletdb.h" // for BackupWallet #include "wallet/walletdb.h" // for BackupWallet
#include <stdint.h> #include <stdint.h>
#include <QDebug> #include <QDebug>
#include <QMessageBox>
#include <QSet> #include <QSet>
#include <QTimer> #include <QTimer>
@ -693,6 +698,71 @@ bool WalletModel::abandonTransaction(uint256 hash) const
return wallet->AbandonTransaction(hash); return wallet->AbandonTransaction(hash);
} }
bool WalletModel::transactionSignalsRBF(uint256 hash) const
{
LOCK2(cs_main, wallet->cs_wallet);
const CWalletTx *wtx = wallet->GetWalletTx(hash);
if (wtx && SignalsOptInRBF(*wtx))
return true;
return false;
}
bool WalletModel::bumpFee(uint256 hash)
{
std::unique_ptr<CFeeBumper> feeBump;
{
LOCK2(cs_main, wallet->cs_wallet);
feeBump.reset(new CFeeBumper(wallet, hash, 0, false, 0, true));
}
if (feeBump->getResult() != BumpFeeResult::OK)
{
QMessageBox::critical(0, tr("Fee bump error"), tr("Increasing transaction fee failed") + "<br />(" +
(feeBump->getErrors().size() ? QString::fromStdString(feeBump->getErrors()[0]) : "") +")");
return false;
}
// allow a user based fee verification
QString questionString = tr("Do you want to increase the fee from %1 to %2").arg(
BitcoinUnits::formatHtmlWithUnit(getOptionsModel()->getDisplayUnit(), feeBump->getOldFee()),
BitcoinUnits::formatHtmlWithUnit(getOptionsModel()->getDisplayUnit(), feeBump->getNewFee()));
SendConfirmationDialog confirmationDialog(tr("Confirm fee bump"), questionString);
confirmationDialog.exec();
QMessageBox::StandardButton retval = (QMessageBox::StandardButton)confirmationDialog.result();
// cancel sign&broadcast if users doesn't want to bump the fee
if (retval != QMessageBox::Yes) {
return false;
}
WalletModel::UnlockContext ctx(requestUnlock());
if(!ctx.isValid())
{
return false;
}
// sign bumped transaction
bool res = false;
{
LOCK2(cs_main, wallet->cs_wallet);
res = feeBump->signTransaction(wallet);
}
if (!res) {
QMessageBox::critical(0, tr("Fee bump error"), tr("Can't sign transaction."));
return false;
}
// commit the bumped transaction
{
LOCK2(cs_main, wallet->cs_wallet);
res = feeBump->commit(wallet);
}
if(!res) {
QMessageBox::critical(0, tr("Fee bump error"), tr("Could not commit transaction") + "<br />(" +
QString::fromStdString(feeBump->getErrors()[0])+")");
return false;
}
return true;
}
bool WalletModel::isWalletEnabled() bool WalletModel::isWalletEnabled()
{ {
return !GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET); return !GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET);

View File

@ -207,6 +207,9 @@ public:
bool transactionCanBeAbandoned(uint256 hash) const; bool transactionCanBeAbandoned(uint256 hash) const;
bool abandonTransaction(uint256 hash) const; bool abandonTransaction(uint256 hash) const;
bool transactionSignalsRBF(uint256 hash) const;
bool bumpFee(uint256 hash);
static bool isWalletEnabled(); static bool isWalletEnabled();
bool hdEnabled() const; bool hdEnabled() const;