Repository navigation
backport: bitcoin#24845, #27318, #34888, #36284 #8001
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
34cdcc2
34e8991
69c6af3
ad9bef6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -412,7 +412,8 @@ static bool isGroupISLocked(const OutputGroup& group, interfaces::Chain& chain) | |
| FilteredOutputGroups GroupOutputs(const CWallet& wallet, | ||
| const CoinsResult& coins, | ||
| const CoinSelectionParams& coin_sel_params, | ||
| const std::vector<SelectionFilter>& filters) | ||
| const std::vector<SelectionFilter>& filters, | ||
| std::vector<OutputGroup>& ret_discarded_groups) | ||
| { | ||
| FilteredOutputGroups filtered_groups; | ||
|
|
||
|
|
@@ -430,11 +431,14 @@ FilteredOutputGroups GroupOutputs(const CWallet& wallet, | |
|
|
||
| bool isISLocked = isGroupISLocked(group, wallet.chain()); | ||
| // Each filter maps to a different set of groups | ||
| bool accepted = false; | ||
| for (const auto& sel_filter : filters) { | ||
| const auto& filter = sel_filter.filter; | ||
| if (!group.EligibleForSpending(filter, isISLocked)) continue; | ||
| filtered_groups[filter].Push(group, type, /*insert_positive=*/true, /*insert_mixed=*/true); | ||
| accepted = true; | ||
| } | ||
| if (!accepted) ret_discarded_groups.emplace_back(group); | ||
| } | ||
| } | ||
| return filtered_groups; | ||
|
|
@@ -499,6 +503,7 @@ FilteredOutputGroups GroupOutputs(const CWallet& wallet, | |
|
|
||
| bool isISLocked = isGroupISLocked(group, wallet.chain()); | ||
| // Each filter maps to a different set of groups | ||
| bool accepted = false; | ||
| for (const auto& sel_filter : filters) { | ||
| const auto& filter = sel_filter.filter; | ||
| if (!group.EligibleForSpending(filter, isISLocked)) continue; | ||
|
|
@@ -511,7 +516,10 @@ FilteredOutputGroups GroupOutputs(const CWallet& wallet, | |
| OutputType type = script.second; | ||
| // Either insert the group into the positive-only groups or the mixed ones. | ||
| filtered_groups[filter].Push(group, type, positive_only, /*insert_mixed=*/!positive_only); | ||
| accepted = true; | ||
| } | ||
| // The positive-only groups are a subset of the mixed ones, don't record them twice | ||
| if (!accepted && !positive_only) ret_discarded_groups.emplace_back(group); | ||
| } | ||
| } | ||
| }; | ||
|
|
@@ -522,6 +530,15 @@ FilteredOutputGroups GroupOutputs(const CWallet& wallet, | |
| return filtered_groups; | ||
| } | ||
|
|
||
| FilteredOutputGroups GroupOutputs(const CWallet& wallet, | ||
| const CoinsResult& coins, | ||
| const CoinSelectionParams& params, | ||
| const std::vector<SelectionFilter>& filters) | ||
| { | ||
| std::vector<OutputGroup> unused; | ||
| return GroupOutputs(wallet, coins, params, filters, unused); | ||
| } | ||
|
|
||
| // Returns true if the result contains an error and the message is not empty | ||
| static bool HasErrorMsg(const util::Result<SelectionResult>& res) { return !util::ErrorString(res).empty(); } | ||
|
|
||
|
|
@@ -699,7 +716,24 @@ util::Result<SelectionResult> AutomaticCoinSelection(const CWallet& wallet, Coin | |
| } | ||
|
|
||
| // Group outputs and map them by coin eligibility filter | ||
| FilteredOutputGroups filtered_groups = GroupOutputs(wallet, available_coins, coin_selection_params, ordered_filters); | ||
| std::vector<OutputGroup> discarded_groups; | ||
| FilteredOutputGroups filtered_groups = GroupOutputs(wallet, available_coins, coin_selection_params, ordered_filters, discarded_groups); | ||
|
|
||
| // Check if we still have enough balance after applying filters (some coins might be discarded) | ||
| CAmount total_discarded = 0; | ||
| CAmount total_unconf_long_chain = 0; | ||
| for (const auto& group : discarded_groups) { | ||
| total_discarded += group.GetSelectionAmount(); | ||
| if (group.m_ancestors >= max_ancestors || group.m_descendants >= max_descendants) total_unconf_long_chain += group.GetSelectionAmount(); | ||
| } | ||
|
|
||
| if (CAmount total_amount = available_coins.GetTotalAmount() - total_discarded; total_amount < value_to_select) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Compare amounts in the same units.
|
||
| // Special case, too-long-mempool cluster. | ||
| if (total_amount + total_unconf_long_chain > value_to_select) { | ||
| return util::Result<SelectionResult>({_("Unconfirmed UTXOs are available, but spending them creates a chain of transactions that will be rejected by the mempool")}); | ||
| } | ||
| return util::Result<SelectionResult>(util::Error()); // General "Insufficient Funds" | ||
| } | ||
|
|
||
| // Walk-through the filters until the solution gets found. | ||
| // If no solution is found, return the first detailed error (if any). | ||
|
|
@@ -718,8 +752,13 @@ util::Result<SelectionResult> AutomaticCoinSelection(const CWallet& wallet, Coin | |
| if (HasErrorMsg(res)) res_detailed_errors.emplace_back(res); | ||
| } | ||
| } | ||
| // Coin Selection failed. | ||
| return res_detailed_errors.empty() ? util::Result<SelectionResult>(util::Error()) : res_detailed_errors.front(); | ||
|
|
||
| // Return right away if we have a detailed error | ||
| if (!res_detailed_errors.empty()) return res_detailed_errors.front(); | ||
|
|
||
|
|
||
| // General "Insufficient Funds" | ||
| return util::Result<SelectionResult>(util::Error()); | ||
| }(); | ||
|
|
||
| return res; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do not discard outputs accepted by the positive-only pass.
With
avoidpartialspends, a confirmed positive-effective output and a negative-effective, long-chain output can share a script. The mixed group is rejected, but the positive-only group can be accepted. This line records the mixed group as discarded. The check at Line 730 can then subtract the confirmed output and return “Insufficient funds” before selection tries it. Reconcile accepted outputs across both passes before using discarded groups to reject the payment. (raw.githubusercontent.com)