None => { // unknown_next_peer
// Note that this is likely a timing oracle for detecting whether an scid is a
// phantom or an intercept.
- if fake_scid::is_valid_phantom(&self.fake_scid_rand_bytes, *short_channel_id, &self.genesis_hash) ||
- fake_scid::is_valid_intercept(&self.fake_scid_rand_bytes, *short_channel_id, &self.genesis_hash)
+ if (self.default_configuration.accept_intercept_htlcs &&
+ fake_scid::is_valid_intercept(&self.fake_scid_rand_bytes, *short_channel_id, &self.genesis_hash)) ||
+ fake_scid::is_valid_phantom(&self.fake_scid_rand_bytes, *short_channel_id, &self.genesis_hash)
{
None
} else {
/// Intercepted HTLCs can be useful for Lightning Service Providers (LSPs) to open a just-in-time
/// channel to a receiving node if the node lacks sufficient inbound liquidity.
///
- /// To make use of intercepted HTLCs, use [`ChannelManager::get_intercept_scid`] to generate short
- /// channel id(s) to put in the receiver's invoice route hints. These route hints will signal to
- /// LDK to generate an [`HTLCIntercepted`] event when it receives the forwarded HTLC, and this
- /// method or [`ChannelManager::fail_intercepted_htlc`] MUST be called in response to the event.
+ /// To make use of intercepted HTLCs, set [`UserConfig::accept_intercept_htlcs`] and use
+ /// [`ChannelManager::get_intercept_scid`] to generate short channel id(s) to put in the
+ /// receiver's invoice route hints. These route hints will signal to LDK to generate an
+ /// [`HTLCIntercepted`] event when it receives the forwarded HTLC, and this method or
+ /// [`ChannelManager::fail_intercepted_htlc`] MUST be called in response to the event.
///
/// Note that LDK does not enforce fee requirements in `amt_to_forward_msat`, and will not stop
/// you from forwarding more than you received.
///
+ /// Errors if the event was not handled in time, in which case the HTLC was automatically failed
+ /// backwards.
+ ///
+ /// [`UserConfig::accept_intercept_htlcs`]: crate::util::config::UserConfig::accept_intercept_htlcs
/// [`HTLCIntercepted`]: events::Event::HTLCIntercepted
// TODO: when we move to deciding the best outbound channel at forward time, only take
// `next_node_id` and not `next_hop_channel_id`
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
let next_hop_scid = match self.channel_state.lock().unwrap().by_id.get(next_hop_channel_id) {
- Some(chan) => chan.get_short_channel_id().unwrap_or(chan.outbound_scid_alias()),
- None => return Err(APIError::APIMisuseError {
- err: format!("Channel with id {:?} not found", next_hop_channel_id)
+ Some(chan) => {
+ if !chan.is_usable() {
+ return Err(APIError::ChannelUnavailable {
+ err: format!("Channel with id {} not fully established", log_bytes!(*next_hop_channel_id))
+ })
+ }
+ chan.get_short_channel_id().unwrap_or(chan.outbound_scid_alias())
+ },
+ None => return Err(APIError::ChannelUnavailable {
+ err: format!("Channel with id {} not found", log_bytes!(*next_hop_channel_id))
})
};
let payment = self.pending_intercepted_htlcs.lock().unwrap().remove(&intercept_id)
.ok_or_else(|| APIError::APIMisuseError {
- err: format!("Payment with intercept id {:?} not found", intercept_id.0)
+ err: format!("Payment with intercept id {} not found", log_bytes!(intercept_id.0))
})?;
let routing = match payment.forward_info.routing {
/// Fails the intercepted HTLC indicated by intercept_id. Should only be called in response to
/// an [`HTLCIntercepted`] event. See [`ChannelManager::forward_intercepted_htlc`].
///
+ /// Errors if the event was not handled in time, in which case the HTLC was automatically failed
+ /// backwards.
+ ///
/// [`HTLCIntercepted`]: events::Event::HTLCIntercepted
pub fn fail_intercepted_htlc(&self, intercept_id: InterceptId) -> Result<(), APIError> {
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(&self.total_consistency_lock, &self.persistence_notifier);
let payment = self.pending_intercepted_htlcs.lock().unwrap().remove(&intercept_id)
.ok_or_else(|| APIError::APIMisuseError {
- err: format!("Payment with InterceptId {:?} not found", intercept_id)
+ err: format!("Payment with intercept id {} not found", log_bytes!(intercept_id.0))
})?;
if let PendingHTLCRouting::Forward { short_channel_id, .. } = payment.forward_info.routing {
if height >= htlc.cltv_expiry - HTLC_FAIL_BACK_BUFFER {
let mut htlc_msat_height_data = byte_utils::be64_to_array(htlc.value).to_vec();
htlc_msat_height_data.extend_from_slice(&byte_utils::be32_to_array(height));
-
timed_out_htlcs.push((HTLCSource::PreviousHopData(htlc.prev_hop.clone()), payment_hash.clone(), HTLCFailReason::Reason {
failure_code: 0x4000 | 15,
data: htlc_msat_height_data
});
!htlcs.is_empty() // Only retain this entry if htlcs has at least one entry.
});
+
+ let mut intercepted_htlcs = self.pending_intercepted_htlcs.lock().unwrap();
+ intercepted_htlcs.retain(|_, htlc| {
+ if height >= htlc.forward_info.outgoing_cltv_value - HTLC_FAIL_BACK_BUFFER {
+ let prev_hop_data = HTLCSource::PreviousHopData(HTLCPreviousHopData {
+ short_channel_id: htlc.prev_short_channel_id,
+ htlc_id: htlc.prev_htlc_id,
+ incoming_packet_shared_secret: htlc.forward_info.incoming_shared_secret,
+ phantom_shared_secret: None,
+ outpoint: htlc.prev_funding_outpoint,
+ });
+
+ let requested_forward_scid /* intercept scid */ = match htlc.forward_info.routing {
+ PendingHTLCRouting::Forward { short_channel_id, .. } => short_channel_id,
+ _ => unreachable!(),
+ };
+ timed_out_htlcs.push((prev_hop_data, htlc.forward_info.payment_hash,
+ HTLCFailReason::Reason { failure_code: 0x2000 | 2, data: Vec::new() },
+ HTLCDestination::InvalidForward { requested_forward_scid }));
+ log_trace!(self.logger, "Timing out intercepted HTLC with requested forward scid {}", requested_forward_scid);
+ false
+ } else { true }
+ });
}
self.handle_init_event_channel_failures(failed_channels);