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::chain::keysinterface::{KeysInterface, KeysManager, Recipient};
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::routing::network_graph::{NetworkGraph, NodeId};
use lightning::util::config::{ChannelConfig, ChannelHandshakeLimits, UserConfig};
use lightning::util::events::EventHandler;
use lightning_invoice::payment::PaymentError;
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 {
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,
};
pub(crate) async fn poll_for_user_input<E: EventHandler>(
invoice_payer: Arc<InvoicePayer<E>>, peer_manager: Arc<PeerManager>,
channel_manager: Arc<ChannelManager>, keys_manager: Arc<KeysManager>,
- network_graph: Arc<NetworkGraph>, scorer: Arc<Mutex<Scorer>>,
- inbound_payments: PaymentInfoStorage, outbound_payments: PaymentInfoStorage,
- ldk_data_dir: String, logger: Arc<FilesystemLogger>, network: Network,
+ network_graph: Arc<NetworkGraph>, inbound_payments: PaymentInfoStorage,
+ outbound_payments: PaymentInfoStorage, ldk_data_dir: String, network: Network,
) {
println!("LDK startup successful. To view available commands: \"help\".");
println!("LDK logs are available at <your-supplied-ldk-data-dir-path>/.ldk/logs");
Some(amt) => amt,
None => {
println!("ERROR: keysend requires an amount in millisatoshis: `keysend <dest_pubkey> <amt_msat>`");
-
continue;
}
};
}
};
keysend(
+ &*invoice_payer,
dest_pubkey,
amt_msat,
- network_graph.clone(),
- channel_manager.clone(),
+ &*keys_manager,
outbound_payments.clone(),
- logger.clone(),
- scorer.clone(),
);
}
"getinvoice" => {
println!("SUCCESS: connected to peer {}", pubkey);
}
}
- "listchannels" => list_channels(channel_manager.clone()),
+ "listchannels" => list_channels(&channel_manager, &network_graph),
"listpayments" => {
list_payments(inbound_payments.clone(), outbound_payments.clone())
}
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");
+ if channel_id_vec.is_none() || channel_id_vec.as_ref().unwrap().len() != 32 {
+ println!("ERROR: couldn't parse channel_id");
continue;
}
let mut channel_id = [0; 32];
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");
+ if channel_id_vec.is_none() || channel_id_vec.as_ref().unwrap().len() != 32 {
+ println!("ERROR: couldn't parse channel_id");
continue;
}
let mut channel_id = [0; 32];
channel_id.copy_from_slice(&channel_id_vec.unwrap());
force_close_channel(channel_id, channel_manager.clone());
}
- "nodeinfo" => node_info(channel_manager.clone(), peer_manager.clone()),
+ "nodeinfo" => node_info(&channel_manager, &peer_manager),
"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");
continue;
"{:?}",
lightning::util::message_signing::sign(
&line.as_bytes()[MSG_STARTPOS..],
- &keys_manager.get_node_secret()
+ &keys_manager.get_node_secret(Recipient::Node).unwrap()
)
);
}
fn help() {
println!("openchannel pubkey@host:port <amt_satoshis>");
println!("sendpayment <invoice>");
+ println!("keysend <dest_pubkey> <amt_msat>");
println!("getinvoice <amt_millisatoshis>");
println!("connectpeer pubkey@host:port");
println!("listchannels");
println!("signmessage <message>");
}
-fn node_info(channel_manager: Arc<ChannelManager>, peer_manager: Arc<PeerManager>) {
+fn node_info(channel_manager: &Arc<ChannelManager>, peer_manager: &Arc<PeerManager>) {
println!("\t{{");
println!("\t\t node_pubkey: {}", channel_manager.get_our_node_id());
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::<u64>();
+ let local_balance_msat = chans.iter().map(|c| c.balance_msat).sum::<u64>();
println!("\t\t local_balance_msat: {}", local_balance_msat);
println!("\t\t num_peers: {}", peer_manager.get_peer_node_ids().len());
println!("\t}},");
println!("\t}},");
}
-fn list_channels(channel_manager: Arc<ChannelManager>) {
+/// Takes some untrusted bytes and returns a sanitized string that is safe to print
+fn sanitize_string(bytes: &[u8]) -> String {
+ let mut ret = String::with_capacity(bytes.len());
+ // We should really support some sane subset of UTF-8 here, but limiting to printable ASCII
+ // instead makes this trivial.
+ for b in bytes {
+ if *b >= 0x20 && *b <= 0x7e {
+ ret.push(*b as char);
+ }
+ }
+ ret
+}
+
+fn list_channels(channel_manager: &Arc<ChannelManager>, network_graph: &Arc<NetworkGraph>) {
print!("[");
for chan_info in channel_manager.list_channels() {
println!("");
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.counterparty.node_id.serialize())
);
+ if let Some(node_info) = network_graph
+ .read_only()
+ .nodes()
+ .get(&NodeId::from_pubkey(&chan_info.counterparty.node_id))
+ {
+ if let Some(announcement) = &node_info.announcement_info {
+ println!("\t\tpeer_alias: {}", sanitize_string(&announcement.alias));
+ }
+ }
+
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
- );
+ println!("\t\tlocal_balance_msat: {},", chan_info.balance_msat);
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);
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<PeerManager>,
+) -> Result<(), ()> {
match lightning_net_tokio::connect_outbound(Arc::clone(&peer_manager), pubkey, peer_addr).await
{
Some(connection_closed_future) => {
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(
);
}
-fn keysend(
- payee_pubkey: PublicKey, amt_msat: u64, network_graph: Arc<NetworkGraph>,
- channel_manager: Arc<ChannelManager>, payment_storage: PaymentInfoStorage,
- logger: Arc<FilesystemLogger>, scorer: Arc<Mutex<Scorer>>,
+fn keysend<E: EventHandler, K: KeysInterface>(
+ invoice_payer: &InvoicePayer<E>, 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::<Vec<_>>()),
- 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)),
},
);
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,