aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWladimir J. van der Laan <laanwj@gmail.com>2014-11-19 16:11:15 +0100
committerWladimir J. van der Laan <laanwj@gmail.com>2014-11-19 16:27:04 +0100
commitb7fe9cd04cd2756b1ce40b668d25a130441ba847 (patch)
tree8d56d76ed4b19a0a5689a5ec5c866c489ed62c78
parent8c4185338001b8ba3ef247b09fb63afb65961c10 (diff)
parentc1c9d5b415fda7d1310c23857e57d98ac14c3063 (diff)
Merge pull request #5200
c1c9d5b [Qt] Add Smartfee to GUI (Cozz Lovan) e7876b2 [Wallet] Prevent user from paying a non-sense fee (Cozz Lovan) ed3e5e4 [Wallet] Add global boolean whether to pay at least the custom fee (default=true) (Cozz Lovan) 0ed9675 [Wallet] Add global boolean whether to send free transactions (default=true) (Cozz Lovan)
-rw-r--r--src/init.cpp2
-rw-r--r--src/qt/bitcoinamountfield.cpp6
-rw-r--r--src/qt/bitcoinamountfield.h3
-rw-r--r--src/qt/coincontroldialog.cpp95
-rw-r--r--src/qt/coincontroldialog.h2
-rw-r--r--src/qt/forms/optionsdialog.ui78
-rw-r--r--src/qt/forms/sendcoinsdialog.ui600
-rw-r--r--src/qt/optionsdialog.cpp18
-rw-r--r--src/qt/optionsdialog.h1
-rw-r--r--src/qt/optionsmodel.cpp24
-rw-r--r--src/qt/optionsmodel.h2
-rw-r--r--src/qt/sendcoinsdialog.cpp196
-rw-r--r--src/qt/sendcoinsdialog.h13
-rw-r--r--src/qt/walletmodel.cpp4
-rw-r--r--src/qt/walletmodel.h3
-rw-r--r--src/qt/walletmodeltransaction.cpp5
-rw-r--r--src/qt/walletmodeltransaction.h1
-rw-r--r--src/qt/walletview.cpp1
-rw-r--r--src/txmempool.h7
-rw-r--r--src/wallet.cpp12
-rw-r--r--src/wallet.h2
21 files changed, 911 insertions, 164 deletions
diff --git a/src/init.cpp b/src/init.cpp
index b290d54158..4a9982a1cd 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -290,6 +290,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += " -paytxfee=<amt> " + strprintf(_("Fee (in BTC/kB) to add to transactions you send (default: %s)"), FormatMoney(payTxFee.GetFeePerK())) + "\n";
strUsage += " -rescan " + _("Rescan the block chain for missing wallet transactions") + " " + _("on startup") + "\n";
strUsage += " -salvagewallet " + _("Attempt to recover private keys from a corrupt wallet.dat") + " " + _("on startup") + "\n";
+ strUsage += " -sendfreetransactions " + strprintf(_("Send transactions as zero-fee transactions if possible (default: %u)"), 0) + "\n";
strUsage += " -spendzeroconfchange " + strprintf(_("Spend unconfirmed change when sending transactions (default: %u)"), 1) + "\n";
strUsage += " -txconfirmtarget=<n> " + strprintf(_("If paytxfee is not set, include enough fee so transactions are confirmed on average within n blocks (default: %u)"), 1) + "\n";
strUsage += " -upgradewallet " + _("Upgrade wallet to latest format") + " " + _("on startup") + "\n";
@@ -704,6 +705,7 @@ bool AppInit2(boost::thread_group& threadGroup)
}
nTxConfirmTarget = GetArg("-txconfirmtarget", 1);
bSpendZeroConfChange = GetArg("-spendzeroconfchange", true);
+ fSendFreeTransactions = GetArg("-sendfreetransactions", false);
std::string strWalletFile = GetArg("-wallet", "wallet.dat");
#endif // ENABLE_WALLET
diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp
index 2c100337d2..5b8ab23b26 100644
--- a/src/qt/bitcoinamountfield.cpp
+++ b/src/qt/bitcoinamountfield.cpp
@@ -221,6 +221,12 @@ void BitcoinAmountField::clear()
unit->setCurrentIndex(0);
}
+void BitcoinAmountField::setEnabled(bool fEnabled)
+{
+ amount->setEnabled(fEnabled);
+ unit->setEnabled(fEnabled);
+}
+
bool BitcoinAmountField::validate()
{
bool valid = false;
diff --git a/src/qt/bitcoinamountfield.h b/src/qt/bitcoinamountfield.h
index 4ab66001f0..1bad8ce1b8 100644
--- a/src/qt/bitcoinamountfield.h
+++ b/src/qt/bitcoinamountfield.h
@@ -48,6 +48,9 @@ public:
/** Make field empty and ready for new input. */
void clear();
+ /** Enable/Disable. */
+ void setEnabled(bool fEnabled);
+
/** Qt messes up the tab chain by default in some cases (issue https://bugreports.qt-project.org/browse/QTBUG-10907),
in these cases we have to set it up manually.
*/
diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp
index ba0febe546..85b43b7b1f 100644
--- a/src/qt/coincontroldialog.cpp
+++ b/src/qt/coincontroldialog.cpp
@@ -24,6 +24,7 @@
#include <QDialogButtonBox>
#include <QFlags>
#include <QIcon>
+#include <QSettings>
#include <QString>
#include <QTreeWidget>
#include <QTreeWidgetItem>
@@ -130,10 +131,22 @@ CoinControlDialog::CoinControlDialog(QWidget *parent) :
// default view is sorted by amount desc
sortView(COLUMN_AMOUNT_INT64, Qt::DescendingOrder);
+
+ // restore list mode and sortorder as a convenience feature
+ QSettings settings;
+ if (settings.contains("nCoinControlMode") && !settings.value("nCoinControlMode").toBool())
+ ui->radioTreeMode->click();
+ if (settings.contains("nCoinControlSortColumn") && settings.contains("nCoinControlSortOrder"))
+ sortView(settings.value("nCoinControlSortColumn").toInt(), ((Qt::SortOrder)settings.value("nCoinControlSortOrder").toInt()));
}
CoinControlDialog::~CoinControlDialog()
{
+ QSettings settings;
+ settings.setValue("nCoinControlMode", ui->radioListMode->isChecked());
+ settings.setValue("nCoinControlSortColumn", sortColumn);
+ settings.setValue("nCoinControlSortOrder", (int)sortOrder);
+
delete ui;
}
@@ -290,19 +303,19 @@ void CoinControlDialog::clipboardAmount()
// copy label "Fee" to clipboard
void CoinControlDialog::clipboardFee()
{
- GUIUtil::setClipboard(ui->labelCoinControlFee->text().left(ui->labelCoinControlFee->text().indexOf(" ")));
+ GUIUtil::setClipboard(ui->labelCoinControlFee->text().left(ui->labelCoinControlFee->text().indexOf(" ")).replace("~", ""));
}
// copy label "After fee" to clipboard
void CoinControlDialog::clipboardAfterFee()
{
- GUIUtil::setClipboard(ui->labelCoinControlAfterFee->text().left(ui->labelCoinControlAfterFee->text().indexOf(" ")));
+ GUIUtil::setClipboard(ui->labelCoinControlAfterFee->text().left(ui->labelCoinControlAfterFee->text().indexOf(" ")).replace("~", ""));
}
// copy label "Bytes" to clipboard
void CoinControlDialog::clipboardBytes()
{
- GUIUtil::setClipboard(ui->labelCoinControlBytes->text());
+ GUIUtil::setClipboard(ui->labelCoinControlBytes->text().replace("~", ""));
}
// copy label "Priority" to clipboard
@@ -320,7 +333,7 @@ void CoinControlDialog::clipboardLowOutput()
// copy label "Change" to clipboard
void CoinControlDialog::clipboardChange()
{
- GUIUtil::setClipboard(ui->labelCoinControlChange->text().left(ui->labelCoinControlChange->text().indexOf(" ")));
+ GUIUtil::setClipboard(ui->labelCoinControlChange->text().left(ui->labelCoinControlChange->text().indexOf(" ")).replace("~", ""));
}
// treeview: sort
@@ -402,26 +415,22 @@ void CoinControlDialog::viewItemChanged(QTreeWidgetItem* item, int column)
}
// return human readable label for priority number
-QString CoinControlDialog::getPriorityLabel(const CTxMemPool& pool, double dPriority)
+QString CoinControlDialog::getPriorityLabel(double dPriority, double mempoolEstimatePriority)
{
- // confirmations -> textual description
- typedef std::map<unsigned int, QString> PriorityDescription;
- const static PriorityDescription priorityDescriptions = boost::assign::map_list_of
- (1, tr("highest"))(2, tr("higher"))(3, tr("high"))
- (5, tr("medium-high"))(6, tr("medium"))
- (10, tr("low-medium"))(15, tr("low"))
- (20, tr("lower"));
-
- BOOST_FOREACH(const PriorityDescription::value_type& i, priorityDescriptions)
- {
- double p = mempool.estimatePriority(i.first);
- if (p > 0 && dPriority >= p) return i.second;
- }
- // Note: if mempool hasn't accumulated enough history (estimatePriority
- // returns -1) we're conservative and classify as "lowest"
- if (mempool.estimatePriority(nTxConfirmTarget) <= 0 && AllowFree(dPriority))
- return ">=" + tr("medium");
- return tr("lowest");
+ double dPriorityMedium = mempoolEstimatePriority;
+
+ if (dPriorityMedium <= 0)
+ dPriorityMedium = AllowFreeThreshold(); // not enough data, back to hard-coded
+
+ if (dPriority / 1000000 > dPriorityMedium) return tr("highest");
+ else if (dPriority / 100000 > dPriorityMedium) return tr("higher");
+ else if (dPriority / 10000 > dPriorityMedium) return tr("high");
+ else if (dPriority / 1000 > dPriorityMedium) return tr("medium-high");
+ else if (dPriority > dPriorityMedium) return tr("medium");
+ else if (dPriority * 10 > dPriorityMedium) return tr("low-medium");
+ else if (dPriority * 100 > dPriorityMedium) return tr("low");
+ else if (dPriority * 1000 > dPriorityMedium) return tr("lower");
+ else return tr("lowest");
}
// shows count of locked unspent outputs
@@ -470,6 +479,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
double dPriorityInputs = 0;
unsigned int nQuantity = 0;
int nQuantityUncompressed = 0;
+ bool fAllowFree = false;
vector<COutPoint> vCoinControl;
vector<COutput> vOutputs;
@@ -522,24 +532,22 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
nBytes = nBytesInputs + ((CoinControlDialog::payAmounts.size() > 0 ? CoinControlDialog::payAmounts.size() + 1 : 2) * 34) + 10; // always assume +1 output for change here
// Priority
+ double mempoolEstimatePriority = mempool.estimatePriority(nTxConfirmTarget);
dPriority = dPriorityInputs / (nBytes - nBytesInputs + (nQuantityUncompressed * 29)); // 29 = 180 - 151 (uncompressed public keys are over the limit. max 151 bytes of the input are ignored for priority)
- sPriorityLabel = CoinControlDialog::getPriorityLabel(mempool, dPriority);
+ sPriorityLabel = CoinControlDialog::getPriorityLabel(dPriority, mempoolEstimatePriority);
- // Voluntary Fee
- nPayFee = payTxFee.GetFee(max((unsigned int)1000, nBytes));
+ // Fee
+ nPayFee = CWallet::GetMinimumFee(nBytes, nTxConfirmTarget, mempool);
- // Min Fee
- if (nPayFee == 0)
- {
- nPayFee = CWallet::GetMinimumFee(nBytes, nTxConfirmTarget, mempool);
-
- double dPriorityNeeded = mempool.estimatePriority(nTxConfirmTarget);
- if (dPriorityNeeded <= 0 && !AllowFree(dPriority)) // not enough mempool history: never send free
- dPriorityNeeded = std::numeric_limits<double>::max();
+ // Allow free?
+ double dPriorityNeeded = mempoolEstimatePriority;
+ if (dPriorityNeeded <= 0)
+ dPriorityNeeded = AllowFreeThreshold(); // not enough data, back to hard-coded
+ fAllowFree = (dPriority >= dPriorityNeeded);
- if (nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE && dPriority >= dPriorityNeeded)
+ if (fSendFreeTransactions)
+ if (fAllowFree && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE)
nPayFee = 0;
- }
if (nPayAmount > 0)
{
@@ -595,7 +603,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
l6->setText(sPriorityLabel); // Priority
l7->setText(fDust ? tr("yes") : tr("no")); // Dust
l8->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nChange)); // Change
- if (nPayFee > 0)
+ if (nPayFee > 0 && !(payTxFee.GetFeePerK() > 0 && fPayAtLeastCustomFee && nBytes < 1000))
{
l3->setText("~" + l3->text());
l4->setText("~" + l4->text());
@@ -605,7 +613,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
// turn labels "red"
l5->setStyleSheet((nBytes >= MAX_FREE_TRANSACTION_CREATE_SIZE) ? "color:red;" : "");// Bytes >= 1000
- l6->setStyleSheet((dPriority > 0 && !AllowFree(dPriority)) ? "color:red;" : ""); // Priority < "medium"
+ l6->setStyleSheet((dPriority > 0 && !fAllowFree) ? "color:red;" : ""); // Priority < "medium"
l7->setStyleSheet((fDust) ? "color:red;" : ""); // Dust = "yes"
// tool tips
@@ -620,7 +628,11 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
QString toolTip3 = tr("This label turns red, if any recipient receives an amount smaller than %1.").arg(BitcoinUnits::formatWithUnit(nDisplayUnit, ::minRelayTxFee.GetFee(546)));
// how many satoshis the estimated fee can vary per byte we guess wrong
- double dFeeVary = (double)std::max(CWallet::minTxFee.GetFeePerK(), std::max(payTxFee.GetFeePerK(), mempool.estimateFee(nTxConfirmTarget).GetFeePerK())) / 1000;
+ double dFeeVary;
+ if (payTxFee.GetFeePerK() > 0)
+ dFeeVary = (double)std::max(CWallet::minTxFee.GetFeePerK(), payTxFee.GetFeePerK()) / 1000;
+ else
+ dFeeVary = (double)std::max(CWallet::minTxFee.GetFeePerK(), mempool.estimateFee(nTxConfirmTarget).GetFeePerK()) / 1000;
QString toolTip4 = tr("Can vary +/- %1 satoshi(s) per input.").arg(dFeeVary);
l3->setToolTip(toolTip4);
@@ -656,6 +668,7 @@ void CoinControlDialog::updateView()
QFlags<Qt::ItemFlag> flgTristate = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsTristate;
int nDisplayUnit = model->getOptionsModel()->getDisplayUnit();
+ double mempoolEstimatePriority = mempool.estimatePriority(nTxConfirmTarget);
map<QString, vector<COutput> > mapCoins;
model->listCoins(mapCoins);
@@ -745,7 +758,7 @@ void CoinControlDialog::updateView()
// priority
double dPriority = ((double)out.tx->vout[out.i].nValue / (nInputSize + 78)) * (out.nDepth+1); // 78 = 2 * 34 + 10
- itemOutput->setText(COLUMN_PRIORITY, CoinControlDialog::getPriorityLabel(mempool, dPriority));
+ itemOutput->setText(COLUMN_PRIORITY, CoinControlDialog::getPriorityLabel(dPriority, mempoolEstimatePriority));
itemOutput->setText(COLUMN_PRIORITY_INT64, strPad(QString::number((int64_t)dPriority), 20, " "));
dPrioritySum += (double)out.tx->vout[out.i].nValue * (out.nDepth+1);
nInputSum += nInputSize;
@@ -778,7 +791,7 @@ void CoinControlDialog::updateView()
itemWalletAddress->setText(COLUMN_CHECKBOX, "(" + QString::number(nChildren) + ")");
itemWalletAddress->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, nSum));
itemWalletAddress->setText(COLUMN_AMOUNT_INT64, strPad(QString::number(nSum), 15, " "));
- itemWalletAddress->setText(COLUMN_PRIORITY, CoinControlDialog::getPriorityLabel(mempool, dPrioritySum));
+ itemWalletAddress->setText(COLUMN_PRIORITY, CoinControlDialog::getPriorityLabel(dPrioritySum, mempoolEstimatePriority));
itemWalletAddress->setText(COLUMN_PRIORITY_INT64, strPad(QString::number((int64_t)dPrioritySum), 20, " "));
}
}
diff --git a/src/qt/coincontroldialog.h b/src/qt/coincontroldialog.h
index cc388d626e..bd4f5d7f1e 100644
--- a/src/qt/coincontroldialog.h
+++ b/src/qt/coincontroldialog.h
@@ -37,7 +37,7 @@ public:
// static because also called from sendcoinsdialog
static void updateLabels(WalletModel*, QDialog*);
- static QString getPriorityLabel(const CTxMemPool& pool, double);
+ static QString getPriorityLabel(double dPriority, double mempoolEstimatePriority);
static QList<CAmount> payAmounts;
static CCoinControl *coinControl;
diff --git a/src/qt/forms/optionsdialog.ui b/src/qt/forms/optionsdialog.ui
index 9d094c1a73..3446cf5c33 100644
--- a/src/qt/forms/optionsdialog.ui
+++ b/src/qt/forms/optionsdialog.ui
@@ -138,65 +138,6 @@
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_Wallet">
<item>
- <widget class="QLabel" name="transactionFeeInfoLabel">
- <property name="text">
- <string>Optional transaction fee per kB that helps make sure your transactions are processed quickly. Most transactions are 1 kB.</string>
- </property>
- <property name="textFormat">
- <enum>Qt::PlainText</enum>
- </property>
- <property name="wordWrap">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item>
- <layout class="QHBoxLayout" name="horizontalLayout_1_Wallet">
- <item>
- <widget class="QLabel" name="transactionFeeLabel">
- <property name="text">
- <string>Pay transaction &amp;fee</string>
- </property>
- <property name="textFormat">
- <enum>Qt::PlainText</enum>
- </property>
- <property name="buddy">
- <cstring>transactionFee</cstring>
- </property>
- </widget>
- </item>
- <item>
- <widget class="BitcoinAmountField" name="transactionFee"/>
- </item>
- <item>
- <spacer name="horizontalSpacer_1_Wallet">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- </layout>
- </item>
- <item>
- <spacer name="verticalSpacer_Wallet">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- <item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Expert</string>
@@ -225,6 +166,19 @@
</layout>
</widget>
</item>
+ <item>
+ <spacer name="verticalSpacer_Wallet">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
</layout>
</widget>
<widget class="QWidget" name="tabNetwork">
@@ -633,12 +587,6 @@
</widget>
<customwidgets>
<customwidget>
- <class>BitcoinAmountField</class>
- <extends>QLineEdit</extends>
- <header>bitcoinamountfield.h</header>
- <container>1</container>
- </customwidget>
- <customwidget>
<class>QValidatedLineEdit</class>
<extends>QLineEdit</extends>
<header>qvalidatedlineedit.h</header>
diff --git a/src/qt/forms/sendcoinsdialog.ui b/src/qt/forms/sendcoinsdialog.ui
index dce7f4ce4c..0bf04c75eb 100644
--- a/src/qt/forms/sendcoinsdialog.ui
+++ b/src/qt/forms/sendcoinsdialog.ui
@@ -7,13 +7,13 @@
<x>0</x>
<y>0</y>
<width>850</width>
- <height>400</height>
+ <height>526</height>
</rect>
</property>
<property name="windowTitle">
<string>Send Coins</string>
</property>
- <layout class="QVBoxLayout" name="verticalLayout" stretch="0,1,0">
+ <layout class="QVBoxLayout" name="verticalLayout" stretch="0,1,0,0">
<property name="bottomMargin">
<number>8</number>
</property>
@@ -617,7 +617,7 @@
<x>0</x>
<y>0</y>
<width>830</width>
- <height>178</height>
+ <height>68</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,1">
@@ -658,6 +658,590 @@
</widget>
</item>
<item>
+ <widget class="QFrame" name="frameFee">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>16777215</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::StyledPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Sunken</enum>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayoutFee1">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayoutFee2" stretch="0,0,0">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>10</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayoutFee1">
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayoutFee7">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <item>
+ <spacer name="verticalSpacerSmartFee">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>1</width>
+ <height>4</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayoutSmartFee">
+ <property name="spacing">
+ <number>10</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="labelFeeHeadline">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Maximum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">font-weight:bold;</string>
+ </property>
+ <property name="text">
+ <string>Transaction Fee:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="labelFeeMinimized">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="buttonChooseFee">
+ <property name="text">
+ <string>Choose...</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="buttonMinimizeFee">
+ <property name="toolTip">
+ <string>collapse fee-settings</string>
+ </property>
+ <property name="text">
+ <string>Minimize</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_5">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>1</width>
+ <height>1</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_4">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QFrame" name="frameFeeSelection">
+ <layout class="QVBoxLayout" name="verticalLayoutFee12">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <layout class="QGridLayout" name="gridLayoutFee">
+ <property name="topMargin">
+ <number>10</number>
+ </property>
+ <property name="bottomMargin">
+ <number>4</number>
+ </property>
+ <property name="horizontalSpacing">
+ <number>10</number>
+ </property>
+ <property name="verticalSpacing">
+ <number>4</number>
+ </property>
+ <item row="1" column="1">
+ <layout class="QVBoxLayout" name="verticalLayoutFee8">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayoutFee13">
+ <item>
+ <widget class="QRadioButton" name="radioCustomPerKilobyte">
+ <property name="toolTip">
+ <string>If the custom fee is set to 1000 satoshis and the transaction is only 250 bytes, then &quot;per kilobyte&quot; only pays 250 satoshis in fee, while &quot;at least&quot; pays 1000 satoshis. For transactions bigger than a kilobyte both pay by kilobyte.</string>
+ </property>
+ <property name="text">
+ <string>per kilobyte</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <attribute name="buttonGroup">
+ <string notr="true">groupCustomFee</string>
+ </attribute>
+ </widget>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="radioCustomAtLeast">
+ <property name="toolTip">
+ <string>If the custom fee is set to 1000 satoshis and the transaction is only 250 bytes, then &quot;per kilobyte&quot; only pays 250 satoshis in fee, while &quot;total at least&quot; pays 1000 satoshis. For transactions bigger than a kilobyte both pay by kilobyte.</string>
+ </property>
+ <property name="text">
+ <string>total at least</string>
+ </property>
+ <attribute name="buttonGroup">
+ <string notr="true">groupCustomFee</string>
+ </attribute>
+ </widget>
+ </item>
+ <item>
+ <widget class="BitcoinAmountField" name="customFee"/>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_6">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>1</width>
+ <height>1</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayoutFee8">
+ <item>
+ <widget class="QCheckBox" name="checkBoxMinimumFee">
+ <property name="toolTip">
+ <string>Paying only the minimum fee is just fine as long as there is less transaction volume than space in the blocks. But be aware that this can end up in a never confirming transaction once there is more demand for bitcoin transactions than the network can process.</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="labelMinFeeWarning">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="toolTip">
+ <string>Paying only the minimum fee is just fine as long as there is less transaction volume than space in the blocks. But be aware that this can end up in a never confirming transaction once there is more demand for bitcoin transactions than the network can process.</string>
+ </property>
+ <property name="text">
+ <string>(read the tooltip)</string>
+ </property>
+ <property name="margin">
+ <number>5</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>1</width>
+ <height>1</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ <item row="0" column="0">
+ <layout class="QVBoxLayout" name="verticalLayoutFee4" stretch="0,1">
+ <item>
+ <widget class="QRadioButton" name="radioSmartFee">
+ <property name="text">
+ <string>Recommended:</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <attribute name="buttonGroup">
+ <string notr="true">groupFee</string>
+ </attribute>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>1</width>
+ <height>1</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item row="1" column="0">
+ <layout class="QVBoxLayout" name="verticalLayoutFee9" stretch="0,1">
+ <item>
+ <widget class="QRadioButton" name="radioCustomFee">
+ <property name="text">
+ <string>Custom:</string>
+ </property>
+ <attribute name="buttonGroup">
+ <string notr="true">groupFee</string>
+ </attribute>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_6">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>1</width>
+ <height>1</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item row="0" column="1">
+ <layout class="QVBoxLayout" name="verticalLayoutFee3" stretch="0,0,1">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="topMargin">
+ <number>2</number>
+ </property>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayoutFee12">
+ <item>
+ <widget class="QLabel" name="labelSmartFee">
+ <property name="text">
+ <string/>
+ </property>
+ <property name="margin">
+ <number>2</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="labelFeeEstimation">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="labelSmartFee2">
+ <property name="text">
+ <string>(Smart fee not initialized yet. This usually takes a few blocks...)</string>
+ </property>
+ <property name="margin">
+ <number>2</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_5">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>1</width>
+ <height>1</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayoutFee9">
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayoutFee6">
+ <item>
+ <widget class="QLabel" name="labelSmartFee3">
+ <property name="text">
+ <string>Confirmation time:</string>
+ </property>
+ <property name="margin">
+ <number>2</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>1</width>
+ <height>1</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="verticalLayoutFee5">
+ <property name="rightMargin">
+ <number>30</number>
+ </property>
+ <item>
+ <widget class="QSlider" name="sliderSmartFee">
+ <property name="minimum">
+ <number>0</number>
+ </property>
+ <property name="maximum">
+ <number>24</number>
+ </property>
+ <property name="pageStep">
+ <number>1</number>
+ </property>
+ <property name="value">
+ <number>0</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="invertedAppearance">
+ <bool>false</bool>
+ </property>
+ <property name="invertedControls">
+ <bool>false</bool>
+ </property>
+ <property name="tickPosition">
+ <enum>QSlider::NoTicks</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayoutFee10">
+ <item>
+ <widget class="QLabel" name="labelSmartFeeNormal">
+ <property name="text">
+ <string>normal</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_3">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QLabel" name="labelSmartFeeFast">
+ <property name="text">
+ <string>fast</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="verticalSpacer_4">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>1</width>
+ <height>1</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayoutFee5" stretch="0,0,0">
+ <property name="spacing">
+ <number>8</number>
+ </property>
+ <property name="bottomMargin">
+ <number>4</number>
+ </property>
+ <item>
+ <widget class="QCheckBox" name="checkBoxFreeTx">
+ <property name="text">
+ <string>Send as zero-fee transaction if possible</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="labelFreeTx">
+ <property name="text">
+ <string>(confirmation may take longer)</string>
+ </property>
+ <property name="margin">
+ <number>5</number>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacerFee5">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>1</width>
+ <height>1</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <spacer name="verticalSpacerFee2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>1</width>
+ <height>1</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacerFee">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>800</width>
+ <height>1</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="sendButton">
@@ -787,9 +1371,19 @@
<extends>QLineEdit</extends>
<header>qvalidatedlineedit.h</header>
</customwidget>
+ <customwidget>
+ <class>BitcoinAmountField</class>
+ <extends>QLineEdit</extends>
+ <header>bitcoinamountfield.h</header>
+ <container>1</container>
+ </customwidget>
</customwidgets>
<resources>
<include location="../bitcoin.qrc"/>
</resources>
<connections/>
+ <buttongroups>
+ <buttongroup name="groupFee"/>
+ <buttongroup name="groupCustomFee"/>
+ </buttongroups>
</ui>
diff --git a/src/qt/optionsdialog.cpp b/src/qt/optionsdialog.cpp
index f5a0759c92..069080219e 100644
--- a/src/qt/optionsdialog.cpp
+++ b/src/qt/optionsdialog.cpp
@@ -105,9 +105,6 @@ OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) :
#endif
ui->unit->setModel(new BitcoinUnits(this));
-#ifdef ENABLE_WALLET
- ui->transactionFee->setSingleStep(CWallet::minTxFee.GetFeePerK());
-#endif
/* Widget-to-option mapper */
mapper = new QDataWidgetMapper(this);
@@ -139,16 +136,11 @@ void OptionsDialog::setModel(OptionsModel *model)
strLabel = tr("none");
ui->overriddenByCommandLineLabel->setText(strLabel);
- connect(model, SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit()));
-
mapper->setModel(model);
setMapper();
mapper->toFirst();
}
- /* update the display unit, to not use the default ("BTC") */
- updateDisplayUnit();
-
/* warn when one of the following settings changes by user action (placed here so init via mapper doesn't trigger them) */
/* Main */
@@ -172,7 +164,6 @@ void OptionsDialog::setMapper()
mapper->addMapping(ui->databaseCache, OptionsModel::DatabaseCache);
/* Wallet */
- mapper->addMapping(ui->transactionFee, OptionsModel::Fee);
mapper->addMapping(ui->spendZeroConfChange, OptionsModel::SpendZeroConfChange);
mapper->addMapping(ui->coinControlFeatures, OptionsModel::CoinControlFeatures);
@@ -264,15 +255,6 @@ void OptionsDialog::clearStatusLabel()
ui->statusLabel->clear();
}
-void OptionsDialog::updateDisplayUnit()
-{
- if(model)
- {
- /* Update transactionFee with the current unit */
- ui->transactionFee->setDisplayUnit(model->getDisplayUnit());
- }
-}
-
void OptionsDialog::doProxyIpChecks(QValidatedLineEdit *pUiProxyIp, int nProxyPort)
{
Q_UNUSED(nProxyPort);
diff --git a/src/qt/optionsdialog.h b/src/qt/optionsdialog.h
index 794a39590c..511719f53c 100644
--- a/src/qt/optionsdialog.h
+++ b/src/qt/optionsdialog.h
@@ -46,7 +46,6 @@ private slots:
void showRestartWarning(bool fPersistent = false);
void clearStatusLabel();
- void updateDisplayUnit();
void doProxyIpChecks(QValidatedLineEdit *pUiProxyIp, int nProxyPort);
signals:
diff --git a/src/qt/optionsmodel.cpp b/src/qt/optionsmodel.cpp
index c941ebd4ca..7054509fe6 100644
--- a/src/qt/optionsmodel.cpp
+++ b/src/qt/optionsmodel.cpp
@@ -90,12 +90,6 @@ void OptionsModel::Init()
// Wallet
#ifdef ENABLE_WALLET
- if (!settings.contains("nTransactionFee"))
- settings.setValue("nTransactionFee", (qint64)DEFAULT_TRANSACTION_FEE);
- payTxFee = CFeeRate(settings.value("nTransactionFee").toLongLong()); // if -paytxfee is set, this will be overridden later in init.cpp
- if (mapArgs.count("-paytxfee"))
- addOverriddenOption("-paytxfee");
-
if (!settings.contains("bSpendZeroConfChange"))
settings.setValue("bSpendZeroConfChange", true);
if (!SoftSetBoolArg("-spendzeroconfchange", settings.value("bSpendZeroConfChange").toBool()))
@@ -185,16 +179,6 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const
}
#ifdef ENABLE_WALLET
- case Fee: {
- // Attention: Init() is called before payTxFee is set in AppInit2()!
- // To ensure we can change the fee on-the-fly update our QSetting when
- // opening OptionsDialog, which queries Fee via the mapper.
- if (!(payTxFee == CFeeRate(settings.value("nTransactionFee").toLongLong(), 1000)))
- settings.setValue("nTransactionFee", (qint64)payTxFee.GetFeePerK());
- // Todo: Consider to revert back to use just payTxFee here, if we don't want
- // -paytxfee to update our QSettings!
- return settings.value("nTransactionFee");
- }
case SpendZeroConfChange:
return settings.value("bSpendZeroConfChange");
#endif
@@ -276,14 +260,6 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in
}
break;
#ifdef ENABLE_WALLET
- case Fee: { // core option - can be changed on-the-fly
- // Todo: Add is valid check and warn via message, if not
- CAmount nTransactionFee(value.toLongLong());
- payTxFee = CFeeRate(nTransactionFee, 1000);
- settings.setValue("nTransactionFee", qint64(nTransactionFee));
- emit transactionFeeChanged(nTransactionFee);
- break;
- }
case SpendZeroConfChange:
if (settings.value("bSpendZeroConfChange") != value) {
settings.setValue("bSpendZeroConfChange", value);
diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h
index e2dc067edb..84fd49a7b8 100644
--- a/src/qt/optionsmodel.h
+++ b/src/qt/optionsmodel.h
@@ -34,7 +34,6 @@ public:
ProxyUse, // bool
ProxyIP, // QString
ProxyPort, // int
- Fee, // qint64
DisplayUnit, // BitcoinUnits::Unit
ThirdPartyTxUrls, // QString
Language, // QString
@@ -84,7 +83,6 @@ private:
signals:
void displayUnitChanged(int unit);
- void transactionFeeChanged(const CAmount&);
void coinControlFeaturesChanged(bool);
};
diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp
index ce94131cce..ff39829b95 100644
--- a/src/qt/sendcoinsdialog.cpp
+++ b/src/qt/sendcoinsdialog.cpp
@@ -7,10 +7,12 @@
#include "addresstablemodel.h"
#include "bitcoinunits.h"
+#include "clientmodel.h"
#include "coincontroldialog.h"
#include "guiutil.h"
#include "optionsmodel.h"
#include "sendcoinsentry.h"
+#include "wallet.h"
#include "walletmodel.h"
#include "base58.h"
@@ -19,6 +21,7 @@
#include <QMessageBox>
#include <QScrollBar>
+#include <QSettings>
#include <QTextDocument>
SendCoinsDialog::SendCoinsDialog(QWidget *parent) :
@@ -72,9 +75,46 @@ SendCoinsDialog::SendCoinsDialog(QWidget *parent) :
ui->labelCoinControlLowOutput->addAction(clipboardLowOutputAction);
ui->labelCoinControlChange->addAction(clipboardChangeAction);
+ // init transaction fee section
+ QSettings settings;
+ if (!settings.contains("fFeeSectionMinimized"))
+ settings.setValue("fFeeSectionMinimized", true);
+ if (!settings.contains("nFeeRadio") && settings.contains("nTransactionFee") && settings.value("nTransactionFee").toLongLong() > 0) // compatibility
+ settings.setValue("nFeeRadio", 1); // custom
+ if (!settings.contains("nFeeRadio"))
+ settings.setValue("nFeeRadio", 0); // recommended
+ if (!settings.contains("nCustomFeeRadio") && settings.contains("nTransactionFee") && settings.value("nTransactionFee").toLongLong() > 0) // compatibility
+ settings.setValue("nCustomFeeRadio", 1); // total at least
+ if (!settings.contains("nCustomFeeRadio"))
+ settings.setValue("nCustomFeeRadio", 0); // per kilobyte
+ if (!settings.contains("nSmartFeeSliderPosition"))
+ settings.setValue("nSmartFeeSliderPosition", 0);
+ if (!settings.contains("nTransactionFee"))
+ settings.setValue("nTransactionFee", (qint64)DEFAULT_TRANSACTION_FEE);
+ if (!settings.contains("fPayOnlyMinFee"))
+ settings.setValue("fPayOnlyMinFee", false);
+ if (!settings.contains("fSendFreeTransactions"))
+ settings.setValue("fSendFreeTransactions", false);
+ ui->groupFee->setId(ui->radioSmartFee, 0);
+ ui->groupFee->setId(ui->radioCustomFee, 1);
+ ui->groupFee->button((int)std::max(0, std::min(1, settings.value("nFeeRadio").toInt())))->setChecked(true);
+ ui->groupCustomFee->setId(ui->radioCustomPerKilobyte, 0);
+ ui->groupCustomFee->setId(ui->radioCustomAtLeast, 1);
+ ui->groupCustomFee->button((int)std::max(0, std::min(1, settings.value("nCustomFeeRadio").toInt())))->setChecked(true);
+ ui->sliderSmartFee->setValue(settings.value("nSmartFeeSliderPosition").toInt());
+ ui->customFee->setValue(settings.value("nTransactionFee").toLongLong());
+ ui->checkBoxMinimumFee->setChecked(settings.value("fPayOnlyMinFee").toBool());
+ ui->checkBoxFreeTx->setChecked(settings.value("fSendFreeTransactions").toBool());
+ minimizeFeeSection(settings.value("fFeeSectionMinimized").toBool());
+
fNewRecipientAllowed = true;
}
+void SendCoinsDialog::setClientModel(ClientModel *clientModel)
+{
+ this->clientModel = clientModel;
+}
+
void SendCoinsDialog::setModel(WalletModel *model)
{
this->model = model;
@@ -94,18 +134,51 @@ void SendCoinsDialog::setModel(WalletModel *model)
model->getWatchBalance(), model->getWatchUnconfirmedBalance(), model->getWatchImmatureBalance());
connect(model, SIGNAL(balanceChanged(CAmount,CAmount,CAmount,CAmount,CAmount,CAmount)), this, SLOT(setBalance(CAmount,CAmount,CAmount,CAmount,CAmount,CAmount)));
connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit()));
+ updateDisplayUnit();
// Coin Control
connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(coinControlUpdateLabels()));
connect(model->getOptionsModel(), SIGNAL(coinControlFeaturesChanged(bool)), this, SLOT(coinControlFeatureChanged(bool)));
- connect(model->getOptionsModel(), SIGNAL(transactionFeeChanged(CAmount)), this, SLOT(coinControlUpdateLabels()));
ui->frameCoinControl->setVisible(model->getOptionsModel()->getCoinControlFeatures());
coinControlUpdateLabels();
+
+ // fee section
+ connect(clientModel, SIGNAL(numBlocksChanged(int)), this, SLOT(updateSmartFeeLabel()));
+ connect(ui->sliderSmartFee, SIGNAL(valueChanged(int)), this, SLOT(updateSmartFeeLabel()));
+ connect(ui->sliderSmartFee, SIGNAL(valueChanged(int)), this, SLOT(updateGlobalFeeVariables()));
+ connect(ui->sliderSmartFee, SIGNAL(valueChanged(int)), this, SLOT(coinControlUpdateLabels()));
+ connect(ui->groupFee, SIGNAL(buttonClicked(int)), this, SLOT(updateFeeSectionControls()));
+ connect(ui->groupFee, SIGNAL(buttonClicked(int)), this, SLOT(updateGlobalFeeVariables()));
+ connect(ui->groupFee, SIGNAL(buttonClicked(int)), this, SLOT(coinControlUpdateLabels()));
+ connect(ui->groupCustomFee, SIGNAL(buttonClicked(int)), this, SLOT(updateGlobalFeeVariables()));
+ connect(ui->groupCustomFee, SIGNAL(buttonClicked(int)), this, SLOT(coinControlUpdateLabels()));
+ connect(ui->customFee, SIGNAL(valueChanged()), this, SLOT(updateGlobalFeeVariables()));
+ connect(ui->customFee, SIGNAL(valueChanged()), this, SLOT(coinControlUpdateLabels()));
+ connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(setMinimumFee()));
+ connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(updateFeeSectionControls()));
+ connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(updateGlobalFeeVariables()));
+ connect(ui->checkBoxMinimumFee, SIGNAL(stateChanged(int)), this, SLOT(coinControlUpdateLabels()));
+ connect(ui->checkBoxFreeTx, SIGNAL(stateChanged(int)), this, SLOT(updateGlobalFeeVariables()));
+ connect(ui->checkBoxFreeTx, SIGNAL(stateChanged(int)), this, SLOT(coinControlUpdateLabels()));
+ ui->customFee->setSingleStep(CWallet::minTxFee.GetFeePerK());
+ updateFeeSectionControls();
+ updateMinFeeLabel();
+ updateSmartFeeLabel();
+ updateGlobalFeeVariables();
}
}
SendCoinsDialog::~SendCoinsDialog()
{
+ QSettings settings;
+ settings.setValue("fFeeSectionMinimized", fFeeMinimized);
+ settings.setValue("nFeeRadio", ui->groupFee->checkedId());
+ settings.setValue("nCustomFeeRadio", ui->groupCustomFee->checkedId());
+ settings.setValue("nSmartFeeSliderPosition", ui->sliderSmartFee->value());
+ settings.setValue("nTransactionFee", (qint64)ui->customFee->value());
+ settings.setValue("fPayOnlyMinFee", ui->checkBoxMinimumFee->isChecked());
+ settings.setValue("fSendFreeTransactions", ui->checkBoxFreeTx->isChecked());
+
delete ui;
}
@@ -214,6 +287,9 @@ void SendCoinsDialog::on_sendButton_clicked()
questionString.append(BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), txFee));
questionString.append("</span> ");
questionString.append(tr("added as transaction fee"));
+
+ // append transaction size
+ questionString.append(" (" + QString::number((double)currentTransaction.getTransactionSize() / 1000) + " kB)");
}
// add total amount in all subdivision units
@@ -402,6 +478,9 @@ void SendCoinsDialog::setBalance(const CAmount& balance, const CAmount& unconfir
void SendCoinsDialog::updateDisplayUnit()
{
setBalance(model->getBalance(), 0, 0, 0, 0, 0);
+ ui->customFee->setDisplayUnit(model->getOptionsModel()->getDisplayUnit());
+ updateMinFeeLabel();
+ updateSmartFeeLabel();
}
void SendCoinsDialog::processSendCoinsReturn(const WalletModel::SendCoinsReturn &sendCoinsReturn, const QString &msgArg)
@@ -438,6 +517,9 @@ void SendCoinsDialog::processSendCoinsReturn(const WalletModel::SendCoinsReturn
msgParams.first = tr("The transaction was rejected! This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here.");
msgParams.second = CClientUIInterface::MSG_ERROR;
break;
+ case WalletModel::InsaneFee:
+ msgParams.first = tr("A fee higher than %1 is considered an insanely high fee.").arg(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), 10000000));
+ break;
// included to prevent a compiler warning.
case WalletModel::OK:
default:
@@ -447,6 +529,110 @@ void SendCoinsDialog::processSendCoinsReturn(const WalletModel::SendCoinsReturn
emit message(tr("Send Coins"), msgParams.first, msgParams.second);
}
+void SendCoinsDialog::minimizeFeeSection(bool fMinimize)
+{
+ ui->labelFeeMinimized->setVisible(fMinimize);
+ ui->buttonChooseFee ->setVisible(fMinimize);
+ ui->buttonMinimizeFee->setVisible(!fMinimize);
+ ui->frameFeeSelection->setVisible(!fMinimize);
+ ui->horizontalLayoutSmartFee->setContentsMargins(0, (fMinimize ? 0 : 6), 0, 0);
+ fFeeMinimized = fMinimize;
+}
+
+void SendCoinsDialog::on_buttonChooseFee_clicked()
+{
+ minimizeFeeSection(false);
+}
+
+void SendCoinsDialog::on_buttonMinimizeFee_clicked()
+{
+ updateFeeMinimizedLabel();
+ minimizeFeeSection(true);
+}
+
+void SendCoinsDialog::setMinimumFee()
+{
+ ui->radioCustomPerKilobyte->setChecked(true);
+ ui->customFee->setValue(CWallet::minTxFee.GetFeePerK());
+}
+
+void SendCoinsDialog::updateFeeSectionControls()
+{
+ ui->sliderSmartFee ->setEnabled(ui->radioSmartFee->isChecked());
+ ui->labelSmartFee ->setEnabled(ui->radioSmartFee->isChecked());
+ ui->labelSmartFee2 ->setEnabled(ui->radioSmartFee->isChecked());
+ ui->labelSmartFee3 ->setEnabled(ui->radioSmartFee->isChecked());
+ ui->labelFeeEstimation ->setEnabled(ui->radioSmartFee->isChecked());
+ ui->labelSmartFeeNormal ->setEnabled(ui->radioSmartFee->isChecked());
+ ui->labelSmartFeeFast ->setEnabled(ui->radioSmartFee->isChecked());
+ ui->checkBoxMinimumFee ->setEnabled(ui->radioCustomFee->isChecked());
+ ui->labelMinFeeWarning ->setEnabled(ui->radioCustomFee->isChecked());
+ ui->radioCustomPerKilobyte ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked());
+ ui->radioCustomAtLeast ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked());
+ ui->customFee ->setEnabled(ui->radioCustomFee->isChecked() && !ui->checkBoxMinimumFee->isChecked());
+}
+
+void SendCoinsDialog::updateGlobalFeeVariables()
+{
+ if (ui->radioSmartFee->isChecked())
+ {
+ nTxConfirmTarget = (int)25 - (int)std::max(0, std::min(24, ui->sliderSmartFee->value()));
+ payTxFee = CFeeRate(0);
+ }
+ else
+ {
+ nTxConfirmTarget = 25;
+ payTxFee = CFeeRate(ui->customFee->value());
+ fPayAtLeastCustomFee = ui->radioCustomAtLeast->isChecked();
+ }
+
+ fSendFreeTransactions = ui->checkBoxFreeTx->isChecked();
+}
+
+void SendCoinsDialog::updateFeeMinimizedLabel()
+{
+ if(!model || !model->getOptionsModel())
+ return;
+
+ if (ui->radioSmartFee->isChecked())
+ ui->labelFeeMinimized->setText(ui->labelSmartFee->text());
+ else {
+ ui->labelFeeMinimized->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), ui->customFee->value()) +
+ ((ui->radioCustomPerKilobyte->isChecked()) ? "/kB" : ""));
+ }
+}
+
+void SendCoinsDialog::updateMinFeeLabel()
+{
+ if (model && model->getOptionsModel())
+ ui->checkBoxMinimumFee->setText(tr("Pay only the minimum fee of %1").arg(
+ BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), CWallet::minTxFee.GetFeePerK()) + "/kB")
+ );
+}
+
+void SendCoinsDialog::updateSmartFeeLabel()
+{
+ if(!model || !model->getOptionsModel())
+ return;
+
+ int nBlocksToConfirm = (int)25 - (int)std::max(0, std::min(24, ui->sliderSmartFee->value()));
+ CFeeRate feeRate = mempool.estimateFee(nBlocksToConfirm);
+ if (feeRate <= CFeeRate(0)) // not enough data => minfee
+ {
+ ui->labelSmartFee->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), CWallet::minTxFee.GetFeePerK()) + "/kB");
+ ui->labelSmartFee2->show(); // (Smart fee not initialized yet. This usually takes a few blocks...)
+ ui->labelFeeEstimation->setText("");
+ }
+ else
+ {
+ ui->labelSmartFee->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), feeRate.GetFeePerK()) + "/kB");
+ ui->labelSmartFee2->hide();
+ ui->labelFeeEstimation->setText(tr("Estimated to begin confirmation within %1 block(s).").arg(nBlocksToConfirm));
+ }
+
+ updateFeeMinimizedLabel();
+}
+
// Coin Control: copy label "Quantity" to clipboard
void SendCoinsDialog::coinControlClipboardQuantity()
{
@@ -462,19 +648,19 @@ void SendCoinsDialog::coinControlClipboardAmount()
// Coin Control: copy label "Fee" to clipboard
void SendCoinsDialog::coinControlClipboardFee()
{
- GUIUtil::setClipboard(ui->labelCoinControlFee->text().left(ui->labelCoinControlFee->text().indexOf(" ")));
+ GUIUtil::setClipboard(ui->labelCoinControlFee->text().left(ui->labelCoinControlFee->text().indexOf(" ")).replace("~", ""));
}
// Coin Control: copy label "After fee" to clipboard
void SendCoinsDialog::coinControlClipboardAfterFee()
{
- GUIUtil::setClipboard(ui->labelCoinControlAfterFee->text().left(ui->labelCoinControlAfterFee->text().indexOf(" ")));
+ GUIUtil::setClipboard(ui->labelCoinControlAfterFee->text().left(ui->labelCoinControlAfterFee->text().indexOf(" ")).replace("~", ""));
}
// Coin Control: copy label "Bytes" to clipboard
void SendCoinsDialog::coinControlClipboardBytes()
{
- GUIUtil::setClipboard(ui->labelCoinControlBytes->text());
+ GUIUtil::setClipboard(ui->labelCoinControlBytes->text().replace("~", ""));
}
// Coin Control: copy label "Priority" to clipboard
@@ -492,7 +678,7 @@ void SendCoinsDialog::coinControlClipboardLowOutput()
// Coin Control: copy label "Change" to clipboard
void SendCoinsDialog::coinControlClipboardChange()
{
- GUIUtil::setClipboard(ui->labelCoinControlChange->text().left(ui->labelCoinControlChange->text().indexOf(" ")));
+ GUIUtil::setClipboard(ui->labelCoinControlChange->text().left(ui->labelCoinControlChange->text().indexOf(" ")).replace("~", ""));
}
// Coin Control: settings menu - coin control enabled/disabled by user
diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h
index eec661cbd0..15d39782a1 100644
--- a/src/qt/sendcoinsdialog.h
+++ b/src/qt/sendcoinsdialog.h
@@ -10,6 +10,7 @@
#include <QDialog>
#include <QString>
+class ClientModel;
class OptionsModel;
class SendCoinsEntry;
class SendCoinsRecipient;
@@ -31,6 +32,7 @@ public:
explicit SendCoinsDialog(QWidget *parent = 0);
~SendCoinsDialog();
+ void setClientModel(ClientModel *clientModel);
void setModel(WalletModel *model);
/** Set up the tab chain manually, as Qt messes up the tab chain by default in some cases (issue https://bugreports.qt-project.org/browse/QTBUG-10907).
@@ -52,16 +54,22 @@ public slots:
private:
Ui::SendCoinsDialog *ui;
+ ClientModel *clientModel;
WalletModel *model;
bool fNewRecipientAllowed;
+ bool fFeeMinimized;
// Process WalletModel::SendCoinsReturn and generate a pair consisting
// of a message and message flags for use in emit message().
// Additional parameter msgArg can be used via .arg(msgArg).
void processSendCoinsReturn(const WalletModel::SendCoinsReturn &sendCoinsReturn, const QString &msgArg = QString());
+ void minimizeFeeSection(bool fMinimize);
+ void updateFeeMinimizedLabel();
private slots:
void on_sendButton_clicked();
+ void on_buttonChooseFee_clicked();
+ void on_buttonMinimizeFee_clicked();
void removeEntry(SendCoinsEntry* entry);
void updateDisplayUnit();
void coinControlFeatureChanged(bool);
@@ -77,6 +85,11 @@ private slots:
void coinControlClipboardPriority();
void coinControlClipboardLowOutput();
void coinControlClipboardChange();
+ void setMinimumFee();
+ void updateFeeSectionControls();
+ void updateMinFeeLabel();
+ void updateSmartFeeLabel();
+ void updateGlobalFeeVariables();
signals:
// Fired when a message should be reported to the user
diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp
index f7b1552f3e..b20465794d 100644
--- a/src/qt/walletmodel.cpp
+++ b/src/qt/walletmodel.cpp
@@ -277,6 +277,10 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
CClientUIInterface::MSG_ERROR);
return TransactionCreationFailed;
}
+
+ // reject insane fee > 0.1 bitcoin
+ if (nFeeRequired > 10000000)
+ return InsaneFee;
}
return SendCoinsReturn(OK);
diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h
index 0c6077963d..d7e391f8d1 100644
--- a/src/qt/walletmodel.h
+++ b/src/qt/walletmodel.h
@@ -110,7 +110,8 @@ public:
AmountWithFeeExceedsBalance,
DuplicateAddress,
TransactionCreationFailed, // Error returned when wallet is still locked
- TransactionCommitFailed
+ TransactionCommitFailed,
+ InsaneFee
};
enum EncryptionStatus
diff --git a/src/qt/walletmodeltransaction.cpp b/src/qt/walletmodeltransaction.cpp
index ddd2d09bb5..e9e20c7d51 100644
--- a/src/qt/walletmodeltransaction.cpp
+++ b/src/qt/walletmodeltransaction.cpp
@@ -31,6 +31,11 @@ CWalletTx *WalletModelTransaction::getTransaction()
return walletTransaction;
}
+unsigned int WalletModelTransaction::getTransactionSize()
+{
+ return (!walletTransaction ? 0 : (::GetSerializeSize(*(CTransaction*)walletTransaction, SER_NETWORK, PROTOCOL_VERSION)));
+}
+
CAmount WalletModelTransaction::getTransactionFee()
{
return fee;
diff --git a/src/qt/walletmodeltransaction.h b/src/qt/walletmodeltransaction.h
index a880384ed6..4272529abe 100644
--- a/src/qt/walletmodeltransaction.h
+++ b/src/qt/walletmodeltransaction.h
@@ -25,6 +25,7 @@ public:
QList<SendCoinsRecipient> getRecipients();
CWalletTx *getTransaction();
+ unsigned int getTransactionSize();
void setTransactionFee(const CAmount& newFee);
CAmount getTransactionFee();
diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp
index 3b8fdd7e5f..9bab180108 100644
--- a/src/qt/walletview.cpp
+++ b/src/qt/walletview.cpp
@@ -101,6 +101,7 @@ void WalletView::setClientModel(ClientModel *clientModel)
this->clientModel = clientModel;
overviewPage->setClientModel(clientModel);
+ sendCoinsPage->setClientModel(clientModel);
}
void WalletView::setWalletModel(WalletModel *walletModel)
diff --git a/src/txmempool.h b/src/txmempool.h
index 0d3c8bba6a..e68b218154 100644
--- a/src/txmempool.h
+++ b/src/txmempool.h
@@ -15,11 +15,16 @@
class CAutoFile;
+inline double AllowFreeThreshold()
+{
+ return COIN * 144 / 250;
+}
+
inline bool AllowFree(double dPriority)
{
// Large (in bytes) low-priority (new, small-coin) transactions
// need a fee.
- return dPriority > COIN * 144 / 250;
+ return dPriority > AllowFreeThreshold();
}
/** Fake height value used in CCoins to signify they are only in the memory pool (since 0.8) */
diff --git a/src/wallet.cpp b/src/wallet.cpp
index ec439c5aad..5aea9881cd 100644
--- a/src/wallet.cpp
+++ b/src/wallet.cpp
@@ -28,6 +28,8 @@ using namespace std;
CFeeRate payTxFee(DEFAULT_TRANSACTION_FEE);
unsigned int nTxConfirmTarget = 1;
bool bSpendZeroConfChange = true;
+bool fSendFreeTransactions = false;
+bool fPayAtLeastCustomFee = true;
/**
* Fees smaller than this (in satoshi) are considered zero fee (for transaction creation)
@@ -1382,7 +1384,7 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, CAmount> >& vecSend,
{
LOCK2(cs_main, cs_wallet);
{
- nFeeRet = payTxFee.GetFeePerK();
+ nFeeRet = 0;
while (true)
{
txNew.vin.clear();
@@ -1502,7 +1504,7 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, CAmount> >& vecSend,
break; // Done, enough fee included.
// Too big to send for free? Include more fee and try again:
- if (nBytes > MAX_FREE_TRANSACTION_CREATE_SIZE)
+ if (!fSendFreeTransactions || nBytes > MAX_FREE_TRANSACTION_CREATE_SIZE)
{
nFeeRet = nFeeNeeded;
continue;
@@ -1628,6 +1630,12 @@ CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarge
{
// payTxFee is user-set "I want to pay this much"
CAmount nFeeNeeded = payTxFee.GetFee(nTxBytes);
+ // prevent user from paying a non-sense fee (like 1 satoshi): 0 < fee < minRelayFee
+ if (nFeeNeeded > 0 && nFeeNeeded < ::minRelayTxFee.GetFee(nTxBytes))
+ nFeeNeeded = ::minRelayTxFee.GetFee(nTxBytes);
+ // user selected total at least (default=true)
+ if (fPayAtLeastCustomFee && nFeeNeeded > 0 && nFeeNeeded < payTxFee.GetFeePerK())
+ nFeeNeeded = payTxFee.GetFeePerK();
// User didn't set: use -txconfirmtarget to estimate...
if (nFeeNeeded == 0)
nFeeNeeded = pool.estimateFee(nConfirmTarget).GetFee(nTxBytes);
diff --git a/src/wallet.h b/src/wallet.h
index b692ad056b..0244ce2368 100644
--- a/src/wallet.h
+++ b/src/wallet.h
@@ -32,6 +32,8 @@
extern CFeeRate payTxFee;
extern unsigned int nTxConfirmTarget;
extern bool bSpendZeroConfChange;
+extern bool fSendFreeTransactions;
+extern bool fPayAtLeastCustomFee;
//! -paytxfee default
static const CAmount DEFAULT_TRANSACTION_FEE = 0;