Skip to content

Commit e879f88

Browse files
nepetShahanaFarooqui
authored andcommitted
lsps: Add service implementation for LSPS0
Implements the LSPS0 service plugin for core lightning Changelog-Added: lsps-plugin: lsps0 service support Signed-off-by: Peter Neuroth <[email protected]>
1 parent f3f222f commit e879f88

File tree

4 files changed

+137
-2
lines changed

4 files changed

+137
-2
lines changed

plugins/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,4 @@ recklessrpc
2121
exposesecret
2222
cln-xpay
2323
cln-lsps-client
24+
cln-lsps-service

plugins/Makefile

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,10 @@ plugins/clnrest: target/${RUST_PROFILE}/clnrest
149149
@cp $< $@
150150
plugins/cln-lsps-client: target/${RUST_PROFILE}/cln-lsps-client
151151
@cp $< $@
152+
plugins/cln-lsps-service: target/${RUST_PROFILE}/cln-lsps-service
153+
@cp $< $@
152154

153-
PLUGINS += plugins/cln-grpc plugins/clnrest plugins/cln-lsps-client
155+
PLUGINS += plugins/cln-grpc plugins/clnrest plugins/cln-lsps-client plugins/cln-lsps-service
154156
endif
155157

156158
PLUGIN_COMMON_OBJS := \
@@ -310,10 +312,12 @@ target/${RUST_PROFILE}/clnrest: ${CLN_REST_PLUGIN_SRC}
310312
cargo build ${CARGO_OPTS} --bin clnrest
311313
target/${RUST_PROFILE}/cln-lsps-client: ${CLN_LSPS_PLUGIN_SRC}
312314
cargo build ${CARGO_OPTS} --bin cln-lsps-client
315+
target/${RUST_PROFILE}/cln-lsps-service: ${CLN_LSPS_PLUGIN_SRC}
316+
cargo build ${CARGO_OPTS} --bin cln-lsps-service
313317

314318
ifneq ($(RUST),0)
315319
include plugins/rest-plugin/Makefile
316-
DEFAULT_TARGETS += $(CLN_PLUGIN_EXAMPLES) plugins/cln-grpc plugins/clnrest plugins/cln-lsps-client
320+
DEFAULT_TARGETS += $(CLN_PLUGIN_EXAMPLES) plugins/cln-grpc plugins/clnrest plugins/cln-lsps-client plugins/cln-lsps-service
317321
endif
318322

319323
clean: plugins-clean

plugins/lsps-plugin/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ edition = "2021"
77
name = "cln-lsps-client"
88
path = "src/client.rs"
99

10+
[[bin]]
11+
name = "cln-lsps-service"
12+
path = "src/service.rs"
13+
1014
[dependencies]
1115
anyhow = "1.0"
1216
async-trait = "0.1"

plugins/lsps-plugin/src/service.rs

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
use anyhow::anyhow;
2+
use async_trait::async_trait;
3+
use cln_lsps::jsonrpc::server::{JsonRpcResponseWriter, RequestHandler};
4+
use cln_lsps::jsonrpc::{server::JsonRpcServer, JsonRpcRequest};
5+
use cln_lsps::jsonrpc::{JsonRpcResponse, RequestObject, RpcError, TransportError};
6+
use cln_lsps::lsps0;
7+
use cln_lsps::lsps0::model::{Lsps0listProtocolsRequest, Lsps0listProtocolsResponse};
8+
use cln_lsps::lsps0::transport::{self, CustomMsg};
9+
use cln_plugin::options::ConfigOption;
10+
use cln_plugin::{options, Plugin};
11+
use cln_rpc::notifications::CustomMsgNotification;
12+
use cln_rpc::primitives::PublicKey;
13+
use log::debug;
14+
use std::path::{Path, PathBuf};
15+
use std::str::FromStr;
16+
use std::sync::Arc;
17+
18+
/// An option to enable this service. It defaults to `false` as we don't want a
19+
/// node to be an LSP per default.
20+
/// If a user want's to run an LSP service on their node this has to explicitly
21+
/// set to true. We keep this as a dev option for now until it actually does
22+
/// something.
23+
const OPTION_ENABLED: options::DefaultBooleanConfigOption = ConfigOption::new_bool_with_default(
24+
"dev-lsps-service",
25+
false,
26+
"Enables an LSPS service on the node.",
27+
);
28+
29+
#[derive(Clone)]
30+
struct State {
31+
lsps_service: JsonRpcServer,
32+
}
33+
34+
#[tokio::main]
35+
async fn main() -> Result<(), anyhow::Error> {
36+
let lsps_service = JsonRpcServer::builder()
37+
.with_handler(
38+
Lsps0listProtocolsRequest::METHOD.to_string(),
39+
Arc::new(Lsps0ListProtocolsHandler),
40+
)
41+
.build();
42+
let state = State { lsps_service };
43+
44+
if let Some(plugin) = cln_plugin::Builder::new(tokio::io::stdin(), tokio::io::stdout())
45+
.option(OPTION_ENABLED)
46+
.hook("custommsg", on_custommsg)
47+
.configure()
48+
.await?
49+
{
50+
if !plugin.option(&OPTION_ENABLED)? {
51+
return plugin
52+
.disable(&format!("`{}` not enabled", OPTION_ENABLED.name))
53+
.await;
54+
}
55+
56+
let plugin = plugin.start(state).await?;
57+
plugin.join().await
58+
} else {
59+
Ok(())
60+
}
61+
}
62+
63+
async fn on_custommsg(
64+
p: Plugin<State>,
65+
v: serde_json::Value,
66+
) -> Result<serde_json::Value, anyhow::Error> {
67+
// All of this could be done async if needed.
68+
let continue_response = Ok(serde_json::json!({
69+
"result": "continue"
70+
}));
71+
let msg: CustomMsgNotification =
72+
serde_json::from_value(v).map_err(|e| anyhow!("invalid custommsg: {e}"))?;
73+
74+
let req = CustomMsg::from_str(&msg.payload).map_err(|e| anyhow!("invalid payload {e}"))?;
75+
if req.message_type != lsps0::transport::LSPS0_MESSAGE_TYPE {
76+
// We don't care if this is not for us!
77+
return continue_response;
78+
}
79+
80+
let dir = p.configuration().lightning_dir;
81+
let rpc_path = Path::new(&dir).join(&p.configuration().rpc_file);
82+
let mut writer = LspsResponseWriter {
83+
peer_id: msg.peer_id,
84+
rpc_path: rpc_path.try_into()?,
85+
};
86+
87+
let service = p.state().lsps_service.clone();
88+
match service.handle_message(&req.payload, &mut writer).await {
89+
Ok(_) => continue_response,
90+
Err(e) => {
91+
debug!("failed to handle lsps message: {}", e);
92+
continue_response
93+
}
94+
}
95+
}
96+
97+
pub struct LspsResponseWriter {
98+
peer_id: PublicKey,
99+
rpc_path: PathBuf,
100+
}
101+
102+
#[async_trait]
103+
impl JsonRpcResponseWriter for LspsResponseWriter {
104+
async fn write(&mut self, payload: &[u8]) -> cln_lsps::jsonrpc::Result<()> {
105+
let mut client = cln_rpc::ClnRpc::new(&self.rpc_path).await.map_err(|e| {
106+
cln_lsps::jsonrpc::Error::Transport(TransportError::Other(e.to_string()))
107+
})?;
108+
transport::send_custommsg(&mut client, payload.to_vec(), self.peer_id).await
109+
}
110+
}
111+
112+
pub struct Lsps0ListProtocolsHandler;
113+
114+
#[async_trait]
115+
impl RequestHandler for Lsps0ListProtocolsHandler {
116+
async fn handle(&self, payload: &[u8]) -> core::result::Result<Vec<u8>, RpcError> {
117+
let req: RequestObject<Lsps0listProtocolsRequest> =
118+
serde_json::from_slice(payload).unwrap();
119+
if let Some(id) = req.id {
120+
let res = Lsps0listProtocolsResponse { protocols: vec![] }.into_response(id);
121+
let res_vec = serde_json::to_vec(&res).unwrap();
122+
return Ok(res_vec);
123+
}
124+
Ok(vec![])
125+
}
126+
}

0 commit comments

Comments
 (0)