Skip to content
Merged
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: 6 additions & 3 deletions README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,6 +10,7 @@ The primary abstraction of the library is the `Node`, which can be retrieved by
```rust
use ldk_node::Builder;
use ldk_node::lightning_invoice::Invoice;
use ldk_node::bitcoin::secp256k1::PublicKey;
use std::str::FromStr;

fn main() {
Expand All@@ -23,13 +24,15 @@ fn main() {
let _funding_address = node.new_funding_address();

// .. fund address ..

node.sync_wallets().unwrap();

node.connect_open_channel("NODE_ID@PEER_ADDR:PORT", 10000, None, false).unwrap();
let node_id = PublicKey::from_str("NODE_ID").unwrap();
let node_addr = "IP_ADDR:PORT".parse().unwrap();
node.connect_open_channel(node_id, node_addr, 10000, None, false).unwrap();

let invoice = Invoice::from_str("INVOICE_STR").unwrap();
node.send_payment(invoice).unwrap();
node.send_payment(&invoice).unwrap();

node.stop().unwrap();
}
Expand Down
15 changes: 0 additions & 15 deletions src/hex_utils.rs
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
use bitcoin::secp256k1::PublicKey;
use std::fmt::Write;

pub fn to_vec(hex: &str) -> Option<Vec<u8>> {
Expand DownExpand Up@@ -30,17 +29,3 @@ pub fn to_string(value: &[u8]) -> String {
}
res
}

pub fn to_compressed_pubkey(hex: &str) -> Option<PublicKey> {
if hex.len() != 33 * 2 {
return None;
}
let data = match to_vec(&hex[0..33 * 2]) {
Some(bytes) => bytes,
None => return None,
};
match PublicKey::from_slice(&data) {
Ok(pk) => Some(pk),
Err(_) => None,
}
}
125 changes: 96 additions & 29 deletions src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -28,6 +28,7 @@
//! ```no_run
//! use ldk_node::Builder;
//! use ldk_node::lightning_invoice::Invoice;
//! use ldk_node::bitcoin::secp256k1::PublicKey;
//! use std::str::FromStr;
//!
//! fn main() {
Expand All@@ -44,10 +45,12 @@
//!
//! node.sync_wallets().unwrap();
//!
//! node.connect_open_channel("NODE_ID@PEER_ADDR:PORT", 10000, None, false).unwrap();
//! let node_id = PublicKey::from_str("NODE_ID").unwrap();
//! let node_addr = "IP_ADDR:PORT".parse().unwrap();
//! node.connect_open_channel(node_id, node_addr, 10000, None, false).unwrap();
//!
//! let invoice = Invoice::from_str("INVOICE_STR").unwrap();
//! node.send_payment(invoice).unwrap();
//! node.send_payment(&invoice).unwrap();
//!
//! node.stop().unwrap();
//! }
Expand All@@ -60,8 +63,8 @@
//! [`send_payment`]: Node::send_payment
//!
#![deny(missing_docs)]
#![deny(broken_intra_doc_links)]
#![deny(private_intra_doc_links)]
#![deny(rustdoc::broken_intra_doc_links)]
#![deny(rustdoc::private_intra_doc_links)]
#![allow(bare_trait_objects)]
#![allow(ellipsis_inclusive_range_patterns)]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
Expand DownExpand Up@@ -100,9 +103,9 @@ use logger::{log_error, log_info, FilesystemLogger, Logger};

use lightning::chain::keysinterface::EntropySource;
use lightning::chain::{chainmonitor, BestBlock, Confirm, Watch};
use lightning::ln::channelmanager::{self, RecipientOnionFields};
use lightning::ln::channelmanager::{
ChainParameters, ChannelDetails, ChannelManagerReadArgs, PaymentId, Retry,
self, ChainParameters, ChannelDetails, ChannelManagerReadArgs, PaymentId, RecipientOnionFields,
Retry,
};
use lightning::ln::peer_handler::{IgnoringMessageHandler, MessageHandler};
use lightning::ln::{PaymentHash, PaymentPreimage};
Expand DownExpand Up@@ -133,7 +136,7 @@ use bitcoin::BlockHash;

use rand::Rng;

use std::convert::{TryFrom, TryInto};
use std::convert::TryInto;
use std::default::Default;
use std::fs;
use std::net::SocketAddr;
Expand DownExpand Up@@ -167,7 +170,7 @@ pub struct Config {
/// The used Bitcoin network.
pub network: bitcoin::Network,
/// The IP address and TCP port the node will listen on.
pub listening_address: Option<String>,
pub listening_address: Option<SocketAddr>,
/// The default CLTV expiry delta to be used for payments.
pub default_cltv_expiry_delta: u32,
}
Expand All@@ -178,7 +181,7 @@ impl Default for Config {
storage_dir_path: "/tmp/ldk_node/".to_string(),
esplora_server_url: "http://localhost:3002".to_string(),
network: bitcoin::Network::Regtest,
listening_address: Some("0.0.0.0:9735".to_string()),
listening_address: Some("0.0.0.0:9735".parse().unwrap()),
default_cltv_expiry_delta: 144,
}
}
Expand DownExpand Up@@ -262,9 +265,8 @@ impl Builder {

/// Sets the IP address and TCP port on which [`Node`] will listen for incoming network connections.
///
/// Format: `ADDR:PORT`
/// Default: `0.0.0.0:9735`
pub fn set_listening_address(&mut self, listening_address: String) -> &mut Self {
pub fn set_listening_address(&mut self, listening_address: SocketAddr) -> &mut Self {
self.config.listening_address = Some(listening_address);
self
}
Expand DownExpand Up@@ -819,9 +821,9 @@ impl Node {
self.channel_manager.get_our_node_id()
}

/// Returns our own listening address and port.
pub fn listening_address(&self) -> Option<String> {
self.config.listening_address.clone()
/// Returns our own listening address.
pub fn listening_address(&self) -> Option<&SocketAddr> {
self.config.listening_address.as_ref()
}

/// Retrieve a new on-chain/funding address.
Expand All@@ -841,7 +843,74 @@ impl Node {
self.channel_manager.list_channels()
}

/// Connect to a node and opens a new channel.
/// Connect to a node on the peer-to-peer network.
///
/// If `permanently` is set to `true`, we'll remember the peer and reconnect to it on restart.
pub fn connect(
&self, node_id: PublicKey, address: SocketAddr, permanently: bool,
) -> Result<(), Error> {
let runtime_lock = self.running.read().unwrap();
if runtime_lock.is_none() {
return Err(Error::NotRunning);
}

let runtime = runtime_lock.as_ref().unwrap();

let peer_info = PeerInfo { pubkey: node_id, address };

let con_peer_pubkey = peer_info.pubkey;
let con_peer_addr = peer_info.address;
let con_success = Arc::new(AtomicBool::new(false));
let con_success_cloned = Arc::clone(&con_success);
let con_logger = Arc::clone(&self.logger);
let con_pm = Arc::clone(&self.peer_manager);

tokio::task::block_in_place(move || {
runtime.tokio_runtime.block_on(async move {
let res =
connect_peer_if_necessary(con_peer_pubkey, con_peer_addr, con_pm, con_logger)
.await;
con_success_cloned.store(res.is_ok(), Ordering::Release);
})
});

if !con_success.load(Ordering::Acquire) {
return Err(Error::ConnectionFailed);
}

log_info!(self.logger, "Connected to peer {}@{}. ", peer_info.pubkey, peer_info.address,);

if permanently {
self.peer_store.add_peer(peer_info)?;
}

Ok(())
}

/// Disconnects the peer with the given node id.
///
/// Will also remove the peer from the peer store, i.e., after this has been called we won't
/// try to reconnect on restart.
pub fn disconnect(&self, counterparty_node_id: &PublicKey) -> Result<(), Error> {
let runtime_lock = self.running.read().unwrap();
if runtime_lock.is_none() {
return Err(Error::NotRunning);
}

log_info!(self.logger, "Disconnecting peer {}..", counterparty_node_id);

match self.peer_store.remove_peer(&counterparty_node_id) {
Ok(()) => {}
Err(e) => {
log_error!(self.logger, "Failed to remove peer {}: {}", counterparty_node_id, e)
}
}

self.peer_manager.disconnect_by_node_id(*counterparty_node_id);
Ok(())
}

/// Connect to a node and open a new channel. Disconnects and re-connects are handled automatically
///
/// Disconnects and reconnects are handled automatically.
///
Expand All@@ -851,7 +920,7 @@ impl Node {
///
/// Returns a temporary channel id.
pub fn connect_open_channel(
&self, node_pubkey_and_address: &str, channel_amount_sats: u64,
&self, node_id: PublicKey, address: SocketAddr, channel_amount_sats: u64,
push_to_counterparty_msat: Option<u64>, announce_channel: bool,
) -> Result<(), Error> {
let runtime_lock = self.running.read().unwrap();
Expand All@@ -867,10 +936,10 @@ impl Node {
return Err(Error::InsufficientFunds);
}

let peer_info = PeerInfo::try_from(node_pubkey_and_address.to_string())?;
let peer_info = PeerInfo { pubkey: node_id, address };

let con_peer_pubkey = peer_info.pubkey.clone();
let con_peer_addr = peer_info.address.clone();
let con_peer_pubkey = peer_info.pubkey;
let con_peer_addr = peer_info.address;
let con_success = Arc::new(AtomicBool::new(false));
let con_success_cloned = Arc::clone(&con_success);
let con_logger = Arc::clone(&self.logger);
Expand DownExpand Up@@ -913,12 +982,12 @@ impl Node {
Some(user_config),
) {
Ok(_) => {
self.peer_store.add_peer(peer_info.clone())?;
log_info!(
self.logger,
"Initiated channel creation with peer {}. ",
peer_info.pubkey
);
self.peer_store.add_peer(peer_info)?;
Ok(())
}
Err(e) => {
Expand DownExpand Up@@ -1005,7 +1074,7 @@ impl Node {
}

/// Send a payement given an invoice.
pub fn send_payment(&self, invoice: Invoice) -> Result<PaymentHash, Error> {
pub fn send_payment(&self, invoice: &Invoice) -> Result<PaymentHash, Error> {
if self.running.read().unwrap().is_none() {
return Err(Error::NotRunning);
}
Expand DownExpand Up@@ -1070,7 +1139,7 @@ impl Node {
/// This can be used to pay a so-called "zero-amount" invoice, i.e., an invoice that leaves the
/// amount paid to be determined by the user.
pub fn send_payment_using_amount(
&self, invoice: Invoice, amount_msat: u64,
&self, invoice: &Invoice, amount_msat: u64,
) -> Result<PaymentHash, Error> {
if self.running.read().unwrap().is_none() {
return Err(Error::NotRunning);
Expand DownExpand Up@@ -1158,20 +1227,18 @@ impl Node {

/// Send a spontaneous, aka. "keysend", payment
pub fn send_spontaneous_payment(
&self, amount_msat: u64, node_id: &str,
&self, amount_msat: u64, node_id: &PublicKey,
) -> Result<PaymentHash, Error> {
if self.running.read().unwrap().is_none() {
return Err(Error::NotRunning);
}

let pubkey = hex_utils::to_compressed_pubkey(node_id).ok_or(Error::PeerInfoParseFailed)?;

let payment_preimage = PaymentPreimage(self.keys_manager.get_secure_random_bytes());
let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0).into_inner());

let route_params = RouteParameters {
payment_params: PaymentParameters::from_node_id(
pubkey,
*node_id,
self.config.default_cltv_expiry_delta,
),
final_value_msat: amount_msat,
Expand DownExpand Up@@ -1330,15 +1397,15 @@ async fn do_connect_peer(
pubkey: PublicKey, peer_addr: SocketAddr, peer_manager: Arc<PeerManager>,
logger: Arc<FilesystemLogger>,
) -> Result<(), Error> {
log_info!(logger, "connecting to peer: {}@{}", pubkey, peer_addr);
log_info!(logger, "Connecting to peer: {}@{}", pubkey, peer_addr);
match lightning_net_tokio::connect_outbound(Arc::clone(&peer_manager), pubkey, peer_addr).await
{
Some(connection_closed_future) => {
let mut connection_closed_future = Box::pin(connection_closed_future);
loop {
match futures::poll!(&mut connection_closed_future) {
std::task::Poll::Ready(_) => {
log_info!(logger, "peer connection closed: {}@{}", pubkey, peer_addr);
log_info!(logger, "Peer connection closed: {}@{}", pubkey, peer_addr);
return Err(Error::ConnectionFailed);
}
std::task::Poll::Pending => {}
Expand All@@ -1351,7 +1418,7 @@ async fn do_connect_peer(
}
}
None => {
log_error!(logger, "failed to connect to peer: {}@{}", pubkey, peer_addr);
log_error!(logger, "Failed to connect to peer: {}@{}", pubkey, peer_addr);
Err(Error::ConnectionFailed)
}
}
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Unify interface types and expose `connect` / `disconnect` methods by tnull · Pull Request #52 · lightningdevkit/ldk-node · GitHub
Skip to content
Merged
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: 6 additions & 3 deletions README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,6 +10,7 @@ The primary abstraction of the library is the `Node`, which can be retrieved by
```rust
use ldk_node::Builder;
use ldk_node::lightning_invoice::Invoice;
use ldk_node::bitcoin::secp256k1::PublicKey;
use std::str::FromStr;

fn main() {
Expand All@@ -23,13 +24,15 @@ fn main() {
let _funding_address = node.new_funding_address();

// .. fund address ..

node.sync_wallets().unwrap();

node.connect_open_channel("NODE_ID@PEER_ADDR:PORT", 10000, None, false).unwrap();
let node_id = PublicKey::from_str("NODE_ID").unwrap();
let node_addr = "IP_ADDR:PORT".parse().unwrap();
node.connect_open_channel(node_id, node_addr, 10000, None, false).unwrap();

let invoice = Invoice::from_str("INVOICE_STR").unwrap();
node.send_payment(invoice).unwrap();
node.send_payment(&invoice).unwrap();

node.stop().unwrap();
}
Expand Down
15 changes: 0 additions & 15 deletions src/hex_utils.rs
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
use bitcoin::secp256k1::PublicKey;
use std::fmt::Write;

pub fn to_vec(hex: &str) -> Option<Vec<u8>> {
Expand DownExpand Up@@ -30,17 +29,3 @@ pub fn to_string(value: &[u8]) -> String {
}
res
}

pub fn to_compressed_pubkey(hex: &str) -> Option<PublicKey> {
if hex.len() != 33 * 2 {
return None;
}
let data = match to_vec(&hex[0..33 * 2]) {
Some(bytes) => bytes,
None => return None,
};
match PublicKey::from_slice(&data) {
Ok(pk) => Some(pk),
Err(_) => None,
}
}
125 changes: 96 additions & 29 deletions src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -28,6 +28,7 @@
//! ```no_run
//! use ldk_node::Builder;
//! use ldk_node::lightning_invoice::Invoice;
//! use ldk_node::bitcoin::secp256k1::PublicKey;
//! use std::str::FromStr;
//!
//! fn main() {
Expand All@@ -44,10 +45,12 @@
//!
//! node.sync_wallets().unwrap();
//!
//! node.connect_open_channel("NODE_ID@PEER_ADDR:PORT", 10000, None, false).unwrap();
//! let node_id = PublicKey::from_str("NODE_ID").unwrap();
//! let node_addr = "IP_ADDR:PORT".parse().unwrap();
//! node.connect_open_channel(node_id, node_addr, 10000, None, false).unwrap();
//!
//! let invoice = Invoice::from_str("INVOICE_STR").unwrap();
//! node.send_payment(invoice).unwrap();
//! node.send_payment(&invoice).unwrap();
//!
//! node.stop().unwrap();
//! }
Expand All@@ -60,8 +63,8 @@
//! [`send_payment`]: Node::send_payment
//!
#![deny(missing_docs)]
#![deny(broken_intra_doc_links)]
#![deny(private_intra_doc_links)]
#![deny(rustdoc::broken_intra_doc_links)]
#![deny(rustdoc::private_intra_doc_links)]
#![allow(bare_trait_objects)]
#![allow(ellipsis_inclusive_range_patterns)]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
Expand DownExpand Up@@ -100,9 +103,9 @@ use logger::{log_error, log_info, FilesystemLogger, Logger};

use lightning::chain::keysinterface::EntropySource;
use lightning::chain::{chainmonitor, BestBlock, Confirm, Watch};
use lightning::ln::channelmanager::{self, RecipientOnionFields};
use lightning::ln::channelmanager::{
ChainParameters, ChannelDetails, ChannelManagerReadArgs, PaymentId, Retry,
self, ChainParameters, ChannelDetails, ChannelManagerReadArgs, PaymentId, RecipientOnionFields,
Retry,
};
use lightning::ln::peer_handler::{IgnoringMessageHandler, MessageHandler};
use lightning::ln::{PaymentHash, PaymentPreimage};
Expand DownExpand Up@@ -133,7 +136,7 @@ use bitcoin::BlockHash;

use rand::Rng;

use std::convert::{TryFrom, TryInto};
use std::convert::TryInto;
use std::default::Default;
use std::fs;
use std::net::SocketAddr;
Expand DownExpand Up@@ -167,7 +170,7 @@ pub struct Config {
/// The used Bitcoin network.
pub network: bitcoin::Network,
/// The IP address and TCP port the node will listen on.
pub listening_address: Option<String>,
pub listening_address: Option<SocketAddr>,
/// The default CLTV expiry delta to be used for payments.
pub default_cltv_expiry_delta: u32,
}
Expand All@@ -178,7 +181,7 @@ impl Default for Config {
storage_dir_path: "/tmp/ldk_node/".to_string(),
esplora_server_url: "http://localhost:3002".to_string(),
network: bitcoin::Network::Regtest,
listening_address: Some("0.0.0.0:9735".to_string()),
listening_address: Some("0.0.0.0:9735".parse().unwrap()),
default_cltv_expiry_delta: 144,
}
}
Expand DownExpand Up@@ -262,9 +265,8 @@ impl Builder {

/// Sets the IP address and TCP port on which [`Node`] will listen for incoming network connections.
///
/// Format: `ADDR:PORT`
/// Default: `0.0.0.0:9735`
pub fn set_listening_address(&mut self, listening_address: String) -> &mut Self {
pub fn set_listening_address(&mut self, listening_address: SocketAddr) -> &mut Self {
self.config.listening_address = Some(listening_address);
self
}
Expand DownExpand Up@@ -819,9 +821,9 @@ impl Node {
self.channel_manager.get_our_node_id()
}

/// Returns our own listening address and port.
pub fn listening_address(&self) -> Option<String> {
self.config.listening_address.clone()
/// Returns our own listening address.
pub fn listening_address(&self) -> Option<&SocketAddr> {
self.config.listening_address.as_ref()
}

/// Retrieve a new on-chain/funding address.
Expand All@@ -841,7 +843,74 @@ impl Node {
self.channel_manager.list_channels()
}

/// Connect to a node and opens a new channel.
/// Connect to a node on the peer-to-peer network.
///
/// If `permanently` is set to `true`, we'll remember the peer and reconnect to it on restart.
pub fn connect(
&self, node_id: PublicKey, address: SocketAddr, permanently: bool,
) -> Result<(), Error> {
let runtime_lock = self.running.read().unwrap();
if runtime_lock.is_none() {
return Err(Error::NotRunning);
}

let runtime = runtime_lock.as_ref().unwrap();

let peer_info = PeerInfo { pubkey: node_id, address };

let con_peer_pubkey = peer_info.pubkey;
let con_peer_addr = peer_info.address;
let con_success = Arc::new(AtomicBool::new(false));
let con_success_cloned = Arc::clone(&con_success);
let con_logger = Arc::clone(&self.logger);
let con_pm = Arc::clone(&self.peer_manager);

tokio::task::block_in_place(move || {
runtime.tokio_runtime.block_on(async move {
let res =
connect_peer_if_necessary(con_peer_pubkey, con_peer_addr, con_pm, con_logger)
.await;
con_success_cloned.store(res.is_ok(), Ordering::Release);
})
});

if !con_success.load(Ordering::Acquire) {
return Err(Error::ConnectionFailed);
}

log_info!(self.logger, "Connected to peer {}@{}. ", peer_info.pubkey, peer_info.address,);

if permanently {
self.peer_store.add_peer(peer_info)?;
}

Ok(())
}

/// Disconnects the peer with the given node id.
///
/// Will also remove the peer from the peer store, i.e., after this has been called we won't
/// try to reconnect on restart.
pub fn disconnect(&self, counterparty_node_id: &PublicKey) -> Result<(), Error> {
let runtime_lock = self.running.read().unwrap();
if runtime_lock.is_none() {
return Err(Error::NotRunning);
}

log_info!(self.logger, "Disconnecting peer {}..", counterparty_node_id);

match self.peer_store.remove_peer(&counterparty_node_id) {
Ok(()) => {}
Err(e) => {
log_error!(self.logger, "Failed to remove peer {}: {}", counterparty_node_id, e)
}
}

self.peer_manager.disconnect_by_node_id(*counterparty_node_id);
Ok(())
}

/// Connect to a node and open a new channel. Disconnects and re-connects are handled automatically
///
/// Disconnects and reconnects are handled automatically.
///
Expand All@@ -851,7 +920,7 @@ impl Node {
///
/// Returns a temporary channel id.
pub fn connect_open_channel(
&self, node_pubkey_and_address: &str, channel_amount_sats: u64,
&self, node_id: PublicKey, address: SocketAddr, channel_amount_sats: u64,
push_to_counterparty_msat: Option<u64>, announce_channel: bool,
) -> Result<(), Error> {
let runtime_lock = self.running.read().unwrap();
Expand All@@ -867,10 +936,10 @@ impl Node {
return Err(Error::InsufficientFunds);
}

let peer_info = PeerInfo::try_from(node_pubkey_and_address.to_string())?;
let peer_info = PeerInfo { pubkey: node_id, address };

let con_peer_pubkey = peer_info.pubkey.clone();
let con_peer_addr = peer_info.address.clone();
let con_peer_pubkey = peer_info.pubkey;
let con_peer_addr = peer_info.address;
let con_success = Arc::new(AtomicBool::new(false));
let con_success_cloned = Arc::clone(&con_success);
let con_logger = Arc::clone(&self.logger);
Expand DownExpand Up@@ -913,12 +982,12 @@ impl Node {
Some(user_config),
) {
Ok(_) => {
self.peer_store.add_peer(peer_info.clone())?;
log_info!(
self.logger,
"Initiated channel creation with peer {}. ",
peer_info.pubkey
);
self.peer_store.add_peer(peer_info)?;
Ok(())
}
Err(e) => {
Expand DownExpand Up@@ -1005,7 +1074,7 @@ impl Node {
}

/// Send a payement given an invoice.
pub fn send_payment(&self, invoice: Invoice) -> Result<PaymentHash, Error> {
pub fn send_payment(&self, invoice: &Invoice) -> Result<PaymentHash, Error> {
if self.running.read().unwrap().is_none() {
return Err(Error::NotRunning);
}
Expand DownExpand Up@@ -1070,7 +1139,7 @@ impl Node {
/// This can be used to pay a so-called "zero-amount" invoice, i.e., an invoice that leaves the
/// amount paid to be determined by the user.
pub fn send_payment_using_amount(
&self, invoice: Invoice, amount_msat: u64,
&self, invoice: &Invoice, amount_msat: u64,
) -> Result<PaymentHash, Error> {
if self.running.read().unwrap().is_none() {
return Err(Error::NotRunning);
Expand DownExpand Up@@ -1158,20 +1227,18 @@ impl Node {

/// Send a spontaneous, aka. "keysend", payment
pub fn send_spontaneous_payment(
&self, amount_msat: u64, node_id: &str,
&self, amount_msat: u64, node_id: &PublicKey,
) -> Result<PaymentHash, Error> {
if self.running.read().unwrap().is_none() {
return Err(Error::NotRunning);
}

let pubkey = hex_utils::to_compressed_pubkey(node_id).ok_or(Error::PeerInfoParseFailed)?;

let payment_preimage = PaymentPreimage(self.keys_manager.get_secure_random_bytes());
let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0).into_inner());

let route_params = RouteParameters {
payment_params: PaymentParameters::from_node_id(
pubkey,
*node_id,
self.config.default_cltv_expiry_delta,
),
final_value_msat: amount_msat,
Expand DownExpand Up@@ -1330,15 +1397,15 @@ async fn do_connect_peer(
pubkey: PublicKey, peer_addr: SocketAddr, peer_manager: Arc<PeerManager>,
logger: Arc<FilesystemLogger>,
) -> Result<(), Error> {
log_info!(logger, "connecting to peer: {}@{}", pubkey, peer_addr);
log_info!(logger, "Connecting to peer: {}@{}", pubkey, peer_addr);
match lightning_net_tokio::connect_outbound(Arc::clone(&peer_manager), pubkey, peer_addr).await
{
Some(connection_closed_future) => {
let mut connection_closed_future = Box::pin(connection_closed_future);
loop {
match futures::poll!(&mut connection_closed_future) {
std::task::Poll::Ready(_) => {
log_info!(logger, "peer connection closed: {}@{}", pubkey, peer_addr);
log_info!(logger, "Peer connection closed: {}@{}", pubkey, peer_addr);
return Err(Error::ConnectionFailed);
}
std::task::Poll::Pending => {}
Expand All@@ -1351,7 +1418,7 @@ async fn do_connect_peer(
}
}
None => {
log_error!(logger, "failed to connect to peer: {}@{}", pubkey, peer_addr);
log_error!(logger, "Failed to connect to peer: {}@{}", pubkey, peer_addr);
Err(Error::ConnectionFailed)
}
}
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Unify interface types and expose `connect` / `disconnect` methods by tnull · Pull Request #52 · lightningdevkit/ldk-node · GitHub
Skip to content
Merged
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: 6 additions & 3 deletions README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,6 +10,7 @@ The primary abstraction of the library is the `Node`, which can be retrieved by
```rust
use ldk_node::Builder;
use ldk_node::lightning_invoice::Invoice;
use ldk_node::bitcoin::secp256k1::PublicKey;
use std::str::FromStr;

fn main() {
Expand All@@ -23,13 +24,15 @@ fn main() {
let _funding_address = node.new_funding_address();

// .. fund address ..

node.sync_wallets().unwrap();

node.connect_open_channel("NODE_ID@PEER_ADDR:PORT", 10000, None, false).unwrap();
let node_id = PublicKey::from_str("NODE_ID").unwrap();
let node_addr = "IP_ADDR:PORT".parse().unwrap();
node.connect_open_channel(node_id, node_addr, 10000, None, false).unwrap();

let invoice = Invoice::from_str("INVOICE_STR").unwrap();
node.send_payment(invoice).unwrap();
node.send_payment(&invoice).unwrap();

node.stop().unwrap();
}
Expand Down
15 changes: 0 additions & 15 deletions src/hex_utils.rs
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
use bitcoin::secp256k1::PublicKey;
use std::fmt::Write;

pub fn to_vec(hex: &str) -> Option<Vec<u8>> {
Expand DownExpand Up@@ -30,17 +29,3 @@ pub fn to_string(value: &[u8]) -> String {
}
res
}

pub fn to_compressed_pubkey(hex: &str) -> Option<PublicKey> {
if hex.len() != 33 * 2 {
return None;
}
let data = match to_vec(&hex[0..33 * 2]) {
Some(bytes) => bytes,
None => return None,
};
match PublicKey::from_slice(&data) {
Ok(pk) => Some(pk),
Err(_) => None,
}
}
125 changes: 96 additions & 29 deletions src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -28,6 +28,7 @@
//! ```no_run
//! use ldk_node::Builder;
//! use ldk_node::lightning_invoice::Invoice;
//! use ldk_node::bitcoin::secp256k1::PublicKey;
//! use std::str::FromStr;
//!
//! fn main() {
Expand All@@ -44,10 +45,12 @@
//!
//! node.sync_wallets().unwrap();
//!
//! node.connect_open_channel("NODE_ID@PEER_ADDR:PORT", 10000, None, false).unwrap();
//! let node_id = PublicKey::from_str("NODE_ID").unwrap();
//! let node_addr = "IP_ADDR:PORT".parse().unwrap();
//! node.connect_open_channel(node_id, node_addr, 10000, None, false).unwrap();
//!
//! let invoice = Invoice::from_str("INVOICE_STR").unwrap();
//! node.send_payment(invoice).unwrap();
//! node.send_payment(&invoice).unwrap();
//!
//! node.stop().unwrap();
//! }
Expand All@@ -60,8 +63,8 @@
//! [`send_payment`]: Node::send_payment
//!
#![deny(missing_docs)]
#![deny(broken_intra_doc_links)]
#![deny(private_intra_doc_links)]
#![deny(rustdoc::broken_intra_doc_links)]
#![deny(rustdoc::private_intra_doc_links)]
#![allow(bare_trait_objects)]
#![allow(ellipsis_inclusive_range_patterns)]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
Expand DownExpand Up@@ -100,9 +103,9 @@ use logger::{log_error, log_info, FilesystemLogger, Logger};

use lightning::chain::keysinterface::EntropySource;
use lightning::chain::{chainmonitor, BestBlock, Confirm, Watch};
use lightning::ln::channelmanager::{self, RecipientOnionFields};
use lightning::ln::channelmanager::{
ChainParameters, ChannelDetails, ChannelManagerReadArgs, PaymentId, Retry,
self, ChainParameters, ChannelDetails, ChannelManagerReadArgs, PaymentId, RecipientOnionFields,
Retry,
};
use lightning::ln::peer_handler::{IgnoringMessageHandler, MessageHandler};
use lightning::ln::{PaymentHash, PaymentPreimage};
Expand DownExpand Up@@ -133,7 +136,7 @@ use bitcoin::BlockHash;

use rand::Rng;

use std::convert::{TryFrom, TryInto};
use std::convert::TryInto;
use std::default::Default;
use std::fs;
use std::net::SocketAddr;
Expand DownExpand Up@@ -167,7 +170,7 @@ pub struct Config {
/// The used Bitcoin network.
pub network: bitcoin::Network,
/// The IP address and TCP port the node will listen on.
pub listening_address: Option<String>,
pub listening_address: Option<SocketAddr>,
/// The default CLTV expiry delta to be used for payments.
pub default_cltv_expiry_delta: u32,
}
Expand All@@ -178,7 +181,7 @@ impl Default for Config {
storage_dir_path: "/tmp/ldk_node/".to_string(),
esplora_server_url: "http://localhost:3002".to_string(),
network: bitcoin::Network::Regtest,
listening_address: Some("0.0.0.0:9735".to_string()),
listening_address: Some("0.0.0.0:9735".parse().unwrap()),
default_cltv_expiry_delta: 144,
}
}
Expand DownExpand Up@@ -262,9 +265,8 @@ impl Builder {

/// Sets the IP address and TCP port on which [`Node`] will listen for incoming network connections.
///
/// Format: `ADDR:PORT`
/// Default: `0.0.0.0:9735`
pub fn set_listening_address(&mut self, listening_address: String) -> &mut Self {
pub fn set_listening_address(&mut self, listening_address: SocketAddr) -> &mut Self {
self.config.listening_address = Some(listening_address);
self
}
Expand DownExpand Up@@ -819,9 +821,9 @@ impl Node {
self.channel_manager.get_our_node_id()
}

/// Returns our own listening address and port.
pub fn listening_address(&self) -> Option<String> {
self.config.listening_address.clone()
/// Returns our own listening address.
pub fn listening_address(&self) -> Option<&SocketAddr> {
self.config.listening_address.as_ref()
}

/// Retrieve a new on-chain/funding address.
Expand All@@ -841,7 +843,74 @@ impl Node {
self.channel_manager.list_channels()
}

/// Connect to a node and opens a new channel.
/// Connect to a node on the peer-to-peer network.
///
/// If `permanently` is set to `true`, we'll remember the peer and reconnect to it on restart.
pub fn connect(
&self, node_id: PublicKey, address: SocketAddr, permanently: bool,
) -> Result<(), Error> {
let runtime_lock = self.running.read().unwrap();
if runtime_lock.is_none() {
return Err(Error::NotRunning);
}

let runtime = runtime_lock.as_ref().unwrap();

let peer_info = PeerInfo { pubkey: node_id, address };

let con_peer_pubkey = peer_info.pubkey;
let con_peer_addr = peer_info.address;
let con_success = Arc::new(AtomicBool::new(false));
let con_success_cloned = Arc::clone(&con_success);
let con_logger = Arc::clone(&self.logger);
let con_pm = Arc::clone(&self.peer_manager);

tokio::task::block_in_place(move || {
runtime.tokio_runtime.block_on(async move {
let res =
connect_peer_if_necessary(con_peer_pubkey, con_peer_addr, con_pm, con_logger)
.await;
con_success_cloned.store(res.is_ok(), Ordering::Release);
})
});

if !con_success.load(Ordering::Acquire) {
return Err(Error::ConnectionFailed);
}

log_info!(self.logger, "Connected to peer {}@{}. ", peer_info.pubkey, peer_info.address,);

if permanently {
self.peer_store.add_peer(peer_info)?;
}

Ok(())
}

/// Disconnects the peer with the given node id.
///
/// Will also remove the peer from the peer store, i.e., after this has been called we won't
/// try to reconnect on restart.
pub fn disconnect(&self, counterparty_node_id: &PublicKey) -> Result<(), Error> {
let runtime_lock = self.running.read().unwrap();
if runtime_lock.is_none() {
return Err(Error::NotRunning);
}

log_info!(self.logger, "Disconnecting peer {}..", counterparty_node_id);

match self.peer_store.remove_peer(&counterparty_node_id) {
Ok(()) => {}
Err(e) => {
log_error!(self.logger, "Failed to remove peer {}: {}", counterparty_node_id, e)
}
}

self.peer_manager.disconnect_by_node_id(*counterparty_node_id);
Ok(())
}

/// Connect to a node and open a new channel. Disconnects and re-connects are handled automatically
///
/// Disconnects and reconnects are handled automatically.
///
Expand All@@ -851,7 +920,7 @@ impl Node {
///
/// Returns a temporary channel id.
pub fn connect_open_channel(
&self, node_pubkey_and_address: &str, channel_amount_sats: u64,
&self, node_id: PublicKey, address: SocketAddr, channel_amount_sats: u64,
push_to_counterparty_msat: Option<u64>, announce_channel: bool,
) -> Result<(), Error> {
let runtime_lock = self.running.read().unwrap();
Expand All@@ -867,10 +936,10 @@ impl Node {
return Err(Error::InsufficientFunds);
}

let peer_info = PeerInfo::try_from(node_pubkey_and_address.to_string())?;
let peer_info = PeerInfo { pubkey: node_id, address };

let con_peer_pubkey = peer_info.pubkey.clone();
let con_peer_addr = peer_info.address.clone();
let con_peer_pubkey = peer_info.pubkey;
let con_peer_addr = peer_info.address;
let con_success = Arc::new(AtomicBool::new(false));
let con_success_cloned = Arc::clone(&con_success);
let con_logger = Arc::clone(&self.logger);
Expand DownExpand Up@@ -913,12 +982,12 @@ impl Node {
Some(user_config),
) {
Ok(_) => {
self.peer_store.add_peer(peer_info.clone())?;
log_info!(
self.logger,
"Initiated channel creation with peer {}. ",
peer_info.pubkey
);
self.peer_store.add_peer(peer_info)?;
Ok(())
}
Err(e) => {
Expand DownExpand Up@@ -1005,7 +1074,7 @@ impl Node {
}

/// Send a payement given an invoice.
pub fn send_payment(&self, invoice: Invoice) -> Result<PaymentHash, Error> {
pub fn send_payment(&self, invoice: &Invoice) -> Result<PaymentHash, Error> {
if self.running.read().unwrap().is_none() {
return Err(Error::NotRunning);
}
Expand DownExpand Up@@ -1070,7 +1139,7 @@ impl Node {
/// This can be used to pay a so-called "zero-amount" invoice, i.e., an invoice that leaves the
/// amount paid to be determined by the user.
pub fn send_payment_using_amount(
&self, invoice: Invoice, amount_msat: u64,
&self, invoice: &Invoice, amount_msat: u64,
) -> Result<PaymentHash, Error> {
if self.running.read().unwrap().is_none() {
return Err(Error::NotRunning);
Expand DownExpand Up@@ -1158,20 +1227,18 @@ impl Node {

/// Send a spontaneous, aka. "keysend", payment
pub fn send_spontaneous_payment(
&self, amount_msat: u64, node_id: &str,
&self, amount_msat: u64, node_id: &PublicKey,
) -> Result<PaymentHash, Error> {
if self.running.read().unwrap().is_none() {
return Err(Error::NotRunning);
}

let pubkey = hex_utils::to_compressed_pubkey(node_id).ok_or(Error::PeerInfoParseFailed)?;

let payment_preimage = PaymentPreimage(self.keys_manager.get_secure_random_bytes());
let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0).into_inner());

let route_params = RouteParameters {
payment_params: PaymentParameters::from_node_id(
pubkey,
*node_id,
self.config.default_cltv_expiry_delta,
),
final_value_msat: amount_msat,
Expand DownExpand Up@@ -1330,15 +1397,15 @@ async fn do_connect_peer(
pubkey: PublicKey, peer_addr: SocketAddr, peer_manager: Arc<PeerManager>,
logger: Arc<FilesystemLogger>,
) -> Result<(), Error> {
log_info!(logger, "connecting to peer: {}@{}", pubkey, peer_addr);
log_info!(logger, "Connecting to peer: {}@{}", pubkey, peer_addr);
match lightning_net_tokio::connect_outbound(Arc::clone(&peer_manager), pubkey, peer_addr).await
{
Some(connection_closed_future) => {
let mut connection_closed_future = Box::pin(connection_closed_future);
loop {
match futures::poll!(&mut connection_closed_future) {
std::task::Poll::Ready(_) => {
log_info!(logger, "peer connection closed: {}@{}", pubkey, peer_addr);
log_info!(logger, "Peer connection closed: {}@{}", pubkey, peer_addr);
return Err(Error::ConnectionFailed);
}
std::task::Poll::Pending => {}
Expand All@@ -1351,7 +1418,7 @@ async fn do_connect_peer(
}
}
None => {
log_error!(logger, "failed to connect to peer: {}@{}", pubkey, peer_addr);
log_error!(logger, "Failed to connect to peer: {}@{}", pubkey, peer_addr);
Err(Error::ConnectionFailed)
}
}
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Unify interface types and expose `connect` / `disconnect` methods by tnull · Pull Request #52 · lightningdevkit/ldk-node · GitHub
Skip to content
Merged
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: 6 additions & 3 deletions README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,6 +10,7 @@ The primary abstraction of the library is the `Node`, which can be retrieved by
```rust
use ldk_node::Builder;
use ldk_node::lightning_invoice::Invoice;
use ldk_node::bitcoin::secp256k1::PublicKey;
use std::str::FromStr;

fn main() {
Expand All@@ -23,13 +24,15 @@ fn main() {
let _funding_address = node.new_funding_address();

// .. fund address ..

node.sync_wallets().unwrap();

node.connect_open_channel("NODE_ID@PEER_ADDR:PORT", 10000, None, false).unwrap();
let node_id = PublicKey::from_str("NODE_ID").unwrap();
let node_addr = "IP_ADDR:PORT".parse().unwrap();
node.connect_open_channel(node_id, node_addr, 10000, None, false).unwrap();

let invoice = Invoice::from_str("INVOICE_STR").unwrap();
node.send_payment(invoice).unwrap();
node.send_payment(&invoice).unwrap();

node.stop().unwrap();
}
Expand Down
15 changes: 0 additions & 15 deletions src/hex_utils.rs
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
use bitcoin::secp256k1::PublicKey;
use std::fmt::Write;

pub fn to_vec(hex: &str) -> Option<Vec<u8>> {
Expand DownExpand Up@@ -30,17 +29,3 @@ pub fn to_string(value: &[u8]) -> String {
}
res
}

pub fn to_compressed_pubkey(hex: &str) -> Option<PublicKey> {
if hex.len() != 33 * 2 {
return None;
}
let data = match to_vec(&hex[0..33 * 2]) {
Some(bytes) => bytes,
None => return None,
};
match PublicKey::from_slice(&data) {
Ok(pk) => Some(pk),
Err(_) => None,
}
}
125 changes: 96 additions & 29 deletions src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -28,6 +28,7 @@
//! ```no_run
//! use ldk_node::Builder;
//! use ldk_node::lightning_invoice::Invoice;
//! use ldk_node::bitcoin::secp256k1::PublicKey;
//! use std::str::FromStr;
//!
//! fn main() {
Expand All@@ -44,10 +45,12 @@
//!
//! node.sync_wallets().unwrap();
//!
//! node.connect_open_channel("NODE_ID@PEER_ADDR:PORT", 10000, None, false).unwrap();
//! let node_id = PublicKey::from_str("NODE_ID").unwrap();
//! let node_addr = "IP_ADDR:PORT".parse().unwrap();
//! node.connect_open_channel(node_id, node_addr, 10000, None, false).unwrap();
//!
//! let invoice = Invoice::from_str("INVOICE_STR").unwrap();
//! node.send_payment(invoice).unwrap();
//! node.send_payment(&invoice).unwrap();
//!
//! node.stop().unwrap();
//! }
Expand All@@ -60,8 +63,8 @@
//! [`send_payment`]: Node::send_payment
//!
#![deny(missing_docs)]
#![deny(broken_intra_doc_links)]
#![deny(private_intra_doc_links)]
#![deny(rustdoc::broken_intra_doc_links)]
#![deny(rustdoc::private_intra_doc_links)]
#![allow(bare_trait_objects)]
#![allow(ellipsis_inclusive_range_patterns)]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
Expand DownExpand Up@@ -100,9 +103,9 @@ use logger::{log_error, log_info, FilesystemLogger, Logger};

use lightning::chain::keysinterface::EntropySource;
use lightning::chain::{chainmonitor, BestBlock, Confirm, Watch};
use lightning::ln::channelmanager::{self, RecipientOnionFields};
use lightning::ln::channelmanager::{
ChainParameters, ChannelDetails, ChannelManagerReadArgs, PaymentId, Retry,
self, ChainParameters, ChannelDetails, ChannelManagerReadArgs, PaymentId, RecipientOnionFields,
Retry,
};
use lightning::ln::peer_handler::{IgnoringMessageHandler, MessageHandler};
use lightning::ln::{PaymentHash, PaymentPreimage};
Expand DownExpand Up@@ -133,7 +136,7 @@ use bitcoin::BlockHash;

use rand::Rng;

use std::convert::{TryFrom, TryInto};
use std::convert::TryInto;
use std::default::Default;
use std::fs;
use std::net::SocketAddr;
Expand DownExpand Up@@ -167,7 +170,7 @@ pub struct Config {
/// The used Bitcoin network.
pub network: bitcoin::Network,
/// The IP address and TCP port the node will listen on.
pub listening_address: Option<String>,
pub listening_address: Option<SocketAddr>,
/// The default CLTV expiry delta to be used for payments.
pub default_cltv_expiry_delta: u32,
}
Expand All@@ -178,7 +181,7 @@ impl Default for Config {
storage_dir_path: "/tmp/ldk_node/".to_string(),
esplora_server_url: "http://localhost:3002".to_string(),
network: bitcoin::Network::Regtest,
listening_address: Some("0.0.0.0:9735".to_string()),
listening_address: Some("0.0.0.0:9735".parse().unwrap()),
default_cltv_expiry_delta: 144,
}
}
Expand DownExpand Up@@ -262,9 +265,8 @@ impl Builder {

/// Sets the IP address and TCP port on which [`Node`] will listen for incoming network connections.
///
/// Format: `ADDR:PORT`
/// Default: `0.0.0.0:9735`
pub fn set_listening_address(&mut self, listening_address: String) -> &mut Self {
pub fn set_listening_address(&mut self, listening_address: SocketAddr) -> &mut Self {
self.config.listening_address = Some(listening_address);
self
}
Expand DownExpand Up@@ -819,9 +821,9 @@ impl Node {
self.channel_manager.get_our_node_id()
}

/// Returns our own listening address and port.
pub fn listening_address(&self) -> Option<String> {
self.config.listening_address.clone()
/// Returns our own listening address.
pub fn listening_address(&self) -> Option<&SocketAddr> {
self.config.listening_address.as_ref()
}

/// Retrieve a new on-chain/funding address.
Expand All@@ -841,7 +843,74 @@ impl Node {
self.channel_manager.list_channels()
}

/// Connect to a node and opens a new channel.
/// Connect to a node on the peer-to-peer network.
///
/// If `permanently` is set to `true`, we'll remember the peer and reconnect to it on restart.
pub fn connect(
&self, node_id: PublicKey, address: SocketAddr, permanently: bool,
) -> Result<(), Error> {
let runtime_lock = self.running.read().unwrap();
if runtime_lock.is_none() {
return Err(Error::NotRunning);
}

let runtime = runtime_lock.as_ref().unwrap();

let peer_info = PeerInfo { pubkey: node_id, address };

let con_peer_pubkey = peer_info.pubkey;
let con_peer_addr = peer_info.address;
let con_success = Arc::new(AtomicBool::new(false));
let con_success_cloned = Arc::clone(&con_success);
let con_logger = Arc::clone(&self.logger);
let con_pm = Arc::clone(&self.peer_manager);

tokio::task::block_in_place(move || {
runtime.tokio_runtime.block_on(async move {
let res =
connect_peer_if_necessary(con_peer_pubkey, con_peer_addr, con_pm, con_logger)
.await;
con_success_cloned.store(res.is_ok(), Ordering::Release);
})
});

if !con_success.load(Ordering::Acquire) {
return Err(Error::ConnectionFailed);
}

log_info!(self.logger, "Connected to peer {}@{}. ", peer_info.pubkey, peer_info.address,);

if permanently {
self.peer_store.add_peer(peer_info)?;
}

Ok(())
}

/// Disconnects the peer with the given node id.
///
/// Will also remove the peer from the peer store, i.e., after this has been called we won't
/// try to reconnect on restart.
pub fn disconnect(&self, counterparty_node_id: &PublicKey) -> Result<(), Error> {
let runtime_lock = self.running.read().unwrap();
if runtime_lock.is_none() {
return Err(Error::NotRunning);
}

log_info!(self.logger, "Disconnecting peer {}..", counterparty_node_id);

match self.peer_store.remove_peer(&counterparty_node_id) {
Ok(()) => {}
Err(e) => {
log_error!(self.logger, "Failed to remove peer {}: {}", counterparty_node_id, e)
}
}

self.peer_manager.disconnect_by_node_id(*counterparty_node_id);
Ok(())
}

/// Connect to a node and open a new channel. Disconnects and re-connects are handled automatically
///
/// Disconnects and reconnects are handled automatically.
///
Expand All@@ -851,7 +920,7 @@ impl Node {
///
/// Returns a temporary channel id.
pub fn connect_open_channel(
&self, node_pubkey_and_address: &str, channel_amount_sats: u64,
&self, node_id: PublicKey, address: SocketAddr, channel_amount_sats: u64,
push_to_counterparty_msat: Option<u64>, announce_channel: bool,
) -> Result<(), Error> {
let runtime_lock = self.running.read().unwrap();
Expand All@@ -867,10 +936,10 @@ impl Node {
return Err(Error::InsufficientFunds);
}

let peer_info = PeerInfo::try_from(node_pubkey_and_address.to_string())?;
let peer_info = PeerInfo { pubkey: node_id, address };

let con_peer_pubkey = peer_info.pubkey.clone();
let con_peer_addr = peer_info.address.clone();
let con_peer_pubkey = peer_info.pubkey;
let con_peer_addr = peer_info.address;
let con_success = Arc::new(AtomicBool::new(false));
let con_success_cloned = Arc::clone(&con_success);
let con_logger = Arc::clone(&self.logger);
Expand DownExpand Up@@ -913,12 +982,12 @@ impl Node {
Some(user_config),
) {
Ok(_) => {
self.peer_store.add_peer(peer_info.clone())?;
log_info!(
self.logger,
"Initiated channel creation with peer {}. ",
peer_info.pubkey
);
self.peer_store.add_peer(peer_info)?;
Ok(())
}
Err(e) => {
Expand DownExpand Up@@ -1005,7 +1074,7 @@ impl Node {
}

/// Send a payement given an invoice.
pub fn send_payment(&self, invoice: Invoice) -> Result<PaymentHash, Error> {
pub fn send_payment(&self, invoice: &Invoice) -> Result<PaymentHash, Error> {
if self.running.read().unwrap().is_none() {
return Err(Error::NotRunning);
}
Expand DownExpand Up@@ -1070,7 +1139,7 @@ impl Node {
/// This can be used to pay a so-called "zero-amount" invoice, i.e., an invoice that leaves the
/// amount paid to be determined by the user.
pub fn send_payment_using_amount(
&self, invoice: Invoice, amount_msat: u64,
&self, invoice: &Invoice, amount_msat: u64,
) -> Result<PaymentHash, Error> {
if self.running.read().unwrap().is_none() {
return Err(Error::NotRunning);
Expand DownExpand Up@@ -1158,20 +1227,18 @@ impl Node {

/// Send a spontaneous, aka. "keysend", payment
pub fn send_spontaneous_payment(
&self, amount_msat: u64, node_id: &str,
&self, amount_msat: u64, node_id: &PublicKey,
) -> Result<PaymentHash, Error> {
if self.running.read().unwrap().is_none() {
return Err(Error::NotRunning);
}

let pubkey = hex_utils::to_compressed_pubkey(node_id).ok_or(Error::PeerInfoParseFailed)?;

let payment_preimage = PaymentPreimage(self.keys_manager.get_secure_random_bytes());
let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0).into_inner());

let route_params = RouteParameters {
payment_params: PaymentParameters::from_node_id(
pubkey,
*node_id,
self.config.default_cltv_expiry_delta,
),
final_value_msat: amount_msat,
Expand DownExpand Up@@ -1330,15 +1397,15 @@ async fn do_connect_peer(
pubkey: PublicKey, peer_addr: SocketAddr, peer_manager: Arc<PeerManager>,
logger: Arc<FilesystemLogger>,
) -> Result<(), Error> {
log_info!(logger, "connecting to peer: {}@{}", pubkey, peer_addr);
log_info!(logger, "Connecting to peer: {}@{}", pubkey, peer_addr);
match lightning_net_tokio::connect_outbound(Arc::clone(&peer_manager), pubkey, peer_addr).await
{
Some(connection_closed_future) => {
let mut connection_closed_future = Box::pin(connection_closed_future);
loop {
match futures::poll!(&mut connection_closed_future) {
std::task::Poll::Ready(_) => {
log_info!(logger, "peer connection closed: {}@{}", pubkey, peer_addr);
log_info!(logger, "Peer connection closed: {}@{}", pubkey, peer_addr);
return Err(Error::ConnectionFailed);
}
std::task::Poll::Pending => {}
Expand All@@ -1351,7 +1418,7 @@ async fn do_connect_peer(
}
}
None => {
log_error!(logger, "failed to connect to peer: {}@{}", pubkey, peer_addr);
log_error!(logger, "Failed to connect to peer: {}@{}", pubkey, peer_addr);
Err(Error::ConnectionFailed)
}
}
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' Unify interface types and expose `connect` / `disconnect` methods by tnull · Pull Request #52 · lightningdevkit/ldk-node · GitHub
Skip to content
Merged
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: 6 additions & 3 deletions README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,6 +10,7 @@ The primary abstraction of the library is the `Node`, which can be retrieved by
```rust
use ldk_node::Builder;
use ldk_node::lightning_invoice::Invoice;
use ldk_node::bitcoin::secp256k1::PublicKey;
use std::str::FromStr;

fn main() {
Expand All@@ -23,13 +24,15 @@ fn main() {
let _funding_address = node.new_funding_address();

// .. fund address ..

node.sync_wallets().unwrap();

node.connect_open_channel("NODE_ID@PEER_ADDR:PORT", 10000, None, false).unwrap();
let node_id = PublicKey::from_str("NODE_ID").unwrap();
let node_addr = "IP_ADDR:PORT".parse().unwrap();
node.connect_open_channel(node_id, node_addr, 10000, None, false).unwrap();

let invoice = Invoice::from_str("INVOICE_STR").unwrap();
node.send_payment(invoice).unwrap();
node.send_payment(&invoice).unwrap();

node.stop().unwrap();
}
Expand Down
15 changes: 0 additions & 15 deletions src/hex_utils.rs
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
use bitcoin::secp256k1::PublicKey;
use std::fmt::Write;

pub fn to_vec(hex: &str) -> Option<Vec<u8>> {
Expand DownExpand Up@@ -30,17 +29,3 @@ pub fn to_string(value: &[u8]) -> String {
}
res
}

pub fn to_compressed_pubkey(hex: &str) -> Option<PublicKey> {
if hex.len() != 33 * 2 {
return None;
}
let data = match to_vec(&hex[0..33 * 2]) {
Some(bytes) => bytes,
None => return None,
};
match PublicKey::from_slice(&data) {
Ok(pk) => Some(pk),
Err(_) => None,
}
}
125 changes: 96 additions & 29 deletions src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -28,6 +28,7 @@
//! ```no_run
//! use ldk_node::Builder;
//! use ldk_node::lightning_invoice::Invoice;
//! use ldk_node::bitcoin::secp256k1::PublicKey;
//! use std::str::FromStr;
//!
//! fn main() {
Expand All@@ -44,10 +45,12 @@
//!
//! node.sync_wallets().unwrap();
//!
//! node.connect_open_channel("NODE_ID@PEER_ADDR:PORT", 10000, None, false).unwrap();
//! let node_id = PublicKey::from_str("NODE_ID").unwrap();
//! let node_addr = "IP_ADDR:PORT".parse().unwrap();
//! node.connect_open_channel(node_id, node_addr, 10000, None, false).unwrap();
//!
//! let invoice = Invoice::from_str("INVOICE_STR").unwrap();
//! node.send_payment(invoice).unwrap();
//! node.send_payment(&invoice).unwrap();
//!
//! node.stop().unwrap();
//! }
Expand All@@ -60,8 +63,8 @@
//! [`send_payment`]: Node::send_payment
//!
#![deny(missing_docs)]
#![deny(broken_intra_doc_links)]
#![deny(private_intra_doc_links)]
#![deny(rustdoc::broken_intra_doc_links)]
#![deny(rustdoc::private_intra_doc_links)]
#![allow(bare_trait_objects)]
#![allow(ellipsis_inclusive_range_patterns)]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
Expand DownExpand Up@@ -100,9 +103,9 @@ use logger::{log_error, log_info, FilesystemLogger, Logger};

use lightning::chain::keysinterface::EntropySource;
use lightning::chain::{chainmonitor, BestBlock, Confirm, Watch};
use lightning::ln::channelmanager::{self, RecipientOnionFields};
use lightning::ln::channelmanager::{
ChainParameters, ChannelDetails, ChannelManagerReadArgs, PaymentId, Retry,
self, ChainParameters, ChannelDetails, ChannelManagerReadArgs, PaymentId, RecipientOnionFields,
Retry,
};
use lightning::ln::peer_handler::{IgnoringMessageHandler, MessageHandler};
use lightning::ln::{PaymentHash, PaymentPreimage};
Expand DownExpand Up@@ -133,7 +136,7 @@ use bitcoin::BlockHash;

use rand::Rng;

use std::convert::{TryFrom, TryInto};
use std::convert::TryInto;
use std::default::Default;
use std::fs;
use std::net::SocketAddr;
Expand DownExpand Up@@ -167,7 +170,7 @@ pub struct Config {
/// The used Bitcoin network.
pub network: bitcoin::Network,
/// The IP address and TCP port the node will listen on.
pub listening_address: Option<String>,
pub listening_address: Option<SocketAddr>,
/// The default CLTV expiry delta to be used for payments.
pub default_cltv_expiry_delta: u32,
}
Expand All@@ -178,7 +181,7 @@ impl Default for Config {
storage_dir_path: "/tmp/ldk_node/".to_string(),
esplora_server_url: "http://localhost:3002".to_string(),
network: bitcoin::Network::Regtest,
listening_address: Some("0.0.0.0:9735".to_string()),
listening_address: Some("0.0.0.0:9735".parse().unwrap()),
default_cltv_expiry_delta: 144,
}
}
Expand DownExpand Up@@ -262,9 +265,8 @@ impl Builder {

/// Sets the IP address and TCP port on which [`Node`] will listen for incoming network connections.
///
/// Format: `ADDR:PORT`
/// Default: `0.0.0.0:9735`
pub fn set_listening_address(&mut self, listening_address: String) -> &mut Self {
pub fn set_listening_address(&mut self, listening_address: SocketAddr) -> &mut Self {
self.config.listening_address = Some(listening_address);
self
}
Expand DownExpand Up@@ -819,9 +821,9 @@ impl Node {
self.channel_manager.get_our_node_id()
}

/// Returns our own listening address and port.
pub fn listening_address(&self) -> Option<String> {
self.config.listening_address.clone()
/// Returns our own listening address.
pub fn listening_address(&self) -> Option<&SocketAddr> {
self.config.listening_address.as_ref()
}

/// Retrieve a new on-chain/funding address.
Expand All@@ -841,7 +843,74 @@ impl Node {
self.channel_manager.list_channels()
}

/// Connect to a node and opens a new channel.
/// Connect to a node on the peer-to-peer network.
///
/// If `permanently` is set to `true`, we'll remember the peer and reconnect to it on restart.
pub fn connect(
&self, node_id: PublicKey, address: SocketAddr, permanently: bool,
) -> Result<(), Error> {
let runtime_lock = self.running.read().unwrap();
if runtime_lock.is_none() {
return Err(Error::NotRunning);
}

let runtime = runtime_lock.as_ref().unwrap();

let peer_info = PeerInfo { pubkey: node_id, address };

let con_peer_pubkey = peer_info.pubkey;
let con_peer_addr = peer_info.address;
let con_success = Arc::new(AtomicBool::new(false));
let con_success_cloned = Arc::clone(&con_success);
let con_logger = Arc::clone(&self.logger);
let con_pm = Arc::clone(&self.peer_manager);

tokio::task::block_in_place(move || {
runtime.tokio_runtime.block_on(async move {
let res =
connect_peer_if_necessary(con_peer_pubkey, con_peer_addr, con_pm, con_logger)
.await;
con_success_cloned.store(res.is_ok(), Ordering::Release);
})
});

if !con_success.load(Ordering::Acquire) {
return Err(Error::ConnectionFailed);
}

log_info!(self.logger, "Connected to peer {}@{}. ", peer_info.pubkey, peer_info.address,);

if permanently {
self.peer_store.add_peer(peer_info)?;
}

Ok(())
}

/// Disconnects the peer with the given node id.
///
/// Will also remove the peer from the peer store, i.e., after this has been called we won't
/// try to reconnect on restart.
pub fn disconnect(&self, counterparty_node_id: &PublicKey) -> Result<(), Error> {
let runtime_lock = self.running.read().unwrap();
if runtime_lock.is_none() {
return Err(Error::NotRunning);
}

log_info!(self.logger, "Disconnecting peer {}..", counterparty_node_id);

match self.peer_store.remove_peer(&counterparty_node_id) {
Ok(()) => {}
Err(e) => {
log_error!(self.logger, "Failed to remove peer {}: {}", counterparty_node_id, e)
}
}

self.peer_manager.disconnect_by_node_id(*counterparty_node_id);
Ok(())
}

/// Connect to a node and open a new channel. Disconnects and re-connects are handled automatically
///
/// Disconnects and reconnects are handled automatically.
///
Expand All@@ -851,7 +920,7 @@ impl Node {
///
/// Returns a temporary channel id.
pub fn connect_open_channel(
&self, node_pubkey_and_address: &str, channel_amount_sats: u64,
&self, node_id: PublicKey, address: SocketAddr, channel_amount_sats: u64,
push_to_counterparty_msat: Option<u64>, announce_channel: bool,
) -> Result<(), Error> {
let runtime_lock = self.running.read().unwrap();
Expand All@@ -867,10 +936,10 @@ impl Node {
return Err(Error::InsufficientFunds);
}

let peer_info = PeerInfo::try_from(node_pubkey_and_address.to_string())?;
let peer_info = PeerInfo { pubkey: node_id, address };

let con_peer_pubkey = peer_info.pubkey.clone();
let con_peer_addr = peer_info.address.clone();
let con_peer_pubkey = peer_info.pubkey;
let con_peer_addr = peer_info.address;
let con_success = Arc::new(AtomicBool::new(false));
let con_success_cloned = Arc::clone(&con_success);
let con_logger = Arc::clone(&self.logger);
Expand DownExpand Up@@ -913,12 +982,12 @@ impl Node {
Some(user_config),
) {
Ok(_) => {
self.peer_store.add_peer(peer_info.clone())?;
log_info!(
self.logger,
"Initiated channel creation with peer {}. ",
peer_info.pubkey
);
self.peer_store.add_peer(peer_info)?;
Ok(())
}
Err(e) => {
Expand DownExpand Up@@ -1005,7 +1074,7 @@ impl Node {
}

/// Send a payement given an invoice.
pub fn send_payment(&self, invoice: Invoice) -> Result<PaymentHash, Error> {
pub fn send_payment(&self, invoice: &Invoice) -> Result<PaymentHash, Error> {
if self.running.read().unwrap().is_none() {
return Err(Error::NotRunning);
}
Expand DownExpand Up@@ -1070,7 +1139,7 @@ impl Node {
/// This can be used to pay a so-called "zero-amount" invoice, i.e., an invoice that leaves the
/// amount paid to be determined by the user.
pub fn send_payment_using_amount(
&self, invoice: Invoice, amount_msat: u64,
&self, invoice: &Invoice, amount_msat: u64,
) -> Result<PaymentHash, Error> {
if self.running.read().unwrap().is_none() {
return Err(Error::NotRunning);
Expand DownExpand Up@@ -1158,20 +1227,18 @@ impl Node {

/// Send a spontaneous, aka. "keysend", payment
pub fn send_spontaneous_payment(
&self, amount_msat: u64, node_id: &str,
&self, amount_msat: u64, node_id: &PublicKey,
) -> Result<PaymentHash, Error> {
if self.running.read().unwrap().is_none() {
return Err(Error::NotRunning);
}

let pubkey = hex_utils::to_compressed_pubkey(node_id).ok_or(Error::PeerInfoParseFailed)?;

let payment_preimage = PaymentPreimage(self.keys_manager.get_secure_random_bytes());
let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0).into_inner());

let route_params = RouteParameters {
payment_params: PaymentParameters::from_node_id(
pubkey,
*node_id,
self.config.default_cltv_expiry_delta,
),
final_value_msat: amount_msat,
Expand DownExpand Up@@ -1330,15 +1397,15 @@ async fn do_connect_peer(
pubkey: PublicKey, peer_addr: SocketAddr, peer_manager: Arc<PeerManager>,
logger: Arc<FilesystemLogger>,
) -> Result<(), Error> {
log_info!(logger, "connecting to peer: {}@{}", pubkey, peer_addr);
log_info!(logger, "Connecting to peer: {}@{}", pubkey, peer_addr);
match lightning_net_tokio::connect_outbound(Arc::clone(&peer_manager), pubkey, peer_addr).await
{
Some(connection_closed_future) => {
let mut connection_closed_future = Box::pin(connection_closed_future);
loop {
match futures::poll!(&mut connection_closed_future) {
std::task::Poll::Ready(_) => {
log_info!(logger, "peer connection closed: {}@{}", pubkey, peer_addr);
log_info!(logger, "Peer connection closed: {}@{}", pubkey, peer_addr);
return Err(Error::ConnectionFailed);
}
std::task::Poll::Pending => {}
Expand All@@ -1351,7 +1418,7 @@ async fn do_connect_peer(
}
}
None => {
log_error!(logger, "failed to connect to peer: {}@{}", pubkey, peer_addr);
log_error!(logger, "Failed to connect to peer: {}@{}", pubkey, peer_addr);
Err(Error::ConnectionFailed)
}
}
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Unify interface types and expose `connect` / `disconnect` methods by tnull · Pull Request #52 · lightningdevkit/ldk-node · GitHub
Skip to content
Merged
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: 6 additions & 3 deletions README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,6 +10,7 @@ The primary abstraction of the library is the `Node`, which can be retrieved by
```rust
use ldk_node::Builder;
use ldk_node::lightning_invoice::Invoice;
use ldk_node::bitcoin::secp256k1::PublicKey;
use std::str::FromStr;

fn main() {
Expand All@@ -23,13 +24,15 @@ fn main() {
let _funding_address = node.new_funding_address();

// .. fund address ..

node.sync_wallets().unwrap();

node.connect_open_channel("NODE_ID@PEER_ADDR:PORT", 10000, None, false).unwrap();
let node_id = PublicKey::from_str("NODE_ID").unwrap();
let node_addr = "IP_ADDR:PORT".parse().unwrap();
node.connect_open_channel(node_id, node_addr, 10000, None, false).unwrap();

let invoice = Invoice::from_str("INVOICE_STR").unwrap();
node.send_payment(invoice).unwrap();
node.send_payment(&invoice).unwrap();

node.stop().unwrap();
}
Expand Down
15 changes: 0 additions & 15 deletions src/hex_utils.rs
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
use bitcoin::secp256k1::PublicKey;
use std::fmt::Write;

pub fn to_vec(hex: &str) -> Option<Vec<u8>> {
Expand DownExpand Up@@ -30,17 +29,3 @@ pub fn to_string(value: &[u8]) -> String {
}
res
}

pub fn to_compressed_pubkey(hex: &str) -> Option<PublicKey> {
if hex.len() != 33 * 2 {
return None;
}
let data = match to_vec(&hex[0..33 * 2]) {
Some(bytes) => bytes,
None => return None,
};
match PublicKey::from_slice(&data) {
Ok(pk) => Some(pk),
Err(_) => None,
}
}
125 changes: 96 additions & 29 deletions src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -28,6 +28,7 @@
//! ```no_run
//! use ldk_node::Builder;
//! use ldk_node::lightning_invoice::Invoice;
//! use ldk_node::bitcoin::secp256k1::PublicKey;
//! use std::str::FromStr;
//!
//! fn main() {
Expand All@@ -44,10 +45,12 @@
//!
//! node.sync_wallets().unwrap();
//!
//! node.connect_open_channel("NODE_ID@PEER_ADDR:PORT", 10000, None, false).unwrap();
//! let node_id = PublicKey::from_str("NODE_ID").unwrap();
//! let node_addr = "IP_ADDR:PORT".parse().unwrap();
//! node.connect_open_channel(node_id, node_addr, 10000, None, false).unwrap();
//!
//! let invoice = Invoice::from_str("INVOICE_STR").unwrap();
//! node.send_payment(invoice).unwrap();
//! node.send_payment(&invoice).unwrap();
//!
//! node.stop().unwrap();
//! }
Expand All@@ -60,8 +63,8 @@
//! [`send_payment`]: Node::send_payment
//!
#![deny(missing_docs)]
#![deny(broken_intra_doc_links)]
#![deny(private_intra_doc_links)]
#![deny(rustdoc::broken_intra_doc_links)]
#![deny(rustdoc::private_intra_doc_links)]
#![allow(bare_trait_objects)]
#![allow(ellipsis_inclusive_range_patterns)]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
Expand DownExpand Up@@ -100,9 +103,9 @@ use logger::{log_error, log_info, FilesystemLogger, Logger};

use lightning::chain::keysinterface::EntropySource;
use lightning::chain::{chainmonitor, BestBlock, Confirm, Watch};
use lightning::ln::channelmanager::{self, RecipientOnionFields};
use lightning::ln::channelmanager::{
ChainParameters, ChannelDetails, ChannelManagerReadArgs, PaymentId, Retry,
self, ChainParameters, ChannelDetails, ChannelManagerReadArgs, PaymentId, RecipientOnionFields,
Retry,
};
use lightning::ln::peer_handler::{IgnoringMessageHandler, MessageHandler};
use lightning::ln::{PaymentHash, PaymentPreimage};
Expand DownExpand Up@@ -133,7 +136,7 @@ use bitcoin::BlockHash;

use rand::Rng;

use std::convert::{TryFrom, TryInto};
use std::convert::TryInto;
use std::default::Default;
use std::fs;
use std::net::SocketAddr;
Expand DownExpand Up@@ -167,7 +170,7 @@ pub struct Config {
/// The used Bitcoin network.
pub network: bitcoin::Network,
/// The IP address and TCP port the node will listen on.
pub listening_address: Option<String>,
pub listening_address: Option<SocketAddr>,
/// The default CLTV expiry delta to be used for payments.
pub default_cltv_expiry_delta: u32,
}
Expand All@@ -178,7 +181,7 @@ impl Default for Config {
storage_dir_path: "/tmp/ldk_node/".to_string(),
esplora_server_url: "http://localhost:3002".to_string(),
network: bitcoin::Network::Regtest,
listening_address: Some("0.0.0.0:9735".to_string()),
listening_address: Some("0.0.0.0:9735".parse().unwrap()),
default_cltv_expiry_delta: 144,
}
}
Expand DownExpand Up@@ -262,9 +265,8 @@ impl Builder {

/// Sets the IP address and TCP port on which [`Node`] will listen for incoming network connections.
///
/// Format: `ADDR:PORT`
/// Default: `0.0.0.0:9735`
pub fn set_listening_address(&mut self, listening_address: String) -> &mut Self {
pub fn set_listening_address(&mut self, listening_address: SocketAddr) -> &mut Self {
self.config.listening_address = Some(listening_address);
self
}
Expand DownExpand Up@@ -819,9 +821,9 @@ impl Node {
self.channel_manager.get_our_node_id()
}

/// Returns our own listening address and port.
pub fn listening_address(&self) -> Option<String> {
self.config.listening_address.clone()
/// Returns our own listening address.
pub fn listening_address(&self) -> Option<&SocketAddr> {
self.config.listening_address.as_ref()
}

/// Retrieve a new on-chain/funding address.
Expand All@@ -841,7 +843,74 @@ impl Node {
self.channel_manager.list_channels()
}

/// Connect to a node and opens a new channel.
/// Connect to a node on the peer-to-peer network.
///
/// If `permanently` is set to `true`, we'll remember the peer and reconnect to it on restart.
pub fn connect(
&self, node_id: PublicKey, address: SocketAddr, permanently: bool,
) -> Result<(), Error> {
let runtime_lock = self.running.read().unwrap();
if runtime_lock.is_none() {
return Err(Error::NotRunning);
}

let runtime = runtime_lock.as_ref().unwrap();

let peer_info = PeerInfo { pubkey: node_id, address };

let con_peer_pubkey = peer_info.pubkey;
let con_peer_addr = peer_info.address;
let con_success = Arc::new(AtomicBool::new(false));
let con_success_cloned = Arc::clone(&con_success);
let con_logger = Arc::clone(&self.logger);
let con_pm = Arc::clone(&self.peer_manager);

tokio::task::block_in_place(move || {
runtime.tokio_runtime.block_on(async move {
let res =
connect_peer_if_necessary(con_peer_pubkey, con_peer_addr, con_pm, con_logger)
.await;
con_success_cloned.store(res.is_ok(), Ordering::Release);
})
});

if !con_success.load(Ordering::Acquire) {
return Err(Error::ConnectionFailed);
}

log_info!(self.logger, "Connected to peer {}@{}. ", peer_info.pubkey, peer_info.address,);

if permanently {
self.peer_store.add_peer(peer_info)?;
}

Ok(())
}

/// Disconnects the peer with the given node id.
///
/// Will also remove the peer from the peer store, i.e., after this has been called we won't
/// try to reconnect on restart.
pub fn disconnect(&self, counterparty_node_id: &PublicKey) -> Result<(), Error> {
let runtime_lock = self.running.read().unwrap();
if runtime_lock.is_none() {
return Err(Error::NotRunning);
}

log_info!(self.logger, "Disconnecting peer {}..", counterparty_node_id);

match self.peer_store.remove_peer(&counterparty_node_id) {
Ok(()) => {}
Err(e) => {
log_error!(self.logger, "Failed to remove peer {}: {}", counterparty_node_id, e)
}
}

self.peer_manager.disconnect_by_node_id(*counterparty_node_id);
Ok(())
}

/// Connect to a node and open a new channel. Disconnects and re-connects are handled automatically
///
/// Disconnects and reconnects are handled automatically.
///
Expand All@@ -851,7 +920,7 @@ impl Node {
///
/// Returns a temporary channel id.
pub fn connect_open_channel(
&self, node_pubkey_and_address: &str, channel_amount_sats: u64,
&self, node_id: PublicKey, address: SocketAddr, channel_amount_sats: u64,
push_to_counterparty_msat: Option<u64>, announce_channel: bool,
) -> Result<(), Error> {
let runtime_lock = self.running.read().unwrap();
Expand All@@ -867,10 +936,10 @@ impl Node {
return Err(Error::InsufficientFunds);
}

let peer_info = PeerInfo::try_from(node_pubkey_and_address.to_string())?;
let peer_info = PeerInfo { pubkey: node_id, address };

let con_peer_pubkey = peer_info.pubkey.clone();
let con_peer_addr = peer_info.address.clone();
let con_peer_pubkey = peer_info.pubkey;
let con_peer_addr = peer_info.address;
let con_success = Arc::new(AtomicBool::new(false));
let con_success_cloned = Arc::clone(&con_success);
let con_logger = Arc::clone(&self.logger);
Expand DownExpand Up@@ -913,12 +982,12 @@ impl Node {
Some(user_config),
) {
Ok(_) => {
self.peer_store.add_peer(peer_info.clone())?;
log_info!(
self.logger,
"Initiated channel creation with peer {}. ",
peer_info.pubkey
);
self.peer_store.add_peer(peer_info)?;
Ok(())
}
Err(e) => {
Expand DownExpand Up@@ -1005,7 +1074,7 @@ impl Node {
}

/// Send a payement given an invoice.
pub fn send_payment(&self, invoice: Invoice) -> Result<PaymentHash, Error> {
pub fn send_payment(&self, invoice: &Invoice) -> Result<PaymentHash, Error> {
if self.running.read().unwrap().is_none() {
return Err(Error::NotRunning);
}
Expand DownExpand Up@@ -1070,7 +1139,7 @@ impl Node {
/// This can be used to pay a so-called "zero-amount" invoice, i.e., an invoice that leaves the
/// amount paid to be determined by the user.
pub fn send_payment_using_amount(
&self, invoice: Invoice, amount_msat: u64,
&self, invoice: &Invoice, amount_msat: u64,
) -> Result<PaymentHash, Error> {
if self.running.read().unwrap().is_none() {
return Err(Error::NotRunning);
Expand DownExpand Up@@ -1158,20 +1227,18 @@ impl Node {

/// Send a spontaneous, aka. "keysend", payment
pub fn send_spontaneous_payment(
&self, amount_msat: u64, node_id: &str,
&self, amount_msat: u64, node_id: &PublicKey,
) -> Result<PaymentHash, Error> {
if self.running.read().unwrap().is_none() {
return Err(Error::NotRunning);
}

let pubkey = hex_utils::to_compressed_pubkey(node_id).ok_or(Error::PeerInfoParseFailed)?;

let payment_preimage = PaymentPreimage(self.keys_manager.get_secure_random_bytes());
let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0).into_inner());

let route_params = RouteParameters {
payment_params: PaymentParameters::from_node_id(
pubkey,
*node_id,
self.config.default_cltv_expiry_delta,
),
final_value_msat: amount_msat,
Expand DownExpand Up@@ -1330,15 +1397,15 @@ async fn do_connect_peer(
pubkey: PublicKey, peer_addr: SocketAddr, peer_manager: Arc<PeerManager>,
logger: Arc<FilesystemLogger>,
) -> Result<(), Error> {
log_info!(logger, "connecting to peer: {}@{}", pubkey, peer_addr);
log_info!(logger, "Connecting to peer: {}@{}", pubkey, peer_addr);
match lightning_net_tokio::connect_outbound(Arc::clone(&peer_manager), pubkey, peer_addr).await
{
Some(connection_closed_future) => {
let mut connection_closed_future = Box::pin(connection_closed_future);
loop {
match futures::poll!(&mut connection_closed_future) {
std::task::Poll::Ready(_) => {
log_info!(logger, "peer connection closed: {}@{}", pubkey, peer_addr);
log_info!(logger, "Peer connection closed: {}@{}", pubkey, peer_addr);
return Err(Error::ConnectionFailed);
}
std::task::Poll::Pending => {}
Expand All@@ -1351,7 +1418,7 @@ async fn do_connect_peer(
}
}
None => {
log_error!(logger, "failed to connect to peer: {}@{}", pubkey, peer_addr);
log_error!(logger, "Failed to connect to peer: {}@{}", pubkey, peer_addr);
Err(Error::ConnectionFailed)
}
}
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Unify interface types and expose `connect` / `disconnect` methods by tnull · Pull Request #52 · lightningdevkit/ldk-node · GitHub
Skip to content
Merged
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: 6 additions & 3 deletions README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,6 +10,7 @@ The primary abstraction of the library is the `Node`, which can be retrieved by
```rust
use ldk_node::Builder;
use ldk_node::lightning_invoice::Invoice;
use ldk_node::bitcoin::secp256k1::PublicKey;
use std::str::FromStr;

fn main() {
Expand All@@ -23,13 +24,15 @@ fn main() {
let _funding_address = node.new_funding_address();

// .. fund address ..

node.sync_wallets().unwrap();

node.connect_open_channel("NODE_ID@PEER_ADDR:PORT", 10000, None, false).unwrap();
let node_id = PublicKey::from_str("NODE_ID").unwrap();
let node_addr = "IP_ADDR:PORT".parse().unwrap();
node.connect_open_channel(node_id, node_addr, 10000, None, false).unwrap();

let invoice = Invoice::from_str("INVOICE_STR").unwrap();
node.send_payment(invoice).unwrap();
node.send_payment(&invoice).unwrap();

node.stop().unwrap();
}
Expand Down
15 changes: 0 additions & 15 deletions src/hex_utils.rs
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
use bitcoin::secp256k1::PublicKey;
use std::fmt::Write;

pub fn to_vec(hex: &str) -> Option<Vec<u8>> {
Expand DownExpand Up@@ -30,17 +29,3 @@ pub fn to_string(value: &[u8]) -> String {
}
res
}

pub fn to_compressed_pubkey(hex: &str) -> Option<PublicKey> {
if hex.len() != 33 * 2 {
return None;
}
let data = match to_vec(&hex[0..33 * 2]) {
Some(bytes) => bytes,
None => return None,
};
match PublicKey::from_slice(&data) {
Ok(pk) => Some(pk),
Err(_) => None,
}
}
125 changes: 96 additions & 29 deletions src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -28,6 +28,7 @@
//! ```no_run
//! use ldk_node::Builder;
//! use ldk_node::lightning_invoice::Invoice;
//! use ldk_node::bitcoin::secp256k1::PublicKey;
//! use std::str::FromStr;
//!
//! fn main() {
Expand All@@ -44,10 +45,12 @@
//!
//! node.sync_wallets().unwrap();
//!
//! node.connect_open_channel("NODE_ID@PEER_ADDR:PORT", 10000, None, false).unwrap();
//! let node_id = PublicKey::from_str("NODE_ID").unwrap();
//! let node_addr = "IP_ADDR:PORT".parse().unwrap();
//! node.connect_open_channel(node_id, node_addr, 10000, None, false).unwrap();
//!
//! let invoice = Invoice::from_str("INVOICE_STR").unwrap();
//! node.send_payment(invoice).unwrap();
//! node.send_payment(&invoice).unwrap();
//!
//! node.stop().unwrap();
//! }
Expand All@@ -60,8 +63,8 @@
//! [`send_payment`]: Node::send_payment
//!
#![deny(missing_docs)]
#![deny(broken_intra_doc_links)]
#![deny(private_intra_doc_links)]
#![deny(rustdoc::broken_intra_doc_links)]
#![deny(rustdoc::private_intra_doc_links)]
#![allow(bare_trait_objects)]
#![allow(ellipsis_inclusive_range_patterns)]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
Expand DownExpand Up@@ -100,9 +103,9 @@ use logger::{log_error, log_info, FilesystemLogger, Logger};

use lightning::chain::keysinterface::EntropySource;
use lightning::chain::{chainmonitor, BestBlock, Confirm, Watch};
use lightning::ln::channelmanager::{self, RecipientOnionFields};
use lightning::ln::channelmanager::{
ChainParameters, ChannelDetails, ChannelManagerReadArgs, PaymentId, Retry,
self, ChainParameters, ChannelDetails, ChannelManagerReadArgs, PaymentId, RecipientOnionFields,
Retry,
};
use lightning::ln::peer_handler::{IgnoringMessageHandler, MessageHandler};
use lightning::ln::{PaymentHash, PaymentPreimage};
Expand DownExpand Up@@ -133,7 +136,7 @@ use bitcoin::BlockHash;

use rand::Rng;

use std::convert::{TryFrom, TryInto};
use std::convert::TryInto;
use std::default::Default;
use std::fs;
use std::net::SocketAddr;
Expand DownExpand Up@@ -167,7 +170,7 @@ pub struct Config {
/// The used Bitcoin network.
pub network: bitcoin::Network,
/// The IP address and TCP port the node will listen on.
pub listening_address: Option<String>,
pub listening_address: Option<SocketAddr>,
/// The default CLTV expiry delta to be used for payments.
pub default_cltv_expiry_delta: u32,
}
Expand All@@ -178,7 +181,7 @@ impl Default for Config {
storage_dir_path: "/tmp/ldk_node/".to_string(),
esplora_server_url: "http://localhost:3002".to_string(),
network: bitcoin::Network::Regtest,
listening_address: Some("0.0.0.0:9735".to_string()),
listening_address: Some("0.0.0.0:9735".parse().unwrap()),
default_cltv_expiry_delta: 144,
}
}
Expand DownExpand Up@@ -262,9 +265,8 @@ impl Builder {

/// Sets the IP address and TCP port on which [`Node`] will listen for incoming network connections.
///
/// Format: `ADDR:PORT`
/// Default: `0.0.0.0:9735`
pub fn set_listening_address(&mut self, listening_address: String) -> &mut Self {
pub fn set_listening_address(&mut self, listening_address: SocketAddr) -> &mut Self {
self.config.listening_address = Some(listening_address);
self
}
Expand DownExpand Up@@ -819,9 +821,9 @@ impl Node {
self.channel_manager.get_our_node_id()
}

/// Returns our own listening address and port.
pub fn listening_address(&self) -> Option<String> {
self.config.listening_address.clone()
/// Returns our own listening address.
pub fn listening_address(&self) -> Option<&SocketAddr> {
self.config.listening_address.as_ref()
}

/// Retrieve a new on-chain/funding address.
Expand All@@ -841,7 +843,74 @@ impl Node {
self.channel_manager.list_channels()
}

/// Connect to a node and opens a new channel.
/// Connect to a node on the peer-to-peer network.
///
/// If `permanently` is set to `true`, we'll remember the peer and reconnect to it on restart.
pub fn connect(
&self, node_id: PublicKey, address: SocketAddr, permanently: bool,
) -> Result<(), Error> {
let runtime_lock = self.running.read().unwrap();
if runtime_lock.is_none() {
return Err(Error::NotRunning);
}

let runtime = runtime_lock.as_ref().unwrap();

let peer_info = PeerInfo { pubkey: node_id, address };

let con_peer_pubkey = peer_info.pubkey;
let con_peer_addr = peer_info.address;
let con_success = Arc::new(AtomicBool::new(false));
let con_success_cloned = Arc::clone(&con_success);
let con_logger = Arc::clone(&self.logger);
let con_pm = Arc::clone(&self.peer_manager);

tokio::task::block_in_place(move || {
runtime.tokio_runtime.block_on(async move {
let res =
connect_peer_if_necessary(con_peer_pubkey, con_peer_addr, con_pm, con_logger)
.await;
con_success_cloned.store(res.is_ok(), Ordering::Release);
})
});

if !con_success.load(Ordering::Acquire) {
return Err(Error::ConnectionFailed);
}

log_info!(self.logger, "Connected to peer {}@{}. ", peer_info.pubkey, peer_info.address,);

if permanently {
self.peer_store.add_peer(peer_info)?;
}

Ok(())
}

/// Disconnects the peer with the given node id.
///
/// Will also remove the peer from the peer store, i.e., after this has been called we won't
/// try to reconnect on restart.
pub fn disconnect(&self, counterparty_node_id: &PublicKey) -> Result<(), Error> {
let runtime_lock = self.running.read().unwrap();
if runtime_lock.is_none() {
return Err(Error::NotRunning);
}

log_info!(self.logger, "Disconnecting peer {}..", counterparty_node_id);

match self.peer_store.remove_peer(&counterparty_node_id) {
Ok(()) => {}
Err(e) => {
log_error!(self.logger, "Failed to remove peer {}: {}", counterparty_node_id, e)
}
}

self.peer_manager.disconnect_by_node_id(*counterparty_node_id);
Ok(())
}

/// Connect to a node and open a new channel. Disconnects and re-connects are handled automatically
///
/// Disconnects and reconnects are handled automatically.
///
Expand All@@ -851,7 +920,7 @@ impl Node {
///
/// Returns a temporary channel id.
pub fn connect_open_channel(
&self, node_pubkey_and_address: &str, channel_amount_sats: u64,
&self, node_id: PublicKey, address: SocketAddr, channel_amount_sats: u64,
push_to_counterparty_msat: Option<u64>, announce_channel: bool,
) -> Result<(), Error> {
let runtime_lock = self.running.read().unwrap();
Expand All@@ -867,10 +936,10 @@ impl Node {
return Err(Error::InsufficientFunds);
}

let peer_info = PeerInfo::try_from(node_pubkey_and_address.to_string())?;
let peer_info = PeerInfo { pubkey: node_id, address };

let con_peer_pubkey = peer_info.pubkey.clone();
let con_peer_addr = peer_info.address.clone();
let con_peer_pubkey = peer_info.pubkey;
let con_peer_addr = peer_info.address;
let con_success = Arc::new(AtomicBool::new(false));
let con_success_cloned = Arc::clone(&con_success);
let con_logger = Arc::clone(&self.logger);
Expand DownExpand Up@@ -913,12 +982,12 @@ impl Node {
Some(user_config),
) {
Ok(_) => {
self.peer_store.add_peer(peer_info.clone())?;
log_info!(
self.logger,
"Initiated channel creation with peer {}. ",
peer_info.pubkey
);
self.peer_store.add_peer(peer_info)?;
Ok(())
}
Err(e) => {
Expand DownExpand Up@@ -1005,7 +1074,7 @@ impl Node {
}

/// Send a payement given an invoice.
pub fn send_payment(&self, invoice: Invoice) -> Result<PaymentHash, Error> {
pub fn send_payment(&self, invoice: &Invoice) -> Result<PaymentHash, Error> {
if self.running.read().unwrap().is_none() {
return Err(Error::NotRunning);
}
Expand DownExpand Up@@ -1070,7 +1139,7 @@ impl Node {
/// This can be used to pay a so-called "zero-amount" invoice, i.e., an invoice that leaves the
/// amount paid to be determined by the user.
pub fn send_payment_using_amount(
&self, invoice: Invoice, amount_msat: u64,
&self, invoice: &Invoice, amount_msat: u64,
) -> Result<PaymentHash, Error> {
if self.running.read().unwrap().is_none() {
return Err(Error::NotRunning);
Expand DownExpand Up@@ -1158,20 +1227,18 @@ impl Node {

/// Send a spontaneous, aka. "keysend", payment
pub fn send_spontaneous_payment(
&self, amount_msat: u64, node_id: &str,
&self, amount_msat: u64, node_id: &PublicKey,
) -> Result<PaymentHash, Error> {
if self.running.read().unwrap().is_none() {
return Err(Error::NotRunning);
}

let pubkey = hex_utils::to_compressed_pubkey(node_id).ok_or(Error::PeerInfoParseFailed)?;

let payment_preimage = PaymentPreimage(self.keys_manager.get_secure_random_bytes());
let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0).into_inner());

let route_params = RouteParameters {
payment_params: PaymentParameters::from_node_id(
pubkey,
*node_id,
self.config.default_cltv_expiry_delta,
),
final_value_msat: amount_msat,
Expand DownExpand Up@@ -1330,15 +1397,15 @@ async fn do_connect_peer(
pubkey: PublicKey, peer_addr: SocketAddr, peer_manager: Arc<PeerManager>,
logger: Arc<FilesystemLogger>,
) -> Result<(), Error> {
log_info!(logger, "connecting to peer: {}@{}", pubkey, peer_addr);
log_info!(logger, "Connecting to peer: {}@{}", pubkey, peer_addr);
match lightning_net_tokio::connect_outbound(Arc::clone(&peer_manager), pubkey, peer_addr).await
{
Some(connection_closed_future) => {
let mut connection_closed_future = Box::pin(connection_closed_future);
loop {
match futures::poll!(&mut connection_closed_future) {
std::task::Poll::Ready(_) => {
log_info!(logger, "peer connection closed: {}@{}", pubkey, peer_addr);
log_info!(logger, "Peer connection closed: {}@{}", pubkey, peer_addr);
return Err(Error::ConnectionFailed);
}
std::task::Poll::Pending => {}
Expand All@@ -1351,7 +1418,7 @@ async fn do_connect_peer(
}
}
None => {
log_error!(logger, "failed to connect to peer: {}@{}", pubkey, peer_addr);
log_error!(logger, "Failed to connect to peer: {}@{}", pubkey, peer_addr);
Err(Error::ConnectionFailed)
}
}
Expand Down
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); Unify interface types and expose `connect` / `disconnect` methods by tnull · Pull Request #52 · lightningdevkit/ldk-node · GitHub
Skip to content
Merged
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: 6 additions & 3 deletions README.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,6 +10,7 @@ The primary abstraction of the library is the `Node`, which can be retrieved by
```rust
use ldk_node::Builder;
use ldk_node::lightning_invoice::Invoice;
use ldk_node::bitcoin::secp256k1::PublicKey;
use std::str::FromStr;

fn main() {
Expand All@@ -23,13 +24,15 @@ fn main() {
let _funding_address = node.new_funding_address();

// .. fund address ..

node.sync_wallets().unwrap();

node.connect_open_channel("NODE_ID@PEER_ADDR:PORT", 10000, None, false).unwrap();
let node_id = PublicKey::from_str("NODE_ID").unwrap();
let node_addr = "IP_ADDR:PORT".parse().unwrap();
node.connect_open_channel(node_id, node_addr, 10000, None, false).unwrap();

let invoice = Invoice::from_str("INVOICE_STR").unwrap();
node.send_payment(invoice).unwrap();
node.send_payment(&invoice).unwrap();

node.stop().unwrap();
}
Expand Down
15 changes: 0 additions & 15 deletions src/hex_utils.rs
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
use bitcoin::secp256k1::PublicKey;
use std::fmt::Write;

pub fn to_vec(hex: &str) -> Option<Vec<u8>> {
Expand DownExpand Up@@ -30,17 +29,3 @@ pub fn to_string(value: &[u8]) -> String {
}
res
}

pub fn to_compressed_pubkey(hex: &str) -> Option<PublicKey> {
if hex.len() != 33 * 2 {
return None;
}
let data = match to_vec(&hex[0..33 * 2]) {
Some(bytes) => bytes,
None => return None,
};
match PublicKey::from_slice(&data) {
Ok(pk) => Some(pk),
Err(_) => None,
}
}
125 changes: 96 additions & 29 deletions src/lib.rs
Original file line numberDiff line numberDiff line change
Expand Up@@ -28,6 +28,7 @@
//! ```no_run
//! use ldk_node::Builder;
//! use ldk_node::lightning_invoice::Invoice;
//! use ldk_node::bitcoin::secp256k1::PublicKey;
//! use std::str::FromStr;
//!
//! fn main() {
Expand All@@ -44,10 +45,12 @@
//!
//! node.sync_wallets().unwrap();
//!
//! node.connect_open_channel("NODE_ID@PEER_ADDR:PORT", 10000, None, false).unwrap();
//! let node_id = PublicKey::from_str("NODE_ID").unwrap();
//! let node_addr = "IP_ADDR:PORT".parse().unwrap();
//! node.connect_open_channel(node_id, node_addr, 10000, None, false).unwrap();
//!
//! let invoice = Invoice::from_str("INVOICE_STR").unwrap();
//! node.send_payment(invoice).unwrap();
//! node.send_payment(&invoice).unwrap();
//!
//! node.stop().unwrap();
//! }
Expand All@@ -60,8 +63,8 @@
//! [`send_payment`]: Node::send_payment
//!
#![deny(missing_docs)]
#![deny(broken_intra_doc_links)]
#![deny(private_intra_doc_links)]
#![deny(rustdoc::broken_intra_doc_links)]
#![deny(rustdoc::private_intra_doc_links)]
#![allow(bare_trait_objects)]
#![allow(ellipsis_inclusive_range_patterns)]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
Expand DownExpand Up@@ -100,9 +103,9 @@ use logger::{log_error, log_info, FilesystemLogger, Logger};

use lightning::chain::keysinterface::EntropySource;
use lightning::chain::{chainmonitor, BestBlock, Confirm, Watch};
use lightning::ln::channelmanager::{self, RecipientOnionFields};
use lightning::ln::channelmanager::{
ChainParameters, ChannelDetails, ChannelManagerReadArgs, PaymentId, Retry,
self, ChainParameters, ChannelDetails, ChannelManagerReadArgs, PaymentId, RecipientOnionFields,
Retry,
};
use lightning::ln::peer_handler::{IgnoringMessageHandler, MessageHandler};
use lightning::ln::{PaymentHash, PaymentPreimage};
Expand DownExpand Up@@ -133,7 +136,7 @@ use bitcoin::BlockHash;

use rand::Rng;

use std::convert::{TryFrom, TryInto};
use std::convert::TryInto;
use std::default::Default;
use std::fs;
use std::net::SocketAddr;
Expand DownExpand Up@@ -167,7 +170,7 @@ pub struct Config {
/// The used Bitcoin network.
pub network: bitcoin::Network,
/// The IP address and TCP port the node will listen on.
pub listening_address: Option<String>,
pub listening_address: Option<SocketAddr>,
/// The default CLTV expiry delta to be used for payments.
pub default_cltv_expiry_delta: u32,
}
Expand All@@ -178,7 +181,7 @@ impl Default for Config {
storage_dir_path: "/tmp/ldk_node/".to_string(),
esplora_server_url: "http://localhost:3002".to_string(),
network: bitcoin::Network::Regtest,
listening_address: Some("0.0.0.0:9735".to_string()),
listening_address: Some("0.0.0.0:9735".parse().unwrap()),
default_cltv_expiry_delta: 144,
}
}
Expand DownExpand Up@@ -262,9 +265,8 @@ impl Builder {

/// Sets the IP address and TCP port on which [`Node`] will listen for incoming network connections.
///
/// Format: `ADDR:PORT`
/// Default: `0.0.0.0:9735`
pub fn set_listening_address(&mut self, listening_address: String) -> &mut Self {
pub fn set_listening_address(&mut self, listening_address: SocketAddr) -> &mut Self {
self.config.listening_address = Some(listening_address);
self
}
Expand DownExpand Up@@ -819,9 +821,9 @@ impl Node {
self.channel_manager.get_our_node_id()
}

/// Returns our own listening address and port.
pub fn listening_address(&self) -> Option<String> {
self.config.listening_address.clone()
/// Returns our own listening address.
pub fn listening_address(&self) -> Option<&SocketAddr> {
self.config.listening_address.as_ref()
}

/// Retrieve a new on-chain/funding address.
Expand All@@ -841,7 +843,74 @@ impl Node {
self.channel_manager.list_channels()
}

/// Connect to a node and opens a new channel.
/// Connect to a node on the peer-to-peer network.
///
/// If `permanently` is set to `true`, we'll remember the peer and reconnect to it on restart.
pub fn connect(
&self, node_id: PublicKey, address: SocketAddr, permanently: bool,
) -> Result<(), Error> {
let runtime_lock = self.running.read().unwrap();
if runtime_lock.is_none() {
return Err(Error::NotRunning);
}

let runtime = runtime_lock.as_ref().unwrap();

let peer_info = PeerInfo { pubkey: node_id, address };

let con_peer_pubkey = peer_info.pubkey;
let con_peer_addr = peer_info.address;
let con_success = Arc::new(AtomicBool::new(false));
let con_success_cloned = Arc::clone(&con_success);
let con_logger = Arc::clone(&self.logger);
let con_pm = Arc::clone(&self.peer_manager);

tokio::task::block_in_place(move || {
runtime.tokio_runtime.block_on(async move {
let res =
connect_peer_if_necessary(con_peer_pubkey, con_peer_addr, con_pm, con_logger)
.await;
con_success_cloned.store(res.is_ok(), Ordering::Release);
})
});

if !con_success.load(Ordering::Acquire) {
return Err(Error::ConnectionFailed);
}

log_info!(self.logger, "Connected to peer {}@{}. ", peer_info.pubkey, peer_info.address,);

if permanently {
self.peer_store.add_peer(peer_info)?;
}

Ok(())
}

/// Disconnects the peer with the given node id.
///
/// Will also remove the peer from the peer store, i.e., after this has been called we won't
/// try to reconnect on restart.
pub fn disconnect(&self, counterparty_node_id: &PublicKey) -> Result<(), Error> {
let runtime_lock = self.running.read().unwrap();
if runtime_lock.is_none() {
return Err(Error::NotRunning);
}

log_info!(self.logger, "Disconnecting peer {}..", counterparty_node_id);

match self.peer_store.remove_peer(&counterparty_node_id) {
Ok(()) => {}
Err(e) => {
log_error!(self.logger, "Failed to remove peer {}: {}", counterparty_node_id, e)
}
}

self.peer_manager.disconnect_by_node_id(*counterparty_node_id);
Ok(())
}

/// Connect to a node and open a new channel. Disconnects and re-connects are handled automatically
///
/// Disconnects and reconnects are handled automatically.
///
Expand All@@ -851,7 +920,7 @@ impl Node {
///
/// Returns a temporary channel id.
pub fn connect_open_channel(
&self, node_pubkey_and_address: &str, channel_amount_sats: u64,
&self, node_id: PublicKey, address: SocketAddr, channel_amount_sats: u64,
push_to_counterparty_msat: Option<u64>, announce_channel: bool,
) -> Result<(), Error> {
let runtime_lock = self.running.read().unwrap();
Expand All@@ -867,10 +936,10 @@ impl Node {
return Err(Error::InsufficientFunds);
}

let peer_info = PeerInfo::try_from(node_pubkey_and_address.to_string())?;
let peer_info = PeerInfo { pubkey: node_id, address };

let con_peer_pubkey = peer_info.pubkey.clone();
let con_peer_addr = peer_info.address.clone();
let con_peer_pubkey = peer_info.pubkey;
let con_peer_addr = peer_info.address;
let con_success = Arc::new(AtomicBool::new(false));
let con_success_cloned = Arc::clone(&con_success);
let con_logger = Arc::clone(&self.logger);
Expand DownExpand Up@@ -913,12 +982,12 @@ impl Node {
Some(user_config),
) {
Ok(_) => {
self.peer_store.add_peer(peer_info.clone())?;
log_info!(
self.logger,
"Initiated channel creation with peer {}. ",
peer_info.pubkey
);
self.peer_store.add_peer(peer_info)?;
Ok(())
}
Err(e) => {
Expand DownExpand Up@@ -1005,7 +1074,7 @@ impl Node {
}

/// Send a payement given an invoice.
pub fn send_payment(&self, invoice: Invoice) -> Result<PaymentHash, Error> {
pub fn send_payment(&self, invoice: &Invoice) -> Result<PaymentHash, Error> {
if self.running.read().unwrap().is_none() {
return Err(Error::NotRunning);
}
Expand DownExpand Up@@ -1070,7 +1139,7 @@ impl Node {
/// This can be used to pay a so-called "zero-amount" invoice, i.e., an invoice that leaves the
/// amount paid to be determined by the user.
pub fn send_payment_using_amount(
&self, invoice: Invoice, amount_msat: u64,
&self, invoice: &Invoice, amount_msat: u64,
) -> Result<PaymentHash, Error> {
if self.running.read().unwrap().is_none() {
return Err(Error::NotRunning);
Expand DownExpand Up@@ -1158,20 +1227,18 @@ impl Node {

/// Send a spontaneous, aka. "keysend", payment
pub fn send_spontaneous_payment(
&self, amount_msat: u64, node_id: &str,
&self, amount_msat: u64, node_id: &PublicKey,
) -> Result<PaymentHash, Error> {
if self.running.read().unwrap().is_none() {
return Err(Error::NotRunning);
}

let pubkey = hex_utils::to_compressed_pubkey(node_id).ok_or(Error::PeerInfoParseFailed)?;

let payment_preimage = PaymentPreimage(self.keys_manager.get_secure_random_bytes());
let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0).into_inner());

let route_params = RouteParameters {
payment_params: PaymentParameters::from_node_id(
pubkey,
*node_id,
self.config.default_cltv_expiry_delta,
),
final_value_msat: amount_msat,
Expand DownExpand Up@@ -1330,15 +1397,15 @@ async fn do_connect_peer(
pubkey: PublicKey, peer_addr: SocketAddr, peer_manager: Arc<PeerManager>,
logger: Arc<FilesystemLogger>,
) -> Result<(), Error> {
log_info!(logger, "connecting to peer: {}@{}", pubkey, peer_addr);
log_info!(logger, "Connecting to peer: {}@{}", pubkey, peer_addr);
match lightning_net_tokio::connect_outbound(Arc::clone(&peer_manager), pubkey, peer_addr).await
{
Some(connection_closed_future) => {
let mut connection_closed_future = Box::pin(connection_closed_future);
loop {
match futures::poll!(&mut connection_closed_future) {
std::task::Poll::Ready(_) => {
log_info!(logger, "peer connection closed: {}@{}", pubkey, peer_addr);
log_info!(logger, "Peer connection closed: {}@{}", pubkey, peer_addr);
return Err(Error::ConnectionFailed);
}
std::task::Poll::Pending => {}
Expand All@@ -1351,7 +1418,7 @@ async fn do_connect_peer(
}
}
None => {
log_error!(logger, "failed to connect to peer: {}@{}", pubkey, peer_addr);
log_error!(logger, "Failed to connect to peer: {}@{}", pubkey, peer_addr);
Err(Error::ConnectionFailed)
}
}
Expand Down
Loading