Skip to content

Commit db50c98

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 a713c58 commit db50c98

File tree

6 files changed

+286
-0
lines changed

6 files changed

+286
-0
lines changed

lightning-liquidity/src/events.rs

Lines changed: 17 additions & 0 deletions
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::sync::{Arc, Mutex};
2223
use alloc::collections::VecDeque;
2324
use alloc::vec::Vec;
@@ -117,6 +118,10 @@ pub enum LiquidityEvent {
117118
LSPS2Client(lsps2::event::LSPS2ClientEvent),
118119
/// An LSPS2 (JIT Channel) server event.
119120
LSPS2Service(lsps2::event::LSPS2ServiceEvent),
121+
/// An LSPS5 (Webhook) client event.
122+
LSPS5Client(lsps5::event::LSPS5ClientEvent),
123+
/// An LSPS5 (Webhook) server event.
124+
LSPS5Service(lsps5::event::LSPS5ServiceEvent),
120125
}
121126

122127
impl From<lsps0::event::LSPS0ClientEvent> for LiquidityEvent {
@@ -150,6 +155,18 @@ impl From<lsps2::event::LSPS2ServiceEvent> for LiquidityEvent {
150155
}
151156
}
152157

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

lightning-liquidity/src/lib.rs

Lines changed: 4 additions & 0 deletions
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)]
@@ -59,6 +62,7 @@ pub mod events;
5962
pub mod lsps0;
6063
pub mod lsps1;
6164
pub mod lsps2;
65+
pub mod lsps5;
6266
mod manager;
6367
pub mod message_queue;
6468
#[allow(dead_code)]

lightning-liquidity/src/lsps0/msgs.rs

Lines changed: 1 addition & 0 deletions
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

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ use crate::lsps1::msgs::{
2121
use crate::lsps2::msgs::{
2222
LSPS2Message, LSPS2Request, LSPS2Response, LSPS2_BUY_METHOD_NAME, LSPS2_GET_INFO_METHOD_NAME,
2323
};
24+
use crate::lsps5::msgs::{
25+
LSPS5Message, LSPS5Request, LSPS5Response, LSPS5_LIST_WEBHOOKS_METHOD_NAME,
26+
LSPS5_REMOVE_WEBHOOK_METHOD_NAME, LSPS5_SET_WEBHOOK_METHOD_NAME,
27+
};
28+
2429
use crate::prelude::HashMap;
2530

2631
use chrono::DateTime;
@@ -62,6 +67,9 @@ pub(crate) enum LSPSMethod {
6267
LSPS1CreateOrder,
6368
LSPS2GetInfo,
6469
LSPS2Buy,
70+
LSPS5SetWebhook,
71+
LSPS5ListWebhooks,
72+
LSPS5RemoveWebhook,
6573
}
6674

6775
impl LSPSMethod {
@@ -73,6 +81,9 @@ impl LSPSMethod {
7381
Self::LSPS1GetOrder => LSPS1_GET_ORDER_METHOD_NAME,
7482
Self::LSPS2GetInfo => LSPS2_GET_INFO_METHOD_NAME,
7583
Self::LSPS2Buy => LSPS2_BUY_METHOD_NAME,
84+
Self::LSPS5SetWebhook => LSPS5_SET_WEBHOOK_METHOD_NAME,
85+
Self::LSPS5ListWebhooks => LSPS5_LIST_WEBHOOKS_METHOD_NAME,
86+
Self::LSPS5RemoveWebhook => LSPS5_REMOVE_WEBHOOK_METHOD_NAME,
7687
}
7788
}
7889
}
@@ -87,6 +98,9 @@ impl FromStr for LSPSMethod {
8798
LSPS1_GET_ORDER_METHOD_NAME => Ok(Self::LSPS1GetOrder),
8899
LSPS2_GET_INFO_METHOD_NAME => Ok(Self::LSPS2GetInfo),
89100
LSPS2_BUY_METHOD_NAME => Ok(Self::LSPS2Buy),
101+
LSPS5_SET_WEBHOOK_METHOD_NAME => Ok(Self::LSPS5SetWebhook),
102+
LSPS5_LIST_WEBHOOKS_METHOD_NAME => Ok(Self::LSPS5ListWebhooks),
103+
LSPS5_REMOVE_WEBHOOK_METHOD_NAME => Ok(Self::LSPS5RemoveWebhook),
90104
_ => Err(&"Unknown method name"),
91105
}
92106
}
@@ -119,6 +133,16 @@ impl From<&LSPS2Request> for LSPSMethod {
119133
}
120134
}
121135

136+
impl From<&LSPS5Request> for LSPSMethod {
137+
fn from(value: &LSPS5Request) -> Self {
138+
match value {
139+
LSPS5Request::SetWebhook(_) => Self::LSPS5SetWebhook,
140+
LSPS5Request::ListWebhooks(_) => Self::LSPS5ListWebhooks,
141+
LSPS5Request::RemoveWebhook(_) => Self::LSPS5RemoveWebhook,
142+
}
143+
}
144+
}
145+
122146
impl<'de> Deserialize<'de> for LSPSMethod {
123147
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
124148
where
@@ -267,6 +291,8 @@ pub enum LSPSMessage {
267291
LSPS1(LSPS1Message),
268292
/// An LSPS2 message.
269293
LSPS2(LSPS2Message),
294+
/// An LSPS5 message.
295+
LSPS5(LSPS5Message),
270296
}
271297

272298
impl LSPSMessage {
@@ -294,6 +320,10 @@ impl LSPSMessage {
294320
LSPSMessage::LSPS2(LSPS2Message::Request(request_id, request)) => {
295321
Some((LSPSRequestId(request_id.0.clone()), request.into()))
296322
},
323+
// Add LSPS5
324+
LSPSMessage::LSPS5(LSPS5Message::Request(request_id, request)) => {
325+
Some((LSPSRequestId(request_id.0.clone()), request.into()))
326+
},
297327
_ => None,
298328
}
299329
}
@@ -410,6 +440,47 @@ impl Serialize for LSPSMessage {
410440
jsonrpc_object.serialize_field(JSONRPC_ID_FIELD_KEY, &serde_json::Value::Null)?;
411441
jsonrpc_object.serialize_field(JSONRPC_ERROR_FIELD_KEY, &error)?;
412442
},
443+
LSPSMessage::LSPS5(LSPS5Message::Request(request_id, request)) => {
444+
jsonrpc_object.serialize_field(JSONRPC_ID_FIELD_KEY, &request_id.0)?;
445+
jsonrpc_object
446+
.serialize_field(JSONRPC_METHOD_FIELD_KEY, &LSPSMethod::from(request))?;
447+
448+
match request {
449+
LSPS5Request::SetWebhook(params) => {
450+
jsonrpc_object.serialize_field(JSONRPC_PARAMS_FIELD_KEY, params)?
451+
},
452+
LSPS5Request::ListWebhooks(params) => {
453+
jsonrpc_object.serialize_field(JSONRPC_PARAMS_FIELD_KEY, params)?
454+
},
455+
LSPS5Request::RemoveWebhook(params) => {
456+
jsonrpc_object.serialize_field(JSONRPC_PARAMS_FIELD_KEY, params)?
457+
},
458+
}
459+
},
460+
LSPSMessage::LSPS5(LSPS5Message::Response(request_id, response)) => {
461+
jsonrpc_object.serialize_field(JSONRPC_ID_FIELD_KEY, &request_id.0)?;
462+
463+
match response {
464+
LSPS5Response::SetWebhook(result) => {
465+
jsonrpc_object.serialize_field(JSONRPC_RESULT_FIELD_KEY, result)?
466+
},
467+
LSPS5Response::SetWebhookError(error) => {
468+
jsonrpc_object.serialize_field(JSONRPC_ERROR_FIELD_KEY, error)?
469+
},
470+
LSPS5Response::ListWebhooks(result) => {
471+
jsonrpc_object.serialize_field(JSONRPC_RESULT_FIELD_KEY, result)?
472+
},
473+
LSPS5Response::ListWebhooksError(error) => {
474+
jsonrpc_object.serialize_field(JSONRPC_ERROR_FIELD_KEY, error)?
475+
},
476+
LSPS5Response::RemoveWebhook(result) => {
477+
jsonrpc_object.serialize_field(JSONRPC_RESULT_FIELD_KEY, result)?
478+
},
479+
LSPS5Response::RemoveWebhookError(error) => {
480+
jsonrpc_object.serialize_field(JSONRPC_ERROR_FIELD_KEY, error)?
481+
},
482+
}
483+
},
413484
}
414485

415486
jsonrpc_object.end()
@@ -523,6 +594,31 @@ impl<'de, 'a> Visitor<'de> for LSPSMessageVisitor<'a> {
523594
.map_err(de::Error::custom)?;
524595
Ok(LSPSMessage::LSPS2(LSPS2Message::Request(id, LSPS2Request::Buy(request))))
525596
},
597+
// Add LSPS5 methods
598+
LSPSMethod::LSPS5SetWebhook => {
599+
let request = serde_json::from_value(params.unwrap_or(json!({})))
600+
.map_err(de::Error::custom)?;
601+
Ok(LSPSMessage::LSPS5(LSPS5Message::Request(
602+
id,
603+
LSPS5Request::SetWebhook(request),
604+
)))
605+
},
606+
LSPSMethod::LSPS5ListWebhooks => {
607+
let request = serde_json::from_value(params.unwrap_or(json!({})))
608+
.map_err(de::Error::custom)?;
609+
Ok(LSPSMessage::LSPS5(LSPS5Message::Request(
610+
id,
611+
LSPS5Request::ListWebhooks(request),
612+
)))
613+
},
614+
LSPSMethod::LSPS5RemoveWebhook => {
615+
let request = serde_json::from_value(params.unwrap_or(json!({})))
616+
.map_err(de::Error::custom)?;
617+
Ok(LSPSMessage::LSPS5(LSPS5Message::Request(
618+
id,
619+
LSPS5Request::RemoveWebhook(request),
620+
)))
621+
},
526622
},
527623
None => match self.request_id_to_method_map.remove(&id) {
528624
Some(method) => match method {
@@ -628,6 +724,58 @@ impl<'de, 'a> Visitor<'de> for LSPSMessageVisitor<'a> {
628724
Err(de::Error::custom("Received invalid JSON-RPC object: one of method, result, or error required"))
629725
}
630726
},
727+
// Add LSPS5 methods
728+
LSPSMethod::LSPS5SetWebhook => {
729+
if let Some(error) = error {
730+
Ok(LSPSMessage::LSPS5(LSPS5Message::Response(
731+
id,
732+
LSPS5Response::SetWebhookError(error),
733+
)))
734+
} else if let Some(result) = result {
735+
let response =
736+
serde_json::from_value(result).map_err(de::Error::custom)?;
737+
Ok(LSPSMessage::LSPS5(LSPS5Message::Response(
738+
id,
739+
LSPS5Response::SetWebhook(response),
740+
)))
741+
} else {
742+
Err(de::Error::custom("Received invalid JSON-RPC object: one of method, result, or error required"))
743+
}
744+
},
745+
LSPSMethod::LSPS5ListWebhooks => {
746+
if let Some(error) = error {
747+
Ok(LSPSMessage::LSPS5(LSPS5Message::Response(
748+
id,
749+
LSPS5Response::ListWebhooksError(error),
750+
)))
751+
} else if let Some(result) = result {
752+
let response =
753+
serde_json::from_value(result).map_err(de::Error::custom)?;
754+
Ok(LSPSMessage::LSPS5(LSPS5Message::Response(
755+
id,
756+
LSPS5Response::ListWebhooks(response),
757+
)))
758+
} else {
759+
Err(de::Error::custom("Received invalid JSON-RPC object: one of method, result, or error required"))
760+
}
761+
},
762+
LSPSMethod::LSPS5RemoveWebhook => {
763+
if let Some(error) = error {
764+
Ok(LSPSMessage::LSPS5(LSPS5Message::Response(
765+
id,
766+
LSPS5Response::RemoveWebhookError(error),
767+
)))
768+
} else if let Some(result) = result {
769+
let response =
770+
serde_json::from_value(result).map_err(de::Error::custom)?;
771+
Ok(LSPSMessage::LSPS5(LSPS5Message::Response(
772+
id,
773+
LSPS5Response::RemoveWebhook(response),
774+
)))
775+
} else {
776+
Err(de::Error::custom("Received invalid JSON-RPC object: one of method, result, or error required"))
777+
}
778+
},
631779
},
632780
None => Err(de::Error::custom(format!(
633781
"Received response for unknown request id: {}",

0 commit comments

Comments
 (0)