From: Jeffrey Czyz Date: Tue, 2 Nov 2021 20:31:28 +0000 (-0500) Subject: Use InvoicePayer in send_payment X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=commitdiff_plain;h=7a39a4df612ae282fcf2329be8fc1bf8da8a84db;p=ldk-sample Use InvoicePayer in send_payment --- diff --git a/src/cli.rs b/src/cli.rs index 610acb6..54b2eab 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,21 +1,22 @@ use crate::disk; use crate::hex_utils; use crate::{ - ChannelManager, FilesystemLogger, HTLCStatus, MillisatAmount, PaymentInfo, PaymentInfoStorage, - PeerManager, + ChannelManager, FilesystemLogger, HTLCStatus, InvoicePayer, MillisatAmount, PaymentInfo, + PaymentInfoStorage, PeerManager, }; use bitcoin::hashes::Hash; use bitcoin::network::constants::Network; use bitcoin::secp256k1::key::PublicKey; use lightning::chain::keysinterface::{KeysInterface, KeysManager}; -use lightning::ln::features::InvoiceFeatures; use lightning::ln::msgs::NetAddress; -use lightning::ln::{PaymentHash, PaymentSecret}; +use lightning::ln::PaymentHash; use lightning::routing::network_graph::NetworkGraph; use lightning::routing::router; -use lightning::routing::router::{Payee, RouteHint, RouteParameters}; +use lightning::routing::router::{Payee, RouteParameters}; use lightning::routing::scorer::Scorer; use lightning::util::config::{ChannelConfig, ChannelHandshakeLimits, UserConfig}; +use lightning::util::events::EventHandler; +use lightning_invoice::payment::PaymentError; use lightning_invoice::{utils, Currency, Invoice}; use std::env; use std::io; @@ -24,7 +25,7 @@ use std::net::{IpAddr, SocketAddr, ToSocketAddrs}; use std::ops::Deref; use std::path::Path; use std::str::FromStr; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use std::time::Duration; pub(crate) struct LdkUserInfo { @@ -139,9 +140,10 @@ pub(crate) fn parse_startup_args() -> Result { }) } -pub(crate) async fn poll_for_user_input( - peer_manager: Arc, channel_manager: Arc, - keys_manager: Arc, network_graph: Arc, +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, ) { @@ -240,35 +242,8 @@ pub(crate) async fn poll_for_user_input( continue; } }; - let last_hops = invoice.route_hints(); - 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 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 = Some(invoice.payment_secret().clone()); - let invoice_features = invoice.features().cloned(); - - send_payment( - payee_pubkey, - amt_msat.unwrap(), - final_cltv, - payment_hash, - payment_secret, - invoice_features, - last_hops, - network_graph.clone(), - channel_manager.clone(), - outbound_payments.clone(), - logger.clone(), - ); + send_payment(&*invoice_payer, &invoice, outbound_payments.clone()); } "keysend" => { let dest_pubkey = match words.next() { @@ -314,6 +289,7 @@ pub(crate) async fn poll_for_user_input( channel_manager.clone(), outbound_payments.clone(), logger.clone(), + scorer.clone(), ); } "getinvoice" => { @@ -603,46 +579,36 @@ fn open_channel( } } -fn send_payment( - payee_pubkey: PublicKey, amt_msat: u64, final_cltv: u32, payment_hash: PaymentHash, - payment_secret: Option, payee_features: Option, - route_hints: Vec, network_graph: Arc, - channel_manager: Arc, payment_storage: PaymentInfoStorage, - logger: Arc, +fn send_payment( + invoice_payer: &InvoicePayer, invoice: &Invoice, payment_storage: PaymentInfoStorage, ) { - let first_hops = channel_manager.list_usable_channels(); - let payer_pubkey = channel_manager.get_our_node_id(); - - 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, - Some(&first_hops.iter().collect::>()), - logger, - &Scorer::default(), - ); - if let Err(e) = route { - println!("ERROR: failed to find route: {}", e.err); - return; - } - let status = match channel_manager.send_payment(&route.unwrap(), payment_hash, &payment_secret) - { + let status = match invoice_payer.pay_invoice(invoice) { Ok(_payment_id) => { + let payee_pubkey = invoice.recover_payee_pub_key(); + let amt_msat = invoice.amount_milli_satoshis().unwrap(); println!("EVENT: initiated sending {} msats to {}", amt_msat, payee_pubkey); + print!("> "); HTLCStatus::Pending } - Err(e) => { + Err(PaymentError::Invoice(e)) => { + println!("ERROR: invalid invoice: {}", 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 payment_hash = PaymentHash(invoice.payment_hash().clone().into_inner()); + let payment_secret = Some(invoice.payment_secret().clone()); + let mut payments = payment_storage.lock().unwrap(); payments.insert( payment_hash, @@ -650,7 +616,7 @@ fn send_payment( preimage: None, secret: payment_secret, status, - amt_msat: MillisatAmount(Some(amt_msat)), + amt_msat: MillisatAmount(invoice.amount_milli_satoshis()), }, ); } @@ -658,7 +624,7 @@ fn send_payment( fn keysend( payee_pubkey: PublicKey, amt_msat: u64, network_graph: Arc, channel_manager: Arc, payment_storage: PaymentInfoStorage, - logger: Arc, + logger: Arc, scorer: Arc>, ) { let first_hops = channel_manager.list_usable_channels(); let payer_pubkey = channel_manager.get_our_node_id(); @@ -672,7 +638,7 @@ fn keysend( &network_graph, Some(&first_hops.iter().collect::>()), logger, - &Scorer::default(), + &scorer.lock().unwrap(), ) { Ok(r) => r, Err(e) => { diff --git a/src/main.rs b/src/main.rs index b81e652..b85fb4b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -24,7 +24,8 @@ use lightning::ln::channelmanager::{ }; use lightning::ln::peer_handler::{IgnoringMessageHandler, MessageHandler, SimpleArcPeerManager}; use lightning::ln::{PaymentHash, PaymentPreimage, PaymentSecret}; -use lightning::routing::network_graph::NetGraphMsgHandler; +use lightning::routing::network_graph::{NetGraphMsgHandler, NetworkGraph}; +use lightning::routing::scorer::Scorer; use lightning::util::config::UserConfig; use lightning::util::events::{Event, PaymentPurpose}; use lightning::util::ser::ReadableArgs; @@ -33,6 +34,8 @@ use lightning_block_sync::init; use lightning_block_sync::poll; use lightning_block_sync::SpvClient; use lightning_block_sync::UnboundedCache; +use lightning_invoice::payment; +use lightning_invoice::utils::DefaultRouter; use lightning_net_tokio::SocketDescriptor; use lightning_persister::FilesystemPersister; use rand::{thread_rng, Rng}; @@ -95,6 +98,16 @@ pub(crate) type PeerManager = SimpleArcPeerManager< pub(crate) type ChannelManager = SimpleArcChannelManager; +pub(crate) type InvoicePayer = payment::InvoicePayer< + Arc, + Router, + Arc>, + Arc, + E, +>; + +type Router = DefaultRouter, Arc>; + async fn handle_ldk_events( channel_manager: Arc, bitcoind_client: Arc, keys_manager: Arc, inbound_payments: PaymentInfoStorage, @@ -203,9 +216,7 @@ async fn handle_ldk_events( Event::PaymentPathFailed { payment_hash, rejected_by_dest, - network_update: _, all_paths_failed, - path: _, short_channel_id, .. } => { @@ -220,7 +231,7 @@ async fn handle_ldk_events( if *rejected_by_dest { println!(": re-attempting the payment will not succeed"); } else { - println!(": payment may be retried"); + println!(": exhausted payment retry attempts"); } print!("> "); io::stdout().flush().unwrap(); @@ -584,14 +595,28 @@ async fn start_ldk() { event, )); }; - // Step 16: Persist ChannelManager + + // Step 16: Create InvoicePayer + let router = DefaultRouter::new(network_graph.clone(), logger.clone()); + let scorer = Arc::new(Mutex::new(Scorer::default())); + let invoice_payer = Arc::new(InvoicePayer::new( + channel_manager.clone(), + router, + scorer.clone(), + logger.clone(), + event_handler, + payment::RetryAttempts(5), + )); + + // Step 17: Persist ChannelManager let data_dir = ldk_data_dir.clone(); let persist_channel_manager_callback = move |node: &ChannelManager| FilesystemPersister::persist_manager(data_dir.clone(), &*node); - // Step 17: Background Processing + + // Step 18: Background Processing let background_processor = BackgroundProcessor::start( persist_channel_manager_callback, - event_handler, + invoice_payer.clone(), chain_monitor.clone(), channel_manager.clone(), Some(network_gossip.clone()), @@ -638,10 +663,12 @@ async fn start_ldk() { // Start the CLI. cli::poll_for_user_input( + invoice_payer.clone(), peer_manager.clone(), channel_manager.clone(), keys_manager.clone(), network_graph.clone(), + scorer.clone(), inbound_payments, outbound_payments, ldk_data_dir.clone(),