@@ -1963,7 +1963,14 @@ fn test_trampoline_inbound_payment_decoding() {
1963
1963
}
1964
1964
1965
1965
#[ cfg( trampoline) ]
1966
- fn do_test_trampoline_single_hop_receive ( success : bool ) {
1966
+ enum TrampolineSingleHopReceiveCheck {
1967
+ SuccessfulBlindedReceive ,
1968
+ SuccessfulUnblindedReceive ,
1969
+ InvalidOnionFailure ,
1970
+ }
1971
+
1972
+ #[ cfg( trampoline) ]
1973
+ fn do_test_trampoline_single_hop_receive ( scenario : TrampolineSingleHopReceiveCheck ) {
1967
1974
const TOTAL_NODE_COUNT : usize = 3 ;
1968
1975
let secp_ctx = Secp256k1 :: new ( ) ;
1969
1976
@@ -1991,7 +1998,7 @@ fn do_test_trampoline_single_hop_receive(success: bool) {
1991
1998
1992
1999
let carol_alice_trampoline_session_priv = secret_from_hex ( "a0f4b8d7b6c2d0ffdfaf718f76e9decaef4d9fb38a8c4addb95c4007cc3eee03" ) ;
1993
2000
let carol_blinding_point = PublicKey :: from_secret_key ( & secp_ctx, & carol_alice_trampoline_session_priv) ;
1994
- let carol_blinded_hops = if success {
2001
+ let carol_blinded_hops = if matches ! ( scenario , TrampolineSingleHopReceiveCheck :: SuccessfulBlindedReceive | TrampolineSingleHopReceiveCheck :: SuccessfulUnblindedReceive ) {
1995
2002
let payee_tlvs = UnauthenticatedReceiveTlvs {
1996
2003
payment_secret,
1997
2004
payment_constraints : PaymentConstraints {
@@ -2081,29 +2088,44 @@ fn do_test_trampoline_single_hop_receive(success: bool) {
2081
2088
2082
2089
check_added_monitors ! ( & nodes[ 0 ] , 1 ) ;
2083
2090
2084
- if success {
2091
+ if matches ! ( scenario , TrampolineSingleHopReceiveCheck :: SuccessfulBlindedReceive ) {
2085
2092
pass_along_route ( & nodes[ 0 ] , & [ & [ & nodes[ 1 ] , & nodes[ 2 ] ] ] , amt_msat, payment_hash, payment_secret) ;
2086
2093
claim_payment ( & nodes[ 0 ] , & [ & nodes[ 1 ] , & nodes[ 2 ] ] , payment_preimage) ;
2087
2094
} else {
2088
2095
let replacement_onion = {
2089
- // create a substitute onion where the last Trampoline hop is a forward
2096
+ // Create a substitute onion:
2097
+ // In the invalid onion failure scenario, it's to make the last payload a forward.
2098
+ // In the unblinded receive scenario, which we (deliberately) do not support out of the
2099
+ // box, it's to make the receive payload unblinded.
2090
2100
let trampoline_secret_key = secret_from_hex ( "0134928f7b7ca6769080d70f16be84c812c741f545b49a34db47ce338a205799" ) ;
2091
2101
let prng_seed = secret_from_hex ( "fe02b4b9054302a3ddf4e1e9f7c411d644aebbd295218ab009dca94435f775a9" ) ;
2092
2102
let recipient_onion_fields = RecipientOnionFields :: spontaneous_empty ( ) ;
2093
2103
2094
2104
let mut blinded_tail = route. paths [ 0 ] . blinded_tail . clone ( ) . unwrap ( ) ;
2095
2105
2096
- // append some dummy blinded hop so the intro hop looks like a forward
2097
- blinded_tail. hops . push ( BlindedHop {
2098
- blinded_node_id : alice_node_id,
2099
- encrypted_payload : vec ! [ ] ,
2100
- } ) ;
2106
+ if matches ! ( scenario, TrampolineSingleHopReceiveCheck :: InvalidOnionFailure ) {
2107
+ // append some dummy blinded hop so the intro hop looks like a forward
2108
+ blinded_tail. hops . push ( BlindedHop {
2109
+ blinded_node_id : alice_node_id,
2110
+ encrypted_payload : vec ! [ ] ,
2111
+ } ) ;
2112
+ }
2101
2113
2102
2114
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 ( ) ;
2103
2115
2104
- // pop the last dummy hop
2105
2116
trampoline_payloads. pop ( ) ;
2106
2117
2118
+ if matches ! ( scenario, TrampolineSingleHopReceiveCheck :: SuccessfulUnblindedReceive ) {
2119
+ trampoline_payloads. push ( msgs:: OutboundTrampolinePayload :: Receive {
2120
+ payment_data : Some ( msgs:: FinalOnionHopData {
2121
+ payment_secret,
2122
+ total_msat : amt_msat,
2123
+ } ) ,
2124
+ sender_intended_htlc_amt_msat : amt_msat,
2125
+ cltv_expiry_height : 104 ,
2126
+ } ) ;
2127
+ }
2128
+
2107
2129
let trampoline_onion_keys = onion_utils:: construct_trampoline_onion_keys ( & secp_ctx, & route. paths [ 0 ] . blinded_tail . as_ref ( ) . unwrap ( ) , & trampoline_secret_key) . unwrap ( ) ;
2108
2130
let trampoline_packet = onion_utils:: construct_trampoline_onion_packet (
2109
2131
trampoline_payloads,
@@ -2142,30 +2164,36 @@ fn do_test_trampoline_single_hop_receive(success: bool) {
2142
2164
} ) ;
2143
2165
2144
2166
let route: & [ & Node ] = & [ & nodes[ 1 ] , & nodes[ 2 ] ] ;
2145
- let args = PassAlongPathArgs :: new ( & nodes[ 0 ] , route, amt_msat, payment_hash, first_message_event)
2146
- . with_payment_preimage ( payment_preimage)
2147
- . without_claimable_event ( )
2148
- . expect_failure ( HTLCDestination :: InvalidOnion ) ;
2149
- do_pass_along_path ( args) ;
2167
+ let args = PassAlongPathArgs :: new ( & nodes[ 0 ] , route, amt_msat, payment_hash, first_message_event) ;
2168
+
2169
+ if matches ! ( scenario, TrampolineSingleHopReceiveCheck :: InvalidOnionFailure ) {
2170
+ let args = args. expect_failure ( HTLCDestination :: InvalidOnion )
2171
+ . with_payment_preimage ( payment_preimage)
2172
+ . without_claimable_event ( ) ;
2173
+ do_pass_along_path ( args) ;
2174
+
2175
+ {
2176
+ let unblinded_node_updates = get_htlc_update_msgs ! ( nodes[ 2 ] , nodes[ 1 ] . node. get_our_node_id( ) ) ;
2177
+ nodes[ 1 ] . node . handle_update_fail_htlc (
2178
+ nodes[ 2 ] . node . get_our_node_id ( ) , & unblinded_node_updates. update_fail_htlcs [ 0 ] ,
2179
+ ) ;
2180
+ do_commitment_signed_dance ( & nodes[ 1 ] , & nodes[ 2 ] , & unblinded_node_updates. commitment_signed , true , false ) ;
2181
+ }
2182
+ {
2183
+ let unblinded_node_updates = get_htlc_update_msgs ! ( nodes[ 1 ] , nodes[ 0 ] . node. get_our_node_id( ) ) ;
2184
+ nodes[ 0 ] . node . handle_update_fail_htlc (
2185
+ nodes[ 1 ] . node . get_our_node_id ( ) , & unblinded_node_updates. update_fail_htlcs [ 0 ] ,
2186
+ ) ;
2187
+ do_commitment_signed_dance ( & nodes[ 0 ] , & nodes[ 1 ] , & unblinded_node_updates. commitment_signed , false , false ) ;
2188
+ }
2150
2189
2151
- {
2152
- let unblinded_node_updates = get_htlc_update_msgs ! ( nodes[ 2 ] , nodes[ 1 ] . node. get_our_node_id( ) ) ;
2153
- nodes[ 1 ] . node . handle_update_fail_htlc (
2154
- nodes[ 2 ] . node . get_our_node_id ( ) , & unblinded_node_updates. update_fail_htlcs [ 0 ]
2155
- ) ;
2156
- do_commitment_signed_dance ( & nodes[ 1 ] , & nodes[ 2 ] , & unblinded_node_updates. commitment_signed , true , false ) ;
2157
- }
2158
- {
2159
- let unblinded_node_updates = get_htlc_update_msgs ! ( nodes[ 1 ] , nodes[ 0 ] . node. get_our_node_id( ) ) ;
2160
- nodes[ 0 ] . node . handle_update_fail_htlc (
2161
- nodes[ 1 ] . node . get_our_node_id ( ) , & unblinded_node_updates. update_fail_htlcs [ 0 ]
2162
- ) ;
2163
- do_commitment_signed_dance ( & nodes[ 0 ] , & nodes[ 1 ] , & unblinded_node_updates. commitment_signed , false , false ) ;
2164
- }
2165
- {
2166
2190
let payment_failed_conditions = PaymentFailedConditions :: new ( )
2167
2191
. expected_htlc_error_data ( 0x4000 | 22 , & [ 0 ; 0 ] ) ;
2168
2192
expect_payment_failed_conditions ( & nodes[ 0 ] , payment_hash, true , payment_failed_conditions) ;
2193
+ } else {
2194
+ let args = args. with_payment_secret ( payment_secret) ;
2195
+ do_pass_along_path ( args) ;
2196
+ claim_payment ( & nodes[ 0 ] , & [ & nodes[ 1 ] , & nodes[ 2 ] ] , payment_preimage) ;
2169
2197
}
2170
2198
}
2171
2199
}
@@ -2174,177 +2202,11 @@ fn do_test_trampoline_single_hop_receive(success: bool) {
2174
2202
#[ cfg( trampoline) ]
2175
2203
fn test_trampoline_single_hop_receive ( ) {
2176
2204
// Simulate a payment of A (0) -> B (1) -> C(Trampoline (blinded intro)) (2)
2177
- do_test_trampoline_single_hop_receive ( true ) ;
2178
-
2179
- // Simulate a payment failure of A (0) -> B (1) -> C(Trampoline (blinded forward)) (2)
2180
- do_test_trampoline_single_hop_receive ( false ) ;
2181
- }
2205
+ do_test_trampoline_single_hop_receive ( TrampolineSingleHopReceiveCheck :: SuccessfulBlindedReceive ) ;
2182
2206
2183
- #[ test]
2184
- #[ cfg( trampoline) ]
2185
- fn test_trampoline_unblinded_receive ( ) {
2186
2207
// Simulate a payment of A (0) -> B (1) -> C(Trampoline) (2)
2208
+ do_test_trampoline_single_hop_receive ( TrampolineSingleHopReceiveCheck :: SuccessfulUnblindedReceive ) ;
2187
2209
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) ;
2210
+ // Simulate a payment failure of A (0) -> B (1) -> C(Trampoline (blinded forward)) (2)
2211
+ do_test_trampoline_single_hop_receive ( TrampolineSingleHopReceiveCheck :: InvalidOnionFailure ) ;
2349
2212
}
2350
-
0 commit comments