Skip to content

Commit 8a95a18

Browse files
committed
Merge pull request #6669
6342a48 Init: Use DEFAULT_TRANSACTION_MINFEE in help message (MarcoFalke) a9c73a1 [wallet] Add comments for doxygen (MarcoFalke) 6b0e622 [wallet] Refactor to use new MIN_CHANGE (MarcoFalke)
2 parents aa03fb3 + 6342a48 commit 8a95a18

File tree

5 files changed

+105
-57
lines changed

5 files changed

+105
-57
lines changed

src/init.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ std::string HelpMessage(HelpMessageMode mode)
376376
strUsage += HelpMessageOpt("-keypool=<n>", strprintf(_("Set key pool size to <n> (default: %u)"), 100));
377377
if (showDebug)
378378
strUsage += HelpMessageOpt("-mintxfee=<amt>", strprintf("Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s)",
379-
CURRENCY_UNIT, FormatMoney(CWallet::minTxFee.GetFeePerK())));
379+
CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MINFEE)));
380380
strUsage += HelpMessageOpt("-paytxfee=<amt>", strprintf(_("Fee (in %s/kB) to add to transactions you send (default: %s)"),
381381
CURRENCY_UNIT, FormatMoney(payTxFee.GetFeePerK())));
382382
strUsage += HelpMessageOpt("-rescan", _("Rescan the block chain for missing wallet transactions") + " " + _("on startup"));

src/qt/coincontroldialog.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -567,7 +567,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog)
567567
nChange -= nPayFee;
568568

569569
// Never create dust outputs; if we would, just add the dust to the fee.
570-
if (nChange > 0 && nChange < CENT)
570+
if (nChange > 0 && nChange < MIN_CHANGE)
571571
{
572572
CTxOut txout(nChange, (CScript)std::vector<unsigned char>(24, 0));
573573
if (txout.IsDust(::minRelayTxFee))

src/wallet/test/wallet_tests.cpp

Lines changed: 67 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests)
117117

118118
// try making 34 cents from 1,2,5,10,20 - we can't do it exactly
119119
BOOST_CHECK( wallet.SelectCoinsMinConf(34 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
120-
BOOST_CHECK_GT(nValueRet, 34 * CENT); // but should get more than 34 cents
120+
BOOST_CHECK_EQUAL(nValueRet, 35 * CENT); // but 35 cents is closest
121121
BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U); // the best should be 20+10+5. it's incredibly unlikely the 1 or 2 got included (but possible)
122122

123123
// when we try making 7 cents, the smaller coins (1,2,5) are enough. We should see just 2+5
@@ -185,33 +185,34 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests)
185185
BOOST_CHECK_EQUAL(nValueRet, 2 * COIN); // we should get 2 BTC in 1 coin
186186
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
187187

188-
// empty the wallet and start again, now with fractions of a cent, to test sub-cent change avoidance
188+
// empty the wallet and start again, now with fractions of a cent, to test small change avoidance
189+
189190
empty_wallet();
190-
add_coin(0.1*CENT);
191-
add_coin(0.2*CENT);
192-
add_coin(0.3*CENT);
193-
add_coin(0.4*CENT);
194-
add_coin(0.5*CENT);
195-
196-
// try making 1 cent from 0.1 + 0.2 + 0.3 + 0.4 + 0.5 = 1.5 cents
197-
// we'll get sub-cent change whatever happens, so can expect 1.0 exactly
198-
BOOST_CHECK( wallet.SelectCoinsMinConf(1 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
199-
BOOST_CHECK_EQUAL(nValueRet, 1 * CENT);
191+
add_coin(0.1*MIN_CHANGE);
192+
add_coin(0.2*MIN_CHANGE);
193+
add_coin(0.3*MIN_CHANGE);
194+
add_coin(0.4*MIN_CHANGE);
195+
add_coin(0.5*MIN_CHANGE);
196+
197+
// try making 1 * MIN_CHANGE from the 1.5 * MIN_CHANGE
198+
// we'll get change smaller than MIN_CHANGE whatever happens, so can expect MIN_CHANGE exactly
199+
BOOST_CHECK( wallet.SelectCoinsMinConf(MIN_CHANGE, 1, 1, vCoins, setCoinsRet, nValueRet));
200+
BOOST_CHECK_EQUAL(nValueRet, MIN_CHANGE);
200201

201-
// but if we add a bigger coin, making it possible to avoid sub-cent change, things change:
202-
add_coin(1111*CENT);
202+
// but if we add a bigger coin, small change is avoided
203+
add_coin(1111*MIN_CHANGE);
203204

204-
// try making 1 cent from 0.1 + 0.2 + 0.3 + 0.4 + 0.5 + 1111 = 1112.5 cents
205-
BOOST_CHECK( wallet.SelectCoinsMinConf(1 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
206-
BOOST_CHECK_EQUAL(nValueRet, 1 * CENT); // we should get the exact amount
205+
// try making 1 from 0.1 + 0.2 + 0.3 + 0.4 + 0.5 + 1111 = 1112.5
206+
BOOST_CHECK( wallet.SelectCoinsMinConf(1 * MIN_CHANGE, 1, 1, vCoins, setCoinsRet, nValueRet));
207+
BOOST_CHECK_EQUAL(nValueRet, 1 * MIN_CHANGE); // we should get the exact amount
207208

208-
// if we add more sub-cent coins:
209-
add_coin(0.6*CENT);
210-
add_coin(0.7*CENT);
209+
// if we add more small coins:
210+
add_coin(0.6*MIN_CHANGE);
211+
add_coin(0.7*MIN_CHANGE);
211212

212-
// and try again to make 1.0 cents, we can still make 1.0 cents
213-
BOOST_CHECK( wallet.SelectCoinsMinConf(1 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
214-
BOOST_CHECK_EQUAL(nValueRet, 1 * CENT); // we should get the exact amount
213+
// and try again to make 1.0 * MIN_CHANGE
214+
BOOST_CHECK( wallet.SelectCoinsMinConf(1 * MIN_CHANGE, 1, 1, vCoins, setCoinsRet, nValueRet));
215+
BOOST_CHECK_EQUAL(nValueRet, 1 * MIN_CHANGE); // we should get the exact amount
215216

216217
// run the 'mtgox' test (see http://blockexplorer.com/tx/29a3efd3ef04f9153d47a990bd7b048a4b2d213daaa5fb8ed670fb85f13bdbcf)
217218
// they tried to consolidate 10 50k coins into one 500k coin, and ended up with 50k in change
@@ -223,45 +224,65 @@ BOOST_AUTO_TEST_CASE(coin_selection_tests)
223224
BOOST_CHECK_EQUAL(nValueRet, 500000 * COIN); // we should get the exact amount
224225
BOOST_CHECK_EQUAL(setCoinsRet.size(), 10U); // in ten coins
225226

226-
// if there's not enough in the smaller coins to make at least 1 cent change (0.5+0.6+0.7 < 1.0+1.0),
227+
// if there's not enough in the smaller coins to make at least 1 * MIN_CHANGE change (0.5+0.6+0.7 < 1.0+1.0),
227228
// we need to try finding an exact subset anyway
228229

229230
// sometimes it will fail, and so we use the next biggest coin:
230231
empty_wallet();
231-
add_coin(0.5 * CENT);
232-
add_coin(0.6 * CENT);
233-
add_coin(0.7 * CENT);
234-
add_coin(1111 * CENT);
235-
BOOST_CHECK( wallet.SelectCoinsMinConf(1 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
236-
BOOST_CHECK_EQUAL(nValueRet, 1111 * CENT); // we get the bigger coin
232+
add_coin(0.5 * MIN_CHANGE);
233+
add_coin(0.6 * MIN_CHANGE);
234+
add_coin(0.7 * MIN_CHANGE);
235+
add_coin(1111 * MIN_CHANGE);
236+
BOOST_CHECK( wallet.SelectCoinsMinConf(1 * MIN_CHANGE, 1, 1, vCoins, setCoinsRet, nValueRet));
237+
BOOST_CHECK_EQUAL(nValueRet, 1111 * MIN_CHANGE); // we get the bigger coin
237238
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
238239

239240
// but sometimes it's possible, and we use an exact subset (0.4 + 0.6 = 1.0)
240241
empty_wallet();
241-
add_coin(0.4 * CENT);
242-
add_coin(0.6 * CENT);
243-
add_coin(0.8 * CENT);
244-
add_coin(1111 * CENT);
245-
BOOST_CHECK( wallet.SelectCoinsMinConf(1 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
246-
BOOST_CHECK_EQUAL(nValueRet, 1 * CENT); // we should get the exact amount
242+
add_coin(0.4 * MIN_CHANGE);
243+
add_coin(0.6 * MIN_CHANGE);
244+
add_coin(0.8 * MIN_CHANGE);
245+
add_coin(1111 * MIN_CHANGE);
246+
BOOST_CHECK( wallet.SelectCoinsMinConf(MIN_CHANGE, 1, 1, vCoins, setCoinsRet, nValueRet));
247+
BOOST_CHECK_EQUAL(nValueRet, MIN_CHANGE); // we should get the exact amount
247248
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U); // in two coins 0.4+0.6
248249

249-
// test avoiding sub-cent change
250+
// test avoiding small change
250251
empty_wallet();
251-
add_coin(0.0005 * COIN);
252-
add_coin(0.01 * COIN);
253-
add_coin(1 * COIN);
252+
add_coin(0.05 * MIN_CHANGE);
253+
add_coin(1 * MIN_CHANGE);
254+
add_coin(100 * MIN_CHANGE);
254255

255-
// trying to make 1.0001 from these three coins
256-
BOOST_CHECK( wallet.SelectCoinsMinConf(1.0001 * COIN, 1, 1, vCoins, setCoinsRet, nValueRet));
257-
BOOST_CHECK_EQUAL(nValueRet, 1.0105 * COIN); // we should get all coins
256+
// trying to make 100.01 from these three coins
257+
BOOST_CHECK( wallet.SelectCoinsMinConf(100.01 * MIN_CHANGE, 1, 1, vCoins, setCoinsRet, nValueRet));
258+
BOOST_CHECK_EQUAL(nValueRet, 101.05 * MIN_CHANGE); // we should get all coins
258259
BOOST_CHECK_EQUAL(setCoinsRet.size(), 3U);
259260

260-
// but if we try to make 0.999, we should take the bigger of the two small coins to avoid sub-cent change
261-
BOOST_CHECK( wallet.SelectCoinsMinConf(0.999 * COIN, 1, 1, vCoins, setCoinsRet, nValueRet));
262-
BOOST_CHECK_EQUAL(nValueRet, 1.01 * COIN); // we should get 1 + 0.01
261+
// but if we try to make 99.9, we should take the bigger of the two small coins to avoid small change
262+
BOOST_CHECK( wallet.SelectCoinsMinConf(99.9 * MIN_CHANGE, 1, 1, vCoins, setCoinsRet, nValueRet));
263+
BOOST_CHECK_EQUAL(nValueRet, 101 * MIN_CHANGE);
263264
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2U);
264265

266+
// test with many inputs
267+
for (CAmount amt=1500; amt < COIN; amt*=10) {
268+
empty_wallet();
269+
// Create 676 inputs (= MAX_STANDARD_TX_SIZE / 148 bytes per input)
270+
for (uint16_t j = 0; j < 676; j++)
271+
add_coin(amt);
272+
BOOST_CHECK(wallet.SelectCoinsMinConf(2000, 1, 1, vCoins, setCoinsRet, nValueRet));
273+
if (amt - 2000 < MIN_CHANGE) {
274+
// needs more than one input:
275+
uint16_t returnSize = std::ceil((2000.0 + MIN_CHANGE)/amt);
276+
CAmount returnValue = amt * returnSize;
277+
BOOST_CHECK_EQUAL(nValueRet, returnValue);
278+
BOOST_CHECK_EQUAL(setCoinsRet.size(), returnSize);
279+
} else {
280+
// one input is sufficient:
281+
BOOST_CHECK_EQUAL(nValueRet, amt);
282+
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1U);
283+
}
284+
}
285+
265286
// test randomness
266287
{
267288
empty_wallet();

src/wallet/wallet.cpp

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ bool fPayAtLeastCustomFee = true;
4747
* Fees smaller than this (in satoshi) are considered zero fee (for transaction creation)
4848
* Override with -mintxfee
4949
*/
50-
CFeeRate CWallet::minTxFee = CFeeRate(1000);
50+
CFeeRate CWallet::minTxFee = CFeeRate(DEFAULT_TRANSACTION_MINFEE);
5151

5252
/** @defgroup mapWallet
5353
*
@@ -1497,9 +1497,6 @@ CAmount CWallet::GetImmatureWatchOnlyBalance() const
14971497
return nTotal;
14981498
}
14991499

1500-
/**
1501-
* populate vCoins with vector of available COutputs.
1502-
*/
15031500
void CWallet::AvailableCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl, bool fIncludeZeroValue) const
15041501
{
15051502
vCoins.clear();
@@ -1619,7 +1616,7 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int
16191616
nValueRet += coin.first;
16201617
return true;
16211618
}
1622-
else if (n < nTargetValue + CENT)
1619+
else if (n < nTargetValue + MIN_CHANGE)
16231620
{
16241621
vValue.push_back(coin);
16251622
nTotalLower += n;
@@ -1654,14 +1651,14 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int
16541651
vector<char> vfBest;
16551652
CAmount nBest;
16561653

1657-
ApproximateBestSubset(vValue, nTotalLower, nTargetValue, vfBest, nBest, 1000);
1658-
if (nBest != nTargetValue && nTotalLower >= nTargetValue + CENT)
1659-
ApproximateBestSubset(vValue, nTotalLower, nTargetValue + CENT, vfBest, nBest, 1000);
1654+
ApproximateBestSubset(vValue, nTotalLower, nTargetValue, vfBest, nBest);
1655+
if (nBest != nTargetValue && nTotalLower >= nTargetValue + MIN_CHANGE)
1656+
ApproximateBestSubset(vValue, nTotalLower, nTargetValue + MIN_CHANGE, vfBest, nBest);
16601657

16611658
// If we have a bigger coin and (either the stochastic approximation didn't find a good solution,
16621659
// or the next bigger coin is closer), return the bigger coin
16631660
if (coinLowestLarger.second.first &&
1664-
((nBest != nTargetValue && nBest < nTargetValue + CENT) || coinLowestLarger.first <= nBest))
1661+
((nBest != nTargetValue && nBest < nTargetValue + MIN_CHANGE) || coinLowestLarger.first <= nBest))
16651662
{
16661663
setCoinsRet.insert(coinLowestLarger.second);
16671664
nValueRet += coinLowestLarger.first;
@@ -1844,6 +1841,7 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
18441841
LOCK2(cs_main, cs_wallet);
18451842
{
18461843
nFeeRet = 0;
1844+
// Start with no fee and loop until there is enough fee
18471845
while (true)
18481846
{
18491847
txNew.vin.clear();

src/wallet/wallet.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,12 @@ extern bool fPayAtLeastCustomFee;
4141
static const CAmount DEFAULT_TRANSACTION_FEE = 0;
4242
//! -paytxfee will warn if called with a higher fee than this amount (in satoshis) per KB
4343
static const CAmount nHighTransactionFeeWarning = 0.01 * COIN;
44+
//! -mintxfee default
45+
static const CAmount DEFAULT_TRANSACTION_MINFEE = 1000;
4446
//! -maxtxfee default
4547
static const CAmount DEFAULT_TRANSACTION_MAXFEE = 0.1 * COIN;
48+
//! minimum change amount
49+
static const CAmount MIN_CHANGE = CENT;
4650
//! -txconfirmtarget default
4751
static const unsigned int DEFAULT_TX_CONFIRM_TARGET = 2;
4852
//! -maxtxfee will warn if called with a higher fee than this amount (in satoshis)
@@ -442,6 +446,11 @@ class CWalletKey
442446
class CWallet : public CCryptoKeyStore, public CValidationInterface
443447
{
444448
private:
449+
/**
450+
* Select a set of coins such that nValueRet >= nTargetValue and at least
451+
* all coins from coinControl are selected; Never select unconfirmed coins
452+
* if they are not ours
453+
*/
445454
bool SelectCoins(const CAmount& nTargetValue, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet, const CCoinControl *coinControl = NULL) const;
446455

447456
CWalletDB *pwalletdbEncryption;
@@ -539,7 +548,17 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface
539548
//! check whether we are allowed to upgrade (or already support) to the named feature
540549
bool CanSupportFeature(enum WalletFeature wf) { AssertLockHeld(cs_wallet); return nWalletMaxVersion >= wf; }
541550

551+
/**
552+
* populate vCoins with vector of available COutputs.
553+
*/
542554
void AvailableCoins(std::vector<COutput>& vCoins, bool fOnlyConfirmed=true, const CCoinControl *coinControl = NULL, bool fIncludeZeroValue=false) const;
555+
556+
/**
557+
* Shuffle and select coins until nTargetValue is reached while avoiding
558+
* small change; This method is stochastic for some inputs and upon
559+
* completion the coin set and corresponding actual target value is
560+
* assembled
561+
*/
543562
bool SelectCoinsMinConf(const CAmount& nTargetValue, int nConfMine, int nConfTheirs, std::vector<COutput> vCoins, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, CAmount& nValueRet) const;
544563

545564
bool IsSpent(const uint256& hash, unsigned int n) const;
@@ -622,7 +641,17 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface
622641
CAmount GetWatchOnlyBalance() const;
623642
CAmount GetUnconfirmedWatchOnlyBalance() const;
624643
CAmount GetImmatureWatchOnlyBalance() const;
644+
645+
/**
646+
* Insert additional inputs into the transaction by
647+
* calling CreateTransaction();
648+
*/
625649
bool FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosRet, std::string& strFailReason, bool includeWatching);
650+
651+
/**
652+
* Create a new transaction paying the recipients with a set of coins
653+
* selected by SelectCoins(); Also create the change output, when needed
654+
*/
626655
bool CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosRet,
627656
std::string& strFailReason, const CCoinControl *coinControl = NULL, bool sign = true);
628657
bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey);

0 commit comments

Comments
 (0)