diff --git a/src/policy/policy.h b/src/policy/policy.h --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -14,6 +14,7 @@ class CCoinsViewCache; class CTransaction; +class CTxOut; /** * Default for -blockmaxsize, which controls the maximum size of block the @@ -100,6 +101,10 @@ static const uint32_t STANDARD_LOCKTIME_VERIFY_FLAGS = LOCKTIME_VERIFY_SEQUENCE | LOCKTIME_MEDIAN_TIME_PAST; +Amount GetDustThreshold(const CTxOut &txout, const CFeeRate &dustRelayFee); + +bool IsDust(const CTxOut &txout, const CFeeRate &dustRelayFee); + /** * Used as the flags parameters to check for sigops as if OP_CHECKDATASIG is * enabled. Can be removed after OP_CHECKDATASIG is activated as the flag is diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -14,6 +14,33 @@ #include #include +Amount 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 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). + */ + if (txout.scriptPubKey.IsUnspendable()) { + return Amount::zero(); + } + + size_t nSize = GetSerializeSize(txout, SER_DISK, 0); + + // the 148 mentioned above + nSize += (32 + 4 + 1 + 107 + 4); + + 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: * @@ -95,7 +122,7 @@ } 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/primitives/transaction.h b/src/primitives/transaction.h --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -161,34 +161,6 @@ bool IsNull() const { return nValue == -SATOSHI; } - Amount 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 Amount::zero(); - } - - size_t nSize = GetSerializeSize(*this, SER_DISK, 0); - - // the 148 mentioned above - nSize += (32 + 4 + 1 + 107 + 4); - - 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 && a.scriptPubKey == b.scriptPubKey); } diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -469,9 +469,7 @@ CTxOut txout(amount, static_cast(std::vector(24, 0))); txDummy.vout.push_back(txout); - if (txout.IsDust(model->node().getDustRelayFee())) { - fDust = true; - } + fDust |= IsDust(txout, model->node().getDustRelayFee()); } } @@ -560,11 +558,11 @@ if (nChange > Amount::zero() && nChange < MIN_CHANGE) { CTxOut txout(nChange, static_cast(std::vector(24, 0))); - if (txout.IsDust(model->node().getDustRelayFee())) { + if (IsDust(txout, model->node().getDustRelayFee())) { // dust-change will be raised until no dust if (CoinControlDialog::fSubtractFeeFromAmount) { - nChange = txout.GetDustThreshold( - model->node().getDustRelayFee()); + nChange = GetDustThreshold( + txout, model->node().getDustRelayFee()); } else { nPayFee += nChange; nChange = Amount::zero(); diff --git a/src/qt/guiutil.cpp b/src/qt/guiutil.cpp --- a/src/qt/guiutil.cpp +++ b/src/qt/guiutil.cpp @@ -290,7 +290,7 @@ CTxDestination dest = DecodeDestination(address.toStdString(), chainParams); CScript script = GetScriptForDestination(dest); CTxOut txOut(amount, script); - return txOut.IsDust(node.getDustRelayFee()); + return IsDust(txOut, node.getDustRelayFee()); } QString HtmlEscape(const QString &str, bool fMultiLine) { diff --git a/src/qt/paymentserver.cpp b/src/qt/paymentserver.cpp --- a/src/qt/paymentserver.cpp +++ b/src/qt/paymentserver.cpp @@ -634,7 +634,7 @@ // Extract and check amounts CTxOut txOut(Amount(sendingTo.second), sendingTo.first); - if (txOut.IsDust(optionsModel->node().getDustRelayFee())) { + if (IsDust(txOut, optionsModel->node().getDustRelayFee())) { Q_EMIT message( tr("Payment request error"), tr("Requested payment amount of %1 is too small (considered " diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2992,7 +2992,7 @@ } } - if (txout.IsDust(dustRelayFee)) { + if (IsDust(txout, dustRelayFee)) { if (recipient.fSubtractFeeFromAmount && nFeeRet > Amount::zero()) { if (txout.nValue < Amount::zero()) { @@ -3050,8 +3050,8 @@ // purpose of the all-inclusive feature. So instead we raise the // change and deduct from the recipient. if (nSubtractFeeFromAmount > 0 && - newTxOut.IsDust(dustRelayFee)) { - Amount nDust = newTxOut.GetDustThreshold(dustRelayFee) - + IsDust(newTxOut, dustRelayFee)) { + Amount nDust = GetDustThreshold(newTxOut, dustRelayFee) - newTxOut.nValue; // Raise change until no more dust. newTxOut.nValue += nDust; @@ -3059,7 +3059,7 @@ for (unsigned int i = 0; i < vecSend.size(); i++) { 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 " @@ -3074,7 +3074,7 @@ // 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; } else { @@ -3150,7 +3150,7 @@ Amount fee_needed_for_change = GetMinimumFee( change_prototype_size, g_mempool, coinControl); Amount minimum_value_for_change = - change_prototype_txout.GetDustThreshold(dustRelayFee); + GetDustThreshold(change_prototype_txout, dustRelayFee); Amount max_excess_fee = fee_needed_for_change + minimum_value_for_change; if (nFeeRet > nFeeNeeded + max_excess_fee &&