Skip to content

Commit 19f4f99

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 4efeb65 commit 19f4f99

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
}
@@ -1666,9 +1669,14 @@ macro_rules! fail_unbroadcast_htlcs {
16661669
// cannot currently change after channel initialization, so we don't
16671670
// need to here.
16681671
let confirmed_htlcs_iter: &mut Iterator<Item = (&HTLCOutputInCommitment, Option<&HTLCSource>)> = &mut $confirmed_htlcs_list;
1672+
16691673
let mut matched_htlc = false;
16701674
for (ref broadcast_htlc, ref broadcast_source) in confirmed_htlcs_iter {
1671-
if broadcast_htlc.transaction_output_index.is_some() && Some(&**source) == *broadcast_source {
1675+
if broadcast_htlc.transaction_output_index.is_some() &&
1676+
(Some(&**source) == *broadcast_source ||
1677+
(broadcast_source.is_none() &&
1678+
broadcast_htlc.payment_hash == htlc.payment_hash &&
1679+
broadcast_htlc.amount_msat == htlc.amount_msat)) {
16721680
matched_htlc = true;
16731681
break;
16741682
}
@@ -2092,8 +2100,14 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
20922100
}
20932101
self.counterparty_commitment_txn_on_chain.insert(commitment_txid, commitment_number);
20942102

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

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

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

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

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

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

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

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

26022608
connect_blocks(&nodes[1], ANTI_REORG_DELAY - 1);
2603-
expect_payment_failed!(nodes[1], payment_hash_2, true);
2609+
assert!(nodes[1].node.get_and_clear_pending_events().is_empty());
26042610

2605-
let node_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap();
2611+
let node_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0);
26062612
assert!(node_txn.len() == 9 || node_txn.len() == 10);
26072613
// ChannelMonitor: justice tx revoked offered htlc, justice tx revoked received htlc, justice tx revoked to_local (3)
26082614
// ChannelManager: local commmitment + local HTLC-timeout (2)
@@ -2634,6 +2640,14 @@ fn claim_htlc_outputs_single_tx() {
26342640
assert_eq!(*witness_lens.iter().skip(0).next().unwrap(), 77); // revoked to_local
26352641
assert_eq!(*witness_lens.iter().skip(1).next().unwrap(), OFFERED_HTLC_SCRIPT_WEIGHT); // revoked offered HTLC
26362642
assert_eq!(*witness_lens.iter().skip(2).next().unwrap(), ACCEPTED_HTLC_SCRIPT_WEIGHT); // revoked received HTLC
2643+
2644+
// Finally, mine the penalty transactions and check that we get an HTLC failure after
2645+
// ANTI_REORG_DELAY confirmations.
2646+
mine_transaction(&nodes[1], &node_txn[2]);
2647+
mine_transaction(&nodes[1], &node_txn[3]);
2648+
mine_transaction(&nodes[1], &node_txn[4]);
2649+
connect_blocks(&nodes[1], ANTI_REORG_DELAY - 1);
2650+
expect_payment_failed!(nodes[1], payment_hash_2, true);
26372651
}
26382652
get_announce_close_broadcast_events(&nodes, 0, 1);
26392653
assert_eq!(nodes[0].node.list_channels().len(), 0);
@@ -7261,37 +7275,25 @@ fn do_test_sweep_outbound_htlc_failure_update(revoked: bool, local: bool) {
72617275
check_added_monitors!(nodes[0], 1);
72627276
check_closed_event!(nodes[0], 1, ClosureReason::CommitmentTxConfirmed);
72637277
assert_eq!(nodes[0].node.get_and_clear_pending_events().len(), 0);
7278+
72647279
connect_blocks(&nodes[0], TEST_FINAL_CLTV - 1); // Confirm blocks until the HTLC expires
7265-
timeout_tx.push(nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap()[1].clone());
7280+
timeout_tx = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().drain(..)
7281+
.filter(|tx| tx.input[0].previous_output.txid == bs_commitment_tx[0].txid()).collect();
7282+
check_spends!(timeout_tx[0], bs_commitment_tx[0]);
7283+
// For both a revoked or non-revoked commitment transaction, after ANTI_REORG_DELAY the
7284+
// dust HTLC should have been failed.
7285+
expect_payment_failed!(nodes[0], dust_hash, true);
7286+
72667287
if !revoked {
7267-
expect_payment_failed!(nodes[0], dust_hash, true);
72687288
assert_eq!(timeout_tx[0].input[0].witness.last().unwrap().len(), ACCEPTED_HTLC_SCRIPT_WEIGHT);
7269-
// We fail non-dust-HTLC 2 by broadcast of local timeout tx on remote commitment tx
7270-
mine_transaction(&nodes[0], &timeout_tx[0]);
7271-
assert_eq!(nodes[0].node.get_and_clear_pending_events().len(), 0);
7272-
connect_blocks(&nodes[0], ANTI_REORG_DELAY - 1);
7273-
expect_payment_failed!(nodes[0], non_dust_hash, true);
72747289
} else {
7275-
// If revoked, both dust & non-dust HTLCs should have been failed after ANTI_REORG_DELAY confs of revoked
7276-
// commitment tx
7277-
let events = nodes[0].node.get_and_clear_pending_events();
7278-
assert_eq!(events.len(), 2);
7279-
let first;
7280-
match events[0] {
7281-
Event::PaymentPathFailed { payment_hash, .. } => {
7282-
if payment_hash == dust_hash { first = true; }
7283-
else { first = false; }
7284-
},
7285-
_ => panic!("Unexpected event"),
7286-
}
7287-
match events[1] {
7288-
Event::PaymentPathFailed { payment_hash, .. } => {
7289-
if first { assert_eq!(payment_hash, non_dust_hash); }
7290-
else { assert_eq!(payment_hash, dust_hash); }
7291-
},
7292-
_ => panic!("Unexpected event"),
7293-
}
7290+
assert_eq!(timeout_tx[0].lock_time, 0);
72947291
}
7292+
// We fail non-dust-HTLC 2 by broadcast of local timeout/revocation-claim tx
7293+
mine_transaction(&nodes[0], &timeout_tx[0]);
7294+
assert_eq!(nodes[0].node.get_and_clear_pending_events().len(), 0);
7295+
connect_blocks(&nodes[0], ANTI_REORG_DELAY - 1);
7296+
expect_payment_failed!(nodes[0], non_dust_hash, true);
72957297
}
72967298
}
72977299

lightning/src/ln/monitor_tests.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,55 @@ fn chanmon_fail_from_stale_commitment() {
8282
expect_payment_failed_with_update!(nodes[0], payment_hash, false, update_a.contents.short_channel_id, true);
8383
}
8484

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

0 commit comments

Comments
 (0)