Skip to content

Commit 4c19069

Browse files
Add integration tests for error event handling
Add tests for the new error events: - Test per-peer request limit rejection (GetInfoFailed) - Test global request limit rejection (BuyRequestFailed) - Test invalid token handling (GetInfoFailed) These tests verify that error events are properly emitted when LSP requests fail, ensuring the error handling behavior works end-to-end.
1 parent 79d5bbd commit 4c19069

File tree

2 files changed

+259
-21
lines changed

2 files changed

+259
-21
lines changed

lightning-liquidity/tests/common/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ type LockingWrapper<T> = lightning::routing::scoring::MultiThreadedLockableScore
6767
#[cfg(not(c_bindings))]
6868
type LockingWrapper<T> = std::sync::Mutex<T>;
6969

70-
type ChannelManager = channelmanager::ChannelManager<
70+
pub(crate) type ChannelManager = channelmanager::ChannelManager<
7171
Arc<ChainMonitor>,
7272
Arc<test_utils::TestBroadcaster>,
7373
Arc<KeysManager>,

lightning-liquidity/tests/lsps2_integration_tests.rs

Lines changed: 258 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22

33
mod common;
44

5-
use common::{create_service_and_client_nodes, get_lsps_message, Node};
5+
use common::create_service_and_client_nodes;
6+
use common::{get_lsps_message, Node};
67

78
use lightning_liquidity::events::LiquidityEvent;
89
use lightning_liquidity::lsps0::ser::LSPSDateTime;
910
use lightning_liquidity::lsps2::client::LSPS2ClientConfig;
10-
use lightning_liquidity::lsps2::event::{LSPS2ClientEvent, LSPS2ServiceEvent};
11+
use lightning_liquidity::lsps2::event::LSPS2ClientEvent;
12+
use lightning_liquidity::lsps2::event::LSPS2ServiceEvent;
1113
use lightning_liquidity::lsps2::msgs::LSPS2RawOpeningFeeParams;
1214
use lightning_liquidity::lsps2::service::LSPS2ServiceConfig;
1315
use lightning_liquidity::lsps2::utils::is_valid_opening_fee_params;
@@ -31,6 +33,9 @@ use bitcoin::Network;
3133
use std::str::FromStr;
3234
use std::time::Duration;
3335

36+
const MAX_PENDING_REQUESTS_PER_PEER: usize = 10;
37+
const MAX_TOTAL_PENDING_REQUESTS: usize = 1000;
38+
3439
fn setup_test_lsps2(
3540
) -> (bitcoin::secp256k1::PublicKey, bitcoin::secp256k1::PublicKey, Node, Node, [u8; 32]) {
3641
let promise_secret = [42; 32];
@@ -258,12 +263,9 @@ fn channel_open_failed() {
258263
let (service_node_id, client_node_id, service_node, client_node, _) = setup_test_lsps2();
259264

260265
let service_handler = service_node.liquidity_manager.lsps2_service_handler().unwrap();
266+
let client_handler = client_node.liquidity_manager.lsps2_client_handler().unwrap();
261267

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);
268+
let get_info_request_id = client_handler.request_opening_params(service_node_id, None);
267269
let get_info_request = get_lsps_message!(client_node, service_node_id);
268270
service_node.liquidity_manager.handle_custom_message(get_info_request, client_node_id).unwrap();
269271

@@ -301,10 +303,7 @@ fn channel_open_failed() {
301303
};
302304

303305
let payment_size_msat = Some(1_000_000);
304-
let buy_request_id = client_node
305-
.liquidity_manager
306-
.lsps2_client_handler()
307-
.unwrap()
306+
let buy_request_id = client_handler
308307
.select_opening_params(service_node_id, payment_size_msat, opening_fee_params.clone())
309308
.unwrap();
310309
let buy_request = get_lsps_message!(client_node, service_node_id);
@@ -410,13 +409,10 @@ fn channel_open_abandoned() {
410409
let (service_node_id, client_node_id, service_node, client_node, _) = setup_test_lsps2();
411410

412411
let service_handler = service_node.liquidity_manager.lsps2_service_handler().unwrap();
412+
let client_handler = client_node.liquidity_manager.lsps2_client_handler().unwrap();
413413

414414
// 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);
415+
let get_info_request_id = client_handler.request_opening_params(service_node_id, None);
420416
let get_info_request = get_lsps_message!(client_node, service_node_id);
421417
service_node.liquidity_manager.handle_custom_message(get_info_request, client_node_id).unwrap();
422418
let _get_info_event = service_node.liquidity_manager.next_event().unwrap();
@@ -453,10 +449,7 @@ fn channel_open_abandoned() {
453449
};
454450

455451
let payment_size_msat = Some(1_000_000);
456-
let buy_request_id = client_node
457-
.liquidity_manager
458-
.lsps2_client_handler()
459-
.unwrap()
452+
let buy_request_id = client_handler
460453
.select_opening_params(service_node_id, payment_size_msat, opening_fee_params.clone())
461454
.unwrap();
462455
let buy_request = get_lsps_message!(client_node, service_node_id);
@@ -504,3 +497,248 @@ fn channel_open_abandoned_nonexistent_channel() {
504497
other => panic!("Unexpected error type: {:?}", other),
505498
}
506499
}
500+
501+
#[test]
502+
fn max_pending_requests_per_peer_rejected() {
503+
let (service_node_id, client_node_id, service_node, client_node, _) = setup_test_lsps2();
504+
505+
let client_handler = client_node.liquidity_manager.lsps2_client_handler().unwrap();
506+
507+
for _ in 0..MAX_PENDING_REQUESTS_PER_PEER {
508+
let _ = client_handler.request_opening_params(service_node_id, None);
509+
let req_msg = get_lsps_message!(client_node, service_node_id);
510+
let result = service_node.liquidity_manager.handle_custom_message(req_msg, client_node_id);
511+
assert!(result.is_ok());
512+
let event = service_node.liquidity_manager.next_event().unwrap();
513+
match event {
514+
LiquidityEvent::LSPS2Service(LSPS2ServiceEvent::GetInfo { .. }) => {},
515+
_ => panic!("Unexpected event"),
516+
}
517+
}
518+
519+
// Test per-peer limit: the next request should be rejected
520+
let rejected_req_id = client_handler.request_opening_params(service_node_id, None);
521+
let rejected_req_msg = get_lsps_message!(client_node, service_node_id);
522+
523+
let result =
524+
service_node.liquidity_manager.handle_custom_message(rejected_req_msg, client_node_id);
525+
assert!(result.is_err(), "We should have hit the per-peer limit");
526+
527+
let get_info_error_response = get_lsps_message!(service_node, client_node_id);
528+
let result = client_node
529+
.liquidity_manager
530+
.handle_custom_message(get_info_error_response, service_node_id);
531+
assert!(result.is_err());
532+
533+
let event = client_node.liquidity_manager.next_event().unwrap();
534+
match event {
535+
LiquidityEvent::LSPS2Client(LSPS2ClientEvent::GetInfoFailed {
536+
request_id,
537+
counterparty_node_id,
538+
error,
539+
}) => {
540+
assert_eq!(request_id, rejected_req_id);
541+
assert_eq!(counterparty_node_id, service_node_id);
542+
assert_eq!(error.code, 1); // LSPS0_CLIENT_REJECTED_ERROR_CODE
543+
},
544+
_ => panic!("Expected LSPS2ClientEvent::GetInfoFailed event"),
545+
}
546+
}
547+
548+
#[test]
549+
fn max_total_requests_buy_rejected() {
550+
let (service_node_id, _, service_node, client_node, _) = setup_test_lsps2();
551+
552+
let client_handler = client_node.liquidity_manager.lsps2_client_handler().unwrap();
553+
let service_handler = service_node.liquidity_manager.lsps2_service_handler().unwrap();
554+
let secp = Secp256k1::new();
555+
556+
let special_sk_bytes = [99u8; 32];
557+
let special_sk = SecretKey::from_slice(&special_sk_bytes).unwrap();
558+
let special_node_id = PublicKey::from_secret_key(&secp, &special_sk);
559+
560+
let _ = client_handler.request_opening_params(service_node_id, None);
561+
let get_info_request = get_lsps_message!(client_node, service_node_id);
562+
service_node
563+
.liquidity_manager
564+
.handle_custom_message(get_info_request, special_node_id)
565+
.unwrap();
566+
567+
let get_info_event = service_node.liquidity_manager.next_event().unwrap();
568+
match get_info_event {
569+
LiquidityEvent::LSPS2Service(LSPS2ServiceEvent::GetInfo { request_id, .. }) => {
570+
let raw_opening_params = LSPS2RawOpeningFeeParams {
571+
min_fee_msat: 100,
572+
proportional: 21,
573+
valid_until: LSPSDateTime::from_str("2035-05-20T08:30:45Z").unwrap(),
574+
min_lifetime: 144,
575+
max_client_to_self_delay: 128,
576+
min_payment_size_msat: 1,
577+
max_payment_size_msat: 100_000_000,
578+
};
579+
580+
service_handler
581+
.opening_fee_params_generated(
582+
&special_node_id,
583+
request_id,
584+
vec![raw_opening_params],
585+
)
586+
.unwrap();
587+
},
588+
_ => panic!("Unexpected event"),
589+
}
590+
591+
let get_info_response = get_lsps_message!(service_node, special_node_id);
592+
client_node
593+
.liquidity_manager
594+
.handle_custom_message(get_info_response, service_node_id)
595+
.unwrap();
596+
597+
let opening_params_event = client_node.liquidity_manager.next_event().unwrap();
598+
let opening_fee_params = match opening_params_event {
599+
LiquidityEvent::LSPS2Client(LSPS2ClientEvent::OpeningParametersReady {
600+
opening_fee_params_menu,
601+
..
602+
}) => opening_fee_params_menu.first().unwrap().clone(),
603+
_ => panic!("Unexpected event"),
604+
};
605+
606+
// Now fill up the global limit with additional GetInfo requests from other peers
607+
let mut filled = 0;
608+
let mut peer_idx = 0;
609+
610+
while filled < MAX_TOTAL_PENDING_REQUESTS {
611+
let sk_bytes = [peer_idx as u8 + 1; 32];
612+
let sk = SecretKey::from_slice(&sk_bytes).unwrap();
613+
let peer_node_id = PublicKey::from_secret_key(&secp, &sk);
614+
615+
// Skip if this is our special node
616+
if peer_node_id == special_node_id {
617+
peer_idx += 1;
618+
continue;
619+
}
620+
621+
for _ in 0..MAX_PENDING_REQUESTS_PER_PEER {
622+
if filled >= MAX_TOTAL_PENDING_REQUESTS {
623+
break;
624+
}
625+
626+
let _ = client_handler.request_opening_params(service_node_id, None);
627+
let req_msg = get_lsps_message!(client_node, service_node_id);
628+
let result =
629+
service_node.liquidity_manager.handle_custom_message(req_msg, peer_node_id);
630+
assert!(result.is_ok());
631+
632+
let event = service_node.liquidity_manager.next_event().unwrap();
633+
match event {
634+
LiquidityEvent::LSPS2Service(LSPS2ServiceEvent::GetInfo { .. }) => {},
635+
_ => panic!("Unexpected event"),
636+
}
637+
638+
filled += 1;
639+
}
640+
peer_idx += 1;
641+
}
642+
643+
// Now try to send a Buy request with our special node, which should be rejected
644+
let payment_size_msat = Some(1_000_000);
645+
let buy_request_id = client_handler
646+
.select_opening_params(service_node_id, payment_size_msat, opening_fee_params)
647+
.unwrap();
648+
let buy_request = get_lsps_message!(client_node, service_node_id);
649+
650+
let result = service_node.liquidity_manager.handle_custom_message(buy_request, special_node_id);
651+
assert!(result.is_err(), "The Buy request should have been rejected");
652+
653+
let buy_error_response = get_lsps_message!(service_node, special_node_id);
654+
let result =
655+
client_node.liquidity_manager.handle_custom_message(buy_error_response, service_node_id);
656+
assert!(result.is_err());
657+
658+
let event = client_node.liquidity_manager.next_event().unwrap();
659+
match event {
660+
LiquidityEvent::LSPS2Client(LSPS2ClientEvent::BuyRequestFailed {
661+
request_id,
662+
counterparty_node_id,
663+
error,
664+
}) => {
665+
assert_eq!(request_id, buy_request_id);
666+
assert_eq!(counterparty_node_id, service_node_id);
667+
assert_eq!(error.code, 1); // LSPS0_CLIENT_REJECTED_ERROR_CODE
668+
},
669+
_ => panic!("Expected LSPS2ClientEvent::BuyRequestFailed event"),
670+
}
671+
}
672+
673+
#[test]
674+
fn invalid_token_flow() {
675+
let (service_node_id, client_node_id, service_node, client_node, _promise_secret) =
676+
setup_test_lsps2();
677+
678+
let client_handler = client_node.liquidity_manager.lsps2_client_handler().unwrap();
679+
let service_handler = service_node.liquidity_manager.lsps2_service_handler().unwrap();
680+
681+
let token = Some("invalid_token".to_string());
682+
let get_info_request_id = client_handler.request_opening_params(service_node_id, token);
683+
let get_info_request = get_lsps_message!(client_node, service_node_id);
684+
685+
service_node.liquidity_manager.handle_custom_message(get_info_request, client_node_id).unwrap();
686+
687+
let get_info_event = service_node.liquidity_manager.next_event().unwrap();
688+
match get_info_event {
689+
LiquidityEvent::LSPS2Service(LSPS2ServiceEvent::GetInfo {
690+
request_id,
691+
counterparty_node_id,
692+
token,
693+
}) => {
694+
assert_eq!(request_id, get_info_request_id);
695+
assert_eq!(counterparty_node_id, client_node_id);
696+
assert_eq!(token, Some("invalid_token".to_string()));
697+
698+
// Service rejects the token as invalid
699+
service_handler.invalid_token_provided(&client_node_id, request_id.clone()).unwrap();
700+
701+
// Attempt to respond to the same request again which should fail
702+
// because the request has been removed from pending_requests
703+
let raw_opening_params = LSPS2RawOpeningFeeParams {
704+
min_fee_msat: 100,
705+
proportional: 21,
706+
valid_until: LSPSDateTime::from_str("2035-05-20T08:30:45Z").unwrap(),
707+
min_lifetime: 144,
708+
max_client_to_self_delay: 128,
709+
min_payment_size_msat: 1,
710+
max_payment_size_msat: 100_000_000,
711+
};
712+
713+
let result = service_handler.opening_fee_params_generated(
714+
&client_node_id,
715+
request_id.clone(),
716+
vec![raw_opening_params],
717+
);
718+
719+
assert!(result.is_err(), "Request should have been removed from pending_requests");
720+
},
721+
_ => panic!("Unexpected event"),
722+
}
723+
724+
let get_info_error_response = get_lsps_message!(service_node, client_node_id);
725+
726+
client_node
727+
.liquidity_manager
728+
.handle_custom_message(get_info_error_response, service_node_id)
729+
.unwrap_err();
730+
731+
let error_event = client_node.liquidity_manager.next_event().unwrap();
732+
match error_event {
733+
LiquidityEvent::LSPS2Client(LSPS2ClientEvent::GetInfoFailed {
734+
request_id,
735+
counterparty_node_id,
736+
error,
737+
}) => {
738+
assert_eq!(request_id, get_info_request_id);
739+
assert_eq!(counterparty_node_id, service_node_id);
740+
assert_eq!(error.code, 200); // LSPS2_GET_INFO_REQUEST_UNRECOGNIZED_OR_STALE_TOKEN_ERROR_CODE
741+
},
742+
_ => panic!("Expected LSPS2ClientEvent::GetInfoFailed event"),
743+
}
744+
}

0 commit comments

Comments
 (0)