Skip to content

Introduce Event Model for Offers Flow #3833

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

Open
wants to merge 9 commits into
base: main
Choose a base branch
from

Conversation

shaavan
Copy link
Member

@shaavan shaavan commented Jun 7, 2025

This PR introduces the event-model for Flow, that allows a user to asynchronously examine, and handle and InvoiceRequest, or an Invoice, based on their selected FlowConfig.# Introduce Event Model for Offers Flow

This PR introduces an event-based model for OffersMessageFlow, enabling users to asynchronously examine and handle incoming InvoiceRequests or Bolt12Invoices based on their configured FlowConfigs.

By setting custom FlowConfigs, users can choose whether to:

  • Automatically respond to offers synchronously (as before), or
  • Receive structured OfferEvents for manual inspection and handling.

This makes offer handling more flexible, especially in use-cases like:

  • Dynamic conversion for currency-denominated offers
  • Selective acceptance of invoices based on custom user-based rules

The default configuration (NeverTrigger) maintains backward compatibility by preserving the existing synchronous behavior.

@ldk-reviews-bot
Copy link

ldk-reviews-bot commented Jun 7, 2025

I've assigned @joostjager as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@shaavan
Copy link
Member Author

shaavan commented Jun 7, 2025

cc @jkczyz

@ldk-reviews-bot
Copy link

🔔 1st Reminder

Hey @joostjager! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

@joostjager
Copy link
Contributor

Is this proposed change a response to a request from a specific user/users?

@shaavan
Copy link
Member Author

shaavan commented Jun 11, 2025

Hi @joostjager!

This PR is actually a continuation of the original thread that led to the OffersMessageFlow: link to thread.

The motivation behind it was to provide users with the ability to handle InvoiceRequests asynchronously—just like we already allow for Bolt12Invoices. However, adding more events into the middle of the ChannelManager flow felt suboptimal.

So, as a first step, we worked on refactoring most of the Offers-related code out of ChannelManager into the new OffersMessageFlow (#3639). Now that the refactor is complete, this PR picks up the original goal again: to let users asynchronously handle both InvoiceRequests and Invoices. This not only gives them more flexibility in analyzing these Offer messages, but also opens the door for creating custom interfaces—for example, to support Offers in different currency denominations.

Hope that gives a clear picture of the intent behind this! Let me know if you have any thoughts or suggestions—would love to hear them. Thanks a lot!

@jkczyz
Copy link
Contributor

jkczyz commented Jun 11, 2025

Another use case is Fedimint, where they'll want to include their own payment hash in the Bolt12Invoice.

@valentinewallace
Copy link
Contributor

Another use case is Fedimint, where they'll want to include their own payment hash in the Bolt12Invoice.

Does Fedimint plan to use the OffersMessageFlow without a ChannelManager?

Comment on lines +12686 to +12693
let invoice_request = match self.flow.determine_invoice_request_handling(invoice_request) {
Ok(Some(ir)) => ir,
Ok(None) => return None,
Err(_) => {
log_trace!(self.logger, "Failed to handle invoice request");
return None;
}
};
Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm... so the user may want to handle this asynchronously because they just need to verify that an amount (or supply that is) is sufficient for the offer's currency denomination. In that case, we should really have some function that continues the code below (i.e. creating a payment hash / secret, building the invoice, and enqueuing it to be sent).

Another reason is they want to supply their own payment hash. Similarly, they would need to build the invoice and enqueue it for sending. They may be even want to customize the invoice in some other ways using the builder.

I'm not quite sure how we want to do this. For the first case, it seems we should make it easy for them, which means they would need to call something on ChannelManager to continue the flow. Whereas, the second case is more about calling methods on OffersMessageFlow. But it would be weird for the former since the events' docs would need to reference ChannelManager.

Comment on lines +226 to +231
pub enum InvoiceBuilderVariant<'a> {
/// An [`InvoiceBuilder`] that uses a derived signing public key.
Derived(InvoiceBuilder<'a, DerivedSigningPubkey>),
/// An [`InvoiceBuilder`] that uses an explicitly set signing public key.
Explicit(InvoiceBuilder<'a, ExplicitSigningPubkey>),
}
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure yet, but we may instead want to try making different VerifiedInvoiceRequest types(w/ and w/o derived keys). Then we'd have have two versions of create_invoice_builder_from_invoice_request for each type.

@@ -588,6 +588,36 @@ pub struct InvoiceRequest {
signature: Signature,
}

impl InvoiceRequestContents {
pub(crate) fn amount_source(&self) -> Result<InvoiceRequestAmountSource, Bolt12SemanticError> {
Copy link
Contributor

Choose a reason for hiding this comment

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

We should look into using InvoiceRequestAmountSource as the internal representation in InvoiceRequestContents to avoid needing a Result. Maybe similar for Bolt12Invoice.

///
/// [`Amount::Currency`]: crate::offers::offer::Amount::Currency
/// [`Amount::Bitcoin`]: crate::offers::offer::Amount::Bitcoin
amount_source: InvoiceRequestAmountSource,
Copy link
Contributor

Choose a reason for hiding this comment

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

May not need this here if we can get it directly from VerifiedInvoiceRequest as per the other comment.

@jkczyz
Copy link
Contributor

jkczyz commented Jun 11, 2025

Does Fedimint plan to use the OffersMessageFlow without a ChannelManager?

I believe with one.

@ldk-reviews-bot
Copy link

🔔 2nd Reminder

Hey @joostjager! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

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.

5 participants