Skip to content

Conversation

@shaavan
Copy link
Owner

@shaavan shaavan commented Dec 5, 2025

Summary by CodeRabbit

  • New Features

    • Support for custom TLV fields in Bolt11/Bolt12 invoice and offer payment flows.
  • Improvements

    • Public APIs and payment state now accept and propagate custom TLVs across lifecycle, persistence, and error handling.
    • New explicit error path for invalid custom TLVs.
  • Tests

    • Numerous tests updated to pass TLV vectors; new async Bolt12 test added to validate custom TLV propagation.

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

Custom TLVs allow the payer, to add additional custom
data to the onion packet, for the payee, which can be used
from anywhere between description to custom authentication.

While the user had ability to set custom_tlvs using send_payment
API, it was not possible from the simpler pay_for_bolt11_invoice API.
This commit fixes that by introducing custom_tlvs in it.

The next commit will do the same for Bol12 payment flow too.
@coderabbitai
Copy link

coderabbitai bot commented Dec 5, 2025

Warning

Rate limit exceeded

@shaavan has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 13 minutes and 25 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

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

📒 Files selected for processing (9)
  • lightning-liquidity/tests/lsps2_integration_tests.rs (4 hunks)
  • lightning/src/ln/async_payments_tests.rs (17 hunks)
  • lightning/src/ln/bolt11_payment_tests.rs (4 hunks)
  • lightning/src/ln/channelmanager.rs (12 hunks)
  • lightning/src/ln/invoice_utils.rs (4 hunks)
  • lightning/src/ln/max_payment_path_len_tests.rs (1 hunks)
  • lightning/src/ln/offers_tests.rs (40 hunks)
  • lightning/src/ln/outbound_payment.rs (26 hunks)
  • lightning/src/ln/payment_tests.rs (1 hunks)

Walkthrough

Added a new custom_tlvs: Vec<(u64, Vec<u8>)> parameter across payment APIs, outbound payment state, TLV persistence, and test helpers, propagating custom TLVs through invoice/offer creation, recipient onion construction, pending payment state transitions, and updated tests to supply vec![] where unused.

Changes

Cohort / File(s) Summary
Channel manager & public payment builders
lightning/src/ln/channelmanager.rs
Inserted custom_tlvs: Vec<(u64, Vec<u8>)> into public APIs (e.g., pay_for_offer, create_offer_builder*, create_refund_builder*) and threaded the value into invoice/offer builder and payment initiation flows.
Outbound payment state & sending logic
lightning/src/ln/outbound_payment.rs
Added custom_tlvs field to PendingOutboundPayment::AwaitingInvoice and ::InvoiceReceived; extended signatures (pay_for_bolt11_invoice, received_offer, add_new_awaiting_invoice) to accept TLVs; added RetryableSendFailure::InvalidCustomTlvs; integrated with_custom_tlvs usage and TLV persistence entries for the new field.
Bolt11 payment flows & tests
lightning/src/ln/bolt11_payment_tests.rs, lightning/src/ln/payment_tests.rs, lightning-liquidity/tests/lsps2_integration_tests.rs
Updated call sites to pass an extra vec![] TLV argument to pay_for_bolt11_invoice and related helpers; tests adapted to new signature.
Offers, Bolt12 & async payment tests / helpers
lightning/src/ln/offers_tests.rs, lightning/src/ln/async_payments_tests.rs, lightning/src/ln/max_payment_path_len_tests.rs
Added/updated helpers and tests to accept and propagate custom_tlvs; introduced route_bolt12_payment_with_custom_tlvs and an async Bolt12 test variant exercising custom TLVs; many pay_for_offer/builder call sites now include vec![].
Invoice utilities & invoice flow tests
lightning/src/ln/invoice_utils.rs
Renamed test to reflect TLV support, added assertions verifying PaymentClaimable contains provided custom_tlvs, and invoked claim helper with TLVs.
Integration tests
lightning-liquidity/tests/lsps2_integration_tests.rs
Updated test call sites to include empty vec![] for new TLV parameter where applicable.
TLV persistence / encoding
lightning/src/ln/outbound_payment.rs (TLV upgrade maps)
Extended TLV write/read mappings to include custom_tlvs entries for AwaitingInvoice and InvoiceReceived upgrade paths.

Sequence Diagram

sequenceDiagram
    participant Caller as Application / Test
    participant CM as ChannelManager
    participant Store as PendingOutboundPaymentStore
    participant RON as RecipientOnionFields
    participant Sender as Payment Sender

    Caller->>CM: pay_for_bolt11_invoice(..., custom_tlvs)
    CM->>Store: add_new_awaiting_invoice(..., custom_tlvs)
    Store->>Store: persist AwaitingInvoice{..., custom_tlvs}

    Note right of Store: Invoice arrives
    Caller->>CM: mark_invoice_received_and_get_details(...)
    CM->>Store: transition AwaitingInvoice -> InvoiceReceived (carry custom_tlvs)
    Store->>RON: RecipientOnionFields::with_custom_tlvs(custom_tlvs)
    alt invalid TLVs
        RON-->>Store: Err(InvalidCustomTlvs)
        Store-->>CM: SendingFailed(InvalidCustomTlvs)
        CM-->>Caller: notify failure
    else valid TLVs
        RON->>Sender: include TLVs in onion packet
        Sender->>Sender: send payment
        Sender-->>Caller: payment success
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Review focus:
    • lightning/src/ln/outbound_payment.rs — state variant additions, TLV serialization/deserialization, upgrade map correctness, and state transition propagation.
    • lightning/src/ln/channelmanager.rs — updated public signatures and correct propagation into builders and downstream calls.
    • Error mapping for RecipientOnionFields::with_custom_tlvsInvalidCustomTlvs.
    • Consistency of updated test call sites and newly added test helpers (Bolt12 async/custom TLVs).

Poem

🐰 I tuck small TLVs beneath my fur so neat,
Hopping past invoices on nimble feet,
Tiny notes bundled in each onion fold,
Routes whisper secrets that were kept untold,
A rabbit cheers — new data hops bold 🥕

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 clearly and specifically describes the main change: introducing custom TLVs (Type-Length-Value extensions) for Bolt11 and Bolt12 payers, which aligns with the comprehensive modifications across multiple files adding custom_tlvs parameters and support.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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.

@shaavan
Copy link
Owner Author

shaavan commented Dec 6, 2025

@coderabbitai full review

@coderabbitai
Copy link

coderabbitai bot commented Dec 6, 2025

✅ Actions performed

Full review triggered.

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

♻️ Duplicate comments (3)
lightning/src/ln/channelmanager.rs (3)

2378-2380: Consistent TLV example across documentation.

The same TLV number concern applies here as noted in the previous comment on lines 2275-2277.

Also applies to: 2438-2440


12881-12900: Breaking API change is consistent.

The addition of custom_tlvs parameter maintains consistency with other payment API changes. The same TLV validation and call site concerns apply as noted in the previous comment on lines 5552-5566.


12923-12944: Consistent API extension.

This router-based variant maintains consistency with create_refund_builder. The same validation concerns apply.

🧹 Nitpick comments (3)
lightning/src/ln/outbound_payment.rs (1)

746-787: Custom TLV validation logic is solid; consider tightening docs/tests

RecipientOnionFields::with_custom_tlvs correctly enforces:

  • TLV types ≥ 2^16 (custom range),
  • exclusion of standardized experimental types (keysend 5482373484 and 77_777),
  • strict increasing order with sort_unstable_by_key plus a prev_type check, and
  • uniqueness (duplicates rejected via prev >= *typ).

The new unit test exercises invalid low-range types and keysend, and a valid increasing set, which covers the main paths.

Two minor polish suggestions:

  • Update the doc comment to also mention the 77_777 reservation, since it’s actively rejected.
  • Optionally add a test that verifies 77_777 is rejected, and one that confirms duplicates or unsorted input are rejected, to lock in the invariants.

Also applies to: 2858-2878

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

550-553: LGTM! API signature updates are correct.

All test call sites have been properly updated to accommodate the new custom_tlvs parameter in pay_for_offer. The empty vectors passed here maintain existing test behavior while allowing the API to accept custom TLVs when needed.

As a future enhancement, consider adding dedicated test cases that exercise the custom TLVs functionality by passing non-empty vectors to verify end-to-end propagation of custom TLV data through the payment flow.

Also applies to: 711-714, 786-789, 918-921, 1019-1022, 1128-1131, 1191-1194, 1277-1280, 1374-1377, 1475-1478, 1721-1724, 1818-1821, 1936-1936, 2380-2383, 2715-2718, 2780-2783, 2884-2887

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

58-58: LGTM! API signature updates are correct.

All test call sites have been properly updated to accommodate the new custom_tlvs parameter in pay_for_bolt11_invoice. The empty vectors maintain existing test behavior.

Consider adding test coverage for custom TLVs in BOLT11 payments to complement the existing payment metadata tests, ensuring the feature works end-to-end for both invoice types.

Also applies to: 72-72, 128-128, 142-142

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

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

📒 Files selected for processing (9)
  • lightning-liquidity/tests/lsps2_integration_tests.rs (4 hunks)
  • lightning/src/ln/async_payments_tests.rs (17 hunks)
  • lightning/src/ln/bolt11_payment_tests.rs (4 hunks)
  • lightning/src/ln/channelmanager.rs (12 hunks)
  • lightning/src/ln/invoice_utils.rs (1 hunks)
  • lightning/src/ln/max_payment_path_len_tests.rs (1 hunks)
  • lightning/src/ln/offers_tests.rs (38 hunks)
  • lightning/src/ln/outbound_payment.rs (21 hunks)
  • lightning/src/ln/payment_tests.rs (1 hunks)
🧰 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/channelmanager.rs
  • lightning/src/ln/invoice_utils.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/channelmanager.rs
  • lightning/src/ln/invoice_utils.rs
  • lightning/src/ln/outbound_payment.rs
🧬 Code graph analysis (3)
lightning/src/ln/channelmanager.rs (1)
lightning/src/ln/outbound_payment.rs (2)
  • custom_tlvs (784-786)
  • custom_tlvs (799-801)
lightning/src/ln/async_payments_tests.rs (1)
lightning/src/offers/signer.rs (1)
  • default (197-199)
lightning/src/ln/offers_tests.rs (1)
lightning/src/ln/channelmanager.rs (3)
  • default (704-713)
  • default (12771-12779)
  • pay_for_offer (13056-13082)
🪛 GitHub Actions: SemVer checks
lightning/src/ln/outbound_payment.rs

[error] 2059-2059: E0433: failed to resolve: use of unresolved module or unlinked crate std while compiling outbound_payment.rs. The code uses std::mem::take (or std::mem), but std is not resolved in this context.

🔇 Additional comments (16)
lightning-liquidity/tests/lsps2_integration_tests.rs (1)

1218-1227: Bolt11 payer callsites correctly updated for custom TLVs parameter

The added vec![] argument matches the new pay_for_bolt11_invoice signature and keeps these LSPS2 integration tests behaviorally identical while wiring through the new custom-TLVs parameter. No issues seen.

Also applies to: 1692-1701, 1884-1893, 2222-2231

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

710-721: Test call updated correctly for new pay_for_bolt11_invoice arity

The added vec![] argument aligns this test with the new pay_for_bolt11_invoice signature without changing its behavior. Looks good.

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

81-99: Custom TLVs added to payment lifecycle and TLV schema look consistent

Adding custom_tlvs: Vec<(u64, Vec<u8>)> to PendingOutboundPayment::AwaitingInvoice and ::InvoiceReceived and wiring them into the TLV schema ((9, custom_tlvs, optional_vec) / (7, custom_tlvs, optional_vec)) matches the existing encoding style and keeps upgrade semantics intact. This lets HRN/BOLT12 flows preserve user-specified TLVs across state transitions and persistence. No functional issues spotted here.

Also applies to: 2768-2786


520-550: New RetryableSendFailure::InvalidCustomTlvs variant is well-scoped

Introducing RetryableSendFailure::InvalidCustomTlvs to specifically represent invalid RecipientOnionFields::custom_tlvs is a clean separation from OnionPacketSizeExceeded. It correctly documents that the error is about invalid ranges/values (as opposed to size) and is only used on early validation paths, so it remains cheap to handle and reason about.


923-969: Bolt11 payer plumbing for custom TLVs is correctly integrated

The updated pay_for_bolt11_invoice:

  • Extends the API with custom_tlvs: Vec<(u64, Vec<u8>)> at a sensible position.
  • Builds RecipientOnionFields::secret_only(*invoice.payment_secret()) and applies .with_custom_tlvs(custom_tlvs), enforcing type range and ordering before any routing work.
  • Maps validation failure to Bolt11PaymentError::SendingFailed(RetryableSendFailure::InvalidCustomTlvs).
  • Continues to populate payment_metadata from the invoice and reuses the existing routing and retry logic unchanged.

This is a clean, early-fail integration point for user-specified TLVs and keeps retry semantics intact.


1067-1075: Mapping InvalidCustomTlvs to PaymentFailureReason::UnexpectedError is acceptable

In the BOLT12 path, adding RetryableSendFailure::InvalidCustomTlvs to the error-to-PaymentFailureReason mapping (as UnexpectedError) keeps the match exhaustive. Given that BOLT12 currently constructs RecipientOnionFields with an empty custom_tlvs set in this module, this case shouldn’t be hit here, but having a conservative fallback reason is fine.

If other call sites start feeding custom TLVs into BOLT12 routes, double-check that this mapping (UnexpectedError vs. a more specific reason) matches your desired UX.


1984-2033: Custom TLVs correctly propagated into AwaitingInvoice from offers

The changes to:

  • received_offer(&self, payment_id, custom_tlvs, retryable_invoice_request) and
  • add_new_awaiting_invoice(&self, payment_id, custom_tlvs, ...)

ensure that user-specified custom_tlvs are stored in PendingOutboundPayment::AwaitingInvoice at both the initial creation and the AwaitingOffer→AwaitingInvoice transition. This keeps state consistent for later invoice handling and matches the new enum fields and TLV schema.


2858-2878: RecipientOnionFields custom TLV test covers main invalid/valid paths

test_recipient_onion_fields_with_custom_tlvs:

  • Confirms types below 2^16 are rejected.
  • Confirms the reserved keysend type is rejected.
  • Confirms a couple of valid, in-range, strictly increasing types are accepted.

This is a useful sanity check for with_custom_tlvs and should prevent regressions in the validation logic.


3055-3160: add_new_awaiting_invoice callsites updated correctly

All test helpers that create AwaitingInvoice payments now pass an explicit vec![] for custom_tlvs, which matches the new add_new_awaiting_invoice signature. Behavior remains unchanged for these tests, since they don’t exercise custom TLVs.

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

5403-5406: Custom TLVs parameter correctly threaded with empty vector

Adapting pay_for_bolt11_invoice to the new custom_tlvs parameter via vec![] preserves the existing test semantics (no custom TLVs) and matches the expected argument order and types.

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

522-528: pay_for_offer updated to new custom_tlvs API with no behavior change

The extra vec![] argument aligns this call with the updated pay_for_offer signature, explicitly specifying “no custom TLVs” while leaving the test’s behavior unchanged.

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

309-309: LGTM! API updates correctly propagated through tests.

All test calls to create_refund_builder, create_refund_builder_using_router, and pay_for_offer have been consistently updated with the new custom_tlvs parameter. Passing empty vectors (vec![]) is appropriate for tests that don't specifically exercise custom TLV functionality.

Also applies to: 476-476, 498-498, 574-574, 605-605, 665-665, 761-761, 830-830, 889-889, 951-951, 998-998, 1078-1078, 1152-1152, 1211-1211, 1283-1283, 1380-1380, 1438-1438, 1521-1521, 1546-1546, 1622-1622, 1638-1638, 1717-1717, 1751-1751, 1822-1822, 1833-1833, 1874-1874, 1885-1885, 1920-1920, 1942-1942, 1977-1977, 2010-2010, 2013-2013, 2034-2034, 2040-2040, 2092-2092, 2159-2159, 2208-2208, 2300-2300, 2384-2384, 2469-2469

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

13056-13141: Consistent API extensions across offer payment functions.

All three offer payment functions (pay_for_offer, pay_for_offer_from_hrn, pay_for_offer_with_quantity) have been updated consistently with the new custom_tlvs parameter. The implementation correctly passes the parameter through the closure to add_new_awaiting_invoice.

However, the same TLV validation and call site verification concerns noted in earlier comments apply to these functions as well.


15789-15794: Appropriate use of empty custom TLVs for internal flow.

The empty vec![] passed to received_offer is correct for this internal automated payment flow where no custom TLVs are needed.


5552-5566: All TLV validation concerns have been addressed.

The custom_tlvs parameter is properly validated via RecipientOnionFields::with_custom_tlvs(), which enforces: TLV type range (>= 65536), rejection of reserved TLVs, sorting by type number, and uniqueness. Size limits are enforced by the 1300-byte onion packet constraint with comprehensive test coverage in max_payment_path_len_tests.rs. All existing call sites across the codebase have been updated to provide the parameter.


2275-2277: The documentation example uses an invalid TLV type number that will fail validation.

The example uses 343493u64 for the TLV type, but the validation in RecipientOnionFields::with_custom_tlvs() requires all custom TLV type numbers to be >= 65536 (enforced by the check if *typ < 1 << 16). Since 343493 < 65536, this will cause validation to fail when invoked.

Replace the TLV type with a valid number >= 65536, such as 65537u64:

/// # let custom_tlvs = vec![
/// #     (65537u64, b"hello".to_vec()),
/// # ];
⛔ Skipped due to learnings
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.
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.

@shaavan shaavan force-pushed the payment-custom-review branch from ad73e34 to bc0fd53 Compare December 6, 2025 12:25
@shaavan
Copy link
Owner Author

shaavan commented Dec 6, 2025

@coderabbitai full review

@coderabbitai
Copy link

coderabbitai bot commented Dec 6, 2025

✅ Actions performed

Full review triggered.

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: 0

🧹 Nitpick comments (6)
lightning/src/ln/outbound_payment.rs (4)

520-550: RetryableSendFailure::InvalidCustomTlvs semantics are reasonable but docs could be clearer

The new variant cleanly separates “invalid TLV values/ranges” from “too large onion” (OnionPacketSizeExceeded), which is useful. The doc string “are of invalid range” is a bit vague; consider clarifying that it covers TLV type-number validity and reserved-type usage (e.g., keysend, async-invoice types), not size.


1150-1212: Static-invoice path uses empty RecipientOnionFields; no custom TLVs yet

For static-invoice async payments, static_invoice_received still calls onion_utils::set_max_path_length with RecipientOnionFields::spontaneous_empty() and later send_payment_for_static_invoice also uses RecipientOnionFields::spontaneous_empty().

If the intent of this PR is “custom TLVs for BOLT11 and BOLT12 payers” in general, you might eventually want to thread custom_tlvs into the StaticInvoiceReceived flow as well (including its state variant and serialization), so async/static BOLT12 payments can carry the same TLVs.

If static invoices are intentionally excluded from custom TLVs for now, this is fine as-is, but it may be worth documenting that distinction.


2052-2085: mark_invoice_received_and_get_details is correct but does an extra clone

Good points:

  • It moves custom_tlvs out of AwaitingInvoice with core::mem::take(custom_tlvs) (no-std friendly).
  • It constructs an InvoiceReceived state with the same custom_tlvs.
  • It returns (payment_hash, retry, config, custom_tlvs_vec, is_newly_marked) for callers that need to build onion fields.

Minor nit: you currently take into custom, then custom.clone() into the InvoiceReceived variant and return custom—this doubles the allocation. Given TLV lists are usually tiny, this is low impact, but you could:

  • Move custom into the new state, and
  • Clone only for the returned value (or vice versa),

to avoid one copy.

Not urgent, just a potential micro-optimization.


2861-2882: Custom TLV unit test covers key validation cases

test_recipient_onion_fields_with_custom_tlvs:

  • Verifies types < 2^16 are rejected.
  • Verifies keysend’s experimental type and your reserved async-invoice type are rejected.
  • Verifies a minimal valid set (≥2^16, strictly increasing) succeeds.

That gives good coverage of the most important invariants. If you want, you could add a small case for duplicate or unsorted types being rejected, but not strictly necessary.

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

173-183: Offer/refund path tests still only exercise empty custom_tlvs; consider adding a non-empty case

All updated tests call the high-level APIs (pay_for_offer, create_refund_builder{_using_router}) with vec![] as custom_tlvs. That’s fine for existing behavior, but it means we don’t yet have an end-to-end test that:

  • Calls pay_for_offer or the refund builders with non-empty custom TLVs, and
  • Asserts that those TLVs make it into the onion / recipient context as expected.

Given this PR’s goal, you may eventually want a dedicated Offers test that round-trips a small custom TLV set through the full BOLT12 offer/refund flow.

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

2275-2277: Good example, but consider documenting TLV type selection guidelines.

The documentation example correctly uses an odd TLV type (343493), which allows nodes to safely ignore it if not understood. However, consider adding a comment or documentation explaining:

  • Use odd TLV types (e.g., 343493) for custom/experimental TLVs that can be ignored
  • Avoid even TLV types unless all nodes must understand them
  • Check BOLT specifications for reserved TLV ranges

Example improvement:

 # let custom_tlvs = vec![
-#     (343493u64, b"hello".to_vec()),
+#     (343493u64, b"hello".to_vec()),  // Use odd TLV types for optional custom data
 # ];
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ad73e34 and bc0fd53.

📒 Files selected for processing (5)
  • lightning/src/ln/async_payments_tests.rs (17 hunks)
  • lightning/src/ln/channelmanager.rs (12 hunks)
  • lightning/src/ln/max_payment_path_len_tests.rs (1 hunks)
  • lightning/src/ln/offers_tests.rs (38 hunks)
  • lightning/src/ln/outbound_payment.rs (26 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • lightning/src/ln/async_payments_tests.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/channelmanager.rs
  • lightning/src/ln/outbound_payment.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/channelmanager.rs
  • lightning/src/ln/outbound_payment.rs
🧬 Code graph analysis (1)
lightning/src/ln/offers_tests.rs (1)
lightning/src/ln/channelmanager.rs (1)
  • pay_for_offer (13056-13082)
⏰ 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-tx-sync (ubuntu-latest, stable)
  • GitHub Check: linting
  • GitHub Check: build-tx-sync (ubuntu-latest, beta)
  • GitHub Check: check_release
  • GitHub Check: benchmark
  • GitHub Check: build (windows-latest, stable)
  • GitHub Check: semver-checks
  • GitHub Check: check_release
  • GitHub Check: build-tx-sync (macos-latest, stable)
  • GitHub Check: linting
  • GitHub Check: build-tx-sync (macos-latest, 1.75.0)
  • GitHub Check: build-tx-sync (macos-latest, beta)
  • GitHub Check: build-tx-sync (ubuntu-latest, 1.75.0)
  • GitHub Check: build-tx-sync (ubuntu-latest, beta)
  • GitHub Check: build-tx-sync (ubuntu-latest, stable)
  • GitHub Check: build (macos-latest, 1.75.0)
  • GitHub Check: build (macos-latest, stable)
  • GitHub Check: build (windows-latest, stable)
  • GitHub Check: benchmark
  • GitHub Check: semver-checks
🔇 Additional comments (31)
lightning/src/ln/outbound_payment.rs (13)

80-98: Threading custom_tlvs through AwaitingInvoice/InvoiceReceived looks consistent

Adding custom_tlvs: Vec<(u64, Vec<u8>)> to PendingOutboundPayment::AwaitingInvoice and ::InvoiceReceived plus wiring it into create_pending_payment and retry logic correctly preserves payer-provided TLVs across the BOLT12 async invoice lifecycle. I don’t see missing state transitions or obvious ownership bugs here.


685-801: RecipientOnionFields::with_custom_tlvs validation and invariants look solid

The function:

  • Enforces custom TLV type numbers ≥ 2^16 and rejects reserved types (keysend and async-invoice request).
  • Sorts by type and then enforces strict monotonic increase, preventing duplicates or unsorted inputs.
  • Ensures custom_tlvs stored in RecipientOnionFields always satisfy the “strictly increasing types” invariant expected by comments and check_merge.

This matches the documented behavior and should integrate cleanly with onion serialization and MPP merging.


811-826: MPP merge behavior for custom TLVs is consistent with spec expectations

check_merge enforces equality of payment_secret and payment_metadata and then:

  • Requires all even-typed custom TLVs to be identical across parts.
  • Intersects the full custom TLV sets, so odd-typed TLVs present in only some parts are dropped.

Given with_custom_tlvs guarantees sorted, unique TLVs, this logic is deterministic and spec-compliant. No changes needed here.


923-969: BOLT11 pay_for_bolt11_invoice custom TLVs path is correct and fail-fast

The new custom_tlvs: Vec<(u64, Vec<u8>)> parameter is:

  • Passed into RecipientOnionFields::secret_only(*invoice.payment_secret()).
  • Validated via .with_custom_tlvs, mapping any error to RetryableSendFailure::InvalidCustomTlvs wrapped in Bolt11PaymentError::SendingFailed.

Because this occurs before adding a pending outbound payment, invalid TLVs never enter state, and callers can fix parameters and retry. This is a good separation between parameter validation and routing.


1023-1076: Handling InvalidCustomTlvs in BOLT12 internal route-finding is future-proof

The added arm:

RetryableSendFailure::InvalidCustomTlvs => PaymentFailureReason::UnexpectedError,

ensures any future InvalidCustomTlvs errors bubbling up from route construction will translate into a concrete PaymentFailureReason and cause abandon_payment. Today find_initial_route doesn’t emit this variant, but having the match arm is harmless and keeps the code exhaustive.


1890-1950: Propagating custom TLVs into Retryable state is correctly wired

add_new_pending_payment and create_pending_payment now:

  • Take recipient_onion: RecipientOnionFields by value.
  • Copy recipient_onion.custom_tlvs into the PendingOutboundPayment::Retryable { custom_tlvs, .. } field.
  • Use this stored custom_tlvs later when reconstructing RecipientOnionFields for retries.

This ensures retries and startup recovery preserve the original custom TLVs, which is exactly what we want.


1952-2004: AwaitingOffer/Received-offer path now threads custom_tlvs into AwaitingInvoice

The received_offer method’s new custom_tlvs argument is inserted into the AwaitingInvoice state, and add_new_awaiting_invoice likewise now stores custom_tlvs. This is consistent with how pay_for_offer in channelmanager.rs passes TLVs into add_new_awaiting_invoice, and it makes the offer → invoice flow carry TLVs end-to-end.


2013-2035: add_new_awaiting_invoice signature change is consistent across call sites

The new parameter order:

add_new_awaiting_invoice(
    payment_id,
    custom_tlvs,
    expiration,
    retry_strategy,
    route_params_config,
    retryable_invoice_request,
)

matches how pay_for_offer constructs it (payment_id, custom_tlvs, expiry, retry, config, invreq). The internal state setup plus awaiting_invoice flag logic are unchanged except for storing custom_tlvs, which is correct.


2040-2049: mark_invoice_received wrapper preserves DuplicateInvoice semantics

Refactoring mark_invoice_received to call mark_invoice_received_and_get_details and interpret the final bool as “was this the first time we marked it?” keeps previous behavior:

  • First invoice → Ok(()).
  • Duplicate invoice for same payment → Bolt12PaymentError::DuplicateInvoice.

This also ensures the new custom-TLV plumbing doesn’t leak into the public helper.


1593-1637: Retry path reconstructs onion fields with cloned custom TLVs as intended

In find_route_and_send_payment, for PendingOutboundPayment::Retryable { .. } you:

  • Rebuild RecipientOnionFields from stored payment_secret, payment_metadata, and custom_tlvs.clone().
  • Generate new per-path session privs and insert them into the payment.

This ensures retries use exactly the same per-recipient onion payload (including custom TLVs) while only changing the routing. Looks correct and consistent with the initial send.


2708-2790: Serialization changes for AwaitingInvoice/InvoiceReceived are compatible

Adding:

  • (9, custom_tlvs, optional_vec) to the AwaitingInvoice TLV layout, and
  • (7, custom_tlvs, optional_vec) to the InvoiceReceived layout,

as optional_vec fields means:

  • Older serialized data (without those TLVs) will deserialize with an empty vec.
  • Newer nodes write the field without breaking older ones that ignore unknown TLVs within these variants.

Together with the existing Retryable.custom_tlvs TLV, this gives a coherent on-disk representation of custom TLVs throughout the payment lifecycle.


3067-3163: Test updates for add_new_awaiting_invoice’s new parameter are correct

The various tests (removes_stale_awaiting_invoice_*, removes_abandoned_awaiting_invoice, BOLT12 invoice expiry/route failure tests) now call:

add_new_awaiting_invoice(
    payment_id,
    vec![],
    expiration,
    Retry::Attempts(0),
    RouteParametersConfig::default(),
    None,
)

This keeps behavior identical while satisfying the new signature. No issues here.


991-1019: Potentially stuck BOLT12 payment when custom TLVs are invalid

In send_payment_for_bolt12_invoice:

  • You transition the payment from AwaitingInvoice to InvoiceReceived via mark_invoice_received_and_get_details.
  • Then you build a RecipientOnionFields::spontaneous_empty().with_custom_tlvs(custom_tlvs).
  • If with_custom_tlvs fails, you return Bolt12PaymentError::SendingFailed(RetryableSendFailure::InvalidCustomTlvs) without calling abandon_payment.

Given:

  • InvoiceReceived is treated as "pre-HTLC-lock-in" (is_pre_htlc_lock_in returns true),
  • remove_stale_payments never prunes InvoiceReceived,
  • and there is no immediate PaymentFailed event here,

you can end up with a permanent InvoiceReceived entry for payment_id if the user supplies invalid custom TLVs. Future attempts with the same PaymentId may behave oddly or be blocked.

Consider treating this like other immediate-send failures (e.g., expired or route-not-found) by:

  • Calling self.abandon_payment(payment_id, PaymentFailureReason::UnexpectedError, pending_events) before returning, and
  • Ensuring a corresponding Event::PaymentFailed is emitted.

This keeps behavior consistent with other SendingFailed cases and prevents lingering state.

lightning/src/ln/max_payment_path_len_tests.rs (3)

193-327: Custom-TLV size tests for one-hop blinded path are well constructed

The one_hop_blinded_path_with_custom_tlv test:

  • Computes max_custom_tlv_len by subtracting serialized payload length (without custom TLV), TLV type/length overhead, payload varint overhead, and HMAC length from 1300 bytes.
  • Builds RecipientOnionFields::spontaneous_empty().with_custom_tlvs(...) with that length and verifies:
    • Payment from the introduction node succeeds.
    • Adding one byte triggers OnionPacketSizeExceeded both at onion construction and via send_payment.
    • Shrinking the TLV allows a two-hop route.

This is a strong integration test for how custom TLVs interact with blinded paths and the onion size limit.


329-481: Multi-hop blinded path test correctly validates custom TLV impact on max path length

The blinded_path_with_custom_tlv test similarly:

  • Uses onion_utils::build_onion_payloads to get the total reserved bytes without custom TLVs.
  • Derives max_custom_tlv_len for a chosen CUSTOM_TLV_TYPE.
  • Asserts:
    • Max-sized TLV works from the last unblinded hop.
    • +1 byte causes OnionPacketSizeExceeded and direct onion construction failure.
    • Reducing TLV length by INTERMED_PAYLOAD_LEN_ESTIMATE enables an extra unblinded hop.

This nicely exercises both the sending API and the low-level onion builder for blinded routes with custom TLVs.


518-529: Updated pay_for_offer call matches new signature

The bolt12_invoice_too_large_blinded_paths test now calls:

bob.node.pay_for_offer(
    &offer,
    Some(5000),
    payment_id,
    vec![],
    OptionalOfferPaymentParams::default(),
)

which aligns with the new pay_for_offer(&Offer, Option<u64>, PaymentId, Vec<(u64, Vec<u8>)>, OptionalOfferPaymentParams) signature. Semantics of the test are unchanged (no custom TLVs in this scenario).

lightning/src/ln/offers_tests.rs (12)

304-316: create_refund_with_no_blinded_path updated builder call is consistent

The test now calls:

create_refund_builder_using_router(
    &router,
    10_000_000,
    absolute_expiry,
    payment_id,
    vec![],
    Retry::Attempts(0),
    RouteParametersConfig::default(),
)

which matches the new (amount, expiry, payment_id, custom_tlvs, retry, config) ordering and keeps behavior identical (empty TLVs).


571-576: Short-lived refund builder now passes empty custom_tlvs explicitly

create_refund_builder(…, payment_id, vec![], Retry::Attempts(0), RouteParametersConfig::default()) is consistent with the updated signature and preserves the original test intent.


603-607: Router-based refund builder update is correctly parameterized

The long-lived refund test’s call to create_refund_builder_using_router now supplies vec![] for custom TLVs, before Retry and RouteParametersConfig, which matches the new function signature.


760-772: Two-hop refund builder in blinded-path test aligns with new API

The two-hop blinded refund test updates create_refund_builder with vec![] for custom_tlvs. This is mechanically correct and doesn’t alter the functional assertions in the test.


888-900: One-hop refund builder test correctly inserts custom_tlvs parameter

Same pattern here: create_refund_builder(..., payment_id, vec![], Retry::Attempts(0), RouteParametersConfig::default()) matches the new signature and preserves semantics.


995-1003: Refund without blinded paths: builder call updated correctly

The “pays_for_refund_without_blinded_paths” test’s create_refund_builder invocation now passes a vec![] custom_tlvs argument, in the expected position. The rest of the test logic remains unchanged.


1150-1155: Refund builder in multi-intro-node test matches new signature

The refund in send_invoice_for_refund_with_distinct_reply_path is also updated to add vec![] for custom_tlvs. This keeps the duplicate-introduction-node behavior test intact.


1716-1720: Refund builder in “fails_authentication_when_handling_invoice_for_refund” is consistent

Again, the extra vec![] correctly occupies the custom_tlvs slot; no change to the authentication failure semantics.


1940-1945: Unsupported-chain refund test correctly updated for new builder signature

The refund created with .chain(Network::Signet) now originates from a builder call that includes vec![] for custom TLVs; the UnsupportedChain assertion remains valid.


1873-1887: Refund builder in “fails_creating_refund_or_sending_invoice_without_connected_peers” is updated consistently

Both the failing create_refund_builder and the succeeding one (after reconnection) include vec![] for custom_tlvs, in the correct argument order. Behavior is unchanged.


2032-2041: Duplicate-payment-id refund test wired to new builder signature

The duplicate-ID tests now call create_refund_builder(..., payment_id, vec![], Retry::Attempts(0), RouteParametersConfig::default()) in both the initial success and the expected-failure case. Signature usage is correct.


475-499: pay_for_offer test calls correctly adapted to new custom_tlvs parameter

All occurrences like:

bob.node.pay_for_offer(&offer, None, payment_id, vec![], Default::default())
david.node.pay_for_offer(&offer, None, payment_id, vec![], Default::default())
nodes[0].node.pay_for_offer(&offer, Some(amt_msat), payment_id, vec![], Default::default())

match the new API in channelmanager.rs:

pay_for_offer(&Offer, Option<u64>, PaymentId, Vec<(u64, Vec<u8>)>, OptionalOfferPaymentParams)

and maintain the tests’ focus on path and authentication behavior rather than TLVs.

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

13055-13067: LGTM - Consistent API extension across offer payment methods.

The custom TLVs parameter is consistently integrated across all three offer payment methods:

  • pay_for_offer
  • pay_for_offer_from_hrn
  • pay_for_offer_with_quantity

All methods follow the same pattern: accept custom_tlvs parameter and pass it through to add_new_awaiting_invoice(). This consistency is good for API usability.

Also applies to: 13085-13097, 13128-13141


15789-15794: Good backward compatibility handling.

Passing an empty vec![] for custom_tlvs in internal code paths that don't yet utilize custom TLVs is appropriate. This maintains functionality while allowing the API to be extended consistently.


5552-5566: TLV validation is properly implemented in the payment flow.

The custom_tlvs parameter is validated in RecipientOnionFields::with_custom_tlvs() before being used in pay_for_bolt11_invoice(). The validation includes:

  1. Type range enforcement: rejects types < 2^16 to prevent reserved BOLT ranges
  2. Duplicate prevention: enforces strictly increasing type numbers
  3. Keysend exclusion: explicitly rejects the standardized keysend type (5482373484)
  4. Even TLV consistency: enforced at the HTLC consistency layer for multi-part payments

The implementation returns InvalidCustomTlvs error on validation failure, and overall onion packet size is limited to 1300 bytes. No changes needed.

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: 0

🧹 Nitpick comments (3)
lightning/src/ln/max_payment_path_len_tests.rs (1)

520-528: Custom-TLV parameter wiring into pay_for_offer looks correct

Passing vec![] as the new custom_tlvs argument matches the updated API and keeps this test focused purely on oversized blinded paths, without altering behavior. The change is safe and semantically appropriate here.

If you find yourself writing vec![] at many call sites, consider a small helper (e.g., NoCustomTlvs) or an overload that defaults this parameter to improve readability.

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

5403-5406: Adaptation to pay_for_bolt11_invoice’s custom-TLV parameter is correct

The added vec![] cleanly satisfies the new custom_tlvs parameter without changing the semantics of max_out_mpp_path, which continues to test MPP path utilization rather than TLV handling.

As with other call sites, you might later introduce a small helper or defaulting API to avoid repeating vec![] everywhere when no custom TLVs are needed.

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

309-309: LGTM! Mechanical updates to test call sites.

All test call sites correctly updated to include the new custom_tlvs parameter (passed as vec![]). The empty vectors preserve existing test behavior while maintaining API compatibility with the new signature.

Consider adding dedicated tests to verify custom TLV propagation through the Bolt12 payment flow—e.g., passing non-empty TLV data and asserting it appears in the expected invoice/payment structures.

Also applies to: 476-476, 498-498, 574-574, 605-605, 665-665, 761-761, 830-830, 889-889, 951-951, 998-998, 1078-1078, 1152-1152, 1211-1211, 1283-1283, 1380-1380, 1438-1438, 1521-1521, 1546-1546, 1622-1622, 1638-1638, 1717-1717, 1751-1751, 1822-1822, 1833-1833, 1874-1874, 1885-1885, 1920-1920, 1942-1942, 1977-1977, 2010-2010, 2013-2013, 2034-2034, 2040-2040, 2092-2092, 2159-2159, 2208-2208, 2300-2300, 2384-2384, 2469-2469

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

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

📒 Files selected for processing (9)
  • lightning-liquidity/tests/lsps2_integration_tests.rs (4 hunks)
  • lightning/src/ln/async_payments_tests.rs (17 hunks)
  • lightning/src/ln/bolt11_payment_tests.rs (4 hunks)
  • lightning/src/ln/channelmanager.rs (12 hunks)
  • lightning/src/ln/invoice_utils.rs (1 hunks)
  • lightning/src/ln/max_payment_path_len_tests.rs (1 hunks)
  • lightning/src/ln/offers_tests.rs (38 hunks)
  • lightning/src/ln/outbound_payment.rs (26 hunks)
  • lightning/src/ln/payment_tests.rs (1 hunks)
🧰 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/channelmanager.rs
  • lightning/src/ln/payment_tests.rs
  • lightning/src/ln/invoice_utils.rs
  • lightning/src/ln/outbound_payment.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/channelmanager.rs
  • lightning/src/ln/invoice_utils.rs
  • lightning/src/ln/outbound_payment.rs
🧬 Code graph analysis (3)
lightning/src/ln/channelmanager.rs (1)
lightning/src/ln/outbound_payment.rs (2)
  • custom_tlvs (784-786)
  • custom_tlvs (799-801)
lightning/src/ln/invoice_utils.rs (2)
lightning/src/ln/channel.rs (2)
  • None (11586-11586)
  • None (14731-14731)
lightning/src/ln/channelmanager.rs (2)
  • default (704-713)
  • default (12771-12779)
lightning/src/ln/offers_tests.rs (1)
lightning/src/ln/channelmanager.rs (1)
  • pay_for_offer (13056-13082)
⏰ 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). (11)
  • GitHub Check: build (windows-latest, stable)
  • GitHub Check: benchmark
  • GitHub Check: build-tx-sync (macos-latest, 1.75.0)
  • GitHub Check: build (macos-latest, 1.75.0)
  • GitHub Check: benchmark
  • GitHub Check: build (windows-latest, stable)
  • GitHub Check: build (macos-latest, stable)
  • GitHub Check: build (macos-latest, 1.75.0)
  • GitHub Check: build (macos-latest, stable)
  • GitHub Check: build (windows-latest, stable)
  • GitHub Check: benchmark
🔇 Additional comments (24)
lightning/src/ln/async_payments_tests.rs (1)

550-553: Updated pay_for_offer call sites correctly pass empty custom TLVs

All updated pay_for_offer invocations now supply vec![] in the new custom_tlvs parameter position while leaving payment IDs, amounts, and params/retry handling unchanged. This keeps existing test behavior (no custom TLVs) and matches the expanded API surface.

Also applies to: 711-714, 786-789, 918-921, 1019-1022, 1128-1131, 1191-1194, 1277-1280, 1374-1377, 1475-1478, 1721-1724, 1818-1821, 1936-1936, 2380-2383, 2715-2718, 2884-2887

lightning-liquidity/tests/lsps2_integration_tests.rs (1)

1220-1227: pay_for_bolt11_invoice test calls aligned with new custom_tlvs parameter

Each pay_for_bolt11_invoice use now includes an explicit vec![] for the new custom_tlvs argument, preserving prior behavior and keeping the rest of the parameters (IDs, options, retry) intact.

Also applies to: 1694-1701, 1886-1893, 2224-2231

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

713-720: create_and_pay_for_bolt11_invoice test updated for custom_tlvs without behavior change

The test’s pay_for_bolt11_invoice call now passes vec![] into the new custom_tlvs slot while retaining the existing retry and default params, so it continues to verify the same flow with no custom TLVs.

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

58-58: LGTM! Bolt11 test call sites correctly updated.

All pay_for_bolt11_invoice calls properly updated with the new custom_tlvs parameter (passed as vec![]). The changes maintain existing test coverage for payment metadata propagation.

Also applies to: 72-72, 128-128, 142-142

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

2275-2279: LGTM!

The documentation example is clear and uses a valid odd TLV type number (343493), which correctly follows the BOLT specification for custom/optional TLVs.


2377-2382: LGTM!

Documentation example is consistent with the pay_for_bolt11_invoice example pattern.


2437-2443: LGTM!

Documentation example is consistent with other API examples.


5551-5566: LGTM!

The custom_tlvs parameter is properly added to the function signature and propagated to the underlying pending_outbound_payments.pay_for_bolt11_invoice call.


12880-12900: LGTM!

The create_refund_builder function properly accepts and propagates the custom_tlvs parameter to add_new_awaiting_invoice.


12922-12944: LGTM!

The create_refund_builder_using_router function mirrors the create_refund_builder implementation for custom_tlvs handling.


13055-13067: LGTM!

The custom_tlvs parameter is properly captured by the closure and moved into add_new_awaiting_invoice. The ownership semantics are correct.


13085-13097: LGTM!

Consistent implementation with pay_for_offer.


13128-13141: LGTM!

Consistent implementation with other pay_for_offer variants.


15789-15794: No custom_tlvs propagation needed for HRN-resolved offer payments.

The hardcoded vec![] is correct and intentional. HRN-based offer payments created through DNS resolution don't support custom TLVs at initiation time—they only store amount and payer note. Custom TLVs are only supported for direct offer payments via pay_for_offer, pay_for_offer_from_hrn, and pay_for_offer_with_quantity, which have them as explicit parameters separate from OptionalOfferPaymentParams.

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

80-98: LGTM! Custom TLVs field additions are well-integrated.

The custom_tlvs field has been correctly added to both AwaitingInvoice and InvoiceReceived variants, allowing custom TLVs to be tracked through the payment state machine from invoice request to invoice receipt and payment initiation.


548-549: LGTM! New error variant appropriately handles custom TLV validation failures.

The InvalidCustomTlvs variant correctly extends RetryableSendFailure to surface validation errors from RecipientOnionFields::with_custom_tlvs().


923-969: LGTM! Bolt11 payment correctly integrates custom TLVs with proper validation.

The pay_for_bolt11_invoice function now accepts custom_tlvs and correctly:

  1. Validates them via RecipientOnionFields::with_custom_tlvs()
  2. Maps validation failures to Bolt11PaymentError::SendingFailed(RetryableSendFailure::InvalidCustomTlvs)
  3. Incorporates them into the payment flow

972-1020: LGTM! Bolt12 invoice payment correctly propagates custom TLVs from state.

The function correctly:

  1. Retrieves custom_tlvs from mark_invoice_received_and_get_details
  2. Validates and builds RecipientOnionFields with error mapping
  3. Passes the recipient_onion with custom TLVs to the internal payment function

1067-1076: LGTM! Error mapping for custom TLV validation is consistent.

The InvalidCustomTlvs error is correctly mapped to PaymentFailureReason::UnexpectedError, consistent with other validation failures like OnionPacketSizeExceeded.


1236-1319: Static invoices intentionally omit custom TLV support.

The send_payment_for_static_invoice function uses RecipientOnionFields::spontaneous_empty() without custom TLVs. This appears intentional as the StaticInvoiceReceived variant (lines 103-115) does not have a custom_tlvs field, unlike AwaitingInvoice and InvoiceReceived which do support them.


1988-2037: LGTM! State transition functions correctly handle custom TLVs.

Both received_offer and add_new_awaiting_invoice properly accept and store custom_tlvs in the AwaitingInvoice state, enabling custom TLVs to be tracked from the initial payment request through invoice receipt.


2051-2086: LGTM! State transition correctly uses core::mem::take and handles custom TLVs.

The implementation correctly:

  1. Uses core::mem::take(custom_tlvs) to move TLVs from AwaitingInvoice (line 2063)
  2. Clones them for both storage in InvoiceReceived and the return value (lines 2068, 2071)
  3. Returns custom TLVs from both newly-marked and already-marked invoice states

Note: The previous review issue regarding std::mem::take vs core::mem::take has been resolved correctly.


2720-2824: LGTM! TLV serialization correctly handles custom_tlvs with backward compatibility.

The custom_tlvs field is serialized using optional_vec in both:

  • AwaitingInvoice (TLV type 9, line 2772)
  • InvoiceReceived (TLV type 7, line 2789)

Using optional_vec ensures backward compatibility with versions that don't have this field, and forward compatibility when reading older serialized data.


3058-3387: LGTM! Test updates appropriately pass empty custom TLVs vectors.

All test calls to add_new_awaiting_invoice correctly pass vec![] for custom_tlvs, which is appropriate for tests not specifically focused on custom TLV functionality. This maintains test focus while adapting to the new signature.

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: 0

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

185-203: Consider refactoring to reduce code duplication.

This helper is nearly identical to route_bolt12_payment (lines 166-183), differing only in the custom TLVs parameter. Consider refactoring by:

  • Adding an optional custom_tlvs parameter to the existing route_bolt12_payment helper, or
  • Creating a shared internal implementation that both functions call

This would reduce maintenance burden and prevent the two implementations from diverging.

</invoke_comment_end>


1371-1494: Consider verifying custom TLVs are actually received by the recipient.

While this test validates that custom TLVs don't break the payment flow, it doesn't verify that the recipient actually receives the custom TLVs in the payment. Consider adding an assertion that checks the custom TLVs are present in the PaymentClaimable event or RecipientOnionFields.

Additionally, this test duplicates significant code from pays_bolt12_invoice_asynchronously (lines 1279-1369). Consider extracting a shared test helper that accepts an optional custom_tlvs parameter to reduce duplication.

</invoke_comment_end>

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bc0fd53 and b0143d7.

📒 Files selected for processing (1)
  • lightning/src/ln/offers_tests.rs (40 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
lightning/src/ln/offers_tests.rs (4)
lightning/src/ln/outbound_payment.rs (4)
  • custom_tlvs (784-786)
  • custom_tlvs (799-801)
  • new (473-479)
  • new (858-876)
lightning/src/offers/invoice_request.rs (2)
  • amount_msats (1145-1156)
  • amount_msats (1199-1201)
lightning/src/offers/flow.rs (1)
  • new (116-147)
lightning/src/offers/offer.rs (12)
  • offer (1892-1892)
  • offer (1903-1903)
  • offer (1982-1982)
  • offer (1991-1991)
  • offer (2033-2033)
  • offer (2049-2049)
  • offer (2058-2058)
  • offer (2073-2073)
  • offer (2079-2079)
  • offer (2087-2087)
  • offer (2095-2095)
  • offer (2103-2103)
⏰ 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). (17)
  • GitHub Check: build-tx-sync (ubuntu-latest, stable)
  • GitHub Check: build-tx-sync (ubuntu-latest, beta)
  • GitHub Check: build-tx-sync (ubuntu-latest, 1.75.0)
  • GitHub Check: build (windows-latest, stable)
  • GitHub Check: benchmark
  • GitHub Check: benchmark
  • GitHub Check: linting
  • GitHub Check: build-tx-sync (macos-latest, beta)
  • 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: build (windows-latest, stable)
  • GitHub Check: check_release
  • GitHub Check: build (macos-latest, 1.75.0)
  • GitHub Check: build (macos-latest, stable)
  • GitHub Check: semver-checks
  • GitHub Check: semver-checks
🔇 Additional comments (1)
lightning/src/ln/offers_tests.rs (1)

329-329: LGTM: Test updates correctly migrate to new API signature.

All test call sites have been properly updated to pass vec![] for the new custom_tlvs parameter, maintaining existing behavior. The migration is consistent and correct across all affected functions (pay_for_offer, create_refund_builder, create_refund_builder_using_router).

</invoke_comment_end>

Also applies to: 496-496, 518-518, 594-594, 625-625, 685-685, 781-781, 850-850, 909-909, 971-971, 1018-1018, 1098-1098, 1172-1172, 1231-1231, 1303-1303, 1525-1525, 1583-1583, 1666-1666, 1691-1691, 1767-1767, 1783-1783, 1862-1862, 1896-1896, 1967-1967, 1978-1978, 2019-2019, 2030-2030, 2065-2065, 2087-2087, 2122-2122, 2155-2155, 2158-2158, 2179-2179, 2185-2185, 2237-2237, 2304-2304, 2353-2353, 2445-2445, 2529-2529, 2614-2614

Bolt11 payments let the payer attach arbitrary custom TLVs in the
onion packet, allowing metadata ranging from descriptions to custom
authentication. Bolt12 currently lacks this capability.

This commit introduces a `custom_tlvs` field in the Bolt12-related
PendingOutboundPayment variants, enabling the same extensibility
with minimal changes.

A follow-up commit will add the user-facing API to set these TLVs.
@shaavan shaavan force-pushed the payment-custom-review branch from b0143d7 to a527304 Compare December 6, 2025 13:11
@shaavan
Copy link
Owner Author

shaavan commented Dec 6, 2025

@coderabbitai full review

@coderabbitai
Copy link

coderabbitai bot commented Dec 6, 2025

✅ Actions performed

Full review triggered.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
lightning/src/ln/channelmanager.rs (1)

12880-12945: Update offer builder functions to match refund builder API.

The create_offer_builder and create_offer_builder_using_router functions (lines 12806 and 12825) lack the custom_tlvs, retry_strategy, and route_params_config parameters present in the corresponding refund builders. Additionally, the offer builders do not call add_new_awaiting_invoice to track the payment. These functions should be updated consistently with the refund builder changes to support custom TLVs and proper payment parameter configuration.

🧹 Nitpick comments (5)
lightning/src/ln/outbound_payment.rs (3)

520-550: New RetryableSendFailure::InvalidCustomTlvs is well-scoped but error semantics are generic

Introducing InvalidCustomTlvs as its own RetryableSendFailure variant is a good separation from routing/expiry issues, and its documentation clearly ties it to RecipientOnionFields::custom_tlvs. However, downstream mapping currently treats it as PaymentFailureReason::UnexpectedError, which may be a bit opaque from an API consumer standpoint; consider a more specific PaymentFailureReason (or expanding docs) in a follow-up to surface “caller constructed invalid custom TLVs” more directly.


1288-1305: Static-invoice send currently omits custom TLVs by design

send_payment_for_static_invoice continues to use RecipientOnionFields::spontaneous_empty() and passes that into send_payment_for_bolt12_invoice_internal without any custom_tlvs. This is consistent with the async static-invoice flow, which does not expose a public surface for custom TLVs yet. If you later extend static invoices to support caller-specified custom TLVs, this is the place to thread them through.


2861-2882: New test_recipient_onion_fields_with_custom_tlvs provides good boundary coverage

The test exercises:

  • TLVs below the custom range (0, 1) → error.
  • Forbidden experimental types (keysend and 77_777) → error.
  • Valid sorted high-range types → success.

One optional improvement is to add a case with duplicate type numbers to confirm the uniqueness check via sort_unstable_by_key + prev >= *typ behaves as intended, but the current coverage already hits the most important boundaries.

lightning/src/ln/offers_tests.rs (2)

185-203: Helper correctly threads custom TLVs through PassAlongPathArgs

This mirrors route_bolt12_payment and just adds .with_custom_tlvs(custom_tlvs), so behavior stays consistent while exercising the new TLV plumbing. If you ever want to avoid the extra clone()s at call sites, you could take an impl Into<Vec<(u64, Vec<u8>)>> or a slice and clone internally, but that’s purely a test‑only micro-optimization.


1371-1494: New async Bolt12 custom‑TLV test is sound; consider asserting TLV propagation explicitly

The flow here closely tracks pays_bolt12_invoice_asynchronously, with custom_tlvs threaded through:

  • pay_for_offer when initiating the offer payment,
  • route_bolt12_payment_with_custom_tlvs for the simulated HTLC forward,
  • ClaimAlongRouteArgs::with_custom_tlvs(custom_tlvs) on fulfillment.

The InvoiceReceived idempotency and send_payment_for_bolt12_invoice error transitions (DuplicateInvoice → UnexpectedInvoice after IDEMPOTENCY_TIMEOUT_TICKS) are exercised correctly, and the PaymentContext extracted from PaymentClaimable is checked against the expected Bolt12Offer context.

Two possible tightenings for this test:

  • If Event::PaymentClaimable and/or outbound payment state exposes the received custom TLVs, adding an assert_eq! against the original custom_tlvs would directly validate end‑to‑end TLV propagation, not just that the payment still succeeds.
  • Given this scenario is specifically an offer payment, the PaymentPurpose::Bolt12RefundPayment match arm is effectively dead code; you could simplify the match to only accept Bolt12OfferPayment and treat other variants as an error for stricter expectations.

Functionally this looks correct; these suggestions would just make the test’s intent and coverage sharper.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b0143d7 and a527304.

📒 Files selected for processing (6)
  • lightning/src/ln/async_payments_tests.rs (17 hunks)
  • lightning/src/ln/channelmanager.rs (12 hunks)
  • lightning/src/ln/invoice_utils.rs (4 hunks)
  • lightning/src/ln/max_payment_path_len_tests.rs (1 hunks)
  • lightning/src/ln/offers_tests.rs (40 hunks)
  • lightning/src/ln/outbound_payment.rs (26 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • lightning/src/ln/invoice_utils.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/channelmanager.rs
  • lightning/src/ln/outbound_payment.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/channelmanager.rs
  • lightning/src/ln/outbound_payment.rs
🧬 Code graph analysis (2)
lightning/src/ln/channelmanager.rs (1)
lightning/src/ln/outbound_payment.rs (2)
  • custom_tlvs (784-786)
  • custom_tlvs (799-801)
lightning/src/ln/offers_tests.rs (3)
lightning/src/ln/outbound_payment.rs (2)
  • custom_tlvs (784-786)
  • custom_tlvs (799-801)
lightning/src/offers/invoice_request.rs (3)
  • amount_msats (1145-1156)
  • amount_msats (1199-1201)
  • as_ref (572-574)
lightning/src/offers/offer.rs (15)
  • absolute_expiry (907-909)
  • offer (1892-1892)
  • offer (1903-1903)
  • offer (1982-1982)
  • offer (1991-1991)
  • offer (2033-2033)
  • offer (2049-2049)
  • offer (2058-2058)
  • offer (2073-2073)
  • offer (2079-2079)
  • offer (2087-2087)
  • offer (2095-2095)
  • offer (2103-2103)
  • as_ref (859-861)
  • as_ref (1172-1174)
⏰ 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). (17)
  • GitHub Check: linting
  • GitHub Check: benchmark
  • GitHub Check: build-tx-sync (ubuntu-latest, 1.75.0)
  • GitHub Check: build (windows-latest, stable)
  • GitHub Check: semver-checks
  • GitHub Check: semver-checks
  • GitHub Check: semver-checks
  • GitHub Check: benchmark
  • GitHub Check: linting
  • GitHub Check: check_release
  • GitHub Check: build-tx-sync (macos-latest, stable)
  • GitHub Check: build-tx-sync (macos-latest, 1.75.0)
  • GitHub Check: build-tx-sync (ubuntu-latest, 1.75.0)
  • GitHub Check: build-tx-sync (ubuntu-latest, beta)
  • GitHub Check: build-tx-sync (ubuntu-latest, stable)
  • GitHub Check: build (windows-latest, stable)
  • GitHub Check: build (macos-latest, 1.75.0)
🔇 Additional comments (19)
lightning/src/ln/async_payments_tests.rs (1)

550-553: LGTM: Consistent test updates for new API parameter.

All pay_for_offer call sites have been correctly updated to include the new custom_tlvs parameter (passed as vec![]). The changes are mechanical, consistent, and appropriate for tests that don't exercise custom TLV functionality.

Also applies to: 711-714, 786-789, 918-921, 1019-1022, 1128-1131, 1191-1194, 1277-1280, 1374-1377, 1475-1478, 1721-1724, 1818-1821, 1936-1936, 2380-2383, 2715-2718, 2780-2783, 2884-2887

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

80-99: Threading custom_tlvs through AwaitingInvoice → InvoiceReceived is consistent and state-safe

Adding custom_tlvs: Vec<(u64, Vec<u8>)> to both PendingOutboundPayment::AwaitingInvoice and ::InvoiceReceived cleanly carries user-provided TLVs through the pre-HTLC lifecycle. Combined with the later core::mem::take in mark_invoice_received_and_get_details, this avoids duplication and ensures a single authoritative copy when transitioning to InvoiceReceived. The fields are only present on pre-lock-in states, so they won’t leak into retry bookkeeping or fulfilled/abandoned variants unintentionally.


756-771: RecipientOnionFields::with_custom_tlvs validation logic looks correct and robust

The builder method correctly enforces:

  • TLV types in the custom range (typ >= 1 << 16),
  • exclusion of reserved experimental types (keysend 5482373484 and 77_777),
  • strict ordering and uniqueness via sort_unstable_by_key plus the prev >= *typ check.

The sort + uniqueness check ensures the invariant documented in custom_tlvs() and keeps serialization stable. The method also avoids partial state by only assigning self.custom_tlvs after all validation passes. Overall this is a solid API surface for callers constructing custom TLVs.


811-824: check_merge correctly enforces even-TLV equality and intersects full TLV sets

Given the guarantees from with_custom_tlvs (sorted, unique TLVs), the merge logic:

  • Enforces that all even-numbered TLVs (critical types) match exactly between HTLC parts.
  • Reduces both tlvs and further_tlvs to their intersection, ensuring only HTLC-order-independent data survives.

This matches the spec intent for multi-part payments and preserves invariants without introducing ordering bugs. Complexity is quadratic in TLV count but acceptable given expected small vectors.


923-969: BOLT11 path: validation and error propagation for custom_tlvs are wired correctly

Extending pay_for_bolt11_invoice to take custom_tlvs: Vec<(u64, Vec<u8>)> and immediately run them through RecipientOnionFields::secret_only(...).with_custom_tlvs(custom_tlvs) ensures:

  • Invalid TLVs are caught before any routing or state changes.
  • Callers receive a precise Bolt11PaymentError::SendingFailed(RetryableSendFailure::InvalidCustomTlvs).

Payment metadata is still taken purely from the invoice, so there’s no ambiguity between invoice-provided payload and user-provided TLVs. The rest of the flow is unchanged, so behavior is preserved for callers passing vec![].


991-1019: BOLT12 path: custom_tlvs propagation from invoice-awaiting state through send is coherent

mark_invoice_received_and_get_details now returns custom_tlvs alongside hash/retry/config, and send_payment_for_bolt12_invoice uses them to build:

let recipient_onion = RecipientOnionFields::spontaneous_empty()
    .with_custom_tlvs(custom_tlvs)
    .map_err(|_| Bolt12PaymentError::SendingFailed(RetryableSendFailure::InvalidCustomTlvs))?;

This mirrors the BOLT11 behavior: invalid TLVs are rejected before route lookup, and state transitions only carry raw TLVs; their validation happens right before onion construction. The additional InvalidCustomTlvs case in the RetryableSendFailure match is also correctly mapped to an abandon with PaymentFailureReason::UnexpectedError.


1026-1076: Internal BOLT12 send: new recipient_onion parameter and error mapping are consistent

send_payment_for_bolt12_invoice_internal now takes an explicit recipient_onion: RecipientOnionFields, which matches how non-BOLT12 payments are handled and keeps onion-related data out of the mutable RouteParameters. The added RetryableSendFailure::InvalidCustomTlvs arm in the route-construction error mapping properly abandons the payment and emits a Bolt12PaymentError::SendingFailed(e), so callers observe the same error variant they triggered.


1989-2005: add_new_pending_payment extension with bolt12_invoice is consistent with retry semantics

Allowing add_new_pending_payment to take an optional PaidBolt12Invoice and storing it in Retryable via create_pending_payment makes BOLT12 retries able to emit PaymentSent with proof-of-payment information. The change is orthogonal to custom_tlvs but integrates cleanly with the extended outbound state.


1988-2011: received_offer and add_new_awaiting_invoice now correctly capture user custom_tlvs

For BOLT12 offers resolved via DNS:

  • received_offer accepts custom_tlvs and populates them into PendingOutboundPayment::AwaitingInvoice.
  • add_new_awaiting_invoice accepts and stores custom_tlvs directly when starting from an awaiting-invoice state.

These flows ensure that any app-level TLVs associated with an offer-based payment are persisted prior to invoice arrival and later consumed when building the actual RecipientOnionFields. No extra copies or lifetimes issues appear here.


2040-2085: Invoice-received transition handles custom_tlvs and no_std correctly

mark_invoice_received and mark_invoice_received_and_get_details now:

  • Move custom_tlvs from AwaitingInvoice to InvoiceReceived using core::mem::take(custom_tlvs) (no_std-friendly).
  • Return custom by value so the send path can construct RecipientOnionFields without re-locking the map.
  • Handle repeated calls on InvoiceReceived by cloning existing custom_tlvs and returning is_newly_marked = false, preserving idempotency.

This avoids double consumption of the TLV vec and matches the existing comment about idempotent event generation.


2720-2790: TLV upgrade mapping for AwaitingInvoice/InvoiceReceived includes custom_tlvs cleanly

Adding:

  • (9, custom_tlvs, optional_vec) to the AwaitingInvoice schema, and
  • (7, custom_tlvs, optional_vec) to the InvoiceReceived schema

ensures custom_tlvs are persisted across restarts and safely ignored by older versions (as an optional vec). The legacy _max_total_routing_fee_msat handling is unchanged, and there are no ordering conflicts with existing TLV type numbers. This is aligned with other optional fields in the enum and maintains backward-compatibility semantics for downgrade.


3059-3239: Updated tests passing vec![] into add_new_awaiting_invoice keep semantics intact

The various tests for staleness, abandonment, and BOLT12 flows now call:

outbound_payments.add_new_awaiting_invoice(
    payment_id,
    vec![],
    expiration,
    Retry::Attempts(0),
    RouteParametersConfig::default(),
    None,
)

or similar, which simply stores an empty custom_tlvs vec in the new field. Because empty TLVs are the pre-existing behavior, this preserves original semantics while satisfying the new API. The assertions about pending payments and events remain logically unchanged.

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

518-528: pay_for_offer call updated correctly with empty custom_tlvs

The new vec![] argument matches the expanded pay_for_offer signature where custom_tlvs: Vec<(u64, Vec<u8>)> sits between payment_id and OptionalOfferPaymentParams. Supplying an empty vec preserves prior behavior while keeping this test focused on “too-large blinded paths” rather than TLV content.

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

12880-12901: Consistent implementation across refund builder variants.

Both create_refund_builder and create_refund_builder_using_router correctly thread custom_tlvs through to add_new_awaiting_invoice. The implementations are consistent with each other.

Also applies to: 12922-12945


13055-13067: Consistent custom TLV propagation across all offer payment variants.

All three offer payment functions (pay_for_offer, pay_for_offer_from_hrn, pay_for_offer_with_quantity) consistently capture and pass custom_tlvs to add_new_awaiting_invoice within their respective closures. The implementation is correct and uniform.

Also applies to: 13085-13097, 13128-13141


2272-2282: Custom TLV examples are well-documented with comprehensive validation in place.

The documentation examples correctly demonstrate custom TLV usage. The validation logic in RecipientOnionFields::with_custom_tlvs() enforces:

  • TLV type range: must be ≥ 2^16 (65536) to use custom range, preventing protocol violations
  • Reserved types rejected: keysend (5482373484) and async payments (77_777)
  • Uniqueness and strict ordering enforced

The example TLV type 343493 is valid (within custom range) and properly follows BOLT conventions as an optional type. Tests confirm validation catches invalid types and edge cases like oversized payloads trigger OnionPacketSizeExceeded error appropriately.


15789-15795: Remove this comment — async payments correctly handle custom TLVs.

The received_offer function properly accepts and stores the custom_tlvs parameter (line 1989 in outbound_payment.rs, persisted at line 2002). The empty vec![] passed at line 15792 is intentional design, not a limitation: the DNSSEC async payment flow doesn't require custom TLVs, and they are appropriately left empty. Custom TLVs are a first-class field in the payment state and will be used when the invoice is received. This is correct behavior.

Likely an incorrect or invalid review comment.

lightning/src/ln/offers_tests.rs (2)

329-329: Refund builder callsites correctly updated for new custom_tlvs parameter

All create_refund_builder / create_refund_builder_using_router invocations now pass vec![] immediately before the Retry argument, matching the likely new signature (amount, expiry, payment_id, custom_tlvs, retry, route_params_cfg). This keeps existing tests behaviorally identical (no custom TLVs) while exercising the new API shape; the usage looks consistent across all sites.

Also applies to: 595-595, 625-625, 781-781, 909-909, 1018-1018, 1172-1172, 1583-1583, 1862-1862, 1896-1896, 2019-2019, 2030-2030, 2087-2087, 2179-2179, 2185-2185, 2304-2304, 2353-2353


496-496: pay_for_offer callsites now pass explicit empty custom TLVs

Each pay_for_offer call has been updated to pass an explicit vec![] (or custom_tlvs in the new test) in the new parameter slot before the retry/config argument, preserving prior semantics (no custom TLVs by default) while conforming to the updated API. The amount / payment_id / retry arguments remain unchanged, and the pattern is consistent across all tests, including failure-path scenarios.

Also applies to: 518-518, 685-685, 850-850, 971-971, 1098-1098, 1231-1231, 1304-1304, 1397-1397, 1667-1667, 1692-1692, 1767-1767, 1783-1783, 1967-1967, 1978-1978, 2065-2065, 2122-2122, 2155-2155, 2158-2158, 2237-2237, 2445-2445, 2529-2529, 2615-2615

Comment on lines 5551 to 5566
/// To use default settings, call the function with [`RouteParametersConfig::default`].
pub fn pay_for_bolt11_invoice(
&self, invoice: &Bolt11Invoice, payment_id: PaymentId, amount_msats: Option<u64>,
route_params_config: RouteParametersConfig, retry_strategy: Retry,
custom_tlvs: Vec<(u64, Vec<u8>)>, route_params_config: RouteParametersConfig,
retry_strategy: Retry,
) -> Result<(), Bolt11PaymentError> {
let best_block_height = self.best_block.read().unwrap().height;
let _persistence_guard = PersistenceNotifierGuard::notify_on_drop(self);
self.pending_outbound_payments.pay_for_bolt11_invoice(
invoice,
payment_id,
amount_msats,
custom_tlvs,
route_params_config,
retry_strategy,
&self.router,
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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Extract pay_for_bolt11_invoice function signature
rg -n -A10 'pub fn pay_for_bolt11_invoice' lightning/src/ln/channelmanager.rs | head -20

Repository: shaavan/rust-lightning

Length of output: 622


🏁 Script executed:

#!/bin/bash
# Extract create_refund_builder function signature and context around lines 12883-12884
sed -n '12875,12895p' lightning/src/ln/channelmanager.rs

Repository: shaavan/rust-lightning

Length of output: 1012


🏁 Script executed:

#!/bin/bash
# Search for all payment-related functions with custom_tlvs parameter
rg -n 'pub fn.*custom_tlvs' lightning/src/ln/channelmanager.rs

Repository: shaavan/rust-lightning

Length of output: 157


🏁 Script executed:

#!/bin/bash
# Search for create_offer_builder and other payment builders with custom_tlvs
rg -n -A6 'pub fn create_offer_builder' lightning/src/ln/channelmanager.rs

Repository: shaavan/rust-lightning

Length of output: 535


🏁 Script executed:

#!/bin/bash
# Search for all pay_ functions to understand the full API surface
rg -n 'pub fn pay_' lightning/src/ln/channelmanager.rs | head -20

Repository: shaavan/rust-lightning

Length of output: 266


🏁 Script executed:

#!/bin/bash
# Get the full signature of create_offer_builder with custom_tlvs pattern
sed -n '12400,12550p' lightning/src/ln/channelmanager.rs | rg -n -B5 -A10 'pub fn create_offer_builder'

Repository: shaavan/rust-lightning

Length of output: 48


🏁 Script executed:

#!/bin/bash
# Extract pay_for_offer function signature
sed -n '13056,13075p' lightning/src/ln/channelmanager.rs

Repository: shaavan/rust-lightning

Length of output: 793


🏁 Script executed:

#!/bin/bash
# Extract pay_for_offer_with_quantity function signature
sed -n '13129,13150p' lightning/src/ln/channelmanager.rs

Repository: shaavan/rust-lightning

Length of output: 802


Parameter ordering inconsistency in custom_tlvs APIs.

The addition of custom_tlvs to pay_for_bolt11_invoice uses the parameter order custom_tlvs, route_params_config, retry_strategy, which differs from create_refund_builder where it's custom_tlvs, retry_strategy, route_params_config (lines 12882-12884). This inconsistency across payment APIs could confuse users. Consider standardizing the parameter order for all payment functions that accept these parameters.

🤖 Prompt for AI Agents
In lightning/src/ln/channelmanager.rs around lines 5551-5566, the
pay_for_bolt11_invoice signature orders parameters as custom_tlvs,
route_params_config, retry_strategy which is inconsistent with other APIs (e.g.
create_refund_builder uses custom_tlvs, retry_strategy, route_params_config);
change the pay_for_bolt11_invoice parameter order to match the established
convention (custom_tlvs, retry_strategy, route_params_config), update the
function signature, all call sites, any doc comments and tests to the new
ordering, and run the build/tests to ensure no breakage.

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