Skip to content

Commit

Permalink
Include invreq in payment onion when retrying async payments
Browse files Browse the repository at this point in the history
While in the last commit we began including invoice requests in async payment
onions on initial send, further work is needed to include them on retry. Here
we begin storing invreqs in our retry data, and pass them along for inclusion
in the onion on payment retry.

Per <lightning/bolts#1149>, when paying a static
invoice we need to include our original invoice request in the HTLC onion since
the recipient wouldn't have received it previously.
  • Loading branch information
valentinewallace committed Oct 24, 2024
1 parent a3c31f6 commit 44f0a7b
Showing 1 changed file with 38 additions and 16 deletions.
54 changes: 38 additions & 16 deletions lightning/src/ln/outbound_payment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ pub(crate) enum PendingOutboundPayment {
payment_secret: Option<PaymentSecret>,
payment_metadata: Option<Vec<u8>>,
keysend_preimage: Option<PaymentPreimage>,
invoice_request: Option<InvoiceRequest>,
custom_tlvs: Vec<(u64, Vec<u8>)>,
pending_amt_msat: u64,
/// Used to track the fee paid. Present iff the payment was serialized on 0.0.103+.
Expand Down Expand Up @@ -948,20 +949,36 @@ impl OutboundPayments {
};

let payment_params = Some(route_params.payment_params.clone());
let (retryable_payment, onion_session_privs) = self.create_pending_payment(
payment_hash, recipient_onion.clone(), keysend_preimage, &route, Some(retry_strategy),
payment_params, entropy_source, best_block_height
);
match self.pending_outbound_payments.lock().unwrap().entry(payment_id) {
macro_rules! create_pending_payment {
($invreq_opt: expr) => {
self.create_pending_payment(
payment_hash, recipient_onion.clone(), keysend_preimage, $invreq_opt, &route,
Some(retry_strategy), payment_params, entropy_source, best_block_height
)
}
}

let mut outbounds = self.pending_outbound_payments.lock().unwrap();
let onion_session_privs = match outbounds.entry(payment_id) {
hash_map::Entry::Occupied(entry) => match entry.get() {
PendingOutboundPayment::InvoiceReceived { .. }
| PendingOutboundPayment::StaticInvoiceReceived { .. } => {
PendingOutboundPayment::InvoiceReceived { .. } => {
let (retryable_payment, onion_session_privs) = create_pending_payment!(None);
*entry.into_mut() = retryable_payment;
onion_session_privs
},
PendingOutboundPayment::StaticInvoiceReceived { .. } => {
let invreq = if let PendingOutboundPayment::StaticInvoiceReceived { invoice_request, .. } = entry.remove() {
invoice_request
} else { unreachable!() };
let (retryable_payment, onion_session_privs) = create_pending_payment!(Some(invreq));
outbounds.insert(payment_id, retryable_payment);
onion_session_privs
},
_ => return Err(Bolt12PaymentError::DuplicateInvoice),
},
hash_map::Entry::Vacant(_) => return Err(Bolt12PaymentError::UnexpectedInvoice),
}
};
core::mem::drop(outbounds);

let result = self.pay_route_internal(
&route, payment_hash, &recipient_onion, keysend_preimage, invoice_request, payment_id,
Expand Down Expand Up @@ -1324,14 +1341,14 @@ impl OutboundPayments {
}
}
}
let (total_msat, recipient_onion, keysend_preimage, onion_session_privs) = {
let (total_msat, recipient_onion, keysend_preimage, onion_session_privs, invoice_request) = {
let mut outbounds = self.pending_outbound_payments.lock().unwrap();
match outbounds.entry(payment_id) {
hash_map::Entry::Occupied(mut payment) => {
match payment.get() {
PendingOutboundPayment::Retryable {
total_msat, keysend_preimage, payment_secret, payment_metadata,
custom_tlvs, pending_amt_msat, ..
custom_tlvs, pending_amt_msat, invoice_request, ..
} => {
const RETRY_OVERFLOW_PERCENTAGE: u64 = 10;
let retry_amt_msat = route.get_total_amount();
Expand All @@ -1354,6 +1371,7 @@ impl OutboundPayments {
custom_tlvs: custom_tlvs.clone(),
};
let keysend_preimage = *keysend_preimage;
let invoice_request = invoice_request.clone();

let mut onion_session_privs = Vec::with_capacity(route.paths.len());
for _ in 0..route.paths.len() {
Expand All @@ -1366,7 +1384,7 @@ impl OutboundPayments {

payment.get_mut().increment_attempts();

(total_msat, recipient_onion, keysend_preimage, onion_session_privs)
(total_msat, recipient_onion, keysend_preimage, onion_session_privs, invoice_request)
},
PendingOutboundPayment::Legacy { .. } => {
log_error!(logger, "Unable to retry payments that were initially sent on LDK versions prior to 0.0.102");
Expand Down Expand Up @@ -1404,8 +1422,8 @@ impl OutboundPayments {
}
};
let res = self.pay_route_internal(&route, payment_hash, &recipient_onion, keysend_preimage,
None, payment_id, Some(total_msat), onion_session_privs, node_signer, best_block_height,
&send_payment_along_path);
invoice_request.as_ref(), payment_id, Some(total_msat), onion_session_privs, node_signer,
best_block_height, &send_payment_along_path);
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);
Expand Down Expand Up @@ -1557,7 +1575,7 @@ impl OutboundPayments {
hash_map::Entry::Occupied(_) => Err(PaymentSendFailure::DuplicatePayment),
hash_map::Entry::Vacant(entry) => {
let (payment, onion_session_privs) = self.create_pending_payment(
payment_hash, recipient_onion, keysend_preimage, route, retry_strategy,
payment_hash, recipient_onion, keysend_preimage, None, route, retry_strategy,
payment_params, entropy_source, best_block_height
);
entry.insert(payment);
Expand All @@ -1568,8 +1586,9 @@ impl OutboundPayments {

fn create_pending_payment<ES: Deref>(
&self, payment_hash: PaymentHash, recipient_onion: RecipientOnionFields,
keysend_preimage: Option<PaymentPreimage>, route: &Route, retry_strategy: Option<Retry>,
payment_params: Option<PaymentParameters>, entropy_source: &ES, best_block_height: u32
keysend_preimage: Option<PaymentPreimage>, invoice_request: Option<InvoiceRequest>,
route: &Route, retry_strategy: Option<Retry>, payment_params: Option<PaymentParameters>,
entropy_source: &ES, best_block_height: u32
) -> (PendingOutboundPayment, Vec<[u8; 32]>)
where
ES::Target: EntropySource,
Expand All @@ -1590,6 +1609,7 @@ impl OutboundPayments {
payment_secret: recipient_onion.payment_secret,
payment_metadata: recipient_onion.payment_metadata,
keysend_preimage,
invoice_request,
custom_tlvs: recipient_onion.custom_tlvs,
starting_block_height: best_block_height,
total_msat: route.get_total_amount(),
Expand Down Expand Up @@ -2151,6 +2171,7 @@ impl OutboundPayments {
payment_secret: None, // only used for retries, and we'll never retry on startup
payment_metadata: None, // only used for retries, and we'll never retry on startup
keysend_preimage: None, // only used for retries, and we'll never retry on startup
invoice_request: None, // only used for retries, and we'll never retry on startup
custom_tlvs: Vec::new(), // only used for retries, and we'll never retry on startup
pending_amt_msat: path_amt,
pending_fee_msat: Some(path_fee),
Expand Down Expand Up @@ -2237,6 +2258,7 @@ impl_writeable_tlv_based_enum_upgradable!(PendingOutboundPayment,
(9, custom_tlvs, optional_vec),
(10, starting_block_height, required),
(11, remaining_max_total_routing_fee_msat, option),
(13, invoice_request, option),
(not_written, retry_strategy, (static_value, None)),
(not_written, attempts, (static_value, PaymentAttempts::new())),
},
Expand Down

0 comments on commit 44f0a7b

Please sign in to comment.