Skip to content

Commit 6de0e77

Browse files
add webhook message support in serializer, events, and liquidity manager
- Update events.rs to include LSPS5 client and service event variants and From conversions. - Enhance lib.rs documentation to reference bLIP-55 / LSPS5 protocol support. - Modify manager.rs to initialize and route LSPS5 client and service handlers in LiquidityService. - Extend lsps0/ser.rs to support LSPS5 methods in LSPSMethod and LSPSMessage (de)serialization for SetWebhook, ListWebhooks, and RemoveWebhook.
1 parent ed23db9 commit 6de0e77

File tree

4 files changed

+240
-0
lines changed

4 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/ser.rs

+148
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ 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, LSPS5_REMOVE_WEBHOOK_METHOD_NAME, LSPS5_SET_WEBHOOK_METHOD_NAME
21+
};
1922
use crate::prelude::{HashMap, String};
2023

2124
use lightning::ln::msgs::LightningError;
@@ -58,6 +61,9 @@ pub(crate) enum LSPSMethod {
5861
LSPS1CreateOrder,
5962
LSPS2GetInfo,
6063
LSPS2Buy,
64+
LSPS5SetWebhook,
65+
LSPS5ListWebhooks,
66+
LSPS5RemoveWebhook,
6167
}
6268

6369
impl LSPSMethod {
@@ -69,6 +75,9 @@ impl LSPSMethod {
6975
Self::LSPS1GetOrder => LSPS1_GET_ORDER_METHOD_NAME,
7076
Self::LSPS2GetInfo => LSPS2_GET_INFO_METHOD_NAME,
7177
Self::LSPS2Buy => LSPS2_BUY_METHOD_NAME,
78+
Self::LSPS5SetWebhook => LSPS5_SET_WEBHOOK_METHOD_NAME,
79+
Self::LSPS5ListWebhooks => LSPS5_LIST_WEBHOOKS_METHOD_NAME,
80+
Self::LSPS5RemoveWebhook => LSPS5_REMOVE_WEBHOOK_METHOD_NAME,
7281
}
7382
}
7483
}
@@ -83,6 +92,10 @@ impl FromStr for LSPSMethod {
8392
LSPS1_GET_ORDER_METHOD_NAME => Ok(Self::LSPS1GetOrder),
8493
LSPS2_GET_INFO_METHOD_NAME => Ok(Self::LSPS2GetInfo),
8594
LSPS2_BUY_METHOD_NAME => Ok(Self::LSPS2Buy),
95+
// Add LSPS5 methods
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,17 @@ impl From<&LSPS2Request> for LSPSMethod {
115128
}
116129
}
117130

131+
// Add implementation for LSPS5Request
132+
impl From<&LSPS5Request> for LSPSMethod {
133+
fn from(value: &LSPS5Request) -> Self {
134+
match value {
135+
LSPS5Request::SetWebhook(_) => Self::LSPS5SetWebhook,
136+
LSPS5Request::ListWebhooks(_) => Self::LSPS5ListWebhooks,
137+
LSPS5Request::RemoveWebhook(_) => Self::LSPS5RemoveWebhook,
138+
}
139+
}
140+
}
141+
118142
impl<'de> Deserialize<'de> for LSPSMethod {
119143
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
120144
where
@@ -252,6 +276,8 @@ pub enum LSPSMessage {
252276
LSPS1(LSPS1Message),
253277
/// An LSPS2 message.
254278
LSPS2(LSPS2Message),
279+
/// An LSPS5 message.
280+
LSPS5(LSPS5Message),
255281
}
256282

257283
impl LSPSMessage {
@@ -279,6 +305,10 @@ impl LSPSMessage {
279305
LSPSMessage::LSPS2(LSPS2Message::Request(request_id, request)) => {
280306
Some((LSPSRequestId(request_id.0.clone()), request.into()))
281307
},
308+
// Add LSPS5
309+
LSPSMessage::LSPS5(LSPS5Message::Request(request_id, request)) => {
310+
Some((LSPSRequestId(request_id.0.clone()), request.into()))
311+
},
282312
_ => None,
283313
}
284314
}
@@ -395,6 +425,47 @@ impl Serialize for LSPSMessage {
395425
jsonrpc_object.serialize_field(JSONRPC_ID_FIELD_KEY, &serde_json::Value::Null)?;
396426
jsonrpc_object.serialize_field(JSONRPC_ERROR_FIELD_KEY, &error)?;
397427
},
428+
LSPSMessage::LSPS5(LSPS5Message::Request(request_id, request)) => {
429+
jsonrpc_object.serialize_field(JSONRPC_ID_FIELD_KEY, &request_id.0)?;
430+
jsonrpc_object
431+
.serialize_field(JSONRPC_METHOD_FIELD_KEY, &LSPSMethod::from(request))?;
432+
433+
match request {
434+
LSPS5Request::SetWebhook(params) => {
435+
jsonrpc_object.serialize_field(JSONRPC_PARAMS_FIELD_KEY, params)?
436+
},
437+
LSPS5Request::ListWebhooks(params) => {
438+
jsonrpc_object.serialize_field(JSONRPC_PARAMS_FIELD_KEY, params)?
439+
},
440+
LSPS5Request::RemoveWebhook(params) => {
441+
jsonrpc_object.serialize_field(JSONRPC_PARAMS_FIELD_KEY, params)?
442+
},
443+
}
444+
},
445+
LSPSMessage::LSPS5(LSPS5Message::Response(request_id, response)) => {
446+
jsonrpc_object.serialize_field(JSONRPC_ID_FIELD_KEY, &request_id.0)?;
447+
448+
match response {
449+
LSPS5Response::SetWebhook(result) => {
450+
jsonrpc_object.serialize_field(JSONRPC_RESULT_FIELD_KEY, result)?
451+
},
452+
LSPS5Response::SetWebhookError(error) => {
453+
jsonrpc_object.serialize_field(JSONRPC_ERROR_FIELD_KEY, error)?
454+
},
455+
LSPS5Response::ListWebhooks(result) => {
456+
jsonrpc_object.serialize_field(JSONRPC_RESULT_FIELD_KEY, result)?
457+
},
458+
LSPS5Response::ListWebhooksError(error) => {
459+
jsonrpc_object.serialize_field(JSONRPC_ERROR_FIELD_KEY, error)?
460+
},
461+
LSPS5Response::RemoveWebhook(result) => {
462+
jsonrpc_object.serialize_field(JSONRPC_RESULT_FIELD_KEY, result)?
463+
},
464+
LSPS5Response::RemoveWebhookError(error) => {
465+
jsonrpc_object.serialize_field(JSONRPC_ERROR_FIELD_KEY, error)?
466+
},
467+
}
468+
},
398469
}
399470

400471
jsonrpc_object.end()
@@ -508,6 +579,31 @@ impl<'de, 'a> Visitor<'de> for LSPSMessageVisitor<'a> {
508579
.map_err(de::Error::custom)?;
509580
Ok(LSPSMessage::LSPS2(LSPS2Message::Request(id, LSPS2Request::Buy(request))))
510581
},
582+
// Add LSPS5 methods
583+
LSPSMethod::LSPS5SetWebhook => {
584+
let request = serde_json::from_value(params.unwrap_or(json!({})))
585+
.map_err(de::Error::custom)?;
586+
Ok(LSPSMessage::LSPS5(LSPS5Message::Request(
587+
id,
588+
LSPS5Request::SetWebhook(request),
589+
)))
590+
},
591+
LSPSMethod::LSPS5ListWebhooks => {
592+
let request = serde_json::from_value(params.unwrap_or(json!({})))
593+
.map_err(de::Error::custom)?;
594+
Ok(LSPSMessage::LSPS5(LSPS5Message::Request(
595+
id,
596+
LSPS5Request::ListWebhooks(request),
597+
)))
598+
},
599+
LSPSMethod::LSPS5RemoveWebhook => {
600+
let request = serde_json::from_value(params.unwrap_or(json!({})))
601+
.map_err(de::Error::custom)?;
602+
Ok(LSPSMessage::LSPS5(LSPS5Message::Request(
603+
id,
604+
LSPS5Request::RemoveWebhook(request),
605+
)))
606+
},
511607
},
512608
None => match self.request_id_to_method_map.remove(&id) {
513609
Some(method) => match method {
@@ -613,6 +709,58 @@ impl<'de, 'a> Visitor<'de> for LSPSMessageVisitor<'a> {
613709
Err(de::Error::custom("Received invalid JSON-RPC object: one of method, result, or error required"))
614710
}
615711
},
712+
// Add LSPS5 methods
713+
LSPSMethod::LSPS5SetWebhook => {
714+
if let Some(error) = error {
715+
Ok(LSPSMessage::LSPS5(LSPS5Message::Response(
716+
id,
717+
LSPS5Response::SetWebhookError(error),
718+
)))
719+
} else if let Some(result) = result {
720+
let response =
721+
serde_json::from_value(result).map_err(de::Error::custom)?;
722+
Ok(LSPSMessage::LSPS5(LSPS5Message::Response(
723+
id,
724+
LSPS5Response::SetWebhook(response),
725+
)))
726+
} else {
727+
Err(de::Error::custom("Received invalid JSON-RPC object: one of method, result, or error required"))
728+
}
729+
},
730+
LSPSMethod::LSPS5ListWebhooks => {
731+
if let Some(error) = error {
732+
Ok(LSPSMessage::LSPS5(LSPS5Message::Response(
733+
id,
734+
LSPS5Response::ListWebhooksError(error),
735+
)))
736+
} else if let Some(result) = result {
737+
let response =
738+
serde_json::from_value(result).map_err(de::Error::custom)?;
739+
Ok(LSPSMessage::LSPS5(LSPS5Message::Response(
740+
id,
741+
LSPS5Response::ListWebhooks(response),
742+
)))
743+
} else {
744+
Err(de::Error::custom("Received invalid JSON-RPC object: one of method, result, or error required"))
745+
}
746+
},
747+
LSPSMethod::LSPS5RemoveWebhook => {
748+
if let Some(error) = error {
749+
Ok(LSPSMessage::LSPS5(LSPS5Message::Response(
750+
id,
751+
LSPS5Response::RemoveWebhookError(error),
752+
)))
753+
} else if let Some(result) = result {
754+
let response =
755+
serde_json::from_value(result).map_err(de::Error::custom)?;
756+
Ok(LSPSMessage::LSPS5(LSPS5Message::Response(
757+
id,
758+
LSPS5Response::RemoveWebhook(response),
759+
)))
760+
} else {
761+
Err(de::Error::custom("Received invalid JSON-RPC object: one of method, result, or error required"))
762+
}
763+
},
616764
},
617765
None => Err(de::Error::custom(format!(
618766
"Received response for unknown request id: {}",

0 commit comments

Comments
 (0)