Skip to content

Commit c502e8d

Browse files
authored
Merge pull request #1486 from TheBlueMatt/2022-05-revoked-txn-edge-cases
Fix two edge cases in handling of counterparty revoked commitment txn
2 parents f512586 + 70ae45f commit c502e8d

File tree

4 files changed

+200
-48
lines changed

4 files changed

+200
-48
lines changed

lightning/src/chain/channelmonitor.rs

Lines changed: 41 additions & 11 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 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
}
@@ -1659,7 +1662,8 @@ impl<Signer: Sign> ChannelMonitor<Signer> {
16591662
/// as long as we examine both the current counterparty commitment transaction and, if it hasn't
16601663
/// been revoked yet, the previous one, we we will never "forget" to resolve an HTLC.
16611664
macro_rules! fail_unbroadcast_htlcs {
1662-
($self: expr, $commitment_tx_type: expr, $commitment_tx_conf_height: expr, $confirmed_htlcs_list: expr, $logger: expr) => { {
1665+
($self: expr, $commitment_tx_type: expr, $commitment_txid_confirmed: expr,
1666+
$commitment_tx_conf_height: expr, $confirmed_htlcs_list: expr, $logger: expr) => { {
16631667
macro_rules! check_htlc_fails {
16641668
($txid: expr, $commitment_tx: expr) => {
16651669
if let Some(ref latest_outpoints) = $self.counterparty_claimable_outpoints.get($txid) {
@@ -1675,9 +1679,14 @@ macro_rules! fail_unbroadcast_htlcs {
16751679
// cannot currently change after channel initialization, so we don't
16761680
// need to here.
16771681
let confirmed_htlcs_iter: &mut Iterator<Item = (&HTLCOutputInCommitment, Option<&HTLCSource>)> = &mut $confirmed_htlcs_list;
1682+
16781683
let mut matched_htlc = false;
16791684
for (ref broadcast_htlc, ref broadcast_source) in confirmed_htlcs_iter {
1680-
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)) {
16811690
matched_htlc = true;
16821691
break;
16831692
}
@@ -1693,7 +1702,7 @@ macro_rules! fail_unbroadcast_htlcs {
16931702
}
16941703
});
16951704
let entry = OnchainEventEntry {
1696-
txid: *$txid,
1705+
txid: $commitment_txid_confirmed,
16971706
height: $commitment_tx_conf_height,
16981707
event: OnchainEvent::HTLCUpdate {
16991708
source: (**source).clone(),
@@ -1702,8 +1711,9 @@ macro_rules! fail_unbroadcast_htlcs {
17021711
commitment_tx_output_idx: None,
17031712
},
17041713
};
1705-
log_trace!($logger, "Failing HTLC with payment_hash {} from {} counterparty commitment tx due to broadcast of {} commitment transaction, waiting for confirmation (at height {})",
1706-
log_bytes!(htlc.payment_hash.0), $commitment_tx, $commitment_tx_type, entry.confirmation_threshold());
1714+
log_trace!($logger, "Failing HTLC with payment_hash {} from {} counterparty commitment tx due to broadcast of {} commitment transaction {}, waiting for confirmation (at height {})",
1715+
log_bytes!(htlc.payment_hash.0), $commitment_tx, $commitment_tx_type,
1716+
$commitment_txid_confirmed, entry.confirmation_threshold());
17071717
$self.onchain_events_awaiting_threshold_conf.push(entry);
17081718
}
17091719
}
@@ -2100,7 +2110,16 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
21002110
}
21012111
self.counterparty_commitment_txn_on_chain.insert(commitment_txid, commitment_number);
21022112

2103-
fail_unbroadcast_htlcs!(self, "revoked counterparty", height, [].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+
}
21042123
}
21052124
} else if let Some(per_commitment_data) = per_commitment_option {
21062125
// While this isn't useful yet, there is a potential race where if a counterparty
@@ -2116,7 +2135,10 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
21162135
self.counterparty_commitment_txn_on_chain.insert(commitment_txid, commitment_number);
21172136

21182137
log_info!(logger, "Got broadcast of non-revoked counterparty commitment transaction {}", commitment_txid);
2119-
fail_unbroadcast_htlcs!(self, "counterparty", height, per_commitment_data.iter().map(|(a, b)| (a, b.as_ref().map(|b| b.as_ref()))), logger);
2138+
fail_unbroadcast_htlcs!(self, "counterparty", commitment_txid, height,
2139+
per_commitment_data.iter().map(|(htlc, htlc_source)|
2140+
(htlc, htlc_source.as_ref().map(|htlc_source| htlc_source.as_ref()))
2141+
), logger);
21202142

21212143
let htlc_claim_reqs = self.get_counterparty_htlc_output_claim_reqs(commitment_number, commitment_txid, Some(tx));
21222144
for req in htlc_claim_reqs {
@@ -2272,15 +2294,19 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
22722294
let res = self.get_broadcasted_holder_claims(&self.current_holder_commitment_tx, height);
22732295
let mut to_watch = self.get_broadcasted_holder_watch_outputs(&self.current_holder_commitment_tx, tx);
22742296
append_onchain_update!(res, to_watch);
2275-
fail_unbroadcast_htlcs!(self, "latest holder", height, self.current_holder_commitment_tx.htlc_outputs.iter().map(|(a, _, c)| (a, c.as_ref())), logger);
2297+
fail_unbroadcast_htlcs!(self, "latest holder", commitment_txid, height,
2298+
self.current_holder_commitment_tx.htlc_outputs.iter()
2299+
.map(|(htlc, _, htlc_source)| (htlc, htlc_source.as_ref())), logger);
22762300
} else if let &Some(ref holder_tx) = &self.prev_holder_signed_commitment_tx {
22772301
if holder_tx.txid == commitment_txid {
22782302
is_holder_tx = true;
22792303
log_info!(logger, "Got broadcast of previous holder commitment tx {}, searching for available HTLCs to claim", commitment_txid);
22802304
let res = self.get_broadcasted_holder_claims(holder_tx, height);
22812305
let mut to_watch = self.get_broadcasted_holder_watch_outputs(holder_tx, tx);
22822306
append_onchain_update!(res, to_watch);
2283-
fail_unbroadcast_htlcs!(self, "previous holder", height, holder_tx.htlc_outputs.iter().map(|(a, _, c)| (a, c.as_ref())), logger);
2307+
fail_unbroadcast_htlcs!(self, "previous holder", commitment_txid, height,
2308+
holder_tx.htlc_outputs.iter().map(|(htlc, _, htlc_source)| (htlc, htlc_source.as_ref())),
2309+
logger);
22842310
}
22852311
}
22862312

@@ -2561,7 +2587,8 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
25612587
matured_htlcs.push(source.clone());
25622588
}
25632589

2564-
log_debug!(logger, "HTLC {} failure update has got enough confirmations to be passed upstream", log_bytes!(payment_hash.0));
2590+
log_debug!(logger, "HTLC {} failure update in {} has got enough confirmations to be passed upstream",
2591+
log_bytes!(payment_hash.0), entry.txid);
25652592
self.pending_monitor_events.push(MonitorEvent::HTLCEvent(HTLCUpdate {
25662593
payment_hash,
25672594
payment_preimage: None,
@@ -2640,7 +2667,10 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
26402667
F::Target: FeeEstimator,
26412668
L::Target: Logger,
26422669
{
2643-
self.onchain_events_awaiting_threshold_conf.retain(|ref entry| entry.txid != *txid);
2670+
self.onchain_events_awaiting_threshold_conf.retain(|ref entry| if entry.txid == *txid {
2671+
log_info!(logger, "Removing onchain event with txid {}", txid);
2672+
false
2673+
} else { true });
26442674
self.onchain_tx_handler.transaction_unconfirmed(txid, broadcaster, fee_estimator, logger);
26452675
}
26462676

lightning/src/ln/functional_tests.rs

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

25192519
// Rebalance the network to generate htlc in the two directions
2520-
send_payment(&nodes[0], &vec!(&nodes[1])[..], 8000000);
2520+
send_payment(&nodes[0], &[&nodes[1]], 8_000_000);
25212521
// 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
2522-
let payment_preimage_1 = route_payment(&nodes[0], &vec!(&nodes[1])[..], 3000000).0;
2523-
let (_payment_preimage_2, payment_hash_2, _) = route_payment(&nodes[1], &vec!(&nodes[0])[..], 3000000);
2522+
let payment_preimage_1 = route_payment(&nodes[0], &[&nodes[1]], 3_000_000).0;
2523+
let (_payment_preimage_2, payment_hash_2, _) = route_payment(&nodes[1], &[&nodes[0]], 3_000_000);
25242524

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

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

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

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

25832589
// Rebalance the network to generate htlc in the two directions
2584-
send_payment(&nodes[0], &vec!(&nodes[1])[..], 8000000);
2590+
send_payment(&nodes[0], &[&nodes[1]], 8_000_000);
25852591
// 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
25862592
// time as two different claim transactions as we're gonna to timeout htlc with given a high current height
2587-
let payment_preimage_1 = route_payment(&nodes[0], &vec!(&nodes[1])[..], 3000000).0;
2588-
let (_payment_preimage_2, payment_hash_2, _payment_secret_2) = route_payment(&nodes[1], &vec!(&nodes[0])[..], 3000000);
2593+
let payment_preimage_1 = route_payment(&nodes[0], &[&nodes[1]], 3_000_000).0;
2594+
let (_payment_preimage_2, payment_hash_2, _payment_secret_2) = route_payment(&nodes[1], &[&nodes[0]], 3_000_000);
25892595

25902596
// Get the will-be-revoked local txn from node[0]
25912597
let revoked_local_txn = get_local_commitment_txn!(nodes[0], chan_1.2);
@@ -2607,9 +2613,9 @@ fn claim_htlc_outputs_single_tx() {
26072613
}
26082614

26092615
connect_blocks(&nodes[1], ANTI_REORG_DELAY - 1);
2610-
expect_payment_failed!(nodes[1], payment_hash_2, true);
2616+
assert!(nodes[1].node.get_and_clear_pending_events().is_empty());
26112617

2612-
let node_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap();
2618+
let node_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap().split_off(0);
26132619
assert!(node_txn.len() == 9 || node_txn.len() == 10);
26142620

26152621
// Check the pair local commitment and HTLC-timeout broadcast due to HTLC expiration
@@ -2637,6 +2643,14 @@ fn claim_htlc_outputs_single_tx() {
26372643
assert_eq!(*witness_lens.iter().skip(0).next().unwrap(), 77); // revoked to_local
26382644
assert_eq!(*witness_lens.iter().skip(1).next().unwrap(), OFFERED_HTLC_SCRIPT_WEIGHT); // revoked offered HTLC
26392645
assert_eq!(*witness_lens.iter().skip(2).next().unwrap(), ACCEPTED_HTLC_SCRIPT_WEIGHT); // revoked received HTLC
2646+
2647+
// Finally, mine the penalty transactions and check that we get an HTLC failure after
2648+
// ANTI_REORG_DELAY confirmations.
2649+
mine_transaction(&nodes[1], &node_txn[2]);
2650+
mine_transaction(&nodes[1], &node_txn[3]);
2651+
mine_transaction(&nodes[1], &node_txn[4]);
2652+
connect_blocks(&nodes[1], ANTI_REORG_DELAY - 1);
2653+
expect_payment_failed!(nodes[1], payment_hash_2, true);
26402654
}
26412655
get_announce_close_broadcast_events(&nodes, 0, 1);
26422656
assert_eq!(nodes[0].node.list_channels().len(), 0);
@@ -7284,37 +7298,25 @@ fn do_test_sweep_outbound_htlc_failure_update(revoked: bool, local: bool) {
72847298
check_added_monitors!(nodes[0], 1);
72857299
check_closed_event!(nodes[0], 1, ClosureReason::CommitmentTxConfirmed);
72867300
assert_eq!(nodes[0].node.get_and_clear_pending_events().len(), 0);
7301+
72877302
connect_blocks(&nodes[0], TEST_FINAL_CLTV - 1); // Confirm blocks until the HTLC expires
7288-
timeout_tx.push(nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap()[1].clone());
7303+
timeout_tx = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap().drain(..)
7304+
.filter(|tx| tx.input[0].previous_output.txid == bs_commitment_tx[0].txid()).collect();
7305+
check_spends!(timeout_tx[0], bs_commitment_tx[0]);
7306+
// For both a revoked or non-revoked commitment transaction, after ANTI_REORG_DELAY the
7307+
// dust HTLC should have been failed.
7308+
expect_payment_failed!(nodes[0], dust_hash, true);
7309+
72897310
if !revoked {
7290-
expect_payment_failed!(nodes[0], dust_hash, true);
72917311
assert_eq!(timeout_tx[0].input[0].witness.last().unwrap().len(), ACCEPTED_HTLC_SCRIPT_WEIGHT);
7292-
// We fail non-dust-HTLC 2 by broadcast of local timeout tx on remote commitment 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);
72977312
} else {
7298-
// If revoked, both dust & non-dust HTLCs should have been failed after ANTI_REORG_DELAY confs of revoked
7299-
// commitment tx
7300-
let events = nodes[0].node.get_and_clear_pending_events();
7301-
assert_eq!(events.len(), 2);
7302-
let first;
7303-
match events[0] {
7304-
Event::PaymentPathFailed { payment_hash, .. } => {
7305-
if payment_hash == dust_hash { first = true; }
7306-
else { first = false; }
7307-
},
7308-
_ => panic!("Unexpected event"),
7309-
}
7310-
match events[1] {
7311-
Event::PaymentPathFailed { payment_hash, .. } => {
7312-
if first { assert_eq!(payment_hash, non_dust_hash); }
7313-
else { assert_eq!(payment_hash, dust_hash); }
7314-
},
7315-
_ => panic!("Unexpected event"),
7316-
}
7313+
assert_eq!(timeout_tx[0].lock_time, 0);
73177314
}
7315+
// We fail non-dust-HTLC 2 by broadcast of local timeout/revocation-claim tx
7316+
mine_transaction(&nodes[0], &timeout_tx[0]);
7317+
assert_eq!(nodes[0].node.get_and_clear_pending_events().len(), 0);
7318+
connect_blocks(&nodes[0], ANTI_REORG_DELAY - 1);
7319+
expect_payment_failed!(nodes[0], non_dust_hash, true);
73187320
}
73197321
}
73207322

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 HTLCs which were present in a broadcasted remote revoked commitment transaction
100+
// are resolved only after a spend of the HTLC output reaches six confirmations. Preivously
101+
// they would resolve after the revoked commitment transaction itself reaches six
102+
// 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)