diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 6a3c066ce14..c88a7b413c4 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -3771,11 +3771,13 @@ where #[cfg(test)] pub fn create_and_insert_outbound_scid_alias_for_test(&self) -> u64 { - self.create_and_insert_outbound_scid_alias() + self.create_and_insert_outbound_scid_alias_with_namespace( + fake_scid::Namespace::OutboundAlias + ) } #[rustfmt::skip] - fn create_and_insert_outbound_scid_alias(&self) -> u64 { + fn create_and_insert_outbound_scid_alias_with_namespace(&self, namespace: fake_scid::Namespace) -> u64 { let height = self.best_block.read().unwrap().height; let mut outbound_scid_alias = 0; let mut i = 0; @@ -3783,7 +3785,7 @@ where if cfg!(fuzzing) { // fuzzing chacha20 doesn't use the key at all so we always get the same alias outbound_scid_alias += 1; } else { - outbound_scid_alias = fake_scid::Namespace::OutboundAlias.get_fake_scid(height, &self.chain_hash, &self.fake_scid_rand_bytes, &self.entropy_source); + outbound_scid_alias = namespace.get_fake_scid(height, &self.chain_hash, &self.fake_scid_rand_bytes, &self.entropy_source); } if outbound_scid_alias != 0 && self.outbound_scid_aliases.lock().unwrap().insert(outbound_scid_alias) { break; @@ -3794,6 +3796,15 @@ where outbound_scid_alias } + /// Determines the appropriate outbound SCID namespace based on the intercept_htlcs_on_channel configuration. + fn get_outbound_scid_namespace(intercept_htlcs_on_channel: bool) -> fake_scid::Namespace { + if intercept_htlcs_on_channel { + fake_scid::Namespace::Intercept + } else { + fake_scid::Namespace::OutboundAlias + } + } + /// Creates a new outbound channel to the given remote node and with the given value. /// /// `user_channel_id` will be provided back as in @@ -3850,9 +3861,10 @@ where } let mut channel = { - let outbound_scid_alias = self.create_and_insert_outbound_scid_alias(); - let their_features = &peer_state.latest_features; let config = if override_config.is_some() { override_config.as_ref().unwrap() } else { &self.default_configuration }; + let outbound_scid_namespace = Self::get_outbound_scid_namespace(config.channel_handshake_config.intercept_htlcs_on_channel); + let outbound_scid_alias = self.create_and_insert_outbound_scid_alias_with_namespace(outbound_scid_namespace); + let their_features = &peer_state.latest_features; match OutboundV1Channel::new(&self.fee_estimator, &self.entropy_source, &self.signer_provider, their_network_key, their_features, channel_value_satoshis, push_msat, user_channel_id, config, self.best_block.read().unwrap().height, outbound_scid_alias, temporary_channel_id, &*self.logger) @@ -8191,7 +8203,8 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ } // Now that we know we have a channel, assign an outbound SCID alias. - let outbound_scid_alias = self.create_and_insert_outbound_scid_alias(); + let outbound_scid_namespace = Self::get_outbound_scid_namespace(config.channel_handshake_config.intercept_htlcs_on_channel); + let outbound_scid_alias = self.create_and_insert_outbound_scid_alias_with_namespace(outbound_scid_namespace); channel.context_mut().set_outbound_scid_alias(outbound_scid_alias); if let Some(message_send_event) = message_send_event { @@ -8407,7 +8420,8 @@ This indicates a bug inside LDK. Please report this error at https://github.com/ }, }; - let outbound_scid_alias = self.create_and_insert_outbound_scid_alias(); + let outbound_scid_namespace = Self::get_outbound_scid_namespace(self.default_configuration.channel_handshake_config.intercept_htlcs_on_channel); + let outbound_scid_alias = self.create_and_insert_outbound_scid_alias_with_namespace(outbound_scid_namespace); channel.context_mut().set_outbound_scid_alias(outbound_scid_alias); if let Some(message_send_event) = message_send_event { @@ -16303,6 +16317,7 @@ mod tests { to_self_delay: Some(200), max_accepted_htlcs: Some(5), channel_reserve_proportional_millionths: Some(20000), + intercept_htlcs_on_channel: None, }), update_overrides: None, }; diff --git a/lightning/src/ln/functional_tests.rs b/lightning/src/ln/functional_tests.rs index 038bd1da431..401b623aaa0 100644 --- a/lightning/src/ln/functional_tests.rs +++ b/lightning/src/ln/functional_tests.rs @@ -7474,6 +7474,7 @@ pub fn test_manually_accept_inbound_channel_request() { to_self_delay: None, max_accepted_htlcs: Some(3), channel_reserve_proportional_millionths: None, + intercept_htlcs_on_channel: None, }), update_overrides: Some(ChannelConfigUpdate { forwarding_fee_proportional_millionths: None, diff --git a/lightning/src/util/config.rs b/lightning/src/util/config.rs index e98b237691c..93c8ad4210b 100644 --- a/lightning/src/util/config.rs +++ b/lightning/src/util/config.rs @@ -233,6 +233,18 @@ pub struct ChannelHandshakeConfig { /// /// [`max_htlcs`]: crate::ln::chan_utils::max_htlcs pub our_max_accepted_htlcs: u16, + /// If set, this channel's SCID alias will be generated in the intercept namespace, causing + /// all HTLCs sent to this channel to trigger [`Event::HTLCIntercepted`] events instead of + /// being automatically forwarded. This enables manual control over all payments to the channel. + /// + /// Requires [`UserConfig::accept_intercept_htlcs`] to be `true`. Useful for scenarios like + /// fee-taking or conditional forwarding. + /// + /// Default value: `false` + /// + /// [`Event::HTLCIntercepted`]: crate::events::Event::HTLCIntercepted + /// [`UserConfig::accept_intercept_htlcs`]: crate::util::config::UserConfig::accept_intercept_htlcs + pub intercept_htlcs_on_channel: bool, } impl Default for ChannelHandshakeConfig { @@ -250,6 +262,7 @@ impl Default for ChannelHandshakeConfig { #[cfg(test)] negotiate_anchor_zero_fee_commitments: false, our_max_accepted_htlcs: 50, + intercept_htlcs_on_channel: false, } } } @@ -273,6 +286,7 @@ impl Readable for ChannelHandshakeConfig { #[cfg(test)] negotiate_anchor_zero_fee_commitments: Readable::read(reader)?, our_max_accepted_htlcs: Readable::read(reader)?, + intercept_htlcs_on_channel: Readable::read(reader)?, }) } } @@ -1009,6 +1023,9 @@ pub struct ChannelHandshakeConfigUpdate { /// The Proportion of the channel value to configure as counterparty's channel reserve. See /// [`ChannelHandshakeConfig::their_channel_reserve_proportional_millionths`]. pub channel_reserve_proportional_millionths: Option, + /// If set, allows this channel's SCID alias to be generated in the intercept namespace. See + /// [`ChannelHandshakeConfig::intercept_htlcs_on_channel`]. + pub intercept_htlcs_on_channel: Option, } impl ChannelHandshakeConfig { @@ -1039,5 +1056,9 @@ impl ChannelHandshakeConfig { if let Some(channel_reserve) = config.channel_reserve_proportional_millionths { self.their_channel_reserve_proportional_millionths = channel_reserve; } + + if let Some(intercept_htlcs) = config.intercept_htlcs_on_channel { + self.intercept_htlcs_on_channel = intercept_htlcs; + } } }