@@ -125,6 +125,82 @@ impl<
125125 }
126126}
127127
128+ /// A trait for registering peers and SCIDs for onion message interception.
129+ ///
130+ /// When a peer is registered for interception and is currently offline, any onion messages
131+ /// intended to be forwarded to them will generate an [`Event::OnionMessageIntercepted`] instead
132+ /// of being dropped. When a registered peer connects, an [`Event::OnionMessagePeerConnected`]
133+ /// will be generated.
134+ ///
135+ /// Additionally, SCIDs (short channel IDs) can be registered for interception. When an onion
136+ /// message is forwarded with a [`NextMessageHop::ShortChannelId`] that cannot be resolved via
137+ /// [`NodeIdLookUp`] but is registered here, an [`Event::OnionMessageIntercepted`] will be
138+ /// generated using the associated peer's node ID. This enables compact SCID-based encoding in
139+ /// blinded message paths for scenarios like LSPS2 JIT channels where the SCID is a fake
140+ /// intercept SCID that does not correspond to a real channel.
141+ ///
142+ /// [`OnionMessenger`] implements this trait, but it is also useful as a trait object to allow
143+ /// external components (e.g., an LSPS2 service) to register peers for interception without
144+ /// needing to know the concrete [`OnionMessenger`] type.
145+ ///
146+ /// [`NextMessageHop::ShortChannelId`]: crate::blinded_path::message::NextMessageHop::ShortChannelId
147+ /// [`Event::OnionMessageIntercepted`]: crate::events::Event::OnionMessageIntercepted
148+ /// [`Event::OnionMessagePeerConnected`]: crate::events::Event::OnionMessagePeerConnected
149+ pub trait OnionMessageInterceptor {
150+ /// Registers a peer for onion message interception.
151+ ///
152+ /// See [`OnionMessenger::register_peer_for_interception`] for more details.
153+ fn register_peer_for_interception ( & self , peer_node_id : PublicKey ) ;
154+
155+ /// Deregisters a peer from onion message interception.
156+ ///
157+ /// See [`OnionMessenger::deregister_peer_for_interception`] for more details.
158+ ///
159+ /// Returns whether the peer was previously registered.
160+ fn deregister_peer_for_interception ( & self , peer_node_id : & PublicKey ) -> bool ;
161+
162+ /// Registers a short channel ID for onion message interception.
163+ ///
164+ /// See [`OnionMessenger::register_scid_for_interception`] for more details.
165+ fn register_scid_for_interception ( & self , scid : u64 , peer_node_id : PublicKey ) ;
166+
167+ /// Deregisters a short channel ID from onion message interception.
168+ ///
169+ /// See [`OnionMessenger::deregister_scid_for_interception`] for more details.
170+ ///
171+ /// Returns whether the SCID was previously registered.
172+ fn deregister_scid_for_interception ( & self , scid : u64 ) -> bool ;
173+ }
174+
175+ impl <
176+ ES : EntropySource ,
177+ NS : NodeSigner ,
178+ L : Logger ,
179+ NL : NodeIdLookUp ,
180+ MR : MessageRouter ,
181+ OMH : OffersMessageHandler ,
182+ APH : AsyncPaymentsMessageHandler ,
183+ DRH : DNSResolverMessageHandler ,
184+ CMH : CustomOnionMessageHandler ,
185+ > OnionMessageInterceptor for OnionMessenger < ES , NS , L , NL , MR , OMH , APH , DRH , CMH >
186+ {
187+ fn register_peer_for_interception ( & self , peer_node_id : PublicKey ) {
188+ OnionMessenger :: register_peer_for_interception ( self , peer_node_id)
189+ }
190+
191+ fn deregister_peer_for_interception ( & self , peer_node_id : & PublicKey ) -> bool {
192+ OnionMessenger :: deregister_peer_for_interception ( self , peer_node_id)
193+ }
194+
195+ fn register_scid_for_interception ( & self , scid : u64 , peer_node_id : PublicKey ) {
196+ OnionMessenger :: register_scid_for_interception ( self , scid, peer_node_id)
197+ }
198+
199+ fn deregister_scid_for_interception ( & self , scid : u64 ) -> bool {
200+ OnionMessenger :: deregister_scid_for_interception ( self , scid)
201+ }
202+ }
203+
128204/// A sender, receiver and forwarder of [`OnionMessage`]s.
129205///
130206/// # Handling Messages
@@ -273,6 +349,8 @@ pub struct OnionMessenger<
273349 dns_resolver_handler : DRH ,
274350 custom_handler : CMH ,
275351 intercept_messages_for_offline_peers : bool ,
352+ peers_registered_for_interception : Mutex < HashSet < PublicKey > > ,
353+ scids_registered_for_interception : Mutex < HashMap < u64 , PublicKey > > ,
276354 pending_intercepted_msgs_events : Mutex < Vec < Event > > ,
277355 pending_peer_connected_events : Mutex < Vec < Event > > ,
278356 pending_events_processor : AtomicBool ,
@@ -1453,6 +1531,8 @@ impl<
14531531 dns_resolver_handler : dns_resolver,
14541532 custom_handler,
14551533 intercept_messages_for_offline_peers,
1534+ peers_registered_for_interception : Mutex :: new ( new_hash_set ( ) ) ,
1535+ scids_registered_for_interception : Mutex :: new ( new_hash_map ( ) ) ,
14561536 pending_intercepted_msgs_events : Mutex :: new ( Vec :: new ( ) ) ,
14571537 pending_peer_connected_events : Mutex :: new ( Vec :: new ( ) ) ,
14581538 pending_events_processor : AtomicBool :: new ( false ) ,
@@ -1470,6 +1550,65 @@ impl<
14701550 self . async_payments_handler = async_payments_handler;
14711551 }
14721552
1553+ /// Registers a peer for onion message interception.
1554+ ///
1555+ /// When an onion message needs to be forwarded to a registered peer that is currently offline,
1556+ /// an [`Event::OnionMessageIntercepted`] will be generated, allowing the message to be stored
1557+ /// and forwarded later when the peer reconnects.
1558+ ///
1559+ /// Similarly, when a registered peer connects, an [`Event::OnionMessagePeerConnected`] will
1560+ /// be generated.
1561+ ///
1562+ /// This is useful for services like LSPS2 that need to intercept onion messages for specific
1563+ /// peers (e.g., those with active JIT channel sessions) without enabling blanket interception
1564+ /// for all offline peers via [`Self::new_with_offline_peer_interception`].
1565+ ///
1566+ /// Use [`Self::deregister_peer_for_interception`] to stop intercepting messages for this peer.
1567+ ///
1568+ /// [`Event::OnionMessageIntercepted`]: crate::events::Event::OnionMessageIntercepted
1569+ /// [`Event::OnionMessagePeerConnected`]: crate::events::Event::OnionMessagePeerConnected
1570+ pub fn register_peer_for_interception ( & self , peer_node_id : PublicKey ) {
1571+ self . peers_registered_for_interception . lock ( ) . unwrap ( ) . insert ( peer_node_id) ;
1572+ }
1573+
1574+ /// Deregisters a peer from onion message interception.
1575+ ///
1576+ /// After this call, onion messages for this peer will no longer be intercepted (unless
1577+ /// blanket interception is enabled via [`Self::new_with_offline_peer_interception`]).
1578+ ///
1579+ /// Returns whether the peer was previously registered.
1580+ pub fn deregister_peer_for_interception ( & self , peer_node_id : & PublicKey ) -> bool {
1581+ self . peers_registered_for_interception . lock ( ) . unwrap ( ) . remove ( peer_node_id)
1582+ }
1583+
1584+ /// Registers a short channel ID for onion message interception, associating it with
1585+ /// `peer_node_id`.
1586+ ///
1587+ /// When an onion message is forwarded with a [`NextMessageHop::ShortChannelId`] that cannot
1588+ /// be resolved via [`NodeIdLookUp`] but matches a registered SCID, an
1589+ /// [`Event::OnionMessageIntercepted`] will be generated using the associated `peer_node_id`.
1590+ ///
1591+ /// This is useful for services like LSPS2 where fake intercept SCIDs are used in compact
1592+ /// blinded message paths. The SCID does not correspond to a real channel, so
1593+ /// [`NodeIdLookUp`] cannot resolve it, but the message should still be intercepted rather
1594+ /// than dropped.
1595+ ///
1596+ /// Use [`Self::deregister_scid_for_interception`] to stop intercepting messages for this
1597+ /// SCID.
1598+ ///
1599+ /// [`NextMessageHop::ShortChannelId`]: crate::blinded_path::message::NextMessageHop::ShortChannelId
1600+ /// [`Event::OnionMessageIntercepted`]: crate::events::Event::OnionMessageIntercepted
1601+ pub fn register_scid_for_interception ( & self , scid : u64 , peer_node_id : PublicKey ) {
1602+ self . scids_registered_for_interception . lock ( ) . unwrap ( ) . insert ( scid, peer_node_id) ;
1603+ }
1604+
1605+ /// Deregisters a short channel ID from onion message interception.
1606+ ///
1607+ /// Returns whether the SCID was previously registered.
1608+ pub fn deregister_scid_for_interception ( & self , scid : u64 ) -> bool {
1609+ self . scids_registered_for_interception . lock ( ) . unwrap ( ) . remove ( & scid) . is_some ( )
1610+ }
1611+
14731612 /// Sends an [`OnionMessage`] based on its [`MessageSendInstructions`].
14741613 pub fn send_onion_message < T : OnionMessageContents > (
14751614 & self , contents : T , instructions : MessageSendInstructions ,
@@ -1664,8 +1803,30 @@ impl<
16641803 NextMessageHop :: ShortChannelId ( scid) => match self . node_id_lookup . next_node_id ( scid) {
16651804 Some ( pubkey) => pubkey,
16661805 None => {
1667- log_trace ! ( self . logger, "Dropping forwarded onion messager: unable to resolve next hop using SCID {} {}" , scid, log_suffix) ;
1668- return Err ( SendError :: GetNodeIdFailed ) ;
1806+ // The SCID is unknown to NodeIdLookUp (not a real channel). Check
1807+ // if it's registered for SCID-based interception before dropping.
1808+ match self . scids_registered_for_interception . lock ( ) . unwrap ( ) . get ( & scid) . copied ( )
1809+ {
1810+ Some ( peer_node_id) => {
1811+ log_trace ! (
1812+ self . logger,
1813+ "Generating OnionMessageIntercepted event for \
1814+ SCID {} (peer {}) {}",
1815+ scid,
1816+ peer_node_id,
1817+ log_suffix
1818+ ) ;
1819+ self . enqueue_intercepted_event ( Event :: OnionMessageIntercepted {
1820+ peer_node_id,
1821+ message : onion_message,
1822+ } ) ;
1823+ return Ok ( ( ) ) ;
1824+ } ,
1825+ None => {
1826+ log_trace ! ( self . logger, "Dropping forwarded onion message: unable to resolve next hop using SCID {} {}" , scid, log_suffix) ;
1827+ return Err ( SendError :: GetNodeIdFailed ) ;
1828+ } ,
1829+ }
16691830 } ,
16701831 } ,
16711832 } ;
@@ -1686,6 +1847,9 @@ impl<
16861847 . entry ( next_node_id)
16871848 . or_insert_with ( || OnionMessageRecipient :: ConnectedPeer ( VecDeque :: new ( ) ) ) ;
16881849
1850+ let should_intercept = self . intercept_messages_for_offline_peers
1851+ || self . peers_registered_for_interception . lock ( ) . unwrap ( ) . contains ( & next_node_id) ;
1852+
16891853 match message_recipients. entry ( next_node_id) {
16901854 hash_map:: Entry :: Occupied ( mut e)
16911855 if matches ! ( e. get( ) , OnionMessageRecipient :: ConnectedPeer ( ..) ) =>
@@ -1699,7 +1863,7 @@ impl<
16991863 ) ;
17001864 Ok ( ( ) )
17011865 } ,
1702- _ if self . intercept_messages_for_offline_peers => {
1866+ _ if should_intercept => {
17031867 log_trace ! (
17041868 self . logger,
17051869 "Generating OnionMessageIntercepted event for peer {} {}" ,
@@ -2142,7 +2306,15 @@ impl<
21422306 . or_insert_with ( || OnionMessageRecipient :: ConnectedPeer ( VecDeque :: new ( ) ) )
21432307 . mark_connected ( ) ;
21442308 }
2145- if self . intercept_messages_for_offline_peers {
2309+ let is_registered =
2310+ self . peers_registered_for_interception . lock ( ) . unwrap ( ) . contains ( & their_node_id) ;
2311+ let is_registered_by_scid = self
2312+ . scids_registered_for_interception
2313+ . lock ( )
2314+ . unwrap ( )
2315+ . values ( )
2316+ . any ( |nid| * nid == their_node_id) ;
2317+ if self . intercept_messages_for_offline_peers || is_registered || is_registered_by_scid {
21462318 let mut pending_peer_connected_events =
21472319 self . pending_peer_connected_events . lock ( ) . unwrap ( ) ;
21482320 pending_peer_connected_events
0 commit comments