@@ -41,6 +41,9 @@ use lightning_invoice::RawBolt11Invoice;
4141use types:: features:: Features ;
4242use crate :: blinded_path:: BlindedHop ;
4343
44+ #[ cfg( trampoline) ]
45+ use crate :: routing:: router:: Route ;
46+
4447pub fn blinded_payment_path (
4548 payment_secret : PaymentSecret , intro_node_min_htlc : u64 , intro_node_max_htlc : u64 ,
4649 node_ids : Vec < PublicKey > , channel_upds : & [ & msgs:: UnsignedChannelUpdate ] ,
@@ -1958,3 +1961,103 @@ fn test_trampoline_inbound_payment_decoding() {
19581961 panic ! ( ) ;
19591962 } ;
19601963}
1964+
1965+ #[ test]
1966+ #[ cfg( trampoline) ]
1967+ fn test_successful_trampoline_single_hop_receive ( ) {
1968+ // Simulate a payment of A (0) -> B (1) -> C(Trampoline (blinded intro)) (2)
1969+
1970+ const TOTAL_NODE_COUNT : usize = 3 ;
1971+ let secp_ctx = Secp256k1 :: new ( ) ;
1972+
1973+ let chanmon_cfgs = create_chanmon_cfgs ( TOTAL_NODE_COUNT ) ;
1974+ let node_cfgs = create_node_cfgs ( TOTAL_NODE_COUNT , & chanmon_cfgs) ;
1975+ let node_chanmgrs = create_node_chanmgrs ( TOTAL_NODE_COUNT , & node_cfgs, & vec ! [ None ; TOTAL_NODE_COUNT ] ) ;
1976+ let mut nodes = create_network ( TOTAL_NODE_COUNT , & node_cfgs, & node_chanmgrs) ;
1977+
1978+ let ( _, _, chan_id_alice_bob, _) = create_announced_chan_between_nodes_with_value ( & nodes, 0 , 1 , 1_000_000 , 0 ) ;
1979+ let ( _, _, chan_id_bob_carol, _) = create_announced_chan_between_nodes_with_value ( & nodes, 1 , 2 , 1_000_000 , 0 ) ;
1980+
1981+ for i in 0 ..TOTAL_NODE_COUNT { // connect all nodes' blocks
1982+ connect_blocks ( & nodes[ i] , ( TOTAL_NODE_COUNT as u32 ) * CHAN_CONFIRM_DEPTH + 1 - nodes[ i] . best_block_info ( ) . 1 ) ;
1983+ }
1984+
1985+ let bob_node_id = nodes[ 1 ] . node ( ) . get_our_node_id ( ) ;
1986+ let carol_node_id = nodes[ 2 ] . node ( ) . get_our_node_id ( ) ;
1987+
1988+ let alice_bob_scid = nodes[ 0 ] . node ( ) . list_channels ( ) . iter ( ) . find ( |c| c. channel_id == chan_id_alice_bob) . unwrap ( ) . short_channel_id . unwrap ( ) ;
1989+ let bob_carol_scid = nodes[ 1 ] . node ( ) . list_channels ( ) . iter ( ) . find ( |c| c. channel_id == chan_id_bob_carol) . unwrap ( ) . short_channel_id . unwrap ( ) ;
1990+
1991+ let amt_msat = 1000 ;
1992+ let ( payment_preimage, payment_hash, payment_secret) = get_payment_preimage_hash ( & nodes[ 2 ] , Some ( amt_msat) , None ) ;
1993+ let payee_tlvs = UnauthenticatedReceiveTlvs {
1994+ payment_secret,
1995+ payment_constraints : PaymentConstraints {
1996+ max_cltv_expiry : u32:: max_value ( ) ,
1997+ htlc_minimum_msat : amt_msat,
1998+ } ,
1999+ payment_context : PaymentContext :: Bolt12Refund ( Bolt12RefundContext { } ) ,
2000+ } ;
2001+
2002+ let nonce = Nonce ( [ 42u8 ; 16 ] ) ;
2003+ let expanded_key = nodes[ 2 ] . keys_manager . get_inbound_payment_key ( ) ;
2004+ let payee_tlvs = payee_tlvs. authenticate ( nonce, & expanded_key) ;
2005+ let carol_unblinded_tlvs = payee_tlvs. encode ( ) ;
2006+
2007+ let path = [ ( carol_node_id, WithoutLength ( & carol_unblinded_tlvs) ) ] ;
2008+ let carol_alice_trampoline_session_priv = secret_from_hex ( "a0f4b8d7b6c2d0ffdfaf718f76e9decaef4d9fb38a8c4addb95c4007cc3eee03" ) ;
2009+ let carol_blinding_point = PublicKey :: from_secret_key ( & secp_ctx, & carol_alice_trampoline_session_priv) ;
2010+ let carol_blinded_hops = blinded_path:: utils:: construct_blinded_hops (
2011+ & secp_ctx, path. into_iter ( ) , & carol_alice_trampoline_session_priv
2012+ ) . unwrap ( ) ;
2013+
2014+ let route = Route {
2015+ paths : vec ! [ Path {
2016+ hops: vec![
2017+ // Bob
2018+ RouteHop {
2019+ pubkey: bob_node_id,
2020+ node_features: NodeFeatures :: empty( ) ,
2021+ short_channel_id: alice_bob_scid,
2022+ channel_features: ChannelFeatures :: empty( ) ,
2023+ fee_msat: 1000 ,
2024+ cltv_expiry_delta: 48 ,
2025+ maybe_announced_channel: false ,
2026+ } ,
2027+
2028+ // Carol
2029+ RouteHop {
2030+ pubkey: carol_node_id,
2031+ node_features: NodeFeatures :: empty( ) ,
2032+ short_channel_id: bob_carol_scid,
2033+ channel_features: ChannelFeatures :: empty( ) ,
2034+ fee_msat: 0 ,
2035+ cltv_expiry_delta: 48 ,
2036+ maybe_announced_channel: false ,
2037+ }
2038+ ] ,
2039+ blinded_tail: Some ( BlindedTail {
2040+ trampoline_hops: vec![
2041+ // Carol
2042+ TrampolineHop {
2043+ pubkey: carol_node_id,
2044+ node_features: Features :: empty( ) ,
2045+ fee_msat: amt_msat,
2046+ cltv_expiry_delta: 24 ,
2047+ } ,
2048+ ] ,
2049+ hops: carol_blinded_hops,
2050+ blinding_point: carol_blinding_point,
2051+ excess_final_cltv_expiry_delta: 39 ,
2052+ final_value_msat: amt_msat,
2053+ } )
2054+ } ] ,
2055+ route_params : None ,
2056+ } ;
2057+
2058+ nodes[ 0 ] . node . send_payment_with_route ( route, payment_hash, RecipientOnionFields :: spontaneous_empty ( ) , PaymentId ( payment_hash. 0 ) ) . unwrap ( ) ;
2059+
2060+ check_added_monitors ! ( & nodes[ 0 ] , 1 ) ;
2061+ pass_along_route ( & nodes[ 0 ] , & [ & [ & nodes[ 1 ] , & nodes[ 2 ] ] ] , amt_msat, payment_hash, payment_secret) ;
2062+ claim_payment ( & nodes[ 0 ] , & [ & nodes[ 1 ] , & nodes[ 2 ] ] , payment_preimage) ;
2063+ }
0 commit comments