Add a new AwaitingOffer outbound payment state for BIP 353

This commit is contained in:
Matt Corallo 2024-07-12 23:37:54 +00:00
parent a39e274e3d
commit 46df35b0ff
2 changed files with 60 additions and 25 deletions

View File

@ -3605,11 +3605,11 @@ where
pub fn list_recent_payments(&self) -> Vec<RecentPaymentDetails> {
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 { .. } => {},

View File

@ -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<u64>,
/// 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<PaymentHash> {
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)]