Skip to content

Commit 4d5e3ea

Browse files
committed
Introduce create_offer_builder_with_recurrence
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.
1 parent 3dfbadd commit 4d5e3ea

File tree

3 files changed

+80
-6
lines changed

3 files changed

+80
-6
lines changed

lightning/src/ln/channelmanager.rs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ use crate::offers::invoice::{Bolt12Invoice, UnsignedBolt12Invoice};
9595
use crate::offers::invoice_error::InvoiceError;
9696
use crate::offers::invoice_request::{InvoiceRequest, InvoiceRequestVerifiedFromOffer};
9797
use crate::offers::nonce::Nonce;
98-
use crate::offers::offer::{Offer, OfferFromHrn};
98+
use crate::offers::offer::{Offer, OfferFromHrn, RecurrenceFields};
9999
use crate::offers::parse::Bolt12SemanticError;
100100
use crate::offers::refund::Refund;
101101
use crate::offers::static_invoice::StaticInvoice;
@@ -12777,6 +12777,32 @@ macro_rules! create_offer_builder { ($self: ident, $builder: ty) => {
1277712777
Ok(builder.into())
1277812778
}
1277912779

12780+
/// Creates an [`OfferBuilder`] for a recurring offer.
12781+
///
12782+
/// This behaves like [`Self::create_offer_builder`] but additionally embeds
12783+
/// the recurrence TLVs defined in `recurrence_fields`.
12784+
///
12785+
/// Use this when constructing subscription-style offers where each invoice
12786+
/// request must correspond to a specific recurrence period. The provided
12787+
/// [`RecurrenceFields`] specify:
12788+
/// - how often invoices may be requested,
12789+
/// - when the first period begins,
12790+
/// - optional paywindows, and
12791+
/// - optional period limits.
12792+
///
12793+
/// Refer to [`Self::create_offer_builder`] for notes on privacy,
12794+
/// requirements, and potential failure cases.
12795+
pub fn create_offer_builder_with_recurrence(
12796+
&$self,
12797+
recurrence_fields: RecurrenceFields
12798+
) -> Result<$builder, Bolt12SemanticError> {
12799+
let builder = $self.flow.create_offer_builder_with_recurrence(
12800+
&*$self.entropy_source, recurrence_fields, $self.get_peers_for_blinded_path()
12801+
)?;
12802+
12803+
Ok(builder.into())
12804+
}
12805+
1278012806
/// Same as [`Self::create_offer_builder`], but allows specifying a custom [`MessageRouter`]
1278112807
/// instead of using the [`MessageRouter`] provided to the [`ChannelManager`] at construction.
1278212808
///

lightning/src/offers/flow.rs

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ use crate::offers::invoice_request::{
4343
InvoiceRequest, InvoiceRequestBuilder, InvoiceRequestVerifiedFromOffer, VerifiedInvoiceRequest,
4444
};
4545
use crate::offers::nonce::Nonce;
46-
use crate::offers::offer::{Amount, DerivedMetadata, Offer, OfferBuilder};
46+
use crate::offers::offer::{Amount, DerivedMetadata, Offer, OfferBuilder, RecurrenceFields};
4747
use crate::offers::parse::Bolt12SemanticError;
4848
use crate::offers::refund::{Refund, RefundBuilder};
4949
use crate::offers::static_invoice::{StaticInvoice, StaticInvoiceBuilder};
@@ -539,7 +539,7 @@ where
539539
}
540540

541541
fn create_offer_builder_intern<ES: Deref, PF, I>(
542-
&self, entropy_source: ES, make_paths: PF,
542+
&self, entropy_source: ES, recurrence_fields: Option<RecurrenceFields>, make_paths: PF,
543543
) -> Result<(OfferBuilder<'_, DerivedMetadata, secp256k1::All>, Nonce), Bolt12SemanticError>
544544
where
545545
ES::Target: EntropySource,
@@ -562,6 +562,10 @@ where
562562
OfferBuilder::deriving_signing_pubkey(node_id, expanded_key, nonce, secp_ctx)
563563
.chain_hash(self.chain_hash);
564564

565+
if let Some(recurrence) = recurrence_fields {
566+
builder = builder.recurrence(recurrence);
567+
}
568+
565569
for path in make_paths(node_id, context, secp_ctx)? {
566570
builder = builder.path(path)
567571
}
@@ -601,14 +605,48 @@ where
601605
where
602606
ES::Target: EntropySource,
603607
{
604-
self.create_offer_builder_intern(&*entropy_source, |_, context, _| {
608+
self.create_offer_builder_intern(&*entropy_source, None, |_, context, _| {
605609
self.create_blinded_paths(peers, context)
606610
.map(|paths| paths.into_iter().take(1))
607611
.map_err(|_| Bolt12SemanticError::MissingPaths)
608612
})
609613
.map(|(builder, _)| builder)
610614
}
611615

616+
/// Creates an [`OfferBuilder`] for a recurring offer.
617+
///
618+
/// This behaves like [`Self::create_offer_builder`] but additionally embeds
619+
/// the recurrence TLVs defined in `recurrence_fields`.
620+
///
621+
/// Use this when constructing subscription-style offers where each invoice
622+
/// request must correspond to a specific recurrence period. The provided
623+
/// [`RecurrenceFields`] specify:
624+
/// - how often invoices may be requested,
625+
/// - when the first period begins,
626+
/// - optional paywindows, and
627+
/// - optional period limits.
628+
///
629+
/// Refer to [`Self::create_offer_builder`] for notes on privacy,
630+
/// requirements, and potential failure cases.
631+
pub fn create_offer_builder_with_recurrence<ES: Deref>(
632+
&self, entropy_source: ES, recurrence_fields: RecurrenceFields,
633+
peers: Vec<MessageForwardNode>,
634+
) -> Result<OfferBuilder<'_, DerivedMetadata, secp256k1::All>, Bolt12SemanticError>
635+
where
636+
ES::Target: EntropySource,
637+
{
638+
self.create_offer_builder_intern(
639+
&*entropy_source,
640+
Some(recurrence_fields),
641+
|_, context, _| {
642+
self.create_blinded_paths(peers, context)
643+
.map(|paths| paths.into_iter().take(1))
644+
.map_err(|_| Bolt12SemanticError::MissingPaths)
645+
},
646+
)
647+
.map(|(builder, _)| builder)
648+
}
649+
612650
/// Same as [`Self::create_offer_builder`], but allows specifying a custom [`MessageRouter`]
613651
/// instead of using the one provided via the [`OffersMessageFlow`] parameterization.
614652
///
@@ -626,7 +664,7 @@ where
626664
ES::Target: EntropySource,
627665
{
628666
let receive_key = self.get_receive_auth_key();
629-
self.create_offer_builder_intern(&*entropy_source, |node_id, context, secp_ctx| {
667+
self.create_offer_builder_intern(&*entropy_source, None, |node_id, context, secp_ctx| {
630668
router
631669
.create_blinded_paths(node_id, receive_key, context, peers, secp_ctx)
632670
.map(|paths| paths.into_iter().take(1))
@@ -651,7 +689,7 @@ where
651689
where
652690
ES::Target: EntropySource,
653691
{
654-
self.create_offer_builder_intern(&*entropy_source, |_, _, _| {
692+
self.create_offer_builder_intern(&*entropy_source, None, |_, _, _| {
655693
Ok(message_paths_to_always_online_node)
656694
})
657695
}

lightning/src/offers/offer.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,16 @@ macro_rules! offer_builder_methods { (
391391
$return_value
392392
}
393393

394+
/// Set the [Offer::recurrence_fields] for the offer.
395+
///
396+
/// Successive calls to this method will override the previous setting.
397+
pub fn recurrence(
398+
$($self_mut)* $self: $self_type, recurrence: RecurrenceFields,
399+
) -> $return_type {
400+
$self.offer.recurrence_fields = Some(recurrence);
401+
$return_value
402+
}
403+
394404
/// Sets the quantity of items for [`Offer::supported_quantity`]. If not called, defaults to
395405
/// [`Quantity::One`].
396406
///

0 commit comments

Comments
 (0)