diff --git a/src/wallet/test/coinselection_tests.cpp b/src/wallet/test/coinselection_tests.cpp index 34b71887edb..6e60af1c0e3 100644 --- a/src/wallet/test/coinselection_tests.cpp +++ b/src/wallet/test/coinselection_tests.cpp @@ -126,6 +126,7 @@ static void TestBnBSuccess(std::string test_title, std::vector& utx 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& 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) @@ -172,7 +173,7 @@ BOOST_AUTO_TEST_CASE(bnb_test) // Test skipping of equivalent input sets std::vector 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`) @@ -244,6 +245,7 @@ static void TestSRDSuccess(std::string test_title, std::vector& utx 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& 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) @@ -276,6 +278,11 @@ BOOST_AUTO_TEST_CASE(srd_test) 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); } } diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp index 740079d2c66..ee6a639a43d 100644 --- a/src/wallet/test/coinselector_tests.cpp +++ b/src/wallet/test/coinselector_tests.cpp @@ -1185,78 +1185,6 @@ BOOST_AUTO_TEST_CASE(coin_grinder_tests) } } -static util::Result SelectCoinsSRD(const CAmount& target, - const CoinSelectionParams& cs_params, - const node::NodeContext& m_node, - int max_selection_weight, - std::function coin_setup) -{ - std::unique_ptr 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, - }; - - { - // ########################### - // 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 select_coins(const CAmount& target, const CoinSelectionParams& cs_params, const CCoinControl& cc, std::function coin_setup, const node::NodeContext& m_node) { std::unique_ptr wallet = NewWallet(m_node);