Skip to content

Commit f31f718

Browse files
committed
LSPS2: Introduce max_pending_requests_per_peer service limit
We introduce a new `LSPS2ServiceLimits` object and use it to limit the number of pending (get_info and buy) requests per peer.
1 parent 59733af commit f31f718

File tree

3 files changed

+112
-32
lines changed

3 files changed

+112
-32
lines changed

lightning-liquidity/src/lsps0/ser.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ pub(crate) const JSONRPC_ERROR_FIELD_KEY: &str = "error";
4141
pub(crate) const JSONRPC_INVALID_MESSAGE_ERROR_CODE: i32 = -32700;
4242
pub(crate) const JSONRPC_INVALID_MESSAGE_ERROR_MESSAGE: &str = "parse error";
4343

44-
pub(crate) const _LSPS0_CLIENT_REJECTED_ERROR_CODE: i32 = 1;
44+
pub(crate) const LSPS0_CLIENT_REJECTED_ERROR_CODE: i32 = 1;
4545

4646
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
4747
pub(crate) enum LSPSMethod {

lightning-liquidity/src/lsps2/service.rs

+109-30
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
//! Contains the main LSPS2 server-side object, [`LSPS2ServiceHandler`].
1111
1212
use crate::events::{Event, EventQueue};
13-
use crate::lsps0::ser::{ProtocolMessageHandler, RequestId, ResponseError};
13+
use crate::lsps0::ser::{
14+
ProtocolMessageHandler, RequestId, ResponseError, LSPS0_CLIENT_REJECTED_ERROR_CODE,
15+
};
1416
use crate::lsps2::event::LSPS2ServiceEvent;
1517
use crate::lsps2::payment_queue::{InterceptedHTLC, PaymentQueue};
1618
use crate::lsps2::utils::{compute_opening_fee, is_valid_opening_fee_params};
@@ -40,13 +42,33 @@ use crate::lsps2::msgs::{
4042
LSPS2_GET_INFO_REQUEST_UNRECOGNIZED_OR_STALE_TOKEN_ERROR_CODE,
4143
};
4244

45+
/// The default value applied for [`LSPS2ServiceLimits::max_pending_requests_per_peer`].
46+
pub const DEFAULT_MAX_PENDING_REQUESTS_PER_PEER: usize = 10;
47+
4348
/// Server-side configuration options for JIT channels.
4449
#[derive(Clone, Debug)]
4550
pub struct LSPS2ServiceConfig {
4651
/// Used to calculate the promise for channel parameters supplied to clients.
4752
///
4853
/// Note: If this changes then old promises given out will be considered invalid.
4954
pub promise_secret: [u8; 32],
55+
/// Configuration limits for JIT channels.
56+
pub service_limits: LSPS2ServiceLimits,
57+
}
58+
59+
/// Server-side configuration limits for JIT channels.
60+
#[derive(Clone, Debug)]
61+
pub struct LSPS2ServiceLimits {
62+
/// The maximum number of pending requests we allow on a per-peer basis.
63+
///
64+
/// Any requests beyond this limit will be ignored.
65+
pub max_pending_requests_per_peer: usize,
66+
}
67+
68+
impl Default for LSPS2ServiceLimits {
69+
fn default() -> Self {
70+
Self { max_pending_requests_per_peer: DEFAULT_MAX_PENDING_REQUESTS_PER_PEER }
71+
}
5072
}
5173

5274
/// Information about the initial payment size and JIT channel opening fee.
@@ -982,21 +1004,51 @@ where
9821004
fn handle_get_info_request(
9831005
&self, request_id: RequestId, counterparty_node_id: &PublicKey, params: GetInfoRequest,
9841006
) -> Result<(), LightningError> {
985-
let mut outer_state_lock = self.per_peer_state.write().unwrap();
986-
let inner_state_lock: &mut Mutex<PeerState> =
987-
outer_state_lock.entry(*counterparty_node_id).or_insert(Mutex::new(PeerState::new()));
988-
let mut peer_state_lock = inner_state_lock.lock().unwrap();
989-
peer_state_lock
990-
.pending_requests
991-
.insert(request_id.clone(), LSPS2Request::GetInfo(params.clone()));
992-
993-
let event = Event::LSPS2Service(LSPS2ServiceEvent::GetInfo {
994-
request_id,
995-
counterparty_node_id: *counterparty_node_id,
996-
token: params.token,
997-
});
998-
self.pending_events.enqueue(event);
999-
Ok(())
1007+
let (result, response) = {
1008+
let mut outer_state_lock = self.per_peer_state.write().unwrap();
1009+
let inner_state_lock: &mut Mutex<PeerState> = outer_state_lock
1010+
.entry(*counterparty_node_id)
1011+
.or_insert(Mutex::new(PeerState::new()));
1012+
let mut peer_state_lock = inner_state_lock.lock().unwrap();
1013+
if peer_state_lock.pending_requests.len()
1014+
< self.config.service_limits.max_pending_requests_per_peer
1015+
{
1016+
peer_state_lock
1017+
.pending_requests
1018+
.insert(request_id.clone(), LSPS2Request::GetInfo(params.clone()));
1019+
1020+
let event = Event::LSPS2Service(LSPS2ServiceEvent::GetInfo {
1021+
request_id,
1022+
counterparty_node_id: *counterparty_node_id,
1023+
token: params.token,
1024+
});
1025+
self.pending_events.enqueue(event);
1026+
(Ok(()), None)
1027+
} else {
1028+
let response = LSPS2Response::GetInfoError(ResponseError {
1029+
code: LSPS0_CLIENT_REJECTED_ERROR_CODE,
1030+
message: "Reached maximum number of pending requests. Please try again later."
1031+
.to_string(),
1032+
data: None,
1033+
});
1034+
let msg = Some(LSPS2Message::Response(request_id, response).into());
1035+
1036+
let err = format!(
1037+
"Peer {} reached maximum number of pending requests: {}",
1038+
counterparty_node_id, self.config.service_limits.max_pending_requests_per_peer
1039+
);
1040+
1041+
let result =
1042+
Err(LightningError { err, action: ErrorAction::IgnoreAndLog(Level::Debug) });
1043+
(result, msg)
1044+
}
1045+
};
1046+
1047+
if let Some(msg) = response {
1048+
self.pending_messages.enqueue(counterparty_node_id, msg);
1049+
}
1050+
1051+
result
10001052
}
10011053

10021054
fn handle_buy_request(
@@ -1070,7 +1122,6 @@ where
10701122
}
10711123

10721124
// TODO: if payment_size_msat is specified, make sure our node has sufficient incoming liquidity from public network to receive it.
1073-
10741125
if !is_valid_opening_fee_params(&params.opening_fee_params, &self.config.promise_secret) {
10751126
let response = LSPS2Response::BuyError(ResponseError {
10761127
code: LSPS2_BUY_REQUEST_INVALID_OPENING_FEE_PARAMS_ERROR_CODE,
@@ -1085,26 +1136,54 @@ where
10851136
});
10861137
}
10871138

1088-
{
1139+
let (result, response) = {
10891140
let mut outer_state_lock = self.per_peer_state.write().unwrap();
10901141
let inner_state_lock = outer_state_lock
10911142
.entry(*counterparty_node_id)
10921143
.or_insert(Mutex::new(PeerState::new()));
10931144
let mut peer_state_lock = inner_state_lock.lock().unwrap();
1094-
peer_state_lock
1095-
.pending_requests
1096-
.insert(request_id.clone(), LSPS2Request::Buy(params.clone()));
1097-
}
10981145

1099-
let event = Event::LSPS2Service(LSPS2ServiceEvent::BuyRequest {
1100-
request_id,
1101-
counterparty_node_id: *counterparty_node_id,
1102-
opening_fee_params: params.opening_fee_params,
1103-
payment_size_msat: params.payment_size_msat,
1104-
});
1105-
self.pending_events.enqueue(event);
1146+
if peer_state_lock.pending_requests.len()
1147+
< self.config.service_limits.max_pending_requests_per_peer
1148+
{
1149+
peer_state_lock
1150+
.pending_requests
1151+
.insert(request_id.clone(), LSPS2Request::Buy(params.clone()));
1152+
1153+
let event = Event::LSPS2Service(LSPS2ServiceEvent::BuyRequest {
1154+
request_id,
1155+
counterparty_node_id: *counterparty_node_id,
1156+
opening_fee_params: params.opening_fee_params,
1157+
payment_size_msat: params.payment_size_msat,
1158+
});
1159+
self.pending_events.enqueue(event);
11061160

1107-
Ok(())
1161+
(Ok(()), None)
1162+
} else {
1163+
let response = LSPS2Response::BuyError(ResponseError {
1164+
code: LSPS0_CLIENT_REJECTED_ERROR_CODE,
1165+
message: "Reached maximum number of pending requests. Please try again later."
1166+
.to_string(),
1167+
data: None,
1168+
});
1169+
let msg = Some(LSPS2Message::Response(request_id, response).into());
1170+
1171+
let err = format!(
1172+
"Peer {} reached maximum number of pending requests: {}",
1173+
counterparty_node_id, self.config.service_limits.max_pending_requests_per_peer
1174+
);
1175+
let result =
1176+
Err(LightningError { err, action: ErrorAction::IgnoreAndLog(Level::Debug) });
1177+
1178+
(result, msg)
1179+
}
1180+
};
1181+
1182+
if let Some(msg) = response {
1183+
self.pending_messages.enqueue(counterparty_node_id, msg);
1184+
}
1185+
1186+
result
11081187
}
11091188
}
11101189

lightning-liquidity/tests/lsps2_integration_tests.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,8 @@ fn create_jit_invoice(
8383
#[test]
8484
fn invoice_generation_flow() {
8585
let promise_secret = [42; 32];
86-
let lsps2_service_config = LSPS2ServiceConfig { promise_secret };
86+
let service_limits = Default::default();
87+
let lsps2_service_config = LSPS2ServiceConfig { promise_secret, service_limits };
8788
let service_config = LiquidityServiceConfig {
8889
#[cfg(lsps1_service)]
8990
lsps1_service_config: None,

0 commit comments

Comments
 (0)