X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=src%2Fcli.rs;h=8033cdada51642a48e64b5eee947da7d956c18fc;hb=432106edae85e3e5edc43e2a7c9053ac2d1e5532;hp=d22f549db02b40761ae638da1f179f1d05764b35;hpb=b17e2989950b4e6f3ac68369b6b18387d760692c;p=ldk-sample diff --git a/src/cli.rs b/src/cli.rs index d22f549..8033cda 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,8 +1,8 @@ use crate::disk; use crate::hex_utils; use crate::{ - ChannelManager, FilesystemLogger, HTLCDirection, HTLCStatus, PaymentInfoStorage, PeerManager, - SatoshiAmount, + ChannelManager, FilesystemLogger, HTLCStatus, MillisatAmount, PaymentInfo, PaymentInfoStorage, + PeerManager, }; use bitcoin::hashes::sha256::Hash as Sha256Hash; use bitcoin::hashes::Hash; @@ -16,6 +16,7 @@ use lightning::routing::network_graph::{NetGraphMsgHandler, RoutingFees}; use lightning::routing::router; use lightning::routing::router::RouteHint; use lightning::util::config::UserConfig; +use lightning_invoice::{Currency, Invoice, InvoiceBuilder, Route, RouteHop}; use rand; use rand::Rng; use std::env; @@ -101,8 +102,9 @@ pub(crate) fn parse_startup_args() -> Result { pub(crate) async fn poll_for_user_input( peer_manager: Arc, channel_manager: Arc, router: Arc, Arc>>, - payment_storage: PaymentInfoStorage, node_privkey: SecretKey, event_notifier: mpsc::Sender<()>, - ldk_data_dir: String, logger: Arc, network: Network, + inbound_payments: PaymentInfoStorage, outbound_payments: PaymentInfoStorage, + node_privkey: SecretKey, event_notifier: mpsc::Sender<()>, ldk_data_dir: String, + logger: Arc, network: Network, ) { println!("LDK startup successful. To view available commands: \"help\".\nLDK logs are available at /.ldk/logs"); let stdin = io::stdin(); @@ -193,7 +195,7 @@ pub(crate) async fn poll_for_user_input( continue; } - let invoice_res = lightning_invoice::Invoice::from_str(invoice_str.unwrap()); + let invoice_res = Invoice::from_str(invoice_str.unwrap()); if invoice_res.is_err() { println!("ERROR: invalid invoice: {:?}", invoice_res.unwrap_err()); print!("> "); @@ -201,7 +203,7 @@ pub(crate) async fn poll_for_user_input( continue; } let invoice = invoice_res.unwrap(); - let route_hints: Vec = + let route_hints: Vec = invoice.routes().iter().map(|&route| route.clone()).collect(); let amt_pico_btc = invoice.amount_pico_btc(); @@ -278,29 +280,29 @@ pub(crate) async fn poll_for_user_input( route_hints, router.clone(), channel_manager.clone(), - payment_storage.clone(), + outbound_payments.clone(), logger.clone(), ); } "getinvoice" => { let amt_str = words.next(); if amt_str.is_none() { - println!("ERROR: getinvoice requires an amount in satoshis"); + println!("ERROR: getinvoice requires an amount in millisatoshis"); print!("> "); io::stdout().flush().unwrap(); continue; } - let amt_sat: Result = amt_str.unwrap().parse(); - if amt_sat.is_err() { + 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( - amt_sat.unwrap(), - payment_storage.clone(), + amt_msat.unwrap(), + inbound_payments.clone(), node_privkey.clone(), channel_manager.clone(), network, @@ -336,7 +338,9 @@ pub(crate) async fn poll_for_user_input( } } "listchannels" => list_channels(channel_manager.clone()), - "listpayments" => list_payments(payment_storage.clone()), + "listpayments" => { + list_payments(inbound_payments.clone(), outbound_payments.clone()) + } "closechannel" => { let channel_id_str = words.next(); if channel_id_str.is_none() { @@ -386,7 +390,7 @@ pub(crate) async fn poll_for_user_input( fn help() { println!("openchannel pubkey@host:port "); println!("sendpayment "); - println!("getinvoice "); + println!("getinvoice "); println!("connectpeer pubkey@host:port"); println!("listchannels"); println!("listpayments"); @@ -419,22 +423,37 @@ fn list_channels(channel_manager: Arc) { println!("]"); } -fn list_payments(payment_storage: PaymentInfoStorage) { - let payments = payment_storage.lock().unwrap(); +fn list_payments(inbound_payments: PaymentInfoStorage, outbound_payments: PaymentInfoStorage) { + let inbound = inbound_payments.lock().unwrap(); + let outbound = outbound_payments.lock().unwrap(); print!("["); - for (payment_hash, payment_info) in payments.deref() { - let direction_str = match payment_info.1 { - HTLCDirection::Inbound => "inbound", - HTLCDirection::Outbound => "outbound", - }; + for (payment_hash, payment_info) in inbound.deref() { println!(""); println!("\t{{"); - println!("\t\tamount_satoshis: {},", payment_info.3); + println!("\t\tamount_millisatoshis: {},", payment_info.amt_msat); println!("\t\tpayment_hash: {},", hex_utils::hex_str(&payment_hash.0)); - println!("\t\thtlc_direction: {},", direction_str); + println!("\t\thtlc_direction: inbound,"); println!( "\t\thtlc_status: {},", - match payment_info.2 { + match payment_info.status { + HTLCStatus::Pending => "pending", + HTLCStatus::Succeeded => "succeeded", + HTLCStatus::Failed => "failed", + } + ); + + println!("\t}},"); + } + + for (payment_hash, payment_info) in outbound.deref() { + println!(""); + println!("\t{{"); + println!("\t\tamount_millisatoshis: {},", payment_info.amt_msat); + println!("\t\tpayment_hash: {},", hex_utils::hex_str(&payment_hash.0)); + println!("\t\thtlc_direction: outbound,"); + println!( + "\t\thtlc_status: {},", + match payment_info.status { HTLCStatus::Pending => "pending", HTLCStatus::Succeeded => "succeeded", HTLCStatus::Failed => "failed", @@ -504,7 +523,7 @@ fn open_channel( fn send_payment( payee: PublicKey, amt_msat: u64, final_cltv: u32, payment_hash: PaymentHash, payment_secret: Option, payee_features: Option, - mut route_hints: Vec, + mut route_hints: Vec, router: Arc, Arc>>, channel_manager: Arc, payment_storage: PaymentInfoStorage, logger: Arc, @@ -558,12 +577,17 @@ fn send_payment( let mut payments = payment_storage.lock().unwrap(); payments.insert( payment_hash, - (None, HTLCDirection::Outbound, status, SatoshiAmount(Some(amt_msat / 1000))), + PaymentInfo { + preimage: None, + secret: payment_secret, + status, + amt_msat: MillisatAmount(Some(amt_msat)), + }, ); } fn get_invoice( - amt_sat: u64, payment_storage: PaymentInfoStorage, our_node_privkey: SecretKey, + amt_msat: u64, payment_storage: PaymentInfoStorage, our_node_privkey: SecretKey, channel_manager: Arc, network: Network, ) { let mut payments = payment_storage.lock().unwrap(); @@ -574,20 +598,21 @@ fn get_invoice( let payment_hash = Sha256Hash::hash(&preimage); let our_node_pubkey = channel_manager.get_our_node_id(); - let mut invoice = lightning_invoice::InvoiceBuilder::new(match network { - Network::Bitcoin => lightning_invoice::Currency::Bitcoin, - Network::Testnet => lightning_invoice::Currency::BitcoinTestnet, - Network::Regtest => lightning_invoice::Currency::Regtest, + let mut invoice = InvoiceBuilder::new(match network { + Network::Bitcoin => Currency::Bitcoin, + Network::Testnet => Currency::BitcoinTestnet, + Network::Regtest => Currency::Regtest, Network::Signet => panic!("Signet invoices not supported"), }) .payment_hash(payment_hash) .description("rust-lightning-bitcoinrpc invoice".to_string()) - .amount_pico_btc(amt_sat * 10_000) + .amount_pico_btc(amt_msat * 10) .current_timestamp() .payee_pub_key(our_node_pubkey); // Add route hints to the invoice. let our_channels = channel_manager.list_usable_channels(); + let mut min_final_cltv_expiry = 9; for channel in our_channels { let short_channel_id = match channel.short_channel_id { Some(id) => id.to_be_bytes(), @@ -597,7 +622,10 @@ fn get_invoice( Some(info) => info, None => continue, }; - invoice = invoice.route(vec![lightning_invoice::RouteHop { + if forwarding_info.cltv_expiry_delta > min_final_cltv_expiry { + min_final_cltv_expiry = forwarding_info.cltv_expiry_delta; + } + invoice = invoice.route(vec![RouteHop { pubkey: channel.remote_network_id, short_channel_id, fee_base_msat: forwarding_info.fee_base_msat, @@ -605,6 +633,7 @@ fn get_invoice( cltv_expiry_delta: forwarding_info.cltv_expiry_delta, }]); } + invoice = invoice.min_final_cltv_expiry(min_final_cltv_expiry.into()); // Sign the invoice. let invoice = @@ -617,12 +646,15 @@ fn get_invoice( payments.insert( PaymentHash(payment_hash.into_inner()), - ( - Some(PaymentPreimage(preimage)), - HTLCDirection::Inbound, - HTLCStatus::Pending, - SatoshiAmount(Some(amt_sat)), - ), + PaymentInfo { + preimage: Some(PaymentPreimage(preimage)), + // We can't add payment secrets to invoices until we support features in invoices. + // Otherwise lnd errors with "destination hop doesn't understand payment addresses" + // (for context, lnd calls payment secrets "payment addresses"). + secret: None, + status: HTLCStatus::Pending, + amt_msat: MillisatAmount(Some(amt_msat)), + }, ); }