mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-04-11 16:17:54 +02:00
test: Add SRD maximum weight tests
Also: - Add weight check to Success cases. Per the introduction of TRUC transactions, it is more likely that we will attempt to build transactions of limited weight (e.g., TRUC child transactions may not exceed 4000 WU). When SRD exceeds the input weight limit, it evicts the OutputGroup with the lowest effective value before selecting additional UTXOs: we test that SRD will find a solution that depends on the eviction working correctly, and that it fails as expected when no solution is possible.
This commit is contained in:
@@ -126,6 +126,7 @@ static void TestBnBSuccess(std::string test_title, std::vector<OutputGroup>& 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<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)
|
||||
@@ -172,7 +173,7 @@ BOOST_AUTO_TEST_CASE(bnb_test)
|
||||
// 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`)
|
||||
@@ -244,6 +245,7 @@ static void TestSRDSuccess(std::string test_title, std::vector<OutputGroup>& 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<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)
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1185,78 +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,
|
||||
};
|
||||
|
||||
{
|
||||
// ###########################
|
||||
// 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