88// licenses.
99
1010use crate :: blinded_path:: payment:: {
11- BlindedPaymentPath , Bolt12RefundContext , DummyTlvs , ForwardTlvs , PaymentConstraints ,
12- PaymentContext , PaymentForwardNode , PaymentRelay , ReceiveTlvs , PAYMENT_PADDING_ROUND_OFF ,
11+ BlindedPaymentPath , Bolt12RefundContext , DummyTlvs , ForwardNode , ForwardTlvs ,
12+ PaymentConstraints , PaymentContext , PaymentForwardNode , PaymentRelay , ReceiveTlvs ,
13+ PAYMENT_PADDING_ROUND_OFF ,
1314} ;
1415use crate :: blinded_path:: utils:: is_padded;
1516use crate :: blinded_path:: { self , BlindedHop } ;
1617use crate :: events:: { Event , HTLCHandlingFailureType , PaymentFailureReason } ;
17- use crate :: ln:: channelmanager:: { self , HTLCFailureMsg , PaymentId } ;
18+ use crate :: ln:: channelmanager:: { self , HTLCFailureMsg , PaymentId , MPP_TIMEOUT_TICKS } ;
1819use crate :: ln:: functional_test_utils:: * ;
1920use crate :: ln:: inbound_payment:: ExpandedKey ;
2021use crate :: ln:: msgs:: {
@@ -34,7 +35,7 @@ use crate::routing::router::{
3435use crate :: sign:: { NodeSigner , PeerStorageKey , ReceiveAuthKey , Recipient } ;
3536use crate :: types:: features:: { BlindedHopFeatures , ChannelFeatures , NodeFeatures } ;
3637use crate :: types:: payment:: { PaymentHash , PaymentSecret } ;
37- use crate :: util:: config:: { HTLCInterceptionFlags , UserConfig } ;
38+ use crate :: util:: config:: { ChannelConfig , HTLCInterceptionFlags , UserConfig } ;
3839use crate :: util:: ser:: { WithoutLength , Writeable } ;
3940use crate :: util:: test_utils:: { self , bytes_from_hex, pubkey_from_hex, secret_from_hex} ;
4041use bitcoin:: hex:: DisplayHex ;
@@ -2715,3 +2716,290 @@ fn do_test_trampoline_relay(blinded: bool, test_case: TrampolineTestCase) {
27152716 claim_payment ( & nodes[ 0 ] , & [ & nodes[ 1 ] , & nodes[ 2 ] ] , payment_preimage) ;
27162717 }
27172718}
2719+
2720+ /// Sets up channels and sends a trampoline MPP payment across two paths.
2721+ ///
2722+ /// Topology:
2723+ /// Alice (0) --> Bob (1) --> Carol (2, trampoline node)
2724+ /// Alice (0) --> Barry (3) --> Carol (2, trampoline node)
2725+ ///
2726+ /// Carol's inner trampoline onion is a forward to an unknown next node. We don't need the
2727+ /// next hop as a real node since forwarding isn't implemented yet -- we just need the onion to
2728+ /// contain a valid forward payload.
2729+ ///
2730+ /// Returns (payment_hash, per_path_amount, ev_to_bob, ev_to_barry).
2731+ fn send_trampoline_mpp_payment < ' a , ' b , ' c > (
2732+ nodes : & ' a Vec < Node < ' a , ' b , ' c > > ,
2733+ ) -> ( PaymentHash , u64 , MessageSendEvent , MessageSendEvent ) {
2734+ let secp_ctx = Secp256k1 :: new ( ) ;
2735+
2736+ let alice_bob_chan =
2737+ create_announced_chan_between_nodes_with_value ( nodes, 0 , 1 , 1_000_000 , 0 ) . 2 ;
2738+ let bob_carol_chan =
2739+ create_announced_chan_between_nodes_with_value ( nodes, 1 , 2 , 1_000_000 , 0 ) . 2 ;
2740+ let alice_barry_chan =
2741+ create_announced_chan_between_nodes_with_value ( nodes, 0 , 3 , 1_000_000 , 0 ) . 2 ;
2742+ let barry_carol_chan =
2743+ create_announced_chan_between_nodes_with_value ( nodes, 3 , 2 , 1_000_000 , 0 ) . 2 ;
2744+
2745+ let per_path_amt = 500_000 ;
2746+ let total_amt = per_path_amt * 2 ;
2747+ let ( _, payment_hash, payment_secret) =
2748+ get_payment_preimage_hash ( & nodes[ 2 ] , Some ( total_amt) , None ) ;
2749+
2750+ let bob_node_id = nodes[ 1 ] . node . get_our_node_id ( ) ;
2751+ let carol_node_id = nodes[ 2 ] . node . get_our_node_id ( ) ;
2752+ let barry_node_id = nodes[ 3 ] . node . get_our_node_id ( ) ;
2753+
2754+ let alice_bob_scid = get_scid_from_channel_id ( & nodes[ 0 ] , alice_bob_chan) ;
2755+ let bob_carol_scid = get_scid_from_channel_id ( & nodes[ 1 ] , bob_carol_chan) ;
2756+ let alice_barry_scid = get_scid_from_channel_id ( & nodes[ 0 ] , alice_barry_chan) ;
2757+ let barry_carol_scid = get_scid_from_channel_id ( & nodes[ 3 ] , barry_carol_chan) ;
2758+
2759+ let trampoline_cltv = 42 ;
2760+ let excess_final_cltv = 70 ;
2761+
2762+ // Not we don't actually have an outgoing channel for Carol, we just use our default fee
2763+ // policy.
2764+ let carol_relay = ChannelConfig :: default ( ) ;
2765+
2766+ let next_trampoline = PublicKey :: from_slice ( & [ 2 ; 33 ] ) . unwrap ( ) ;
2767+ let fwd_tail = || {
2768+ let intermediate_nodes = [ ForwardNode {
2769+ tlvs : blinded_path:: payment:: TrampolineForwardTlvs {
2770+ next_trampoline,
2771+ payment_constraints : PaymentConstraints {
2772+ max_cltv_expiry : u32:: max_value ( ) ,
2773+ htlc_minimum_msat : 1 ,
2774+ } ,
2775+ features : BlindedHopFeatures :: empty ( ) ,
2776+ payment_relay : PaymentRelay {
2777+ cltv_expiry_delta : carol_relay. cltv_expiry_delta ,
2778+ fee_proportional_millionths : carol_relay. forwarding_fee_proportional_millionths ,
2779+ fee_base_msat : carol_relay. forwarding_fee_base_msat ,
2780+ } ,
2781+ next_blinding_override : None ,
2782+ } ,
2783+ node_id : carol_node_id,
2784+ htlc_maximum_msat : u64:: max_value ( ) ,
2785+ } ] ;
2786+ let payee_tlvs = ReceiveTlvs {
2787+ payment_secret : PaymentSecret ( [ 0 ; 32 ] ) ,
2788+ payment_constraints : PaymentConstraints {
2789+ max_cltv_expiry : u32:: max_value ( ) ,
2790+ htlc_minimum_msat : 1 ,
2791+ } ,
2792+ payment_context : PaymentContext :: Bolt12Refund ( Bolt12RefundContext { } ) ,
2793+ } ;
2794+ create_trampoline_forward_blinded_tail (
2795+ & secp_ctx,
2796+ & nodes[ 2 ] . keys_manager ,
2797+ & intermediate_nodes,
2798+ next_trampoline,
2799+ ReceiveAuthKey ( [ 0 ; 32 ] ) ,
2800+ payee_tlvs,
2801+ trampoline_cltv,
2802+ excess_final_cltv,
2803+ per_path_amt,
2804+ )
2805+ } ;
2806+
2807+ let hop = |pubkey, short_channel_id, fee_msat, cltv_expiry_delta| RouteHop {
2808+ pubkey,
2809+ node_features : NodeFeatures :: empty ( ) ,
2810+ short_channel_id,
2811+ channel_features : ChannelFeatures :: empty ( ) ,
2812+ fee_msat,
2813+ cltv_expiry_delta,
2814+ maybe_announced_channel : true ,
2815+ } ;
2816+ let build_path_hops = |first_hop_node_id, first_hop_scid, second_hop_scid| {
2817+ vec ! [
2818+ hop( first_hop_node_id, first_hop_scid, 1000 , 48 ) ,
2819+ hop( carol_node_id, second_hop_scid, 0 , trampoline_cltv + excess_final_cltv) ,
2820+ ]
2821+ } ;
2822+
2823+ let placeholder_tail = fwd_tail ( ) ;
2824+ let mut route = Route {
2825+ paths : vec ! [
2826+ Path {
2827+ hops: build_path_hops( bob_node_id, alice_bob_scid, bob_carol_scid) ,
2828+ blinded_tail: Some ( placeholder_tail. clone( ) ) ,
2829+ } ,
2830+ Path {
2831+ hops: build_path_hops( barry_node_id, alice_barry_scid, barry_carol_scid) ,
2832+ blinded_tail: Some ( placeholder_tail) ,
2833+ } ,
2834+ ] ,
2835+ route_params : None ,
2836+ } ;
2837+
2838+ let cur_height = nodes[ 0 ] . best_block_info ( ) . 1 + 1 ;
2839+ let payment_id = PaymentId ( payment_hash. 0 ) ;
2840+ let onion = RecipientOnionFields :: secret_only ( payment_secret, total_amt) ;
2841+ let session_privs = nodes[ 0 ]
2842+ . node
2843+ . test_add_new_pending_payment ( payment_hash, onion. clone ( ) , payment_id, & route)
2844+ . unwrap ( ) ;
2845+
2846+ route. paths [ 0 ] . blinded_tail = Some ( fwd_tail ( ) ) ;
2847+ route. paths [ 1 ] . blinded_tail = Some ( fwd_tail ( ) ) ;
2848+
2849+ for ( i, path) in route. paths . iter ( ) . enumerate ( ) {
2850+ nodes[ 0 ]
2851+ . node
2852+ . test_send_payment_along_path (
2853+ path,
2854+ & payment_hash,
2855+ onion. clone ( ) ,
2856+ cur_height,
2857+ payment_id,
2858+ & None ,
2859+ session_privs[ i] ,
2860+ )
2861+ . unwrap ( ) ;
2862+ check_added_monitors ( & nodes[ 0 ] , 1 ) ;
2863+ }
2864+
2865+ let mut events = nodes[ 0 ] . node . get_and_clear_pending_msg_events ( ) ;
2866+ assert_eq ! ( events. len( ) , 2 ) ;
2867+ let ev_bob = remove_first_msg_event_to_node ( & bob_node_id, & mut events) ;
2868+ let ev_barry = remove_first_msg_event_to_node ( & barry_node_id, & mut events) ;
2869+ ( payment_hash, per_path_amt, ev_bob, ev_barry)
2870+ }
2871+
2872+ /// How an incomplete trampoline MPP times out (if at all).
2873+ enum TrampolineTimeout {
2874+ /// Tick timers until MPP timeout fires.
2875+ Ticks ,
2876+ /// Mine blocks until on-chain CLTV timeout fires.
2877+ OnChain ,
2878+ }
2879+
2880+ fn do_trampoline_mpp_test ( timeout : Option < TrampolineTimeout > ) {
2881+ let chanmon_cfgs = create_chanmon_cfgs ( 4 ) ;
2882+ let node_cfgs = create_node_cfgs ( 4 , & chanmon_cfgs) ;
2883+ let node_chanmgrs = create_node_chanmgrs ( 4 , & node_cfgs, & vec ! [ None ; 4 ] ) ;
2884+ let nodes = create_network ( 4 , & node_cfgs, & node_chanmgrs) ;
2885+
2886+ let ( payment_hash, per_path_amt, ev_bob, ev_barry) = send_trampoline_mpp_payment ( & nodes) ;
2887+ let send_both = timeout. is_none ( ) ;
2888+
2889+ let bob_path: & [ & Node ] = & [ & nodes[ 1 ] , & nodes[ 2 ] ] ;
2890+ let barry_path: & [ & Node ] = & [ & nodes[ 3 ] , & nodes[ 2 ] ] ;
2891+
2892+ // Pass first part along Alice -> Bob -> Carol.
2893+ let args = PassAlongPathArgs :: new ( & nodes[ 0 ] , bob_path, per_path_amt, payment_hash, ev_bob)
2894+ . without_claimable_event ( ) ;
2895+ do_pass_along_path ( args) ;
2896+
2897+ // Either complete the MPP (triggering trampoline rejection) or trigger a timeout.
2898+ let expected_reason = match timeout {
2899+ None => {
2900+ let args =
2901+ PassAlongPathArgs :: new ( & nodes[ 0 ] , barry_path, per_path_amt, payment_hash, ev_barry)
2902+ . without_clearing_recipient_events ( ) ;
2903+ do_pass_along_path ( args) ;
2904+ LocalHTLCFailureReason :: TemporaryTrampolineFailure
2905+ } ,
2906+ Some ( TrampolineTimeout :: Ticks ) => {
2907+ for _ in 0 ..MPP_TIMEOUT_TICKS {
2908+ nodes[ 2 ] . node . timer_tick_occurred ( ) ;
2909+ }
2910+ LocalHTLCFailureReason :: MPPTimeout
2911+ } ,
2912+ Some ( TrampolineTimeout :: OnChain ) => {
2913+ let current_height = nodes[ 2 ] . best_block_info ( ) . 1 ;
2914+ connect_blocks ( & nodes[ 2 ] , 200 - current_height) ;
2915+ LocalHTLCFailureReason :: CLTVExpiryTooSoon
2916+ } ,
2917+ } ;
2918+
2919+ // Carol rejects the trampoline forward (either after MPP completion or timeout).
2920+ let events = nodes[ 2 ] . node . get_and_clear_pending_events ( ) ;
2921+ assert_eq ! ( events. len( ) , 1 ) ;
2922+ match events[ 0 ] {
2923+ crate :: events:: Event :: HTLCHandlingFailed {
2924+ ref failure_type, ref failure_reason, ..
2925+ } => {
2926+ assert_eq ! ( failure_type, & HTLCHandlingFailureType :: TrampolineForward { } ) ;
2927+ match failure_reason {
2928+ Some ( crate :: events:: HTLCHandlingFailureReason :: Local { reason } ) => {
2929+ assert_eq ! ( * reason, expected_reason)
2930+ } ,
2931+ Some ( _) | None => panic ! ( "expected failure_reason for failed trampoline" ) ,
2932+ }
2933+ } ,
2934+ _ => panic ! ( "Unexpected destination" ) ,
2935+ }
2936+ expect_and_process_pending_htlcs ( & nodes[ 2 ] , false ) ;
2937+ assert ! ( nodes[ 2 ] . node. get_and_clear_pending_events( ) . is_empty( ) ) ;
2938+
2939+ // Propagate failures back through each forwarded path to Alice.
2940+ let both: [ & [ & Node ] ; 2 ] = [ bob_path, barry_path] ;
2941+ let one: [ & [ & Node ] ; 1 ] = [ bob_path] ;
2942+ let forwarded: & [ & [ & Node ] ] = if send_both { & both } else { & one } ;
2943+ let carol_id = nodes[ 2 ] . node . get_our_node_id ( ) ;
2944+ check_added_monitors ( & nodes[ 2 ] , forwarded. len ( ) ) ;
2945+ let mut carol_msgs = nodes[ 2 ] . node . get_and_clear_pending_msg_events ( ) ;
2946+ assert_eq ! ( carol_msgs. len( ) , forwarded. len( ) ) ;
2947+ for path in forwarded {
2948+ let hop = path[ 0 ] ;
2949+ let hop_id = hop. node . get_our_node_id ( ) ;
2950+ let ev = remove_first_msg_event_to_node ( & hop_id, & mut carol_msgs) ;
2951+ let updates = match ev {
2952+ MessageSendEvent :: UpdateHTLCs { updates, .. } => updates,
2953+ _ => panic ! ( "Expected UpdateHTLCs" ) ,
2954+ } ;
2955+ hop. node . handle_update_fail_htlc ( carol_id, & updates. update_fail_htlcs [ 0 ] ) ;
2956+ do_commitment_signed_dance ( hop, & nodes[ 2 ] , & updates. commitment_signed , true , false ) ;
2957+
2958+ let fwd = get_htlc_update_msgs ( hop, & nodes[ 0 ] . node . get_our_node_id ( ) ) ;
2959+ nodes[ 0 ] . node . handle_update_fail_htlc ( hop_id, & fwd. update_fail_htlcs [ 0 ] ) ;
2960+ do_commitment_signed_dance ( & nodes[ 0 ] , hop, & fwd. commitment_signed , false , false ) ;
2961+ }
2962+
2963+ // Check Alice's failure events.
2964+ let events = nodes[ 0 ] . node . get_and_clear_pending_events ( ) ;
2965+ assert_eq ! ( events. len( ) , if send_both { 3 } else { 1 } ) ;
2966+ for ev in & events[ ..forwarded. len ( ) ] {
2967+ match ev {
2968+ Event :: PaymentPathFailed { payment_hash : h, payment_failed_permanently, .. } => {
2969+ assert_eq ! ( * h, payment_hash) ;
2970+ assert ! ( !payment_failed_permanently) ;
2971+ } ,
2972+ _ => panic ! ( "Expected PaymentPathFailed, got {:?}" , ev) ,
2973+ }
2974+ }
2975+ if send_both {
2976+ match & events[ 2 ] {
2977+ Event :: PaymentFailed { payment_hash : h, reason, .. } => {
2978+ assert_eq ! ( * h, Some ( payment_hash) ) ;
2979+ assert_eq ! ( * reason, Some ( PaymentFailureReason :: RetriesExhausted ) ) ;
2980+ } ,
2981+ _ => panic ! ( "Expected PaymentFailed, got {:?}" , events[ 2 ] ) ,
2982+ }
2983+
2984+ // Verify no spurious timeout fires after the MPP set was dispatched.
2985+ for _ in 0 ..( MPP_TIMEOUT_TICKS * 3 ) {
2986+ nodes[ 2 ] . node . timer_tick_occurred ( ) ;
2987+ }
2988+ assert ! ( nodes[ 2 ] . node. get_and_clear_pending_events( ) . is_empty( ) ) ;
2989+ }
2990+ }
2991+
2992+ #[ test]
2993+ fn test_trampoline_mpp_receive_success ( ) {
2994+ do_trampoline_mpp_test ( None ) ;
2995+ }
2996+
2997+ #[ test]
2998+ fn test_trampoline_mpp_timeout_partial ( ) {
2999+ do_trampoline_mpp_test ( Some ( TrampolineTimeout :: Ticks ) ) ;
3000+ }
3001+
3002+ #[ test]
3003+ fn test_trampoline_mpp_onchain_timeout ( ) {
3004+ do_trampoline_mpp_test ( Some ( TrampolineTimeout :: OnChain ) ) ;
3005+ }
0 commit comments