Skip to content

Commit 095368e

Browse files
add messaging types for webhook operations
- Define LSPS5Request and LSPS5Response enums for webhook registration, listing, and removal. - Implement WebhookNotification and associated helper constructors for different notification types. - Implement serialization/deserialization support with comprehensive tests.
1 parent 3c8c6d3 commit 095368e

File tree

1 file changed

+245
-0
lines changed
  • lightning-liquidity/src/lsps5

1 file changed

+245
-0
lines changed

lightning-liquidity/src/lsps5/msgs.rs

Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
// This file is Copyright its original authors, visible in version control
2+
// history.
3+
//
4+
// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
5+
// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
7+
// You may not use this file except in accordance with one or both of these
8+
// licenses.
9+
10+
//! LSPS5 message formats for webhook registration
11+
12+
use crate::{
13+
lsps0::ser::{LSPSMessage, LSPSRequestId, LSPSResponseError},
14+
prelude::*,
15+
};
16+
use serde::{Deserialize, Serialize};
17+
use serde_json::{json, Value};
18+
19+
pub(crate) const LSPS5_TOO_LONG_ERROR_CODE: i32 = 500;
20+
pub(crate) const LSPS5_URL_PARSE_ERROR_CODE: i32 = 501;
21+
pub(crate) const LSPS5_UNSUPPORTED_PROTOCOL_ERROR_CODE: i32 = 502;
22+
pub(crate) const LSPS5_TOO_MANY_WEBHOOKS_ERROR_CODE: i32 = 503;
23+
pub(crate) const LSPS5_APP_NAME_NOT_FOUND_ERROR_CODE: i32 = 1010;
24+
25+
pub(crate) const LSPS5_SET_WEBHOOK_METHOD_NAME: &str = "lsps5.set_webhook";
26+
pub(crate) const LSPS5_LIST_WEBHOOKS_METHOD_NAME: &str = "lsps5.list_webhooks";
27+
pub(crate) const LSPS5_REMOVE_WEBHOOK_METHOD_NAME: &str = "lsps5.remove_webhook";
28+
29+
/// Webhook notification methods defined in LSPS5
30+
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
31+
#[serde(rename_all = "snake_case")]
32+
pub enum WebhookNotificationMethod {
33+
/// Webhook has been successfully registered
34+
#[serde(rename = "lsps5.webhook_registered")]
35+
WebhookRegistered,
36+
/// Client has payments pending to be received
37+
#[serde(rename = "lsps5.payment_incoming")]
38+
PaymentIncoming,
39+
/// HTLC or time-bound contract is about to expire
40+
#[serde(rename = "lsps5.expiry_soon")]
41+
ExpirySoon,
42+
/// LSP wants to take back some liquidity
43+
#[serde(rename = "lsps5.liquidity_management_request")]
44+
LiquidityManagementRequest,
45+
/// Client has onion messages pending
46+
#[serde(rename = "lsps5.onion_message_incoming")]
47+
OnionMessageIncoming,
48+
}
49+
50+
/// Parameters for lsps5.set_webhook request
51+
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
52+
pub struct SetWebhookRequest {
53+
/// Human-readable name for the webhook (max 64 bytes)
54+
pub app_name: String,
55+
/// URL of the webhook (max 1024 ASCII chars)
56+
pub webhook: String,
57+
}
58+
59+
/// Response for lsps5.set_webhook
60+
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
61+
pub struct SetWebhookResponse {
62+
/// Current number of webhooks registered for this client
63+
pub num_webhooks: u32,
64+
/// Maximum number of webhooks allowed by LSP
65+
pub max_webhooks: u32,
66+
/// Whether this is an unchanged registration
67+
pub no_change: bool,
68+
}
69+
70+
/// Parameters for lsps5.list_webhooks request (empty)
71+
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
72+
pub struct ListWebhooksRequest {}
73+
74+
/// Response for lsps5.list_webhooks
75+
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
76+
pub struct ListWebhooksResponse {
77+
/// List of app_names with registered webhooks
78+
pub app_names: Vec<String>,
79+
/// Maximum number of webhooks allowed by LSP
80+
pub max_webhooks: u32,
81+
}
82+
83+
/// Parameters for lsps5.remove_webhook request
84+
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
85+
pub struct RemoveWebhookRequest {
86+
/// App name identifying the webhook to remove
87+
pub app_name: String,
88+
}
89+
90+
/// Response for lsps5.remove_webhook (empty)
91+
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
92+
pub struct RemoveWebhookResponse {}
93+
94+
/// Parameters for lsps5.expiry_soon webhook notification
95+
#[derive(Debug, Clone, Serialize, Deserialize)]
96+
pub struct ExpirySoonParams {
97+
/// Block height of the timeout
98+
pub timeout: u32,
99+
}
100+
101+
/// Webhook notification payload
102+
#[derive(Debug, Clone, Serialize, Deserialize)]
103+
pub struct WebhookNotification {
104+
/// JSON-RPC version (must be "2.0")
105+
pub jsonrpc: String,
106+
/// Notification method
107+
pub method: WebhookNotificationMethod,
108+
/// Notification parameters
109+
pub params: Value,
110+
}
111+
112+
impl WebhookNotification {
113+
/// Create a new webhook notification
114+
pub fn new(method: WebhookNotificationMethod, params: Value) -> Self {
115+
Self { jsonrpc: "2.0".to_string(), method, params }
116+
}
117+
118+
/// Create webhook_registered notification (no params)
119+
pub fn webhook_registered() -> Self {
120+
Self::new(WebhookNotificationMethod::WebhookRegistered, json!({}))
121+
}
122+
123+
/// Create payment_incoming notification (no params)
124+
pub fn payment_incoming() -> Self {
125+
Self::new(WebhookNotificationMethod::PaymentIncoming, json!({}))
126+
}
127+
128+
/// Create expiry_soon notification
129+
pub fn expiry_soon(timeout: u32) -> Self {
130+
Self::new(WebhookNotificationMethod::ExpirySoon, json!({ "timeout": timeout }))
131+
}
132+
133+
/// Create liquidity_management_request notification (no params)
134+
pub fn liquidity_management_request() -> Self {
135+
Self::new(WebhookNotificationMethod::LiquidityManagementRequest, json!({}))
136+
}
137+
138+
/// Create onion_message_incoming notification (no params)
139+
pub fn onion_message_incoming() -> Self {
140+
Self::new(WebhookNotificationMethod::OnionMessageIncoming, json!({}))
141+
}
142+
}
143+
144+
/// An LSPS5 protocol request
145+
#[derive(Clone, Debug, PartialEq, Eq)]
146+
pub enum LSPS5Request {
147+
/// Register or update a webhook
148+
SetWebhook(SetWebhookRequest),
149+
/// List all registered webhooks
150+
ListWebhooks(ListWebhooksRequest),
151+
/// Remove a webhook
152+
RemoveWebhook(RemoveWebhookRequest),
153+
}
154+
155+
/// An LSPS5 protocol response
156+
#[derive(Clone, Debug, PartialEq, Eq)]
157+
pub enum LSPS5Response {
158+
/// Response to SetWebhook request
159+
SetWebhook(SetWebhookResponse),
160+
/// Error response to SetWebhook request
161+
SetWebhookError(LSPSResponseError),
162+
/// Response to ListWebhooks request
163+
ListWebhooks(ListWebhooksResponse),
164+
/// Error response to ListWebhooks request
165+
ListWebhooksError(LSPSResponseError),
166+
/// Response to RemoveWebhook request
167+
RemoveWebhook(RemoveWebhookResponse),
168+
/// Error response to RemoveWebhook request
169+
RemoveWebhookError(LSPSResponseError),
170+
}
171+
172+
#[derive(Clone, Debug, PartialEq, Eq)]
173+
/// An LSPS5 protocol message
174+
pub enum LSPS5Message {
175+
/// A request variant
176+
Request(LSPSRequestId, LSPS5Request),
177+
/// A response variant
178+
Response(LSPSRequestId, LSPS5Response),
179+
}
180+
181+
impl TryFrom<LSPSMessage> for LSPS5Message {
182+
type Error = ();
183+
184+
fn try_from(message: LSPSMessage) -> Result<Self, Self::Error> {
185+
match message {
186+
LSPSMessage::LSPS5(message) => Ok(message),
187+
_ => Err(()),
188+
}
189+
}
190+
}
191+
192+
impl From<LSPS5Message> for LSPSMessage {
193+
fn from(message: LSPS5Message) -> Self {
194+
LSPSMessage::LSPS5(message)
195+
}
196+
}
197+
198+
#[cfg(test)]
199+
mod tests {
200+
use super::*;
201+
use crate::alloc::string::ToString;
202+
203+
#[test]
204+
fn webhook_notification_serialization() {
205+
let notification = WebhookNotification::webhook_registered();
206+
let json_str = r#"{"jsonrpc":"2.0","method":"lsps5.webhook_registered","params":{}}"#;
207+
assert_eq!(json_str, serde_json::json!(notification).to_string());
208+
209+
let notification = WebhookNotification::expiry_soon(144);
210+
let json_str = r#"{"jsonrpc":"2.0","method":"lsps5.expiry_soon","params":{"timeout":144}}"#;
211+
assert_eq!(json_str, serde_json::json!(notification).to_string());
212+
}
213+
214+
#[test]
215+
fn parse_set_webhook_request() {
216+
let json_str = r#"{"app_name":"my_app","webhook":"https://example.com/webhook"}"#;
217+
let request: SetWebhookRequest = serde_json::from_str(json_str).unwrap();
218+
assert_eq!(request.app_name, "my_app");
219+
assert_eq!(request.webhook, "https://example.com/webhook");
220+
}
221+
222+
#[test]
223+
fn parse_set_webhook_response() {
224+
let json_str = r#"{"num_webhooks":1,"max_webhooks":5,"no_change":false}"#;
225+
let response: SetWebhookResponse = serde_json::from_str(json_str).unwrap();
226+
assert_eq!(response.num_webhooks, 1);
227+
assert_eq!(response.max_webhooks, 5);
228+
assert_eq!(response.no_change, false);
229+
}
230+
231+
#[test]
232+
fn parse_list_webhooks_response() {
233+
let json_str = r#"{"app_names":["app1","app2"],"max_webhooks":5}"#;
234+
let response: ListWebhooksResponse = serde_json::from_str(json_str).unwrap();
235+
assert_eq!(response.app_names, vec!["app1".to_string(), "app2".to_string()]);
236+
assert_eq!(response.max_webhooks, 5);
237+
}
238+
239+
#[test]
240+
fn parse_empty_requests_responses() {
241+
let json_str = r#"{}"#;
242+
let _list_req: ListWebhooksRequest = serde_json::from_str(json_str).unwrap();
243+
let _remove_resp: RemoveWebhookResponse = serde_json::from_str(json_str).unwrap();
244+
}
245+
}

0 commit comments

Comments
 (0)