X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fln%2Foutbound_payment.rs;h=50dcdd546083a326e6f4b14340e0d815a621af88;hb=1babca4e2c7e062ecf044cf96c703de8f9808e3a;hp=f107f3b558395fe7ea9da8f8a8194f8f52a9f634;hpb=6775b957bc0e738afff46eb819c69f45410f1843;p=rust-lightning diff --git a/lightning/src/ln/outbound_payment.rs b/lightning/src/ln/outbound_payment.rs index f107f3b5..50dcdd54 100644 --- a/lightning/src/ln/outbound_payment.rs +++ b/lightning/src/ln/outbound_payment.rs @@ -239,7 +239,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 +274,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))] @@ -312,7 +312,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`]. @@ -414,9 +414,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 @@ -447,10 +447,13 @@ impl RecipientOnionFields { } /// 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 } } @@ -470,6 +473,18 @@ impl RecipientOnionFields { } } +/// 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 +511,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 +526,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 +547,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 +566,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 +595,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, @@ -655,8 +665,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>, { #[cfg(feature = "std")] { if has_expired(&route_params) { @@ -666,7 +675,7 @@ impl OutboundPayments { 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)?; @@ -696,8 +705,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>, { #[cfg(feature = "std")] { if has_expired(&route_params) { @@ -709,7 +717,7 @@ 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, @@ -818,8 +826,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 +898,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()); @@ -986,8 +992,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 +1033,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 +1048,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"); @@ -1095,8 +1102,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, @@ -1477,8 +1483,8 @@ 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].0 { @@ -1488,8 +1494,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"); } } } @@ -1525,8 +1530,8 @@ 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].0 { } else { panic!("Unexpected event"); } @@ -1534,8 +1539,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"); } } @@ -1584,8 +1588,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 +1606,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 +1614,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 {