-
Notifications
You must be signed in to change notification settings - Fork 366
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
Include invoice requests in async payment onions #3207
Include invoice requests in async payment onions #3207
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #3207 +/- ##
==========================================
- Coverage 89.64% 89.62% -0.02%
==========================================
Files 126 126
Lines 102676 102705 +29
Branches 102676 102705 +29
==========================================
+ Hits 92043 92053 +10
- Misses 7909 7926 +17
- Partials 2724 2726 +2 ☔ View full report in Codecov by Sentry. |
dca3ac9
to
f02d149
Compare
f02d149
to
6c56443
Compare
c202e04
to
e3ae4c1
Compare
e3ae4c1
to
4d0da54
Compare
Needs rebase. |
4d0da54
to
1350979
Compare
1350979
to
b5ccb98
Compare
lightning/src/ln/outbound_payment.rs
Outdated
match outbounds.entry(payment_id) { | ||
hash_map::Entry::Occupied(entry) => match entry.remove() { | ||
PendingOutboundPayment::InvoiceReceived { .. } => { | ||
outbounds.insert(payment_id, retryable_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.
Please use the entry
API to avoid rehashing here.
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 did this to avoid cloning the invoice request. It seemed necessary to fully move the payment out of the map to do so. Or do you mean avoid remove
ing in the ::InvoiceReceived
case, and keep it in the ::StaticInvoiceReceived
case?
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.
You could split it, yea, but you could also take
the invoice_request
before overwriting the field?
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.
Check out the latest version! I ended up splitting it, because it looks like you can't just take
since it's also used later in pay_route_internal
.
b5ccb98
to
568decf
Compare
invoice_request: | ||
retryable_invoice_request | ||
.take() | ||
.ok_or(Bolt12PaymentError::UnexpectedInvoice)? | ||
.invoice_request, |
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.
This may be None
if the InvoiceRequest
was already retried, so we'll need to have some other means of tracking whether or not to retry. I think this was mentioned in an earlier PR, so perhaps it will be handled in a follow-up?
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.
Ah yeah, probably will do that in a follow-up. Looks like it's tracked here #2298. Thanks for the reminder!
568decf
to
9e316c7
Compare
486beb9
to
ed8cc0c
Compare
Rebased due to conflicts, then pushed an additional typo fix: diff --git a/lightning/src/events/mod.rs b/lightning/src/events/mod.rs
index bc7bbb641..823c5e2c2 100644
--- a/lightning/src/events/mod.rs
+++ b/lightning/src/events/mod.rs
@@ -566,7 +566,7 @@ pub enum PaymentFailureReason {
/// routes - we tried the payment over a few routes but were not able to find any further
/// candidate routes beyond those.
///
- /// Also used for [`BlindedPathCreationFailed`] down downgrading to versions prior to 0.0.124.
+ /// Also used for [`BlindedPathCreationFailed`] when downgrading to versions prior to 0.0.124.
RouteNotFound,
/// This error should generally never happen. This likely means that there is a problem with
/// your router. |
ed8cc0c
to
c56d8ef
Compare
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.
LGTM other than some minor comments.
@@ -613,8 +613,9 @@ impl RouteParameters { | |||
&mut self, recipient_onion: &RecipientOnionFields, is_keysend: bool, best_block_height: u32 | |||
) -> Result<(), ()> { | |||
let keysend_preimage_opt = is_keysend.then(|| PaymentPreimage([42; 32])); | |||
// TODO: no way to account for the invoice request here yet |
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.
Can't we pass it to RouteParameters::set_max_path_length
as an Option
parameter? Doesn't look like it is ever called internally, though. Is this mainly for use outside of ChannelManager
?
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.
We can though we'll need some way to clarify in the API that it should only be passed in for payments to a static invoice, which isn't part of LDK's public API yet. Not sure if it's worth addressing now.
Doesn't look like it is ever called internally, though. Is this mainly for use outside of ChannelManager?
Yep, just seems like a useful util on its own IIRC.
} => { | ||
// We need to update [`ln::outbound_payment::RecipientOnionFields::with_custom_tlvs`] | ||
// to reject any reserved types in the experimental range if new ones are ever | ||
// standardized. | ||
let invoice_request_tlv = invoice_request.map(|invreq| (77_777, invreq.encode())); // TODO: update TLV type once the async payments spec is merged |
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.
Will this type eventually be less than 2^16? If so, should we just pick something there and move this to _encode_varint_length_prefixed_tlv
instead of using custom_tlvs
?
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.
My point of reference is mainly that eclair's approach has been to use experimental TLVs until the corresponding BOLT is close(r) to merge, e.g. for their experimental trampoline features. It does seem a little bit safer and might allow us to end up with a lower type since there wouldn't be risk of overlap with another feature?
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.
LGTM. Pretty annoyed about copying the invoice requests around when sending a payment, but don't see an obvious way to fix it.
c56d8ef
to
47d82a8
Compare
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.
LGTM. Good to squash whenever.
RouteNotFound did not fit here because that error is reserved for failing to find a route for a payment, whereas here we are failing to create a blinded path back to ourselves..
Since we started using this error in send_payment_for_bolt12_invoice, this error type is no longer only used on retry but also on initial send.
"Release" is overloaded in the trait's release_pending_messages method, since the latter releases pending async payments onion messages to the peer manager, vs the release_held_htlc method handles the release_held_htlc onion message by attempting to send an HTLC to the recipient.
The method doesn't actually use its &self parameter, and this makes it more obvious that we aren't going to deadlock by calling the method if the outbound_payments lock is already acquired.
Per BOLTs PR 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. We use an experimental TLV type for this new onion payload field, since the spec is still not merged in the BOLTs.
Add a new invoice request parameter to onion_utils::build_onion_payloads. As of this commit it will always be passed in as None, to be updated in future commits. Per BOLTs PR 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.
Add a new invoice request parameter to onion_utils::create_payment_onion. As of this commit it will always be passed in as None, to be updated in future commits. Per BOLTs PR 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.
Add a new invoice request parameter to outbound_payments and channelmanager send-to-route internal utils. As of this commit the invreq will always be passed in as None, to be updated in future commits. Per BOLTs PR 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.
When transitioning outbound payments from AwaitingInvoice to StaticInvoiceReceived, include the invreq in the new state's outbound payment storage for future inclusion in an async payment onion. Per BOLTs PR 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.
Past commits have set us up to include invoice requests in outbound async payment onions. Here we actually pull the invoice request from where it's stored in outbound_payments and pass it into the correct utility for inclusion in the onion on initial send. Per BOLTs PR 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.
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 BOLTs PR 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.
Async payments include the original invoice request in the payment onion. Since invreqs may include blinded paths, it's important to factor them into our max path length calculations since they may take up a significant portion of the 1300-byte onion.
47d82a8
to
240dd0a
Compare
I went ahead and squashed, the diff prior was: diff --git a/lightning/src/ln/outbound_payment.rs b/lightning/src/ln/outbound_payment.rs
index c68209e8d..c3fde629a 100644
--- a/lightning/src/ln/outbound_payment.rs
+++ b/lightning/src/ln/outbound_payment.rs
@@ -954,7 +954,7 @@ impl OutboundPayments {
let onion_session_privs = match outbounds.entry(payment_id) {
hash_map::Entry::Occupied(entry) => match entry.get() {
PendingOutboundPayment::InvoiceReceived { .. } => {
- let (retryable_payment, onion_session_privs) = self.create_pending_payment(
+ let (retryable_payment, onion_session_privs) = Self::create_pending_payment(
payment_hash, recipient_onion.clone(), keysend_preimage, None, &route,
Some(retry_strategy), payment_params, entropy_source, best_block_height
);
@@ -965,7 +965,7 @@ impl OutboundPayments {
let invreq = if let PendingOutboundPayment::StaticInvoiceReceived { invoice_request, .. } = entry.remove() {
invoice_request
} else { unreachable!() };
- let (retryable_payment, onion_session_privs) = self.create_pending_payment(
+ let (retryable_payment, onion_session_privs) = Self::create_pending_payment(
payment_hash, recipient_onion.clone(), keysend_preimage, Some(invreq), &route,
Some(retry_strategy), payment_params, entropy_source, best_block_height
);
@@ -1572,7 +1572,7 @@ impl OutboundPayments {
match pending_outbounds.entry(payment_id) {
hash_map::Entry::Occupied(_) => Err(PaymentSendFailure::DuplicatePayment),
hash_map::Entry::Vacant(entry) => {
- let (payment, onion_session_privs) = self.create_pending_payment(
+ let (payment, onion_session_privs) = Self::create_pending_payment(
payment_hash, recipient_onion, keysend_preimage, None, route, retry_strategy,
payment_params, entropy_source, best_block_height
);
@@ -1583,7 +1583,7 @@ impl OutboundPayments {
}
fn create_pending_payment<ES: Deref>(
- &self, payment_hash: PaymentHash, recipient_onion: RecipientOnionFields,
+ payment_hash: PaymentHash, recipient_onion: RecipientOnionFields,
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
@@ -2276,8 +2276,8 @@ impl_writeable_tlv_based_enum_upgradable!(PendingOutboundPayment,
(2, retry_strategy, required),
(4, max_total_routing_fee_msat, option),
},
- // Added in 0.0.125. Prior versions will drop these outbounds on downgrade, which is safe because
- // no HTLCs are in-flight.
+ // Added in 0.1. Prior versions will drop these outbounds on downgrade, which is safe because no
+ // HTLCs are in-flight.
(9, StaticInvoiceReceived) => {
(0, payment_hash, required),
(2, keysend_preimage, required), I also removed the links to lightning/bolts#1149 in the commit messages because I realized they were majorly cluttering up that PR. |
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.
Thanks!
Title. Implements the sending-side of the final commit of lightning/bolts#1149.
Based on #3140.