@@ -40,6 +40,7 @@ use crate::util::test_utils;
4040use lightning_invoice:: RawBolt11Invoice ;
4141use types:: features:: Features ;
4242use crate :: blinded_path:: BlindedHop ;
43+ use crate :: routing:: router:: Route ;
4344
4445pub fn blinded_payment_path (
4546 payment_secret : PaymentSecret , intro_node_min_htlc : u64 , intro_node_max_htlc : u64 ,
@@ -1957,3 +1958,219 @@ fn test_trampoline_inbound_payment_decoding() {
19571958 panic ! ( ) ;
19581959 } ;
19591960}
1961+
1962+ fn do_test_trampoline_single_hop_receive ( success : bool ) {
1963+ const TOTAL_NODE_COUNT : usize = 3 ;
1964+ let secp_ctx = Secp256k1 :: new ( ) ;
1965+
1966+ let chanmon_cfgs = create_chanmon_cfgs ( TOTAL_NODE_COUNT ) ;
1967+ let node_cfgs = create_node_cfgs ( TOTAL_NODE_COUNT , & chanmon_cfgs) ;
1968+ let node_chanmgrs = create_node_chanmgrs ( TOTAL_NODE_COUNT , & node_cfgs, & vec ! [ None ; TOTAL_NODE_COUNT ] ) ;
1969+ let mut nodes = create_network ( TOTAL_NODE_COUNT , & node_cfgs, & node_chanmgrs) ;
1970+
1971+ let ( _, _, chan_id_alice_bob, _) = create_announced_chan_between_nodes_with_value ( & nodes, 0 , 1 , 1_000_000 , 0 ) ;
1972+ let ( _, _, chan_id_bob_carol, _) = create_announced_chan_between_nodes_with_value ( & nodes, 1 , 2 , 1_000_000 , 0 ) ;
1973+
1974+ for i in 0 ..TOTAL_NODE_COUNT { // connect all nodes' blocks
1975+ connect_blocks ( & nodes[ i] , ( TOTAL_NODE_COUNT as u32 ) * CHAN_CONFIRM_DEPTH + 1 - nodes[ i] . best_block_info ( ) . 1 ) ;
1976+ }
1977+
1978+ let alice_node_id = nodes[ 0 ] . node ( ) . get_our_node_id ( ) ;
1979+ let bob_node_id = nodes[ 1 ] . node ( ) . get_our_node_id ( ) ;
1980+ let carol_node_id = nodes[ 2 ] . node ( ) . get_our_node_id ( ) ;
1981+
1982+ let alice_bob_scid = nodes[ 0 ] . node ( ) . list_channels ( ) . iter ( ) . find ( |c| c. channel_id == chan_id_alice_bob) . unwrap ( ) . short_channel_id . unwrap ( ) ;
1983+ let bob_carol_scid = nodes[ 1 ] . node ( ) . list_channels ( ) . iter ( ) . find ( |c| c. channel_id == chan_id_bob_carol) . unwrap ( ) . short_channel_id . unwrap ( ) ;
1984+
1985+ let amt_msat = 1000 ;
1986+ let ( payment_preimage, payment_hash, payment_secret) = get_payment_preimage_hash ( & nodes[ 2 ] , Some ( amt_msat) , None ) ;
1987+
1988+ let carol_alice_trampoline_session_priv = secret_from_hex ( "a0f4b8d7b6c2d0ffdfaf718f76e9decaef4d9fb38a8c4addb95c4007cc3eee03" ) ;
1989+ let carol_blinding_point = PublicKey :: from_secret_key ( & secp_ctx, & carol_alice_trampoline_session_priv) ;
1990+ let carol_blinded_hops = if success {
1991+ let payee_tlvs = UnauthenticatedReceiveTlvs {
1992+ payment_secret,
1993+ payment_constraints : PaymentConstraints {
1994+ max_cltv_expiry : u32:: max_value ( ) ,
1995+ htlc_minimum_msat : amt_msat,
1996+ } ,
1997+ payment_context : PaymentContext :: Bolt12Refund ( Bolt12RefundContext { } ) ,
1998+ } ;
1999+
2000+ let nonce = Nonce ( [ 42u8 ; 16 ] ) ;
2001+ let expanded_key = nodes[ 2 ] . keys_manager . get_inbound_payment_key ( ) ;
2002+ let payee_tlvs = payee_tlvs. authenticate ( nonce, & expanded_key) ;
2003+ let carol_unblinded_tlvs = payee_tlvs. encode ( ) ;
2004+
2005+ let path = [ ( carol_node_id, WithoutLength ( & carol_unblinded_tlvs) ) ] ;
2006+ blinded_path:: utils:: construct_blinded_hops (
2007+ & secp_ctx, path. into_iter ( ) , & carol_alice_trampoline_session_priv
2008+ ) . unwrap ( )
2009+ } else {
2010+ let payee_tlvs = blinded_path:: payment:: TrampolineForwardTlvs {
2011+ next_trampoline : alice_node_id,
2012+ payment_constraints : PaymentConstraints {
2013+ max_cltv_expiry : u32:: max_value ( ) ,
2014+ htlc_minimum_msat : amt_msat,
2015+ } ,
2016+ features : BlindedHopFeatures :: empty ( ) ,
2017+ payment_relay : PaymentRelay {
2018+ cltv_expiry_delta : 0 ,
2019+ fee_proportional_millionths : 0 ,
2020+ fee_base_msat : 0 ,
2021+ } ,
2022+ next_blinding_override : None ,
2023+ } ;
2024+
2025+ let carol_unblinded_tlvs = payee_tlvs. encode ( ) ;
2026+ let path = [ ( carol_node_id, WithoutLength ( & carol_unblinded_tlvs) ) ] ;
2027+ blinded_path:: utils:: construct_blinded_hops (
2028+ & secp_ctx, path. into_iter ( ) , & carol_alice_trampoline_session_priv
2029+ ) . unwrap ( )
2030+ } ;
2031+
2032+ let route = Route {
2033+ paths : vec ! [ Path {
2034+ hops: vec![
2035+ // Bob
2036+ RouteHop {
2037+ pubkey: bob_node_id,
2038+ node_features: NodeFeatures :: empty( ) ,
2039+ short_channel_id: alice_bob_scid,
2040+ channel_features: ChannelFeatures :: empty( ) ,
2041+ fee_msat: 1000 ,
2042+ cltv_expiry_delta: 48 ,
2043+ maybe_announced_channel: false ,
2044+ } ,
2045+
2046+ // Carol
2047+ RouteHop {
2048+ pubkey: carol_node_id,
2049+ node_features: NodeFeatures :: empty( ) ,
2050+ short_channel_id: bob_carol_scid,
2051+ channel_features: ChannelFeatures :: empty( ) ,
2052+ fee_msat: 0 ,
2053+ cltv_expiry_delta: 48 ,
2054+ maybe_announced_channel: false ,
2055+ }
2056+ ] ,
2057+ blinded_tail: Some ( BlindedTail {
2058+ trampoline_hops: vec![
2059+ // Carol
2060+ TrampolineHop {
2061+ pubkey: carol_node_id,
2062+ node_features: Features :: empty( ) ,
2063+ fee_msat: amt_msat,
2064+ cltv_expiry_delta: 24 ,
2065+ } ,
2066+ ] ,
2067+ hops: carol_blinded_hops,
2068+ blinding_point: carol_blinding_point,
2069+ excess_final_cltv_expiry_delta: 39 ,
2070+ final_value_msat: amt_msat,
2071+ } )
2072+ } ] ,
2073+ route_params : None ,
2074+ } ;
2075+
2076+ nodes[ 0 ] . node . send_payment_with_route ( route. clone ( ) , payment_hash, RecipientOnionFields :: spontaneous_empty ( ) , PaymentId ( payment_hash. 0 ) ) . unwrap ( ) ;
2077+
2078+ check_added_monitors ! ( & nodes[ 0 ] , 1 ) ;
2079+
2080+ if success {
2081+ pass_along_route ( & nodes[ 0 ] , & [ & [ & nodes[ 1 ] , & nodes[ 2 ] ] ] , amt_msat, payment_hash, payment_secret) ;
2082+ claim_payment ( & nodes[ 0 ] , & [ & nodes[ 1 ] , & nodes[ 2 ] ] , payment_preimage) ;
2083+ } else {
2084+ let replacement_onion = {
2085+ // create a substitute onion where the last Trampoline hop is a forward
2086+ let trampoline_secret_key = secret_from_hex ( "0134928f7b7ca6769080d70f16be84c812c741f545b49a34db47ce338a205799" ) ;
2087+ let prng_seed = secret_from_hex ( "fe02b4b9054302a3ddf4e1e9f7c411d644aebbd295218ab009dca94435f775a9" ) ;
2088+ let recipient_onion_fields = RecipientOnionFields :: spontaneous_empty ( ) ;
2089+
2090+ let mut blinded_tail = route. paths [ 0 ] . blinded_tail . clone ( ) . unwrap ( ) ;
2091+
2092+ // append some dummy blinded hop so the intro hop looks like a forward
2093+ blinded_tail. hops . push ( BlindedHop {
2094+ blinded_node_id : alice_node_id,
2095+ encrypted_payload : vec ! [ ] ,
2096+ } ) ;
2097+
2098+ 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 ( ) ;
2099+
2100+ // pop the last dummy hop
2101+ trampoline_payloads. pop ( ) ;
2102+
2103+ let trampoline_onion_keys = onion_utils:: construct_trampoline_onion_keys ( & secp_ctx, & route. paths [ 0 ] . blinded_tail . as_ref ( ) . unwrap ( ) , & trampoline_secret_key) . unwrap ( ) ;
2104+ let trampoline_packet = onion_utils:: construct_trampoline_onion_packet (
2105+ trampoline_payloads,
2106+ trampoline_onion_keys,
2107+ prng_seed. secret_bytes ( ) ,
2108+ & payment_hash,
2109+ None ,
2110+ ) . unwrap ( ) ;
2111+
2112+ let outer_session_priv = secret_from_hex ( "e52c20461ed7acd46c4e7b591a37610519179482887bd73bf3b94617f8f03677" ) ;
2113+
2114+ 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 ( ) ;
2115+ let outer_onion_keys = onion_utils:: construct_onion_keys ( & secp_ctx, & route. clone ( ) . paths [ 0 ] , & outer_session_priv) . unwrap ( ) ;
2116+ let outer_packet = onion_utils:: construct_onion_packet (
2117+ outer_payloads,
2118+ outer_onion_keys,
2119+ prng_seed. secret_bytes ( ) ,
2120+ & payment_hash,
2121+ ) . unwrap ( ) ;
2122+
2123+ outer_packet
2124+ } ;
2125+
2126+ let mut events = nodes[ 0 ] . node . get_and_clear_pending_msg_events ( ) ;
2127+ assert_eq ! ( events. len( ) , 1 ) ;
2128+ let mut first_message_event = remove_first_msg_event_to_node ( & nodes[ 1 ] . node . get_our_node_id ( ) , & mut events) ;
2129+ let mut update_message = match first_message_event {
2130+ MessageSendEvent :: UpdateHTLCs { ref mut updates, .. } => {
2131+ assert_eq ! ( updates. update_add_htlcs. len( ) , 1 ) ;
2132+ updates. update_add_htlcs . get_mut ( 0 )
2133+ } ,
2134+ _ => panic ! ( )
2135+ } ;
2136+ update_message. map ( |msg| {
2137+ msg. onion_routing_packet = replacement_onion. clone ( ) ;
2138+ } ) ;
2139+
2140+ let route: & [ & Node ] = & [ & nodes[ 1 ] , & nodes[ 2 ] ] ;
2141+ let args = PassAlongPathArgs :: new ( & nodes[ 0 ] , route, amt_msat, payment_hash, first_message_event)
2142+ . with_payment_preimage ( payment_preimage)
2143+ . without_claimable_event ( )
2144+ . expect_failure ( HTLCDestination :: InvalidOnion ) ;
2145+ do_pass_along_path ( args) ;
2146+
2147+ {
2148+ let unblinded_node_updates = get_htlc_update_msgs ! ( nodes[ 2 ] , nodes[ 1 ] . node. get_our_node_id( ) ) ;
2149+ nodes[ 1 ] . node . handle_update_fail_htlc (
2150+ nodes[ 2 ] . node . get_our_node_id ( ) , & unblinded_node_updates. update_fail_htlcs [ 0 ]
2151+ ) ;
2152+ do_commitment_signed_dance ( & nodes[ 1 ] , & nodes[ 2 ] , & unblinded_node_updates. commitment_signed , true , false ) ;
2153+ }
2154+ {
2155+ let unblinded_node_updates = get_htlc_update_msgs ! ( nodes[ 1 ] , nodes[ 0 ] . node. get_our_node_id( ) ) ;
2156+ nodes[ 0 ] . node . handle_update_fail_htlc (
2157+ nodes[ 1 ] . node . get_our_node_id ( ) , & unblinded_node_updates. update_fail_htlcs [ 0 ]
2158+ ) ;
2159+ do_commitment_signed_dance ( & nodes[ 0 ] , & nodes[ 1 ] , & unblinded_node_updates. commitment_signed , false , false ) ;
2160+ }
2161+ {
2162+ let payment_failed_conditions = PaymentFailedConditions :: new ( )
2163+ . expected_htlc_error_data ( INVALID_ONION_BLINDING , & [ 0 ; 0 ] ) ;
2164+ expect_payment_failed_conditions ( & nodes[ 0 ] , payment_hash, true , payment_failed_conditions) ;
2165+ }
2166+ }
2167+ }
2168+
2169+ #[ test]
2170+ fn test_trampoline_single_hop_receive ( ) {
2171+ // Simulate a payment of A (0) -> B (1) -> C(Trampoline (blinded intro)) (2)
2172+ do_test_trampoline_single_hop_receive ( true ) ;
2173+
2174+ // Simulate a payment failure of A (0) -> B (1) -> C(Trampoline (blinded forward)) (2)
2175+ do_test_trampoline_single_hop_receive ( false ) ;
2176+ }
0 commit comments