Skip to content

Commit 37f66f3

Browse files
committed
feat: add LSPS7 (Channel Lease Extensions) client handler
Implement bLIP-57 / LSPS7 client-side support in lightning-liquidity. LSPS7 allows extending the lifetime of LSP-purchased channels. Three JSON-RPC methods: - lsps7.get_extendable_channels: list channels eligible for extension - lsps7.create_order: create extension order with payment info - lsps7.get_order: poll order status Reuses LSPS1PaymentInfo for the payment object as specified. Client-only scope (no service handler).
1 parent e31b5c8 commit 37f66f3

File tree

13 files changed

+1115
-0
lines changed

13 files changed

+1115
-0
lines changed

lightning-liquidity/src/events/mod.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use crate::lsps0;
2424
use crate::lsps1;
2525
use crate::lsps2;
2626
use crate::lsps5;
27+
use crate::lsps7;
2728

2829
/// An event which you should probably take some action in response to.
2930
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -42,6 +43,8 @@ pub enum LiquidityEvent {
4243
LSPS5Client(lsps5::event::LSPS5ClientEvent),
4344
/// An LSPS5 (Webhook) server event.
4445
LSPS5Service(lsps5::event::LSPS5ServiceEvent),
46+
/// An LSPS7 (Channel Lease Extension) client event.
47+
LSPS7Client(lsps7::event::LSPS7ClientEvent),
4548
}
4649

4750
impl From<lsps0::event::LSPS0ClientEvent> for LiquidityEvent {
@@ -85,3 +88,9 @@ impl From<lsps5::event::LSPS5ServiceEvent> for LiquidityEvent {
8588
Self::LSPS5Service(event)
8689
}
8790
}
91+
92+
impl From<lsps7::event::LSPS7ClientEvent> for LiquidityEvent {
93+
fn from(event: lsps7::event::LSPS7ClientEvent) -> Self {
94+
Self::LSPS7Client(event)
95+
}
96+
}

lightning-liquidity/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ pub mod lsps0;
6363
pub mod lsps1;
6464
pub mod lsps2;
6565
pub mod lsps5;
66+
pub mod lsps7;
6667
mod manager;
6768
pub mod message_queue;
6869
pub mod persist;

lightning-liquidity/src/lsps0/msgs.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ impl TryFrom<LSPSMessage> for LSPS0Message {
8484
LSPSMessage::LSPS1(_) => Err(()),
8585
LSPSMessage::LSPS2(_) => Err(()),
8686
LSPSMessage::LSPS5(_) => Err(()),
87+
LSPSMessage::LSPS7(_) => Err(()),
8788
}
8889
}
8990
}

lightning-liquidity/src/lsps0/ser.rs

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ use crate::lsps5::msgs::{
2525
LSPS5Message, LSPS5Request, LSPS5Response, LSPS5_LIST_WEBHOOKS_METHOD_NAME,
2626
LSPS5_REMOVE_WEBHOOK_METHOD_NAME, LSPS5_SET_WEBHOOK_METHOD_NAME,
2727
};
28+
use crate::lsps7::msgs::{
29+
LSPS7Message, LSPS7Request, LSPS7Response, LSPS7_CREATE_ORDER_METHOD_NAME,
30+
LSPS7_GET_EXTENDABLE_CHANNELS_METHOD_NAME, LSPS7_GET_ORDER_METHOD_NAME,
31+
};
2832

2933
use crate::prelude::HashMap;
3034

@@ -69,6 +73,9 @@ pub(crate) enum LSPSMethod {
6973
LSPS5SetWebhook,
7074
LSPS5ListWebhooks,
7175
LSPS5RemoveWebhook,
76+
LSPS7GetExtendableChannels,
77+
LSPS7CreateOrder,
78+
LSPS7GetOrder,
7279
}
7380

7481
impl LSPSMethod {
@@ -83,6 +90,9 @@ impl LSPSMethod {
8390
Self::LSPS5SetWebhook => LSPS5_SET_WEBHOOK_METHOD_NAME,
8491
Self::LSPS5ListWebhooks => LSPS5_LIST_WEBHOOKS_METHOD_NAME,
8592
Self::LSPS5RemoveWebhook => LSPS5_REMOVE_WEBHOOK_METHOD_NAME,
93+
Self::LSPS7GetExtendableChannels => LSPS7_GET_EXTENDABLE_CHANNELS_METHOD_NAME,
94+
Self::LSPS7CreateOrder => LSPS7_CREATE_ORDER_METHOD_NAME,
95+
Self::LSPS7GetOrder => LSPS7_GET_ORDER_METHOD_NAME,
8696
}
8797
}
8898
}
@@ -100,6 +110,9 @@ impl FromStr for LSPSMethod {
100110
LSPS5_SET_WEBHOOK_METHOD_NAME => Ok(Self::LSPS5SetWebhook),
101111
LSPS5_LIST_WEBHOOKS_METHOD_NAME => Ok(Self::LSPS5ListWebhooks),
102112
LSPS5_REMOVE_WEBHOOK_METHOD_NAME => Ok(Self::LSPS5RemoveWebhook),
113+
LSPS7_GET_EXTENDABLE_CHANNELS_METHOD_NAME => Ok(Self::LSPS7GetExtendableChannels),
114+
LSPS7_CREATE_ORDER_METHOD_NAME => Ok(Self::LSPS7CreateOrder),
115+
LSPS7_GET_ORDER_METHOD_NAME => Ok(Self::LSPS7GetOrder),
103116
_ => Err(&"Unknown method name"),
104117
}
105118
}
@@ -142,6 +155,16 @@ impl From<&LSPS5Request> for LSPSMethod {
142155
}
143156
}
144157

158+
impl From<&LSPS7Request> for LSPSMethod {
159+
fn from(value: &LSPS7Request) -> Self {
160+
match value {
161+
LSPS7Request::GetExtendableChannels(_) => Self::LSPS7GetExtendableChannels,
162+
LSPS7Request::CreateOrder(_) => Self::LSPS7CreateOrder,
163+
LSPS7Request::GetOrder(_) => Self::LSPS7GetOrder,
164+
}
165+
}
166+
}
167+
145168
impl<'de> Deserialize<'de> for LSPSMethod {
146169
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
147170
where
@@ -326,6 +349,8 @@ pub enum LSPSMessage {
326349
LSPS2(LSPS2Message),
327350
/// An LSPS5 message.
328351
LSPS5(LSPS5Message),
352+
/// An LSPS7 message.
353+
LSPS7(LSPS7Message),
329354
}
330355

331356
impl LSPSMessage {
@@ -356,6 +381,9 @@ impl LSPSMessage {
356381
LSPSMessage::LSPS5(LSPS5Message::Request(request_id, request)) => {
357382
Some((LSPSRequestId(request_id.0.clone()), request.into()))
358383
},
384+
LSPSMessage::LSPS7(LSPS7Message::Request(request_id, request)) => {
385+
Some((LSPSRequestId(request_id.0.clone()), request.into()))
386+
},
359387
_ => None,
360388
}
361389
}
@@ -510,6 +538,47 @@ impl Serialize for LSPSMessage {
510538
},
511539
}
512540
},
541+
LSPSMessage::LSPS7(LSPS7Message::Request(request_id, request)) => {
542+
jsonrpc_object.serialize_field(JSONRPC_ID_FIELD_KEY, &request_id.0)?;
543+
jsonrpc_object
544+
.serialize_field(JSONRPC_METHOD_FIELD_KEY, &LSPSMethod::from(request))?;
545+
546+
match request {
547+
LSPS7Request::GetExtendableChannels(params) => {
548+
jsonrpc_object.serialize_field(JSONRPC_PARAMS_FIELD_KEY, params)?
549+
},
550+
LSPS7Request::CreateOrder(params) => {
551+
jsonrpc_object.serialize_field(JSONRPC_PARAMS_FIELD_KEY, params)?
552+
},
553+
LSPS7Request::GetOrder(params) => {
554+
jsonrpc_object.serialize_field(JSONRPC_PARAMS_FIELD_KEY, params)?
555+
},
556+
}
557+
},
558+
LSPSMessage::LSPS7(LSPS7Message::Response(request_id, response)) => {
559+
jsonrpc_object.serialize_field(JSONRPC_ID_FIELD_KEY, &request_id.0)?;
560+
561+
match response {
562+
LSPS7Response::GetExtendableChannels(result) => {
563+
jsonrpc_object.serialize_field(JSONRPC_RESULT_FIELD_KEY, result)?
564+
},
565+
LSPS7Response::GetExtendableChannelsError(error) => {
566+
jsonrpc_object.serialize_field(JSONRPC_ERROR_FIELD_KEY, error)?
567+
},
568+
LSPS7Response::CreateOrder(result) => {
569+
jsonrpc_object.serialize_field(JSONRPC_RESULT_FIELD_KEY, result)?
570+
},
571+
LSPS7Response::CreateOrderError(error) => {
572+
jsonrpc_object.serialize_field(JSONRPC_ERROR_FIELD_KEY, error)?
573+
},
574+
LSPS7Response::GetOrder(result) => {
575+
jsonrpc_object.serialize_field(JSONRPC_RESULT_FIELD_KEY, result)?
576+
},
577+
LSPS7Response::GetOrderError(error) => {
578+
jsonrpc_object.serialize_field(JSONRPC_ERROR_FIELD_KEY, error)?
579+
},
580+
}
581+
},
513582
}
514583

515584
jsonrpc_object.end()
@@ -647,6 +716,30 @@ impl<'de, 'a> Visitor<'de> for LSPSMessageVisitor<'a> {
647716
LSPS5Request::RemoveWebhook(request),
648717
)))
649718
},
719+
LSPSMethod::LSPS7GetExtendableChannels => {
720+
let request = serde_json::from_value(params.unwrap_or(json!({})))
721+
.map_err(de::Error::custom)?;
722+
Ok(LSPSMessage::LSPS7(LSPS7Message::Request(
723+
id,
724+
LSPS7Request::GetExtendableChannels(request),
725+
)))
726+
},
727+
LSPSMethod::LSPS7CreateOrder => {
728+
let request = serde_json::from_value(params.unwrap_or(json!({})))
729+
.map_err(de::Error::custom)?;
730+
Ok(LSPSMessage::LSPS7(LSPS7Message::Request(
731+
id,
732+
LSPS7Request::CreateOrder(request),
733+
)))
734+
},
735+
LSPSMethod::LSPS7GetOrder => {
736+
let request = serde_json::from_value(params.unwrap_or(json!({})))
737+
.map_err(de::Error::custom)?;
738+
Ok(LSPSMessage::LSPS7(LSPS7Message::Request(
739+
id,
740+
LSPS7Request::GetOrder(request),
741+
)))
742+
},
650743
},
651744
None => match self.request_id_to_method_map.remove(&id) {
652745
Some(method) => match method {
@@ -798,6 +891,57 @@ impl<'de, 'a> Visitor<'de> for LSPSMessageVisitor<'a> {
798891
Err(de::Error::custom("Received invalid JSON-RPC object: one of method, result, or error required"))
799892
}
800893
},
894+
LSPSMethod::LSPS7GetExtendableChannels => {
895+
if let Some(error) = error {
896+
Ok(LSPSMessage::LSPS7(LSPS7Message::Response(
897+
id,
898+
LSPS7Response::GetExtendableChannelsError(error),
899+
)))
900+
} else if let Some(result) = result {
901+
let response =
902+
serde_json::from_value(result).map_err(de::Error::custom)?;
903+
Ok(LSPSMessage::LSPS7(LSPS7Message::Response(
904+
id,
905+
LSPS7Response::GetExtendableChannels(response),
906+
)))
907+
} else {
908+
Err(de::Error::custom("Received invalid JSON-RPC object: one of method, result, or error required"))
909+
}
910+
},
911+
LSPSMethod::LSPS7CreateOrder => {
912+
if let Some(error) = error {
913+
Ok(LSPSMessage::LSPS7(LSPS7Message::Response(
914+
id,
915+
LSPS7Response::CreateOrderError(error),
916+
)))
917+
} else if let Some(result) = result {
918+
let response =
919+
serde_json::from_value(result).map_err(de::Error::custom)?;
920+
Ok(LSPSMessage::LSPS7(LSPS7Message::Response(
921+
id,
922+
LSPS7Response::CreateOrder(response),
923+
)))
924+
} else {
925+
Err(de::Error::custom("Received invalid JSON-RPC object: one of method, result, or error required"))
926+
}
927+
},
928+
LSPSMethod::LSPS7GetOrder => {
929+
if let Some(error) = error {
930+
Ok(LSPSMessage::LSPS7(LSPS7Message::Response(
931+
id,
932+
LSPS7Response::GetOrderError(error),
933+
)))
934+
} else if let Some(result) = result {
935+
let response =
936+
serde_json::from_value(result).map_err(de::Error::custom)?;
937+
Ok(LSPSMessage::LSPS7(LSPS7Message::Response(
938+
id,
939+
LSPS7Response::GetOrder(response),
940+
)))
941+
} else {
942+
Err(de::Error::custom("Received invalid JSON-RPC object: one of method, result, or error required"))
943+
}
944+
},
801945
},
802946
None => Err(de::Error::custom(format!(
803947
"Received response for unknown request id: {}",

0 commit comments

Comments
 (0)