Skip to content

Commit 4ea5745

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 4f21cb2 commit 4ea5745

File tree

5 files changed

+240
-0
lines changed

5 files changed

+240
-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 lightning::ln::msgs::LightningError;
@@ -58,6 +62,9 @@ pub(crate) enum LSPSMethod {
5862
LSPS1CreateOrder,
5963
LSPS2GetInfo,
6064
LSPS2Buy,
65+
LSPS5SetWebhook,
66+
LSPS5ListWebhooks,
67+
LSPS5RemoveWebhook,
6168
}
6269

6370
impl LSPSMethod {
@@ -69,6 +76,9 @@ impl LSPSMethod {
6976
Self::LSPS1GetOrder => LSPS1_GET_ORDER_METHOD_NAME,
7077
Self::LSPS2GetInfo => LSPS2_GET_INFO_METHOD_NAME,
7178
Self::LSPS2Buy => LSPS2_BUY_METHOD_NAME,
79+
Self::LSPS5SetWebhook => LSPS5_SET_WEBHOOK_METHOD_NAME,
80+
Self::LSPS5ListWebhooks => LSPS5_LIST_WEBHOOKS_METHOD_NAME,
81+
Self::LSPS5RemoveWebhook => LSPS5_REMOVE_WEBHOOK_METHOD_NAME,
7282
}
7383
}
7484
}
@@ -83,6 +93,9 @@ impl FromStr for LSPSMethod {
8393
LSPS1_GET_ORDER_METHOD_NAME => Ok(Self::LSPS1GetOrder),
8494
LSPS2_GET_INFO_METHOD_NAME => Ok(Self::LSPS2GetInfo),
8595
LSPS2_BUY_METHOD_NAME => Ok(Self::LSPS2Buy),
96+
LSPS5_SET_WEBHOOK_METHOD_NAME => Ok(Self::LSPS5SetWebhook),
97+
LSPS5_LIST_WEBHOOKS_METHOD_NAME => Ok(Self::LSPS5ListWebhooks),
98+
LSPS5_REMOVE_WEBHOOK_METHOD_NAME => Ok(Self::LSPS5RemoveWebhook),
8699
_ => Err(&"Unknown method name"),
87100
}
88101
}
@@ -115,6 +128,16 @@ impl From<&LSPS2Request> for LSPSMethod {
115128
}
116129
}
117130

131+
impl From<&LSPS5Request> for LSPSMethod {
132+
fn from(value: &LSPS5Request) -> Self {
133+
match value {
134+
LSPS5Request::SetWebhook(_) => Self::LSPS5SetWebhook,
135+
LSPS5Request::ListWebhooks(_) => Self::LSPS5ListWebhooks,
136+
LSPS5Request::RemoveWebhook(_) => Self::LSPS5RemoveWebhook,
137+
}
138+
}
139+
}
140+
118141
impl<'de> Deserialize<'de> for LSPSMethod {
119142
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
120143
where
@@ -252,6 +275,8 @@ pub enum LSPSMessage {
252275
LSPS1(LSPS1Message),
253276
/// An LSPS2 message.
254277
LSPS2(LSPS2Message),
278+
/// An LSPS5 message.
279+
LSPS5(LSPS5Message),
255280
}
256281

257282
impl LSPSMessage {
@@ -279,6 +304,10 @@ impl LSPSMessage {
279304
LSPSMessage::LSPS2(LSPS2Message::Request(request_id, request)) => {
280305
Some((LSPSRequestId(request_id.0.clone()), request.into()))
281306
},
307+
// Add LSPS5
308+
LSPSMessage::LSPS5(LSPS5Message::Request(request_id, request)) => {
309+
Some((LSPSRequestId(request_id.0.clone()), request.into()))
310+
},
282311
_ => None,
283312
}
284313
}
@@ -395,6 +424,47 @@ impl Serialize for LSPSMessage {
395424
jsonrpc_object.serialize_field(JSONRPC_ID_FIELD_KEY, &serde_json::Value::Null)?;
396425
jsonrpc_object.serialize_field(JSONRPC_ERROR_FIELD_KEY, &error)?;
397426
},
427+
LSPSMessage::LSPS5(LSPS5Message::Request(request_id, request)) => {
428+
jsonrpc_object.serialize_field(JSONRPC_ID_FIELD_KEY, &request_id.0)?;
429+
jsonrpc_object
430+
.serialize_field(JSONRPC_METHOD_FIELD_KEY, &LSPSMethod::from(request))?;
431+
432+
match request {
433+
LSPS5Request::SetWebhook(params) => {
434+
jsonrpc_object.serialize_field(JSONRPC_PARAMS_FIELD_KEY, params)?
435+
},
436+
LSPS5Request::ListWebhooks(params) => {
437+
jsonrpc_object.serialize_field(JSONRPC_PARAMS_FIELD_KEY, params)?
438+
},
439+
LSPS5Request::RemoveWebhook(params) => {
440+
jsonrpc_object.serialize_field(JSONRPC_PARAMS_FIELD_KEY, params)?
441+
},
442+
}
443+
},
444+
LSPSMessage::LSPS5(LSPS5Message::Response(request_id, response)) => {
445+
jsonrpc_object.serialize_field(JSONRPC_ID_FIELD_KEY, &request_id.0)?;
446+
447+
match response {
448+
LSPS5Response::SetWebhook(result) => {
449+
jsonrpc_object.serialize_field(JSONRPC_RESULT_FIELD_KEY, result)?
450+
},
451+
LSPS5Response::SetWebhookError(error) => {
452+
jsonrpc_object.serialize_field(JSONRPC_ERROR_FIELD_KEY, error)?
453+
},
454+
LSPS5Response::ListWebhooks(result) => {
455+
jsonrpc_object.serialize_field(JSONRPC_RESULT_FIELD_KEY, result)?
456+
},
457+
LSPS5Response::ListWebhooksError(error) => {
458+
jsonrpc_object.serialize_field(JSONRPC_ERROR_FIELD_KEY, error)?
459+
},
460+
LSPS5Response::RemoveWebhook(result) => {
461+
jsonrpc_object.serialize_field(JSONRPC_RESULT_FIELD_KEY, result)?
462+
},
463+
LSPS5Response::RemoveWebhookError(error) => {
464+
jsonrpc_object.serialize_field(JSONRPC_ERROR_FIELD_KEY, error)?
465+
},
466+
}
467+
},
398468
}
399469

400470
jsonrpc_object.end()
@@ -508,6 +578,31 @@ impl<'de, 'a> Visitor<'de> for LSPSMessageVisitor<'a> {
508578
.map_err(de::Error::custom)?;
509579
Ok(LSPSMessage::LSPS2(LSPS2Message::Request(id, LSPS2Request::Buy(request))))
510580
},
581+
// Add LSPS5 methods
582+
LSPSMethod::LSPS5SetWebhook => {
583+
let request = serde_json::from_value(params.unwrap_or(json!({})))
584+
.map_err(de::Error::custom)?;
585+
Ok(LSPSMessage::LSPS5(LSPS5Message::Request(
586+
id,
587+
LSPS5Request::SetWebhook(request),
588+
)))
589+
},
590+
LSPSMethod::LSPS5ListWebhooks => {
591+
let request = serde_json::from_value(params.unwrap_or(json!({})))
592+
.map_err(de::Error::custom)?;
593+
Ok(LSPSMessage::LSPS5(LSPS5Message::Request(
594+
id,
595+
LSPS5Request::ListWebhooks(request),
596+
)))
597+
},
598+
LSPSMethod::LSPS5RemoveWebhook => {
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::RemoveWebhook(request),
604+
)))
605+
},
511606
},
512607
None => match self.request_id_to_method_map.remove(&id) {
513608
Some(method) => match method {
@@ -613,6 +708,58 @@ impl<'de, 'a> Visitor<'de> for LSPSMessageVisitor<'a> {
613708
Err(de::Error::custom("Received invalid JSON-RPC object: one of method, result, or error required"))
614709
}
615710
},
711+
// Add LSPS5 methods
712+
LSPSMethod::LSPS5SetWebhook => {
713+
if let Some(error) = error {
714+
Ok(LSPSMessage::LSPS5(LSPS5Message::Response(
715+
id,
716+
LSPS5Response::SetWebhookError(error),
717+
)))
718+
} else if let Some(result) = result {
719+
let response =
720+
serde_json::from_value(result).map_err(de::Error::custom)?;
721+
Ok(LSPSMessage::LSPS5(LSPS5Message::Response(
722+
id,
723+
LSPS5Response::SetWebhook(response),
724+
)))
725+
} else {
726+
Err(de::Error::custom("Received invalid JSON-RPC object: one of method, result, or error required"))
727+
}
728+
},
729+
LSPSMethod::LSPS5ListWebhooks => {
730+
if let Some(error) = error {
731+
Ok(LSPSMessage::LSPS5(LSPS5Message::Response(
732+
id,
733+
LSPS5Response::ListWebhooksError(error),
734+
)))
735+
} else if let Some(result) = result {
736+
let response =
737+
serde_json::from_value(result).map_err(de::Error::custom)?;
738+
Ok(LSPSMessage::LSPS5(LSPS5Message::Response(
739+
id,
740+
LSPS5Response::ListWebhooks(response),
741+
)))
742+
} else {
743+
Err(de::Error::custom("Received invalid JSON-RPC object: one of method, result, or error required"))
744+
}
745+
},
746+
LSPSMethod::LSPS5RemoveWebhook => {
747+
if let Some(error) = error {
748+
Ok(LSPSMessage::LSPS5(LSPS5Message::Response(
749+
id,
750+
LSPS5Response::RemoveWebhookError(error),
751+
)))
752+
} else if let Some(result) = result {
753+
let response =
754+
serde_json::from_value(result).map_err(de::Error::custom)?;
755+
Ok(LSPSMessage::LSPS5(LSPS5Message::Response(
756+
id,
757+
LSPS5Response::RemoveWebhook(response),
758+
)))
759+
} else {
760+
Err(de::Error::custom("Received invalid JSON-RPC object: one of method, result, or error required"))
761+
}
762+
},
616763
},
617764
None => Err(de::Error::custom(format!(
618765
"Received response for unknown request id: {}",

0 commit comments

Comments
 (0)