Merge pull request #2002 from valentinewallace/2023-01-keysend-retries
[rust-lightning] / lightning / src / ln / outbound_payment.rs
index f745363036b7b2caf4ba72ad8ab8bb01185b46e1..a9ced49f647a75e13a1a2a535c38fa86ef29a1c9 100644 (file)
@@ -48,6 +48,7 @@ pub(crate) enum PendingOutboundPayment {
                session_privs: HashSet<[u8; 32]>,
                payment_hash: PaymentHash,
                payment_secret: Option<PaymentSecret>,
+               keysend_preimage: Option<PaymentPreimage>,
                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<u64>,
@@ -415,7 +416,7 @@ impl OutboundPayments {
                F: Fn(&Vec<RouteHop>, &Option<PaymentParameters>, &PaymentHash, &Option<PaymentSecret>, u64,
                         u32, PaymentId, &Option<PaymentPreimage>, [u8; 32]) -> Result<(), APIError>,
        {
-               self.pay_internal(payment_id, Some((payment_hash, payment_secret, retry_strategy)),
+               self.pay_internal(payment_id, Some((payment_hash, payment_secret, None, retry_strategy)),
                        route_params, router, first_hops, inflight_htlcs, entropy_source, node_signer,
                        best_block_height, logger, &send_payment_along_path)
                        .map_err(|e| { self.remove_outbound_if_all_failed(payment_id, &e); e })
@@ -432,13 +433,37 @@ impl OutboundPayments {
                F: Fn(&Vec<RouteHop>, &Option<PaymentParameters>, &PaymentHash, &Option<PaymentSecret>, u64,
                   u32, PaymentId, &Option<PaymentPreimage>, [u8; 32]) -> Result<(), APIError>
        {
-               let onion_session_privs = self.add_new_pending_payment(payment_hash, *payment_secret, payment_id, route, None, None, entropy_source, best_block_height)?;
+               let onion_session_privs = self.add_new_pending_payment(payment_hash, *payment_secret, payment_id, None, route, None, None, entropy_source, best_block_height)?;
                self.pay_route_internal(route, payment_hash, payment_secret, None, payment_id, None,
                        onion_session_privs, node_signer, best_block_height, &send_payment_along_path)
                        .map_err(|e| { self.remove_outbound_if_all_failed(payment_id, &e); e })
        }
 
-       pub(super) fn send_spontaneous_payment<ES: Deref, NS: Deref, F>(
+       pub(super) fn send_spontaneous_payment<R: Deref, ES: Deref, NS: Deref, F, L: Deref>(
+               &self, payment_preimage: Option<PaymentPreimage>, payment_id: PaymentId,
+               retry_strategy: Retry, route_params: RouteParameters, router: &R,
+               first_hops: Vec<ChannelDetails>, inflight_htlcs: InFlightHtlcs, entropy_source: &ES,
+               node_signer: &NS, best_block_height: u32, logger: &L, send_payment_along_path: F
+       ) -> Result<PaymentHash, PaymentSendFailure>
+       where
+               R::Target: Router,
+               ES::Target: EntropySource,
+               NS::Target: NodeSigner,
+               L::Target: Logger,
+               F: Fn(&Vec<RouteHop>, &Option<PaymentParameters>, &PaymentHash, &Option<PaymentSecret>, u64,
+                        u32, PaymentId, &Option<PaymentPreimage>, [u8; 32]) -> Result<(), APIError>,
+       {
+               let preimage = payment_preimage
+                       .unwrap_or_else(|| PaymentPreimage(entropy_source.get_secure_random_bytes()));
+               let payment_hash = PaymentHash(Sha256::hash(&preimage.0).into_inner());
+               self.pay_internal(payment_id, Some((payment_hash, &None, Some(preimage), retry_strategy)),
+                       route_params, router, first_hops, inflight_htlcs, entropy_source, node_signer,
+                       best_block_height, logger, &send_payment_along_path)
+                       .map(|()| payment_hash)
+                       .map_err(|e| { self.remove_outbound_if_all_failed(payment_id, &e); e })
+       }
+
+       pub(super) fn send_spontaneous_payment_with_route<ES: Deref, NS: Deref, F>(
                &self, route: &Route, payment_preimage: Option<PaymentPreimage>, payment_id: PaymentId,
                entropy_source: &ES, node_signer: &NS, best_block_height: u32, send_payment_along_path: F
        ) -> Result<PaymentHash, PaymentSendFailure>
@@ -448,12 +473,10 @@ impl OutboundPayments {
                F: Fn(&Vec<RouteHop>, &Option<PaymentParameters>, &PaymentHash, &Option<PaymentSecret>, u64,
                   u32, PaymentId, &Option<PaymentPreimage>, [u8; 32]) -> Result<(), APIError>
        {
-               let preimage = match payment_preimage {
-                       Some(p) => p,
-                       None => PaymentPreimage(entropy_source.get_secure_random_bytes()),
-               };
+               let preimage = payment_preimage
+                       .unwrap_or_else(|| PaymentPreimage(entropy_source.get_secure_random_bytes()));
                let payment_hash = PaymentHash(Sha256::hash(&preimage.0).into_inner());
-               let onion_session_privs = self.add_new_pending_payment(payment_hash, None, payment_id, &route, None, None, entropy_source, best_block_height)?;
+               let onion_session_privs = self.add_new_pending_payment(payment_hash, None, payment_id, Some(preimage), &route, None, None, entropy_source, best_block_height)?;
 
                match self.pay_route_internal(route, payment_hash, &None, Some(preimage), payment_id, None, onion_session_privs, node_signer, best_block_height, &send_payment_along_path) {
                        Ok(()) => Ok(payment_hash),
@@ -511,7 +534,7 @@ impl OutboundPayments {
 
        fn pay_internal<R: Deref, NS: Deref, ES: Deref, F, L: Deref>(
                &self, payment_id: PaymentId,
-               initial_send_info: Option<(PaymentHash, &Option<PaymentSecret>, Retry)>,
+               initial_send_info: Option<(PaymentHash, &Option<PaymentSecret>, Option<PaymentPreimage>, Retry)>,
                route_params: RouteParameters, router: &R, first_hops: Vec<ChannelDetails>,
                inflight_htlcs: InFlightHtlcs, entropy_source: &ES, node_signer: &NS, best_block_height: u32,
                logger: &L, send_payment_along_path: &F,
@@ -539,8 +562,8 @@ impl OutboundPayments {
                        err: format!("Failed to find a route for payment {}: {:?}", log_bytes!(payment_id.0), e), // TODO: add APIError::RouteNotFound
                }))?;
 
-               let res = if let Some((payment_hash, payment_secret, retry_strategy)) = initial_send_info {
-                       let onion_session_privs = self.add_new_pending_payment(payment_hash, *payment_secret, payment_id, &route, Some(retry_strategy), Some(route_params.payment_params.clone()), entropy_source, best_block_height)?;
+               let res = if let Some((payment_hash, payment_secret, keysend_preimage, retry_strategy)) = initial_send_info {
+                       let onion_session_privs = self.add_new_pending_payment(payment_hash, *payment_secret, payment_id, keysend_preimage, &route, Some(retry_strategy), Some(route_params.payment_params.clone()), entropy_source, best_block_height)?;
                        self.pay_route_internal(&route, payment_hash, payment_secret, None, payment_id, None, onion_session_privs, node_signer, best_block_height, send_payment_along_path)
                } else {
                        self.retry_payment_with_route(&route, payment_id, entropy_source, node_signer, best_block_height, send_payment_along_path)
@@ -596,13 +619,13 @@ impl OutboundPayments {
                        onion_session_privs.push(entropy_source.get_secure_random_bytes());
                }
 
-               let (total_msat, payment_hash, payment_secret) = {
+               let (total_msat, payment_hash, payment_secret, keysend_preimage) = {
                        let mut outbounds = self.pending_outbound_payments.lock().unwrap();
                        match outbounds.get_mut(&payment_id) {
                                Some(payment) => {
                                        let res = match payment {
                                                PendingOutboundPayment::Retryable {
-                                                       total_msat, payment_hash, payment_secret, pending_amt_msat, ..
+                                                       total_msat, payment_hash, keysend_preimage, payment_secret, pending_amt_msat, ..
                                                } => {
                                                        let retry_amt_msat: u64 = route.paths.iter().map(|path| path.last().unwrap().fee_msat).sum();
                                                        if retry_amt_msat + *pending_amt_msat > *total_msat * (100 + RETRY_OVERFLOW_PERCENTAGE) / 100 {
@@ -610,7 +633,7 @@ impl OutboundPayments {
                                                                        err: format!("retry_amt_msat of {} will put pending_amt_msat (currently: {}) more than 10% over total_payment_amt_msat of {}", retry_amt_msat, pending_amt_msat, total_msat).to_string()
                                                                }))
                                                        }
-                                                       (*total_msat, *payment_hash, *payment_secret)
+                                                       (*total_msat, *payment_hash, *payment_secret, *keysend_preimage)
                                                },
                                                PendingOutboundPayment::Legacy { .. } => {
                                                        return Err(PaymentSendFailure::ParameterError(APIError::APIMisuseError {
@@ -645,7 +668,7 @@ impl OutboundPayments {
                                        })),
                        }
                };
-               self.pay_route_internal(route, payment_hash, &payment_secret, None, payment_id, Some(total_msat), onion_session_privs, node_signer, best_block_height, &send_payment_along_path)
+               self.pay_route_internal(route, payment_hash, &payment_secret, keysend_preimage, payment_id, Some(total_msat), onion_session_privs, node_signer, best_block_height, &send_payment_along_path)
        }
 
        pub(super) fn send_probe<ES: Deref, NS: Deref, F>(
@@ -669,7 +692,7 @@ impl OutboundPayments {
                }
 
                let route = Route { paths: vec![hops], payment_params: None };
-               let onion_session_privs = self.add_new_pending_payment(payment_hash, None, payment_id, &route, None, None, entropy_source, best_block_height)?;
+               let onion_session_privs = self.add_new_pending_payment(payment_hash, None, payment_id, None, &route, None, None, entropy_source, best_block_height)?;
 
                match self.pay_route_internal(&route, payment_hash, &None, None, payment_id, None, onion_session_privs, node_signer, best_block_height, &send_payment_along_path) {
                        Ok(()) => Ok((payment_hash, payment_id)),
@@ -685,13 +708,13 @@ impl OutboundPayments {
                &self, payment_hash: PaymentHash, payment_secret: Option<PaymentSecret>, payment_id: PaymentId,
                route: &Route, retry_strategy: Option<Retry>, entropy_source: &ES, best_block_height: u32
        ) -> Result<Vec<[u8; 32]>, PaymentSendFailure> where ES::Target: EntropySource {
-               self.add_new_pending_payment(payment_hash, payment_secret, payment_id, route, retry_strategy, None, entropy_source, best_block_height)
+               self.add_new_pending_payment(payment_hash, payment_secret, payment_id, None, route, retry_strategy, None, entropy_source, best_block_height)
        }
 
        pub(super) fn add_new_pending_payment<ES: Deref>(
                &self, payment_hash: PaymentHash, payment_secret: Option<PaymentSecret>, payment_id: PaymentId,
-               route: &Route, retry_strategy: Option<Retry>, payment_params: Option<PaymentParameters>,
-               entropy_source: &ES, best_block_height: u32
+               keysend_preimage: Option<PaymentPreimage>, route: &Route, retry_strategy: Option<Retry>,
+               payment_params: Option<PaymentParameters>, entropy_source: &ES, best_block_height: u32
        ) -> Result<Vec<[u8; 32]>, PaymentSendFailure> where ES::Target: EntropySource {
                let mut onion_session_privs = Vec::with_capacity(route.paths.len());
                for _ in 0..route.paths.len() {
@@ -711,6 +734,7 @@ impl OutboundPayments {
                                        pending_fee_msat: Some(0),
                                        payment_hash,
                                        payment_secret,
+                                       keysend_preimage,
                                        starting_block_height: best_block_height,
                                        total_msat: route.get_total_amount(),
                                });
@@ -1153,6 +1177,7 @@ impl_writeable_tlv_based_enum_upgradable!(PendingOutboundPayment,
                (2, payment_hash, required),
                (3, payment_params, option),
                (4, payment_secret, option),
+               (5, keysend_preimage, option),
                (6, total_msat, required),
                (8, pending_amt_msat, required),
                (10, starting_block_height, required),
@@ -1247,9 +1272,9 @@ mod tests {
                        Err(LightningError { err: String::new(), action: ErrorAction::IgnoreError }));
 
                let err = if on_retry {
-                       outbound_payments.add_new_pending_payment(PaymentHash([0; 32]), None, PaymentId([0; 32]),
-                       &Route { paths: vec![], payment_params: None }, Some(Retry::Attempts(1)), Some(route_params.payment_params.clone()),
-                       &&keys_manager, 0).unwrap();
+                       outbound_payments.add_new_pending_payment(PaymentHash([0; 32]), None, PaymentId([0; 32]), None,
+                               &Route { paths: vec![], payment_params: None }, Some(Retry::Attempts(1)),
+                               Some(route_params.payment_params.clone()), &&keys_manager, 0).unwrap();
                        outbound_payments.pay_internal(
                                PaymentId([0; 32]), None, route_params, &&router, vec![], InFlightHtlcs::new(),
                                &&keys_manager, &&keys_manager, 0, &&logger, &|_, _, _, _, _, _, _, _, _| Ok(())).unwrap_err()