mirror of
https://github.com/bitcoin/bitcoin.git
synced 2024-11-20 02:25:40 +01:00
wallet: return error msg for too-long-mempool-chain failure
We currently return "Insufficient funds" which doesn't really describe what went wrong; the tx creation failed because of a long-mempool-chain, not because of a lack of funds. Also, return early from Coin Selection if the sum of the discarded coins decreases the available balance below the target amount.
This commit is contained in:
parent
86bacd75e7
commit
acf0119d24
@ -407,7 +407,8 @@ std::map<CTxDestination, std::vector<COutput>> ListCoins(const CWallet& wallet)
|
||||
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;
|
||||
|
||||
@ -427,11 +428,14 @@ FilteredOutputGroups GroupOutputs(const CWallet& wallet,
|
||||
group.Insert(std::make_shared<COutput>(output), ancestors, descendants);
|
||||
|
||||
// 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)) 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;
|
||||
@ -497,6 +501,7 @@ FilteredOutputGroups GroupOutputs(const CWallet& wallet,
|
||||
const OutputGroup& group = *group_it;
|
||||
|
||||
// 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)) continue;
|
||||
@ -509,7 +514,9 @@ 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;
|
||||
}
|
||||
if (!accepted) ret_discarded_groups.emplace_back(group);
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -520,6 +527,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(); }
|
||||
|
||||
@ -692,7 +708,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 < value_to_select) {
|
||||
// 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).
|
||||
@ -711,8 +744,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;
|
||||
|
Loading…
Reference in New Issue
Block a user