X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fln%2Foutbound_payment.rs;h=aa92043cf9124ba5c62778ebea0f4bd6dd9127e2;hb=7f4c473c88a0fd43de2b72bb368f244b63492dc6;hp=a13a618d83c4c2b0e8988f6103a1a26c2fcb2d63;hpb=f068df03c5c400ca7942e4491809239710ebe472;p=rust-lightning diff --git a/lightning/src/ln/outbound_payment.rs b/lightning/src/ln/outbound_payment.rs index a13a618d..aa92043c 100644 --- a/lightning/src/ln/outbound_payment.rs +++ b/lightning/src/ln/outbound_payment.rs @@ -16,8 +16,8 @@ use bitcoin::secp256k1::{self, Secp256k1, SecretKey}; use crate::sign::{EntropySource, NodeSigner, Recipient}; use crate::events::{self, PaymentFailureReason}; use crate::ln::{PaymentHash, PaymentPreimage, PaymentSecret}; -use crate::ln::channelmanager::{ChannelDetails, EventCompletionAction, HTLCSource, IDEMPOTENCY_TIMEOUT_TICKS, PaymentId}; -use crate::ln::onion_utils::HTLCFailReason; +use crate::ln::channelmanager::{ChannelDetails, EventCompletionAction, HTLCSource, PaymentId}; +use crate::ln::onion_utils::{DecodedOnionFailure, HTLCFailReason}; use crate::routing::router::{InFlightHtlcs, Path, PaymentParameters, Route, RouteParameters, Router}; use crate::util::errors::APIError; use crate::util::logger::Logger; @@ -32,12 +32,27 @@ use core::ops::Deref; use crate::prelude::*; use crate::sync::Mutex; +/// The number of ticks of [`ChannelManager::timer_tick_occurred`] until we time-out the idempotency +/// of payments by [`PaymentId`]. See [`OutboundPayments::remove_stale_payments`]. +/// +/// [`ChannelManager::timer_tick_occurred`]: crate::ln::channelmanager::ChannelManager::timer_tick_occurred +pub(crate) const IDEMPOTENCY_TIMEOUT_TICKS: u8 = 7; + +/// The number of ticks of [`ChannelManager::timer_tick_occurred`] until an invoice request without +/// a response is timed out. +/// +/// [`ChannelManager::timer_tick_occurred`]: crate::ln::channelmanager::ChannelManager::timer_tick_occurred +const INVOICE_REQUEST_TIMEOUT_TICKS: u8 = 3; + /// Stores the session_priv for each part of a payment that is still pending. For versions 0.0.102 /// and later, also stores information for retrying the payment. pub(crate) enum PendingOutboundPayment { Legacy { session_privs: HashSet<[u8; 32]>, }, + AwaitingInvoice { + timer_ticks_without_response: u8, + }, Retryable { retry_strategy: Option, attempts: PaymentAttempts, @@ -47,6 +62,7 @@ pub(crate) enum PendingOutboundPayment { payment_secret: Option, payment_metadata: Option>, keysend_preimage: Option, + custom_tlvs: Vec<(u64, Vec)>, pending_amt_msat: u64, /// Used to track the fee paid. Only present if the payment was serialized on 0.0.103+. pending_fee_msat: Option, @@ -107,6 +123,12 @@ impl PendingOutboundPayment { params.previously_failed_channels.push(scid); } } + fn is_awaiting_invoice(&self) -> bool { + match self { + PendingOutboundPayment::AwaitingInvoice { .. } => true, + _ => false, + } + } pub(super) fn is_fulfilled(&self) -> bool { match self { PendingOutboundPayment::Fulfilled { .. } => true, @@ -129,6 +151,7 @@ impl PendingOutboundPayment { fn payment_hash(&self) -> Option { match self { PendingOutboundPayment::Legacy { .. } => None, + PendingOutboundPayment::AwaitingInvoice { .. } => None, PendingOutboundPayment::Retryable { payment_hash, .. } => Some(*payment_hash), PendingOutboundPayment::Fulfilled { payment_hash, .. } => *payment_hash, PendingOutboundPayment::Abandoned { payment_hash, .. } => Some(*payment_hash), @@ -141,8 +164,11 @@ impl PendingOutboundPayment { PendingOutboundPayment::Legacy { session_privs } | PendingOutboundPayment::Retryable { session_privs, .. } | PendingOutboundPayment::Fulfilled { session_privs, .. } | - PendingOutboundPayment::Abandoned { session_privs, .. } - => session_privs, + PendingOutboundPayment::Abandoned { session_privs, .. } => session_privs, + PendingOutboundPayment::AwaitingInvoice { .. } => { + debug_assert!(false); + return; + }, }); let payment_hash = self.payment_hash(); *self = PendingOutboundPayment::Fulfilled { session_privs, payment_hash, timer_ticks_without_htlcs: 0 }; @@ -168,7 +194,11 @@ impl PendingOutboundPayment { PendingOutboundPayment::Fulfilled { session_privs, .. } | PendingOutboundPayment::Abandoned { session_privs, .. } => { session_privs.remove(session_priv) - } + }, + PendingOutboundPayment::AwaitingInvoice { .. } => { + debug_assert!(false); + false + }, }; if remove_res { if let PendingOutboundPayment::Retryable { ref mut pending_amt_msat, ref mut pending_fee_msat, .. } = self { @@ -188,6 +218,10 @@ impl PendingOutboundPayment { PendingOutboundPayment::Retryable { session_privs, .. } => { session_privs.insert(session_priv) } + PendingOutboundPayment::AwaitingInvoice { .. } => { + debug_assert!(false); + false + }, PendingOutboundPayment::Fulfilled { .. } => false, PendingOutboundPayment::Abandoned { .. } => false, }; @@ -209,7 +243,8 @@ impl PendingOutboundPayment { PendingOutboundPayment::Fulfilled { session_privs, .. } | PendingOutboundPayment::Abandoned { session_privs, .. } => { session_privs.len() - } + }, + PendingOutboundPayment::AwaitingInvoice { .. } => 0, } } } @@ -239,7 +274,7 @@ impl Retry { }, #[cfg(all(not(feature = "no-std"), not(test)))] (Retry::Timeout(max_duration), PaymentAttempts { first_attempted_at, .. }) => - *max_duration >= std::time::Instant::now().duration_since(*first_attempted_at), + *max_duration >= crate::util::time::MonotonicTime::now().duration_since(*first_attempted_at), #[cfg(all(not(feature = "no-std"), test))] (Retry::Timeout(max_duration), PaymentAttempts { first_attempted_at, .. }) => *max_duration >= SinceEpoch::now().duration_since(*first_attempted_at), @@ -274,7 +309,7 @@ pub(crate) struct PaymentAttemptsUsingTime { } #[cfg(not(any(feature = "no-std", test)))] -type ConfiguredTime = std::time::Instant; +type ConfiguredTime = crate::util::time::MonotonicTime; #[cfg(feature = "no-std")] type ConfiguredTime = crate::util::time::Eternity; #[cfg(all(not(feature = "no-std"), test))] @@ -414,9 +449,9 @@ pub struct RecipientOnionFields { /// If you do not have one, the [`Route`] you pay over must not contain multiple paths as /// multi-path payments require a recipient-provided secret. /// - /// Note that for spontaneous payments most lightning nodes do not currently support MPP - /// receives, thus you should generally never be providing a secret here for spontaneous - /// payments. + /// Some implementations may reject spontaneous payments with payment secrets, so you may only + /// want to provide a secret for a spontaneous payment if MPP is needed and you know your + /// recipient will not reject it. pub payment_secret: Option, /// The payment metadata serves a similar purpose as [`Self::payment_secret`] but is of /// arbitrary length. This gives recipients substantially more flexibility to receive @@ -431,10 +466,13 @@ pub struct RecipientOnionFields { /// [`Self::payment_secret`] and while nearly all lightning senders support secrets, metadata /// may not be supported as universally. pub payment_metadata: Option>, + /// See [`Self::custom_tlvs`] for more info. + pub(super) custom_tlvs: Vec<(u64, Vec)>, } impl_writeable_tlv_based!(RecipientOnionFields, { (0, payment_secret, option), + (1, custom_tlvs, optional_vec), (2, payment_metadata, option), }); @@ -443,16 +481,58 @@ impl RecipientOnionFields { /// set of onion fields for today's BOLT11 invoices - most nodes require a [`PaymentSecret`] /// but do not require or provide any further data. pub fn secret_only(payment_secret: PaymentSecret) -> Self { - Self { payment_secret: Some(payment_secret), payment_metadata: None } + Self { payment_secret: Some(payment_secret), payment_metadata: None, custom_tlvs: Vec::new() } } /// Creates a new [`RecipientOnionFields`] with no fields. This generally does not create - /// payable HTLCs except for spontaneous payments, i.e. this should generally only be used for - /// calls to [`ChannelManager::send_spontaneous_payment`]. + /// payable HTLCs except for single-path spontaneous payments, i.e. this should generally + /// only be used for calls to [`ChannelManager::send_spontaneous_payment`]. If you are sending + /// a spontaneous MPP this will not work as all MPP require payment secrets; you may + /// instead want to use [`RecipientOnionFields::secret_only`]. /// /// [`ChannelManager::send_spontaneous_payment`]: super::channelmanager::ChannelManager::send_spontaneous_payment + /// [`RecipientOnionFields::secret_only`]: RecipientOnionFields::secret_only pub fn spontaneous_empty() -> Self { - Self { payment_secret: None, payment_metadata: None } + Self { payment_secret: None, payment_metadata: None, custom_tlvs: Vec::new() } + } + + /// Creates a new [`RecipientOnionFields`] from an existing one, adding custom TLVs. Each + /// TLV is provided as a `(u64, Vec)` for the type number and serialized value + /// respectively. TLV type numbers must be unique and within the range + /// reserved for custom types, i.e. >= 2^16, otherwise this method will return `Err(())`. + /// + /// This method will also error for types in the experimental range which have been + /// standardized within the protocol, which only includes 5482373484 (keysend) for now. + /// + /// See [`Self::custom_tlvs`] for more info. + pub fn with_custom_tlvs(mut self, mut custom_tlvs: Vec<(u64, Vec)>) -> Result { + custom_tlvs.sort_unstable_by_key(|(typ, _)| *typ); + let mut prev_type = None; + for (typ, _) in custom_tlvs.iter() { + if *typ < 1 << 16 { return Err(()); } + if *typ == 5482373484 { return Err(()); } // keysend + match prev_type { + Some(prev) if prev >= *typ => return Err(()), + _ => {}, + } + prev_type = Some(*typ); + } + self.custom_tlvs = custom_tlvs; + Ok(self) + } + + /// Gets the custom TLVs that will be sent or have been received. + /// + /// Custom TLVs allow sending extra application-specific data with a payment. They provide + /// additional flexibility on top of payment metadata, as while other implementations may + /// require `payment_metadata` to reflect metadata provided in an invoice, custom TLVs + /// do not have this restriction. + /// + /// Note that if this field is non-empty, it will contain strictly increasing TLVs, each + /// represented by a `(u64, Vec)` for its type number and serialized value respectively. + /// This is validated when setting this field using [`Self::with_custom_tlvs`]. + pub fn custom_tlvs(&self) -> &Vec<(u64, Vec)> { + &self.custom_tlvs } /// When we have received some HTLC(s) towards an MPP payment, as we receive further HTLC(s) we @@ -465,11 +545,33 @@ impl RecipientOnionFields { pub(super) fn check_merge(&mut self, further_htlc_fields: &mut Self) -> Result<(), ()> { if self.payment_secret != further_htlc_fields.payment_secret { return Err(()); } if self.payment_metadata != further_htlc_fields.payment_metadata { return Err(()); } - // For custom TLVs we should just drop non-matching ones, but not reject the payment. + + let tlvs = &mut self.custom_tlvs; + let further_tlvs = &mut further_htlc_fields.custom_tlvs; + + let even_tlvs = tlvs.iter().filter(|(typ, _)| *typ % 2 == 0); + let further_even_tlvs = further_tlvs.iter().filter(|(typ, _)| *typ % 2 == 0); + if even_tlvs.ne(further_even_tlvs) { return Err(()) } + + tlvs.retain(|tlv| further_tlvs.iter().any(|further_tlv| tlv == further_tlv)); + further_tlvs.retain(|further_tlv| tlvs.iter().any(|tlv| tlv == further_tlv)); + Ok(()) } } +/// Arguments for [`super::channelmanager::ChannelManager::send_payment_along_path`]. +pub(super) struct SendAlongPathArgs<'a> { + pub path: &'a Path, + pub payment_hash: &'a PaymentHash, + pub recipient_onion: RecipientOnionFields, + pub total_value: u64, + pub cur_height: u32, + pub payment_id: PaymentId, + pub keysend_preimage: &'a Option, + pub session_priv_bytes: [u8; 32], +} + pub(super) struct OutboundPayments { pub(super) pending_outbound_payments: Mutex>, pub(super) retry_lock: Mutex<()>, @@ -496,8 +598,7 @@ impl OutboundPayments { NS::Target: NodeSigner, L::Target: Logger, IH: Fn() -> InFlightHtlcs, - SP: Fn(&Path, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId, - &Option, [u8; 32]) -> Result<(), APIError>, + SP: Fn(SendAlongPathArgs) -> Result<(), APIError>, { self.send_payment_internal(payment_id, payment_hash, recipient_onion, None, retry_strategy, route_params, router, first_hops, &compute_inflight_htlcs, entropy_source, node_signer, @@ -512,8 +613,7 @@ impl OutboundPayments { where ES::Target: EntropySource, NS::Target: NodeSigner, - F: Fn(&Path, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId, - &Option, [u8; 32]) -> Result<(), APIError> + F: Fn(SendAlongPathArgs) -> Result<(), APIError> { let onion_session_privs = self.add_new_pending_payment(payment_hash, recipient_onion.clone(), payment_id, None, route, None, None, entropy_source, best_block_height)?; self.pay_route_internal(route, payment_hash, recipient_onion, None, payment_id, None, @@ -534,8 +634,7 @@ impl OutboundPayments { NS::Target: NodeSigner, L::Target: Logger, IH: Fn() -> InFlightHtlcs, - SP: Fn(&Path, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId, - &Option, [u8; 32]) -> Result<(), APIError>, + SP: Fn(SendAlongPathArgs) -> Result<(), APIError>, { let preimage = payment_preimage .unwrap_or_else(|| PaymentPreimage(entropy_source.get_secure_random_bytes())); @@ -554,8 +653,7 @@ impl OutboundPayments { where ES::Target: EntropySource, NS::Target: NodeSigner, - F: Fn(&Path, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId, - &Option, [u8; 32]) -> Result<(), APIError> + F: Fn(SendAlongPathArgs) -> Result<(), APIError>, { let preimage = payment_preimage .unwrap_or_else(|| PaymentPreimage(entropy_source.get_secure_random_bytes())); @@ -584,8 +682,7 @@ impl OutboundPayments { R::Target: Router, ES::Target: EntropySource, NS::Target: NodeSigner, - SP: Fn(&Path, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId, - &Option, [u8; 32]) -> Result<(), APIError>, + SP: Fn(SendAlongPathArgs) -> Result<(), APIError>, IH: Fn() -> InFlightHtlcs, FH: Fn() -> Vec, L::Target: Logger, @@ -616,7 +713,7 @@ impl OutboundPayments { let mut outbounds = self.pending_outbound_payments.lock().unwrap(); outbounds.retain(|pmt_id, pmt| { let mut retain = true; - if !pmt.is_auto_retryable_now() && pmt.remaining_parts() == 0 { + if !pmt.is_auto_retryable_now() && pmt.remaining_parts() == 0 && !pmt.is_awaiting_invoice() { pmt.mark_abandoned(PaymentFailureReason::RetriesExhausted); if let PendingOutboundPayment::Abandoned { payment_hash, reason, .. } = pmt { pending_events.lock().unwrap().push_back((events::Event::PaymentFailed { @@ -634,7 +731,8 @@ impl OutboundPayments { pub(super) fn needs_abandon(&self) -> bool { let outbounds = self.pending_outbound_payments.lock().unwrap(); outbounds.iter().any(|(_, pmt)| - !pmt.is_auto_retryable_now() && pmt.remaining_parts() == 0 && !pmt.is_fulfilled()) + !pmt.is_auto_retryable_now() && pmt.remaining_parts() == 0 && !pmt.is_fulfilled() && + !pmt.is_awaiting_invoice()) } /// Errors immediately on [`RetryableSendFailure`] error conditions. Otherwise, further errors may @@ -655,29 +753,39 @@ impl OutboundPayments { NS::Target: NodeSigner, L::Target: Logger, IH: Fn() -> InFlightHtlcs, - SP: Fn(&Path, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId, - &Option, [u8; 32]) -> Result<(), APIError> + SP: Fn(SendAlongPathArgs) -> Result<(), APIError>, { #[cfg(feature = "std")] { if has_expired(&route_params) { + log_error!(logger, "Payment with id {} and hash {} had expired before we started paying", + payment_id, payment_hash); return Err(RetryableSendFailure::PaymentExpired) } } let route = router.find_route_with_id( &node_signer.get_node_id(Recipient::Node).unwrap(), &route_params, - Some(&first_hops.iter().collect::>()), &inflight_htlcs(), + Some(&first_hops.iter().collect::>()), inflight_htlcs(), payment_hash, payment_id, - ).map_err(|_| RetryableSendFailure::RouteNotFound)?; + ).map_err(|_| { + log_error!(logger, "Failed to find route for payment with id {} and hash {}", + payment_id, payment_hash); + RetryableSendFailure::RouteNotFound + })?; let onion_session_privs = self.add_new_pending_payment(payment_hash, recipient_onion.clone(), payment_id, keysend_preimage, &route, Some(retry_strategy), Some(route_params.payment_params.clone()), entropy_source, best_block_height) - .map_err(|_| RetryableSendFailure::DuplicatePayment)?; + .map_err(|_| { + log_error!(logger, "Payment with id {} is already pending. New payment had payment hash {}", + payment_id, payment_hash); + RetryableSendFailure::DuplicatePayment + })?; - let res = self.pay_route_internal(&route, payment_hash, recipient_onion, None, payment_id, None, + let res = self.pay_route_internal(&route, payment_hash, recipient_onion, keysend_preimage, payment_id, None, onion_session_privs, node_signer, best_block_height, &send_payment_along_path); - log_info!(logger, "Result sending payment with id {}: {:?}", log_bytes!(payment_id.0), res); + log_info!(logger, "Sending payment with id {} and hash {} returned {:?}", + payment_id, payment_hash, res); if let Err(e) = res { self.handle_pay_route_err(e, payment_id, payment_hash, route, route_params, router, first_hops, &inflight_htlcs, entropy_source, node_signer, best_block_height, logger, pending_events, &send_payment_along_path); } @@ -696,12 +804,11 @@ impl OutboundPayments { NS::Target: NodeSigner, L::Target: Logger, IH: Fn() -> InFlightHtlcs, - SP: Fn(&Path, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId, - &Option, [u8; 32]) -> Result<(), APIError> + SP: Fn(SendAlongPathArgs) -> Result<(), APIError>, { #[cfg(feature = "std")] { if has_expired(&route_params) { - log_error!(logger, "Payment params expired on retry, abandoning payment {}", log_bytes!(payment_id.0)); + log_error!(logger, "Payment params expired on retry, abandoning payment {}", &payment_id); self.abandon_payment(payment_id, PaymentFailureReason::PaymentExpired, pending_events); return } @@ -709,12 +816,12 @@ impl OutboundPayments { let route = match router.find_route_with_id( &node_signer.get_node_id(Recipient::Node).unwrap(), &route_params, - Some(&first_hops.iter().collect::>()), &inflight_htlcs(), + Some(&first_hops.iter().collect::>()), inflight_htlcs(), payment_hash, payment_id, ) { Ok(route) => route, Err(e) => { - log_error!(logger, "Failed to find a route on retry, abandoning payment {}: {:#?}", log_bytes!(payment_id.0), e); + log_error!(logger, "Failed to find a route on retry, abandoning payment {}: {:#?}", &payment_id, e); self.abandon_payment(payment_id, PaymentFailureReason::RouteNotFound, pending_events); return } @@ -754,7 +861,8 @@ impl OutboundPayments { hash_map::Entry::Occupied(mut payment) => { let res = match payment.get() { PendingOutboundPayment::Retryable { - total_msat, keysend_preimage, payment_secret, payment_metadata, pending_amt_msat, .. + total_msat, keysend_preimage, payment_secret, payment_metadata, + custom_tlvs, pending_amt_msat, .. } => { let retry_amt_msat = route.get_total_amount(); if retry_amt_msat + *pending_amt_msat > *total_msat * (100 + RETRY_OVERFLOW_PERCENTAGE) / 100 { @@ -765,12 +873,17 @@ impl OutboundPayments { (*total_msat, RecipientOnionFields { payment_secret: *payment_secret, payment_metadata: payment_metadata.clone(), + custom_tlvs: custom_tlvs.clone(), }, *keysend_preimage) }, PendingOutboundPayment::Legacy { .. } => { log_error!(logger, "Unable to retry payments that were initially sent on LDK versions prior to 0.0.102"); return }, + PendingOutboundPayment::AwaitingInvoice { .. } => { + log_error!(logger, "Payment not yet sent"); + return + }, PendingOutboundPayment::Fulfilled { .. } => { log_error!(logger, "Payment already completed"); return @@ -781,7 +894,7 @@ impl OutboundPayments { }, }; if !payment.get().is_retryable_now() { - log_error!(logger, "Retries exhausted for payment id {}", log_bytes!(payment_id.0)); + log_error!(logger, "Retries exhausted for payment id {}", &payment_id); abandon_with_entry!(payment, PaymentFailureReason::RetriesExhausted); return } @@ -792,7 +905,7 @@ impl OutboundPayments { res }, hash_map::Entry::Vacant(_) => { - log_error!(logger, "Payment with ID {} not found", log_bytes!(payment_id.0)); + log_error!(logger, "Payment with ID {} not found", &payment_id); return } } @@ -800,7 +913,7 @@ impl OutboundPayments { let res = self.pay_route_internal(&route, payment_hash, recipient_onion, keysend_preimage, payment_id, Some(total_msat), onion_session_privs, node_signer, best_block_height, &send_payment_along_path); - log_info!(logger, "Result retrying payment id {}: {:?}", log_bytes!(payment_id.0), res); + log_info!(logger, "Result retrying payment id {}: {:?}", &payment_id, res); if let Err(e) = res { self.handle_pay_route_err(e, payment_id, payment_hash, route, route_params, router, first_hops, inflight_htlcs, entropy_source, node_signer, best_block_height, logger, pending_events, send_payment_along_path); } @@ -818,8 +931,7 @@ impl OutboundPayments { NS::Target: NodeSigner, L::Target: Logger, IH: Fn() -> InFlightHtlcs, - SP: Fn(&Path, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId, - &Option, [u8; 32]) -> Result<(), APIError> + SP: Fn(SendAlongPathArgs) -> Result<(), APIError>, { match err { PaymentSendFailure::AllFailedResendSafe(errs) => { @@ -891,8 +1003,7 @@ impl OutboundPayments { where ES::Target: EntropySource, NS::Target: NodeSigner, - F: Fn(&Path, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId, - &Option, [u8; 32]) -> Result<(), APIError> + F: Fn(SendAlongPathArgs) -> Result<(), APIError>, { let payment_id = PaymentId(entropy_source.get_secure_random_bytes()); @@ -904,7 +1015,7 @@ impl OutboundPayments { })) } - let route = Route { paths: vec![path], payment_params: None }; + let route = Route { paths: vec![path], route_params: None }; let onion_session_privs = self.add_new_pending_payment(payment_hash, RecipientOnionFields::spontaneous_empty(), payment_id, None, &route, None, None, entropy_source, best_block_height)?; @@ -965,6 +1076,7 @@ impl OutboundPayments { payment_secret: recipient_onion.payment_secret, payment_metadata: recipient_onion.payment_metadata, keysend_preimage, + custom_tlvs: recipient_onion.custom_tlvs, starting_block_height: best_block_height, total_msat: route.get_total_amount(), }); @@ -978,6 +1090,21 @@ impl OutboundPayments { } } + #[allow(unused)] + pub(super) fn add_new_awaiting_invoice(&self, payment_id: PaymentId) -> Result<(), ()> { + let mut pending_outbounds = self.pending_outbound_payments.lock().unwrap(); + match pending_outbounds.entry(payment_id) { + hash_map::Entry::Occupied(_) => Err(()), + hash_map::Entry::Vacant(entry) => { + entry.insert(PendingOutboundPayment::AwaitingInvoice { + timer_ticks_without_response: 0, + }); + + Ok(()) + }, + } + } + fn pay_route_internal( &self, route: &Route, payment_hash: PaymentHash, recipient_onion: RecipientOnionFields, keysend_preimage: Option, payment_id: PaymentId, recv_value_msat: Option, @@ -986,8 +1113,7 @@ impl OutboundPayments { ) -> Result<(), PaymentSendFailure> where NS::Target: NodeSigner, - F: Fn(&Path, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId, - &Option, [u8; 32]) -> Result<(), APIError> + F: Fn(SendAlongPathArgs) -> Result<(), APIError>, { if route.paths.len() < 1 { return Err(PaymentSendFailure::ParameterError(APIError::InvalidRoute{err: "There must be at least one path to send over".to_owned()})); @@ -1028,9 +1154,11 @@ impl OutboundPayments { let cur_height = best_block_height + 1; let mut results = Vec::new(); debug_assert_eq!(route.paths.len(), onion_session_privs.len()); - for (path, session_priv) in route.paths.iter().zip(onion_session_privs.into_iter()) { - let mut path_res = send_payment_along_path(&path, &payment_hash, recipient_onion.clone(), - total_value, cur_height, payment_id, &keysend_preimage, session_priv); + for (path, session_priv_bytes) in route.paths.iter().zip(onion_session_privs.into_iter()) { + let mut path_res = send_payment_along_path(SendAlongPathArgs { + path: &path, payment_hash: &payment_hash, recipient_onion: recipient_onion.clone(), + total_value, cur_height, payment_id, keysend_preimage: &keysend_preimage, session_priv_bytes + }); match path_res { Ok(_) => {}, Err(APIError::MonitorUpdateInProgress) => { @@ -1041,7 +1169,7 @@ impl OutboundPayments { Err(_) => { let mut pending_outbounds = self.pending_outbound_payments.lock().unwrap(); if let Some(payment) = pending_outbounds.get_mut(&payment_id) { - let removed = payment.remove(&session_priv, Some(path)); + let removed = payment.remove(&session_priv_bytes, Some(path)); debug_assert!(removed, "This can't happen as the payment has an entry for this path added by callers"); } else { debug_assert!(false, "This can't happen as the payment was added by callers"); @@ -1071,9 +1199,9 @@ impl OutboundPayments { results, payment_id, failed_paths_retry: if pending_amt_unsent != 0 { - if let Some(payment_params) = &route.payment_params { + if let Some(payment_params) = route.route_params.as_ref().map(|p| p.payment_params.clone()) { Some(RouteParameters { - payment_params: payment_params.clone(), + payment_params: payment_params, final_value_msat: pending_amt_unsent, }) } else { None } @@ -1095,8 +1223,7 @@ impl OutboundPayments { ) -> Result<(), PaymentSendFailure> where NS::Target: NodeSigner, - F: Fn(&Path, &PaymentHash, RecipientOnionFields, u64, u32, PaymentId, - &Option, [u8; 32]) -> Result<(), APIError> + F: Fn(SendAlongPathArgs) -> Result<(), APIError>, { self.pay_route_internal(route, payment_hash, recipient_onion, keysend_preimage, payment_id, recv_value_msat, onion_session_privs, node_signer, best_block_height, @@ -1115,7 +1242,7 @@ impl OutboundPayments { pub(super) fn claim_htlc( &self, payment_id: PaymentId, payment_preimage: PaymentPreimage, session_priv: SecretKey, - path: Path, from_onchain: bool, + path: Path, from_onchain: bool, ev_completion_action: EventCompletionAction, pending_events: &Mutex)>>, logger: &L, ) where L::Target: Logger { @@ -1126,13 +1253,14 @@ impl OutboundPayments { if let hash_map::Entry::Occupied(mut payment) = outbounds.entry(payment_id) { if !payment.get().is_fulfilled() { let payment_hash = PaymentHash(Sha256::hash(&payment_preimage.0).into_inner()); + log_info!(logger, "Payment with id {} and hash {} sent!", payment_id, payment_hash); let fee_paid_msat = payment.get().get_pending_fee_msat(); pending_events.push_back((events::Event::PaymentSent { payment_id: Some(payment_id), payment_preimage, payment_hash, fee_paid_msat, - }, None)); + }, Some(ev_completion_action.clone()))); payment.get_mut().mark_fulfilled(); } @@ -1149,11 +1277,11 @@ impl OutboundPayments { payment_id, payment_hash, path, - }, None)); + }, Some(ev_completion_action))); } } } else { - log_trace!(logger, "Received duplicative fulfill for HTLC with payment_preimage {}", log_bytes!(payment_preimage.0)); + log_trace!(logger, "Received duplicative fulfill for HTLC with payment_preimage {}", &payment_preimage); } } @@ -1182,19 +1310,19 @@ impl OutboundPayments { } } - pub(super) fn remove_stale_resolved_payments(&self, - pending_events: &Mutex)>>) + pub(super) fn remove_stale_payments( + &self, pending_events: &Mutex)>>) { - // If an outbound payment was completed, and no pending HTLCs remain, we should remove it - // from the map. However, if we did that immediately when the last payment HTLC is claimed, - // this could race the user making a duplicate send_payment call and our idempotency - // guarantees would be violated. Instead, we wait a few timer ticks to do the actual - // removal. This should be more than sufficient to ensure the idempotency of any - // `send_payment` calls that were made at the same time the `PaymentSent` event was being - // processed. let mut pending_outbound_payments = self.pending_outbound_payments.lock().unwrap(); - let pending_events = pending_events.lock().unwrap(); + let mut pending_events = pending_events.lock().unwrap(); pending_outbound_payments.retain(|payment_id, payment| { + // If an outbound payment was completed, and no pending HTLCs remain, we should remove it + // from the map. However, if we did that immediately when the last payment HTLC is claimed, + // this could race the user making a duplicate send_payment call and our idempotency + // guarantees would be violated. Instead, we wait a few timer ticks to do the actual + // removal. This should be more than sufficient to ensure the idempotency of any + // `send_payment` calls that were made at the same time the `PaymentSent` event was being + // processed. if let PendingOutboundPayment::Fulfilled { session_privs, timer_ticks_without_htlcs, .. } = payment { let mut no_remaining_entries = session_privs.is_empty(); if no_remaining_entries { @@ -1219,6 +1347,16 @@ impl OutboundPayments { *timer_ticks_without_htlcs = 0; true } + } else if let PendingOutboundPayment::AwaitingInvoice { timer_ticks_without_response, .. } = payment { + *timer_ticks_without_response += 1; + if *timer_ticks_without_response <= INVOICE_REQUEST_TIMEOUT_TICKS { + true + } else { + pending_events.push_back( + (events::Event::InvoiceRequestFailed { payment_id: *payment_id }, None) + ); + false + } } else { true } }); } @@ -1231,9 +1369,12 @@ impl OutboundPayments { pending_events: &Mutex)>>, logger: &L, ) -> bool where L::Target: Logger { #[cfg(test)] - let (network_update, short_channel_id, payment_retryable, onion_error_code, onion_error_data) = onion_error.decode_onion_failure(secp_ctx, logger, &source); + let DecodedOnionFailure { + network_update, short_channel_id, payment_retryable, onion_error_code, onion_error_data + } = onion_error.decode_onion_failure(secp_ctx, logger, &source); #[cfg(not(test))] - let (network_update, short_channel_id, payment_retryable, _, _) = onion_error.decode_onion_failure(secp_ctx, logger, &source); + let DecodedOnionFailure { network_update, short_channel_id, payment_retryable } = + onion_error.decode_onion_failure(secp_ctx, logger, &source); let payment_is_probe = payment_is_probe(payment_hash, &payment_id, probing_cookie_secret); let mut session_priv_bytes = [0; 32]; @@ -1258,11 +1399,11 @@ impl OutboundPayments { let mut pending_retry_ev = false; let attempts_remaining = if let hash_map::Entry::Occupied(mut payment) = outbounds.entry(*payment_id) { if !payment.get_mut().remove(&session_priv_bytes, Some(&path)) { - log_trace!(logger, "Received duplicative fail for HTLC with payment_hash {}", log_bytes!(payment_hash.0)); + log_trace!(logger, "Received duplicative fail for HTLC with payment_hash {}", &payment_hash); return false } if payment.get().is_fulfilled() { - log_trace!(logger, "Received failure of HTLC with payment_hash {} after payment completion", log_bytes!(payment_hash.0)); + log_trace!(logger, "Received failure of HTLC with payment_hash {} after payment completion", &payment_hash); return false } let mut is_retryable_now = payment.get().is_auto_retryable_now(); @@ -1296,11 +1437,11 @@ impl OutboundPayments { } is_retryable_now } else { - log_trace!(logger, "Received duplicative fail for HTLC with payment_hash {}", log_bytes!(payment_hash.0)); + log_trace!(logger, "Received duplicative fail for HTLC with payment_hash {}", &payment_hash); return false }; core::mem::drop(outbounds); - log_trace!(logger, "Failing outbound payment HTLC with payment_hash {}", log_bytes!(payment_hash.0)); + log_trace!(logger, "Failing outbound payment HTLC with payment_hash {}", &payment_hash); let path_failure = { if payment_is_probe { @@ -1361,6 +1502,11 @@ impl OutboundPayments { }, None)); payment.remove(); } + } else if let PendingOutboundPayment::AwaitingInvoice { .. } = payment.get() { + pending_events.lock().unwrap().push_back((events::Event::InvoiceRequestFailed { + payment_id, + }, None)); + payment.remove(); } } } @@ -1414,6 +1560,7 @@ impl_writeable_tlv_based_enum_upgradable!(PendingOutboundPayment, (6, total_msat, required), (7, payment_metadata, option), (8, pending_amt_msat, required), + (9, custom_tlvs, optional_vec), (10, starting_block_height, required), (not_written, retry_strategy, (static_value, None)), (not_written, attempts, (static_value, PaymentAttempts::new())), @@ -1423,6 +1570,9 @@ impl_writeable_tlv_based_enum_upgradable!(PendingOutboundPayment, (1, reason, option), (2, payment_hash, required), }, + (5, AwaitingInvoice) => { + (0, timer_ticks_without_response, required), + }, ); #[cfg(test)] @@ -1438,12 +1588,34 @@ mod tests { use crate::ln::outbound_payment::{OutboundPayments, Retry, RetryableSendFailure}; use crate::routing::gossip::NetworkGraph; use crate::routing::router::{InFlightHtlcs, Path, PaymentParameters, Route, RouteHop, RouteParameters}; - use crate::sync::{Arc, Mutex}; + use crate::sync::{Arc, Mutex, RwLock}; use crate::util::errors::APIError; use crate::util::test_utils; use alloc::collections::VecDeque; + #[test] + fn test_recipient_onion_fields_with_custom_tlvs() { + let onion_fields = RecipientOnionFields::spontaneous_empty(); + + let bad_type_range_tlvs = vec![ + (0, vec![42]), + (1, vec![42; 32]), + ]; + assert!(onion_fields.clone().with_custom_tlvs(bad_type_range_tlvs).is_err()); + + let keysend_tlv = vec![ + (5482373484, vec![42; 32]), + ]; + assert!(onion_fields.clone().with_custom_tlvs(keysend_tlv).is_err()); + + let good_tlvs = vec![ + ((1 << 16) + 1, vec![42]), + ((1 << 16) + 3, vec![42; 32]), + ]; + assert!(onion_fields.with_custom_tlvs(good_tlvs).is_ok()); + } + #[test] #[cfg(feature = "std")] fn fails_paying_after_expiration() { @@ -1455,7 +1627,7 @@ mod tests { let outbound_payments = OutboundPayments::new(); let logger = test_utils::TestLogger::new(); let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, &logger)); - let scorer = Mutex::new(test_utils::TestScorer::new()); + let scorer = RwLock::new(test_utils::TestScorer::new()); let router = test_utils::TestRouter::new(network_graph, &scorer); let secp_ctx = Secp256k1::new(); let keys_manager = test_utils::TestKeysInterface::new(&[0; 32], Network::Testnet); @@ -1465,20 +1637,17 @@ mod tests { PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap()), 0 ).with_expiry_time(past_expiry_time); - let expired_route_params = RouteParameters { - payment_params, - final_value_msat: 0, - }; + let expired_route_params = RouteParameters::from_payment_params_and_value(payment_params, 0); let pending_events = Mutex::new(VecDeque::new()); if on_retry { outbound_payments.add_new_pending_payment(PaymentHash([0; 32]), RecipientOnionFields::spontaneous_empty(), - PaymentId([0; 32]), None, &Route { paths: vec![], payment_params: None }, + PaymentId([0; 32]), None, &Route { paths: vec![], route_params: None }, Some(Retry::Attempts(1)), Some(expired_route_params.payment_params.clone()), &&keys_manager, 0).unwrap(); outbound_payments.retry_payment_internal( PaymentHash([0; 32]), PaymentId([0; 32]), expired_route_params, &&router, vec![], - &|| InFlightHtlcs::new(), &&keys_manager, &&keys_manager, 0, &&logger, - &pending_events, &|_, _, _, _, _, _, _, _| Ok(())); + &|| InFlightHtlcs::new(), &&keys_manager, &&keys_manager, 0, &&logger, &pending_events, + &|_| Ok(())); let events = pending_events.lock().unwrap(); assert_eq!(events.len(), 1); if let Event::PaymentFailed { ref reason, .. } = events[0].0 { @@ -1488,8 +1657,7 @@ mod tests { let err = outbound_payments.send_payment( PaymentHash([0; 32]), RecipientOnionFields::spontaneous_empty(), PaymentId([0; 32]), Retry::Attempts(0), expired_route_params, &&router, vec![], || InFlightHtlcs::new(), - &&keys_manager, &&keys_manager, 0, &&logger, - &pending_events, |_, _, _, _, _, _, _, _| Ok(())).unwrap_err(); + &&keys_manager, &&keys_manager, 0, &&logger, &pending_events, |_| Ok(())).unwrap_err(); if let RetryableSendFailure::PaymentExpired = err { } else { panic!("Unexpected error"); } } } @@ -1503,30 +1671,27 @@ mod tests { let outbound_payments = OutboundPayments::new(); let logger = test_utils::TestLogger::new(); let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, &logger)); - let scorer = Mutex::new(test_utils::TestScorer::new()); + let scorer = RwLock::new(test_utils::TestScorer::new()); let router = test_utils::TestRouter::new(network_graph, &scorer); let secp_ctx = Secp256k1::new(); let keys_manager = test_utils::TestKeysInterface::new(&[0; 32], Network::Testnet); let payment_params = PaymentParameters::from_node_id( PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap()), 0); - let route_params = RouteParameters { - payment_params, - final_value_msat: 0, - }; + let route_params = RouteParameters::from_payment_params_and_value(payment_params, 0); router.expect_find_route(route_params.clone(), Err(LightningError { err: String::new(), action: ErrorAction::IgnoreError })); let pending_events = Mutex::new(VecDeque::new()); if on_retry { outbound_payments.add_new_pending_payment(PaymentHash([0; 32]), RecipientOnionFields::spontaneous_empty(), - PaymentId([0; 32]), None, &Route { paths: vec![], payment_params: None }, + PaymentId([0; 32]), None, &Route { paths: vec![], route_params: None }, Some(Retry::Attempts(1)), Some(route_params.payment_params.clone()), &&keys_manager, 0).unwrap(); outbound_payments.retry_payment_internal( PaymentHash([0; 32]), PaymentId([0; 32]), route_params, &&router, vec![], - &|| InFlightHtlcs::new(), &&keys_manager, &&keys_manager, 0, &&logger, - &pending_events, &|_, _, _, _, _, _, _, _| Ok(())); + &|| InFlightHtlcs::new(), &&keys_manager, &&keys_manager, 0, &&logger, &pending_events, + &|_| Ok(())); let events = pending_events.lock().unwrap(); assert_eq!(events.len(), 1); if let Event::PaymentFailed { .. } = events[0].0 { } else { panic!("Unexpected event"); } @@ -1534,8 +1699,7 @@ mod tests { let err = outbound_payments.send_payment( PaymentHash([0; 32]), RecipientOnionFields::spontaneous_empty(), PaymentId([0; 32]), Retry::Attempts(0), route_params, &&router, vec![], || InFlightHtlcs::new(), - &&keys_manager, &&keys_manager, 0, &&logger, - &pending_events, |_, _, _, _, _, _, _, _| Ok(())).unwrap_err(); + &&keys_manager, &&keys_manager, 0, &&logger, &pending_events, |_| Ok(())).unwrap_err(); if let RetryableSendFailure::RouteNotFound = err { } else { panic!("Unexpected error"); } } @@ -1546,7 +1710,7 @@ mod tests { let outbound_payments = OutboundPayments::new(); let logger = test_utils::TestLogger::new(); let network_graph = Arc::new(NetworkGraph::new(Network::Testnet, &logger)); - let scorer = Mutex::new(test_utils::TestScorer::new()); + let scorer = RwLock::new(test_utils::TestScorer::new()); let router = test_utils::TestRouter::new(network_graph, &scorer); let secp_ctx = Secp256k1::new(); let keys_manager = test_utils::TestKeysInterface::new(&[0; 32], Network::Testnet); @@ -1554,10 +1718,7 @@ mod tests { let sender_pk = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap()); let receiver_pk = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[43; 32]).unwrap()); let payment_params = PaymentParameters::from_node_id(sender_pk, 0); - let route_params = RouteParameters { - payment_params: payment_params.clone(), - final_value_msat: 0, - }; + let route_params = RouteParameters::from_payment_params_and_value(payment_params.clone(), 0); let failed_scid = 42; let route = Route { paths: vec![Path { hops: vec![RouteHop { @@ -1568,7 +1729,7 @@ mod tests { fee_msat: 0, cltv_expiry_delta: 0, }], blinded_tail: None }], - payment_params: Some(payment_params), + route_params: Some(route_params.clone()), }; router.expect_find_route(route_params.clone(), Ok(route.clone())); let mut route_params_w_failed_scid = route_params.clone(); @@ -1584,8 +1745,7 @@ mod tests { PaymentHash([0; 32]), RecipientOnionFields::spontaneous_empty(), PaymentId([0; 32]), Retry::Attempts(0), route_params.clone(), &&router, vec![], || InFlightHtlcs::new(), &&keys_manager, &&keys_manager, 0, &&logger, &pending_events, - |_, _, _, _, _, _, _, _| Err(APIError::ChannelUnavailable { err: "test".to_owned() })) - .unwrap(); + |_| Err(APIError::ChannelUnavailable { err: "test".to_owned() })).unwrap(); let mut events = pending_events.lock().unwrap(); assert_eq!(events.len(), 2); if let Event::PaymentPathFailed { @@ -1603,7 +1763,7 @@ mod tests { PaymentHash([0; 32]), RecipientOnionFields::spontaneous_empty(), PaymentId([0; 32]), Retry::Attempts(0), route_params.clone(), &&router, vec![], || InFlightHtlcs::new(), &&keys_manager, &&keys_manager, 0, &&logger, &pending_events, - |_, _, _, _, _, _, _, _| Err(APIError::MonitorUpdateInProgress)).unwrap(); + |_| Err(APIError::MonitorUpdateInProgress)).unwrap(); assert_eq!(pending_events.lock().unwrap().len(), 0); // Ensure that any other error will result in a PaymentPathFailed event but no blamed scid. @@ -1611,8 +1771,7 @@ mod tests { PaymentHash([0; 32]), RecipientOnionFields::spontaneous_empty(), PaymentId([1; 32]), Retry::Attempts(0), route_params.clone(), &&router, vec![], || InFlightHtlcs::new(), &&keys_manager, &&keys_manager, 0, &&logger, &pending_events, - |_, _, _, _, _, _, _, _| Err(APIError::APIMisuseError { err: "test".to_owned() })) - .unwrap(); + |_| Err(APIError::APIMisuseError { err: "test".to_owned() })).unwrap(); let events = pending_events.lock().unwrap(); assert_eq!(events.len(), 2); if let Event::PaymentPathFailed {