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:
furszy 2023-03-07 18:47:28 -03:00
parent 86bacd75e7
commit acf0119d24
No known key found for this signature in database
GPG Key ID: 5DD23CCC686AA623

View File

@ -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;