Refactor SelectCoinsMinConf() and add unit tests.
AvailableCoins() makes a vector of available outputs which is then passed to SelectCoinsMinConf(). This allows unit tests to test the coin selection algorithm without having the whole blockchain available.
This commit is contained in:
parent
882ba0e752
commit
9b0369c773
256
src/test/wallet_tests.cpp
Normal file
256
src/test/wallet_tests.cpp
Normal file
@ -0,0 +1,256 @@
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include "main.h"
|
||||
#include "wallet.h"
|
||||
|
||||
// how many times to run all the tests to have a chance to catch errors that only show up with particular random shuffles
|
||||
#define RUN_TESTS 100
|
||||
|
||||
// enables tests which currently fail, due to code which tries too hard to avoid sub-cent change
|
||||
// #define STRICT
|
||||
|
||||
using namespace std;
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(wallet_tests)
|
||||
|
||||
static CWallet wallet;
|
||||
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;
|
||||
if (fIsFromMe)
|
||||
{
|
||||
// IsFromMe() returns (GetDebit() > 0), and GetDebit() is 0 if vin.empty(),
|
||||
// so stop vin being empty, and cache a non-zero Debit to fake out IsFromMe()
|
||||
wtx->vin.resize(1);
|
||||
wtx->fDebitCached = true;
|
||||
wtx->nDebitCached = 1;
|
||||
}
|
||||
COutput output(wtx, nInput, nAge);
|
||||
vCoins.push_back(output);
|
||||
}
|
||||
|
||||
static void empty_wallet(void)
|
||||
{
|
||||
BOOST_FOREACH(COutput output, vCoins)
|
||||
delete output.tx;
|
||||
vCoins.clear();
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(coin_selection_tests)
|
||||
{
|
||||
static set<pair<const CWalletTx*,unsigned int> > setCoinsRet;
|
||||
static int64 nValueRet;
|
||||
|
||||
// test multiple times to allow for differences in the shuffle order
|
||||
for (int i = 0; i < RUN_TESTS; i++)
|
||||
{
|
||||
empty_wallet();
|
||||
|
||||
// with an empty wallet we can't even pay one cent
|
||||
BOOST_CHECK(!wallet.SelectCoinsMinConf( 1 * CENT, 1, 6, vCoins, setCoinsRet, nValueRet));
|
||||
|
||||
add_coin(1*CENT, 4); // add a new 1 cent coin
|
||||
|
||||
// with a new 1 cent coin, we still can't find a mature 1 cent
|
||||
BOOST_CHECK(!wallet.SelectCoinsMinConf( 1 * CENT, 1, 6, vCoins, setCoinsRet, nValueRet));
|
||||
|
||||
// but we can find a new 1 cent
|
||||
BOOST_CHECK( wallet.SelectCoinsMinConf( 1 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
|
||||
BOOST_CHECK_EQUAL(nValueRet, 1 * CENT);
|
||||
|
||||
add_coin(2*CENT); // add a mature 2 cent coin
|
||||
|
||||
// we can't make 3 cents of mature coins
|
||||
BOOST_CHECK(!wallet.SelectCoinsMinConf( 3 * CENT, 1, 6, vCoins, setCoinsRet, nValueRet));
|
||||
|
||||
// we can make 3 cents of new coins
|
||||
BOOST_CHECK( wallet.SelectCoinsMinConf( 3 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
|
||||
BOOST_CHECK_EQUAL(nValueRet, 3 * CENT);
|
||||
|
||||
add_coin(5*CENT); // add a mature 5 cent coin,
|
||||
add_coin(10*CENT, 3, true); // a new 10 cent coin sent from one of our own addresses
|
||||
add_coin(20*CENT); // and a mature 20 cent coin
|
||||
|
||||
// now we have new: 1+10=11 (of which 10 was self-sent), and mature: 2+5+20=27. total = 38
|
||||
|
||||
// we can't make 38 cents only if we disallow new coins:
|
||||
BOOST_CHECK(!wallet.SelectCoinsMinConf(38 * CENT, 1, 6, vCoins, setCoinsRet, nValueRet));
|
||||
// we can't even make 37 cents if we don't allow new coins even if they're from us
|
||||
BOOST_CHECK(!wallet.SelectCoinsMinConf(38 * CENT, 6, 6, vCoins, setCoinsRet, nValueRet));
|
||||
// but we can make 37 cents if we accept new coins from ourself
|
||||
BOOST_CHECK( wallet.SelectCoinsMinConf(37 * CENT, 1, 6, vCoins, setCoinsRet, nValueRet));
|
||||
BOOST_CHECK_EQUAL(nValueRet, 37 * CENT);
|
||||
// and we can make 38 cents if we accept all new coins
|
||||
BOOST_CHECK( wallet.SelectCoinsMinConf(38 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
|
||||
BOOST_CHECK_EQUAL(nValueRet, 38 * CENT);
|
||||
|
||||
// try making 34 cents from 1,2,5,10,20 - we can't do it exactly
|
||||
BOOST_CHECK( wallet.SelectCoinsMinConf(34 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
|
||||
BOOST_CHECK_GT(nValueRet, 34 * CENT); // but should get more than 34 cents
|
||||
BOOST_CHECK_EQUAL(setCoinsRet.size(), 3); // the best should be 20+10+5. it's incredibly unlikely the 1 or 2 got included (but possible)
|
||||
|
||||
// when we try making 7 cents, the smaller coins (1,2,5) are enough. I'd hope to see just 2+5, but 1 gets included too
|
||||
BOOST_CHECK( wallet.SelectCoinsMinConf( 7 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
|
||||
#ifdef STRICT
|
||||
BOOST_CHECK_EQUAL(nValueRet, 7 * CENT);
|
||||
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2);
|
||||
#else
|
||||
BOOST_CHECK(nValueRet == 7 * CENT || nValueRet == 8 * CENT);
|
||||
BOOST_CHECK(setCoinsRet.size() == 2 || setCoinsRet.size() == 3);
|
||||
#endif
|
||||
|
||||
// when we try making 8 cents, the smaller coins (1,2,5) are exactly enough.
|
||||
BOOST_CHECK( wallet.SelectCoinsMinConf( 8 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
|
||||
BOOST_CHECK(nValueRet == 8 * CENT);
|
||||
BOOST_CHECK_EQUAL(setCoinsRet.size(), 3);
|
||||
|
||||
// when we try making 9 cents, no subset of smaller coins is enough, and we get the next bigger coin (10)
|
||||
BOOST_CHECK( wallet.SelectCoinsMinConf( 9 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
|
||||
BOOST_CHECK_EQUAL(nValueRet, 10 * CENT);
|
||||
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1);
|
||||
|
||||
// now clear out the wallet and start again to test chosing 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
|
||||
|
||||
// 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));
|
||||
|
||||
// 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
|
||||
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1);
|
||||
|
||||
add_coin( 5*CENT); // now we have 5+6+7+8+20+30 = 75 cents 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
|
||||
BOOST_CHECK_EQUAL(setCoinsRet.size(), 3);
|
||||
|
||||
add_coin( 18*CENT); // 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
|
||||
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1); // because in the event of a tie, the biggest coin wins
|
||||
|
||||
// now try making 11 cents. we hope for 5+6 but get 5+7 due to fear of sub-cent change
|
||||
BOOST_CHECK( wallet.SelectCoinsMinConf(11 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
|
||||
#ifdef STRICT
|
||||
BOOST_CHECK_EQUAL(nValueRet, 11 * CENT);
|
||||
#else
|
||||
BOOST_CHECK(nValueRet == 11 * CENT || nValueRet == 12 * CENT);
|
||||
#endif
|
||||
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2);
|
||||
|
||||
// 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 bitcoin in 1 coin
|
||||
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1);
|
||||
|
||||
BOOST_CHECK( wallet.SelectCoinsMinConf(195 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
|
||||
BOOST_CHECK_EQUAL(nValueRet, 2 * COIN); // we should get 2 bitcoins in 1 coin
|
||||
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1);
|
||||
|
||||
// empty the wallet and start again, now with fractions of a cent, to test sub-cent change avoidance
|
||||
empty_wallet();
|
||||
add_coin(0.1*CENT);
|
||||
add_coin(0.2*CENT);
|
||||
add_coin(0.3*CENT);
|
||||
add_coin(0.4*CENT);
|
||||
add_coin(0.5*CENT);
|
||||
|
||||
// try making 1 cent from 0.1 + 0.2 + 0.3 + 0.4 + 0.5 = 1.5 cents
|
||||
// we'll get sub-cent change whatever happens, so can expect 1.0 exactly
|
||||
BOOST_CHECK( wallet.SelectCoinsMinConf(1 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
|
||||
BOOST_CHECK_EQUAL(nValueRet, 1 * CENT);
|
||||
|
||||
// but if we add a bigger coin, making it possible to avoid sub-cent change, things change:
|
||||
add_coin(1111*CENT);
|
||||
|
||||
// try making 1 cent from 0.1 + 0.2 + 0.3 + 0.4 + 0.5 + 1111 = 1112.5 cents
|
||||
BOOST_CHECK( wallet.SelectCoinsMinConf(1 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
|
||||
#ifdef STRICT
|
||||
BOOST_CHECK_EQUAL(nValueRet, 1 * CENT); // we hope for the exact amount
|
||||
#else
|
||||
BOOST_CHECK(nValueRet == 1 * CENT || nValueRet == 1111 * CENT); // but get a single 1111 cent coin
|
||||
#endif
|
||||
|
||||
// if we add more sub-cent coins:
|
||||
add_coin(0.6*CENT);
|
||||
add_coin(0.7*CENT);
|
||||
|
||||
// and try again to make 1.0 cents, the new coins will be used to make 2.0 cents - leaving 1.0 cent change
|
||||
BOOST_CHECK( wallet.SelectCoinsMinConf(1 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
|
||||
#ifdef STRICT
|
||||
BOOST_CHECK_EQUAL(nValueRet, 1 * CENT); // we hope for the exact amount
|
||||
#else
|
||||
BOOST_CHECK(nValueRet == 1 * CENT || nValueRet == 2 * CENT); // but get a bunch of sub-cent coins totalling 2 cents
|
||||
BOOST_CHECK_GE(setCoinsRet.size(), 4); // 0.7 + 0.6 + 0.5 + 0.2 = 2.0 for example
|
||||
#endif
|
||||
|
||||
// run the 'mtgox' test (see http://blockexplorer.com/tx/29a3efd3ef04f9153d47a990bd7b048a4b2d213daaa5fb8ed670fb85f13bdbcf)
|
||||
// they tried to consolidate 10 50k coins into one 500k coin, and ended up with 50k in change
|
||||
empty_wallet();
|
||||
for (int i = 0; i < 20; i++)
|
||||
add_coin(50000 * COIN);
|
||||
|
||||
BOOST_CHECK( wallet.SelectCoinsMinConf(500000 * COIN, 1, 1, vCoins, setCoinsRet, nValueRet));
|
||||
#ifdef STRICT
|
||||
BOOST_CHECK_EQUAL(nValueRet, 500000 * COIN); // we hope for the exact amount
|
||||
BOOST_CHECK_EQUAL(setCoinsRet.size(), 10); // in ten coins
|
||||
#else
|
||||
BOOST_CHECK(nValueRet == 500000 * COIN || nValueRet == 550000 * COIN); // but get 50k too much
|
||||
BOOST_CHECK(setCoinsRet.size() == 10 || setCoinsRet.size() == 11); // in 11 coins
|
||||
#endif
|
||||
|
||||
// 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),
|
||||
// we need to try finding an exact subset anyway
|
||||
|
||||
// sometimes it will fail, and so we use the next biggest coin:
|
||||
empty_wallet();
|
||||
add_coin(0.5 * CENT);
|
||||
add_coin(0.6 * CENT);
|
||||
add_coin(0.7 * CENT);
|
||||
add_coin(1111 * CENT);
|
||||
BOOST_CHECK( wallet.SelectCoinsMinConf(1 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
|
||||
BOOST_CHECK_EQUAL(nValueRet, 1111 * CENT); // we get the bigger coin
|
||||
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1);
|
||||
|
||||
// but sometimes it's possible, and we use an exact subset (0.4 + 0.6 = 1.0)
|
||||
empty_wallet();
|
||||
add_coin(0.4 * CENT);
|
||||
add_coin(0.6 * CENT);
|
||||
add_coin(0.8 * CENT);
|
||||
add_coin(1111 * CENT);
|
||||
BOOST_CHECK( wallet.SelectCoinsMinConf(1 * CENT, 1, 1, vCoins, setCoinsRet, nValueRet));
|
||||
#ifdef STRICT
|
||||
BOOST_CHECK_EQUAL(nValueRet, 1 * CENT); // we hope for the exact amount
|
||||
BOOST_CHECK_EQUAL(setCoinsRet.size(), 2); // in two coins 0.4+0.6
|
||||
#else
|
||||
BOOST_CHECK_EQUAL(nValueRet, 1111 * CENT); // but since value of smaller coins < target+cent, we get the bigger coin again
|
||||
BOOST_CHECK_EQUAL(setCoinsRet.size(), 1);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
102
src/wallet.cpp
102
src/wallet.cpp
@ -889,7 +889,32 @@ int64 CWallet::GetImmatureBalance() const
|
||||
return nTotal;
|
||||
}
|
||||
|
||||
bool CWallet::SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const
|
||||
// populate vCoins with vector of spendable (age, (value, (transaction, output_number))) outputs
|
||||
void CWallet::AvailableCoins(vector<COutput>& vCoins) const
|
||||
{
|
||||
vCoins.clear();
|
||||
|
||||
{
|
||||
LOCK(cs_wallet);
|
||||
for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
|
||||
{
|
||||
const CWalletTx* pcoin = &(*it).second;
|
||||
|
||||
if (!pcoin->IsFinal() || !pcoin->IsConfirmed())
|
||||
continue;
|
||||
|
||||
if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0)
|
||||
continue;
|
||||
|
||||
for (int i = 0; i < pcoin->vout.size(); i++)
|
||||
if (!(pcoin->IsSpent(i)) && IsMine(pcoin->vout[i]) && pcoin->vout[i].nValue > 0)
|
||||
vCoins.push_back(COutput(pcoin, i, pcoin->GetDepthInMainChain()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CWallet::SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, vector<COutput> vCoins,
|
||||
set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const
|
||||
{
|
||||
setCoinsRet.clear();
|
||||
nValueRet = 0;
|
||||
@ -901,54 +926,32 @@ bool CWallet::SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfThe
|
||||
vector<pair<int64, pair<const CWalletTx*,unsigned int> > > vValue;
|
||||
int64 nTotalLower = 0;
|
||||
|
||||
BOOST_FOREACH(COutput output, vCoins)
|
||||
{
|
||||
LOCK(cs_wallet);
|
||||
vector<const CWalletTx*> vCoins;
|
||||
vCoins.reserve(mapWallet.size());
|
||||
for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
|
||||
vCoins.push_back(&(*it).second);
|
||||
random_shuffle(vCoins.begin(), vCoins.end(), GetRandInt);
|
||||
const CWalletTx *pcoin = output.tx;
|
||||
|
||||
BOOST_FOREACH(const CWalletTx* pcoin, vCoins)
|
||||
{
|
||||
if (!pcoin->IsFinal() || !pcoin->IsConfirmed())
|
||||
continue;
|
||||
if (output.nDepth < (pcoin->IsFromMe() ? nConfMine : nConfTheirs))
|
||||
continue;
|
||||
|
||||
if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0)
|
||||
continue;
|
||||
int i = output.i;
|
||||
int64 n = pcoin->vout[i].nValue;
|
||||
|
||||
int nDepth = pcoin->GetDepthInMainChain();
|
||||
if (nDepth < (pcoin->IsFromMe() ? nConfMine : nConfTheirs))
|
||||
continue;
|
||||
pair<int64,pair<const CWalletTx*,unsigned int> > coin = make_pair(n,make_pair(pcoin, i));
|
||||
|
||||
for (unsigned int i = 0; i < pcoin->vout.size(); i++)
|
||||
{
|
||||
if (pcoin->IsSpent(i) || !IsMine(pcoin->vout[i]))
|
||||
continue;
|
||||
|
||||
int64 n = pcoin->vout[i].nValue;
|
||||
|
||||
if (n <= 0)
|
||||
continue;
|
||||
|
||||
pair<int64,pair<const CWalletTx*,unsigned int> > coin = make_pair(n,make_pair(pcoin,i));
|
||||
|
||||
if (n == nTargetValue)
|
||||
{
|
||||
setCoinsRet.insert(coin.second);
|
||||
nValueRet += coin.first;
|
||||
return true;
|
||||
}
|
||||
else if (n < nTargetValue + CENT)
|
||||
{
|
||||
vValue.push_back(coin);
|
||||
nTotalLower += n;
|
||||
}
|
||||
else if (n < coinLowestLarger.first)
|
||||
{
|
||||
coinLowestLarger = coin;
|
||||
}
|
||||
}
|
||||
if (n == nTargetValue)
|
||||
{
|
||||
setCoinsRet.insert(coin.second);
|
||||
nValueRet += coin.first;
|
||||
return true;
|
||||
}
|
||||
else if (n < nTargetValue + CENT)
|
||||
{
|
||||
vValue.push_back(coin);
|
||||
nTotalLower += n;
|
||||
}
|
||||
else if (n < coinLowestLarger.first)
|
||||
{
|
||||
coinLowestLarger = coin;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1023,12 +1026,14 @@ bool CWallet::SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfThe
|
||||
nValueRet += vValue[i].first;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
//// debug print
|
||||
printf("SelectCoins() best subset: ");
|
||||
for (unsigned int i = 0; i < vValue.size(); i++)
|
||||
if (vfBest[i])
|
||||
printf("%s ", FormatMoney(vValue[i].first).c_str());
|
||||
printf("total %s\n", FormatMoney(nBest).c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -1036,9 +1041,12 @@ bool CWallet::SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfThe
|
||||
|
||||
bool CWallet::SelectCoins(int64 nTargetValue, set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const
|
||||
{
|
||||
return (SelectCoinsMinConf(nTargetValue, 1, 6, setCoinsRet, nValueRet) ||
|
||||
SelectCoinsMinConf(nTargetValue, 1, 1, setCoinsRet, nValueRet) ||
|
||||
SelectCoinsMinConf(nTargetValue, 0, 1, setCoinsRet, nValueRet));
|
||||
vector<COutput> vCoins;
|
||||
AvailableCoins(vCoins);
|
||||
|
||||
return (SelectCoinsMinConf(nTargetValue, 1, 6, vCoins, setCoinsRet, nValueRet) ||
|
||||
SelectCoinsMinConf(nTargetValue, 1, 1, vCoins, setCoinsRet, nValueRet) ||
|
||||
SelectCoinsMinConf(nTargetValue, 0, 1, vCoins, setCoinsRet, nValueRet));
|
||||
}
|
||||
|
||||
|
||||
|
33
src/wallet.h
33
src/wallet.h
@ -14,6 +14,7 @@
|
||||
class CWalletTx;
|
||||
class CReserveKey;
|
||||
class CWalletDB;
|
||||
class COutput;
|
||||
|
||||
/** (client) version numbers for particular wallet features */
|
||||
enum WalletFeature
|
||||
@ -60,7 +61,7 @@ public:
|
||||
class CWallet : public CCryptoKeyStore
|
||||
{
|
||||
private:
|
||||
bool SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const;
|
||||
void AvailableCoins(std::vector<COutput>& vCoins) const;
|
||||
bool SelectCoins(int64 nTargetValue, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const;
|
||||
|
||||
CWalletDB *pwalletdbEncryption;
|
||||
@ -112,6 +113,8 @@ public:
|
||||
// check whether we are allowed to upgrade (or already support) to the named feature
|
||||
bool CanSupportFeature(enum WalletFeature wf) { return nWalletMaxVersion >= wf; }
|
||||
|
||||
bool SelectCoinsMinConf(int64 nTargetValue, int nConfMine, int nConfTheirs, std::vector<COutput> vCoins, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet) const;
|
||||
|
||||
// keystore implementation
|
||||
// Generate a new key
|
||||
CPubKey GenerateNewKey();
|
||||
@ -602,6 +605,34 @@ public:
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
class COutput
|
||||
{
|
||||
public:
|
||||
const CWalletTx *tx;
|
||||
int i;
|
||||
int nDepth;
|
||||
|
||||
COutput(const CWalletTx *txIn, int iIn, int nDepthIn)
|
||||
{
|
||||
tx = txIn; i = iIn; nDepth = nDepthIn;
|
||||
}
|
||||
|
||||
std::string ToString() const
|
||||
{
|
||||
return strprintf("COutput(%s, %d, %d) [%s]", tx->GetHash().ToString().substr(0,10).c_str(), i, nDepth, FormatMoney(tx->vout[i].nValue).c_str());
|
||||
}
|
||||
|
||||
void print() const
|
||||
{
|
||||
printf("%s\n", ToString().c_str());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/** Private key that includes an expiration date in case it never gets used. */
|
||||
class CWalletKey
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user