diff --git a/src/main.h b/src/main.h index 666a1293c..155ad413d 100644 --- a/src/main.h +++ b/src/main.h @@ -59,7 +59,7 @@ static const unsigned int BLOCKFILE_CHUNK_SIZE = 0x1000000; // 16 MiB /** The pre-allocation chunk size for rev?????.dat files (since 0.8) */ static const unsigned int UNDOFILE_CHUNK_SIZE = 0x100000; // 1 MiB /** Dust Soft Limit, allowed with additional fee per output */ -static const int64_t DUST_SOFT_LIMIT = 100000000; +static const int64_t DUST_SOFT_LIMIT = COIN; /** Dust Hard Limit, ignored as wallet inputs (mininput default) */ static const int64_t DUST_HARD_LIMIT = 1000000; /** Coinbase transaction outputs can only be spent after this number of new blocks (network rule) */ diff --git a/src/test/wallet_tests.cpp b/src/test/wallet_tests.cpp index bd0517ae0..b32daba99 100644 --- a/src/test/wallet_tests.cpp +++ b/src/test/wallet_tests.cpp @@ -130,51 +130,51 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) // now clear out the wallet and start again to test choosing between subsets of smaller coins and the next biggest coin empty_wallet(); - add_coin( 6*CENT); - add_coin( 7*CENT); - add_coin( 8*CENT); - add_coin(20*CENT); - add_coin(30*CENT); // now we have 6+7+8+20+30 = 71 cents total + add_coin( 6*COIN); + add_coin( 7*COIN); + add_coin( 8*COIN); + add_coin(20*COIN); + add_coin(30*COIN); // now we have 6+7+8+20+30 = 71 coins total // check that we have 71 and not 72 - BOOST_CHECK( wallet.SelectCoinsMinConf(71 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); - BOOST_CHECK(!wallet.SelectCoinsMinConf(72 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK( wallet.SelectCoinsMinConf(71 * COIN, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK(!wallet.SelectCoinsMinConf(72 * COIN, 1, 1, vCoins, setCoinsRet, nValueRet)); - // now try making 16 cents. the best smaller coins can do is 6+7+8 = 21; not as good at the next biggest coin, 20 - BOOST_CHECK( wallet.SelectCoinsMinConf(16 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); - BOOST_CHECK_EQUAL(nValueRet, 20 * CENT); // we should get 20 in one coin + // now try making 16 coins. the best smaller coins can do is 6+7+8 = 21; not as good at the next biggest coin, 20 + BOOST_CHECK( wallet.SelectCoinsMinConf(16 * COIN, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK_EQUAL(nValueRet, 20 * COIN); // we should get 20 in one coin BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); - add_coin( 5*CENT); // now we have 5+6+7+8+20+30 = 75 cents total + add_coin( 5*COIN); // now we have 5+6+7+8+20+30 = 75 coins total - // now if we try making 16 cents again, the smaller coins can make 5+6+7 = 18 cents, better than the next biggest coin, 20 - BOOST_CHECK( wallet.SelectCoinsMinConf(16 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); - BOOST_CHECK_EQUAL(nValueRet, 18 * CENT); // we should get 18 in 3 coins + // now if we try making 16 coins again, the smaller coins can make 5+6+7 = 18 coins, better than the next biggest coin, 20 + BOOST_CHECK( wallet.SelectCoinsMinConf(16 * COIN, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK_EQUAL(nValueRet, 18 * COIN); // we should get 18 in 3 coins BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U); - add_coin( 18*CENT); // now we have 5+6+7+8+18+20+30 + add_coin( 18*COIN); // now we have 5+6+7+8+18+20+30 - // and now if we try making 16 cents again, the smaller coins can make 5+6+7 = 18 cents, the same as the next biggest coin, 18 - BOOST_CHECK( wallet.SelectCoinsMinConf(16 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); - BOOST_CHECK_EQUAL(nValueRet, 18 * CENT); // we should get 18 in 1 coin + // and now if we try making 16 coins again, the smaller coins can make 5+6+7 = 18 coins, the same as the next biggest coin, 18 + BOOST_CHECK( wallet.SelectCoinsMinConf(16 * COIN, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK_EQUAL(nValueRet, 18 * COIN); // we should get 18 in 1 coin BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); // because in the event of a tie, the biggest coin wins - // now try making 11 cents. we should get 5+6 - BOOST_CHECK( wallet.SelectCoinsMinConf(11 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); - BOOST_CHECK_EQUAL(nValueRet, 11 * CENT); + // now try making 11 coins. we should get 5+6 + BOOST_CHECK( wallet.SelectCoinsMinConf(11 * COIN, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK_EQUAL(nValueRet, 11 * COIN); BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U); // check that the smallest bigger coin is used - add_coin( 1*COIN); - add_coin( 2*COIN); - add_coin( 3*COIN); - add_coin( 4*COIN); // now we have 5+6+7+8+18+20+30+100+200+300+400 = 1094 cents - BOOST_CHECK( wallet.SelectCoinsMinConf(95 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); - BOOST_CHECK_EQUAL(nValueRet, 1 * COIN); // we should get 1 BTC in 1 coin + add_coin( 100*COIN); + add_coin( 200*COIN); + add_coin( 300*COIN); + add_coin( 400*COIN); // now we have 5+6+7+8+18+20+30+100+200+300+400 = 1094 coins + BOOST_CHECK( wallet.SelectCoinsMinConf(95 * COIN, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK_EQUAL(nValueRet, 100 * COIN); // we should get 200 DOGE in 1 coin. BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); - BOOST_CHECK( wallet.SelectCoinsMinConf(195 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet)); - BOOST_CHECK_EQUAL(nValueRet, 2 * COIN); // we should get 2 BTC in 1 coin + BOOST_CHECK( wallet.SelectCoinsMinConf(195 * COIN, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK_EQUAL(nValueRet, 200 * COIN); // we should get 3 DOGE in 1 coin BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U); // empty the wallet and start again, now with fractions of a cent, to test sub-cent change avoidance @@ -240,18 +240,18 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests) // test avoiding sub-cent change empty_wallet(); - add_coin(0.0005 * COIN); - add_coin(0.01 * COIN); + add_coin(0.05 * COIN); add_coin(1 * COIN); + add_coin(100 * COIN); - // trying to make 1.0001 from these three coins - BOOST_CHECK( wallet.SelectCoinsMinConf(1.0001 * COIN, 1, 1, vCoins, setCoinsRet, nValueRet)); - BOOST_CHECK_EQUAL(nValueRet, 1.0105 * COIN); // we should get all coins + // trying to make 100.01 from these three coins + BOOST_CHECK( wallet.SelectCoinsMinConf(100.01 * COIN, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK_EQUAL(nValueRet, 101.05 * COIN); // we should get all coins BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U); - // but if we try to make 0.999, we should take the bigger of the two small coins to avoid sub-cent change - BOOST_CHECK( wallet.SelectCoinsMinConf(0.999 * COIN, 1, 1, vCoins, setCoinsRet, nValueRet)); - BOOST_CHECK_EQUAL(nValueRet, 1.01 * COIN); // we should get 1 + 0.01 + // but if we try to make 99.9, we should take the bigger of the two small coins to avoid sub-cent change + BOOST_CHECK( wallet.SelectCoinsMinConf(99.9 * COIN, 1, 1, vCoins, setCoinsRet, nValueRet)); + BOOST_CHECK_EQUAL(nValueRet, 101 * COIN); // we should get 100 + 1 BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U); // test randomness diff --git a/src/wallet.cpp b/src/wallet.cpp index c6bb835e5..feeee86ce 100644 --- a/src/wallet.cpp +++ b/src/wallet.cpp @@ -1226,8 +1226,14 @@ bool CWallet::SelectCoinsMinConf(int64_t nTargetValue, int nConfMine, int nConfT nValueRet += coin.first; return true; } - else if (n < nTargetValue + COIN) + else if (n < nTargetValue + DUST_SOFT_LIMIT) { + // This coin is not sufficient to cover the target plus change above the dust + // limit. The dust limit is important here, as we don't want to leave change + // which cannot be spent (is below the network transaction fee). + + // Push the coin into an array for potential matching later, but keep trying to find + // an exact match vValue.push_back(coin); nTotalLower += n; } @@ -1262,13 +1268,13 @@ bool CWallet::SelectCoinsMinConf(int64_t nTargetValue, int nConfMine, int nConfT int64_t nBest; ApproximateBestSubset(vValue, nTotalLower, nTargetValue, vfBest, nBest, 1000); - if (nBest != nTargetValue && nTotalLower >= nTargetValue + COIN) - ApproximateBestSubset(vValue, nTotalLower, nTargetValue + COIN, vfBest, nBest, 1000); + if (nBest != nTargetValue && nTotalLower >= nTargetValue + DUST_SOFT_LIMIT) + ApproximateBestSubset(vValue, nTotalLower, nTargetValue + DUST_SOFT_LIMIT, vfBest, nBest, 1000); // If we have a bigger coin and (either the stochastic approximation didn't find a good solution, // or the next bigger coin is closer), return the bigger coin if (coinLowestLarger.second.first && - ((nBest != nTargetValue && nBest < nTargetValue + COIN) || coinLowestLarger.first <= nBest)) + ((nBest != nTargetValue && nBest < nTargetValue + DUST_SOFT_LIMIT) || coinLowestLarger.first <= nBest)) { setCoinsRet.insert(coinLowestLarger.second); nValueRet += coinLowestLarger.first;