X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fln%2Foutbound_payment.rs;h=f60bf565efa35ddadac28416f2a7da22233d4892;hb=3695b2aa13dcd271c9c4252cda3a0b9b79d3d879;hp=5270ed35d8835d6f240e8ae0fcd90b12e0851685;hpb=c89fd38f2a6681b28f853d0f7d80795ca38d9ffd;p=rust-lightning diff --git a/lightning/src/ln/outbound_payment.rs b/lightning/src/ln/outbound_payment.rs index 5270ed35..f60bf565 100644 --- a/lightning/src/ln/outbound_payment.rs +++ b/lightning/src/ln/outbound_payment.rs @@ -13,11 +13,11 @@ use bitcoin::hashes::Hash; use bitcoin::hashes::sha256::Hash as Sha256; use bitcoin::secp256k1::{self, Secp256k1, SecretKey}; -use crate::chain::keysinterface::{EntropySource, NodeSigner, Recipient}; +use crate::sign::{EntropySource, NodeSigner, Recipient}; use crate::events::{self, PaymentFailureReason}; use crate::ln::{PaymentHash, PaymentPreimage, PaymentSecret}; -use crate::ln::channelmanager::{ChannelDetails, HTLCSource, IDEMPOTENCY_TIMEOUT_TICKS, PaymentId}; -use crate::ln::onion_utils::HTLCFailReason; +use crate::ln::channelmanager::{ChannelDetails, EventCompletionAction, HTLCSource, IDEMPOTENCY_TIMEOUT_TICKS, 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; @@ -47,6 +47,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, @@ -60,6 +61,7 @@ pub(crate) enum PendingOutboundPayment { /// and add a pending payment that was already fulfilled. Fulfilled { session_privs: HashSet<[u8; 32]>, + /// Filled in for any payment which moved to `Fulfilled` on LDK 0.0.104 or later. payment_hash: Option, timer_ticks_without_htlcs: u8, }, @@ -238,7 +240,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), @@ -273,7 +275,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))] @@ -311,7 +313,7 @@ impl Display for PaymentAttemptsUsingTime { /// [`ChannelManager::send_payment`]: crate::ln::channelmanager::ChannelManager::send_payment /// [`Event::PaymentPathFailed`]: crate::events::Event::PaymentPathFailed /// [`Event::PaymentFailed`]: crate::events::Event::PaymentFailed -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq, Eq)] pub enum RetryableSendFailure { /// The provided [`PaymentParameters::expiry_time`] indicated that the payment has expired. Note /// that this error is *not* caused by [`Retry::Timeout`]. @@ -413,9 +415,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 @@ -430,10 +432,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), }); @@ -442,16 +447,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 @@ -464,11 +511,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<()>, @@ -487,7 +556,7 @@ impl OutboundPayments { retry_strategy: Retry, route_params: RouteParameters, router: &R, first_hops: Vec, compute_inflight_htlcs: IH, entropy_source: &ES, node_signer: &NS, best_block_height: u32, logger: &L, - pending_events: &Mutex>, send_payment_along_path: SP, + pending_events: &Mutex)>>, send_payment_along_path: SP, ) -> Result<(), RetryableSendFailure> where R::Target: Router, @@ -495,8 +564,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, @@ -511,8 +579,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, @@ -525,7 +592,7 @@ impl OutboundPayments { payment_id: PaymentId, retry_strategy: Retry, route_params: RouteParameters, router: &R, first_hops: Vec, inflight_htlcs: IH, entropy_source: &ES, node_signer: &NS, best_block_height: u32, logger: &L, - pending_events: &Mutex>, send_payment_along_path: SP + pending_events: &Mutex)>>, send_payment_along_path: SP ) -> Result where R::Target: Router, @@ -533,8 +600,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())); @@ -553,8 +619,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())); @@ -575,15 +640,15 @@ impl OutboundPayments { pub(super) fn check_retry_payments( &self, router: &R, first_hops: FH, inflight_htlcs: IH, entropy_source: &ES, node_signer: &NS, - best_block_height: u32, pending_events: &Mutex>, logger: &L, + best_block_height: u32, + pending_events: &Mutex)>>, logger: &L, send_payment_along_path: SP, ) where 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, @@ -617,11 +682,11 @@ impl OutboundPayments { if !pmt.is_auto_retryable_now() && pmt.remaining_parts() == 0 { pmt.mark_abandoned(PaymentFailureReason::RetriesExhausted); if let PendingOutboundPayment::Abandoned { payment_hash, reason, .. } = pmt { - pending_events.lock().unwrap().push(events::Event::PaymentFailed { + pending_events.lock().unwrap().push_back((events::Event::PaymentFailed { payment_id: *pmt_id, payment_hash: *payment_hash, reason: *reason, - }); + }, None)); retain = false; } } @@ -645,7 +710,7 @@ impl OutboundPayments { keysend_preimage: Option, retry_strategy: Retry, route_params: RouteParameters, router: &R, first_hops: Vec, inflight_htlcs: IH, entropy_source: &ES, node_signer: &NS, best_block_height: u32, logger: &L, - pending_events: &Mutex>, send_payment_along_path: SP, + pending_events: &Mutex)>>, send_payment_along_path: SP, ) -> Result<(), RetryableSendFailure> where R::Target: Router, @@ -653,29 +718,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); } @@ -686,7 +761,7 @@ impl OutboundPayments { &self, payment_hash: PaymentHash, payment_id: PaymentId, route_params: RouteParameters, router: &R, first_hops: Vec, inflight_htlcs: &IH, entropy_source: &ES, node_signer: &NS, best_block_height: u32, logger: &L, - pending_events: &Mutex>, send_payment_along_path: &SP, + pending_events: &Mutex)>>, send_payment_along_path: &SP, ) where R::Target: Router, @@ -694,12 +769,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 } @@ -707,12 +781,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 } @@ -736,11 +810,11 @@ impl OutboundPayments { $payment.get_mut().mark_abandoned($reason); if let PendingOutboundPayment::Abandoned { reason, .. } = $payment.get() { if $payment.get().remaining_parts() == 0 { - pending_events.lock().unwrap().push(events::Event::PaymentFailed { + pending_events.lock().unwrap().push_back((events::Event::PaymentFailed { payment_id, payment_hash, reason: *reason, - }); + }, None)); $payment.remove(); } } @@ -752,7 +826,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 { @@ -763,6 +838,7 @@ impl OutboundPayments { (*total_msat, RecipientOnionFields { payment_secret: *payment_secret, payment_metadata: payment_metadata.clone(), + custom_tlvs: custom_tlvs.clone(), }, *keysend_preimage) }, PendingOutboundPayment::Legacy { .. } => { @@ -779,7 +855,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 } @@ -790,7 +866,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 } } @@ -798,7 +874,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); } @@ -808,7 +884,7 @@ impl OutboundPayments { &self, err: PaymentSendFailure, payment_id: PaymentId, payment_hash: PaymentHash, route: Route, mut route_params: RouteParameters, router: &R, first_hops: Vec, inflight_htlcs: &IH, entropy_source: &ES, node_signer: &NS, best_block_height: u32, logger: &L, - pending_events: &Mutex>, send_payment_along_path: &SP, + pending_events: &Mutex)>>, send_payment_along_path: &SP, ) where R::Target: Router, @@ -816,8 +892,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) => { @@ -851,7 +926,8 @@ impl OutboundPayments { fn push_path_failed_evs_and_scids>, L: Deref>( payment_id: PaymentId, payment_hash: PaymentHash, route_params: &mut RouteParameters, - paths: Vec, path_results: I, logger: &L, pending_events: &Mutex> + paths: Vec, path_results: I, logger: &L, + pending_events: &Mutex)>>, ) where L::Target: Logger { let mut events = pending_events.lock().unwrap(); debug_assert_eq!(paths.len(), path_results.len()); @@ -865,7 +941,7 @@ impl OutboundPayments { failed_scid = Some(scid); route_params.payment_params.previously_failed_channels.push(scid); } - events.push(events::Event::PaymentPathFailed { + events.push_back((events::Event::PaymentPathFailed { payment_id: Some(payment_id), payment_hash, payment_failed_permanently: false, @@ -876,7 +952,7 @@ impl OutboundPayments { error_code: None, #[cfg(test)] error_data: None, - }); + }, None)); } } } @@ -888,8 +964,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()); @@ -962,6 +1037,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(), }); @@ -983,8 +1059,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()})); @@ -1025,9 +1100,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) => { @@ -1038,7 +1115,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"); @@ -1092,8 +1169,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, @@ -1112,7 +1188,9 @@ impl OutboundPayments { pub(super) fn claim_htlc( &self, payment_id: PaymentId, payment_preimage: PaymentPreimage, session_priv: SecretKey, - path: Path, from_onchain: bool, pending_events: &Mutex>, logger: &L + path: Path, from_onchain: bool, ev_completion_action: EventCompletionAction, + pending_events: &Mutex)>>, + logger: &L, ) where L::Target: Logger { let mut session_priv_bytes = [0; 32]; session_priv_bytes.copy_from_slice(&session_priv[..]); @@ -1121,15 +1199,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( - events::Event::PaymentSent { - payment_id: Some(payment_id), - payment_preimage, - payment_hash, - fee_paid_msat, - } - ); + pending_events.push_back((events::Event::PaymentSent { + payment_id: Some(payment_id), + payment_preimage, + payment_hash, + fee_paid_msat, + }, Some(ev_completion_action.clone()))); payment.get_mut().mark_fulfilled(); } @@ -1142,21 +1219,21 @@ impl OutboundPayments { // irrevocably fulfilled. if payment.get_mut().remove(&session_priv_bytes, Some(&path)) { let payment_hash = Some(PaymentHash(Sha256::hash(&payment_preimage.0).into_inner())); - pending_events.push( - events::Event::PaymentPathSuccessful { - payment_id, - payment_hash, - path, - } - ); + pending_events.push_back((events::Event::PaymentPathSuccessful { + payment_id, + payment_hash, + path, + }, 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); } } - pub(super) fn finalize_claims(&self, sources: Vec, pending_events: &Mutex>) { + pub(super) fn finalize_claims(&self, sources: Vec, + pending_events: &Mutex)>>) + { let mut outbounds = self.pending_outbound_payments.lock().unwrap(); let mut pending_events = pending_events.lock().unwrap(); for source in sources { @@ -1166,20 +1243,22 @@ impl OutboundPayments { if let hash_map::Entry::Occupied(mut payment) = outbounds.entry(payment_id) { assert!(payment.get().is_fulfilled()); if payment.get_mut().remove(&session_priv_bytes, None) { - pending_events.push( - events::Event::PaymentPathSuccessful { - payment_id, - payment_hash: payment.get().payment_hash(), - path, - } - ); + let payment_hash = payment.get().payment_hash(); + debug_assert!(payment_hash.is_some()); + pending_events.push_back((events::Event::PaymentPathSuccessful { + payment_id, + payment_hash, + path, + }, None)); } } } } } - pub(super) fn remove_stale_resolved_payments(&self, pending_events: &Mutex>) { + pub(super) fn remove_stale_resolved_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 @@ -1193,7 +1272,7 @@ impl OutboundPayments { if let PendingOutboundPayment::Fulfilled { session_privs, timer_ticks_without_htlcs, .. } = payment { let mut no_remaining_entries = session_privs.is_empty(); if no_remaining_entries { - for ev in pending_events.iter() { + for (ev, _) in pending_events.iter() { match ev { events::Event::PaymentSent { payment_id: Some(ev_payment_id), .. } | events::Event::PaymentPathSuccessful { payment_id: ev_payment_id, .. } | @@ -1221,13 +1300,17 @@ impl OutboundPayments { // Returns a bool indicating whether a PendingHTLCsForwardable event should be generated. pub(super) fn fail_htlc( &self, source: &HTLCSource, payment_hash: &PaymentHash, onion_error: &HTLCFailReason, - path: &Path, session_priv: &SecretKey, payment_id: &PaymentId, probing_cookie_secret: [u8; 32], - secp_ctx: &Secp256k1, pending_events: &Mutex>, logger: &L + path: &Path, session_priv: &SecretKey, payment_id: &PaymentId, + probing_cookie_secret: [u8; 32], secp_ctx: &Secp256k1, + 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]; @@ -1252,11 +1335,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(); @@ -1290,11 +1373,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 { @@ -1334,24 +1417,25 @@ impl OutboundPayments { } }; let mut pending_events = pending_events.lock().unwrap(); - pending_events.push(path_failure); - if let Some(ev) = full_failure_ev { pending_events.push(ev); } + pending_events.push_back((path_failure, None)); + if let Some(ev) = full_failure_ev { pending_events.push_back((ev, None)); } pending_retry_ev } pub(super) fn abandon_payment( - &self, payment_id: PaymentId, reason: PaymentFailureReason, pending_events: &Mutex> + &self, payment_id: PaymentId, reason: PaymentFailureReason, + pending_events: &Mutex)>> ) { let mut outbounds = self.pending_outbound_payments.lock().unwrap(); if let hash_map::Entry::Occupied(mut payment) = outbounds.entry(payment_id) { payment.get_mut().mark_abandoned(reason); if let PendingOutboundPayment::Abandoned { payment_hash, reason, .. } = payment.get() { if payment.get().remaining_parts() == 0 { - pending_events.lock().unwrap().push(events::Event::PaymentFailed { + pending_events.lock().unwrap().push_back((events::Event::PaymentFailed { payment_id, payment_hash: *payment_hash, reason: *reason, - }); + }, None)); payment.remove(); } } @@ -1407,6 +1491,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())), @@ -1431,10 +1516,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() { @@ -1446,7 +1555,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); @@ -1460,7 +1569,7 @@ mod tests { payment_params, final_value_msat: 0, }; - let pending_events = Mutex::new(Vec::new()); + 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 }, @@ -1468,19 +1577,18 @@ mod tests { &&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] { + if let Event::PaymentFailed { ref reason, .. } = events[0].0 { assert_eq!(reason.unwrap(), PaymentFailureReason::PaymentExpired); } else { panic!("Unexpected event"); } } else { 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"); } } } @@ -1494,7 +1602,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); @@ -1508,7 +1616,7 @@ mod tests { router.expect_find_route(route_params.clone(), Err(LightningError { err: String::new(), action: ErrorAction::IgnoreError })); - let pending_events = Mutex::new(Vec::new()); + 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 }, @@ -1516,17 +1624,16 @@ mod tests { &&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] { } else { panic!("Unexpected event"); } + if let Event::PaymentFailed { .. } = events[0].0 { } else { panic!("Unexpected event"); } } else { 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"); } } @@ -1537,7 +1644,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); @@ -1570,22 +1677,21 @@ mod tests { // Ensure that a ChannelUnavailable error will result in blaming an scid in the // PaymentPathFailed event. - let pending_events = Mutex::new(Vec::new()); + let pending_events = Mutex::new(VecDeque::new()); outbound_payments.send_payment( 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 { short_channel_id, - failure: PathFailure::InitialSend { err: APIError::ChannelUnavailable { .. }}, .. } = events[0] + failure: PathFailure::InitialSend { err: APIError::ChannelUnavailable { .. }}, .. } = events[0].0 { assert_eq!(short_channel_id, Some(failed_scid)); } else { panic!("Unexpected event"); } - if let Event::PaymentFailed { .. } = events[1] { } else { panic!("Unexpected event"); } + if let Event::PaymentFailed { .. } = events[1].0 { } else { panic!("Unexpected event"); } events.clear(); core::mem::drop(events); @@ -1594,7 +1700,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. @@ -1602,16 +1708,15 @@ 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 { short_channel_id, - failure: PathFailure::InitialSend { err: APIError::APIMisuseError { .. }}, .. } = events[0] + failure: PathFailure::InitialSend { err: APIError::APIMisuseError { .. }}, .. } = events[0].0 { assert_eq!(short_channel_id, None); } else { panic!("Unexpected event"); } - if let Event::PaymentFailed { .. } = events[1] { } else { panic!("Unexpected event"); } + if let Event::PaymentFailed { .. } = events[1].0 { } else { panic!("Unexpected event"); } } }