Skip to content

Commit de15b0f

Browse files
morehouseTheBlueMatt
authored andcommitted
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 lightningdevkit#3537.
1 parent 276d082 commit de15b0f

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};
@@ -2774,6 +2775,264 @@ fn claim_htlc_outputs() {
27742775
assert_eq!(nodes[1].node.list_channels().len(), 0);
27752776
}
27762777

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

0 commit comments

Comments
 (0)