X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=src%2Fcli.rs;h=c8c14f2f6c007016bca07f63aaf77eeb0cec1ab1;hb=0878cb69e0e575c0e834c59e170a3ee2823325e9;hp=32119aef9ea14baa6b68f12b527102f0bb08ea74;hpb=9fe42a5d0e0bbeb3da443acfcebbb29391e7f09d;p=ldk-sample diff --git a/src/cli.rs b/src/cli.rs index 32119ae..c8c14f2 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -9,6 +9,7 @@ use bitcoin::secp256k1::key::PublicKey; use lightning::chain; use lightning::chain::keysinterface::KeysManager; use lightning::ln::features::InvoiceFeatures; +use lightning::ln::msgs::NetAddress; use lightning::ln::{PaymentHash, PaymentSecret}; use lightning::routing::network_graph::NetGraphMsgHandler; use lightning::routing::router; @@ -18,7 +19,7 @@ use lightning_invoice::{utils, Currency, Invoice}; use std::env; use std::io; use std::io::{BufRead, Write}; -use std::net::{SocketAddr, TcpStream}; +use std::net::{IpAddr, SocketAddr, ToSocketAddrs}; use std::ops::Deref; use std::path::Path; use std::str::FromStr; @@ -33,12 +34,14 @@ pub(crate) struct LdkUserInfo { pub(crate) bitcoind_rpc_host: String, pub(crate) ldk_storage_dir_path: String, pub(crate) ldk_peer_listening_port: u16, + pub(crate) ldk_announced_listen_addr: Option, + pub(crate) ldk_announced_node_name: [u8; 32], pub(crate) network: Network, } pub(crate) fn parse_startup_args() -> Result { if env::args().len() < 3 { - println!("ldk-tutorial-node requires 3 arguments: `cargo run :@: ldk_storage_directory_path [] [bitcoin-network]`"); + println!("ldk-tutorial-node requires 3 arguments: `cargo run :@: ldk_storage_directory_path [] [bitcoin-network] [announced-listen-addr announced-node-name]`"); return Err(()); } let bitcoind_rpc_info = env::args().skip(1).next().unwrap(); @@ -84,6 +87,32 @@ pub(crate) fn parse_startup_args() -> Result { Some(_) => panic!("Unsupported network provided. Options are: `regtest`, `testnet`"), None => Network::Testnet, }; + + let ldk_announced_listen_addr = match env::args().skip(arg_idx + 1).next().as_ref() { + Some(s) => match IpAddr::from_str(s) { + Ok(IpAddr::V4(a)) => { + Some(NetAddress::IPv4 { addr: a.octets(), port: ldk_peer_listening_port }) + } + Ok(IpAddr::V6(a)) => { + Some(NetAddress::IPv6 { addr: a.octets(), port: ldk_peer_listening_port }) + } + Err(_) => panic!("Failed to parse announced-listen-addr into an IP address"), + }, + None => None, + }; + + let ldk_announced_node_name = match env::args().skip(arg_idx + 2).next().as_ref() { + Some(s) => { + if s.len() > 32 { + panic!("Node Alias can not be longer than 32 bytes"); + } + let mut bytes = [0; 32]; + bytes[..s.len()].copy_from_slice(s.as_bytes()); + bytes + } + None => [0; 32], + }; + Ok(LdkUserInfo { bitcoind_rpc_username, bitcoind_rpc_password, @@ -91,6 +120,8 @@ pub(crate) fn parse_startup_args() -> Result { bitcoind_rpc_port, ldk_storage_dir_path, ldk_peer_listening_port, + ldk_announced_listen_addr, + ldk_announced_node_name, network, }) } @@ -151,6 +182,7 @@ pub(crate) async fn poll_for_user_input( peer_manager.clone(), event_notifier.clone(), ) + .await .is_err() { print!("> "); @@ -300,6 +332,7 @@ pub(crate) async fn poll_for_user_input( peer_manager.clone(), event_notifier.clone(), ) + .await .is_ok() { println!("SUCCESS: connected to peer {}", pubkey); @@ -358,14 +391,16 @@ pub(crate) async fn poll_for_user_input( } fn help() { - println!("openchannel pubkey@host:port "); + println!("openchannel pubkey@host:port "); println!("sendpayment "); - println!("getinvoice "); + println!("getinvoice "); println!("connectpeer pubkey@host:port"); println!("listchannels"); println!("listpayments"); println!("closechannel "); println!("forceclosechannel "); + println!("nodeinfo"); + println!("listpeers"); } fn node_info(channel_manager: Arc, peer_manager: Arc) { @@ -391,20 +426,23 @@ fn list_channels(channel_manager: Arc) { println!(""); println!("\t{{"); println!("\t\tchannel_id: {},", hex_utils::hex_str(&chan_info.channel_id[..])); + if let Some(funding_txo) = chan_info.funding_txo { + println!("\t\tfunding_txid: {},", funding_txo.txid); + } println!( "\t\tpeer_pubkey: {},", hex_utils::hex_str(&chan_info.remote_network_id.serialize()) ); - let mut pending_channel = false; - match chan_info.short_channel_id { - Some(id) => println!("\t\tshort_channel_id: {},", id), - None => { - pending_channel = true; - } + if let Some(id) = chan_info.short_channel_id { + println!("\t\tshort_channel_id: {},", id); } - println!("\t\tpending_open: {},", pending_channel); + println!("\t\tis_confirmed_onchain: {},", chan_info.is_funding_locked); println!("\t\tchannel_value_satoshis: {},", chan_info.channel_value_satoshis); - println!("\t\tchannel_can_send_payments: {},", chan_info.is_live); + if chan_info.is_usable { + println!("\t\tavailable_balance_for_send_msat: {},", chan_info.outbound_capacity_msat); + println!("\t\tavailable_balance_for_recv_msat: {},", chan_info.inbound_capacity_msat); + } + println!("\t\tchannel_can_send_payments: {},", chan_info.is_usable); println!("\t}},"); } println!("]"); @@ -452,7 +490,7 @@ fn list_payments(inbound_payments: PaymentInfoStorage, outbound_payments: Paymen println!("]"); } -pub(crate) fn connect_peer_if_necessary( +pub(crate) async fn connect_peer_if_necessary( pubkey: PublicKey, peer_addr: SocketAddr, peer_manager: Arc, event_notifier: mpsc::Sender<()>, ) -> Result<(), ()> { @@ -461,24 +499,36 @@ pub(crate) fn connect_peer_if_necessary( return Ok(()); } } - match TcpStream::connect_timeout(&peer_addr, Duration::from_secs(10)) { - Ok(stream) => { - let peer_mgr = peer_manager.clone(); - let event_ntfns = event_notifier.clone(); - tokio::spawn(async move { - lightning_net_tokio::setup_outbound(peer_mgr, event_ntfns, pubkey, stream).await; - }); + match lightning_net_tokio::connect_outbound( + Arc::clone(&peer_manager), + event_notifier, + pubkey, + peer_addr, + ) + .await + { + Some(conn_closed_fut) => { + let mut closed_fut_box = Box::pin(conn_closed_fut); let mut peer_connected = false; while !peer_connected { + match futures::poll!(&mut closed_fut_box) { + std::task::Poll::Ready(_) => { + println!("ERROR: Peer disconnected before we finished the handshake"); + return Err(()); + } + std::task::Poll::Pending => {} + } for node_pubkey in peer_manager.get_peer_node_ids() { if node_pubkey == pubkey { peer_connected = true; } } + // Avoid blocking the tokio context by sleeping a bit + tokio::time::sleep(Duration::from_millis(10)).await; } } - Err(e) => { - println!("ERROR: failed to connect to peer: {:?}", e); + None => { + println!("ERROR: failed to connect to peer"); return Err(()); } } @@ -625,12 +675,12 @@ pub(crate) fn parse_peer_info( return Err(std::io::Error::new( std::io::ErrorKind::Other, "ERROR: incorrectly formatted peer - info. Should be formatted as: `pubkey@host:port`", + info. Should be formatted as: `pubkey@host:port`", )); } - let peer_addr: Result = peer_addr_str.unwrap().parse(); - if peer_addr.is_err() { + let peer_addr = peer_addr_str.unwrap().to_socket_addrs().map(|mut r| r.next()); + if peer_addr.is_err() || peer_addr.as_ref().unwrap().is_none() { return Err(std::io::Error::new( std::io::ErrorKind::Other, "ERROR: couldn't parse pubkey@host:port into a socket address", @@ -645,5 +695,5 @@ pub(crate) fn parse_peer_info( )); } - Ok((pubkey.unwrap(), peer_addr.unwrap())) + Ok((pubkey.unwrap(), peer_addr.unwrap().unwrap())) }