From 07413c20b9f19362f437fc22fcda655777426c09 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 6 Apr 2023 09:03:24 +0930 Subject: [PATCH] onchaind: use lightningd to send "delayed_output_to_us" from HTLC txs. Signed-off-by: Rusty Russell --- onchaind/onchaind.c | 30 ++++++---------- onchaind/test/run-grind_feerate-bug.c | 3 ++ onchaind/test/run-grind_feerate.c | 3 ++ tests/test_closing.py | 50 ++++++++++++++------------- tests/test_misc.py | 19 +++++----- 5 files changed, 54 insertions(+), 51 deletions(-) diff --git a/onchaind/onchaind.c b/onchaind/onchaind.c index 9c838a946..c287c0139 100644 --- a/onchaind/onchaind.c +++ b/onchaind/onchaind.c @@ -1240,7 +1240,7 @@ static void propose_resolution_at_block(struct tracked_output *out, } /* Modern style: we don't create tx outselves, but tell lightningd. */ -static void UNNEEDED propose_resolution_to_master(struct tracked_output *out, +static void propose_resolution_to_master(struct tracked_output *out, const u8 *send_message TAKES, unsigned int block_required, enum tx_type tx_type) @@ -1704,12 +1704,11 @@ static void resolve_htlc_tx(struct tracked_output ***outs, u32 tx_blockheight) { struct tracked_output *out; - struct bitcoin_tx *tx; struct amount_sat amt; struct amount_asset asset; struct bitcoin_outpoint outpoint; - enum tx_type tx_type = OUR_DELAYED_RETURN_TO_WALLET; - u8 *wscript = bitcoin_wscript_htlc_tx(htlc_tx, to_self_delay[LOCAL], + u8 *msg; + u8 *wscript = bitcoin_wscript_htlc_tx(tmpctx, to_self_delay[LOCAL], &keyset->self_revocation_key, &keyset->self_delayed_payment_key); @@ -1736,21 +1735,14 @@ static void resolve_htlc_tx(struct tracked_output ***outs, DELAYED_OUTPUT_TO_US, NULL, NULL, NULL); - /* BOLT #3: - * - * ## HTLC-Timeout and HTLC-Success Transactions - * - * These HTLC transactions are almost identical, except the - * HTLC-timeout transaction is timelocked. - * - * ... to collect the output, the local node uses an input with - * nSequence `to_self_delay` and a witness stack ` - * 0` - */ - tx = tx_to_us(*outs, delayed_payment_to_us, out, to_self_delay[LOCAL], - 0, NULL, 0, wscript, &tx_type, htlc_feerate); - - propose_resolution(out, tx, to_self_delay[LOCAL], tx_type); + msg = towire_onchaind_spend_to_us(NULL, + &outpoint, amt, + rel_blockheight(out, to_self_delay[LOCAL]), + commit_num, + wscript); + propose_resolution_to_master(out, take(msg), + rel_blockheight(out, to_self_delay[LOCAL]), + OUR_DELAYED_RETURN_TO_WALLET); } /* BOLT #5: diff --git a/onchaind/test/run-grind_feerate-bug.c b/onchaind/test/run-grind_feerate-bug.c index aabdac956..460583c79 100644 --- a/onchaind/test/run-grind_feerate-bug.c +++ b/onchaind/test/run-grind_feerate-bug.c @@ -285,6 +285,9 @@ u8 *towire_onchaind_missing_htlc_output(const tal_t *ctx UNNEEDED, const struct /* Generated stub for towire_onchaind_notify_coin_mvt */ u8 *towire_onchaind_notify_coin_mvt(const tal_t *ctx UNNEEDED, const struct chain_coin_mvt *mvt UNNEEDED) { fprintf(stderr, "towire_onchaind_notify_coin_mvt called!\n"); abort(); } +/* Generated stub for towire_onchaind_spend_to_us */ +u8 *towire_onchaind_spend_to_us(const tal_t *ctx UNNEEDED, const struct bitcoin_outpoint *outpoint UNNEEDED, struct amount_sat outpoint_amount UNNEEDED, u32 minblock UNNEEDED, u64 commit_num UNNEEDED, const u8 *wscript UNNEEDED) +{ fprintf(stderr, "towire_onchaind_spend_to_us called!\n"); abort(); } /* Generated stub for towire_onchaind_unwatch_tx */ u8 *towire_onchaind_unwatch_tx(const tal_t *ctx UNNEEDED, const struct bitcoin_txid *txid UNNEEDED) { fprintf(stderr, "towire_onchaind_unwatch_tx called!\n"); abort(); } diff --git a/onchaind/test/run-grind_feerate.c b/onchaind/test/run-grind_feerate.c index 1a2955779..a1aa26fee 100644 --- a/onchaind/test/run-grind_feerate.c +++ b/onchaind/test/run-grind_feerate.c @@ -317,6 +317,9 @@ u8 *towire_onchaind_missing_htlc_output(const tal_t *ctx UNNEEDED, const struct /* Generated stub for towire_onchaind_notify_coin_mvt */ u8 *towire_onchaind_notify_coin_mvt(const tal_t *ctx UNNEEDED, const struct chain_coin_mvt *mvt UNNEEDED) { fprintf(stderr, "towire_onchaind_notify_coin_mvt called!\n"); abort(); } +/* Generated stub for towire_onchaind_spend_to_us */ +u8 *towire_onchaind_spend_to_us(const tal_t *ctx UNNEEDED, const struct bitcoin_outpoint *outpoint UNNEEDED, struct amount_sat outpoint_amount UNNEEDED, u32 minblock UNNEEDED, u64 commit_num UNNEEDED, const u8 *wscript UNNEEDED) +{ fprintf(stderr, "towire_onchaind_spend_to_us called!\n"); abort(); } /* Generated stub for towire_onchaind_unwatch_tx */ u8 *towire_onchaind_unwatch_tx(const tal_t *ctx UNNEEDED, const struct bitcoin_txid *txid UNNEEDED) { fprintf(stderr, "towire_onchaind_unwatch_tx called!\n"); abort(); } diff --git a/tests/test_closing.py b/tests/test_closing.py index 02876d608..967ac7013 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -1304,7 +1304,9 @@ def test_penalty_htlc_tx_fulfill(node_factory, bitcoind, chainparams): 'OUR_UNILATERAL/THEIR_HTLC') bitcoind.generate_block(1) - l2.daemon.wait_for_log('Propose handling OUR_HTLC_SUCCESS_TX/DELAYED_OUTPUT_TO_US by OUR_DELAYED_RETURN_TO_WALLET .* after 5 blocks') + ((_, txid, blocks),) = l2.wait_for_onchaind_tx('OUR_DELAYED_RETURN_TO_WALLET', + 'OUR_HTLC_SUCCESS_TX/DELAYED_OUTPUT_TO_US') + assert blocks == 4 # l3 comes back up, sees cheat, penalizes l2 (revokes the htlc they've offered; # notes that they've successfully claimed to_local and the fulfilled htlc) @@ -1497,20 +1499,17 @@ def test_penalty_htlc_tx_timeout(node_factory, bitcoind, chainparams): 'OUR_UNILATERAL/THEIR_HTLC') bitcoind.generate_block(1, wait_for_mempool=1) - l2.daemon.wait_for_log('Propose handling OUR_HTLC_SUCCESS_TX/DELAYED_OUTPUT_TO_US by OUR_DELAYED_RETURN_TO_WALLET .* after 5 blocks') - - # after 5 blocks, l2 reclaims both their DELAYED_OUTPUT_TO_US and their delayed output - bitcoind.generate_block(5, wait_for_mempool=0) - sync_blockheight(bitcoind, [l2]) - l2.daemon.wait_for_logs(['Broadcasting OUR_DELAYED_RETURN_TO_WALLET .* to resolve OUR_HTLC_SUCCESS_TX/DELAYED_OUTPUT_TO_US', - 'Broadcasting OUR_DELAYED_RETURN_TO_WALLET .* to resolve OUR_UNILATERAL/DELAYED_OUTPUT_TO_US']) + ((_, txid, blocks),) = l2.wait_for_onchaind_tx('OUR_DELAYED_RETURN_TO_WALLET', + 'OUR_HTLC_SUCCESS_TX/DELAYED_OUTPUT_TO_US') + assert blocks == 4 + # At depth 5, l2 reclaims both their DELAYED_OUTPUT_TO_US and their delayed output + bitcoind.generate_block(4) bitcoind.generate_block(10, wait_for_mempool=2) l2.wait_for_onchaind_broadcast('OUR_HTLC_TIMEOUT_TX', 'OUR_UNILATERAL/OUR_HTLC') bitcoind.generate_block(1, wait_for_mempool=1) - l2.daemon.wait_for_log('Propose handling OUR_HTLC_TIMEOUT_TX/DELAYED_OUTPUT_TO_US by OUR_DELAYED_RETURN_TO_WALLET .* after 5 blocks') # l3 comes back up, sees cheat, penalizes l2 (revokes the htlc they've offered; # notes that they've successfully claimed to_local and the fulfilled htlc) @@ -2114,6 +2113,10 @@ def test_onchain_timeout(node_factory, bitcoind, executor): bitcoind.generate_block(1) l1.wait_for_onchaind_broadcast('OUR_HTLC_TIMEOUT_TX', 'OUR_UNILATERAL/OUR_HTLC') + bitcoind.generate_block(1, wait_for_mempool=1) + ((rawtx, txid, blocks),) = l1.wait_for_onchaind_tx('OUR_DELAYED_RETURN_TO_WALLET', + 'OUR_HTLC_TIMEOUT_TX/DELAYED_OUTPUT_TO_US') + assert blocks == 4 # We use 3 blocks for "reasonable depth" bitcoind.generate_block(3) @@ -2122,13 +2125,11 @@ def test_onchain_timeout(node_factory, bitcoind, executor): with pytest.raises(RpcError, match=r'WIRE_PERMANENT_CHANNEL_FAILURE: timed out'): payfuture.result(TIMEOUT) - # 2 later, l1 spends HTLC (5 blocks total). - bitcoind.generate_block(2) - l1.wait_for_onchaind_broadcast('OUR_DELAYED_RETURN_TO_WALLET', - 'OUR_HTLC_TIMEOUT_TX/DELAYED_OUTPUT_TO_US') + # 1 later, l1 spends HTLC (depth = 5 blocks). + bitcoind.generate_block(1) # 89 later, l2 is done. - bitcoind.generate_block(89) + bitcoind.generate_block(89, wait_for_mempool=txid) l2.daemon.wait_for_log('onchaind complete, forgetting peer') # Now, 100 blocks and l1 should be done. @@ -2240,18 +2241,20 @@ def test_onchain_middleman_simple(node_factory, bitcoind): t.join(timeout=1) assert not t.is_alive() + ((_, txid, blocks),) = l2.wait_for_onchaind_tx('OUR_DELAYED_RETURN_TO_WALLET', + 'OUR_HTLC_SUCCESS_TX/DELAYED_OUTPUT_TO_US') + assert blocks == 4 + # Three more, l2 can spend to-us. bitcoind.generate_block(3) l2.wait_for_onchaind_broadcast('OUR_DELAYED_RETURN_TO_WALLET', 'OUR_UNILATERAL/DELAYED_OUTPUT_TO_US') # One more block, HTLC tx is now spendable. - l1.bitcoin.generate_block(1) - l2.wait_for_onchaind_broadcast('OUR_DELAYED_RETURN_TO_WALLET', - 'OUR_HTLC_SUCCESS_TX/DELAYED_OUTPUT_TO_US') + bitcoind.generate_block(1, wait_for_mempool=1) # 100 blocks after last spend, l2 should be done. - l1.bitcoin.generate_block(100) + l1.bitcoin.generate_block(100, wait_for_mempool=txid) l2.daemon.wait_for_log('onchaind complete, forgetting peer') # Verify accounting for l1 & l2 @@ -3089,16 +3092,15 @@ def test_permfail_htlc_in(node_factory, bitcoind, executor): # OK, l1 sees l2 fulfill htlc. l1.daemon.wait_for_log('THEIR_UNILATERAL/OUR_HTLC gave us preimage') - l2.daemon.wait_for_log('Propose handling OUR_HTLC_SUCCESS_TX/DELAYED_OUTPUT_TO_US by OUR_DELAYED_RETURN_TO_WALLET .* after 5 blocks') - bitcoind.generate_block(5) - - l2.wait_for_onchaind_broadcast('OUR_DELAYED_RETURN_TO_WALLET', - 'OUR_HTLC_SUCCESS_TX/DELAYED_OUTPUT_TO_US') + ((_, txid, blocks),) = l2.wait_for_onchaind_tx('OUR_DELAYED_RETURN_TO_WALLET', + 'OUR_HTLC_SUCCESS_TX/DELAYED_OUTPUT_TO_US') + assert blocks == 4 + bitcoind.generate_block(4) t.cancel() # Now, 100 blocks it should be done. - bitcoind.generate_block(95) + bitcoind.generate_block(95, wait_for_mempool=txid) l1.daemon.wait_for_log('onchaind complete, forgetting peer') assert not l2.daemon.is_in_log('onchaind complete, forgetting peer') bitcoind.generate_block(5) diff --git a/tests/test_misc.py b/tests/test_misc.py index e3a113549..a009d1e23 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -363,12 +363,14 @@ def test_htlc_out_timeout(node_factory, bitcoind, executor): l1.daemon.wait_for_log('sendrawtx exit 0') bitcoind.generate_block(1) - l1.daemon.wait_for_log('Propose handling OUR_HTLC_TIMEOUT_TX/DELAYED_OUTPUT_TO_US by OUR_DELAYED_RETURN_TO_WALLET .* after 5 blocks') + ((rawtx, txid, blocks),) = l1.wait_for_onchaind_tx('OUR_DELAYED_RETURN_TO_WALLET', + 'OUR_HTLC_TIMEOUT_TX/DELAYED_OUTPUT_TO_US') + assert blocks == 4 bitcoind.generate_block(4) + # It should now claim both the to-local and htlc-timeout-tx outputs. l1.daemon.wait_for_logs(['Broadcasting OUR_DELAYED_RETURN_TO_WALLET', - 'Broadcasting OUR_DELAYED_RETURN_TO_WALLET', - 'sendrawtx exit 0', + 'sendrawtx exit 0.*{}'.format(rawtx), 'sendrawtx exit 0']) # Now, 100 blocks it should be done. @@ -424,14 +426,15 @@ def test_htlc_in_timeout(node_factory, bitcoind, executor): # L2 will collect HTLC (iff no shadow route) l2.daemon.wait_for_log('Propose handling OUR_UNILATERAL/THEIR_HTLC by OUR_HTLC_SUCCESS_TX .* after 0 blocks') l2.daemon.wait_for_log('sendrawtx exit 0') - bitcoind.generate_block(1) - l2.daemon.wait_for_log('Propose handling OUR_HTLC_SUCCESS_TX/DELAYED_OUTPUT_TO_US by OUR_DELAYED_RETURN_TO_WALLET .* after 5 blocks') + bitcoind.generate_block(1, wait_for_mempool=1) + ((rawtx, txid, blocks),) = l2.wait_for_onchaind_tx('OUR_DELAYED_RETURN_TO_WALLET', + 'OUR_HTLC_SUCCESS_TX/DELAYED_OUTPUT_TO_US') + assert blocks == 4 bitcoind.generate_block(4) - l2.daemon.wait_for_log('Broadcasting OUR_DELAYED_RETURN_TO_WALLET') - l2.daemon.wait_for_log('sendrawtx exit 0') + l2.daemon.wait_for_log('sendrawtx exit 0.*{}'.format(rawtx)) # Now, 100 blocks it should be both done. - bitcoind.generate_block(100) + bitcoind.generate_block(100, wait_for_mempool=txid) l1.daemon.wait_for_log('onchaind complete, forgetting peer') l2.daemon.wait_for_log('onchaind complete, forgetting peer')