-
Notifications
You must be signed in to change notification settings - Fork 422
BOLT12 Recurrence: Proof-of-Concept Implementation (Core TLVs + Payee Flow) #4245
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
👋 I see @valentinewallace was un-assigned. |
This commit begins the introduction of BOLT12 recurrence support in LDK. It adds the core recurrence-related fields to `Offer`, enabling subscription-style and periodic payments as described in the draft spec. Since this is a PoC, the focus is on establishing the data model and documenting the intended semantics. Where the spec is ambiguous or redundant, accompanying comments note possible simplifications or improvements. This lays the foundation for the following commits, which will implement invoice-request parsing, payee-side validation, and period/paywindow handling. Spec reference: https://github.com/rustyrussell/bolts/blob/guilt/offers-recurrence/12-offer-encoding.md#tlv-fields-for-offers
|
Fixed CI: .01 → .02 |
This commit adds the recurrence-related TLVs to `InvoiceRequest`, allowing payers to specify the intended period index, an optional starting offset, and (when applicable) a recurrence cancellation signal. Spec reference: https://github.com/rustyrussell/bolts/blob/guilt/offers-recurrence/12-offer-encoding.md#tlv-fields-for-invoice_request
This commit adds the recurrence-related TLVs to the `Invoice` encoding, allowing the payee to include `invoice_recurrence_basetime`. This field anchors the start time (UNIX timestamp) of the recurrence schedule and is required for validating period boundaries across successive invoices. Additional initialization logic, validation notes, and design considerations are documented inline within the commit. Spec reference: https://github.com/rustyrussell/bolts/blob/guilt/offers-recurrence/12-offer-encoding.md#invoices
This begins the payee-side recurrence implementation by adding a dedicated builder API for constructing Offers that include recurrence fields. The new `create_offer_builder_with_recurrence` helper mirrors the existing offer builder but ensures that the recurrence TLVs are always included, making it easier for users to define subscription-style Offers.
This commit adds a minimal state tracker in `ChannelManager` for handling inbound recurring BOLT12 payments. Each entry records the payer’s recurrence progress (offset, next expected counter, and basetime), giving the payee enough information to validate successive `invoice_request`s and produce consistent invoices. LDK inbound payments have historically been fully stateless. Introducing a stateful mechanism here is a deliberate PoC choice to make recurrence behavior correct and testable end-to-end. For production, we may instead push this state to the user layer, or provide hooks so nodes can manage their own recurrence state externally. For now, this internal tracker gives us a clear foundation to build and evaluate the recurrence flow.
…` split) This refactor removes the separate `respond_with` / `respond_with_no_std` variants and replaces them with a single unified `respond_using_derived_keys(created_at)` API. Reasoning: - Upcoming recurrence logic requires setting `invoice_recurrence_basetime` based on the invoice’s `created_at` timestamp. - For consistency with Offer and Refund builders, we want a single method that accepts an explicit `created_at` value at the callsite. - The only real difference between the std/no_std response paths was how `created_at` was sourced; once it becomes a parameter, the split becomes unnecessary. This change consolidates the response flow, reduces API surface, and makes future recurrence-related changes simpler and more uniform across Offer, InvoiceRequest, and Refund builders.
This commit adds payee-side handling for recurrence-enabled `InvoiceRequest`s. The logic now: - Distinguishes between one-off requests, initial recurring requests, and successive recurring requests. - Initializes a new `RecurrenceData` session on the first recurring request (counter = 0). - Validates successive requests against stored session state (offset, expected counter, basetime). - Enforces paywindow timing when applicable. - Handles recurrence cancellation by removing the session and returning no invoice. This forms the core stateful logic required for a node to act as a BOLT12 recurrence payee. Payment-acceptance and state-update logic will follow in the next commit.
This commit adds the final piece of the payee-side recurrence flow: updating the internal `next_payable_counter` once a recurring payment has been successfully claimed. The update is performed immediately before emitting the `PaymentClaimed` event, ensuring the counter is advanced only after the payment is fully completed and acknowledged by the node. This provides a clear correctness boundary and avoids premature state transitions. The approach is intentionally conservative for this PoC. Future refinements may place the update earlier in the pipeline or integrate it more tightly with the payment-claim flow, but the current design offers simple and reliable semantics.
|
Updated: .02 → .03 Changes:
|
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #4245 +/- ##
==========================================
- Coverage 89.32% 89.17% -0.15%
==========================================
Files 180 180
Lines 138730 139103 +373
Branches 138730 139103 +373
==========================================
+ Hits 123914 124052 +138
- Misses 12190 12417 +227
- Partials 2626 2634 +8
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
This PR introduces a Proof of Concept implementation of BOLT12 Recurrence in LDK.
It adds all recurrence-related
Offer,InvoiceRequest, andInvoicefields from the spec, and implements the complete payee-side flow: parsing and validating recurring invoice requests, maintaining per-payer recurrence state, enforcing period and paywindow semantics, and producing invoices with the correct recurrence metadata.What This PR Adds
This PR introduces a Proof of Concept implementation of BOLT12 Recurrence in LDK. It adds all recurrence-related Offer, InvoiceRequest, and Invoice fields from the spec, and implements the complete payee-side flow: parsing and validating recurring invoice requests, maintaining per-payer recurrence state, enforcing period and paywindow semantics, and producing invoices with the correct recurrence metadata.
1. Introduce Recurrence Fields
All recurrence TLVs are implemented with full parsing for Offers, InvoiceRequest, and Invoice. Key points:
RecurrenceFieldsrepresentation, avoiding optional/compulsory branching and making recurrence semantics explicit.2. Recurrence State in ChannelManager (Payee Side)
Adds a minimal in-memory state machine to track recurrence sessions: the payer’s offset (
invoice_request_start), the next expected counter (next_payable_counter), and the fixed basetime (recurrence_basetime). A session is created on the first recurring request, validated on each successive one, and removed on cancellation. This gives the payee a single source of truth for recurrence flow without modifying payer behavior.3. Successive Request Handling and Paywindow Enforcement
Successive invoice requests are validated against stored state, paywindows are enforced (using simplified PoC period math), misaligned or out-of-window requests are rejected, and the correct recurrence basetime is inserted into each generated invoice. This completes the end-to-end flow for the payee.
4. State Update on
PaymentClaimedWhen payment is actually claimed,
next_payable_counteris incremented. This ensures the recurrence state only advances on real settlement, preventing incorrect progression if invoices are produced but not paid.Design Notes
Commentary-First Approach
Because the recurrence spec is still evolving, the implementation deliberately includes commentary at points where semantics are unclear, redundant, or could be simplified. These notes highlight naming inconsistencies (period vs unit), field duplication, optional/compulsory interplay, and potential simplifications. The intent is to contribute both code and clarity to the spec discussion.
Intentional PoC Simplifications
Certain behaviors are simplified for clarity and reviewability: period calculations use fixed approximations, only payee-side logic is implemented, and edge cases (month overflow, leap seconds) are deferred. The PR also does not persist recurrence state across restarts. All simplifications are explicitly documented.
What This PR Does Not Implement Yet
These are intentionally deferred to keep this PR focused and reviewable.