diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h --- a/src/qt/sendcoinsdialog.h +++ b/src/qt/sendcoinsdialog.h @@ -79,6 +79,8 @@ const QString &msgArg = QString()); void minimizeFeeSection(bool fMinimize); void updateFeeMinimizedLabel(); + // Update the passed in CCoinControl with state from the GUI + void updateCoinControlState(CCoinControl &ctrl); private Q_SLOTS: void on_sendButton_clicked(); @@ -103,7 +105,6 @@ void updateFeeSectionControls(); void updateMinFeeLabel(); void updateSmartFeeLabel(); - void updateGlobalFeeVariables(); Q_SIGNALS: // Fired when a message should be reported to the user diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -192,24 +192,16 @@ // fee section 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())); @@ -217,7 +209,6 @@ updateFeeSectionControls(); updateMinFeeLabel(); updateSmartFeeLabel(); - updateGlobalFeeVariables(); // Cleanup old confirmation target related settings // TODO: Remove these in 0.20 @@ -286,7 +277,9 @@ ctrl = *CoinControlDialog::coinControl; } - prepareStatus = model->prepareTransaction(currentTransaction, &ctrl); + updateCoinControlState(ctrl); + + prepareStatus = model->prepareTransaction(currentTransaction, ctrl); // process prepareStatus and on error generate message shown to user processSendCoinsReturn( @@ -685,14 +678,6 @@ !ui->checkBoxMinimumFee->isChecked()); } -void SendCoinsDialog::updateGlobalFeeVariables() { - if (ui->radioSmartFee->isChecked()) { - payTxFee = CFeeRate(Amount::zero()); - } else { - payTxFee = CFeeRate(Amount(ui->customFee->value())); - } -} - void SendCoinsDialog::updateFeeMinimizedLabel() { if (!model || !model->getOptionsModel()) { return; @@ -720,30 +705,32 @@ } } +void SendCoinsDialog::updateCoinControlState(CCoinControl &ctrl) { + if (ui->radioCustomFee->isChecked()) { + ctrl.m_feerate = CFeeRate(ui->customFee->value()); + } else { + ctrl.m_feerate.reset(); + } +} + void SendCoinsDialog::updateSmartFeeLabel() { if (!model || !model->getOptionsModel()) { return; } CFeeRate feeRate = g_mempool.estimateFee(); + + ui->labelSmartFee->setText( + BitcoinUnits::formatWithUnit( + model->getOptionsModel()->getDisplayUnit(), + std::max(feeRate.GetFeePerK(), GetMinimumFee(1000, g_mempool))) + + "/kB"); // not enough data => minfee if (feeRate <= CFeeRate(Amount::zero())) { - ui->labelSmartFee->setText( - BitcoinUnits::formatWithUnit( - model->getOptionsModel()->getDisplayUnit(), - std::max(CWallet::fallbackFee.GetFeePerK(), - GetMinimumFee(1000, g_mempool))) + - "/kB"); // (Smart fee not initialized yet. This usually takes a few blocks...) ui->labelSmartFee2->show(); ui->labelFeeEstimation->setText(""); } else { - ui->labelSmartFee->setText( - BitcoinUnits::formatWithUnit( - model->getOptionsModel()->getDisplayUnit(), - std::max(feeRate.GetFeePerK(), - GetMinimumFee(1000, g_mempool))) + - "/kB"); ui->labelSmartFee2->hide(); ui->labelFeeEstimation->setText( tr("Estimated to begin confirmation by next block.")); @@ -807,8 +794,6 @@ CoinControlDialog::coinControl->SetNull(); } - // make sure we set back the confirmation target - updateGlobalFeeVariables(); coinControlUpdateLabels(); } @@ -899,6 +884,8 @@ return; } + updateCoinControlState(*CoinControlDialog::coinControl); + // set pay amounts CoinControlDialog::payAmounts.clear(); CoinControlDialog::fSubtractFeeFromAmount = false; diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -168,9 +168,8 @@ }; // prepare transaction for getting txfee before sending coins - SendCoinsReturn - prepareTransaction(WalletModelTransaction &transaction, - const CCoinControl *coinControl = nullptr); + SendCoinsReturn prepareTransaction(WalletModelTransaction &transaction, + const CCoinControl &coinControl); // Send coins to a list of recipients SendCoinsReturn sendCoins(WalletModelTransaction &transaction); diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -20,6 +20,7 @@ #include "ui_interface.h" #include "util.h" // for GetBoolArg #include "validation.h" +#include "wallet/coincontrol.h" #include "wallet/wallet.h" #include "wallet/walletdb.h" // for BackupWallet @@ -178,7 +179,7 @@ WalletModel::SendCoinsReturn WalletModel::prepareTransaction(WalletModelTransaction &transaction, - const CCoinControl *coinControl) { + const CCoinControl &coinControl) { Amount total = Amount::zero(); bool fSubtractFeeFromAmount = false; QList recipients = transaction.getRecipients(); @@ -245,7 +246,7 @@ return DuplicateAddress; } - Amount nBalance = getBalance(coinControl); + Amount nBalance = getBalance(&coinControl); if (total > nBalance) { return AmountExceedsBalance; diff --git a/src/wallet/coincontrol.h b/src/wallet/coincontrol.h --- a/src/wallet/coincontrol.h +++ b/src/wallet/coincontrol.h @@ -7,6 +7,8 @@ #include "primitives/transaction.h" +#include + /** Coin Control Features. */ class CCoinControl { public: @@ -17,12 +19,12 @@ //! Includes watch only addresses which match the ISMINE_WATCH_SOLVABLE //! criteria bool fAllowWatchOnly; - //! Override estimated feerate + //! Override automatic min/max checks on fee, m_feerate must be set if true bool fOverrideFeeRate; - //! Feerate to use if overrideFeeRate is true - CFeeRate nFeeRate; - //! Override the default confirmation target, 0 = use default - int nConfirmTarget; + //! Override the default payTxFee if set + boost::optional m_feerate; + //! Override the default confirmation target if set + boost::optional m_confirm_target; CCoinControl() { SetNull(); } @@ -31,9 +33,9 @@ fAllowOtherInputs = false; fAllowWatchOnly = false; setSelected.clear(); - nFeeRate = CFeeRate(Amount::zero()); + m_feerate.reset(); fOverrideFeeRate = false; - nConfirmTarget = 0; + m_confirm_target.reset(); } bool HasSelected() const { return (setSelected.size() > 0); } diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -457,8 +457,9 @@ CRecipient recipient = {scriptPubKey, nValue, fSubtractFeeFromAmount}; vecSend.push_back(recipient); + CCoinControl coinControl; if (!pwallet->CreateTransaction(vecSend, wtxNew, reservekey, nFeeRequired, - nChangePosRet, strError)) { + nChangePosRet, strError, coinControl)) { if (!fSubtractFeeFromAmount && nValue + nFeeRequired > curBalance) { strError = strprintf("Error: This transaction requires a " "transaction fee of at least %s", @@ -1331,8 +1332,10 @@ Amount nFeeRequired = Amount::zero(); int nChangePosRet = -1; std::string strFailReason; - bool fCreated = pwallet->CreateTransaction( - vecSend, wtx, keyChange, nFeeRequired, nChangePosRet, strFailReason); + CCoinControl coinControl; + bool fCreated = + pwallet->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired, + nChangePosRet, strFailReason, coinControl); if (!fCreated) { throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason); } @@ -3482,14 +3485,9 @@ pwallet->BlockUntilSyncedToCurrentChain(); CCoinControl coinControl; - coinControl.destChange = CNoDestination(); int changePosition = -1; - // include watching - coinControl.fAllowWatchOnly = false; bool lockUnspents = false; bool reserveChangeKey = true; - coinControl.nFeeRate = CFeeRate(Amount::zero()); - coinControl.fOverrideFeeRate = false; UniValue subtractFeeFromOutputs; std::set setSubtractFeeFromOutputs; @@ -3548,7 +3546,7 @@ } if (options.exists("feeRate")) { - coinControl.nFeeRate = + coinControl.m_feerate = CFeeRate(AmountFromValue(options["feeRate"])); coinControl.fOverrideFeeRate = true; } diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -11,6 +11,7 @@ #include "rpc/server.h" #include "test/test_bitcoin.h" #include "validation.h" +#include "wallet/coincontrol.h" #include "wallet/rpcdump.h" #include "wallet/test/wallet_test_fixture.h" @@ -729,8 +730,9 @@ Amount fee; int changePos = -1; std::string error; + CCoinControl dummy; BOOST_CHECK(wallet->CreateTransaction({recipient}, wtx, reservekey, fee, - changePos, error)); + changePos, error, dummy)); CValidationState state; BOOST_CHECK(wallet->CommitTransaction(wtx, reservekey, nullptr, state)); CMutableTransaction blocktx; diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -1009,8 +1009,7 @@ CWalletTx &wtxNew, CReserveKey &reservekey, Amount &nFeeRet, int &nChangePosInOut, std::string &strFailReason, - const CCoinControl *coinControl = nullptr, - bool sign = true); + const CCoinControl &coinControl, bool sign = true); bool CommitTransaction(CWalletTx &wtxNew, CReserveKey &reservekey, CConnman *connman, CValidationState &state); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2777,7 +2777,7 @@ CReserveKey reservekey(this); CWalletTx wtx; if (!CreateTransaction(vecSend, wtx, reservekey, nFeeRet, nChangePosInOut, - strFailReason, &coinControl, false)) { + strFailReason, coinControl, false)) { return false; } @@ -2816,9 +2816,8 @@ CWalletTx &wtxNew, CReserveKey &reservekey, Amount &nFeeRet, int &nChangePosInOut, std::string &strFailReason, - const CCoinControl *coinControl, bool sign) { + const CCoinControl &coinControl, bool sign) { Amount nValue = Amount::zero(); - int nChangePosRequest = nChangePosInOut; unsigned int nSubtractFeeFromAmount = 0; for (const auto &recipient : vecSend) { @@ -2881,7 +2880,7 @@ LOCK2(cs_main, cs_wallet); std::vector vAvailableCoins; - AvailableCoins(vAvailableCoins, true, coinControl); + AvailableCoins(vAvailableCoins, true, &coinControl); // Create change script that will be used if we need change // TODO: pass in scriptChange instead of reservekey so @@ -2889,9 +2888,8 @@ CScript scriptChange; // coin control: send change to custom address - if (coinControl && - !boost::get(&coinControl->destChange)) { - scriptChange = GetScriptForDestination(coinControl->destChange); + if (!boost::get(&coinControl.destChange)) { + scriptChange = GetScriptForDestination(coinControl.destChange); // no coin control: send change to newly generated address } else { @@ -2979,7 +2977,7 @@ nValueIn = Amount::zero(); setCoins.clear(); if (!SelectCoins(vAvailableCoins, nValueToSelect, setCoins, - nValueIn, coinControl)) { + nValueIn, &coinControl)) { strFailReason = _("Insufficient funds"); return false; } @@ -3083,9 +3081,6 @@ } Amount nFeeNeeded = GetMinimumFee(nBytes, g_mempool); - if (coinControl && coinControl->fOverrideFeeRate) { - nFeeNeeded = coinControl->nFeeRate.GetFeeCeiling(nBytes); - } // If we made it here and we aren't even able to meet the relay fee // on the next pass, give up because we must be at the maximum