-
Notifications
You must be signed in to change notification settings - Fork 402
Authenticate InvoiceError
messages
#3192
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
f52bd0b
8119fbf
0ca9faf
bb445a3
144d488
fbaf093
cfd0980
e4ca166
05db67b
19dec4b
16bbb7b
4567ce5
14153ed
a9e6363
f00b782
457ba24
1563186
bb94320
075a2e3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -502,6 +502,12 @@ impl_writeable_tlv_based_enum!(InterceptNextHop, | |
#[derive(Clone, Copy, Debug, PartialEq, Eq)] | ||
pub enum PaymentFailureReason { | ||
/// The intended recipient rejected our payment. | ||
/// | ||
/// Also used for [`UnknownRequiredFeatures`] and [`InvoiceRequestRejected`] when downgrading to | ||
/// version prior to 0.0.124. | ||
/// | ||
/// [`UnknownRequiredFeatures`]: Self::UnknownRequiredFeatures | ||
/// [`InvoiceRequestRejected`]: Self::InvoiceRequestRejected | ||
RecipientRejected, | ||
/// The user chose to abandon this payment by calling [`ChannelManager::abandon_payment`]. | ||
/// | ||
|
@@ -517,7 +523,10 @@ pub enum PaymentFailureReason { | |
/// The payment expired while retrying, based on the provided | ||
/// [`PaymentParameters::expiry_time`]. | ||
/// | ||
/// Also used for [`InvoiceRequestExpired`] when downgrading to version prior to 0.0.124. | ||
/// | ||
/// [`PaymentParameters::expiry_time`]: crate::routing::router::PaymentParameters::expiry_time | ||
/// [`InvoiceRequestExpired`]: Self::InvoiceRequestExpired | ||
PaymentExpired, | ||
/// We failed to find a route while retrying the payment. | ||
/// | ||
|
@@ -528,12 +537,23 @@ pub enum PaymentFailureReason { | |
/// This error should generally never happen. This likely means that there is a problem with | ||
/// your router. | ||
UnexpectedError, | ||
/// An invoice was received that required unknown features. | ||
UnknownRequiredFeatures, | ||
/// A [`Bolt12Invoice`] was not received in a reasonable amount of time. | ||
InvoiceRequestExpired, | ||
/// An [`InvoiceRequest`] for the payment was rejected by the recipient. | ||
/// | ||
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest | ||
InvoiceRequestRejected, | ||
} | ||
|
||
impl_writeable_tlv_based_enum!(PaymentFailureReason, | ||
impl_writeable_tlv_based_enum_upgradable!(PaymentFailureReason, | ||
(0, RecipientRejected) => {}, | ||
(1, UnknownRequiredFeatures) => {}, | ||
(2, UserAbandoned) => {}, | ||
(3, InvoiceRequestExpired) => {}, | ||
(4, RetriesExhausted) => {}, | ||
(5, InvoiceRequestRejected) => {}, | ||
(6, PaymentExpired) => {}, | ||
(8, RouteNotFound) => {}, | ||
(10, UnexpectedError) => {}, | ||
|
@@ -769,22 +789,6 @@ pub enum Event { | |
/// Sockets for connecting to the node. | ||
addresses: Vec<msgs::SocketAddress>, | ||
}, | ||
/// Indicates a request for an invoice failed to yield a response in a reasonable amount of time | ||
/// or was explicitly abandoned by [`ChannelManager::abandon_payment`]. This may be for an | ||
/// [`InvoiceRequest`] sent for an [`Offer`] or for a [`Refund`] that hasn't been redeemed. | ||
/// | ||
/// # Failure Behavior and Persistence | ||
/// This event will eventually be replayed after failures-to-handle (i.e., the event handler | ||
/// returning `Err(ReplayEvent ())`) and will be persisted across restarts. | ||
/// | ||
/// [`ChannelManager::abandon_payment`]: crate::ln::channelmanager::ChannelManager::abandon_payment | ||
/// [`InvoiceRequest`]: crate::offers::invoice_request::InvoiceRequest | ||
/// [`Offer`]: crate::offers::offer::Offer | ||
/// [`Refund`]: crate::offers::refund::Refund | ||
InvoiceRequestFailed { | ||
/// The `payment_id` to have been associated with payment for the requested invoice. | ||
payment_id: PaymentId, | ||
}, | ||
/// Indicates a [`Bolt12Invoice`] in response to an [`InvoiceRequest`] or a [`Refund`] was | ||
/// received. | ||
/// | ||
|
@@ -876,12 +880,15 @@ pub enum Event { | |
/// | ||
/// [`ChannelManager::send_payment`]: crate::ln::channelmanager::ChannelManager::send_payment | ||
payment_id: PaymentId, | ||
/// The hash that was given to [`ChannelManager::send_payment`]. | ||
/// The hash that was given to [`ChannelManager::send_payment`]. `None` if the payment failed | ||
/// before receiving an invoice when paying a BOLT12 [`Offer`]. | ||
/// | ||
/// [`ChannelManager::send_payment`]: crate::ln::channelmanager::ChannelManager::send_payment | ||
payment_hash: PaymentHash, | ||
/// [`Offer`]: crate::offers::offer::Offer | ||
payment_hash: Option<PaymentHash>, | ||
/// The reason the payment failed. This is only `None` for events generated or serialized | ||
/// by versions prior to 0.0.115. | ||
/// by versions prior to 0.0.115, or when downgrading to a version with a reason that was | ||
/// added after. | ||
reason: Option<PaymentFailureReason>, | ||
}, | ||
/// Indicates that a path for an outbound payment was successful. | ||
|
@@ -1552,10 +1559,34 @@ impl Writeable for Event { | |
}, | ||
&Event::PaymentFailed { ref payment_id, ref payment_hash, ref reason } => { | ||
15u8.write(writer)?; | ||
let (payment_hash, invoice_received) = match payment_hash { | ||
Some(payment_hash) => (payment_hash, true), | ||
None => (&PaymentHash([0; 32]), false), | ||
}; | ||
let legacy_reason = match reason { | ||
None => &None, | ||
// Variants available prior to version 0.0.124. | ||
Some(PaymentFailureReason::RecipientRejected) | ||
| Some(PaymentFailureReason::UserAbandoned) | ||
| Some(PaymentFailureReason::RetriesExhausted) | ||
| Some(PaymentFailureReason::PaymentExpired) | ||
| Some(PaymentFailureReason::RouteNotFound) | ||
| Some(PaymentFailureReason::UnexpectedError) => reason, | ||
// Variants introduced at version 0.0.124 or later. Prior versions fail to parse | ||
// unknown variants, while versions 0.0.124 or later will use None. | ||
Some(PaymentFailureReason::UnknownRequiredFeatures) => | ||
&Some(PaymentFailureReason::RecipientRejected), | ||
Some(PaymentFailureReason::InvoiceRequestExpired) => | ||
&Some(PaymentFailureReason::RetriesExhausted), | ||
Some(PaymentFailureReason::InvoiceRequestRejected) => | ||
&Some(PaymentFailureReason::RecipientRejected), | ||
}; | ||
write_tlv_fields!(writer, { | ||
(0, payment_id, required), | ||
(1, reason, option), | ||
(1, legacy_reason, option), | ||
(2, payment_hash, required), | ||
(3, invoice_received, required), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks like if someone downgraded after writing a zeroed out payment hash, then upgraded again, their event would then contain a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, not sure how concerning that is but went ahead with your alternative. @TheBlueMatt I'm not checking in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we keep the old code writing the extra flag but then drop 0 payment hash if its missing? While people shouldn't be paying 0 payment hashes, its a bit weird that the payment hash will dissapear on you if you try to pay one because your counterparty made one up. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated accordingly, IIUC. |
||
(5, reason, option), | ||
}) | ||
}, | ||
&Event::OpenChannelRequest { .. } => { | ||
|
@@ -1634,12 +1665,6 @@ impl Writeable for Event { | |
(8, funding_txo, required), | ||
}); | ||
}, | ||
&Event::InvoiceRequestFailed { ref payment_id } => { | ||
33u8.write(writer)?; | ||
write_tlv_fields!(writer, { | ||
(0, payment_id, required), | ||
}) | ||
}, | ||
&Event::ConnectionNeeded { .. } => { | ||
35u8.write(writer)?; | ||
// Never write ConnectionNeeded events as buffered onion messages aren't serialized. | ||
|
@@ -1929,15 +1954,24 @@ impl MaybeReadable for Event { | |
let mut payment_hash = PaymentHash([0; 32]); | ||
let mut payment_id = PaymentId([0; 32]); | ||
let mut reason = None; | ||
let mut legacy_reason = None; | ||
let mut invoice_received: Option<bool> = None; | ||
read_tlv_fields!(reader, { | ||
(0, payment_id, required), | ||
(1, reason, upgradable_option), | ||
(1, legacy_reason, upgradable_option), | ||
(2, payment_hash, required), | ||
(3, invoice_received, option), | ||
(5, reason, upgradable_option), | ||
}); | ||
let payment_hash = match invoice_received { | ||
Some(invoice_received) => invoice_received.then(|| payment_hash), | ||
None => (payment_hash != PaymentHash([0; 32])).then(|| payment_hash), | ||
}; | ||
let reason = reason.or(legacy_reason); | ||
Ok(Some(Event::PaymentFailed { | ||
payment_id, | ||
payment_hash, | ||
reason, | ||
reason: _init_tlv_based_struct_field!(reason, upgradable_option), | ||
})) | ||
}; | ||
f() | ||
|
@@ -2076,13 +2110,16 @@ impl MaybeReadable for Event { | |
}; | ||
f() | ||
}, | ||
// This was Event::InvoiceRequestFailed prior to version 0.0.124. | ||
33u8 => { | ||
let mut f = || { | ||
_init_and_read_len_prefixed_tlv_fields!(reader, { | ||
(0, payment_id, required), | ||
}); | ||
Ok(Some(Event::InvoiceRequestFailed { | ||
Ok(Some(Event::PaymentFailed { | ||
payment_id: payment_id.0.unwrap(), | ||
payment_hash: None, | ||
reason: Some(PaymentFailureReason::InvoiceRequestExpired), | ||
})) | ||
}; | ||
f() | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm pretty meh on the idea that users might upgrade, use bolt12, and suddenly be unable to downgrade even one version. Not entirely sure what to do about it, though - we could delay this part of the patch for a release, or we could write the
PaymentFailureReason
twice, once with these versions in a new TLV and once without, mapping them toUnexpectedError
or whatever.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Went ahead and wrote the reason twice.
UnexpectedError
would be a misleading, IMO, though. UsedNone
instead, but let me know if there was some other alternative you were thinking about.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I suppose
None
is fine, but its a bit annoying to suddenly have no reason when the docs on 123 say that its "only None for events generated by versions prior to 0.0.115". I guess forInvoiceRequestRejected
we could easily just sayRecipientRejected
, butInvoiceRequestExpired
is tougher...maybeRetriesExhausted
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm...
UnknownRequiredFeatures
also needs to map to something.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RecipientRejected
, I assume - they would have rejected a payment that has unknown features in the onion so...?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here it is us -- the payer -- rejecting an invoice for an outbound payment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea but the principle is the same - we have some issue causing us to be unable to talk to the intended recipient so cannot pay. RecipientRejected makes sense imo as it's also the most final - "don't bother trying this again cause they already said they won't accept it"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gotcha. Updated now.