@@ -24,10 +24,10 @@ use super::offers::{OffersMessage, OffersMessageHandler};
2424use super :: packet:: { OnionMessageContents , Packet } ;
2525use crate :: blinded_path:: message:: {
2626 AsyncPaymentsContext , BlindedMessagePath , DNSResolverContext , MessageContext ,
27- MessageForwardNode , OffersContext , MESSAGE_PADDING_ROUND_OFF ,
27+ MessageForwardNode , NextMessageHop , OffersContext , MESSAGE_PADDING_ROUND_OFF ,
2828} ;
2929use crate :: blinded_path:: utils:: is_padded;
30- use crate :: blinded_path:: EmptyNodeIdLookUp ;
30+ use crate :: blinded_path:: NodeIdLookUp ;
3131use crate :: events:: { Event , EventsProvider } ;
3232use crate :: ln:: msgs:: { self , BaseMessageHandler , DecodeError , OnionMessageHandler } ;
3333use crate :: routing:: gossip:: { NetworkGraph , P2PGossipSync } ;
@@ -60,17 +60,40 @@ struct MessengerNode {
6060 Arc < TestKeysInterface > ,
6161 Arc < TestNodeSigner > ,
6262 Arc < TestLogger > ,
63- Arc < EmptyNodeIdLookUp > ,
63+ Arc < TestNodeIdLookUp > ,
6464 Arc < MessageRouter > ,
6565 Arc < TestOffersMessageHandler > ,
6666 Arc < TestAsyncPaymentsMessageHandler > ,
6767 Arc < TestDNSResolverMessageHandler > ,
6868 Arc < TestCustomMessageHandler > ,
6969 > ,
70+ node_id_lookup : Arc < TestNodeIdLookUp > ,
7071 custom_message_handler : Arc < TestCustomMessageHandler > ,
7172 gossip_sync : Arc < P2PGossipSync < Arc < NetGraph > , Arc < TestChainSource > , Arc < TestLogger > > > ,
7273}
7374
75+ /// A [`NodeIdLookUp`] that resolves SCIDs to node ids from an insertable map (empty by default,
76+ /// so it behaves like an empty lookup unless a mapping is added).
77+ struct TestNodeIdLookUp {
78+ scid_to_node_id : Mutex < HashMap < u64 , PublicKey > > ,
79+ }
80+
81+ impl TestNodeIdLookUp {
82+ fn new ( ) -> Self {
83+ Self { scid_to_node_id : Mutex :: new ( new_hash_map ( ) ) }
84+ }
85+
86+ fn add_mapping ( & self , scid : u64 , node_id : PublicKey ) {
87+ self . scid_to_node_id . lock ( ) . unwrap ( ) . insert ( scid, node_id) ;
88+ }
89+ }
90+
91+ impl NodeIdLookUp for TestNodeIdLookUp {
92+ fn next_node_id ( & self , short_channel_id : u64 ) -> Option < PublicKey > {
93+ self . scid_to_node_id . lock ( ) . unwrap ( ) . get ( & short_channel_id) . copied ( )
94+ }
95+ }
96+
7497impl Drop for MessengerNode {
7598 fn drop ( & mut self ) {
7699 if std:: thread:: panicking ( ) {
@@ -275,10 +298,15 @@ fn create_nodes(num_messengers: u8) -> Vec<MessengerNode> {
275298struct MessengerCfg {
276299 secret_override : Option < SecretKey > ,
277300 intercept_offline_peer_oms : bool ,
301+ intercept_unknown_scid_oms : bool ,
278302}
279303impl MessengerCfg {
280304 fn new ( ) -> Self {
281- Self { secret_override : None , intercept_offline_peer_oms : false }
305+ Self {
306+ secret_override : None ,
307+ intercept_offline_peer_oms : false ,
308+ intercept_unknown_scid_oms : false ,
309+ }
282310 }
283311 fn with_node_secret ( mut self , secret : SecretKey ) -> Self {
284312 self . secret_override = Some ( secret) ;
@@ -288,6 +316,10 @@ impl MessengerCfg {
288316 self . intercept_offline_peer_oms = true ;
289317 self
290318 }
319+ fn with_unknown_scid_interception ( mut self ) -> Self {
320+ self . intercept_unknown_scid_oms = true ;
321+ self
322+ }
291323}
292324
293325fn create_nodes_using_cfgs ( cfgs : Vec < MessengerCfg > ) -> Vec < MessengerNode > {
@@ -304,31 +336,32 @@ fn create_nodes_using_cfgs(cfgs: Vec<MessengerCfg>) -> Vec<MessengerNode> {
304336 let entropy_source = Arc :: new ( TestKeysInterface :: new ( & seed, Network :: Testnet ) ) ;
305337 let node_signer = Arc :: new ( TestNodeSigner :: new ( secret_key) ) ;
306338
307- let node_id_lookup = Arc :: new ( EmptyNodeIdLookUp { } ) ;
339+ let node_id_lookup = Arc :: new ( TestNodeIdLookUp :: new ( ) ) ;
308340 let message_router =
309341 DefaultMessageRouter :: new ( Arc :: clone ( & network_graph) , Arc :: clone ( & entropy_source) ) ;
310342 let offers_message_handler = Arc :: new ( TestOffersMessageHandler { } ) ;
311343 let async_payments_message_handler = Arc :: new ( TestAsyncPaymentsMessageHandler { } ) ;
312344 let dns_resolver_message_handler = Arc :: new ( TestDNSResolverMessageHandler { } ) ;
313345 let custom_message_handler = Arc :: new ( TestCustomMessageHandler :: new ( ) ) ;
314- let messenger = if cfg. intercept_offline_peer_oms {
346+ let messenger = if cfg. intercept_offline_peer_oms || cfg . intercept_unknown_scid_oms {
315347 OnionMessenger :: new_with_offline_peer_interception (
316348 Arc :: clone ( & entropy_source) ,
317349 Arc :: clone ( & node_signer) ,
318350 logger,
319- node_id_lookup,
351+ Arc :: clone ( & node_id_lookup) ,
320352 Arc :: new ( message_router) ,
321353 offers_message_handler,
322354 async_payments_message_handler,
323355 dns_resolver_message_handler,
324356 Arc :: clone ( & custom_message_handler) ,
357+ cfg. intercept_unknown_scid_oms ,
325358 )
326359 } else {
327360 OnionMessenger :: new (
328361 Arc :: clone ( & entropy_source) ,
329362 Arc :: clone ( & node_signer) ,
330363 logger,
331- node_id_lookup,
364+ Arc :: clone ( & node_id_lookup) ,
332365 Arc :: new ( message_router) ,
333366 offers_message_handler,
334367 async_payments_message_handler,
@@ -341,6 +374,7 @@ fn create_nodes_using_cfgs(cfgs: Vec<MessengerCfg>) -> Vec<MessengerNode> {
341374 node_id : node_signer. get_node_id ( Recipient :: Node ) . unwrap ( ) ,
342375 entropy_source,
343376 messenger,
377+ node_id_lookup,
344378 custom_message_handler,
345379 gossip_sync : Arc :: clone ( & gossip_sync) ,
346380 } ) ;
@@ -1133,9 +1167,14 @@ fn intercept_offline_peer_oms() {
11331167 let mut events = release_events ( & nodes[ 1 ] ) ;
11341168 assert_eq ! ( events. len( ) , 1 ) ;
11351169 let onion_message = match events. remove ( 0 ) {
1136- Event :: OnionMessageIntercepted { peer_node_id, message } => {
1137- assert_eq ! ( peer_node_id, final_node_vec[ 0 ] . node_id) ;
1138- message
1170+ Event :: OnionMessageIntercepted { prev_hop, next_hop, message } => {
1171+ assert_eq ! ( prev_hop, Some ( nodes[ 0 ] . node_id) ) ;
1172+ if let NextMessageHop :: NodeId ( peer_node_id) = next_hop {
1173+ assert_eq ! ( peer_node_id, final_node_vec[ 0 ] . node_id) ;
1174+ message
1175+ } else {
1176+ panic ! ( ) ;
1177+ }
11391178 } ,
11401179 _ => panic ! ( ) ,
11411180 } ;
@@ -1162,6 +1201,130 @@ fn intercept_offline_peer_oms() {
11621201 pass_along_path ( & vec ! [ nodes. remove( 1 ) , final_node_vec. remove( 0 ) ] ) ;
11631202}
11641203
1204+ #[ test]
1205+ fn intercept_unknown_scid_oms ( ) {
1206+ // Ensure that if OnionMessenger is initialized with
1207+ // new_with_offline_peer_interception and `intercept_for_unknown_scids` set, we will
1208+ // intercept OMs that use an unknown SCID as the next hop, generate the right events, and
1209+ // forward OMs when they are re-injected by the user.
1210+ let node_cfgs = vec ! [
1211+ MessengerCfg :: new( ) ,
1212+ MessengerCfg :: new( ) . with_unknown_scid_interception( ) ,
1213+ MessengerCfg :: new( ) ,
1214+ ] ;
1215+ let mut nodes = create_nodes_using_cfgs ( node_cfgs) ;
1216+
1217+ let peer_conn_evs = release_events ( & nodes[ 1 ] ) ;
1218+ assert_eq ! ( peer_conn_evs. len( ) , 2 ) ;
1219+ for ( i, ev) in peer_conn_evs. iter ( ) . enumerate ( ) {
1220+ match ev {
1221+ Event :: OnionMessagePeerConnected { peer_node_id } => {
1222+ let node_idx = if i == 0 { 0 } else { 2 } ;
1223+ assert_eq ! ( peer_node_id, & nodes[ node_idx] . node_id) ;
1224+ } ,
1225+ _ => panic ! ( ) ,
1226+ }
1227+ }
1228+
1229+ // Use a SCID-based intermediate hop to trigger the unknown SCID interception path. Since no
1230+ // mapping was added to the `TestNodeIdLookUp`, the SCID cannot be resolved, so the
1231+ // OnionMessenger will generate an `OnionMessageIntercepted` event with a `ShortChannelId`
1232+ // next hop.
1233+ let scid = 42 ;
1234+ let message = TestCustomMessage :: Pong ;
1235+ let intermediate_nodes =
1236+ [ MessageForwardNode { node_id : nodes[ 1 ] . node_id , short_channel_id : Some ( scid) } ] ;
1237+ let blinded_path = BlindedMessagePath :: new (
1238+ & intermediate_nodes,
1239+ nodes[ 2 ] . node_id ,
1240+ nodes[ 2 ] . messenger . node_signer . get_receive_auth_key ( ) ,
1241+ MessageContext :: Custom ( Vec :: new ( ) ) ,
1242+ false ,
1243+ & * nodes[ 2 ] . entropy_source ,
1244+ & Secp256k1 :: new ( ) ,
1245+ ) ;
1246+ let destination = Destination :: BlindedPath ( blinded_path) ;
1247+ let instructions = MessageSendInstructions :: WithoutReplyPath { destination } ;
1248+
1249+ nodes[ 0 ] . messenger . send_onion_message ( message, instructions) . unwrap ( ) ;
1250+ let mut final_node_vec = nodes. split_off ( 2 ) ;
1251+ pass_along_path ( & nodes) ;
1252+
1253+ // We expect an `OnionMessageIntercepted` event with a `ShortChannelId` next hop since the
1254+ // SCID is not resolvable (no mapping was added to the `TestNodeIdLookUp`).
1255+ let mut events = release_events ( & nodes[ 1 ] ) ;
1256+ assert_eq ! ( events. len( ) , 1 ) ;
1257+ let onion_message = match events. remove ( 0 ) {
1258+ Event :: OnionMessageIntercepted { prev_hop, next_hop, message } => {
1259+ assert_eq ! ( prev_hop, Some ( nodes[ 0 ] . node_id) ) ;
1260+ if let NextMessageHop :: ShortChannelId ( intercepted_scid) = next_hop {
1261+ assert_eq ! ( intercepted_scid, scid) ;
1262+ message
1263+ } else {
1264+ panic ! ( "Expected ShortChannelId next hop, got NodeId" ) ;
1265+ }
1266+ } ,
1267+ _ => panic ! ( ) ,
1268+ } ;
1269+
1270+ // The user resolves the SCID externally and forwards the intercepted message to the
1271+ // correct peer.
1272+ nodes[ 1 ] . messenger . forward_onion_message ( onion_message, & final_node_vec[ 0 ] . node_id ) . unwrap ( ) ;
1273+ final_node_vec[ 0 ] . custom_message_handler . expect_message ( TestCustomMessage :: Pong ) ;
1274+ pass_along_path ( & vec ! [ nodes. remove( 1 ) , final_node_vec. remove( 0 ) ] ) ;
1275+ }
1276+
1277+ #[ test]
1278+ fn intercept_resolved_scid_offline_peer_oms ( ) {
1279+ // Ensure that when a forwarded OM's next hop is a SCID that resolves to a known but offline
1280+ // peer, the offline-peer interception path reports the resolved node id rather than the SCID,
1281+ // even though `intercept_for_unknown_scids` is disabled.
1282+ let node_cfgs = vec ! [
1283+ MessengerCfg :: new( ) ,
1284+ MessengerCfg :: new( ) . with_offline_peer_interception( ) ,
1285+ MessengerCfg :: new( ) ,
1286+ ] ;
1287+ let mut nodes = create_nodes_using_cfgs ( node_cfgs) ;
1288+
1289+ // Clear the initial `OnionMessagePeerConnected` events.
1290+ let _ = release_events ( & nodes[ 1 ] ) ;
1291+
1292+ // Resolve the SCID to nodes[2] and disconnect it so it appears as a known-but-offline peer.
1293+ let scid = 42 ;
1294+ nodes[ 1 ] . node_id_lookup . add_mapping ( scid, nodes[ 2 ] . node_id ) ;
1295+ disconnect_peers ( & nodes[ 1 ] , & nodes[ 2 ] ) ;
1296+
1297+ let message = TestCustomMessage :: Pong ;
1298+ let intermediate_nodes =
1299+ [ MessageForwardNode { node_id : nodes[ 1 ] . node_id , short_channel_id : Some ( scid) } ] ;
1300+ let blinded_path = BlindedMessagePath :: new (
1301+ & intermediate_nodes,
1302+ nodes[ 2 ] . node_id ,
1303+ nodes[ 2 ] . messenger . node_signer . get_receive_auth_key ( ) ,
1304+ MessageContext :: Custom ( Vec :: new ( ) ) ,
1305+ false ,
1306+ & * nodes[ 2 ] . entropy_source ,
1307+ & Secp256k1 :: new ( ) ,
1308+ ) ;
1309+ let destination = Destination :: BlindedPath ( blinded_path) ;
1310+ let instructions = MessageSendInstructions :: WithoutReplyPath { destination } ;
1311+
1312+ nodes[ 0 ] . messenger . send_onion_message ( message, instructions) . unwrap ( ) ;
1313+ let final_node_vec = nodes. split_off ( 2 ) ;
1314+ pass_along_path ( & nodes) ;
1315+
1316+ // The next hop resolved to a known (but offline) peer, so the event must carry its node id
1317+ // rather than the SCID variant (which `intercept_for_unknown_scids` would have produced).
1318+ let mut events = release_events ( & nodes[ 1 ] ) ;
1319+ assert_eq ! ( events. len( ) , 1 ) ;
1320+ match events. remove ( 0 ) {
1321+ Event :: OnionMessageIntercepted { next_hop, .. } => {
1322+ assert_eq ! ( next_hop, NextMessageHop :: NodeId ( final_node_vec[ 0 ] . node_id) ) ;
1323+ } ,
1324+ _ => panic ! ( ) ,
1325+ }
1326+ }
1327+
11651328#[ test]
11661329fn spec_test_vector ( ) {
11671330 let node_cfgs = [
0 commit comments