@@ -2879,6 +2879,7 @@ fn async_payment_e2e() {
28792879 . unwrap ( ) ;
28802880 let ( peer_node_id, static_invoice_om, static_invoice) =
28812881 extract_static_invoice_om ( invoice_server, & [ sender_lsp, sender] ) ;
2882+
28822883 let payment_hash =
28832884 lock_in_htlc_for_static_invoice ( & static_invoice_om, peer_node_id, sender, sender_lsp) ;
28842885
@@ -2965,6 +2966,167 @@ fn async_payment_e2e() {
29652966 assert_eq ! ( res, Some ( PaidBolt12Invoice :: StaticInvoice ( static_invoice) ) ) ;
29662967}
29672968
2969+ #[ test]
2970+ fn async_payment_e2e_release_before_hold_registered ( ) {
2971+ let chanmon_cfgs = create_chanmon_cfgs ( 4 ) ;
2972+ let node_cfgs = create_node_cfgs ( 4 , & chanmon_cfgs) ;
2973+
2974+ let ( sender_cfg, recipient_cfg) = ( often_offline_node_cfg ( ) , often_offline_node_cfg ( ) ) ;
2975+ let mut sender_lsp_cfg = test_default_channel_config ( ) ;
2976+ sender_lsp_cfg. enable_htlc_hold = true ;
2977+ let mut invoice_server_cfg = test_default_channel_config ( ) ;
2978+ invoice_server_cfg. accept_forwards_to_priv_channels = true ;
2979+
2980+ let node_chanmgrs = create_node_chanmgrs (
2981+ 4 ,
2982+ & node_cfgs,
2983+ & [ Some ( sender_cfg) , Some ( sender_lsp_cfg) , Some ( invoice_server_cfg) , Some ( recipient_cfg) ] ,
2984+ ) ;
2985+ let nodes = create_network ( 4 , & node_cfgs, & node_chanmgrs) ;
2986+ create_unannounced_chan_between_nodes_with_value ( & nodes, 0 , 1 , 1_000_000 , 0 ) ;
2987+ create_announced_chan_between_nodes_with_value ( & nodes, 1 , 2 , 1_000_000 , 0 ) ;
2988+ create_unannounced_chan_between_nodes_with_value ( & nodes, 2 , 3 , 1_000_000 , 0 ) ;
2989+ unify_blockheight_across_nodes ( & nodes) ;
2990+ let sender = & nodes[ 0 ] ;
2991+ let sender_lsp = & nodes[ 1 ] ;
2992+ let invoice_server = & nodes[ 2 ] ;
2993+ let recipient = & nodes[ 3 ] ;
2994+
2995+ let recipient_id = vec ! [ 42 ; 32 ] ;
2996+ let inv_server_paths =
2997+ invoice_server. node . blinded_paths_for_async_recipient ( recipient_id. clone ( ) , None ) . unwrap ( ) ;
2998+ recipient. node . set_paths_to_static_invoice_server ( inv_server_paths) . unwrap ( ) ;
2999+ expect_offer_paths_requests ( recipient, & [ invoice_server, sender_lsp] ) ;
3000+ let invoice_flow_res =
3001+ pass_static_invoice_server_messages ( invoice_server, recipient, recipient_id. clone ( ) ) ;
3002+ let invoice = invoice_flow_res. invoice ;
3003+ let invreq_path = invoice_flow_res. invoice_request_path ;
3004+
3005+ let offer = recipient. node . get_async_receive_offer ( ) . unwrap ( ) ;
3006+ recipient. node . peer_disconnected ( invoice_server. node . get_our_node_id ( ) ) ;
3007+ recipient. onion_messenger . peer_disconnected ( invoice_server. node . get_our_node_id ( ) ) ;
3008+ invoice_server. node . peer_disconnected ( recipient. node . get_our_node_id ( ) ) ;
3009+ invoice_server. onion_messenger . peer_disconnected ( recipient. node . get_our_node_id ( ) ) ;
3010+
3011+ let amt_msat = 5000 ;
3012+ let payment_id = PaymentId ( [ 1 ; 32 ] ) ;
3013+ sender. node . pay_for_offer ( & offer, Some ( amt_msat) , payment_id, Default :: default ( ) ) . unwrap ( ) ;
3014+
3015+ let ( peer_id, invreq_om) = extract_invoice_request_om ( sender, & [ sender_lsp, invoice_server] ) ;
3016+ invoice_server. onion_messenger . handle_onion_message ( peer_id, & invreq_om) ;
3017+
3018+ let mut events = invoice_server. node . get_and_clear_pending_events ( ) ;
3019+ assert_eq ! ( events. len( ) , 1 ) ;
3020+ let ( reply_path, invreq) = match events. pop ( ) . unwrap ( ) {
3021+ Event :: StaticInvoiceRequested {
3022+ recipient_id : ev_id, reply_path, invoice_request, ..
3023+ } => {
3024+ assert_eq ! ( recipient_id, ev_id) ;
3025+ ( reply_path, invoice_request)
3026+ } ,
3027+ _ => panic ! ( ) ,
3028+ } ;
3029+
3030+ invoice_server
3031+ . node
3032+ . respond_to_static_invoice_request ( invoice, reply_path, invreq, invreq_path)
3033+ . unwrap ( ) ;
3034+ let ( peer_node_id, static_invoice_om, static_invoice) =
3035+ extract_static_invoice_om ( invoice_server, & [ sender_lsp, sender] ) ;
3036+
3037+ // Lock the HTLC in with the sender LSP, but stop before the sender's revoke_and_ack is handed
3038+ // back to the sender LSP. This reproduces the real LSPS2 timing where ReleaseHeldHtlc can
3039+ // arrive before the held HTLC is queued for decode on the sender LSP.
3040+ sender. onion_messenger . handle_onion_message ( peer_node_id, & static_invoice_om) ;
3041+ check_added_monitors ( sender, 1 ) ;
3042+ let commitment_update = get_htlc_update_msgs ( & sender, & sender_lsp. node . get_our_node_id ( ) ) ;
3043+ let update_add = commitment_update. update_add_htlcs [ 0 ] . clone ( ) ;
3044+ let payment_hash = update_add. payment_hash ;
3045+ assert ! ( update_add. hold_htlc. is_some( ) ) ;
3046+ sender_lsp. node . handle_update_add_htlc ( sender. node . get_our_node_id ( ) , & update_add) ;
3047+ sender_lsp. node . handle_commitment_signed_batch_test (
3048+ sender. node . get_our_node_id ( ) ,
3049+ & commitment_update. commitment_signed ,
3050+ ) ;
3051+ check_added_monitors ( sender_lsp, 1 ) ;
3052+ let ( _extra_msg_option, sender_raa, sender_holding_cell_htlcs) =
3053+ do_main_commitment_signed_dance ( sender_lsp, sender, false ) ;
3054+ assert ! ( sender_holding_cell_htlcs. is_empty( ) ) ;
3055+
3056+ let held_htlc_om_to_inv_server = sender
3057+ . onion_messenger
3058+ . next_onion_message_for_peer ( invoice_server. node . get_our_node_id ( ) )
3059+ . unwrap ( ) ;
3060+ invoice_server
3061+ . onion_messenger
3062+ . handle_onion_message ( sender_lsp. node . get_our_node_id ( ) , & held_htlc_om_to_inv_server) ;
3063+
3064+ let mut events_rc = core:: cell:: RefCell :: new ( Vec :: new ( ) ) ;
3065+ invoice_server. onion_messenger . process_pending_events ( & |e| Ok ( events_rc. borrow_mut ( ) . push ( e) ) ) ;
3066+ let events = events_rc. into_inner ( ) ;
3067+ let held_htlc_om = events
3068+ . into_iter ( )
3069+ . find_map ( |ev| {
3070+ if let Event :: OnionMessageIntercepted { message, .. } = ev {
3071+ let peeled_onion = recipient. onion_messenger . peel_onion_message ( & message) . unwrap ( ) ;
3072+ if matches ! (
3073+ peeled_onion,
3074+ PeeledOnion :: Offers ( OffersMessage :: InvoiceRequest { .. } , _, _)
3075+ ) {
3076+ return None ;
3077+ }
3078+
3079+ assert ! ( matches!(
3080+ peeled_onion,
3081+ PeeledOnion :: AsyncPayments ( AsyncPaymentsMessage :: HeldHtlcAvailable ( _) , _, _)
3082+ ) ) ;
3083+ Some ( message)
3084+ } else {
3085+ None
3086+ }
3087+ } )
3088+ . unwrap ( ) ;
3089+
3090+ let mut reconnect_args = ReconnectArgs :: new ( invoice_server, recipient) ;
3091+ reconnect_args. send_channel_ready = ( true , true ) ;
3092+ reconnect_nodes ( reconnect_args) ;
3093+
3094+ let events = core:: cell:: RefCell :: new ( Vec :: new ( ) ) ;
3095+ invoice_server. onion_messenger . process_pending_events ( & |e| Ok ( events. borrow_mut ( ) . push ( e) ) ) ;
3096+ assert_eq ! ( events. borrow( ) . len( ) , 1 ) ;
3097+ assert ! ( matches!( events. into_inner( ) . pop( ) . unwrap( ) , Event :: OnionMessagePeerConnected { .. } ) ) ;
3098+ expect_offer_paths_requests ( recipient, & [ invoice_server] ) ;
3099+
3100+ recipient
3101+ . onion_messenger
3102+ . handle_onion_message ( invoice_server. node . get_our_node_id ( ) , & held_htlc_om) ;
3103+ let ( peer_id, release_htlc_om) =
3104+ extract_release_htlc_oms ( recipient, & [ sender, sender_lsp, invoice_server] ) . pop ( ) . unwrap ( ) ;
3105+ sender_lsp. onion_messenger . handle_onion_message ( peer_id, & release_htlc_om) ;
3106+
3107+ // Now let the sender LSP receive the sender's revoke_and_ack and continue processing the held
3108+ // HTLC. Old code drops the release request above; fixed code remembers it and forwards the HTLC.
3109+ sender_lsp. node . handle_revoke_and_ack ( sender. node . get_our_node_id ( ) , & sender_raa) ;
3110+ check_added_monitors ( sender_lsp, 1 ) ;
3111+ assert ! ( sender_lsp. node. get_and_clear_pending_msg_events( ) . is_empty( ) ) ;
3112+ sender_lsp. node . process_pending_htlc_forwards ( ) ;
3113+ let mut events = sender_lsp. node . get_and_clear_pending_msg_events ( ) ;
3114+ assert_eq ! ( events. len( ) , 1 ) ;
3115+ let ev = remove_first_msg_event_to_node ( & invoice_server. node . get_our_node_id ( ) , & mut events) ;
3116+ check_added_monitors ( & sender_lsp, 1 ) ;
3117+
3118+ let path: & [ & Node ] = & [ invoice_server, recipient] ;
3119+ let args = PassAlongPathArgs :: new ( sender_lsp, path, amt_msat, payment_hash, ev)
3120+ . with_dummy_tlvs ( & [ DummyTlvs :: default ( ) ; DEFAULT_PAYMENT_DUMMY_HOPS ] ) ;
3121+ let claimable_ev = do_pass_along_path ( args) . unwrap ( ) ;
3122+
3123+ let route: & [ & [ & Node ] ] = & [ & [ sender_lsp, invoice_server, recipient] ] ;
3124+ let keysend_preimage = extract_payment_preimage ( & claimable_ev) ;
3125+ let ( res, _) =
3126+ claim_payment_along_route ( ClaimAlongRouteArgs :: new ( sender, route, keysend_preimage) ) ;
3127+ assert_eq ! ( res, Some ( PaidBolt12Invoice :: StaticInvoice ( static_invoice) ) ) ;
3128+ }
3129+
29683130#[ test]
29693131fn held_htlc_timeout ( ) {
29703132 // Test that if a held HTLC doesn't get released for a long time, it will eventually time out and
0 commit comments