From cdb0ddf9e74bd917c30c641f5e8a0efb1a4773e6 Mon Sep 17 00:00:00 2001 From: moisesPomilio <93723302+moisesPompilio@users.noreply.github.com> Date: Thu, 26 Jun 2025 19:14:21 -0300 Subject: [PATCH 1/5] Refactor CloseChannel API to remove force close functionality --- ldk-server-protos/src/api.rs | 10 +--------- ldk-server-protos/src/proto/api.proto | 14 ++------------ ldk-server/src/api/close_channel.rs | 10 +--------- 3 files changed, 4 insertions(+), 30 deletions(-) diff --git a/ldk-server-protos/src/api.rs b/ldk-server-protos/src/api.rs index 770cbc3..af93fcb 100644 --- a/ldk-server-protos/src/api.rs +++ b/ldk-server-protos/src/api.rs @@ -270,9 +270,7 @@ pub struct UpdateChannelConfigRequest { #[derive(Clone, PartialEq, ::prost::Message)] pub struct UpdateChannelConfigResponse {} /// Closes the channel specified by given request. -/// See more: -/// - -/// - +/// See more: #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct CloseChannelRequest { @@ -282,12 +280,6 @@ pub struct CloseChannelRequest { /// The hex-encoded public key of the node to close a channel with. #[prost(string, tag = "2")] pub counterparty_node_id: ::prost::alloc::string::String, - /// Whether to force close the specified channel. - #[prost(bool, optional, tag = "3")] - pub force_close: ::core::option::Option, - /// The reason for force-closing, can only be set while force closing a channel. - #[prost(string, optional, tag = "4")] - pub force_close_reason: ::core::option::Option<::prost::alloc::string::String>, } /// The response `content` for the `CloseChannel` API, when HttpStatusCode is OK (200). /// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. diff --git a/ldk-server-protos/src/proto/api.proto b/ldk-server-protos/src/proto/api.proto index 9f61ba9..b65b2d5 100644 --- a/ldk-server-protos/src/proto/api.proto +++ b/ldk-server-protos/src/proto/api.proto @@ -261,9 +261,7 @@ message UpdateChannelConfigResponse { } // Closes the channel specified by given request. -// See more: -// - https://docs.rs/ldk-node/latest/ldk_node/struct.Node.html#method.close_channel -// - https://docs.rs/ldk-node/latest/ldk_node/struct.Node.html#method.force_close_channel +// See more: https://docs.rs/ldk-node/latest/ldk_node/struct.Node.html#method.close_channel message CloseChannelRequest { // The local `user_channel_id` of this channel. @@ -271,19 +269,11 @@ message CloseChannelRequest { // The hex-encoded public key of the node to close a channel with. string counterparty_node_id = 2; - - // Whether to force close the specified channel. - optional bool force_close = 3; - - // The reason for force-closing, can only be set while force closing a channel. - optional string force_close_reason = 4; } // The response `content` for the `CloseChannel` API, when HttpStatusCode is OK (200). // When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. -message CloseChannelResponse { - -} +message CloseChannelResponse {} // Returns a list of known channels. // See more: https://docs.rs/ldk-node/latest/ldk_node/struct.Node.html#method.list_channels diff --git a/ldk-server/src/api/close_channel.rs b/ldk-server/src/api/close_channel.rs index 95054a4..170195f 100644 --- a/ldk-server/src/api/close_channel.rs +++ b/ldk-server/src/api/close_channel.rs @@ -21,15 +21,7 @@ pub(crate) fn handle_close_channel_request( format!("Invalid counterparty node ID, error: {}", e), ) })?; - - match request.force_close { - Some(true) => context.node.force_close_channel( - &user_channel_id, - counterparty_node_id, - request.force_close_reason, - )?, - _ => context.node.close_channel(&user_channel_id, counterparty_node_id)?, - }; + context.node.close_channel(&user_channel_id, counterparty_node_id)?; let response = CloseChannelResponse {}; Ok(response) From 2c926ae8358c4b4d725c59d19829d6f5e873eaf7 Mon Sep 17 00:00:00 2001 From: moisesPomilio <93723302+moisesPompilio@users.noreply.github.com> Date: Thu, 26 Jun 2025 19:20:53 -0300 Subject: [PATCH 2/5] add close channel in the cli --- ldk-server-cli/src/main.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/ldk-server-cli/src/main.rs b/ldk-server-cli/src/main.rs index 7ebd5e9..20efa89 100644 --- a/ldk-server-cli/src/main.rs +++ b/ldk-server-cli/src/main.rs @@ -6,8 +6,8 @@ use ldk_server_client::error::LdkServerErrorCode::{ }; use ldk_server_client::ldk_server_protos::api::{ Bolt11ReceiveRequest, Bolt11SendRequest, Bolt12ReceiveRequest, Bolt12SendRequest, - GetBalancesRequest, GetNodeInfoRequest, ListChannelsRequest, ListPaymentsRequest, - OnchainReceiveRequest, OnchainSendRequest, OpenChannelRequest, + CloseChannelRequest, GetBalancesRequest, GetNodeInfoRequest, ListChannelsRequest, + ListPaymentsRequest, OnchainReceiveRequest, OnchainSendRequest, OpenChannelRequest, }; use ldk_server_client::ldk_server_protos::types::{ bolt11_invoice_description, Bolt11InvoiceDescription, PageToken, Payment, @@ -75,6 +75,12 @@ enum Commands { #[arg(short, long)] payer_note: Option, }, + CloseChannel { + #[arg(short, long)] + user_channel_id: String, + #[arg(short, long)] + counterparty_node_id: String, + }, OpenChannel { #[arg(short, long)] node_pubkey: String, @@ -170,6 +176,13 @@ async fn main() { .await, ); }, + Commands::CloseChannel { user_channel_id, counterparty_node_id } => { + handle_response_result( + client + .close_channel(CloseChannelRequest { user_channel_id, counterparty_node_id }) + .await, + ); + }, Commands::OpenChannel { node_pubkey, address, From 889e0a315fd49094f52d695f01eb5d0a6897e09e Mon Sep 17 00:00:00 2001 From: moisesPomilio <93723302+moisesPompilio@users.noreply.github.com> Date: Thu, 26 Jun 2025 19:29:42 -0300 Subject: [PATCH 3/5] Add force_close_channel API endpoint and handler Introduce ForceCloseChannelRequest and ForceCloseChannelResponse to the API, implement the handle_force_close_channel_request function, and register the new endpoint in the service router. --- ldk-server-protos/src/api.rs | 20 +++++++++++ ldk-server-protos/src/proto/api.proto | 15 ++++++++ ldk-server/src/api/close_channel.rs | 49 +++++++++++++++++++++------ ldk-server/src/service.rs | 8 ++++- 4 files changed, 80 insertions(+), 12 deletions(-) diff --git a/ldk-server-protos/src/api.rs b/ldk-server-protos/src/api.rs index af93fcb..d89283b 100644 --- a/ldk-server-protos/src/api.rs +++ b/ldk-server-protos/src/api.rs @@ -286,6 +286,26 @@ pub struct CloseChannelRequest { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct CloseChannelResponse {} +/// Force closes the channel specified by given request. +/// See more: +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ForceCloseChannelRequest { + /// The local `user_channel_id` of this channel. + #[prost(string, tag = "1")] + pub user_channel_id: ::prost::alloc::string::String, + /// The hex-encoded public key of the node to close a channel with. + #[prost(string, tag = "2")] + pub counterparty_node_id: ::prost::alloc::string::String, + /// The reason for force-closing. + #[prost(string, optional, tag = "3")] + pub force_close_reason: ::core::option::Option<::prost::alloc::string::String>, +} +/// The response `content` for the `ForceCloseChannel` API, when HttpStatusCode is OK (200). +/// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ForceCloseChannelResponse {} /// Returns a list of known channels. /// See more: #[allow(clippy::derive_partial_eq_without_eq)] diff --git a/ldk-server-protos/src/proto/api.proto b/ldk-server-protos/src/proto/api.proto index b65b2d5..32be3c3 100644 --- a/ldk-server-protos/src/proto/api.proto +++ b/ldk-server-protos/src/proto/api.proto @@ -275,6 +275,21 @@ message CloseChannelRequest { // When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. message CloseChannelResponse {} +// Force closes the channel specified by given request. +// See more: https://docs.rs/ldk-node/latest/ldk_node/struct.Node.html#method.force_close_channel +message ForceCloseChannelRequest { + // The local `user_channel_id` of this channel. + string user_channel_id = 1; + // The hex-encoded public key of the node to close a channel with. + string counterparty_node_id = 2; + // The reason for force-closing. + optional string force_close_reason = 3; +} + +// The response `content` for the `ForceCloseChannel` API, when HttpStatusCode is OK (200). +// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`. +message ForceCloseChannelResponse {} + // Returns a list of known channels. // See more: https://docs.rs/ldk-node/latest/ldk_node/struct.Node.html#method.list_channels message ListChannelsRequest {} diff --git a/ldk-server/src/api/close_channel.rs b/ldk-server/src/api/close_channel.rs index 170195f..1d9d0b0 100644 --- a/ldk-server/src/api/close_channel.rs +++ b/ldk-server/src/api/close_channel.rs @@ -3,7 +3,9 @@ use crate::api::error::LdkServerErrorCode::InvalidRequestError; use crate::service::Context; use ldk_node::bitcoin::secp256k1::PublicKey; use ldk_node::UserChannelId; -use ldk_server_protos::api::{CloseChannelRequest, CloseChannelResponse}; +use ldk_server_protos::api::{ + CloseChannelRequest, CloseChannelResponse, ForceCloseChannelRequest, ForceCloseChannelResponse, +}; use std::str::FromStr; pub(crate) const CLOSE_CHANNEL_PATH: &str = "CloseChannel"; @@ -11,18 +13,43 @@ pub(crate) const CLOSE_CHANNEL_PATH: &str = "CloseChannel"; pub(crate) fn handle_close_channel_request( context: Context, request: CloseChannelRequest, ) -> Result { - let user_channel_id = - UserChannelId((&request.user_channel_id).parse::().map_err(|_| { - LdkServerError::new(InvalidRequestError, "Invalid UserChannelId.".to_string()) - })?); - let counterparty_node_id = PublicKey::from_str(&request.counterparty_node_id).map_err(|e| { + let user_channel_id = parse_user_channel_id(&request.user_channel_id)?; + let counterparty_node_id = parse_counterparty_node_id(&request.counterparty_node_id)?; + + context.node.close_channel(&user_channel_id, counterparty_node_id)?; + + Ok(CloseChannelResponse {}) +} + +pub(crate) const FORCE_CLOSE_CHANNEL_PATH: &str = "ForceCloseChannel"; + +pub(crate) fn handle_force_close_channel_request( + context: Context, request: ForceCloseChannelRequest, +) -> Result { + let user_channel_id = parse_user_channel_id(&request.user_channel_id)?; + let counterparty_node_id = parse_counterparty_node_id(&request.counterparty_node_id)?; + + context.node.force_close_channel( + &user_channel_id, + counterparty_node_id, + request.force_close_reason, + )?; + + Ok(ForceCloseChannelResponse {}) +} + +fn parse_user_channel_id(id: &str) -> Result { + let parsed = id.parse::().map_err(|_| { + LdkServerError::new(InvalidRequestError, "Invalid UserChannelId.".to_string()) + })?; + Ok(UserChannelId(parsed)) +} + +fn parse_counterparty_node_id(id: &str) -> Result { + PublicKey::from_str(id).map_err(|e| { LdkServerError::new( InvalidRequestError, format!("Invalid counterparty node ID, error: {}", e), ) - })?; - context.node.close_channel(&user_channel_id, counterparty_node_id)?; - - let response = CloseChannelResponse {}; - Ok(response) + }) } diff --git a/ldk-server/src/service.rs b/ldk-server/src/service.rs index 328ded0..6eb02c5 100644 --- a/ldk-server/src/service.rs +++ b/ldk-server/src/service.rs @@ -11,7 +11,10 @@ use crate::api::bolt11_receive::{handle_bolt11_receive_request, BOLT11_RECEIVE_P use crate::api::bolt11_send::{handle_bolt11_send_request, BOLT11_SEND_PATH}; use crate::api::bolt12_receive::{handle_bolt12_receive_request, BOLT12_RECEIVE_PATH}; use crate::api::bolt12_send::{handle_bolt12_send_request, BOLT12_SEND_PATH}; -use crate::api::close_channel::{handle_close_channel_request, CLOSE_CHANNEL_PATH}; +use crate::api::close_channel::{ + handle_close_channel_request, handle_force_close_channel_request, CLOSE_CHANNEL_PATH, + FORCE_CLOSE_CHANNEL_PATH, +}; use crate::api::error::LdkServerError; use crate::api::error::LdkServerErrorCode::InvalidRequestError; use crate::api::get_balances::{handle_get_balances_request, GET_BALANCES}; @@ -85,6 +88,9 @@ impl Service> for NodeService { CLOSE_CHANNEL_PATH => { Box::pin(handle_request(context, req, handle_close_channel_request)) }, + FORCE_CLOSE_CHANNEL_PATH => { + Box::pin(handle_request(context, req, handle_force_close_channel_request)) + }, LIST_CHANNELS_PATH => { Box::pin(handle_request(context, req, handle_list_channels_request)) }, From fecd3933f6419dccd7849f78c5e51ec769b7cae7 Mon Sep 17 00:00:00 2001 From: moisesPomilio <93723302+moisesPompilio@users.noreply.github.com> Date: Thu, 26 Jun 2025 23:06:48 -0300 Subject: [PATCH 4/5] add force close channel in the cli --- ldk-server-cli/src/main.rs | 28 ++++++++++++++++++++++++++-- ldk-server-client/src/client.rs | 19 +++++++++++++++---- 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/ldk-server-cli/src/main.rs b/ldk-server-cli/src/main.rs index 20efa89..a45ec6e 100644 --- a/ldk-server-cli/src/main.rs +++ b/ldk-server-cli/src/main.rs @@ -6,8 +6,9 @@ use ldk_server_client::error::LdkServerErrorCode::{ }; use ldk_server_client::ldk_server_protos::api::{ Bolt11ReceiveRequest, Bolt11SendRequest, Bolt12ReceiveRequest, Bolt12SendRequest, - CloseChannelRequest, GetBalancesRequest, GetNodeInfoRequest, ListChannelsRequest, - ListPaymentsRequest, OnchainReceiveRequest, OnchainSendRequest, OpenChannelRequest, + CloseChannelRequest, ForceCloseChannelRequest, GetBalancesRequest, GetNodeInfoRequest, + ListChannelsRequest, ListPaymentsRequest, OnchainReceiveRequest, OnchainSendRequest, + OpenChannelRequest, }; use ldk_server_client::ldk_server_protos::types::{ bolt11_invoice_description, Bolt11InvoiceDescription, PageToken, Payment, @@ -81,6 +82,14 @@ enum Commands { #[arg(short, long)] counterparty_node_id: String, }, + ForceCloseChannel { + #[arg(short, long)] + user_channel_id: String, + #[arg(short, long)] + counterparty_node_id: String, + #[arg(long)] + force_close_reason: Option, + }, OpenChannel { #[arg(short, long)] node_pubkey: String, @@ -183,6 +192,21 @@ async fn main() { .await, ); }, + Commands::ForceCloseChannel { + user_channel_id, + counterparty_node_id, + force_close_reason, + } => { + handle_response_result( + client + .force_close_channel(ForceCloseChannelRequest { + user_channel_id, + counterparty_node_id, + force_close_reason, + }) + .await, + ); + }, Commands::OpenChannel { node_pubkey, address, diff --git a/ldk-server-client/src/client.rs b/ldk-server-client/src/client.rs index e4a519b..02d9e56 100644 --- a/ldk-server-client/src/client.rs +++ b/ldk-server-client/src/client.rs @@ -7,10 +7,11 @@ use crate::error::LdkServerErrorCode::{ use ldk_server_protos::api::{ Bolt11ReceiveRequest, Bolt11ReceiveResponse, Bolt11SendRequest, Bolt11SendResponse, Bolt12ReceiveRequest, Bolt12ReceiveResponse, Bolt12SendRequest, Bolt12SendResponse, - CloseChannelRequest, CloseChannelResponse, GetBalancesRequest, GetBalancesResponse, - GetNodeInfoRequest, GetNodeInfoResponse, ListChannelsRequest, ListChannelsResponse, - ListPaymentsRequest, ListPaymentsResponse, OnchainReceiveRequest, OnchainReceiveResponse, - OnchainSendRequest, OnchainSendResponse, OpenChannelRequest, OpenChannelResponse, + CloseChannelRequest, CloseChannelResponse, ForceCloseChannelRequest, ForceCloseChannelResponse, + GetBalancesRequest, GetBalancesResponse, GetNodeInfoRequest, GetNodeInfoResponse, + ListChannelsRequest, ListChannelsResponse, ListPaymentsRequest, ListPaymentsResponse, + OnchainReceiveRequest, OnchainReceiveResponse, OnchainSendRequest, OnchainSendResponse, + OpenChannelRequest, OpenChannelResponse, }; use ldk_server_protos::error::{ErrorCode, ErrorResponse}; use reqwest::header::CONTENT_TYPE; @@ -28,6 +29,7 @@ const BOLT12_RECEIVE_PATH: &str = "Bolt12Receive"; const BOLT12_SEND_PATH: &str = "Bolt12Send"; const OPEN_CHANNEL_PATH: &str = "OpenChannel"; const CLOSE_CHANNEL_PATH: &str = "CloseChannel"; +const FORCE_CLOSE_CHANNEL_PATH: &str = "ForceCloseChannel"; const LIST_CHANNELS_PATH: &str = "ListChannels"; const LIST_PAYMENTS_PATH: &str = "ListPayments"; @@ -134,6 +136,15 @@ impl LdkServerClient { self.post_request(&request, &url).await } + /// Force closes the channel specified by given request. + /// For API contract/usage, refer to docs for [`ForceCloseChannelRequest`] and [`ForceCloseChannelResponse`]. + pub async fn force_close_channel( + &self, request: ForceCloseChannelRequest, + ) -> Result { + let url = format!("http://{}/{FORCE_CLOSE_CHANNEL_PATH}", self.base_url); + self.post_request(&request, &url).await + } + /// Retrieves list of known channels. /// For API contract/usage, refer to docs for [`ListChannelsRequest`] and [`ListChannelsResponse`]. pub async fn list_channels( From fd52396cd10dccab0c955e8fd8935f3682fdd0eb Mon Sep 17 00:00:00 2001 From: moisesPomilio <93723302+moisesPompilio@users.noreply.github.com> Date: Thu, 26 Jun 2025 23:10:04 -0300 Subject: [PATCH 5/5] fix configuration file name in running instructions --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b15eafb..8ea7071 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ cargo build ### Running ``` -cargo run --bin ldk-server ./ldk-server/ldk-server.config +cargo run --bin ldk-server ./ldk-server/ldk-server-config.toml ``` Interact with the node using CLI: