diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index 6f8b6c2953..2b19a6714b 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -15,6 +15,43 @@ #include +CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFee) +{ + // "Dust" is defined in terms of dustRelayFee, + // which has units satoshis-per-kilobyte. + // If you'd pay more than 1/3 in fees + // to spend something, then we consider it dust. + // A typical spendable non-segwit txout is 34 bytes big, and will + // need a CTxIn of at least 148 bytes to spend: + // so dust is a spendable txout less than + // 546*dustRelayFee/1000 (in satoshis). + // A typical spendable segwit txout is 31 bytes big, and will + // need a CTxIn of at least 67 bytes to spend: + // so dust is a spendable txout less than + // 294*dustRelayFee/1000 (in satoshis). + if (txout.scriptPubKey.IsUnspendable()) + return 0; + + size_t nSize = GetSerializeSize(txout, SER_DISK, 0); + int witnessversion = 0; + std::vector witnessprogram; + + if (txout.scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) { + // sum the sizes of the parts of a transaction input + // with 75% segwit discount applied to the script size. + nSize += (32 + 4 + 1 + (107 / WITNESS_SCALE_FACTOR) + 4); + } else { + nSize += (32 + 4 + 1 + 107 + 4); // the 148 mentioned above + } + + return 3 * dustRelayFee.GetFee(nSize); +} + +bool IsDust(const CTxOut& txout, const CFeeRate& dustRelayFee) +{ + return (txout.nValue < GetDustThreshold(txout, dustRelayFee)); +} + /** * Check transaction inputs to mitigate two * potential denial-of-service attacks: @@ -106,7 +143,7 @@ bool IsStandardTx(const CTransaction& tx, std::string& reason, const bool witnes else if ((whichType == TX_MULTISIG) && (!fIsBareMultisigStd)) { reason = "bare-multisig"; return false; - } else if (txout.IsDust(dustRelayFee)) { + } else if (IsDust(txout, ::dustRelayFee)) { reason = "dust"; return false; } diff --git a/src/policy/policy.h b/src/policy/policy.h index 6df541bc0f..24bf3fa5d7 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -13,6 +13,7 @@ #include class CCoinsViewCache; +class CTxOut; /** Default for -blockmaxsize, which controls the maximum size of block the mining code will create **/ static const unsigned int DEFAULT_BLOCK_MAX_SIZE = 750000; @@ -72,6 +73,10 @@ static const unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS = STANDARD_SCRIPT_ static const unsigned int STANDARD_LOCKTIME_VERIFY_FLAGS = LOCKTIME_VERIFY_SEQUENCE | LOCKTIME_MEDIAN_TIME_PAST; +CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFee); + +bool IsDust(const CTxOut& txout, const CFeeRate& dustRelayFee); + bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType, const bool witnessEnabled = false); /** * Check for standard transaction types diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index 5059030309..00ac0b92b5 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -161,43 +161,6 @@ public: return (nValue == -1); } - CAmount GetDustThreshold(const CFeeRate &minRelayTxFee) const - { - // "Dust" is defined in terms of CTransaction::minRelayTxFee, - // which has units satoshis-per-kilobyte. - // If you'd pay more than 1/3 in fees - // to spend something, then we consider it dust. - // A typical spendable non-segwit txout is 34 bytes big, and will - // need a CTxIn of at least 148 bytes to spend: - // so dust is a spendable txout less than - // 546*minRelayTxFee/1000 (in satoshis). - // A typical spendable segwit txout is 31 bytes big, and will - // need a CTxIn of at least 67 bytes to spend: - // so dust is a spendable txout less than - // 294*minRelayTxFee/1000 (in satoshis). - if (scriptPubKey.IsUnspendable()) - return 0; - - size_t nSize = GetSerializeSize(*this, SER_DISK, 0); - int witnessversion = 0; - std::vector witnessprogram; - - if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) { - // sum the sizes of the parts of a transaction input - // with 75% segwit discount applied to the script size. - nSize += (32 + 4 + 1 + (107 / WITNESS_SCALE_FACTOR) + 4); - } else { - nSize += (32 + 4 + 1 + 107 + 4); // the 148 mentioned above - } - - return 3 * minRelayTxFee.GetFee(nSize); - } - - bool IsDust(const CFeeRate &minRelayTxFee) const - { - return (nValue < GetDustThreshold(minRelayTxFee)); - } - friend bool operator==(const CTxOut& a, const CTxOut& b) { return (a.nValue == b.nValue && diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index 38ad6e9aab..2a331d4fae 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -434,8 +434,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) { CTxOut txout(amount, (CScript)std::vector(24, 0)); txDummy.vout.push_back(txout); - if (txout.IsDust(dustRelayFee)) - fDust = true; + fDust |= IsDust(txout, ::dustRelayFee); } } @@ -527,10 +526,10 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) if (nChange > 0 && nChange < MIN_CHANGE) { CTxOut txout(nChange, (CScript)std::vector(24, 0)); - if (txout.IsDust(dustRelayFee)) + if (IsDust(txout, ::dustRelayFee)) { if (CoinControlDialog::fSubtractFeeFromAmount) // dust-change will be raised until no dust - nChange = txout.GetDustThreshold(dustRelayFee); + nChange = GetDustThreshold(txout, ::dustRelayFee); else { nPayFee += nChange; diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp index 3f3f9b9ccb..bffa81137b 100644 --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -251,7 +251,7 @@ bool isDust(const QString& address, const CAmount& amount) CTxDestination dest = CBitcoinAddress(address.toStdString()).Get(); CScript script = GetScriptForDestination(dest); CTxOut txOut(amount, script); - return txOut.IsDust(dustRelayFee); + return IsDust(txOut, ::dustRelayFee); } QString HtmlEscape(const QString& str, bool fMultiLine) diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp index dd75f12076..77fa6b2f3b 100644 --- a/src/qt/paymentserver.cpp +++ b/src/qt/paymentserver.cpp @@ -580,7 +580,7 @@ bool PaymentServer::processPaymentRequest(const PaymentRequestPlus& request, Sen // Extract and check amounts CTxOut txOut(sendingTo.second, sendingTo.first); - if (txOut.IsDust(dustRelayFee)) { + if (IsDust(txOut, ::dustRelayFee)) { Q_EMIT message(tr("Payment request error"), tr("Requested payment amount of %1 is too small (considered dust).") .arg(BitcoinUnits::formatWithUnit(optionsModel->getDisplayUnit(), sendingTo.second)), CClientUIInterface::MSG_ERROR); diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp index f5b63c1ecd..b3cb6a718c 100644 --- a/src/wallet/feebumper.cpp +++ b/src/wallet/feebumper.cpp @@ -214,7 +214,7 @@ CFeeBumper::CFeeBumper(const CWallet *pWallet, const uint256 txidIn, int newConf // If the output would become dust, discard it (converting the dust to fee) poutput->nValue -= nDelta; - if (poutput->nValue <= poutput->GetDustThreshold(::dustRelayFee)) { + if (poutput->nValue <= GetDustThreshold(*poutput, ::dustRelayFee)) { LogPrint(BCLog::RPC, "Bumping fee and discarding dust output\n"); nNewFee += poutput->nValue; mtx.vout.erase(mtx.vout.begin() + nOutput); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index fa82c9c9df..ea329d6ebe 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2449,7 +2449,7 @@ bool CWallet::CreateTransaction(const std::vector& vecSend, CWalletT } } - if (txout.IsDust(dustRelayFee)) + if (IsDust(txout, ::dustRelayFee)) { if (recipient.fSubtractFeeFromAmount && nFeeRet > 0) { @@ -2514,16 +2514,16 @@ bool CWallet::CreateTransaction(const std::vector& vecSend, CWalletT // We do not move dust-change to fees, because the sender would end up paying more than requested. // This would be against the purpose of the all-inclusive feature. // So instead we raise the change and deduct from the recipient. - if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(dustRelayFee)) + if (nSubtractFeeFromAmount > 0 && IsDust(newTxOut, ::dustRelayFee)) { - CAmount nDust = newTxOut.GetDustThreshold(dustRelayFee) - newTxOut.nValue; + CAmount nDust = GetDustThreshold(newTxOut, ::dustRelayFee) - newTxOut.nValue; newTxOut.nValue += nDust; // raise change until no more dust for (unsigned int i = 0; i < vecSend.size(); i++) // subtract from first recipient { if (vecSend[i].fSubtractFeeFromAmount) { txNew.vout[i].nValue -= nDust; - if (txNew.vout[i].IsDust(dustRelayFee)) + if (IsDust(txNew.vout[i], ::dustRelayFee)) { strFailReason = _("The transaction amount is too small to send after the fee has been deducted"); return false; @@ -2535,7 +2535,7 @@ bool CWallet::CreateTransaction(const std::vector& vecSend, CWalletT // Never create dust outputs; if we would, just // add the dust to the fee. - if (newTxOut.IsDust(dustRelayFee)) + if (IsDust(newTxOut, ::dustRelayFee)) { nChangePosInOut = -1; nFeeRet += nChange;