From eaf2dd6b0586919f1302fb2ee9b954e789f00eee Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Mon, 2 Oct 2023 11:42:19 +1030 Subject: [PATCH 1/2] pytest: test that we can CPFP a mutual close. You can't (yet): it doesn't even show in listfunds. Signed-off-by: Rusty Russell --- tests/test_closing.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tests/test_closing.py b/tests/test_closing.py index c96ae9f2b0f6..de1abac3dcf1 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -3878,3 +3878,34 @@ def test_closing_minfee(node_factory, bitcoind): txid = l1.rpc.close(l2.info['id'])['txid'] bitcoind.generate_block(1, wait_for_mempool=txid) + + +@pytest.mark.xfail(strict=True) +def test_closing_cpfp(node_factory, bitcoind): + l1, l2 = node_factory.line_graph(2) + + # We want to ignore l1's change output + change = only_one(l1.rpc.listfunds()['outputs']) + + # Make sure both sides have some output + l1.rpc.pay(l2.rpc.invoice(10000000, 'test', 'test')['bolt11']) + + # Mutual close + close_txid = l1.rpc.close(l2.info['id'])['txid'] + + l1out = only_one([o for o in l1.rpc.listfunds()['outputs'] if o != change]) + assert l1out['txid'] == close_txid + l1.rpc.withdraw(l1.rpc.newaddr()['bech32'], 'all', '20000perkb', minconf=0, utxos=["{}:{}".format(l1out['txid'], l1out['output'])]) + + # l2 should be able to do this too! + l2out = only_one(l2.rpc.listfunds()['outputs']) + assert l2out['txid'] == close_txid + l2.rpc.withdraw(l2.rpc.newaddr()['bech32'], 'all', '20000perkb', minconf=0, utxos=["{}:{}".format(l2out['txid'], l2out['output'])]) + + # There should be *three* transactions in mempool now! + bitcoind.generate_block(1, wait_for_mempool=3) + + # They should now see a single additional output each + sync_blockheight(bitcoind, [l1, l2]) + assert len(l1.rpc.listfunds()['outputs']) == 2 + assert len(l2.rpc.listfunds()['outputs']) == 1 From 53e1a038f6582eb4bde1edd9d273c6232956bd4c Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 19 Oct 2023 16:29:35 +1030 Subject: [PATCH 2/2] lightningd: add direct close outputs to listfunds (mutual close). We had a complaint that you can't CPFP a mutual close, which you should be able to do. Fixes: #6692 Changelog-Fixed: wallet: close change outputs show up immediately in `listfunds` so you can CPFP. Signed-off-by: Rusty Russell --- lightningd/peer_control.c | 1 + lightningd/test/run-invoice-select-inchan.c | 6 ++++++ tests/test_closing.py | 3 +-- wallet/wallet.c | 8 +++++--- wallet/wallet.h | 1 + 5 files changed, 14 insertions(+), 5 deletions(-) diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index f473e6843f1c..7e8c7c12ff1f 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -300,6 +300,7 @@ static struct bitcoin_tx *sign_and_send_last(const tal_t *ctx, tx = sign_last_tx(ctx, channel, last_tx, last_sig); bitcoin_txid(tx, &txid); wallet_transaction_add(ld->wallet, tx->wtx, 0, 0); + wallet_extract_owned_outputs(ld->wallet, tx->wtx, false, NULL, NULL); /* Remember anchor information for commit_tx_boost */ adet = create_anchor_details(NULL, channel, tx); diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 989270b84852..0981757b3d27 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -964,6 +964,12 @@ void wallet_channeltxs_add(struct wallet *w UNNEEDED, struct channel *chan UNNEE /* Generated stub for wallet_delete_peer_if_unused */ void wallet_delete_peer_if_unused(struct wallet *w UNNEEDED, u64 peer_dbid UNNEEDED) { fprintf(stderr, "wallet_delete_peer_if_unused called!\n"); abort(); } +/* Generated stub for wallet_extract_owned_outputs */ +int wallet_extract_owned_outputs(struct wallet *w UNNEEDED, const struct wally_tx *tx UNNEEDED, + bool is_coinbase UNNEEDED, + const u32 *blockheight UNNEEDED, + struct amount_sat *total UNNEEDED) +{ fprintf(stderr, "wallet_extract_owned_outputs called!\n"); abort(); } /* Generated stub for wallet_htlcs_load_in_for_channel */ bool wallet_htlcs_load_in_for_channel(struct wallet *wallet UNNEEDED, struct channel *chan UNNEEDED, diff --git a/tests/test_closing.py b/tests/test_closing.py index de1abac3dcf1..bda0b01add8a 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -3853,7 +3853,7 @@ def test_closing_tx_valid(node_factory, bitcoind): assert bitcoind.rpc.getrawtransaction(close['txid']) == close['tx'] bitcoind.generate_block(1) # Change output and the closed channel output. - wait_for(lambda: len(l1.rpc.listfunds()['outputs']) == 2) + wait_for(lambda: [o['status'] for o in l1.rpc.listfunds()['outputs']] == ['confirmed'] * 2) # Now, unilateral close. l1.rpc.connect(l2.info['id'], 'localhost', l2.port) @@ -3880,7 +3880,6 @@ def test_closing_minfee(node_factory, bitcoind): bitcoind.generate_block(1, wait_for_mempool=txid) -@pytest.mark.xfail(strict=True) def test_closing_cpfp(node_factory, bitcoind): l1, l2 = node_factory.line_graph(2) diff --git a/wallet/wallet.c b/wallet/wallet.c index 301efe5f4703..f2ee951d9cb4 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -140,7 +140,8 @@ struct wallet *wallet_new(struct lightningd *ld, struct timers *timers) * * This can fail if we've already seen UTXO. */ -static bool wallet_add_utxo(struct wallet *w, struct utxo *utxo, +static bool wallet_add_utxo(struct wallet *w, + const struct utxo *utxo, enum wallet_output_type type) { struct db_stmt *stmt; @@ -2573,7 +2574,8 @@ int wallet_extract_owned_outputs(struct wallet *w, const struct wally_tx *wtx, { int num_utxos = 0; - *total = AMOUNT_SAT(0); + if (total) + *total = AMOUNT_SAT(0); for (size_t output = 0; output < wtx->num_outputs; output++) { struct utxo *utxo; u32 index; @@ -2646,7 +2648,7 @@ int wallet_extract_owned_outputs(struct wallet *w, const struct wally_tx *wtx, outpointfilter_add(w->owned_outpoints, &utxo->outpoint); - if (!amount_sat_add(total, *total, utxo->amount)) + if (total && !amount_sat_add(total, *total, utxo->amount)) fatal("Cannot add utxo output %zu/%zu %s + %s", output, wtx->num_outputs, type_to_string(tmpctx, struct amount_sat, total), diff --git a/wallet/wallet.h b/wallet/wallet.h index 189673694f97..1676c70fff54 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -569,6 +569,7 @@ bool wallet_has_funds(struct wallet *wallet, const struct utxo **excludes, u32 current_blockheight, struct amount_sat sats); + /** * wallet_add_onchaind_utxo - Add a UTXO with spending info from onchaind. *