Skip to content
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 a PaymentContext in PaymentPurpose #2970

Merged
merged 17 commits into from Apr 18, 2024

Conversation

jkczyz
Copy link
Contributor

@jkczyz jkczyz commented Mar 26, 2024

This is an alternative approach to #2929. Instead of adding an InvoiceGenerated event, expand PaymentPurpose with more variants that include additional context, which is exposed in PaymentClaimable and PaymentClaimed events. PaymentContext is included to payment::ReceiveTlvs such that the sender includes it in the onion when making a payment.

@jkczyz
Copy link
Contributor Author

jkczyz commented Mar 26, 2024

@TheBlueMatt @tnull This doesn't include adding the data the payment path yet. Just wanted to draft this to make sure the approach of piping the data from payment::ReceiveTlvs to Event is desirable. In particular, if including PaymentContext in PaymentPurpose is reasonable or if it should be a separate field.

@jkczyz
Copy link
Contributor Author

jkczyz commented Mar 26, 2024

FY, the top three commits are relevant.

@codecov-commenter
Copy link

codecov-commenter commented Mar 26, 2024

Codecov Report

Attention: Patch coverage is 78.96907% with 102 lines in your changes are missing coverage. Please review.

Project coverage is 89.26%. Comparing base (59778da) to head (c4524a0).
Report is 4 commits behind head on main.

❗ Current head c4524a0 differs from pull request most recent head 478911d. Consider uploading reports for the commit 478911d to get more accurate results

Files Patch % Lines
lightning/src/events/mod.rs 62.50% 24 Missing ⚠️
lightning/src/blinded_path/payment.rs 52.77% 17 Missing ⚠️
lightning/src/ln/channelmanager.rs 82.14% 15 Missing ⚠️
lightning/src/ln/functional_test_utils.rs 55.00% 9 Missing ⚠️
lightning/src/offers/invoice_error.rs 0.00% 9 Missing ⚠️
lightning/src/offers/invoice_request.rs 90.62% 8 Missing and 1 partial ⚠️
lightning/src/ln/chanmon_update_fail_tests.rs 50.00% 5 Missing ⚠️
lightning/src/ln/functional_tests.rs 50.00% 5 Missing ⚠️
lightning/src/ln/offers_tests.rs 95.52% 3 Missing ⚠️
lightning/src/offers/invoice.rs 76.92% 3 Missing ⚠️
... and 1 more

❗ Your organization needs to install the Codecov GitHub app to enable full functionality.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2970      +/-   ##
==========================================
- Coverage   89.29%   89.26%   -0.04%     
==========================================
  Files         117      117              
  Lines       96251    98421    +2170     
  Branches    96251    98421    +2170     
==========================================
+ Hits        85950    87858    +1908     
- Misses       8076     8339     +263     
+ Partials     2225     2224       -1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@@ -67,6 +68,8 @@ pub enum PaymentPurpose {
/// [`ChannelManager::create_inbound_payment`]: crate::ln::channelmanager::ChannelManager::create_inbound_payment
/// [`ChannelManager::create_inbound_payment_for_hash`]: crate::ln::channelmanager::ChannelManager::create_inbound_payment_for_hash
payment_secret: PaymentSecret,
///
payment_context: Option<PaymentContext>,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we have a new enum variant for BOLT12 that is distinct from InvoicePayment? If BOLT12 were the same flow I'd think not but now we have the ability to have an invoice paid more than once, which feels like it would be a separate flow on the event-consuming end?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initially, my intention was to have a top-level field in PaymentClaimable. But while piping that data through I noticed that the approach here seemed to make sense internally at least. We have ClaimingPayment and ClaimablePayment, both of which have a required PaymentPurpose field. So while I could make another variant to OnionPayload for PaymentContext, we'd still need a PaymentPurpose to create those structs (ClaimingPayment and ClaimablePayment). So this seemed like the least disruptive approach, but definitely looking for a Concept ACK on this or an alternative approach.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I didn't mean a different variant in PaymentContext for BOLT11, I meant a new variant in PaymentPurpose for BOLT12.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, let me give that a try.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, PaymentPurpose doesn't use impl_writeable_tlv_based_enum_upgradable. Would that be a problem?

Copy link
Contributor

@tnull tnull Mar 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That way we don't have two levels of payment types (i.e. PaymentPurpose and PaymentContext). And we don't break downgrades that way. Thoughts?

I'm probably waaay to optimistic, but having an entirely separate variant for BOLT12 might also allow us to drop the BOLT11 variant more easily some day?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can add a Bolt12InvoicePayment variant though it's gonna have the same fields as InvoicePayment plus an optional (still) PaymentContext because we want to support downgrades there.

Not sure I understand this, can't we have it be required and read an upgradable_required and then support downgrades by discarding the event?

I'm fine with that, but maybe it makes more sense to rename InvoicePayment to ExpectedPayment (opposite to the SpontaneousPayment variant) and add an Option as is currently done here?

Seems fine with me.

That way we don't have two levels of payment types (i.e. PaymentPurpose and PaymentContext). And we don't break downgrades that way. Thoughts?

I wonder if we shouldn't make PaymentContext non-Option and have an empty BOLT11 variant, then?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I understand this, can't we have it be required and read an upgradable_required and then support downgrades by discarding the event?

Yeah, I think you're right.

I'm fine with that, but maybe it makes more sense to rename InvoicePayment to ExpectedPayment (opposite to the SpontaneousPayment variant) and add an Option as is currently done here?

Seems fine with me.

Ok, I'll try this out.

I wonder if we shouldn't make PaymentContext non-Option and have an empty BOLT11 variant, then?

Same here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI, still need to finish this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The latest push makes PaymentContext non-Option in all structs, which means downgrades will fail for unknown contexts. It also uses a Bolt11Invoice context for any existing serializations without one. @TheBlueMatt let me know if this is what you had in mind.

lightning/src/blinded_path/payment.rs Outdated Show resolved Hide resolved
/// [`BlindedPath`]: crate::blinded_path::BlindedPath
/// [`Event::PaymentClaimable`]: crate::events::Event::PaymentClaimable
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum PaymentContext {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to be an enum? If its only going to be a BOLT12 thing it seems like overkill, though if we ever end up with a BOLT13 we'd need it? Or is it also intended to have a refund variant and that's why?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, possibly for refunds and future / alternative payment protocols / extensions. Hard to say for sure, but could need a Bolt12Subscription, for instance. Maybe static invoices (for offline receive) unless we are able to obtain a sender-included InvoiceRequest for use here.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hard to say for sure, but could need a Bolt12Subscription, for instance

Wouldn't that just be repeated payments for the same OfferId?

Maybe static invoices (for offline receive) unless we are able to obtain a sender-included InvoiceRequest for use here.

I believe the current thinking is the InvoiceRequest will be copied into the HTLC onion.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hard to say for sure, but could need a Bolt12Subscription, for instance

Wouldn't that just be repeated payments for the same OfferId?

It's certainly possible. But who knows what the spec will ultimately look like. In general, it seems better to allow expanding the number of variants than adding increasingly more Option fields for future uses. The latter may require the user to figure out how to interpret it.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, hard to balance future uses at the serialization layer vs complexifying the API today for it. I wonder if we can't make the API simpler (ie expose just a struct) but at the serialization layer do something smarter?

lightning/src/offers/offer.rs Outdated Show resolved Hide resolved
Copy link
Contributor Author

@jkczyz jkczyz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pushed a few commits that add the PaymentContext to the BlindedPaths.

Comment on lines 113 to 131
Bolt12Offer {
/// The identifier of the [`Offer`].
///
/// [`Offer`]: crate::offers::offer::Offer
offer_id: OfferId,
},
/// The payment was made for invoice sent for a BOLT 12 [`Refund`].
///
/// [`Refund`]: crate::offers::refund::Refund
Bolt12Refund {},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where does the offer id come from? Also is there no id for a refund?

Our main concern for Mutiny is we can't label the receives from bolt 12 if we can't tie it to our original bolt12

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At the moment, the OfferId is a nonce taken from the offer metadata. You can use OfferBuilder::offer_id to retrieve it. Maybe we could have a VerifiedOffer so you don't need to grab it from the builder before creating the offer.

Note that OfferId is the id for inbound payments. We're going to provide most if not all of the data from the InvoiceRequest in the PaymentContext::Bolt12Offer variant, too. And you'll have the PaymentHash from the Bolt12Invoice directly in Event::PaymentClaimable. So you should be able to use that to correlated all inbound payments to an offer.

For outbound payments, we already use the PaymentId passed to pay_for_offer which is given back in Event::PaymentSent.

For a Refund creator (i.e., an outbound payment), the id is the PaymentId passed to create_refund_builder, which is encrypted in the payer_metadata and also given back in Event::PaymentSent. For the inbound payment, the id is the PaymentHash in the Bolt12Invoice sent to the Refund creator. That invoice is now returned by request_refund_payment in this PR.

Copy link
Contributor

@tnull tnull Mar 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that OfferId is the id for inbound payments. We're going to provide most if not all of the data from the InvoiceRequest in the PaymentContext::Bolt12Offer variant, too. And you'll have the PaymentHash from the Bolt12Invoice directly in Event::PaymentClaimable. So you should be able to use that to correlated all inbound payments to an offer.

I'm still not entirely convinced just using payment_hash will be an adequate identifier for inbound payments going forward, given that we're about to potentially have (more) spurious payment events after #2957. It would be really nice if we could also expose PaymentId for inbound payments that could function as a "real" idempotency token with documented guarantees for the user, even if we'd derive it from the PaymentHash internally. This would make the API more uniform and probably less confusing for the user.

Also, with BOLT12 multiple invoices may be generated for each offer and each of these could potentially get paid multiple times. Even though the latter is a bug on the payer's end it would be nice if we could expose a way to discern duplicate payments from spurious/duplicate events.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm still not entirely convinced just using payment_hash will be an adequate identifier for inbound payments going forward, given that we're about to potentially have (more) spurious payment events after #2957. It would be really nice if we could also expose PaymentId for inbound payments that could function as a "real" idempotency token with documented guarantees for the user, even if we'd derive it from the PaymentHash internally. This would make the API more uniform and probably less confusing for the user.

Do you have a concrete proposal in mind?

Also, with BOLT12 multiple invoices may be generated for each offer and each of these could potentially get paid multiple times. Even though the latter is a bug on the payer's end it would be nice if we could expose a way to discern duplicate payments from spurious/duplicate events.

Just to be clear, don't you already need to handle duplicate payments for the same invoice in BOLT 11? And, at least according to our docs, you shouldn't claim duplicate payments: https://docs.rs/lightning/latest/lightning/events/enum.Event.html#note-1

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you have a concrete proposal in mind?

I think the ~only thing we can do is channel_id + htlc_id (picking one by some tiebreaker for MPP payments). We should probably do this, but its largely unrelated to this PR, I think.

@@ -236,10 +254,15 @@ macro_rules! offer_derived_metadata_builder_methods { ($secp_context: ty) => {
features: OfferFeatures::empty(), absolute_expiry: None, issuer: None, paths: None,
supported_quantity: Quantity::One, signing_pubkey: node_id,
},
metadata_strategy: core::marker::PhantomData,
metadata_strategy: DerivedMetadata(OfferId(nonce)),
Copy link
Contributor Author

@jkczyz jkczyz Mar 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a drawback to using the Nonce for the OfferId. For the bindings version of the builders, you'd be able to create two different offers but that have the same id. For instance, since build doesn't consume the builder, you could build an offer and reuse the builder to create a different offer. Invoice requests would still verify correctly, but the offers would have the same id because the nonce is generated at builder-construction time.

An alternative could be to use the tagged hash of the merkle root computed at build time for the OfferId. This would allow us to have an OfferId for any offer, not just the offers we create from ChannelManager.

We could do the same for Refund if having an id is desirable for inbound payments. Though, the PaymentHash should suffice.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An alternative could be to use the tagged hash of the merkle root computed at build time for the OfferId.

Does it have to be at build-time? Can't we just calculate a hash of all the fields in the offer when we see it again in the invoice_request? I would feel a lot happier doing it this way, I think.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, it wouldn't necessarily need to be done by OfferBuilder. But a user like LDK Node is probably going to store the Offer so would want to use the OfferId as the key after creating it. So it could also just be a method on Offer that computes it on the fly.

But, yes, we'd then re-create the OfferId when receiving the InvoiceRequest.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, re-calculating it every time the user calls id() is probably not ideal...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated OfferId to use the tagged hash of the merkle root.

@@ -67,6 +68,8 @@ pub enum PaymentPurpose {
/// [`ChannelManager::create_inbound_payment`]: crate::ln::channelmanager::ChannelManager::create_inbound_payment
/// [`ChannelManager::create_inbound_payment_for_hash`]: crate::ln::channelmanager::ChannelManager::create_inbound_payment_for_hash
payment_secret: PaymentSecret,
///
payment_context: Option<PaymentContext>,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I understand this, can't we have it be required and read an upgradable_required and then support downgrades by discarding the event?

Yeah, I think you're right.

I'm fine with that, but maybe it makes more sense to rename InvoicePayment to ExpectedPayment (opposite to the SpontaneousPayment variant) and add an Option as is currently done here?

Seems fine with me.

Ok, I'll try this out.

I wonder if we shouldn't make PaymentContext non-Option and have an empty BOLT11 variant, then?

Same here.

@TheBlueMatt
Copy link
Collaborator

Looks like this needs a small rebase.

@jkczyz
Copy link
Contributor Author

jkczyz commented Mar 29, 2024

Looks like this needs a small rebase.

Rebased

@jkczyz jkczyz marked this pull request as ready for review March 29, 2024 22:17
@jkczyz
Copy link
Contributor Author

jkczyz commented Mar 29, 2024

This is good for review. Need to add/update unit tests for InvoiceRequestFields construction, but shouldn't be much work.

@TheBlueMatt TheBlueMatt added this to the 0.0.123 milestone Apr 10, 2024
Copy link
Collaborator

@TheBlueMatt TheBlueMatt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly LGTM, mentioned offline but I think we need to move the context into the event because we may want a context in SpontaneousPayment events with async BOLT12 payments.

lightning/src/events/mod.rs Show resolved Hide resolved
lightning/src/ln/channelmanager.rs Show resolved Hide resolved
lightning/src/ln/channelmanager.rs Show resolved Hide resolved
lightning/src/offers/invoice_request.rs Outdated Show resolved Hide resolved
lightning/src/offers/merkle.rs Outdated Show resolved Hide resolved
lightning/src/offers/offer.rs Outdated Show resolved Hide resolved
Copy link
Contributor

@tnull tnull left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did a pass, looks good so far, only one minor question regarding logging.

While reviewing I noticed that we have several instances of SecretKey::from_slice(hmac.as_byte_array()).unwrap() in signer.rs. The libsecp256k1 docs say that there is a (although negligible) chance that this would panic. Seems in other places we make sure to handle the error. Do we also want to do this in these instances, or are we fine with taking the chances?

lightning/src/ln/channelmanager.rs Show resolved Hide resolved
@TheBlueMatt
Copy link
Collaborator

TheBlueMatt commented Apr 11, 2024

While reviewing I noticed that we have several instances of SecretKey::from_slice(hmac.as_byte_array()).unwrap() in signer.rs. The libsecp256k1 docs say that there is a (although negligible) chance that this would panic.

The probability of this is somewhere between 1/2^127 and 1/2^128. If we ever hit that its more likely someone found an issue in SHA-256 and is exploiting it, so panic is probably fine (but we should consider expect in general).

coderabbitai[bot]
coderabbitai bot previously approved these changes Apr 15, 2024
@jkczyz
Copy link
Contributor Author

jkczyz commented Apr 15, 2024

Mostly LGTM, mentioned offline but I think we need to move the context into the event because we may want a context in SpontaneousPayment events with async BOLT12 payments.

Discussed offline. Ended up expanding PaymentPurpose with new variants. PaymentContext is used when creating a BlindedPath, which is piped through ChannelManager until an event is created with a PaymentPurpose. The PaymentContext variants wrap a specific context which is re-used in the PaymentPurpose variants to avoid unnecessary duplication.

@jkczyz
Copy link
Contributor Author

jkczyz commented Apr 15, 2024

@coderabbitai coming in hot out of nowhere

Copy link

coderabbitai bot commented Apr 15, 2024

Tip

For best results, initiate chat on the files or code changes.

@jkczyz, thanks for the tag! If there's anything specific you need help with, such as reviewing code, generating unit tests, or analyzing coverage issues, please let me know how I can assist!

Copy link
Contributor

@tnull tnull left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really like that we have the relevant fields available in Payment{Claimable,Claimed} API now!

Btw, this seems to need a rebase.

lightning/src/blinded_path/payment.rs Outdated Show resolved Hide resolved
lightning/src/blinded_path/payment.rs Outdated Show resolved Hide resolved
lightning/src/blinded_path/payment.rs Show resolved Hide resolved
lightning/src/events/mod.rs Outdated Show resolved Hide resolved
///
/// [`Offer`]: crate::offers::offer::Offer
Bolt12OfferPayment {
/// The preimage to the payment hash. If provided, this can be handed directly to
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Just realized that generally the PaymentPurpose docs only really make sense in the context of PaymentClaimable, even though we now include it in PaymentClaimed for some time. E.g., handing the preimage directly to claim_funds (again) would lead to trying to double-claim in the context of PaymentClaimed.

This is really pre-existing so no need to fix it here, but eventually might be nice to have the docs make sense in all used contexts?

lightning/src/ln/channelmanager.rs Outdated Show resolved Hide resolved
lightning/src/blinded_path/payment.rs Show resolved Hide resolved
@TheBlueMatt
Copy link
Collaborator

Needs rebase as well, it seems.

@jkczyz
Copy link
Contributor Author

jkczyz commented Apr 15, 2024

Rebasing in the meanwhile.

@jkczyz
Copy link
Contributor Author

jkczyz commented Apr 15, 2024

Rebased

@jkczyz jkczyz force-pushed the 2024-03-offer-id branch 2 times, most recently from 2691b1d to dc9fa10 Compare April 16, 2024 00:31
Copy link
Collaborator

@TheBlueMatt TheBlueMatt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can probably drop the TODO from the "Define an OfferId when built with DerivedMetadata" commit message when you squash :)

@@ -137,7 +201,8 @@ impl Writeable for ReceiveTlvs {
fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
encode_tlv_stream!(w, {
(12, self.payment_constraints, required),
(65536, self.payment_secret, required)
(65536, self.payment_secret, required),
(65538, self.payment_context, required)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be an odd value so that users can get invoices issued by new LDK, downgrade, and still accept the payment? Not sure it matters much cause we have important offers bugfixes elsewhere but still.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure thing. Updated.

lightning/src/events/mod.rs Outdated Show resolved Hide resolved
lightning/src/ln/channelmanager.rs Outdated Show resolved Hide resolved
lightning/src/offers/offer.rs Outdated Show resolved Hide resolved
lightning/src/offers/offer.rs Outdated Show resolved Hide resolved
pub payer_id: PublicKey,

/// A chain from [`Offer::chains`] that the offer is valid for.
pub chain: Option<ChainHash>,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this? Every byte here is very expensive so IMO we should avoid if possible.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can probably drop it. In practice, it will be None since it may not be set when the offer is for mainnet, but we can't count on the counterparty to do so. And this should always be ChannelManager's chain, so should be ok to drop.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, right, forgot its usually None, but, yea, we already check it, no need to store it again.

lightning/src/offers/invoice_request.rs Outdated Show resolved Hide resolved
lightning/src/offers/invoice_request.rs Outdated Show resolved Hide resolved
lightning/src/offers/invoice_request.rs Outdated Show resolved Hide resolved
lightning/src/blinded_path/payment.rs Show resolved Hide resolved
@jkczyz jkczyz force-pushed the 2024-03-offer-id branch 2 times, most recently from 20a8a38 to c4524a0 Compare April 17, 2024 22:07
When sending an invoice for a refund, information from the invoice may
be useful for caller. For instance, the payment_hash can be used to
track whether the refund was paid. Return the invoice to facilitate this
use case.
Providing LDK-specific data in a BlindedPath allows for the data to be
received with the corresponding payment. This is useful as it can then
be surfaced in PaymentClaimable events where the user may correlated
with an Offer, for instance. Define a PaymentContext enum for including
various context (e.g., offer, refund, static invoice).
PendingInboundPayment::BlindedReceive includes a PaymentContext, which
the recipient includes in each blinded path. Included this when
processing HTLCs in ChannelManager, first into PendingHTLCRouting and
then to OnionPayload. Later, this will be included in the PaymentPurpose
when surfaced to user in PaymentClaimable and PaymentClaimed events.
Use a merkle root hash of the offer TLV records to define an offer id.
Will be included in a BOLT 12 invoice's blinded payment paths in order
for the recipient to identify which offer the payment is for.
Extract the OfferId from the offer metadata sent back in the
InvoiceRequest and include it in VerifiedInvoiceRequest. This can be
used to correspond the eventual payment for the invoice response by
including it in the invoice's blinded payment paths.
When constructing blinded payment paths for a Bolt12Invoice response,
include the OfferId so that the eventual payment can be correlated with
the Offer.
When requesting a payment for a refund, include a context in the
Bolt12Invoice's blinded payment paths indicated it is for such. When the
eventual payment is received, the user can use the payment hash to
correlate it with the corresponding Refund.
PaymentPurpose will be expanded to include BOLT 12 payment context.
Rename the InvoicePayment variant accordinly before new variants are
added.
In order to provide more context in PaymentClaimable and PaymentClaimed
events, introduce new variants of PaymentPurpose for use with BOLT 12
payments. Separate variants are used for offers and refunds. An unknown
variant is used for backwards compatibility and ease of testing, but is
otherwise not publicly constructable.
When constructing a PaymentPurpose in ChannelManager, use the
PaymentContext from OnionPayload to determine which variant to
construct, including those for BOLT 12 payments.
When receiving a payment, it's useful to know information about the
InvoiceRequest. Include this data in PaymentContext::Bolt12Offer so
users can display information about an inbound payment (e.g., the payer
note).
Clarify that payment_preimage should only be used to claim a payment
when handling Event::PaymentClaimable, not Event::PaymentClaimed.
let purpose = events::PaymentPurpose::from_parts(
payment_preimage.clone(),
payment_data.payment_secret,
payment_context.clone(),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to clean up all the clones around here in a followup. They used to be cheap but now they have some malloc cost.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm... I'm not sure if we can. Maybe we could replace OnionPayload in ClaimableHTLC with a PaymentPurpose?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean maybe we shouldn't have added payment_context to OnionPayload? It looks trivial to remove.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, so then we would check PaymentPurpose in PaymentClaimable against the one received in PendingHTLCRouting::Receive but not store it in each ClaimableHTLC (via OnionPayload). Yeah, that should work.

/// must be greater than or equal to [`Offer::amount`], converted if necessary.
///
/// [`chain`]: InvoiceRequest::chain
pub amount_msats: Option<u64>,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, what's the point of including this? Do users ever actually care about the amount set in the invoice_request by the time they've received the actual payment (and know exactly how much it was for)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably not? But I guess the sender could have overpaid, so it might be needed for accounting purposes.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But this doesn't fully capture the overpayment - PaymentClaimable::amount_msat exists for that purpose, and the sender could overpay in their invoice_request and then overpay more in the actual HTLCs, so it'd be wrong to look at this.

@TheBlueMatt
Copy link
Collaborator

Gonna go ahead and land this but I'd like to discuss my above two comments before we release.

@TheBlueMatt TheBlueMatt merged commit 56a87cc into lightningdevkit:main Apr 18, 2024
16 checks passed
TheBlueMatt added a commit that referenced this pull request Apr 29, 2024
TheBlueMatt added a commit to TheBlueMatt/rust-lightning that referenced this pull request May 6, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants