@@ -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}
@@ -359,6 +375,18 @@ static struct command_result *add_blindedpaths(struct command *cmd,
359375 found_best_peer , ir );
360376}
361377
378+ static struct command_result * cancel_invoice (struct command * cmd ,
379+ struct invreq * ir )
380+ {
381+ /* We create an invoice, so we can mark the cancellation, but with
382+ * expiry 0. And we don't send it to them! */
383+ * ir -> inv -> invoice_relative_expiry = 0 ;
384+
385+ /* In case they set a reply path! */
386+ ir -> reply_path = tal_free (ir -> reply_path );
387+ return create_invoicereq (cmd , ir );
388+ }
389+
362390static struct command_result * check_period (struct command * cmd ,
363391 struct invreq * ir ,
364392 u64 basetime )
@@ -470,6 +498,10 @@ static struct command_result *check_period(struct command *cmd,
470498 }
471499 }
472500
501+ /* If this is actually a cancel, we create an expired invoice */
502+ if (ir -> invreq -> invreq_recurrence_cancel )
503+ return cancel_invoice (cmd , ir );
504+
473505 return add_blindedpaths (cmd , ir );
474506}
475507
@@ -613,19 +645,23 @@ static struct command_result *invreq_base_amount_simple(struct command *cmd,
613645
614646 * amt = amount_msat (raw_amount );
615647 } else {
616- /* BOLT #12:
648+ /* BOLT-recurrence #12:
617649 *
618650 * The reader:
619651 *...
620652 * - otherwise (no `offer_amount`):
621- * - MUST reject the invoice request if it does not contain
622- * `invreq_amount`.
653+ * - MUST reject the invoice request if `invreq_recurrence_cancel`
654+ * is not present and it does not contain `invreq_amount`.
623655 */
624- err = invreq_must_have (cmd , ir , invreq_amount );
625- if (err )
626- return err ;
627-
628- * amt = amount_msat (* ir -> invreq -> invreq_amount );
656+ if (!ir -> invreq -> invreq_recurrence_cancel ) {
657+ err = invreq_must_have (cmd , ir , invreq_amount );
658+ if (err )
659+ return err ;
660+ }
661+ if (ir -> invreq -> invreq_amount )
662+ * amt = amount_msat (* ir -> invreq -> invreq_amount );
663+ else
664+ * amt = AMOUNT_MSAT (0 );
629665 }
630666 return NULL ;
631667}
@@ -763,6 +799,7 @@ static struct command_result *listoffers_done(struct command *cmd,
763799 bool active ;
764800 struct command_result * err ;
765801 struct amount_msat amt ;
802+ struct tlv_invoice_request_invreq_recurrence_cancel * cancel ;
766803
767804 /* BOLT #12:
768805 *
@@ -848,25 +885,29 @@ static struct command_result *listoffers_done(struct command *cmd,
848885 return fail_invreq (cmd , ir , "Offer expired" );
849886 }
850887
851- /* BOLT #12:
888+ /* BOLT-recurrence #12:
852889 * - if `offer_quantity_max` is present:
853- * - MUST reject the invoice request if there is no `invreq_quantity` field.
890+ * - MUST reject the invoice request if `invreq_recurrence_cancel`
891+ * is not present and there is no `invreq_quantity` field.
854892 * - if `offer_quantity_max` is non-zero:
855893 * - MUST reject the invoice request if `invreq_quantity` is zero, OR greater than
856894 * `offer_quantity_max`.
857895 * - otherwise:
858896 * - MUST reject the invoice request if there is an `invreq_quantity` field.
859897 */
860898 if (ir -> invreq -> offer_quantity_max ) {
861- err = invreq_must_have (cmd , ir , invreq_quantity );
862- if (err )
863- return err ;
899+ if (!ir -> invreq -> invreq_recurrence_cancel ) {
900+ err = invreq_must_have (cmd , ir , invreq_quantity );
901+ if (err )
902+ return err ;
903+ }
864904
865- if (* ir -> invreq -> invreq_quantity == 0 )
905+ if (ir -> invreq -> invreq_quantity && * ir -> invreq -> invreq_quantity == 0 )
866906 return fail_invreq (cmd , ir ,
867907 "quantity zero invalid" );
868908
869- if (* ir -> invreq -> offer_quantity_max &&
909+ if (ir -> invreq -> invreq_quantity &&
910+ * ir -> invreq -> offer_quantity_max &&
870911 * ir -> invreq -> invreq_quantity > * ir -> invreq -> offer_quantity_max ) {
871912 return fail_invreq (cmd , ir ,
872913 "quantity %" PRIu64 " > %" PRIu64 ,
@@ -910,13 +951,18 @@ static struct command_result *listoffers_done(struct command *cmd,
910951 * field.
911952 * - MUST reject the invoice request if there is a `invreq_recurrence_start`
912953 * field.
954+ * - MUST reject the invoice request if there is a `invreq_recurrence_cancel`
955+ * field.
913956 */
914957 err = invreq_must_not_have (cmd , ir , invreq_recurrence_counter );
915958 if (err )
916959 return err ;
917960 err = invreq_must_not_have (cmd , ir , invreq_recurrence_start );
918961 if (err )
919962 return err ;
963+ err = invreq_must_not_have (cmd , ir , invreq_recurrence_cancel );
964+ if (err )
965+ return err ;
920966 }
921967
922968 /* BOLT #12:
@@ -926,8 +972,12 @@ static struct command_result *listoffers_done(struct command *cmd,
926972 * - MUST copy all non-signature fields from the invoice request (including
927973 * unknown fields).
928974 */
975+ /* But "invreq_recurrence_cancel" doesn't exist in invoices, so temporarily remove */
976+ cancel = ir -> invreq -> invreq_recurrence_cancel ;
977+ ir -> invreq -> invreq_recurrence_cancel = NULL ;
929978 ir -> inv = invoice_for_invreq (cmd , ir -> invreq );
930979 assert (ir -> inv -> invreq_payer_id );
980+ ir -> invreq -> invreq_recurrence_cancel = cancel ;
931981
932982 /* BOLT #12:
933983 * - if `offer_issuer_id` is present:
0 commit comments