@@ -1434,8 +1434,8 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
1434
1434
features, signing_pubkey,
1435
1435
} ;
1436
1436
1437
- match offer_tlv_stream. node_id {
1438
- Some ( expected_signing_pubkey) => {
1437
+ match ( offer_tlv_stream. node_id , & offer_tlv_stream . paths ) {
1438
+ ( Some ( expected_signing_pubkey) , _ ) => {
1439
1439
if fields. signing_pubkey != expected_signing_pubkey {
1440
1440
return Err ( Bolt12SemanticError :: InvalidSigningPubkey ) ;
1441
1441
}
@@ -1445,7 +1445,21 @@ impl TryFrom<PartialInvoiceTlvStream> for InvoiceContents {
1445
1445
) ?;
1446
1446
Ok ( InvoiceContents :: ForOffer { invoice_request, fields } )
1447
1447
} ,
1448
- None => {
1448
+ ( None , Some ( paths) ) => {
1449
+ if !paths
1450
+ . iter ( )
1451
+ . filter_map ( |path| path. blinded_hops . last ( ) )
1452
+ . any ( |last_hop| fields. signing_pubkey == last_hop. blinded_node_id )
1453
+ {
1454
+ return Err ( Bolt12SemanticError :: InvalidSigningPubkey ) ;
1455
+ }
1456
+
1457
+ let invoice_request = InvoiceRequestContents :: try_from (
1458
+ ( payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream)
1459
+ ) ?;
1460
+ Ok ( InvoiceContents :: ForOffer { invoice_request, fields } )
1461
+ } ,
1462
+ ( None , None ) => {
1449
1463
let refund = RefundContents :: try_from (
1450
1464
( payer_tlv_stream, offer_tlv_stream, invoice_request_tlv_stream)
1451
1465
) ?;
@@ -1463,7 +1477,7 @@ mod tests {
1463
1477
use bitcoin:: blockdata:: script:: ScriptBuf ;
1464
1478
use bitcoin:: hashes:: Hash ;
1465
1479
use bitcoin:: network:: constants:: Network ;
1466
- use bitcoin:: secp256k1:: { Message , Secp256k1 , XOnlyPublicKey , self } ;
1480
+ use bitcoin:: secp256k1:: { KeyPair , Message , Secp256k1 , SecretKey , XOnlyPublicKey , self } ;
1467
1481
use bitcoin:: address:: { Address , Payload , WitnessProgram , WitnessVersion } ;
1468
1482
use bitcoin:: key:: TweakedPublicKey ;
1469
1483
@@ -2366,6 +2380,81 @@ mod tests {
2366
2380
}
2367
2381
}
2368
2382
2383
+ #[ test]
2384
+ fn parses_invoice_with_node_id_from_blinded_path ( ) {
2385
+ let paths = vec ! [
2386
+ BlindedPath {
2387
+ introduction_node: IntroductionNode :: NodeId ( pubkey( 40 ) ) ,
2388
+ blinding_point: pubkey( 41 ) ,
2389
+ blinded_hops: vec![
2390
+ BlindedHop { blinded_node_id: pubkey( 43 ) , encrypted_payload: vec![ 0 ; 43 ] } ,
2391
+ BlindedHop { blinded_node_id: pubkey( 44 ) , encrypted_payload: vec![ 0 ; 44 ] } ,
2392
+ ] ,
2393
+ } ,
2394
+ BlindedPath {
2395
+ introduction_node: IntroductionNode :: NodeId ( pubkey( 40 ) ) ,
2396
+ blinding_point: pubkey( 41 ) ,
2397
+ blinded_hops: vec![
2398
+ BlindedHop { blinded_node_id: pubkey( 45 ) , encrypted_payload: vec![ 0 ; 45 ] } ,
2399
+ BlindedHop { blinded_node_id: pubkey( 46 ) , encrypted_payload: vec![ 0 ; 46 ] } ,
2400
+ ] ,
2401
+ } ,
2402
+ ] ;
2403
+
2404
+ let blinded_node_id_sign = |message : & UnsignedBolt12Invoice | {
2405
+ let secp_ctx = Secp256k1 :: new ( ) ;
2406
+ let keys = KeyPair :: from_secret_key ( & secp_ctx, & SecretKey :: from_slice ( & [ 46 ; 32 ] ) . unwrap ( ) ) ;
2407
+ Ok ( secp_ctx. sign_schnorr_no_aux_rand ( message. as_ref ( ) . as_digest ( ) , & keys) )
2408
+ } ;
2409
+
2410
+ let invoice = OfferBuilder :: new ( "foo" . into ( ) , recipient_pubkey ( ) )
2411
+ . clear_signing_pubkey ( )
2412
+ . amount_msats ( 1000 )
2413
+ . path ( paths[ 0 ] . clone ( ) )
2414
+ . path ( paths[ 1 ] . clone ( ) )
2415
+ . build ( ) . unwrap ( )
2416
+ . request_invoice ( vec ! [ 1 ; 32 ] , payer_pubkey ( ) ) . unwrap ( )
2417
+ . build ( ) . unwrap ( )
2418
+ . sign ( payer_sign) . unwrap ( )
2419
+ . respond_with_no_std_using_signing_pubkey (
2420
+ payment_paths ( ) , payment_hash ( ) , now ( ) , pubkey ( 46 )
2421
+ ) . unwrap ( )
2422
+ . build ( ) . unwrap ( )
2423
+ . sign ( blinded_node_id_sign) . unwrap ( ) ;
2424
+
2425
+ let mut buffer = Vec :: new ( ) ;
2426
+ invoice. write ( & mut buffer) . unwrap ( ) ;
2427
+
2428
+ if let Err ( e) = Bolt12Invoice :: try_from ( buffer) {
2429
+ panic ! ( "error parsing invoice: {:?}" , e) ;
2430
+ }
2431
+
2432
+ let invoice = OfferBuilder :: new ( "foo" . into ( ) , recipient_pubkey ( ) )
2433
+ . clear_signing_pubkey ( )
2434
+ . amount_msats ( 1000 )
2435
+ . path ( paths[ 0 ] . clone ( ) )
2436
+ . path ( paths[ 1 ] . clone ( ) )
2437
+ . build ( ) . unwrap ( )
2438
+ . request_invoice ( vec ! [ 1 ; 32 ] , payer_pubkey ( ) ) . unwrap ( )
2439
+ . build ( ) . unwrap ( )
2440
+ . sign ( payer_sign) . unwrap ( )
2441
+ . respond_with_no_std_using_signing_pubkey (
2442
+ payment_paths ( ) , payment_hash ( ) , now ( ) , recipient_pubkey ( )
2443
+ ) . unwrap ( )
2444
+ . build ( ) . unwrap ( )
2445
+ . sign ( recipient_sign) . unwrap ( ) ;
2446
+
2447
+ let mut buffer = Vec :: new ( ) ;
2448
+ invoice. write ( & mut buffer) . unwrap ( ) ;
2449
+
2450
+ match Bolt12Invoice :: try_from ( buffer) {
2451
+ Ok ( _) => panic ! ( "expected error" ) ,
2452
+ Err ( e) => {
2453
+ assert_eq ! ( e, Bolt12ParseError :: InvalidSemantics ( Bolt12SemanticError :: InvalidSigningPubkey ) ) ;
2454
+ } ,
2455
+ }
2456
+ }
2457
+
2369
2458
#[ test]
2370
2459
fn fails_parsing_invoice_without_signature ( ) {
2371
2460
let mut buffer = Vec :: new ( ) ;
0 commit comments