Skip to content

Conversation

@shaavan
Copy link
Owner

@shaavan shaavan commented Dec 25, 2025

Summary by CodeRabbit

Release Notes

  • New Features

    • Added currency conversion support throughout invoice and offer operations.
    • Introduced event-driven asynchronous processing for offer message flows, enabling deferred invoice request handling.
  • Bug Fixes

    • Enhanced payment hash and preimage validation to prevent mismatched or duplicate payment claims.
    • Improved amount validation when invoices or offers specify currency requirements.
  • Tests

    • Expanded test coverage for currency conversion handling and hash/preimage validation scenarios.

✏️ Tip: You can customize this high-level summary in your review settings.

Adds the `CurrencyConversion` trait to allow users to define custom
logic for converting fiat amounts into millisatoshis (msat).

This abstraction lays the groundwork for supporting Offers denominated
in fiat currencies, where conversion is inherently context-dependent.
This commit updates the Bolt12Invoice amount creation logic to utilize
the `CurrencyConversion` trait, enabling more flexible and customizable
handling of fiat-to-msat conversions.

Reasoning

The `CurrencyConversion` trait is passed upstream into the invoice's amount
creation flow, where it is used to interpret the Offer’s currency amount
(if present) into millisatoshis.

This change establishes a unified mechanism for amount handling—regardless
of whether the Offer’s amount is denominated in Bitcoin or fiat, or whether
the InvoiceRequest specifies an amount or not.
We introduce this check in pay_for_offer, to ensure that
if the offer amount is specified in currency, a corresponding amount
to be used in invoice request must be provided.

**Reasoning:** When responding to an offer with currency, we enforce
that the invoice request must always include an amount. This ensures we
never receive an invoice tied to a currency-denominated offer without
a corresponding request amount.

By moving currency conversion upfront into the invoice request creation
where the user can supply their own conversion logic — we avoid pushing
conversion concerns into invoice parsing. This significantly reduces
complexity during invoice verification.
Previously, the `enqueue_invoice` function in the `Flow` component
accepted a `Refund` as input and dispatched the invoice either directly
to a known `PublicKey` or via `BlindedMessagePath`s, depending on what
was available within the `Refund`.

While this worked for the refund-based flow, it tightly coupled invoice
dispatch logic to the `Refund` abstraction, limiting its general
usability outside of that context.

The upcoming commits will introduce support for constructing and
enqueuing invoices from manually handled `InvoiceRequest`s—decoupled
from the `Refund` flow. To enable this, we are preemptively introducing
more flexible, destination-specific variants of the enqueue function.

Specifically, the `Flow` now exposes two dedicated methods:

- `enqueue_invoice_using_node_id`: For sending an invoice directly to a
  known `PublicKey`.
- `enqueue_invoice_using_reply_paths`: For sending an invoice over a
  set of explicitly provided `BlindedMessagePath`s.

This separation improves clarity, enables reuse in broader contexts,
and lays the groundwork for more composable invoice handling across the
Offers/Refund flow.
Adds an API to send an `InvoiceError` to the counterparty via the flow.

This becomes useful with the introduction of Flow events in upcoming
commits, where the user can choose to either respond to Offers Messages
or return an `InvoiceError`.

Note:
Given the small scope of changes in this commit, we also take the
opportunity to perform minor documentation cleanups in `flow.rs`.
Until now, offers messages were processed internally without exposing
intermediate steps. This made it harder for callers to intercept or
analyse offer messages before deciding how to respond to them.

`FlowEvents` provide an optional mechanism to surface these events back
to the user. With events enabled, the caller can manually inspect an
incoming message, choose to construct and sign an invoice, or send back
an InvoiceError. This shifts control to the user where needed, while
keeping the default automatic flow unchanged.
@coderabbitai
Copy link

coderabbitai bot commented Dec 25, 2025

📝 Walkthrough

Walkthrough

These changes introduce a currency conversion abstraction and event-driven flow handling throughout the Lightning offers system. A new CurrencyConversion trait enables pluggable currency-to-msats conversion with a no-op default implementation. Public APIs now thread currency conversion through invoice building and payment operations. An event-driven OfferMessageFlowEvent mechanism enables asynchronous processing of invoice requests. Enhanced validation ensures payment hash-preimage consistency and currency-specific amount requirements across payment claiming and invoice verification.

Changes

Cohort / File(s) Summary
Core Currency Conversion Abstraction
lightning/src/offers/invoice_request.rs
Introduces public CurrencyConversion trait with fiat_to_msats method and DefaultCurrencyConversion no-op implementation. Updates respond_with and respond_with_no_std to accept generic CC: Deref parameter with CurrencyConversion bound. Adds min_invoice_request_amount utility method for computing minimum required amounts.
Amount Conversion
lightning/src/offers/offer.rs
Adds to_msats<CC: Deref> method to Amount type to convert fiat currencies using provided CurrencyConversion, handling overflow and unsupported currency errors. Imports Deref and CurrencyConversion.
Invoice Builder Updates
lightning/src/offers/invoice.rs
Updates for_offer and for_offer_using_keys builder methods and amount_msats macro-generated functions to accept generic CC: Deref with CurrencyConversion bound. Threads currency conversion through invoice construction and amount validation. Updates test scaffolding to pass DefaultCurrencyConversion.
Outbound Payment Flow
lightning/src/ln/outbound_payment.rs
Adds generic CC: Deref parameter to static_invoice_received function with CurrencyConversion bound. Updates amount calculation to use provided currency_conversion in InvoiceBuilder::amount_msats.
Offers Message Flow & Events
lightning/src/offers/flow.rs
Introduces OfferMessageFlowEvent enum with InvoiceRequestReceived variant for asynchronous event-driven processing. Adds enable_events flag and pending_flow_events queue to OffersMessageFlow. Updates verify_invoice_request to accept Responder and return AsynchronouslyHandleResponse when events enabled. Adds enqueue_flow_event, release_pending_flow_events, and new invoice enqueueing methods. Threads currency conversion through create_invoice_builder_from_invoice_request_* methods.
Channel Manager Payment Validation
lightning/src/ln/channelmanager.rs
Adds payment hash-preimage matching validation to claim_funds, claim_funds_with_known_custom_tlvs, and verify_bolt12_invoice. Enforces amount_msats requirement when offer specifies currency via Amount::Currency in create_inbound_payment, create_refund_builder, and payment creation methods. Passes DefaultCurrencyConversion to invoice request creation. Updates test coverage for duplicate payment rejection and hash mismatch failures.
Integration Tests & Flow Events
lightning/src/ln/offers_tests.rs
Introduces new integration tests for one-hop blinded path offer payments with manual invoice request response. Updates invoice request handling test to pass DefaultCurrencyConversion to builder methods. Adds import for OfferMessageFlowEvent and updates call sites to include currency conversion parameter.
Fuzzing Infrastructure
fuzz/src/invoice_request_deser.rs
Introduces FuzzCurrencyConversion struct implementing CurrencyConversion trait with panic-based fiat_to_msats. Updates build_response to supply CurrencyConversion instance to invoice_request.respond_with.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant OffersFlow as OffersMessageFlow
    participant EventQueue as Flow Events Queue
    participant Request as InvoiceRequest Handler
    participant Invoice as Invoice Builder

    Client->>OffersFlow: invoke with enable_events=true
    activate OffersFlow
    OffersFlow->>OffersFlow: receive_invoice_request()
    OffersFlow->>Request: verify_invoice_request(request, responder)
    activate Request
    alt enable_events is true
        Request->>EventQueue: push InvoiceRequestReceived event
        rect rgb(200, 220, 240)
            Note over Request,EventQueue: Async handling branch
        end
        Request-->>OffersFlow: AsynchronouslyHandleResponse
    else enable_events is false
        rect rgb(240, 240, 240)
            Note over Request,Invoice: Synchronous handling
        end
        Request->>Invoice: create_invoice_builder_from_invoice_request_with_keys<br/>(currency_conversion, ...)
        activate Invoice
        Invoice-->>Request: InvoiceBuilder
        deactivate Invoice
        Request-->>OffersFlow: SendInvoice(verified_request)
    end
    deactivate Request
    deactivate OffersFlow

    Client->>OffersFlow: release_pending_flow_events()
    OffersFlow-->>Client: [InvoiceRequestReceived{...}, ...]
    Client->>Invoice: create_invoice_builder_from_invoice_request(...)<br/>with currency_conversion
    activate Invoice
    Invoice->>Invoice: amount_msats(invoice_request, currency_conversion)
    Invoice-->>Client: InvoiceBuilder
    deactivate Invoice
    Client->>Invoice: build_and_sign()
    Client->>Client: send invoice via reply_path
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

The changes span 8 files with heterogeneous modifications: a new currency conversion abstraction that threads through multiple systems, event-driven flow control with async handling, enhanced payment validation logic across channel manager, and coordinated test updates. Understanding requires tracing currency conversion propagation through builder APIs and payment flows, plus validating the new event queueing and async response mechanisms.

Poem

🐰 A fuzzy friend hops through offers anew,
With currency converters and events that flow true,
Hash-preimage pairs dancing in sync,
Async invoices processed in a blink!
Bolt-by-bolt payments, more robust than before!

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the main changes: currency-based offers support and async invoice handling via FlowEvents, both of which are substantial features introduced across multiple files.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch currency-review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (5)
lightning/src/ln/offers_tests.rs (1)

870-872: Update test comment to clarify manual handling.

The test comment is identical to the previous test (creates_and_pays_for_offer_using_one_hop_blinded_path), which may cause confusion. Consider updating it to explicitly mention that this test exercises manual invoice response handling via flow events, which is the key difference from the previous test.

lightning/src/offers/invoice.rs (1)

245-266: Centralizing offer-based amount computation via amount_msats looks sound

Making for_offer / for_offer_using_keys generic over CC: Deref<Target = CurrencyConversion> and routing both through the shared

pub(crate) fn amount_msats<CC: Deref>(
    invoice_request: &InvoiceRequest,
    currency_conversion: CC,
) -> Result<u64, Bolt12SemanticError>

is a good consolidation.

The 4-way match over (invoice_request.amount_msats(), min_amount) behaves as expected:

  • (Some(ir), Some(min)) with ir >= min ⇒ use the explicit request amount.
  • (Some(_), Some(_)) with ir < minInsufficientAmount, correctly rejecting under-paying requests.
  • (Some(ir), None) ⇒ donation offers with a request-specified amount are accepted as-is.
  • (None, Some(min)) ⇒ fall back to the offer-implied min (including currency conversion + quantity).
  • (None, None)MissingAmount, preventing amount-less invoices.

Because min_invoice_request_amount already applies the provided currency_conversion (including overflow/validation), this keeps offer-vs-request semantics and currency handling in one place. You might consider adding a small, focused unit test (matrix over these four cases, including a currency-based offer) around InvoiceBuilder::amount_msats to protect this logic explicitly, but the structure looks correct.

Also applies to: 321-343, 405-438

lightning/src/offers/flow.rs (3)

1027-1044: Inconsistent type parameter usage for amount_msats call.

On lines 1040-1043 and 1106-1109, the code calls InvoiceBuilder::<DerivedSigningPubkey>::amount_msats() even in create_invoice_builder_from_invoice_request_without_keys which deals with ExplicitSigningPubkey. While this may work if amount_msats is a static/associated function that doesn't depend on the type parameter, it's potentially confusing. Consider using a type-agnostic path or documenting why DerivedSigningPubkey is used in both cases.

Also applies to: 1093-1109


1269-1287: Consider whether enqueue_invoice_error should return Result.

The enqueue_invoice_error method returns Result<(), Bolt12SemanticError> but the function body cannot fail - it always succeeds. Consider returning () instead for API clarity, or document under what conditions this could theoretically fail in the future.

🔎 Proposed simplification
-	pub fn enqueue_invoice_error(
-		&self, invoice_error: InvoiceError, path: BlindedMessagePath,
-	) -> Result<(), Bolt12SemanticError> {
+	pub fn enqueue_invoice_error(
+		&self, invoice_error: InvoiceError, path: BlindedMessagePath,
+	) {
 		let mut pending_offers_messages = self.pending_offers_messages.lock().unwrap();

 		let instructions = MessageSendInstructions::WithoutReplyPath {
 			destination: Destination::BlindedPath(path),
 		};

 		let message = OffersMessage::InvoiceError(invoice_error);
 		pending_offers_messages.push((message, instructions));
-
-		Ok(())
 	}

465-467: Add documentation for the new variant.

The AsynchronouslyHandleResponse variant lacks documentation, unlike the other variants in the enum. Consider adding a brief doc comment explaining when this variant is returned.

🔎 Proposed documentation
+	/// We are recipient of this payment, and should handle the response asynchronously.
+	///
+	/// This variant is returned when [`OffersMessageFlow::enable_events`] is set, allowing
+	/// the caller to process the invoice request via [`OfferMessageFlowEvent::InvoiceRequestReceived`].
 	AsynchronouslyHandleResponse,
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between de384ff and a4742bd.

📒 Files selected for processing (8)
  • fuzz/src/invoice_request_deser.rs
  • lightning/src/ln/channelmanager.rs
  • lightning/src/ln/offers_tests.rs
  • lightning/src/ln/outbound_payment.rs
  • lightning/src/offers/flow.rs
  • lightning/src/offers/invoice.rs
  • lightning/src/offers/invoice_request.rs
  • lightning/src/offers/offer.rs
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-11-28T12:47:06.857Z
Learnt from: shaavan
Repo: shaavan/rust-lightning PR: 8
File: lightning/src/ln/channelmanager.rs:15468-15486
Timestamp: 2025-11-28T12:47:06.857Z
Learning: In `lightning/src/ln/channelmanager.rs`, limit checks for invoice request recurrence fields are handled during the parsing phase, so they don't need to be performed again in the recurrence logic handling code.

Applied to files:

  • lightning/src/ln/outbound_payment.rs
  • lightning/src/ln/channelmanager.rs
  • lightning/src/offers/offer.rs
  • lightning/src/offers/invoice.rs
  • fuzz/src/invoice_request_deser.rs
  • lightning/src/offers/invoice_request.rs
📚 Learning: 2025-11-28T12:48:22.008Z
Learnt from: shaavan
Repo: shaavan/rust-lightning PR: 8
File: lightning/src/ln/channelmanager.rs:15488-15508
Timestamp: 2025-11-28T12:48:22.008Z
Learning: In `lightning/src/ln/channelmanager.rs`, limit checks for invoice request recurrence fields are already handled during the parsing phase, so they don't need to be re-validated in the recurrence handling code.

Applied to files:

  • lightning/src/ln/outbound_payment.rs
  • lightning/src/ln/channelmanager.rs
  • lightning/src/offers/offer.rs
  • lightning/src/offers/invoice.rs
  • fuzz/src/invoice_request_deser.rs
  • lightning/src/offers/invoice_request.rs
🧬 Code graph analysis (7)
lightning/src/ln/outbound_payment.rs (1)
lightning/src/offers/test_utils.rs (3)
  • payment_paths (75-110)
  • payment_hash (112-114)
  • now (116-120)
lightning/src/ln/channelmanager.rs (2)
lightning/src/offers/invoice.rs (1)
  • amount_msats (1284-1286)
lightning/src/offers/invoice_request.rs (2)
  • amount_msats (1229-1240)
  • amount_msats (1283-1285)
lightning/src/offers/offer.rs (1)
lightning/src/offers/invoice_request.rs (2)
  • amount_msats (1229-1240)
  • amount_msats (1283-1285)
lightning/src/offers/invoice.rs (1)
lightning/src/offers/invoice_request.rs (3)
  • amount_msats (1229-1240)
  • amount_msats (1283-1285)
  • min_invoice_request_amount (1049-1064)
lightning/src/offers/flow.rs (2)
lightning/src/blinded_path/message.rs (1)
  • new (68-85)
lightning/src/offers/offer.rs (1)
  • paths (931-933)
fuzz/src/invoice_request_deser.rs (1)
lightning/src/offers/invoice_request.rs (2)
  • fiat_to_msats (595-595)
  • fiat_to_msats (602-604)
lightning/src/offers/invoice_request.rs (1)
fuzz/src/invoice_request_deser.rs (1)
  • fiat_to_msats (86-88)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
  • GitHub Check: build (windows-latest, stable)
  • GitHub Check: build-tx-sync (ubuntu-latest, beta)
  • GitHub Check: build-tx-sync (ubuntu-latest, stable)
  • GitHub Check: build-tx-sync (ubuntu-latest, 1.75.0)
  • GitHub Check: benchmark
  • GitHub Check: check_release
  • GitHub Check: semver-checks
  • GitHub Check: build (windows-latest, stable)
  • GitHub Check: build-tx-sync (macos-latest, stable)
  • GitHub Check: build (macos-latest, 1.75.0)
  • GitHub Check: build (macos-latest, stable)
  • GitHub Check: build-tx-sync (ubuntu-latest, 1.75.0)
  • GitHub Check: build-tx-sync (macos-latest, beta)
  • GitHub Check: build-tx-sync (macos-latest, 1.75.0)
  • GitHub Check: build-tx-sync (ubuntu-latest, stable)
  • GitHub Check: build-tx-sync (ubuntu-latest, beta)
  • GitHub Check: benchmark
  • GitHub Check: check_release
  • GitHub Check: semver-checks
  • GitHub Check: check_docs
🔇 Additional comments (33)
fuzz/src/invoice_request_deser.rs (2)

20-23: LGTM! Fuzzing implementation is appropriate.

The FuzzCurrencyConversion struct correctly implements the CurrencyConversion trait for fuzzing purposes. The unreachable!() panic is intentional—it will surface any unexpected code paths during fuzzing that attempt currency conversion from deserialized invoice requests.

Also applies to: 83-89


157-157: LGTM! Currency conversion parameter correctly threaded through.

The call to respond_with now properly includes the FuzzCurrencyConversion parameter, aligning with the currency-conversion aware API introduced across the codebase.

lightning/src/offers/offer.rs (2)

85-85: LGTM! Required imports for currency conversion.

The imports are necessary to support the new to_msats method on the Amount enum.

Also applies to: 103-103


1130-1146: LGTM! Currency conversion logic is well-structured.

The to_msats method correctly handles both Bitcoin and fiat currency amounts:

  • Bitcoin amounts return the amount directly
  • Currency amounts convert via exchange rate with proper overflow checking
  • Error handling appropriately maps to UnsupportedCurrency and InvalidAmount
lightning/src/ln/offers_tests.rs (2)

54-54: LGTM! Required imports for flow events and currency conversion.

The imports support the new test that exercises manual invoice response handling via flow events and the currency-conversion aware API.

Also applies to: 61-61


2448-2448: LGTM! Currency conversion parameter correctly added.

The call to respond_using_derived_keys_no_std now properly includes &DefaultCurrencyConversion, aligning with the updated API signature across the codebase.

lightning/src/ln/outbound_payment.rs (1)

3186-3235: Tests updated to respond_with_no_conversion align with the new API

The BOLT‑12 invoice tests here now consistently use respond_with_no_conversion(...) when constructing invoices from offers, which matches the currency‑conversion refactor (the actual conversion still comes via DefaultCurrencyConversion inside invoice APIs). The behavioural expectations in the tests (expiry, route not found, duplicate/unexpected invoice cases) remain unchanged and still read correctly against the updated outbound payment flow.

Also applies to: 3241-3299, 3303-3397

lightning/src/offers/invoice.rs (2)

27-61: Docs and imports correctly reflect the new currency-conversion API

Importing DefaultCurrencyConversion and CurrencyConversion, plus core::ops::Deref, and updating the module‑level example to use:

  • .respond_with(&DefaultCurrencyConversion, ...) under feature = "std", and
  • .respond_with_no_std(&DefaultCurrencyConversion, ...) under not(feature = "std")

keeps the public docs aligned with the new conversion‑aware respond APIs. This also matches how tests exercise these paths, so the example should stay compilable and accurate across both std and no‑std builds assuming DefaultCurrencyConversion is available in both.

Also applies to: 128-132, 161-162


1849-1853: Test updates for DefaultCurrencyConversion / respond_with*_no_conversion are consistent

The tests now:

  • Import DefaultCurrencyConversion where needed.
  • Call .respond_with(&DefaultCurrencyConversion, ...) for std offer flows.
  • Use .respond_with_no_conversion(...) and .respond_with_no_std_using_signing_pubkey(&DefaultCurrencyConversion, ...) helpers in the various offer‑based invoice builder tests.
  • Keep refund-related tests on the original, conversion‑free respond APIs.

This cleanly exercises both conversion-aware and “no conversion” convenience paths without changing the underlying behavioural assertions (amounts, expiry, quantity scaling, signature verification). The changes look internally consistent with the new builder signatures.

Also applies to: 2181-2201, 2268-2283, 2926-2933, 3078-3116, 3406-3437

lightning/src/offers/invoice_request.rs (6)

579-605: Well-designed currency conversion abstraction.

The CurrencyConversion trait with its documentation about minor units (ISO-4217 exponent) is clear and helpful for implementers. The DefaultCurrencyConversion no-op implementation provides a sensible fallback for BTC-only use cases.


798-809: LGTM - Currency conversion properly threaded through response methods.

The respond_with and respond_with_no_std methods correctly accept a generic CC: Deref parameter with the CurrencyConversion trait bound, and pass it through to for_offer. The pattern is consistent with Rust's zero-cost abstraction idiom.

Also applies to: 836-853


857-861: Test helper correctly uses DefaultCurrencyConversion.

The respond_with_no_conversion test helper appropriately wraps respond_with_no_std with DefaultCurrencyConversion, providing a convenient API for tests that don't need currency conversion.


1078-1089: Derived keys response methods correctly updated.

Both respond_using_derived_keys and respond_using_derived_keys_no_std consistently thread the currency_conversion parameter through to the underlying builder methods.

Also applies to: 1098-1119


1815-1821: Test correctly updated to use new API.

The test properly uses respond_with_no_conversion instead of the previous method name.


2399-2403: Test correctly updated for new API.

Consistent use of respond_with_no_conversion in the test.

lightning/src/offers/flow.rs (8)

74-98: Well-documented event enum with clear usage guidance.

The OfferMessageFlowEvent enum with the InvoiceRequestReceived variant is well-documented with clear instructions on how to respond. The documentation correctly references the appropriate builder methods for each InvoiceRequestVerifiedFromOffer variant.


122-123: Consider visibility of enable_events field.

The enable_events field is marked pub(crate), which allows internal modification but the enable_events() method on line 205 sets it to true. However, the struct only has an immutable &self reference in enable_events() on line 205, but line 206 attempts to assign to self.enable_events. This should require &mut self.

Wait, looking again at line 205-206:

pub fn enable_events(&mut self) {
    self.enable_events = true;
}

This is correct - it does take &mut self. The field visibility as pub(crate) is fine for internal testing/debugging access.

Also applies to: 137-138


542-553: Event flow correctly handles async vs sync paths.

The conditional logic correctly pushes an InvoiceRequestReceived event when enable_events is true and returns AsynchronouslyHandleResponse, otherwise returns the verified invoice request directly via SendInvoice. This provides a clean separation between synchronous and asynchronous handling modes.


1204-1237: LGTM - Clear separation of enqueue methods by destination type.

The enqueue_invoice_using_node_id method correctly creates reply paths and sends the invoice to a direct PublicKey destination. The loop structure is appropriate for sending to multiple reply paths.


1239-1267: LGTM - Blinded path variant correctly uses helper function.

The enqueue_invoice_using_reply_paths correctly leverages enqueue_onion_message_with_reply_paths for sending to blinded paths, maintaining consistency with other similar operations in the codebase.


1438-1458: LGTM - Flow event methods follow established patterns.

The enqueue_flow_event and release_pending_flow_events methods correctly mirror the existing patterns used for pending_offers_messages and pending_async_payments_messages, using core::mem::take for efficient draining of the mutex-protected vector.


148-183: Constructor correctly initializes new fields.

The new constructor properly accepts enable_events as a parameter and initializes both enable_events and pending_flow_events fields. The default empty vector for pending events is appropriate.


506-554: The Responder::into_blinded_path() method is properly defined and accessible. It exists at lightning/src/onion_message/messenger.rs:439 with signature pub(crate) fn into_blinded_path(self) -> BlindedMessagePath. The Responder type is correctly imported in flow.rs at line 57. The code at line 546 is valid.

Likely an incorrect or invalid review comment.

lightning/src/ln/channelmanager.rs (10)

96-100: LGTM!

The import additions for DefaultCurrencyConversion and Amount are necessary to support the currency-based offer validation and conversion threading introduced in this PR.


2669-2672: LGTM!

Standard pattern for exposing router and node_signer fields to tests via conditional compilation.

Also applies to: 2898-2901


5675-5681: LGTM!

Threading DefaultCurrencyConversion through static_invoice_received is consistent with the currency conversion abstraction pattern used throughout the PR.


12951-12962: LGTM!

The documentation clearly explains the amount handling requirements, especially that callers must provide amount_msats for currency-denominated offers and that the recipient may reject insufficient amounts post-conversion.


13100-13106: LGTM!

The validation correctly enforces that amount_msats must be provided when the offer specifies a currency amount. This aligns with the documented contract and the fact that InvoiceRequest::amount_msats() returns None for currency-based offers (as seen in the relevant code snippet).


13189-13201: LGTM!

The conditional logic correctly handles invoice delivery based on whether the refund has reply paths available, falling back to node ID-based routing when paths are empty.


13425-13428: LGTM!

Visibility upgrade to pub(crate) is appropriate to allow other modules to obtain peer information for blinded path construction.


15338-15348: LGTM!

The new AsynchronouslyHandleResponse variant enables deferred invoice handling via the event-driven flow. Returning None is correct since the response will be generated asynchronously through the OfferMessageFlowEvent mechanism mentioned in the PR objectives.


15361-15364: LGTM!

DefaultCurrencyConversion is consistently threaded through both the derived-keys and explicit-keys invoice builder paths, maintaining uniformity in the currency conversion abstraction.

Also applies to: 15386-15389


3956-3957: > Likely an incorrect or invalid review comment.

Comment on lines +26 to 27
use crate::offers::invoice_request::CurrencyConversion;
use crate::offers::invoice_request::InvoiceRequest;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Currency-conversion aware static_invoice_received looks correct; minor note on error mapping

Threading currency_conversion: CC with CC: Deref<Target = CurrencyConversion> into static_invoice_received and delegating amount derivation to InvoiceBuilder::<DerivedSigningPubkey>::amount_msats(invreq, currency_conversion) centralizes the BOLT‑12 amount semantics and matches the new currency‑aware builder API.

The only nit is the Err(_) branch from amount_msats: it debug_assert!s, abandons with PaymentFailureReason::UnexpectedError, but surfaces Bolt12PaymentError::UnknownRequiredFeatures. If this branch can ever be hit in practice (e.g., a custom CurrencyConversion returning an error), you might want to align the outward error variant with the internal PaymentFailureReason (or explicitly mark the branch as unreachable) for clearer debugging and caller expectations. Functionally it’s safe as written.

Also applies to: 1119-1185

🤖 Prompt for AI Agents
In lightning/src/ln/outbound_payment.rs around lines 26-27 and also covering
1119-1185, the review notes that the Err(_) branch from
InvoiceBuilder::amount_msats maps internally to
PaymentFailureReason::UnexpectedError but returns
Bolt12PaymentError::UnknownRequiredFeatures outward; update the error mapping so
the outward Bolt12PaymentError aligns with the internal PaymentFailureReason
(or, if the branch is truly impossible, replace the branch with an explicit
unreachable!(...) / debug_assert!(false, ...) and document that assumption).
Concretely: locate the Err(_) arm(s) in static_invoice_received where
amount_msats is called and either map the error to a Bolt12PaymentError variant
that corresponds to UnexpectedError (preserving the original error info in the
log) or change the arm to an unreachable path with a clear debug assertion and a
matching return in release builds.

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.

2 participants