Skip to content

Commit a1d6356

Browse files
committed
Fix package splitting logic
When scanning confirmed transactions for spends that conflict with our existing packages, we should continue scanning after detecting the first conflicting package since a transaction can conflict with multiple packages. This ensures that we remove *all* inputs from our packages that have already been spent by the counterparty so that valid claim transactions are generated. Fixes #3537.
1 parent ad462bd commit a1d6356

File tree

2 files changed

+259
-1
lines changed

2 files changed

+259
-1
lines changed

lightning/src/chain/onchaintx.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -964,7 +964,6 @@ impl<ChannelSigner: EcdsaChannelSigner> OnchainTxHandler<ChannelSigner> {
964964
self.pending_claim_events.retain(|entry| entry.0 != *claim_id);
965965
}
966966
}
967-
break; //No need to iterate further, either tx is our or their
968967
} else {
969968
panic!("Inconsistencies between pending_claim_requests map and claimable_outpoints map");
970969
}

lightning/src/ln/functional_tests.rs

Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use crate::chain::channelmonitor;
1818
use crate::chain::channelmonitor::{Balance, ChannelMonitorUpdateStep, CLTV_CLAIM_BUFFER, LATENCY_GRACE_PERIOD_BLOCKS, ANTI_REORG_DELAY};
1919
use crate::chain::transaction::OutPoint;
2020
use crate::sign::{ecdsa::EcdsaChannelSigner, EntropySource, OutputSpender, SignerProvider};
21+
use crate::events::bump_transaction::WalletSource;
2122
use crate::events::{Event, FundingInfo, MessageSendEvent, MessageSendEventsProvider, PathFailure, PaymentPurpose, ClosureReason, HTLCDestination, PaymentFailureReason};
2223
use crate::ln::types::ChannelId;
2324
use crate::types::payment::{PaymentPreimage, PaymentSecret, PaymentHash};
@@ -2762,6 +2763,264 @@ fn claim_htlc_outputs() {
27622763
assert_eq!(nodes[1].node.list_channels().len(), 0);
27632764
}
27642765

2766+
// Test that the HTLC package logic removes HTLCs from the package when they are claimed by the
2767+
// counterparty, even when the counterparty claims HTLCs from multiple packages in a single
2768+
// transaction.
2769+
//
2770+
// This is a regression test for https://github.com/lightningdevkit/rust-lightning/issues/3537.
2771+
#[test]
2772+
fn test_multiple_package_conflicts() {
2773+
let chanmon_cfgs = create_chanmon_cfgs(3);
2774+
let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
2775+
let mut user_cfg = test_default_channel_config();
2776+
2777+
// Anchor channels are required so that multiple HTLC-Successes can be aggregated into a single
2778+
// transaction.
2779+
user_cfg.channel_handshake_config.negotiate_anchors_zero_fee_htlc_tx = true;
2780+
user_cfg.manually_accept_inbound_channels = true;
2781+
2782+
let node_chanmgrs =
2783+
create_node_chanmgrs(3, &node_cfgs, &[Some(user_cfg), Some(user_cfg), Some(user_cfg)]);
2784+
let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
2785+
2786+
// Since we're using anchor channels, make sure each node has a UTXO for paying fees.
2787+
let coinbase_tx = Transaction {
2788+
version: Version::TWO,
2789+
lock_time: LockTime::ZERO,
2790+
input: vec![TxIn { ..Default::default() }],
2791+
output: vec![
2792+
TxOut {
2793+
value: Amount::ONE_BTC,
2794+
script_pubkey: nodes[0].wallet_source.get_change_script().unwrap(),
2795+
},
2796+
TxOut {
2797+
value: Amount::ONE_BTC,
2798+
script_pubkey: nodes[1].wallet_source.get_change_script().unwrap(),
2799+
},
2800+
TxOut {
2801+
value: Amount::ONE_BTC,
2802+
script_pubkey: nodes[2].wallet_source.get_change_script().unwrap(),
2803+
},
2804+
],
2805+
};
2806+
nodes[0].wallet_source.add_utxo(
2807+
bitcoin::OutPoint { txid: coinbase_tx.compute_txid(), vout: 0 },
2808+
coinbase_tx.output[0].value,
2809+
);
2810+
nodes[1].wallet_source.add_utxo(
2811+
bitcoin::OutPoint { txid: coinbase_tx.compute_txid(), vout: 1 },
2812+
coinbase_tx.output[1].value,
2813+
);
2814+
nodes[2].wallet_source.add_utxo(
2815+
bitcoin::OutPoint { txid: coinbase_tx.compute_txid(), vout: 2 },
2816+
coinbase_tx.output[2].value,
2817+
);
2818+
2819+
// Create the network.
2820+
// 0 -- 1 -- 2
2821+
//
2822+
// Payments will be routed from node 0 to node 2. Node 2 will force close and spend HTLCs from
2823+
// two of node 1's packages. We will then verify that node 1 correctly removes the conflicting
2824+
// HTLC spends from its packages.
2825+
const CHAN_CAPACITY: u64 = 10_000_000;
2826+
create_announced_chan_between_nodes_with_value(&nodes, 0, 1, CHAN_CAPACITY, 0);
2827+
let (_, _, cid_1_2, funding_tx_1_2) =
2828+
create_announced_chan_between_nodes_with_value(&nodes, 1, 2, CHAN_CAPACITY, 0);
2829+
2830+
// Ensure all nodes are at the same initial height.
2831+
let node_max_height = nodes.iter().map(|node| node.best_block_info().1).max().unwrap();
2832+
for node in &nodes {
2833+
let blocks_to_mine = node_max_height - node.best_block_info().1;
2834+
if blocks_to_mine > 0 {
2835+
connect_blocks(node, blocks_to_mine);
2836+
}
2837+
}
2838+
2839+
// Route HTLC 1.
2840+
let (preimage_1, payment_hash_1, ..) =
2841+
route_payment(&nodes[0], &[&nodes[1], &nodes[2]], 1_000_000);
2842+
2843+
// Route HTLCs 2 and 3, with CLTVs 1 higher than HTLC 1. The higher CLTVs will cause these
2844+
// HTLCs to be included in a different package than HTLC 1.
2845+
connect_blocks(&nodes[0], 1);
2846+
connect_blocks(&nodes[1], 1);
2847+
connect_blocks(&nodes[2], 1);
2848+
let (preimage_2, payment_hash_2, ..) =
2849+
route_payment(&nodes[0], &[&nodes[1], &nodes[2]], 1_000_000);
2850+
route_payment(&nodes[0], &[&nodes[1], &nodes[2]], 900_000_000);
2851+
2852+
// Mine blocks until HTLC 1 times out in 1 block and HTLCs 2 and 3 time out in 2 blocks.
2853+
connect_blocks(&nodes[1], TEST_FINAL_CLTV - 1);
2854+
2855+
// Node 2 force closes, causing node 1 to group the HTLCs into the following packages:
2856+
// Package 1: HTLC 1
2857+
// Package 2: HTLCs 2 and 3
2858+
let node2_commit_tx = get_local_commitment_txn!(nodes[2], cid_1_2);
2859+
assert_eq!(node2_commit_tx.len(), 1);
2860+
let node2_commit_tx = &node2_commit_tx[0];
2861+
check_spends!(node2_commit_tx, funding_tx_1_2);
2862+
mine_transaction(&nodes[1], node2_commit_tx);
2863+
check_closed_event(
2864+
&nodes[1],
2865+
1,
2866+
ClosureReason::CommitmentTxConfirmed,
2867+
false,
2868+
&[nodes[2].node.get_our_node_id()],
2869+
CHAN_CAPACITY,
2870+
);
2871+
check_closed_broadcast!(nodes[1], true);
2872+
check_added_monitors(&nodes[1], 1);
2873+
2874+
// Node 1 should immediately claim package 1 but has to wait a block to claim package 2.
2875+
let timeout_tx = nodes[1].tx_broadcaster.txn_broadcast();
2876+
assert_eq!(timeout_tx.len(), 1);
2877+
check_spends!(timeout_tx[0], node2_commit_tx);
2878+
assert_eq!(timeout_tx[0].input.len(), 1);
2879+
2880+
// After one block, node 1 should also attempt to claim package 2.
2881+
connect_blocks(&nodes[1], 1);
2882+
let timeout_tx = nodes[1].tx_broadcaster.txn_broadcast();
2883+
assert_eq!(timeout_tx.len(), 1);
2884+
check_spends!(timeout_tx[0], node2_commit_tx);
2885+
assert_eq!(timeout_tx[0].input.len(), 2);
2886+
2887+
// Force node 2 to broadcast an aggregated HTLC-Success transaction spending HTLCs 1 and 2.
2888+
// This will conflict with both of node 1's HTLC packages.
2889+
{
2890+
let broadcaster = &node_cfgs[2].tx_broadcaster;
2891+
let fee_estimator = &LowerBoundedFeeEstimator::new(node_cfgs[2].fee_estimator);
2892+
let logger = &node_cfgs[2].logger;
2893+
let monitor = get_monitor!(nodes[2], cid_1_2);
2894+
monitor.provide_payment_preimage_unsafe_legacy(
2895+
&payment_hash_1,
2896+
&preimage_1,
2897+
broadcaster,
2898+
fee_estimator,
2899+
logger,
2900+
);
2901+
monitor.provide_payment_preimage_unsafe_legacy(
2902+
&payment_hash_2,
2903+
&preimage_2,
2904+
broadcaster,
2905+
fee_estimator,
2906+
logger,
2907+
);
2908+
}
2909+
mine_transaction(&nodes[2], node2_commit_tx);
2910+
check_closed_event(
2911+
&nodes[2],
2912+
1,
2913+
ClosureReason::CommitmentTxConfirmed,
2914+
false,
2915+
&[nodes[1].node.get_our_node_id()],
2916+
CHAN_CAPACITY,
2917+
);
2918+
check_closed_broadcast!(nodes[2], true);
2919+
check_added_monitors(&nodes[2], 1);
2920+
2921+
let process_bump_event = |node: &Node| {
2922+
let events = node.chain_monitor.chain_monitor.get_and_clear_pending_events();
2923+
assert_eq!(events.len(), 1);
2924+
let bump_event = match &events[0] {
2925+
Event::BumpTransaction(bump_event) => bump_event,
2926+
_ => panic!("Unexepected event"),
2927+
};
2928+
node.bump_tx_handler.handle_event(bump_event);
2929+
2930+
let mut tx = node.tx_broadcaster.txn_broadcast();
2931+
assert_eq!(tx.len(), 1);
2932+
tx.pop().unwrap()
2933+
};
2934+
2935+
let conflict_tx = process_bump_event(&nodes[2]);
2936+
assert_eq!(conflict_tx.input.len(), 3);
2937+
assert_eq!(conflict_tx.input[0].previous_output.txid, node2_commit_tx.compute_txid());
2938+
assert_eq!(conflict_tx.input[1].previous_output.txid, node2_commit_tx.compute_txid());
2939+
assert_eq!(conflict_tx.input[2].previous_output.txid, coinbase_tx.compute_txid());
2940+
2941+
// Mine node 2's aggregated HTLC-Success transaction on node 1, causing the package splitting
2942+
// logic to run. Package 2 should get split so that only HTLC 3 gets claimed.
2943+
mine_transaction(&nodes[1], &conflict_tx);
2944+
2945+
// Check that node 1 only attempts to claim HTLC 3 now. There should be no conflicting spends
2946+
// in the newly broadcasted transaction.
2947+
let broadcasted_txs = nodes[1].tx_broadcaster.txn_broadcast();
2948+
assert_eq!(broadcasted_txs.len(), 1);
2949+
let txins = &broadcasted_txs[0].input;
2950+
assert_eq!(txins.len(), 1);
2951+
assert_eq!(txins[0].previous_output.txid, node2_commit_tx.compute_txid());
2952+
for conflict_in in &conflict_tx.input {
2953+
assert_ne!(txins[0].previous_output, conflict_in.previous_output);
2954+
}
2955+
2956+
// Node 1 should also extract the preimages from the mined transaction and claim them upstream.
2957+
//
2958+
// Because two update_fulfill_htlc messages are created at once, the commitment_signed_dance
2959+
// macro doesn't work properly and we must process the first update_fulfill_htlc manually.
2960+
let updates = get_htlc_update_msgs(&nodes[1], &nodes[0].node.get_our_node_id());
2961+
assert_eq!(updates.update_fulfill_htlcs.len(), 1);
2962+
nodes[0].node.handle_update_fulfill_htlc(
2963+
nodes[1].node.get_our_node_id(),
2964+
&updates.update_fulfill_htlcs[0],
2965+
);
2966+
nodes[0]
2967+
.node
2968+
.handle_commitment_signed(nodes[1].node.get_our_node_id(), &updates.commitment_signed);
2969+
check_added_monitors(&nodes[0], 1);
2970+
2971+
let (revoke_ack, commit_signed) =
2972+
get_revoke_commit_msgs(&nodes[0], &nodes[1].node.get_our_node_id());
2973+
nodes[1].node.handle_revoke_and_ack(nodes[0].node.get_our_node_id(), &revoke_ack);
2974+
nodes[1].node.handle_commitment_signed(nodes[0].node.get_our_node_id(), &commit_signed);
2975+
check_added_monitors(&nodes[1], 4);
2976+
2977+
let events = nodes[1].node.get_and_clear_pending_msg_events();
2978+
assert_eq!(events.len(), 2);
2979+
let revoke_ack = match &events[1] {
2980+
MessageSendEvent::SendRevokeAndACK { node_id: _, msg } => msg,
2981+
_ => panic!("Unexpected event"),
2982+
};
2983+
nodes[0].node.handle_revoke_and_ack(nodes[1].node.get_our_node_id(), revoke_ack);
2984+
expect_payment_sent!(nodes[0], preimage_1);
2985+
2986+
let updates = match &events[0] {
2987+
MessageSendEvent::UpdateHTLCs { node_id: _, updates } => updates,
2988+
_ => panic!("Unexpected event"),
2989+
};
2990+
assert_eq!(updates.update_fulfill_htlcs.len(), 1);
2991+
nodes[0].node.handle_update_fulfill_htlc(
2992+
nodes[1].node.get_our_node_id(),
2993+
&updates.update_fulfill_htlcs[0],
2994+
);
2995+
commitment_signed_dance!(nodes[0], nodes[1], updates.commitment_signed, false);
2996+
expect_payment_sent!(nodes[0], preimage_2);
2997+
2998+
let mut events = nodes[1].node.get_and_clear_pending_events();
2999+
assert_eq!(events.len(), 2);
3000+
expect_payment_forwarded(
3001+
events.pop().unwrap(),
3002+
&nodes[1],
3003+
&nodes[0],
3004+
&nodes[2],
3005+
Some(1000),
3006+
None,
3007+
false,
3008+
true,
3009+
false,
3010+
);
3011+
expect_payment_forwarded(
3012+
events.pop().unwrap(),
3013+
&nodes[1],
3014+
&nodes[0],
3015+
&nodes[2],
3016+
Some(1000),
3017+
None,
3018+
false,
3019+
true,
3020+
false,
3021+
);
3022+
}
3023+
27653024
#[test]
27663025
fn test_htlc_on_chain_success() {
27673026
// Test that in case of a unilateral close onchain, we detect the state of output and pass

0 commit comments

Comments
 (0)