aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGavin Andresen <gavinandresen@gmail.com>2013-04-24 18:27:00 -0400
committerGavin Andresen <gavinandresen@gmail.com>2013-05-03 10:52:09 -0400
commit8de9bb53af32f7f6b09c06f831f2c0a7b4e95303 (patch)
treeafb7c68d3fd15203308f38a47a3cc3875d476a2f
parentb8e1dc2e53b9d19df26a87686dfd435b3b346f9c (diff)
Define dust transaction outputs, and make them non-standard
-rw-r--r--src/main.cpp2
-rw-r--r--src/main.h18
-rw-r--r--src/test/script_P2SH_tests.cpp5
-rw-r--r--src/test/transaction_tests.cpp34
-rw-r--r--src/test/wallet_tests.cpp17
-rw-r--r--src/wallet.cpp25
6 files changed, 74 insertions, 27 deletions
diff --git a/src/main.cpp b/src/main.cpp
index aace382d8b..d0d6a99eb1 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -384,7 +384,7 @@ bool CTransaction::IsStandard() const
BOOST_FOREACH(const CTxOut& txout, vout) {
if (!::IsStandard(txout.scriptPubKey))
return false;
- if (txout.nValue == 0)
+ if (txout.IsDust())
return false;
}
return true;
diff --git a/src/main.h b/src/main.h
index 24b2cb2aa6..8c1e6d265c 100644
--- a/src/main.h
+++ b/src/main.h
@@ -439,6 +439,24 @@ public:
return !(a == b);
}
+ size_t size() const
+ {
+ return sizeof(nValue)+scriptPubKey.size();
+ }
+
+ bool IsDust() const
+ {
+ // "Dust" is defined in terms of MIN_RELAY_TX_FEE, 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 txout is 32 bytes big, and will
+ // need a CTxIn of at least 148 bytes to spend,
+ // so dust is a txout less than 54 uBTC
+ // (5400 satoshis)
+ return ((nValue*1000)/(3*(size()+148)) < MIN_RELAY_TX_FEE);
+ }
+
std::string ToString() const
{
if (scriptPubKey.size() < 6)
diff --git a/src/test/script_P2SH_tests.cpp b/src/test/script_P2SH_tests.cpp
index 3444726ca0..ee0d25a2f5 100644
--- a/src/test/script_P2SH_tests.cpp
+++ b/src/test/script_P2SH_tests.cpp
@@ -78,7 +78,9 @@ BOOST_AUTO_TEST_CASE(sign)
for (int i = 0; i < 4; i++)
{
txFrom.vout[i].scriptPubKey = evalScripts[i];
+ txFrom.vout[i].nValue = COIN;
txFrom.vout[i+4].scriptPubKey = standardScripts[i];
+ txFrom.vout[i+4].nValue = COIN;
}
BOOST_CHECK(txFrom.IsStandard());
@@ -169,6 +171,7 @@ BOOST_AUTO_TEST_CASE(set)
for (int i = 0; i < 4; i++)
{
txFrom.vout[i].scriptPubKey = outer[i];
+ txFrom.vout[i].nValue = CENT;
}
BOOST_CHECK(txFrom.IsStandard());
@@ -179,7 +182,7 @@ BOOST_AUTO_TEST_CASE(set)
txTo[i].vout.resize(1);
txTo[i].vin[0].prevout.n = i;
txTo[i].vin[0].prevout.hash = txFrom.GetHash();
- txTo[i].vout[0].nValue = 1;
+ txTo[i].vout[0].nValue = 1*CENT;
txTo[i].vout[0].scriptPubKey = inner[i];
BOOST_CHECK_MESSAGE(IsMine(keystore, txFrom.vout[i].scriptPubKey), strprintf("IsMine %d", i));
}
diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp
index f44d46fdb8..ddff2acd4e 100644
--- a/src/test/transaction_tests.cpp
+++ b/src/test/transaction_tests.cpp
@@ -242,24 +242,34 @@ BOOST_AUTO_TEST_CASE(test_Get)
BOOST_CHECK(!t1.AreInputsStandard(coins));
}
-BOOST_AUTO_TEST_CASE(test_GetThrow)
+BOOST_AUTO_TEST_CASE(test_IsStandard)
{
CBasicKeyStore keystore;
CCoinsView coinsDummy;
CCoinsViewCache coins(coinsDummy);
std::vector<CTransaction> dummyTransactions = SetupDummyInputs(keystore, coins);
- CTransaction t1;
- t1.vin.resize(3);
- t1.vin[0].prevout.hash = dummyTransactions[0].GetHash();
- t1.vin[0].prevout.n = 0;
- t1.vin[1].prevout.hash = dummyTransactions[1].GetHash();;
- t1.vin[1].prevout.n = 0;
- t1.vin[2].prevout.hash = dummyTransactions[1].GetHash();;
- t1.vin[2].prevout.n = 1;
- t1.vout.resize(2);
- t1.vout[0].nValue = 90*CENT;
- t1.vout[0].scriptPubKey << OP_1;
+ CTransaction t;
+ t.vin.resize(1);
+ t.vin[0].prevout.hash = dummyTransactions[0].GetHash();
+ t.vin[0].prevout.n = 1;
+ t.vin[0].scriptSig << std::vector<unsigned char>(65, 0);
+ t.vout.resize(1);
+ t.vout[0].nValue = 90*CENT;
+ CKey key;
+ key.MakeNewKey(true);
+ t.vout[0].scriptPubKey.SetDestination(key.GetPubKey().GetID());
+
+ BOOST_CHECK(t.IsStandard());
+
+ t.vout[0].nValue = 5011; // dust
+ BOOST_CHECK(!t.IsStandard());
+
+ t.vout[0].nValue = 6011; // not dust
+ BOOST_CHECK(t.IsStandard());
+
+ t.vout[0].scriptPubKey = CScript() << OP_1;
+ BOOST_CHECK(!t.IsStandard());
}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/src/test/wallet_tests.cpp b/src/test/wallet_tests.cpp
index a4cbfaee41..a14f6b2b70 100644
--- a/src/test/wallet_tests.cpp
+++ b/src/test/wallet_tests.cpp
@@ -21,13 +21,12 @@ static vector<COutput> vCoins;
static void add_coin(int64 nValue, int nAge = 6*24, bool fIsFromMe = false, int nInput=0)
{
- static int i;
- CTransaction* tx = new CTransaction;
- tx->nLockTime = i++; // so all transactions get different hashes
- tx->vout.resize(nInput+1);
- tx->vout[nInput].nValue = nValue;
- CWalletTx* wtx = new CWalletTx(&wallet, *tx);
- delete tx;
+ static int nextLockTime = 0;
+ CTransaction tx;
+ tx.nLockTime = nextLockTime++; // so all transactions get different hashes
+ tx.vout.resize(nInput+1);
+ tx.vout[nInput].nValue = nValue;
+ CWalletTx* wtx = new CWalletTx(&wallet, tx);
if (fIsFromMe)
{
// IsFromMe() returns (GetDebit() > 0), and GetDebit() is 0 if vin.empty(),
@@ -55,8 +54,8 @@ static bool equal_sets(CoinSet a, CoinSet b)
BOOST_AUTO_TEST_CASE(coin_selection_tests)
{
- static CoinSet setCoinsRet, setCoinsRet2;
- static int64 nValueRet;
+ CoinSet setCoinsRet, setCoinsRet2;
+ int64 nValueRet;
// test multiple times to allow for differences in the shuffle order
for (int i = 0; i < RUN_TESTS; i++)
diff --git a/src/wallet.cpp b/src/wallet.cpp
index c7eb4f74e8..1554b15768 100644
--- a/src/wallet.cpp
+++ b/src/wallet.cpp
@@ -1162,7 +1162,12 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64> >& vecSend, CW
double dPriority = 0;
// vouts to the payees
BOOST_FOREACH (const PAIRTYPE(CScript, int64)& s, vecSend)
- wtxNew.vout.push_back(CTxOut(s.second, s.first));
+ {
+ CTxOut txout(s.second, s.first);
+ if (txout.IsDust())
+ return false;
+ wtxNew.vout.push_back(txout);
+ }
// Choose coins to use
set<pair<const CWalletTx*,unsigned int> > setCoins;
@@ -1208,9 +1213,21 @@ bool CWallet::CreateTransaction(const vector<pair<CScript, int64> >& vecSend, CW
CScript scriptChange;
scriptChange.SetDestination(vchPubKey.GetID());
- // Insert change txn at random position:
- vector<CTxOut>::iterator position = wtxNew.vout.begin()+GetRandInt(wtxNew.vout.size()+1);
- wtxNew.vout.insert(position, CTxOut(nChange, scriptChange));
+ CTxOut newTxOut(nChange, scriptChange);
+
+ // Never create dust outputs; if we would, just
+ // add the dust to the fee.
+ if (newTxOut.IsDust())
+ {
+ nFeeRet += nChange;
+ reservekey.ReturnKey();
+ }
+ else
+ {
+ // Insert change txn at random position:
+ vector<CTxOut>::iterator position = wtxNew.vout.begin()+GetRandInt(wtxNew.vout.size()+1);
+ wtxNew.vout.insert(position, newTxOut);
+ }
}
else
reservekey.ReturnKey();