Skip to content

Commit 032673c

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 c041472 commit 032673c

File tree

3 files changed

+105
-40
lines changed

3 files changed

+105
-40
lines changed

lightning/src/chain/channelmonitor.rs

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -865,6 +865,9 @@ impl<Signer: Sign> Writeable for ChannelMonitorImpl<Signer> {
865865
writer.write_all(&txid[..])?;
866866
writer.write_all(&byte_utils::be64_to_array(htlc_infos.len() as u64))?;
867867
for &(ref htlc_output, ref htlc_source) in htlc_infos.iter() {
868+
debug_assert!(htlc_source.is_none() || Some(**txid) == self.current_counterparty_commitment_txid
869+
|| Some(**txid) == self.prev_counterparty_commitment_txid,
870+
"HTLC Sources for all but revoked commitment transactions should be none!");
868871
serialize_htlc_in_commitment!(htlc_output);
869872
htlc_source.as_ref().map(|b| b.as_ref()).write(writer)?;
870873
}
@@ -1667,9 +1670,14 @@ macro_rules! fail_unbroadcast_htlcs {
16671670
// cannot currently change after channel initialization, so we don't
16681671
// need to here.
16691672
let confirmed_htlcs_iter: &mut Iterator<Item = (&HTLCOutputInCommitment, Option<&HTLCSource>)> = &mut $confirmed_htlcs_list;
1673+
16701674
let mut matched_htlc = false;
16711675
for (ref broadcast_htlc, ref broadcast_source) in confirmed_htlcs_iter {
1672-
if broadcast_htlc.transaction_output_index.is_some() && Some(&**source) == *broadcast_source {
1676+
if broadcast_htlc.transaction_output_index.is_some() &&
1677+
(Some(&**source) == *broadcast_source ||
1678+
(broadcast_source.is_none() &&
1679+
broadcast_htlc.payment_hash == htlc.payment_hash &&
1680+
broadcast_htlc.amount_msat == htlc.amount_msat)) {
16731681
matched_htlc = true;
16741682
break;
16751683
}
@@ -2093,8 +2101,14 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
20932101
}
20942102
self.counterparty_commitment_txn_on_chain.insert(commitment_txid, commitment_number);
20952103

2096-
fail_unbroadcast_htlcs!(self, "revoked counterparty", commitment_txid, height,
2097-
[].iter().map(|a| *a), logger);
2104+
if let Some(per_commitment_data) = per_commitment_option {
2105+
fail_unbroadcast_htlcs!(self, "revoked_counterparty", commitment_txid, height,
2106+
per_commitment_data.iter().map(|(a, b)| (a, b.as_ref().map(|b| b.as_ref()))), logger);
2107+
} else {
2108+
debug_assert!(false, "We should have per-commitment option for any recognized old commitment txn");
2109+
fail_unbroadcast_htlcs!(self, "revoked counterparty", commitment_txid, height,
2110+
[].iter().map(|a| *a), logger);
2111+
}
20982112
}
20992113
} else if let Some(per_commitment_data) = per_commitment_option {
21002114
// 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
@@ -2509,10 +2509,10 @@ fn claim_htlc_outputs_shared_tx() {
25092509
let chan_1 = create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::known(), InitFeatures::known());
25102510

25112511
// Rebalance the network to generate htlc in the two directions
2512-
send_payment(&nodes[0], &vec!(&nodes[1])[..], 8000000);
2512+
send_payment(&nodes[0], &[&nodes[1]], 8_000_000);
25132513
// 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
2514-
let payment_preimage_1 = route_payment(&nodes[0], &vec!(&nodes[1])[..], 3000000).0;
2515-
let (_payment_preimage_2, payment_hash_2, _) = route_payment(&nodes[1], &vec!(&nodes[0])[..], 3000000);
2514+
let payment_preimage_1 = route_payment(&nodes[0], &[&nodes[1]], 3_000_000).0;
2515+
let (_payment_preimage_2, payment_hash_2, _) = route_payment(&nodes[1], &[&nodes[0]], 3_000_000);
25162516

25172517
// Get the will-be-revoked local txn from node[0]
25182518
let revoked_local_txn = get_local_commitment_txn!(nodes[0], chan_1.2);
@@ -2535,9 +2535,9 @@ fn claim_htlc_outputs_shared_tx() {
25352535
check_added_monitors!(nodes[1], 1);
25362536
check_closed_event!(nodes[1], 1, ClosureReason::CommitmentTxConfirmed);
25372537
connect_blocks(&nodes[1], ANTI_REORG_DELAY - 1);
2538-
expect_payment_failed!(nodes[1], payment_hash_2, true);
2538+
assert!(nodes[1].node.get_and_clear_pending_events().is_empty());
25392539

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

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

25552555
// Next nodes[1] broadcasts its current local tx state:
25562556
assert_eq!(node_txn[1].input.len(), 1);
2557-
assert_eq!(node_txn[1].input[0].previous_output.txid, chan_1.3.txid()); //Spending funding tx unique txouput, tx broadcasted by ChannelManager
2557+
check_spends!(node_txn[1], chan_1.3);
2558+
2559+
// Finally, mine the penalty transaction and check that we get an HTLC failure after
2560+
// ANTI_REORG_DELAY confirmations.
2561+
mine_transaction(&nodes[1], &node_txn[0]);
2562+
connect_blocks(&nodes[1], ANTI_REORG_DELAY - 1);
2563+
expect_payment_failed!(nodes[1], payment_hash_2, true);
25582564
}
25592565
get_announce_close_broadcast_events(&nodes, 0, 1);
25602566
assert_eq!(nodes[0].node.list_channels().len(), 0);
@@ -2573,11 +2579,11 @@ fn claim_htlc_outputs_single_tx() {
25732579
let chan_1 = create_announced_chan_between_nodes(&nodes, 0, 1, InitFeatures::known(), InitFeatures::known());
25742580

25752581
// Rebalance the network to generate htlc in the two directions
2576-
send_payment(&nodes[0], &vec!(&nodes[1])[..], 8000000);
2582+
send_payment(&nodes[0], &[&nodes[1]], 8_000_000);
25772583
// 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
25782584
// time as two different claim transactions as we're gonna to timeout htlc with given a high current height
2579-
let payment_preimage_1 = route_payment(&nodes[0], &vec!(&nodes[1])[..], 3000000).0;
2580-
let (_payment_preimage_2, payment_hash_2, _payment_secret_2) = route_payment(&nodes[1], &vec!(&nodes[0])[..], 3000000);
2585+
let payment_preimage_1 = route_payment(&nodes[0], &[&nodes[1]], 3_000_000).0;
2586+
let (_payment_preimage_2, payment_hash_2, _payment_secret_2) = route_payment(&nodes[1], &[&nodes[0]], 3_000_000);
25812587

25822588
// Get the will-be-revoked local txn from node[0]
25832589
let revoked_local_txn = get_local_commitment_txn!(nodes[0], chan_1.2);
@@ -2599,9 +2605,9 @@ fn claim_htlc_outputs_single_tx() {
25992605
}
26002606

26012607
connect_blocks(&nodes[1], ANTI_REORG_DELAY - 1);
2602-
expect_payment_failed!(nodes[1], payment_hash_2, true);
2608+
assert!(nodes[1].node.get_and_clear_pending_events().is_empty());
26032609

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

26072613
// Check the pair local commitment and HTLC-timeout broadcast due to HTLC expiration
@@ -2629,6 +2635,14 @@ fn claim_htlc_outputs_single_tx() {
26292635
assert_eq!(*witness_lens.iter().skip(0).next().unwrap(), 77); // revoked to_local
26302636
assert_eq!(*witness_lens.iter().skip(1).next().unwrap(), OFFERED_HTLC_SCRIPT_WEIGHT); // revoked offered HTLC
26312637
assert_eq!(*witness_lens.iter().skip(2).next().unwrap(), ACCEPTED_HTLC_SCRIPT_WEIGHT); // revoked received HTLC
2638+
2639+
// Finally, mine the penalty transactions and check that we get an HTLC failure after
2640+
// ANTI_REORG_DELAY confirmations.
2641+
mine_transaction(&nodes[1], &node_txn[2]);
2642+
mine_transaction(&nodes[1], &node_txn[3]);
2643+
mine_transaction(&nodes[1], &node_txn[4]);
2644+
connect_blocks(&nodes[1], ANTI_REORG_DELAY - 1);
2645+
expect_payment_failed!(nodes[1], payment_hash_2, true);
26322646
}
26332647
get_announce_close_broadcast_events(&nodes, 0, 1);
26342648
assert_eq!(nodes[0].node.list_channels().len(), 0);
@@ -7259,37 +7273,25 @@ fn do_test_sweep_outbound_htlc_failure_update(revoked: bool, local: bool) {
72597273
check_added_monitors!(nodes[0], 1);
72607274
check_closed_event!(nodes[0], 1, ClosureReason::CommitmentTxConfirmed);
72617275
assert_eq!(nodes[0].node.get_and_clear_pending_events().len(), 0);
7276+
72627277
connect_blocks(&nodes[0], TEST_FINAL_CLTV - 1); // Confirm blocks until the HTLC expires
7263-
timeout_tx.push(nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap()[1].clone());
7278+
timeout_tx = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().drain(..)
7279+
.filter(|tx| tx.input[0].previous_output.txid == bs_commitment_tx[0].txid()).collect();
7280+
check_spends!(timeout_tx[0], bs_commitment_tx[0]);
7281+
// For both a revoked or non-revoked commitment transaction, after ANTI_REORG_DELAY the
7282+
// dust HTLC should have been failed.
7283+
expect_payment_failed!(nodes[0], dust_hash, true);
7284+
72647285
if !revoked {
7265-
expect_payment_failed!(nodes[0], dust_hash, true);
72667286
assert_eq!(timeout_tx[0].input[0].witness.last().unwrap().len(), ACCEPTED_HTLC_SCRIPT_WEIGHT);
7267-
// We fail non-dust-HTLC 2 by broadcast of local timeout tx on remote commitment tx
7268-
mine_transaction(&nodes[0], &timeout_tx[0]);
7269-
assert_eq!(nodes[0].node.get_and_clear_pending_events().len(), 0);
7270-
connect_blocks(&nodes[0], ANTI_REORG_DELAY - 1);
7271-
expect_payment_failed!(nodes[0], non_dust_hash, true);
72727287
} else {
7273-
// If revoked, both dust & non-dust HTLCs should have been failed after ANTI_REORG_DELAY confs of revoked
7274-
// commitment tx
7275-
let events = nodes[0].node.get_and_clear_pending_events();
7276-
assert_eq!(events.len(), 2);
7277-
let first;
7278-
match events[0] {
7279-
Event::PaymentPathFailed { payment_hash, .. } => {
7280-
if payment_hash == dust_hash { first = true; }
7281-
else { first = false; }
7282-
},
7283-
_ => panic!("Unexpected event"),
7284-
}
7285-
match events[1] {
7286-
Event::PaymentPathFailed { payment_hash, .. } => {
7287-
if first { assert_eq!(payment_hash, non_dust_hash); }
7288-
else { assert_eq!(payment_hash, dust_hash); }
7289-
},
7290-
_ => panic!("Unexpected event"),
7291-
}
7288+
assert_eq!(timeout_tx[0].lock_time, 0);
72927289
}
7290+
// We fail non-dust-HTLC 2 by broadcast of local timeout/revocation-claim 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);
72937295
}
72947296
}
72957297

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)