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)
This commit is contained in:
Wladimir J. van der Laan 2014-11-19 16:11:15 +01:00
commit b7fe9cd04c
No known key found for this signature in database
GPG key ID: 74810B012346C9A6
21 changed files with 910 additions and 163 deletions

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 += " -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 += " -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 += " -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 += " -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 += " -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"; strUsage += " -upgradewallet " + _("Upgrade wallet to latest format") + " " + _("on startup") + "\n";
@ -704,6 +705,7 @@ bool AppInit2(boost::thread_group& threadGroup)
} }
nTxConfirmTarget = GetArg("-txconfirmtarget", 1); nTxConfirmTarget = GetArg("-txconfirmtarget", 1);
bSpendZeroConfChange = GetArg("-spendzeroconfchange", true); bSpendZeroConfChange = GetArg("-spendzeroconfchange", true);
fSendFreeTransactions = GetArg("-sendfreetransactions", false);
std::string strWalletFile = GetArg("-wallet", "wallet.dat"); std::string strWalletFile = GetArg("-wallet", "wallet.dat");
#endif // ENABLE_WALLET #endif // ENABLE_WALLET

View file

@ -221,6 +221,12 @@ void BitcoinAmountField::clear()
unit->setCurrentIndex(0); unit->setCurrentIndex(0);
} }
void BitcoinAmountField::setEnabled(bool fEnabled)
{
amount->setEnabled(fEnabled);
unit->setEnabled(fEnabled);
}
bool BitcoinAmountField::validate() bool BitcoinAmountField::validate()
{ {
bool valid = false; bool valid = false;

View file

@ -48,6 +48,9 @@ public:
/** Make field empty and ready for new input. */ /** Make field empty and ready for new input. */
void clear(); 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), /** 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. in these cases we have to set it up manually.
*/ */

View file

@ -24,6 +24,7 @@
#include <QDialogButtonBox> #include <QDialogButtonBox>
#include <QFlags> #include <QFlags>
#include <QIcon> #include <QIcon>
#include <QSettings>
#include <QString> #include <QString>
#include <QTreeWidget> #include <QTreeWidget>
#include <QTreeWidgetItem> #include <QTreeWidgetItem>
@ -130,10 +131,22 @@ CoinControlDialog::CoinControlDialog(QWidget *parent) :
// default view is sorted by amount desc // default view is sorted by amount desc
sortView(COLUMN_AMOUNT_INT64, Qt::DescendingOrder); 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() CoinControlDialog::~CoinControlDialog()
{ {
QSettings settings;
settings.setValue("nCoinControlMode", ui->radioListMode->isChecked());
settings.setValue("nCoinControlSortColumn", sortColumn);
settings.setValue("nCoinControlSortOrder", (int)sortOrder);
delete ui; delete ui;
} }
@ -290,19 +303,19 @@ void CoinControlDialog::clipboardAmount()
// copy label "Fee" to clipboard // copy label "Fee" to clipboard
void CoinControlDialog::clipboardFee() 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 // copy label "After fee" to clipboard
void CoinControlDialog::clipboardAfterFee() 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 // copy label "Bytes" to clipboard
void CoinControlDialog::clipboardBytes() void CoinControlDialog::clipboardBytes()
{ {
GUIUtil::setClipboard(ui->labelCoinControlBytes->text()); GUIUtil::setClipboard(ui->labelCoinControlBytes->text().replace("~", ""));
} }
// copy label "Priority" to clipboard // copy label "Priority" to clipboard
@ -320,7 +333,7 @@ void CoinControlDialog::clipboardLowOutput()
// copy label "Change" to clipboard // copy label "Change" to clipboard
void CoinControlDialog::clipboardChange() 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 // treeview: sort
@ -402,26 +415,22 @@ void CoinControlDialog::viewItemChanged(QTreeWidgetItem* item, int column)
} }
// return human readable label for priority number // 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 double dPriorityMedium = mempoolEstimatePriority;
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) if (dPriorityMedium <= 0)
{ dPriorityMedium = AllowFreeThreshold(); // not enough data, back to hard-coded
double p = mempool.estimatePriority(i.first);
if (p > 0 && dPriority >= p) return i.second; if (dPriority / 1000000 > dPriorityMedium) return tr("highest");
} else if (dPriority / 100000 > dPriorityMedium) return tr("higher");
// Note: if mempool hasn't accumulated enough history (estimatePriority else if (dPriority / 10000 > dPriorityMedium) return tr("high");
// returns -1) we're conservative and classify as "lowest" else if (dPriority / 1000 > dPriorityMedium) return tr("medium-high");
if (mempool.estimatePriority(nTxConfirmTarget) <= 0 && AllowFree(dPriority)) else if (dPriority > dPriorityMedium) return tr("medium");
return ">=" + tr("medium"); else if (dPriority * 10 > dPriorityMedium) return tr("low-medium");
return tr("lowest"); 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 // shows count of locked unspent outputs
@ -470,6 +479,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
double dPriorityInputs = 0; double dPriorityInputs = 0;
unsigned int nQuantity = 0; unsigned int nQuantity = 0;
int nQuantityUncompressed = 0; int nQuantityUncompressed = 0;
bool fAllowFree = false;
vector<COutPoint> vCoinControl; vector<COutPoint> vCoinControl;
vector<COutput> vOutputs; 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 nBytes = nBytesInputs + ((CoinControlDialog::payAmounts.size() > 0 ? CoinControlDialog::payAmounts.size() + 1 : 2) * 34) + 10; // always assume +1 output for change here
// Priority // 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) 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 // Fee
nPayFee = payTxFee.GetFee(max((unsigned int)1000, nBytes)); nPayFee = CWallet::GetMinimumFee(nBytes, nTxConfirmTarget, mempool);
// Min Fee // Allow free?
if (nPayFee == 0) double dPriorityNeeded = mempoolEstimatePriority;
{ if (dPriorityNeeded <= 0)
nPayFee = CWallet::GetMinimumFee(nBytes, nTxConfirmTarget, mempool); dPriorityNeeded = AllowFreeThreshold(); // not enough data, back to hard-coded
fAllowFree = (dPriority >= dPriorityNeeded);
double dPriorityNeeded = mempool.estimatePriority(nTxConfirmTarget); if (fSendFreeTransactions)
if (dPriorityNeeded <= 0 && !AllowFree(dPriority)) // not enough mempool history: never send free if (fAllowFree && nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE)
dPriorityNeeded = std::numeric_limits<double>::max();
if (nBytes <= MAX_FREE_TRANSACTION_CREATE_SIZE && dPriority >= dPriorityNeeded)
nPayFee = 0; nPayFee = 0;
}
if (nPayAmount > 0) if (nPayAmount > 0)
{ {
@ -595,7 +603,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
l6->setText(sPriorityLabel); // Priority l6->setText(sPriorityLabel); // Priority
l7->setText(fDust ? tr("yes") : tr("no")); // Dust l7->setText(fDust ? tr("yes") : tr("no")); // Dust
l8->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nChange)); // Change l8->setText(BitcoinUnits::formatWithUnit(nDisplayUnit, nChange)); // Change
if (nPayFee > 0) if (nPayFee > 0 && !(payTxFee.GetFeePerK() > 0 && fPayAtLeastCustomFee && nBytes < 1000))
{ {
l3->setText("~" + l3->text()); l3->setText("~" + l3->text());
l4->setText("~" + l4->text()); l4->setText("~" + l4->text());
@ -605,7 +613,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
// turn labels "red" // turn labels "red"
l5->setStyleSheet((nBytes >= MAX_FREE_TRANSACTION_CREATE_SIZE) ? "color:red;" : "");// Bytes >= 1000 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" l7->setStyleSheet((fDust) ? "color:red;" : ""); // Dust = "yes"
// tool tips // 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))); 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 // 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); QString toolTip4 = tr("Can vary +/- %1 satoshi(s) per input.").arg(dFeeVary);
l3->setToolTip(toolTip4); l3->setToolTip(toolTip4);
@ -656,6 +668,7 @@ void CoinControlDialog::updateView()
QFlags<Qt::ItemFlag> flgTristate = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsTristate; QFlags<Qt::ItemFlag> flgTristate = Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable | Qt::ItemIsTristate;
int nDisplayUnit = model->getOptionsModel()->getDisplayUnit(); int nDisplayUnit = model->getOptionsModel()->getDisplayUnit();
double mempoolEstimatePriority = mempool.estimatePriority(nTxConfirmTarget);
map<QString, vector<COutput> > mapCoins; map<QString, vector<COutput> > mapCoins;
model->listCoins(mapCoins); model->listCoins(mapCoins);
@ -745,7 +758,7 @@ void CoinControlDialog::updateView()
// priority // priority
double dPriority = ((double)out.tx->vout[out.i].nValue / (nInputSize + 78)) * (out.nDepth+1); // 78 = 2 * 34 + 10 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, " ")); itemOutput->setText(COLUMN_PRIORITY_INT64, strPad(QString::number((int64_t)dPriority), 20, " "));
dPrioritySum += (double)out.tx->vout[out.i].nValue * (out.nDepth+1); dPrioritySum += (double)out.tx->vout[out.i].nValue * (out.nDepth+1);
nInputSum += nInputSize; nInputSum += nInputSize;
@ -778,7 +791,7 @@ void CoinControlDialog::updateView()
itemWalletAddress->setText(COLUMN_CHECKBOX, "(" + QString::number(nChildren) + ")"); itemWalletAddress->setText(COLUMN_CHECKBOX, "(" + QString::number(nChildren) + ")");
itemWalletAddress->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, nSum)); itemWalletAddress->setText(COLUMN_AMOUNT, BitcoinUnits::format(nDisplayUnit, nSum));
itemWalletAddress->setText(COLUMN_AMOUNT_INT64, strPad(QString::number(nSum), 15, " ")); 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, " ")); itemWalletAddress->setText(COLUMN_PRIORITY_INT64, strPad(QString::number((int64_t)dPrioritySum), 20, " "));
} }
} }

View file

@ -37,7 +37,7 @@ public:
// static because also called from sendcoinsdialog // static because also called from sendcoinsdialog
static void updateLabels(WalletModel*, QDialog*); static void updateLabels(WalletModel*, QDialog*);
static QString getPriorityLabel(const CTxMemPool& pool, double); static QString getPriorityLabel(double dPriority, double mempoolEstimatePriority);
static QList<CAmount> payAmounts; static QList<CAmount> payAmounts;
static CCoinControl *coinControl; static CCoinControl *coinControl;

View file

@ -137,65 +137,6 @@
<string>W&amp;allet</string> <string>W&amp;allet</string>
</attribute> </attribute>
<layout class="QVBoxLayout" name="verticalLayout_Wallet"> <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> <item>
<widget class="QGroupBox" name="groupBox"> <widget class="QGroupBox" name="groupBox">
<property name="title"> <property name="title">
@ -225,6 +166,19 @@
</layout> </layout>
</widget> </widget>
</item> </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> </layout>
</widget> </widget>
<widget class="QWidget" name="tabNetwork"> <widget class="QWidget" name="tabNetwork">
@ -632,12 +586,6 @@
</layout> </layout>
</widget> </widget>
<customwidgets> <customwidgets>
<customwidget>
<class>BitcoinAmountField</class>
<extends>QLineEdit</extends>
<header>bitcoinamountfield.h</header>
<container>1</container>
</customwidget>
<customwidget> <customwidget>
<class>QValidatedLineEdit</class> <class>QValidatedLineEdit</class>
<extends>QLineEdit</extends> <extends>QLineEdit</extends>

View file

@ -7,13 +7,13 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>850</width> <width>850</width>
<height>400</height> <height>526</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>Send Coins</string> <string>Send Coins</string>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,1,0"> <layout class="QVBoxLayout" name="verticalLayout" stretch="0,1,0,0">
<property name="bottomMargin"> <property name="bottomMargin">
<number>8</number> <number>8</number>
</property> </property>
@ -617,7 +617,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>830</width> <width>830</width>
<height>178</height> <height>68</height>
</rect> </rect>
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,1"> <layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,1">
@ -657,6 +657,590 @@
</widget> </widget>
</widget> </widget>
</item> </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> <item>
<layout class="QHBoxLayout" name="horizontalLayout"> <layout class="QHBoxLayout" name="horizontalLayout">
<item> <item>
@ -787,9 +1371,19 @@
<extends>QLineEdit</extends> <extends>QLineEdit</extends>
<header>qvalidatedlineedit.h</header> <header>qvalidatedlineedit.h</header>
</customwidget> </customwidget>
<customwidget>
<class>BitcoinAmountField</class>
<extends>QLineEdit</extends>
<header>bitcoinamountfield.h</header>
<container>1</container>
</customwidget>
</customwidgets> </customwidgets>
<resources> <resources>
<include location="../bitcoin.qrc"/> <include location="../bitcoin.qrc"/>
</resources> </resources>
<connections/> <connections/>
<buttongroups>
<buttongroup name="groupFee"/>
<buttongroup name="groupCustomFee"/>
</buttongroups>
</ui> </ui>

View file

@ -105,9 +105,6 @@ OptionsDialog::OptionsDialog(QWidget *parent, bool enableWallet) :
#endif #endif
ui->unit->setModel(new BitcoinUnits(this)); ui->unit->setModel(new BitcoinUnits(this));
#ifdef ENABLE_WALLET
ui->transactionFee->setSingleStep(CWallet::minTxFee.GetFeePerK());
#endif
/* Widget-to-option mapper */ /* Widget-to-option mapper */
mapper = new QDataWidgetMapper(this); mapper = new QDataWidgetMapper(this);
@ -139,16 +136,11 @@ void OptionsDialog::setModel(OptionsModel *model)
strLabel = tr("none"); strLabel = tr("none");
ui->overriddenByCommandLineLabel->setText(strLabel); ui->overriddenByCommandLineLabel->setText(strLabel);
connect(model, SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit()));
mapper->setModel(model); mapper->setModel(model);
setMapper(); setMapper();
mapper->toFirst(); 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) */ /* warn when one of the following settings changes by user action (placed here so init via mapper doesn't trigger them) */
/* Main */ /* Main */
@ -172,7 +164,6 @@ void OptionsDialog::setMapper()
mapper->addMapping(ui->databaseCache, OptionsModel::DatabaseCache); mapper->addMapping(ui->databaseCache, OptionsModel::DatabaseCache);
/* Wallet */ /* Wallet */
mapper->addMapping(ui->transactionFee, OptionsModel::Fee);
mapper->addMapping(ui->spendZeroConfChange, OptionsModel::SpendZeroConfChange); mapper->addMapping(ui->spendZeroConfChange, OptionsModel::SpendZeroConfChange);
mapper->addMapping(ui->coinControlFeatures, OptionsModel::CoinControlFeatures); mapper->addMapping(ui->coinControlFeatures, OptionsModel::CoinControlFeatures);
@ -264,15 +255,6 @@ void OptionsDialog::clearStatusLabel()
ui->statusLabel->clear(); 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) void OptionsDialog::doProxyIpChecks(QValidatedLineEdit *pUiProxyIp, int nProxyPort)
{ {
Q_UNUSED(nProxyPort); Q_UNUSED(nProxyPort);

View file

@ -46,7 +46,6 @@ private slots:
void showRestartWarning(bool fPersistent = false); void showRestartWarning(bool fPersistent = false);
void clearStatusLabel(); void clearStatusLabel();
void updateDisplayUnit();
void doProxyIpChecks(QValidatedLineEdit *pUiProxyIp, int nProxyPort); void doProxyIpChecks(QValidatedLineEdit *pUiProxyIp, int nProxyPort);
signals: signals:

View file

@ -90,12 +90,6 @@ void OptionsModel::Init()
// Wallet // Wallet
#ifdef ENABLE_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")) if (!settings.contains("bSpendZeroConfChange"))
settings.setValue("bSpendZeroConfChange", true); settings.setValue("bSpendZeroConfChange", true);
if (!SoftSetBoolArg("-spendzeroconfchange", settings.value("bSpendZeroConfChange").toBool())) if (!SoftSetBoolArg("-spendzeroconfchange", settings.value("bSpendZeroConfChange").toBool()))
@ -185,16 +179,6 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const
} }
#ifdef ENABLE_WALLET #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: case SpendZeroConfChange:
return settings.value("bSpendZeroConfChange"); return settings.value("bSpendZeroConfChange");
#endif #endif
@ -276,14 +260,6 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in
} }
break; break;
#ifdef ENABLE_WALLET #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: case SpendZeroConfChange:
if (settings.value("bSpendZeroConfChange") != value) { if (settings.value("bSpendZeroConfChange") != value) {
settings.setValue("bSpendZeroConfChange", value); settings.setValue("bSpendZeroConfChange", value);

View file

@ -34,7 +34,6 @@ public:
ProxyUse, // bool ProxyUse, // bool
ProxyIP, // QString ProxyIP, // QString
ProxyPort, // int ProxyPort, // int
Fee, // qint64
DisplayUnit, // BitcoinUnits::Unit DisplayUnit, // BitcoinUnits::Unit
ThirdPartyTxUrls, // QString ThirdPartyTxUrls, // QString
Language, // QString Language, // QString
@ -84,7 +83,6 @@ private:
signals: signals:
void displayUnitChanged(int unit); void displayUnitChanged(int unit);
void transactionFeeChanged(const CAmount&);
void coinControlFeaturesChanged(bool); void coinControlFeaturesChanged(bool);
}; };

View file

@ -7,10 +7,12 @@
#include "addresstablemodel.h" #include "addresstablemodel.h"
#include "bitcoinunits.h" #include "bitcoinunits.h"
#include "clientmodel.h"
#include "coincontroldialog.h" #include "coincontroldialog.h"
#include "guiutil.h" #include "guiutil.h"
#include "optionsmodel.h" #include "optionsmodel.h"
#include "sendcoinsentry.h" #include "sendcoinsentry.h"
#include "wallet.h"
#include "walletmodel.h" #include "walletmodel.h"
#include "base58.h" #include "base58.h"
@ -19,6 +21,7 @@
#include <QMessageBox> #include <QMessageBox>
#include <QScrollBar> #include <QScrollBar>
#include <QSettings>
#include <QTextDocument> #include <QTextDocument>
SendCoinsDialog::SendCoinsDialog(QWidget *parent) : SendCoinsDialog::SendCoinsDialog(QWidget *parent) :
@ -72,9 +75,46 @@ SendCoinsDialog::SendCoinsDialog(QWidget *parent) :
ui->labelCoinControlLowOutput->addAction(clipboardLowOutputAction); ui->labelCoinControlLowOutput->addAction(clipboardLowOutputAction);
ui->labelCoinControlChange->addAction(clipboardChangeAction); 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; fNewRecipientAllowed = true;
} }
void SendCoinsDialog::setClientModel(ClientModel *clientModel)
{
this->clientModel = clientModel;
}
void SendCoinsDialog::setModel(WalletModel *model) void SendCoinsDialog::setModel(WalletModel *model)
{ {
this->model = model; this->model = model;
@ -94,18 +134,51 @@ void SendCoinsDialog::setModel(WalletModel *model)
model->getWatchBalance(), model->getWatchUnconfirmedBalance(), model->getWatchImmatureBalance()); 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, 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())); connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit()));
updateDisplayUnit();
// Coin Control // Coin Control
connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(coinControlUpdateLabels())); connect(model->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(coinControlUpdateLabels()));
connect(model->getOptionsModel(), SIGNAL(coinControlFeaturesChanged(bool)), this, SLOT(coinControlFeatureChanged(bool))); 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()); ui->frameCoinControl->setVisible(model->getOptionsModel()->getCoinControlFeatures());
coinControlUpdateLabels(); 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() 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; delete ui;
} }
@ -214,6 +287,9 @@ void SendCoinsDialog::on_sendButton_clicked()
questionString.append(BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), txFee)); questionString.append(BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), txFee));
questionString.append("</span> "); questionString.append("</span> ");
questionString.append(tr("added as transaction fee")); 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 // add total amount in all subdivision units
@ -402,6 +478,9 @@ void SendCoinsDialog::setBalance(const CAmount& balance, const CAmount& unconfir
void SendCoinsDialog::updateDisplayUnit() void SendCoinsDialog::updateDisplayUnit()
{ {
setBalance(model->getBalance(), 0, 0, 0, 0, 0); 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) 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.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; msgParams.second = CClientUIInterface::MSG_ERROR;
break; 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. // included to prevent a compiler warning.
case WalletModel::OK: case WalletModel::OK:
default: default:
@ -447,6 +529,110 @@ void SendCoinsDialog::processSendCoinsReturn(const WalletModel::SendCoinsReturn
emit message(tr("Send Coins"), msgParams.first, msgParams.second); 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 // Coin Control: copy label "Quantity" to clipboard
void SendCoinsDialog::coinControlClipboardQuantity() void SendCoinsDialog::coinControlClipboardQuantity()
{ {
@ -462,19 +648,19 @@ void SendCoinsDialog::coinControlClipboardAmount()
// Coin Control: copy label "Fee" to clipboard // Coin Control: copy label "Fee" to clipboard
void SendCoinsDialog::coinControlClipboardFee() 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 // Coin Control: copy label "After fee" to clipboard
void SendCoinsDialog::coinControlClipboardAfterFee() 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 // Coin Control: copy label "Bytes" to clipboard
void SendCoinsDialog::coinControlClipboardBytes() void SendCoinsDialog::coinControlClipboardBytes()
{ {
GUIUtil::setClipboard(ui->labelCoinControlBytes->text()); GUIUtil::setClipboard(ui->labelCoinControlBytes->text().replace("~", ""));
} }
// Coin Control: copy label "Priority" to clipboard // Coin Control: copy label "Priority" to clipboard
@ -492,7 +678,7 @@ void SendCoinsDialog::coinControlClipboardLowOutput()
// Coin Control: copy label "Change" to clipboard // Coin Control: copy label "Change" to clipboard
void SendCoinsDialog::coinControlClipboardChange() 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 // Coin Control: settings menu - coin control enabled/disabled by user

View file

@ -10,6 +10,7 @@
#include <QDialog> #include <QDialog>
#include <QString> #include <QString>
class ClientModel;
class OptionsModel; class OptionsModel;
class SendCoinsEntry; class SendCoinsEntry;
class SendCoinsRecipient; class SendCoinsRecipient;
@ -31,6 +32,7 @@ public:
explicit SendCoinsDialog(QWidget *parent = 0); explicit SendCoinsDialog(QWidget *parent = 0);
~SendCoinsDialog(); ~SendCoinsDialog();
void setClientModel(ClientModel *clientModel);
void setModel(WalletModel *model); 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). /** 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: private:
Ui::SendCoinsDialog *ui; Ui::SendCoinsDialog *ui;
ClientModel *clientModel;
WalletModel *model; WalletModel *model;
bool fNewRecipientAllowed; bool fNewRecipientAllowed;
bool fFeeMinimized;
// Process WalletModel::SendCoinsReturn and generate a pair consisting // Process WalletModel::SendCoinsReturn and generate a pair consisting
// of a message and message flags for use in emit message(). // of a message and message flags for use in emit message().
// Additional parameter msgArg can be used via .arg(msgArg). // Additional parameter msgArg can be used via .arg(msgArg).
void processSendCoinsReturn(const WalletModel::SendCoinsReturn &sendCoinsReturn, const QString &msgArg = QString()); void processSendCoinsReturn(const WalletModel::SendCoinsReturn &sendCoinsReturn, const QString &msgArg = QString());
void minimizeFeeSection(bool fMinimize);
void updateFeeMinimizedLabel();
private slots: private slots:
void on_sendButton_clicked(); void on_sendButton_clicked();
void on_buttonChooseFee_clicked();
void on_buttonMinimizeFee_clicked();
void removeEntry(SendCoinsEntry* entry); void removeEntry(SendCoinsEntry* entry);
void updateDisplayUnit(); void updateDisplayUnit();
void coinControlFeatureChanged(bool); void coinControlFeatureChanged(bool);
@ -77,6 +85,11 @@ private slots:
void coinControlClipboardPriority(); void coinControlClipboardPriority();
void coinControlClipboardLowOutput(); void coinControlClipboardLowOutput();
void coinControlClipboardChange(); void coinControlClipboardChange();
void setMinimumFee();
void updateFeeSectionControls();
void updateMinFeeLabel();
void updateSmartFeeLabel();
void updateGlobalFeeVariables();
signals: signals:
// Fired when a message should be reported to the user // Fired when a message should be reported to the user

View file

@ -277,6 +277,10 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransact
CClientUIInterface::MSG_ERROR); CClientUIInterface::MSG_ERROR);
return TransactionCreationFailed; return TransactionCreationFailed;
} }
// reject insane fee > 0.1 bitcoin
if (nFeeRequired > 10000000)
return InsaneFee;
} }
return SendCoinsReturn(OK); return SendCoinsReturn(OK);

View file

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

View file

@ -31,6 +31,11 @@ CWalletTx *WalletModelTransaction::getTransaction()
return walletTransaction; return walletTransaction;
} }
unsigned int WalletModelTransaction::getTransactionSize()
{
return (!walletTransaction ? 0 : (::GetSerializeSize(*(CTransaction*)walletTransaction, SER_NETWORK, PROTOCOL_VERSION)));
}
CAmount WalletModelTransaction::getTransactionFee() CAmount WalletModelTransaction::getTransactionFee()
{ {
return fee; return fee;

View file

@ -25,6 +25,7 @@ public:
QList<SendCoinsRecipient> getRecipients(); QList<SendCoinsRecipient> getRecipients();
CWalletTx *getTransaction(); CWalletTx *getTransaction();
unsigned int getTransactionSize();
void setTransactionFee(const CAmount& newFee); void setTransactionFee(const CAmount& newFee);
CAmount getTransactionFee(); CAmount getTransactionFee();

View file

@ -101,6 +101,7 @@ void WalletView::setClientModel(ClientModel *clientModel)
this->clientModel = clientModel; this->clientModel = clientModel;
overviewPage->setClientModel(clientModel); overviewPage->setClientModel(clientModel);
sendCoinsPage->setClientModel(clientModel);
} }
void WalletView::setWalletModel(WalletModel *walletModel) void WalletView::setWalletModel(WalletModel *walletModel)

View file

@ -15,11 +15,16 @@
class CAutoFile; class CAutoFile;
inline double AllowFreeThreshold()
{
return COIN * 144 / 250;
}
inline bool AllowFree(double dPriority) inline bool AllowFree(double dPriority)
{ {
// Large (in bytes) low-priority (new, small-coin) transactions // Large (in bytes) low-priority (new, small-coin) transactions
// need a fee. // 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) */ /** Fake height value used in CCoins to signify they are only in the memory pool (since 0.8) */

View file

@ -28,6 +28,8 @@ using namespace std;
CFeeRate payTxFee(DEFAULT_TRANSACTION_FEE); CFeeRate payTxFee(DEFAULT_TRANSACTION_FEE);
unsigned int nTxConfirmTarget = 1; unsigned int nTxConfirmTarget = 1;
bool bSpendZeroConfChange = true; bool bSpendZeroConfChange = true;
bool fSendFreeTransactions = false;
bool fPayAtLeastCustomFee = true;
/** /**
* Fees smaller than this (in satoshi) are considered zero fee (for transaction creation) * 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); LOCK2(cs_main, cs_wallet);
{ {
nFeeRet = payTxFee.GetFeePerK(); nFeeRet = 0;
while (true) while (true)
{ {
txNew.vin.clear(); txNew.vin.clear();
@ -1502,7 +1504,7 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, CAmount> >& vecSend,
break; // Done, enough fee included. break; // Done, enough fee included.
// Too big to send for free? Include more fee and try again: // 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; nFeeRet = nFeeNeeded;
continue; continue;
@ -1628,6 +1630,12 @@ CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarge
{ {
// payTxFee is user-set "I want to pay this much" // payTxFee is user-set "I want to pay this much"
CAmount nFeeNeeded = payTxFee.GetFee(nTxBytes); 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... // User didn't set: use -txconfirmtarget to estimate...
if (nFeeNeeded == 0) if (nFeeNeeded == 0)
nFeeNeeded = pool.estimateFee(nConfirmTarget).GetFee(nTxBytes); nFeeNeeded = pool.estimateFee(nConfirmTarget).GetFee(nTxBytes);

View file

@ -32,6 +32,8 @@
extern CFeeRate payTxFee; extern CFeeRate payTxFee;
extern unsigned int nTxConfirmTarget; extern unsigned int nTxConfirmTarget;
extern bool bSpendZeroConfChange; extern bool bSpendZeroConfChange;
extern bool fSendFreeTransactions;
extern bool fPayAtLeastCustomFee;
//! -paytxfee default //! -paytxfee default
static const CAmount DEFAULT_TRANSACTION_FEE = 0; static const CAmount DEFAULT_TRANSACTION_FEE = 0;