@@ -11,23 +11,54 @@ use lightning_liquidity::lsps2::event::{LSPS2ClientEvent, LSPS2ServiceEvent};
11
11
use lightning_liquidity:: lsps2:: msgs:: LSPS2RawOpeningFeeParams ;
12
12
use lightning_liquidity:: lsps2:: service:: LSPS2ServiceConfig ;
13
13
use lightning_liquidity:: lsps2:: utils:: is_valid_opening_fee_params;
14
- use lightning_liquidity:: { LiquidityClientConfig , LiquidityServiceConfig } ;
15
14
16
- use lightning:: ln:: channelmanager:: MIN_FINAL_CLTV_EXPIRY_DELTA ;
15
+ use lightning:: ln:: channelmanager:: { InterceptId , MIN_FINAL_CLTV_EXPIRY_DELTA } ;
17
16
use lightning:: ln:: peer_handler:: CustomMessageHandler ;
18
17
use lightning:: log_error;
19
18
use lightning:: routing:: router:: { RouteHint , RouteHintHop } ;
19
+ use lightning:: util:: errors:: APIError ;
20
20
use lightning:: util:: logger:: Logger ;
21
21
22
22
use lightning_invoice:: { Bolt11Invoice , InvoiceBuilder , RoutingFees } ;
23
23
24
+ use lightning_liquidity:: { LiquidityClientConfig , LiquidityServiceConfig } ;
25
+ use lightning_types:: payment:: PaymentHash ;
26
+
24
27
use bitcoin:: hashes:: { sha256, Hash } ;
25
- use bitcoin:: secp256k1:: { PublicKey , Secp256k1 } ;
28
+ use bitcoin:: secp256k1:: { PublicKey , Secp256k1 , SecretKey } ;
26
29
use bitcoin:: Network ;
27
30
28
31
use std:: str:: FromStr ;
29
32
use std:: time:: Duration ;
30
33
34
+ fn setup_test_lsps2 (
35
+ ) -> ( bitcoin:: secp256k1:: PublicKey , bitcoin:: secp256k1:: PublicKey , Node , Node , [ u8 ; 32 ] ) {
36
+ let promise_secret = [ 42 ; 32 ] ;
37
+ let signing_key = SecretKey :: from_slice ( & promise_secret) . unwrap ( ) ;
38
+ let lsps2_service_config = LSPS2ServiceConfig { promise_secret } ;
39
+ let service_config = LiquidityServiceConfig {
40
+ #[ cfg( lsps1_service) ]
41
+ lsps1_service_config : None ,
42
+ lsps2_service_config : Some ( lsps2_service_config) ,
43
+ advertise_service : true ,
44
+ } ;
45
+
46
+ let lsps2_client_config = LSPS2ClientConfig :: default ( ) ;
47
+ let client_config = LiquidityClientConfig {
48
+ lsps1_client_config : None ,
49
+ lsps2_client_config : Some ( lsps2_client_config) ,
50
+ } ;
51
+
52
+ let ( service_node, client_node) =
53
+ create_service_and_client_nodes ( "webhook_registration_flow" , service_config, client_config) ;
54
+
55
+ let secp = bitcoin:: secp256k1:: Secp256k1 :: new ( ) ;
56
+ let service_node_id = bitcoin:: secp256k1:: PublicKey :: from_secret_key ( & secp, & signing_key) ;
57
+ let client_node_id = client_node. channel_manager . get_our_node_id ( ) ;
58
+
59
+ ( service_node_id, client_node_id, service_node, client_node, promise_secret)
60
+ }
61
+
31
62
fn create_jit_invoice (
32
63
node : & Node , service_node_id : PublicKey , intercept_scid : u64 , cltv_expiry_delta : u32 ,
33
64
payment_size_msat : Option < u64 > , description : & str , expiry_secs : u32 ,
@@ -82,29 +113,11 @@ fn create_jit_invoice(
82
113
83
114
#[ test]
84
115
fn invoice_generation_flow ( ) {
85
- let promise_secret = [ 42 ; 32 ] ;
86
- let lsps2_service_config = LSPS2ServiceConfig { promise_secret } ;
87
- let service_config = LiquidityServiceConfig {
88
- #[ cfg( lsps1_service) ]
89
- lsps1_service_config : None ,
90
- lsps2_service_config : Some ( lsps2_service_config) ,
91
- advertise_service : true ,
92
- } ;
93
-
94
- let lsps2_client_config = LSPS2ClientConfig :: default ( ) ;
95
- let client_config = LiquidityClientConfig {
96
- lsps1_client_config : None ,
97
- lsps2_client_config : Some ( lsps2_client_config) ,
98
- } ;
99
-
100
- let ( service_node, client_node) =
101
- create_service_and_client_nodes ( "invoice_generation_flow" , service_config, client_config) ;
102
-
103
- let service_handler = service_node. liquidity_manager . lsps2_service_handler ( ) . unwrap ( ) ;
104
- let service_node_id = service_node. channel_manager . get_our_node_id ( ) ;
116
+ let ( service_node_id, client_node_id, service_node, client_node, promise_secret) =
117
+ setup_test_lsps2 ( ) ;
105
118
106
119
let client_handler = client_node. liquidity_manager . lsps2_client_handler ( ) . unwrap ( ) ;
107
- let client_node_id = client_node . channel_manager . get_our_node_id ( ) ;
120
+ let service_handler = service_node . liquidity_manager . lsps2_service_handler ( ) . unwrap ( ) ;
108
121
109
122
let get_info_request_id = client_handler. request_opening_params ( service_node_id, None ) ;
110
123
let get_info_request = get_lsps_message ! ( client_node, service_node_id) ;
@@ -239,3 +252,255 @@ fn invoice_generation_flow() {
239
252
)
240
253
. unwrap ( ) ;
241
254
}
255
+
256
+ #[ test]
257
+ fn channel_open_failed ( ) {
258
+ let ( service_node_id, client_node_id, service_node, client_node, _) = setup_test_lsps2 ( ) ;
259
+
260
+ let service_handler = service_node. liquidity_manager . lsps2_service_handler ( ) . unwrap ( ) ;
261
+
262
+ let get_info_request_id = client_node
263
+ . liquidity_manager
264
+ . lsps2_client_handler ( )
265
+ . unwrap ( )
266
+ . request_opening_params ( service_node_id, None ) ;
267
+ let get_info_request = get_lsps_message ! ( client_node, service_node_id) ;
268
+ service_node. liquidity_manager . handle_custom_message ( get_info_request, client_node_id) . unwrap ( ) ;
269
+
270
+ let _get_info_event = service_node. liquidity_manager . next_event ( ) . unwrap ( ) ;
271
+
272
+ let raw_opening_params = LSPS2RawOpeningFeeParams {
273
+ min_fee_msat : 100 ,
274
+ proportional : 21 ,
275
+ valid_until : LSPSDateTime :: from_str ( "2035-05-20T08:30:45Z" ) . unwrap ( ) ,
276
+ min_lifetime : 144 ,
277
+ max_client_to_self_delay : 128 ,
278
+ min_payment_size_msat : 1 ,
279
+ max_payment_size_msat : 100_000_000 ,
280
+ } ;
281
+ service_handler
282
+ . opening_fee_params_generated (
283
+ & client_node_id,
284
+ get_info_request_id. clone ( ) ,
285
+ vec ! [ raw_opening_params] ,
286
+ )
287
+ . unwrap ( ) ;
288
+
289
+ let get_info_response = get_lsps_message ! ( service_node, client_node_id) ;
290
+ client_node
291
+ . liquidity_manager
292
+ . handle_custom_message ( get_info_response, service_node_id)
293
+ . unwrap ( ) ;
294
+
295
+ let opening_fee_params = match client_node. liquidity_manager . next_event ( ) . unwrap ( ) {
296
+ LiquidityEvent :: LSPS2Client ( LSPS2ClientEvent :: OpeningParametersReady {
297
+ opening_fee_params_menu,
298
+ ..
299
+ } ) => opening_fee_params_menu. first ( ) . unwrap ( ) . clone ( ) ,
300
+ _ => panic ! ( "Unexpected event" ) ,
301
+ } ;
302
+
303
+ let payment_size_msat = Some ( 1_000_000 ) ;
304
+ let buy_request_id = client_node
305
+ . liquidity_manager
306
+ . lsps2_client_handler ( )
307
+ . unwrap ( )
308
+ . select_opening_params ( service_node_id, payment_size_msat, opening_fee_params. clone ( ) )
309
+ . unwrap ( ) ;
310
+ let buy_request = get_lsps_message ! ( client_node, service_node_id) ;
311
+ service_node. liquidity_manager . handle_custom_message ( buy_request, client_node_id) . unwrap ( ) ;
312
+
313
+ let _buy_event = service_node. liquidity_manager . next_event ( ) . unwrap ( ) ;
314
+ let user_channel_id = 42 ;
315
+ let cltv_expiry_delta = 144 ;
316
+ let intercept_scid = service_node. channel_manager . get_intercept_scid ( ) ;
317
+ let client_trusts_lsp = true ;
318
+
319
+ service_handler
320
+ . invoice_parameters_generated (
321
+ & client_node_id,
322
+ buy_request_id. clone ( ) ,
323
+ intercept_scid,
324
+ cltv_expiry_delta,
325
+ client_trusts_lsp,
326
+ user_channel_id,
327
+ )
328
+ . unwrap ( ) ;
329
+
330
+ let buy_response = get_lsps_message ! ( service_node, client_node_id) ;
331
+ client_node. liquidity_manager . handle_custom_message ( buy_response, service_node_id) . unwrap ( ) ;
332
+ let _invoice_params_event = client_node. liquidity_manager . next_event ( ) . unwrap ( ) ;
333
+
334
+ // Test calling channel_open_failed in invalid state (before HTLC interception)
335
+ let result = service_handler. channel_open_failed ( & client_node_id, user_channel_id) ;
336
+ assert ! ( result. is_err( ) ) ;
337
+ match result. unwrap_err ( ) {
338
+ APIError :: APIMisuseError { err } => {
339
+ assert ! ( err. contains( "Channel is not in the PendingChannelOpen state." ) ) ;
340
+ } ,
341
+ other => panic ! ( "Unexpected error type: {:?}" , other) ,
342
+ }
343
+
344
+ let htlc_amount_msat = 1_000_000 ;
345
+ let intercept_id = InterceptId ( [ 0 ; 32 ] ) ;
346
+ let payment_hash = PaymentHash ( [ 1 ; 32 ] ) ;
347
+
348
+ // This should trigger an OpenChannel event
349
+ service_handler
350
+ . htlc_intercepted ( intercept_scid, intercept_id, htlc_amount_msat, payment_hash)
351
+ . unwrap ( ) ;
352
+
353
+ let _ = match service_node. liquidity_manager . next_event ( ) . unwrap ( ) {
354
+ LiquidityEvent :: LSPS2Service ( LSPS2ServiceEvent :: OpenChannel {
355
+ user_channel_id : channel_id,
356
+ intercept_scid : scid,
357
+ ..
358
+ } ) => {
359
+ assert_eq ! ( channel_id, user_channel_id) ;
360
+ assert_eq ! ( scid, intercept_scid) ;
361
+ true
362
+ } ,
363
+ _ => panic ! ( "Expected OpenChannel event" ) ,
364
+ } ;
365
+
366
+ service_handler. channel_open_failed ( & client_node_id, user_channel_id) . unwrap ( ) ;
367
+
368
+ // Verify we can restart the flow with another HTLC
369
+ let new_intercept_id = InterceptId ( [ 1 ; 32 ] ) ;
370
+ service_handler
371
+ . htlc_intercepted ( intercept_scid, new_intercept_id, htlc_amount_msat, payment_hash)
372
+ . unwrap ( ) ;
373
+
374
+ // Should get another OpenChannel event which confirms the reset worked
375
+ let _ = match service_node. liquidity_manager . next_event ( ) . unwrap ( ) {
376
+ LiquidityEvent :: LSPS2Service ( LSPS2ServiceEvent :: OpenChannel {
377
+ user_channel_id : channel_id,
378
+ intercept_scid : scid,
379
+ ..
380
+ } ) => {
381
+ assert_eq ! ( channel_id, user_channel_id) ;
382
+ assert_eq ! ( scid, intercept_scid) ;
383
+ true
384
+ } ,
385
+ _ => panic ! ( "Expected OpenChannel event after reset" ) ,
386
+ } ;
387
+ }
388
+
389
+ #[ test]
390
+ fn channel_open_failed_nonexistent_channel ( ) {
391
+ let ( _, client_node_id, service_node, _, _) = setup_test_lsps2 ( ) ;
392
+
393
+ let service_handler = service_node. liquidity_manager . lsps2_service_handler ( ) . unwrap ( ) ;
394
+
395
+ // Call channel_open_failed with a nonexistent user_channel_id
396
+ let nonexistent_user_channel_id = 999 ;
397
+ let result = service_handler. channel_open_failed ( & client_node_id, nonexistent_user_channel_id) ;
398
+
399
+ assert ! ( result. is_err( ) ) ;
400
+ match result. unwrap_err ( ) {
401
+ APIError :: APIMisuseError { err } => {
402
+ assert ! ( err. contains( "No counterparty state for" ) ) ;
403
+ } ,
404
+ other => panic ! ( "Unexpected error type: {:?}" , other) ,
405
+ }
406
+ }
407
+
408
+ #[ test]
409
+ fn channel_open_abandoned ( ) {
410
+ let ( service_node_id, client_node_id, service_node, client_node, _) = setup_test_lsps2 ( ) ;
411
+
412
+ let service_handler = service_node. liquidity_manager . lsps2_service_handler ( ) . unwrap ( ) ;
413
+
414
+ // Set up a JIT channel
415
+ let get_info_request_id = client_node
416
+ . liquidity_manager
417
+ . lsps2_client_handler ( )
418
+ . unwrap ( )
419
+ . request_opening_params ( service_node_id, None ) ;
420
+ let get_info_request = get_lsps_message ! ( client_node, service_node_id) ;
421
+ service_node. liquidity_manager . handle_custom_message ( get_info_request, client_node_id) . unwrap ( ) ;
422
+ let _get_info_event = service_node. liquidity_manager . next_event ( ) . unwrap ( ) ;
423
+
424
+ let raw_opening_params = LSPS2RawOpeningFeeParams {
425
+ min_fee_msat : 100 ,
426
+ proportional : 21 ,
427
+ valid_until : LSPSDateTime :: from_str ( "2035-05-20T08:30:45Z" ) . unwrap ( ) ,
428
+ min_lifetime : 144 ,
429
+ max_client_to_self_delay : 128 ,
430
+ min_payment_size_msat : 1 ,
431
+ max_payment_size_msat : 100_000_000 ,
432
+ } ;
433
+ service_handler
434
+ . opening_fee_params_generated (
435
+ & client_node_id,
436
+ get_info_request_id. clone ( ) ,
437
+ vec ! [ raw_opening_params] ,
438
+ )
439
+ . unwrap ( ) ;
440
+
441
+ let get_info_response = get_lsps_message ! ( service_node, client_node_id) ;
442
+ client_node
443
+ . liquidity_manager
444
+ . handle_custom_message ( get_info_response, service_node_id)
445
+ . unwrap ( ) ;
446
+
447
+ let opening_fee_params = match client_node. liquidity_manager . next_event ( ) . unwrap ( ) {
448
+ LiquidityEvent :: LSPS2Client ( LSPS2ClientEvent :: OpeningParametersReady {
449
+ opening_fee_params_menu,
450
+ ..
451
+ } ) => opening_fee_params_menu. first ( ) . unwrap ( ) . clone ( ) ,
452
+ _ => panic ! ( "Unexpected event" ) ,
453
+ } ;
454
+
455
+ let payment_size_msat = Some ( 1_000_000 ) ;
456
+ let buy_request_id = client_node
457
+ . liquidity_manager
458
+ . lsps2_client_handler ( )
459
+ . unwrap ( )
460
+ . select_opening_params ( service_node_id, payment_size_msat, opening_fee_params. clone ( ) )
461
+ . unwrap ( ) ;
462
+ let buy_request = get_lsps_message ! ( client_node, service_node_id) ;
463
+ service_node. liquidity_manager . handle_custom_message ( buy_request, client_node_id) . unwrap ( ) ;
464
+
465
+ let _buy_event = service_node. liquidity_manager . next_event ( ) . unwrap ( ) ;
466
+ let user_channel_id = 42 ;
467
+ let cltv_expiry_delta = 144 ;
468
+ let intercept_scid = service_node. channel_manager . get_intercept_scid ( ) ;
469
+ let client_trusts_lsp = true ;
470
+
471
+ service_handler
472
+ . invoice_parameters_generated (
473
+ & client_node_id,
474
+ buy_request_id. clone ( ) ,
475
+ intercept_scid,
476
+ cltv_expiry_delta,
477
+ client_trusts_lsp,
478
+ user_channel_id,
479
+ )
480
+ . unwrap ( ) ;
481
+
482
+ // Call channel_open_abandoned
483
+ service_handler. channel_open_abandoned ( & client_node_id, user_channel_id) . unwrap ( ) ;
484
+
485
+ // Verify the channel is gone by trying to abandon it again, which should fail
486
+ let result = service_handler. channel_open_abandoned ( & client_node_id, user_channel_id) ;
487
+ assert ! ( result. is_err( ) ) ;
488
+ }
489
+
490
+ #[ test]
491
+ fn channel_open_abandoned_nonexistent_channel ( ) {
492
+ let ( _, client_node_id, service_node, _, _) = setup_test_lsps2 ( ) ;
493
+ let service_handler = service_node. liquidity_manager . lsps2_service_handler ( ) . unwrap ( ) ;
494
+
495
+ // Call channel_open_abandoned with a nonexistent user_channel_id
496
+ let nonexistent_user_channel_id = 999 ;
497
+ let result =
498
+ service_handler. channel_open_abandoned ( & client_node_id, nonexistent_user_channel_id) ;
499
+ assert ! ( result. is_err( ) ) ;
500
+ match result. unwrap_err ( ) {
501
+ APIError :: APIMisuseError { err } => {
502
+ assert ! ( err. contains( "No counterparty state for" ) ) ;
503
+ } ,
504
+ other => panic ! ( "Unexpected error type: {:?}" , other) ,
505
+ }
506
+ }
0 commit comments