Skip to content

Commit 1cfe65e

Browse files
committed
Add LSPS2 commands to ldk-server-client and ldk-server-cli
Expose the new LSPS2 JIT invoice endpoints through the Rust client and CLI so callers can request either fixed or variable-amount invoices without constructing protobuf messages manually. Generated with the assistance of AI. Co-Authored-By: HAL 9000
1 parent 6558453 commit 1cfe65e

2 files changed

Lines changed: 123 additions & 18 deletions

File tree

ldk-server-cli/src/main.rs

Lines changed: 94 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ use ldk_server_client::error::LdkServerErrorCode::{
2121
AuthError, InternalError, InternalServerError, InvalidRequestError, LightningError,
2222
};
2323
use ldk_server_client::ldk_server_protos::api::{
24-
Bolt11ReceiveRequest, Bolt11ReceiveResponse, Bolt11SendRequest, Bolt11SendResponse,
24+
Bolt11ReceiveRequest, Bolt11ReceiveResponse, Bolt11ReceiveVariableAmountViaJitChannelRequest,
25+
Bolt11ReceiveVariableAmountViaJitChannelResponse, Bolt11ReceiveViaJitChannelRequest,
26+
Bolt11ReceiveViaJitChannelResponse, Bolt11SendRequest, Bolt11SendResponse,
2527
Bolt12ReceiveRequest, Bolt12ReceiveResponse, Bolt12SendRequest, Bolt12SendResponse,
2628
CloseChannelRequest, CloseChannelResponse, ConnectPeerRequest, ConnectPeerResponse,
2729
DisconnectPeerRequest, DisconnectPeerResponse, ExportPathfindingScoresRequest,
@@ -133,6 +135,41 @@ enum Commands {
133135
#[arg(short, long, help = "Invoice expiry time in seconds (default: 86400)")]
134136
expiry_secs: Option<u32>,
135137
},
138+
#[command(about = "Create a fixed-amount BOLT11 invoice to receive via an LSPS2 JIT channel")]
139+
Bolt11ReceiveViaJitChannel {
140+
#[arg(help = "Amount to request, e.g. 50sat or 50000msat")]
141+
amount: Amount,
142+
#[arg(short, long, help = "Description to attach along with the invoice")]
143+
description: Option<String>,
144+
#[arg(
145+
long,
146+
help = "SHA-256 hash of the description (hex). Use instead of description for longer text"
147+
)]
148+
description_hash: Option<String>,
149+
#[arg(short, long, help = "Invoice expiry time in seconds (default: 86400)")]
150+
expiry_secs: Option<u32>,
151+
#[arg(
152+
long,
153+
help = "Maximum total fee an LSP may deduct for opening the JIT channel, e.g. 50sat or 50000msat"
154+
)]
155+
max_total_lsp_fee_limit: Option<Amount>,
156+
},
157+
#[command(
158+
about = "Create a variable-amount BOLT11 invoice to receive via an LSPS2 JIT channel"
159+
)]
160+
Bolt11ReceiveVariableAmountViaJitChannel {
161+
#[arg(short, long, help = "Description to attach along with the invoice")]
162+
description: Option<String>,
163+
#[arg(
164+
long,
165+
help = "SHA-256 hash of the description (hex). Use instead of description for longer text"
166+
)]
167+
description_hash: Option<String>,
168+
#[arg(short, long, help = "Invoice expiry time in seconds (default: 86400)")]
169+
expiry_secs: Option<u32>,
170+
#[arg(long, help = "Maximum proportional fee the LSP may deduct in ppm-msat")]
171+
max_proportional_lsp_fee_limit_ppm_msat: Option<u64>,
172+
},
136173
#[command(about = "Pay a BOLT11 invoice")]
137174
Bolt11Send {
138175
#[arg(help = "A BOLT11 invoice for a payment within the Lightning Network")]
@@ -510,21 +547,8 @@ async fn main() {
510547
},
511548
Commands::Bolt11Receive { description, description_hash, expiry_secs, amount } => {
512549
let amount_msat = amount.map(|a| a.to_msat());
513-
let invoice_description = match (description, description_hash) {
514-
(Some(desc), None) => Some(Bolt11InvoiceDescription {
515-
kind: Some(bolt11_invoice_description::Kind::Direct(desc)),
516-
}),
517-
(None, Some(hash)) => Some(Bolt11InvoiceDescription {
518-
kind: Some(bolt11_invoice_description::Kind::Hash(hash)),
519-
}),
520-
(Some(_), Some(_)) => {
521-
handle_error(LdkServerError::new(
522-
InternalError,
523-
"Only one of description or description_hash can be set.".to_string(),
524-
));
525-
},
526-
(None, None) => None,
527-
};
550+
let invoice_description =
551+
parse_bolt11_invoice_description(description, description_hash);
528552

529553
let expiry_secs = expiry_secs.unwrap_or(DEFAULT_EXPIRY_SECS);
530554
let request =
@@ -534,6 +558,40 @@ async fn main() {
534558
client.bolt11_receive(request).await,
535559
);
536560
},
561+
Commands::Bolt11ReceiveViaJitChannel {
562+
amount,
563+
description,
564+
description_hash,
565+
expiry_secs,
566+
max_total_lsp_fee_limit,
567+
} => {
568+
let request = Bolt11ReceiveViaJitChannelRequest {
569+
amount_msat: amount.to_msat(),
570+
description: parse_bolt11_invoice_description(description, description_hash),
571+
expiry_secs: expiry_secs.unwrap_or(DEFAULT_EXPIRY_SECS),
572+
max_total_lsp_fee_limit_msat: max_total_lsp_fee_limit.map(|a| a.to_msat()),
573+
};
574+
575+
handle_response_result::<_, Bolt11ReceiveViaJitChannelResponse>(
576+
client.bolt11_receive_via_jit_channel(request).await,
577+
);
578+
},
579+
Commands::Bolt11ReceiveVariableAmountViaJitChannel {
580+
description,
581+
description_hash,
582+
expiry_secs,
583+
max_proportional_lsp_fee_limit_ppm_msat,
584+
} => {
585+
let request = Bolt11ReceiveVariableAmountViaJitChannelRequest {
586+
description: parse_bolt11_invoice_description(description, description_hash),
587+
expiry_secs: expiry_secs.unwrap_or(DEFAULT_EXPIRY_SECS),
588+
max_proportional_lsp_fee_limit_ppm_msat,
589+
};
590+
591+
handle_response_result::<_, Bolt11ReceiveVariableAmountViaJitChannelResponse>(
592+
client.bolt11_receive_variable_amount_via_jit_channel(request).await,
593+
);
594+
},
537595
Commands::Bolt11Send {
538596
invoice,
539597
amount,
@@ -928,6 +986,26 @@ where
928986
}
929987
}
930988

989+
fn parse_bolt11_invoice_description(
990+
description: Option<String>, description_hash: Option<String>,
991+
) -> Option<Bolt11InvoiceDescription> {
992+
match (description, description_hash) {
993+
(Some(desc), None) => Some(Bolt11InvoiceDescription {
994+
kind: Some(bolt11_invoice_description::Kind::Direct(desc)),
995+
}),
996+
(None, Some(hash)) => Some(Bolt11InvoiceDescription {
997+
kind: Some(bolt11_invoice_description::Kind::Hash(hash)),
998+
}),
999+
(Some(_), Some(_)) => {
1000+
handle_error(LdkServerError::new(
1001+
InternalError,
1002+
"Only one of description or description_hash can be set.".to_string(),
1003+
));
1004+
},
1005+
(None, None) => None,
1006+
}
1007+
}
1008+
9311009
fn parse_page_token(token_str: &str) -> Result<PageToken, LdkServerError> {
9321010
let parts: Vec<&str> = token_str.split(':').collect();
9331011
if parts.len() != 2 {

ldk-server-client/src/client.rs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ use std::time::{SystemTime, UNIX_EPOCH};
1212
use bitcoin_hashes::hmac::{Hmac, HmacEngine};
1313
use bitcoin_hashes::{sha256, Hash, HashEngine};
1414
use ldk_server_protos::api::{
15-
Bolt11ReceiveRequest, Bolt11ReceiveResponse, Bolt11SendRequest, Bolt11SendResponse,
15+
Bolt11ReceiveRequest, Bolt11ReceiveResponse, Bolt11ReceiveVariableAmountViaJitChannelRequest,
16+
Bolt11ReceiveVariableAmountViaJitChannelResponse, Bolt11ReceiveViaJitChannelRequest,
17+
Bolt11ReceiveViaJitChannelResponse, Bolt11SendRequest, Bolt11SendResponse,
1618
Bolt12ReceiveRequest, Bolt12ReceiveResponse, Bolt12SendRequest, Bolt12SendResponse,
1719
CloseChannelRequest, CloseChannelResponse, ConnectPeerRequest, ConnectPeerResponse,
1820
DisconnectPeerRequest, DisconnectPeerResponse, ExportPathfindingScoresRequest,
@@ -29,7 +31,8 @@ use ldk_server_protos::api::{
2931
UpdateChannelConfigResponse, VerifySignatureRequest, VerifySignatureResponse,
3032
};
3133
use ldk_server_protos::endpoints::{
32-
BOLT11_RECEIVE_PATH, BOLT11_SEND_PATH, BOLT12_RECEIVE_PATH, BOLT12_SEND_PATH,
34+
BOLT11_RECEIVE_PATH, BOLT11_RECEIVE_VARIABLE_AMOUNT_VIA_JIT_CHANNEL_PATH,
35+
BOLT11_RECEIVE_VIA_JIT_CHANNEL_PATH, BOLT11_SEND_PATH, BOLT12_RECEIVE_PATH, BOLT12_SEND_PATH,
3336
CLOSE_CHANNEL_PATH, CONNECT_PEER_PATH, DISCONNECT_PEER_PATH, EXPORT_PATHFINDING_SCORES_PATH,
3437
FORCE_CLOSE_CHANNEL_PATH, GET_BALANCES_PATH, GET_NODE_INFO_PATH, GET_PAYMENT_DETAILS_PATH,
3538
GRAPH_GET_CHANNEL_PATH, GRAPH_GET_NODE_PATH, GRAPH_LIST_CHANNELS_PATH, GRAPH_LIST_NODES_PATH,
@@ -142,6 +145,30 @@ impl LdkServerClient {
142145
self.post_request(&request, &url).await
143146
}
144147

148+
/// Retrieve a new fixed-amount BOLT11 invoice for receiving via an LSPS2 JIT channel.
149+
/// For API contract/usage, refer to docs for [`Bolt11ReceiveViaJitChannelRequest`] and
150+
/// [`Bolt11ReceiveViaJitChannelResponse`].
151+
pub async fn bolt11_receive_via_jit_channel(
152+
&self, request: Bolt11ReceiveViaJitChannelRequest,
153+
) -> Result<Bolt11ReceiveViaJitChannelResponse, LdkServerError> {
154+
let url = format!("https://{}/{BOLT11_RECEIVE_VIA_JIT_CHANNEL_PATH}", self.base_url);
155+
self.post_request(&request, &url).await
156+
}
157+
158+
/// Retrieve a new variable-amount BOLT11 invoice for receiving via an LSPS2 JIT channel.
159+
/// For API contract/usage, refer to docs for
160+
/// [`Bolt11ReceiveVariableAmountViaJitChannelRequest`] and
161+
/// [`Bolt11ReceiveVariableAmountViaJitChannelResponse`].
162+
pub async fn bolt11_receive_variable_amount_via_jit_channel(
163+
&self, request: Bolt11ReceiveVariableAmountViaJitChannelRequest,
164+
) -> Result<Bolt11ReceiveVariableAmountViaJitChannelResponse, LdkServerError> {
165+
let url = format!(
166+
"https://{}/{BOLT11_RECEIVE_VARIABLE_AMOUNT_VIA_JIT_CHANNEL_PATH}",
167+
self.base_url,
168+
);
169+
self.post_request(&request, &url).await
170+
}
171+
145172
/// Send a payment for a BOLT11 invoice.
146173
/// For API contract/usage, refer to docs for [`Bolt11SendRequest`] and [`Bolt11SendResponse`].
147174
pub async fn bolt11_send(

0 commit comments

Comments
 (0)