Skip to content

Commit 147e86c

Browse files
committed
BOLT 12: Allow either optional or compulsory recurrence variants.
This allows the writer of the offer to decide how pre-recurrence-supporting wallets should behave. Signed-off-by: Rusty Russell <[email protected]>
1 parent 08fc679 commit 147e86c

File tree

1 file changed

+31
-14
lines changed

1 file changed

+31
-14
lines changed

12-offer-encoding.md

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,11 @@ The human-readable prefix for offers is `lno`.
229229
1. type: 22 (`offer_issuer_id`)
230230
2. data:
231231
* [`point`:`id`]
232-
1. type: 24 (`offer_recurrence`)
232+
1. type: 24 (`offer_recurrence_compulsory`)
233+
2. data:
234+
* [`byte`:`time_unit`]
235+
* [`tu32`:`period`]
236+
1. type: 25 (`offer_recurrence_optional`)
233237
2. data:
234238
* [`byte`:`time_unit`]
235239
* [`tu32`:`period`]
@@ -273,6 +277,8 @@ Thus, each offer containing a recurring payment has:
273277

274278
Note that the `offer_absolute_expiry` field already covers the case where an offer is no longer valid after January 1st 2021.
275279

280+
For wallets which don't (yet) support recurrence, the basic recurrence field comes in two variants: `offer_recurrence_compulsory` if they should not attempt a payment, and `offer_recurrence_optional` if it still makes sense for them to attempt a single payment.
281+
276282
### Offer Period Calculation
277283

278284
Each period has a zero-based index, and a start time and an end time. Because the periods can be in non-seconds units, the duration of a period can depend on when it starts. The period with index N+1 begins immediately following the end of period with index N.
@@ -356,8 +362,11 @@ A writer of an offer:
356362
- MUST set `offer_quantity_max` to 0.
357363
- otherwise:
358364
- MUST NOT set `offer_quantity_max`.
359-
- MAY include `offer_recurrence` to indicate offer should trigger time-spaced invoice requests.
360-
- if it includes `offer_recurrence`:
365+
- If an offer MAY trigger time-spaced invoice requests:
366+
- MUST include exactly one of `offer_recurrence_optional` or `offer_recurrence_compulsory`.
367+
- MAY use `offer_recurrence_optional` if a payment of a single period would be useful (compatibility with pre-recurrence readers).
368+
- MUST NOT use `offer_recurrence_optional` if `offer_recurrence_base` is present.
369+
- if it includes either `offer_recurrence_optional` or `offer_recurrence_compulsory`:
361370
- MUST set `time_unit` to 0 (seconds), 1 (days), 2 (months) or 3 (years).
362371
- MUST set `period` to how often (in `time-unit`) it wants to be paid.
363372
- if there is a maximum number of payments:
@@ -418,13 +427,13 @@ A reader of an offer:
418427
- MUST warn the user if the received `invoice_amount` differs significantly
419428
from that estimate.
420429
- if the current time is after `offer_absolute_expiry`:
421-
- MUST NOT make an initial response to the offer (i.e. continuing an existing offer with `offer_recurrence` is ok)
422-
- if `offer_recurrence` is set:
430+
- MUST NOT make an initial response to the offer (i.e. continuing an existing offer with recurrence is ok)
431+
- if `offer_recurrence_optional` or `offer_recurrence_compulsory` are set:
423432
- if `time_unit` is not one of 0, 1, 2 or 3:
424433
- MUST NOT respond to the offer.
425434
- if `offer_recurrence_limit` is set and `max_period` is 0:
426435
- MUST NOT respond to the offer.
427-
- otherwise: (no `offer_recurrence`):
436+
- otherwise: (no recurrence):
428437
- if it `offer_recurrence_paywindow`, `offer_recurrence_limit` or `offer_recurrence_base` are set:
429438
- MUST NOT respond to the offer.
430439
- if it chooses to send an invoice request, it sends an onion message:
@@ -531,7 +540,11 @@ while still allowing signature validation.
531540
1. type: 22 (`offer_issuer_id`)
532541
2. data:
533542
* [`point`:`id`]
534-
1. type: 24 (`offer_recurrence`)
543+
1. type: 24 (`offer_recurrence_compulsory`)
544+
2. data:
545+
* [`byte`:`time_unit`]
546+
* [`tu32`:`period`]
547+
1. type: 25 (`offer_recurrence_optional`)
535548
2. data:
536549
* [`byte`:`time_unit`]
537550
* [`tu32`:`period`]
@@ -607,7 +620,7 @@ The writer:
607620
- MUST set `invreq_quantity` less than or equal to `offer_quantity_max`.
608621
- otherwise:
609622
- MUST NOT set `invreq_quantity`
610-
- if `offer_recurrence` is present:
623+
- if `offer_recurrence_optional` or `offer_recurrence_compulsory` are present:
611624
- for the initial request:
612625
- MUST use a unique `invreq_payer_id`.
613626
- MUST set `invreq_recurrence_counter` `counter` to 0.
@@ -692,7 +705,7 @@ The reader:
692705
- MAY reject the invoice request if `invreq_amount`.`msat` greatly exceeds the *expected amount*.
693706
- otherwise (no `offer_amount`):
694707
- MUST reject the invoice request if it does not contain `invreq_amount`.
695-
- if `offer_recurrence` is present:
708+
- if `offer_recurrence_optional` or `offer_recurrence_compulsory` are present:
696709
- MUST reject the invoice request if there is no `invreq_recurrence_counter` field.
697710
- if `offer_recurrence_base` is present:
698711
- MUST reject the invoice request if there is no `invreq_recurrence_start` field.
@@ -710,7 +723,7 @@ The reader:
710723
- SHOULD reject the invoice request if the current time is equal to or after the start of the period plus `seconds_after`.
711724
- otherwise:
712725
- SHOULD reject the invoice request if the current time is prior to the start of the previous period.
713-
- otherwise (no `offer_recurrence`):
726+
- otherwise (no recurrence):
714727
- MUST reject the invoice request if there is a `invreq_recurrence_counter` field.
715728
- MUST reject the invoice request if there is a `invreq_recurrence_start` field.
716729
- SHOULD send an invoice in response using the `onionmsg_tlv` `reply_path`.
@@ -802,7 +815,11 @@ the `onion_message` `invoice` field.
802815
1. type: 22 (`offer_issuer_id`)
803816
2. data:
804817
* [`point`:`id`]
805-
1. type: 24 (`offer_recurrence`)
818+
1. type: 24 (`offer_recurrence_compulsory`)
819+
2. data:
820+
* [`byte`:`time_unit`]
821+
* [`tu32`:`period`]
822+
1. type: 25 (`offer_recurrence_optional`)
806823
2. data:
807824
* [`byte`:`time_unit`]
808825
* [`tu32`:`period`]
@@ -949,7 +966,7 @@ A writer of an invoice:
949966
- for the bitcoin chain, it MUST set each `fallback_address` with
950967
`version` as a valid witness version and `address` as a valid witness
951968
program
952-
- if `offer_recurrence` is present:
969+
- if `offer_recurrence_optional` or `offer_recurrence_compulsory` are present:
953970
- MUST set `invoice_recurrence_basetime`.`basetime` to the start of period #0 as calculated by [Period Calculation](#offer-period-calculation).
954971
- if it sets `invoice_relative_expiry`:
955972
- MUST NOT set `invoice_relative_expiry`.`seconds_from_creation` more than the number of seconds after `invoice_created_at` that payment for this period will be accepted.
@@ -1007,7 +1024,7 @@ A reader of an invoice:
10071024
- MUST ignore any `fallback_address` for which `version` is greater than 16.
10081025
- MUST ignore any `fallback_address` for which `address` is less than 2 or greater than 40 bytes.
10091026
- MUST ignore any `fallback_address` for which `address` does not meet known requirements for the given `version`
1010-
- if `offer_recurrence` is present:
1027+
- if `offer_recurrence_optional` or `offer_recurrence_compulsory` are present:
10111028
- MUST reject the invoice if `invoice_recurrence_basetime` is not present.
10121029
- if `invreq_recurrence_counter` is 0:
10131030
- if `offer_recurrence_base` is present:
@@ -1024,7 +1041,7 @@ A reader of an invoice:
10241041
- MUST reject the invoice if it did not arrive via invoice request `onionmsg_tlv` `reply_path`.
10251042
- if it pays the invoice:
10261043
- MUST have authorization for the payment purpose, recipient and amount.
1027-
- if `offer_recurrence` is present:
1044+
- if `offer_recurrence_optional` or `offer_recurrence_compulsory` are present:
10281045
- SHOULD obtain single authorization which covers all expected payments.
10291046

10301047
## Rationale

0 commit comments

Comments
 (0)