Skip to content

Commit acb1bb8

Browse files
joostjagerclaude
andcommitted
Add ConnectPeer API endpoint
Adds a new endpoint to connect to a peer on the Lightning Network without opening a channel. This is useful for establishing connections before channel operations or for maintaining peer connectivity. The endpoint accepts node_pubkey, address, and a persist flag that indicates whether the peer should be re-connected on restart. Also adds client library and CLI support for the new endpoint. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 7e55f97 commit acb1bb8

File tree

8 files changed

+133
-23
lines changed

8 files changed

+133
-23
lines changed

ldk-server-cli/src/main.rs

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,13 @@ use ldk_server_client::error::LdkServerErrorCode::{
2323
use ldk_server_client::ldk_server_protos::api::{
2424
Bolt11ReceiveRequest, Bolt11ReceiveResponse, Bolt11SendRequest, Bolt11SendResponse,
2525
Bolt12ReceiveRequest, Bolt12ReceiveResponse, Bolt12SendRequest, Bolt12SendResponse,
26-
CloseChannelRequest, CloseChannelResponse, ForceCloseChannelRequest, ForceCloseChannelResponse,
27-
GetBalancesRequest, GetBalancesResponse, GetNodeInfoRequest, GetNodeInfoResponse,
28-
GetPaymentDetailsRequest, GetPaymentDetailsResponse, ListChannelsRequest, ListChannelsResponse,
29-
ListForwardedPaymentsRequest, ListPaymentsRequest, OnchainReceiveRequest,
30-
OnchainReceiveResponse, OnchainSendRequest, OnchainSendResponse, OpenChannelRequest,
31-
OpenChannelResponse, SpliceInRequest, SpliceInResponse, SpliceOutRequest, SpliceOutResponse,
32-
UpdateChannelConfigRequest, UpdateChannelConfigResponse,
26+
CloseChannelRequest, CloseChannelResponse, ConnectPeerRequest, ConnectPeerResponse,
27+
ForceCloseChannelRequest, ForceCloseChannelResponse, GetBalancesRequest, GetBalancesResponse,
28+
GetNodeInfoRequest, GetNodeInfoResponse, GetPaymentDetailsRequest, GetPaymentDetailsResponse,
29+
ListChannelsRequest, ListChannelsResponse, ListForwardedPaymentsRequest, ListPaymentsRequest,
30+
OnchainReceiveRequest, OnchainReceiveResponse, OnchainSendRequest, OnchainSendResponse,
31+
OpenChannelRequest, OpenChannelResponse, SpliceInRequest, SpliceInResponse, SpliceOutRequest,
32+
SpliceOutResponse, UpdateChannelConfigRequest, UpdateChannelConfigResponse,
3333
};
3434
use ldk_server_client::ldk_server_protos::types::{
3535
bolt11_invoice_description, Bolt11InvoiceDescription, ChannelConfig, PageToken,
@@ -336,6 +336,23 @@ enum Commands {
336336
)]
337337
cltv_expiry_delta: Option<u32>,
338338
},
339+
#[command(about = "Connect to a peer on the Lightning Network without opening a channel")]
340+
ConnectPeer {
341+
#[arg(short, long, help = "The hex-encoded public key of the node to connect to")]
342+
node_pubkey: String,
343+
#[arg(
344+
short,
345+
long,
346+
help = "Address to connect to remote peer (IPv4:port, IPv6:port, OnionV3:port, or hostname:port)"
347+
)]
348+
address: String,
349+
#[arg(
350+
long,
351+
default_value_t = false,
352+
help = "Whether to persist the connection for automatic reconnection on restart"
353+
)]
354+
persist: bool,
355+
},
339356
#[command(about = "Generate shell completions for the CLI")]
340357
Completions {
341358
#[arg(
@@ -673,6 +690,11 @@ async fn main() {
673690
.await,
674691
);
675692
},
693+
Commands::ConnectPeer { node_pubkey, address, persist } => {
694+
handle_response_result::<_, ConnectPeerResponse>(
695+
client.connect_peer(ConnectPeerRequest { node_pubkey, address, persist }).await,
696+
);
697+
},
676698
Commands::Completions { .. } => unreachable!("Handled above"),
677699
}
678700
}

ldk-server-client/src/client.rs

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,21 @@ use bitcoin_hashes::{sha256, Hash, HashEngine};
1414
use ldk_server_protos::api::{
1515
Bolt11ReceiveRequest, Bolt11ReceiveResponse, Bolt11SendRequest, Bolt11SendResponse,
1616
Bolt12ReceiveRequest, Bolt12ReceiveResponse, Bolt12SendRequest, Bolt12SendResponse,
17-
CloseChannelRequest, CloseChannelResponse, ForceCloseChannelRequest, ForceCloseChannelResponse,
18-
GetBalancesRequest, GetBalancesResponse, GetNodeInfoRequest, GetNodeInfoResponse,
19-
GetPaymentDetailsRequest, GetPaymentDetailsResponse, ListChannelsRequest, ListChannelsResponse,
20-
ListForwardedPaymentsRequest, ListForwardedPaymentsResponse, ListPaymentsRequest,
21-
ListPaymentsResponse, OnchainReceiveRequest, OnchainReceiveResponse, OnchainSendRequest,
22-
OnchainSendResponse, OpenChannelRequest, OpenChannelResponse, SpliceInRequest,
23-
SpliceInResponse, SpliceOutRequest, SpliceOutResponse, UpdateChannelConfigRequest,
24-
UpdateChannelConfigResponse,
17+
CloseChannelRequest, CloseChannelResponse, ConnectPeerRequest, ConnectPeerResponse,
18+
ForceCloseChannelRequest, ForceCloseChannelResponse, GetBalancesRequest, GetBalancesResponse,
19+
GetNodeInfoRequest, GetNodeInfoResponse, GetPaymentDetailsRequest, GetPaymentDetailsResponse,
20+
ListChannelsRequest, ListChannelsResponse, ListForwardedPaymentsRequest,
21+
ListForwardedPaymentsResponse, ListPaymentsRequest, ListPaymentsResponse,
22+
OnchainReceiveRequest, OnchainReceiveResponse, OnchainSendRequest, OnchainSendResponse,
23+
OpenChannelRequest, OpenChannelResponse, SpliceInRequest, SpliceInResponse, SpliceOutRequest,
24+
SpliceOutResponse, UpdateChannelConfigRequest, UpdateChannelConfigResponse,
2525
};
2626
use ldk_server_protos::endpoints::{
2727
BOLT11_RECEIVE_PATH, BOLT11_SEND_PATH, BOLT12_RECEIVE_PATH, BOLT12_SEND_PATH,
28-
CLOSE_CHANNEL_PATH, FORCE_CLOSE_CHANNEL_PATH, GET_BALANCES_PATH, GET_NODE_INFO_PATH,
29-
GET_PAYMENT_DETAILS_PATH, LIST_CHANNELS_PATH, LIST_FORWARDED_PAYMENTS_PATH, LIST_PAYMENTS_PATH,
30-
ONCHAIN_RECEIVE_PATH, ONCHAIN_SEND_PATH, OPEN_CHANNEL_PATH, SPLICE_IN_PATH, SPLICE_OUT_PATH,
31-
UPDATE_CHANNEL_CONFIG_PATH,
28+
CLOSE_CHANNEL_PATH, CONNECT_PEER_PATH, FORCE_CLOSE_CHANNEL_PATH, GET_BALANCES_PATH,
29+
GET_NODE_INFO_PATH, GET_PAYMENT_DETAILS_PATH, LIST_CHANNELS_PATH, LIST_FORWARDED_PAYMENTS_PATH,
30+
LIST_PAYMENTS_PATH, ONCHAIN_RECEIVE_PATH, ONCHAIN_SEND_PATH, OPEN_CHANNEL_PATH, SPLICE_IN_PATH,
31+
SPLICE_OUT_PATH, UPDATE_CHANNEL_CONFIG_PATH,
3232
};
3333
use ldk_server_protos::error::{ErrorCode, ErrorResponse};
3434
use prost::Message;
@@ -252,6 +252,15 @@ impl LdkServerClient {
252252
self.post_request(&request, &url).await
253253
}
254254

255+
/// Connect to a peer on the Lightning Network.
256+
/// For API contract/usage, refer to docs for [`ConnectPeerRequest`] and [`ConnectPeerResponse`].
257+
pub async fn connect_peer(
258+
&self, request: ConnectPeerRequest,
259+
) -> Result<ConnectPeerResponse, LdkServerError> {
260+
let url = format!("https://{}/{CONNECT_PEER_PATH}", self.base_url);
261+
self.post_request(&request, &url).await
262+
}
263+
255264
async fn post_request<Rq: Message, Rs: Message + Default>(
256265
&self, request: &Rq, url: &str,
257266
) -> Result<Rs, LdkServerError> {

ldk-server-protos/src/api.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -600,3 +600,29 @@ pub struct GetBalancesResponse {
600600
pub pending_balances_from_channel_closures:
601601
::prost::alloc::vec::Vec<super::types::PendingSweepBalance>,
602602
}
603+
/// Connect to a peer on the Lightning Network.
604+
/// See more: <https://docs.rs/ldk-node/latest/ldk_node/struct.Node.html#method.connect>
605+
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
606+
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
607+
#[allow(clippy::derive_partial_eq_without_eq)]
608+
#[derive(Clone, PartialEq, ::prost::Message)]
609+
pub struct ConnectPeerRequest {
610+
/// The hex-encoded public key of the node to connect to.
611+
#[prost(string, tag = "1")]
612+
pub node_pubkey: ::prost::alloc::string::String,
613+
/// An address which can be used to connect to a remote peer.
614+
/// It can be of type IPv4:port, IPv6:port, OnionV3:port or hostname:port
615+
#[prost(string, tag = "2")]
616+
pub address: ::prost::alloc::string::String,
617+
/// Whether to persist the peer connection, i.e., whether the peer will be re-connected on
618+
/// restart.
619+
#[prost(bool, tag = "3")]
620+
pub persist: bool,
621+
}
622+
/// The response `content` for the `ConnectPeer` API, when HttpStatusCode is OK (200).
623+
/// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`.
624+
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
625+
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
626+
#[allow(clippy::derive_partial_eq_without_eq)]
627+
#[derive(Clone, PartialEq, ::prost::Message)]
628+
pub struct ConnectPeerResponse {}

ldk-server-protos/src/endpoints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,4 @@ pub const LIST_PAYMENTS_PATH: &str = "ListPayments";
2525
pub const LIST_FORWARDED_PAYMENTS_PATH: &str = "ListForwardedPayments";
2626
pub const UPDATE_CHANNEL_CONFIG_PATH: &str = "UpdateChannelConfig";
2727
pub const GET_PAYMENT_DETAILS_PATH: &str = "GetPaymentDetails";
28+
pub const CONNECT_PEER_PATH: &str = "ConnectPeer";

ldk-server-protos/src/proto/api.proto

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,3 +480,22 @@ message GetBalancesResponse {
480480
// might not already be accounted for in `total_onchain_balance_sats`.
481481
repeated types.PendingSweepBalance pending_balances_from_channel_closures = 6;
482482
}
483+
484+
// Connect to a peer on the Lightning Network.
485+
// See more: https://docs.rs/ldk-node/latest/ldk_node/struct.Node.html#method.connect
486+
message ConnectPeerRequest {
487+
// The hex-encoded public key of the node to connect to.
488+
string node_pubkey = 1;
489+
490+
// An address which can be used to connect to a remote peer.
491+
// It can be of type IPv4:port, IPv6:port, OnionV3:port or hostname:port
492+
string address = 2;
493+
494+
// Whether to persist the peer connection, i.e., whether the peer will be re-connected on
495+
// restart.
496+
bool persist = 3;
497+
}
498+
499+
// The response `content` for the `ConnectPeer` API, when HttpStatusCode is OK (200).
500+
// When HttpStatusCode is not OK (non-200), the response `content` contains a serialized `ErrorResponse`.
501+
message ConnectPeerResponse {}

ldk-server/src/api/connect_peer.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// This file is Copyright its original authors, visible in version control
2+
// history.
3+
//
4+
// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
5+
// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
7+
// You may not use this file except in accordance with one or both of these
8+
// licenses.
9+
10+
use crate::api::error::LdkServerError;
11+
use crate::service::Context;
12+
use ldk_node::bitcoin::secp256k1::PublicKey;
13+
use ldk_node::lightning::ln::msgs::SocketAddress;
14+
use ldk_server_protos::api::{ConnectPeerRequest, ConnectPeerResponse};
15+
use std::str::FromStr;
16+
17+
pub(crate) fn handle_connect_peer(
18+
context: Context, request: ConnectPeerRequest,
19+
) -> Result<ConnectPeerResponse, LdkServerError> {
20+
let node_id = PublicKey::from_str(&request.node_pubkey)
21+
.map_err(|_| ldk_node::NodeError::InvalidPublicKey)?;
22+
let address = SocketAddress::from_str(&request.address)
23+
.map_err(|_| ldk_node::NodeError::InvalidSocketAddress)?;
24+
25+
context.node.connect(node_id, address, request.persist)?;
26+
27+
Ok(ConnectPeerResponse {})
28+
}

ldk-server/src/api/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ pub(crate) mod bolt11_send;
1818
pub(crate) mod bolt12_receive;
1919
pub(crate) mod bolt12_send;
2020
pub(crate) mod close_channel;
21+
pub(crate) mod connect_peer;
2122
pub(crate) mod error;
2223
pub(crate) mod get_balances;
2324
pub(crate) mod get_node_info;

ldk-server/src/service.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ use ldk_node::bitcoin::hashes::{sha256, Hash, HashEngine};
2020
use ldk_node::Node;
2121
use ldk_server_protos::endpoints::{
2222
BOLT11_RECEIVE_PATH, BOLT11_SEND_PATH, BOLT12_RECEIVE_PATH, BOLT12_SEND_PATH,
23-
CLOSE_CHANNEL_PATH, FORCE_CLOSE_CHANNEL_PATH, GET_BALANCES_PATH, GET_NODE_INFO_PATH,
24-
GET_PAYMENT_DETAILS_PATH, LIST_CHANNELS_PATH, LIST_FORWARDED_PAYMENTS_PATH, LIST_PAYMENTS_PATH,
25-
ONCHAIN_RECEIVE_PATH, ONCHAIN_SEND_PATH, OPEN_CHANNEL_PATH, SPLICE_IN_PATH, SPLICE_OUT_PATH,
26-
UPDATE_CHANNEL_CONFIG_PATH,
23+
CLOSE_CHANNEL_PATH, CONNECT_PEER_PATH, FORCE_CLOSE_CHANNEL_PATH, GET_BALANCES_PATH,
24+
GET_NODE_INFO_PATH, GET_PAYMENT_DETAILS_PATH, LIST_CHANNELS_PATH, LIST_FORWARDED_PAYMENTS_PATH,
25+
LIST_PAYMENTS_PATH, ONCHAIN_RECEIVE_PATH, ONCHAIN_SEND_PATH, OPEN_CHANNEL_PATH, SPLICE_IN_PATH,
26+
SPLICE_OUT_PATH, UPDATE_CHANNEL_CONFIG_PATH,
2727
};
2828
use prost::Message;
2929

@@ -32,6 +32,7 @@ use crate::api::bolt11_send::handle_bolt11_send_request;
3232
use crate::api::bolt12_receive::handle_bolt12_receive_request;
3333
use crate::api::bolt12_send::handle_bolt12_send_request;
3434
use crate::api::close_channel::{handle_close_channel_request, handle_force_close_channel_request};
35+
use crate::api::connect_peer::handle_connect_peer;
3536
use crate::api::error::LdkServerError;
3637
use crate::api::error::LdkServerErrorCode::{AuthError, InvalidRequestError};
3738
use crate::api::get_balances::handle_get_balances_request;
@@ -292,6 +293,9 @@ impl Service<Request<Incoming>> for NodeService {
292293
api_key,
293294
handle_list_forwarded_payments_request,
294295
)),
296+
CONNECT_PEER_PATH => {
297+
Box::pin(handle_request(context, req, auth_params, api_key, handle_connect_peer))
298+
},
295299
path => {
296300
let error = format!("Unknown request: {}", path).into_bytes();
297301
Box::pin(async {

0 commit comments

Comments
 (0)