Skip to content

Commit 8de9bb5

Browse files
committed
Define dust transaction outputs, and make them non-standard
1 parent b8e1dc2 commit 8de9bb5

6 files changed

+74
-27
lines changed

src/main.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,7 @@ bool CTransaction::IsStandard() const
384384
BOOST_FOREACH(const CTxOut& txout, vout) {
385385
if (!::IsStandard(txout.scriptPubKey))
386386
return false;
387-
if (txout.nValue == 0)
387+
if (txout.IsDust())
388388
return false;
389389
}
390390
return true;

src/main.h

+18
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,24 @@ class CTxOut
439439
return !(a == b);
440440
}
441441

442+
size_t size() const
443+
{
444+
return sizeof(nValue)+scriptPubKey.size();
445+
}
446+
447+
bool IsDust() const
448+
{
449+
// "Dust" is defined in terms of MIN_RELAY_TX_FEE, which
450+
// has units satoshis-per-kilobyte.
451+
// If you'd pay more than 1/3 in fees
452+
// to spend something, then we consider it dust.
453+
// A typical txout is 32 bytes big, and will
454+
// need a CTxIn of at least 148 bytes to spend,
455+
// so dust is a txout less than 54 uBTC
456+
// (5400 satoshis)
457+
return ((nValue*1000)/(3*(size()+148)) < MIN_RELAY_TX_FEE);
458+
}
459+
442460
std::string ToString() const
443461
{
444462
if (scriptPubKey.size() < 6)

src/test/script_P2SH_tests.cpp

+4-1
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,9 @@ BOOST_AUTO_TEST_CASE(sign)
7878
for (int i = 0; i < 4; i++)
7979
{
8080
txFrom.vout[i].scriptPubKey = evalScripts[i];
81+
txFrom.vout[i].nValue = COIN;
8182
txFrom.vout[i+4].scriptPubKey = standardScripts[i];
83+
txFrom.vout[i+4].nValue = COIN;
8284
}
8385
BOOST_CHECK(txFrom.IsStandard());
8486

@@ -169,6 +171,7 @@ BOOST_AUTO_TEST_CASE(set)
169171
for (int i = 0; i < 4; i++)
170172
{
171173
txFrom.vout[i].scriptPubKey = outer[i];
174+
txFrom.vout[i].nValue = CENT;
172175
}
173176
BOOST_CHECK(txFrom.IsStandard());
174177

@@ -179,7 +182,7 @@ BOOST_AUTO_TEST_CASE(set)
179182
txTo[i].vout.resize(1);
180183
txTo[i].vin[0].prevout.n = i;
181184
txTo[i].vin[0].prevout.hash = txFrom.GetHash();
182-
txTo[i].vout[0].nValue = 1;
185+
txTo[i].vout[0].nValue = 1*CENT;
183186
txTo[i].vout[0].scriptPubKey = inner[i];
184187
BOOST_CHECK_MESSAGE(IsMine(keystore, txFrom.vout[i].scriptPubKey), strprintf("IsMine %d", i));
185188
}

src/test/transaction_tests.cpp

+22-12
Original file line numberDiff line numberDiff line change
@@ -242,24 +242,34 @@ BOOST_AUTO_TEST_CASE(test_Get)
242242
BOOST_CHECK(!t1.AreInputsStandard(coins));
243243
}
244244

245-
BOOST_AUTO_TEST_CASE(test_GetThrow)
245+
BOOST_AUTO_TEST_CASE(test_IsStandard)
246246
{
247247
CBasicKeyStore keystore;
248248
CCoinsView coinsDummy;
249249
CCoinsViewCache coins(coinsDummy);
250250
std::vector<CTransaction> dummyTransactions = SetupDummyInputs(keystore, coins);
251251

252-
CTransaction t1;
253-
t1.vin.resize(3);
254-
t1.vin[0].prevout.hash = dummyTransactions[0].GetHash();
255-
t1.vin[0].prevout.n = 0;
256-
t1.vin[1].prevout.hash = dummyTransactions[1].GetHash();;
257-
t1.vin[1].prevout.n = 0;
258-
t1.vin[2].prevout.hash = dummyTransactions[1].GetHash();;
259-
t1.vin[2].prevout.n = 1;
260-
t1.vout.resize(2);
261-
t1.vout[0].nValue = 90*CENT;
262-
t1.vout[0].scriptPubKey << OP_1;
252+
CTransaction t;
253+
t.vin.resize(1);
254+
t.vin[0].prevout.hash = dummyTransactions[0].GetHash();
255+
t.vin[0].prevout.n = 1;
256+
t.vin[0].scriptSig << std::vector<unsigned char>(65, 0);
257+
t.vout.resize(1);
258+
t.vout[0].nValue = 90*CENT;
259+
CKey key;
260+
key.MakeNewKey(true);
261+
t.vout[0].scriptPubKey.SetDestination(key.GetPubKey().GetID());
262+
263+
BOOST_CHECK(t.IsStandard());
264+
265+
t.vout[0].nValue = 5011; // dust
266+
BOOST_CHECK(!t.IsStandard());
267+
268+
t.vout[0].nValue = 6011; // not dust
269+
BOOST_CHECK(t.IsStandard());
270+
271+
t.vout[0].scriptPubKey = CScript() << OP_1;
272+
BOOST_CHECK(!t.IsStandard());
263273
}
264274

265275
BOOST_AUTO_TEST_SUITE_END()

src/test/wallet_tests.cpp

+8-9
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,12 @@ static vector<COutput> vCoins;
2121

2222
static void add_coin(int64 nValue, int nAge = 6*24, bool fIsFromMe = false, int nInput=0)
2323
{
24-
static int i;
25-
CTransaction* tx = new CTransaction;
26-
tx->nLockTime = i++; // so all transactions get different hashes
27-
tx->vout.resize(nInput+1);
28-
tx->vout[nInput].nValue = nValue;
29-
CWalletTx* wtx = new CWalletTx(&wallet, *tx);
30-
delete tx;
24+
static int nextLockTime = 0;
25+
CTransaction tx;
26+
tx.nLockTime = nextLockTime++; // so all transactions get different hashes
27+
tx.vout.resize(nInput+1);
28+
tx.vout[nInput].nValue = nValue;
29+
CWalletTx* wtx = new CWalletTx(&wallet, tx);
3130
if (fIsFromMe)
3231
{
3332
// IsFromMe() returns (GetDebit() > 0), and GetDebit() is 0 if vin.empty(),
@@ -55,8 +54,8 @@ static bool equal_sets(CoinSet a, CoinSet b)
5554

5655
BOOST_AUTO_TEST_CASE(coin_selection_tests)
5756
{
58-
static CoinSet setCoinsRet, setCoinsRet2;
59-
static int64 nValueRet;
57+
CoinSet setCoinsRet, setCoinsRet2;
58+
int64 nValueRet;
6059

6160
// test multiple times to allow for differences in the shuffle order
6261
for (int i = 0; i < RUN_TESTS; i++)

src/wallet.cpp

+21-4
Original file line numberDiff line numberDiff line change
@@ -1162,7 +1162,12 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64> >& vecSend, CW
11621162
double dPriority = 0;
11631163
// vouts to the payees
11641164
BOOST_FOREACH (const PAIRTYPE(CScript, int64)& s, vecSend)
1165-
wtxNew.vout.push_back(CTxOut(s.second, s.first));
1165+
{
1166+
CTxOut txout(s.second, s.first);
1167+
if (txout.IsDust())
1168+
return false;
1169+
wtxNew.vout.push_back(txout);
1170+
}
11661171

11671172
// Choose coins to use
11681173
set<pair<const CWalletTx*,unsigned int> > setCoins;
@@ -1208,9 +1213,21 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64> >& vecSend, CW
12081213
CScript scriptChange;
12091214
scriptChange.SetDestination(vchPubKey.GetID());
12101215

1211-
// Insert change txn at random position:
1212-
vector<CTxOut>::iterator position = wtxNew.vout.begin()+GetRandInt(wtxNew.vout.size()+1);
1213-
wtxNew.vout.insert(position, CTxOut(nChange, scriptChange));
1216+
CTxOut newTxOut(nChange, scriptChange);
1217+
1218+
// Never create dust outputs; if we would, just
1219+
// add the dust to the fee.
1220+
if (newTxOut.IsDust())
1221+
{
1222+
nFeeRet += nChange;
1223+
reservekey.ReturnKey();
1224+
}
1225+
else
1226+
{
1227+
// Insert change txn at random position:
1228+
vector<CTxOut>::iterator position = wtxNew.vout.begin()+GetRandInt(wtxNew.vout.size()+1);
1229+
wtxNew.vout.insert(position, newTxOut);
1230+
}
12141231
}
12151232
else
12161233
reservekey.ReturnKey();

0 commit comments

Comments
 (0)