Browse Source

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)
tags/v0.15.1
Wladimir J. van der Laan 5 years ago
parent
commit
b7fe9cd04c
No account linked to committer's email address

+ 2
- 0
src/init.cpp View File

@@ -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

+ 6
- 0
src/qt/bitcoinamountfield.cpp View File

@@ -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;

+ 3
- 0
src/qt/bitcoinamountfield.h View File

@@ -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.
*/

+ 54
- 41
src/qt/coincontroldialog.cpp View File

@@ -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, " "));
}
}

+ 1
- 1
src/qt/coincontroldialog.h View File

@@ -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;

+ 13
- 65
src/qt/forms/optionsdialog.ui View File

@@ -137,65 +137,6 @@
<string>W&amp;allet</string>
</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">
@@ -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">
@@ -632,12 +586,6 @@
</layout>
</widget>
<customwidgets>
<customwidget>
<class>BitcoinAmountField</class>
<extends>QLineEdit</extends>
<header>bitcoinamountfield.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QValidatedLineEdit</class>
<extends>QLineEdit</extends>

+ 597
- 3
src/qt/forms/sendcoinsdialog.ui View File

@@ -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">
@@ -657,6 +657,590 @@
</widget>
</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>
@@ -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>

+ 0
- 18
src/qt/optionsdialog.cpp View File

@@ -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);

+ 0
- 1
src/qt/optionsdialog.h View File

@@ -46,7 +46,6 @@ private slots:

void showRestartWarning(bool fPersistent = false);
void clearStatusLabel();
void updateDisplayUnit();
void doProxyIpChecks(QValidatedLineEdit *pUiProxyIp, int nProxyPort);

signals:

+ 0
- 24
src/qt/optionsmodel.cpp View File

@@ -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);

+ 0
- 2
src/qt/optionsmodel.h View File

@@ -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);
};


+ 191
- 5
src/qt/sendcoinsdialog.cpp View File

@@ -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

+ 13
- 0
src/qt/sendcoinsdialog.h View File

@@ -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

+ 4
- 0
src/qt/walletmodel.cpp View File

@@ -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);

+ 2
- 1
src/qt/walletmodel.h View File

@@ -110,7 +110,8 @@ public:
AmountWithFeeExceedsBalance,
DuplicateAddress,
TransactionCreationFailed, // Error returned when wallet is still locked
TransactionCommitFailed
TransactionCommitFailed,
InsaneFee
};

enum EncryptionStatus

+ 5
- 0
src/qt/walletmodeltransaction.cpp View File

@@ -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;

+ 1
- 0
src/qt/walletmodeltransaction.h View File

@@ -25,6 +25,7 @@ public:
QList<SendCoinsRecipient> getRecipients();

CWalletTx *getTransaction();
unsigned int getTransactionSize();

void setTransactionFee(const CAmount& newFee);
CAmount getTransactionFee();

+ 1
- 0
src/qt/walletview.cpp View File

@@ -101,6 +101,7 @@ void WalletView::setClientModel(ClientModel *clientModel)
this->clientModel = clientModel;

overviewPage->setClientModel(clientModel);
sendCoinsPage->setClientModel(clientModel);
}

void WalletView::setWalletModel(WalletModel *walletModel)

+ 6
- 1
src/txmempool.h View File

@@ -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) */

+ 10
- 2
src/wallet.cpp View File

@@ -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);

+ 2
- 0
src/wallet.h View File

@@ -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;

Loading…
Cancel
Save