From e0c9abc2b32a44592446c8f6aa1ab72e2dafd096 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 30 Oct 2023 15:51:59 +1030 Subject: [PATCH] wallet: routine to gather UTXOs to meet a certain feerate. We want to use this for boosting txs: either attaching fees to zero-fee HTLCs, or making anchor transactions. Signed-off-by: Rusty Russell --- wallet/wallet.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++ wallet/wallet.h | 19 +++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/wallet/wallet.c b/wallet/wallet.c index 1502b3ab0..2487a98aa 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -450,6 +450,62 @@ struct utxo *wallet_utxo_get(const tal_t *ctx, struct wallet *w, return utxo; } +/* Gather enough utxos to meet feerate, otherwise all we can. */ +struct utxo **wallet_utxo_boost(const tal_t *ctx, + struct wallet *w, + u32 blockheight, + struct amount_sat fee_amount, + u32 feerate_target, + size_t *weight) +{ + struct utxo **all_utxos = wallet_get_unspent_utxos(tmpctx, w); + struct utxo **utxos = tal_arr(ctx, struct utxo *, 0); + u32 feerate; + + /* Select in random order */ + tal_arr_randomize(all_utxos, struct utxo *); + + /* Can't overflow, it's from our tx! */ + if (!amount_feerate(&feerate, fee_amount, *weight)) + abort(); + + for (size_t i = 0; i < tal_count(all_utxos); i++) { + u32 new_feerate; + size_t new_weight; + struct amount_sat new_fee_amount; + /* Convenience var */ + struct utxo *utxo = all_utxos[i]; + + /* Are we already happy? */ + if (feerate >= feerate_target) + break; + + /* Don't add reserved ones */ + if (utxo_is_reserved(utxo, blockheight)) + continue; + + /* UTXOs must be sane amounts */ + if (!amount_sat_add(&new_fee_amount, + fee_amount, utxo->amount)) + abort(); + + new_weight = *weight + utxo_spend_weight(utxo, 0); + if (!amount_feerate(&new_feerate, new_fee_amount, new_weight)) + abort(); + + /* Don't add uneconomic ones! */ + if (new_feerate < feerate) + continue; + + feerate = new_feerate; + *weight = new_weight; + fee_amount = new_fee_amount; + tal_arr_expand(&utxos, tal_steal(utxos, utxo)); + } + + return utxos; +} + static void db_set_utxo(struct db *db, const struct utxo *utxo) { struct db_stmt *stmt; diff --git a/wallet/wallet.h b/wallet/wallet.h index c47e1e656..f88f12796 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -537,6 +537,25 @@ void wallet_unreserve_utxo(struct wallet *w, struct utxo *utxo, struct utxo *wallet_utxo_get(const tal_t *ctx, struct wallet *w, const struct bitcoin_outpoint *outpoint); +/** + * wallet_utxo_boost - get (unreserved) utxos to meet a given feerate. + * @ctx: context to tal return array from + * @w: the wallet + * @blockheight: current height (to determine reserved status) + * @fee_amount: amount already paying in fees + * @feerate_target: feerate we want, in perkw. + * @weight: (in)existing weight before any utxos added, (out)final weight with utxos added. + * + * May not meet the feerate, but will spend all available utxos to try. + * You may also need to create change, as it may exceed. + */ +struct utxo **wallet_utxo_boost(const tal_t *ctx, + struct wallet *w, + u32 blockheight, + struct amount_sat fee_amount, + u32 feerate_target, + size_t *weight); + /** * wallet_can_spend - Do we have the private key matching this scriptpubkey? *