-
Notifications
You must be signed in to change notification settings - Fork 385
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
Support receiving async payments #3440
Support receiving async payments #3440
Conversation
This still needs some work and tests but I opened it in case it's useful for reviewing #3408. |
904adf6
to
6f7fcba
Compare
6f7fcba
to
f2ac259
Compare
9262e08
to
f6370df
Compare
use bitcoin::secp256k1; | ||
use bitcoin::secp256k1::Secp256k1; | ||
|
||
use core::convert::Infallible; | ||
use core::time::Duration; | ||
|
||
// Goes through the async receive onion message flow, returning the final release_held_htlc OM. |
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.
For reviewers: the last commit here is rather large here but it's all tests and test util changes. Let me know if it would be helpful to break it up!
My text editor removes trailing whitespace automatically so let's make the removal official.
Store an absolute expiry in blinded message contexts for inbound async payments. Without this, anyone with the path corresponding to this context is able to trivially ask if we're online forever.
If we receive a message that an HTLC is being held upstream for us, send a reply onion message back releasing it since we are online to receive the corresponding payment.
As part of receiving an async payment, we need to verify the sender's original invoice request. Therefore, add support for parsing the invreq contained in the onion and storing it in PendingHTLCForwards to prepare for when we add this verification in an upcoming commit. The invreq also needs to be bubbled up for inclusion in the PaymentClaimable event's PaymentPurpose.
f6370df
to
c21fea2
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.
Looks good. Need to review the tests still.
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 only skimmed the tests but they LGTM
c21fea2
to
4b929c3
Compare
Added a comment: diff --git a/lightning/src/events/mod.rs b/lightning/src/events/mod.rs
index 46304b1ae..0b7673158 100644
--- a/lightning/src/events/mod.rs
+++ b/lightning/src/events/mod.rs
@@ -1867,6 +1867,9 @@ impl MaybeReadable for Event {
(11, payment_context, option),
(13, payment_id, option),
});
+ // We can pass in `None` for the invoice request parameter here because if this is an
+ // offer payment we will only ever have written a `Bolt12OfferContext` (which already
+ // contains the invreq), never an `AsyncBolt12OfferContext`.
let purpose = match payment_secret {
Some(secret) => PaymentPurpose::from_parts(payment_preimage, secret, payment_context, None)
.map_err(|()| msgs::DecodeError::InvalidValue)?, |
assert_eq!(events_2.len(), 2); | ||
// If we successfully decode the HTLC onion but then fail later in | ||
// process_pending_htlc_forwards, then we'll queue the failure and generate a new | ||
// `ProcessPendingHTLCForwards` event. If we fail during the process of decoding the HTLC, | ||
// we'll fail it immediately with no intermediate forwarding event. | ||
assert!(events_2.len() == 1 || events_2.len() == 2); |
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.
Wouldn't this pass when two events is expected but a regression results in only one? Is there another way to assert that would avoid this? Maybe through what's in expected_failure
or in how it set?
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.
IIRC I couldn't find a way to tell with the current code. This would be fixed if #1302 (comment) (or possibly #3561) were addressed, though. There really shouldn't be 1 event ever except for the bug documented in the former link.
After a lot of setup in prior commits, here we finally finish support for receiving HTLCs paid to static BOLT 12 invoices. It amounts to verifying the invoice request contained within the onion and generating the right PaymentPurpose for the claimable event.
Previously, this test util did not account for config overrides supplied at node creation time. Uncovered because it caused test nodes to be unable to forward over private channels created with this util because that is not the default.
4b929c3
to
c5bd26f
Compare
Refactored diff --git a/lightning/src/events/mod.rs b/lightning/src/events/mod.rs
index 0b7673158..bbaaca4e8 100644
--- a/lightning/src/events/mod.rs
+++ b/lightning/src/events/mod.rs
@@ -28,7 +28,6 @@ use crate::ln::msgs;
use crate::ln::types::ChannelId;
use crate::types::payment::{PaymentPreimage, PaymentHash, PaymentSecret};
use crate::offers::invoice::Bolt12Invoice;
-use crate::offers::invoice_request::VerifiedInvoiceRequest;
use crate::onion_message::messenger::Responder;
use crate::routing::gossip::NetworkUpdate;
use crate::routing::router::{BlindedTail, Path, RouteHop, RouteParameters};
@@ -179,9 +178,10 @@ impl PaymentPurpose {
}
}
+ /// Errors when provided an `AsyncBolt12OfferContext`, see below.
pub(crate) fn from_parts(
payment_preimage: Option<PaymentPreimage>, payment_secret: PaymentSecret,
- payment_context: Option<PaymentContext>, invreq: Option<VerifiedInvoiceRequest>,
+ payment_context: Option<PaymentContext>
) -> Result<Self, ()> {
match payment_context {
None => {
@@ -205,16 +205,10 @@ impl PaymentPurpose {
})
},
Some(PaymentContext::AsyncBolt12Offer(_)) => {
- let invoice_request = invreq.ok_or(())?;
- if payment_preimage.is_none() { return Err(()) }
- Ok(PaymentPurpose::Bolt12OfferPayment {
- payment_preimage,
- payment_secret,
- payment_context: Bolt12OfferContext {
- offer_id: invoice_request.offer_id,
- invoice_request: invoice_request.fields(),
- },
- })
+ // Callers are expected to convert from `AsyncBolt12OfferContext` to `Bolt12OfferContext`
+ // using the invoice request provided in the payment onion prior to calling this method.
+ debug_assert!(false);
+ Err(())
}
}
}
@@ -1867,11 +1861,8 @@ impl MaybeReadable for Event {
(11, payment_context, option),
(13, payment_id, option),
});
- // We can pass in `None` for the invoice request parameter here because if this is an
- // offer payment we will only ever have written a `Bolt12OfferContext` (which already
- // contains the invreq), never an `AsyncBolt12OfferContext`.
let purpose = match payment_secret {
- Some(secret) => PaymentPurpose::from_parts(payment_preimage, secret, payment_context, None)
+ Some(secret) => PaymentPurpose::from_parts(payment_preimage, secret, payment_context)
.map_err(|()| msgs::DecodeError::InvalidValue)?,
None if payment_preimage.is_some() => PaymentPurpose::SpontaneousPayment(payment_preimage.unwrap()),
None => return Err(msgs::DecodeError::InvalidValue),
diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index cc7f4ad4f..b4fb5312f 100644
--- a/lightning/src/ln/channelmanager.rs
+++ b/lightning/src/ln/channelmanager.rs
@@ -6265,7 +6265,6 @@ where
payment_preimage,
payment_data.payment_secret,
payment_context,
- None,
) {
Ok(purpose) => purpose,
Err(()) => {
@@ -6303,9 +6302,13 @@ where
fail_htlc!(claimable_htlc, payment_hash);
}
};
+ let payment_purpose_context = PaymentContext::Bolt12Offer(Bolt12OfferContext {
+ offer_id: verified_invreq.offer_id,
+ invoice_request: verified_invreq.fields(),
+ });
match events::PaymentPurpose::from_parts(
- Some(keysend_preimage), payment_data.payment_secret, payment_context,
- Some(verified_invreq),
+ Some(keysend_preimage), payment_data.payment_secret,
+ Some(payment_purpose_context),
) {
Ok(purpose) => purpose,
Err(()) => { |
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
97a4337
to
5144036
Compare
Feel free to squash. |
5144036
to
95e453a
Compare
In the previous commit we completed support for async receive from an always-online sender to an often-offline receiver, minus support for acting as the async receiver's always-online channel counterparty.
95e453a
to
1f53a31
Compare
Squashed and had to push one more time to fix CI: diff --git a/lightning/src/ln/channelmanager.rs b/lightning/src/ln/channelmanager.rs
index 9c4367a5b..a9d934056 100644
--- a/lightning/src/ln/channelmanager.rs
+++ b/lightning/src/ln/channelmanager.rs
@@ -4693,7 +4693,7 @@ where
self.pending_outbound_payments.test_add_new_pending_payment(payment_hash, recipient_onion, payment_id, route, None, &self.entropy_source, best_block_height)
}
- #[cfg(test)]
+ #[cfg(all(test, async_payments))]
pub(crate) fn test_modify_pending_payment<Fn>(
&self, payment_id: &PaymentId, mut callback: Fn
) where Fn: FnMut(&mut PendingOutboundPayment) { |
let htlc_updates = get_htlc_update_msgs!(nodes[0], nodes[1].node.get_our_node_id()); | ||
assert_eq!(htlc_updates.update_add_htlcs.len(), 1); | ||
let mut events = nodes[0].node.get_and_clear_pending_msg_events(); | ||
assert_eq!(events.len(), 1); |
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.
For this kind of test it would be really nice to check that we didn't release the HTLC prior to handling the onion message above.
Finishes support for receiving async payments.
The last piece after this for the async-receive side is support for being the async receiver's LSP and serving invoices on their behalf.
Partially addresses #2298
Based on
#3408,#3517Also based on #2933, it enables the use of one of the test utils due to more uniform handling of HTLC receive failures.