Skip to content

Commit 330bb5a

Browse files
committed
Consensus: Minimal way to move dust out of consensus
1 parent 35da2ae commit 330bb5a

File tree

8 files changed

+54
-50
lines changed

8 files changed

+54
-50
lines changed

src/policy/policy.cpp

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,43 @@
1515

1616
#include <boost/foreach.hpp>
1717

18+
CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFee)
19+
{
20+
// "Dust" is defined in terms of dustRelayFee,
21+
// which has units satoshis-per-kilobyte.
22+
// If you'd pay more than 1/3 in fees
23+
// to spend something, then we consider it dust.
24+
// A typical spendable non-segwit txout is 34 bytes big, and will
25+
// need a CTxIn of at least 148 bytes to spend:
26+
// so dust is a spendable txout less than
27+
// 546*dustRelayFee/1000 (in satoshis).
28+
// A typical spendable segwit txout is 31 bytes big, and will
29+
// need a CTxIn of at least 67 bytes to spend:
30+
// so dust is a spendable txout less than
31+
// 294*dustRelayFee/1000 (in satoshis).
32+
if (txout.scriptPubKey.IsUnspendable())
33+
return 0;
34+
35+
size_t nSize = GetSerializeSize(txout, SER_DISK, 0);
36+
int witnessversion = 0;
37+
std::vector<unsigned char> witnessprogram;
38+
39+
if (txout.scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) {
40+
// sum the sizes of the parts of a transaction input
41+
// with 75% segwit discount applied to the script size.
42+
nSize += (32 + 4 + 1 + (107 / WITNESS_SCALE_FACTOR) + 4);
43+
} else {
44+
nSize += (32 + 4 + 1 + 107 + 4); // the 148 mentioned above
45+
}
46+
47+
return 3 * dustRelayFee.GetFee(nSize);
48+
}
49+
50+
bool IsDust(const CTxOut& txout, const CFeeRate& dustRelayFee)
51+
{
52+
return (txout.nValue < GetDustThreshold(txout, dustRelayFee));
53+
}
54+
1855
/**
1956
* Check transaction inputs to mitigate two
2057
* potential denial-of-service attacks:
@@ -106,7 +143,7 @@ bool IsStandardTx(const CTransaction& tx, std::string& reason, const bool witnes
106143
else if ((whichType == TX_MULTISIG) && (!fIsBareMultisigStd)) {
107144
reason = "bare-multisig";
108145
return false;
109-
} else if (txout.IsDust(dustRelayFee)) {
146+
} else if (IsDust(txout, ::dustRelayFee)) {
110147
reason = "dust";
111148
return false;
112149
}

src/policy/policy.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <string>
1414

1515
class CCoinsViewCache;
16+
class CTxOut;
1617

1718
/** Default for -blockmaxsize, which controls the maximum size of block the mining code will create **/
1819
static const unsigned int DEFAULT_BLOCK_MAX_SIZE = 750000;
@@ -72,6 +73,10 @@ static const unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS = STANDARD_SCRIPT_
7273
static const unsigned int STANDARD_LOCKTIME_VERIFY_FLAGS = LOCKTIME_VERIFY_SEQUENCE |
7374
LOCKTIME_MEDIAN_TIME_PAST;
7475

76+
CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFee);
77+
78+
bool IsDust(const CTxOut& txout, const CFeeRate& dustRelayFee);
79+
7580
bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType, const bool witnessEnabled = false);
7681
/**
7782
* Check for standard transaction types

src/primitives/transaction.h

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -161,43 +161,6 @@ class CTxOut
161161
return (nValue == -1);
162162
}
163163

164-
CAmount GetDustThreshold(const CFeeRate &minRelayTxFee) const
165-
{
166-
// "Dust" is defined in terms of CTransaction::minRelayTxFee,
167-
// which has units satoshis-per-kilobyte.
168-
// If you'd pay more than 1/3 in fees
169-
// to spend something, then we consider it dust.
170-
// A typical spendable non-segwit txout is 34 bytes big, and will
171-
// need a CTxIn of at least 148 bytes to spend:
172-
// so dust is a spendable txout less than
173-
// 546*minRelayTxFee/1000 (in satoshis).
174-
// A typical spendable segwit txout is 31 bytes big, and will
175-
// need a CTxIn of at least 67 bytes to spend:
176-
// so dust is a spendable txout less than
177-
// 294*minRelayTxFee/1000 (in satoshis).
178-
if (scriptPubKey.IsUnspendable())
179-
return 0;
180-
181-
size_t nSize = GetSerializeSize(*this, SER_DISK, 0);
182-
int witnessversion = 0;
183-
std::vector<unsigned char> witnessprogram;
184-
185-
if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) {
186-
// sum the sizes of the parts of a transaction input
187-
// with 75% segwit discount applied to the script size.
188-
nSize += (32 + 4 + 1 + (107 / WITNESS_SCALE_FACTOR) + 4);
189-
} else {
190-
nSize += (32 + 4 + 1 + 107 + 4); // the 148 mentioned above
191-
}
192-
193-
return 3 * minRelayTxFee.GetFee(nSize);
194-
}
195-
196-
bool IsDust(const CFeeRate &minRelayTxFee) const
197-
{
198-
return (nValue < GetDustThreshold(minRelayTxFee));
199-
}
200-
201164
friend bool operator==(const CTxOut& a, const CTxOut& b)
202165
{
203166
return (a.nValue == b.nValue &&

src/qt/coincontroldialog.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -434,8 +434,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
434434
{
435435
CTxOut txout(amount, (CScript)std::vector<unsigned char>(24, 0));
436436
txDummy.vout.push_back(txout);
437-
if (txout.IsDust(dustRelayFee))
438-
fDust = true;
437+
fDust |= IsDust(txout, ::dustRelayFee);
439438
}
440439
}
441440

@@ -527,10 +526,10 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
527526
if (nChange > 0 && nChange < MIN_CHANGE)
528527
{
529528
CTxOut txout(nChange, (CScript)std::vector<unsigned char>(24, 0));
530-
if (txout.IsDust(dustRelayFee))
529+
if (IsDust(txout, ::dustRelayFee))
531530
{
532531
if (CoinControlDialog::fSubtractFeeFromAmount) // dust-change will be raised until no dust
533-
nChange = txout.GetDustThreshold(dustRelayFee);
532+
nChange = GetDustThreshold(txout, ::dustRelayFee);
534533
else
535534
{
536535
nPayFee += nChange;

src/qt/guiutil.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ bool isDust(const QString& address, const CAmount& amount)
251251
CTxDestination dest = CBitcoinAddress(address.toStdString()).Get();
252252
CScript script = GetScriptForDestination(dest);
253253
CTxOut txOut(amount, script);
254-
return txOut.IsDust(dustRelayFee);
254+
return IsDust(txOut, ::dustRelayFee);
255255
}
256256

257257
QString HtmlEscape(const QString& str, bool fMultiLine)

src/qt/paymentserver.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -580,7 +580,7 @@ bool PaymentServer::processPaymentRequest(const PaymentRequestPlus& request, Sen
580580

581581
// Extract and check amounts
582582
CTxOut txOut(sendingTo.second, sendingTo.first);
583-
if (txOut.IsDust(dustRelayFee)) {
583+
if (IsDust(txOut, ::dustRelayFee)) {
584584
Q_EMIT message(tr("Payment request error"), tr("Requested payment amount of %1 is too small (considered dust).")
585585
.arg(BitcoinUnits::formatWithUnit(optionsModel->getDisplayUnit(), sendingTo.second)),
586586
CClientUIInterface::MSG_ERROR);

src/wallet/feebumper.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,7 @@ CFeeBumper::CFeeBumper(const CWallet *pWallet, const uint256 txidIn, int newConf
214214

215215
// If the output would become dust, discard it (converting the dust to fee)
216216
poutput->nValue -= nDelta;
217-
if (poutput->nValue <= poutput->GetDustThreshold(::dustRelayFee)) {
217+
if (poutput->nValue <= GetDustThreshold(*poutput, ::dustRelayFee)) {
218218
LogPrint(BCLog::RPC, "Bumping fee and discarding dust output\n");
219219
nNewFee += poutput->nValue;
220220
mtx.vout.erase(mtx.vout.begin() + nOutput);

src/wallet/wallet.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2449,7 +2449,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
24492449
}
24502450
}
24512451

2452-
if (txout.IsDust(dustRelayFee))
2452+
if (IsDust(txout, ::dustRelayFee))
24532453
{
24542454
if (recipient.fSubtractFeeFromAmount && nFeeRet > 0)
24552455
{
@@ -2514,16 +2514,16 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
25142514
// We do not move dust-change to fees, because the sender would end up paying more than requested.
25152515
// This would be against the purpose of the all-inclusive feature.
25162516
// So instead we raise the change and deduct from the recipient.
2517-
if (nSubtractFeeFromAmount > 0 && newTxOut.IsDust(dustRelayFee))
2517+
if (nSubtractFeeFromAmount > 0 && IsDust(newTxOut, ::dustRelayFee))
25182518
{
2519-
CAmount nDust = newTxOut.GetDustThreshold(dustRelayFee) - newTxOut.nValue;
2519+
CAmount nDust = GetDustThreshold(newTxOut, ::dustRelayFee) - newTxOut.nValue;
25202520
newTxOut.nValue += nDust; // raise change until no more dust
25212521
for (unsigned int i = 0; i < vecSend.size(); i++) // subtract from first recipient
25222522
{
25232523
if (vecSend[i].fSubtractFeeFromAmount)
25242524
{
25252525
txNew.vout[i].nValue -= nDust;
2526-
if (txNew.vout[i].IsDust(dustRelayFee))
2526+
if (IsDust(txNew.vout[i], ::dustRelayFee))
25272527
{
25282528
strFailReason = _("The transaction amount is too small to send after the fee has been deducted");
25292529
return false;
@@ -2535,7 +2535,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
25352535

25362536
// Never create dust outputs; if we would, just
25372537
// add the dust to the fee.
2538-
if (newTxOut.IsDust(dustRelayFee))
2538+
if (IsDust(newTxOut, ::dustRelayFee))
25392539
{
25402540
nChangePosInOut = -1;
25412541
nFeeRet += nChange;

0 commit comments

Comments
 (0)