Skip to content

Commit 146daef

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

File tree

2 files changed

+142
-2
lines changed

2 files changed

+142
-2
lines changed

lightning/src/chain/channelmonitor.rs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5502,6 +5502,67 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
55025502
}
55035503
}
55045504

5505+
// Immediate fail-back on stale force-close, regardless of expiry or whether we're allowed to send further updates.
5506+
let current_counterparty_htlcs = if let Some(txid) = self.funding.current_counterparty_commitment_txid {
5507+
if let Some(htlc_outputs) = self.funding.counterparty_claimable_outpoints.get(&txid) {
5508+
Some(htlc_outputs.iter().map(|&(ref a, ref b)| (a, b.as_ref().map(|boxed| &**boxed))))
5509+
} else { None }
5510+
} else { None }.into_iter().flatten();
5511+
5512+
let prev_counterparty_htlcs = if let Some(txid) = self.funding.prev_counterparty_commitment_txid {
5513+
if let Some(htlc_outputs) = self.funding.counterparty_claimable_outpoints.get(&txid) {
5514+
Some(htlc_outputs.iter().map(|&(ref a, ref b)| (a, b.as_ref().map(|boxed| &**boxed))))
5515+
} else { None }
5516+
} else { None }.into_iter().flatten();
5517+
5518+
let htlcs = holder_commitment_htlcs!(self, CURRENT_WITH_SOURCES)
5519+
.chain(current_counterparty_htlcs)
5520+
.chain(prev_counterparty_htlcs);
5521+
5522+
// Group by payment hash so we fail back all identical HTLCs together
5523+
let mut htlcs_by_hash: HashMap<PaymentHash, Vec<(&HTLCOutputInCommitment, &HTLCSource)>> = new_hash_map();
5524+
for (htlc, source_opt) in htlcs {
5525+
if let Some(source) = source_opt {
5526+
htlcs_by_hash.entry(htlc.payment_hash).or_default().push((htlc, source));
5527+
}
5528+
}
5529+
5530+
let monitor_htlc_sources: HashSet<&HTLCSource> = self
5531+
.onchain_events_awaiting_threshold_conf
5532+
.iter()
5533+
.filter_map(|event_entry| match event_entry.event {
5534+
OnchainEvent::HTLCUpdate { ref source, .. } => Some(source),
5535+
_ => None,
5536+
})
5537+
.collect();
5538+
5539+
for (payment_hash, htlc_group) in htlcs_by_hash {
5540+
let is_any_htlc_missing = htlc_group
5541+
.iter()
5542+
.any(|(_, source)| !monitor_htlc_sources.contains(source));
5543+
if is_any_htlc_missing {
5544+
log_info!(logger,
5545+
"Detected stale force-close. Failing back related HTLCs for hash {}.",
5546+
&payment_hash);
5547+
for (htlc, source) in htlc_group {
5548+
if self
5549+
.failed_back_htlc_ids
5550+
.insert(SentHTLCId::from_source(source))
5551+
{
5552+
log_error!(logger,
5553+
"Failing back HTLC for payment {} due to stale close.",
5554+
log_bytes!(payment_hash.0));
5555+
self.pending_monitor_events.push(MonitorEvent::HTLCEvent(HTLCUpdate {
5556+
source: source.clone(),
5557+
payment_preimage: None,
5558+
payment_hash,
5559+
htlc_value_satoshis: Some(htlc.amount_msat / 1000),
5560+
}));
5561+
}
5562+
}
5563+
}
5564+
}
5565+
55055566
if self.no_further_updates_allowed() {
55065567
// Fail back HTLCs on backwards channels if they expire within
55075568
// `LATENCY_GRACE_PERIOD_BLOCKS` blocks and the channel is closed (i.e. we're at a

lightning/src/ln/functional_tests.rs

Lines changed: 81 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,82 @@ 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+
let chanmon_cfgs = create_chanmon_cfgs(3);
9687+
let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
9688+
let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]);
9689+
let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
9690+
9691+
let chan_a_b = create_announced_chan_between_nodes(&nodes, 0, 1);
9692+
let _chan_b_c = create_announced_chan_between_nodes(&nodes, 1, 2);
9693+
9694+
let stale_tx = get_local_commitment_txn!(nodes[0], chan_a_b.2)[0].clone();
9695+
9696+
let (_payment_preimage, payment_hash, payment_secret) = get_payment_preimage_hash!(nodes[2]);
9697+
9698+
let make_event = |pid: PaymentId| {
9699+
let payment_params = PaymentParameters::from_node_id(nodes[2].node.get_our_node_id(), 42);
9700+
let route_params = RouteParameters::from_payment_params_and_value(payment_params, 10_000);
9701+
nodes[0]
9702+
.node
9703+
.send_payment(
9704+
payment_hash,
9705+
RecipientOnionFields::secret_only(payment_secret),
9706+
pid,
9707+
route_params,
9708+
Retry::Attempts(0),
9709+
)
9710+
.unwrap();
9711+
check_added_monitors!(&nodes[0], 1);
9712+
let mut events = nodes[0].node.get_and_clear_pending_msg_events();
9713+
remove_first_msg_event_to_node(&nodes[1].node.get_our_node_id(), &mut events)
9714+
};
9715+
9716+
let first_event = make_event(PaymentId([1; 32]));
9717+
do_pass_along_path(
9718+
PassAlongPathArgs::new(
9719+
&nodes[0],
9720+
&[&nodes[1], &nodes[2]],
9721+
10_000,
9722+
payment_hash,
9723+
first_event,
9724+
)
9725+
.with_payment_secret(payment_secret)
9726+
.without_clearing_recipient_events()
9727+
.without_claimable_event(),
9728+
);
9729+
9730+
let second_event = make_event(PaymentId([2; 32]));
9731+
do_pass_along_path(
9732+
PassAlongPathArgs::new(
9733+
&nodes[0],
9734+
&[&nodes[1], &nodes[2]],
9735+
10_000,
9736+
payment_hash,
9737+
second_event,
9738+
)
9739+
.with_payment_secret(payment_secret)
9740+
.without_clearing_recipient_events()
9741+
.without_claimable_event(),
9742+
);
9743+
9744+
mine_transaction(&nodes[1], &stale_tx);
9745+
check_added_monitors(&nodes[1], 1);
9746+
nodes[1].node.process_pending_htlc_forwards();
9747+
9748+
let events = nodes[1].node.get_and_clear_pending_events();
9749+
let failed = events
9750+
.iter()
9751+
.filter(|e| matches!(e, crate::events::Event::HTLCHandlingFailed { .. }))
9752+
.count();
9753+
assert_eq!(
9754+
failed, 2,
9755+
"ChannelMonitor should immediately surface two HTLC failures after stale close"
9756+
);
9757+
9758+
nodes[1].node.get_and_clear_pending_msg_events();
9759+
nodes[2].node.get_and_clear_pending_events();
9760+
nodes[2].node.get_and_clear_pending_msg_events();
9761+
}

0 commit comments

Comments
 (0)