X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=src%2Fcli.rs;h=e2f4a6379235c0d6da53a0a5d7464993983189b1;hb=5ec1c3f55f5c6794972846ccbe6dc67440313dab;hp=56c788fa3b72e2042c8b68d09b58d62acac70cdd;hpb=b630ab9aecd9cae3ca264aaafb1af70ce81de0d7;p=ldk-sample diff --git a/src/cli.rs b/src/cli.rs index 56c788f..e2f4a63 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, HTLCDirection, HTLCStatus, MillisatAmount, PaymentInfo, + PaymentInfoStorage, PeerManager, }; use bitcoin::hashes::sha256::Hash as Sha256Hash; use bitcoin::hashes::Hash; @@ -12,8 +12,9 @@ use bitcoin::secp256k1::Secp256k1; use lightning::chain; use lightning::ln::channelmanager::{PaymentHash, PaymentPreimage, PaymentSecret}; use lightning::ln::features::InvoiceFeatures; -use lightning::routing::network_graph::NetGraphMsgHandler; +use lightning::routing::network_graph::{NetGraphMsgHandler, RoutingFees}; use lightning::routing::router; +use lightning::routing::router::RouteHint; use lightning::util::config::UserConfig; use rand; use rand::Rng; @@ -156,7 +157,6 @@ pub(crate) async fn poll_for_user_input( continue; }; - // let private_channel = match words.next().as_ref().map(String::as_str) { let announce_channel = match words.next() { Some("--public") | Some("--public=true") => true, Some("--public=false") => false, @@ -201,6 +201,8 @@ pub(crate) async fn poll_for_user_input( continue; } let invoice = invoice_res.unwrap(); + let route_hints: Vec = + invoice.routes().iter().map(|&route| route.clone()).collect(); let amt_pico_btc = invoice.amount_pico_btc(); if amt_pico_btc.is_none() { @@ -273,6 +275,7 @@ pub(crate) async fn poll_for_user_input( payment_hash, payment_secret, invoice_features_opt, + route_hints, router.clone(), channel_manager.clone(), payment_storage.clone(), @@ -282,21 +285,21 @@ pub(crate) async fn poll_for_user_input( "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(), + amt_msat.unwrap(), payment_storage.clone(), node_privkey.clone(), channel_manager.clone(), @@ -383,7 +386,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"); @@ -420,18 +423,18 @@ fn list_payments(payment_storage: PaymentInfoStorage) { let payments = payment_storage.lock().unwrap(); print!("["); for (payment_hash, payment_info) in payments.deref() { - let direction_str = match payment_info.1 { + let direction_str = match payment_info.direction { HTLCDirection::Inbound => "inbound", HTLCDirection::Outbound => "outbound", }; 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_status: {},", - match payment_info.2 { + match payment_info.status { HTLCStatus::Pending => "pending", HTLCStatus::Succeeded => "succeeded", HTLCStatus::Failed => "failed", @@ -501,6 +504,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, router: Arc, Arc>>, channel_manager: Arc, payment_storage: PaymentInfoStorage, logger: Arc, @@ -509,13 +513,29 @@ fn send_payment( let first_hops = channel_manager.list_usable_channels(); let payer_pubkey = channel_manager.get_our_node_id(); + let mut hints: Vec = Vec::new(); + for route in route_hints.drain(..) { + let route_hops = route.into_inner(); + let last_hop = &route_hops[route_hops.len() - 1]; + hints.push(RouteHint { + src_node_id: last_hop.pubkey, + short_channel_id: u64::from_be_bytes(last_hop.short_channel_id), + fees: RoutingFees { + base_msat: last_hop.fee_base_msat, + proportional_millionths: last_hop.fee_proportional_millionths, + }, + cltv_expiry_delta: last_hop.cltv_expiry_delta, + htlc_minimum_msat: None, + htlc_maximum_msat: None, + }) + } let route = router::get_route( &payer_pubkey, &network_graph, &payee, payee_features, Some(&first_hops.iter().collect::>()), - &vec![], + &hints.iter().collect::>(), amt_msat, final_cltv, logger, @@ -538,12 +558,18 @@ 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, + direction: HTLCDirection::Outbound, + 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(); @@ -562,12 +588,13 @@ fn get_invoice( }) .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(), @@ -577,7 +604,9 @@ fn get_invoice( Some(info) => info, None => continue, }; - println!("VMW: adding routehop, info.fee base: {}", forwarding_info.fee_base_msat); + if forwarding_info.cltv_expiry_delta > min_final_cltv_expiry { + min_final_cltv_expiry = forwarding_info.cltv_expiry_delta; + } invoice = invoice.route(vec![lightning_invoice::RouteHop { pubkey: channel.remote_network_id, short_channel_id, @@ -586,6 +615,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 = @@ -598,12 +628,16 @@ 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, + direction: HTLCDirection::Inbound, + status: HTLCStatus::Pending, + amt_msat: MillisatAmount(Some(amt_msat)), + }, ); }