@@ -60,7 +60,7 @@ use crate::offers::invoice_error::InvoiceError;
6060use crate :: offers:: invoice_request:: { InvoiceRequest , InvoiceRequestFields , InvoiceRequestVerifiedFromOffer } ;
6161use crate :: offers:: nonce:: Nonce ;
6262use crate :: offers:: parse:: Bolt12SemanticError ;
63- use crate :: onion_message:: messenger:: { DefaultMessageRouter , Destination , MessageSendInstructions , NodeIdMessageRouter , NullMessageRouter , PeeledOnion , PADDED_PATH_LENGTH } ;
63+ use crate :: onion_message:: messenger:: { DefaultMessageRouter , Destination , MessageSendInstructions , NodeIdMessageRouter , NullMessageRouter , PeeledOnion , PADDED_PATH_LENGTH , QR_CODED_PADDED_PATH_LENGTH } ;
6464use crate :: onion_message:: offers:: OffersMessage ;
6565use crate :: routing:: gossip:: { NodeAlias , NodeId } ;
6666use crate :: routing:: router:: { PaymentParameters , RouteParameters , RouteParametersConfig } ;
@@ -163,6 +163,17 @@ fn check_compact_path_introduction_node<'a, 'b, 'c>(
163163 && matches ! ( path. introduction_node( ) , IntroductionNode :: DirectedShortChannelId ( ..) )
164164}
165165
166+ fn check_padded_path_length < ' a , ' b , ' c > (
167+ path : & BlindedMessagePath ,
168+ lookup_node : & Node < ' a , ' b , ' c > ,
169+ expected_introduction_node : PublicKey ,
170+ expected_path_length : usize ,
171+ ) -> bool {
172+ let introduction_node_id = resolve_introduction_node ( lookup_node, path) ;
173+ introduction_node_id == expected_introduction_node
174+ && path. blinded_hops ( ) . len ( ) == expected_path_length
175+ }
176+
166177fn route_bolt12_payment < ' a , ' b , ' c > (
167178 node : & Node < ' a , ' b , ' c > , path : & [ & Node < ' a , ' b , ' c > ] , invoice : & Bolt12Invoice
168179) {
@@ -455,7 +466,7 @@ fn check_dummy_hop_pattern_in_offer() {
455466 let bob_id = bob. node . get_our_node_id ( ) ;
456467
457468 // Case 1: DefaultMessageRouter → uses compact blinded paths (via SCIDs)
458- // Expected: No dummy hops; each path contains only the recipient.
469+ // Expected: Padded to QR_CODED_PADDED_PATH_LENGTH for QR code size optimization
459470 let default_router = DefaultMessageRouter :: new ( alice. network_graph , alice. keys_manager ) ;
460471
461472 let compact_offer = alice. node
@@ -467,8 +478,8 @@ fn check_dummy_hop_pattern_in_offer() {
467478
468479 for path in compact_offer. paths ( ) {
469480 assert_eq ! (
470- path. blinded_hops( ) . len( ) , 1 ,
471- "Compact paths must include only the recipient "
481+ path. blinded_hops( ) . len( ) , QR_CODED_PADDED_PATH_LENGTH ,
482+ "Compact offer paths are padded to QR_CODED_PADDED_PATH_LENGTH "
472483 ) ;
473484 }
474485
@@ -480,10 +491,10 @@ fn check_dummy_hop_pattern_in_offer() {
480491
481492 assert_eq ! ( invoice_request. amount_msats( ) , Some ( 10_000_000 ) ) ;
482493 assert_ne ! ( invoice_request. payer_signing_pubkey( ) , bob_id) ;
483- assert ! ( check_compact_path_introduction_node ( & reply_path, alice, bob_id) ) ;
494+ assert ! ( check_padded_path_length ( & reply_path, alice, bob_id, PADDED_PATH_LENGTH ) ) ;
484495
485496 // Case 2: NodeIdMessageRouter → uses node ID-based blinded paths
486- // Expected: 0 to MAX_DUMMY_HOPS_COUNT dummy hops, followed by recipient.
497+ // Expected: Also padded to QR_CODED_PADDED_PATH_LENGTH for QR code size optimization
487498 let node_id_router = NodeIdMessageRouter :: new ( alice. network_graph , alice. keys_manager ) ;
488499
489500 let padded_offer = alice. node
@@ -492,7 +503,7 @@ fn check_dummy_hop_pattern_in_offer() {
492503 . build ( ) . unwrap ( ) ;
493504
494505 assert ! ( !padded_offer. paths( ) . is_empty( ) ) ;
495- assert ! ( padded_offer. paths( ) . iter( ) . all( |path| path. blinded_hops( ) . len( ) == PADDED_PATH_LENGTH ) ) ;
506+ assert ! ( padded_offer. paths( ) . iter( ) . all( |path| path. blinded_hops( ) . len( ) == QR_CODED_PADDED_PATH_LENGTH ) ) ;
496507
497508 let payment_id = PaymentId ( [ 2 ; 32 ] ) ;
498509 bob. node . pay_for_offer ( & padded_offer, None , payment_id, Default :: default ( ) ) . unwrap ( ) ;
@@ -502,7 +513,7 @@ fn check_dummy_hop_pattern_in_offer() {
502513
503514 assert_eq ! ( invoice_request. amount_msats( ) , Some ( 10_000_000 ) ) ;
504515 assert_ne ! ( invoice_request. payer_signing_pubkey( ) , bob_id) ;
505- assert ! ( check_compact_path_introduction_node ( & reply_path, alice, bob_id) ) ;
516+ assert ! ( check_padded_path_length ( & reply_path, alice, bob_id, PADDED_PATH_LENGTH ) ) ;
506517}
507518
508519/// Checks that blinded paths are compact for short-lived offers.
@@ -687,7 +698,7 @@ fn creates_and_pays_for_offer_using_two_hop_blinded_path() {
687698 } ) ;
688699 assert_eq ! ( invoice_request. amount_msats( ) , Some ( 10_000_000 ) ) ;
689700 assert_ne ! ( invoice_request. payer_signing_pubkey( ) , david_id) ;
690- assert ! ( check_compact_path_introduction_node ( & reply_path, bob, charlie_id) ) ;
701+ assert ! ( check_padded_path_length ( & reply_path, bob, charlie_id, PADDED_PATH_LENGTH ) ) ;
691702
692703 let onion_message = alice. onion_messenger . next_onion_message_for_peer ( charlie_id) . unwrap ( ) ;
693704 charlie. onion_messenger . handle_onion_message ( alice_id, & onion_message) ;
@@ -706,8 +717,8 @@ fn creates_and_pays_for_offer_using_two_hop_blinded_path() {
706717 // to Alice when she's handling the message. Therefore, either Bob or Charlie could
707718 // serve as the introduction node for the reply path back to Alice.
708719 assert ! (
709- check_compact_path_introduction_node ( & reply_path, david, bob_id) ||
710- check_compact_path_introduction_node ( & reply_path, david, charlie_id)
720+ check_padded_path_length ( & reply_path, david, bob_id, PADDED_PATH_LENGTH ) ||
721+ check_padded_path_length ( & reply_path, david, charlie_id, PADDED_PATH_LENGTH )
711722 ) ;
712723
713724 route_bolt12_payment ( david, & [ charlie, bob, alice] , & invoice) ;
@@ -790,7 +801,7 @@ fn creates_and_pays_for_refund_using_two_hop_blinded_path() {
790801 for path in invoice. payment_paths ( ) {
791802 assert_eq ! ( path. introduction_node( ) , & IntroductionNode :: NodeId ( bob_id) ) ;
792803 }
793- assert ! ( check_compact_path_introduction_node ( & reply_path, alice, bob_id) ) ;
804+ assert ! ( check_padded_path_length ( & reply_path, alice, bob_id, PADDED_PATH_LENGTH ) ) ;
794805
795806 route_bolt12_payment ( david, & [ charlie, bob, alice] , & invoice) ;
796807 expect_recent_payment ! ( david, RecentPaymentDetails :: Pending , payment_id) ;
@@ -845,7 +856,7 @@ fn creates_and_pays_for_offer_using_one_hop_blinded_path() {
845856 } ) ;
846857 assert_eq ! ( invoice_request. amount_msats( ) , Some ( 10_000_000 ) ) ;
847858 assert_ne ! ( invoice_request. payer_signing_pubkey( ) , bob_id) ;
848- assert ! ( check_compact_path_introduction_node ( & reply_path, alice, bob_id) ) ;
859+ assert ! ( check_padded_path_length ( & reply_path, alice, bob_id, PADDED_PATH_LENGTH ) ) ;
849860
850861 let onion_message = alice. onion_messenger . next_onion_message_for_peer ( bob_id) . unwrap ( ) ;
851862 bob. onion_messenger . handle_onion_message ( alice_id, & onion_message) ;
@@ -857,7 +868,7 @@ fn creates_and_pays_for_offer_using_one_hop_blinded_path() {
857868 for path in invoice. payment_paths ( ) {
858869 assert_eq ! ( path. introduction_node( ) , & IntroductionNode :: NodeId ( alice_id) ) ;
859870 }
860- assert ! ( check_compact_path_introduction_node ( & reply_path, bob, alice_id) ) ;
871+ assert ! ( check_padded_path_length ( & reply_path, bob, alice_id, PADDED_PATH_LENGTH ) ) ;
861872
862873 route_bolt12_payment ( bob, & [ alice] , & invoice) ;
863874 expect_recent_payment ! ( bob, RecentPaymentDetails :: Pending , payment_id) ;
@@ -913,7 +924,7 @@ fn creates_and_pays_for_refund_using_one_hop_blinded_path() {
913924 for path in invoice. payment_paths ( ) {
914925 assert_eq ! ( path. introduction_node( ) , & IntroductionNode :: NodeId ( alice_id) ) ;
915926 }
916- assert ! ( check_compact_path_introduction_node ( & reply_path, bob, alice_id) ) ;
927+ assert ! ( check_padded_path_length ( & reply_path, bob, alice_id, PADDED_PATH_LENGTH ) ) ;
917928
918929 route_bolt12_payment ( bob, & [ alice] , & invoice) ;
919930 expect_recent_payment ! ( bob, RecentPaymentDetails :: Pending , payment_id) ;
@@ -1059,6 +1070,7 @@ fn send_invoice_requests_with_distinct_reply_path() {
10591070 let bob_id = bob. node . get_our_node_id ( ) ;
10601071 let charlie_id = charlie. node . get_our_node_id ( ) ;
10611072 let david_id = david. node . get_our_node_id ( ) ;
1073+ let frank_id = nodes[ 6 ] . node . get_our_node_id ( ) ;
10621074
10631075 disconnect_peers ( alice, & [ charlie, david, & nodes[ 4 ] , & nodes[ 5 ] , & nodes[ 6 ] ] ) ;
10641076 disconnect_peers ( david, & [ bob, & nodes[ 4 ] , & nodes[ 5 ] ] ) ;
@@ -1089,7 +1101,7 @@ fn send_invoice_requests_with_distinct_reply_path() {
10891101 alice. onion_messenger . handle_onion_message ( bob_id, & onion_message) ;
10901102
10911103 let ( _, reply_path) = extract_invoice_request ( alice, & onion_message) ;
1092- assert ! ( check_compact_path_introduction_node ( & reply_path, alice, charlie_id) ) ;
1104+ assert ! ( check_padded_path_length ( & reply_path, alice, charlie_id, PADDED_PATH_LENGTH ) ) ;
10931105
10941106 // Send, extract and verify the second Invoice Request message
10951107 let onion_message = david. onion_messenger . next_onion_message_for_peer ( bob_id) . unwrap ( ) ;
@@ -1099,7 +1111,7 @@ fn send_invoice_requests_with_distinct_reply_path() {
10991111 alice. onion_messenger . handle_onion_message ( bob_id, & onion_message) ;
11001112
11011113 let ( _, reply_path) = extract_invoice_request ( alice, & onion_message) ;
1102- assert ! ( check_compact_path_introduction_node ( & reply_path, alice, nodes [ 6 ] . node . get_our_node_id ( ) ) ) ;
1114+ assert ! ( check_padded_path_length ( & reply_path, alice, frank_id , PADDED_PATH_LENGTH ) ) ;
11031115}
11041116
11051117/// This test checks that when multiple potential introduction nodes are available for the payee,
@@ -1170,7 +1182,7 @@ fn send_invoice_for_refund_with_distinct_reply_path() {
11701182 let onion_message = bob. onion_messenger . next_onion_message_for_peer ( alice_id) . unwrap ( ) ;
11711183
11721184 let ( _, reply_path) = extract_invoice ( alice, & onion_message) ;
1173- assert ! ( check_compact_path_introduction_node ( & reply_path, alice, charlie_id) ) ;
1185+ assert ! ( check_padded_path_length ( & reply_path, alice, charlie_id, PADDED_PATH_LENGTH ) ) ;
11741186
11751187 // Send, extract and verify the second Invoice Request message
11761188 let onion_message = david. onion_messenger . next_onion_message_for_peer ( bob_id) . unwrap ( ) ;
@@ -1179,7 +1191,7 @@ fn send_invoice_for_refund_with_distinct_reply_path() {
11791191 let onion_message = bob. onion_messenger . next_onion_message_for_peer ( alice_id) . unwrap ( ) ;
11801192
11811193 let ( _, reply_path) = extract_invoice ( alice, & onion_message) ;
1182- assert ! ( check_compact_path_introduction_node ( & reply_path, alice, nodes[ 6 ] . node. get_our_node_id( ) ) ) ;
1194+ assert ! ( check_padded_path_length ( & reply_path, alice, nodes[ 6 ] . node. get_our_node_id( ) , PADDED_PATH_LENGTH ) ) ;
11831195}
11841196
11851197/// Verifies that the invoice request message can be retried if it fails to reach the
@@ -1233,7 +1245,7 @@ fn creates_and_pays_for_offer_with_retry() {
12331245 } ) ;
12341246 assert_eq ! ( invoice_request. amount_msats( ) , Some ( 10_000_000 ) ) ;
12351247 assert_ne ! ( invoice_request. payer_signing_pubkey( ) , bob_id) ;
1236- assert ! ( check_compact_path_introduction_node ( & reply_path, alice, bob_id) ) ;
1248+ assert ! ( check_padded_path_length ( & reply_path, alice, bob_id, PADDED_PATH_LENGTH ) ) ;
12371249 let onion_message = alice. onion_messenger . next_onion_message_for_peer ( bob_id) . unwrap ( ) ;
12381250 bob. onion_messenger . handle_onion_message ( alice_id, & onion_message) ;
12391251
@@ -1534,7 +1546,7 @@ fn fails_authentication_when_handling_invoice_request() {
15341546 let ( invoice_request, reply_path) = extract_invoice_request ( alice, & onion_message) ;
15351547 assert_eq ! ( invoice_request. amount_msats( ) , Some ( 10_000_000 ) ) ;
15361548 assert_ne ! ( invoice_request. payer_signing_pubkey( ) , david_id) ;
1537- assert ! ( check_compact_path_introduction_node ( & reply_path, david, charlie_id) ) ;
1549+ assert ! ( check_padded_path_length ( & reply_path, david, charlie_id, PADDED_PATH_LENGTH ) ) ;
15381550
15391551 assert_eq ! ( alice. onion_messenger. next_onion_message_for_peer( charlie_id) , None ) ;
15401552
@@ -1563,7 +1575,7 @@ fn fails_authentication_when_handling_invoice_request() {
15631575 let ( invoice_request, reply_path) = extract_invoice_request ( alice, & onion_message) ;
15641576 assert_eq ! ( invoice_request. amount_msats( ) , Some ( 10_000_000 ) ) ;
15651577 assert_ne ! ( invoice_request. payer_signing_pubkey( ) , david_id) ;
1566- assert ! ( check_compact_path_introduction_node ( & reply_path, david, charlie_id) ) ;
1578+ assert ! ( check_padded_path_length ( & reply_path, david, charlie_id, PADDED_PATH_LENGTH ) ) ;
15671579
15681580 assert_eq ! ( alice. onion_messenger. next_onion_message_for_peer( charlie_id) , None ) ;
15691581}
@@ -1663,7 +1675,7 @@ fn fails_authentication_when_handling_invoice_for_offer() {
16631675 let ( invoice_request, reply_path) = extract_invoice_request ( alice, & onion_message) ;
16641676 assert_eq ! ( invoice_request. amount_msats( ) , Some ( 10_000_000 ) ) ;
16651677 assert_ne ! ( invoice_request. payer_signing_pubkey( ) , david_id) ;
1666- assert ! ( check_compact_path_introduction_node ( & reply_path, david, charlie_id) ) ;
1678+ assert ! ( check_padded_path_length ( & reply_path, david, charlie_id, PADDED_PATH_LENGTH ) ) ;
16671679
16681680 let onion_message = alice. onion_messenger . next_onion_message_for_peer ( charlie_id) . unwrap ( ) ;
16691681 charlie. onion_messenger . handle_onion_message ( alice_id, & onion_message) ;
0 commit comments