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