@@ -2179,3 +2179,172 @@ fn test_trampoline_single_hop_receive() {
2179
2179
// Simulate a payment failure of A (0) -> B (1) -> C(Trampoline (blinded forward)) (2)
2180
2180
do_test_trampoline_single_hop_receive ( false ) ;
2181
2181
}
2182
+
2183
+ #[ test]
2184
+ #[ cfg( trampoline) ]
2185
+ fn test_trampoline_unblinded_receive ( ) {
2186
+ // Simulate a payment of A (0) -> B (1) -> C(Trampoline) (2)
2187
+
2188
+ const TOTAL_NODE_COUNT : usize = 3 ;
2189
+ let secp_ctx = Secp256k1 :: new ( ) ;
2190
+
2191
+ let chanmon_cfgs = create_chanmon_cfgs ( TOTAL_NODE_COUNT ) ;
2192
+ let node_cfgs = create_node_cfgs ( TOTAL_NODE_COUNT , & chanmon_cfgs) ;
2193
+ let node_chanmgrs = create_node_chanmgrs ( TOTAL_NODE_COUNT , & node_cfgs, & vec ! [ None ; TOTAL_NODE_COUNT ] ) ;
2194
+ let mut nodes = create_network ( TOTAL_NODE_COUNT , & node_cfgs, & node_chanmgrs) ;
2195
+
2196
+ let ( _, _, chan_id_alice_bob, _) = create_announced_chan_between_nodes_with_value ( & nodes, 0 , 1 , 1_000_000 , 0 ) ;
2197
+ let ( _, _, chan_id_bob_carol, _) = create_announced_chan_between_nodes_with_value ( & nodes, 1 , 2 , 1_000_000 , 0 ) ;
2198
+
2199
+ for i in 0 ..TOTAL_NODE_COUNT { // connect all nodes' blocks
2200
+ connect_blocks ( & nodes[ i] , ( TOTAL_NODE_COUNT as u32 ) * CHAN_CONFIRM_DEPTH + 1 - nodes[ i] . best_block_info ( ) . 1 ) ;
2201
+ }
2202
+
2203
+ let alice_node_id = nodes[ 0 ] . node ( ) . get_our_node_id ( ) ;
2204
+ let bob_node_id = nodes[ 1 ] . node ( ) . get_our_node_id ( ) ;
2205
+ let carol_node_id = nodes[ 2 ] . node ( ) . get_our_node_id ( ) ;
2206
+
2207
+ let alice_bob_scid = nodes[ 0 ] . node ( ) . list_channels ( ) . iter ( ) . find ( |c| c. channel_id == chan_id_alice_bob) . unwrap ( ) . short_channel_id . unwrap ( ) ;
2208
+ let bob_carol_scid = nodes[ 1 ] . node ( ) . list_channels ( ) . iter ( ) . find ( |c| c. channel_id == chan_id_bob_carol) . unwrap ( ) . short_channel_id . unwrap ( ) ;
2209
+
2210
+ let amt_msat = 1000 ;
2211
+ let ( payment_preimage, payment_hash, payment_secret) = get_payment_preimage_hash ( & nodes[ 2 ] , Some ( amt_msat) , None ) ;
2212
+ let payee_tlvs = blinded_path:: payment:: TrampolineForwardTlvs {
2213
+ next_trampoline : alice_node_id,
2214
+ payment_constraints : PaymentConstraints {
2215
+ max_cltv_expiry : u32:: max_value ( ) ,
2216
+ htlc_minimum_msat : amt_msat,
2217
+ } ,
2218
+ features : BlindedHopFeatures :: empty ( ) ,
2219
+ payment_relay : PaymentRelay {
2220
+ cltv_expiry_delta : 0 ,
2221
+ fee_proportional_millionths : 0 ,
2222
+ fee_base_msat : 0 ,
2223
+ } ,
2224
+ next_blinding_override : None ,
2225
+ } ;
2226
+
2227
+ let carol_unblinded_tlvs = payee_tlvs. encode ( ) ;
2228
+ let path = [ ( carol_node_id, WithoutLength ( & carol_unblinded_tlvs) ) ] ;
2229
+ let carol_alice_trampoline_session_priv = secret_from_hex ( "a0f4b8d7b6c2d0ffdfaf718f76e9decaef4d9fb38a8c4addb95c4007cc3eee03" ) ;
2230
+ let carol_blinding_point = PublicKey :: from_secret_key ( & secp_ctx, & carol_alice_trampoline_session_priv) ;
2231
+ let carol_blinded_hops = blinded_path:: utils:: construct_blinded_hops (
2232
+ & secp_ctx, path. into_iter ( ) , & carol_alice_trampoline_session_priv
2233
+ ) . unwrap ( ) ;
2234
+
2235
+ let route = Route {
2236
+ paths : vec ! [ Path {
2237
+ hops: vec![
2238
+ // Bob
2239
+ RouteHop {
2240
+ pubkey: bob_node_id,
2241
+ node_features: NodeFeatures :: empty( ) ,
2242
+ short_channel_id: alice_bob_scid,
2243
+ channel_features: ChannelFeatures :: empty( ) ,
2244
+ fee_msat: 1000 ,
2245
+ cltv_expiry_delta: 48 ,
2246
+ maybe_announced_channel: false ,
2247
+ } ,
2248
+
2249
+ // Carol
2250
+ RouteHop {
2251
+ pubkey: carol_node_id,
2252
+ node_features: NodeFeatures :: empty( ) ,
2253
+ short_channel_id: bob_carol_scid,
2254
+ channel_features: ChannelFeatures :: empty( ) ,
2255
+ fee_msat: 0 ,
2256
+ cltv_expiry_delta: 48 ,
2257
+ maybe_announced_channel: false ,
2258
+ }
2259
+ ] ,
2260
+ blinded_tail: Some ( BlindedTail {
2261
+ trampoline_hops: vec![
2262
+ // Carol
2263
+ TrampolineHop {
2264
+ pubkey: carol_node_id,
2265
+ node_features: Features :: empty( ) ,
2266
+ fee_msat: amt_msat,
2267
+ cltv_expiry_delta: 24 ,
2268
+ } ,
2269
+ ] ,
2270
+ hops: carol_blinded_hops,
2271
+ blinding_point: carol_blinding_point,
2272
+ excess_final_cltv_expiry_delta: 39 ,
2273
+ final_value_msat: amt_msat,
2274
+ } )
2275
+ } ] ,
2276
+ route_params : None ,
2277
+ } ;
2278
+
2279
+ nodes[ 0 ] . node . send_payment_with_route ( route. clone ( ) , payment_hash, RecipientOnionFields :: spontaneous_empty ( ) , PaymentId ( payment_hash. 0 ) ) . unwrap ( ) ;
2280
+
2281
+ let replacement_onion = {
2282
+ // create a substitute onion where the last Trampoline hop is an unblinded receive, which we
2283
+ // (deliberately) do not support out of the box, therefore necessitating this workaround
2284
+ let trampoline_secret_key = secret_from_hex ( "0134928f7b7ca6769080d70f16be84c812c741f545b49a34db47ce338a205799" ) ;
2285
+ let prng_seed = secret_from_hex ( "fe02b4b9054302a3ddf4e1e9f7c411d644aebbd295218ab009dca94435f775a9" ) ;
2286
+ let recipient_onion_fields = RecipientOnionFields :: spontaneous_empty ( ) ;
2287
+
2288
+ let blinded_tail = route. paths [ 0 ] . blinded_tail . clone ( ) . unwrap ( ) ;
2289
+ 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 ( ) ;
2290
+
2291
+ // pop the last dummy hop
2292
+ trampoline_payloads. pop ( ) ;
2293
+
2294
+ trampoline_payloads. push ( msgs:: OutboundTrampolinePayload :: Receive {
2295
+ payment_data : Some ( msgs:: FinalOnionHopData {
2296
+ payment_secret,
2297
+ total_msat : amt_msat,
2298
+ } ) ,
2299
+ sender_intended_htlc_amt_msat : amt_msat,
2300
+ cltv_expiry_height : 104 ,
2301
+ } ) ;
2302
+
2303
+ let trampoline_onion_keys = onion_utils:: construct_trampoline_onion_keys ( & secp_ctx, & route. paths [ 0 ] . blinded_tail . as_ref ( ) . unwrap ( ) , & trampoline_secret_key) . unwrap ( ) ;
2304
+ let trampoline_packet = onion_utils:: construct_trampoline_onion_packet (
2305
+ trampoline_payloads,
2306
+ trampoline_onion_keys,
2307
+ prng_seed. secret_bytes ( ) ,
2308
+ & payment_hash,
2309
+ None ,
2310
+ ) . unwrap ( ) ;
2311
+
2312
+ let outer_session_priv = secret_from_hex ( "e52c20461ed7acd46c4e7b591a37610519179482887bd73bf3b94617f8f03677" ) ;
2313
+
2314
+ 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 ( ) ;
2315
+ let outer_onion_keys = onion_utils:: construct_onion_keys ( & secp_ctx, & route. clone ( ) . paths [ 0 ] , & outer_session_priv) . unwrap ( ) ;
2316
+ let outer_packet = onion_utils:: construct_onion_packet (
2317
+ outer_payloads,
2318
+ outer_onion_keys,
2319
+ prng_seed. secret_bytes ( ) ,
2320
+ & payment_hash,
2321
+ ) . unwrap ( ) ;
2322
+
2323
+ outer_packet
2324
+ } ;
2325
+
2326
+ check_added_monitors ! ( & nodes[ 0 ] , 1 ) ;
2327
+
2328
+ // Check that we've queued the HTLCs of the async keysend payment.
2329
+ let mut events = nodes[ 0 ] . node . get_and_clear_pending_msg_events ( ) ;
2330
+ assert_eq ! ( events. len( ) , 1 ) ;
2331
+ let mut first_message_event = remove_first_msg_event_to_node ( & nodes[ 1 ] . node . get_our_node_id ( ) , & mut events) ;
2332
+ let mut update_message = match first_message_event {
2333
+ MessageSendEvent :: UpdateHTLCs { ref mut updates, .. } => {
2334
+ assert_eq ! ( updates. update_add_htlcs. len( ) , 1 ) ;
2335
+ updates. update_add_htlcs . get_mut ( 0 )
2336
+ } ,
2337
+ _ => panic ! ( )
2338
+ } ;
2339
+ update_message. map ( |msg| {
2340
+ msg. onion_routing_packet = replacement_onion. clone ( ) ;
2341
+ } ) ;
2342
+
2343
+ let route: & [ & Node ] = & [ & nodes[ 1 ] , & nodes[ 2 ] ] ;
2344
+ let args = PassAlongPathArgs :: new ( & nodes[ 0 ] , route, amt_msat, payment_hash, first_message_event)
2345
+ . with_payment_secret ( payment_secret) ;
2346
+ do_pass_along_path ( args) ;
2347
+
2348
+ claim_payment ( & nodes[ 0 ] , & [ & nodes[ 1 ] , & nodes[ 2 ] ] , payment_preimage) ;
2349
+ }
2350
+
0 commit comments