@@ -3150,3 +3150,259 @@ fn test_blinded_trampoline_forward() {
3150
3150
do_test_blinded_trampoline_forward ( Some ( TrampolineForwardFailureScenario :: InvalidInterTrampolineOnion ) ) ;
3151
3151
do_test_blinded_trampoline_forward ( Some ( TrampolineForwardFailureScenario :: InvalidRecipientOnion ) ) ;
3152
3152
}
3153
+
3154
+ #[ test]
3155
+ fn test_trampoline_mpp_rejection ( ) {
3156
+ // Simulate a payment of A (0) -> B (1) -> C(Trampoline) (2) -> D(Trampoline(receive)) (3)
3157
+ // MPP segment A: trampoline hops C -> T0 (4) -> D
3158
+ // MPP segment B: trampoline hops C -> T1 (5) -> D
3159
+ const TOTAL_NODE_COUNT : usize = 6 ;
3160
+ let secp_ctx = Secp256k1 :: new ( ) ;
3161
+
3162
+ let chanmon_cfgs = create_chanmon_cfgs ( TOTAL_NODE_COUNT ) ;
3163
+ let node_cfgs = create_node_cfgs ( TOTAL_NODE_COUNT , & chanmon_cfgs) ;
3164
+ let node_chanmgrs = create_node_chanmgrs ( TOTAL_NODE_COUNT , & node_cfgs, & vec ! [ None ; TOTAL_NODE_COUNT ] ) ;
3165
+ let mut nodes = create_network ( TOTAL_NODE_COUNT , & node_cfgs, & node_chanmgrs) ;
3166
+
3167
+ let large_channel_size = 21_000 ;
3168
+ let small_channel_size = 15_000 ;
3169
+ let ( _, _, chan_id_alice_bob, _) = create_announced_chan_between_nodes_with_value ( & nodes, 0 , 1 , large_channel_size, 0 ) ;
3170
+ let ( _, _, chan_id_bob_carol, _) = create_announced_chan_between_nodes_with_value ( & nodes, 1 , 2 , large_channel_size, 0 ) ;
3171
+
3172
+ // inter-Trampoline-path A
3173
+ let ( _, _, _, _) = create_announced_chan_between_nodes_with_value ( & nodes, 2 , 4 , small_channel_size, 0 ) ;
3174
+ let ( _, _, _, _) = create_announced_chan_between_nodes_with_value ( & nodes, 4 , 3 , small_channel_size, 0 ) ;
3175
+
3176
+ // inter-Trampoline-path B
3177
+ let ( _, _, _, _) = create_announced_chan_between_nodes_with_value ( & nodes, 2 , 5 , small_channel_size, 0 ) ;
3178
+ let ( _, _, _, _) = create_announced_chan_between_nodes_with_value ( & nodes, 5 , 3 , small_channel_size, 0 ) ;
3179
+
3180
+ for i in 0 ..TOTAL_NODE_COUNT { // connect all nodes' blocks
3181
+ connect_blocks ( & nodes[ i] , ( TOTAL_NODE_COUNT as u32 ) * CHAN_CONFIRM_DEPTH + 1 - nodes[ i] . best_block_info ( ) . 1 ) ;
3182
+ }
3183
+
3184
+ let alice_node_id = nodes[ 0 ] . node ( ) . get_our_node_id ( ) ;
3185
+ let bob_node_id = nodes[ 1 ] . node ( ) . get_our_node_id ( ) ;
3186
+ let carol_node_id = nodes[ 2 ] . node ( ) . get_our_node_id ( ) ;
3187
+ let dave_node_id = nodes[ 3 ] . node ( ) . get_our_node_id ( ) ;
3188
+
3189
+ let alice_bob_scid = nodes[ 0 ] . node ( ) . list_channels ( ) . iter ( ) . find ( |c| c. channel_id == chan_id_alice_bob) . unwrap ( ) . short_channel_id . unwrap ( ) ;
3190
+ let bob_carol_scid = nodes[ 1 ] . node ( ) . list_channels ( ) . iter ( ) . find ( |c| c. channel_id == chan_id_bob_carol) . unwrap ( ) . short_channel_id . unwrap ( ) ;
3191
+
3192
+ let amt_msat = 2_000_000 ; // send 2k satoshis over channels allowing 20k satoshis
3193
+ let ( payment_preimage, payment_hash, payment_secret) = get_payment_preimage_hash ( & nodes[ 3 ] , Some ( amt_msat) , None ) ;
3194
+
3195
+ let route = Route {
3196
+ paths : vec ! [ Path {
3197
+ hops: vec![
3198
+ // Bob
3199
+ RouteHop {
3200
+ pubkey: bob_node_id,
3201
+ node_features: NodeFeatures :: empty( ) ,
3202
+ short_channel_id: alice_bob_scid,
3203
+ channel_features: ChannelFeatures :: empty( ) ,
3204
+ fee_msat: 1000 , // forwarding fee to Carol
3205
+ cltv_expiry_delta: 48 ,
3206
+ maybe_announced_channel: false ,
3207
+ } ,
3208
+
3209
+ // Carol
3210
+ RouteHop {
3211
+ pubkey: carol_node_id,
3212
+ node_features: NodeFeatures :: empty( ) ,
3213
+ short_channel_id: bob_carol_scid,
3214
+ channel_features: ChannelFeatures :: empty( ) ,
3215
+ fee_msat: 3000 , // fee for the usage of the entire blinded path, including Trampoline
3216
+ cltv_expiry_delta: 48 ,
3217
+ maybe_announced_channel: false ,
3218
+ }
3219
+ ] ,
3220
+ blinded_tail: Some ( BlindedTail {
3221
+ trampoline_hops: vec![
3222
+ // Carol
3223
+ TrampolineHop {
3224
+ pubkey: carol_node_id,
3225
+ node_features: Features :: empty( ) ,
3226
+ fee_msat: amt_msat,
3227
+ cltv_expiry_delta: 176 , // let her cook
3228
+ } ,
3229
+
3230
+ // Dave (recipient)
3231
+ TrampolineHop {
3232
+ pubkey: dave_node_id,
3233
+ node_features: Features :: empty( ) ,
3234
+ fee_msat: 0 , // no need to charge a fee as the recipient
3235
+ cltv_expiry_delta: 24 ,
3236
+ } ,
3237
+ ] ,
3238
+ hops: vec![
3239
+ // Dave's blinded node id
3240
+ BlindedHop {
3241
+ blinded_node_id: pubkey_from_hex( "0295d40514096a8be54859e7dfe947b376eaafea8afe5cb4eb2c13ff857ed0b4be" ) ,
3242
+ encrypted_payload: bytes_from_hex( "0ccf3c8a58deaa603f657ee2a5ed9d604eb5c8ca1e5f801989afa8f3ea6d789bbdde2c7e7a1ef9ca8c38d2c54760febad8446d3f273ddb537569ef56613846ccd3aba78a" ) ,
3243
+ }
3244
+ ] ,
3245
+ blinding_point: alice_node_id,
3246
+ excess_final_cltv_expiry_delta: 39 ,
3247
+ final_value_msat: amt_msat,
3248
+ } )
3249
+ } ] ,
3250
+ route_params : None ,
3251
+ } ;
3252
+
3253
+ nodes[ 0 ] . node . send_payment_with_route ( route. clone ( ) , payment_hash, RecipientOnionFields :: spontaneous_empty ( ) , PaymentId ( payment_hash. 0 ) ) . unwrap ( ) ;
3254
+
3255
+ let replacement_onion = {
3256
+ // create a substitute onion where the last Trampoline hop is an unblinded receive, which we
3257
+ // (deliberately) do not support out of the box, therefore necessitating this workaround
3258
+ let trampoline_secret_key = secret_from_hex ( "0134928f7b7ca6769080d70f16be84c812c741f545b49a34db47ce338a205799" ) ;
3259
+ let prng_seed = secret_from_hex ( "fe02b4b9054302a3ddf4e1e9f7c411d644aebbd295218ab009dca94435f775a9" ) ;
3260
+ let recipient_onion_fields = RecipientOnionFields :: spontaneous_empty ( ) ;
3261
+
3262
+ let blinded_tail = route. paths [ 0 ] . blinded_tail . clone ( ) . unwrap ( ) ;
3263
+ 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 ( ) ;
3264
+
3265
+ // pop the last dummy hop
3266
+ trampoline_payloads. pop ( ) ;
3267
+
3268
+ trampoline_payloads. push ( msgs:: OutboundTrampolinePayload :: Receive {
3269
+ payment_data : Some ( msgs:: FinalOnionHopData {
3270
+ payment_secret,
3271
+ total_msat : amt_msat,
3272
+ } ) ,
3273
+ sender_intended_htlc_amt_msat : amt_msat,
3274
+ cltv_expiry_height : 96 ,
3275
+ } ) ;
3276
+
3277
+ let trampoline_onion_keys = onion_utils:: construct_trampoline_onion_keys ( & secp_ctx, & route. paths [ 0 ] . blinded_tail . as_ref ( ) . unwrap ( ) , & trampoline_secret_key) ;
3278
+ let trampoline_packet = onion_utils:: construct_trampoline_onion_packet (
3279
+ trampoline_payloads,
3280
+ trampoline_onion_keys,
3281
+ prng_seed. secret_bytes ( ) ,
3282
+ & payment_hash,
3283
+ None ,
3284
+ ) . unwrap ( ) ;
3285
+
3286
+ let outer_session_priv = secret_from_hex ( "e52c20461ed7acd46c4e7b591a37610519179482887bd73bf3b94617f8f03677" ) ;
3287
+
3288
+ 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 ( ) ;
3289
+ let outer_onion_keys = onion_utils:: construct_onion_keys ( & secp_ctx, & route. clone ( ) . paths [ 0 ] , & outer_session_priv) ;
3290
+ let outer_packet = onion_utils:: construct_onion_packet (
3291
+ outer_payloads,
3292
+ outer_onion_keys,
3293
+ prng_seed. secret_bytes ( ) ,
3294
+ & payment_hash,
3295
+ ) . unwrap ( ) ;
3296
+
3297
+ outer_packet
3298
+ } ;
3299
+
3300
+ check_added_monitors ! ( & nodes[ 0 ] , 1 ) ;
3301
+
3302
+ let mut events = nodes[ 0 ] . node . get_and_clear_pending_msg_events ( ) ;
3303
+ assert_eq ! ( events. len( ) , 1 ) ;
3304
+ let mut first_message_event = remove_first_msg_event_to_node ( & nodes[ 1 ] . node . get_our_node_id ( ) , & mut events) ;
3305
+ let mut update_message = match first_message_event {
3306
+ MessageSendEvent :: UpdateHTLCs { ref mut updates, .. } => {
3307
+ assert_eq ! ( updates. update_add_htlcs. len( ) , 1 ) ;
3308
+ updates. update_add_htlcs . get_mut ( 0 )
3309
+ } ,
3310
+ _ => panic ! ( )
3311
+ } ;
3312
+ update_message. map ( |msg| {
3313
+ msg. onion_routing_packet = replacement_onion. clone ( ) ;
3314
+ } ) ;
3315
+
3316
+ let unforked_route: & [ & Node ] = & [ & nodes[ 1 ] , & nodes[ 2 ] ] ;
3317
+ pass_along_path ( & nodes[ 0 ] , unforked_route, amt_msat, payment_hash. clone ( ) ,
3318
+ None , first_message_event. clone ( ) , false , Some ( payment_preimage) ) ;
3319
+
3320
+ check_added_monitors ! ( & nodes[ 2 ] , 2 ) ;
3321
+ let mut events = nodes[ 2 ] . node . get_and_clear_pending_msg_events ( ) ;
3322
+ assert_eq ! ( events. len( ) , 2 ) ;
3323
+
3324
+ let intermediate_message_event_a = remove_first_msg_event_to_node ( & nodes[ 4 ] . node . get_our_node_id ( ) , & mut events) ;
3325
+ let intermediate_message_event_b = remove_first_msg_event_to_node ( & nodes[ 5 ] . node . get_our_node_id ( ) , & mut events) ;
3326
+
3327
+ let route_via_t0: & [ & Node ] = & [ & nodes[ 4 ] , & nodes[ 3 ] ] ;
3328
+ let route_via_t1: & [ & Node ] = & [ & nodes[ 5 ] , & nodes[ 3 ] ] ;
3329
+ let args_a = PassAlongPathArgs :: new ( & nodes[ 2 ] , route_via_t0, amt_msat, payment_hash, intermediate_message_event_a. clone ( ) )
3330
+ . expect_failure ( HTLCDestination :: FailedPayment { payment_hash } ) ;
3331
+ let args_b = PassAlongPathArgs :: new ( & nodes[ 2 ] , route_via_t1, amt_msat, payment_hash, intermediate_message_event_b. clone ( ) )
3332
+ . expect_failure ( HTLCDestination :: FailedPayment { payment_hash } ) ;
3333
+
3334
+ {
3335
+ do_pass_along_path ( args_a) ;
3336
+ {
3337
+ let downstream_id = 3 ;
3338
+ let upstream_id = 4 ;
3339
+ let unblinded_node_updates = get_htlc_update_msgs ! ( nodes[ downstream_id] , nodes[ upstream_id] . node. get_our_node_id( ) ) ;
3340
+ nodes[ upstream_id] . node . handle_update_fail_htlc (
3341
+ nodes[ downstream_id] . node . get_our_node_id ( ) , & unblinded_node_updates. update_fail_htlcs [ 0 ]
3342
+ ) ;
3343
+ do_commitment_signed_dance ( & nodes[ upstream_id] , & nodes[ downstream_id] , & unblinded_node_updates. commitment_signed , true , false ) ;
3344
+ }
3345
+ {
3346
+ let downstream_id = 4 ;
3347
+ let upstream_id = 2 ;
3348
+ let unblinded_node_updates = get_htlc_update_msgs ! ( nodes[ downstream_id] , nodes[ upstream_id] . node. get_our_node_id( ) ) ;
3349
+ nodes[ upstream_id] . node . handle_update_fail_htlc (
3350
+ nodes[ downstream_id] . node . get_our_node_id ( ) , & unblinded_node_updates. update_fail_htlcs [ 0 ]
3351
+ ) ;
3352
+ do_commitment_signed_dance ( & nodes[ upstream_id] , & nodes[ downstream_id] , & unblinded_node_updates. commitment_signed , true , false ) ;
3353
+ }
3354
+ {
3355
+ let downstream_id = 2 ;
3356
+ let upstream_id = 1 ;
3357
+ let unblinded_node_updates = get_htlc_update_msgs ! ( nodes[ downstream_id] , nodes[ upstream_id] . node. get_our_node_id( ) ) ;
3358
+ nodes[ upstream_id] . node . handle_update_fail_htlc (
3359
+ nodes[ downstream_id] . node . get_our_node_id ( ) , & unblinded_node_updates. update_fail_htlcs [ 0 ]
3360
+ ) ;
3361
+ do_commitment_signed_dance ( & nodes[ upstream_id] , & nodes[ downstream_id] , & unblinded_node_updates. commitment_signed , true , false ) ;
3362
+ }
3363
+ {
3364
+ let downstream_id = 1 ;
3365
+ let upstream_id = 0 ;
3366
+ let unblinded_node_updates = get_htlc_update_msgs ! ( nodes[ downstream_id] , nodes[ upstream_id] . node. get_our_node_id( ) ) ;
3367
+ nodes[ upstream_id] . node . handle_update_fail_htlc (
3368
+ nodes[ downstream_id] . node . get_our_node_id ( ) , & unblinded_node_updates. update_fail_htlcs [ 0 ]
3369
+ ) ;
3370
+ do_commitment_signed_dance ( & nodes[ upstream_id] , & nodes[ downstream_id] , & unblinded_node_updates. commitment_signed , false , false ) ;
3371
+ }
3372
+ {
3373
+ let payment_failed_conditions = PaymentFailedConditions :: new ( )
3374
+ . expected_htlc_error_data ( 0x2000 | 25 , & [ 0 ; 0 ] ) ;
3375
+ expect_payment_failed_conditions ( & nodes[ 0 ] , payment_hash, false , payment_failed_conditions) ;
3376
+ }
3377
+ }
3378
+
3379
+ {
3380
+ do_pass_along_path ( args_b) ;
3381
+ {
3382
+ let downstream_id = 3 ;
3383
+ let upstream_id = 5 ;
3384
+ let unblinded_node_updates = get_htlc_update_msgs ! ( nodes[ downstream_id] , nodes[ upstream_id] . node. get_our_node_id( ) ) ;
3385
+ nodes[ upstream_id] . node . handle_update_fail_htlc (
3386
+ nodes[ downstream_id] . node . get_our_node_id ( ) , & unblinded_node_updates. update_fail_htlcs [ 0 ]
3387
+ ) ;
3388
+ do_commitment_signed_dance ( & nodes[ upstream_id] , & nodes[ downstream_id] , & unblinded_node_updates. commitment_signed , true , false ) ;
3389
+ }
3390
+ {
3391
+ let downstream_id = 5 ;
3392
+ let upstream_id = 2 ;
3393
+ let unblinded_node_updates = get_htlc_update_msgs ! ( nodes[ downstream_id] , nodes[ upstream_id] . node. get_our_node_id( ) ) ;
3394
+ nodes[ upstream_id] . node . handle_update_fail_htlc (
3395
+ nodes[ downstream_id] . node . get_our_node_id ( ) , & unblinded_node_updates. update_fail_htlcs [ 0 ]
3396
+ ) ;
3397
+ do_commitment_signed_dance ( & nodes[ upstream_id] , & nodes[ downstream_id] , & unblinded_node_updates. commitment_signed , false , false ) ;
3398
+ }
3399
+ {
3400
+ let events = nodes[ 2 ] . node . get_and_clear_pending_events ( ) ;
3401
+ assert_eq ! ( events. len( ) , 2 ) ;
3402
+ match events[ 0 ] {
3403
+ Event :: HTLCHandlingFailed { .. } => { }
3404
+ _ => panic ! ( "unexpected event" )
3405
+ } ;
3406
+ }
3407
+ }
3408
+ }
0 commit comments