Skip to content

Commit 813802c

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 9eb7681 commit 813802c

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
@@ -266,6 +289,8 @@ pub enum LSPSMessage {
266289
LSPS1(LSPS1Message),
267290
/// An LSPS2 message.
268291
LSPS2(LSPS2Message),
292+
/// An LSPS5 message.
293+
LSPS5(LSPS5Message),
269294
}
270295

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

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

0 commit comments

Comments
 (0)