|
2 | 2 |
|
3 | 3 | mod common;
|
4 | 4 |
|
5 |
| -use common::{create_service_and_client_nodes, get_lsps_message, Node}; |
| 5 | +use common::{get_lsps_message, setup_test_lsps2, Node}; |
6 | 6 |
|
7 | 7 | use lightning_liquidity::events::LiquidityEvent;
|
8 | 8 | use lightning_liquidity::lsps0::ser::LSPSDateTime;
|
9 |
| -use lightning_liquidity::lsps2::client::LSPS2ClientConfig; |
10 | 9 | use lightning_liquidity::lsps2::event::{LSPS2ClientEvent, LSPS2ServiceEvent};
|
11 | 10 | use lightning_liquidity::lsps2::msgs::LSPS2RawOpeningFeeParams;
|
12 |
| -use lightning_liquidity::lsps2::service::LSPS2ServiceConfig; |
13 | 11 | use lightning_liquidity::lsps2::utils::is_valid_opening_fee_params;
|
14 |
| -use lightning_liquidity::{LiquidityClientConfig, LiquidityServiceConfig}; |
15 | 12 |
|
16 |
| -use lightning::ln::channelmanager::MIN_FINAL_CLTV_EXPIRY_DELTA; |
| 13 | +use lightning::ln::channelmanager::{InterceptId, MIN_FINAL_CLTV_EXPIRY_DELTA}; |
17 | 14 | use lightning::ln::peer_handler::CustomMessageHandler;
|
18 | 15 | use lightning::log_error;
|
19 | 16 | use lightning::routing::router::{RouteHint, RouteHintHop};
|
| 17 | +use lightning::util::errors::APIError; |
20 | 18 | use lightning::util::logger::Logger;
|
21 | 19 |
|
22 | 20 | use lightning_invoice::{Bolt11Invoice, InvoiceBuilder, RoutingFees};
|
23 | 21 |
|
| 22 | +use lightning_types::payment::PaymentHash; |
| 23 | + |
24 | 24 | use bitcoin::hashes::{sha256, Hash};
|
25 | 25 | use bitcoin::secp256k1::{PublicKey, Secp256k1};
|
26 | 26 | use bitcoin::Network;
|
@@ -82,29 +82,15 @@ fn create_jit_invoice(
|
82 | 82 |
|
83 | 83 | #[test]
|
84 | 84 | 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(); |
105 |
| - |
106 |
| - let client_handler = client_node.liquidity_manager.lsps2_client_handler().unwrap(); |
107 |
| - let client_node_id = client_node.channel_manager.get_our_node_id(); |
| 85 | + let ( |
| 86 | + client_handler, |
| 87 | + service_handler, |
| 88 | + service_node_id, |
| 89 | + client_node_id, |
| 90 | + service_node, |
| 91 | + client_node, |
| 92 | + promise_secret, |
| 93 | + ) = setup_test_lsps2(); |
108 | 94 |
|
109 | 95 | let get_info_request_id = client_handler.request_opening_params(service_node_id, None);
|
110 | 96 | let get_info_request = get_lsps_message!(client_node, service_node_id);
|
@@ -239,3 +225,326 @@ fn invoice_generation_flow() {
|
239 | 225 | )
|
240 | 226 | .unwrap();
|
241 | 227 | }
|
| 228 | + |
| 229 | +#[test] |
| 230 | +fn channel_open_failed() { |
| 231 | + let (_, service_handler, service_node_id, client_node_id, service_node, client_node, _) = |
| 232 | + setup_test_lsps2(); |
| 233 | + |
| 234 | + let get_info_request_id = client_node |
| 235 | + .liquidity_manager |
| 236 | + .lsps2_client_handler() |
| 237 | + .unwrap() |
| 238 | + .request_opening_params(service_node_id, None); |
| 239 | + let get_info_request = get_lsps_message!(client_node, service_node_id); |
| 240 | + service_node.liquidity_manager.handle_custom_message(get_info_request, client_node_id).unwrap(); |
| 241 | + |
| 242 | + let _get_info_event = service_node.liquidity_manager.next_event().unwrap(); |
| 243 | + |
| 244 | + let raw_opening_params = LSPS2RawOpeningFeeParams { |
| 245 | + min_fee_msat: 100, |
| 246 | + proportional: 21, |
| 247 | + valid_until: LSPSDateTime::from_str("2035-05-20T08:30:45Z").unwrap(), |
| 248 | + min_lifetime: 144, |
| 249 | + max_client_to_self_delay: 128, |
| 250 | + min_payment_size_msat: 1, |
| 251 | + max_payment_size_msat: 100_000_000, |
| 252 | + }; |
| 253 | + service_handler |
| 254 | + .opening_fee_params_generated( |
| 255 | + &client_node_id, |
| 256 | + get_info_request_id.clone(), |
| 257 | + vec![raw_opening_params], |
| 258 | + ) |
| 259 | + .unwrap(); |
| 260 | + |
| 261 | + let get_info_response = get_lsps_message!(service_node, client_node_id); |
| 262 | + client_node |
| 263 | + .liquidity_manager |
| 264 | + .handle_custom_message(get_info_response, service_node_id) |
| 265 | + .unwrap(); |
| 266 | + |
| 267 | + let opening_fee_params = match client_node.liquidity_manager.next_event().unwrap() { |
| 268 | + LiquidityEvent::LSPS2Client(LSPS2ClientEvent::OpeningParametersReady { |
| 269 | + opening_fee_params_menu, |
| 270 | + .. |
| 271 | + }) => opening_fee_params_menu.first().unwrap().clone(), |
| 272 | + _ => panic!("Unexpected event"), |
| 273 | + }; |
| 274 | + |
| 275 | + let payment_size_msat = Some(1_000_000); |
| 276 | + let buy_request_id = client_node |
| 277 | + .liquidity_manager |
| 278 | + .lsps2_client_handler() |
| 279 | + .unwrap() |
| 280 | + .select_opening_params(service_node_id, payment_size_msat, opening_fee_params.clone()) |
| 281 | + .unwrap(); |
| 282 | + let buy_request = get_lsps_message!(client_node, service_node_id); |
| 283 | + service_node.liquidity_manager.handle_custom_message(buy_request, client_node_id).unwrap(); |
| 284 | + |
| 285 | + let _buy_event = service_node.liquidity_manager.next_event().unwrap(); |
| 286 | + let user_channel_id = 42; |
| 287 | + let cltv_expiry_delta = 144; |
| 288 | + let intercept_scid = service_node.channel_manager.get_intercept_scid(); |
| 289 | + let client_trusts_lsp = true; |
| 290 | + |
| 291 | + service_handler |
| 292 | + .invoice_parameters_generated( |
| 293 | + &client_node_id, |
| 294 | + buy_request_id.clone(), |
| 295 | + intercept_scid, |
| 296 | + cltv_expiry_delta, |
| 297 | + client_trusts_lsp, |
| 298 | + user_channel_id, |
| 299 | + ) |
| 300 | + .unwrap(); |
| 301 | + |
| 302 | + let buy_response = get_lsps_message!(service_node, client_node_id); |
| 303 | + client_node.liquidity_manager.handle_custom_message(buy_response, service_node_id).unwrap(); |
| 304 | + let _invoice_params_event = client_node.liquidity_manager.next_event().unwrap(); |
| 305 | + |
| 306 | + let htlc_amount_msat = 1_000_000; |
| 307 | + let intercept_id = InterceptId([0; 32]); |
| 308 | + let payment_hash = PaymentHash([1; 32]); |
| 309 | + |
| 310 | + // This should trigger an OpenChannel event |
| 311 | + service_handler |
| 312 | + .htlc_intercepted(intercept_scid, intercept_id, htlc_amount_msat, payment_hash) |
| 313 | + .unwrap(); |
| 314 | + |
| 315 | + let _ = match service_node.liquidity_manager.next_event().unwrap() { |
| 316 | + LiquidityEvent::LSPS2Service(LSPS2ServiceEvent::OpenChannel { |
| 317 | + user_channel_id: channel_id, |
| 318 | + intercept_scid: scid, |
| 319 | + .. |
| 320 | + }) => { |
| 321 | + assert_eq!(channel_id, user_channel_id); |
| 322 | + assert_eq!(scid, intercept_scid); |
| 323 | + true |
| 324 | + }, |
| 325 | + _ => panic!("Expected OpenChannel event"), |
| 326 | + }; |
| 327 | + |
| 328 | + service_handler.channel_open_failed(&client_node_id, user_channel_id).unwrap(); |
| 329 | + |
| 330 | + // Verify we can restart the flow with another HTLC |
| 331 | + let new_intercept_id = InterceptId([1; 32]); |
| 332 | + service_handler |
| 333 | + .htlc_intercepted(intercept_scid, new_intercept_id, htlc_amount_msat, payment_hash) |
| 334 | + .unwrap(); |
| 335 | + |
| 336 | + // Should get another OpenChannel event which confirms the reset worked |
| 337 | + let _ = match service_node.liquidity_manager.next_event().unwrap() { |
| 338 | + LiquidityEvent::LSPS2Service(LSPS2ServiceEvent::OpenChannel { |
| 339 | + user_channel_id: channel_id, |
| 340 | + intercept_scid: scid, |
| 341 | + .. |
| 342 | + }) => { |
| 343 | + assert_eq!(channel_id, user_channel_id); |
| 344 | + assert_eq!(scid, intercept_scid); |
| 345 | + true |
| 346 | + }, |
| 347 | + _ => panic!("Expected OpenChannel event after reset"), |
| 348 | + }; |
| 349 | +} |
| 350 | + |
| 351 | +#[test] |
| 352 | +fn channel_open_failed_invalid_state() { |
| 353 | + let (_, service_handler, service_node_id, client_node_id, service_node, client_node, _) = |
| 354 | + setup_test_lsps2(); |
| 355 | + |
| 356 | + let get_info_request_id = client_node |
| 357 | + .liquidity_manager |
| 358 | + .lsps2_client_handler() |
| 359 | + .unwrap() |
| 360 | + .request_opening_params(service_node_id, None); |
| 361 | + let get_info_request = get_lsps_message!(client_node, service_node_id); |
| 362 | + service_node.liquidity_manager.handle_custom_message(get_info_request, client_node_id).unwrap(); |
| 363 | + let _get_info_event = service_node.liquidity_manager.next_event().unwrap(); |
| 364 | + |
| 365 | + let raw_opening_params = LSPS2RawOpeningFeeParams { |
| 366 | + min_fee_msat: 100, |
| 367 | + proportional: 21, |
| 368 | + valid_until: LSPSDateTime::from_str("2035-05-20T08:30:45Z").unwrap(), |
| 369 | + min_lifetime: 144, |
| 370 | + max_client_to_self_delay: 128, |
| 371 | + min_payment_size_msat: 1, |
| 372 | + max_payment_size_msat: 100_000_000, |
| 373 | + }; |
| 374 | + service_handler |
| 375 | + .opening_fee_params_generated( |
| 376 | + &client_node_id, |
| 377 | + get_info_request_id.clone(), |
| 378 | + vec![raw_opening_params], |
| 379 | + ) |
| 380 | + .unwrap(); |
| 381 | + |
| 382 | + let get_info_response = get_lsps_message!(service_node, client_node_id); |
| 383 | + client_node |
| 384 | + .liquidity_manager |
| 385 | + .handle_custom_message(get_info_response, service_node_id) |
| 386 | + .unwrap(); |
| 387 | + |
| 388 | + let opening_fee_params = match client_node.liquidity_manager.next_event().unwrap() { |
| 389 | + LiquidityEvent::LSPS2Client(LSPS2ClientEvent::OpeningParametersReady { |
| 390 | + opening_fee_params_menu, |
| 391 | + .. |
| 392 | + }) => opening_fee_params_menu.first().unwrap().clone(), |
| 393 | + _ => panic!("Unexpected event"), |
| 394 | + }; |
| 395 | + |
| 396 | + let payment_size_msat = Some(1_000_000); |
| 397 | + let buy_request_id = client_node |
| 398 | + .liquidity_manager |
| 399 | + .lsps2_client_handler() |
| 400 | + .unwrap() |
| 401 | + .select_opening_params(service_node_id, payment_size_msat, opening_fee_params.clone()) |
| 402 | + .unwrap(); |
| 403 | + let buy_request = get_lsps_message!(client_node, service_node_id); |
| 404 | + service_node.liquidity_manager.handle_custom_message(buy_request, client_node_id).unwrap(); |
| 405 | + |
| 406 | + let _buy_event = service_node.liquidity_manager.next_event().unwrap(); |
| 407 | + let user_channel_id = 42; |
| 408 | + let cltv_expiry_delta = 144; |
| 409 | + let intercept_scid = service_node.channel_manager.get_intercept_scid(); |
| 410 | + let client_trusts_lsp = true; |
| 411 | + |
| 412 | + service_handler |
| 413 | + .invoice_parameters_generated( |
| 414 | + &client_node_id, |
| 415 | + buy_request_id.clone(), |
| 416 | + intercept_scid, |
| 417 | + cltv_expiry_delta, |
| 418 | + client_trusts_lsp, |
| 419 | + user_channel_id, |
| 420 | + ) |
| 421 | + .unwrap(); |
| 422 | + |
| 423 | + // We're purposely not intercepting an HTLC, so the state remains PendingInitialPayment |
| 424 | + |
| 425 | + // Try to call channel_open_failed, which should fail because the channel is not in PendingChannelOpen state |
| 426 | + let result = service_handler.channel_open_failed(&client_node_id, user_channel_id); |
| 427 | + |
| 428 | + assert!(result.is_err()); |
| 429 | + match result.unwrap_err() { |
| 430 | + APIError::APIMisuseError { err } => { |
| 431 | + assert!(err.contains("Channel is not in the PendingChannelOpen state.")); |
| 432 | + }, |
| 433 | + other => panic!("Unexpected error type: {:?}", other), |
| 434 | + } |
| 435 | +} |
| 436 | + |
| 437 | +#[test] |
| 438 | +fn channel_open_failed_nonexistent_channel() { |
| 439 | + let (_, service_handler, _, client_node_id, _, _, _) = setup_test_lsps2(); |
| 440 | + |
| 441 | + // Call channel_open_failed with a nonexistent user_channel_id |
| 442 | + let nonexistent_user_channel_id = 999; |
| 443 | + let result = service_handler.channel_open_failed(&client_node_id, nonexistent_user_channel_id); |
| 444 | + |
| 445 | + assert!(result.is_err()); |
| 446 | + match result.unwrap_err() { |
| 447 | + APIError::APIMisuseError { err } => { |
| 448 | + assert!(err.contains("No counterparty state for")); |
| 449 | + }, |
| 450 | + other => panic!("Unexpected error type: {:?}", other), |
| 451 | + } |
| 452 | +} |
| 453 | + |
| 454 | +#[test] |
| 455 | +fn channel_open_abandoned() { |
| 456 | + let (_, service_handler, service_node_id, client_node_id, service_node, client_node, _) = |
| 457 | + setup_test_lsps2(); |
| 458 | + |
| 459 | + // Set up a JIT channel |
| 460 | + let get_info_request_id = client_node |
| 461 | + .liquidity_manager |
| 462 | + .lsps2_client_handler() |
| 463 | + .unwrap() |
| 464 | + .request_opening_params(service_node_id, None); |
| 465 | + let get_info_request = get_lsps_message!(client_node, service_node_id); |
| 466 | + service_node.liquidity_manager.handle_custom_message(get_info_request, client_node_id).unwrap(); |
| 467 | + let _get_info_event = service_node.liquidity_manager.next_event().unwrap(); |
| 468 | + |
| 469 | + let raw_opening_params = LSPS2RawOpeningFeeParams { |
| 470 | + min_fee_msat: 100, |
| 471 | + proportional: 21, |
| 472 | + valid_until: LSPSDateTime::from_str("2035-05-20T08:30:45Z").unwrap(), |
| 473 | + min_lifetime: 144, |
| 474 | + max_client_to_self_delay: 128, |
| 475 | + min_payment_size_msat: 1, |
| 476 | + max_payment_size_msat: 100_000_000, |
| 477 | + }; |
| 478 | + service_handler |
| 479 | + .opening_fee_params_generated( |
| 480 | + &client_node_id, |
| 481 | + get_info_request_id.clone(), |
| 482 | + vec![raw_opening_params], |
| 483 | + ) |
| 484 | + .unwrap(); |
| 485 | + |
| 486 | + let get_info_response = get_lsps_message!(service_node, client_node_id); |
| 487 | + client_node |
| 488 | + .liquidity_manager |
| 489 | + .handle_custom_message(get_info_response, service_node_id) |
| 490 | + .unwrap(); |
| 491 | + |
| 492 | + let opening_fee_params = match client_node.liquidity_manager.next_event().unwrap() { |
| 493 | + LiquidityEvent::LSPS2Client(LSPS2ClientEvent::OpeningParametersReady { |
| 494 | + opening_fee_params_menu, |
| 495 | + .. |
| 496 | + }) => opening_fee_params_menu.first().unwrap().clone(), |
| 497 | + _ => panic!("Unexpected event"), |
| 498 | + }; |
| 499 | + |
| 500 | + let payment_size_msat = Some(1_000_000); |
| 501 | + let buy_request_id = client_node |
| 502 | + .liquidity_manager |
| 503 | + .lsps2_client_handler() |
| 504 | + .unwrap() |
| 505 | + .select_opening_params(service_node_id, payment_size_msat, opening_fee_params.clone()) |
| 506 | + .unwrap(); |
| 507 | + let buy_request = get_lsps_message!(client_node, service_node_id); |
| 508 | + service_node.liquidity_manager.handle_custom_message(buy_request, client_node_id).unwrap(); |
| 509 | + |
| 510 | + let _buy_event = service_node.liquidity_manager.next_event().unwrap(); |
| 511 | + let user_channel_id = 42; |
| 512 | + let cltv_expiry_delta = 144; |
| 513 | + let intercept_scid = service_node.channel_manager.get_intercept_scid(); |
| 514 | + let client_trusts_lsp = true; |
| 515 | + |
| 516 | + service_handler |
| 517 | + .invoice_parameters_generated( |
| 518 | + &client_node_id, |
| 519 | + buy_request_id.clone(), |
| 520 | + intercept_scid, |
| 521 | + cltv_expiry_delta, |
| 522 | + client_trusts_lsp, |
| 523 | + user_channel_id, |
| 524 | + ) |
| 525 | + .unwrap(); |
| 526 | + |
| 527 | + // Call channel_open_abandoned |
| 528 | + service_handler.channel_open_abandoned(&client_node_id, user_channel_id).unwrap(); |
| 529 | + |
| 530 | + // Verify the channel is gone by trying to abandon it again, which should fail |
| 531 | + let result = service_handler.channel_open_abandoned(&client_node_id, user_channel_id); |
| 532 | + assert!(result.is_err()); |
| 533 | +} |
| 534 | + |
| 535 | +#[test] |
| 536 | +fn channel_open_abandoned_nonexistent_channel() { |
| 537 | + let (_, service_handler, _, client_node_id, _, _, _) = setup_test_lsps2(); |
| 538 | + |
| 539 | + // Call channel_open_abandoned with a nonexistent user_channel_id |
| 540 | + let nonexistent_user_channel_id = 999; |
| 541 | + let result = |
| 542 | + service_handler.channel_open_abandoned(&client_node_id, nonexistent_user_channel_id); |
| 543 | + assert!(result.is_err()); |
| 544 | + match result.unwrap_err() { |
| 545 | + APIError::APIMisuseError { err } => { |
| 546 | + assert!(err.contains("No counterparty state for")); |
| 547 | + }, |
| 548 | + other => panic!("Unexpected error type: {:?}", other), |
| 549 | + } |
| 550 | +} |
0 commit comments