Skip to content

Commit b2958d7

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 an optional persist flag that defaults to true for automatic reconnection on restart. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent ebef52e commit b2958d7

File tree

6 files changed

+86
-13
lines changed

6 files changed

+86
-13
lines changed

ldk-server-client/src/client.rs

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,20 @@ use bitcoin_hashes::{sha256, Hash, HashEngine};
1818
use ldk_server_protos::api::{
1919
Bolt11ReceiveRequest, Bolt11ReceiveResponse, Bolt11SendRequest, Bolt11SendResponse,
2020
Bolt12ReceiveRequest, Bolt12ReceiveResponse, Bolt12SendRequest, Bolt12SendResponse,
21-
CloseChannelRequest, CloseChannelResponse, ForceCloseChannelRequest, ForceCloseChannelResponse,
22-
GetBalancesRequest, GetBalancesResponse, GetNodeInfoRequest, GetNodeInfoResponse,
23-
ListChannelsRequest, ListChannelsResponse, ListPaymentsRequest, ListPaymentsResponse,
24-
OnchainReceiveRequest, OnchainReceiveResponse, OnchainSendRequest, OnchainSendResponse,
25-
OpenChannelRequest, OpenChannelResponse, SpliceInRequest, SpliceInResponse, SpliceOutRequest,
26-
SpliceOutResponse, UpdateChannelConfigRequest, UpdateChannelConfigResponse,
21+
CloseChannelRequest, CloseChannelResponse, ConnectPeerRequest, ConnectPeerResponse,
22+
ForceCloseChannelRequest, ForceCloseChannelResponse, GetBalancesRequest, GetBalancesResponse,
23+
GetNodeInfoRequest, GetNodeInfoResponse, ListChannelsRequest, ListChannelsResponse,
24+
ListPaymentsRequest, ListPaymentsResponse, OnchainReceiveRequest, OnchainReceiveResponse,
25+
OnchainSendRequest, OnchainSendResponse, OpenChannelRequest, OpenChannelResponse,
26+
SpliceInRequest, SpliceInResponse, SpliceOutRequest, SpliceOutResponse,
27+
UpdateChannelConfigRequest, UpdateChannelConfigResponse,
2728
};
2829
use ldk_server_protos::endpoints::{
2930
BOLT11_RECEIVE_PATH, BOLT11_SEND_PATH, BOLT12_RECEIVE_PATH, BOLT12_SEND_PATH,
30-
CLOSE_CHANNEL_PATH, FORCE_CLOSE_CHANNEL_PATH, GET_BALANCES_PATH, GET_NODE_INFO_PATH,
31-
LIST_CHANNELS_PATH, LIST_PAYMENTS_PATH, ONCHAIN_RECEIVE_PATH, ONCHAIN_SEND_PATH,
32-
OPEN_CHANNEL_PATH, SPLICE_IN_PATH, SPLICE_OUT_PATH, UPDATE_CHANNEL_CONFIG_PATH,
31+
CLOSE_CHANNEL_PATH, CONNECT_PEER_PATH, FORCE_CLOSE_CHANNEL_PATH, GET_BALANCES_PATH,
32+
GET_NODE_INFO_PATH, LIST_CHANNELS_PATH, LIST_PAYMENTS_PATH, ONCHAIN_RECEIVE_PATH,
33+
ONCHAIN_SEND_PATH, OPEN_CHANNEL_PATH, SPLICE_IN_PATH, SPLICE_OUT_PATH,
34+
UPDATE_CHANNEL_CONFIG_PATH,
3335
};
3436
use ldk_server_protos::error::{ErrorCode, ErrorResponse};
3537
use reqwest::header::CONTENT_TYPE;
@@ -230,6 +232,15 @@ impl LdkServerClient {
230232
self.post_request(&request, &url).await
231233
}
232234

235+
/// Connect to a peer on the Lightning Network.
236+
/// For API contract/usage, refer to docs for [`ConnectPeerRequest`] and [`ConnectPeerResponse`].
237+
pub async fn connect_peer(
238+
&self, request: ConnectPeerRequest,
239+
) -> Result<ConnectPeerResponse, LdkServerError> {
240+
let url = format!("https://{}/{CONNECT_PEER_PATH}", self.base_url);
241+
self.post_request(&request, &url).await
242+
}
243+
233244
async fn post_request<Rq: Message, Rs: Message + Default>(
234245
&self, request: &Rq, url: &str,
235246
) -> 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. Defaults to true.
619+
#[prost(bool, optional, tag = "3")]
620+
pub persist: ::core::option::Option<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/src/api/connect_peer.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
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+
let persist = request.persist.unwrap_or(true);
26+
27+
context.node.connect(node_id, address, persist)?;
28+
29+
Ok(ConnectPeerResponse {})
30+
}

ldk-server/src/api/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ pub(crate) mod bolt11_send;
1717
pub(crate) mod bolt12_receive;
1818
pub(crate) mod bolt12_send;
1919
pub(crate) mod close_channel;
20+
pub(crate) mod connect_peer;
2021
pub(crate) mod error;
2122
pub(crate) mod get_balances;
2223
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
@@ -18,10 +18,10 @@ use hyper::{Request, Response, StatusCode};
1818

1919
use ldk_server_protos::endpoints::{
2020
BOLT11_RECEIVE_PATH, BOLT11_SEND_PATH, BOLT12_RECEIVE_PATH, BOLT12_SEND_PATH,
21-
CLOSE_CHANNEL_PATH, FORCE_CLOSE_CHANNEL_PATH, GET_BALANCES_PATH, GET_NODE_INFO_PATH,
22-
GET_PAYMENT_DETAILS_PATH, LIST_CHANNELS_PATH, LIST_FORWARDED_PAYMENTS_PATH, LIST_PAYMENTS_PATH,
23-
ONCHAIN_RECEIVE_PATH, ONCHAIN_SEND_PATH, OPEN_CHANNEL_PATH, SPLICE_IN_PATH, SPLICE_OUT_PATH,
24-
UPDATE_CHANNEL_CONFIG_PATH,
21+
CLOSE_CHANNEL_PATH, CONNECT_PEER_PATH, FORCE_CLOSE_CHANNEL_PATH, GET_BALANCES_PATH,
22+
GET_NODE_INFO_PATH, GET_PAYMENT_DETAILS_PATH, LIST_CHANNELS_PATH, LIST_FORWARDED_PAYMENTS_PATH,
23+
LIST_PAYMENTS_PATH, ONCHAIN_RECEIVE_PATH, ONCHAIN_SEND_PATH, OPEN_CHANNEL_PATH, SPLICE_IN_PATH,
24+
SPLICE_OUT_PATH, UPDATE_CHANNEL_CONFIG_PATH,
2525
};
2626

2727
use prost::Message;
@@ -31,6 +31,7 @@ use crate::api::bolt11_send::handle_bolt11_send_request;
3131
use crate::api::bolt12_receive::handle_bolt12_receive_request;
3232
use crate::api::bolt12_send::handle_bolt12_send_request;
3333
use crate::api::close_channel::{handle_close_channel_request, handle_force_close_channel_request};
34+
use crate::api::connect_peer::handle_connect_peer;
3435
use crate::api::error::LdkServerError;
3536
use crate::api::error::LdkServerErrorCode::{AuthError, InvalidRequestError};
3637
use crate::api::get_balances::handle_get_balances_request;
@@ -294,6 +295,9 @@ impl Service<Request<Incoming>> for NodeService {
294295
api_key,
295296
handle_list_forwarded_payments_request,
296297
)),
298+
CONNECT_PEER_PATH => {
299+
Box::pin(handle_request(context, req, auth_params, api_key, handle_connect_peer))
300+
},
297301
path => {
298302
let error = format!("Unknown request: {}", path).into_bytes();
299303
Box::pin(async {

0 commit comments

Comments
 (0)