lsp_plugin: refactor lsps0listprotocols handler
What changed, and why it matters
This commit is a routine code cleanup (refactor) in the Core Lightning 'lsps-plugin'. It moves the handler for the 'lsps0.listProtocols' JSON-RPC method into its own file and adds a flag so the handler can later include protocol number 2 in its response when enabled. Currently, the flag is hard-coded to false, so the behavior is unchanged from before: the handler still returns an empty list of protocols. There is no security fix or vulnerability visible in this change.
No security action required. Treat as normal code maintenance. If LSPS2 support is intended to be enabled, ensure the flag is wired to configuration and that the LSPS2 protocol implementation is reviewed separately.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch refactors Lsps0ListProtocolsHandler from plugins/lsps-plugin/src/service.rs into a new plugins/lsps-plugin/src/lsps0/handler.rs module. It adds an lsps2_enabled boolean field to the handler; when true, the response includes protocol ‘2’ in the protocols vector, otherwise it returns an empty vector. The service.rs builder instantiates the handler with lsps2_enabled: false, preserving the prior behavior of returning protocols: []. The change also adds unit tests for both enabled and disabled states. No input validation, serialization, authorization, or resource-handling behavior is altered in a security-relevant way.
Changed components
plugins/lsps-plugin/src/service.rsplugins/lsps-plugin/src/lsps0/handler.rsplugins/lsps-plugin/src/lsps0/mod.rsInspect captured patch +98 / −20
diff --git a/plugins/lsps-plugin/src/lsps0/handler.rs b/plugins/lsps-plugin/src/lsps0/handler.rs
new file mode 100644
index 00000000..6b552f47
--- /dev/null
+++ b/plugins/lsps-plugin/src/lsps0/handler.rs
@@ -0,0 +1,90 @@
+use crate::{
+ jsonrpc::{server::RequestHandler, JsonRpcResponse, RequestObject, RpcError},
+ lsps0::model::{Lsps0listProtocolsRequest, Lsps0listProtocolsResponse},
+ util::unwrap_payload_with_peer_id,
+};
+use async_trait::async_trait;
+
+pub struct Lsps0ListProtocolsHandler {
+ pub lsps2_enabled: bool,
+}
+
+#[async_trait]
+impl RequestHandler for Lsps0ListProtocolsHandler {
+ async fn handle(&self, payload: &[u8]) -> core::result::Result<Vec<u8>, RpcError> {
+ let (payload, _) = unwrap_payload_with_peer_id(payload);
+
+ let req: RequestObject<Lsps0listProtocolsRequest> =
+ serde_json::from_slice(&payload).unwrap();
+ if let Some(id) = req.id {
+ let mut protocols = vec![];
+ if self.lsps2_enabled {
+ protocols.push(2);
+ }
+ let res = Lsps0listProtocolsResponse { protocols }.into_response(id);
+ let res_vec = serde_json::to_vec(&res).unwrap();
+ return Ok(res_vec);
+ }
+ // If request has no ID (notification), return empty Ok result.
+ Ok(vec![])
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+ use crate::{
+ jsonrpc::{JsonRpcRequest, ResponseObject},
+ util::wrap_payload_with_peer_id,
+ };
+ use cln_rpc::primitives::PublicKey;
+
+ const PUBKEY: [u8; 33] = [
+ 0x02, 0x79, 0xbe, 0x66, 0x7e, 0xf9, 0xdc, 0xbb, 0xac, 0x55, 0xa0, 0x62, 0x95, 0xce, 0x87,
+ 0x0b, 0x07, 0x02, 0x9b, 0xfc, 0xdb, 0x2d, 0xce, 0x28, 0xd9, 0x59, 0xf2, 0x81, 0x5b, 0x16,
+ 0xf8, 0x17, 0x98,
+ ];
+
+ fn create_peer_id() -> PublicKey {
+ PublicKey::from_slice(&PUBKEY).expect("Valid pubkey")
+ }
+
+ fn create_wrapped_request(request: &RequestObject<Lsps0listProtocolsRequest>) -> Vec<u8> {
+ let payload = serde_json::to_vec(request).expect("Failed to serialize request");
+ wrap_payload_with_peer_id(&payload, create_peer_id())
+ }
+
+ #[tokio::test]
+ async fn test_lsps2_disabled_returns_empty_protocols() {
+ let handler = Lsps0ListProtocolsHandler {
+ lsps2_enabled: false,
+ };
+
+ let request = Lsps0listProtocolsRequest {}.into_request(Some("test-id".to_string()));
+ let payload = create_wrapped_request(&request);
+
+ let result = handler.handle(&payload).await.unwrap();
+ let response: ResponseObject<Lsps0listProtocolsResponse> =
+ serde_json::from_slice(&result).unwrap();
+
+ let data = response.into_inner().expect("Should have result data");
+ assert!(data.protocols.is_empty());
+ }
+
+ #[tokio::test]
+ async fn test_lsps2_enabled_returns_protocol_2() {
+ let handler = Lsps0ListProtocolsHandler {
+ lsps2_enabled: true,
+ };
+
+ let request = Lsps0listProtocolsRequest {}.into_request(Some("test-id".to_string()));
+ let payload = create_wrapped_request(&request);
+
+ let result = handler.handle(&payload).await.unwrap();
+ let response: ResponseObject<Lsps0listProtocolsResponse> =
+ serde_json::from_slice(&result).unwrap();
+
+ let data = response.into_inner().expect("Should have result data");
+ assert_eq!(data.protocols, vec![2]);
+ }
+}
diff --git a/plugins/lsps-plugin/src/lsps0/mod.rs b/plugins/lsps-plugin/src/lsps0/mod.rs
index df78189c..f32b0a55 100644
--- a/plugins/lsps-plugin/src/lsps0/mod.rs
+++ b/plugins/lsps-plugin/src/lsps0/mod.rs
@@ -1,3 +1,4 @@
+pub mod handler;
pub mod model;
pub mod primitives;
pub mod transport;
diff --git a/plugins/lsps-plugin/src/service.rs b/plugins/lsps-plugin/src/service.rs
index 6e3a578f..116e13f2 100644
--- a/plugins/lsps-plugin/src/service.rs
+++ b/plugins/lsps-plugin/src/service.rs
@@ -1,9 +1,10 @@
use anyhow::anyhow;
use async_trait::async_trait;
-use cln_lsps::jsonrpc::server::{JsonRpcResponseWriter, RequestHandler};
+use cln_lsps::jsonrpc::server::JsonRpcResponseWriter;
+use cln_lsps::jsonrpc::TransportError;
use cln_lsps::jsonrpc::{server::JsonRpcServer, JsonRpcRequest};
-use cln_lsps::jsonrpc::{JsonRpcResponse, RequestObject, RpcError, TransportError};
-use cln_lsps::lsps0::model::{Lsps0listProtocolsRequest, Lsps0listProtocolsResponse};
+use cln_lsps::lsps0::handler::Lsps0ListProtocolsHandler;
+use cln_lsps::lsps0::model::Lsps0listProtocolsRequest;
use cln_lsps::lsps0::transport::{self, CustomMsg};
use cln_lsps::util::wrap_payload_with_peer_id;
use cln_lsps::{lsps0, util, LSP_FEATURE_BIT};
@@ -51,7 +52,9 @@ async fn main() -> Result<(), anyhow::Error> {
let lsps_builder = JsonRpcServer::builder().with_handler(
Lsps0listProtocolsRequest::METHOD.to_string(),
- Arc::new(Lsps0ListProtocolsHandler {}),
+ Arc::new(Lsps0ListProtocolsHandler {
+ lsps2_enabled: false,
+ }),
);
let lsps_service = lsps_builder.build();
@@ -115,19 +118,3 @@ impl JsonRpcResponseWriter for LspsResponseWriter {
transport::send_custommsg(&mut client, payload.to_vec(), self.peer_id).await
}
}
-
-pub struct Lsps0ListProtocolsHandler;
-
-#[async_trait]
-impl RequestHandler for Lsps0ListProtocolsHandler {
- async fn handle(&self, payload: &[u8]) -> core::result::Result<Vec<u8>, RpcError> {
- let req: RequestObject<Lsps0listProtocolsRequest> =
- serde_json::from_slice(payload).unwrap();
- if let Some(id) = req.id {
- let res = Lsps0listProtocolsResponse { protocols: vec![] }.into_response(id);
- let res_vec = serde_json::to_vec(&res).unwrap();
- return Ok(res_vec);
- }
- Ok(vec![])
- }
-}
Why this scored 13/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.