@@ -122,7 +122,9 @@ use crate::types::features::{
122122};
123123use crate::types::payment::{PaymentHash, PaymentPreimage, PaymentSecret};
124124use crate::types::string::UntrustedString;
125- use crate::util::config::{ChannelConfig, ChannelConfigOverrides, ChannelConfigUpdate, UserConfig};
125+ use crate::util::config::{
126+ ChannelConfig, ChannelConfigOverrides, ChannelConfigUpdate, HTLCInterceptionFlags, UserConfig,
127+ };
126128use crate::util::errors::APIError;
127129use crate::util::logger::{Level, Logger, WithContext};
128130use crate::util::scid_utils::fake_scid;
@@ -4768,27 +4770,53 @@ where
47684770 }
47694771 }
47704772
4771- fn forward_needs_intercept(
4772- &self, outbound_chan: Option<&FundedChannel<SP>>, outgoing_scid: u64,
4773- ) -> bool {
4774- if outbound_chan.is_none() {
4775- if fake_scid::is_valid_intercept(
4776- &self.fake_scid_rand_bytes,
4777- outgoing_scid,
4778- &self.chain_hash,
4779- ) {
4780- if self.config.read().unwrap().accept_intercept_htlcs {
4773+ fn forward_needs_intercept_to_known_chan(&self, outbound_chan: &FundedChannel<SP>) -> bool {
4774+ let intercept_flags = self.config.read().unwrap().htlc_interception_flags;
4775+ if !outbound_chan.context.should_announce() {
4776+ if outbound_chan.context.is_connected() {
4777+ if intercept_flags & (HTLCInterceptionFlags::ToOnlinePrivateChannels as u8) != 0 {
4778+ return true;
4779+ }
4780+ } else {
4781+ if intercept_flags & (HTLCInterceptionFlags::ToOfflinePrivateChannels as u8) != 0 {
47814782 return true;
47824783 }
47834784 }
4785+ } else {
4786+ if intercept_flags & (HTLCInterceptionFlags::ToPublicChannels as u8) != 0 {
4787+ return true;
4788+ }
4789+ }
4790+ false
4791+ }
4792+
4793+ fn forward_needs_intercept_to_unknown_chan(&self, outgoing_scid: u64) -> bool {
4794+ let intercept_flags = self.config.read().unwrap().htlc_interception_flags;
4795+ if fake_scid::is_valid_intercept(
4796+ &self.fake_scid_rand_bytes,
4797+ outgoing_scid,
4798+ &self.chain_hash,
4799+ ) {
4800+ if intercept_flags & (HTLCInterceptionFlags::ToInterceptSCIDs as u8) != 0 {
4801+ return true;
4802+ }
4803+ } else if fake_scid::is_valid_phantom(
4804+ &self.fake_scid_rand_bytes,
4805+ outgoing_scid,
4806+ &self.chain_hash,
4807+ ) {
4808+ // Handled as a normal forward
4809+ } else if intercept_flags & (HTLCInterceptionFlags::ToUnknownSCIDs as u8) != 0 {
4810+ return true;
47844811 }
47854812 false
47864813 }
47874814
47884815 #[rustfmt::skip]
47894816 fn can_forward_htlc_to_outgoing_channel(
4790- &self, chan: &mut FundedChannel<SP>, msg: &msgs::UpdateAddHTLC, next_packet: &NextPacketDetails
4791- ) -> Result<bool, LocalHTLCFailureReason> {
4817+ &self, chan: &mut FundedChannel<SP>, msg: &msgs::UpdateAddHTLC,
4818+ next_packet: &NextPacketDetails, will_intercept: bool,
4819+ ) -> Result<(), LocalHTLCFailureReason> {
47924820 if !chan.context.should_announce()
47934821 && !self.config.read().unwrap().accept_forwards_to_priv_channels
47944822 {
@@ -4797,15 +4825,13 @@ where
47974825 // we don't allow forwards outbound over them.
47984826 return Err(LocalHTLCFailureReason::PrivateChannelForward);
47994827 }
4800- let intercepted;
48014828 if let HopConnector::ShortChannelId(outgoing_scid) = next_packet.outgoing_connector {
48024829 if chan.funding.get_channel_type().supports_scid_privacy() && outgoing_scid != chan.context.outbound_scid_alias() {
48034830 // `option_scid_alias` (referred to in LDK as `scid_privacy`) means
48044831 // "refuse to forward unless the SCID alias was used", so we pretend
48054832 // we don't have the channel here.
48064833 return Err(LocalHTLCFailureReason::RealSCIDForward);
48074834 }
4808- intercepted = self.forward_needs_intercept(Some(chan), outgoing_scid);
48094835 } else {
48104836 return Err(LocalHTLCFailureReason::InvalidTrampolineForward);
48114837 }
@@ -4815,7 +4841,7 @@ where
48154841 // around to doing the actual forward, but better to fail early if we can and
48164842 // hopefully an attacker trying to path-trace payments cannot make this occur
48174843 // on a small/per-node/per-channel scale.
4818- if !intercepted && !chan.context.is_live() {
4844+ if !will_intercept && !chan.context.is_live() {
48194845 if !chan.context.is_enabled() {
48204846 return Err(LocalHTLCFailureReason::ChannelDisabled);
48214847 } else if !chan.context.is_connected() {
@@ -4827,9 +4853,7 @@ where
48274853 if next_packet.outgoing_amt_msat < chan.context.get_counterparty_htlc_minimum_msat() {
48284854 return Err(LocalHTLCFailureReason::AmountBelowMinimum);
48294855 }
4830- chan.htlc_satisfies_config(msg, next_packet.outgoing_amt_msat, next_packet.outgoing_cltv_value)?;
4831-
4832- Ok(intercepted)
4856+ chan.htlc_satisfies_config(msg, next_packet.outgoing_amt_msat, next_packet.outgoing_cltv_value)
48334857 }
48344858
48354859 /// Executes a callback `C` that returns some value `X` on the channel found with the given
@@ -4855,10 +4879,10 @@ where
48554879 }
48564880 }
48574881
4858- fn can_forward_htlc_intercepted (
4859- &self, msg: &msgs::UpdateAddHTLC, next_packet_details : &NextPacketDetails,
4882+ fn can_forward_htlc_should_intercept (
4883+ &self, msg: &msgs::UpdateAddHTLC, next_hop : &NextPacketDetails,
48604884 ) -> Result<bool, LocalHTLCFailureReason> {
4861- let outgoing_scid = match next_packet_details .outgoing_connector {
4885+ let outgoing_scid = match next_hop .outgoing_connector {
48624886 HopConnector::ShortChannelId(scid) => scid,
48634887 HopConnector::Dummy => {
48644888 // Dummy hops are only used for path padding and must not reach HTLC processing.
@@ -4870,22 +4894,23 @@ where
48704894 },
48714895 };
48724896 // TODO: We do the fake SCID namespace check a bunch of times here (and indirectly via
4873- // `forward_needs_intercept `, including as called in
4897+ // `forward_needs_intercept_* `, including as called in
48744898 // `can_forward_htlc_to_outgoing_channel`), we should find a way to reduce the number of
48754899 // times we do it.
48764900 let intercept =
48774901 match self.do_funded_channel_callback(outgoing_scid, |chan: &mut FundedChannel<SP>| {
4878- self.can_forward_htlc_to_outgoing_channel(chan, msg, next_packet_details)
4902+ let intercept = self.forward_needs_intercept_to_known_chan(chan);
4903+ self.can_forward_htlc_to_outgoing_channel(chan, msg, next_hop, intercept)?;
4904+ Ok(intercept)
48794905 }) {
48804906 Some(Ok(intercept)) => intercept,
48814907 Some(Err(e)) => return Err(e),
48824908 None => {
48834909 // Perform basic sanity checks on the amounts and CLTV being forwarded
4884- if next_packet_details .outgoing_amt_msat > msg.amount_msat {
4910+ if next_hop .outgoing_amt_msat > msg.amount_msat {
48854911 return Err(LocalHTLCFailureReason::FeeInsufficient);
48864912 }
4887- let cltv_delta =
4888- msg.cltv_expiry.saturating_sub(next_packet_details.outgoing_cltv_value);
4913+ let cltv_delta = msg.cltv_expiry.saturating_sub(next_hop.outgoing_cltv_value);
48894914 if cltv_delta < MIN_CLTV_EXPIRY_DELTA.into() {
48904915 return Err(LocalHTLCFailureReason::IncorrectCLTVExpiry);
48914916 }
@@ -4896,7 +4921,7 @@ where
48964921 &self.chain_hash,
48974922 ) {
48984923 false
4899- } else if self.forward_needs_intercept(None, outgoing_scid) {
4924+ } else if self.forward_needs_intercept_to_unknown_chan( outgoing_scid) {
49004925 true
49014926 } else {
49024927 return Err(LocalHTLCFailureReason::UnknownNextPeer);
@@ -4905,11 +4930,7 @@ where
49054930 };
49064931
49074932 let cur_height = self.best_block.read().unwrap().height + 1;
4908- check_incoming_htlc_cltv(
4909- cur_height,
4910- next_packet_details.outgoing_cltv_value,
4911- msg.cltv_expiry,
4912- )?;
4933+ check_incoming_htlc_cltv(cur_height, next_hop.outgoing_cltv_value, msg.cltv_expiry)?;
49134934
49144935 Ok(intercept)
49154936 }
@@ -6641,11 +6662,8 @@ where
66416662 /// Intercepted HTLCs can be useful for Lightning Service Providers (LSPs) to open a just-in-time
66426663 /// channel to a receiving node if the node lacks sufficient inbound liquidity.
66436664 ///
6644- /// To make use of intercepted HTLCs, set [`UserConfig::accept_intercept_htlcs`] and use
6645- /// [`ChannelManager::get_intercept_scid`] to generate short channel id(s) to put in the
6646- /// receiver's invoice route hints. These route hints will signal to LDK to generate an
6647- /// [`HTLCIntercepted`] event when it receives the forwarded HTLC, and this method or
6648- /// [`ChannelManager::fail_intercepted_htlc`] MUST be called in response to the event.
6665+ /// To make use of intercepted HTLCs, set [`UserConfig::htlc_interception_flags`] must have a
6666+ /// non-0 value.
66496667 ///
66506668 /// Note that LDK does not enforce fee requirements in `amt_to_forward_msat`, and will not stop
66516669 /// you from forwarding more than you received. See
@@ -6655,7 +6673,7 @@ where
66556673 /// Errors if the event was not handled in time, in which case the HTLC was automatically failed
66566674 /// backwards.
66576675 ///
6658- /// [`UserConfig::accept_intercept_htlcs `]: crate::util::config::UserConfig::accept_intercept_htlcs
6676+ /// [`UserConfig::htlc_interception_flags `]: crate::util::config::UserConfig::htlc_interception_flags
66596677 /// [`HTLCIntercepted`]: events::Event::HTLCIntercepted
66606678 /// [`HTLCIntercepted::expected_outbound_amount_msat`]: events::Event::HTLCIntercepted::expected_outbound_amount_msat
66616679 // TODO: when we move to deciding the best outbound channel at forward time, only take
@@ -6973,7 +6991,9 @@ where
69736991 // Now process the HTLC on the outgoing channel if it's a forward.
69746992 let mut intercept_forward = false;
69756993 if let Some(next_packet_details) = next_packet_details_opt.as_ref() {
6976- match self.can_forward_htlc_intercepted(&update_add_htlc, next_packet_details) {
6994+ match self
6995+ .can_forward_htlc_should_intercept(&update_add_htlc, next_packet_details)
6996+ {
69776997 Err(reason) => {
69786998 fail_htlc_continue_to_next!(reason);
69796999 },
@@ -16329,9 +16349,9 @@ where
1632916349
1633016350 let should_intercept = self
1633116351 .do_funded_channel_callback(next_hop_scid, |chan| {
16332- self.forward_needs_intercept(Some( chan), next_hop_scid )
16352+ self.forward_needs_intercept_to_known_chan( chan)
1633316353 })
16334- .unwrap_or_else(|| self.forward_needs_intercept(None, next_hop_scid));
16354+ .unwrap_or_else(|| self.forward_needs_intercept_to_unknown_chan( next_hop_scid));
1633516355
1633616356 if should_intercept {
1633716357 let intercept_id = InterceptId::from_htlc_id_and_chan_id(
0 commit comments