Skip to content

Commit 706d90d

Browse files
committed
Add per-peer onion message interception API to OnionMessenger
Add `register_peer_for_interception()` and `deregister_peer_for_interception()` methods to `OnionMessenger`, allowing specific peers to be registered for onion message interception without enabling blanket interception for all offline peers. When a registered peer is offline and an onion message needs to be forwarded to them, `Event::OnionMessageIntercepted` is emitted. When a registered peer connects, `Event::OnionMessagePeerConnected` is emitted. This works alongside the existing global `new_with_offline_peer_interception()` flag — if either the global flag is set or the peer is specifically registered, interception occurs. This enables LSPS2 services to intercept onion messages only for peers with active JIT channel sessions, rather than intercepting messages for all offline peers. Co-Authored-By: HAL 9000
1 parent ab31f99 commit 706d90d

1 file changed

Lines changed: 176 additions & 4 deletions

File tree

lightning/src/onion_message/messenger.rs

Lines changed: 176 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)