Skip to content

Commit

Permalink
Test full coinselection algorithms
Browse files Browse the repository at this point in the history
Test the full coin selection algorithm which begins with the branch and bound
and falls back to the original algo
  • Loading branch information
achow101 committed Jun 20, 2017
1 parent 6f684ab commit 0246f1f
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 10 deletions.
44 changes: 44 additions & 0 deletions src/wallet/test/coinselector_tests.cpp
Expand Up @@ -11,6 +11,7 @@
#include "wallet/test/wallet_test_fixture.h"

#include <boost/test/unit_test.hpp>
#include <random>

BOOST_FIXTURE_TEST_SUITE(coin_selection_tests, WalletTestingSetup)

Expand All @@ -27,6 +28,7 @@ typedef std::set<CInputCoin> CoinSet;

static std::vector<COutput> vCoins;
static const CWallet testWallet;
static CAmount balance = 0;

static void add_coin(const CAmount& nValue, int nInput, std::vector<CInputCoin>& set)
{
Expand All @@ -49,6 +51,7 @@ static void add_coin(const CAmount& nValue, int nInput, CoinSet& set)

static void add_coin(const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = false, int nInput=0)
{
balance += nValue;
static int nextLockTime = 0;
CMutableTransaction tx;
tx.nLockTime = nextLockTime++; // so all transactions get different hashes
Expand All @@ -74,6 +77,7 @@ static void empty_wallet(void)
{
vCoins.clear();
wtxn.clear();
balance = 0;
}

static bool equal_sets(CoinSet a, CoinSet b)
Expand Down Expand Up @@ -507,4 +511,44 @@ BOOST_AUTO_TEST_CASE(ApproximateBestSubset)
empty_wallet();
}

// Tests that with the ideal conditions, the coin selector will always be able to find a solution that can pay the target value
BOOST_AUTO_TEST_CASE(SelectCoins_test)
{
// Random generator stuff
std::default_random_engine generator;
std::exponential_distribution<double> distribution (100);
FastRandomContext rand;

// Output stuff
CAmount out_value = 0;
CoinSet out_set;
CAmount target = 0;

// Run this test 100 times
for (int i = 0; i < 100; ++i)
{
// Reset
out_value = 0;
target = 0;
out_set.clear();
empty_wallet();

// Make a wallet with 1000 exponentially distributed random inputs
for (int j = 0; j < 1000; ++j)
{
add_coin((unsigned long)(distribution(generator)*10000000));
}

// Generate a random fee rate in the range of 100 - 400
CFeeRate rate(rand.randrange(300) + 100);

// Generate a random target value between 1000 and wallet balance
target = rand.randrange(balance - 1000) + 1000;

// Perform selection
BOOST_CHECK(testWallet.SelectCoinsMinConf(target, 1, 6, 0, vCoins, out_set, out_value, rate));
BOOST_CHECK_GE(out_value, target);
}
}

BOOST_AUTO_TEST_SUITE_END()
12 changes: 3 additions & 9 deletions src/wallet/wallet.cpp
Expand Up @@ -2517,7 +2517,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
currentConfirmationTarget = coinControl->nConfirmTarget;
}

CFeeRate nFeeRateNeeded = GetMinimumFeeRate(currentConfirmationTarget, ::mempool, ::feeEstimator);
CFeeRate nFeeRateNeeded = GetMinimumFeeRate(currentConfirmationTarget, ::mempool, ::feeEstimator, &feeCalc);

if (coinControl && coinControl->fOverrideFeeRate) {
nFeeRateNeeded = coinControl->nFeeRate;
Expand Down Expand Up @@ -2652,11 +2652,6 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
vin.scriptWitness.SetNull();
}

// Allow to override the default confirmation target over the CoinControl instance
int currentConfirmationTarget = nTxConfirmTarget;
if (coinControl && coinControl->nConfirmTarget > 0)
currentConfirmationTarget = coinControl->nConfirmTarget;

CAmount nFeeNeeded = GetMinimumFee(nBytes, currentConfirmationTarget, ::mempool, ::feeEstimator, &feeCalc);
if (coinControl && coinControl->fOverrideFeeRate)
nFeeNeeded = coinControl->nFeeRate.GetFee(nBytes);
Expand Down Expand Up @@ -2872,14 +2867,13 @@ CAmount CWallet::GetMinimumFee(unsigned int nTxBytes, unsigned int nConfirmTarge
return nFeeNeeded;
}

CFeeRate CWallet::GetMinimumFeeRate(unsigned int nConfirmTarget, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, bool ignoreUserSetFee)
CFeeRate CWallet::GetMinimumFeeRate(unsigned int nConfirmTarget, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation *feeCalc, bool ignoreUserSetFee)
{
CFeeRate nFeeRateNeeded = payTxFee;
// payTxFee is the user-set global for desired feerate
// User didn't set: use -txconfirmtarget to estimate...
if (nFeeRateNeeded == CFeeRate(0) || ignoreUserSetFee) {
int estimateFoundTarget = nConfirmTarget;
nFeeRateNeeded = estimator.estimateSmartFee(nConfirmTarget, &estimateFoundTarget, pool);
nFeeRateNeeded = estimator.estimateSmartFee(nConfirmTarget, feeCalc, pool);
// ... unless we don't have enough mempool data for estimatefee, then use fallbackFee
if (nFeeRateNeeded == CFeeRate(0))
nFeeRateNeeded = fallbackFee;
Expand Down
2 changes: 1 addition & 1 deletion src/wallet/wallet.h
Expand Up @@ -977,7 +977,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface
* Estimate the minimum fee rate considering user set parameters
* and the required fee
*/
static CFeeRate GetMinimumFeeRate(unsigned int nConfirmTarget, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, bool ignoreUserSetFee = false);
static CFeeRate GetMinimumFeeRate(unsigned int nConfirmTarget, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation *feeCalc, bool ignoreUserSetFee = false);

/**
* Return the minimum required fee taking into account the
Expand Down

0 comments on commit 0246f1f

Please sign in to comment.