Skip to content

Add close-channel command to the CLI #65

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
41 changes: 39 additions & 2 deletions ldk-server-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ 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, ForceCloseChannelRequest, GetBalancesRequest, GetNodeInfoRequest,
ListChannelsRequest, ListPaymentsRequest, OnchainReceiveRequest, OnchainSendRequest,
OpenChannelRequest,
};
use ldk_server_client::ldk_server_protos::types::{
bolt11_invoice_description, Bolt11InvoiceDescription, PageToken, Payment,
Expand Down Expand Up @@ -75,6 +76,20 @@ enum Commands {
#[arg(short, long)]
payer_note: Option<String>,
},
CloseChannel {
#[arg(short, long)]
user_channel_id: String,
#[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<String>,
},
OpenChannel {
#[arg(short, long)]
node_pubkey: String,
Expand Down Expand Up @@ -170,6 +185,28 @@ 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::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,
Expand Down
19 changes: 15 additions & 4 deletions ldk-server-client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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";

Expand Down Expand Up @@ -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<ForceCloseChannelResponse, LdkServerError> {
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(
Expand Down
30 changes: 21 additions & 9 deletions ldk-server-protos/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -270,9 +270,7 @@ pub struct UpdateChannelConfigRequest {
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct 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>
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct CloseChannelRequest {
Expand All @@ -282,18 +280,32 @@ 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<bool>,
/// 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`.
#[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: <https://docs.rs/ldk-node/latest/ldk_node/struct.Node.html#method.force_close_channel>
#[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, can only be set while force closing a channel.
#[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: <https://docs.rs/ldk-node/latest/ldk_node/struct.Node.html#method.list_channels>
#[allow(clippy::derive_partial_eq_without_eq)]
Expand Down
25 changes: 15 additions & 10 deletions ldk-server-protos/src/proto/api.proto
Original file line number Diff line number Diff line change
Expand Up @@ -261,30 +261,35 @@ 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.
string user_channel_id = 1;

// 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 {}

// 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, can only be set while force closing a channel.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"can only be set while force closing a channel" isn't needed anymore.

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 {}
Expand Down
27 changes: 4 additions & 23 deletions ldk-server/src/api/close_channel.rs
Original file line number Diff line number Diff line change
@@ -1,35 +1,16 @@
use crate::api::error::LdkServerError;
use crate::api::error::LdkServerErrorCode::InvalidRequestError;
use crate::api::{error::LdkServerError, parse_counterparty_node_id, parse_user_channel_id};
use crate::service::Context;
use ldk_node::bitcoin::secp256k1::PublicKey;
use ldk_node::UserChannelId;
use ldk_server_protos::api::{CloseChannelRequest, CloseChannelResponse};
use std::str::FromStr;

pub(crate) const CLOSE_CHANNEL_PATH: &str = "CloseChannel";

pub(crate) fn handle_close_channel_request(
context: Context, request: CloseChannelRequest,
) -> Result<CloseChannelResponse, LdkServerError> {
let user_channel_id =
UserChannelId((&request.user_channel_id).parse::<u128>().map_err(|_| {
LdkServerError::new(InvalidRequestError, "Invalid UserChannelId.".to_string())
})?);
let counterparty_node_id = PublicKey::from_str(&request.counterparty_node_id).map_err(|e| {
LdkServerError::new(
InvalidRequestError,
format!("Invalid counterparty node ID, error: {}", 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)?;

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)
Expand Down
21 changes: 21 additions & 0 deletions ldk-server/src/api/force_close_channel.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use crate::api::{error::LdkServerError, parse_counterparty_node_id, parse_user_channel_id};
use crate::service::Context;
use ldk_server_protos::api::{ForceCloseChannelRequest, ForceCloseChannelResponse};

pub(crate) const FORCE_CLOSE_CHANNEL_PATH: &str = "ForceCloseChannel";

pub(crate) fn handle_force_close_channel_request(
context: Context, request: ForceCloseChannelRequest,
) -> Result<ForceCloseChannelResponse, LdkServerError> {
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,
)?;

let response = ForceCloseChannelResponse {};
Ok(response)
}
21 changes: 21 additions & 0 deletions ldk-server/src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pub(crate) mod bolt12_receive;
pub(crate) mod bolt12_send;
pub(crate) mod close_channel;
pub(crate) mod error;
pub(crate) mod force_close_channel;
pub(crate) mod get_balances;
pub(crate) mod get_node_info;
pub(crate) mod get_payment_details;
Expand All @@ -14,3 +15,23 @@ pub(crate) mod onchain_receive;
pub(crate) mod onchain_send;
pub(crate) mod open_channel;
pub(crate) mod update_channel_config;

use crate::api::error::{LdkServerError, LdkServerErrorCode::InvalidRequestError};
use ldk_node::bitcoin::secp256k1::PublicKey;
use ldk_node::UserChannelId;
use std::str::FromStr;

pub fn parse_user_channel_id(id: &str) -> Result<UserChannelId, LdkServerError> {
Ok(UserChannelId(id.parse::<u128>().map_err(|_| {
LdkServerError::new(InvalidRequestError, "Invalid UserChannelId.".to_string())
})?))
}

pub fn parse_counterparty_node_id(id: &str) -> Result<PublicKey, LdkServerError> {
PublicKey::from_str(id).map_err(|e| {
LdkServerError::new(
InvalidRequestError,
format!("Invalid counterparty node ID, error: {}", e),
)
})
}
Comment on lines +24 to +37
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do these need to be pub? IMO, we should just leave these in close_channel.rs and not make a separate file for handle_force_close_channel_request. There's enough in common that it can go in close_channel.rs.

6 changes: 6 additions & 0 deletions ldk-server/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ 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::error::LdkServerError;
use crate::api::error::LdkServerErrorCode::InvalidRequestError;
use crate::api::force_close_channel::{
handle_force_close_channel_request, FORCE_CLOSE_CHANNEL_PATH,
};
use crate::api::get_balances::{handle_get_balances_request, GET_BALANCES};
use crate::api::get_node_info::{handle_get_node_info_request, GET_NODE_INFO};
use crate::api::get_payment_details::{
Expand Down Expand Up @@ -85,6 +88,9 @@ impl Service<Request<Incoming>> 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))
},
Expand Down