Skip to content

Commit 8e0cf4f

Browse files
committed
Merge bitcoin/bitcoin#27846: [coinselection] Increase SRD target by change_fee
1771daa [fuzz] Show that SRD budgets for non-dust change (Murch) 941b8c6 [bug] Increase SRD target by change_fee (Murch) Pull request description: I discovered via fuzzing of another coin selection approach that at extremely high feerates SRD may find input sets that lead to transactions without change outputs. This is an unintended outcome since SRD is meant to always produce a transaction with a change output—we use other algorithms to specifically search for changeless solutions. The issue occurs when the flat allowance of 50,000 ṩ for change is insufficient to pay for the creation of a change output with a non-dust amount, at and above 1,613 ṩ/vB. Increasing the change budget by `change_fee` makes SRD behave as expected at any feerates. Note: The intermittent failures of `test/functional/interface_usdt_mempool.py` are a known issue: bitcoin/bitcoin#27380 ACKs for top commit: achow101: ACK 1771daa S3RK: ACK 1771daa Tree-SHA512: 3f36a3e317ef0a711d0e409069c05032bff1d45403023f3728bf73dfd55ddd9e0dc2a9969d4d69fe0a426807ebb0bed1f54abfc05581409bfe42c327acf766d4
2 parents 2c2150a + 1771daa commit 8e0cf4f

File tree

5 files changed

+10
-7
lines changed

5 files changed

+10
-7
lines changed

src/wallet/coinselection.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ class MinOutputGroupComparator
191191
}
192192
};
193193

194-
util::Result<SelectionResult> SelectCoinsSRD(const std::vector<OutputGroup>& utxo_pool, CAmount target_value, FastRandomContext& rng,
194+
util::Result<SelectionResult> SelectCoinsSRD(const std::vector<OutputGroup>& utxo_pool, CAmount target_value, CAmount change_fee, FastRandomContext& rng,
195195
int max_weight)
196196
{
197197
SelectionResult result(target_value, SelectionAlgorithm::SRD);
@@ -201,7 +201,7 @@ util::Result<SelectionResult> SelectCoinsSRD(const std::vector<OutputGroup>& utx
201201
// barely meets the target. Just use the lower bound change target instead of the randomly
202202
// generated one, since SRD will result in a random change amount anyway; avoid making the
203203
// target needlessly large.
204-
target_value += CHANGE_LOWER;
204+
target_value += CHANGE_LOWER + change_fee;
205205

206206
std::vector<size_t> indexes;
207207
indexes.resize(utxo_pool.size());

src/wallet/coinselection.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -421,7 +421,7 @@ util::Result<SelectionResult> SelectCoinsBnB(std::vector<OutputGroup>& utxo_pool
421421
* @param[in] max_weight The maximum allowed weight for a selection result to be valid
422422
* @returns If successful, a valid SelectionResult, otherwise, util::Error
423423
*/
424-
util::Result<SelectionResult> SelectCoinsSRD(const std::vector<OutputGroup>& utxo_pool, CAmount target_value, FastRandomContext& rng,
424+
util::Result<SelectionResult> SelectCoinsSRD(const std::vector<OutputGroup>& utxo_pool, CAmount target_value, CAmount change_fee, FastRandomContext& rng,
425425
int max_weight);
426426

427427
// Original coin selection algorithm as a fallback

src/wallet/spend.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -583,7 +583,7 @@ util::Result<SelectionResult> ChooseSelectionResult(const CAmount& nTargetValue,
583583
results.push_back(*knapsack_result);
584584
} else append_error(knapsack_result);
585585

586-
if (auto srd_result{SelectCoinsSRD(groups.positive_group, nTargetValue, coin_selection_params.rng_fast, max_inputs_weight)}) {
586+
if (auto srd_result{SelectCoinsSRD(groups.positive_group, nTargetValue, coin_selection_params.m_change_fee, coin_selection_params.rng_fast, max_inputs_weight)}) {
587587
srd_result->ComputeAndSetWaste(coin_selection_params.min_viable_change, coin_selection_params.m_cost_of_change, coin_selection_params.m_change_fee);
588588
results.push_back(*srd_result);
589589
} else append_error(srd_result);

src/wallet/test/coinselector_tests.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -968,7 +968,7 @@ static util::Result<SelectionResult> SelectCoinsSRD(const CAmount& target,
968968
std::unique_ptr<CWallet> wallet = NewWallet(m_node);
969969
CoinEligibilityFilter filter(0, 0, 0); // accept all coins without ancestors
970970
Groups group = GroupOutputs(*wallet, coin_setup(*wallet), cs_params, {{filter}})[filter].all_groups;
971-
return SelectCoinsSRD(group.positive_group, target, cs_params.rng_fast, max_weight);
971+
return SelectCoinsSRD(group.positive_group, target, cs_params.m_change_fee, cs_params.rng_fast, max_weight);
972972
}
973973

974974
BOOST_AUTO_TEST_CASE(srd_tests)

src/wallet/test/fuzz/coinselection.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,11 @@ FUZZ_TARGET(coinselection)
9090
// Run coinselection algorithms
9191
const auto result_bnb = SelectCoinsBnB(group_pos, target, cost_of_change, MAX_STANDARD_TX_WEIGHT);
9292

93-
auto result_srd = SelectCoinsSRD(group_pos, target, fast_random_context, MAX_STANDARD_TX_WEIGHT);
94-
if (result_srd) result_srd->ComputeAndSetWaste(cost_of_change, cost_of_change, 0);
93+
auto result_srd = SelectCoinsSRD(group_pos, target, coin_params.m_change_fee, fast_random_context, MAX_STANDARD_TX_WEIGHT);
94+
if (result_srd) {
95+
assert(result_srd->GetChange(CHANGE_LOWER, coin_params.m_change_fee) > 0); // Demonstrate that SRD creates change of at least CHANGE_LOWER
96+
result_srd->ComputeAndSetWaste(cost_of_change, cost_of_change, 0);
97+
}
9598

9699
CAmount change_target{GenerateChangeTarget(target, coin_params.m_change_fee, fast_random_context)};
97100
auto result_knapsack = KnapsackSolver(group_all, target, change_target, fast_random_context, MAX_STANDARD_TX_WEIGHT);

0 commit comments

Comments
 (0)