From: Matt Corallo <649246+TheBlueMatt@users.noreply.github.com> Date: Tue, 11 Jan 2022 18:44:36 +0000 (+0000) Subject: Merge pull request #43 from TheBlueMatt/2021-12-0.0.104 X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=commitdiff_plain;h=86a474d8acb1d8495307a38517dfc53b805d3b9b;hp=-c;p=ldk-sample Merge pull request #43 from TheBlueMatt/2021-12-0.0.104 LDK 0.0.104 --- 86a474d8acb1d8495307a38517dfc53b805d3b9b diff --combined src/cli.rs index 42696c6,8ee0465..a167027 --- a/src/cli.rs +++ b/src/cli.rs @@@ -1,19 -1,16 +1,16 @@@ use crate::disk; use crate::hex_utils; use crate::{ - ChannelManager, FilesystemLogger, HTLCStatus, InvoicePayer, MillisatAmount, PaymentInfo, - PaymentInfoStorage, PeerManager, + ChannelManager, HTLCStatus, InvoicePayer, MillisatAmount, PaymentInfo, PaymentInfoStorage, + PeerManager, }; + use bitcoin::hashes::sha256::Hash as Sha256; use bitcoin::hashes::Hash; use bitcoin::network::constants::Network; use bitcoin::secp256k1::key::PublicKey; use lightning::chain::keysinterface::{KeysInterface, KeysManager}; use lightning::ln::msgs::NetAddress; - use lightning::ln::PaymentHash; - use lightning::routing::network_graph::NetworkGraph; - use lightning::routing::router; - use lightning::routing::router::{Payee, RouteParameters}; - use lightning::routing::scorer::Scorer; + use lightning::ln::{PaymentHash, PaymentPreimage}; use lightning::util::config::{ChannelConfig, ChannelHandshakeLimits, UserConfig}; use lightning::util::events::EventHandler; use lightning_invoice::payment::PaymentError; @@@ -25,7 -22,7 +22,7 @@@ use std::net::{IpAddr, SocketAddr, ToSo use std::ops::Deref; use std::path::Path; use std::str::FromStr; - use std::sync::{Arc, Mutex}; + use std::sync::Arc; use std::time::Duration; pub(crate) struct LdkUserInfo { @@@ -88,9 -85,8 +85,9 @@@ pub(crate) fn parse_startup_args() -> R let network: Network = match env::args().skip(arg_idx).next().as_ref().map(String::as_str) { Some("testnet") => Network::Testnet, Some("regtest") => Network::Regtest, + Some("signet") => Network::Signet, Some(net) => { - panic!("Unsupported network provided. Options are: `regtest`, `testnet`. Got {}", net); + panic!("Unsupported network provided. Options are: `regtest`, `testnet`, and `signet`. Got {}", net); } None => Network::Testnet, }; @@@ -144,18 -140,21 +141,21 @@@ pub(crate) async fn poll_for_user_input( invoice_payer: Arc>, peer_manager: Arc, channel_manager: Arc, keys_manager: Arc, - network_graph: Arc, scorer: Arc>, inbound_payments: PaymentInfoStorage, outbound_payments: PaymentInfoStorage, - ldk_data_dir: String, logger: Arc, network: Network, + ldk_data_dir: String, network: Network, ) { println!("LDK startup successful. To view available commands: \"help\"."); println!("LDK logs are available at /.ldk/logs"); println!("Local Node ID is {}.", channel_manager.get_our_node_id()); let stdin = io::stdin(); - print!("> "); - io::stdout().flush().unwrap(); // Without flushing, the `>` doesn't print - for line in stdin.lock().lines() { - let line = line.unwrap(); + let mut line_reader = stdin.lock().lines(); + loop { + print!("> "); + io::stdout().flush().unwrap(); // Without flushing, the `>` doesn't print + let line = match line_reader.next() { + Some(l) => l.unwrap(), + None => break, + }; let mut words = line.split_whitespace(); if let Some(word) = words.next() { match word { @@@ -165,8 -164,6 +165,6 @@@ let channel_value_sat = words.next(); if peer_pubkey_and_ip_addr.is_none() || channel_value_sat.is_none() { println!("ERROR: openchannel has 2 required arguments: `openchannel pubkey@host:port channel_amt_satoshis` [--public]"); - print!("> "); - io::stdout().flush().unwrap(); continue; } let peer_pubkey_and_ip_addr = peer_pubkey_and_ip_addr.unwrap(); @@@ -175,8 -172,6 +173,6 @@@ Ok(info) => info, Err(e) => { println!("{:?}", e.into_inner().unwrap()); - print!("> "); - io::stdout().flush().unwrap(); continue; } }; @@@ -184,8 -179,6 +180,6 @@@ let chan_amt_sat: Result = channel_value_sat.unwrap().parse(); if chan_amt_sat.is_err() { println!("ERROR: channel amount must be a number"); - print!("> "); - io::stdout().flush().unwrap(); continue; } @@@ -193,8 -186,6 +187,6 @@@ .await .is_err() { - print!("> "); - io::stdout().flush().unwrap(); continue; }; @@@ -203,8 -194,6 +195,6 @@@ Some("--public=false") => false, Some(_) => { println!("ERROR: invalid `--public` command format. Valid formats: `--public`, `--public=true` `--public=false`"); - print!("> "); - io::stdout().flush().unwrap(); continue; } None => false, @@@ -229,8 -218,6 +219,6 @@@ let invoice_str = words.next(); if invoice_str.is_none() { println!("ERROR: sendpayment requires an invoice: `sendpayment `"); - print!("> "); - io::stdout().flush().unwrap(); continue; } @@@ -238,8 -225,6 +226,6 @@@ Ok(inv) => inv, Err(e) => { println!("ERROR: invalid invoice: {:?}", e); - print!("> "); - io::stdout().flush().unwrap(); continue; } }; @@@ -252,15 -237,11 +238,11 @@@ Some(pk) => pk, None => { println!("ERROR: couldn't parse destination pubkey"); - print!("> "); - io::stdout().flush().unwrap(); continue; } }, None => { println!("ERROR: keysend requires a destination pubkey: `keysend `"); - print!("> "); - io::stdout().flush().unwrap(); continue; } }; @@@ -268,9 -249,6 +250,6 @@@ Some(amt) => amt, None => { println!("ERROR: keysend requires an amount in millisatoshis: `keysend `"); - - print!("> "); - io::stdout().flush().unwrap(); continue; } }; @@@ -278,35 -256,27 +257,27 @@@ Ok(amt) => amt, Err(e) => { println!("ERROR: couldn't parse amount_msat: {}", e); - print!("> "); - io::stdout().flush().unwrap(); continue; } }; keysend( + &*invoice_payer, dest_pubkey, amt_msat, - network_graph.clone(), - channel_manager.clone(), + &*keys_manager, outbound_payments.clone(), - logger.clone(), - scorer.clone(), ); } "getinvoice" => { let amt_str = words.next(); if amt_str.is_none() { println!("ERROR: getinvoice requires an amount in millisatoshis"); - print!("> "); - io::stdout().flush().unwrap(); continue; } let amt_msat: Result = amt_str.unwrap().parse(); if amt_msat.is_err() { println!("ERROR: getinvoice provided payment amount was not a number"); - print!("> "); - io::stdout().flush().unwrap(); continue; } get_invoice( @@@ -321,8 -291,6 +292,6 @@@ let peer_pubkey_and_ip_addr = words.next(); if peer_pubkey_and_ip_addr.is_none() { println!("ERROR: connectpeer requires peer connection info: `connectpeer pubkey@host:port`"); - print!("> "); - io::stdout().flush().unwrap(); continue; } let (pubkey, peer_addr) = @@@ -330,8 -298,6 +299,6 @@@ Ok(info) => info, Err(e) => { println!("{:?}", e.into_inner().unwrap()); - print!("> "); - io::stdout().flush().unwrap(); continue; } }; @@@ -350,15 -316,11 +317,11 @@@ let channel_id_str = words.next(); if channel_id_str.is_none() { println!("ERROR: closechannel requires a channel ID: `closechannel `"); - print!("> "); - io::stdout().flush().unwrap(); continue; } let channel_id_vec = hex_utils::to_vec(channel_id_str.unwrap()); if channel_id_vec.is_none() { println!("ERROR: couldn't parse channel_id as hex"); - print!("> "); - io::stdout().flush().unwrap(); continue; } let mut channel_id = [0; 32]; @@@ -369,15 -331,11 +332,11 @@@ let channel_id_str = words.next(); if channel_id_str.is_none() { println!("ERROR: forceclosechannel requires a channel ID: `forceclosechannel `"); - print!("> "); - io::stdout().flush().unwrap(); continue; } let channel_id_vec = hex_utils::to_vec(channel_id_str.unwrap()); if channel_id_vec.is_none() { println!("ERROR: couldn't parse channel_id as hex"); - print!("> "); - io::stdout().flush().unwrap(); continue; } let mut channel_id = [0; 32]; @@@ -387,11 -345,9 +346,9 @@@ "nodeinfo" => node_info(channel_manager.clone(), peer_manager.clone()), "listpeers" => list_peers(peer_manager.clone()), "signmessage" => { - const MSG_STARTPOS: usize = "signmsg".len() + 1; + const MSG_STARTPOS: usize = "signmessage".len() + 1; if line.as_bytes().len() <= MSG_STARTPOS { println!("ERROR: signmsg requires a message"); - print!("> "); - io::stdout().flush().unwrap(); continue; } println!( @@@ -401,14 -357,10 +358,10 @@@ &keys_manager.get_node_secret() ) ); - print!("> "); - io::stdout().flush().unwrap(); } _ => println!("Unknown command. See `\"help\" for available commands."), } } - print!("> "); - io::stdout().flush().unwrap(); } } @@@ -429,8 -381,14 +382,14 @@@ fn help() fn node_info(channel_manager: Arc, peer_manager: Arc) { println!("\t{{"); println!("\t\t node_pubkey: {}", channel_manager.get_our_node_id()); - println!("\t\t num_channels: {}", channel_manager.list_channels().len()); - println!("\t\t num_usable_channels: {}", channel_manager.list_usable_channels().len()); + let chans = channel_manager.list_channels(); + println!("\t\t num_channels: {}", chans.len()); + println!("\t\t num_usable_channels: {}", chans.iter().filter(|c| c.is_usable).count()); + let local_balance_msat = chans + .iter() + .map(|c| c.unspendable_punishment_reserve.unwrap_or(0) * 1000 + c.outbound_capacity_msat) + .sum::(); + println!("\t\t local_balance_msat: {}", local_balance_msat); println!("\t\t num_peers: {}", peer_manager.get_peer_node_ids().len()); println!("\t}},"); } @@@ -527,6 -485,16 +486,16 @@@ pub(crate) async fn connect_peer_if_nec return Ok(()); } } + let res = do_connect_peer(pubkey, peer_addr, peer_manager).await; + if res.is_err() { + println!("ERROR: failed to connect to peer"); + } + res + } + + pub(crate) async fn do_connect_peer( + pubkey: PublicKey, peer_addr: SocketAddr, peer_manager: Arc, + ) -> Result<(), ()> { match lightning_net_tokio::connect_outbound(Arc::clone(&peer_manager), pubkey, peer_addr).await { Some(connection_closed_future) => { @@@ -534,24 -502,19 +503,19 @@@ loop { match futures::poll!(&mut connection_closed_future) { std::task::Poll::Ready(_) => { - println!("ERROR: Peer disconnected before we finished the handshake"); return Err(()); } std::task::Poll::Pending => {} } // Avoid blocking the tokio context by sleeping a bit match peer_manager.get_peer_node_ids().iter().find(|id| **id == pubkey) { - Some(_) => break, + Some(_) => return Ok(()), None => tokio::time::sleep(Duration::from_millis(10)).await, } } } - None => { - println!("ERROR: failed to connect to peer"); - return Err(()); - } + None => Err(()), } - Ok(()) } fn open_channel( @@@ -622,40 -585,47 +586,47 @@@ fn send_payment ); } - fn keysend( - payee_pubkey: PublicKey, amt_msat: u64, network_graph: Arc, - channel_manager: Arc, payment_storage: PaymentInfoStorage, - logger: Arc, scorer: Arc>, + fn keysend( + invoice_payer: &InvoicePayer, payee_pubkey: PublicKey, amt_msat: u64, keys: &K, + payment_storage: PaymentInfoStorage, ) { - let first_hops = channel_manager.list_usable_channels(); - let payer_pubkey = channel_manager.get_our_node_id(); - - let payee = Payee::for_keysend(payee_pubkey); - let params = RouteParameters { payee, final_value_msat: amt_msat, final_cltv_expiry_delta: 40 }; - - let route = match router::find_route( - &payer_pubkey, - ¶ms, - &network_graph, - Some(&first_hops.iter().collect::>()), - logger, - &scorer.lock().unwrap(), + let payment_preimage = keys.get_secure_random_bytes(); + + let status = match invoice_payer.pay_pubkey( + payee_pubkey, + PaymentPreimage(payment_preimage), + amt_msat, + 40, ) { - Ok(r) => r, - Err(e) => { + Ok(_payment_id) => { + println!("EVENT: initiated sending {} msats to {}", amt_msat, payee_pubkey); + print!("> "); + HTLCStatus::Pending + } + Err(PaymentError::Invoice(e)) => { + println!("ERROR: invalid payee: {}", e); + print!("> "); + return; + } + Err(PaymentError::Routing(e)) => { println!("ERROR: failed to find route: {}", e.err); + print!("> "); return; } + Err(PaymentError::Sending(e)) => { + println!("ERROR: failed to send payment: {:?}", e); + print!("> "); + HTLCStatus::Failed + } }; let mut payments = payment_storage.lock().unwrap(); - let payment_hash = channel_manager.send_spontaneous_payment(&route, None).unwrap().0; payments.insert( - payment_hash, + PaymentHash(Sha256::hash(&payment_preimage).into_inner()), PaymentInfo { preimage: None, secret: None, - status: HTLCStatus::Pending, + status, amt_msat: MillisatAmount(Some(amt_msat)), }, ); @@@ -670,7 -640,7 +641,7 @@@ fn get_invoice Network::Bitcoin => Currency::Bitcoin, Network::Testnet => Currency::BitcoinTestnet, Network::Regtest => Currency::Regtest, - Network::Signet => panic!("Signet unsupported"), + Network::Signet => Currency::Signet, }; let invoice = match utils::create_invoice_from_channelmanager( &channel_manager, diff --combined src/main.rs index 389ff05,3959386..0e18bc0 --- a/src/main.rs +++ b/src/main.rs @@@ -25,7 -25,7 +25,7 @@@ use lightning::ln::channelmanager:: use lightning::ln::peer_handler::{IgnoringMessageHandler, MessageHandler, SimpleArcPeerManager}; use lightning::ln::{PaymentHash, PaymentPreimage, PaymentSecret}; use lightning::routing::network_graph::{NetGraphMsgHandler, NetworkGraph}; - use lightning::routing::scorer::Scorer; + use lightning::routing::scoring::Scorer; use lightning::util::config::UserConfig; use lightning::util::events::{Event, PaymentPurpose}; use lightning::util::ser::ReadableArgs; @@@ -128,7 -128,7 +128,7 @@@ async fn handle_ldk_events Network::Bitcoin => bitcoin_bech32::constants::Network::Bitcoin, Network::Testnet => bitcoin_bech32::constants::Network::Testnet, Network::Regtest => bitcoin_bech32::constants::Network::Regtest, - Network::Signet => panic!("Signet unsupported"), + Network::Signet => bitcoin_bech32::constants::Network::Signet, }, ) .expect("Lightning funding tx should always be to a SegWit output") @@@ -195,16 -195,21 +195,21 @@@ } } } - Event::PaymentSent { payment_preimage, payment_hash, .. } => { + Event::PaymentSent { payment_preimage, payment_hash, fee_paid_msat, .. } => { let mut payments = outbound_payments.lock().unwrap(); for (hash, payment) in payments.iter_mut() { if *hash == *payment_hash { payment.preimage = Some(*payment_preimage); payment.status = HTLCStatus::Succeeded; println!( - "\nEVENT: successfully sent payment of {} millisatoshis from \ + "\nEVENT: successfully sent payment of {} millisatoshis{} from \ payment hash {:?} with preimage {:?}", payment.amt_msat, + if let Some(fee) = fee_paid_msat { + format!(" (fee {} msat)", fee) + } else { + "".to_string() + }, hex_utils::hex_str(&payment_hash.0), hex_utils::hex_str(&payment_preimage.0) ); @@@ -213,26 -218,13 +218,13 @@@ } } } - Event::PaymentPathFailed { - payment_hash, - rejected_by_dest, - all_paths_failed, - short_channel_id, - .. - } => { + Event::PaymentPathSuccessful { .. } => {} + Event::PaymentPathFailed { .. } => {} + Event::PaymentFailed { payment_hash, .. } => { print!( - "\nEVENT: Failed to send payment{} to payment hash {:?}", - if *all_paths_failed { "" } else { " along MPP path" }, + "\nEVENT: Failed to send payment to payment hash {:?}: exhausted payment retry attempts", hex_utils::hex_str(&payment_hash.0) ); - if let Some(scid) = short_channel_id { - print!(" because of failure at channel {}", scid); - } - if *rejected_by_dest { - println!(": re-attempting the payment will not succeed"); - } else { - println!(": exhausted payment retry attempts"); - } print!("> "); io::stdout().flush().unwrap(); @@@ -641,22 -633,39 +633,39 @@@ async fn start_ldk() logger.clone(), ); - // Reconnect to channel peers if possible. + // Regularly reconnect to channel peers. + let connect_cm = Arc::clone(&channel_manager); + let connect_pm = Arc::clone(&peer_manager); let peer_data_path = format!("{}/channel_peer_data", ldk_data_dir.clone()); - match disk::read_channel_peer_data(Path::new(&peer_data_path)) { - Ok(mut info) => { - for (pubkey, peer_addr) in info.drain() { - for chan_info in channel_manager.list_channels() { - if pubkey == chan_info.counterparty.node_id { - let _ = - cli::connect_peer_if_necessary(pubkey, peer_addr, peer_manager.clone()) + tokio::spawn(async move { + let mut interval = tokio::time::interval(Duration::from_secs(1)); + loop { + interval.tick().await; + match disk::read_channel_peer_data(Path::new(&peer_data_path)) { + Ok(info) => { + let peers = connect_pm.get_peer_node_ids(); + for node_id in connect_cm + .list_channels() + .iter() + .map(|chan| chan.counterparty.node_id) + .filter(|id| !peers.contains(id)) + { + for (pubkey, peer_addr) in info.iter() { + if *pubkey == node_id { + let _ = cli::do_connect_peer( + *pubkey, + peer_addr.clone(), + Arc::clone(&connect_pm), + ) .await; + } + } } } + Err(e) => println!("ERROR: errored reading channel peer info from disk: {:?}", e), } } - Err(e) => println!("ERROR: errored reading channel peer info from disk: {:?}", e), - } + }); // Regularly broadcast our node_announcement. This is only required (or possible) if we have // some public channels, and is only useful if we have public listen address(es) to announce. @@@ -684,12 -693,9 +693,9 @@@ peer_manager.clone(), channel_manager.clone(), keys_manager.clone(), - network_graph.clone(), - scorer.clone(), inbound_payments, outbound_payments, ldk_data_dir.clone(), - logger.clone(), network, ) .await;