Skip to content

Commit 275f706

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 c045137 commit 275f706

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;
@@ -54,6 +58,9 @@ pub(crate) enum LSPSMethod {
5458
LSPS1CreateOrder,
5559
LSPS2GetInfo,
5660
LSPS2Buy,
61+
LSPS5SetWebhook,
62+
LSPS5ListWebhooks,
63+
LSPS5RemoveWebhook,
5764
}
5865

5966
impl LSPSMethod {
@@ -65,6 +72,9 @@ impl LSPSMethod {
6572
Self::LSPS1GetOrder => LSPS1_GET_ORDER_METHOD_NAME,
6673
Self::LSPS2GetInfo => LSPS2_GET_INFO_METHOD_NAME,
6774
Self::LSPS2Buy => LSPS2_BUY_METHOD_NAME,
75+
Self::LSPS5SetWebhook => LSPS5_SET_WEBHOOK_METHOD_NAME,
76+
Self::LSPS5ListWebhooks => LSPS5_LIST_WEBHOOKS_METHOD_NAME,
77+
Self::LSPS5RemoveWebhook => LSPS5_REMOVE_WEBHOOK_METHOD_NAME,
6878
}
6979
}
7080
}
@@ -79,6 +89,10 @@ impl FromStr for LSPSMethod {
7989
LSPS1_GET_ORDER_METHOD_NAME => Ok(Self::LSPS1GetOrder),
8090
LSPS2_GET_INFO_METHOD_NAME => Ok(Self::LSPS2GetInfo),
8191
LSPS2_BUY_METHOD_NAME => Ok(Self::LSPS2Buy),
92+
// Add LSPS5 methods
93+
LSPS5_SET_WEBHOOK_METHOD_NAME => Ok(Self::LSPS5SetWebhook),
94+
LSPS5_LIST_WEBHOOKS_METHOD_NAME => Ok(Self::LSPS5ListWebhooks),
95+
LSPS5_REMOVE_WEBHOOK_METHOD_NAME => Ok(Self::LSPS5RemoveWebhook),
8296
_ => Err(&"Unknown method name"),
8397
}
8498
}
@@ -111,6 +125,17 @@ impl From<&LSPS2Request> for LSPSMethod {
111125
}
112126
}
113127

128+
// Add implementation for LSPS5Request
129+
impl From<&LSPS5Request> for LSPSMethod {
130+
fn from(value: &LSPS5Request) -> Self {
131+
match value {
132+
LSPS5Request::SetWebhook(_) => Self::LSPS5SetWebhook,
133+
LSPS5Request::ListWebhooks(_) => Self::LSPS5ListWebhooks,
134+
LSPS5Request::RemoveWebhook(_) => Self::LSPS5RemoveWebhook,
135+
}
136+
}
137+
}
138+
114139
impl<'de> Deserialize<'de> for LSPSMethod {
115140
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
116141
where
@@ -268,6 +293,8 @@ pub enum LSPSMessage {
268293
LSPS1(LSPS1Message),
269294
/// An LSPS2 message.
270295
LSPS2(LSPS2Message),
296+
/// An LSPS5 message.
297+
LSPS5(LSPS5Message),
271298
}
272299

273300
impl LSPSMessage {
@@ -295,6 +322,10 @@ impl LSPSMessage {
295322
LSPSMessage::LSPS2(LSPS2Message::Request(request_id, request)) => {
296323
Some((LSPSRequestId(request_id.0.clone()), request.into()))
297324
},
325+
// Add LSPS5
326+
LSPSMessage::LSPS5(LSPS5Message::Request(request_id, request)) => {
327+
Some((LSPSRequestId(request_id.0.clone()), request.into()))
328+
},
298329
_ => None,
299330
}
300331
}
@@ -411,6 +442,47 @@ impl Serialize for LSPSMessage {
411442
jsonrpc_object.serialize_field(JSONRPC_ID_FIELD_KEY, &serde_json::Value::Null)?;
412443
jsonrpc_object.serialize_field(JSONRPC_ERROR_FIELD_KEY, &error)?;
413444
},
445+
LSPSMessage::LSPS5(LSPS5Message::Request(request_id, request)) => {
446+
jsonrpc_object.serialize_field(JSONRPC_ID_FIELD_KEY, &request_id.0)?;
447+
jsonrpc_object
448+
.serialize_field(JSONRPC_METHOD_FIELD_KEY, &LSPSMethod::from(request))?;
449+
450+
match request {
451+
LSPS5Request::SetWebhook(params) => {
452+
jsonrpc_object.serialize_field(JSONRPC_PARAMS_FIELD_KEY, params)?
453+
},
454+
LSPS5Request::ListWebhooks(params) => {
455+
jsonrpc_object.serialize_field(JSONRPC_PARAMS_FIELD_KEY, params)?
456+
},
457+
LSPS5Request::RemoveWebhook(params) => {
458+
jsonrpc_object.serialize_field(JSONRPC_PARAMS_FIELD_KEY, params)?
459+
},
460+
}
461+
},
462+
LSPSMessage::LSPS5(LSPS5Message::Response(request_id, response)) => {
463+
jsonrpc_object.serialize_field(JSONRPC_ID_FIELD_KEY, &request_id.0)?;
464+
465+
match response {
466+
LSPS5Response::SetWebhook(result) => {
467+
jsonrpc_object.serialize_field(JSONRPC_RESULT_FIELD_KEY, result)?
468+
},
469+
LSPS5Response::SetWebhookError(error) => {
470+
jsonrpc_object.serialize_field(JSONRPC_ERROR_FIELD_KEY, error)?
471+
},
472+
LSPS5Response::ListWebhooks(result) => {
473+
jsonrpc_object.serialize_field(JSONRPC_RESULT_FIELD_KEY, result)?
474+
},
475+
LSPS5Response::ListWebhooksError(error) => {
476+
jsonrpc_object.serialize_field(JSONRPC_ERROR_FIELD_KEY, error)?
477+
},
478+
LSPS5Response::RemoveWebhook(result) => {
479+
jsonrpc_object.serialize_field(JSONRPC_RESULT_FIELD_KEY, result)?
480+
},
481+
LSPS5Response::RemoveWebhookError(error) => {
482+
jsonrpc_object.serialize_field(JSONRPC_ERROR_FIELD_KEY, error)?
483+
},
484+
}
485+
},
414486
}
415487

416488
jsonrpc_object.end()
@@ -524,6 +596,31 @@ impl<'de, 'a> Visitor<'de> for LSPSMessageVisitor<'a> {
524596
.map_err(de::Error::custom)?;
525597
Ok(LSPSMessage::LSPS2(LSPS2Message::Request(id, LSPS2Request::Buy(request))))
526598
},
599+
// Add LSPS5 methods
600+
LSPSMethod::LSPS5SetWebhook => {
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::SetWebhook(request),
606+
)))
607+
},
608+
LSPSMethod::LSPS5ListWebhooks => {
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::ListWebhooks(request),
614+
)))
615+
},
616+
LSPSMethod::LSPS5RemoveWebhook => {
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::RemoveWebhook(request),
622+
)))
623+
},
527624
},
528625
None => match self.request_id_to_method_map.remove(&id) {
529626
Some(method) => match method {
@@ -629,6 +726,58 @@ impl<'de, 'a> Visitor<'de> for LSPSMessageVisitor<'a> {
629726
Err(de::Error::custom("Received invalid JSON-RPC object: one of method, result, or error required"))
630727
}
631728
},
729+
// Add LSPS5 methods
730+
LSPSMethod::LSPS5SetWebhook => {
731+
if let Some(error) = error {
732+
Ok(LSPSMessage::LSPS5(LSPS5Message::Response(
733+
id,
734+
LSPS5Response::SetWebhookError(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::SetWebhook(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::LSPS5ListWebhooks => {
748+
if let Some(error) = error {
749+
Ok(LSPSMessage::LSPS5(LSPS5Message::Response(
750+
id,
751+
LSPS5Response::ListWebhooksError(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::ListWebhooks(response),
759+
)))
760+
} else {
761+
Err(de::Error::custom("Received invalid JSON-RPC object: one of method, result, or error required"))
762+
}
763+
},
764+
LSPSMethod::LSPS5RemoveWebhook => {
765+
if let Some(error) = error {
766+
Ok(LSPSMessage::LSPS5(LSPS5Message::Response(
767+
id,
768+
LSPS5Response::RemoveWebhookError(error),
769+
)))
770+
} else if let Some(result) = result {
771+
let response =
772+
serde_json::from_value(result).map_err(de::Error::custom)?;
773+
Ok(LSPSMessage::LSPS5(LSPS5Message::Response(
774+
id,
775+
LSPS5Response::RemoveWebhook(response),
776+
)))
777+
} else {
778+
Err(de::Error::custom("Received invalid JSON-RPC object: one of method, result, or error required"))
779+
}
780+
},
632781
},
633782
None => Err(de::Error::custom(format!(
634783
"Received response for unknown request id: {}",

0 commit comments

Comments
 (0)