@@ -357,17 +357,44 @@ bool CWallet::AttemptSelection(const CAmount& nTargetValue, const CoinEligibilit
357
357
{
358
358
setCoinsRet.clear ();
359
359
nValueRet = 0 ;
360
+ // Vector of results for use with waste calculation
361
+ // In order: calculated waste, selected inputs, selected input value (sum of input values)
362
+ // TODO: Use a struct representing the selection result
363
+ std::vector<std::tuple<CAmount, std::set<CInputCoin>, CAmount>> results;
360
364
361
365
// Note that unlike KnapsackSolver, we do not include the fee for creating a change output as BnB will not create a change output.
362
366
std::vector<OutputGroup> positive_groups = GroupOutputs (coins, coin_selection_params, eligibility_filter, true /* positive_only */ );
363
- if (SelectCoinsBnB (positive_groups, nTargetValue, coin_selection_params.m_cost_of_change , setCoinsRet, nValueRet)) {
364
- return true ;
367
+ std::set<CInputCoin> bnb_coins;
368
+ CAmount bnb_value;
369
+ if (SelectCoinsBnB (positive_groups, nTargetValue, coin_selection_params.m_cost_of_change , bnb_coins, bnb_value)) {
370
+ const auto waste = GetSelectionWaste (bnb_coins, /* cost of change */ CAmount (0 ), nTargetValue, !coin_selection_params.m_subtract_fee_outputs );
371
+ results.emplace_back (std::make_tuple (waste, std::move (bnb_coins), bnb_value));
365
372
}
373
+
366
374
// The knapsack solver has some legacy behavior where it will spend dust outputs. We retain this behavior, so don't filter for positive only here.
367
375
std::vector<OutputGroup> all_groups = GroupOutputs (coins, coin_selection_params, eligibility_filter, false /* positive_only */ );
368
376
// While nTargetValue includes the transaction fees for non-input things, it does not include the fee for creating a change output.
369
377
// So we need to include that for KnapsackSolver as well, as we are expecting to create a change output.
370
- return KnapsackSolver (nTargetValue + coin_selection_params.m_change_fee , all_groups, setCoinsRet, nValueRet);
378
+ std::set<CInputCoin> knapsack_coins;
379
+ CAmount knapsack_value;
380
+ if (KnapsackSolver (nTargetValue + coin_selection_params.m_change_fee , all_groups, knapsack_coins, knapsack_value)) {
381
+ const auto waste = GetSelectionWaste (knapsack_coins, coin_selection_params.m_cost_of_change , nTargetValue + coin_selection_params.m_change_fee , !coin_selection_params.m_subtract_fee_outputs );
382
+ results.emplace_back (std::make_tuple (waste, std::move (knapsack_coins), knapsack_value));
383
+ }
384
+
385
+ if (results.size () == 0 ) {
386
+ // No solution found
387
+ return false ;
388
+ }
389
+
390
+ // Choose the result with the least waste
391
+ // If the waste is the same, choose the one which spends more inputs.
392
+ const auto & best_result = std::min_element (results.begin (), results.end (), [](const auto & a, const auto & b) {
393
+ return std::get<0 >(a) < std::get<0 >(b) || (std::get<0 >(a) == std::get<0 >(b) && std::get<1 >(a).size () > std::get<1 >(b).size ());
394
+ });
395
+ setCoinsRet = std::get<1 >(*best_result);
396
+ nValueRet = std::get<2 >(*best_result);
397
+ return true ;
371
398
}
372
399
373
400
bool CWallet::SelectCoins (const std::vector<COutput>& vAvailableCoins, const CAmount& nTargetValue, std::set<CInputCoin>& setCoinsRet, CAmount& nValueRet, const CCoinControl& coin_control, CoinSelectionParams& coin_selection_params) const
0 commit comments