Skip to content

Commit d3a1c09

Browse files
committed
test: coin selection, add coverage for SRD
1 parent 9d9689e commit d3a1c09

File tree

1 file changed

+94
-0
lines changed

1 file changed

+94
-0
lines changed

src/wallet/test/coinselector_tests.cpp

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -929,6 +929,100 @@ BOOST_AUTO_TEST_CASE(effective_value_test)
929929
BOOST_CHECK_EQUAL(output5.GetEffectiveValue(), nValue); // The effective value should be equal to the absolute value if input_bytes is -1
930930
}
931931

932+
933+
static util::Result<SelectionResult> SelectCoinsSRD(const CAmount& target,
934+
const CoinSelectionParams& cs_params,
935+
const node::NodeContext& m_node,
936+
int max_weight,
937+
std::function<CoinsResult(CWallet&)> coin_setup)
938+
{
939+
std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(m_node.chain.get(), "", CreateMockWalletDatabase());
940+
wallet->LoadWallet();
941+
LOCK(wallet->cs_wallet);
942+
wallet->SetWalletFlag(WALLET_FLAG_DESCRIPTORS);
943+
wallet->SetupDescriptorScriptPubKeyMans();
944+
945+
CoinEligibilityFilter filter(0, 0, 0); // accept all coins without ancestors
946+
Groups group = GroupOutputs(*wallet, coin_setup(*wallet), cs_params, {{filter}})[filter].all_groups;
947+
return SelectCoinsSRD(group.positive_group, target, cs_params.rng_fast, max_weight);
948+
}
949+
950+
BOOST_AUTO_TEST_CASE(srd_tests)
951+
{
952+
// Test SRD:
953+
// 1) Insufficient funds, select all provided coins and fail.
954+
// 2) Exceeded max weight, coin selection always surpasses the max allowed weight.
955+
// 3) Select coins without surpassing the max weight (some coins surpasses the max allowed weight, some others not)
956+
957+
FastRandomContext rand;
958+
CoinSelectionParams dummy_params{ // Only used to provide the 'avoid_partial' flag.
959+
rand,
960+
/*change_output_size=*/34,
961+
/*change_spend_size=*/68,
962+
/*min_change_target=*/CENT,
963+
/*effective_feerate=*/CFeeRate(0),
964+
/*long_term_feerate=*/CFeeRate(0),
965+
/*discard_feerate=*/CFeeRate(0),
966+
/*tx_noinputs_size=*/10 + 34, // static header size + output size
967+
/*avoid_partial=*/false,
968+
};
969+
970+
{
971+
// #########################################################
972+
// 1) Insufficient funds, select all provided coins and fail
973+
// #########################################################
974+
CAmount target = 49.5L * COIN;
975+
int max_weight = 10000; // high enough to not fail for this reason.
976+
const auto& res = SelectCoinsSRD(target, dummy_params, m_node, max_weight, [&](CWallet& wallet) {
977+
CoinsResult available_coins;
978+
for (int j = 0; j < 10; ++j) {
979+
add_coin(available_coins, wallet, CAmount(1 * COIN));
980+
add_coin(available_coins, wallet, CAmount(2 * COIN));
981+
}
982+
return available_coins;
983+
});
984+
BOOST_CHECK(!res);
985+
BOOST_CHECK(util::ErrorString(res).empty()); // empty means "insufficient funds"
986+
}
987+
988+
{
989+
// ###########################
990+
// 2) Test max weight exceeded
991+
// ###########################
992+
CAmount target = 49.5L * COIN;
993+
int max_weight = 3000;
994+
const auto& res = SelectCoinsSRD(target, dummy_params, m_node, max_weight, [&](CWallet& wallet) {
995+
CoinsResult available_coins;
996+
for (int j = 0; j < 10; ++j) {
997+
add_coin(available_coins, wallet, CAmount(1 * COIN), CFeeRate(0), 144, false, 0, true);
998+
add_coin(available_coins, wallet, CAmount(2 * COIN), CFeeRate(0), 144, false, 0, true);
999+
}
1000+
return available_coins;
1001+
});
1002+
BOOST_CHECK(!res);
1003+
BOOST_CHECK(util::ErrorString(res).original.find("The inputs size exceeds the maximum weight") != std::string::npos);
1004+
}
1005+
1006+
{
1007+
// ################################################################################################################
1008+
// 3) Test selection when some coins surpass the max allowed weight while others not. --> must find a good solution
1009+
// ################################################################################################################
1010+
CAmount target = 25.33L * COIN;
1011+
int max_weight = 10000; // WU
1012+
const auto& res = SelectCoinsSRD(target, dummy_params, m_node, max_weight, [&](CWallet& wallet) {
1013+
CoinsResult available_coins;
1014+
for (int j = 0; j < 60; ++j) { // 60 UTXO --> 19,8 BTC total --> 60 × 272 WU = 16320 WU
1015+
add_coin(available_coins, wallet, CAmount(0.33 * COIN), CFeeRate(0), 144, false, 0, true);
1016+
}
1017+
for (int i = 0; i < 10; i++) { // 10 UTXO --> 20 BTC total --> 10 × 272 WU = 2720 WU
1018+
add_coin(available_coins, wallet, CAmount(2 * COIN), CFeeRate(0), 144, false, 0, true);
1019+
}
1020+
return available_coins;
1021+
});
1022+
BOOST_CHECK(res);
1023+
}
1024+
}
1025+
9321026
static util::Result<SelectionResult> select_coins(const CAmount& target, const CoinSelectionParams& cs_params, const CCoinControl& cc, std::function<CoinsResult(CWallet&)> coin_setup, interfaces::Chain* chain)
9331027
{
9341028
std::unique_ptr<CWallet> wallet = std::make_unique<CWallet>(chain, "", CreateMockWalletDatabase());

0 commit comments

Comments
 (0)