Skip to content

Commit 731e890

Browse files
Integrate LSPS5 with liquidity manager
Fully integrates the LSPS5 webhook components into the lightning-liquidity framework, enabling usage through the LiquidityManager. It includes - Registering LSPS5 events in the event system - Adding LSPS5 module to the main library exports - Updating LSPS0 serialization to handle LSPS5 messages - Adding LSPS5 configuration options to client and service config structures - Implementing message handling for LSPS5 requests and responses - Adding accessor methods for LSPS5 client and service handlers With this change, LSPS5 webhook functionality can now be accessed through the standard LiquidityManager interface, following the same pattern as other LSPS protocols.
1 parent 123d929 commit 731e890

File tree

6 files changed

+285
-0
lines changed

6 files changed

+285
-0
lines changed

lightning-liquidity/src/events.rs

+17
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use crate::lsps0;
1919
use crate::lsps1;
2020
use crate::lsps2;
21+
use crate::lsps5;
2122
use crate::prelude::{Vec, VecDeque};
2223
use crate::sync::{Arc, Mutex};
2324

@@ -116,6 +117,10 @@ pub enum LiquidityEvent {
116117
LSPS2Client(lsps2::event::LSPS2ClientEvent),
117118
/// An LSPS2 (JIT Channel) server event.
118119
LSPS2Service(lsps2::event::LSPS2ServiceEvent),
120+
/// An LSPS5 (Webhook) client event.
121+
LSPS5Client(lsps5::event::LSPS5ClientEvent),
122+
/// An LSPS5 (Webhook) server event.
123+
LSPS5Service(lsps5::event::LSPS5ServiceEvent),
119124
}
120125

121126
impl From<lsps0::event::LSPS0ClientEvent> for LiquidityEvent {
@@ -149,6 +154,18 @@ impl From<lsps2::event::LSPS2ServiceEvent> for LiquidityEvent {
149154
}
150155
}
151156

157+
impl From<lsps5::event::LSPS5ClientEvent> for LiquidityEvent {
158+
fn from(event: lsps5::event::LSPS5ClientEvent) -> Self {
159+
Self::LSPS5Client(event)
160+
}
161+
}
162+
163+
impl From<lsps5::event::LSPS5ServiceEvent> for LiquidityEvent {
164+
fn from(event: lsps5::event::LSPS5ServiceEvent) -> Self {
165+
Self::LSPS5Service(event)
166+
}
167+
}
168+
152169
struct EventFuture {
153170
event_queue: Arc<Mutex<VecDeque<LiquidityEvent>>>,
154171
waker: Arc<Mutex<Option<Waker>>>,

lightning-liquidity/src/lib.rs

+4
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
//! an LSP will open a "just-in-time" channel. This is useful for the initial on-boarding of
2424
//! clients as the channel opening fees are deducted from the incoming payment, i.e., no funds are
2525
//! required client-side to initiate this flow.
26+
//! - [bLIP-55 / LSPS5] defines a protocol for sending webhook notifications to clients. This is
27+
//! useful for notifying clients about incoming payments, channel expiries, etc.
2628
//!
2729
//! To get started, you'll want to setup a [`LiquidityManager`] and configure it to be the
2830
//! [`CustomMessageHandler`] of your LDK node. You can then for example call
@@ -37,6 +39,7 @@
3739
//! [bLIP-50 / LSPS0]: https://github.com/lightning/blips/blob/master/blip-0050.md
3840
//! [bLIP-51 / LSPS1]: https://github.com/lightning/blips/blob/master/blip-0051.md
3941
//! [bLIP-52 / LSPS2]: https://github.com/lightning/blips/blob/master/blip-0052.md
42+
//! [bLIP-55 / LSPS5]: https://github.com/lightning/blips/pull/55/files
4043
//! [`CustomMessageHandler`]: lightning::ln::peer_handler::CustomMessageHandler
4144
//! [`LiquidityManager::next_event`]: crate::LiquidityManager::next_event
4245
#![deny(missing_docs)]
@@ -65,6 +68,7 @@ pub mod events;
6568
pub mod lsps0;
6669
pub mod lsps1;
6770
pub mod lsps2;
71+
pub mod lsps5;
6872
mod manager;
6973
pub mod message_queue;
7074
#[allow(dead_code)]

lightning-liquidity/src/lsps0/msgs.rs

+1
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ impl TryFrom<LSPSMessage> for LSPS0Message {
8383
LSPSMessage::LSPS0(message) => Ok(message),
8484
LSPSMessage::LSPS1(_) => Err(()),
8585
LSPSMessage::LSPS2(_) => Err(()),
86+
LSPSMessage::LSPS5(_) => Err(()),
8687
}
8788
}
8889
}

lightning-liquidity/src/lsps0/ser.rs

+147
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ use crate::lsps1::msgs::{
1616
use crate::lsps2::msgs::{
1717
LSPS2Message, LSPS2Request, LSPS2Response, LSPS2_BUY_METHOD_NAME, LSPS2_GET_INFO_METHOD_NAME,
1818
};
19+
use crate::lsps5::msgs::{
20+
LSPS5Message, LSPS5Request, LSPS5Response, LSPS5_LIST_WEBHOOKS_METHOD_NAME,
21+
LSPS5_REMOVE_WEBHOOK_METHOD_NAME, LSPS5_SET_WEBHOOK_METHOD_NAME,
22+
};
1923
use crate::prelude::{HashMap, String};
2024

2125
use chrono::DateTime;
@@ -60,6 +64,9 @@ pub(crate) enum LSPSMethod {
6064
LSPS1CreateOrder,
6165
LSPS2GetInfo,
6266
LSPS2Buy,
67+
LSPS5SetWebhook,
68+
LSPS5ListWebhooks,
69+
LSPS5RemoveWebhook,
6370
}
6471

6572
impl LSPSMethod {
@@ -71,6 +78,9 @@ impl LSPSMethod {
7178
Self::LSPS1GetOrder => LSPS1_GET_ORDER_METHOD_NAME,
7279
Self::LSPS2GetInfo => LSPS2_GET_INFO_METHOD_NAME,
7380
Self::LSPS2Buy => LSPS2_BUY_METHOD_NAME,
81+
Self::LSPS5SetWebhook => LSPS5_SET_WEBHOOK_METHOD_NAME,
82+
Self::LSPS5ListWebhooks => LSPS5_LIST_WEBHOOKS_METHOD_NAME,
83+
Self::LSPS5RemoveWebhook => LSPS5_REMOVE_WEBHOOK_METHOD_NAME,
7484
}
7585
}
7686
}
@@ -85,6 +95,9 @@ impl FromStr for LSPSMethod {
8595
LSPS1_GET_ORDER_METHOD_NAME => Ok(Self::LSPS1GetOrder),
8696
LSPS2_GET_INFO_METHOD_NAME => Ok(Self::LSPS2GetInfo),
8797
LSPS2_BUY_METHOD_NAME => Ok(Self::LSPS2Buy),
98+
LSPS5_SET_WEBHOOK_METHOD_NAME => Ok(Self::LSPS5SetWebhook),
99+
LSPS5_LIST_WEBHOOKS_METHOD_NAME => Ok(Self::LSPS5ListWebhooks),
100+
LSPS5_REMOVE_WEBHOOK_METHOD_NAME => Ok(Self::LSPS5RemoveWebhook),
88101
_ => Err(&"Unknown method name"),
89102
}
90103
}
@@ -117,6 +130,16 @@ impl From<&LSPS2Request> for LSPSMethod {
117130
}
118131
}
119132

133+
impl From<&LSPS5Request> for LSPSMethod {
134+
fn from(value: &LSPS5Request) -> Self {
135+
match value {
136+
LSPS5Request::SetWebhook(_) => Self::LSPS5SetWebhook,
137+
LSPS5Request::ListWebhooks(_) => Self::LSPS5ListWebhooks,
138+
LSPS5Request::RemoveWebhook(_) => Self::LSPS5RemoveWebhook,
139+
}
140+
}
141+
}
142+
120143
impl<'de> Deserialize<'de> for LSPSMethod {
121144
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
122145
where
@@ -265,6 +288,8 @@ pub enum LSPSMessage {
265288
LSPS1(LSPS1Message),
266289
/// An LSPS2 message.
267290
LSPS2(LSPS2Message),
291+
/// An LSPS5 message.
292+
LSPS5(LSPS5Message),
268293
}
269294

270295
impl LSPSMessage {
@@ -292,6 +317,10 @@ impl LSPSMessage {
292317
LSPSMessage::LSPS2(LSPS2Message::Request(request_id, request)) => {
293318
Some((LSPSRequestId(request_id.0.clone()), request.into()))
294319
},
320+
// Add LSPS5
321+
LSPSMessage::LSPS5(LSPS5Message::Request(request_id, request)) => {
322+
Some((LSPSRequestId(request_id.0.clone()), request.into()))
323+
},
295324
_ => None,
296325
}
297326
}
@@ -408,6 +437,47 @@ impl Serialize for LSPSMessage {
408437
jsonrpc_object.serialize_field(JSONRPC_ID_FIELD_KEY, &serde_json::Value::Null)?;
409438
jsonrpc_object.serialize_field(JSONRPC_ERROR_FIELD_KEY, &error)?;
410439
},
440+
LSPSMessage::LSPS5(LSPS5Message::Request(request_id, request)) => {
441+
jsonrpc_object.serialize_field(JSONRPC_ID_FIELD_KEY, &request_id.0)?;
442+
jsonrpc_object
443+
.serialize_field(JSONRPC_METHOD_FIELD_KEY, &LSPSMethod::from(request))?;
444+
445+
match request {
446+
LSPS5Request::SetWebhook(params) => {
447+
jsonrpc_object.serialize_field(JSONRPC_PARAMS_FIELD_KEY, params)?
448+
},
449+
LSPS5Request::ListWebhooks(params) => {
450+
jsonrpc_object.serialize_field(JSONRPC_PARAMS_FIELD_KEY, params)?
451+
},
452+
LSPS5Request::RemoveWebhook(params) => {
453+
jsonrpc_object.serialize_field(JSONRPC_PARAMS_FIELD_KEY, params)?
454+
},
455+
}
456+
},
457+
LSPSMessage::LSPS5(LSPS5Message::Response(request_id, response)) => {
458+
jsonrpc_object.serialize_field(JSONRPC_ID_FIELD_KEY, &request_id.0)?;
459+
460+
match response {
461+
LSPS5Response::SetWebhook(result) => {
462+
jsonrpc_object.serialize_field(JSONRPC_RESULT_FIELD_KEY, result)?
463+
},
464+
LSPS5Response::SetWebhookError(error) => {
465+
jsonrpc_object.serialize_field(JSONRPC_ERROR_FIELD_KEY, error)?
466+
},
467+
LSPS5Response::ListWebhooks(result) => {
468+
jsonrpc_object.serialize_field(JSONRPC_RESULT_FIELD_KEY, result)?
469+
},
470+
LSPS5Response::ListWebhooksError(error) => {
471+
jsonrpc_object.serialize_field(JSONRPC_ERROR_FIELD_KEY, error)?
472+
},
473+
LSPS5Response::RemoveWebhook(result) => {
474+
jsonrpc_object.serialize_field(JSONRPC_RESULT_FIELD_KEY, result)?
475+
},
476+
LSPS5Response::RemoveWebhookError(error) => {
477+
jsonrpc_object.serialize_field(JSONRPC_ERROR_FIELD_KEY, error)?
478+
},
479+
}
480+
},
411481
}
412482

413483
jsonrpc_object.end()
@@ -521,6 +591,31 @@ impl<'de, 'a> Visitor<'de> for LSPSMessageVisitor<'a> {
521591
.map_err(de::Error::custom)?;
522592
Ok(LSPSMessage::LSPS2(LSPS2Message::Request(id, LSPS2Request::Buy(request))))
523593
},
594+
// Add LSPS5 methods
595+
LSPSMethod::LSPS5SetWebhook => {
596+
let request = serde_json::from_value(params.unwrap_or(json!({})))
597+
.map_err(de::Error::custom)?;
598+
Ok(LSPSMessage::LSPS5(LSPS5Message::Request(
599+
id,
600+
LSPS5Request::SetWebhook(request),
601+
)))
602+
},
603+
LSPSMethod::LSPS5ListWebhooks => {
604+
let request = serde_json::from_value(params.unwrap_or(json!({})))
605+
.map_err(de::Error::custom)?;
606+
Ok(LSPSMessage::LSPS5(LSPS5Message::Request(
607+
id,
608+
LSPS5Request::ListWebhooks(request),
609+
)))
610+
},
611+
LSPSMethod::LSPS5RemoveWebhook => {
612+
let request = serde_json::from_value(params.unwrap_or(json!({})))
613+
.map_err(de::Error::custom)?;
614+
Ok(LSPSMessage::LSPS5(LSPS5Message::Request(
615+
id,
616+
LSPS5Request::RemoveWebhook(request),
617+
)))
618+
},
524619
},
525620
None => match self.request_id_to_method_map.remove(&id) {
526621
Some(method) => match method {
@@ -626,6 +721,58 @@ impl<'de, 'a> Visitor<'de> for LSPSMessageVisitor<'a> {
626721
Err(de::Error::custom("Received invalid JSON-RPC object: one of method, result, or error required"))
627722
}
628723
},
724+
// Add LSPS5 methods
725+
LSPSMethod::LSPS5SetWebhook => {
726+
if let Some(error) = error {
727+
Ok(LSPSMessage::LSPS5(LSPS5Message::Response(
728+
id,
729+
LSPS5Response::SetWebhookError(error),
730+
)))
731+
} else if let Some(result) = result {
732+
let response =
733+
serde_json::from_value(result).map_err(de::Error::custom)?;
734+
Ok(LSPSMessage::LSPS5(LSPS5Message::Response(
735+
id,
736+
LSPS5Response::SetWebhook(response),
737+
)))
738+
} else {
739+
Err(de::Error::custom("Received invalid JSON-RPC object: one of method, result, or error required"))
740+
}
741+
},
742+
LSPSMethod::LSPS5ListWebhooks => {
743+
if let Some(error) = error {
744+
Ok(LSPSMessage::LSPS5(LSPS5Message::Response(
745+
id,
746+
LSPS5Response::ListWebhooksError(error),
747+
)))
748+
} else if let Some(result) = result {
749+
let response =
750+
serde_json::from_value(result).map_err(de::Error::custom)?;
751+
Ok(LSPSMessage::LSPS5(LSPS5Message::Response(
752+
id,
753+
LSPS5Response::ListWebhooks(response),
754+
)))
755+
} else {
756+
Err(de::Error::custom("Received invalid JSON-RPC object: one of method, result, or error required"))
757+
}
758+
},
759+
LSPSMethod::LSPS5RemoveWebhook => {
760+
if let Some(error) = error {
761+
Ok(LSPSMessage::LSPS5(LSPS5Message::Response(
762+
id,
763+
LSPS5Response::RemoveWebhookError(error),
764+
)))
765+
} else if let Some(result) = result {
766+
let response =
767+
serde_json::from_value(result).map_err(de::Error::custom)?;
768+
Ok(LSPSMessage::LSPS5(LSPS5Message::Response(
769+
id,
770+
LSPS5Response::RemoveWebhook(response),
771+
)))
772+
} else {
773+
Err(de::Error::custom("Received invalid JSON-RPC object: one of method, result, or error required"))
774+
}
775+
},
629776
},
630777
None => Err(de::Error::custom(format!(
631778
"Received response for unknown request id: {}",

0 commit comments

Comments
 (0)