Skip to content

Commit 6041e71

Browse files
fixup: return error when rejecting due to DOS so it does not silently fail and the client knows what's going on
1 parent e9e2add commit 6041e71

File tree

5 files changed

+66
-23
lines changed

5 files changed

+66
-23
lines changed

lightning-liquidity/src/lsps5/client.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,9 @@ where
185185
/// Also ensure the URL is valid, has HTTPS protocol, its length does not exceed [`MAX_WEBHOOK_URL_LENGTH`]
186186
/// and that the URL points to a public host.
187187
///
188+
/// Your request may fail if you recently opened a channel or started an LSPS1 / LSPS2 flow.
189+
/// Please retry shortly.
190+
///
188191
/// [`MAX_WEBHOOK_URL_LENGTH`]: super::msgs::MAX_WEBHOOK_URL_LENGTH
189192
/// [`MAX_APP_NAME_LENGTH`]: super::msgs::MAX_APP_NAME_LENGTH
190193
/// [`WebhookRegistered`]: super::event::LSPS5ClientEvent::WebhookRegistered

lightning-liquidity/src/lsps5/msgs.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ pub const LSPS5_UNKNOWN_ERROR_CODE: i32 = 1000;
5353
pub const LSPS5_SERIALIZATION_ERROR_CODE: i32 = 1001;
5454
/// A notification was sent too frequently.
5555
pub const LSPS5_SLOW_DOWN_ERROR_CODE: i32 = 1002;
56+
/// A request was rejected because the client has no prior activity with the LSP (no open channel and no active LSPS1 or LSPS2 flow). The client should first open a channel
57+
pub const LSPS5_NO_PRIOR_ACTIVITY_ERROR_CODE: i32 = 1003;
5658

5759
pub(crate) const LSPS5_SET_WEBHOOK_METHOD_NAME: &str = "lsps5.set_webhook";
5860
pub(crate) const LSPS5_LIST_WEBHOOKS_METHOD_NAME: &str = "lsps5.list_webhooks";
@@ -113,6 +115,10 @@ pub enum LSPS5ProtocolError {
113115
///
114116
/// [`NOTIFICATION_COOLDOWN_TIME`]: super::service::NOTIFICATION_COOLDOWN_TIME
115117
SlowDownError,
118+
119+
/// Request rejected because the client has no prior activity with the LSP (no open channel and no active LSPS1 or LSPS2 flow). The client should first open a channel
120+
/// or initiate an LSPS1/LSPS2 interaction before retrying.
121+
NoPriorActivityError,
116122
}
117123

118124
impl LSPS5ProtocolError {
@@ -129,6 +135,7 @@ impl LSPS5ProtocolError {
129135
LSPS5ProtocolError::UnknownError => LSPS5_UNKNOWN_ERROR_CODE,
130136
LSPS5ProtocolError::SerializationError => LSPS5_SERIALIZATION_ERROR_CODE,
131137
LSPS5ProtocolError::SlowDownError => LSPS5_SLOW_DOWN_ERROR_CODE,
138+
LSPS5ProtocolError::NoPriorActivityError => LSPS5_NO_PRIOR_ACTIVITY_ERROR_CODE,
132139
}
133140
}
134141
/// The error message for the LSPS5 protocol error.
@@ -145,6 +152,9 @@ impl LSPS5ProtocolError {
145152
"Error serializing LSPS5 webhook notification"
146153
},
147154
LSPS5ProtocolError::SlowDownError => "Notification sent too frequently",
155+
LSPS5ProtocolError::NoPriorActivityError => {
156+
"Request rejected due to no prior activity with the LSP"
157+
},
148158
}
149159
}
150160
}
@@ -249,6 +259,9 @@ impl From<LSPSResponseError> for LSPS5ProtocolError {
249259
LSPS5_UNSUPPORTED_PROTOCOL_ERROR_CODE => LSPS5ProtocolError::UnsupportedProtocol,
250260
LSPS5_TOO_MANY_WEBHOOKS_ERROR_CODE => LSPS5ProtocolError::TooManyWebhooks,
251261
LSPS5_APP_NAME_NOT_FOUND_ERROR_CODE => LSPS5ProtocolError::AppNameNotFound,
262+
LSPS5_SERIALIZATION_ERROR_CODE => LSPS5ProtocolError::SerializationError,
263+
LSPS5_SLOW_DOWN_ERROR_CODE => LSPS5ProtocolError::SlowDownError,
264+
LSPS5_NO_PRIOR_ACTIVITY_ERROR_CODE => LSPS5ProtocolError::NoPriorActivityError,
252265
_ => LSPS5ProtocolError::UnknownError,
253266
}
254267
}

lightning-liquidity/src/lsps5/service.rs

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -149,14 +149,32 @@ where
149149
}
150150
}
151151

152-
/// Returns whether a request from the given client should be accepted.
153-
///
154-
/// Prior activity includes an existing open channel, an active LSPS1 flow,
155-
/// or an LSPS2 flow that has an opening or open JIT channel.
156-
pub(crate) fn can_accept_request(
152+
/// Enforces the prior-activity requirement for state-allocating LSPS5 requests (e.g.
153+
/// `lsps5.set_webhook`), rejecting and replying with `NoPriorActivityError` if not met.
154+
pub(crate) fn enforce_prior_activity_or_reject(
157155
&self, client_id: &PublicKey, lsps2_has_active_requests: bool, lsps1_has_activity: bool,
158-
) -> bool {
159-
self.client_has_open_channel(client_id) || lsps2_has_active_requests || lsps1_has_activity
156+
request_id: LSPSRequestId,
157+
) -> Result<(), LightningError> {
158+
let can_accept = self.client_has_open_channel(client_id)
159+
|| lsps2_has_active_requests
160+
|| lsps1_has_activity;
161+
162+
let mut message_queue_notifier = self.pending_messages.notifier();
163+
if !can_accept {
164+
let error = LSPS5ProtocolError::NoPriorActivityError;
165+
let msg = LSPS5Message::Response(
166+
request_id,
167+
LSPS5Response::SetWebhookError(error.clone().into()),
168+
)
169+
.into();
170+
message_queue_notifier.enqueue(&client_id, msg);
171+
return Err(LightningError {
172+
err: error.message().into(),
173+
action: ErrorAction::IgnoreAndLog(Level::Info),
174+
});
175+
} else {
176+
Ok(())
177+
}
160178
}
161179

162180
fn check_prune_stale_webhooks<'a>(

lightning-liquidity/src/manager.rs

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -568,7 +568,7 @@ where
568568
LSPSMessage::LSPS5(msg @ LSPS5Message::Request(..)) => {
569569
match &self.lsps5_service_handler {
570570
Some(lsps5_service_handler) => {
571-
if let LSPS5Message::Request(_, ref req) = msg {
571+
if let LSPS5Message::Request(ref req_id, ref req) = msg {
572572
if req.is_state_allocating() {
573573
let lsps2_has_active_requests = self
574574
.lsps2_service_handler
@@ -582,19 +582,12 @@ where
582582
#[cfg(not(lsps1_service))]
583583
let lsps1_has_active_requests = false;
584584

585-
if !lsps5_service_handler.can_accept_request(
585+
lsps5_service_handler.enforce_prior_activity_or_reject(
586586
sender_node_id,
587587
lsps2_has_active_requests,
588588
lsps1_has_active_requests,
589-
) {
590-
return Err(LightningError {
591-
err: format!(
592-
"Rejecting LSPS5 request from {:?} without prior activity (requires open channel or active LSPS1 or LSPS2 flow)",
593-
sender_node_id
594-
),
595-
action: ErrorAction::IgnoreAndLog(Level::Debug),
596-
});
597-
}
589+
req_id.clone(),
590+
)?
598591
}
599592
}
600593

lightning-liquidity/tests/lsps5_integration_tests.rs

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1241,11 +1241,23 @@ macro_rules! assert_lsps5_reject {
12411241
.expect("Request should send");
12421242
let request = get_lsps_message!($client_node, $service_node_id);
12431243

1244-
let result =
1244+
let service_result =
12451245
$service_node.liquidity_manager.handle_custom_message(request, $client_node_id);
1246-
assert!(result.is_err(), "Service should reject request without prior interaction");
1247-
1248-
assert!($service_node.liquidity_manager.get_and_clear_pending_msg().is_empty());
1246+
assert!(service_result.is_err(), "Service should reject request without prior interaction");
1247+
1248+
let req = get_lsps_message!($service_node, $client_node_id);
1249+
$client_node.liquidity_manager.handle_custom_message(req, $service_node_id).unwrap();
1250+
let event = $client_node.liquidity_manager.next_event().unwrap();
1251+
match event {
1252+
LiquidityEvent::LSPS5Client(LSPS5ClientEvent::WebhookRegistrationFailed {
1253+
error,
1254+
..
1255+
}) => {
1256+
let error_to_check = LSPS5ProtocolError::NoPriorActivityError;
1257+
assert_eq!(error, error_to_check.into());
1258+
},
1259+
_ => panic!("Expected WebhookRegistrationFailed event, got {:?}", event),
1260+
}
12491261
}};
12501262
}
12511263

@@ -1269,7 +1281,11 @@ macro_rules! assert_lsps5_accept {
12691281
.liquidity_manager
12701282
.handle_custom_message(response, $service_node_id)
12711283
.expect("Client should handle response");
1272-
let _ = $client_node.liquidity_manager.next_event().unwrap();
1284+
let event = $client_node.liquidity_manager.next_event().unwrap();
1285+
match event {
1286+
LiquidityEvent::LSPS5Client(LSPS5ClientEvent::WebhookRegistered { .. }) => {},
1287+
_ => panic!("Expected WebhookRegistered event, got {:?}", event),
1288+
}
12731289
}};
12741290
}
12751291

0 commit comments

Comments
 (0)