X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=src%2Fcli.rs;h=610acb6f637f1ac9820586f54bb20f219fb91e4d;hb=cd4e88ff77a35a586bbb03f1059870244dbea52f;hp=bfea9d25072b1ec053c70369aa7eb0c26f6ba867;hpb=93d3b7942b2a0a4990e5a93fda262f9edb671364;p=ldk-sample diff --git a/src/cli.rs b/src/cli.rs index bfea9d2..610acb6 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -7,14 +7,14 @@ use crate::{ use bitcoin::hashes::Hash; use bitcoin::network::constants::Network; use bitcoin::secp256k1::key::PublicKey; -use lightning::chain; -use lightning::chain::keysinterface::KeysManager; +use lightning::chain::keysinterface::{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::network_graph::NetworkGraph; use lightning::routing::router; -use lightning::routing::router::RouteHint; +use lightning::routing::router::{Payee, RouteHint, RouteParameters}; +use lightning::routing::scorer::Scorer; use lightning::util::config::{ChannelConfig, ChannelHandshakeLimits, UserConfig}; use lightning_invoice::{utils, Currency, Invoice}; use std::env; @@ -45,19 +45,19 @@ pub(crate) fn parse_startup_args() -> Result { return Err(()); } let bitcoind_rpc_info = env::args().skip(1).next().unwrap(); - let bitcoind_rpc_info_parts: Vec<&str> = bitcoind_rpc_info.split("@").collect(); + let bitcoind_rpc_info_parts: Vec<&str> = bitcoind_rpc_info.rsplitn(2, "@").collect(); if bitcoind_rpc_info_parts.len() != 2 { println!("ERROR: bad bitcoind RPC URL provided"); return Err(()); } - let rpc_user_and_password: Vec<&str> = bitcoind_rpc_info_parts[0].split(":").collect(); + let rpc_user_and_password: Vec<&str> = bitcoind_rpc_info_parts[1].split(":").collect(); if rpc_user_and_password.len() != 2 { println!("ERROR: bad bitcoind RPC username/password combo provided"); return Err(()); } let bitcoind_rpc_username = rpc_user_and_password[0].to_string(); let bitcoind_rpc_password = rpc_user_and_password[1].to_string(); - let bitcoind_rpc_path: Vec<&str> = bitcoind_rpc_info_parts[1].split(":").collect(); + let bitcoind_rpc_path: Vec<&str> = bitcoind_rpc_info_parts[0].split(":").collect(); if bitcoind_rpc_path.len() != 2 { println!("ERROR: bad bitcoind RPC path provided"); return Err(()); @@ -70,7 +70,10 @@ pub(crate) fn parse_startup_args() -> Result { let mut ldk_peer_port_set = true; let ldk_peer_listening_port: u16 = match env::args().skip(3).next().map(|p| p.parse()) { Some(Ok(p)) => p, - Some(Err(e)) => panic!("{}", e), + Some(Err(_)) => { + ldk_peer_port_set = false; + 9735 + } None => { ldk_peer_port_set = false; 9735 @@ -84,7 +87,9 @@ pub(crate) fn parse_startup_args() -> Result { 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(_) => panic!("Unsupported network provided. Options are: `regtest`, `testnet`"), + Some(net) => { + panic!("Unsupported network provided. Options are: `regtest`, `testnet`. Got {}", net); + } None => Network::Testnet, }; @@ -136,8 +141,7 @@ pub(crate) fn parse_startup_args() -> Result { pub(crate) async fn poll_for_user_input( peer_manager: Arc, channel_manager: Arc, - keys_manager: Arc, - router: Arc, Arc>>, + keys_manager: Arc, network_graph: Arc, inbound_payments: PaymentInfoStorage, outbound_payments: PaymentInfoStorage, ldk_data_dir: String, logger: Arc, network: Network, ) { @@ -238,30 +242,75 @@ pub(crate) async fn poll_for_user_input( }; let last_hops = invoice.route_hints(); - let amt_pico_btc = invoice.amount_pico_btc(); - if amt_pico_btc.is_none() { + let amt_msat = invoice.amount_milli_satoshis(); + if amt_msat.is_none() { println!("ERROR: invalid invoice: must contain amount to pay"); print!("> "); io::stdout().flush().unwrap(); continue; } - let amt_msat = amt_pico_btc.unwrap() / 10; let payee_pubkey = invoice.recover_payee_pub_key(); let final_cltv = invoice.min_final_cltv_expiry() as u32; let payment_hash = PaymentHash(invoice.payment_hash().clone().into_inner()); - let payment_secret = invoice.payment_secret().cloned(); + let payment_secret = Some(invoice.payment_secret().clone()); let invoice_features = invoice.features().cloned(); send_payment( payee_pubkey, - amt_msat, + amt_msat.unwrap(), final_cltv, payment_hash, payment_secret, invoice_features, last_hops, - router.clone(), + network_graph.clone(), + channel_manager.clone(), + outbound_payments.clone(), + logger.clone(), + ); + } + "keysend" => { + let dest_pubkey = match words.next() { + Some(dest) => match hex_utils::to_compressed_pubkey(dest) { + 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; + } + }; + let amt_msat_str = match words.next() { + Some(amt) => amt, + None => { + println!("ERROR: keysend requires an amount in millisatoshis: `keysend `"); + + print!("> "); + io::stdout().flush().unwrap(); + continue; + } + }; + let amt_msat: u64 = match amt_msat_str.parse() { + Ok(amt) => amt, + Err(e) => { + println!("ERROR: couldn't parse amount_msat: {}", e); + print!("> "); + io::stdout().flush().unwrap(); + continue; + } + }; + keysend( + dest_pubkey, + amt_msat, + network_graph.clone(), channel_manager.clone(), outbound_payments.clone(), logger.clone(), @@ -360,6 +409,24 @@ pub(crate) async fn poll_for_user_input( } "nodeinfo" => node_info(channel_manager.clone(), peer_manager.clone()), "listpeers" => list_peers(peer_manager.clone()), + "signmessage" => { + const MSG_STARTPOS: usize = "signmsg".len() + 1; + if line.as_bytes().len() <= MSG_STARTPOS { + println!("ERROR: signmsg requires a message"); + print!("> "); + io::stdout().flush().unwrap(); + continue; + } + println!( + "{:?}", + lightning::util::message_signing::sign( + &line.as_bytes()[MSG_STARTPOS..], + &keys_manager.get_node_secret() + ) + ); + print!("> "); + io::stdout().flush().unwrap(); + } _ => println!("Unknown command. See `\"help\" for available commands."), } } @@ -379,6 +446,7 @@ fn help() { println!("forceclosechannel "); println!("nodeinfo"); println!("listpeers"); + println!("signmessage "); } fn node_info(channel_manager: Arc, peer_manager: Arc) { @@ -409,13 +477,18 @@ fn list_channels(channel_manager: Arc) { } println!( "\t\tpeer_pubkey: {},", - hex_utils::hex_str(&chan_info.remote_network_id.serialize()) + hex_utils::hex_str(&chan_info.counterparty.node_id.serialize()) ); if let Some(id) = chan_info.short_channel_id { println!("\t\tshort_channel_id: {},", id); } println!("\t\tis_confirmed_onchain: {},", chan_info.is_funding_locked); println!("\t\tchannel_value_satoshis: {},", chan_info.channel_value_satoshis); + println!( + "\t\tlocal_balance_msat: {},", + chan_info.outbound_capacity_msat + + chan_info.unspendable_punishment_reserve.unwrap_or(0) * 1000 + ); 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); @@ -531,27 +604,29 @@ fn open_channel( } fn send_payment( - payee: PublicKey, amt_msat: u64, final_cltv: u32, payment_hash: PaymentHash, + payee_pubkey: PublicKey, amt_msat: u64, final_cltv: u32, payment_hash: PaymentHash, payment_secret: Option, payee_features: Option, - route_hints: Vec<&RouteHint>, - router: Arc, Arc>>, + route_hints: Vec, network_graph: Arc, channel_manager: Arc, payment_storage: PaymentInfoStorage, logger: Arc, ) { - let network_graph = router.network_graph.read().unwrap(); let first_hops = channel_manager.list_usable_channels(); let payer_pubkey = channel_manager.get_our_node_id(); - let route = router::get_route( + let mut payee = Payee::from_node_id(payee_pubkey).with_route_hints(route_hints); + if let Some(features) = payee_features { + payee = payee.with_features(features); + } + let params = + RouteParameters { payee, final_value_msat: amt_msat, final_cltv_expiry_delta: final_cltv }; + + let route = router::find_route( &payer_pubkey, + ¶ms, &network_graph, - &payee, - payee_features, Some(&first_hops.iter().collect::>()), - &route_hints, - amt_msat, - final_cltv, logger, + &Scorer::default(), ); if let Err(e) = route { println!("ERROR: failed to find route: {}", e.err); @@ -559,8 +634,8 @@ fn send_payment( } let status = match channel_manager.send_payment(&route.unwrap(), payment_hash, &payment_secret) { - Ok(()) => { - println!("EVENT: initiated sending {} msats to {}", amt_msat, payee); + Ok(_payment_id) => { + println!("EVENT: initiated sending {} msats to {}", amt_msat, payee_pubkey); HTLCStatus::Pending } Err(e) => { @@ -580,6 +655,45 @@ fn send_payment( ); } +fn keysend( + payee_pubkey: PublicKey, amt_msat: u64, network_graph: Arc, + channel_manager: Arc, payment_storage: PaymentInfoStorage, + logger: Arc, +) { + 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::default(), + ) { + Ok(r) => r, + Err(e) => { + println!("ERROR: failed to find route: {}", e.err); + return; + } + }; + + let mut payments = payment_storage.lock().unwrap(); + let payment_hash = channel_manager.send_spontaneous_payment(&route, None).unwrap().0; + payments.insert( + payment_hash, + PaymentInfo { + preimage: None, + secret: None, + status: HTLCStatus::Pending, + amt_msat: MillisatAmount(Some(amt_msat)), + }, + ); +} + fn get_invoice( amt_msat: u64, payment_storage: PaymentInfoStorage, channel_manager: Arc, keys_manager: Arc, network: Network, @@ -613,7 +727,7 @@ fn get_invoice( payment_hash, PaymentInfo { preimage: None, - secret: invoice.payment_secret().cloned(), + secret: Some(invoice.payment_secret().clone()), status: HTLCStatus::Pending, amt_msat: MillisatAmount(Some(amt_msat)), },