@@ -70,6 +70,9 @@ fail_invreq_level(struct command *cmd,
7070 err -> error = tal_dup_arr (err , char , msg , strlen (msg ), 0 );
7171 /* FIXME: Add suggested_value / erroneous_field! */
7272
73+ if (!invreq -> reply_path )
74+ return command_hook_success (cmd );
75+
7376 payload = tlv_onionmsg_tlv_new (tmpctx );
7477 payload -> invoice_error = tal_arr (payload , u8 , 0 );
7578 towire_tlv_invoice_error (& payload -> invoice_error , err );
@@ -194,6 +197,13 @@ static struct command_result *createinvoice_done(struct command *cmd,
194197 json_tok_full (buf , t ));
195198 }
196199
200+ /* BOLT-recurrence #12:
201+ * - if `invreq_recurrence_cancel` is present:
202+ * - MUST NOT send an invoice in reply.
203+ */
204+ if (!ir -> reply_path )
205+ return command_hook_success (cmd );
206+
197207 payload = tlv_onionmsg_tlv_new (tmpctx );
198208 payload -> invoice = tal_steal (payload , rawinv );
199209 return send_onion_reply (cmd , ir -> reply_path , payload );
@@ -206,13 +216,19 @@ static struct command_result *createinvoice_error(struct command *cmd,
206216 struct invreq * ir )
207217{
208218 u32 code ;
219+ const char * status ;
209220
210221 /* If it already exists, we can reuse its bolt12 directly. */
211222 if (json_scan (tmpctx , buf , err ,
212- "{code:%}" , JSON_SCAN (json_to_u32 , & code )) == NULL
223+ "{code:%,data:{status:%}}" ,
224+ JSON_SCAN (json_to_u32 , & code ),
225+ JSON_SCAN_TAL (tmpctx , json_strdup , & status )) == NULL
213226 && code == INVOICE_LABEL_ALREADY_EXISTS ) {
214- return createinvoice_done (cmd , method , buf ,
215- json_get_member (buf , err , "data" ), ir );
227+ if (streq (status , "unpaid" ))
228+ return createinvoice_done (cmd , method , buf ,
229+ json_get_member (buf , err , "data" ), ir );
230+ if (streq (status , "expired" ))
231+ return fail_invreq (cmd , ir , "invoice expired (cancelled?)" );
216232 }
217233 return error (cmd , method , buf , err , ir );
218234}
@@ -372,6 +388,18 @@ static struct command_result *add_blindedpaths(struct command *cmd,
372388 found_best_peer , ir );
373389}
374390
391+ static struct command_result * cancel_invoice (struct command * cmd ,
392+ struct invreq * ir )
393+ {
394+ /* We create an invoice, so we can mark the cancellation, but with
395+ * expiry 0. And we don't send it to them! */
396+ * ir -> inv -> invoice_relative_expiry = 0 ;
397+
398+ /* In case they set a reply path! */
399+ ir -> reply_path = tal_free (ir -> reply_path );
400+ return create_invoicereq (cmd , ir );
401+ }
402+
375403static struct command_result * check_period (struct command * cmd ,
376404 struct invreq * ir ,
377405 u64 basetime )
@@ -483,6 +511,10 @@ static struct command_result *check_period(struct command *cmd,
483511 }
484512 }
485513
514+ /* If this is actually a cancel, we create an expired invoice */
515+ if (ir -> invreq -> invreq_recurrence_cancel )
516+ return cancel_invoice (cmd , ir );
517+
486518 return add_blindedpaths (cmd , ir );
487519}
488520
@@ -626,19 +658,23 @@ static struct command_result *invreq_base_amount_simple(struct command *cmd,
626658
627659 * amt = amount_msat (raw_amount );
628660 } else {
629- /* BOLT #12:
661+ /* BOLT-recurrence #12:
630662 *
631663 * The reader:
632664 *...
633665 * - otherwise (no `offer_amount`):
634- * - MUST reject the invoice request if it does not contain
635- * `invreq_amount`.
666+ * - MUST reject the invoice request if `invreq_recurrence_cancel`
667+ * is not present and it does not contain `invreq_amount`.
636668 */
637- err = invreq_must_have (cmd , ir , invreq_amount );
638- if (err )
639- return err ;
640-
641- * amt = amount_msat (* ir -> invreq -> invreq_amount );
669+ if (!ir -> invreq -> invreq_recurrence_cancel ) {
670+ err = invreq_must_have (cmd , ir , invreq_amount );
671+ if (err )
672+ return err ;
673+ }
674+ if (ir -> invreq -> invreq_amount )
675+ * amt = amount_msat (* ir -> invreq -> invreq_amount );
676+ else
677+ * amt = AMOUNT_MSAT (0 );
642678 }
643679 return NULL ;
644680}
@@ -776,6 +812,7 @@ static struct command_result *listoffers_done(struct command *cmd,
776812 bool active ;
777813 struct command_result * err ;
778814 struct amount_msat amt ;
815+ struct tlv_invoice_request_invreq_recurrence_cancel * cancel ;
779816
780817 /* BOLT #12:
781818 *
@@ -861,25 +898,29 @@ static struct command_result *listoffers_done(struct command *cmd,
861898 return fail_invreq (cmd , ir , "Offer expired" );
862899 }
863900
864- /* BOLT #12:
901+ /* BOLT-recurrence #12:
865902 * - if `offer_quantity_max` is present:
866- * - MUST reject the invoice request if there is no `invreq_quantity` field.
903+ * - MUST reject the invoice request if `invreq_recurrence_cancel`
904+ * is not present and there is no `invreq_quantity` field.
867905 * - if `offer_quantity_max` is non-zero:
868906 * - MUST reject the invoice request if `invreq_quantity` is zero, OR greater than
869907 * `offer_quantity_max`.
870908 * - otherwise:
871909 * - MUST reject the invoice request if there is an `invreq_quantity` field.
872910 */
873911 if (ir -> invreq -> offer_quantity_max ) {
874- err = invreq_must_have (cmd , ir , invreq_quantity );
875- if (err )
876- return err ;
912+ if (!ir -> invreq -> invreq_recurrence_cancel ) {
913+ err = invreq_must_have (cmd , ir , invreq_quantity );
914+ if (err )
915+ return err ;
916+ }
877917
878- if (* ir -> invreq -> invreq_quantity == 0 )
918+ if (ir -> invreq -> invreq_quantity && * ir -> invreq -> invreq_quantity == 0 )
879919 return fail_invreq (cmd , ir ,
880920 "quantity zero invalid" );
881921
882- if (* ir -> invreq -> offer_quantity_max &&
922+ if (ir -> invreq -> invreq_quantity &&
923+ * ir -> invreq -> offer_quantity_max &&
883924 * ir -> invreq -> invreq_quantity > * ir -> invreq -> offer_quantity_max ) {
884925 return fail_invreq (cmd , ir ,
885926 "quantity %" PRIu64 " > %" PRIu64 ,
@@ -923,13 +964,18 @@ static struct command_result *listoffers_done(struct command *cmd,
923964 * field.
924965 * - MUST reject the invoice request if there is a `invreq_recurrence_start`
925966 * field.
967+ * - MUST reject the invoice request if there is a `invreq_recurrence_cancel`
968+ * field.
926969 */
927970 err = invreq_must_not_have (cmd , ir , invreq_recurrence_counter );
928971 if (err )
929972 return err ;
930973 err = invreq_must_not_have (cmd , ir , invreq_recurrence_start );
931974 if (err )
932975 return err ;
976+ err = invreq_must_not_have (cmd , ir , invreq_recurrence_cancel );
977+ if (err )
978+ return err ;
933979 }
934980
935981 /* BOLT #12:
@@ -939,8 +985,12 @@ static struct command_result *listoffers_done(struct command *cmd,
939985 * - MUST copy all non-signature fields from the invoice request (including
940986 * unknown fields).
941987 */
988+ /* But "invreq_recurrence_cancel" doesn't exist in invoices, so temporarily remove */
989+ cancel = ir -> invreq -> invreq_recurrence_cancel ;
990+ ir -> invreq -> invreq_recurrence_cancel = NULL ;
942991 ir -> inv = invoice_for_invreq (cmd , ir -> invreq );
943992 assert (ir -> inv -> invreq_payer_id );
993+ ir -> invreq -> invreq_recurrence_cancel = cancel ;
944994
945995 /* BOLT #12:
946996 * - if `offer_issuer_id` is present:
0 commit comments