Skip to content

Commit 349ac26

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. - Extend the match in TryFrom<LSPSMessage> to handle LSPSMessage::LSPS5 and explicitly return an error.
1 parent eb99488 commit 349ac26

File tree

5 files changed

+242
-0
lines changed

5 files changed

+242
-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::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

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)]
@@ -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

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: 149 additions & 0 deletions
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;
@@ -59,6 +63,9 @@ pub(crate) enum LSPSMethod {
5963
LSPS1CreateOrder,
6064
LSPS2GetInfo,
6165
LSPS2Buy,
66+
LSPS5SetWebhook,
67+
LSPS5ListWebhooks,
68+
LSPS5RemoveWebhook,
6269
}
6370

6471
impl LSPSMethod {
@@ -70,6 +77,9 @@ impl LSPSMethod {
7077
Self::LSPS1GetOrder => LSPS1_GET_ORDER_METHOD_NAME,
7178
Self::LSPS2GetInfo => LSPS2_GET_INFO_METHOD_NAME,
7279
Self::LSPS2Buy => LSPS2_BUY_METHOD_NAME,
80+
Self::LSPS5SetWebhook => LSPS5_SET_WEBHOOK_METHOD_NAME,
81+
Self::LSPS5ListWebhooks => LSPS5_LIST_WEBHOOKS_METHOD_NAME,
82+
Self::LSPS5RemoveWebhook => LSPS5_REMOVE_WEBHOOK_METHOD_NAME,
7383
}
7484
}
7585
}
@@ -84,6 +94,10 @@ impl FromStr for LSPSMethod {
8494
LSPS1_GET_ORDER_METHOD_NAME => Ok(Self::LSPS1GetOrder),
8595
LSPS2_GET_INFO_METHOD_NAME => Ok(Self::LSPS2GetInfo),
8696
LSPS2_BUY_METHOD_NAME => Ok(Self::LSPS2Buy),
97+
// Add LSPS5 methods
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),
87101
_ => Err(&"Unknown method name"),
88102
}
89103
}
@@ -116,6 +130,17 @@ impl From<&LSPS2Request> for LSPSMethod {
116130
}
117131
}
118132

133+
// Add implementation for LSPS5Request
134+
impl From<&LSPS5Request> for LSPSMethod {
135+
fn from(value: &LSPS5Request) -> Self {
136+
match value {
137+
LSPS5Request::SetWebhook(_) => Self::LSPS5SetWebhook,
138+
LSPS5Request::ListWebhooks(_) => Self::LSPS5ListWebhooks,
139+
LSPS5Request::RemoveWebhook(_) => Self::LSPS5RemoveWebhook,
140+
}
141+
}
142+
}
143+
119144
impl<'de> Deserialize<'de> for LSPSMethod {
120145
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
121146
where
@@ -276,6 +301,8 @@ pub enum LSPSMessage {
276301
LSPS1(LSPS1Message),
277302
/// An LSPS2 message.
278303
LSPS2(LSPS2Message),
304+
/// An LSPS5 message.
305+
LSPS5(LSPS5Message),
279306
}
280307

281308
impl LSPSMessage {
@@ -303,6 +330,10 @@ impl LSPSMessage {
303330
LSPSMessage::LSPS2(LSPS2Message::Request(request_id, request)) => {
304331
Some((LSPSRequestId(request_id.0.clone()), request.into()))
305332
},
333+
// Add LSPS5
334+
LSPSMessage::LSPS5(LSPS5Message::Request(request_id, request)) => {
335+
Some((LSPSRequestId(request_id.0.clone()), request.into()))
336+
},
306337
_ => None,
307338
}
308339
}
@@ -419,6 +450,47 @@ impl Serialize for LSPSMessage {
419450
jsonrpc_object.serialize_field(JSONRPC_ID_FIELD_KEY, &serde_json::Value::Null)?;
420451
jsonrpc_object.serialize_field(JSONRPC_ERROR_FIELD_KEY, &error)?;
421452
},
453+
LSPSMessage::LSPS5(LSPS5Message::Request(request_id, request)) => {
454+
jsonrpc_object.serialize_field(JSONRPC_ID_FIELD_KEY, &request_id.0)?;
455+
jsonrpc_object
456+
.serialize_field(JSONRPC_METHOD_FIELD_KEY, &LSPSMethod::from(request))?;
457+
458+
match request {
459+
LSPS5Request::SetWebhook(params) => {
460+
jsonrpc_object.serialize_field(JSONRPC_PARAMS_FIELD_KEY, params)?
461+
},
462+
LSPS5Request::ListWebhooks(params) => {
463+
jsonrpc_object.serialize_field(JSONRPC_PARAMS_FIELD_KEY, params)?
464+
},
465+
LSPS5Request::RemoveWebhook(params) => {
466+
jsonrpc_object.serialize_field(JSONRPC_PARAMS_FIELD_KEY, params)?
467+
},
468+
}
469+
},
470+
LSPSMessage::LSPS5(LSPS5Message::Response(request_id, response)) => {
471+
jsonrpc_object.serialize_field(JSONRPC_ID_FIELD_KEY, &request_id.0)?;
472+
473+
match response {
474+
LSPS5Response::SetWebhook(result) => {
475+
jsonrpc_object.serialize_field(JSONRPC_RESULT_FIELD_KEY, result)?
476+
},
477+
LSPS5Response::SetWebhookError(error) => {
478+
jsonrpc_object.serialize_field(JSONRPC_ERROR_FIELD_KEY, error)?
479+
},
480+
LSPS5Response::ListWebhooks(result) => {
481+
jsonrpc_object.serialize_field(JSONRPC_RESULT_FIELD_KEY, result)?
482+
},
483+
LSPS5Response::ListWebhooksError(error) => {
484+
jsonrpc_object.serialize_field(JSONRPC_ERROR_FIELD_KEY, error)?
485+
},
486+
LSPS5Response::RemoveWebhook(result) => {
487+
jsonrpc_object.serialize_field(JSONRPC_RESULT_FIELD_KEY, result)?
488+
},
489+
LSPS5Response::RemoveWebhookError(error) => {
490+
jsonrpc_object.serialize_field(JSONRPC_ERROR_FIELD_KEY, error)?
491+
},
492+
}
493+
},
422494
}
423495

424496
jsonrpc_object.end()
@@ -532,6 +604,31 @@ impl<'de, 'a> Visitor<'de> for LSPSMessageVisitor<'a> {
532604
.map_err(de::Error::custom)?;
533605
Ok(LSPSMessage::LSPS2(LSPS2Message::Request(id, LSPS2Request::Buy(request))))
534606
},
607+
// Add LSPS5 methods
608+
LSPSMethod::LSPS5SetWebhook => {
609+
let request = serde_json::from_value(params.unwrap_or(json!({})))
610+
.map_err(de::Error::custom)?;
611+
Ok(LSPSMessage::LSPS5(LSPS5Message::Request(
612+
id,
613+
LSPS5Request::SetWebhook(request),
614+
)))
615+
},
616+
LSPSMethod::LSPS5ListWebhooks => {
617+
let request = serde_json::from_value(params.unwrap_or(json!({})))
618+
.map_err(de::Error::custom)?;
619+
Ok(LSPSMessage::LSPS5(LSPS5Message::Request(
620+
id,
621+
LSPS5Request::ListWebhooks(request),
622+
)))
623+
},
624+
LSPSMethod::LSPS5RemoveWebhook => {
625+
let request = serde_json::from_value(params.unwrap_or(json!({})))
626+
.map_err(de::Error::custom)?;
627+
Ok(LSPSMessage::LSPS5(LSPS5Message::Request(
628+
id,
629+
LSPS5Request::RemoveWebhook(request),
630+
)))
631+
},
535632
},
536633
None => match self.request_id_to_method_map.remove(&id) {
537634
Some(method) => match method {
@@ -637,6 +734,58 @@ impl<'de, 'a> Visitor<'de> for LSPSMessageVisitor<'a> {
637734
Err(de::Error::custom("Received invalid JSON-RPC object: one of method, result, or error required"))
638735
}
639736
},
737+
// Add LSPS5 methods
738+
LSPSMethod::LSPS5SetWebhook => {
739+
if let Some(error) = error {
740+
Ok(LSPSMessage::LSPS5(LSPS5Message::Response(
741+
id,
742+
LSPS5Response::SetWebhookError(error),
743+
)))
744+
} else if let Some(result) = result {
745+
let response =
746+
serde_json::from_value(result).map_err(de::Error::custom)?;
747+
Ok(LSPSMessage::LSPS5(LSPS5Message::Response(
748+
id,
749+
LSPS5Response::SetWebhook(response),
750+
)))
751+
} else {
752+
Err(de::Error::custom("Received invalid JSON-RPC object: one of method, result, or error required"))
753+
}
754+
},
755+
LSPSMethod::LSPS5ListWebhooks => {
756+
if let Some(error) = error {
757+
Ok(LSPSMessage::LSPS5(LSPS5Message::Response(
758+
id,
759+
LSPS5Response::ListWebhooksError(error),
760+
)))
761+
} else if let Some(result) = result {
762+
let response =
763+
serde_json::from_value(result).map_err(de::Error::custom)?;
764+
Ok(LSPSMessage::LSPS5(LSPS5Message::Response(
765+
id,
766+
LSPS5Response::ListWebhooks(response),
767+
)))
768+
} else {
769+
Err(de::Error::custom("Received invalid JSON-RPC object: one of method, result, or error required"))
770+
}
771+
},
772+
LSPSMethod::LSPS5RemoveWebhook => {
773+
if let Some(error) = error {
774+
Ok(LSPSMessage::LSPS5(LSPS5Message::Response(
775+
id,
776+
LSPS5Response::RemoveWebhookError(error),
777+
)))
778+
} else if let Some(result) = result {
779+
let response =
780+
serde_json::from_value(result).map_err(de::Error::custom)?;
781+
Ok(LSPSMessage::LSPS5(LSPS5Message::Response(
782+
id,
783+
LSPS5Response::RemoveWebhook(response),
784+
)))
785+
} else {
786+
Err(de::Error::custom("Received invalid JSON-RPC object: one of method, result, or error required"))
787+
}
788+
},
640789
},
641790
None => Err(de::Error::custom(format!(
642791
"Received response for unknown request id: {}",

0 commit comments

Comments
 (0)