Skip to content

Commit 5566e07

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 9af5699 commit 5566e07

File tree

2 files changed

+34
-4
lines changed

2 files changed

+34
-4
lines changed

lightning/src/ln/channel.rs

+12
Original file line numberDiff line numberDiff line change
@@ -2193,6 +2193,18 @@ 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(InboundHTLCRemovalReason::FailRelay(_)) => true,
2203+
InboundHTLCState::LocalRemoved(InboundHTLCRemovalReason::FailMalformed(_)) => true,
2204+
_ => false,
2205+
})
2206+
}
2207+
21962208
/// Claims an HTLC while we're disconnected from a peer, dropping the [`ChannelMonitorUpdate`]
21972209
/// entirely.
21982210
///

lightning/src/ln/channelmanager.rs

+22-4
Original file line numberDiff line numberDiff line change
@@ -6215,10 +6215,28 @@ where
62156215
log_trace!(self.logger, "Claiming HTLC with preimage {} from our monitor", log_bytes!(preimage.0));
62166216
self.claim_funds_internal(htlc_update.source, preimage, htlc_update.htlc_value_satoshis.map(|v| v * 1000), true, funding_outpoint.to_channel_id());
62176217
} else {
6218-
log_trace!(self.logger, "Failing HTLC with hash {} from our monitor", log_bytes!(htlc_update.payment_hash.0));
6219-
let receiver = HTLCDestination::NextHopChannel { node_id: counterparty_node_id, channel_id: funding_outpoint.to_channel_id() };
6220-
let reason = HTLCFailReason::from_failure_code(0x4000 | 8);
6221-
self.fail_htlc_backwards_internal(&htlc_update.source, &htlc_update.payment_hash, &reason, receiver);
6218+
let mut already_failed = false;
6219+
if let HTLCSource::PreviousHopData(HTLCPreviousHopData { htlc_id, ref short_channel_id, .. }) = htlc_update.source {
6220+
let counterparty_and_channel_id = self.short_to_chan_info.read().unwrap()
6221+
.get(short_channel_id).map(|v| v.clone());
6222+
match counterparty_and_channel_id {
6223+
Some((counterparty_node_id, channel_id)) => {
6224+
let per_peer_state = self.per_peer_state.read().unwrap();
6225+
if let Some(peer_state_mutex) = per_peer_state.get(&counterparty_node_id) {
6226+
if let Some(chan) = peer_state_mutex.lock().unwrap().channel_by_id.get(&channel_id) {
6227+
already_failed = chan.check_inbound_htlc_failed(htlc_id).unwrap_or(true);
6228+
}
6229+
}
6230+
},
6231+
None => {},
6232+
}
6233+
}
6234+
if !already_failed {
6235+
log_trace!(self.logger, "Failing HTLC with hash {} from our monitor", log_bytes!(htlc_update.payment_hash.0));
6236+
let receiver = HTLCDestination::NextHopChannel { node_id: counterparty_node_id, channel_id: funding_outpoint.to_channel_id() };
6237+
let reason = HTLCFailReason::from_failure_code(0x4000 | 8);
6238+
self.fail_htlc_backwards_internal(&htlc_update.source, &htlc_update.payment_hash, &reason, receiver);
6239+
}
62226240
}
62236241
},
62246242
MonitorEvent::CommitmentTxConfirmed(funding_outpoint) |

0 commit comments

Comments
 (0)