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
9 changes: 5 additions & 4 deletions bindings/ldk_node.udl
Original file line number Diff line number Diff line change
Expand Up @@ -105,13 +105,13 @@ interface Node {
[Throws=NodeError]
void disconnect(PublicKey node_id);
[Throws=NodeError]
UserChannelId open_channel(PublicKey node_id, SocketAddress address, u64 channel_amount_sats, u64? push_to_counterparty_msat, ChannelConfig? channel_config);
UserChannelId open_channel(PublicKey node_id, SocketAddress? address, u64 channel_amount_sats, u64? push_to_counterparty_msat, ChannelConfig? channel_config);
[Throws=NodeError]
UserChannelId open_announced_channel(PublicKey node_id, SocketAddress address, u64 channel_amount_sats, u64? push_to_counterparty_msat, ChannelConfig? channel_config);
UserChannelId open_announced_channel(PublicKey node_id, SocketAddress? address, u64 channel_amount_sats, u64? push_to_counterparty_msat, ChannelConfig? channel_config);
[Throws=NodeError]
UserChannelId open_channel_with_all(PublicKey node_id, SocketAddress address, u64? push_to_counterparty_msat, ChannelConfig? channel_config);
UserChannelId open_channel_with_all(PublicKey node_id, SocketAddress? address, u64? push_to_counterparty_msat, ChannelConfig? channel_config);
[Throws=NodeError]
UserChannelId open_announced_channel_with_all(PublicKey node_id, SocketAddress address, u64? push_to_counterparty_msat, ChannelConfig? channel_config);
UserChannelId open_announced_channel_with_all(PublicKey node_id, SocketAddress? address, u64? push_to_counterparty_msat, ChannelConfig? channel_config);
[Throws=NodeError]
void splice_in([ByRef]UserChannelId user_channel_id, PublicKey counterparty_node_id, u64 splice_amount_sats);
[Throws=NodeError]
Expand Down Expand Up @@ -169,6 +169,7 @@ enum NodeError {
"NotRunning",
"OnchainTxCreationFailed",
"ConnectionFailed",
"NotConnected",
"InvoiceCreationFailed",
"InvoiceRequestCreationFailed",
"OfferCreationFailed",
Expand Down
3 changes: 3 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ pub enum Error {
OnchainTxCreationFailed,
/// A network connection has been closed.
ConnectionFailed,
/// The peer is not connected.
NotConnected,
/// Invoice creation failed.
InvoiceCreationFailed,
/// Invoice request creation failed.
Expand Down Expand Up @@ -148,6 +150,7 @@ impl fmt::Display for Error {
write!(f, "On-chain transaction could not be created.")
},
Self::ConnectionFailed => write!(f, "Network connection closed."),
Self::NotConnected => write!(f, "The peer is not connected."),
Self::InvoiceCreationFailed => write!(f, "Failed to create invoice."),
Self::InvoiceRequestCreationFailed => write!(f, "Failed to create invoice request."),
Self::OfferCreationFailed => write!(f, "Failed to create offer."),
Expand Down
73 changes: 41 additions & 32 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
//!
//! let node_id = PublicKey::from_str("NODE_ID").unwrap();
//! let node_addr = SocketAddress::from_str("IP_ADDR:PORT").unwrap();
//! node.open_channel(node_id, node_addr, 10000, None, None).unwrap();
//! node.open_channel(node_id, Some(node_addr), 10000, None, None).unwrap();
//!
//! let event = node.wait_next_event();
//! println!("EVENT: {:?}", event);
Expand Down Expand Up @@ -1126,39 +1126,47 @@ impl Node {
}

fn open_channel_inner(
&self, node_id: PublicKey, address: SocketAddress, channel_amount_sats: FundingAmount,
push_to_counterparty_msat: Option<u64>, channel_config: Option<ChannelConfig>,
announce_for_forwarding: bool,
&self, node_id: PublicKey, address: Option<SocketAddress>,
channel_amount_sats: FundingAmount, push_to_counterparty_msat: Option<u64>,
channel_config: Option<ChannelConfig>, announce_for_forwarding: bool,
) -> Result<UserChannelId, Error> {
if !*self.is_running.read().unwrap() {
return Err(Error::NotRunning);
}

let peer_info = PeerInfo { node_id, address };

let con_node_id = peer_info.node_id;
let con_addr = peer_info.address.clone();
let con_cm = Arc::clone(&self.connection_manager);

// We need to use our main runtime here as a local runtime might not be around to poll
// connection futures going forward.
self.runtime.block_on(async move {
con_cm.connect_peer_if_necessary(con_node_id, con_addr).await
})?;
// if we don't have the socket address, check if we are already connected
let address = match address {
Some(address) => {
// We need to use our main runtime here as a local runtime might not be around to poll
// connection futures going forward.
let con_cm = Arc::clone(&self.connection_manager);
let con_addr = address.clone();
self.runtime.block_on(async move {
con_cm.connect_peer_if_necessary(node_id, con_addr).await
})?;
Some(address)
},
None => {
// If we are connected, grab the socket address as we need to make sure we have it persisted
// in our peer storage for future reconnections.
let peer =
self.peer_manager.peer_by_node_id(&node_id).ok_or(Error::NotConnected)?;
peer.socket_address
},
};

let channel_amount_sats = match channel_amount_sats {
FundingAmount::Exact { amount_sats } => {
// Check funds availability after connection (includes anchor reserve
// calculation).
self.check_sufficient_funds_for_channel(amount_sats, &peer_info.node_id)?;
self.check_sufficient_funds_for_channel(amount_sats, &node_id)?;
amount_sats
},
FundingAmount::Max => {
// Determine max funding amount from all available on-chain funds.
let cur_anchor_reserve_sats =
total_anchor_channels_reserve_sats(&self.channel_manager, &self.config);
let new_channel_reserve =
self.new_channel_anchor_reserve_sats(&peer_info.node_id)?;
let new_channel_reserve = self.new_channel_anchor_reserve_sats(&node_id)?;
let total_anchor_reserve_sats = cur_anchor_reserve_sats + new_channel_reserve;

let fee_rate =
Expand Down Expand Up @@ -1197,20 +1205,21 @@ impl Node {
);

match self.channel_manager.create_channel(
peer_info.node_id,
node_id,
channel_amount_sats,
push_msat,
user_channel_id,
None,
Some(user_config),
) {
Ok(_) => {
log_info!(
self.logger,
"Initiated channel creation with peer {}. ",
peer_info.node_id
);
self.peer_store.add_peer(peer_info)?;
log_info!(self.logger, "Initiated channel creation with peer {}. ", node_id);

if let Some(address) = address {
let peer_info = PeerInfo { node_id, address };
self.peer_store.add_peer(peer_info)?;
}

Ok(UserChannelId(user_channel_id))
},
Err(e) => {
Expand All @@ -1224,7 +1233,7 @@ impl Node {
let init_features = self
.peer_manager
.peer_by_node_id(peer_node_id)
.ok_or(Error::ConnectionFailed)?
.ok_or(Error::NotConnected)?
.init_features;
let anchor_channel = init_features.requires_anchors_zero_fee_htlc_tx();
Ok(new_channel_anchor_reserve_sats(&self.config, peer_node_id, anchor_channel))
Expand Down Expand Up @@ -1280,7 +1289,7 @@ impl Node {
///
/// [`AnchorChannelsConfig::per_channel_reserve_sats`]: crate::config::AnchorChannelsConfig::per_channel_reserve_sats
pub fn open_channel(
&self, node_id: PublicKey, address: SocketAddress, channel_amount_sats: u64,
&self, node_id: PublicKey, address: Option<SocketAddress>, channel_amount_sats: u64,
push_to_counterparty_msat: Option<u64>, channel_config: Option<ChannelConfig>,
) -> Result<UserChannelId, Error> {
self.open_channel_inner(
Expand Down Expand Up @@ -1315,7 +1324,7 @@ impl Node {
///
/// [`AnchorChannelsConfig::per_channel_reserve_sats`]: crate::config::AnchorChannelsConfig::per_channel_reserve_sats
pub fn open_announced_channel(
&self, node_id: PublicKey, address: SocketAddress, channel_amount_sats: u64,
&self, node_id: PublicKey, address: Option<SocketAddress>, channel_amount_sats: u64,
push_to_counterparty_msat: Option<u64>, channel_config: Option<ChannelConfig>,
) -> Result<UserChannelId, Error> {
if let Err(err) = may_announce_channel(&self.config) {
Expand Down Expand Up @@ -1348,8 +1357,8 @@ impl Node {
///
/// [`AnchorChannelsConfig::per_channel_reserve_sats`]: crate::config::AnchorChannelsConfig::per_channel_reserve_sats
pub fn open_channel_with_all(
&self, node_id: PublicKey, address: SocketAddress, push_to_counterparty_msat: Option<u64>,
channel_config: Option<ChannelConfig>,
&self, node_id: PublicKey, address: Option<SocketAddress>,
push_to_counterparty_msat: Option<u64>, channel_config: Option<ChannelConfig>,
) -> Result<UserChannelId, Error> {
self.open_channel_inner(
node_id,
Expand Down Expand Up @@ -1380,8 +1389,8 @@ impl Node {
///
/// [`AnchorChannelsConfig::per_channel_reserve_sats`]: crate::config::AnchorChannelsConfig::per_channel_reserve_sats
pub fn open_announced_channel_with_all(
&self, node_id: PublicKey, address: SocketAddress, push_to_counterparty_msat: Option<u64>,
channel_config: Option<ChannelConfig>,
&self, node_id: PublicKey, address: Option<SocketAddress>,
push_to_counterparty_msat: Option<u64>, channel_config: Option<ChannelConfig>,
) -> Result<UserChannelId, Error> {
if let Err(err) = may_announce_channel(&self.config) {
log_error!(self.logger, "Failed to open announced channel as the node hasn't been sufficiently configured to act as a forwarding node: {err}");
Expand Down
10 changes: 5 additions & 5 deletions tests/common/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -721,7 +721,7 @@ pub async fn open_channel_push_amt(
node_a
.open_announced_channel(
node_b.node_id(),
node_b.listening_addresses().unwrap().first().unwrap().clone(),
node_b.listening_addresses().unwrap().first().cloned(),
funding_amount_sat,
push_amount_msat,
None,
Expand All @@ -731,7 +731,7 @@ pub async fn open_channel_push_amt(
node_a
.open_channel(
node_b.node_id(),
node_b.listening_addresses().unwrap().first().unwrap().clone(),
node_b.listening_addresses().unwrap().first().cloned(),
funding_amount_sat,
push_amount_msat,
None,
Expand All @@ -755,7 +755,7 @@ pub async fn open_channel_with_all(
node_a
.open_announced_channel_with_all(
node_b.node_id(),
node_b.listening_addresses().unwrap().first().unwrap().clone(),
node_b.listening_addresses().unwrap().first().cloned(),
None,
None,
)
Expand All @@ -764,7 +764,7 @@ pub async fn open_channel_with_all(
node_a
.open_channel_with_all(
node_b.node_id(),
node_b.listening_addresses().unwrap().first().unwrap().clone(),
node_b.listening_addresses().unwrap().first().cloned(),
None,
None,
)
Expand Down Expand Up @@ -851,7 +851,7 @@ pub(crate) async fn do_channel_full_cycle<E: ElectrumApi>(
node_a
.open_announced_channel(
node_b.node_id(),
node_b.listening_addresses().unwrap().first().unwrap().clone(),
node_b.listening_addresses().unwrap().first().cloned(),
funding_amount_sat,
Some(push_msat),
None,
Expand Down
2 changes: 1 addition & 1 deletion tests/integration_tests_cln.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ async fn test_cln() {
// Open the channel
let funding_amount_sat = 1_000_000;

node.open_channel(cln_node_id, cln_address, funding_amount_sat, Some(500_000_000), None)
node.open_channel(cln_node_id, Some(cln_address), funding_amount_sat, Some(500_000_000), None)
.unwrap();

let funding_txo = common::expect_channel_pending_event!(node, cln_node_id);
Expand Down
2 changes: 1 addition & 1 deletion tests/integration_tests_lnd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ async fn test_lnd() {
// Open the channel
let funding_amount_sat = 1_000_000;

node.open_channel(lnd_node_id, lnd_address, funding_amount_sat, Some(500_000_000), None)
node.open_channel(lnd_node_id, Some(lnd_address), funding_amount_sat, Some(500_000_000), None)
.unwrap();

let funding_txo = common::expect_channel_pending_event!(node, lnd_node_id);
Expand Down
50 changes: 49 additions & 1 deletion tests/integration_tests_rust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ async fn channel_open_fails_when_funds_insufficient() {
Err(NodeError::InsufficientFunds),
node_a.open_channel(
node_b.node_id(),
node_b.listening_addresses().unwrap().first().unwrap().clone(),
node_b.listening_addresses().unwrap().first().cloned(),
120000,
None,
None,
Expand Down Expand Up @@ -877,6 +877,54 @@ async fn do_connection_restart_behavior(persist: bool) {
}
}

#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
async fn open_channel_with_optional_address() {
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();
let chain_source = random_chain_source(&bitcoind, &electrsd);
let (node_a, node_b) = setup_two_nodes(&chain_source, false, true, false);

let addr_a = node_a.onchain_payment().new_address().unwrap();
let addr_b = node_b.onchain_payment().new_address().unwrap();

let premine_amount_sat = 2_125_000;

premine_and_distribute_funds(
&bitcoind.client,
&electrsd.client,
vec![addr_a, addr_b],
Amount::from_sat(premine_amount_sat),
)
.await;
node_a.sync_wallets().unwrap();
node_b.sync_wallets().unwrap();

// Opening a channel with no address and no prior connection should fail.
let res = node_a.open_channel(node_b.node_id(), None, 120000, None, None);
assert_eq!(res, Err(NodeError::NotConnected));

// Connect to the peer with persist=false.
let node_addr_b = node_b.listening_addresses().unwrap().first().unwrap().clone();
node_a.connect(node_b.node_id(), node_addr_b, false).unwrap();
assert!(!node_a.list_peers().first().unwrap().is_persisted);

// Opening a channel with no address should now succeed since we're already connected.
node_a.open_channel(node_b.node_id(), None, 120000, None, None).unwrap();

// The peer should now be persisted after the channel open.
assert!(node_a.list_peers().first().unwrap().is_persisted);

let funding_txo = expect_channel_pending_event!(node_a, node_b.node_id());
expect_channel_pending_event!(node_b, node_a.node_id());

wait_for_tx(&electrsd.client, funding_txo.txid).await;
generate_blocks_and_wait(&bitcoind.client, &electrsd.client, 6).await;
node_a.sync_wallets().unwrap();
node_b.sync_wallets().unwrap();

expect_channel_ready_event!(node_a, node_b.node_id());
expect_channel_ready_event!(node_b, node_a.node_id());
}

#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
async fn concurrent_connections_succeed() {
let (bitcoind, electrsd) = setup_bitcoind_and_electrsd();
Expand Down
Loading