@@ -2695,3 +2695,124 @@ fn test_unblinded_trampoline_forward() {
2695
2695
do_test_unblinded_trampoline_forward ( true ) ;
2696
2696
do_test_unblinded_trampoline_forward ( false ) ;
2697
2697
}
2698
+
2699
+ #[ test]
2700
+ fn test_blinded_trampoline_forward ( ) {
2701
+ // Simulate a payment of A (0) -> B (1) -> C(Trampoline (blinded forward)) (2) -> D(Trampoline(blinded receive)) (3)
2702
+ // trampoline hops C -> T0 (4) -> D
2703
+ // make it fail at B, then at C's outer onion, then at C's inner onion
2704
+ const TOTAL_NODE_COUNT : usize = 5 ;
2705
+ let secp_ctx = Secp256k1 :: new ( ) ;
2706
+
2707
+ let chanmon_cfgs = create_chanmon_cfgs ( TOTAL_NODE_COUNT ) ;
2708
+ let node_cfgs = create_node_cfgs ( TOTAL_NODE_COUNT , & chanmon_cfgs) ;
2709
+ let node_chanmgrs = create_node_chanmgrs ( TOTAL_NODE_COUNT , & node_cfgs, & vec ! [ None ; TOTAL_NODE_COUNT ] ) ;
2710
+ let mut nodes = create_network ( TOTAL_NODE_COUNT , & node_cfgs, & node_chanmgrs) ;
2711
+
2712
+ let ( _, _, chan_id_alice_bob, _) = create_announced_chan_between_nodes_with_value ( & nodes, 0 , 1 , 1_000_000 , 0 ) ;
2713
+ let ( _, _, chan_id_bob_carol, _) = create_announced_chan_between_nodes_with_value ( & nodes, 1 , 2 , 1_000_000 , 0 ) ;
2714
+ let ( _, _, _, _) = create_announced_chan_between_nodes_with_value ( & nodes, 2 , 4 , 1_000_000 , 0 ) ;
2715
+ let ( _, _, _, _) = create_announced_chan_between_nodes_with_value ( & nodes, 4 , 3 , 1_000_000 , 0 ) ;
2716
+
2717
+ for i in 0 ..TOTAL_NODE_COUNT { // connect all nodes' blocks
2718
+ connect_blocks ( & nodes[ i] , ( TOTAL_NODE_COUNT as u32 ) * CHAN_CONFIRM_DEPTH + 1 - nodes[ i] . best_block_info ( ) . 1 ) ;
2719
+ }
2720
+
2721
+ let bob_node_id = nodes[ 1 ] . node ( ) . get_our_node_id ( ) ;
2722
+ let carol_node_id = nodes[ 2 ] . node ( ) . get_our_node_id ( ) ;
2723
+ let dave_node_id = nodes[ 3 ] . node ( ) . get_our_node_id ( ) ;
2724
+
2725
+ let alice_bob_scid = nodes[ 0 ] . node ( ) . list_channels ( ) . iter ( ) . find ( |c| c. channel_id == chan_id_alice_bob) . unwrap ( ) . short_channel_id . unwrap ( ) ;
2726
+ let bob_carol_scid = nodes[ 1 ] . node ( ) . list_channels ( ) . iter ( ) . find ( |c| c. channel_id == chan_id_bob_carol) . unwrap ( ) . short_channel_id . unwrap ( ) ;
2727
+
2728
+ let amt_msat = 1000 ;
2729
+ let ( payment_preimage, payment_hash, payment_secret) = get_payment_preimage_hash ( & nodes[ 3 ] , Some ( amt_msat) , None ) ;
2730
+
2731
+ let alice_carol_trampoline_shared_secret = secret_from_hex ( "a0f4b8d7b6c2d0ffdfaf718f76e9decaef4d9fb38a8c4addb95c4007cc3eee03" ) ;
2732
+ let carol_blinding_point = PublicKey :: from_secret_key ( & secp_ctx, & alice_carol_trampoline_shared_secret) ;
2733
+
2734
+ let forwarding_tlvs = blinded_path:: payment:: TrampolineForwardTlvs {
2735
+ next_trampoline : dave_node_id,
2736
+ payment_relay : PaymentRelay {
2737
+ cltv_expiry_delta : 224 ,
2738
+ fee_proportional_millionths : 0 ,
2739
+ fee_base_msat : 2000 ,
2740
+ } ,
2741
+ payment_constraints : PaymentConstraints {
2742
+ max_cltv_expiry : u32:: max_value ( ) ,
2743
+ htlc_minimum_msat : amt_msat
2744
+ } ,
2745
+ features : BlindedHopFeatures :: empty ( ) ,
2746
+ next_blinding_override : None ,
2747
+ } ;
2748
+ let carol_unblinded_tlvs = forwarding_tlvs. encode ( ) ;
2749
+
2750
+ let payee_tlvs = UnauthenticatedReceiveTlvs {
2751
+ payment_secret,
2752
+ payment_constraints : PaymentConstraints {
2753
+ max_cltv_expiry : u32:: max_value ( ) ,
2754
+ htlc_minimum_msat : amt_msat,
2755
+ } ,
2756
+ payment_context : PaymentContext :: Bolt12Refund ( Bolt12RefundContext { } ) ,
2757
+ } ;
2758
+
2759
+ let nonce = Nonce ( [ 42u8 ; 16 ] ) ;
2760
+ let expanded_key = nodes[ 3 ] . keys_manager . get_inbound_payment_key ( ) ;
2761
+ let payee_tlvs = payee_tlvs. authenticate ( nonce, & expanded_key) ;
2762
+ let dave_unblinded_tlvs = payee_tlvs. encode ( ) ;
2763
+
2764
+ let path = [ ( carol_node_id, WithoutLength ( & carol_unblinded_tlvs) ) , ( dave_node_id, WithoutLength ( & dave_unblinded_tlvs) ) ] ;
2765
+ let blinded_hops = blinded_path:: utils:: construct_blinded_hops (
2766
+ & secp_ctx, path. into_iter ( ) , & alice_carol_trampoline_shared_secret,
2767
+ ) . unwrap ( ) ;
2768
+
2769
+ let route = Route {
2770
+ paths : vec ! [ Path {
2771
+ hops: vec![
2772
+ // Bob
2773
+ RouteHop {
2774
+ pubkey: bob_node_id,
2775
+ node_features: NodeFeatures :: empty( ) ,
2776
+ short_channel_id: alice_bob_scid,
2777
+ channel_features: ChannelFeatures :: empty( ) ,
2778
+ fee_msat: 1000 , // forwarding fee to Carol
2779
+ cltv_expiry_delta: 48 ,
2780
+ maybe_announced_channel: false ,
2781
+ } ,
2782
+
2783
+ // Carol
2784
+ RouteHop {
2785
+ pubkey: carol_node_id,
2786
+ node_features: NodeFeatures :: empty( ) ,
2787
+ short_channel_id: bob_carol_scid,
2788
+ channel_features: ChannelFeatures :: empty( ) ,
2789
+ fee_msat: 2000 , // fee for the usage of the entire blinded path, including Trampoline
2790
+ cltv_expiry_delta: 48 ,
2791
+ maybe_announced_channel: false ,
2792
+ }
2793
+ ] ,
2794
+ blinded_tail: Some ( BlindedTail {
2795
+ trampoline_hops: vec![
2796
+ // Carol
2797
+ TrampolineHop {
2798
+ pubkey: carol_node_id,
2799
+ node_features: Features :: empty( ) ,
2800
+ fee_msat: amt_msat,
2801
+ cltv_expiry_delta: 176 , // let her cook
2802
+ } ,
2803
+ ] ,
2804
+ hops: blinded_hops,
2805
+ blinding_point: carol_blinding_point,
2806
+ excess_final_cltv_expiry_delta: 39 ,
2807
+ final_value_msat: amt_msat,
2808
+ } )
2809
+ } ] ,
2810
+ route_params : None ,
2811
+ } ;
2812
+
2813
+ nodes[ 0 ] . node . send_payment_with_route ( route. clone ( ) , payment_hash, RecipientOnionFields :: spontaneous_empty ( ) , PaymentId ( payment_hash. 0 ) ) . unwrap ( ) ;
2814
+ check_added_monitors ! ( & nodes[ 0 ] , 1 ) ;
2815
+
2816
+ pass_along_route ( & nodes[ 0 ] , & [ & [ & nodes[ 1 ] , & nodes[ 2 ] , & nodes[ 4 ] , & nodes[ 3 ] ] ] , amt_msat, payment_hash, payment_secret) ;
2817
+ claim_payment ( & nodes[ 0 ] , & [ & nodes[ 1 ] , & nodes[ 2 ] , & nodes[ 4 ] , & nodes[ 3 ] ] , payment_preimage) ;
2818
+ }
0 commit comments