mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-04-11 08:07:33 +02:00
Merge bitcoin/bitcoin#34886: test: Rework Single Random Draw coin selection tests
858a0a9c96test: Add SRD maximum weight tests (Murch)fe9f53bf0btest: Add SRD success tests (Murch)2840f041c5test: Rework SRD insufficient balance test (Murch)64ab97466fTest: Add new minimum to tested feerates (Murch)65900f8dc6test: Init coin selection params with feerate (Murch) Pull request description: This transfers the tests from the old coin selection test framework that was based on ignoring fees to the new coin selection framework that tests the coin selection algorithms with realistic feerates and fees. The PR also includes a minor improvement of the test framework to allow the CoinSelectionParams used by the tests to be initialized with other feerates, and adds some feerates around the new minimum feerate to the tested feerates. ACKs for top commit: achow101: ACK858a0a9c96w0xlt: ACK858a0a9c96brunoerg: reACK858a0a9c96Tree-SHA512: cbd7ed3169d225a0b0f751481ca936a10939ff899c345e9469821d46083b04e835d164c50a72f18851544314611c3d596eb4956c1d86903c22e8b4996b8eb861
This commit is contained in:
@@ -18,30 +18,46 @@ static FastRandomContext default_rand;
|
||||
static const int P2WPKH_INPUT_VSIZE = 68;
|
||||
static const int P2WPKH_OUTPUT_VSIZE = 31;
|
||||
|
||||
/** Default coin selection parameters (dcsp) allow us to only explicitly set
|
||||
* parameters when a diverging value is relevant in the context of a test.
|
||||
* We use P2WPKH input and output weights for the change weights. */
|
||||
static CoinSelectionParams init_default_params()
|
||||
/**
|
||||
* This set of feerates is used in the tests to test edge cases around the
|
||||
* default minimum feerate and other potential special cases:
|
||||
* - zero: 0 s/kvB
|
||||
* - minimum non-zero s/kvB: 1 s/kvB
|
||||
* - just below the new default minimum feerate: 99 s/kvB
|
||||
* - new default minimum feerate: 100 s/kvB
|
||||
* - old default minimum feerate: 1000 s/kvB
|
||||
* - a few non-round realistic feerates around default minimum feerate,
|
||||
* dust feerate, and default LTFRE: 315 s/kvB, 2345 s/kvB, and
|
||||
* 10'292 s/kvB
|
||||
* - a high feerate that has been exceeded occasionally: 59'764 s/kvB
|
||||
* - a huge feerate that is extremely uncommon: 1'500'000 s/kvB */
|
||||
static const std::vector<int> FEERATES = {0, 1, 99, 100, 315, 1'000, 2'345, 10'292, 59'764, 1'500'000};
|
||||
|
||||
/** Default coin selection parameters allow us to only explicitly set
|
||||
* parameters when a diverging value is relevant in the context of a test,
|
||||
* without reiterating the defaults in every test. We use P2WPKH input and
|
||||
* output weights for the change weights. */
|
||||
static CoinSelectionParams init_cs_params(int eff_feerate = 5000)
|
||||
{
|
||||
CoinSelectionParams dcsp{
|
||||
CoinSelectionParams csp{
|
||||
/*rng_fast=*/default_rand,
|
||||
/*change_output_size=*/P2WPKH_OUTPUT_VSIZE,
|
||||
/*change_spend_size=*/P2WPKH_INPUT_VSIZE,
|
||||
/*min_change_target=*/50'000,
|
||||
/*effective_feerate=*/CFeeRate(5000),
|
||||
/*effective_feerate=*/CFeeRate(eff_feerate),
|
||||
/*long_term_feerate=*/CFeeRate(10'000),
|
||||
/*discard_feerate=*/CFeeRate(3000),
|
||||
/*tx_noinputs_size=*/11 + P2WPKH_OUTPUT_VSIZE, //static header size + output size
|
||||
/*avoid_partial=*/false,
|
||||
};
|
||||
dcsp.m_change_fee = /*155 sats=*/dcsp.m_effective_feerate.GetFee(dcsp.change_output_size);
|
||||
dcsp.min_viable_change = /*204 sats=*/dcsp.m_discard_feerate.GetFee(dcsp.change_spend_size);
|
||||
dcsp.m_cost_of_change = /*204 + 155 sats=*/dcsp.min_viable_change + dcsp.m_change_fee;
|
||||
dcsp.m_subtract_fee_outputs = false;
|
||||
return dcsp;
|
||||
csp.m_change_fee = csp.m_effective_feerate.GetFee(csp.change_output_size); // 155 sats for default feerate of 5000 s/kvB
|
||||
csp.min_viable_change = /*204 sats=*/csp.m_discard_feerate.GetFee(csp.change_spend_size);
|
||||
csp.m_cost_of_change = csp.min_viable_change + csp.m_change_fee; // 204 + 155 sats for default feerate of 5000 s/kvB
|
||||
csp.m_subtract_fee_outputs = false;
|
||||
return csp;
|
||||
}
|
||||
|
||||
static const CoinSelectionParams default_cs_params = init_default_params();
|
||||
static const CoinSelectionParams default_cs_params = init_cs_params();
|
||||
|
||||
/** Make one OutputGroup with a single UTXO that either has a given effective value (default) or a given amount (`is_eff_value = false`). */
|
||||
static OutputGroup MakeCoin(const CAmount& amount, bool is_eff_value = true, CoinSelectionParams cs_params = default_cs_params, int custom_spending_vsize = P2WPKH_INPUT_VSIZE)
|
||||
@@ -106,15 +122,16 @@ static void TestBnBSuccess(std::string test_title, std::vector<OutputGroup>& utx
|
||||
expected_result.AddInput(group);
|
||||
}
|
||||
|
||||
const auto result = SelectCoinsBnB(utxo_pool, selection_target, /*cost_of_change=*/default_cs_params.m_cost_of_change, max_selection_weight);
|
||||
const auto result = SelectCoinsBnB(utxo_pool, selection_target, /*cost_of_change=*/cs_params.m_cost_of_change, max_selection_weight);
|
||||
BOOST_CHECK_MESSAGE(result, "Falsy result in BnB-Success: " + test_title);
|
||||
BOOST_CHECK_MESSAGE(HaveEquivalentValues(expected_result, *result), strprintf("Result mismatch in BnB-Success: %s. Expected %s, but got %s", test_title, InputAmountsToString(expected_result), InputAmountsToString(*result)));
|
||||
BOOST_CHECK_MESSAGE(result->GetSelectedValue() == expected_amount, strprintf("Selected amount mismatch in BnB-Success: %s. Expected %d, but got %d", test_title, expected_amount, result->GetSelectedValue()));
|
||||
BOOST_CHECK_MESSAGE(result->GetWeight() <= max_selection_weight, strprintf("Selected weight is higher than permitted in BnB-Success: %s. Expected %d, but got %d", test_title, max_selection_weight, result->GetWeight()));
|
||||
}
|
||||
|
||||
static void TestBnBFail(std::string test_title, std::vector<OutputGroup>& utxo_pool, const CAmount& selection_target, int max_selection_weight = MAX_STANDARD_TX_WEIGHT, const bool expect_max_weight_exceeded = false)
|
||||
static void TestBnBFail(std::string test_title, std::vector<OutputGroup>& utxo_pool, const CAmount& selection_target, const CoinSelectionParams& cs_params = default_cs_params, int max_selection_weight = MAX_STANDARD_TX_WEIGHT, const bool expect_max_weight_exceeded = false)
|
||||
{
|
||||
const auto result = SelectCoinsBnB(utxo_pool, selection_target, /*cost_of_change=*/default_cs_params.m_cost_of_change, max_selection_weight);
|
||||
const auto result = SelectCoinsBnB(utxo_pool, selection_target, /*cost_of_change=*/cs_params.m_cost_of_change, max_selection_weight);
|
||||
BOOST_CHECK_MESSAGE(!result, "BnB-Fail: " + test_title);
|
||||
bool max_weight_exceeded = util::ErrorString(result).original.find("The inputs size exceeds the maximum weight") != std::string::npos;
|
||||
BOOST_CHECK(expect_max_weight_exceeded == max_weight_exceeded);
|
||||
@@ -122,16 +139,12 @@ static void TestBnBFail(std::string test_title, std::vector<OutputGroup>& utxo_p
|
||||
|
||||
BOOST_AUTO_TEST_CASE(bnb_test)
|
||||
{
|
||||
std::vector<int> feerates = {0, 1, 5'000, 10'000, 25'000, 59'764, 500'000, 999'000, 1'500'000};
|
||||
|
||||
for (int feerate : feerates) {
|
||||
for (int feerate : FEERATES) {
|
||||
std::vector<OutputGroup> utxo_pool;
|
||||
|
||||
CoinSelectionParams cs_params = init_default_params();
|
||||
cs_params.m_effective_feerate = CFeeRate{feerate};
|
||||
const CoinSelectionParams cs_params = init_cs_params(feerate);
|
||||
|
||||
// Fail for empty UTXO pool
|
||||
TestBnBFail("Empty UTXO pool", utxo_pool, /*selection_target=*/1 * CENT);
|
||||
TestBnBFail("Empty UTXO pool", utxo_pool, /*selection_target=*/1 * CENT, cs_params);
|
||||
|
||||
AddCoins(utxo_pool, {1 * CENT, 3 * CENT, 5 * CENT}, cs_params);
|
||||
|
||||
@@ -143,24 +156,24 @@ BOOST_AUTO_TEST_CASE(bnb_test)
|
||||
TestBnBSuccess("Select all UTXOs", utxo_pool, /*selection_target=*/9 * CENT, /*expected_input_amounts=*/{1 * CENT, 3 * CENT, 5 * CENT}, cs_params);
|
||||
|
||||
// BnB finds changeless solution while overshooting by up to cost_of_change
|
||||
TestBnBSuccess("Select upper bound", utxo_pool, /*selection_target=*/4 * CENT - default_cs_params.m_cost_of_change, /*expected_input_amounts=*/{1 * CENT, 3 * CENT}, cs_params);
|
||||
TestBnBSuccess("Select upper bound", utxo_pool, /*selection_target=*/4 * CENT - cs_params.m_cost_of_change, /*expected_input_amounts=*/{1 * CENT, 3 * CENT}, cs_params);
|
||||
|
||||
// BnB fails to find changeless solution when overshooting by cost_of_change + 1 sat
|
||||
TestBnBFail("Overshoot upper bound", utxo_pool, /*selection_target=*/4 * CENT - default_cs_params.m_cost_of_change - 1);
|
||||
TestBnBFail("Overshoot upper bound", utxo_pool, /*selection_target=*/4 * CENT - cs_params.m_cost_of_change - 1, cs_params);
|
||||
|
||||
TestBnBSuccess("Select max weight", utxo_pool, /*selection_target=*/4 * CENT, /*expected_input_amounts=*/{1 * CENT, 3 * CENT}, cs_params, /*custom_spending_vsize=*/P2WPKH_INPUT_VSIZE, /*max_selection_weight=*/4 * 2 * P2WPKH_INPUT_VSIZE);
|
||||
|
||||
TestBnBFail("Exceed max weight", utxo_pool, /*selection_target=*/4 * CENT, /*max_selection_weight=*/4 * 2 * P2WPKH_INPUT_VSIZE - 1, /*expect_max_weight_exceeded=*/true);
|
||||
TestBnBFail("Exceed max weight", utxo_pool, /*selection_target=*/4 * CENT, cs_params, /*max_selection_weight=*/4 * 2 * P2WPKH_INPUT_VSIZE - 1, /*expect_max_weight_exceeded=*/true);
|
||||
|
||||
// Simple cases without BnB solution
|
||||
TestBnBFail("Smallest combination too big", utxo_pool, /*selection_target=*/0.5 * CENT);
|
||||
TestBnBFail("No UTXO combination in target window", utxo_pool, /*selection_target=*/7 * CENT);
|
||||
TestBnBFail("Select more than available", utxo_pool, /*selection_target=*/10 * CENT);
|
||||
TestBnBFail("Smallest combination too big", utxo_pool, /*selection_target=*/0.5 * CENT, cs_params);
|
||||
TestBnBFail("No UTXO combination in target window", utxo_pool, /*selection_target=*/7 * CENT, cs_params);
|
||||
TestBnBFail("Select more than available", utxo_pool, /*selection_target=*/10 * CENT, cs_params);
|
||||
|
||||
// Test skipping of equivalent input sets
|
||||
std::vector<OutputGroup> clone_pool;
|
||||
AddCoins(clone_pool, {2 * CENT, 7 * CENT, 7 * CENT}, cs_params);
|
||||
AddDuplicateCoins(clone_pool, 50'000, 5 * CENT, cs_params);
|
||||
AddDuplicateCoins(clone_pool, /*count=*/50'000, /*amount=*/5 * CENT, cs_params);
|
||||
TestBnBSuccess("Skip equivalent input sets", clone_pool, /*selection_target=*/16 * CENT, /*expected_input_amounts=*/{2 * CENT, 7 * CENT, 7 * CENT}, cs_params);
|
||||
|
||||
/* Test BnB attempt limit (`TOTAL_TRIES`)
|
||||
@@ -189,7 +202,7 @@ BOOST_AUTO_TEST_CASE(bnb_test)
|
||||
expected_inputs.push_back(doppelgangers[i]);
|
||||
} else {
|
||||
// Any eight UTXOs including at least one UTXO with the added cost_of_change will exceed target window
|
||||
doppelgangers.push_back(1 * CENT + default_cs_params.m_cost_of_change + i);
|
||||
doppelgangers.push_back(1 * CENT + cs_params.m_cost_of_change + i);
|
||||
}
|
||||
}
|
||||
AddCoins(doppelganger_pool, doppelgangers, cs_params);
|
||||
@@ -197,8 +210,8 @@ BOOST_AUTO_TEST_CASE(bnb_test)
|
||||
TestBnBSuccess("Combine smallest 8 of 17 unique UTXOs", doppelganger_pool, /*selection_target=*/8 * CENT, /*expected_input_amounts=*/expected_inputs, cs_params);
|
||||
|
||||
// Starting with 18 unique UTXOs of similar effective value we will not find the solution due to exceeding the attempt limit
|
||||
AddCoins(doppelganger_pool, {1 * CENT + default_cs_params.m_cost_of_change + 17}, cs_params);
|
||||
TestBnBFail("Exhaust looking for smallest 8 of 18 unique UTXOs", doppelganger_pool, /*selection_target=*/8 * CENT);
|
||||
AddCoins(doppelganger_pool, {1 * CENT + cs_params.m_cost_of_change + 17}, cs_params);
|
||||
TestBnBFail("Exhaust looking for smallest 8 of 18 unique UTXOs", doppelganger_pool, /*selection_target=*/8 * CENT, cs_params);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,8 +222,7 @@ BOOST_AUTO_TEST_CASE(bnb_feerate_sensitivity_test)
|
||||
AddCoins(low_feerate_pool, {2 * CENT, 3 * CENT, 5 * CENT, 10 * CENT});
|
||||
TestBnBSuccess("Select many inputs at low feerates", low_feerate_pool, /*selection_target=*/10 * CENT, /*expected_input_amounts=*/{2 * CENT, 3 * CENT, 5 * CENT});
|
||||
|
||||
CoinSelectionParams high_feerate_params = init_default_params();
|
||||
high_feerate_params.m_effective_feerate = CFeeRate{25'000};
|
||||
const CoinSelectionParams high_feerate_params = init_cs_params(/*eff_feerate=*/25'000);
|
||||
std::vector<OutputGroup> high_feerate_pool; // 25 sat/vB (greater than long_term_feerate of 10 sat/vB)
|
||||
AddCoins(high_feerate_pool, {2 * CENT, 3 * CENT, 5 * CENT, 10 * CENT}, high_feerate_params);
|
||||
TestBnBSuccess("Select one input at high feerates", high_feerate_pool, /*selection_target=*/10 * CENT, /*expected_input_amounts=*/{10 * CENT}, high_feerate_params);
|
||||
@@ -225,5 +237,54 @@ BOOST_AUTO_TEST_CASE(bnb_feerate_sensitivity_test)
|
||||
TestBnBSuccess("Prefer two light inputs over two heavy inputs at high feerates", high_feerate_pool, /*selection_target=*/13 * CENT, /*expected_input_amounts=*/{3 * CENT, 10 * CENT}, high_feerate_params);
|
||||
}
|
||||
|
||||
static void TestSRDSuccess(std::string test_title, std::vector<OutputGroup>& utxo_pool, const CAmount& selection_target, const CoinSelectionParams& cs_params = default_cs_params, const int max_selection_weight = MAX_STANDARD_TX_WEIGHT)
|
||||
{
|
||||
CAmount expected_min_amount = selection_target + cs_params.m_change_fee + CHANGE_LOWER;
|
||||
|
||||
const auto result = SelectCoinsSRD(utxo_pool, selection_target, cs_params.m_change_fee, cs_params.rng_fast, max_selection_weight);
|
||||
BOOST_CHECK_MESSAGE(result, "Falsy result in SRD-Success: " + test_title);
|
||||
const CAmount selected_effective_value = result->GetSelectedEffectiveValue();
|
||||
BOOST_CHECK_MESSAGE(selected_effective_value >= expected_min_amount, strprintf("Selected effective value is lower than expected in SRD-Success: %s. Expected %d, but got %d", test_title, expected_min_amount, selected_effective_value));
|
||||
BOOST_CHECK_MESSAGE(result->GetWeight() <= max_selection_weight, strprintf("Selected weight is higher than permitted in SRD-Success: %s. Expected %d, but got %d", test_title, max_selection_weight, result->GetWeight()));
|
||||
}
|
||||
|
||||
static void TestSRDFail(std::string test_title, std::vector<OutputGroup>& utxo_pool, const CAmount& selection_target, const CoinSelectionParams& cs_params = default_cs_params, int max_selection_weight = MAX_STANDARD_TX_WEIGHT, const bool expect_max_weight_exceeded = false)
|
||||
{
|
||||
const auto result = SelectCoinsSRD(utxo_pool, selection_target, cs_params.m_change_fee, cs_params.rng_fast, max_selection_weight);
|
||||
BOOST_CHECK_MESSAGE(!result, "SRD-Fail: " + test_title);
|
||||
bool max_weight_exceeded = util::ErrorString(result).original.find("The inputs size exceeds the maximum weight") != std::string::npos;
|
||||
BOOST_CHECK(expect_max_weight_exceeded == max_weight_exceeded);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(srd_test)
|
||||
{
|
||||
for (int feerate : FEERATES) {
|
||||
std::vector<OutputGroup> utxo_pool;
|
||||
|
||||
const CoinSelectionParams cs_params = init_cs_params(feerate);
|
||||
|
||||
TestSRDFail("Empty UTXO pool", utxo_pool, /*selection_target=*/1 * CENT, cs_params);
|
||||
|
||||
AddCoins(utxo_pool, {1 * CENT, 3 * CENT, 5 * CENT}, cs_params);
|
||||
|
||||
TestSRDSuccess("Select 21k sats", utxo_pool, /*selection_target=*/21'000, cs_params);
|
||||
TestSRDSuccess("Select 1 CENT", utxo_pool, /*selection_target=*/1 * CENT, cs_params);
|
||||
TestSRDSuccess("Select 3.125 CENT", utxo_pool, /*selection_target=*/3'125'000, cs_params);
|
||||
TestSRDSuccess("Select 4 CENT", utxo_pool, /*selection_target=*/4 * CENT, cs_params);
|
||||
TestSRDSuccess("Select 7 CENT", utxo_pool, /*selection_target=*/7 * CENT, cs_params);
|
||||
|
||||
// The minimum change amount for SRD is the feerate dependent `change_fee` plus CHANGE_LOWER
|
||||
TestSRDSuccess("Create minimum change", utxo_pool, /*selection_target=*/9 * CENT - cs_params.m_change_fee - CHANGE_LOWER, cs_params);
|
||||
TestSRDFail("Undershoot minimum change by one sat", utxo_pool, /*selection_target=*/9 * CENT - cs_params.m_change_fee - CHANGE_LOWER + 1, cs_params);
|
||||
TestSRDFail("Spend more than available", utxo_pool, /*selection_target=*/9 * CENT + 1, cs_params);
|
||||
TestSRDFail("Spend everything", utxo_pool, /*selection_target=*/9 * CENT, cs_params);
|
||||
|
||||
AddDuplicateCoins(utxo_pool, /*count=*/100, /*amount=*/5 * CENT, cs_params);
|
||||
AddDuplicateCoins(utxo_pool, /*count=*/3, /*amount=*/7 * CENT, cs_params);
|
||||
TestSRDSuccess("Select most valuable UTXOs for acceptable weight", utxo_pool, /*selection_target=*/20 * CENT, cs_params, /*max_selection_weight=*/4 * 4 * (P2WPKH_INPUT_VSIZE - 1 ));
|
||||
TestSRDFail("No acceptable weight possible", utxo_pool, /*selection_target=*/25 * CENT, cs_params, /*max_selection_weight=*/4 * 3 * P2WPKH_INPUT_VSIZE, /*expect_max_weight_exceeded=*/true);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
} // namespace wallet
|
||||
|
||||
@@ -1185,96 +1185,6 @@ BOOST_AUTO_TEST_CASE(coin_grinder_tests)
|
||||
}
|
||||
}
|
||||
|
||||
static util::Result<SelectionResult> SelectCoinsSRD(const CAmount& target,
|
||||
const CoinSelectionParams& cs_params,
|
||||
const node::NodeContext& m_node,
|
||||
int max_selection_weight,
|
||||
std::function<CoinsResult(CWallet&)> coin_setup)
|
||||
{
|
||||
std::unique_ptr<CWallet> wallet = NewWallet(m_node);
|
||||
CoinEligibilityFilter filter(0, 0, 0); // accept all coins without ancestors
|
||||
Groups group = GroupOutputs(*wallet, coin_setup(*wallet), cs_params, {{filter}})[filter].all_groups;
|
||||
return SelectCoinsSRD(group.positive_group, target, cs_params.m_change_fee, cs_params.rng_fast, max_selection_weight);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(srd_tests)
|
||||
{
|
||||
// Test SRD:
|
||||
// 1) Insufficient funds, select all provided coins and fail.
|
||||
// 2) Exceeded max weight, coin selection always surpasses the max allowed weight.
|
||||
// 3) Select coins without surpassing the max weight (some coins surpasses the max allowed weight, some others not)
|
||||
|
||||
FastRandomContext rand;
|
||||
CoinSelectionParams dummy_params{ // Only used to provide the 'avoid_partial' flag.
|
||||
rand,
|
||||
/*change_output_size=*/34,
|
||||
/*change_spend_size=*/68,
|
||||
/*min_change_target=*/CENT,
|
||||
/*effective_feerate=*/CFeeRate(0),
|
||||
/*long_term_feerate=*/CFeeRate(0),
|
||||
/*discard_feerate=*/CFeeRate(0),
|
||||
/*tx_noinputs_size=*/10 + 34, // static header size + output size
|
||||
/*avoid_partial=*/false,
|
||||
};
|
||||
|
||||
{
|
||||
// #########################################################
|
||||
// 1) Insufficient funds, select all provided coins and fail
|
||||
// #########################################################
|
||||
CAmount target = 49.5L * COIN;
|
||||
int max_selection_weight = 10000; // high enough to not fail for this reason.
|
||||
const auto& res = SelectCoinsSRD(target, dummy_params, m_node, max_selection_weight, [&](CWallet& wallet) {
|
||||
CoinsResult available_coins;
|
||||
for (int j = 0; j < 10; ++j) {
|
||||
add_coin(available_coins, wallet, CAmount(1 * COIN));
|
||||
add_coin(available_coins, wallet, CAmount(2 * COIN));
|
||||
}
|
||||
return available_coins;
|
||||
});
|
||||
BOOST_CHECK(!res);
|
||||
BOOST_CHECK(util::ErrorString(res).empty()); // empty means "insufficient funds"
|
||||
}
|
||||
|
||||
{
|
||||
// ###########################
|
||||
// 2) Test max weight exceeded
|
||||
// ###########################
|
||||
CAmount target = 49.5L * COIN;
|
||||
int max_selection_weight = 3000;
|
||||
const auto& res = SelectCoinsSRD(target, dummy_params, m_node, max_selection_weight, [&](CWallet& wallet) {
|
||||
CoinsResult available_coins;
|
||||
for (int j = 0; j < 10; ++j) {
|
||||
/* 10 × 1 BTC + 10 × 2 BTC = 30 BTC. 20 × 272 WU = 5440 WU */
|
||||
add_coin(available_coins, wallet, CAmount(1 * COIN), CFeeRate(0), 144, false, 0, true);
|
||||
add_coin(available_coins, wallet, CAmount(2 * COIN), CFeeRate(0), 144, false, 0, true);
|
||||
}
|
||||
return available_coins;
|
||||
});
|
||||
BOOST_CHECK(!res);
|
||||
BOOST_CHECK(util::ErrorString(res).original.find("The inputs size exceeds the maximum weight") != std::string::npos);
|
||||
}
|
||||
|
||||
{
|
||||
// ################################################################################################################
|
||||
// 3) Test that SRD result does not exceed the max weight
|
||||
// ################################################################################################################
|
||||
CAmount target = 25.33L * COIN;
|
||||
int max_selection_weight = 10000; // WU
|
||||
const auto& res = SelectCoinsSRD(target, dummy_params, m_node, max_selection_weight, [&](CWallet& wallet) {
|
||||
CoinsResult available_coins;
|
||||
for (int j = 0; j < 60; ++j) { // 60 UTXO --> 19,8 BTC total --> 60 × 272 WU = 16320 WU
|
||||
add_coin(available_coins, wallet, CAmount(0.33 * COIN), CFeeRate(0), 144, false, 0, true);
|
||||
}
|
||||
for (int i = 0; i < 10; i++) { // 10 UTXO --> 20 BTC total --> 10 × 272 WU = 2720 WU
|
||||
add_coin(available_coins, wallet, CAmount(2 * COIN), CFeeRate(0), 144, false, 0, true);
|
||||
}
|
||||
return available_coins;
|
||||
});
|
||||
BOOST_CHECK(res);
|
||||
BOOST_CHECK(res->GetWeight() <= max_selection_weight);
|
||||
}
|
||||
}
|
||||
|
||||
static util::Result<SelectionResult> select_coins(const CAmount& target, const CoinSelectionParams& cs_params, const CCoinControl& cc, std::function<CoinsResult(CWallet&)> coin_setup, const node::NodeContext& m_node)
|
||||
{
|
||||
std::unique_ptr<CWallet> wallet = NewWallet(m_node);
|
||||
|
||||
Reference in New Issue
Block a user