Skip to content

Commit 18a8c34

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 67f0b3d commit 18a8c34

File tree

4 files changed

+241
-0
lines changed

4 files changed

+241
-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

+149
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,10 @@ 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+
// Add LSPS5 methods
97+
LSPS5_SET_WEBHOOK_METHOD_NAME => Ok(Self::LSPS5SetWebhook),
98+
LSPS5_LIST_WEBHOOKS_METHOD_NAME => Ok(Self::LSPS5ListWebhooks),
99+
LSPS5_REMOVE_WEBHOOK_METHOD_NAME => Ok(Self::LSPS5RemoveWebhook),
86100
_ => Err(&"Unknown method name"),
87101
}
88102
}
@@ -115,6 +129,17 @@ impl From<&LSPS2Request> for LSPSMethod {
115129
}
116130
}
117131

132+
// Add implementation for LSPS5Request
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+
118143
impl<'de> Deserialize<'de> for LSPSMethod {
119144
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
120145
where
@@ -252,6 +277,8 @@ pub enum LSPSMessage {
252277
LSPS1(LSPS1Message),
253278
/// An LSPS2 message.
254279
LSPS2(LSPS2Message),
280+
/// An LSPS5 message.
281+
LSPS5(LSPS5Message),
255282
}
256283

257284
impl LSPSMessage {
@@ -279,6 +306,10 @@ impl LSPSMessage {
279306
LSPSMessage::LSPS2(LSPS2Message::Request(request_id, request)) => {
280307
Some((LSPSRequestId(request_id.0.clone()), request.into()))
281308
},
309+
// Add LSPS5
310+
LSPSMessage::LSPS5(LSPS5Message::Request(request_id, request)) => {
311+
Some((LSPSRequestId(request_id.0.clone()), request.into()))
312+
},
282313
_ => None,
283314
}
284315
}
@@ -395,6 +426,47 @@ impl Serialize for LSPSMessage {
395426
jsonrpc_object.serialize_field(JSONRPC_ID_FIELD_KEY, &serde_json::Value::Null)?;
396427
jsonrpc_object.serialize_field(JSONRPC_ERROR_FIELD_KEY, &error)?;
397428
},
429+
LSPSMessage::LSPS5(LSPS5Message::Request(request_id, request)) => {
430+
jsonrpc_object.serialize_field(JSONRPC_ID_FIELD_KEY, &request_id.0)?;
431+
jsonrpc_object
432+
.serialize_field(JSONRPC_METHOD_FIELD_KEY, &LSPSMethod::from(request))?;
433+
434+
match request {
435+
LSPS5Request::SetWebhook(params) => {
436+
jsonrpc_object.serialize_field(JSONRPC_PARAMS_FIELD_KEY, params)?
437+
},
438+
LSPS5Request::ListWebhooks(params) => {
439+
jsonrpc_object.serialize_field(JSONRPC_PARAMS_FIELD_KEY, params)?
440+
},
441+
LSPS5Request::RemoveWebhook(params) => {
442+
jsonrpc_object.serialize_field(JSONRPC_PARAMS_FIELD_KEY, params)?
443+
},
444+
}
445+
},
446+
LSPSMessage::LSPS5(LSPS5Message::Response(request_id, response)) => {
447+
jsonrpc_object.serialize_field(JSONRPC_ID_FIELD_KEY, &request_id.0)?;
448+
449+
match response {
450+
LSPS5Response::SetWebhook(result) => {
451+
jsonrpc_object.serialize_field(JSONRPC_RESULT_FIELD_KEY, result)?
452+
},
453+
LSPS5Response::SetWebhookError(error) => {
454+
jsonrpc_object.serialize_field(JSONRPC_ERROR_FIELD_KEY, error)?
455+
},
456+
LSPS5Response::ListWebhooks(result) => {
457+
jsonrpc_object.serialize_field(JSONRPC_RESULT_FIELD_KEY, result)?
458+
},
459+
LSPS5Response::ListWebhooksError(error) => {
460+
jsonrpc_object.serialize_field(JSONRPC_ERROR_FIELD_KEY, error)?
461+
},
462+
LSPS5Response::RemoveWebhook(result) => {
463+
jsonrpc_object.serialize_field(JSONRPC_RESULT_FIELD_KEY, result)?
464+
},
465+
LSPS5Response::RemoveWebhookError(error) => {
466+
jsonrpc_object.serialize_field(JSONRPC_ERROR_FIELD_KEY, error)?
467+
},
468+
}
469+
},
398470
}
399471

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

0 commit comments

Comments
 (0)