@@ -544,13 +544,13 @@ FilteredOutputGroups GroupOutputs(const CWallet& wallet,
544544// Returns true if the result contains an error and the message is not empty
545545static bool HasErrorMsg (const util::Result<SelectionResult>& res) { return !util::ErrorString (res).empty (); }
546546
547- util::Result<SelectionResult> AttemptSelection (const CAmount& nTargetValue, OutputGroupTypeMap& groups,
547+ util::Result<SelectionResult> AttemptSelection (interfaces::Chain& chain, const CAmount& nTargetValue, OutputGroupTypeMap& groups,
548548 const CoinSelectionParams& coin_selection_params, bool allow_mixed_output_types)
549549{
550550 // Run coin selection on each OutputType and compute the Waste Metric
551551 std::vector<SelectionResult> results;
552552 for (auto & [type, group] : groups.groups_by_type ) {
553- auto result{ChooseSelectionResult (nTargetValue, group, coin_selection_params)};
553+ auto result{ChooseSelectionResult (chain, nTargetValue, group, coin_selection_params)};
554554 // If any specific error message appears here, then something particularly wrong happened.
555555 if (HasErrorMsg (result)) return result; // So let's return the specific error.
556556 // Append the favorable result.
@@ -564,14 +564,14 @@ util::Result<SelectionResult> AttemptSelection(const CAmount& nTargetValue, Outp
564564 // over all available coins, which would allow mixing.
565565 // If TypesCount() <= 1, there is nothing to mix.
566566 if (allow_mixed_output_types && groups.TypesCount () > 1 ) {
567- return ChooseSelectionResult (nTargetValue, groups.all_groups , coin_selection_params);
567+ return ChooseSelectionResult (chain, nTargetValue, groups.all_groups , coin_selection_params);
568568 }
569569 // Either mixing is not allowed and we couldn't find a solution from any single OutputType, or mixing was allowed and we still couldn't
570570 // find a solution using all available coins
571571 return util::Error ();
572572};
573573
574- util::Result<SelectionResult> ChooseSelectionResult (const CAmount& nTargetValue, Groups& groups, const CoinSelectionParams& coin_selection_params)
574+ util::Result<SelectionResult> ChooseSelectionResult (interfaces::Chain& chain, const CAmount& nTargetValue, Groups& groups, const CoinSelectionParams& coin_selection_params)
575575{
576576 // Vector of results. We will choose the best one based on waste.
577577 std::vector<SelectionResult> results;
@@ -596,12 +596,10 @@ util::Result<SelectionResult> ChooseSelectionResult(const CAmount& nTargetValue,
596596
597597 // 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.
598598 if (auto knapsack_result{KnapsackSolver (groups.mixed_group , nTargetValue, coin_selection_params.m_min_change_target , coin_selection_params.rng_fast , max_inputs_weight)}) {
599- knapsack_result->ComputeAndSetWaste (coin_selection_params.min_viable_change , coin_selection_params.m_cost_of_change , coin_selection_params.m_change_fee );
600599 results.push_back (*knapsack_result);
601600 } else append_error (knapsack_result);
602601
603602 if (auto srd_result{SelectCoinsSRD (groups.positive_group , nTargetValue, coin_selection_params.m_change_fee , coin_selection_params.rng_fast , max_inputs_weight)}) {
604- srd_result->ComputeAndSetWaste (coin_selection_params.min_viable_change , coin_selection_params.m_cost_of_change , coin_selection_params.m_change_fee );
605603 results.push_back (*srd_result);
606604 } else append_error (srd_result);
607605
@@ -611,6 +609,27 @@ util::Result<SelectionResult> ChooseSelectionResult(const CAmount& nTargetValue,
611609 return errors.empty () ? util::Error () : errors.front ();
612610 }
613611
612+ // If the chosen input set has unconfirmed inputs, check for synergies from overlapping ancestry
613+ for (auto & result : results) {
614+ std::vector<COutPoint> outpoints;
615+ std::set<std::shared_ptr<COutput>> coins = result.GetInputSet ();
616+ CAmount summed_bump_fees = 0 ;
617+ for (auto & coin : coins) {
618+ if (coin->depth > 0 ) continue ; // Bump fees only exist for unconfirmed inputs
619+ outpoints.push_back (coin->outpoint );
620+ summed_bump_fees += coin->ancestor_bump_fees ;
621+ }
622+ std::optional<CAmount> combined_bump_fee = chain.CalculateCombinedBumpFee (outpoints, coin_selection_params.m_effective_feerate );
623+ if (!combined_bump_fee.has_value ()) {
624+ return util::Error{_ (" Failed to calculate bump fees, because unconfirmed UTXOs depend on enormous cluster of unconfirmed transactions." )};
625+ }
626+ CAmount bump_fee_overestimate = summed_bump_fees - combined_bump_fee.value ();
627+ if (bump_fee_overestimate) {
628+ result.SetBumpFeeDiscount (bump_fee_overestimate);
629+ }
630+ result.ComputeAndSetWaste (coin_selection_params.min_viable_change , coin_selection_params.m_cost_of_change , coin_selection_params.m_change_fee );
631+ }
632+
614633 // Choose the result with the least waste
615634 // If the waste is the same, choose the one which spends more inputs.
616635 return *std::min_element (results.begin (), results.end ());
@@ -740,7 +759,7 @@ util::Result<SelectionResult> AutomaticCoinSelection(const CWallet& wallet, Coin
740759 for (const auto & select_filter : ordered_filters) {
741760 auto it = filtered_groups.find (select_filter.filter );
742761 if (it == filtered_groups.end ()) continue ;
743- if (auto res{AttemptSelection (value_to_select, it->second ,
762+ if (auto res{AttemptSelection (wallet. chain (), value_to_select, it->second ,
744763 coin_selection_params, select_filter.allow_mixed_output_types )}) {
745764 return res; // result found
746765 } else {
0 commit comments