Skip to content

Commit 9854c91

Browse files
committed
Don't fail HTLCs in revoked commitment txn until we spend them
When we see a counterparty revoked commitment transaction on-chain we shouldn't immediately queue up HTLCs present in it for resolution until we have spent the HTLC outputs in some kind of claim transaction. In order to do so, we first have to change the `fail_unbroadcast_htlcs!()` call to provide it with the HTLCs which are present in the (revoked) commitment transaction which was broadcast. However, this is not sufficient - because all of those HTLCs had their `HTLCSource` removed when the commitment transaction was revoked, we also have to update `fail_unbroadcast_htlcs` to check the payment hash and amount when the `HTLCSource` is `None`. Somewhat surprisingly, several tests actually explicitly tested for the old behavior, which required amending to pass with the new changes. Finally, this adds a debug assertion when writing `ChannelMonitor`s to ensure `HTLCSource`s do not leak.
1 parent 44d1dfa commit 9854c91

File tree

3 files changed

+107
-40
lines changed

3 files changed

+107
-40
lines changed

lightning/src/chain/channelmonitor.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -869,6 +869,9 @@ impl<Signer: Sign> Writeable for ChannelMonitorImpl<Signer> {
869869
writer.write_all(&txid[..])?;
870870
writer.write_all(&byte_utils::be64_to_array(htlc_infos.len() as u64))?;
871871
for &(ref htlc_output, ref htlc_source) in htlc_infos.iter() {
872+
debug_assert!(htlc_source.is_none() || Some(**txid) == self.current_counterparty_commitment_txid
873+
|| Some(**txid) == self.prev_counterparty_commitment_txid,
874+
"HTLC Sources for all but revoked commitment transactions should be none!");
872875
serialize_htlc_in_commitment!(htlc_output);
873876
htlc_source.as_ref().map(|b| b.as_ref()).write(writer)?;
874877
}
@@ -1676,9 +1679,14 @@ macro_rules! fail_unbroadcast_htlcs {
16761679
// cannot currently change after channel initialization, so we don't
16771680
// need to here.
16781681
let confirmed_htlcs_iter: &mut Iterator<Item = (&HTLCOutputInCommitment, Option<&HTLCSource>)> = &mut $confirmed_htlcs_list;
1682+
16791683
let mut matched_htlc = false;
16801684
for (ref broadcast_htlc, ref broadcast_source) in confirmed_htlcs_iter {
1681-
if broadcast_htlc.transaction_output_index.is_some() && Some(&**source) == *broadcast_source {
1685+
if broadcast_htlc.transaction_output_index.is_some() &&
1686+
(Some(&**source) == *broadcast_source ||
1687+
(broadcast_source.is_none() &&
1688+
broadcast_htlc.payment_hash == htlc.payment_hash &&
1689+
broadcast_htlc.amount_msat == htlc.amount_msat)) {
16821690
matched_htlc = true;
16831691
break;
16841692
}
@@ -2102,8 +2110,16 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
21022110
}
21032111
self.counterparty_commitment_txn_on_chain.insert(commitment_txid, commitment_number);
21042112

2105-
fail_unbroadcast_htlcs!(self, "revoked counterparty", commitment_txid, height,
2106-
[].iter().map(|a| *a), logger);
2113+
if let Some(per_commitment_data) = per_commitment_option {
2114+
fail_unbroadcast_htlcs!(self, "revoked_counterparty", commitment_txid, height,
2115+
per_commitment_data.iter().map(|(htlc, htlc_source)|
2116+
(htlc, htlc_source.as_ref().map(|htlc_source| htlc_source.as_ref()))
2117+
), logger);
2118+
} else {
2119+
debug_assert!(false, "We should have per-commitment option for any recognized old commitment txn");
2120+
fail_unbroadcast_htlcs!(self, "revoked counterparty", commitment_txid, height,
2121+
[].iter().map(|reference| *reference), logger);
2122+
}
21072123
}
21082124
} else if let Some(per_commitment_data) = per_commitment_option {
21092125
// While this isn't useful yet, there is a potential race where if a counterparty

lightning/src/ln/functional_tests.rs

Lines changed: 39 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2515,10 +2515,10 @@ fn claim_htlc_outputs_shared_tx() {
25152515
let chan_1 = create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::known(), InitFeatures::known());
25162516

25172517
// Rebalance the network to generate htlc in the two directions
2518-
send_payment(&nodes[0], &vec!(&nodes[1])[..], 8000000);
2518+
send_payment(&nodes[0], &[&nodes[1]], 8_000_000);
25192519
// node[0] is gonna to revoke an old state thus node[1] should be able to claim both offered/received HTLC outputs on top of commitment tx
2520-
let payment_preimage_1 = route_payment(&nodes[0], &vec!(&nodes[1])[..], 3000000).0;
2521-
let (_payment_preimage_2, payment_hash_2, _) = route_payment(&nodes[1], &vec!(&nodes[0])[..], 3000000);
2520+
let payment_preimage_1 = route_payment(&nodes[0], &[&nodes[1]], 3_000_000).0;
2521+
let (_payment_preimage_2, payment_hash_2, _) = route_payment(&nodes[1], &[&nodes[0]], 3_000_000);
25222522

25232523
// Get the will-be-revoked local txn from node[0]
25242524
let revoked_local_txn = get_local_commitment_txn!(nodes[0], chan_1.2);
@@ -2541,9 +2541,9 @@ fn claim_htlc_outputs_shared_tx() {
25412541
check_added_monitors!(nodes[1], 1);
25422542
check_closed_event!(nodes[1], 1, ClosureReason::CommitmentTxConfirmed);
25432543
connect_blocks(&nodes[1], ANTI_REORG_DELAY - 1);
2544-
expect_payment_failed!(nodes[1], payment_hash_2, true);
2544+
assert!(nodes[1].node.get_and_clear_pending_events().is_empty());
25452545

2546-
let node_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap();
2546+
let node_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0);
25472547
assert_eq!(node_txn.len(), 2); // ChannelMonitor: penalty tx, ChannelManager: local commitment
25482548

25492549
assert_eq!(node_txn[0].input.len(), 3); // Claim the revoked output + both revoked HTLC outputs
@@ -2560,7 +2560,13 @@ fn claim_htlc_outputs_shared_tx() {
25602560

25612561
// Next nodes[1] broadcasts its current local tx state:
25622562
assert_eq!(node_txn[1].input.len(), 1);
2563-
assert_eq!(node_txn[1].input[0].previous_output.txid, chan_1.3.txid()); //Spending funding tx unique txouput, tx broadcasted by ChannelManager
2563+
check_spends!(node_txn[1], chan_1.3);
2564+
2565+
// Finally, mine the penalty transaction and check that we get an HTLC failure after
2566+
// ANTI_REORG_DELAY confirmations.
2567+
mine_transaction(&nodes[1], &node_txn[0]);
2568+
connect_blocks(&nodes[1], ANTI_REORG_DELAY - 1);
2569+
expect_payment_failed!(nodes[1], payment_hash_2, true);
25642570
}
25652571
get_announce_close_broadcast_events(&nodes, 0, 1);
25662572
assert_eq!(nodes[0].node.list_channels().len(), 0);
@@ -2579,11 +2585,11 @@ fn claim_htlc_outputs_single_tx() {
25792585
let chan_1 = create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::known(), InitFeatures::known());
25802586

25812587
// Rebalance the network to generate htlc in the two directions
2582-
send_payment(&nodes[0], &vec!(&nodes[1])[..], 8000000);
2588+
send_payment(&nodes[0], &[&nodes[1]], 8_000_000);
25832589
// node[0] is gonna to revoke an old state thus node[1] should be able to claim both offered/received HTLC outputs on top of commitment tx, but this
25842590
// time as two different claim transactions as we're gonna to timeout htlc with given a high current height
2585-
let payment_preimage_1 = route_payment(&nodes[0], &vec!(&nodes[1])[..], 3000000).0;
2586-
let (_payment_preimage_2, payment_hash_2, _payment_secret_2) = route_payment(&nodes[1], &vec!(&nodes[0])[..], 3000000);
2591+
let payment_preimage_1 = route_payment(&nodes[0], &[&nodes[1]], 3_000_000).0;
2592+
let (_payment_preimage_2, payment_hash_2, _payment_secret_2) = route_payment(&nodes[1], &[&nodes[0]], 3_000_000);
25872593

25882594
// Get the will-be-revoked local txn from node[0]
25892595
let revoked_local_txn = get_local_commitment_txn!(nodes[0], chan_1.2);
@@ -2605,9 +2611,9 @@ fn claim_htlc_outputs_single_tx() {
26052611
}
26062612

26072613
connect_blocks(&nodes[1], ANTI_REORG_DELAY - 1);
2608-
expect_payment_failed!(nodes[1], payment_hash_2, true);
2614+
assert!(nodes[1].node.get_and_clear_pending_events().is_empty());
26092615

2610-
let node_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap();
2616+
let node_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0);
26112617
assert!(node_txn.len() == 9 || node_txn.len() == 10);
26122618

26132619
// Check the pair local commitment and HTLC-timeout broadcast due to HTLC expiration
@@ -2635,6 +2641,14 @@ fn claim_htlc_outputs_single_tx() {
26352641
assert_eq!(*witness_lens.iter().skip(0).next().unwrap(), 77); // revoked to_local
26362642
assert_eq!(*witness_lens.iter().skip(1).next().unwrap(), OFFERED_HTLC_SCRIPT_WEIGHT); // revoked offered HTLC
26372643
assert_eq!(*witness_lens.iter().skip(2).next().unwrap(), ACCEPTED_HTLC_SCRIPT_WEIGHT); // revoked received HTLC
2644+
2645+
// Finally, mine the penalty transactions and check that we get an HTLC failure after
2646+
// ANTI_REORG_DELAY confirmations.
2647+
mine_transaction(&nodes[1], &node_txn[2]);
2648+
mine_transaction(&nodes[1], &node_txn[3]);
2649+
mine_transaction(&nodes[1], &node_txn[4]);
2650+
connect_blocks(&nodes[1], ANTI_REORG_DELAY - 1);
2651+
expect_payment_failed!(nodes[1], payment_hash_2, true);
26382652
}
26392653
get_announce_close_broadcast_events(&nodes, 0, 1);
26402654
assert_eq!(nodes[0].node.list_channels().len(), 0);
@@ -7282,37 +7296,25 @@ fn do_test_sweep_outbound_htlc_failure_update(revoked: bool, local: bool) {
72827296
check_added_monitors!(nodes[0], 1);
72837297
check_closed_event!(nodes[0], 1, ClosureReason::CommitmentTxConfirmed);
72847298
assert_eq!(nodes[0].node.get_and_clear_pending_events().len(), 0);
7299+
72857300
connect_blocks(&nodes[0], TEST_FINAL_CLTV - 1); // Confirm blocks until the HTLC expires
7286-
timeout_tx.push(nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap()[1].clone());
7301+
timeout_tx = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().drain(..)
7302+
.filter(|tx| tx.input[0].previous_output.txid == bs_commitment_tx[0].txid()).collect();
7303+
check_spends!(timeout_tx[0], bs_commitment_tx[0]);
7304+
// For both a revoked or non-revoked commitment transaction, after ANTI_REORG_DELAY the
7305+
// dust HTLC should have been failed.
7306+
expect_payment_failed!(nodes[0], dust_hash, true);
7307+
72877308
if !revoked {
7288-
expect_payment_failed!(nodes[0], dust_hash, true);
72897309
assert_eq!(timeout_tx[0].input[0].witness.last().unwrap().len(), ACCEPTED_HTLC_SCRIPT_WEIGHT);
7290-
// We fail non-dust-HTLC 2 by broadcast of local timeout tx on remote commitment tx
7291-
mine_transaction(&nodes[0], &timeout_tx[0]);
7292-
assert_eq!(nodes[0].node.get_and_clear_pending_events().len(), 0);
7293-
connect_blocks(&nodes[0], ANTI_REORG_DELAY - 1);
7294-
expect_payment_failed!(nodes[0], non_dust_hash, true);
72957310
} else {
7296-
// If revoked, both dust & non-dust HTLCs should have been failed after ANTI_REORG_DELAY confs of revoked
7297-
// commitment tx
7298-
let events = nodes[0].node.get_and_clear_pending_events();
7299-
assert_eq!(events.len(), 2);
7300-
let first;
7301-
match events[0] {
7302-
Event::PaymentPathFailed { payment_hash, .. } => {
7303-
if payment_hash == dust_hash { first = true; }
7304-
else { first = false; }
7305-
},
7306-
_ => panic!("Unexpected event"),
7307-
}
7308-
match events[1] {
7309-
Event::PaymentPathFailed { payment_hash, .. } => {
7310-
if first { assert_eq!(payment_hash, non_dust_hash); }
7311-
else { assert_eq!(payment_hash, dust_hash); }
7312-
},
7313-
_ => panic!("Unexpected event"),
7314-
}
7311+
assert_eq!(timeout_tx[0].lock_time, 0);
73157312
}
7313+
// We fail non-dust-HTLC 2 by broadcast of local timeout/revocation-claim tx
7314+
mine_transaction(&nodes[0], &timeout_tx[0]);
7315+
assert_eq!(nodes[0].node.get_and_clear_pending_events().len(), 0);
7316+
connect_blocks(&nodes[0], ANTI_REORG_DELAY - 1);
7317+
expect_payment_failed!(nodes[0], non_dust_hash, true);
73167318
}
73177319
}
73187320

lightning/src/ln/monitor_tests.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,55 @@ fn test_spendable_output<'a, 'b, 'c, 'd>(node: &'a Node<'b, 'c, 'd>, spendable_t
9494
} else { panic!(); }
9595
}
9696

97+
#[test]
98+
fn revoked_output_htlc_resolution_timing() {
99+
// Tests that the resoluting time of HTLCs which were present in a broadcasted remote revoked
100+
// commitment transaction are resolved only after a spend of the HTLC output reaches six
101+
// confirmations. Preivously they would resolve after the revoked commitment transaction itself
102+
// reaches six confirmations.
103+
let chanmon_cfgs = create_chanmon_cfgs(2);
104+
let node_cfgs = create_node_cfgs(2, &chanmon_cfgs);
105+
let node_chanmgrs = create_node_chanmgrs(2, &node_cfgs, &[None, None]);
106+
let nodes = create_network(2, &node_cfgs, &node_chanmgrs);
107+
108+
let chan = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 1_000_000, 500_000_000, InitFeatures::known(), InitFeatures::known());
109+
110+
let payment_hash_1 = route_payment(&nodes[1], &[&nodes[0]], 1_000_000).1;
111+
112+
// Get a commitment transaction which contains the HTLC we care about, but which we'll revoke
113+
// before forwarding.
114+
let revoked_local_txn = get_local_commitment_txn!(nodes[0], chan.2);
115+
assert_eq!(revoked_local_txn.len(), 1);
116+
117+
// Route a dust payment to revoke the above commitment transaction
118+
route_payment(&nodes[0], &[&nodes[1]], 1_000);
119+
120+
// Confirm the revoked commitment transaction, closing the channel.
121+
mine_transaction(&nodes[1], &revoked_local_txn[0]);
122+
check_added_monitors!(nodes[1], 1);
123+
check_closed_event!(nodes[1], 1, ClosureReason::CommitmentTxConfirmed);
124+
check_closed_broadcast!(nodes[1], true);
125+
126+
let bs_spend_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0);
127+
assert_eq!(bs_spend_txn.len(), 2);
128+
check_spends!(bs_spend_txn[0], revoked_local_txn[0]);
129+
check_spends!(bs_spend_txn[1], chan.3);
130+
131+
// After the commitment transaction confirms, we should still wait on the HTLC spend
132+
// transaction to confirm before resolving the HTLC.
133+
connect_blocks(&nodes[1], ANTI_REORG_DELAY - 1);
134+
assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());
135+
assert!(nodes[1].node.get_and_clear_pending_events().is_empty());
136+
137+
// Spend the HTLC output, generating a HTLC failure event after ANTI_REORG_DELAY confirmations.
138+
mine_transaction(&nodes[1], &bs_spend_txn[0]);
139+
assert!(nodes[1].node.get_and_clear_pending_msg_events().is_empty());
140+
assert!(nodes[1].node.get_and_clear_pending_events().is_empty());
141+
142+
connect_blocks(&nodes[1], ANTI_REORG_DELAY - 1);
143+
expect_payment_failed!(nodes[1], payment_hash_1, true);
144+
}
145+
97146
#[test]
98147
fn chanmon_claim_value_coop_close() {
99148
// Tests `get_claimable_balances` returns the correct values across a simple cooperative claim.

0 commit comments

Comments
 (0)