From 74bb5686d1dbaee5cb2b30bb3ab0111ae74cb31f Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 7 Jan 2026 15:40:53 +1030 Subject: [PATCH] common: tighten restrictions on periods, as per latest draft. Signed-off-by: Rusty Russell --- common/bolt12.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/common/bolt12.c b/common/bolt12.c index 1435b35d854a..8335dbfff1d5 100644 --- a/common/bolt12.c +++ b/common/bolt12.c @@ -171,6 +171,7 @@ struct tlv_offer *offer_decode(const tal_t *ctx, const u8 *data; size_t dlen; const struct tlv_field *badf; + const struct recurrence *recurr; data = string_to_data(tmpctx, b12, b12len, "lno", &dlen, fail); if (!data) @@ -242,6 +243,46 @@ struct tlv_offer *offer_decode(const tal_t *ctx, } } + /* BOLT-recurrence #12 + * - if `offer_recurrence_optional` or `offer_recurrence_compulsory` are set: + * - if `time_unit` is not one of 0, 1, or 2: + * - MUST NOT respond to the offer. + * - if `period` is 0: + * - MUST NOT respond to the offer. + * - if `offer_recurrence_limit` is set and `max_period_index` is 0: + * - MUST NOT respond to the offer. + */ + recurr = offer_recurrence(offer); + if (recurr) { + if (recurr->time_unit != 0 + && recurr->time_unit != 1 + && recurr->time_unit != 2) { + *fail = tal_fmt(ctx, "Offer contains invalid recurrence time_unit %u", recurr->time_unit); + return tal_free(offer); + } + if (recurr->period == 0) { + *fail = tal_fmt(ctx, "Offer contains invalid recurrence period %u", recurr->period); + return tal_free(offer); + } + if (offer->offer_recurrence_limit && *offer->offer_recurrence_limit == 0) { + *fail = tal_fmt(ctx, "Offer contains invalid recurrence limit %u", + *offer->offer_recurrence_limit); + return tal_free(offer); + } + } else { + /* BOLT-recurrence #12 + * - otherwise: (no recurrence): + * - if it `offer_recurrence_paywindow`, `offer_recurrence_limit` or `offer_recurrence_base` are set: + * - MUST NOT respond to the offer. + */ + if (offer->offer_recurrence_paywindow + || offer->offer_recurrence_limit + || offer->offer_recurrence_base) { + *fail = tal_strdup(ctx, "Offer contains recurrence fields but no recurrence"); + return tal_free(offer); + } + } + return offer; }