@@ -60,7 +60,7 @@ use crate::sign::NodeSigner;
6060use crate :: sync:: Mutex ;
6161use crate :: types:: features:: Bolt12InvoiceFeatures ;
6262use crate :: types:: payment:: { PaymentHash , PaymentPreimage , PaymentSecret } ;
63- use crate :: util:: config:: { HTLCInterceptionFlags , UserConfig } ;
63+ use crate :: util:: config:: { ChannelConfigUpdate , HTLCInterceptionFlags , UserConfig } ;
6464use crate :: util:: ser:: Writeable ;
6565use bitcoin:: constants:: ChainHash ;
6666use bitcoin:: network:: Network ;
@@ -416,6 +416,55 @@ fn extract_static_invoice_om<'a>(
416416 ( peer_id, om, static_invoice. unwrap ( ) )
417417}
418418
419+ /// Extracts the next static invoice update while ignoring unrelated offer-path requests.
420+ fn extract_serve_static_invoice_om < ' a > (
421+ recipient : & ' a Node , next_hop_nodes : & [ & ' a Node ] ,
422+ ) -> ( PublicKey , msgs:: OnionMessage , StaticInvoice ) {
423+ let mut static_invoice = None ;
424+ let mut expected_msg_type = |peeled_onion : & _ | match peeled_onion {
425+ PeeledOnion :: AsyncPayments ( AsyncPaymentsMessage :: ServeStaticInvoice ( msg) , _, _) => {
426+ static_invoice = Some ( msg. invoice . clone ( ) ) ;
427+ true
428+ } ,
429+ _ => false ,
430+ } ;
431+ let expected_msg_type_to_ignore = |peeled_onion : & _ | {
432+ matches ! (
433+ peeled_onion,
434+ & PeeledOnion :: AsyncPayments ( AsyncPaymentsMessage :: OfferPathsRequest ( _) , _, _)
435+ )
436+ } ;
437+ let ( peer_id, om) = extract_expected_om (
438+ recipient,
439+ next_hop_nodes,
440+ expected_msg_type,
441+ expected_msg_type_to_ignore,
442+ )
443+ . pop ( )
444+ . unwrap ( ) ;
445+ ( peer_id, om, static_invoice. unwrap ( ) )
446+ }
447+
448+ /// Delivers a static invoice update and checks that the server persists it in the expected slot.
449+ fn expect_static_invoice_persist_event (
450+ server : & Node , recipient : & Node , serve_static_invoice_om : & msgs:: OnionMessage ,
451+ expected_invoice : & StaticInvoice , expected_invoice_slot : u16 , expected_recipient_id : & [ u8 ] ,
452+ ) {
453+ server
454+ . onion_messenger
455+ . handle_onion_message ( recipient. node . get_our_node_id ( ) , serve_static_invoice_om) ;
456+ let mut events = server. node . get_and_clear_pending_events ( ) ;
457+ assert_eq ! ( events. len( ) , 1 ) ;
458+ match events. pop ( ) . unwrap ( ) {
459+ Event :: PersistStaticInvoice { invoice, invoice_slot, recipient_id, .. } => {
460+ assert_eq ! ( & invoice, expected_invoice) ;
461+ assert_eq ! ( invoice_slot, expected_invoice_slot) ;
462+ assert_eq ! ( recipient_id, expected_recipient_id) ;
463+ } ,
464+ _ => panic ! ( ) ,
465+ }
466+ }
467+
419468fn extract_held_htlc_available_oms < ' a > (
420469 payer : & ' a Node , next_hop_nodes : & [ & ' a Node ] ,
421470) -> Vec < ( PublicKey , msgs:: OnionMessage ) > {
@@ -2507,6 +2556,135 @@ fn refresh_static_invoices_for_used_offers() {
25072556 assert_eq ! ( res. 0 , Some ( PaidBolt12Invoice :: StaticInvoice ( updated_invoice) ) ) ;
25082557}
25092558
2559+ /// Checks that a used async receive offer gets a fresh server-side static invoice when a new
2560+ /// channel becomes usable. Used offers may already be published, so they should not wait for the
2561+ /// normal invoice refresh threshold after local payment paths change.
2562+ #[ test]
2563+ fn refresh_static_invoices_for_used_offers_when_channel_opens ( ) {
2564+ let chanmon_cfgs = create_chanmon_cfgs ( 3 ) ;
2565+ let node_cfgs = create_node_cfgs ( 3 , & chanmon_cfgs) ;
2566+
2567+ let mut allow_priv_chan_fwds_cfg = test_default_channel_config ( ) ;
2568+ allow_priv_chan_fwds_cfg. accept_forwards_to_priv_channels = true ;
2569+ let node_chanmgrs =
2570+ create_node_chanmgrs ( 3 , & node_cfgs, & [ None , Some ( allow_priv_chan_fwds_cfg) , None ] ) ;
2571+
2572+ let nodes = create_network ( 3 , & node_cfgs, & node_chanmgrs) ;
2573+ create_announced_chan_between_nodes_with_value ( & nodes, 0 , 1 , 1_000_000 , 0 ) ;
2574+ create_unannounced_chan_between_nodes_with_value ( & nodes, 1 , 2 , 1_000_000 , 0 ) ;
2575+ let server = & nodes[ 1 ] ;
2576+ let recipient = & nodes[ 2 ] ;
2577+
2578+ let recipient_id = vec ! [ 42 ; 32 ] ;
2579+ let inv_server_paths =
2580+ server. node . blinded_paths_for_async_recipient ( recipient_id. clone ( ) , None ) . unwrap ( ) ;
2581+ recipient. node . set_paths_to_static_invoice_server ( inv_server_paths) . unwrap ( ) ;
2582+ expect_offer_paths_requests ( recipient, & [ & nodes[ 0 ] , server] ) ;
2583+
2584+ let flow_res = pass_static_invoice_server_messages ( server, recipient, recipient_id. clone ( ) ) ;
2585+ let original_invoice = flow_res. invoice ;
2586+ assert_eq ! ( original_invoice. payment_paths( ) . len( ) , 1 ) ;
2587+
2588+ // Mark the offer as used so the cache treats it as potentially published by the application.
2589+ let _offer = recipient. node . get_async_receive_offer ( ) . unwrap ( ) ;
2590+
2591+ // Keep onion delivery direct so the test only checks that opening a channel refreshes the
2592+ // invoice after its forwarding information is available.
2593+ server. message_router . peers_override . lock ( ) . unwrap ( ) . push ( recipient. node . get_our_node_id ( ) ) ;
2594+ recipient. message_router . peers_override . lock ( ) . unwrap ( ) . push ( server. node . get_our_node_id ( ) ) ;
2595+
2596+ create_unannounced_chan_between_nodes_with_value ( & nodes, 1 , 2 , 1_000_000 , 0 ) ;
2597+ let ( peer_node_id, serve_static_invoice_om, updated_invoice) =
2598+ extract_serve_static_invoice_om ( recipient, & [ server] ) ;
2599+ assert_eq ! ( peer_node_id, server. node. get_our_node_id( ) ) ;
2600+ assert_ne ! ( original_invoice, updated_invoice) ;
2601+ assert_eq ! ( updated_invoice. payment_paths( ) . len( ) , 2 ) ;
2602+
2603+ expect_static_invoice_persist_event (
2604+ server,
2605+ recipient,
2606+ & serve_static_invoice_om,
2607+ & updated_invoice,
2608+ flow_res. invoice_slot ,
2609+ & recipient_id,
2610+ ) ;
2611+ }
2612+
2613+ /// Checks that changed forwarding parameters refresh the static invoice for a used offer without
2614+ /// waiting for the normal invoice refresh threshold.
2615+ #[ test]
2616+ fn refresh_static_invoices_for_used_offers_when_forwarding_fees_change ( ) {
2617+ let chanmon_cfgs = create_chanmon_cfgs ( 3 ) ;
2618+ let node_cfgs = create_node_cfgs ( 3 , & chanmon_cfgs) ;
2619+
2620+ let mut allow_priv_chan_fwds_cfg = test_default_channel_config ( ) ;
2621+ allow_priv_chan_fwds_cfg. accept_forwards_to_priv_channels = true ;
2622+ let node_chanmgrs =
2623+ create_node_chanmgrs ( 3 , & node_cfgs, & [ None , Some ( allow_priv_chan_fwds_cfg) , None ] ) ;
2624+
2625+ let nodes = create_network ( 3 , & node_cfgs, & node_chanmgrs) ;
2626+ create_announced_chan_between_nodes_with_value ( & nodes, 0 , 1 , 1_000_000 , 0 ) ;
2627+ create_unannounced_chan_between_nodes_with_value ( & nodes, 1 , 2 , 1_000_000 , 0 ) ;
2628+ let server = & nodes[ 1 ] ;
2629+ let recipient = & nodes[ 2 ] ;
2630+
2631+ let recipient_id = vec ! [ 42 ; 32 ] ;
2632+ let inv_server_paths =
2633+ server. node . blinded_paths_for_async_recipient ( recipient_id. clone ( ) , None ) . unwrap ( ) ;
2634+ recipient. node . set_paths_to_static_invoice_server ( inv_server_paths) . unwrap ( ) ;
2635+ expect_offer_paths_requests ( recipient, & [ & nodes[ 0 ] , server] ) ;
2636+
2637+ let flow_res = pass_static_invoice_server_messages ( server, recipient, recipient_id. clone ( ) ) ;
2638+ let original_invoice = flow_res. invoice ;
2639+ let _offer = recipient. node . get_async_receive_offer ( ) . unwrap ( ) ;
2640+
2641+ // Keep onion delivery direct so the test only checks the forwarding update trigger.
2642+ server. message_router . peers_override . lock ( ) . unwrap ( ) . push ( recipient. node . get_our_node_id ( ) ) ;
2643+ recipient. message_router . peers_override . lock ( ) . unwrap ( ) . push ( server. node . get_our_node_id ( ) ) ;
2644+
2645+ let channel = server
2646+ . node
2647+ . list_channels ( )
2648+ . into_iter ( )
2649+ . find ( |channel| channel. counterparty . node_id == recipient. node . get_our_node_id ( ) )
2650+ . unwrap ( ) ;
2651+ let updated_fee_base_msat = channel. config . unwrap ( ) . forwarding_fee_base_msat + 10 ;
2652+ let config_update = ChannelConfigUpdate {
2653+ forwarding_fee_base_msat : Some ( updated_fee_base_msat) ,
2654+ ..ChannelConfigUpdate :: default ( )
2655+ } ;
2656+ server
2657+ . node
2658+ . update_partial_channel_config (
2659+ & recipient. node . get_our_node_id ( ) ,
2660+ & [ channel. channel_id ] ,
2661+ & config_update,
2662+ )
2663+ . unwrap ( ) ;
2664+ let channel_update = get_event_msg ! (
2665+ server,
2666+ MessageSendEvent :: SendChannelUpdate ,
2667+ recipient. node. get_our_node_id( )
2668+ ) ;
2669+ recipient. node . handle_channel_update ( server. node . get_our_node_id ( ) , & channel_update) ;
2670+
2671+ let ( peer_node_id, serve_static_invoice_om, updated_invoice) =
2672+ extract_serve_static_invoice_om ( recipient, & [ server] ) ;
2673+ assert_eq ! ( peer_node_id, server. node. get_our_node_id( ) ) ;
2674+ assert_ne ! ( original_invoice, updated_invoice) ;
2675+ assert_eq ! ( updated_invoice. payment_paths( ) . len( ) , 1 ) ;
2676+ assert_eq ! ( updated_invoice. payment_paths( ) [ 0 ] . payinfo. fee_base_msat, updated_fee_base_msat) ;
2677+
2678+ expect_static_invoice_persist_event (
2679+ server,
2680+ recipient,
2681+ & serve_static_invoice_om,
2682+ & updated_invoice,
2683+ flow_res. invoice_slot ,
2684+ & recipient_id,
2685+ ) ;
2686+ }
2687+
25102688#[ cfg_attr( feature = "std" , ignore) ]
25112689#[ test]
25122690fn ignore_expired_static_invoice ( ) {
0 commit comments