plugins: lsps: move transport to core module
What changed, and why it matters
This commit is a straightforward internal code reorganization in the Core Lightning LSPS plugin. It moves the JSON-RPC transport/client code from one module to another and updates import paths. There is no security-relevant change to behavior, no bug fix, and no disclosed vulnerability.
No security action required. Treat as normal refactoring.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change relocates JsonRpcClient, Transport trait, and related error types from plugins/lsps-plugin/src/jsonrpc/client.rs to a new plugins/lsps-plugin/src/core/transport.rs module. It also removes the legacy Error/Result types from proto/jsonrpc.rs and adjusts call sites in client.rs, jsonrpc/server.rs, lsps0/transport.rs, and service.rs to use the new module paths. The call_raw method now returns JsonRpcResponse
Changed components
plugins/lsps-plugin/src/jsonrpc/client.rs (deleted)plugins/lsps-plugin/src/core/transport.rs (new)plugins/lsps-plugin/src/jsonrpc/mod.rsplugins/lsps-plugin/src/jsonrpc/server.rsplugins/lsps-plugin/src/lib.rsplugins/lsps-plugin/src/lsps0/transport.rsplugins/lsps-plugin/src/proto/jsonrpc.rsplugins/lsps-plugin/src/service.rsplugins/lsps-plugin/src/client.rsInspect captured patch +374 / −423
diff --git a/plugins/lsps-plugin/src/client.rs b/plugins/lsps-plugin/src/client.rs
index c0a6270b..80892a62 100644
--- a/plugins/lsps-plugin/src/client.rs
+++ b/plugins/lsps-plugin/src/client.rs
@@ -1,7 +1,7 @@
use anyhow::{anyhow, bail, Context};
use bitcoin::hashes::{hex::FromHex, sha256, Hash};
use chrono::{Duration, Utc};
-use cln_lsps::jsonrpc::client::JsonRpcClient;
+use cln_lsps::core::transport::JsonRpcClient;
use cln_lsps::lsps0::transport::{
Bolt8Transport, CustomMessageHookManager, WithCustomMessageHookManager,
};
diff --git a/plugins/lsps-plugin/src/core/mod.rs b/plugins/lsps-plugin/src/core/mod.rs
new file mode 100644
index 00000000..bfc7a330
--- /dev/null
+++ b/plugins/lsps-plugin/src/core/mod.rs
@@ -0,0 +1 @@
+pub mod transport;
diff --git a/plugins/lsps-plugin/src/core/transport.rs b/plugins/lsps-plugin/src/core/transport.rs
new file mode 100644
index 00000000..016970a3
--- /dev/null
+++ b/plugins/lsps-plugin/src/core/transport.rs
@@ -0,0 +1,364 @@
+use crate::proto::jsonrpc::{JsonRpcRequest, JsonRpcResponse, RequestObject};
+use async_trait::async_trait;
+use core::fmt::Debug;
+use log::{debug, error};
+use rand::rngs::OsRng;
+use rand::TryRngCore;
+use serde::{de::DeserializeOwned, Serialize};
+use serde_json::Value;
+use std::sync::Arc;
+use thiserror::Error;
+
+/// Transport-specific errors that may occur when sending or receiving JSON-RPC
+/// messages.
+#[derive(Error, Debug)]
+pub enum Error {
+ #[error("Timeout")]
+ Timeout,
+ #[error("Internal error: {0}")]
+ Internal(String),
+ #[error("Couldn't parse JSON-RPC request")]
+ ParseRequest {
+ #[source]
+ source: serde_json::Error,
+ },
+}
+
+impl From<serde_json::Error> for Error {
+ fn from(value: serde_json::Error) -> Self {
+ Self::ParseRequest { source: value }
+ }
+}
+
+pub type Result<T> = std::result::Result<T, Error>;
+
+/// Defines the interface for transporting JSON-RPC messages.
+///
+/// Implementors of this trait are responsible for actually sending the JSON-RPC
+/// request over some transport mechanism (RPC, Bolt8, etc.)
+#[async_trait]
+pub trait Transport {
+ async fn send(&self, request: String) -> core::result::Result<String, Error>;
+ async fn notify(&self, request: String) -> core::result::Result<(), Error>;
+}
+
+/// A typed JSON-RPC client that works with any transport implementation.
+///
+/// This client handles the JSON-RPC protocol details including message
+/// formatting, request ID generation, and response parsing.
+#[derive(Clone)]
+pub struct JsonRpcClient<T: Transport> {
+ transport: Arc<T>,
+}
+
+impl<T: Transport> JsonRpcClient<T> {
+ pub fn new(transport: T) -> Self {
+ Self {
+ transport: Arc::new(transport),
+ }
+ }
+
+ /// Makes a JSON-RPC method call with raw JSON parameters and returns a raw
+ /// JSON result.
+ pub async fn call_raw(
+ &self,
+ method: &str,
+ params: Option<Value>,
+ ) -> Result<JsonRpcResponse<Value>> {
+ let id = generate_random_id();
+
+ debug!("Preparing request: method={}, id={}", method, id);
+ let request = RequestObject {
+ jsonrpc: "2.0".into(),
+ method: method.into(),
+ params,
+ id: Some(id.clone().into()),
+ };
+
+ let response: JsonRpcResponse<Value> = self.send_request(method, &request, id).await?;
+ Ok(response)
+ }
+
+ /// Makes a typed JSON-RPC method call with a request object and returns a
+ /// typed response.
+ ///
+ /// This method provides type safety by using request and response types
+ /// that implement the necessary traits.
+ pub async fn call_typed<RQ, RS>(&self, request: RQ) -> Result<JsonRpcResponse<RS>>
+ where
+ RQ: JsonRpcRequest + Serialize + Send + Sync,
+ RS: DeserializeOwned + Serialize + Debug + Send + Sync,
+ {
+ let method = RQ::METHOD;
+ let id = generate_random_id();
+
+ debug!("Preparing request: method={}, id={}", method, id);
+ let request = request.into_request(Some(id.clone().into()));
+ let response: JsonRpcResponse<RS> = self.send_request(method, &request, id).await?;
+ Ok(response)
+ }
+
+ /// Sends a notification with raw JSON parameters (no response expected).
+ pub async fn notify_raw(&self, method: &str, params: Option<Value>) -> Result<()> {
+ debug!("Preparing notification: method={}", method);
+ let request = RequestObject {
+ jsonrpc: "2.0".into(),
+ method: method.into(),
+ params,
+ id: None,
+ };
+ Ok(self.send_notification(method, &request).await?)
+ }
+
+ /// Sends a typed notification (no response expected).
+ pub async fn notify_typed<RQ>(&self, request: RQ) -> Result<()>
+ where
+ RQ: JsonRpcRequest + Serialize + Send + Sync,
+ {
+ let method = RQ::METHOD;
+
+ debug!("Preparing notification: method={}", method);
+ let request = request.into_request(None);
+ Ok(self.send_notification(method, &request).await?)
+ }
+
+ async fn send_request<RS, RP>(
+ &self,
+ method: &str,
+ payload: &RP,
+ id: String,
+ ) -> Result<JsonRpcResponse<RS>>
+ where
+ RP: Serialize + Send + Sync,
+ RS: DeserializeOwned + Serialize + Debug + Send + Sync,
+ {
+ let request_json = serde_json::to_string(&payload)?;
+ debug!(
+ "Sending request: method={}, id={}, request={:?}",
+ method, id, &request_json
+ );
+ let start = tokio::time::Instant::now();
+ let res_str = self.transport.send(request_json).await?;
+ let elapsed = start.elapsed();
+ debug!(
+ "Received response: method={}, id={}, response={}, elapsed={}ms",
+ method,
+ id,
+ &res_str,
+ elapsed.as_millis()
+ );
+ Ok(serde_json::from_str(&res_str)?)
+ }
+
+ async fn send_notification<RP>(&self, method: &str, payload: &RP) -> Result<()>
+ where
+ RP: Serialize + Send + Sync,
+ {
+ let request_json = serde_json::to_string(&payload)?;
+ debug!("Sending notification: method={}", method);
+ let start = tokio::time::Instant::now();
+ self.transport.notify(request_json).await?;
+ let elapsed = start.elapsed();
+ debug!(
+ "Sent notification: method={}, elapsed={}ms",
+ method,
+ elapsed.as_millis()
+ );
+ Ok(())
+ }
+}
+
+/// Generates a random ID for JSON-RPC requests.
+///
+/// Uses a secure random number generator to create a hex-encoded ID. Falls back
+/// to a timestamp-based ID if random generation fails.
+fn generate_random_id() -> String {
+ let mut bytes = [0u8; 10];
+ match OsRng.try_fill_bytes(&mut bytes) {
+ Ok(_) => hex::encode(bytes),
+ Err(e) => {
+ // Fallback to a timestamp-based ID if random generation fails
+ error!(
+ "Failed to generate random ID: {}, falling back to timestamp",
+ e
+ );
+ let timestamp = std::time::SystemTime::now()
+ .duration_since(std::time::UNIX_EPOCH)
+ .unwrap_or_default()
+ .as_nanos();
+ format!("fallback-{}", timestamp)
+ }
+ }
+}
+
+#[cfg(test)]
+
+mod test_json_rpc {
+ use super::*;
+ use crate::proto::jsonrpc::RpcError;
+ use serde::Deserialize;
+ use tokio::sync::OnceCell;
+
+ #[derive(Clone)]
+ struct TestTransport {
+ req: Arc<OnceCell<String>>,
+ res: Arc<Option<String>>,
+ err: Arc<Option<String>>,
+ }
+
+ impl TestTransport {
+ // Get the last request as parsed JSON
+ fn last_request_json(&self) -> Option<Value> {
+ self.req
+ .get()
+ .and_then(|req_str| serde_json::from_str(req_str).ok())
+ }
+ }
+
+ #[async_trait]
+ impl Transport for TestTransport {
+ async fn send(&self, req: String) -> core::result::Result<String, Error> {
+ // Store the request
+ let _ = self.req.set(req);
+
+ // Check for error first
+ if let Some(err) = &*self.err {
+ return Err(Error::Internal(err.into()));
+ }
+
+ // Then check for response
+ if let Some(res) = &*self.res {
+ return Ok(res.clone());
+ }
+
+ panic!("TestTransport: neither result nor error is set.");
+ }
+
+ async fn notify(&self, req: String) -> core::result::Result<(), Error> {
+ // Store the request
+ let _ = self.req.set(req);
+
+ // Check for error
+ if let Some(err) = &*self.err {
+ return Err(Error::Internal(err.into()));
+ }
+
+ Ok(())
+ }
+ }
+
+ #[derive(Default, Clone, Serialize, Deserialize, Debug)]
+ struct DummyCall {
+ foo: String,
+ bar: i32,
+ }
+
+ impl JsonRpcRequest for DummyCall {
+ const METHOD: &'static str = "dummy_call";
+ }
+
+ #[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
+ struct DummyResponse {
+ foo: String,
+ bar: i32,
+ }
+
+ #[tokio::test]
+ async fn test_typed_call_w_response() {
+ let req = DummyCall {
+ foo: String::from("hello world!"),
+ bar: 13,
+ };
+
+ let expected_res = DummyResponse {
+ foo: String::from("hello client!"),
+ bar: 10,
+ };
+
+ let res_obj = JsonRpcResponse::success(&expected_res, "my-id-123");
+ let res_str = serde_json::to_string(&res_obj).unwrap();
+
+ let transport = TestTransport {
+ req: Arc::new(OnceCell::const_new()),
+ res: Arc::new(Some(res_str)),
+ err: Arc::new(None),
+ };
+
+ let client_1 = JsonRpcClient::new(transport.clone());
+ let res = client_1
+ .call_typed::<_, DummyResponse>(req.clone())
+ .await
+ .expect("Should have an OK result")
+ .expect("Should not be a JSON-RPC error");
+ assert_eq!(res, expected_res);
+ let transport_req = transport
+ .last_request_json()
+ .expect("Transport should have gotten a request");
+ assert_eq!(
+ transport_req
+ .get("jsonrpc")
+ .and_then(|v| v.as_str())
+ .unwrap(),
+ "2.0"
+ );
+ assert_eq!(
+ transport_req
+ .get("params")
+ .and_then(|v| v.as_object())
+ .unwrap(),
+ serde_json::to_value(&req).unwrap().as_object().unwrap()
+ );
+ }
+
+ #[tokio::test]
+ async fn test_typed_call_w_rpc_error() {
+ let req = DummyCall {
+ foo: "hello world!".into(),
+ bar: 13,
+ };
+
+ let err_res = RpcError::custom_error_with_data(
+ -32099,
+ "got a custom error",
+ serde_json::json!({"got": "some"}),
+ );
+
+ let res_obj = JsonRpcResponse::error(err_res.clone(), "unique-id-123");
+ let res_str = serde_json::to_string(&res_obj).unwrap();
+
+ let transport = TestTransport {
+ req: Arc::new(OnceCell::const_new()),
+ res: Arc::new(Some(res_str)),
+ err: Arc::new(None),
+ };
+
+ let client_1 = JsonRpcClient::new(transport);
+ let res = client_1
+ .call_typed::<_, DummyResponse>(req)
+ .await
+ .expect("only inner rpc error")
+ .expect_err("expect rpc error");
+ assert_eq!(res, err_res);
+ }
+
+ #[tokio::test]
+ async fn test_typed_call_w_internal_error() {
+ let req = DummyCall {
+ foo: "hello world!".into(),
+ bar: 13,
+ };
+
+ let transport = TestTransport {
+ req: Arc::new(OnceCell::const_new()),
+ res: Arc::new(None),
+ err: Arc::new(Some(String::from("transport error"))),
+ };
+
+ let client_1 = JsonRpcClient::new(transport);
+ let res = client_1
+ .call_typed::<_, DummyResponse>(req)
+ .await
+ .expect_err("Expected error response");
+ assert!(matches!(res, Error::Internal(..)));
+ }
+}
diff --git a/plugins/lsps-plugin/src/jsonrpc/client.rs b/plugins/lsps-plugin/src/jsonrpc/client.rs
deleted file mode 100644
index 49ec130f..00000000
--- a/plugins/lsps-plugin/src/jsonrpc/client.rs
+++ /dev/null
@@ -1,374 +0,0 @@
-use crate::proto::jsonrpc::{
- Error as JsonRpcError, JsonRpcRequest, JsonRpcResponse, RequestObject, RpcError,
-};
-use async_trait::async_trait;
-use core::fmt::Debug;
-use log::{debug, error};
-use rand::rngs::OsRng;
-use rand::TryRngCore;
-use serde::{de::DeserializeOwned, Serialize};
-use serde_json::Value;
-use std::sync::Arc;
-use thiserror::Error;
-
-/// Transport-specific errors that may occur when sending or receiving JSON-RPC
-/// messages.
-#[derive(Error, Debug)]
-pub enum Error {
- #[error("Timeout")]
- Timeout,
- #[error("Internal error: {0}")]
- Internal(String),
- #[error("Got JSON-RPC error")]
- JsonRpcError(#[from] JsonRpcError),
- #[error("{0}")]
- RpcError(#[from] RpcError),
- #[error("Couldn't parse JSON-RPC request")]
- ParseRequest {
- #[source]
- source: serde_json::Error,
- },
-}
-
-impl From<serde_json::Error> for Error {
- fn from(value: serde_json::Error) -> Self {
- Self::ParseRequest { source: value }
- }
-}
-
-pub type Result<T> = std::result::Result<T, Error>;
-
-impl<R> From<JsonRpcResponse<R>> for Result<R> {
- fn from(value: JsonRpcResponse<R>) -> Self {
- value
- .into_result()
- .map_err(|e| Error::JsonRpcError(JsonRpcError::RpcError(e)))
- }
-}
-
-/// Defines the interface for transporting JSON-RPC messages.
-///
-/// Implementors of this trait are responsible for actually sending the JSON-RPC
-/// request over some transport mechanism (RPC, Bolt8, etc.)
-#[async_trait]
-pub trait Transport {
- async fn send(&self, request: String) -> core::result::Result<String, Error>;
- async fn notify(&self, request: String) -> core::result::Result<(), Error>;
-}
-
-/// A typed JSON-RPC client that works with any transport implementation.
-///
-/// This client handles the JSON-RPC protocol details including message
-/// formatting, request ID generation, and response parsing.
-#[derive(Clone)]
-pub struct JsonRpcClient<T: Transport> {
- transport: Arc<T>,
-}
-
-impl<T: Transport> JsonRpcClient<T> {
- pub fn new(transport: T) -> Self {
- Self {
- transport: Arc::new(transport),
- }
- }
-
- /// Makes a JSON-RPC method call with raw JSON parameters and returns a raw
- /// JSON result.
- pub async fn call_raw(&self, method: &str, params: Option<Value>) -> Result<Value> {
- let id = generate_random_id();
-
- debug!("Preparing request: method={}, id={}", method, id);
- let request = RequestObject {
- jsonrpc: "2.0".into(),
- method: method.into(),
- params,
- id: Some(id.clone().into()),
- };
-
- let response: JsonRpcResponse<Value> = self.send_request(method, &request, id).await?;
- response.into()
- }
-
- /// Makes a typed JSON-RPC method call with a request object and returns a
- /// typed response.
- ///
- /// This method provides type safety by using request and response types
- /// that implement the necessary traits.
- pub async fn call_typed<RQ, RS>(&self, request: RQ) -> Result<JsonRpcResponse<RS>>
- where
- RQ: JsonRpcRequest + Serialize + Send + Sync,
- RS: DeserializeOwned + Serialize + Debug + Send + Sync,
- {
- let method = RQ::METHOD;
- let id = generate_random_id();
-
- debug!("Preparing request: method={}, id={}", method, id);
- let request = request.into_request(Some(id.clone().into()));
- let response: JsonRpcResponse<RS> = self.send_request(method, &request, id).await?;
- Ok(response)
- }
-
- /// Sends a notification with raw JSON parameters (no response expected).
- pub async fn notify_raw(&self, method: &str, params: Option<Value>) -> Result<()> {
- debug!("Preparing notification: method={}", method);
- let request = RequestObject {
- jsonrpc: "2.0".into(),
- method: method.into(),
- params,
- id: None,
- };
- Ok(self.send_notification(method, &request).await?)
- }
-
- /// Sends a typed notification (no response expected).
- pub async fn notify_typed<RQ>(&self, request: RQ) -> Result<()>
- where
- RQ: JsonRpcRequest + Serialize + Send + Sync,
- {
- let method = RQ::METHOD;
-
- debug!("Preparing notification: method={}", method);
- let request = request.into_request(None);
- Ok(self.send_notification(method, &request).await?)
- }
-
- async fn send_request<RS, RP>(
- &self,
- method: &str,
- payload: &RP,
- id: String,
- ) -> Result<JsonRpcResponse<RS>>
- where
- RP: Serialize + Send + Sync,
- RS: DeserializeOwned + Serialize + Debug + Send + Sync,
- {
- let request_json = serde_json::to_string(&payload)?;
- debug!(
- "Sending request: method={}, id={}, request={:?}",
- method, id, &request_json
- );
- let start = tokio::time::Instant::now();
- let res_str = self.transport.send(request_json).await?;
- let elapsed = start.elapsed();
- debug!(
- "Received response: method={}, id={}, response={}, elapsed={}ms",
- method,
- id,
- &res_str,
- elapsed.as_millis()
- );
- Ok(serde_json::from_str(&res_str)?)
- }
-
- async fn send_notification<RP>(&self, method: &str, payload: &RP) -> Result<()>
- where
- RP: Serialize + Send + Sync,
- {
- let request_json = serde_json::to_string(&payload)?;
- debug!("Sending notification: method={}", method);
- let start = tokio::time::Instant::now();
- self.transport.notify(request_json).await?;
- let elapsed = start.elapsed();
- debug!(
- "Sent notification: method={}, elapsed={}ms",
- method,
- elapsed.as_millis()
- );
- Ok(())
- }
-}
-
-/// Generates a random ID for JSON-RPC requests.
-///
-/// Uses a secure random number generator to create a hex-encoded ID. Falls back
-/// to a timestamp-based ID if random generation fails.
-fn generate_random_id() -> String {
- let mut bytes = [0u8; 10];
- match OsRng.try_fill_bytes(&mut bytes) {
- Ok(_) => hex::encode(bytes),
- Err(e) => {
- // Fallback to a timestamp-based ID if random generation fails
- error!(
- "Failed to generate random ID: {}, falling back to timestamp",
- e
- );
- let timestamp = std::time::SystemTime::now()
- .duration_since(std::time::UNIX_EPOCH)
- .unwrap_or_default()
- .as_nanos();
- format!("fallback-{}", timestamp)
- }
- }
-}
-
-#[cfg(test)]
-
-mod test_json_rpc {
- use super::*;
- use crate::proto::jsonrpc::RpcError;
- use serde::Deserialize;
- use tokio::sync::OnceCell;
-
- #[derive(Clone)]
- struct TestTransport {
- req: Arc<OnceCell<String>>,
- res: Arc<Option<String>>,
- err: Arc<Option<String>>,
- }
-
- impl TestTransport {
- // Get the last request as parsed JSON
- fn last_request_json(&self) -> Option<Value> {
- self.req
- .get()
- .and_then(|req_str| serde_json::from_str(req_str).ok())
- }
- }
-
- #[async_trait]
- impl Transport for TestTransport {
- async fn send(&self, req: String) -> core::result::Result<String, Error> {
- // Store the request
- let _ = self.req.set(req);
-
- // Check for error first
- if let Some(err) = &*self.err {
- return Err(Error::Internal(err.into()));
- }
-
- // Then check for response
- if let Some(res) = &*self.res {
- return Ok(res.clone());
- }
-
- panic!("TestTransport: neither result nor error is set.");
- }
-
- async fn notify(&self, req: String) -> core::result::Result<(), Error> {
- // Store the request
- let _ = self.req.set(req);
-
- // Check for error
- if let Some(err) = &*self.err {
- return Err(Error::Internal(err.into()));
- }
-
- Ok(())
- }
- }
-
- #[derive(Default, Clone, Serialize, Deserialize, Debug)]
- struct DummyCall {
- foo: String,
- bar: i32,
- }
-
- impl JsonRpcRequest for DummyCall {
- const METHOD: &'static str = "dummy_call";
- }
-
- #[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq)]
- struct DummyResponse {
- foo: String,
- bar: i32,
- }
-
- #[tokio::test]
- async fn test_typed_call_w_response() {
- let req = DummyCall {
- foo: String::from("hello world!"),
- bar: 13,
- };
-
- let expected_res = DummyResponse {
- foo: String::from("hello client!"),
- bar: 10,
- };
-
- let res_obj = JsonRpcResponse::success(&expected_res, "my-id-123");
- let res_str = serde_json::to_string(&res_obj).unwrap();
-
- let transport = TestTransport {
- req: Arc::new(OnceCell::const_new()),
- res: Arc::new(Some(res_str)),
- err: Arc::new(None),
- };
-
- let client_1 = JsonRpcClient::new(transport.clone());
- let res = client_1
- .call_typed::<_, DummyResponse>(req.clone())
- .await
- .expect("Should have an OK result")
- .expect("Should not be a JSON-RPC error");
- assert_eq!(res, expected_res);
- let transport_req = transport
- .last_request_json()
- .expect("Transport should have gotten a request");
- assert_eq!(
- transport_req
- .get("jsonrpc")
- .and_then(|v| v.as_str())
- .unwrap(),
- "2.0"
- );
- assert_eq!(
- transport_req
- .get("params")
- .and_then(|v| v.as_object())
- .unwrap(),
- serde_json::to_value(&req).unwrap().as_object().unwrap()
- );
- }
-
- #[tokio::test]
- async fn test_typed_call_w_rpc_error() {
- let req = DummyCall {
- foo: "hello world!".into(),
- bar: 13,
- };
-
- let err_res = RpcError::custom_error_with_data(
- -32099,
- "got a custom error",
- serde_json::json!({"got": "some"}),
- );
-
- let res_obj = JsonRpcResponse::error(err_res.clone(), "unique-id-123");
- let res_str = serde_json::to_string(&res_obj).unwrap();
-
- let transport = TestTransport {
- req: Arc::new(OnceCell::const_new()),
- res: Arc::new(Some(res_str)),
- err: Arc::new(None),
- };
-
- let client_1 = JsonRpcClient::new(transport);
- let res = client_1
- .call_typed::<_, DummyResponse>(req)
- .await
- .expect("only inner rpc error")
- .expect_err("expect rpc error");
- assert_eq!(res, err_res);
- }
-
- #[tokio::test]
- async fn test_typed_call_w_internal_error() {
- let req = DummyCall {
- foo: "hello world!".into(),
- bar: 13,
- };
-
- let transport = TestTransport {
- req: Arc::new(OnceCell::const_new()),
- res: Arc::new(None),
- err: Arc::new(Some(String::from("transport error"))),
- };
-
- let client_1 = JsonRpcClient::new(transport);
- let res = client_1
- .call_typed::<_, DummyResponse>(req)
- .await
- .expect_err("Expected error response");
- assert!(matches!(res, Error::Internal(..)));
- }
-}
diff --git a/plugins/lsps-plugin/src/jsonrpc/mod.rs b/plugins/lsps-plugin/src/jsonrpc/mod.rs
index c07f47e0..74f47ad3 100644
--- a/plugins/lsps-plugin/src/jsonrpc/mod.rs
+++ b/plugins/lsps-plugin/src/jsonrpc/mod.rs
@@ -1,2 +1 @@
-pub mod client;
pub mod server;
diff --git a/plugins/lsps-plugin/src/jsonrpc/server.rs b/plugins/lsps-plugin/src/jsonrpc/server.rs
index f0ebdbb4..6b998f4b 100644
--- a/plugins/lsps-plugin/src/jsonrpc/server.rs
+++ b/plugins/lsps-plugin/src/jsonrpc/server.rs
@@ -1,5 +1,5 @@
use crate::proto::jsonrpc::RpcError;
-use crate::{jsonrpc::client::Result, proto::jsonrpc::JsonRpcResponse};
+use crate::{core::transport::Result, proto::jsonrpc::JsonRpcResponse};
use async_trait::async_trait;
use log::{debug, trace};
use std::{collections::HashMap, sync::Arc};
diff --git a/plugins/lsps-plugin/src/lib.rs b/plugins/lsps-plugin/src/lib.rs
index 3c37daa9..d7412fb4 100644
--- a/plugins/lsps-plugin/src/lib.rs
+++ b/plugins/lsps-plugin/src/lib.rs
@@ -1,3 +1,4 @@
+pub mod core;
pub mod jsonrpc;
pub mod lsps0;
pub mod lsps2;
diff --git a/plugins/lsps-plugin/src/lsps0/transport.rs b/plugins/lsps-plugin/src/lsps0/transport.rs
index 77b648f9..bae41a98 100644
--- a/plugins/lsps-plugin/src/lsps0/transport.rs
+++ b/plugins/lsps-plugin/src/lsps0/transport.rs
@@ -1,5 +1,5 @@
use crate::{
- jsonrpc::client::{Error, Transport},
+ core::transport::{Error, Transport},
proto::lsps0::LSPS0_MESSAGE_TYPE,
};
use async_trait::async_trait;
diff --git a/plugins/lsps-plugin/src/proto/jsonrpc.rs b/plugins/lsps-plugin/src/proto/jsonrpc.rs
index 8fc682d8..7c4f94fc 100644
--- a/plugins/lsps-plugin/src/proto/jsonrpc.rs
+++ b/plugins/lsps-plugin/src/proto/jsonrpc.rs
@@ -1,7 +1,6 @@
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use serde_json::{self, Value};
use std::fmt;
-use thiserror::Error;
// Constants for JSON-RPC error codes.
pub const PARSE_ERROR: i64 = -32700;
@@ -10,41 +9,6 @@ pub const METHOD_NOT_FOUND: i64 = -32601;
pub const INVALID_PARAMS: i64 = -32602;
pub const INTERNAL_ERROR: i64 = -32603;
-/// Error type for JSON-RPC related operations.
-///
-/// Encapsulates various error conditions that may occur during JSON-RPC
-/// operations, including serialization errors, transport issues, and
-/// protocol-specific errors.
-#[derive(Debug, Error)]
-pub enum Error {
- #[error("Failed to parse JSON-RPC response")]
- ParseResponse,
- #[error("Failed to parse JSON-RPC response")]
- ParseJsonResponse {
- #[source]
- source: serde_json::Error,
- },
- #[error("Got JSON-RPC error")]
- RpcError(#[from] RpcError),
- #[error("Internal error: {0}")]
- Other(String),
-}
-
-impl Error {
- pub fn other<T: core::fmt::Display>(v: T) -> Self {
- return Self::Other(v.to_string());
- }
-}
-
-impl From<serde_json::Error> for Error {
- fn from(value: serde_json::Error) -> Self {
- Self::ParseJsonResponse { source: value }
- }
-}
-
-/// Convenience type alias for Result with the JSON-RPC Error type.
-pub type Result<T> = std::result::Result<T, Error>;
-
/// Trait for types that can be converted into JSON-RPC request objects.
///
/// Implementing this trait allows a struct to be used as a typed JSON-RPC
diff --git a/plugins/lsps-plugin/src/service.rs b/plugins/lsps-plugin/src/service.rs
index 9c59c7e6..a4450aea 100644
--- a/plugins/lsps-plugin/src/service.rs
+++ b/plugins/lsps-plugin/src/service.rs
@@ -1,6 +1,6 @@
use anyhow::{anyhow, bail};
use async_trait::async_trait;
-use cln_lsps::jsonrpc::client::Error as CError;
+use cln_lsps::core::transport::{Error, Result as TransportResult};
use cln_lsps::jsonrpc::server::JsonRpcResponseWriter;
use cln_lsps::jsonrpc::server::JsonRpcServer;
use cln_lsps::lsps0::handler::Lsps0ListProtocolsHandler;
@@ -8,7 +8,7 @@ use cln_lsps::lsps0::transport::{self, CustomMsg};
use cln_lsps::lsps2;
use cln_lsps::lsps2::cln::{HtlcAcceptedRequest, HtlcAcceptedResponse};
use cln_lsps::lsps2::handler::{ClnApiRpc, HtlcAcceptedHookHandler};
-use cln_lsps::proto::jsonrpc::{Error as JError, JsonRpcRequest as _};
+use cln_lsps::proto::jsonrpc::JsonRpcRequest as _;
use cln_lsps::proto::lsps0::{Lsps0listProtocolsRequest, LSPS0_MESSAGE_TYPE};
use cln_lsps::proto::lsps2::{Lsps2BuyRequest, Lsps2GetInfoRequest};
use cln_lsps::util::wrap_payload_with_peer_id;
@@ -185,15 +185,11 @@ pub struct LspsResponseWriter {
#[async_trait]
impl JsonRpcResponseWriter for LspsResponseWriter {
- async fn write(&mut self, payload: &[u8]) -> std::result::Result<(), CError> {
+ async fn write(&mut self, payload: &[u8]) -> TransportResult<()> {
let mut client = cln_rpc::ClnRpc::new(&self.rpc_path)
.await
- .map_err(|e| JError::Other(e.to_string()))?;
+ .map_err(|e| Error::Internal(e.to_string()))?;
- Ok(
- transport::send_custommsg(&mut client, payload.to_vec(), self.peer_id)
- .await
- .map_err(|e| JError::Other(e.to_string()))?,
- )
+ transport::send_custommsg(&mut client, payload.to_vec(), self.peer_id).await
}
}
Why this scored 15/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.