From 46df35b0ff466a33c901d1ab5a77dff6facfca2e Mon Sep 17 00:00:00 2001 From: Matt Corallo Date: Fri, 12 Jul 2024 23:37:54 +0000 Subject: [PATCH] Add a new `AwaitingOffer` outbound payment state for BIP 353 --- lightning/src/ln/channelmanager.rs | 11 +++-- lightning/src/ln/outbound_payment.rs | 74 ++++++++++++++++++++-------- 2 files changed, 60 insertions(+), 25 deletions(-) diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs index 575a1b201..b6b3f4fd7 100644 --- a/lightning/src/ln/channelmanager.rs +++ b/lightning/src/ln/channelmanager.rs @@ -3605,11 +3605,11 @@ where pub fn list_recent_payments(&self) -> Vec { self.pending_outbound_payments.pending_outbound_payments.lock().unwrap().iter() .filter_map(|(payment_id, pending_outbound_payment)| match pending_outbound_payment { - PendingOutboundPayment::AwaitingInvoice { .. } => { - Some(RecentPaymentDetails::AwaitingInvoice { payment_id: *payment_id }) - }, - // InvoiceReceived is an intermediate state and doesn't need to be exposed - PendingOutboundPayment::InvoiceReceived { .. } => { + PendingOutboundPayment::AwaitingInvoice { .. } + | PendingOutboundPayment::AwaitingOffer { .. } + // InvoiceReceived is an intermediate state and doesn't need to be exposed + | PendingOutboundPayment::InvoiceReceived { .. } => + { Some(RecentPaymentDetails::AwaitingInvoice { payment_id: *payment_id }) }, PendingOutboundPayment::StaticInvoiceReceived { .. } => { @@ -12254,6 +12254,7 @@ where } } PendingOutboundPayment::AwaitingInvoice { .. } => {}, + PendingOutboundPayment::AwaitingOffer { .. } => {}, PendingOutboundPayment::InvoiceReceived { .. } => {}, PendingOutboundPayment::StaticInvoiceReceived { .. } => {}, PendingOutboundPayment::Fulfilled { .. } => {}, diff --git a/lightning/src/ln/outbound_payment.rs b/lightning/src/ln/outbound_payment.rs index c3fde629a..8cdeadeaa 100644 --- a/lightning/src/ln/outbound_payment.rs +++ b/lightning/src/ln/outbound_payment.rs @@ -58,6 +58,15 @@ pub(crate) enum PendingOutboundPayment { Legacy { session_privs: HashSet<[u8; 32]>, }, + /// Used when we are waiting for an Offer to come back from a BIP 353 resolution + AwaitingOffer { + expiration: StaleExpiration, + retry_strategy: Retry, + max_total_routing_fee_msat: Option, + /// Human Readable Names-originated payments should always specify an explicit amount to + /// send up-front, which we track here and enforce once we receive the offer. + amount_msats: u64, + }, AwaitingInvoice { expiration: StaleExpiration, retry_strategy: Retry, @@ -201,6 +210,7 @@ impl PendingOutboundPayment { fn payment_hash(&self) -> Option { match self { PendingOutboundPayment::Legacy { .. } => None, + PendingOutboundPayment::AwaitingOffer { .. } => None, PendingOutboundPayment::AwaitingInvoice { .. } => None, PendingOutboundPayment::InvoiceReceived { payment_hash, .. } => Some(*payment_hash), PendingOutboundPayment::StaticInvoiceReceived { payment_hash, .. } => Some(*payment_hash), @@ -217,7 +227,8 @@ impl PendingOutboundPayment { PendingOutboundPayment::Retryable { session_privs, .. } | PendingOutboundPayment::Fulfilled { session_privs, .. } | PendingOutboundPayment::Abandoned { session_privs, .. } => session_privs, - PendingOutboundPayment::AwaitingInvoice { .. } | + PendingOutboundPayment::AwaitingOffer { .. } | + PendingOutboundPayment::AwaitingInvoice { .. } | PendingOutboundPayment::InvoiceReceived { .. } | PendingOutboundPayment::StaticInvoiceReceived { .. } => { debug_assert!(false); return; }, }); @@ -258,7 +269,8 @@ impl PendingOutboundPayment { PendingOutboundPayment::Abandoned { session_privs, .. } => { session_privs.remove(session_priv) }, - PendingOutboundPayment::AwaitingInvoice { .. } | + PendingOutboundPayment::AwaitingOffer { .. } | + PendingOutboundPayment::AwaitingInvoice { .. } | PendingOutboundPayment::InvoiceReceived { .. } | PendingOutboundPayment::StaticInvoiceReceived { .. } => { debug_assert!(false); false }, }; @@ -288,7 +300,8 @@ impl PendingOutboundPayment { PendingOutboundPayment::Retryable { session_privs, .. } => { session_privs.insert(session_priv) }, - PendingOutboundPayment::AwaitingInvoice { .. } | + PendingOutboundPayment::AwaitingOffer { .. } | + PendingOutboundPayment::AwaitingInvoice { .. } | PendingOutboundPayment::InvoiceReceived { .. } | PendingOutboundPayment::StaticInvoiceReceived { .. } => { debug_assert!(false); false }, PendingOutboundPayment::Fulfilled { .. } => false, @@ -322,6 +335,7 @@ impl PendingOutboundPayment { session_privs.len() }, PendingOutboundPayment::AwaitingInvoice { .. } => 0, + PendingOutboundPayment::AwaitingOffer { .. } => 0, PendingOutboundPayment::InvoiceReceived { .. } => 0, PendingOutboundPayment::StaticInvoiceReceived { .. } => 0, } @@ -416,8 +430,9 @@ impl Display for PaymentAttempts { } } -/// How long before a [`PendingOutboundPayment::AwaitingInvoice`] should be considered stale and -/// candidate for removal in [`OutboundPayments::remove_stale_payments`]. +/// How long before a [`PendingOutboundPayment::AwaitingInvoice`] or +/// [`PendingOutboundPayment::AwaitingOffer`] should be considered stale and candidate for removal +/// in [`OutboundPayments::remove_stale_payments`]. #[derive(Clone, Copy)] pub(crate) enum StaleExpiration { /// Number of times [`OutboundPayments::remove_stale_payments`] is called. @@ -1388,7 +1403,9 @@ impl OutboundPayments { log_error!(logger, "Unable to retry payments that were initially sent on LDK versions prior to 0.0.102"); return }, - PendingOutboundPayment::AwaitingInvoice { .. } => { + PendingOutboundPayment::AwaitingInvoice { .. } + | PendingOutboundPayment::AwaitingOffer { .. } => + { log_error!(logger, "Payment not yet sent"); debug_assert!(false); return @@ -1910,7 +1927,9 @@ impl OutboundPayments { true } }, - PendingOutboundPayment::AwaitingInvoice { expiration, .. } => { + PendingOutboundPayment::AwaitingInvoice { expiration, .. } + | PendingOutboundPayment::AwaitingOffer { expiration, .. } => + { let is_stale = match expiration { StaleExpiration::AbsoluteTimeout(absolute_expiry) => { *absolute_expiry <= duration_since_epoch @@ -2096,22 +2115,28 @@ impl OutboundPayments { 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 { + match payment.get() { + PendingOutboundPayment::Abandoned { payment_hash, reason, .. } => { + if payment.get().remaining_parts() == 0 { + pending_events.lock().unwrap().push_back((events::Event::PaymentFailed { + payment_id, + payment_hash: Some(*payment_hash), + reason: *reason, + }, None)); + payment.remove(); + } + }, + PendingOutboundPayment::AwaitingInvoice { .. } + | PendingOutboundPayment::AwaitingOffer { .. } => + { pending_events.lock().unwrap().push_back((events::Event::PaymentFailed { payment_id, - payment_hash: Some(*payment_hash), - reason: *reason, + payment_hash: None, + reason: Some(reason), }, None)); payment.remove(); - } - } else if let PendingOutboundPayment::AwaitingInvoice { .. } = payment.get() { - pending_events.lock().unwrap().push_back((events::Event::PaymentFailed { - payment_id, - payment_hash: None, - reason: Some(reason), - }, None)); - payment.remove(); + }, + _ => {}, } } } @@ -2183,7 +2208,8 @@ impl OutboundPayments { match self.pending_outbound_payments.lock().unwrap().entry(payment_id) { hash_map::Entry::Occupied(mut entry) => { let newly_added = match entry.get() { - PendingOutboundPayment::AwaitingInvoice { .. } | + PendingOutboundPayment::AwaitingOffer { .. } | + PendingOutboundPayment::AwaitingInvoice { .. } | PendingOutboundPayment::InvoiceReceived { .. } | PendingOutboundPayment::StaticInvoiceReceived { .. } => { @@ -2285,6 +2311,14 @@ impl_writeable_tlv_based_enum_upgradable!(PendingOutboundPayment, (6, route_params, required), (8, invoice_request, required), }, + // Added in 0.1. Prior versions will drop these outbounds on downgrade, which is safe because + // no HTLCs are in-flight. + (11, AwaitingOffer) => { + (0, expiration, required), + (2, retry_strategy, required), + (4, max_total_routing_fee_msat, option), + (6, amount_msats, required), + }, ); #[cfg(test)] -- 2.39.5