@@ -1982,6 +1982,194 @@ fn test_trampoline_inbound_payment_decoding() {
19821982 } ;
19831983}
19841984
1985+ #[ test]
1986+ fn test_trampoline_forward_payload_encoded_as_receive ( ) {
1987+ // Test that we'll fail backwards as expected when receiving a well-formed blinded forward
1988+ // trampoline onion payload with no next hop present.
1989+ const TOTAL_NODE_COUNT : usize = 3 ;
1990+ let secp_ctx = Secp256k1 :: new ( ) ;
1991+
1992+ let chanmon_cfgs = create_chanmon_cfgs ( TOTAL_NODE_COUNT ) ;
1993+ let node_cfgs = create_node_cfgs ( TOTAL_NODE_COUNT , & chanmon_cfgs) ;
1994+ let node_chanmgrs = create_node_chanmgrs ( TOTAL_NODE_COUNT , & node_cfgs, & vec ! [ None ; TOTAL_NODE_COUNT ] ) ;
1995+ let mut nodes = create_network ( TOTAL_NODE_COUNT , & node_cfgs, & node_chanmgrs) ;
1996+
1997+ let ( _, _, chan_id_alice_bob, _) = create_announced_chan_between_nodes_with_value ( & nodes, 0 , 1 , 1_000_000 , 0 ) ;
1998+ let ( _, _, chan_id_bob_carol, _) = create_announced_chan_between_nodes_with_value ( & nodes, 1 , 2 , 1_000_000 , 0 ) ;
1999+
2000+ for i in 0 ..TOTAL_NODE_COUNT { // connect all nodes' blocks
2001+ connect_blocks ( & nodes[ i] , ( TOTAL_NODE_COUNT as u32 ) * CHAN_CONFIRM_DEPTH + 1 - nodes[ i] . best_block_info ( ) . 1 ) ;
2002+ }
2003+
2004+ let alice_node_id = nodes[ 0 ] . node ( ) . get_our_node_id ( ) ;
2005+ let bob_node_id = nodes[ 1 ] . node ( ) . get_our_node_id ( ) ;
2006+ let carol_node_id = nodes[ 2 ] . node ( ) . get_our_node_id ( ) ;
2007+
2008+ let alice_bob_scid = nodes[ 0 ] . node ( ) . list_channels ( ) . iter ( ) . find ( |c| c. channel_id == chan_id_alice_bob) . unwrap ( ) . short_channel_id . unwrap ( ) ;
2009+ let bob_carol_scid = nodes[ 1 ] . node ( ) . list_channels ( ) . iter ( ) . find ( |c| c. channel_id == chan_id_bob_carol) . unwrap ( ) . short_channel_id . unwrap ( ) ;
2010+
2011+ let amt_msat = 1000 ;
2012+ let ( payment_preimage, payment_hash, _) = get_payment_preimage_hash ( & nodes[ 2 ] , Some ( amt_msat) , None ) ;
2013+
2014+ // We need the session priv to construct an invalid onion packet later.
2015+ let override_random_bytes = [ 3 ; 32 ] ;
2016+ * nodes[ 0 ] . keys_manager . override_random_bytes . lock ( ) . unwrap ( ) = Some ( override_random_bytes) ;
2017+
2018+ let outer_session_priv = SecretKey :: from_slice ( & override_random_bytes) . unwrap ( ) ;
2019+ let trampoline_session_priv = onion_utils:: compute_trampoline_session_priv ( & outer_session_priv) ;
2020+
2021+ // Create a blinded hop for the recipient that is encoded as a trampoline forward.
2022+ let carol_blinding_point = PublicKey :: from_secret_key ( & secp_ctx, & trampoline_session_priv) ;
2023+ let carol_blinded_hops = {
2024+ let payee_tlvs = blinded_path:: payment:: TrampolineForwardTlvs {
2025+ next_trampoline : alice_node_id,
2026+ payment_constraints : PaymentConstraints {
2027+ max_cltv_expiry : u32:: max_value ( ) ,
2028+ htlc_minimum_msat : amt_msat,
2029+ } ,
2030+ features : BlindedHopFeatures :: empty ( ) ,
2031+ payment_relay : PaymentRelay {
2032+ cltv_expiry_delta : 0 ,
2033+ fee_proportional_millionths : 0 ,
2034+ fee_base_msat : 0 ,
2035+ } ,
2036+ next_blinding_override : None ,
2037+ } ;
2038+
2039+ let carol_unblinded_tlvs = payee_tlvs. encode ( ) ;
2040+ let path = [ ( ( carol_node_id, None ) , WithoutLength ( & carol_unblinded_tlvs) ) ] ;
2041+ blinded_path:: utils:: construct_blinded_hops (
2042+ & secp_ctx, path. into_iter ( ) , & trampoline_session_priv,
2043+ ) . unwrap ( )
2044+ } ;
2045+
2046+ let route = Route {
2047+ paths : vec ! [ Path {
2048+ hops: vec![
2049+ // Bob
2050+ RouteHop {
2051+ pubkey: bob_node_id,
2052+ node_features: NodeFeatures :: empty( ) ,
2053+ short_channel_id: alice_bob_scid,
2054+ channel_features: ChannelFeatures :: empty( ) ,
2055+ fee_msat: 1000 ,
2056+ cltv_expiry_delta: 48 ,
2057+ maybe_announced_channel: false ,
2058+ } ,
2059+
2060+ // Carol
2061+ RouteHop {
2062+ pubkey: carol_node_id,
2063+ node_features: NodeFeatures :: empty( ) ,
2064+ short_channel_id: bob_carol_scid,
2065+ channel_features: ChannelFeatures :: empty( ) ,
2066+ fee_msat: 0 ,
2067+ cltv_expiry_delta: 48 ,
2068+ maybe_announced_channel: false ,
2069+ }
2070+ ] ,
2071+ blinded_tail: Some ( BlindedTail {
2072+ trampoline_hops: vec![
2073+ // Carol
2074+ TrampolineHop {
2075+ pubkey: carol_node_id,
2076+ node_features: Features :: empty( ) ,
2077+ fee_msat: amt_msat,
2078+ cltv_expiry_delta: 24 ,
2079+ } ,
2080+ ] ,
2081+ hops: carol_blinded_hops,
2082+ blinding_point: carol_blinding_point,
2083+ excess_final_cltv_expiry_delta: 39 ,
2084+ final_value_msat: amt_msat,
2085+ } )
2086+ } ] ,
2087+ route_params : None ,
2088+ } ;
2089+
2090+ nodes[ 0 ] . node . send_payment_with_route ( route. clone ( ) , payment_hash, RecipientOnionFields :: spontaneous_empty ( ) , PaymentId ( payment_hash. 0 ) ) . unwrap ( ) ;
2091+ check_added_monitors ! ( & nodes[ 0 ] , 1 ) ;
2092+
2093+ let replacement_onion = {
2094+ // create a substitute onion where the last Trampoline hop is a forward
2095+ let recipient_onion_fields = RecipientOnionFields :: spontaneous_empty ( ) ;
2096+
2097+ let mut blinded_tail = route. paths [ 0 ] . blinded_tail . clone ( ) . unwrap ( ) ;
2098+
2099+ // append some dummy blinded hop so the intro hop looks like a forward
2100+ blinded_tail. hops . push ( BlindedHop {
2101+ blinded_node_id : alice_node_id,
2102+ encrypted_payload : vec ! [ ] ,
2103+ } ) ;
2104+
2105+ let ( mut trampoline_payloads, outer_total_msat, outer_starting_htlc_offset) = onion_utils:: build_trampoline_onion_payloads ( & blinded_tail, amt_msat, & recipient_onion_fields, 32 , & None ) . unwrap ( ) ;
2106+
2107+ // pop the last dummy hop
2108+ trampoline_payloads. pop ( ) ;
2109+
2110+ let trampoline_onion_keys = onion_utils:: construct_trampoline_onion_keys ( & secp_ctx, & route. paths [ 0 ] . blinded_tail . as_ref ( ) . unwrap ( ) , & trampoline_session_priv) ;
2111+ let trampoline_packet = onion_utils:: construct_trampoline_onion_packet (
2112+ trampoline_payloads,
2113+ trampoline_onion_keys,
2114+ override_random_bytes,
2115+ & payment_hash,
2116+ None ,
2117+ ) . unwrap ( ) ;
2118+
2119+ let ( outer_payloads, _, _) = onion_utils:: build_onion_payloads ( & route. paths [ 0 ] , outer_total_msat, & recipient_onion_fields, outer_starting_htlc_offset, & None , None , Some ( trampoline_packet) ) . unwrap ( ) ;
2120+ let outer_onion_keys = onion_utils:: construct_onion_keys ( & secp_ctx, & route. clone ( ) . paths [ 0 ] , & outer_session_priv) ;
2121+ let outer_packet = onion_utils:: construct_onion_packet (
2122+ outer_payloads,
2123+ outer_onion_keys,
2124+ override_random_bytes,
2125+ & payment_hash,
2126+ ) . unwrap ( ) ;
2127+
2128+ outer_packet
2129+ } ;
2130+
2131+ let mut events = nodes[ 0 ] . node . get_and_clear_pending_msg_events ( ) ;
2132+ assert_eq ! ( events. len( ) , 1 ) ;
2133+ let mut first_message_event = remove_first_msg_event_to_node ( & nodes[ 1 ] . node . get_our_node_id ( ) , & mut events) ;
2134+ let mut update_message = match first_message_event {
2135+ MessageSendEvent :: UpdateHTLCs { ref mut updates, .. } => {
2136+ assert_eq ! ( updates. update_add_htlcs. len( ) , 1 ) ;
2137+ updates. update_add_htlcs . get_mut ( 0 )
2138+ } ,
2139+ _ => panic ! ( )
2140+ } ;
2141+ update_message. map ( |msg| {
2142+ msg. onion_routing_packet = replacement_onion. clone ( ) ;
2143+ } ) ;
2144+
2145+ let route: & [ & Node ] = & [ & nodes[ 1 ] , & nodes[ 2 ] ] ;
2146+ let args = PassAlongPathArgs :: new ( & nodes[ 0 ] , route, amt_msat, payment_hash, first_message_event)
2147+ . with_payment_preimage ( payment_preimage)
2148+ . without_claimable_event ( )
2149+ . expect_failure ( HTLCHandlingFailureType :: InvalidOnion ) ;
2150+ do_pass_along_path ( args) ;
2151+
2152+ {
2153+ let unblinded_node_updates = get_htlc_update_msgs ! ( nodes[ 2 ] , nodes[ 1 ] . node. get_our_node_id( ) ) ;
2154+ nodes[ 1 ] . node . handle_update_fail_htlc (
2155+ nodes[ 2 ] . node . get_our_node_id ( ) , & unblinded_node_updates. update_fail_htlcs [ 0 ]
2156+ ) ;
2157+ do_commitment_signed_dance ( & nodes[ 1 ] , & nodes[ 2 ] , & unblinded_node_updates. commitment_signed , true , false ) ;
2158+ }
2159+ {
2160+ let unblinded_node_updates = get_htlc_update_msgs ! ( nodes[ 1 ] , nodes[ 0 ] . node. get_our_node_id( ) ) ;
2161+ nodes[ 0 ] . node . handle_update_fail_htlc (
2162+ nodes[ 1 ] . node . get_our_node_id ( ) , & unblinded_node_updates. update_fail_htlcs [ 0 ]
2163+ ) ;
2164+ do_commitment_signed_dance ( & nodes[ 0 ] , & nodes[ 1 ] , & unblinded_node_updates. commitment_signed , false , false ) ;
2165+ }
2166+ {
2167+ let payment_failed_conditions = PaymentFailedConditions :: new ( )
2168+ . expected_htlc_error_data ( LocalHTLCFailureReason :: InvalidOnionPayload , & [ 0 ; 0 ] ) ;
2169+ expect_payment_failed_conditions ( & nodes[ 0 ] , payment_hash, true , payment_failed_conditions) ;
2170+ }
2171+ }
2172+
19852173fn do_test_trampoline_single_hop_receive ( success : bool ) {
19862174 const TOTAL_NODE_COUNT : usize = 3 ;
19872175 let secp_ctx = Secp256k1 :: new ( ) ;
0 commit comments