Skip to content

Commit ab4f2d2

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 0c0ca39 commit ab4f2d2

File tree

5 files changed

+242
-0
lines changed

5 files changed

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

+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;
@@ -55,6 +59,9 @@ pub(crate) enum LSPSMethod {
5559
LSPS1CreateOrder,
5660
LSPS2GetInfo,
5761
LSPS2Buy,
62+
LSPS5SetWebhook,
63+
LSPS5ListWebhooks,
64+
LSPS5RemoveWebhook,
5865
}
5966

6067
impl LSPSMethod {
@@ -66,6 +73,9 @@ impl LSPSMethod {
6673
Self::LSPS1GetOrder => LSPS1_GET_ORDER_METHOD_NAME,
6774
Self::LSPS2GetInfo => LSPS2_GET_INFO_METHOD_NAME,
6875
Self::LSPS2Buy => LSPS2_BUY_METHOD_NAME,
76+
Self::LSPS5SetWebhook => LSPS5_SET_WEBHOOK_METHOD_NAME,
77+
Self::LSPS5ListWebhooks => LSPS5_LIST_WEBHOOKS_METHOD_NAME,
78+
Self::LSPS5RemoveWebhook => LSPS5_REMOVE_WEBHOOK_METHOD_NAME,
6979
}
7080
}
7181
}
@@ -80,6 +90,10 @@ impl FromStr for LSPSMethod {
8090
LSPS1_GET_ORDER_METHOD_NAME => Ok(Self::LSPS1GetOrder),
8191
LSPS2_GET_INFO_METHOD_NAME => Ok(Self::LSPS2GetInfo),
8292
LSPS2_BUY_METHOD_NAME => Ok(Self::LSPS2Buy),
93+
// Add LSPS5 methods
94+
LSPS5_SET_WEBHOOK_METHOD_NAME => Ok(Self::LSPS5SetWebhook),
95+
LSPS5_LIST_WEBHOOKS_METHOD_NAME => Ok(Self::LSPS5ListWebhooks),
96+
LSPS5_REMOVE_WEBHOOK_METHOD_NAME => Ok(Self::LSPS5RemoveWebhook),
8397
_ => Err(&"Unknown method name"),
8498
}
8599
}
@@ -112,6 +126,17 @@ impl From<&LSPS2Request> for LSPSMethod {
112126
}
113127
}
114128

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

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

417489
jsonrpc_object.end()
@@ -525,6 +597,31 @@ impl<'de, 'a> Visitor<'de> for LSPSMessageVisitor<'a> {
525597
.map_err(de::Error::custom)?;
526598
Ok(LSPSMessage::LSPS2(LSPS2Message::Request(id, LSPS2Request::Buy(request))))
527599
},
600+
// Add LSPS5 methods
601+
LSPSMethod::LSPS5SetWebhook => {
602+
let request = serde_json::from_value(params.unwrap_or(json!({})))
603+
.map_err(de::Error::custom)?;
604+
Ok(LSPSMessage::LSPS5(LSPS5Message::Request(
605+
id,
606+
LSPS5Request::SetWebhook(request),
607+
)))
608+
},
609+
LSPSMethod::LSPS5ListWebhooks => {
610+
let request = serde_json::from_value(params.unwrap_or(json!({})))
611+
.map_err(de::Error::custom)?;
612+
Ok(LSPSMessage::LSPS5(LSPS5Message::Request(
613+
id,
614+
LSPS5Request::ListWebhooks(request),
615+
)))
616+
},
617+
LSPSMethod::LSPS5RemoveWebhook => {
618+
let request = serde_json::from_value(params.unwrap_or(json!({})))
619+
.map_err(de::Error::custom)?;
620+
Ok(LSPSMessage::LSPS5(LSPS5Message::Request(
621+
id,
622+
LSPS5Request::RemoveWebhook(request),
623+
)))
624+
},
528625
},
529626
None => match self.request_id_to_method_map.remove(&id) {
530627
Some(method) => match method {
@@ -630,6 +727,58 @@ impl<'de, 'a> Visitor<'de> for LSPSMessageVisitor<'a> {
630727
Err(de::Error::custom("Received invalid JSON-RPC object: one of method, result, or error required"))
631728
}
632729
},
730+
// Add LSPS5 methods
731+
LSPSMethod::LSPS5SetWebhook => {
732+
if let Some(error) = error {
733+
Ok(LSPSMessage::LSPS5(LSPS5Message::Response(
734+
id,
735+
LSPS5Response::SetWebhookError(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::SetWebhook(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::LSPS5ListWebhooks => {
749+
if let Some(error) = error {
750+
Ok(LSPSMessage::LSPS5(LSPS5Message::Response(
751+
id,
752+
LSPS5Response::ListWebhooksError(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::ListWebhooks(response),
760+
)))
761+
} else {
762+
Err(de::Error::custom("Received invalid JSON-RPC object: one of method, result, or error required"))
763+
}
764+
},
765+
LSPSMethod::LSPS5RemoveWebhook => {
766+
if let Some(error) = error {
767+
Ok(LSPSMessage::LSPS5(LSPS5Message::Response(
768+
id,
769+
LSPS5Response::RemoveWebhookError(error),
770+
)))
771+
} else if let Some(result) = result {
772+
let response =
773+
serde_json::from_value(result).map_err(de::Error::custom)?;
774+
Ok(LSPSMessage::LSPS5(LSPS5Message::Response(
775+
id,
776+
LSPS5Response::RemoveWebhook(response),
777+
)))
778+
} else {
779+
Err(de::Error::custom("Received invalid JSON-RPC object: one of method, result, or error required"))
780+
}
781+
},
633782
},
634783
None => Err(de::Error::custom(format!(
635784
"Received response for unknown request id: {}",

0 commit comments

Comments
 (0)