X-Git-Url: http://git.bitcoin.ninja/index.cgi?a=blobdiff_plain;f=lightning%2Fsrc%2Fln%2Foutbound_payment.rs;h=b806b138a59092721d10b8e9289d9f0d28df23a0;hb=89749eddec2ea178858bb5bc96c7d79e02944586;hp=cdf5627a7aac6013371cd2b55e3cf14224a8423a;hpb=1ac53ed02bf26520d3b1a2c3c0e90c8691c83099;p=rust-lightning diff --git a/lightning/src/ln/outbound_payment.rs b/lightning/src/ln/outbound_payment.rs index cdf5627a..b806b138 100644 --- a/lightning/src/ln/outbound_payment.rs +++ b/lightning/src/ln/outbound_payment.rs @@ -594,10 +594,26 @@ impl RecipientOnionFields { /// 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`]. + #[cfg(not(c_bindings))] pub fn custom_tlvs(&self) -> &Vec<(u64, Vec)> { &self.custom_tlvs } + /// 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`]. + #[cfg(c_bindings)] + pub fn custom_tlvs(&self) -> Vec<(u64, Vec)> { + self.custom_tlvs.clone() + } + /// When we have received some HTLC(s) towards an MPP payment, as we receive further HTLC(s) we /// have to make sure that some fields match exactly across the parts. For those that aren't /// required to match, if they don't match we should remove them so as to not expose data @@ -875,7 +891,7 @@ impl OutboundPayments { } } - let route = router.find_route_with_id( + let mut route = router.find_route_with_id( &node_signer.get_node_id(Recipient::Node).unwrap(), &route_params, Some(&first_hops.iter().collect::>()), inflight_htlcs(), payment_hash, payment_id, @@ -885,6 +901,14 @@ impl OutboundPayments { RetryableSendFailure::RouteNotFound })?; + if let Some(route_route_params) = route.route_params.as_mut() { + if route_route_params.final_value_msat != route_params.final_value_msat { + debug_assert!(false, + "Routers are expected to return a route which includes the requested final_value_msat"); + route_route_params.final_value_msat = route_params.final_value_msat; + } + } + 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) @@ -926,7 +950,7 @@ impl OutboundPayments { } } - let route = match router.find_route_with_id( + let mut route = match router.find_route_with_id( &node_signer.get_node_id(Recipient::Node).unwrap(), &route_params, Some(&first_hops.iter().collect::>()), inflight_htlcs(), payment_hash, payment_id, @@ -938,6 +962,15 @@ impl OutboundPayments { return } }; + + if let Some(route_route_params) = route.route_params.as_mut() { + if route_route_params.final_value_msat != route_params.final_value_msat { + debug_assert!(false, + "Routers are expected to return a route which includes the requested final_value_msat"); + route_route_params.final_value_msat = route_params.final_value_msat; + } + } + for path in route.paths.iter() { if path.hops.len() == 0 { log_error!(logger, "Unusable path in route (path.hops.len() must be at least 1"); @@ -1337,12 +1370,14 @@ impl OutboundPayments { } let mut has_ok = false; let mut has_err = false; - let mut pending_amt_unsent = 0; + let mut has_unsent = false; let mut total_ok_fees_msat = 0; + let mut total_ok_amt_sent_msat = 0; for (res, path) in results.iter().zip(route.paths.iter()) { if res.is_ok() { has_ok = true; total_ok_fees_msat += path.fee_msat(); + total_ok_amt_sent_msat += path.final_value_msat(); } if res.is_err() { has_err = true; } if let &Err(APIError::MonitorUpdateInProgress) = res { @@ -1351,23 +1386,27 @@ impl OutboundPayments { has_err = true; has_ok = true; total_ok_fees_msat += path.fee_msat(); + total_ok_amt_sent_msat += path.final_value_msat(); } else if res.is_err() { - pending_amt_unsent += path.final_value_msat(); + has_unsent = true; } } if has_err && has_ok { Err(PaymentSendFailure::PartialFailure { results, payment_id, - failed_paths_retry: if pending_amt_unsent != 0 { + failed_paths_retry: if has_unsent { if let Some(route_params) = &route.route_params { let mut route_params = route_params.clone(); // We calculate the leftover fee budget we're allowed to spend by // subtracting the used fee from the total fee budget. route_params.max_total_routing_fee_msat = route_params .max_total_routing_fee_msat.map(|m| m.saturating_sub(total_ok_fees_msat)); - route_params.final_value_msat = pending_amt_unsent; + // We calculate the remaining target amount by subtracting the succeded + // path values. + route_params.final_value_msat = route_params.final_value_msat + .saturating_sub(total_ok_amt_sent_msat); Some(route_params) } else { None } } else { None }, @@ -1479,6 +1518,9 @@ impl OutboundPayments { &self, pending_events: &Mutex)>>) { let mut pending_outbound_payments = self.pending_outbound_payments.lock().unwrap(); + #[cfg(not(invreqfailed))] + let pending_events = pending_events.lock().unwrap(); + #[cfg(invreqfailed)] 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 @@ -1517,6 +1559,7 @@ impl OutboundPayments { if *timer_ticks_without_response <= INVOICE_REQUEST_TIMEOUT_TICKS { true } else { + #[cfg(invreqfailed)] pending_events.push_back( (events::Event::InvoiceRequestFailed { payment_id: *payment_id }, None) ); @@ -1669,6 +1712,7 @@ impl OutboundPayments { payment.remove(); } } else if let PendingOutboundPayment::AwaitingInvoice { .. } = payment.get() { + #[cfg(invreqfailed)] pending_events.lock().unwrap().push_back((events::Event::InvoiceRequestFailed { payment_id, }, None)); @@ -1759,7 +1803,9 @@ mod tests { use crate::ln::channelmanager::{PaymentId, RecipientOnionFields}; use crate::ln::features::{ChannelFeatures, NodeFeatures}; use crate::ln::msgs::{ErrorAction, LightningError}; - use crate::ln::outbound_payment::{Bolt12PaymentError, INVOICE_REQUEST_TIMEOUT_TICKS, OutboundPayments, Retry, RetryableSendFailure}; + use crate::ln::outbound_payment::{Bolt12PaymentError, OutboundPayments, Retry, RetryableSendFailure}; + #[cfg(invreqfailed)] + use crate::ln::outbound_payment::INVOICE_REQUEST_TIMEOUT_TICKS; use crate::offers::invoice::DEFAULT_RELATIVE_EXPIRY; use crate::offers::offer::OfferBuilder; use crate::offers::test_utils::*; @@ -1962,6 +2008,7 @@ mod tests { } #[test] + #[cfg(invreqfailed)] fn removes_stale_awaiting_invoice() { let pending_events = Mutex::new(VecDeque::new()); let outbound_payments = OutboundPayments::new(); @@ -2000,6 +2047,7 @@ mod tests { } #[test] + #[cfg(invreqfailed)] fn removes_abandoned_awaiting_invoice() { let pending_events = Mutex::new(VecDeque::new()); let outbound_payments = OutboundPayments::new(); @@ -2084,11 +2132,6 @@ mod tests { let outbound_payments = OutboundPayments::new(); let payment_id = PaymentId([0; 32]); - assert!( - outbound_payments.add_new_awaiting_invoice(payment_id, Retry::Attempts(0), None).is_ok() - ); - assert!(outbound_payments.has_pending_payments()); - let invoice = OfferBuilder::new("foo".into(), recipient_pubkey()) .amount_msats(1000) .build().unwrap() @@ -2099,6 +2142,12 @@ mod tests { .build().unwrap() .sign(recipient_sign).unwrap(); + assert!(outbound_payments.add_new_awaiting_invoice( + payment_id, Retry::Attempts(0), Some(invoice.amount_msats() / 100 + 50_000)) + .is_ok() + ); + assert!(outbound_payments.has_pending_payments()); + router.expect_find_route( RouteParameters::from_payment_params_and_value( PaymentParameters::from_bolt12_invoice(&invoice), @@ -2139,11 +2188,6 @@ mod tests { let outbound_payments = OutboundPayments::new(); let payment_id = PaymentId([0; 32]); - assert!( - outbound_payments.add_new_awaiting_invoice(payment_id, Retry::Attempts(0), None).is_ok() - ); - assert!(outbound_payments.has_pending_payments()); - let invoice = OfferBuilder::new("foo".into(), recipient_pubkey()) .amount_msats(1000) .build().unwrap() @@ -2154,6 +2198,12 @@ mod tests { .build().unwrap() .sign(recipient_sign).unwrap(); + assert!(outbound_payments.add_new_awaiting_invoice( + payment_id, Retry::Attempts(0), Some(invoice.amount_msats() / 100 + 50_000)) + .is_ok() + ); + assert!(outbound_payments.has_pending_payments()); + let route_params = RouteParameters::from_payment_params_and_value( PaymentParameters::from_bolt12_invoice(&invoice), invoice.amount_msats(),