Skip to content
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
111 changes: 105 additions & 6 deletions ldk-server-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,22 @@ use ldk_server_client::ldk_server_protos::api::{
Bolt11ReceiveRequest, Bolt11ReceiveResponse, Bolt11SendRequest, Bolt11SendResponse,
Bolt12ReceiveRequest, Bolt12ReceiveResponse, Bolt12SendRequest, Bolt12SendResponse,
CloseChannelRequest, CloseChannelResponse, ConnectPeerRequest, ConnectPeerResponse,
ForceCloseChannelRequest, ForceCloseChannelResponse, GetBalancesRequest, GetBalancesResponse,
GetNodeInfoRequest, GetNodeInfoResponse, GetPaymentDetailsRequest, GetPaymentDetailsResponse,
ListChannelsRequest, ListChannelsResponse, ListForwardedPaymentsRequest, ListPaymentsRequest,
OnchainReceiveRequest, OnchainReceiveResponse, OnchainSendRequest, OnchainSendResponse,
OpenChannelRequest, OpenChannelResponse, SpliceInRequest, SpliceInResponse, SpliceOutRequest,
SpliceOutResponse, UpdateChannelConfigRequest, UpdateChannelConfigResponse,
ExportPathfindingScoresRequest, ForceCloseChannelRequest, ForceCloseChannelResponse,
GetBalancesRequest, GetBalancesResponse, GetNodeInfoRequest, GetNodeInfoResponse,
GetPaymentDetailsRequest, GetPaymentDetailsResponse, ListChannelsRequest, ListChannelsResponse,
ListForwardedPaymentsRequest, ListPaymentsRequest, OnchainReceiveRequest,
OnchainReceiveResponse, OnchainSendRequest, OnchainSendResponse, OpenChannelRequest,
OpenChannelResponse, SignMessageRequest, SignMessageResponse, SpliceInRequest,
SpliceInResponse, SpliceOutRequest, SpliceOutResponse, SpontaneousSendRequest,
SpontaneousSendResponse, UpdateChannelConfigRequest, UpdateChannelConfigResponse,
VerifySignatureRequest, VerifySignatureResponse,
};
use ldk_server_client::ldk_server_protos::types::{
bolt11_invoice_description, Bolt11InvoiceDescription, ChannelConfig, PageToken,
RouteParametersConfig,
};
use serde::Serialize;
use serde_json::{json, Value};
use types::{CliListForwardedPaymentsResponse, CliListPaymentsResponse, CliPaginatedResponse};

mod config;
Expand Down Expand Up @@ -196,6 +200,30 @@ enum Commands {
)]
max_channel_saturation_power_of_half: Option<u32>,
},
#[command(about = "Send a spontaneous payment (keysend) to a node")]
SpontaneousSend {
#[arg(short, long, help = "The hex-encoded public key of the node to send the payment to")]
node_id: String,
#[arg(short, long, help = "The amount in millisatoshis to send")]
amount_msat: u64,
#[arg(
long,
help = "Maximum total fees in millisatoshis that may accrue during route finding. Defaults to 1% of payment + 50 sats"
)]
max_total_routing_fee_msat: Option<u64>,
#[arg(long, help = "Maximum total CLTV delta we accept for the route (default: 1008)")]
max_total_cltv_expiry_delta: Option<u32>,
#[arg(
long,
help = "Maximum number of paths that may be used by MPP payments (default: 10)"
)]
max_path_count: Option<u32>,
#[arg(
long,
help = "Maximum share of a channel's total capacity to send over a channel, as a power of 1/2 (default: 2)"
)]
max_channel_saturation_power_of_half: Option<u32>,
},
#[command(about = "Cooperatively close the channel specified by the given channel ID")]
CloseChannel {
#[arg(short, long, help = "The local user_channel_id of this channel")]
Expand Down Expand Up @@ -353,6 +381,22 @@ enum Commands {
)]
persist: bool,
},
#[command(about = "Sign a message with the node's secret key")]
SignMessage {
#[arg(short, long, help = "The message to sign")]
message: String,
},
#[command(about = "Verify a signature against a message and public key")]
VerifySignature {
#[arg(short, long, help = "The message that was signed")]
message: String,
#[arg(short, long, help = "The zbase32-encoded signature to verify")]
signature: String,
#[arg(short, long, help = "The hex-encoded public key of the signer")]
public_key: String,
},
#[command(about = "Export the pathfinding scores used by the router")]
ExportPathfindingScores,
#[command(about = "Generate shell completions for the CLI")]
Completions {
#[arg(
Expand Down Expand Up @@ -543,6 +587,33 @@ async fn main() {
.await,
);
},
Commands::SpontaneousSend {
node_id,
amount_msat,
max_total_routing_fee_msat,
max_total_cltv_expiry_delta,
max_path_count,
max_channel_saturation_power_of_half,
} => {
let route_parameters = RouteParametersConfig {
max_total_routing_fee_msat,
max_total_cltv_expiry_delta: max_total_cltv_expiry_delta
.unwrap_or(DEFAULT_MAX_TOTAL_CLTV_EXPIRY_DELTA),
max_path_count: max_path_count.unwrap_or(DEFAULT_MAX_PATH_COUNT),
max_channel_saturation_power_of_half: max_channel_saturation_power_of_half
.unwrap_or(DEFAULT_MAX_CHANNEL_SATURATION_POWER_OF_HALF),
};

handle_response_result::<_, SpontaneousSendResponse>(
client
.spontaneous_send(SpontaneousSendRequest {
amount_msat,
node_id,
route_parameters: Some(route_parameters),
})
.await,
);
},
Commands::CloseChannel { user_channel_id, counterparty_node_id } => {
handle_response_result::<_, CloseChannelResponse>(
client
Expand Down Expand Up @@ -695,6 +766,34 @@ async fn main() {
client.connect_peer(ConnectPeerRequest { node_pubkey, address, persist }).await,
);
},
Commands::SignMessage { message } => {
handle_response_result::<_, SignMessageResponse>(
client
.sign_message(SignMessageRequest { message: message.into_bytes().into() })
.await,
);
},
Commands::VerifySignature { message, signature, public_key } => {
handle_response_result::<_, VerifySignatureResponse>(
client
.verify_signature(VerifySignatureRequest {
message: message.into_bytes().into(),
signature,
public_key,
})
.await,
);
},
Commands::ExportPathfindingScores => {
handle_response_result::<_, Value>(
client.export_pathfinding_scores(ExportPathfindingScoresRequest {}).await.map(
|s| {
let scores_hex = s.scores.as_hex().to_string();
json!({ "pathfinding_scores": scores_hex })
},
),
);
},
Commands::Completions { .. } => unreachable!("Handled above"),
}
}
Expand Down
61 changes: 50 additions & 11 deletions ldk-server-client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,23 @@ use ldk_server_protos::api::{
Bolt11ReceiveRequest, Bolt11ReceiveResponse, Bolt11SendRequest, Bolt11SendResponse,
Bolt12ReceiveRequest, Bolt12ReceiveResponse, Bolt12SendRequest, Bolt12SendResponse,
CloseChannelRequest, CloseChannelResponse, ConnectPeerRequest, ConnectPeerResponse,
ForceCloseChannelRequest, ForceCloseChannelResponse, GetBalancesRequest, GetBalancesResponse,
GetNodeInfoRequest, GetNodeInfoResponse, GetPaymentDetailsRequest, GetPaymentDetailsResponse,
ListChannelsRequest, ListChannelsResponse, ListForwardedPaymentsRequest,
ListForwardedPaymentsResponse, ListPaymentsRequest, ListPaymentsResponse,
OnchainReceiveRequest, OnchainReceiveResponse, OnchainSendRequest, OnchainSendResponse,
OpenChannelRequest, OpenChannelResponse, SpliceInRequest, SpliceInResponse, SpliceOutRequest,
SpliceOutResponse, UpdateChannelConfigRequest, UpdateChannelConfigResponse,
ExportPathfindingScoresRequest, ExportPathfindingScoresResponse, ForceCloseChannelRequest,
ForceCloseChannelResponse, GetBalancesRequest, GetBalancesResponse, GetNodeInfoRequest,
GetNodeInfoResponse, GetPaymentDetailsRequest, GetPaymentDetailsResponse, ListChannelsRequest,
ListChannelsResponse, ListForwardedPaymentsRequest, ListForwardedPaymentsResponse,
ListPaymentsRequest, ListPaymentsResponse, OnchainReceiveRequest, OnchainReceiveResponse,
OnchainSendRequest, OnchainSendResponse, OpenChannelRequest, OpenChannelResponse,
SignMessageRequest, SignMessageResponse, SpliceInRequest, SpliceInResponse, SpliceOutRequest,
SpliceOutResponse, SpontaneousSendRequest, SpontaneousSendResponse, UpdateChannelConfigRequest,
UpdateChannelConfigResponse, VerifySignatureRequest, VerifySignatureResponse,
};
use ldk_server_protos::endpoints::{
BOLT11_RECEIVE_PATH, BOLT11_SEND_PATH, BOLT12_RECEIVE_PATH, BOLT12_SEND_PATH,
CLOSE_CHANNEL_PATH, CONNECT_PEER_PATH, FORCE_CLOSE_CHANNEL_PATH, GET_BALANCES_PATH,
GET_NODE_INFO_PATH, GET_PAYMENT_DETAILS_PATH, LIST_CHANNELS_PATH, LIST_FORWARDED_PAYMENTS_PATH,
LIST_PAYMENTS_PATH, ONCHAIN_RECEIVE_PATH, ONCHAIN_SEND_PATH, OPEN_CHANNEL_PATH, SPLICE_IN_PATH,
SPLICE_OUT_PATH, UPDATE_CHANNEL_CONFIG_PATH,
CLOSE_CHANNEL_PATH, CONNECT_PEER_PATH, EXPORT_PATHFINDING_SCORES_PATH,
FORCE_CLOSE_CHANNEL_PATH, GET_BALANCES_PATH, GET_NODE_INFO_PATH, GET_PAYMENT_DETAILS_PATH,
LIST_CHANNELS_PATH, LIST_FORWARDED_PAYMENTS_PATH, LIST_PAYMENTS_PATH, ONCHAIN_RECEIVE_PATH,
ONCHAIN_SEND_PATH, OPEN_CHANNEL_PATH, SIGN_MESSAGE_PATH, SPLICE_IN_PATH, SPLICE_OUT_PATH,
SPONTANEOUS_SEND_PATH, UPDATE_CHANNEL_CONFIG_PATH, VERIFY_SIGNATURE_PATH,
};
use ldk_server_protos::error::{ErrorCode, ErrorResponse};
use prost::Message;
Expand Down Expand Up @@ -261,6 +264,42 @@ impl LdkServerClient {
self.post_request(&request, &url).await
}

/// Send a spontaneous payment (keysend) to a node.
/// For API contract/usage, refer to docs for [`SpontaneousSendRequest`] and [`SpontaneousSendResponse`].
pub async fn spontaneous_send(
&self, request: SpontaneousSendRequest,
) -> Result<SpontaneousSendResponse, LdkServerError> {
let url = format!("https://{}/{SPONTANEOUS_SEND_PATH}", self.base_url);
self.post_request(&request, &url).await
}

/// Sign a message with the node's secret key.
/// For API contract/usage, refer to docs for [`SignMessageRequest`] and [`SignMessageResponse`].
pub async fn sign_message(
&self, request: SignMessageRequest,
) -> Result<SignMessageResponse, LdkServerError> {
let url = format!("https://{}/{SIGN_MESSAGE_PATH}", self.base_url);
self.post_request(&request, &url).await
}

/// Verify a signature against a message and public key.
/// For API contract/usage, refer to docs for [`VerifySignatureRequest`] and [`VerifySignatureResponse`].
pub async fn verify_signature(
&self, request: VerifySignatureRequest,
) -> Result<VerifySignatureResponse, LdkServerError> {
let url = format!("https://{}/{VERIFY_SIGNATURE_PATH}", self.base_url);
self.post_request(&request, &url).await
}

/// Export the pathfinding scores used by the router.
/// For API contract/usage, refer to docs for [`ExportPathfindingScoresRequest`] and [`ExportPathfindingScoresResponse`].
pub async fn export_pathfinding_scores(
&self, request: ExportPathfindingScoresRequest,
) -> Result<ExportPathfindingScoresResponse, LdkServerError> {
let url = format!("https://{}/{EXPORT_PATHFINDING_SCORES_PATH}", self.base_url);
self.post_request(&request, &url).await
}

async fn post_request<Rq: Message, Rs: Message + Default>(
&self, request: &Rq, url: &str,
) -> Result<Rs, LdkServerError> {
Expand Down
111 changes: 111 additions & 0 deletions ldk-server-protos/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,21 @@ pub struct GetNodeInfoResponse {
/// Will be `None` if we have no public channels or we haven’t broadcasted since the node was initialized.
#[prost(uint64, optional, tag = "8")]
pub latest_node_announcement_broadcast_timestamp: ::core::option::Option<u64>,
/// The addresses the node is currently listening on for incoming connections.
///
/// Will be empty if the node is not listening on any addresses.
#[prost(string, repeated, tag = "9")]
pub listening_addresses: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
/// The addresses the node announces to the network.
///
/// Will be empty if no announcement addresses are configured.
#[prost(string, repeated, tag = "10")]
pub announcement_addresses: ::prost::alloc::vec::Vec<::prost::alloc::string::String>,
/// The node alias, if configured.
///
/// Will be `None` if no alias is configured.
#[prost(string, optional, tag = "11")]
pub node_alias: ::core::option::Option<::prost::alloc::string::String>,
}
/// Retrieve a new on-chain funding address.
/// See more: <https://docs.rs/ldk-node/latest/ldk_node/payment/struct.OnchainPayment.html#method.new_address>
Expand Down Expand Up @@ -258,6 +273,34 @@ pub struct Bolt12SendResponse {
#[prost(string, tag = "1")]
pub payment_id: ::prost::alloc::string::String,
}
/// Send a spontaneous payment, also known as "keysend", to a node.
/// See more: <https://docs.rs/ldk-node/latest/ldk_node/payment/struct.SpontaneousPayment.html#method.send>
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct SpontaneousSendRequest {
/// The amount in millisatoshis to send.
#[prost(uint64, tag = "1")]
pub amount_msat: u64,
/// The hex-encoded public key of the node to send the payment to.
#[prost(string, tag = "2")]
pub node_id: ::prost::alloc::string::String,
/// Configuration options for payment routing and pathfinding.
#[prost(message, optional, tag = "3")]
pub route_parameters: ::core::option::Option<super::types::RouteParametersConfig>,
}
/// The response `content` for the `SpontaneousSend` API, when HttpStatusCode is OK (200).
/// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct SpontaneousSendResponse {
/// An identifier used to uniquely identify a payment in hex-encoded form.
#[prost(string, tag = "1")]
pub payment_id: ::prost::alloc::string::String,
}
/// Creates a new outbound channel to the given remote node.
/// See more: <https://docs.rs/ldk-node/latest/ldk_node/struct.Node.html#method.connect_open_channel>
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
Expand Down Expand Up @@ -547,6 +590,74 @@ pub struct ListForwardedPaymentsResponse {
#[prost(message, optional, tag = "2")]
pub next_page_token: ::core::option::Option<super::types::PageToken>,
}
/// Sign a message with the node's secret key.
/// See more: <https://docs.rs/ldk-node/latest/ldk_node/struct.Node.html#method.sign_message>
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct SignMessageRequest {
/// The message to sign, as raw bytes.
#[prost(bytes = "bytes", tag = "1")]
pub message: ::prost::bytes::Bytes,
}
/// The response `content` for the `SignMessage` API, when HttpStatusCode is OK (200).
/// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct SignMessageResponse {
/// The signature of the message, as a zbase32-encoded string.
#[prost(string, tag = "1")]
pub signature: ::prost::alloc::string::String,
}
/// Verify a signature against a message and public key.
/// See more: <https://docs.rs/ldk-node/latest/ldk_node/struct.Node.html#method.verify_signature>
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct VerifySignatureRequest {
/// The message that was signed, as raw bytes.
#[prost(bytes = "bytes", tag = "1")]
pub message: ::prost::bytes::Bytes,
/// The signature to verify, as a zbase32-encoded string.
#[prost(string, tag = "2")]
pub signature: ::prost::alloc::string::String,
/// The hex-encoded public key of the signer.
#[prost(string, tag = "3")]
pub public_key: ::prost::alloc::string::String,
}
/// The response `content` for the `VerifySignature` API, when HttpStatusCode is OK (200).
/// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct VerifySignatureResponse {
/// Whether the signature is valid.
#[prost(bool, tag = "1")]
pub valid: bool,
}
/// Export the pathfinding scores used by the router.
/// See more: <https://docs.rs/ldk-node/latest/ldk_node/struct.Node.html#method.export_pathfinding_scores>
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct ExportPathfindingScoresRequest {}
/// The response `content` for the `ExportPathfindingScores` API, when HttpStatusCode is OK (200).
/// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct ExportPathfindingScoresResponse {
/// The serialized pathfinding scores data.
#[prost(bytes = "bytes", tag = "1")]
pub scores: ::prost::bytes::Bytes,
}
/// Retrieves an overview of all known balances.
/// See more: <https://docs.rs/ldk-node/latest/ldk_node/struct.Node.html#method.list_balances>
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
Expand Down
4 changes: 4 additions & 0 deletions ldk-server-protos/src/endpoints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,7 @@ pub const LIST_FORWARDED_PAYMENTS_PATH: &str = "ListForwardedPayments";
pub const UPDATE_CHANNEL_CONFIG_PATH: &str = "UpdateChannelConfig";
pub const GET_PAYMENT_DETAILS_PATH: &str = "GetPaymentDetails";
pub const CONNECT_PEER_PATH: &str = "ConnectPeer";
pub const SPONTANEOUS_SEND_PATH: &str = "SpontaneousSend";
pub const SIGN_MESSAGE_PATH: &str = "SignMessage";
pub const VERIFY_SIGNATURE_PATH: &str = "VerifySignature";
pub const EXPORT_PATHFINDING_SCORES_PATH: &str = "ExportPathfindingScores";
Loading