Add `invalid_token_provided` API method
What changed, and why it matters
This commit adds a new API method called invalid_token_provided to the LSPS1 (Liquidity Service Provider Specification 1) service handler in rust-lightning. It lets a Lightning Service Provider (LSP) tell a client that the token they used to request a channel is invalid or stale, using error code 102 as proposed in a community draft. The change also removes an unused token field from the service configuration struct. There is no direct security vulnerability here; it is a protocol/API improvement that makes error handling clearer and removes dead configuration.
No security action required. Reviewers may want to confirm that removing LSPS1ServiceConfig::token does not break downstream consumers and that the new invalid_token_provided API is documented in release notes as an API addition.
Security signals we found
New error-response API path added to LSPS1 service handler
Removal of unused token field from LSPS1ServiceConfig
Error code 102 introduced per draft specification
No input validation, parsing, or cryptographic changes observed
No memory-safety, concurrency, or privilege-escalation changes observed
Evidence from the diff
The patch introduces LSPS1_CREATE_ORDER_REQUEST_UNRECOGNIZED_OR_STALE_TOKEN_ERROR_CODE (102) and a new LSPS1ServiceHandler::invalid_token_provided method that sends a JSON-RPC-style LSPS1Response::CreateOrderError to the counterparty when a token is rejected. It also removes the token: Option
Changed components
lightning-liquidity/src/lsps1/service.rslightning-liquidity/src/lsps1/event.rslightning-liquidity/src/lsps1/msgs.rslightning-liquidity/tests/lsps1_integration_tests.rslightning-liquidity/tests/lsps0_integration_tests.rsInspect captured patch +53 / −6
diff --git a/lightning-liquidity/src/lsps1/event.rs b/lightning-liquidity/src/lsps1/event.rs
index cdd0995..c9a1844 100644
--- a/lightning-liquidity/src/lsps1/event.rs
+++ b/lightning-liquidity/src/lsps1/event.rs
@@ -153,9 +153,13 @@ pub enum LSPS1ServiceEvent {
/// send order parameters including the details regarding the
/// payment and order id for this order for the client.
///
+ /// You should call [`LSPS1ServiceHandler::invalid_token_provided`] if the token provided as
+ /// part of the order parameters is invalid.
+ ///
/// **Note: ** This event will *not* be persisted across restarts.
///
/// [`LSPS1ServiceHandler::send_payment_details`]: crate::lsps1::service::LSPS1ServiceHandler::send_payment_details
+ /// [`LSPS1ServiceHandler::invalid_token_provided`]: crate::lsps1::service::LSPS1ServiceHandler::invalid_token_provided
RequestForPaymentDetails {
/// An identifier that must be passed to [`LSPS1ServiceHandler::send_payment_details`].
///
diff --git a/lightning-liquidity/src/lsps1/msgs.rs b/lightning-liquidity/src/lsps1/msgs.rs
index 5bf1304..a2382e0 100644
--- a/lightning-liquidity/src/lsps1/msgs.rs
+++ b/lightning-liquidity/src/lsps1/msgs.rs
@@ -35,6 +35,7 @@ pub(crate) const _LSPS1_CREATE_ORDER_REQUEST_INVALID_PARAMS_ERROR_CODE: i32 = -3
pub(crate) const LSPS1_CREATE_ORDER_REQUEST_ORDER_MISMATCH_ERROR_CODE: i32 = 100;
#[cfg(lsps1_service)]
pub(crate) const LSPS1_GET_ORDER_REQUEST_ORDER_NOT_FOUND_ERROR_CODE: i32 = 101;
+pub(crate) const LSPS1_CREATE_ORDER_REQUEST_UNRECOGNIZED_OR_STALE_TOKEN_ERROR_CODE: i32 = 102;
/// The identifier of an order.
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, Hash)]
diff --git a/lightning-liquidity/src/lsps1/service.rs b/lightning-liquidity/src/lsps1/service.rs
index d02d0f3..e81213c 100644
--- a/lightning-liquidity/src/lsps1/service.rs
+++ b/lightning-liquidity/src/lsps1/service.rs
@@ -9,7 +9,7 @@
//! Contains the main bLIP-51 / LSPS1 server object, [`LSPS1ServiceHandler`].
-use alloc::string::{String, ToString};
+use alloc::string::ToString;
use alloc::vec::Vec;
use core::future::Future as StdFuture;
@@ -24,6 +24,7 @@ use super::msgs::{
LSPS1GetOrderRequest, LSPS1Message, LSPS1Options, LSPS1OrderId, LSPS1OrderParams,
LSPS1OrderState, LSPS1PaymentInfo, LSPS1PaymentState, LSPS1Request, LSPS1Response,
LSPS1_CREATE_ORDER_REQUEST_ORDER_MISMATCH_ERROR_CODE,
+ LSPS1_CREATE_ORDER_REQUEST_UNRECOGNIZED_OR_STALE_TOKEN_ERROR_CODE,
LSPS1_GET_ORDER_REQUEST_ORDER_NOT_FOUND_ERROR_CODE,
};
use super::peer_state::PeerState;
@@ -56,8 +57,6 @@ use bitcoin::secp256k1::PublicKey;
/// Server-side configuration options for bLIP-51 / LSPS1 channel requests.
#[derive(Clone, Debug)]
pub struct LSPS1ServiceConfig {
- /// A token to be send with each channel request.
- pub token: Option<String>,
/// The options supported by the LSP.
pub supported_options: LSPS1Options,
}
@@ -462,6 +461,41 @@ where
Ok(())
}
+ /// Used by LSP to inform a client that an order was rejected because the used token was invalid.
+ ///
+ /// Should be called in response to receiving a [`LSPS1ServiceEvent::RequestForPaymentDetails`]
+ /// event if the provided token is invalid.
+ ///
+ /// [`LSPS1ServiceEvent::RequestForPaymentDetails`]: crate::lsps1::event::LSPS1ServiceEvent::RequestForPaymentDetails
+ pub fn invalid_token_provided(
+ &self, counterparty_node_id: PublicKey, request_id: LSPSRequestId,
+ ) -> Result<(), APIError> {
+ let mut message_queue_notifier = self.pending_messages.notifier();
+
+ match self.per_peer_state.read().unwrap().get(&counterparty_node_id) {
+ Some(inner_state_lock) => {
+ let mut peer_state_lock = inner_state_lock.lock().unwrap();
+ peer_state_lock.remove_request(&request_id).map_err(|e| {
+ let err = format!("Failed to send response due to: {}", e);
+ APIError::APIMisuseError { err }
+ })?;
+
+ let response = LSPS1Response::CreateOrderError(LSPSResponseError {
+ code: LSPS1_CREATE_ORDER_REQUEST_UNRECOGNIZED_OR_STALE_TOKEN_ERROR_CODE,
+ message: "An unrecognized or stale token was provided".to_string(),
+ data: None,
+ });
+
+ let msg = LSPS1Message::Response(request_id, response).into();
+ message_queue_notifier.enqueue(&counterparty_node_id, msg);
+ Ok(())
+ },
+ None => Err(APIError::APIMisuseError {
+ err: format!("No state for the counterparty exists: {}", counterparty_node_id),
+ }),
+ }
+ }
+
fn handle_get_order_request(
&self, request_id: LSPSRequestId, counterparty_node_id: &PublicKey,
params: LSPS1GetOrderRequest,
@@ -653,6 +687,15 @@ where
}
}
+ /// Used by LSP to inform a client that an order was rejected because the used token was invalid.
+ ///
+ /// Wraps [`LSPS1ServiceHandler::invalid_token_provided`].
+ pub fn invalid_token_provided(
+ &self, counterparty_node_id: PublicKey, request_id: LSPSRequestId,
+ ) -> Result<(), APIError> {
+ self.inner.invalid_token_provided(counterparty_node_id, request_id)
+ }
+
/// Used by LSP to give details to client regarding the status of channel opening.
///
/// Wraps [`LSPS1ServiceHandler::update_order_status`].
diff --git a/lightning-liquidity/tests/lsps0_integration_tests.rs b/lightning-liquidity/tests/lsps0_integration_tests.rs
index 7f0e01b..58d9e86 100644
--- a/lightning-liquidity/tests/lsps0_integration_tests.rs
+++ b/lightning-liquidity/tests/lsps0_integration_tests.rs
@@ -49,7 +49,7 @@ fn list_protocols_integration_test() {
min_channel_balance_sat: 100_000,
max_channel_balance_sat: 100_000_000,
};
- LSPS1ServiceConfig { supported_options, token: None }
+ LSPS1ServiceConfig { supported_options }
};
let lsps5_service_config = LSPS5ServiceConfig::default();
let service_config = LiquidityServiceConfig {
diff --git a/lightning-liquidity/tests/lsps1_integration_tests.rs b/lightning-liquidity/tests/lsps1_integration_tests.rs
index 0261a08..93a4bdd 100644
--- a/lightning-liquidity/tests/lsps1_integration_tests.rs
+++ b/lightning-liquidity/tests/lsps1_integration_tests.rs
@@ -35,7 +35,7 @@ use lightning_liquidity::utils::time::TimeProvider;
fn build_lsps1_configs(
supported_options: LSPS1Options,
) -> (LiquidityServiceConfig, LiquidityClientConfig) {
- let lsps1_service_config = LSPS1ServiceConfig { token: None, supported_options };
+ let lsps1_service_config = LSPS1ServiceConfig { supported_options };
let service_config = LiquidityServiceConfig {
lsps1_service_config: Some(lsps1_service_config),
lsps2_service_config: None,
@@ -284,7 +284,6 @@ fn lsps1_service_handler_persistence_across_restarts() {
let service_config = LiquidityServiceConfig {
lsps1_service_config: Some(LSPS1ServiceConfig {
supported_options: supported_options.clone(),
- token: None,
}),
lsps2_service_config: None,
lsps5_service_config: None,
Why this scored 18/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.