@@ -165,6 +165,12 @@ fn check_compact_path_introduction_node<'a, 'b, 'c>(
165165
166166fn route_bolt12_payment < ' a , ' b , ' c > (
167167 node : & Node < ' a , ' b , ' c > , path : & [ & Node < ' a , ' b , ' c > ] , invoice : & Bolt12Invoice
168+ ) {
169+ route_bolt12_payment_with_custom_tlvs ( node, path, invoice, Vec :: new ( ) ) ;
170+ }
171+
172+ fn route_bolt12_payment_with_custom_tlvs < ' a , ' b , ' c > (
173+ node : & Node < ' a , ' b , ' c > , path : & [ & Node < ' a , ' b , ' c > ] , invoice : & Bolt12Invoice , custom_tlvs : Vec < ( u64 , Vec < u8 > ) >
168174) {
169175 // Monitor added when handling the invoice onion message.
170176 check_added_monitors ( node, 1 ) ;
@@ -178,7 +184,8 @@ fn route_bolt12_payment<'a, 'b, 'c>(
178184 let amount_msats = invoice. amount_msats ( ) ;
179185 let payment_hash = invoice. payment_hash ( ) ;
180186 let args = PassAlongPathArgs :: new ( node, path, amount_msats, payment_hash, ev)
181- . without_clearing_recipient_events ( ) ;
187+ . without_clearing_recipient_events ( )
188+ . with_custom_tlvs ( custom_tlvs) ;
182189 do_pass_along_path ( args) ;
183190}
184191
@@ -1348,6 +1355,128 @@ fn pays_bolt12_invoice_asynchronously() {
13481355 ) ;
13491356}
13501357
1358+ /// Checks that a deferred invoice can be paid asynchronously from an Event::InvoiceReceived.
1359+ #[ test]
1360+ fn pays_bolt12_invoice_asynchronously_with_custom_tlvs ( ) {
1361+ let mut manually_pay_cfg = test_default_channel_config ( ) ;
1362+ manually_pay_cfg. manually_handle_bolt12_invoices = true ;
1363+
1364+ let chanmon_cfgs = create_chanmon_cfgs ( 2 ) ;
1365+ let node_cfgs = create_node_cfgs ( 2 , & chanmon_cfgs) ;
1366+ let node_chanmgrs = create_node_chanmgrs ( 2 , & node_cfgs, & [ None , Some ( manually_pay_cfg) ] ) ;
1367+ let nodes = create_network ( 2 , & node_cfgs, & node_chanmgrs) ;
1368+
1369+ create_announced_chan_between_nodes_with_value ( & nodes, 0 , 1 , 10_000_000 , 1_000_000_000 ) ;
1370+
1371+ let alice = & nodes[ 0 ] ;
1372+ let bob = & nodes[ 1 ] ;
1373+ let alice_id = alice. node . get_our_node_id ( ) ;
1374+ let bob_id = bob. node . get_our_node_id ( ) ;
1375+
1376+ let offer = alice. node
1377+ . create_offer_builder ( ) . unwrap ( )
1378+ . amount_msats ( 10_000_000 )
1379+ . build ( ) . unwrap ( ) ;
1380+
1381+ const CUSTOM_TLV_TYPE : u64 = 65537 ;
1382+ let custom_tlvs = vec ! [ ( CUSTOM_TLV_TYPE , vec![ 42 ; 42 ] ) ] ;
1383+ let payment_id = PaymentId ( [ 1 ; 32 ] ) ;
1384+
1385+ bob. node
1386+ . pay_for_offer ( & offer, None , payment_id, custom_tlvs. clone ( ) , Default :: default ( ) )
1387+ . unwrap ( ) ;
1388+ expect_recent_payment ! ( bob, RecentPaymentDetails :: AwaitingInvoice , payment_id) ;
1389+
1390+ let onion_message = bob. onion_messenger . next_onion_message_for_peer ( alice_id) . unwrap ( ) ;
1391+ alice. onion_messenger . handle_onion_message ( bob_id, & onion_message) ;
1392+
1393+ let ( invoice_request, _) = extract_invoice_request ( alice, & onion_message) ;
1394+ let expected_payment_context = PaymentContext :: Bolt12Offer ( Bolt12OfferContext {
1395+ offer_id : offer. id ( ) ,
1396+ invoice_request : InvoiceRequestFields {
1397+ payer_signing_pubkey : invoice_request. payer_signing_pubkey ( ) ,
1398+ quantity : None ,
1399+ payer_note_truncated : None ,
1400+ human_readable_name : None ,
1401+ } ,
1402+ } ) ;
1403+
1404+ let onion_message = alice. onion_messenger . next_onion_message_for_peer ( bob_id) . unwrap ( ) ;
1405+ bob. onion_messenger . handle_onion_message ( alice_id, & onion_message) ;
1406+
1407+ // Re-process the same onion message to ensure idempotency —
1408+ // we should not generate a duplicate `InvoiceReceived` event.
1409+ bob. onion_messenger . handle_onion_message ( alice_id, & onion_message) ;
1410+
1411+ let mut events = bob. node . get_and_clear_pending_events ( ) ;
1412+ assert_eq ! ( events. len( ) , 1 ) ;
1413+
1414+ let ( invoice, context) = match events. pop ( ) . unwrap ( ) {
1415+ Event :: InvoiceReceived { payment_id : actual, invoice, context, .. } => {
1416+ assert_eq ! ( actual, payment_id) ;
1417+ ( invoice, context)
1418+ } ,
1419+ _ => panic ! ( "No Event::InvoiceReceived" ) ,
1420+ } ;
1421+
1422+ assert_eq ! ( invoice. amount_msats( ) , 10_000_000 ) ;
1423+ assert_ne ! ( invoice. signing_pubkey( ) , alice_id) ;
1424+ assert ! ( !invoice. payment_paths( ) . is_empty( ) ) ;
1425+ for path in invoice. payment_paths ( ) {
1426+ assert_eq ! ( path. introduction_node( ) , & IntroductionNode :: NodeId ( alice_id) ) ;
1427+ }
1428+
1429+ assert ! ( bob. node. send_payment_for_bolt12_invoice( & invoice, context. as_ref( ) ) . is_ok( ) ) ;
1430+ assert_eq ! (
1431+ bob. node. send_payment_for_bolt12_invoice( & invoice, context. as_ref( ) ) ,
1432+ Err ( Bolt12PaymentError :: DuplicateInvoice ) ,
1433+ ) ;
1434+
1435+ route_bolt12_payment_with_custom_tlvs ( bob, & [ alice] , & invoice, custom_tlvs. clone ( ) ) ;
1436+ expect_recent_payment ! ( bob, RecentPaymentDetails :: Pending , payment_id) ;
1437+
1438+ let purpose = match get_event ! ( & alice, Event :: PaymentClaimable ) {
1439+ Event :: PaymentClaimable { purpose, .. } => purpose,
1440+ _ => panic ! ( "No Event::PaymentClaimable" ) ,
1441+ } ;
1442+ let payment_preimage = purpose. preimage ( ) . expect ( "No preimage in Event::PaymentClaimable" ) ;
1443+
1444+ match purpose {
1445+ PaymentPurpose :: Bolt12OfferPayment { payment_context, .. } => {
1446+ assert_eq ! ( PaymentContext :: Bolt12Offer ( payment_context) , expected_payment_context) ;
1447+ } ,
1448+ _ => panic ! ( "Unexpected payment purpose: {:?}" , purpose) ,
1449+ }
1450+
1451+ let route = & [ & [ alice] as & [ & Node ] ] ;
1452+
1453+ let claim_payment_args =
1454+ ClaimAlongRouteArgs :: new ( bob, route, payment_preimage)
1455+ . with_custom_tlvs ( custom_tlvs) ;
1456+
1457+ if let Some ( inv) = claim_payment_along_route ( claim_payment_args) . 0 {
1458+ assert_eq ! ( inv, PaidBolt12Invoice :: Bolt12Invoice ( invoice. clone( ) ) ) ;
1459+ } else {
1460+ panic ! ( "Expected PaidBolt12Invoice::Bolt12Invoice" ) ;
1461+ }
1462+
1463+ expect_recent_payment ! ( bob, RecentPaymentDetails :: Fulfilled , payment_id) ;
1464+
1465+ assert_eq ! (
1466+ bob. node. send_payment_for_bolt12_invoice( & invoice, context. as_ref( ) ) ,
1467+ Err ( Bolt12PaymentError :: DuplicateInvoice ) ,
1468+ ) ;
1469+
1470+ for _ in 0 ..=IDEMPOTENCY_TIMEOUT_TICKS {
1471+ bob. node . timer_tick_occurred ( ) ;
1472+ }
1473+
1474+ assert_eq ! (
1475+ bob. node. send_payment_for_bolt12_invoice( & invoice, context. as_ref( ) ) ,
1476+ Err ( Bolt12PaymentError :: UnexpectedInvoice ) ,
1477+ ) ;
1478+ }
1479+
13511480/// Checks that an offer can be created using an unannounced node as a blinded path's introduction
13521481/// node. This is only preferred if there are no other options which may indicated either the offer
13531482/// is intended for the unannounced node or that the node is actually announced (e.g., an LSP) but
0 commit comments