@@ -357,17 +357,44 @@ bool CWallet::AttemptSelection(const CAmount& nTargetValue, const CoinEligibilit
357357{
358358 setCoinsRet.clear ();
359359 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;
360364
361365 // Note that unlike KnapsackSolver, we do not include the fee for creating a change output as BnB will not create a change output.
362366 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));
365372 }
373+
366374 // 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.
367375 std::vector<OutputGroup> all_groups = GroupOutputs (coins, coin_selection_params, eligibility_filter, false /* positive_only */ );
368376 // While nTargetValue includes the transaction fees for non-input things, it does not include the fee for creating a change output.
369377 // 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 ;
371398}
372399
373400bool 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