Skip to content

Commit 42e9ba7

Browse files
committed
fix duplicate HTLC fail-back on stale force-close
1 parent ecce859 commit 42e9ba7

File tree

2 files changed

+232
-7
lines changed

2 files changed

+232
-7
lines changed

lightning/src/chain/channelmonitor.rs

Lines changed: 86 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1287,7 +1287,7 @@ pub(crate) struct ChannelMonitorImpl<Signer: EcdsaChannelSigner> {
12871287
/// a downstream channel force-close remaining unconfirmed by the time the upstream timeout
12881288
/// expires. This is used to tell us we already generated an event to fail this HTLC back
12891289
/// during a previous block scan.
1290-
failed_back_htlc_ids: HashSet<SentHTLCId>,
1290+
failed_back_htlc_ids: HashMap<SentHTLCId, u64>,
12911291

12921292
// The auxiliary HTLC data associated with a holder commitment transaction. This includes
12931293
// non-dust HTLC sources, along with dust HTLCs and their sources. Note that this assumes any
@@ -1885,7 +1885,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
18851885
initial_counterparty_commitment_tx: None,
18861886
balances_empty_height: None,
18871887

1888-
failed_back_htlc_ids: new_hash_set(),
1888+
failed_back_htlc_ids: new_hash_map(),
18891889

18901890
// There are never any HTLCs in the initial commitment transaction
18911891
current_holder_htlc_data: CommitmentHTLCData::new(),
@@ -5435,7 +5435,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
54355435
let mut matured_htlcs = Vec::new();
54365436

54375437
// Produce actionable events from on-chain events having reached their threshold.
5438-
for entry in onchain_events_reaching_threshold_conf {
5438+
for entry in onchain_events_reaching_threshold_conf.clone() {
54395439
match entry.event {
54405440
OnchainEvent::HTLCUpdate { source, payment_hash, htlc_value_satoshis, commitment_tx_output_idx } => {
54415441
// Check for duplicate HTLC resolutions.
@@ -5488,6 +5488,85 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
54885488
OnchainEvent::FundingSpendConfirmation { commitment_tx_to_counterparty_output, .. } => {
54895489
self.funding_spend_confirmed = Some(entry.txid);
54905490
self.confirmed_commitment_tx_counterparty_output = commitment_tx_to_counterparty_output;
5491+
5492+
// Only act on stale force-closes where the confirmed funding spend is the
5493+
// previous counterparty commitment transaction.
5494+
let prev_txid = match self.funding.prev_counterparty_commitment_txid {
5495+
Some(txid) => {
5496+
if txid != entry.txid { continue; }
5497+
txid
5498+
},
5499+
None => { continue; }
5500+
};
5501+
5502+
// Process only HTLCs from that previous counterparty commitment.
5503+
let prev_outputs = if let Some(outputs) =
5504+
self.funding.counterparty_claimable_outpoints.get(&prev_txid) {
5505+
outputs
5506+
} else { continue; };
5507+
5508+
// Build the set of HTLC ids present in the stale (previous) commitment.
5509+
let mut prev_ids: HashSet<SentHTLCId> = new_hash_set();
5510+
for &(_, ref source_opt) in prev_outputs.iter() {
5511+
if let Some(source) = source_opt.as_ref() {
5512+
prev_ids.insert(SentHTLCId::from_source(&**source));
5513+
}
5514+
}
5515+
5516+
// Avoid duplicate fail-back if an HTLCUpdate for this source has already been
5517+
// generated (either matured or still awaiting maturity).
5518+
let mut seen_monitor_htlc_ids: HashSet<SentHTLCId> = new_hash_set();
5519+
for e in onchain_events_reaching_threshold_conf.iter() {
5520+
if let OnchainEvent::HTLCUpdate { source, .. } = &e.event {
5521+
seen_monitor_htlc_ids.insert(SentHTLCId::from_source(source));
5522+
}
5523+
}
5524+
for e in self.onchain_events_awaiting_threshold_conf.iter() {
5525+
if let OnchainEvent::HTLCUpdate { source, .. } = &e.event {
5526+
seen_monitor_htlc_ids.insert(SentHTLCId::from_source(source));
5527+
}
5528+
}
5529+
5530+
// Consider current live HTLCs (holder + current counterparty) with sources,
5531+
// and fail back only those NOT present in the stale (previous) commitment.
5532+
let current_counterparty_iter = if let Some(txid) = self.funding.current_counterparty_commitment_txid {
5533+
if let Some(htlc_outputs) = self.funding.counterparty_claimable_outpoints.get(&txid) {
5534+
Some(htlc_outputs.iter().map(|&(ref a, ref b)| (a, b.as_ref().map(|boxed| &**boxed))))
5535+
} else { None }
5536+
} else { None }.into_iter().flatten();
5537+
let current_htlcs = holder_commitment_htlcs!(self, CURRENT_WITH_SOURCES)
5538+
.chain(current_counterparty_iter);
5539+
5540+
// Count expected duplicates per id from the current view to bound emissions.
5541+
let mut expected_current_counts: HashMap<SentHTLCId, u64> = new_hash_map();
5542+
for (_, source_opt) in current_htlcs.clone() {
5543+
if let Some(source) = source_opt { *expected_current_counts.entry(SentHTLCId::from_source(source)).or_default() += 1; }
5544+
}
5545+
5546+
for (htlc, source_opt) in current_htlcs {
5547+
if let Some(source) = source_opt {
5548+
5549+
let sent_id = SentHTLCId::from_source(source);
5550+
if prev_ids.contains(&sent_id) { continue; }
5551+
if seen_monitor_htlc_ids.contains(&sent_id) { continue; }
5552+
5553+
let already_emitted_count = *self.failed_back_htlc_ids.get(&sent_id).unwrap_or(&0);
5554+
let expected_count = *expected_current_counts.get(&sent_id).unwrap_or(&0);
5555+
5556+
if already_emitted_count < expected_count {
5557+
log_info!(logger,
5558+
"Detected stale force-close. Failing back all HTLCs for hash {}.",
5559+
&htlc.payment_hash);
5560+
self.pending_monitor_events.push(MonitorEvent::HTLCEvent(HTLCUpdate {
5561+
source: source.clone(),
5562+
payment_preimage: None,
5563+
payment_hash: htlc.payment_hash,
5564+
htlc_value_satoshis: Some(htlc.amount_msat / 1000),
5565+
}));
5566+
*self.failed_back_htlc_ids.entry(sent_id).or_default() += 1;
5567+
}
5568+
}
5569+
}
54915570
},
54925571
OnchainEvent::AlternativeFundingConfirmation {} => {
54935572
// An alternative funding transaction has irrevocably confirmed and we're no
@@ -5547,7 +5626,8 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
55475626
if duplicate_event {
55485627
continue;
55495628
}
5550-
if !self.failed_back_htlc_ids.insert(SentHTLCId::from_source(source)) {
5629+
let htlc_id = SentHTLCId::from_source(source);
5630+
if *self.failed_back_htlc_ids.get(&htlc_id).unwrap_or(&0) > 0 {
55515631
continue;
55525632
}
55535633
if !duplicate_event {
@@ -5560,6 +5640,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
55605640
payment_hash: htlc.payment_hash,
55615641
htlc_value_satoshis: Some(htlc.amount_msat / 1000),
55625642
}));
5643+
*self.failed_back_htlc_ids.entry(htlc_id).or_default() += 1;
55635644
}
55645645
}
55655646
}
@@ -6546,7 +6627,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
65466627
initial_counterparty_commitment_info,
65476628
initial_counterparty_commitment_tx,
65486629
balances_empty_height,
6549-
failed_back_htlc_ids: new_hash_set(),
6630+
failed_back_htlc_ids: new_hash_map(),
65506631

65516632
current_holder_htlc_data,
65526633
prev_holder_htlc_data,

lightning/src/ln/functional_tests.rs

Lines changed: 146 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ use crate::ln::channel::{
3232
MIN_CHAN_DUST_LIMIT_SATOSHIS, UNFUNDED_CHANNEL_AGE_LIMIT_TICKS,
3333
};
3434
use crate::ln::channelmanager::{
35-
PaymentId, RAACommitmentOrder, RecipientOnionFields, BREAKDOWN_TIMEOUT, DISABLE_GOSSIP_TICKS,
36-
ENABLE_GOSSIP_TICKS, MIN_CLTV_EXPIRY_DELTA,
35+
PaymentId, RAACommitmentOrder, RecipientOnionFields, Retry, BREAKDOWN_TIMEOUT,
36+
DISABLE_GOSSIP_TICKS, ENABLE_GOSSIP_TICKS, MIN_CLTV_EXPIRY_DELTA,
3737
};
3838
use crate::ln::msgs;
3939
use crate::ln::msgs::{
@@ -9680,3 +9680,147 @@ pub fn test_multi_post_event_actions() {
96809680
do_test_multi_post_event_actions(true);
96819681
do_test_multi_post_event_actions(false);
96829682
}
9683+
9684+
#[xtest(feature = "_externalize_tests")]
9685+
fn test_stale_force_close_with_identical_htlcs() {
9686+
// Test that when two identical HTLCs are relayed and force-closes
9687+
// with a stale state, that we fail both HTLCs back immediately.
9688+
let chanmon_cfgs = create_chanmon_cfgs(4);
9689+
let node_cfgs = create_node_cfgs(4, &chanmon_cfgs);
9690+
let node_chanmgrs = create_node_chanmgrs(4, &node_cfgs, &[None, None, None, None]);
9691+
let mut nodes = create_network(4, &node_cfgs, &node_chanmgrs);
9692+
9693+
let chan_a_b = create_announced_chan_between_nodes(&nodes, 0, 1);
9694+
let _chan_b_c = create_announced_chan_between_nodes(&nodes, 1, 2);
9695+
let _chan_d_b = create_announced_chan_between_nodes(&nodes, 3, 1);
9696+
9697+
let (_payment_preimage, payment_hash, payment_secret) = get_payment_preimage_hash!(nodes[2]);
9698+
9699+
let payment_params = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id(), 100);
9700+
let scorer = test_utils::TestScorer::new();
9701+
let random_seed_bytes = chanmon_cfgs[1].keys_manager.get_secure_random_bytes();
9702+
let route_params = RouteParameters::from_payment_params_and_value(payment_params, 1_000_000);
9703+
9704+
let route = get_route(
9705+
&nodes[0].node.get_our_node_id(),
9706+
&route_params,
9707+
&nodes[0].network_graph.read_only(),
9708+
None,
9709+
nodes[0].logger,
9710+
&scorer,
9711+
&Default::default(),
9712+
&random_seed_bytes,
9713+
)
9714+
.unwrap();
9715+
9716+
nodes[0].router.expect_find_route(route_params.clone(), Ok(route.clone()));
9717+
nodes[0]
9718+
.node
9719+
.send_payment(
9720+
payment_hash,
9721+
RecipientOnionFields::secret_only(payment_secret),
9722+
PaymentId([1; 32]),
9723+
route_params.clone(),
9724+
Retry::Attempts(0),
9725+
)
9726+
.unwrap();
9727+
9728+
let ev1 = remove_first_msg_event_to_node(
9729+
&nodes[1].node.get_our_node_id(),
9730+
&mut nodes[0].node.get_and_clear_pending_msg_events(),
9731+
);
9732+
let mut send_ev1 = SendEvent::from_event(ev1);
9733+
9734+
nodes[1].node.handle_update_add_htlc(nodes[0].node.get_our_node_id(), &send_ev1.msgs[0]);
9735+
nodes[1].node.handle_commitment_signed_batch_test(
9736+
nodes[0].node.get_our_node_id(),
9737+
&send_ev1.commitment_msg,
9738+
);
9739+
9740+
let mut b_events = nodes[1].node.get_and_clear_pending_msg_events();
9741+
for ev in b_events.drain(..) {
9742+
match ev {
9743+
MessageSendEvent::SendRevokeAndACK { node_id, msg } => {
9744+
assert_eq!(node_id, nodes[0].node.get_our_node_id());
9745+
nodes[0].node.handle_revoke_and_ack(nodes[1].node.get_our_node_id(), &msg);
9746+
},
9747+
MessageSendEvent::UpdateHTLCs { node_id, updates, .. } => {
9748+
assert_eq!(node_id, nodes[0].node.get_our_node_id());
9749+
nodes[0].node.handle_commitment_signed_batch_test(
9750+
nodes[1].node.get_our_node_id(),
9751+
&updates.commitment_signed,
9752+
);
9753+
let mut a_events = nodes[0].node.get_and_clear_pending_msg_events();
9754+
for a_ev in a_events.drain(..) {
9755+
if let MessageSendEvent::SendRevokeAndACK { node_id, msg } = a_ev {
9756+
assert_eq!(node_id, nodes[1].node.get_our_node_id());
9757+
nodes[1].node.handle_revoke_and_ack(nodes[0].node.get_our_node_id(), &msg);
9758+
}
9759+
}
9760+
},
9761+
_ => {},
9762+
}
9763+
}
9764+
9765+
nodes[1].node.process_pending_htlc_forwards();
9766+
let _ = nodes[1].node.get_and_clear_pending_msg_events();
9767+
9768+
let stale_commitment_tx = get_local_commitment_txn!(nodes[0], chan_a_b.2)[0].clone();
9769+
9770+
*nodes[0].network_payment_count.borrow_mut() -= 1;
9771+
nodes[0].router.expect_find_route(route_params.clone(), Ok(route.clone()));
9772+
nodes[0]
9773+
.node
9774+
.send_payment(
9775+
payment_hash,
9776+
RecipientOnionFields::secret_only(payment_secret),
9777+
PaymentId([2; 32]),
9778+
route_params.clone(),
9779+
Retry::Attempts(0),
9780+
)
9781+
.unwrap();
9782+
9783+
let ev2 = remove_first_msg_event_to_node(
9784+
&nodes[1].node.get_our_node_id(),
9785+
&mut nodes[0].node.get_and_clear_pending_msg_events(),
9786+
);
9787+
let mut send_ev2 = SendEvent::from_event(ev2);
9788+
9789+
nodes[1].node.handle_update_add_htlc(nodes[0].node.get_our_node_id(), &send_ev2.msgs[0]);
9790+
nodes[1].node.handle_commitment_signed_batch_test(
9791+
nodes[0].node.get_our_node_id(),
9792+
&send_ev2.commitment_msg,
9793+
);
9794+
9795+
let mut b2_events = nodes[1].node.get_and_clear_pending_msg_events();
9796+
for ev in b2_events.drain(..) {
9797+
match ev {
9798+
MessageSendEvent::SendRevokeAndACK { node_id, msg } => {
9799+
assert_eq!(node_id, nodes[0].node.get_our_node_id());
9800+
nodes[0].node.handle_revoke_and_ack(nodes[1].node.get_our_node_id(), &msg);
9801+
},
9802+
MessageSendEvent::UpdateHTLCs { node_id, updates, .. } => {
9803+
assert_eq!(node_id, nodes[0].node.get_our_node_id());
9804+
nodes[0].node.handle_commitment_signed_batch_test(
9805+
nodes[1].node.get_our_node_id(),
9806+
&updates.commitment_signed,
9807+
);
9808+
},
9809+
_ => {},
9810+
}
9811+
}
9812+
9813+
nodes[1].node.process_pending_htlc_forwards();
9814+
let _ = nodes[1].node.get_and_clear_pending_msg_events();
9815+
9816+
mine_transaction(&nodes[1], &stale_commitment_tx);
9817+
connect_blocks(&nodes[1], ANTI_REORG_DELAY);
9818+
9819+
let events = nodes[1].node.get_and_clear_pending_events();
9820+
let failed_count =
9821+
events.iter().filter(|e| matches!(e, Event::HTLCHandlingFailed { .. })).count();
9822+
assert_eq!(failed_count, 2);
9823+
9824+
check_added_monitors!(&nodes[1], 1);
9825+
nodes[1].node.get_and_clear_pending_msg_events();
9826+
}

0 commit comments

Comments
 (0)