Skip to content

Commit f4ab077

Browse files
authored
Merge pull request #1857 from TheBlueMatt/2022-11-reload-htlc
Fail HTLCs which were removed from a channel but not persisted
2 parents de2acc0 + dbe4aad commit f4ab077

File tree

5 files changed

+322
-65
lines changed

5 files changed

+322
-65
lines changed

lightning/src/chain/channelmonitor.rs

Lines changed: 62 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1837,12 +1837,60 @@ impl<Signer: Sign> ChannelMonitor<Signer> {
18371837
res
18381838
}
18391839

1840+
/// Gets the set of outbound HTLCs which can be (or have been) resolved by this
1841+
/// `ChannelMonitor`. This is used to determine if an HTLC was removed from the channel prior
1842+
/// to the `ChannelManager` having been persisted.
1843+
///
1844+
/// This is similar to [`Self::get_pending_outbound_htlcs`] except it includes HTLCs which were
1845+
/// resolved by this `ChannelMonitor`.
1846+
pub(crate) fn get_all_current_outbound_htlcs(&self) -> HashMap<HTLCSource, HTLCOutputInCommitment> {
1847+
let mut res = HashMap::new();
1848+
// Just examine the available counterparty commitment transactions. See docs on
1849+
// `fail_unbroadcast_htlcs`, below, for justification.
1850+
let us = self.inner.lock().unwrap();
1851+
macro_rules! walk_counterparty_commitment {
1852+
($txid: expr) => {
1853+
if let Some(ref latest_outpoints) = us.counterparty_claimable_outpoints.get($txid) {
1854+
for &(ref htlc, ref source_option) in latest_outpoints.iter() {
1855+
if let &Some(ref source) = source_option {
1856+
res.insert((**source).clone(), htlc.clone());
1857+
}
1858+
}
1859+
}
1860+
}
1861+
}
1862+
if let Some(ref txid) = us.current_counterparty_commitment_txid {
1863+
walk_counterparty_commitment!(txid);
1864+
}
1865+
if let Some(ref txid) = us.prev_counterparty_commitment_txid {
1866+
walk_counterparty_commitment!(txid);
1867+
}
1868+
res
1869+
}
1870+
18401871
/// Gets the set of outbound HTLCs which are pending resolution in this channel.
18411872
/// This is used to reconstruct pending outbound payments on restart in the ChannelManager.
18421873
pub(crate) fn get_pending_outbound_htlcs(&self) -> HashMap<HTLCSource, HTLCOutputInCommitment> {
1843-
let mut res = HashMap::new();
18441874
let us = self.inner.lock().unwrap();
1875+
// We're only concerned with the confirmation count of HTLC transactions, and don't
1876+
// actually care how many confirmations a commitment transaction may or may not have. Thus,
1877+
// we look for either a FundingSpendConfirmation event or a funding_spend_confirmed.
1878+
let confirmed_txid = us.funding_spend_confirmed.or_else(|| {
1879+
us.onchain_events_awaiting_threshold_conf.iter().find_map(|event| {
1880+
if let OnchainEvent::FundingSpendConfirmation { .. } = event.event {
1881+
Some(event.txid)
1882+
} else { None }
1883+
})
1884+
});
1885+
1886+
if confirmed_txid.is_none() {
1887+
// If we have not seen a commitment transaction on-chain (ie the channel is not yet
1888+
// closed), just get the full set.
1889+
mem::drop(us);
1890+
return self.get_all_current_outbound_htlcs();
1891+
}
18451892

1893+
let mut res = HashMap::new();
18461894
macro_rules! walk_htlcs {
18471895
($holder_commitment: expr, $htlc_iter: expr) => {
18481896
for (htlc, source) in $htlc_iter {
@@ -1878,54 +1926,22 @@ impl<Signer: Sign> ChannelMonitor<Signer> {
18781926
}
18791927
}
18801928

1881-
// We're only concerned with the confirmation count of HTLC transactions, and don't
1882-
// actually care how many confirmations a commitment transaction may or may not have. Thus,
1883-
// we look for either a FundingSpendConfirmation event or a funding_spend_confirmed.
1884-
let confirmed_txid = us.funding_spend_confirmed.or_else(|| {
1885-
us.onchain_events_awaiting_threshold_conf.iter().find_map(|event| {
1886-
if let OnchainEvent::FundingSpendConfirmation { .. } = event.event {
1887-
Some(event.txid)
1929+
let txid = confirmed_txid.unwrap();
1930+
if Some(txid) == us.current_counterparty_commitment_txid || Some(txid) == us.prev_counterparty_commitment_txid {
1931+
walk_htlcs!(false, us.counterparty_claimable_outpoints.get(&txid).unwrap().iter().filter_map(|(a, b)| {
1932+
if let &Some(ref source) = b {
1933+
Some((a, &**source))
18881934
} else { None }
1889-
})
1890-
});
1891-
if let Some(txid) = confirmed_txid {
1892-
if Some(txid) == us.current_counterparty_commitment_txid || Some(txid) == us.prev_counterparty_commitment_txid {
1893-
walk_htlcs!(false, us.counterparty_claimable_outpoints.get(&txid).unwrap().iter().filter_map(|(a, b)| {
1894-
if let &Some(ref source) = b {
1895-
Some((a, &**source))
1896-
} else { None }
1897-
}));
1898-
} else if txid == us.current_holder_commitment_tx.txid {
1899-
walk_htlcs!(true, us.current_holder_commitment_tx.htlc_outputs.iter().filter_map(|(a, _, c)| {
1935+
}));
1936+
} else if txid == us.current_holder_commitment_tx.txid {
1937+
walk_htlcs!(true, us.current_holder_commitment_tx.htlc_outputs.iter().filter_map(|(a, _, c)| {
1938+
if let Some(source) = c { Some((a, source)) } else { None }
1939+
}));
1940+
} else if let Some(prev_commitment) = &us.prev_holder_signed_commitment_tx {
1941+
if txid == prev_commitment.txid {
1942+
walk_htlcs!(true, prev_commitment.htlc_outputs.iter().filter_map(|(a, _, c)| {
19001943
if let Some(source) = c { Some((a, source)) } else { None }
19011944
}));
1902-
} else if let Some(prev_commitment) = &us.prev_holder_signed_commitment_tx {
1903-
if txid == prev_commitment.txid {
1904-
walk_htlcs!(true, prev_commitment.htlc_outputs.iter().filter_map(|(a, _, c)| {
1905-
if let Some(source) = c { Some((a, source)) } else { None }
1906-
}));
1907-
}
1908-
}
1909-
} else {
1910-
// If we have not seen a commitment transaction on-chain (ie the channel is not yet
1911-
// closed), just examine the available counterparty commitment transactions. See docs
1912-
// on `fail_unbroadcast_htlcs`, below, for justification.
1913-
macro_rules! walk_counterparty_commitment {
1914-
($txid: expr) => {
1915-
if let Some(ref latest_outpoints) = us.counterparty_claimable_outpoints.get($txid) {
1916-
for &(ref htlc, ref source_option) in latest_outpoints.iter() {
1917-
if let &Some(ref source) = source_option {
1918-
res.insert((**source).clone(), htlc.clone());
1919-
}
1920-
}
1921-
}
1922-
}
1923-
}
1924-
if let Some(ref txid) = us.current_counterparty_commitment_txid {
1925-
walk_counterparty_commitment!(txid);
1926-
}
1927-
if let Some(ref txid) = us.prev_counterparty_commitment_txid {
1928-
walk_counterparty_commitment!(txid);
19291945
}
19301946
}
19311947

lightning/src/ln/channel.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5922,15 +5922,16 @@ impl<Signer: Sign> Channel<Signer> {
59225922
(monitor_update, dropped_outbound_htlcs)
59235923
}
59245924

5925-
pub fn inflight_htlc_sources(&self) -> impl Iterator<Item=&HTLCSource> {
5925+
pub fn inflight_htlc_sources(&self) -> impl Iterator<Item=(&HTLCSource, &PaymentHash)> {
59265926
self.holding_cell_htlc_updates.iter()
59275927
.flat_map(|htlc_update| {
59285928
match htlc_update {
5929-
HTLCUpdateAwaitingACK::AddHTLC { source, .. } => { Some(source) }
5930-
_ => None
5929+
HTLCUpdateAwaitingACK::AddHTLC { source, payment_hash, .. }
5930+
=> Some((source, payment_hash)),
5931+
_ => None,
59315932
}
59325933
})
5933-
.chain(self.pending_outbound_htlcs.iter().map(|htlc| &htlc.source))
5934+
.chain(self.pending_outbound_htlcs.iter().map(|htlc| (&htlc.source, &htlc.payment_hash)))
59345935
}
59355936
}
59365937

lightning/src/ln/channelmanager.rs

Lines changed: 60 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5909,7 +5909,7 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
59095909
let mut inflight_htlcs = InFlightHtlcs::new();
59105910

59115911
for chan in self.channel_state.lock().unwrap().by_id.values() {
5912-
for htlc_source in chan.inflight_htlc_sources() {
5912+
for (htlc_source, _) in chan.inflight_htlc_sources() {
59135913
if let HTLCSource::OutboundRoute { path, .. } = htlc_source {
59145914
inflight_htlcs.process_path(path, self.get_our_node_id());
59155915
}
@@ -5927,6 +5927,12 @@ impl<M: Deref, T: Deref, K: Deref, F: Deref, L: Deref> ChannelManager<M, T, K, F
59275927
events.into_inner()
59285928
}
59295929

5930+
#[cfg(test)]
5931+
pub fn pop_pending_event(&self) -> Option<events::Event> {
5932+
let mut events = self.pending_events.lock().unwrap();
5933+
if events.is_empty() { None } else { Some(events.remove(0)) }
5934+
}
5935+
59305936
#[cfg(test)]
59315937
pub fn has_pending_payments(&self) -> bool {
59325938
!self.pending_outbound_payments.lock().unwrap().is_empty()
@@ -7420,6 +7426,25 @@ impl<'a, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
74207426
user_channel_id: channel.get_user_id(),
74217427
reason: ClosureReason::OutdatedChannelManager
74227428
});
7429+
for (channel_htlc_source, payment_hash) in channel.inflight_htlc_sources() {
7430+
let mut found_htlc = false;
7431+
for (monitor_htlc_source, _) in monitor.get_all_current_outbound_htlcs() {
7432+
if *channel_htlc_source == monitor_htlc_source { found_htlc = true; break; }
7433+
}
7434+
if !found_htlc {
7435+
// If we have some HTLCs in the channel which are not present in the newer
7436+
// ChannelMonitor, they have been removed and should be failed back to
7437+
// ensure we don't forget them entirely. Note that if the missing HTLC(s)
7438+
// were actually claimed we'd have generated and ensured the previous-hop
7439+
// claim update ChannelMonitor updates were persisted prior to persising
7440+
// the ChannelMonitor update for the forward leg, so attempting to fail the
7441+
// backwards leg of the HTLC will simply be rejected.
7442+
log_info!(args.logger,
7443+
"Failing HTLC with hash {} as it is missing in the ChannelMonitor for channel {} but was present in the (stale) ChannelManager",
7444+
log_bytes!(channel.channel_id()), log_bytes!(payment_hash.0));
7445+
failed_htlcs.push((channel_htlc_source.clone(), *payment_hash, channel.get_counterparty_node_id(), channel.channel_id()));
7446+
}
7447+
}
74237448
} else {
74247449
log_info!(args.logger, "Successfully loaded channel {}", log_bytes!(channel.channel_id()));
74257450
if let Some(short_channel_id) = channel.get_short_channel_id() {
@@ -7500,16 +7525,6 @@ impl<'a, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
75007525
None => continue,
75017526
}
75027527
}
7503-
if forward_htlcs_count > 0 {
7504-
// If we have pending HTLCs to forward, assume we either dropped a
7505-
// `PendingHTLCsForwardable` or the user received it but never processed it as they
7506-
// shut down before the timer hit. Either way, set the time_forwardable to a small
7507-
// constant as enough time has likely passed that we should simply handle the forwards
7508-
// now, or at least after the user gets a chance to reconnect to our peers.
7509-
pending_events_read.push(events::Event::PendingHTLCsForwardable {
7510-
time_forwardable: Duration::from_secs(2),
7511-
});
7512-
}
75137528

75147529
let background_event_count: u64 = Readable::read(reader)?;
75157530
let mut pending_background_events_read: Vec<BackgroundEvent> = Vec::with_capacity(cmp::min(background_event_count as usize, MAX_ALLOC_SIZE/mem::size_of::<BackgroundEvent>()));
@@ -7620,10 +7635,44 @@ impl<'a, M: Deref, T: Deref, K: Deref, F: Deref, L: Deref>
76207635
}
76217636
}
76227637
}
7638+
for (htlc_source, htlc) in monitor.get_all_current_outbound_htlcs() {
7639+
if let HTLCSource::PreviousHopData(prev_hop_data) = htlc_source {
7640+
// The ChannelMonitor is now responsible for this HTLC's
7641+
// failure/success and will let us know what its outcome is. If we
7642+
// still have an entry for this HTLC in `forward_htlcs`, we were
7643+
// apparently not persisted after the monitor was when forwarding
7644+
// the payment.
7645+
forward_htlcs.retain(|_, forwards| {
7646+
forwards.retain(|forward| {
7647+
if let HTLCForwardInfo::AddHTLC(htlc_info) = forward {
7648+
if htlc_info.prev_short_channel_id == prev_hop_data.short_channel_id &&
7649+
htlc_info.prev_htlc_id == prev_hop_data.htlc_id
7650+
{
7651+
log_info!(args.logger, "Removing pending to-forward HTLC with hash {} as it was forwarded to the closed channel {}",
7652+
log_bytes!(htlc.payment_hash.0), log_bytes!(monitor.get_funding_txo().0.to_channel_id()));
7653+
false
7654+
} else { true }
7655+
} else { true }
7656+
});
7657+
!forwards.is_empty()
7658+
})
7659+
}
7660+
}
76237661
}
76247662
}
76257663
}
76267664

7665+
if !forward_htlcs.is_empty() {
7666+
// If we have pending HTLCs to forward, assume we either dropped a
7667+
// `PendingHTLCsForwardable` or the user received it but never processed it as they
7668+
// shut down before the timer hit. Either way, set the time_forwardable to a small
7669+
// constant as enough time has likely passed that we should simply handle the forwards
7670+
// now, or at least after the user gets a chance to reconnect to our peers.
7671+
pending_events_read.push(events::Event::PendingHTLCsForwardable {
7672+
time_forwardable: Duration::from_secs(2),
7673+
});
7674+
}
7675+
76277676
let inbound_pmt_key_material = args.keys_manager.get_inbound_payment_key_material();
76287677
let expanded_inbound_key = inbound_payment::ExpandedKey::new(&inbound_pmt_key_material);
76297678

lightning/src/ln/functional_test_utils.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1127,7 +1127,7 @@ macro_rules! check_closed_event {
11271127
use $crate::util::events::Event;
11281128

11291129
let events = $node.node.get_and_clear_pending_events();
1130-
assert_eq!(events.len(), $events);
1130+
assert_eq!(events.len(), $events, "{:?}", events);
11311131
let expected_reason = $reason;
11321132
let mut issues_discard_funding = false;
11331133
for event in events {
@@ -1386,7 +1386,7 @@ macro_rules! expect_pending_htlcs_forwardable_conditions {
13861386
let events = $node.node.get_and_clear_pending_events();
13871387
match events[0] {
13881388
$crate::util::events::Event::PendingHTLCsForwardable { .. } => { },
1389-
_ => panic!("Unexpected event"),
1389+
_ => panic!("Unexpected event {:?}", events),
13901390
};
13911391

13921392
let count = expected_failures.len() + 1;
@@ -1596,7 +1596,7 @@ macro_rules! expect_payment_forwarded {
15961596
if !$downstream_force_closed {
15971597
assert!($node.node.list_channels().iter().any(|x| x.counterparty.node_id == $next_node.node.get_our_node_id() && x.channel_id == next_channel_id.unwrap()));
15981598
}
1599-
assert_eq!(claim_from_onchain_tx, $upstream_force_closed);
1599+
assert_eq!(claim_from_onchain_tx, $downstream_force_closed);
16001600
},
16011601
_ => panic!("Unexpected event"),
16021602
}

0 commit comments

Comments
 (0)