Skip to content

Commit 88d59ce

Browse files
committed
Avoid duplicate fails from monitor events
In an upcoming commit, we'll fail back an HTLC early to preserve a backwards channel in the event that a forward channel force close doesn't confirm soon before the backwards channel's timeout. After failing back, the forward channel may eventually confirm on-chain, possibly generating another fail back event. Since we may have already failed back this HTLC, before failing we check that the HTLC exists and hasn't already been marked for failure.
1 parent bd989dc commit 88d59ce

File tree

2 files changed

+36
-4
lines changed

2 files changed

+36
-4
lines changed

lightning/src/ln/channel.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2193,6 +2193,20 @@ impl<Signer: WriteableEcdsaChannelSigner> Channel<Signer> {
21932193
self.context.channel_transaction_parameters.funding_outpoint.unwrap()
21942194
}
21952195

2196+
/// Returns whether an inbound HTLC on this channel has already been failed.
2197+
/// Returns None if the HTLC is not found.
2198+
pub fn check_inbound_htlc_failed(&self, htlc_id: u64) -> Option<bool> {
2199+
self.context.pending_inbound_htlcs.iter()
2200+
.find(|htlc| htlc.htlc_id == htlc_id)
2201+
.map(|htlc| match htlc.state {
2202+
InboundHTLCState::LocalRemoved(
2203+
InboundHTLCRemovalReason::FailRelay(_)
2204+
| InboundHTLCRemovalReason::FailMalformed(_)
2205+
) => true,
2206+
_ => false,
2207+
})
2208+
}
2209+
21962210
/// Claims an HTLC while we're disconnected from a peer, dropping the [`ChannelMonitorUpdate`]
21972211
/// entirely.
21982212
///

lightning/src/ln/channelmanager.rs

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6131,10 +6131,28 @@ where
61316131
log_trace!(self.logger, "Claiming HTLC with preimage {} from our monitor", log_bytes!(preimage.0));
61326132
self.claim_funds_internal(htlc_update.source, preimage, htlc_update.htlc_value_satoshis.map(|v| v * 1000), true, funding_outpoint.to_channel_id());
61336133
} else {
6134-
log_trace!(self.logger, "Failing HTLC with hash {} from our monitor", log_bytes!(htlc_update.payment_hash.0));
6135-
let receiver = HTLCDestination::NextHopChannel { node_id: counterparty_node_id, channel_id: funding_outpoint.to_channel_id() };
6136-
let reason = HTLCFailReason::from_failure_code(0x4000 | 8);
6137-
self.fail_htlc_backwards_internal(&htlc_update.source, &htlc_update.payment_hash, &reason, receiver);
6134+
let mut already_failed = false;
6135+
if let HTLCSource::PreviousHopData(HTLCPreviousHopData { htlc_id, ref short_channel_id, .. }) = htlc_update.source {
6136+
let counterparty_and_channel_id = self.short_to_chan_info.read().unwrap()
6137+
.get(short_channel_id).map(|v| v.clone());
6138+
match counterparty_and_channel_id {
6139+
Some((counterparty_node_id, channel_id)) => {
6140+
let per_peer_state = self.per_peer_state.read().unwrap();
6141+
if let Some(peer_state_mutex) = per_peer_state.get(&counterparty_node_id) {
6142+
if let Some(chan) = peer_state_mutex.lock().unwrap().channel_by_id.get(&channel_id) {
6143+
already_failed = chan.check_inbound_htlc_failed(htlc_id).unwrap_or(true);
6144+
}
6145+
}
6146+
},
6147+
None => {},
6148+
}
6149+
}
6150+
if !already_failed {
6151+
log_trace!(self.logger, "Failing HTLC with hash {} from our monitor", log_bytes!(htlc_update.payment_hash.0));
6152+
let receiver = HTLCDestination::NextHopChannel { node_id: counterparty_node_id, channel_id: funding_outpoint.to_channel_id() };
6153+
let reason = HTLCFailReason::from_failure_code(0x4000 | 8);
6154+
self.fail_htlc_backwards_internal(&htlc_update.source, &htlc_update.payment_hash, &reason, receiver);
6155+
}
61386156
}
61396157
},
61406158
MonitorEvent::CommitmentTxConfirmed(funding_outpoint) |

0 commit comments

Comments
 (0)