wallet: Exclude uneconomical UTXOs from fundchannel

If a node has an onchain balance with at least one uneconomical UTXO, the fundchannel RPC call will lock up the node and will eventually crash it with OOM issues if the economical UTXO(s) do not add up to the fundchannel amount. This is because the while loop never exits because it keeps pulling in the same uneconomical UTXOs forever.

Changelog-Fixed: wallet: fundchannel no longer loops forever if the wallet contains insufficient funds, but an uneconomical UTXO.
This commit is contained in:
Tony Giorgio 2023-09-13 09:55:40 +09:30 committed by Rusty Russell
parent 47f4e11c87
commit a5367773d5
2 changed files with 21 additions and 3 deletions

View File

@ -2420,7 +2420,6 @@ def test_anchor_min_emergency(bitcoind, node_factory):
wait_for(lambda: l1.rpc.listfunds()['outputs'] == [])
@pytest.mark.xfail(strict=True)
def test_fundchannel_utxo_too_small(bitcoind, node_factory):
l1, l2 = node_factory.get_nodes(2)

View File

@ -537,6 +537,9 @@ static struct command_result *json_fundpsbt(struct command *cmd,
/* We keep adding until we meet their output requirements. */
utxos = tal_arr(cmd, struct utxo *, 0);
/* We seperate out UTXOs to exclude if they are uneconomical */
struct utxo **uneconomical_utxos = tal_arr(cmd, struct utxo *, 0);
input = AMOUNT_SAT(0);
while (!inputs_sufficient(input, *amount, *feerate_per_kw, *weight,
&diff)) {
@ -544,21 +547,35 @@ static struct command_result *json_fundpsbt(struct command *cmd,
struct amount_sat fee;
u32 utxo_weight;
/* Merge the two lists for exclusion */
struct utxo **all_excluded = tal_arr(cmd, struct utxo *, 0);
for(size_t i = 0; i < tal_count(utxos); i++) {
tal_arr_expand(&all_excluded, utxos[i]);
}
for(size_t i = 0; i < tal_count(uneconomical_utxos); i++) {
tal_arr_expand(&all_excluded, uneconomical_utxos[i]);
}
utxo = wallet_find_utxo(utxos, cmd->ld->wallet,
current_height,
&diff,
*feerate_per_kw,
maxheight,
*nonwrapped,
cast_const2(const struct utxo **, utxos));
cast_const2(const struct utxo **, all_excluded));
tal_free(all_excluded);
if (utxo) {
utxo_weight = utxo_spend_weight(utxo,
*min_witness_weight);
fee = amount_tx_fee(*feerate_per_kw, utxo_weight);
/* Uneconomic to add this utxo, skip it */
if (!all && amount_sat_greater_eq(fee, utxo->amount))
if (!all && amount_sat_greater_eq(fee, utxo->amount)){
tal_arr_expand(&uneconomical_utxos, utxo);
continue;
}
tal_arr_expand(&utxos, utxo);
@ -596,6 +613,8 @@ static struct command_result *json_fundpsbt(struct command *cmd,
&diff));
}
tal_free(uneconomical_utxos);
if (all) {
/* We need to afford one non-dust output, at least. */
if (!inputs_sufficient(input, AMOUNT_SAT(0),