@@ -2501,6 +2501,187 @@ mod tests {
25012501 assert_eq ! ( decrypted_failure. onion_error_code, Some ( 0x400f ) ) ;
25022502 }
25032503
2504+ #[ test]
2505+ fn test_trampoline_onion_decryption ( ) {
2506+ // In this test, we construct a dummy path that uses Trampoline hops, and ensure that the
2507+ // correct shared secrets are used to decrypt the error packets. The actual path configuration
2508+ // is not particularly relevant.
2509+
2510+ let dummy_amt_msat = 150_000_000 ;
2511+
2512+ let path = Path {
2513+ hops : vec ! [
2514+ // Bob
2515+ RouteHop {
2516+ pubkey: PublicKey :: from_slice( & <Vec <u8 >>:: from_hex( "0324653eac434488002cc06bbfb7f10fe18991e35f9fe4302dbea6d2353dc0ab1c" ) . unwrap( ) ) . unwrap( ) ,
2517+ node_features: NodeFeatures :: empty( ) ,
2518+ short_channel_id: 0 ,
2519+ channel_features: ChannelFeatures :: empty( ) ,
2520+ fee_msat: 3_000 ,
2521+ cltv_expiry_delta: 24 ,
2522+ maybe_announced_channel: false ,
2523+ } ,
2524+
2525+ // Carol
2526+ RouteHop {
2527+ pubkey: PublicKey :: from_slice( & <Vec <u8 >>:: from_hex( "027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007" ) . unwrap( ) ) . unwrap( ) ,
2528+ node_features: NodeFeatures :: empty( ) ,
2529+ short_channel_id: ( 572330 << 40 ) + ( 42 << 16 ) + 2821 ,
2530+ channel_features: ChannelFeatures :: empty( ) ,
2531+ fee_msat: 153_000 ,
2532+ cltv_expiry_delta: 0 ,
2533+ maybe_announced_channel: false ,
2534+ } ,
2535+ ] ,
2536+ blinded_tail : Some ( BlindedTail {
2537+ trampoline_hops : vec ! [
2538+ // Carol's pubkey
2539+ TrampolineHop {
2540+ pubkey: PublicKey :: from_slice( & <Vec <u8 >>:: from_hex( "027f31ebc5462c1fdce1b737ecff52d37d75dea43ce11c74d25aa297165faa2007" ) . unwrap( ) ) . unwrap( ) ,
2541+ node_features: Features :: empty( ) ,
2542+ fee_msat: 2_500 ,
2543+ cltv_expiry_delta: 24 ,
2544+ } ,
2545+ // Dave's pubkey
2546+ TrampolineHop {
2547+ pubkey: PublicKey :: from_slice( & <Vec <u8 >>:: from_hex( "020e2dbadcc2005e859819ddebbe88a834ae8a6d2b049233c07335f15cd1dc5f22" ) . unwrap( ) ) . unwrap( ) ,
2548+ node_features: Features :: empty( ) ,
2549+ fee_msat: 2_500 ,
2550+ cltv_expiry_delta: 24 ,
2551+ } ,
2552+ // Emily's pubkey (the intro node needs to be duplicated)
2553+ TrampolineHop {
2554+ pubkey: PublicKey :: from_slice( & <Vec <u8 >>:: from_hex( "032c0b7cf95324a07d05398b240174dc0c2be444d96b159aa6c7f7b1e668680991" ) . unwrap( ) ) . unwrap( ) ,
2555+ node_features: Features :: empty( ) ,
2556+ fee_msat: 150_500 ,
2557+ cltv_expiry_delta: 36 ,
2558+ }
2559+ ] ,
2560+ hops : vec ! [
2561+ // Emily's blinded node id
2562+ BlindedHop {
2563+ blinded_node_id: PublicKey :: from_slice( & <Vec <u8 >>:: from_hex( "0295d40514096a8be54859e7dfe947b376eaafea8afe5cb4eb2c13ff857ed0b4be" ) . unwrap( ) ) . unwrap( ) ,
2564+ encrypted_payload: vec![ ] ,
2565+ } ,
2566+ // Forrest's blinded node id
2567+ BlindedHop {
2568+ blinded_node_id: PublicKey :: from_slice( & <Vec <u8 >>:: from_hex( "020e2dbadcc2005e859819ddebbe88a834ae8a6d2b049233c07335f15cd1dc5f22" ) . unwrap( ) ) . unwrap( ) ,
2569+ encrypted_payload: vec![ ] ,
2570+ }
2571+ ] ,
2572+ blinding_point : PublicKey :: from_slice ( & <Vec < u8 > >:: from_hex ( "02988face71e92c345a068f740191fd8e53be14f0bb957ef730d3c5f76087b960e" ) . unwrap ( ) ) . unwrap ( ) ,
2573+ excess_final_cltv_expiry_delta : 0 ,
2574+ final_value_msat : dummy_amt_msat
2575+ } ) ,
2576+ } ;
2577+
2578+ // all dummy values
2579+ let secp_ctx = Secp256k1 :: new ( ) ;
2580+ let session_priv = get_test_session_key ( ) ;
2581+
2582+ let trampoline_onion_keys = construct_trampoline_onion_keys (
2583+ & secp_ctx,
2584+ & path. blinded_tail . as_ref ( ) . unwrap ( ) ,
2585+ & session_priv,
2586+ )
2587+ . unwrap ( ) ;
2588+
2589+ let outer_onion_keys = {
2590+ let session_priv_hash = Sha256 :: hash ( & session_priv. secret_bytes ( ) ) . to_byte_array ( ) ;
2591+ let outer_session_priv = SecretKey :: from_slice ( & session_priv_hash[ ..] ) . unwrap ( ) ;
2592+ construct_onion_keys ( & Secp256k1 :: new ( ) , & path, & outer_session_priv) . unwrap ( )
2593+ } ;
2594+
2595+ let logger: Arc < TestLogger > = Arc :: new ( TestLogger :: new ( ) ) ;
2596+ let htlc_source = HTLCSource :: OutboundRoute {
2597+ path,
2598+ session_priv,
2599+ first_hop_htlc_msat : dummy_amt_msat,
2600+ payment_id : PaymentId ( [ 1 ; 32 ] ) ,
2601+ } ;
2602+
2603+ {
2604+ let error_code = 0x2002 ;
2605+ let onion_error = super :: build_failure_packet (
2606+ outer_onion_keys[ 0 ] . shared_secret . as_ref ( ) ,
2607+ error_code,
2608+ & [ 0 ; 0 ] ,
2609+ ) ;
2610+ let first_hop_error_packet = super :: encrypt_failure_packet (
2611+ outer_onion_keys[ 0 ] . shared_secret . as_ref ( ) ,
2612+ & onion_error. encode ( ) [ ..] ,
2613+ ) ;
2614+
2615+ let decrypted_failure = process_onion_failure (
2616+ & secp_ctx,
2617+ & logger,
2618+ & htlc_source,
2619+ first_hop_error_packet. data ,
2620+ None ,
2621+ ) ;
2622+ assert_eq ! ( decrypted_failure. onion_error_code, Some ( error_code) ) ;
2623+ } ;
2624+
2625+ {
2626+ let error_code = 0x2003 ;
2627+ let onion_error = super :: build_failure_packet (
2628+ outer_onion_keys[ 1 ] . shared_secret . as_ref ( ) ,
2629+ error_code,
2630+ & [ 0 ; 0 ] ,
2631+ ) ;
2632+ let error_packet_1 = super :: encrypt_failure_packet (
2633+ outer_onion_keys[ 1 ] . shared_secret . as_ref ( ) ,
2634+ & onion_error. encode ( ) [ ..] ,
2635+ ) ;
2636+
2637+ let trampoline_outer_hop_error_packet = super :: encrypt_failure_packet (
2638+ outer_onion_keys[ 0 ] . shared_secret . as_ref ( ) ,
2639+ & error_packet_1. data [ ..] ,
2640+ ) ;
2641+
2642+ let decrypted_failure = process_onion_failure (
2643+ & secp_ctx,
2644+ & logger,
2645+ & htlc_source,
2646+ trampoline_outer_hop_error_packet. data ,
2647+ None ,
2648+ ) ;
2649+ assert_eq ! ( decrypted_failure. onion_error_code, Some ( error_code) ) ;
2650+ } ;
2651+
2652+ {
2653+ let error_code = 0x2004 ;
2654+ let onion_error = super :: build_failure_packet (
2655+ trampoline_onion_keys[ 0 ] . shared_secret . as_ref ( ) ,
2656+ error_code,
2657+ & [ 0 ; 0 ] ,
2658+ ) ;
2659+ let error_packet_t0 = super :: encrypt_failure_packet (
2660+ trampoline_onion_keys[ 0 ] . shared_secret . as_ref ( ) ,
2661+ & onion_error. encode ( ) [ ..] ,
2662+ ) ;
2663+
2664+ let error_packet_1 = super :: encrypt_failure_packet (
2665+ outer_onion_keys[ 1 ] . shared_secret . as_ref ( ) ,
2666+ & error_packet_t0. data [ ..] ,
2667+ ) ;
2668+
2669+ let trampoline_inner_hop_error_packet = super :: encrypt_failure_packet (
2670+ outer_onion_keys[ 0 ] . shared_secret . as_ref ( ) ,
2671+ & error_packet_1. data [ ..] ,
2672+ ) ;
2673+
2674+ let decrypted_failure = process_onion_failure (
2675+ & secp_ctx,
2676+ & logger,
2677+ & htlc_source,
2678+ trampoline_inner_hop_error_packet. data ,
2679+ None ,
2680+ ) ;
2681+ assert_eq ! ( decrypted_failure. onion_error_code, Some ( error_code) ) ;
2682+ }
2683+ }
2684+
25042685 #[ test]
25052686 fn test_non_attributable_failure_packet_onion ( ) {
25062687 // Create a failure packet with bogus data.
0 commit comments