Skip to content

Commit 2850e09

Browse files
committed
Make address optional in open-channel
Previously we always required providing the node's address when opening a channel. This was annoying because we may not even need it because we're already connected to the peer. Now we look up the node's address from our peers list if the address is not provided. Also did the same handling as connect peer where you can provide the node and address as pk@addr instead of 2 separate args for easier use in the cli.
1 parent 3764edb commit 2850e09

6 files changed

Lines changed: 79 additions & 24 deletions

File tree

e2e-tests/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -410,7 +410,7 @@ pub async fn setup_funded_channel(
410410
.client()
411411
.open_channel(OpenChannelRequest {
412412
node_pubkey: server_b.node_id().to_string(),
413-
address: format!("127.0.0.1:{}", server_b.p2p_port),
413+
address: Some(format!("127.0.0.1:{}", server_b.p2p_port)),
414414
channel_amount_sats,
415415
push_to_counterparty_msat: None,
416416
channel_config: None,

e2e-tests/tests/e2e.rs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ use std::time::Duration;
1212

1313
use e2e_tests::{
1414
find_available_port, mine_and_sync, run_cli, run_cli_raw, setup_funded_channel,
15-
wait_for_onchain_balance, LdkServerHandle, RabbitMqEventConsumer, TestBitcoind,
15+
wait_for_onchain_balance, wait_for_usable_channel, LdkServerHandle, RabbitMqEventConsumer,
16+
TestBitcoind,
1617
};
1718
use ldk_node::lightning::ln::msgs::SocketAddress;
1819
use ldk_server_client::ldk_server_protos::api::{
@@ -226,9 +227,27 @@ async fn test_cli_open_channel() {
226227
let addr = format!("127.0.0.1:{}", server_b.p2p_port);
227228
let output = run_cli(
228229
&server_a,
229-
&["open-channel", server_b.node_id(), &addr, "100000sat", "--announce-channel"],
230+
&[
231+
"open-channel",
232+
server_b.node_id(),
233+
"100000sat",
234+
"--address",
235+
&addr,
236+
"--announce-channel",
237+
],
230238
);
231-
assert!(!output["user_channel_id"].as_str().unwrap().is_empty());
239+
let first_user_channel_id = output["user_channel_id"].as_str().unwrap().to_string();
240+
assert!(!first_user_channel_id.is_empty());
241+
242+
// Confirm first channel is usable before opening a second one.
243+
mine_and_sync(&bitcoind, &[&server_a, &server_b], 6).await;
244+
wait_for_usable_channel(server_a.client(), &bitcoind, Duration::from_secs(60)).await;
245+
246+
// Open a second channel without specifying address (resolved by backend from connected peers).
247+
let output = run_cli(&server_a, &["open-channel", server_b.node_id(), "50000sat"]);
248+
let second_user_channel_id = output["user_channel_id"].as_str().unwrap().to_string();
249+
assert!(!second_user_channel_id.is_empty());
250+
assert_ne!(first_user_channel_id, second_user_channel_id);
232251
}
233252

234253
#[tokio::test]

ldk-server-cli/src/main.rs

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -249,16 +249,18 @@ enum Commands {
249249
},
250250
#[command(about = "Create a new outbound channel to the given remote node")]
251251
OpenChannel {
252-
#[arg(help = "The hex-encoded public key of the node to open a channel with")]
252+
#[arg(help = "The node to open a channel with, as hex pubkey or pubkey@address format")]
253253
node_pubkey: String,
254-
#[arg(
255-
help = "Address to connect to remote peer (IPv4:port, IPv6:port, OnionV3:port, or hostname:port)"
256-
)]
257-
address: String,
258254
#[arg(
259255
help = "The amount to commit to the channel, e.g. 100sat or 100000msat, must be a whole sat amount, cannot send msats on-chain."
260256
)]
261257
channel_amount: Amount,
258+
#[arg(
259+
short,
260+
long,
261+
help = "Address to connect to remote peer (IPv4:port, IPv6:port, OnionV3:port, or hostname:port). Optional if included in pubkey via @ separator or if peer is already connected."
262+
)]
263+
address: Option<String>,
262264
#[arg(long, help = "Amount to push to the remote side, e.g. 50sat or 50000msat")]
263265
push_to_counterparty: Option<Amount>,
264266
#[arg(long, help = "Whether the channel should be public")]
@@ -678,14 +680,21 @@ async fn main() {
678680
},
679681
Commands::OpenChannel {
680682
node_pubkey,
681-
address,
682683
channel_amount,
684+
address,
683685
push_to_counterparty,
684686
announce_channel,
685687
forwarding_fee_proportional_millionths,
686688
forwarding_fee_base_msat,
687689
cltv_expiry_delta,
688690
} => {
691+
let (node_pubkey, inline_address) = parse_node_pubkey_and_inline_address(node_pubkey);
692+
if inline_address.is_some() && address.is_some() {
693+
handle_error_msg(
694+
"Address was provided twice. Use either pubkey@address or a separate address argument.",
695+
);
696+
}
697+
let address = address.or(inline_address);
689698
let channel_amount_sats =
690699
channel_amount.to_sat().unwrap_or_else(|e| handle_error_msg(&e));
691700
let push_to_counterparty_msat = push_to_counterparty.map(|a| a.to_msat());
@@ -804,12 +813,13 @@ async fn main() {
804813
);
805814
},
806815
Commands::ConnectPeer { node_pubkey, address, persist } => {
807-
let (node_pubkey, address) = if let Some(address) = address {
808-
(node_pubkey, address)
809-
} else if let Some((pubkey, addr)) = node_pubkey.split_once('@') {
810-
(pubkey.to_string(), addr.to_string())
816+
let (node_pubkey, inline_address) = parse_node_pubkey_and_inline_address(node_pubkey);
817+
let address = if let Some(address) = address.or(inline_address) {
818+
address
811819
} else {
812-
eprintln!("Error: address is required. Provide it as pubkey@address or as a separate argument.");
820+
eprintln!(
821+
"Error: address is required. Provide it as pubkey@address or as a separate argument."
822+
);
813823
std::process::exit(1);
814824
};
815825
handle_response_result::<_, ConnectPeerResponse>(
@@ -878,6 +888,14 @@ async fn main() {
878888
}
879889
}
880890

891+
fn parse_node_pubkey_and_inline_address(node_pubkey: String) -> (String, Option<String>) {
892+
if let Some((pubkey, address)) = node_pubkey.split_once('@') {
893+
(pubkey.to_string(), Some(address.to_string()))
894+
} else {
895+
(node_pubkey, None)
896+
}
897+
}
898+
881899
fn build_open_channel_config(
882900
forwarding_fee_proportional_millionths: Option<u32>, forwarding_fee_base_msat: Option<u32>,
883901
cltv_expiry_delta: Option<u32>,

ldk-server-protos/src/api.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -317,10 +317,11 @@ pub struct OpenChannelRequest {
317317
/// The hex-encoded public key of the node to open a channel with.
318318
#[prost(string, tag = "1")]
319319
pub node_pubkey: ::prost::alloc::string::String,
320-
/// An address which can be used to connect to a remote peer.
321-
/// It can be of type IPv4:port, IPv6:port, OnionV3:port or hostname:port
322-
#[prost(string, tag = "2")]
323-
pub address: ::prost::alloc::string::String,
320+
/// An optional address which can be used to connect to a remote peer.
321+
/// It can be of type IPv4:port, IPv6:port, OnionV3:port or hostname:port.
322+
/// If unset, the server will try to resolve it from currently connected peers.
323+
#[prost(string, optional, tag = "2")]
324+
pub address: ::core::option::Option<::prost::alloc::string::String>,
324325
/// The amount of satoshis the caller is willing to commit to the channel.
325326
#[prost(uint64, tag = "3")]
326327
pub channel_amount_sats: u64,

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -263,9 +263,10 @@ message OpenChannelRequest {
263263
// The hex-encoded public key of the node to open a channel with.
264264
string node_pubkey = 1;
265265

266-
// An address which can be used to connect to a remote peer.
267-
// It can be of type IPv4:port, IPv6:port, OnionV3:port or hostname:port
268-
string address = 2;
266+
// An optional address which can be used to connect to a remote peer.
267+
// It can be of type IPv4:port, IPv6:port, OnionV3:port or hostname:port.
268+
// If unset, the server will try to resolve it from currently connected peers.
269+
optional string address = 2;
269270

270271
// The amount of satoshis the caller is willing to commit to the channel.
271272
uint64 channel_amount_sats = 3;

ldk-server/src/api/open_channel.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,31 @@ use ldk_server_protos::api::{OpenChannelRequest, OpenChannelResponse};
1616

1717
use crate::api::build_channel_config_from_proto;
1818
use crate::api::error::LdkServerError;
19+
use crate::api::error::LdkServerErrorCode::InvalidRequestError;
1920
use crate::service::Context;
2021

2122
pub(crate) fn handle_open_channel(
2223
context: Context, request: OpenChannelRequest,
2324
) -> Result<OpenChannelResponse, LdkServerError> {
2425
let node_id = PublicKey::from_str(&request.node_pubkey)
2526
.map_err(|_| ldk_node::NodeError::InvalidPublicKey)?;
26-
let address = SocketAddress::from_str(&request.address)
27-
.map_err(|_| ldk_node::NodeError::InvalidSocketAddress)?;
27+
let address = match request.address {
28+
Some(address) => {
29+
SocketAddress::from_str(&address).map_err(|_| ldk_node::NodeError::InvalidSocketAddress)?
30+
},
31+
None => context
32+
.node
33+
.list_peers()
34+
.into_iter()
35+
.find(|peer| peer.node_id == node_id)
36+
.map(|peer| peer.address)
37+
.ok_or_else(|| {
38+
LdkServerError::new(
39+
InvalidRequestError,
40+
"Address is required unless the peer is currently connected. Provide an address or connect-peer first.".to_string(),
41+
)
42+
})?,
43+
};
2844

2945
let channel_config = request
3046
.channel_config

0 commit comments

Comments
 (0)