mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-08 10:31:50 -05:00
Compare commits
8 commits
13bc8e57ae
...
93a8f6d2b1
Author | SHA1 | Date | |
---|---|---|---|
![]() |
93a8f6d2b1 | ||
![]() |
280e65d3a7 | ||
![]() |
8bf2b22fcc | ||
![]() |
9686a9a20e | ||
![]() |
c9417ee593 | ||
![]() |
bfd2fe70ae | ||
![]() |
f3b0593b8b | ||
![]() |
a8bf940ae2 |
3 changed files with 199 additions and 163 deletions
|
@ -10,6 +10,7 @@ target_sources(test_bitcoin
|
|||
wallet_test_fixture.cpp
|
||||
db_tests.cpp
|
||||
coinselector_tests.cpp
|
||||
coinselection_tests.cpp
|
||||
feebumper_tests.cpp
|
||||
group_outputs_tests.cpp
|
||||
init_tests.cpp
|
||||
|
|
195
src/wallet/test/coinselection_tests.cpp
Normal file
195
src/wallet/test/coinselection_tests.cpp
Normal file
|
@ -0,0 +1,195 @@
|
|||
// Copyright (c) 2024 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <consensus/amount.h>
|
||||
#include <policy/policy.h>
|
||||
#include <wallet/coinselection.h>
|
||||
#include <wallet/test/wallet_test_fixture.h>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
namespace wallet {
|
||||
BOOST_FIXTURE_TEST_SUITE(coinselection_tests, TestingSetup)
|
||||
|
||||
static int next_lock_time = 0;
|
||||
static FastRandomContext default_rand;
|
||||
|
||||
/** Default coin selection parameters (dcsp) allow us to only explicitly set
|
||||
* parameters when a diverging value is relevant in the context of a test. */
|
||||
static CoinSelectionParams init_default_params()
|
||||
{
|
||||
CoinSelectionParams dcsp{
|
||||
/*rng_fast*/default_rand,
|
||||
/*change_output_size=*/31,
|
||||
/*change_spend_size=*/68,
|
||||
/*min_change_target=*/50'000,
|
||||
/*effective_feerate=*/CFeeRate(5000),
|
||||
/*long_term_feerate=*/CFeeRate(10'000),
|
||||
/*discard_feerate=*/CFeeRate(3000),
|
||||
/*tx_noinputs_size=*/11 + 31, //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;
|
||||
}
|
||||
|
||||
static const CoinSelectionParams default_cs_params = init_default_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 = 68)
|
||||
{
|
||||
// Always assume that we only have one input
|
||||
CMutableTransaction tx;
|
||||
tx.vout.resize(1);
|
||||
CAmount fees = cs_params.m_effective_feerate.GetFee(custom_spending_vsize);
|
||||
tx.vout[0].nValue = amount + int(is_eff_value) * fees;
|
||||
tx.nLockTime = next_lock_time++; // so all transactions get different hashes
|
||||
OutputGroup group(cs_params);
|
||||
group.Insert(std::make_shared<COutput>(COutPoint(tx.GetHash(), 0), tx.vout.at(0), /*depth=*/1, /*input_bytes=*/custom_spending_vsize, /*spendable=*/true, /*solvable=*/true, /*safe=*/true, /*time=*/0, /*from_me=*/false, /*fees=*/fees), /*ancestors=*/0, /*descendants=*/0);
|
||||
return group;
|
||||
}
|
||||
|
||||
/** Make multiple OutputGroups with the given values as their effective value */
|
||||
static void AddCoins(std::vector<OutputGroup>& utxo_pool, std::vector<CAmount> coins, CoinSelectionParams cs_params = default_cs_params)
|
||||
{
|
||||
for (CAmount c : coins) {
|
||||
utxo_pool.push_back(MakeCoin(c, true, cs_params));
|
||||
}
|
||||
}
|
||||
|
||||
/** Make multiple coins that share the same effective value */
|
||||
static void AddDuplicateCoins(std::vector<OutputGroup>& utxo_pool, int count, int amount) {
|
||||
for (int i = 0 ; i < count; ++i) {
|
||||
utxo_pool.push_back(MakeCoin(amount));
|
||||
}
|
||||
}
|
||||
|
||||
/** Check if SelectionResult a is equivalent to SelectionResult b.
|
||||
* Equivalent means same input values, but maybe different inputs (i.e. same value, different prevout) */
|
||||
static bool EquivalentResult(const SelectionResult& a, const SelectionResult& b)
|
||||
{
|
||||
std::vector<CAmount> a_amts;
|
||||
std::vector<CAmount> b_amts;
|
||||
for (const auto& coin : a.GetInputSet()) {
|
||||
a_amts.push_back(coin->txout.nValue);
|
||||
}
|
||||
for (const auto& coin : b.GetInputSet()) {
|
||||
b_amts.push_back(coin->txout.nValue);
|
||||
}
|
||||
std::sort(a_amts.begin(), a_amts.end());
|
||||
std::sort(b_amts.begin(), b_amts.end());
|
||||
|
||||
std::pair<std::vector<CAmount>::iterator, std::vector<CAmount>::iterator> ret = std::mismatch(a_amts.begin(), a_amts.end(), b_amts.begin());
|
||||
return ret.first == a_amts.end() && ret.second == b_amts.end();
|
||||
}
|
||||
|
||||
static void TestBnBSuccess(std::string test_title, std::vector<OutputGroup>& utxo_pool, const CAmount& selection_target, const std::vector<CAmount>& expected_input_amounts, const CoinSelectionParams& cs_params = default_cs_params)
|
||||
{
|
||||
SelectionResult expected_result(CAmount(0), SelectionAlgorithm::BNB);
|
||||
CAmount expected_amount = 0;
|
||||
for (CAmount input_amount : expected_input_amounts) {
|
||||
OutputGroup group = MakeCoin(input_amount, true, cs_params);
|
||||
expected_amount += group.m_value;
|
||||
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=*/MAX_STANDARD_TX_WEIGHT);
|
||||
BOOST_CHECK_MESSAGE(result, "Falsy result in BnB-Success: " + test_title);
|
||||
BOOST_CHECK_MESSAGE(EquivalentResult(expected_result, *result), "Result mismatch in BnB-Success: " + test_title);
|
||||
BOOST_CHECK_MESSAGE(result->GetSelectedValue() == expected_amount, "Selected amount mismatch in BnB-Success: " + test_title);
|
||||
}
|
||||
|
||||
static void TestBnBFail(std::string test_title, std::vector<OutputGroup>& utxo_pool, const CAmount& selection_target)
|
||||
{
|
||||
BOOST_CHECK_MESSAGE(!SelectCoinsBnB(utxo_pool, selection_target, /*cost_of_change=*/default_cs_params.m_cost_of_change, /*max_selection_weight=*/MAX_STANDARD_TX_WEIGHT), "BnB-Fail: " + test_title);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(bnb_test)
|
||||
{
|
||||
std::vector<OutputGroup> utxo_pool;
|
||||
|
||||
// Fail for empty UTXO pool
|
||||
TestBnBFail("Empty UTXO pool", utxo_pool, /*selection_target=*/1 * CENT);
|
||||
|
||||
AddCoins(utxo_pool, {1 * CENT, 3 * CENT, 5 * CENT});
|
||||
|
||||
// Simple success cases
|
||||
TestBnBSuccess("Select smallest UTXO", utxo_pool, /*selection_target=*/1 * CENT, /*expected_input_amounts=*/{1 * CENT});
|
||||
TestBnBSuccess("Select middle UTXO", utxo_pool, /*selection_target=*/3 * CENT, /*expected_input_amounts=*/{3 * CENT});
|
||||
TestBnBSuccess("Select biggest UTXO", utxo_pool, /*selection_target=*/5 * CENT, /*expected_input_amounts=*/{5 * CENT});
|
||||
TestBnBSuccess("Select two UTXOs", utxo_pool, /*selection_target=*/4 * CENT, /*expected_input_amounts=*/{1 * CENT, 3 * CENT});
|
||||
TestBnBSuccess("Select all UTXOs", utxo_pool, /*selection_target=*/9 * CENT, /*expected_input_amounts=*/{1 * CENT, 3 * CENT, 5 * CENT});
|
||||
|
||||
// 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});
|
||||
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
|
||||
// Test skipping of equivalent input sets
|
||||
std::vector<OutputGroup> clone_pool;
|
||||
AddCoins(clone_pool, {2 * CENT, 7 * CENT, 7 * CENT});
|
||||
AddDuplicateCoins(clone_pool, 50'000, 5 * CENT);
|
||||
TestBnBSuccess("Skip equivalent input sets", clone_pool, /*selection_target=*/16 * CENT, /*expected_input_amounts=*/{2 * CENT, 7 * CENT, 7 * CENT});
|
||||
|
||||
/* Test BnB attempt limit (`TOTAL_TRIES`)
|
||||
*
|
||||
* Generally, on a diverse UTXO pool BnB will quickly pass over UTXOs bigger than the target and then start
|
||||
* combining small counts of UTXOs that in sum remain under the selection_target+cost_of_change. When there are
|
||||
* multiple UTXOs that have matching amount and cost, combinations with equivalent input sets are skipped. The UTXO
|
||||
* pool for this test is specifically crafted to create as much branching as possible. The selection target is
|
||||
* 8 CENT while all UTXOs are slightly bigger than 1 CENT. The smallest eight are 100,000…100,007 sats, while the larger
|
||||
* ten are 100,009,…,100,018 sats plus cost_of_change.
|
||||
*
|
||||
* Because BnB will only select input sets that fall between selection_target and selection_target + cost_of_change,
|
||||
* and the search traverses the UTXO pool from large amount to small amount, the search will traverse every single
|
||||
* combination of eight inputs, but all except the last will overshoot by more than cost_of_change on the eighth
|
||||
* input. Only the very last combination with the eight smallest UTXOs falls into the target window.
|
||||
*/
|
||||
std::vector<OutputGroup> doppelganger_pool;
|
||||
std::vector<CAmount> doppelgangers;
|
||||
std::vector<CAmount> expected_inputs;
|
||||
for (int i = 0; i < 17; ++i) {
|
||||
if (i < 8) {
|
||||
// The eight smallest UTXOs can be combined to create expected_result
|
||||
doppelgangers.push_back(1 * CENT + i);
|
||||
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);
|
||||
}
|
||||
}
|
||||
AddCoins(doppelganger_pool, doppelgangers);
|
||||
// Among up to 17 unique UTXOs of similar effective value we will find a solution composed of the eight smallest UTXOs
|
||||
TestBnBSuccess("Combine smallest 8 of 17 unique UTXOs", doppelganger_pool, /*selection_target=*/8 * CENT, /*expected_input_amounts=*/expected_inputs);
|
||||
|
||||
// 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});
|
||||
TestBnBFail("Exhaust looking for smallest 8 of 18 unique UTXOs", doppelganger_pool, /*selection_target=*/8 * CENT);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(bnb_feerate_sensitivity_test)
|
||||
{
|
||||
// Create sets of UTXOs with the same effective amounts at different feerates (but different absolute amounts)
|
||||
std::vector<OutputGroup> low_feerate_pool; // 5 sat/vB (default, and lower than long_term_feerate of 10 sat/vB)
|
||||
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};
|
||||
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);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
} // namespace wallet
|
|
@ -37,15 +37,6 @@ static const CoinEligibilityFilter filter_confirmed(1, 1, 0);
|
|||
static const CoinEligibilityFilter filter_standard_extra(6, 6, 0);
|
||||
static int nextLockTime = 0;
|
||||
|
||||
static void add_coin(const CAmount& nValue, int nInput, std::vector<COutput>& set)
|
||||
{
|
||||
CMutableTransaction tx;
|
||||
tx.vout.resize(nInput + 1);
|
||||
tx.vout[nInput].nValue = nValue;
|
||||
tx.nLockTime = nextLockTime++; // so all transactions get different hashes
|
||||
set.emplace_back(COutPoint(tx.GetHash(), nInput), tx.vout.at(nInput), /*depth=*/ 1, /*input_bytes=*/ -1, /*spendable=*/ true, /*solvable=*/ true, /*safe=*/ true, /*time=*/ 0, /*from_me=*/ false, /*fees=*/ 0);
|
||||
}
|
||||
|
||||
static void add_coin(const CAmount& nValue, int nInput, SelectionResult& result)
|
||||
{
|
||||
CMutableTransaction tx;
|
||||
|
@ -133,18 +124,6 @@ static bool EqualResult(const SelectionResult& a, const SelectionResult& b)
|
|||
return ret.first == a.GetInputSet().end() && ret.second == b.GetInputSet().end();
|
||||
}
|
||||
|
||||
static CAmount make_hard_case(int utxos, std::vector<COutput>& utxo_pool)
|
||||
{
|
||||
utxo_pool.clear();
|
||||
CAmount target = 0;
|
||||
for (int i = 0; i < utxos; ++i) {
|
||||
target += CAmount{1} << (utxos+i);
|
||||
add_coin(CAmount{1} << (utxos+i), 2*i, utxo_pool);
|
||||
add_coin((CAmount{1} << (utxos+i)) + (CAmount{1} << (utxos-1-i)), 2*i + 1, utxo_pool);
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
inline std::vector<OutputGroup>& GroupCoins(const std::vector<COutput>& available_coins, bool subtract_fee_outputs = false)
|
||||
{
|
||||
static std::vector<OutputGroup> static_groups;
|
||||
|
@ -195,115 +174,9 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
|
|||
std::vector<COutput> utxo_pool;
|
||||
SelectionResult expected_result(CAmount(0), SelectionAlgorithm::BNB);
|
||||
|
||||
/////////////////////////
|
||||
// Known Outcome tests //
|
||||
/////////////////////////
|
||||
|
||||
// Empty utxo pool
|
||||
BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), 1 * CENT, 0.5 * CENT));
|
||||
|
||||
// Add utxos
|
||||
add_coin(1 * CENT, 1, utxo_pool);
|
||||
add_coin(2 * CENT, 2, utxo_pool);
|
||||
add_coin(3 * CENT, 3, utxo_pool);
|
||||
add_coin(4 * CENT, 4, utxo_pool);
|
||||
|
||||
// Select 1 Cent
|
||||
add_coin(1 * CENT, 1, expected_result);
|
||||
const auto result1 = SelectCoinsBnB(GroupCoins(utxo_pool), 1 * CENT, 0.5 * CENT);
|
||||
BOOST_CHECK(result1);
|
||||
BOOST_CHECK(EquivalentResult(expected_result, *result1));
|
||||
BOOST_CHECK_EQUAL(result1->GetSelectedValue(), 1 * CENT);
|
||||
expected_result.Clear();
|
||||
|
||||
// Select 2 Cent
|
||||
add_coin(2 * CENT, 2, expected_result);
|
||||
const auto result2 = SelectCoinsBnB(GroupCoins(utxo_pool), 2 * CENT, 0.5 * CENT);
|
||||
BOOST_CHECK(result2);
|
||||
BOOST_CHECK(EquivalentResult(expected_result, *result2));
|
||||
BOOST_CHECK_EQUAL(result2->GetSelectedValue(), 2 * CENT);
|
||||
expected_result.Clear();
|
||||
|
||||
// Select 5 Cent
|
||||
add_coin(3 * CENT, 3, expected_result);
|
||||
add_coin(2 * CENT, 2, expected_result);
|
||||
const auto result3 = SelectCoinsBnB(GroupCoins(utxo_pool), 5 * CENT, 0.5 * CENT);
|
||||
BOOST_CHECK(result3);
|
||||
BOOST_CHECK(EquivalentResult(expected_result, *result3));
|
||||
BOOST_CHECK_EQUAL(result3->GetSelectedValue(), 5 * CENT);
|
||||
expected_result.Clear();
|
||||
|
||||
// Select 11 Cent, not possible
|
||||
BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), 11 * CENT, 0.5 * CENT));
|
||||
expected_result.Clear();
|
||||
|
||||
// Cost of change is greater than the difference between target value and utxo sum
|
||||
add_coin(1 * CENT, 1, expected_result);
|
||||
const auto result4 = SelectCoinsBnB(GroupCoins(utxo_pool), 0.9 * CENT, 0.5 * CENT);
|
||||
BOOST_CHECK(result4);
|
||||
BOOST_CHECK_EQUAL(result4->GetSelectedValue(), 1 * CENT);
|
||||
BOOST_CHECK(EquivalentResult(expected_result, *result4));
|
||||
expected_result.Clear();
|
||||
|
||||
// Cost of change is less than the difference between target value and utxo sum
|
||||
BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), 0.9 * CENT, 0));
|
||||
expected_result.Clear();
|
||||
|
||||
// Select 10 Cent
|
||||
add_coin(5 * CENT, 5, utxo_pool);
|
||||
add_coin(4 * CENT, 4, expected_result);
|
||||
add_coin(3 * CENT, 3, expected_result);
|
||||
add_coin(2 * CENT, 2, expected_result);
|
||||
add_coin(1 * CENT, 1, expected_result);
|
||||
const auto result5 = SelectCoinsBnB(GroupCoins(utxo_pool), 10 * CENT, 0.5 * CENT);
|
||||
BOOST_CHECK(result5);
|
||||
BOOST_CHECK(EquivalentResult(expected_result, *result5));
|
||||
BOOST_CHECK_EQUAL(result5->GetSelectedValue(), 10 * CENT);
|
||||
expected_result.Clear();
|
||||
|
||||
// Select 0.25 Cent, not possible
|
||||
BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), 0.25 * CENT, 0.5 * CENT));
|
||||
expected_result.Clear();
|
||||
|
||||
// Iteration exhaustion test
|
||||
CAmount target = make_hard_case(17, utxo_pool);
|
||||
BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), target, 1)); // Should exhaust
|
||||
target = make_hard_case(14, utxo_pool);
|
||||
const auto result7 = SelectCoinsBnB(GroupCoins(utxo_pool), target, 1); // Should not exhaust
|
||||
BOOST_CHECK(result7);
|
||||
|
||||
// Test same value early bailout optimization
|
||||
utxo_pool.clear();
|
||||
add_coin(7 * CENT, 7, expected_result);
|
||||
add_coin(7 * CENT, 7, expected_result);
|
||||
add_coin(7 * CENT, 7, expected_result);
|
||||
add_coin(7 * CENT, 7, expected_result);
|
||||
add_coin(2 * CENT, 7, expected_result);
|
||||
add_coin(7 * CENT, 7, utxo_pool);
|
||||
add_coin(7 * CENT, 7, utxo_pool);
|
||||
add_coin(7 * CENT, 7, utxo_pool);
|
||||
add_coin(7 * CENT, 7, utxo_pool);
|
||||
add_coin(2 * CENT, 7, utxo_pool);
|
||||
for (int i = 0; i < 50000; ++i) {
|
||||
add_coin(5 * CENT, 7, utxo_pool);
|
||||
}
|
||||
const auto result8 = SelectCoinsBnB(GroupCoins(utxo_pool), 30 * CENT, 5000);
|
||||
BOOST_CHECK(result8);
|
||||
BOOST_CHECK_EQUAL(result8->GetSelectedValue(), 30 * CENT);
|
||||
BOOST_CHECK(EquivalentResult(expected_result, *result8));
|
||||
|
||||
////////////////////
|
||||
// Behavior tests //
|
||||
////////////////////
|
||||
// Select 1 Cent with pool of only greater than 5 Cent
|
||||
utxo_pool.clear();
|
||||
for (int i = 5; i <= 20; ++i) {
|
||||
add_coin(i * CENT, i, utxo_pool);
|
||||
}
|
||||
// Run 100 times, to make sure it is never finding a solution
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
BOOST_CHECK(!SelectCoinsBnB(GroupCoins(utxo_pool), 1 * CENT, 2 * CENT));
|
||||
}
|
||||
|
||||
// Make sure that effective value is working in AttemptSelection when BnB is used
|
||||
CoinSelectionParams coin_selection_params_bnb{
|
||||
|
@ -366,7 +239,7 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
|
|||
|
||||
CoinsResult available_coins;
|
||||
|
||||
// single coin should be selected when effective fee > long term fee
|
||||
// pre selected coin should be selected even if disadvantageous
|
||||
coin_selection_params_bnb.m_effective_feerate = CFeeRate(5000);
|
||||
coin_selection_params_bnb.m_long_term_feerate = CFeeRate(3000);
|
||||
|
||||
|
@ -377,42 +250,9 @@ BOOST_AUTO_TEST_CASE(bnb_search_test)
|
|||
add_coin(available_coins, *wallet, 1 * CENT + input_fee, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
|
||||
|
||||
expected_result.Clear();
|
||||
add_coin(10 * CENT + input_fee, 2, expected_result);
|
||||
add_coin(9 * CENT + input_fee, 2, expected_result);
|
||||
add_coin(1 * CENT + input_fee, 2, expected_result);
|
||||
CCoinControl coin_control;
|
||||
const auto result11 = SelectCoins(*wallet, available_coins, /*pre_set_inputs=*/{}, 10 * CENT, coin_control, coin_selection_params_bnb);
|
||||
BOOST_CHECK(EquivalentResult(expected_result, *result11));
|
||||
available_coins.Clear();
|
||||
|
||||
// more coins should be selected when effective fee < long term fee
|
||||
coin_selection_params_bnb.m_effective_feerate = CFeeRate(3000);
|
||||
coin_selection_params_bnb.m_long_term_feerate = CFeeRate(5000);
|
||||
|
||||
// Add selectable outputs, increasing their raw amounts by their input fee to make the effective value equal to the raw amount
|
||||
input_fee = coin_selection_params_bnb.m_effective_feerate.GetFee(/*num_bytes=*/68); // bech32 input size (default test output type)
|
||||
add_coin(available_coins, *wallet, 10 * CENT + input_fee, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
|
||||
add_coin(available_coins, *wallet, 9 * CENT + input_fee, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
|
||||
add_coin(available_coins, *wallet, 1 * CENT + input_fee, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
|
||||
|
||||
expected_result.Clear();
|
||||
add_coin(9 * CENT + input_fee, 2, expected_result);
|
||||
add_coin(1 * CENT + input_fee, 2, expected_result);
|
||||
const auto result12 = SelectCoins(*wallet, available_coins, /*pre_set_inputs=*/{}, 10 * CENT, coin_control, coin_selection_params_bnb);
|
||||
BOOST_CHECK(EquivalentResult(expected_result, *result12));
|
||||
available_coins.Clear();
|
||||
|
||||
// pre selected coin should be selected even if disadvantageous
|
||||
coin_selection_params_bnb.m_effective_feerate = CFeeRate(5000);
|
||||
coin_selection_params_bnb.m_long_term_feerate = CFeeRate(3000);
|
||||
|
||||
// Add selectable outputs, increasing their raw amounts by their input fee to make the effective value equal to the raw amount
|
||||
input_fee = coin_selection_params_bnb.m_effective_feerate.GetFee(/*num_bytes=*/68); // bech32 input size (default test output type)
|
||||
add_coin(available_coins, *wallet, 10 * CENT + input_fee, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
|
||||
add_coin(available_coins, *wallet, 9 * CENT + input_fee, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
|
||||
add_coin(available_coins, *wallet, 1 * CENT + input_fee, coin_selection_params_bnb.m_effective_feerate, 6 * 24, false, 0, true);
|
||||
|
||||
expected_result.Clear();
|
||||
add_coin(9 * CENT + input_fee, 2, expected_result);
|
||||
add_coin(1 * CENT + input_fee, 2, expected_result);
|
||||
coin_control.m_allow_other_inputs = true;
|
||||
COutput select_coin = available_coins.All().at(1); // pre select 9 coin
|
||||
coin_control.Select(select_coin.outpoint);
|
||||
|
|
Loading…
Add table
Reference in a new issue