bolt12: import the latest spec, update to fit.

I know this is an unforgivably large diff, but the spec has changed so
much that most of this amounts to a rewrite.

Some points:
* We no longer have "offer_id" fields, we generate that locally, as all
  offer fields are mirrored into invoice_request and then invoice.
* Because of that mirroring, field names all have explicit offer/invreq/invoice
  prefixes.
* The `refund_for` fields have been removed from spec: will re-add locally later.
* quantity_min was removed, max == 0 now mean "must specify a quantity".
* I have put recurrence fields back in locally.

This brings us to 655df03d8729c0918bdacac99eb13fdb0ee93345 ("BOLT 12:
add explicit invoice_node_id.")

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell 2022-11-09 13:02:00 +10:30 committed by Christian Decker
parent 846a520bc2
commit 1e3cb01546
21 changed files with 1377 additions and 1379 deletions

View File

@ -11,9 +11,9 @@
#include <time.h>
/* If chains is NULL, max_num_chains is ignored */
static bool bolt12_chains_match(const struct bitcoin_blkid *chains,
size_t max_num_chains,
const struct chainparams *must_be_chain)
bool bolt12_chains_match(const struct bitcoin_blkid *chains,
size_t max_num_chains,
const struct chainparams *must_be_chain)
{
/* BOLT-offers #12:
* - if the chain for the invoice is not solely bitcoin:
@ -181,26 +181,13 @@ struct tlv_offer *offer_decode(const tal_t *ctx,
*fail = check_features_and_chain(ctx,
our_features, must_be_chain,
offer->features,
offer->offer_features,
BOLT12_OFFER_FEATURE,
offer->chains,
tal_count(offer->chains));
offer->offer_chains,
tal_count(offer->offer_chains));
if (*fail)
return tal_free(offer);
/* BOLT-offers #12:
* - if `signature` is present, but is not a valid signature using
* `node_id` as described in [Signature Calculation](#signature-calculation):
* - MUST NOT respond to the offer.
*/
if (offer->signature) {
*fail = check_signature(ctx, offer->fields,
"offer", "signature",
offer->node_id, offer->signature);
if (*fail)
return tal_free(offer);
}
return offer;
}
@ -230,15 +217,15 @@ struct tlv_invoice_request *invrequest_decode(const tal_t *ctx,
invrequest = fromwire_tlv_invoice_request(ctx, &data, &dlen);
if (!invrequest) {
*fail = tal_fmt(ctx, "invalid invoice_request data");
*fail = tal_fmt(ctx, "invalid invreq data");
return NULL;
}
*fail = check_features_and_chain(ctx,
our_features, must_be_chain,
invrequest->features,
invrequest->invreq_features,
BOLT12_INVREQ_FEATURE,
invrequest->chain, 1);
invrequest->invreq_chain, 1);
if (*fail)
return tal_free(invrequest);
@ -277,9 +264,9 @@ struct tlv_invoice *invoice_decode_nosig(const tal_t *ctx,
*fail = check_features_and_chain(ctx,
our_features, must_be_chain,
invoice->features,
invoice->invoice_features,
BOLT12_INVOICE_FEATURE,
invoice->chain, 1);
invoice->invreq_chain, 1);
if (*fail)
return tal_free(invoice);
@ -328,7 +315,7 @@ static u64 time_change(u64 prevstart, u32 number,
}
u64 offer_period_start(u64 basetime, size_t n,
const struct tlv_offer_recurrence *recur)
const struct recurrence *recur)
{
/* BOLT-offers-recurrence #12:
* 1. A `time_unit` defining 0 (seconds), 1 (days), 2 (months),
@ -349,9 +336,9 @@ u64 offer_period_start(u64 basetime, size_t n,
}
}
void offer_period_paywindow(const struct tlv_offer_recurrence *recurrence,
const struct tlv_offer_recurrence_paywindow *recurrence_paywindow,
const struct tlv_offer_recurrence_base *recurrence_base,
void offer_period_paywindow(const struct recurrence *recurrence,
const struct recurrence_paywindow *recurrence_paywindow,
const struct recurrence_base *recurrence_base,
u64 basetime, u64 period_idx,
u64 *start, u64 *end)
{
@ -364,9 +351,9 @@ void offer_period_paywindow(const struct tlv_offer_recurrence *recurrence,
/* BOLT-offers-recurrence #12:
* - if the offer has a `recurrence_basetime` or the
* `recurrence_counter` is non-zero:
* - SHOULD NOT send an `invoice_request` for a period prior to
* - SHOULD NOT send an `invreq` for a period prior to
* `seconds_before` seconds before that period start.
* - SHOULD NOT send an `invoice_request` for a period later
* - SHOULD NOT send an `invreq` for a period later
* than `seconds_after` seconds past that period start.
*/
*start = pstart - recurrence_paywindow->seconds_before;
@ -381,7 +368,7 @@ void offer_period_paywindow(const struct tlv_offer_recurrence *recurrence,
} else {
/* BOLT-offers-recurrence #12:
* - otherwise:
* - SHOULD NOT send an `invoice_request` with
* - SHOULD NOT send an `invreq` with
* `recurrence_counter` is non-zero for a period whose
* immediate predecessor has not yet begun.
*/
@ -392,7 +379,7 @@ void offer_period_paywindow(const struct tlv_offer_recurrence *recurrence,
recurrence);
/* BOLT-offers-recurrence #12:
* - SHOULD NOT send an `invoice_request` for a period which
* - SHOULD NOT send an `invreq` for a period which
* has already passed.
*/
*end = offer_period_start(basetime, period_idx+1,
@ -413,7 +400,8 @@ struct tlv_invoice *invoice_decode(const tal_t *ctx,
if (invoice) {
*fail = check_signature(ctx, invoice->fields,
"invoice", "signature",
invoice->node_id, invoice->signature);
invoice->invoice_node_id,
invoice->signature);
if (*fail)
invoice = tal_free(invoice);
}

View File

@ -49,13 +49,13 @@ char *invrequest_encode(const tal_t *ctx,
/**
* invrequest_decode - decode this complete bolt12 text into a TLV.
* @ctx: the context to allocate return or *@fail off.
* @b12: the invoice_request string
* @b12len: the invoice_request string length
* @b12: the invreq string
* @b12len: the invreq string length
* @our_features: if non-NULL, feature set to check against.
* @must_be_chain: if non-NULL, chain to enforce.
* @fail: pointer to descriptive error string, set if this returns NULL.
*
* Note: invoice_request doesn't always have a signature, so no checking is done!
* Note: invreq doesn't always have a signature, so no checking is done!
*/
struct tlv_invoice_request *invrequest_decode(const tal_t *ctx,
const char *b12, size_t b12len,
@ -103,14 +103,20 @@ bool bolt12_check_signature(const struct tlv_field *fields,
bool bolt12_chain_matches(const struct bitcoin_blkid *chain,
const struct chainparams *must_be_chain);
/* Given an array of max_num_chains chains (or NULL == bitcoin), does
* it match? */
bool bolt12_chains_match(const struct bitcoin_blkid *chains,
size_t max_num_chains,
const struct chainparams *must_be_chain);
/* Given a basetime, when does period N start? */
u64 offer_period_start(u64 basetime, size_t n,
const struct tlv_offer_recurrence *recurrence);
const struct recurrence *recurrence);
/* Get the start and end of the payment window for period N. */
void offer_period_paywindow(const struct tlv_offer_recurrence *recurrence,
const struct tlv_offer_recurrence_paywindow *recurrence_paywindow,
const struct tlv_offer_recurrence_base *recurrence_base,
void offer_period_paywindow(const struct recurrence *recurrence,
const struct recurrence_paywindow *recurrence_paywindow,
const struct recurrence_base *recurrence_base,
u64 basetime, u64 period_idx,
u64 *period_start, u64 *period_end);

View File

@ -192,7 +192,7 @@ int main(int argc, char *argv[])
/* We deal in UTC; mktime() uses local time */
setenv("TZ", "", 1);
json_for_each_arr(i, t, toks) {
struct tlv_offer_recurrence recurrence;
struct recurrence recurrence;
int unit;
const jsmntok_t *exp;
u64 base, secs, n;

View File

@ -157,20 +157,15 @@ static void print_node_id(const struct pubkey *node_id)
printf("node_id: %s\n", type_to_string(tmpctx, struct pubkey, node_id));
}
static void print_quantity_min(u64 min)
{
printf("quantity_min: %"PRIu64"\n", min);
}
static void print_quantity_max(u64 max)
{
printf("quantity_max: %"PRIu64"\n", max);
}
static bool print_recurrance(const struct tlv_offer_recurrence *recurrence,
const struct tlv_offer_recurrence_paywindow *paywindow,
static bool print_recurrance(const struct recurrence *recurrence,
const struct recurrence_paywindow *paywindow,
const u32 *limit,
const struct tlv_offer_recurrence_base *base)
const struct recurrence_base *base)
{
const char *unit;
bool ok = true;
@ -293,17 +288,6 @@ static bool print_blindedpaths(struct blinded_path **paths,
return true;
}
static void print_send_invoice(void)
{
printf("send_invoice\n");
}
static void print_refund_for(const struct sha256 *payment_hash)
{
printf("refund_for: %s\n",
type_to_string(tmpctx, struct sha256, payment_hash));
}
static bool print_signature(const char *messagename,
const char *fieldname,
const struct tlv_field *fields,
@ -328,12 +312,6 @@ static bool print_signature(const char *messagename,
return true;
}
static void print_offer_id(const struct sha256 *offer_id)
{
printf("offer_id: %s\n",
type_to_string(tmpctx, struct sha256, offer_id));
}
static void print_quantity(u64 q)
{
printf("quantity: %"PRIu64"\n", q);
@ -363,13 +341,14 @@ static bool print_recurrence_counter_with_base(const u32 *recurrence_counter,
return true;
}
static void print_payer_key(const struct pubkey *payer_key,
const u8 *payer_info)
static void print_payer_id(const struct pubkey *payer_id,
const u8 *metadata)
{
printf("payer_key: %s",
type_to_string(tmpctx, struct pubkey, payer_key));
if (payer_info)
printf(" (payer_info %s)", tal_hex(tmpctx, payer_info));
printf("invreq_payer_id: %s",
type_to_string(tmpctx, struct pubkey, payer_id));
if (metadata)
printf(" (invreq_metadata %s)",
tal_hex(tmpctx, metadata));
printf("\n");
}
@ -391,11 +370,6 @@ static void print_payment_hash(const struct sha256 *payment_hash)
type_to_string(tmpctx, struct sha256, payment_hash));
}
static void print_cltv(u32 cltv)
{
printf("min_final_cltv_expiry: %u\n", cltv);
}
static void print_relative_expiry(u64 *created_at, u32 *relative)
{
/* Ignore if already malformed */
@ -553,42 +527,31 @@ int main(int argc, char *argv[])
if (!offer)
errx(ERROR_BAD_DECODE, "Bad offer: %s", fail);
if (offer->send_invoice)
print_send_invoice();
if (offer->chains)
print_chains(offer->chains);
if (offer->refund_for)
print_refund_for(offer->refund_for);
if (offer->amount)
well_formed &= print_amount(offer->chains,
offer->currency,
*offer->amount);
if (must_have(offer, description))
print_description(offer->description);
if (offer->issuer)
print_issuer(offer->issuer);
if (must_have(offer, node_id))
print_node_id(offer->node_id);
if (offer->quantity_min)
print_quantity_min(*offer->quantity_min);
if (offer->quantity_max)
print_quantity_max(*offer->quantity_max);
if (offer->recurrence)
well_formed &= print_recurrance(offer->recurrence,
offer->recurrence_paywindow,
offer->recurrence_limit,
offer->recurrence_base);
if (offer->absolute_expiry)
print_absolute_expiry(*offer->absolute_expiry);
if (offer->features)
print_features(offer->features);
if (offer->paths)
print_blindedpaths(offer->paths, NULL);
if (offer->signature && offer->node_id)
well_formed &= print_signature("offer", "signature",
offer->fields,
offer->node_id,
offer->signature);
if (offer->offer_chains)
print_chains(offer->offer_chains);
if (offer->offer_amount)
well_formed &= print_amount(offer->offer_chains,
offer->offer_currency,
*offer->offer_amount);
if (must_have(offer, offer_description))
print_description(offer->offer_description);
if (offer->offer_issuer)
print_issuer(offer->offer_issuer);
if (must_have(offer, offer_node_id))
print_node_id(offer->offer_node_id);
if (offer->offer_quantity_max)
print_quantity_max(*offer->offer_quantity_max);
if (offer->offer_recurrence)
well_formed &= print_recurrance(offer->offer_recurrence,
offer->offer_recurrence_paywindow,
offer->offer_recurrence_limit,
offer->offer_recurrence_base);
if (offer->offer_absolute_expiry)
print_absolute_expiry(*offer->offer_absolute_expiry);
if (offer->offer_features)
print_features(offer->offer_features);
if (offer->offer_paths)
print_blindedpaths(offer->offer_paths, NULL);
if (!print_extra_fields(offer->fields))
well_formed = false;
} else if (streq(hrp, "lnr")) {
@ -596,43 +559,35 @@ int main(int argc, char *argv[])
= invrequest_decode(ctx, argv[2], strlen(argv[2]),
NULL, NULL, &fail);
if (!invreq)
errx(ERROR_BAD_DECODE, "Bad invoice_request: %s", fail);
errx(ERROR_BAD_DECODE, "Bad invreq: %s", fail);
if (invreq->chain)
print_chain(invreq->chain);
if (must_have(invreq, payer_key))
print_payer_key(invreq->payer_key, invreq->payer_info);
if (invreq->payer_note)
print_payer_note(invreq->payer_note);
if (must_have(invreq, offer_id))
print_offer_id(invreq->offer_id);
if (must_have(invreq, amount))
well_formed &= print_amount(invreq->chain,
if (invreq->invreq_chain)
print_chain(invreq->invreq_chain);
if (must_have(invreq, invreq_payer_id))
print_payer_id(invreq->invreq_payer_id,
invreq->invreq_metadata);
if (invreq->invreq_payer_note)
print_payer_note(invreq->invreq_payer_note);
if (must_have(invreq, invreq_amount))
well_formed &= print_amount(invreq->invreq_chain,
NULL,
*invreq->amount);
if (invreq->features)
print_features(invreq->features);
if (invreq->quantity)
print_quantity(*invreq->quantity);
*invreq->invreq_amount);
if (invreq->invreq_features)
print_features(invreq->invreq_features);
if (invreq->invreq_quantity)
print_quantity(*invreq->invreq_quantity);
if (must_have(invreq, signature)) {
if (!print_signature("invoice_request",
"signature",
invreq->fields,
invreq->payer_key,
invreq->signature)) {
/* FIXME: We temporarily allow the old "payer_signature" name */
well_formed &= print_signature("invoice_request",
"payer_signature",
invreq->fields,
invreq->payer_key,
invreq->signature);
}
well_formed = print_signature("invoice_request",
"signature",
invreq->fields,
invreq->invreq_payer_id,
invreq->signature);
}
if (invreq->recurrence_counter) {
print_recurrence_counter(invreq->recurrence_counter,
invreq->recurrence_start);
if (invreq->invreq_recurrence_counter) {
print_recurrence_counter(invreq->invreq_recurrence_counter,
invreq->invreq_recurrence_start);
} else {
must_not_have(invreq, recurrence_start);
must_not_have(invreq, invreq_recurrence_start);
}
if (!print_extra_fields(invreq->fields))
well_formed = false;
@ -643,70 +598,55 @@ int main(int argc, char *argv[])
if (!invoice)
errx(ERROR_BAD_DECODE, "Bad invoice: %s", fail);
if (invoice->chain)
print_chain(invoice->chain);
if (invoice->invreq_chain)
print_chain(invoice->invreq_chain);
if (invoice->offer_id) {
print_offer_id(invoice->offer_id);
}
if (must_have(invoice, amount))
well_formed &= print_amount(invoice->chain,
if (must_have(invoice, invoice_amount))
well_formed &= print_amount(invoice->invreq_chain,
NULL,
*invoice->amount);
if (must_have(invoice, description))
print_description(invoice->description);
if (invoice->features)
print_features(invoice->features);
if (invoice->paths) {
must_have(invoice, blindedpay);
well_formed &= print_blindedpaths(invoice->paths,
invoice->blindedpay);
*invoice->invoice_amount);
if (must_have(invoice, offer_description))
print_description(invoice->offer_description);
if (invoice->invoice_features)
print_features(invoice->invoice_features);
if (invoice->invoice_paths) {
must_have(invoice, invoice_blindedpay);
well_formed &= print_blindedpaths(invoice->invoice_paths,
invoice->invoice_blindedpay);
} else
must_not_have(invoice, blindedpay);
if (invoice->issuer)
print_issuer(invoice->issuer);
if (must_have(invoice, node_id))
print_node_id(invoice->node_id);
if (invoice->quantity)
print_quantity(*invoice->quantity);
if (invoice->refund_for) {
print_refund_for(invoice->refund_for);
if (must_have(invoice, refund_signature))
well_formed &= print_signature("invoice",
"refund_signature",
invoice->fields,
invoice->payer_key,
invoice->refund_signature);
} else {
must_not_have(invoice, refund_signature);
}
if (invoice->recurrence_counter) {
must_not_have(invoice, invoice_blindedpay);
if (invoice->offer_issuer)
print_issuer(invoice->offer_issuer);
if (must_have(invoice, offer_node_id))
print_node_id(invoice->offer_node_id);
if (invoice->invreq_quantity)
print_quantity(*invoice->invreq_quantity);
if (invoice->invreq_recurrence_counter) {
well_formed &=
print_recurrence_counter_with_base(invoice->recurrence_counter,
invoice->recurrence_start,
invoice->recurrence_basetime);
print_recurrence_counter_with_base(invoice->invreq_recurrence_counter,
invoice->invreq_recurrence_start,
invoice->invoice_recurrence_basetime);
} else {
must_not_have(invoice, recurrence_start);
must_not_have(invoice, recurrence_basetime);
must_not_have(invoice, invreq_recurrence_start);
must_not_have(invoice, invoice_recurrence_basetime);
}
if (must_have(invoice, payer_key))
print_payer_key(invoice->payer_key, invoice->payer_info);
if (must_have(invoice, created_at))
print_created_at(*invoice->created_at);
if (invoice->payer_note)
print_payer_note(invoice->payer_note);
print_relative_expiry(invoice->created_at,
invoice->relative_expiry);
if (must_have(invoice, payment_hash))
print_payment_hash(invoice->payment_hash);
if (must_have(invoice, cltv))
print_cltv(*invoice->cltv);
if (invoice->fallbacks)
print_fallbacks(invoice->fallbacks);
if (must_have(invoice, invreq_payer_id))
print_payer_id(invoice->invreq_payer_id,
invoice->invreq_metadata);
if (must_have(invoice, invoice_created_at))
print_created_at(*invoice->invoice_created_at);
if (invoice->invreq_payer_note)
print_payer_note(invoice->invreq_payer_note);
print_relative_expiry(invoice->invoice_created_at,
invoice->invoice_relative_expiry);
if (must_have(invoice, invoice_payment_hash))
print_payment_hash(invoice->invoice_payment_hash);
if (invoice->invoice_fallbacks)
print_fallbacks(invoice->invoice_fallbacks);
if (must_have(invoice, signature))
well_formed &= print_signature("invoice", "signature",
invoice->fields,
invoice->node_id,
invoice->offer_node_id,
invoice->signature);
if (!print_extra_fields(invoice->fields))
well_formed = false;

View File

@ -50,8 +50,7 @@ If **type** is "bolt12 offer", and **valid** is *true*:
- **path** (array of objects): an individual path:
- **blinded\_node\_id** (pubkey): node_id of the hop
- **encrypted\_recipient\_data** (hex): encrypted TLV entry for this hop
- **quantity\_min** (u64, optional): the minimum quantity
- **quantity\_max** (u64, optional): the maximum quantity
- **quantity\_max** (u64, optional): the maximum quantity (or, if 0, means any quantity)
- **recurrence** (object, optional): how often to this offer should be used:
- **time\_unit** (u32): the BOLT12 time unit
- **period** (u32): how many *time_unit* per payment period
@ -80,7 +79,6 @@ If **type** is "bolt12 invoice", and **valid** is *true*:
- **created\_at** (u64): the UNIX timestamp of invoice creation
- **payment\_hash** (hex): the hash of the *payment_preimage* (always 64 characters)
- **relative\_expiry** (u32): the number of seconds after *created_at* when this expires
- **min\_final\_cltv\_expiry** (u32): the number of blocks required by destination
- **offer\_id** (hex, optional): the id of this offer (merkle hash of non-signature fields) (always 64 characters)
- **chain** (hex, optional): which blockchain this invoice is for (missing implies bitcoin mainnet only) (always 64 characters)
- **send\_invoice** (boolean, optional): present if this offer was a send_invoice offer (always *true*)
@ -102,7 +100,7 @@ If **type** is "bolt12 invoice", and **valid** is *true*:
- **recurrence\_start** (u32, optional): the optional start period for a recurring payment
- **recurrence\_basetime** (u32, optional): the UNIX timestamp of the first recurrence period start
- **payer\_key** (pubkey, optional): the transient key which identifies the payer
- **payer\_info** (hex, optional): the payer-provided blob to derive payer_key
- **invreq\_metadata** (hex, optional): the payer-provided blob to derive payer_key
- **fallbacks** (array of objects, optional): onchain addresses:
- **version** (u8): Segwit address version
- **hex** (hex): Raw encoded segwit address
@ -136,7 +134,7 @@ If **type** is "bolt12 invoice_request", and **valid** is *true*:
- **quantity** (u64, optional): the quantity ordered
- **recurrence\_counter** (u32, optional): the 0-based counter for a recurring payment
- **recurrence\_start** (u32, optional): the optional start period for a recurring payment
- **payer\_info** (hex, optional): the payer-provided blob to derive payer_key
- **invreq\_metadata** (hex, optional): the payer-provided blob to derive payer_key
- **recurrence\_signature** (bip340sig, optional): the payer key signature
If **type** is "bolt12 invoice_request", and **valid** is *false*:
@ -217,4 +215,4 @@ RESOURCES
Main web site: <https://github.com/ElementsProject/lightning>
[comment]: # ( SHA256STAMP:bbe57fd87e729e1203055d983a72757b9647ea67dca23c254a05b38b7b7020d9)
[comment]: # ( SHA256STAMP:3f0c78dd665bff6749352801b0fdd958ee138fda6ede5b3631d9cb136fc45d76)

View File

@ -6,7 +6,7 @@ SYNOPSIS
**(WARNING: experimental-offers only)**
**offer** *amount* *description* [*issuer*] [*label*] [*quantity_min*] [*quantity_max*] [*absolute_expiry*] [*recurrence*] [*recurrence_base*] [*recurrence_paywindow*] [*recurrence_limit*] [*single_use*]
**offer** *amount* *description* [*issuer*] [*label*] [*quantity_max*] [*absolute_expiry*] [*recurrence*] [*recurrence_base*] [*recurrence_paywindow*] [*recurrence_limit*] [*single_use*]
DESCRIPTION
-----------
@ -43,10 +43,11 @@ reflects who is issuing this offer (i.e. you) if appropriate.
The *label* field is an internal-use name for the offer, which can
be any UTF-8 string.
The present of *quantity_min* or *quantity_max* indicates that the
invoice can specify more than one of the items within this (inclusive)
range. The *amount* for the invoice will need to be multiplied
accordingly. These are encoded in the offer.
The presence of *quantity_max* indicates that the
invoice can specify more than one of the items up (and including)
this maximum: 0 is a special value meaning "no maximuim".
The *amount* for the invoice will need to be multiplied
accordingly. This is encoded in the offer.
The *absolute_expiry* is optionally the time the offer is valid until,
in seconds since the first day of 1970 UTC. If not set, the offer

View File

@ -170,13 +170,9 @@
}
}
},
"quantity_min": {
"type": "u64",
"description": "the minimum quantity"
},
"quantity_max": {
"type": "u64",
"description": "the maximum quantity"
"description": "the maximum quantity (or, if 0, means any quantity)"
},
"recurrence": {
"type": "object",
@ -281,7 +277,6 @@
"features": {},
"absolute_expiry": {},
"paths": {},
"quantity_min": {},
"quantity_max": {},
"recurrence": {},
"warning_offer_missing_description": {
@ -316,8 +311,7 @@
"description",
"created_at",
"payment_hash",
"relative_expiry",
"min_final_cltv_expiry"
"relative_expiry"
],
"additionalProperties": false,
"properties": {
@ -453,7 +447,7 @@
"type": "pubkey",
"description": "the transient key which identifies the payer"
},
"payer_info": {
"invreq_metadata": {
"type": "hex",
"description": "the payer-provided blob to derive payer_key"
},
@ -474,10 +468,6 @@
"type": "u32",
"description": "the number of seconds after *created_at* when this expires"
},
"min_final_cltv_expiry": {
"type": "u32",
"description": "the number of blocks required by destination"
},
"fallbacks": {
"type": "array",
"description": "onchain addresses",
@ -550,12 +540,11 @@
"recurrence_start": {},
"recurrence_basetime": {},
"payer_key": {},
"payer_info": {},
"invreq_metadata": {},
"timestamp": {},
"created_at": {},
"payment_hash": {},
"relative_expiry": {},
"min_final_cltv_expiry": {},
"fallbacks": {
"type": "array",
"items": {
@ -681,7 +670,7 @@
"type": "pubkey",
"description": "the transient key which identifies the payer"
},
"payer_info": {
"invreq_metadata": {
"type": "hex",
"description": "the payer-provided blob to derive payer_key"
},
@ -723,7 +712,7 @@
"recurrence_counter": {},
"recurrence_start": {},
"payer_key": {},
"payer_info": {},
"invreq_metadata": {},
"recurrence_signature": {},
"warning_invoice_request_missing_offer_id": {
"type": "string",

View File

@ -70,10 +70,11 @@ static void json_add_invoice_fields(struct json_stream *response,
tinv = invoice_decode(tmpctx,
inv->invstring, strlen(inv->invstring),
NULL, NULL, &fail);
if (tinv && tinv->payer_note)
/* FIXME-OFFERS: Rename all fields to offer_ as per spec */
if (tinv && tinv->invreq_payer_note)
json_add_stringn(response, "payer_note",
tinv->payer_note,
tal_bytelen(tinv->payer_note));
tinv->invreq_payer_note,
tal_bytelen(tinv->invreq_payer_note));
}
}
@ -1352,11 +1353,11 @@ static struct command_result *json_listinvoices(struct command *cmd,
strlen(invstring),
cmd->ld->our_features, NULL,
&fail);
if (!b12 || !b12->payment_hash) {
if (!b12 || !b12->invoice_payment_hash) {
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"Invalid invstring");
}
payment_hash = b12->payment_hash;
payment_hash = b12->invoice_payment_hash;
}
}
@ -1658,7 +1659,7 @@ static struct tlv_invoice *add_stub_blindedpath(const tal_t *ctx,
tlv = tlv_encrypted_data_tlv_new(tmpctx);
tlv->path_id = invoice_path_id(inv,
&ld->invoicesecret_base,
inv->payment_hash);
inv->invoice_payment_hash);
path->path[0]->encrypted_recipient_data
= encrypt_tlv_encrypted_data(path->path[0],
@ -1668,22 +1669,23 @@ static struct tlv_invoice *add_stub_blindedpath(const tal_t *ctx,
NULL,
&path->path[0]->blinded_node_id);
inv->paths = tal_arr(inv, struct blinded_path *, 1);
inv->paths[0] = tal_steal(inv->paths, path);
inv->invoice_paths = tal_arr(inv, struct blinded_path *, 1);
inv->invoice_paths[0] = tal_steal(inv->invoice_paths, path);
/* BOLT-offers #12:
* - MUST include `invoice_paths` containing one or more paths to the node.
* - MUST specify `invoice_paths` in order of most-preferred to least-preferred if it has a preference.
* - MUST include `invoice_blindedpay` with exactly one `blinded_payinfo` for each `blinded_path` in `paths`, in order.
*/
inv->blindedpay = tal_arr(inv, struct blinded_payinfo *, 1);
inv->blindedpay[0] = tal(inv->blindedpay, struct blinded_payinfo);
inv->blindedpay[0]->fee_base_msat = 0;
inv->blindedpay[0]->fee_proportional_millionths = 0;
inv->blindedpay[0]->cltv_expiry_delta = ld->config.cltv_final;
inv->blindedpay[0]->htlc_minimum_msat = AMOUNT_MSAT(0);
inv->blindedpay[0]->htlc_maximum_msat = AMOUNT_MSAT(21000000 * MSAT_PER_BTC);
inv->blindedpay[0]->features = NULL;
inv->invoice_blindedpay = tal_arr(inv, struct blinded_payinfo *, 1);
inv->invoice_blindedpay[0] = tal(inv->invoice_blindedpay,
struct blinded_payinfo);
inv->invoice_blindedpay[0]->fee_base_msat = 0;
inv->invoice_blindedpay[0]->fee_proportional_millionths = 0;
inv->invoice_blindedpay[0]->cltv_expiry_delta = ld->config.cltv_final;
inv->invoice_blindedpay[0]->htlc_minimum_msat = AMOUNT_MSAT(0);
inv->invoice_blindedpay[0]->htlc_maximum_msat = AMOUNT_MSAT(21000000 * MSAT_PER_BTC);
inv->invoice_blindedpay[0]->features = NULL;
/* But we need to update ->fields, so re-linearize */
wire = tal_arr(tmpctx, u8, 0);
@ -1756,7 +1758,7 @@ static struct command_result *json_createinvoice(struct command *cmd,
notify_invoice_creation(cmd->ld, b11->msat, *preimage, label);
} else {
struct tlv_invoice *inv;
struct sha256 *local_offer_id;
struct sha256 offer_id, *local_offer_id;
char *b12enc;
struct amount_msat msat;
const char *desc;
@ -1771,10 +1773,16 @@ static struct command_result *json_createinvoice(struct command *cmd,
"Unparsable invoice '%s': %s",
invstring, fail);
/* BOLT-offers #12:
* A writer of an invoice:
*...
* - MUST include `invoice_paths` containing one or more paths
* to the node.
*/
/* If they don't create a blinded path, add a simple one so we
* can recognize payments (bolt12 doesn't use
* payment_secret) */
if (!inv->paths)
if (!inv->invoice_paths)
inv = add_stub_blindedpath(cmd, cmd->ld, inv);
if (inv->signature)
@ -1783,54 +1791,65 @@ static struct command_result *json_createinvoice(struct command *cmd,
hsm_sign_b12_invoice(cmd->ld, inv);
b12enc = invoice_encode(cmd, inv);
if (inv->offer_id
&& wallet_offer_find(tmpctx, cmd->ld->wallet,
inv->offer_id, NULL, &status)) {
if (!offer_status_active(status))
return command_fail(cmd, INVOICE_OFFER_INACTIVE,
"offer not active");
local_offer_id = inv->offer_id;
if (inv->offer_node_id) {
invoice_offer_id(inv, &offer_id);
if (wallet_offer_find(tmpctx, cmd->ld->wallet,
&offer_id, NULL, &status)) {
if (!offer_status_active(status))
return command_fail(cmd,
INVOICE_OFFER_INACTIVE,
"offer not active");
local_offer_id = &offer_id;
} else
local_offer_id = NULL;
} else
local_offer_id = NULL;
if (inv->amount)
msat = amount_msat(*inv->amount);
/* BOLT-offers #12:
* A writer of an invoice:
*...
* - MUST set `invoice_amount` to the minimum amount it will
* accept, in units of the minimal lightning-payable unit
* (e.g. milli-satoshis for bitcoin) for `invreq_chain`.
*/
if (!inv->invoice_amount)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"Missing invoice_amount in invoice");
msat = amount_msat(*inv->invoice_amount);
if (inv->relative_expiry)
expiry = *inv->relative_expiry;
if (inv->invoice_relative_expiry)
expiry = *inv->invoice_relative_expiry;
else
expiry = BOLT12_DEFAULT_REL_EXPIRY;
if (!inv->payment_hash)
if (!inv->invoice_payment_hash)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"Missing payment_hash in invoice");
if (!sha256_eq(&payment_hash, inv->payment_hash))
if (!sha256_eq(&payment_hash, inv->invoice_payment_hash))
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"Incorrect preimage");
if (!inv->description)
if (!inv->offer_description)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"Missing description in invoice");
desc = tal_strndup(cmd,
cast_signed(char *, inv->description),
tal_bytelen(inv->description));
inv->offer_description,
tal_bytelen(inv->offer_description));
if (!wallet_invoice_create(cmd->ld->wallet,
&invoice,
inv->amount ? &msat : NULL,
&msat,
label,
expiry,
b12enc,
desc,
inv->features,
inv->invoice_features,
preimage,
&payment_hash,
local_offer_id))
return fail_exists(cmd, label);
notify_invoice_creation(cmd->ld,
inv->amount ? &msat : NULL,
*preimage, label);
notify_invoice_creation(cmd->ld, &msat, *preimage, label);
}
response = json_stream_success(cmd);

View File

@ -43,9 +43,6 @@ static struct command_result *param_b12_offer(struct command *cmd,
cmd->ld->our_features, chainparams, &fail);
if (!*offer)
return command_fail_badparam(cmd, name, buffer, tok, fail);
if ((*offer)->signature)
return command_fail_badparam(cmd, name, buffer, tok,
"must be unsigned offer");
return NULL;
}
@ -86,7 +83,7 @@ static struct command_result *json_createoffer(struct command *cmd,
struct json_stream *response;
struct json_escape *label;
struct tlv_offer *offer;
struct sha256 merkle;
struct sha256 offer_id;
const char *b12str;
bool *single_use;
enum offer_status status;
@ -103,15 +100,14 @@ static struct command_result *json_createoffer(struct command *cmd,
status = OFFER_SINGLE_USE_UNUSED;
else
status = OFFER_MULTIPLE_USE_UNUSED;
merkle_tlv(offer->fields, &merkle);
offer->signature = NULL;
b12str = offer_encode(cmd, offer);
offer_offer_id(offer, &offer_id);
/* If it already exists, we use that one instead (and then
* the offer plugin will complain if it's inactive or expired) */
if (!wallet_offer_create(cmd->ld->wallet, &merkle,
if (!wallet_offer_create(cmd->ld->wallet, &offer_id,
b12str, label, status)) {
if (!wallet_offer_find(cmd, cmd->ld->wallet, &merkle,
if (!wallet_offer_find(cmd, cmd->ld->wallet, &offer_id,
cast_const2(const struct json_escape **,
&label),
&status)) {
@ -122,10 +118,8 @@ static struct command_result *json_createoffer(struct command *cmd,
} else
created = true;
offer->signature = tal_free(offer->signature);
response = json_stream_success(cmd);
json_populate_offer(response, &merkle, b12str, label, status);
json_populate_offer(response, &offer_id, b12str, label, status);
json_add_bool(response, "created", created);
return command_success(cmd, response);
}
@ -240,7 +234,7 @@ static const struct json_command disableoffer_command = {
AUTODATA(json_command, &disableoffer_command);
/* We do some sanity checks now, since we're looking up prev payment anyway,
* but our main purpose is to fill in invreq->payer_info tweak. */
* but our main purpose is to fill in invreq->invreq_metadata tweak. */
static struct command_result *prev_payment(struct command *cmd,
const char *label,
struct tlv_invoice_request *invreq,
@ -248,13 +242,16 @@ static struct command_result *prev_payment(struct command *cmd,
{
const struct wallet_payment **payments;
bool prev_paid = false;
struct sha256 invreq_oid;
assert(!invreq->payer_info);
invreq_offer_id(invreq, &invreq_oid);
assert(!invreq->invreq_metadata);
payments = wallet_payment_list(cmd, cmd->ld->wallet, NULL);
for (size_t i = 0; i < tal_count(payments); i++) {
const struct tlv_invoice *inv;
char *fail;
struct sha256 inv_oid;
/* FIXME: Restrict db queries instead */
if (!payments[i]->label || !streq(label, payments[i]->label))
@ -270,12 +267,13 @@ static struct command_result *prev_payment(struct command *cmd,
continue;
/* They can reuse labels across different offers. */
if (!sha256_eq(inv->offer_id, invreq->offer_id))
invoice_offer_id(inv, &inv_oid);
if (!sha256_eq(&inv_oid, &invreq_oid))
continue;
/* Be paranoid, in case someone inserts their own
* clashing label! */
if (!inv->recurrence_counter)
if (!inv->invreq_recurrence_counter)
continue;
/* BOLT-offers-recurrence #12:
@ -287,40 +285,40 @@ static struct command_result *prev_payment(struct command *cmd,
* - MUST set `period_offset` to the same value on all
* following requests.
*/
if (invreq->recurrence_start) {
if (!inv->recurrence_start)
if (invreq->invreq_recurrence_start) {
if (!inv->invreq_recurrence_start)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"unexpected"
" recurrence_start");
if (*inv->recurrence_start != *invreq->recurrence_start)
if (*inv->invreq_recurrence_start != *invreq->invreq_recurrence_start)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"recurrence_start was"
" previously %u",
*inv->recurrence_start);
*inv->invreq_recurrence_start);
} else {
if (inv->recurrence_start)
if (inv->invreq_recurrence_start)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"missing"
" recurrence_start");
}
if (*inv->recurrence_counter == *invreq->recurrence_counter-1) {
if (*inv->invreq_recurrence_counter == *invreq->invreq_recurrence_counter-1) {
if (payments[i]->status == PAYMENT_COMPLETE)
prev_paid = true;
}
if (inv->payer_info) {
invreq->payer_info
= tal_dup_talarr(invreq, u8, inv->payer_info);
if (inv->invreq_metadata) {
invreq->invreq_metadata
= tal_dup_talarr(invreq, u8, inv->invreq_metadata);
*prev_basetime = tal_dup(cmd, u64,
inv->recurrence_basetime);
inv->invoice_recurrence_basetime);
}
if (prev_paid && inv->payer_info)
if (prev_paid && inv->invreq_metadata)
break;
}
if (!invreq->payer_info)
if (!invreq->invreq_metadata)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"No previous payment attempted for this"
" label and offer");
@ -347,13 +345,13 @@ static struct command_result *param_b12_invreq(struct command *cmd,
return command_fail_badparam(cmd, name, buffer, tok, fail);
#if !DEVELOPER
/* We use this for testing with known payer_info */
if ((*invreq)->payer_info)
if ((*invreq)->invreq_metadata)
return command_fail_badparam(cmd, name, buffer, tok,
"must not have payer_info");
"must not have invreq_metadata");
#endif
if ((*invreq)->payer_key)
if ((*invreq)->invreq_payer_id)
return command_fail_badparam(cmd, name, buffer, tok,
"must not have payer_key");
"must not have invreq_payer_id");
return NULL;
}
@ -389,12 +387,14 @@ static struct command_result *json_createinvoicerequest(struct command *cmd,
NULL))
return command_param_failed();
if (invreq->recurrence_counter) {
/* If it's a recurring payment, we look for previous to copy
* invreq_metadata, basetime */
if (invreq->invreq_recurrence_counter) {
if (!label)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"Need payment label for recurring payments");
if (*invreq->recurrence_counter != 0) {
if (*invreq->invreq_recurrence_counter != 0) {
struct command_result *err
= prev_payment(cmd, label, invreq,
&prev_basetime);
@ -403,25 +403,29 @@ static struct command_result *json_createinvoicerequest(struct command *cmd,
}
}
if (!invreq->payer_info) {
if (!invreq->invreq_metadata) {
/* BOLT-offers #12:
* `payer_info` might typically contain information about the
* derivation of the `payer_key`. This should not leak any
* information (such as using a simple BIP-32 derivation
* path); a valid system might be for a node to maintain a
* base payer key, and encode a 128-bit tweak here. The
* payer_key would be derived by tweaking the base key with
* SHA256(payer_base_pubkey || tweak).
*
* `invreq_metadata` might typically contain information about
* the derivation of the `invreq_payer_id`. This should not
* leak any information (such as using a simple BIP-32
* derivation path); a valid system might be for a node to
* maintain a base payer key and encode a 128-bit tweak here.
* The payer_id would be derived by tweaking the base key with
* SHA256(payer_base_pubkey || tweak). It's also the first
* entry (if present), ensuring an unpredictable nonce for
* hashing.
*/
invreq->payer_info = tal_arr(invreq, u8, 16);
randombytes_buf(invreq->payer_info,
tal_bytelen(invreq->payer_info));
invreq->invreq_metadata = tal_arr(invreq, u8, 16);
randombytes_buf(invreq->invreq_metadata,
tal_bytelen(invreq->invreq_metadata));
}
invreq->payer_key = tal(invreq, struct pubkey);
invreq->invreq_payer_id = tal(invreq, struct pubkey);
if (!payer_key(cmd->ld,
invreq->payer_info, tal_bytelen(invreq->payer_info),
invreq->payer_key)) {
invreq->invreq_metadata,
tal_bytelen(invreq->invreq_metadata),
invreq->invreq_payer_id)) {
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"Invalid tweak");
}
@ -435,7 +439,7 @@ static struct command_result *json_createinvoicerequest(struct command *cmd,
merkle_tlv(invreq->fields, &merkle);
invreq->signature = tal(invreq, struct bip340sig);
hsm_sign_b12(cmd->ld, "invoice_request", "signature",
&merkle, invreq->payer_info, invreq->payer_key,
&merkle, invreq->invreq_metadata, invreq->invreq_payer_id,
invreq->signature);
response = json_stream_success(cmd);

View File

@ -1592,8 +1592,8 @@ static struct command_result *json_listsendpays(struct command *cmd,
b12 = invoice_decode(cmd, invstring, strlen(invstring),
cmd->ld->our_features,
chainparams, &fail);
if (b12 && b12->payment_hash)
rhash = b12->payment_hash;
if (b12 && b12->invoice_payment_hash)
rhash = b12->invoice_payment_hash;
else
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"Invalid invstring: %s", fail);

View File

@ -315,6 +315,9 @@ struct tlv_invoice *invoice_decode_nosig(const tal_t *ctx UNNEEDED,
/* Generated stub for invoice_encode */
char *invoice_encode(const tal_t *ctx UNNEEDED, const struct tlv_invoice *bolt12_tlv UNNEEDED)
{ fprintf(stderr, "invoice_encode called!\n"); abort(); }
/* Generated stub for invoice_offer_id */
void invoice_offer_id(const struct tlv_invoice *invoice UNNEEDED, struct sha256 *id UNNEEDED)
{ fprintf(stderr, "invoice_offer_id called!\n"); abort(); }
/* Generated stub for invoice_path_id */
u8 *invoice_path_id(const tal_t *ctx UNNEEDED,
const struct secret *base_secret UNNEEDED,

View File

@ -1192,10 +1192,10 @@ static char *fetch_out_desc_invstr(const tal_t *ctx, const char *buf,
return NULL;
}
if (bolt12->description)
if (bolt12->offer_description)
desc = tal_strndup(ctx,
cast_signed(char *, bolt12->description),
tal_bytelen(bolt12->description));
cast_signed(char *, bolt12->offer_description),
tal_bytelen(bolt12->offer_description));
else
desc = NULL;
} else

View File

@ -59,37 +59,6 @@ static struct sent *find_sent_by_secret(const struct secret *pathsecret)
return NULL;
}
static const char *field_diff_(struct plugin *plugin,
const tal_t *a, const tal_t *b,
const char *fieldname)
{
/* One is set and the other isn't? */
if ((a == NULL) != (b == NULL)) {
plugin_log(plugin, LOG_DBG, "field_diff %s: a is %s, b is %s",
fieldname, a ? "set": "unset", b ? "set": "unset");
return fieldname;
}
if (!memeq(a, tal_bytelen(a), b, tal_bytelen(b))) {
plugin_log(plugin, LOG_DBG, "field_diff %s: a=%s, b=%s",
fieldname, tal_hex(tmpctx, a), tal_hex(tmpctx, b));
return fieldname;
}
return NULL;
}
#define field_diff(a, b, fieldname) \
field_diff_((cmd)->plugin, a->fieldname, b->fieldname, #fieldname)
/* Returns true if b is a with something appended. */
static bool description_is_appended(const char *a, const char *b)
{
if (!a || !b)
return false;
if (tal_bytelen(b) < tal_bytelen(a))
return false;
return memeq(a, tal_bytelen(a), b, tal_bytelen(a));
}
/* Hack to suppress warnings when we finish a different command */
static void discard_result(struct command_result *ret)
{
@ -155,12 +124,33 @@ static struct command_result *handle_error(struct command *cmd,
return command_hook_success(cmd);
}
/* BOLT-offers #12:
* - if the invoice is a response to an `invoice_request`:
* - MUST reject the invoice if all fields less than type 160 do not
* exactly match the `invoice_request`.
*/
static bool invoice_matches_request(struct command *cmd,
const u8 *invbin,
const struct tlv_invoice_request *invreq)
{
size_t len1, len2;
u8 *wire;
/* We linearize then strip signature. This is dumb! */
wire = tal_arr(tmpctx, u8, 0);
towire_tlv_invoice_request(&wire, invreq);
len1 = tlv_span(wire, 0, 159, NULL);
len2 = tlv_span(invbin, 0, 159, NULL);
return memeq(wire, len1, invbin, len2);
}
static struct command_result *handle_invreq_response(struct command *cmd,
struct sent *sent,
const char *buf,
const jsmntok_t *om)
{
const u8 *invbin;
const u8 *invbin, *cursor;
const jsmntok_t *invtok;
size_t len;
struct tlv_invoice *inv;
@ -184,8 +174,9 @@ static struct command_result *handle_invreq_response(struct command *cmd,
}
invbin = json_tok_bin_from_hex(cmd, buf, invtok);
cursor = invbin;
len = tal_bytelen(invbin);
inv = fromwire_tlv_invoice(cmd, &invbin, &len);
inv = fromwire_tlv_invoice(cmd, &cursor, &len);
if (!inv) {
badfield = "invoice";
goto badinv;
@ -202,88 +193,64 @@ static struct command_result *handle_invreq_response(struct command *cmd,
#endif /* DEVELOPER */
/* BOLT-offers #12:
* - MUST reject the invoice unless `node_id` is equal to the offer.
* - if the invoice is a response to an `invoice_request`:
* - MUST reject the invoice if all fields less than type 160 do not
* exactly match the `invoice_request`.
*/
if (!pubkey_eq(sent->offer->node_id, inv->node_id)) {
badfield = "node_id";
if (!invoice_matches_request(cmd, invbin, sent->invreq)) {
badfield = "invoice_request match";
goto badinv;
}
/* BOLT-offers #12:
* - if `offer_node_id` is present (invoice_request for an offer):
* - MUST reject the invoice if `invoice_node_id` is not equal to `offer_node_id`.
*/
if (!inv->invoice_node_id || !pubkey_eq(inv->offer_node_id, inv->invoice_node_id)) {
badfield = "invoice_node_id";
goto badinv;
}
/* BOLT-offers #12:
* - MUST reject the invoice if `signature` is not a valid signature
* using `node_id` as described in [Signature Calculation]
* using `invoice_node_id` as described in [Signature Calculation]
*/
merkle_tlv(inv->fields, &merkle);
sighash_from_merkle("invoice", "signature", &merkle, &sighash);
if (!inv->signature
|| !check_schnorr_sig(&sighash, &inv->node_id->pubkey, inv->signature)) {
|| !check_schnorr_sig(&sighash, &inv->invoice_node_id->pubkey, inv->signature)) {
badfield = "signature";
goto badinv;
}
/* BOLT-offers #12:
* - MUST reject the invoice if `msat` is not present.
* A reader of an invoice:
* - MUST reject the invoice if `invoice_amount` is not present.
*/
if (!inv->amount) {
badfield = "amount";
if (!inv->invoice_amount) {
badfield = "invoice_amount";
goto badinv;
}
/* BOLT-offers #12:
* - MUST reject the invoice unless `offer_id` is equal to the id of the
* offer.
*/
if ((badfield = field_diff(sent->invreq, inv, offer_id)))
goto badinv;
/* BOLT-offers #12:
* - if the invoice is a reply to an `invoice_request`:
*...
* - MUST reject the invoice unless the following fields are equal or
* unset exactly as they are in the `invoice_request:`
* - `quantity`
* - `payer_key`
* - `payer_info`
*/
/* BOLT-offers-recurrence #12:
* - if the invoice is a reply to an `invoice_request`:
*...
* - MUST reject the invoice unless the following fields are equal or
* unset exactly as they are in the `invoice_request:`
* - `quantity`
* - `recurrence_counter`
* - `recurrence_start`
* - `payer_key`
* - `payer_info`
*/
if ((badfield = field_diff(sent->invreq, inv, quantity)))
goto badinv;
if ((badfield = field_diff(sent->invreq, inv, recurrence_counter)))
goto badinv;
if ((badfield = field_diff(sent->invreq, inv, recurrence_start)))
goto badinv;
if ((badfield = field_diff(sent->invreq, inv, payer_key)))
goto badinv;
if ((badfield = field_diff(sent->invreq, inv, payer_info)))
goto badinv;
/* FIXME-OFFERS: Examine fields in inv directly! */
/* Get the amount we expected: firstly, if that's what we sent,
* secondly, if specified in the invoice. */
if (sent->invreq->amount) {
expected_amount = tal_dup(tmpctx, u64, sent->invreq->amount);
} else if (sent->offer->amount && !sent->offer->currency) {
if (sent->invreq->invreq_amount) {
expected_amount = tal_dup(tmpctx, u64, sent->invreq->invreq_amount);
} else if (sent->offer->offer_amount && !sent->offer->offer_currency) {
expected_amount = tal(tmpctx, u64);
*expected_amount = *sent->offer->amount;
if (sent->invreq->quantity) {
*expected_amount = *sent->offer->offer_amount;
if (sent->invreq->invreq_quantity) {
/* We should never have sent this! */
if (mul_overflows_u64(*expected_amount,
*sent->invreq->quantity)) {
*sent->invreq->invreq_quantity)) {
badfield = "quantity overflow";
goto badinv;
}
*expected_amount *= *sent->invreq->quantity;
*expected_amount *= *sent->invreq->invreq_quantity;
}
} else
expected_amount = NULL;
@ -292,97 +259,56 @@ static struct command_result *handle_invreq_response(struct command *cmd,
* - if the offer contained `recurrence`:
* - MUST reject the invoice if `recurrence_basetime` is not set.
*/
if (sent->invreq->recurrence_counter && !inv->recurrence_basetime) {
if (sent->invreq->invreq_recurrence_counter && !inv->invoice_recurrence_basetime) {
badfield = "recurrence_basetime";
goto badinv;
}
/* BOLT-offers #12:
* - SHOULD confirm authorization if the `description` does not exactly
* match the `offer`
* - MAY highlight if `description` has simply had a change appended.
*/
/* We highlight these changes to the caller, for them to handle */
out = jsonrpc_stream_success(sent->cmd);
json_add_string(out, "invoice", invoice_encode(tmpctx, inv));
json_object_start(out, "changes");
if (field_diff(sent->offer, inv, description)) {
/* Did they simply append? */
if (description_is_appended(sent->offer->description,
inv->description)) {
size_t off = tal_bytelen(sent->offer->description);
json_add_stringn(out, "description_appended",
inv->description + off,
tal_bytelen(inv->description) - off);
} else if (!inv->description)
json_add_stringn(out, "description_removed",
sent->offer->description,
tal_bytelen(sent->offer->description));
else
json_add_stringn(out, "description",
inv->description,
tal_bytelen(inv->description));
}
/* BOLT-offers #12:
* - SHOULD confirm authorization if `issuer` does not exactly
* match the `offer`
*/
if (field_diff(sent->offer, inv, issuer)) {
if (!inv->issuer)
json_add_stringn(out, "issuer_removed",
sent->offer->issuer,
tal_bytelen(sent->offer->issuer));
else
json_add_stringn(out, "issuer",
inv->issuer,
tal_bytelen(inv->issuer));
}
/* BOLT-offers #12:
* - SHOULD confirm authorization if `msat` is not within the amount
* range authorized.
*/
/* We always tell them this unless it's trivial to calc and
* exactly as expected. */
if (!expected_amount || *inv->amount != *expected_amount) {
if (deprecated_apis)
json_add_amount_msat_only(out, "msat",
amount_msat(*inv->amount));
if (!expected_amount || *inv->invoice_amount != *expected_amount) {
json_add_amount_msat_only(out, "amount_msat",
amount_msat(*inv->amount));
amount_msat(*inv->invoice_amount));
}
json_object_end(out);
/* We tell them about next period at this point, if any. */
if (sent->offer->recurrence) {
if (sent->offer->offer_recurrence) {
u64 next_counter, next_period_idx;
u64 paywindow_start, paywindow_end;
next_counter = *sent->invreq->recurrence_counter + 1;
if (sent->invreq->recurrence_start)
next_period_idx = *sent->invreq->recurrence_start
next_counter = *sent->invreq->invreq_recurrence_counter + 1;
if (sent->invreq->invreq_recurrence_start)
next_period_idx = *sent->invreq->invreq_recurrence_start
+ next_counter;
else
next_period_idx = next_counter;
/* If this was the last, don't tell them about a next! */
if (!sent->offer->recurrence_limit
|| next_period_idx <= *sent->offer->recurrence_limit) {
if (!sent->offer->offer_recurrence_limit
|| next_period_idx <= *sent->offer->offer_recurrence_limit) {
json_object_start(out, "next_period");
json_add_u64(out, "counter", next_counter);
json_add_u64(out, "starttime",
offer_period_start(*inv->recurrence_basetime,
offer_period_start(*inv->invoice_recurrence_basetime,
next_period_idx,
sent->offer->recurrence));
sent->offer->offer_recurrence));
json_add_u64(out, "endtime",
offer_period_start(*inv->recurrence_basetime,
offer_period_start(*inv->invoice_recurrence_basetime,
next_period_idx + 1,
sent->offer->recurrence) - 1);
sent->offer->offer_recurrence) - 1);
offer_period_paywindow(sent->offer->recurrence,
sent->offer->recurrence_paywindow,
sent->offer->recurrence_base,
*inv->recurrence_basetime,
offer_period_paywindow(sent->offer->offer_recurrence,
sent->offer->offer_recurrence_paywindow,
sent->offer->offer_recurrence_base,
*inv->invoice_recurrence_basetime,
next_period_idx,
&paywindow_start, &paywindow_end);
json_add_u64(out, "paywindow_start", paywindow_start);
@ -500,18 +426,8 @@ static struct command_result *param_offer(struct command *cmd,
struct tlv_offer **offer)
{
char *fail;
int badf;
/* BOLT-offers #12:
* - if `features` contains unknown _odd_ bits that are non-zero:
* - MUST ignore the bit.
* - if `features` contains unknown _even_ bits that are non-zero:
* - MUST NOT respond to the offer.
* - SHOULD indicate the unknown bit to the user.
*/
/* BOLT-offers #12:
* - MUST NOT set or imply any `chain_hash` not set or implied by
* the offer.
*/
*offer = offer_decode(cmd, buffer + tok->start, tok->end - tok->start,
plugin_feature_set(cmd->plugin), chainparams,
&fail);
@ -520,19 +436,61 @@ static struct command_result *param_offer(struct command *cmd,
tal_fmt(cmd,
"Unparsable offer: %s",
fail));
/* BOLT-offers #12:
*
* - if `node_id` or `description` is not set:
* - MUST NOT respond to the offer.
* A reader of an offer:
* - if the offer contains any unknown TLV fields greater or equal to 80:
* - MUST NOT respond to the offer.
* - if `offer_features` contains unknown _odd_ bits that are non-zero:
* - MUST ignore the bit.
* - if `offer_features` contains unknown _even_ bits that are non-zero:
* - MUST NOT respond to the offer.
* - SHOULD indicate the unknown bit to the user.
* - if `offer_description` is not set:
* - MUST NOT respond to the offer.
* - if `offer_node_id` is not set:
* - MUST NOT respond to the offer.
*/
if (!(*offer)->node_id)
for (size_t i = 0; i < tal_count((*offer)->fields); i++) {
if ((*offer)->fields[i].numtype > 80) {
return command_fail_badparam(cmd, name, buffer, tok,
tal_fmt(tmpctx,
"Offer %"PRIu64
" field >= 80",
(*offer)->fields[i].numtype));
}
}
badf = features_unsupported(plugin_feature_set(cmd->plugin),
(*offer)->offer_features,
BOLT12_OFFER_FEATURE);
if (badf != -1) {
return command_fail_badparam(cmd, name, buffer, tok,
tal_fmt(tmpctx,
"unknown feature %i",
badf));
}
if (!(*offer)->offer_description)
return command_fail_badparam(cmd, name, buffer, tok,
"Offer does not contain a description");
if (!(*offer)->offer_node_id)
return command_fail_badparam(cmd, name, buffer, tok,
"Offer does not contain a node_id");
if (!(*offer)->description)
/* BOLT-offers #12:
* - if `offer_chains` is not set:
* - if the node does not accept bitcoin invoices:
* - MUST NOT respond to the offer
* - otherwise: (`offer_chains` is set):
* - if the node does not accept invoices for any of the `chains`:
* - MUST NOT respond to the offer
*/
if (!bolt12_chains_match((*offer)->offer_chains,
tal_count((*offer)->offer_chains),
chainparams)) {
return command_fail_badparam(cmd, name, buffer, tok,
"Offer does not contain a description");
"Offer for wrong chains");
}
return NULL;
}
@ -875,26 +833,26 @@ static struct command_result *invreq_done(struct command *cmd,
/* Now that's given us the previous base, check this is an OK time
* to request an invoice. */
if (sent->invreq->recurrence_counter) {
if (sent->invreq->invreq_recurrence_counter) {
u64 *base;
const jsmntok_t *pbtok;
u64 period_idx = *sent->invreq->recurrence_counter;
u64 period_idx = *sent->invreq->invreq_recurrence_counter;
if (sent->invreq->recurrence_start)
period_idx += *sent->invreq->recurrence_start;
if (sent->invreq->invreq_recurrence_start)
period_idx += *sent->invreq->invreq_recurrence_start;
/* BOLT-offers-recurrence #12:
* - if the offer contained `recurrence_limit`:
* - MUST NOT send an `invoice_request` for a period greater
* than `max_period`
*/
if (sent->offer->recurrence_limit
&& period_idx > *sent->offer->recurrence_limit)
if (sent->offer->offer_recurrence_limit
&& period_idx > *sent->offer->offer_recurrence_limit)
return command_fail(cmd, LIGHTNINGD,
"Can't send invreq for period %"
PRIu64" (limit %u)",
period_idx,
*sent->offer->recurrence_limit);
*sent->offer->offer_recurrence_limit);
/* BOLT-offers-recurrence #12:
* - SHOULD NOT send an `invoice_request` for a period which has
@ -907,19 +865,19 @@ static struct command_result *invreq_done(struct command *cmd,
if (pbtok) {
base = tal(tmpctx, u64);
json_to_u64(buf, pbtok, base);
} else if (sent->offer->recurrence_base)
base = &sent->offer->recurrence_base->basetime;
} else if (sent->offer->offer_recurrence_base)
base = &sent->offer->offer_recurrence_base->basetime;
else {
/* happens with *recurrence_base == 0 */
assert(*sent->invreq->recurrence_counter == 0);
assert(*sent->invreq->invreq_recurrence_counter == 0);
base = NULL;
}
if (base) {
u64 period_start, period_end, now = time_now().ts.tv_sec;
offer_period_paywindow(sent->offer->recurrence,
sent->offer->recurrence_paywindow,
sent->offer->recurrence_base,
offer_period_paywindow(sent->offer->offer_recurrence,
sent->offer->offer_recurrence_paywindow,
sent->offer->offer_recurrence_base,
*base, period_idx,
&period_start, &period_end);
if (now < period_start)
@ -938,9 +896,9 @@ static struct command_result *invreq_done(struct command *cmd,
}
sent->path = path_to_node(sent, cmd->plugin,
sent->offer->node_id);
sent->offer->offer_node_id);
if (!sent->path)
return connect_direct(cmd, sent->offer->node_id,
return connect_direct(cmd, sent->offer->offer_node_id,
sendinvreq_after_connect, sent);
return sendinvreq_after_connect(cmd, NULL, NULL, sent);
@ -963,10 +921,10 @@ force_payer_secret(struct command *cmd,
if (secp256k1_keypair_create(secp256k1_ctx, &kp, payer_secret->data) != 1)
return command_fail(cmd, LIGHTNINGD, "Bad payer_secret");
invreq->payer_key = tal(invreq, struct pubkey);
invreq->invreq_payer_id = tal(invreq, struct pubkey);
/* Docs say this only happens if arguments are invalid! */
if (secp256k1_keypair_pub(secp256k1_ctx,
&invreq->payer_key->pubkey,
&invreq->invreq_payer_id->pubkey,
&kp) != 1)
plugin_err(cmd->plugin,
"secp256k1_keypair_pub failed on %s?",
@ -996,9 +954,9 @@ force_payer_secret(struct command *cmd,
}
sent->path = path_to_node(sent, cmd->plugin,
sent->offer->node_id);
sent->offer->offer_node_id);
if (!sent->path)
return connect_direct(cmd, sent->offer->node_id,
return connect_direct(cmd, sent->offer->offer_node_id,
sendinvreq_after_connect, sent);
return sendinvreq_after_connect(cmd, NULL, NULL, sent);
@ -1016,16 +974,15 @@ static struct command_result *json_fetchinvoice(struct command *cmd,
struct sent *sent = tal(cmd, struct sent);
struct secret *payer_secret = NULL;
u32 *timeout;
u64 *quantity;
u32 *recurrence_counter, *recurrence_start;
invreq = tlv_invoice_request_new(sent);
if (!param(cmd, buffer, params,
p_req("offer", param_offer, &sent->offer),
p_opt("amount_msat|msatoshi", param_msat, &msat),
p_opt("quantity", param_u64, &invreq->quantity),
p_opt("recurrence_counter", param_number,
&invreq->recurrence_counter),
p_opt("recurrence_start", param_number,
&invreq->recurrence_start),
p_opt("quantity", param_u64, &quantity),
p_opt("recurrence_counter", param_number, &recurrence_counter),
p_opt("recurrence_start", param_number, &recurrence_start),
p_opt("recurrence_label", param_string, &rec_label),
p_opt_def("timeout", param_number, &timeout, 60),
p_opt("payer_note", param_string, &payer_note),
@ -1037,74 +994,66 @@ static struct command_result *json_fetchinvoice(struct command *cmd,
sent->wait_timeout = *timeout;
/* BOLT-offers #12:
* - MUST set `offer_id` to the Merkle root of the offer as described
* in [Signature Calculation](#signature-calculation).
*/
invreq->offer_id = tal(invreq, struct sha256);
merkle_tlv(sent->offer->fields, invreq->offer_id);
/* Check if they are trying to send us money. */
if (sent->offer->send_invoice)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"Offer wants an invoice, not invoice_request");
/* BOLT-offers #12:
* - SHOULD not respond to an offer if the current time is after
* `absolute_expiry`.
*/
if (sent->offer->absolute_expiry
&& time_now().ts.tv_sec > *sent->offer->absolute_expiry)
if (sent->offer->offer_absolute_expiry
&& time_now().ts.tv_sec > *sent->offer->offer_absolute_expiry)
return command_fail(cmd, OFFER_EXPIRED, "Offer expired");
/* BOLT-offers-recurrence #12:
* - if the offer did not specify `amount`:
* - MUST specify `amount`.`msat` in multiples of the minimum
* lightning-payable unit (e.g. milli-satoshis for bitcoin) for
* `chain` (or for bitcoin, if there is no `chain`).
* - otherwise:
* - MAY omit `amount`.
* - if it sets `amount`:
* - MUST specify `amount`.`msat` as greater or equal to amount
* expected by the offer (before any proportional period amount).
/* BOLT-offers #12:
* The writer:
* - if it is responding to an offer:
* - MUST copy all fields from the offer (including unknown fields).
*/
if (sent->offer->amount) {
invreq = invoice_request_for_offer(sent, sent->offer);
invreq->invreq_recurrence_counter = tal_steal(invreq, recurrence_counter);
invreq->invreq_recurrence_start = tal_steal(invreq, recurrence_start);
/* BOLT-offers-recurrence #12:
* - if `offer_amount` is not present:
* - MUST specify `invreq_amount`.
* - otherwise:
* - MAY omit `invreq_amount`.
* - if it sets `invreq_amount`:
* - MUST specify `invreq_amount`.`msat` as greater or equal to
* amount expected by `offer_amount` (and, if present,
* `offer_currency` and `invreq_quantity`).
*/
if (sent->offer->offer_amount) {
/* FIXME: Check after quantity? */
if (msat) {
invreq->amount = tal_dup(invreq, u64,
&msat->millisatoshis); /* Raw: tu64 */
invreq->invreq_amount = tal_dup(invreq, u64,
&msat->millisatoshis); /* Raw: tu64 */
}
} else {
if (!msat)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"msatoshi parameter required");
invreq->amount = tal_dup(invreq, u64,
&msat->millisatoshis); /* Raw: tu64 */
invreq->invreq_amount = tal_dup(invreq, u64,
&msat->millisatoshis); /* Raw: tu64 */
}
/* FIXME-OFFERS: Examine fields in inv directly! */
/* BOLT-offers #12:
* - if the offer had a `quantity_min` or `quantity_max` field:
* - MUST set `quantity`
* - MUST set it within that (inclusive) range.
* - otherwise:
* - MUST NOT set `quantity`
* - if `offer_quantity_max` is present:
* - MUST set `invreq_quantity`
* - if `offer_quantity_max` is non-zero:
* - MUST set `invreq_quantity` less than or equal to
* `offer_quantity_max`.
*/
if (sent->offer->quantity_min || sent->offer->quantity_max) {
if (!invreq->quantity)
if (sent->offer->offer_quantity_max) {
if (!invreq->invreq_quantity)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"quantity parameter required");
if (sent->offer->quantity_min
&& *invreq->quantity < *sent->offer->quantity_min)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"quantity must be >= %"PRIu64,
*sent->offer->quantity_min);
if (sent->offer->quantity_max
&& *invreq->quantity > *sent->offer->quantity_max)
if (*sent->offer->offer_quantity_max
&& *invreq->invreq_quantity > *sent->offer->offer_quantity_max)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"quantity must be <= %"PRIu64,
*sent->offer->quantity_max);
*sent->offer->offer_quantity_max);
} else {
if (invreq->quantity)
if (invreq->invreq_quantity)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"quantity parameter unnecessary");
}
@ -1112,7 +1061,7 @@ static struct command_result *json_fetchinvoice(struct command *cmd,
/* BOLT-offers-recurrence #12:
* - if the offer contained `recurrence`:
*/
if (sent->offer->recurrence) {
if (sent->offer->offer_recurrence) {
/* BOLT-offers-recurrence #12:
* - for the initial request:
*...
@ -1124,7 +1073,7 @@ static struct command_result *json_fetchinvoice(struct command *cmd,
* - MUST set `recurrence_counter` `counter` to one greater
* than the highest-paid invoice.
*/
if (!invreq->recurrence_counter)
if (!invreq->invreq_recurrence_counter)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"needs recurrence_counter");
@ -1136,13 +1085,13 @@ static struct command_result *json_fetchinvoice(struct command *cmd,
* - otherwise:
* - MUST NOT include `recurrence_start`
*/
if (sent->offer->recurrence_base
&& sent->offer->recurrence_base->start_any_period) {
if (!invreq->recurrence_start)
if (sent->offer->offer_recurrence_base
&& sent->offer->offer_recurrence_base->start_any_period) {
if (!invreq->invreq_recurrence_start)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"needs recurrence_start");
} else {
if (invreq->recurrence_start)
if (invreq->invreq_recurrence_start)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"unnecessary recurrence_start");
}
@ -1158,34 +1107,41 @@ static struct command_result *json_fetchinvoice(struct command *cmd,
* - MUST NOT set `recurrence_counter`.
* - MUST NOT set `recurrence_start`
*/
if (invreq->recurrence_counter)
if (invreq->invreq_recurrence_counter)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"unnecessary recurrence_counter");
if (invreq->recurrence_start)
if (invreq->invreq_recurrence_start)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"unnecessary recurrence_start");
}
/* BOLT-offers #12:
*
* - if the chain for the invoice is not solely bitcoin:
* - MUST specify `chains` the offer is valid for.
* - if `offer_chains` is set:
* - MUST set `invreq_chain` to one of `offer_chains` unless that
* chain is bitcoin, in which case it MAY omit `invreq_chain`.
* - otherwise:
* - the bitcoin chain is implied as the first and only entry.
* - if it sets `invreq_chain` it MUST set it to bitcoin.
*/
/* We already checked that we're compatible chain, in param_offer */
if (!streq(chainparams->network_name, "bitcoin")) {
invreq->chain = tal_dup(invreq, struct bitcoin_blkid,
&chainparams->genesis_blockhash);
invreq->invreq_chain = tal_dup(invreq, struct bitcoin_blkid,
&chainparams->genesis_blockhash);
}
invreq->features
= plugin_feature_set(cmd->plugin)->bits[BOLT11_FEATURE];
/* BOLT-offers #12:
* - if it supports bolt12 invoice request features:
* - MUST set `invreq_features`.`features` to the bitmap of features.
*/
invreq->invreq_features
= plugin_feature_set(cmd->plugin)->bits[BOLT12_OFFER_FEATURE];
/* invreq->payer_note is not a nul-terminated string! */
if (payer_note)
invreq->payer_note = tal_dup_arr(invreq, utf8,
payer_note, strlen(payer_note),
0);
invreq->invreq_payer_note = tal_dup_arr(invreq, utf8,
payer_note,
strlen(payer_note),
0);
/* They can provide a secret, and we don't assume it's our job
* to pay. */

View File

@ -317,100 +317,97 @@ static void json_add_offer(struct json_stream *js, const struct tlv_offer *offer
struct sha256 offer_id;
bool valid = true;
merkle_tlv(offer->fields, &offer_id);
/* FIXME-OFFERS: Rename all fields to offer_ as per spec */
offer_offer_id(offer, &offer_id);
json_add_sha256(js, "offer_id", &offer_id);
if (offer->chains)
json_add_chains(js, offer->chains);
if (offer->currency) {
if (offer->offer_chains)
json_add_chains(js, offer->offer_chains);
if (offer->offer_currency) {
const struct iso4217_name_and_divisor *iso4217;
json_add_stringn(js, "currency",
offer->currency, tal_bytelen(offer->currency));
if (offer->amount)
json_add_u64(js, "amount", *offer->amount);
iso4217 = find_iso4217(offer->currency,
tal_bytelen(offer->currency));
offer->offer_currency,
tal_bytelen(offer->offer_currency));
if (offer->offer_amount)
json_add_u64(js, "amount", *offer->offer_amount);
iso4217 = find_iso4217(offer->offer_currency,
tal_bytelen(offer->offer_currency));
if (iso4217)
json_add_num(js, "minor_unit", iso4217->minor_unit);
else
json_add_string(js, "warning_offer_unknown_currency",
"unknown currency code");
} else if (offer->amount)
} else if (offer->offer_amount)
json_add_amount_msat_only(js, "amount_msat",
amount_msat(*offer->amount));
if (offer->send_invoice)
json_add_bool(js, "send_invoice", true);
if (offer->refund_for)
json_add_sha256(js, "refund_for", offer->refund_for);
amount_msat(*offer->offer_amount));
/* BOLT-offers #12:
* A reader of an offer:
*...
* - if `node_id` or `description` is not set:
* - MUST NOT respond to the offer.
* - if `offer_description` is not set:
* - MUST NOT respond to the offer.
*/
if (offer->description)
if (offer->offer_description)
json_add_stringn(js, "description",
offer->description,
tal_bytelen(offer->description));
offer->offer_description,
tal_bytelen(offer->offer_description));
else {
json_add_string(js, "warning_offer_missing_description",
"offers without a description are invalid");
valid = false;
}
if (offer->issuer)
json_add_stringn(js, "issuer", offer->issuer,
tal_bytelen(offer->issuer));
if (offer->features)
json_add_hex_talarr(js, "features", offer->features);
if (offer->absolute_expiry)
if (offer->offer_issuer)
json_add_stringn(js, "issuer", offer->offer_issuer,
tal_bytelen(offer->offer_issuer));
if (offer->offer_features)
json_add_hex_talarr(js, "features", offer->offer_features);
if (offer->offer_absolute_expiry)
json_add_u64(js, "absolute_expiry",
*offer->absolute_expiry);
if (offer->paths)
valid &= json_add_blinded_paths(js, offer->paths, NULL);
*offer->offer_absolute_expiry);
if (offer->offer_paths)
valid &= json_add_blinded_paths(js, offer->offer_paths, NULL);
if (offer->quantity_min)
json_add_u64(js, "quantity_min", *offer->quantity_min);
if (offer->quantity_max)
json_add_u64(js, "quantity_max", *offer->quantity_max);
if (offer->recurrence) {
if (offer->offer_quantity_max)
json_add_u64(js, "quantity_max", *offer->offer_quantity_max);
if (offer->offer_recurrence) {
const char *name;
json_object_start(js, "recurrence");
json_add_num(js, "time_unit", offer->recurrence->time_unit);
name = recurrence_time_unit_name(offer->recurrence->time_unit);
json_add_num(js, "time_unit", offer->offer_recurrence->time_unit);
name = recurrence_time_unit_name(offer->offer_recurrence->time_unit);
if (name)
json_add_string(js, "time_unit_name", name);
json_add_num(js, "period", offer->recurrence->period);
if (offer->recurrence_base) {
json_add_num(js, "period", offer->offer_recurrence->period);
if (offer->offer_recurrence_base) {
json_add_u64(js, "basetime",
offer->recurrence_base->basetime);
if (offer->recurrence_base->start_any_period)
offer->offer_recurrence_base->basetime);
if (offer->offer_recurrence_base->start_any_period)
json_add_bool(js, "start_any_period", true);
}
if (offer->recurrence_limit)
json_add_u32(js, "limit", *offer->recurrence_limit);
if (offer->recurrence_paywindow) {
if (offer->offer_recurrence_limit)
json_add_u32(js, "limit", *offer->offer_recurrence_limit);
if (offer->offer_recurrence_paywindow) {
json_object_start(js, "paywindow");
json_add_u32(js, "seconds_before",
offer->recurrence_paywindow->seconds_before);
offer->offer_recurrence_paywindow->seconds_before);
json_add_u32(js, "seconds_after",
offer->recurrence_paywindow->seconds_after);
if (offer->recurrence_paywindow->proportional_amount)
offer->offer_recurrence_paywindow->seconds_after);
if (offer->offer_recurrence_paywindow->proportional_amount)
json_add_bool(js, "proportional_amount", true);
json_object_end(js);
}
json_object_end(js);
}
if (offer->node_id)
json_add_pubkey(js, "node_id", offer->node_id);
/* BOLT-offers #12:
* - if `offer_node_id` is not set:
* - MUST NOT respond to the offer.
*/
/* FIXME-OFFERS: Rename all fields to offer_ as per spec */
if (offer->offer_node_id)
json_add_pubkey(js, "node_id", offer->offer_node_id);
else
valid = false;
/* If it's present, offer_decode checked it was valid */
if (offer->signature)
json_add_bip340sig(js, "signature", offer->signature);
json_add_bool(js, "valid", valid);
}
@ -491,41 +488,83 @@ static void json_add_b12_invoice(struct json_stream *js,
{
bool valid = true;
if (invoice->chain)
json_add_sha256(js, "chain", &invoice->chain->shad.sha);
if (invoice->offer_id)
json_add_sha256(js, "offer_id", invoice->offer_id);
/* If there's an offer_node_id, then there's an offer. */
if (invoice->offer_node_id) {
struct sha256 offer_id;
invoice_offer_id(invoice, &offer_id);
json_add_sha256(js, "offer_id", &offer_id);
}
/* FIXME-OFFERS: Rename all fields to invoice_ as per spec */
if (invoice->invreq_chain)
json_add_sha256(js, "chain", &invoice->invreq_chain->shad.sha);
/* BOLT-offers #12:
* - MUST reject the invoice if `msat` is not present.
* - MUST reject the invoice if `invoice_amount` is not present.
* - MUST reject the invoice if `invreq_payer_id` is not present.
*/
if (invoice->amount)
if (invoice->invoice_amount)
json_add_amount_msat_only(js, "amount_msat",
amount_msat(*invoice->amount));
amount_msat(*invoice->invoice_amount));
else {
json_add_string(js, "warning_invoice_missing_amount",
"invoices without an amount are invalid");
valid = false;
}
if (invoice->invreq_payer_id)
json_add_pubkey(js, "payer_key", invoice->invreq_payer_id);
else {
json_add_string(js, "warning_invoice_missing_invreq_payer_id",
"invoices without an invreq_payer_id are invald");
valid = false;
}
/* BOLT-offers #12:
* - MUST reject the invoice if `description` is not present.
* - MUST reject the invoice if `offer_description` is not present.
* - MUST reject the invoice if `invoice_created_at` is not present.
* - MUST reject the invoice if `invoice_payment_hash` is not present.
*/
if (invoice->description)
json_add_stringn(js, "description", invoice->description,
tal_bytelen(invoice->description));
if (invoice->offer_description)
json_add_stringn(js, "description", invoice->offer_description,
tal_bytelen(invoice->offer_description));
else {
json_add_string(js, "warning_invoice_missing_description",
"invoices without a description are invalid");
valid = false;
}
if (invoice->issuer)
json_add_stringn(js, "issuer", invoice->issuer,
tal_bytelen(invoice->issuer));
if (invoice->features)
json_add_hex_talarr(js, "features", invoice->features);
if (invoice->paths) {
if (invoice->invoice_created_at) {
json_add_u64(js, "created_at", *invoice->invoice_created_at);
} else {
json_add_string(js, "warning_invoice_missing_created_at",
"invoices without created_at are invalid");
valid = false;
}
if (invoice->invoice_payment_hash)
json_add_sha256(js, "payment_hash", invoice->invoice_payment_hash);
else {
json_add_string(js, "warning_invoice_missing_payment_hash",
"invoices without a payment_hash are invalid");
valid = false;
}
if (invoice->offer_issuer)
json_add_stringn(js, "issuer", invoice->offer_issuer,
tal_bytelen(invoice->offer_issuer));
if (invoice->invoice_features)
json_add_hex_talarr(js, "features", invoice->invoice_features);
/* BOLT-offers #12:
* - MUST reject the invoice if `invoice_paths` is not present
* or is empty.
* - MUST reject the invoice if `invoice_blindedpay` is not present.
* - MUST reject the invoice if `invoice_blindedpay` does not contain
* exactly one `blinded_payinfo` per `invoice_paths`.`blinded_path`.
*/
if (invoice->invoice_paths) {
/* BOLT-offers #12:
* - if `blinded_path` is present:
* - MUST reject the invoice if `blinded_payinfo` is not
@ -534,33 +573,35 @@ static void json_add_b12_invoice(struct json_stream *js,
* contain exactly as many `payinfo` as total `onionmsg_path`
* in `blinded_path`.
*/
if (!invoice->blindedpay) {
if (!invoice->invoice_blindedpay) {
json_add_string(js, "warning_invoice_missing_blinded_payinfo",
"invoices with blinded_path without blinded_payinfo are invalid");
valid = false;
}
valid &= json_add_blinded_paths(js, invoice->paths, invoice->blindedpay);
valid &= json_add_blinded_paths(js, invoice->invoice_paths,
invoice->invoice_blindedpay);
} else {
json_add_string(js, "warning_invoice_missing_blinded_path",
"invoices without a payment_hash are invalid");
valid = false;
}
if (invoice->quantity)
json_add_u64(js, "quantity", *invoice->quantity);
if (invoice->send_invoice)
json_add_bool(js, "send_invoice", true);
if (invoice->refund_for)
json_add_sha256(js, "refund_for", invoice->refund_for);
if (invoice->recurrence_counter) {
if (invoice->invreq_quantity)
json_add_u64(js, "quantity", *invoice->invreq_quantity);
if (invoice->invreq_recurrence_counter) {
json_add_u32(js, "recurrence_counter",
*invoice->recurrence_counter);
if (invoice->recurrence_start)
*invoice->invreq_recurrence_counter);
if (invoice->invreq_recurrence_start)
json_add_u32(js, "recurrence_start",
*invoice->recurrence_start);
*invoice->invreq_recurrence_start);
/* BOLT-offers-recurrence #12:
* - if the offer contained `recurrence`:
* - MUST reject the invoice if `recurrence_basetime` is not
* set.
*/
if (invoice->recurrence_basetime)
if (invoice->invoice_recurrence_basetime)
json_add_u64(js, "recurrence_basetime",
*invoice->recurrence_basetime);
*invoice->invoice_recurrence_basetime);
else {
json_add_string(js, "warning_invoice_missing_recurrence_basetime",
"recurring invoices without a recurrence_basetime are invalid");
@ -568,96 +609,34 @@ static void json_add_b12_invoice(struct json_stream *js,
}
}
if (invoice->payer_key)
json_add_pubkey(js, "payer_key", invoice->payer_key);
if (invoice->payer_info)
json_add_hex_talarr(js, "payer_info", invoice->payer_info);
if (invoice->payer_note)
json_add_stringn(js, "payer_note", invoice->payer_note,
tal_bytelen(invoice->payer_note));
/* BOLT-offers #12:
* - MUST reject the invoice if `created_at` is not present.
*/
if (invoice->created_at) {
json_add_u64(js, "created_at", *invoice->created_at);
} else {
json_add_string(js, "warning_invoice_missing_created_at",
"invoices without created_at are invalid");
valid = false;
}
/* BOLT-offers #12:
* - MUST reject the invoice if `payment_hash` is not present.
*/
if (invoice->payment_hash)
json_add_sha256(js, "payment_hash", invoice->payment_hash);
else {
json_add_string(js, "warning_invoice_missing_payment_hash",
"invoices without a payment_hash are invalid");
valid = false;
}
if (invoice->invreq_metadata)
json_add_hex_talarr(js, "invreq_metadata",
invoice->invreq_metadata);
if (invoice->invreq_payer_note)
json_add_stringn(js, "payer_note", invoice->invreq_payer_note,
tal_bytelen(invoice->invreq_payer_note));
/* BOLT-offers #12:
*
* - if the expiry for accepting payment is not 7200 seconds after
* `created_at`:
* - MUST set `relative_expiry`
* - if `invoice_relative_expiry` is present:
* - MUST reject the invoice if the current time since 1970-01-01 UTC
* is greater than `invoice_created_at` plus `seconds_from_creation`.
* - otherwise:
* - MUST reject the invoice if the current time since 1970-01-01 UTC
* is greater than `invoice_created_at` plus 7200.
*/
if (invoice->relative_expiry)
json_add_u32(js, "relative_expiry", *invoice->relative_expiry);
if (invoice->invoice_relative_expiry)
json_add_u32(js, "relative_expiry", *invoice->invoice_relative_expiry);
else
json_add_u32(js, "relative_expiry", 7200);
/* BOLT-offers #12:
* - if the `min_final_cltv_expiry` for the last HTLC in the route is
* not 18:
* - MUST set `min_final_cltv_expiry`.
*/
if (invoice->cltv)
json_add_u32(js, "min_final_cltv_expiry", *invoice->cltv);
else
json_add_u32(js, "min_final_cltv_expiry", 18);
if (invoice->fallbacks)
if (invoice->invoice_fallbacks)
valid &= json_add_fallbacks(js,
invoice->chain,
invoice->fallbacks);
/* BOLT-offers #12:
* - if the offer contained `refund_for`:
* - MUST reject the invoice if `payer_key` does not match the invoice
* whose `payment_hash` is equal to `refund_for`
* `refunded_payment_hash`
* - MUST reject the invoice if `refund_signature` is not set.
* - MUST reject the invoice if `refund_signature` is not a valid
* signature using `payer_key` as described in
* [Signature Calculation](#signature-calculation).
*/
if (invoice->refund_signature) {
json_add_bip340sig(js, "refund_signature",
invoice->refund_signature);
if (!invoice->payer_key) {
json_add_string(js, "warning_invoice_refund_signature_missing_payer_key",
"Can't have refund_signature without payer key");
valid = false;
} else if (!bolt12_check_signature(invoice->fields,
"invoice",
"refund_signature",
invoice->payer_key,
invoice->refund_signature)) {
json_add_string(js, "warning_invoice_refund_signature_invalid",
"refund_signature does not match");
valid = false;
}
} else if (invoice->refund_for) {
json_add_string(js, "warning_invoice_refund_missing_signature",
"refund_for requires refund_signature");
valid = false;
}
invoice->invreq_chain,
invoice->invoice_fallbacks);
/* invoice_decode checked these */
json_add_pubkey(js, "node_id", invoice->node_id);
json_add_pubkey(js, "node_id", invoice->offer_node_id);
json_add_bip340sig(js, "signature", invoice->signature);
json_add_bool(js, "valid", valid);
@ -668,8 +647,17 @@ static void json_add_invoice_request(struct json_stream *js,
{
bool valid = true;
if (invreq->chain)
json_add_sha256(js, "chain", &invreq->chain->shad.sha);
/* If there's an offer_node_id, then there's an offer. */
if (invreq->offer_node_id) {
struct sha256 offer_id;
invreq_offer_id(invreq, &offer_id);
json_add_sha256(js, "offer_id", &offer_id);
}
/* FIXME-OFFERS: Rename all fields to invreq_ as per spec */
if (invreq->invreq_chain)
json_add_sha256(js, "chain", &invreq->invreq_chain->shad.sha);
/* BOLT-offers #12:
* - MUST fail the request if `payer_key` is not present.
@ -677,50 +665,54 @@ static void json_add_invoice_request(struct json_stream *js,
* - MUST fail the request if `features` contains unknown even bits.
* - MUST fail the request if `offer_id` is not present.
*/
if (invreq->offer_id)
json_add_sha256(js, "offer_id", invreq->offer_id);
else {
json_add_string(js, "warning_invoice_request_missing_offer_id",
"invoice_request requires offer_id");
valid = false;
}
if (invreq->amount)
if (invreq->invreq_amount)
json_add_amount_msat_only(js, "amount_msat",
amount_msat(*invreq->amount));
if (invreq->features)
json_add_hex_talarr(js, "features", invreq->features);
if (invreq->quantity)
json_add_u64(js, "quantity", *invreq->quantity);
amount_msat(*invreq->invreq_amount));
if (invreq->invreq_features)
json_add_hex_talarr(js, "features", invreq->invreq_features);
if (invreq->invreq_quantity)
json_add_u64(js, "quantity", *invreq->invreq_quantity);
if (invreq->recurrence_counter)
if (invreq->invreq_recurrence_counter)
json_add_u32(js, "recurrence_counter",
*invreq->recurrence_counter);
if (invreq->recurrence_start)
*invreq->invreq_recurrence_counter);
if (invreq->invreq_recurrence_start)
json_add_u32(js, "recurrence_start",
*invreq->recurrence_start);
if (invreq->payer_key)
json_add_pubkey(js, "payer_key", invreq->payer_key);
*invreq->invreq_recurrence_start);
/* BOLT-offers #12:
* - MUST fail the request if `invreq_payer_id` or `invreq_metadata`
* are not present.
*/
if (invreq->invreq_payer_id)
json_add_pubkey(js, "payer_key", invreq->invreq_payer_id);
else {
json_add_string(js, "warning_invoice_request_missing_payer_key",
"invoice_request requires payer_key");
valid = false;
}
if (invreq->payer_info)
json_add_hex_talarr(js, "payer_info", invreq->payer_info);
if (invreq->payer_note)
json_add_stringn(js, "payer_note", invreq->payer_note,
tal_bytelen(invreq->payer_note));
if (invreq->invreq_metadata)
json_add_hex_talarr(js, "invreq_metadata", invreq->invreq_metadata);
else {
json_add_string(js, "warning_invoice_request_missing_invreq_metadata",
"invoice_request requires invreq_metadata");
valid = false;
}
if (invreq->invreq_payer_note)
json_add_stringn(js, "payer_note", invreq->invreq_payer_note,
tal_bytelen(invreq->invreq_payer_note));
/* BOLT-offers #12:
* - MUST fail the request if there is no `signature` field.
* - MUST fail the request if `signature` is not correct.
* - MUST fail the request if `signature` is not correct as detailed
* in [Signature Calculation](#signature-calculation) using the
* `invreq_payer_id`.
*/
if (invreq->signature) {
if (invreq->payer_key
if (invreq->invreq_payer_id
&& !bolt12_check_signature(invreq->fields,
"invoice_request",
"signature",
invreq->payer_key,
invreq->invreq_payer_id,
invreq->signature)) {
json_add_string(js, "warning_invoice_request_invalid_signature",
"Bad signature");

View File

@ -22,6 +22,8 @@ struct invreq {
/* The offer, once we've looked it up. */
struct tlv_offer *offer;
/* The offer id */
struct sha256 offer_id;
/* The invoice we're preparing (can require additional lookups) */
struct tlv_invoice *inv;
@ -40,14 +42,10 @@ fail_invreq_level(struct command *cmd,
struct tlv_onionmsg_tlv *payload;
struct tlv_invoice_error *err;
full_fmt = tal_fmt(tmpctx, "Failed invoice_request");
full_fmt = tal_fmt(tmpctx, "Failed invreq");
if (invreq->invreq) {
tal_append_fmt(&full_fmt, " %s",
invrequest_encode(tmpctx, invreq->invreq));
if (invreq->invreq->offer_id)
tal_append_fmt(&full_fmt, " for offer %s",
type_to_string(tmpctx, struct sha256,
invreq->invreq->offer_id));
}
tal_append_fmt(&full_fmt, ": %s", fmt);
@ -124,13 +122,13 @@ test_field(struct command *cmd,
*/
static void set_recurring_inv_expiry(struct tlv_invoice *inv, u64 last_pay)
{
inv->relative_expiry = tal(inv, u32);
inv->invoice_relative_expiry = tal(inv, u32);
/* Don't give them a 0 second invoice, even if it's true. */
if (last_pay <= *inv->created_at)
*inv->relative_expiry = 1;
if (last_pay <= *inv->invoice_created_at)
*inv->invoice_relative_expiry = 1;
else
*inv->relative_expiry = last_pay - *inv->created_at;
*inv->invoice_relative_expiry = last_pay - *inv->invoice_created_at;
/* FIXME: Shorten expiry if we're doing currency conversion! */
}
@ -221,9 +219,9 @@ static struct command_result *create_invoicereq(struct command *cmd,
json_add_string(req->js, "invstring", invoice_encode(tmpctx, ir->inv));
json_add_preimage(req->js, "preimage", &ir->preimage);
json_add_label(req->js, ir->inv->offer_id, ir->inv->payer_key,
ir->inv->recurrence_counter
? *ir->inv->recurrence_counter : 0);
json_add_label(req->js, &ir->offer_id, ir->inv->invreq_payer_id,
ir->inv->invreq_recurrence_counter
? *ir->inv->invreq_recurrence_counter : 0);
return send_outreq(cmd->plugin, req);
}
@ -323,7 +321,8 @@ static struct command_result *listincoming_done(struct command *cmd,
if (!feature_offered(features, OPT_ROUTE_BLINDING))
continue;
if (amount_msat_less(ci.htlc_max, amount_msat(*ir->inv->amount)))
if (amount_msat_less(ci.htlc_max,
amount_msat(*ir->inv->invoice_amount)))
continue;
/* Only pick a private one if no public candidates. */
@ -346,7 +345,8 @@ static struct command_result *listincoming_done(struct command *cmd,
/* Note: since we don't make one, createinvoice adds a dummy. */
plugin_log(cmd->plugin, LOG_UNUSUAL,
"No incoming channel for %s, so no blinded path",
fmt_amount_msat(tmpctx, amount_msat(*ir->inv->amount)));
fmt_amount_msat(tmpctx,
amount_msat(*ir->inv->invoice_amount)));
} else {
struct privkey blinding;
struct tlv_encrypted_data_tlv_payment_relay relay;
@ -358,9 +358,14 @@ static struct command_result *listincoming_done(struct command *cmd,
relay.fee_base_msat = best->feebase;
relay.fee_proportional_millionths = best->feeppm;
/* BOLT-offers #12:
* - if the expiry for accepting payment is not 7200 seconds
* after `invoice_created_at`:
* - MUST set `invoice_relative_expiry`
*/
/* Give them 6 blocks, plus one per 10 minutes until expiry. */
if (ir->inv->relative_expiry)
base = blockheight + 6 + *ir->inv->relative_expiry / 600;
if (ir->inv->invoice_relative_expiry)
base = blockheight + 6 + *ir->inv->invoice_relative_expiry / 600;
else
base = blockheight + 6 + 7200 / 600;
constraints.max_cltv_expiry = base + best->cltv + cltv_final;
@ -368,14 +373,14 @@ static struct command_result *listincoming_done(struct command *cmd,
randombytes_buf(&blinding, sizeof(blinding));
ir->inv->paths = tal_arr(ir->inv, struct blinded_path *, 1);
ir->inv->paths[0] = tal(ir->inv->paths, struct blinded_path);
ir->inv->paths[0]->first_node_id = best->id;
ir->inv->invoice_paths = tal_arr(ir->inv, struct blinded_path *, 1);
ir->inv->invoice_paths[0] = tal(ir->inv->invoice_paths, struct blinded_path);
ir->inv->invoice_paths[0]->first_node_id = best->id;
if (!pubkey_from_privkey(&blinding,
&ir->inv->paths[0]->blinding))
&ir->inv->invoice_paths[0]->blinding))
abort();
hops = tal_arr(ir->inv->paths[0], struct onionmsg_hop *, 2);
ir->inv->paths[0]->path = hops;
hops = tal_arr(ir->inv->invoice_paths[0], struct onionmsg_hop *, 2);
ir->inv->invoice_paths[0]->path = hops;
/* First hop is the peer */
hops[0] = tal(hops, struct onionmsg_hop);
@ -395,18 +400,18 @@ static struct command_result *listincoming_done(struct command *cmd,
&id,
NULL, NULL, NULL,
invoice_path_id(tmpctx, &invoicesecret_base,
ir->inv->payment_hash),
ir->inv->invoice_payment_hash),
&hops[1]->blinded_node_id);
/* FIXME: This should be a "normal" feerate and range. */
ir->inv->blindedpay = tal_arr(ir->inv, struct blinded_payinfo *, 1);
ir->inv->blindedpay[0] = tal(ir->inv->blindedpay, struct blinded_payinfo);
ir->inv->blindedpay[0]->fee_base_msat = best->feebase;
ir->inv->blindedpay[0]->fee_proportional_millionths = best->feeppm;
ir->inv->blindedpay[0]->cltv_expiry_delta = best->cltv;
ir->inv->blindedpay[0]->htlc_minimum_msat = best->htlc_min;
ir->inv->blindedpay[0]->htlc_maximum_msat = best->htlc_max;
ir->inv->blindedpay[0]->features = NULL;
ir->inv->invoice_blindedpay = tal_arr(ir->inv, struct blinded_payinfo *, 1);
ir->inv->invoice_blindedpay[0] = tal(ir->inv->invoice_blindedpay, struct blinded_payinfo);
ir->inv->invoice_blindedpay[0]->fee_base_msat = best->feebase;
ir->inv->invoice_blindedpay[0]->fee_proportional_millionths = best->feeppm;
ir->inv->invoice_blindedpay[0]->cltv_expiry_delta = best->cltv;
ir->inv->invoice_blindedpay[0]->htlc_minimum_msat = best->htlc_min;
ir->inv->invoice_blindedpay[0]->htlc_maximum_msat = best->htlc_max;
ir->inv->invoice_blindedpay[0]->features = NULL;
}
done:
@ -432,17 +437,17 @@ static struct command_result *check_period(struct command *cmd,
struct command_result *err;
/* If we have a recurrence base, that overrides. */
if (ir->offer->recurrence_base)
basetime = ir->offer->recurrence_base->basetime;
if (ir->offer->offer_recurrence_base)
basetime = ir->offer->offer_recurrence_base->basetime;
/* BOLT-offers-recurrence #12:
* - if the invoice corresponds to an offer with `recurrence`:
* - MUST set `recurrence_basetime` to the start of period #0 as
* calculated by [Period Calculation](#offer-period-calculation).
*/
ir->inv->recurrence_basetime = tal_dup(ir->inv, u64, &basetime);
ir->inv->invoice_recurrence_basetime = tal_dup(ir->inv, u64, &basetime);
period_idx = *ir->invreq->recurrence_counter;
period_idx = *ir->invreq->invreq_recurrence_counter;
/* BOLT-offers-recurrence #12:
* - if the offer had `recurrence_base` and `start_any_period`
@ -453,19 +458,19 @@ static struct command_result *check_period(struct command *cmd,
* `recurrence_start` field plus the `recurrence_counter`
* `counter` field.
*/
if (ir->offer->recurrence_base
&& ir->offer->recurrence_base->start_any_period) {
err = invreq_must_have(cmd, ir, recurrence_start);
if (ir->offer->offer_recurrence_base
&& ir->offer->offer_recurrence_base->start_any_period) {
err = invreq_must_have(cmd, ir, invreq_recurrence_start);
if (err)
return err;
period_idx += *ir->invreq->recurrence_start;
period_idx += *ir->invreq->invreq_recurrence_start;
/* BOLT-offers-recurrence #12:
* - MUST set (or not set) `recurrence_start` exactly as the
* invoice_request did.
* invreq did.
*/
ir->inv->recurrence_start
= tal_dup(ir->inv, u32, ir->invreq->recurrence_start);
ir->inv->invreq_recurrence_start
= tal_dup(ir->inv, u32, ir->invreq->invreq_recurrence_start);
} else {
/* BOLT-offers-recurrence #12:
*
@ -475,7 +480,7 @@ static struct command_result *check_period(struct command *cmd,
* - MUST consider the period index for this request to be the
* `recurrence_counter` `counter` field.
*/
err = invreq_must_not_have(cmd, ir, recurrence_start);
err = invreq_must_not_have(cmd, ir, invreq_recurrence_start);
if (err)
return err;
}
@ -485,26 +490,26 @@ static struct command_result *check_period(struct command *cmd,
* - MUST fail the request if the period index is greater than
* `max_period`.
*/
if (ir->offer->recurrence_limit
&& period_idx > *ir->offer->recurrence_limit) {
if (ir->offer->offer_recurrence_limit
&& period_idx > *ir->offer->offer_recurrence_limit) {
return fail_invreq(cmd, ir,
"period_index %"PRIu64" too great",
period_idx);
}
offer_period_paywindow(ir->offer->recurrence,
ir->offer->recurrence_paywindow,
ir->offer->recurrence_base,
offer_period_paywindow(ir->offer->offer_recurrence,
ir->offer->offer_recurrence_paywindow,
ir->offer->offer_recurrence_base,
basetime, period_idx,
&paywindow_start, &paywindow_end);
if (*ir->inv->created_at < paywindow_start) {
if (*ir->inv->invoice_created_at < paywindow_start) {
return fail_invreq(cmd, ir,
"period_index %"PRIu64
" too early (start %"PRIu64")",
period_idx,
paywindow_start);
}
if (*ir->inv->created_at > paywindow_end) {
if (*ir->inv->invoice_created_at > paywindow_end) {
return fail_invreq(cmd, ir,
"period_index %"PRIu64
" too late (ended %"PRIu64")",
@ -524,21 +529,21 @@ static struct command_result *check_period(struct command *cmd,
* - MUST adjust the *base invoice amount* proportional to time
* remaining in the period.
*/
if (*ir->invreq->recurrence_counter != 0
&& ir->offer->recurrence_paywindow
&& ir->offer->recurrence_paywindow->proportional_amount == 1) {
if (*ir->invreq->invreq_recurrence_counter != 0
&& ir->offer->offer_recurrence_paywindow
&& ir->offer->offer_recurrence_paywindow->proportional_amount == 1) {
u64 start = offer_period_start(basetime, period_idx,
ir->offer->recurrence);
ir->offer->offer_recurrence);
u64 end = offer_period_start(basetime, period_idx + 1,
ir->offer->recurrence);
ir->offer->offer_recurrence);
if (*ir->inv->created_at > start) {
*ir->inv->amount
*= (double)((*ir->inv->created_at - start)
if (*ir->inv->invoice_created_at > start) {
*ir->inv->invoice_amount
*= (double)((*ir->inv->invoice_created_at - start)
/ (end - start));
/* Round up to make it non-zero if necessary. */
if (*ir->inv->amount == 0)
*ir->inv->amount = 1;
if (*ir->inv->invoice_amount == 0)
*ir->inv->invoice_amount = 1;
}
}
@ -559,7 +564,7 @@ static struct command_result *prev_invoice_done(struct command *cmd,
if (arr->size == 0) {
return fail_invreq(cmd, ir,
"No previous invoice #%u",
*ir->inv->recurrence_counter - 1);
*ir->inv->invreq_recurrence_counter - 1);
}
/* Was it paid? */
@ -567,7 +572,7 @@ static struct command_result *prev_invoice_done(struct command *cmd,
if (!json_tok_streq(buf, status, "paid")) {
return fail_invreq(cmd, ir,
"Previous invoice #%u status %.*s",
*ir->inv->recurrence_counter - 1,
*ir->inv->invreq_recurrence_counter - 1,
json_tok_full_len(status),
json_tok_full(buf, status));
}
@ -577,7 +582,7 @@ static struct command_result *prev_invoice_done(struct command *cmd,
if (!b12) {
return fail_internalerr(cmd, ir,
"Previous invoice #%u no bolt12 (%.*s)",
*ir->inv->recurrence_counter - 1,
*ir->inv->invreq_recurrence_counter - 1,
json_tok_full_len(arr + 1),
json_tok_full(buf, arr + 1));
}
@ -590,12 +595,12 @@ static struct command_result *prev_invoice_done(struct command *cmd,
json_tok_full_len(b12),
json_tok_full(buf, b12));
}
if (!previnv->recurrence_basetime) {
if (!previnv->invoice_recurrence_basetime) {
return fail_internalerr(cmd, ir,
"Previous invoice %.*s no recurrence_basetime?",
json_tok_full_len(b12), json_tok_full(buf, b12));
}
return check_period(cmd, ir, *previnv->recurrence_basetime);
return check_period(cmd, ir, *previnv->invoice_recurrence_basetime);
}
/* Now, we need to check the previous invoice was paid, and maybe get timebase */
@ -605,8 +610,8 @@ static struct command_result *check_previous_invoice(struct command *cmd,
struct out_req *req;
/* No previous? Just pass through */
if (*ir->invreq->recurrence_counter == 0)
return check_period(cmd, ir, *ir->inv->created_at);
if (*ir->invreq->invreq_recurrence_counter == 0)
return check_period(cmd, ir, *ir->inv->invoice_created_at);
req = jsonrpc_request_start(cmd->plugin, cmd,
"listinvoices",
@ -614,9 +619,9 @@ static struct command_result *check_previous_invoice(struct command *cmd,
error,
ir);
json_add_label(req->js,
ir->invreq->offer_id,
ir->invreq->payer_key,
*ir->invreq->recurrence_counter - 1);
&ir->offer_id,
ir->invreq->invreq_payer_id,
*ir->invreq->invreq_recurrence_counter - 1);
return send_outreq(cmd->plugin, req);
}
@ -639,24 +644,24 @@ static struct command_result *invreq_amount_by_quantity(struct command *cmd,
const struct invreq *ir,
u64 *raw_amt)
{
assert(ir->offer->amount);
assert(ir->offer->offer_amount);
/* BOLT-offers #12:
* - MUST calculate the *base invoice amount* using the offer `amount`:
*/
*raw_amt = *ir->offer->amount;
*raw_amt = *ir->offer->offer_amount;
/* BOLT-offers #12:
* - if request contains `quantity`, multiply by `quantity`.
*/
if (ir->invreq->quantity) {
if (mul_overflows_u64(*ir->invreq->quantity, *raw_amt)) {
if (ir->invreq->invreq_quantity) {
if (mul_overflows_u64(*ir->invreq->invreq_quantity, *raw_amt)) {
return fail_invreq(cmd, ir,
"quantity %"PRIu64
" causes overflow",
*ir->invreq->quantity);
*ir->invreq->invreq_quantity);
}
*raw_amt *= *ir->invreq->quantity;
*raw_amt *= *ir->invreq->invreq_quantity;
}
return NULL;
@ -669,28 +674,28 @@ static struct command_result *invreq_base_amount_simple(struct command *cmd,
{
struct command_result *err;
if (ir->offer->amount) {
if (ir->offer->offer_amount) {
u64 raw_amount;
assert(!ir->offer->currency);
assert(!ir->offer->offer_currency);
err = invreq_amount_by_quantity(cmd, ir, &raw_amount);
if (err)
return err;
*amt = amount_msat(raw_amount);
} else {
/* BOLT-offers-recurrence #12:
/* BOLT-offers #12:
*
* - otherwise:
* - MUST fail the request if it does not contain `amount`.
* - MUST use the request `amount` as the *base invoice amount*.
* (Note: invoice amount can be further modified by recurrence
* below)
* The reader:
*...
* - otherwise (no `offer_amount`):
* - MUST fail the request if it does not contain
* `invreq_amount`.
*/
err = invreq_must_have(cmd, ir, amount);
err = invreq_must_have(cmd, ir, invreq_amount);
if (err)
return err;
*amt = amount_msat(*ir->invreq->amount);
*amt = amount_msat(*ir->invreq->invreq_amount);
}
return NULL;
}
@ -706,8 +711,8 @@ static struct command_result *handle_amount_and_recurrence(struct command *cmd,
* - MUST fail the request if its `amount` is less than the
* *base invoice amount*.
*/
if (ir->offer->amount && ir->invreq->amount) {
if (amount_msat_less(amount_msat(*ir->invreq->amount), base_inv_amount)) {
if (ir->offer->offer_amount && ir->invreq->invreq_amount) {
if (amount_msat_less(amount_msat(*ir->invreq->invreq_amount), base_inv_amount)) {
return fail_invreq(cmd, ir, "Amount must be at least %s",
type_to_string(tmpctx, struct amount_msat,
&base_inv_amount));
@ -717,7 +722,7 @@ static struct command_result *handle_amount_and_recurrence(struct command *cmd,
* the *base invoice amount*.
*/
/* Much == 5? Easier to divide and compare, than multiply. */
if (amount_msat_greater(amount_msat_div(amount_msat(*ir->invreq->amount), 5),
if (amount_msat_greater(amount_msat_div(amount_msat(*ir->invreq->invreq_amount), 5),
base_inv_amount)) {
return fail_invreq(cmd, ir, "Amount vastly exceeds %s",
type_to_string(tmpctx, struct amount_msat,
@ -727,22 +732,16 @@ static struct command_result *handle_amount_and_recurrence(struct command *cmd,
* - MUST use the request's `amount` as the *base invoice
* amount*.
*/
base_inv_amount = amount_msat(*ir->invreq->amount);
base_inv_amount = amount_msat(*ir->invreq->invreq_amount);
}
/* This may be adjusted by recurrence if proportional_amount set */
ir->inv->amount = tal_dup(ir->inv, u64,
&base_inv_amount.millisatoshis); /* Raw: wire protocol */
ir->inv->invoice_amount = tal_dup(ir->inv, u64,
&base_inv_amount.millisatoshis); /* Raw: wire protocol */
/* Last of all, we handle recurrence details, which often requires
* further lookups. */
/* BOLT-offers-recurrence #12:
* - MUST set (or not set) `recurrence_counter` exactly as the
* invoice_request did.
*/
if (ir->invreq->recurrence_counter) {
ir->inv->recurrence_counter = ir->invreq->recurrence_counter;
if (ir->inv->invreq_recurrence_counter) {
return check_previous_invoice(cmd, ir);
}
/* We're happy with 2 hours timeout (default): they can always
@ -764,16 +763,16 @@ static struct command_result *currency_done(struct command *cmd,
if (!msat)
return fail_internalerr(cmd, ir,
"Cannot convert currency %.*s: %.*s",
(int)tal_bytelen(ir->offer->currency),
(const char *)ir->offer->currency,
(int)tal_bytelen(ir->offer->offer_currency),
(const char *)ir->offer->offer_currency,
json_tok_full_len(result),
json_tok_full(buf, result));
if (!json_to_msat(buf, msat, &amount))
return fail_internalerr(cmd, ir,
"Bad convert for currency %.*s: %.*s",
(int)tal_bytelen(ir->offer->currency),
(const char *)ir->offer->currency,
(int)tal_bytelen(ir->offer->offer_currency),
(const char *)ir->offer->offer_currency,
json_tok_full_len(msat),
json_tok_full(buf, msat));
@ -789,7 +788,7 @@ static struct command_result *convert_currency(struct command *cmd,
struct command_result *err;
const struct iso4217_name_and_divisor *iso4217;
assert(ir->offer->currency);
assert(ir->offer->offer_currency);
/* Multiply by quantity *first*, for best precision */
err = invreq_amount_by_quantity(cmd, ir, &raw_amount);
@ -802,14 +801,14 @@ static struct command_result *convert_currency(struct command *cmd,
* - if offer `currency` is not the invoice currency, convert
* to the invoice currency.
*/
iso4217 = find_iso4217(ir->offer->currency,
tal_bytelen(ir->offer->currency));
iso4217 = find_iso4217(ir->offer->offer_currency,
tal_bytelen(ir->offer->offer_currency));
/* We should not create offer with unknown currency! */
if (!iso4217)
return fail_internalerr(cmd, ir,
"Unknown offer currency %.*s",
(int)tal_bytelen(ir->offer->currency),
ir->offer->currency);
(int)tal_bytelen(ir->offer->offer_currency),
ir->offer->offer_currency);
double_amount = (double)raw_amount;
for (size_t i = 0; i < iso4217->minor_unit; i++)
double_amount /= 10;
@ -817,8 +816,8 @@ static struct command_result *convert_currency(struct command *cmd,
req = jsonrpc_request_start(cmd->plugin, cmd, "currencyconvert",
currency_done, error, ir);
json_add_stringn(req->js, "currency",
(const char *)ir->offer->currency,
tal_bytelen(ir->offer->currency));
(const char *)ir->offer->offer_currency,
tal_bytelen(ir->offer->offer_currency));
json_add_primitive_fmt(req->js, "amount", "%f", double_amount);
return send_outreq(cmd->plugin, req);
}
@ -837,8 +836,8 @@ static struct command_result *listoffers_done(struct command *cmd,
/* BOLT-offers #12:
*
* - MUST fail the request if the `offer_id` does not refer to an
* unexpired offer.
* - MUST fail the request if the offer fields do not exactly match a
* valid, unexpired offer.
*/
if (arr->size == 0)
return fail_invreq(cmd, ir, "Unknown offer");
@ -863,6 +862,8 @@ static struct command_result *listoffers_done(struct command *cmd,
json_tok_full_len(offertok),
json_tok_full(buf, offertok));
}
/* FIXME-OFFERS: we have these fields in invreq! */
ir->offer = offer_decode(ir,
buf + b12tok->start,
b12tok->end - b12tok->start,
@ -876,63 +877,65 @@ static struct command_result *listoffers_done(struct command *cmd,
json_tok_full(buf, offertok));
}
if (ir->offer->absolute_expiry
&& time_now().ts.tv_sec >= *ir->offer->absolute_expiry) {
if (ir->offer->offer_absolute_expiry
&& time_now().ts.tv_sec >= *ir->offer->offer_absolute_expiry) {
/* FIXME: do deloffer to disable it */
return fail_invreq(cmd, ir, "Offer expired");
}
/* BOLT-offers #12:
* - if the offer had a `quantity_min` or `quantity_max` field:
* - MUST fail the request if there is no `quantity` field.
* - MUST fail the request if there is `quantity` is not within
* that (inclusive) range.
* - if `offer_quantity_max` is present:
* - MUST fail the request if there is no `invreq_quantity` field.
* - if `offer_quantity_max` is non-zero:
* - MUST fail the request if `invreq_quantity` is zero, OR greater than
* `offer_quantity_max`.
* - otherwise:
* - MUST fail the request if there is a `quantity` field.
* - MUST fail the request if there is an `invreq_quantity` field.
*/
if (ir->offer->quantity_min || ir->offer->quantity_max) {
err = invreq_must_have(cmd, ir, quantity);
if (ir->offer->offer_quantity_max) {
err = invreq_must_have(cmd, ir, invreq_quantity);
if (err)
return err;
if (ir->offer->quantity_min &&
*ir->invreq->quantity < *ir->offer->quantity_min) {
if (*ir->invreq->invreq_quantity == 0)
return fail_invreq(cmd, ir,
"quantity %"PRIu64 " < %"PRIu64,
*ir->invreq->quantity,
*ir->offer->quantity_min);
}
"quantity zero invalid");
if (ir->offer->quantity_max &&
*ir->invreq->quantity > *ir->offer->quantity_max) {
if (*ir->offer->offer_quantity_max &&
*ir->invreq->invreq_quantity > *ir->offer->offer_quantity_max) {
return fail_invreq(cmd, ir,
"quantity %"PRIu64" > %"PRIu64,
*ir->invreq->quantity,
*ir->offer->quantity_max);
*ir->invreq->invreq_quantity,
*ir->offer->offer_quantity_max);
}
} else {
err = invreq_must_not_have(cmd, ir, quantity);
err = invreq_must_not_have(cmd, ir, invreq_quantity);
if (err)
return err;
}
/* BOLT-offers #12:
* - MUST fail the request if `invreq_signature` is not correct as
* detailed in [Signature Calculation](#signature-calculation) using
* the `invreq_payer_id`.
*/
err = invreq_must_have(cmd, ir, signature);
if (err)
return err;
if (!check_payer_sig(cmd, ir->invreq,
ir->invreq->payer_key,
ir->invreq->invreq_payer_id,
ir->invreq->signature)) {
return fail_invreq(cmd, ir, "bad signature");
}
if (ir->offer->recurrence) {
if (ir->offer->offer_recurrence) {
/* BOLT-offers-recurrence #12:
*
* - if the offer had a `recurrence`:
* - MUST fail the request if there is no `recurrence_counter`
* field.
*/
err = invreq_must_have(cmd, ir, recurrence_counter);
err = invreq_must_have(cmd, ir, invreq_recurrence_counter);
if (err)
return err;
} else {
@ -943,78 +946,54 @@ static struct command_result *listoffers_done(struct command *cmd,
* - MUST fail the request if there is a `recurrence_start`
* field.
*/
err = invreq_must_not_have(cmd, ir, recurrence_counter);
err = invreq_must_not_have(cmd, ir, invreq_recurrence_counter);
if (err)
return err;
err = invreq_must_not_have(cmd, ir, recurrence_start);
err = invreq_must_not_have(cmd, ir, invreq_recurrence_start);
if (err)
return err;
}
ir->inv = tlv_invoice_new(cmd);
/* BOLT-offers #12:
* - if the chain for the invoice is not solely bitcoin:
* - MUST specify `chains` the offer is valid for.
* The writer:
* - MUST copy all non-signature fields from the invreq (including
* unknown fields).
*/
if (!streq(chainparams->network_name, "bitcoin")) {
ir->inv->chain = tal_dup(ir->inv, struct bitcoin_blkid,
&chainparams->genesis_blockhash);
}
ir->inv = invoice_for_invreq(cmd, ir->invreq);
assert(ir->inv->invreq_payer_id);
/* BOLT-offers #12:
* - MUST set `offer_id` to the id of the offer.
* - if `offer_node_id` is present:
* - MUST set `invoice_node_id` to `offer_node_id`.
*/
/* Which is the same as the invreq */
ir->inv->offer_id = tal_dup(ir->inv, struct sha256,
ir->invreq->offer_id);
ir->inv->description = tal_dup_talarr(ir->inv, char,
ir->offer->description);
ir->inv->features = tal_dup_talarr(ir->inv, u8,
plugin_feature_set(cmd->plugin)
->bits[BOLT11_FEATURE]);
/* FIXME: Insert paths and payinfo */
ir->inv->issuer = tal_dup_talarr(ir->inv, char, ir->offer->issuer);
ir->inv->node_id = tal_dup(ir->inv, struct pubkey, ir->offer->node_id);
/* BOLT-offers #12:
* - MUST set (or not set) `quantity` exactly as the invoice_request
* did.
*/
if (ir->offer->quantity_min || ir->offer->quantity_max)
ir->inv->quantity = tal_dup(ir->inv, u64, ir->invreq->quantity);
/* We always provide an offer_node_id! */
ir->inv->invoice_node_id = ir->inv->offer_node_id;
/* BOLT-offers #12:
* - MUST set `payer_key` exactly as the invoice_request did.
* - MUST set `invoice_created_at` to the number of seconds since
* Midnight 1 January 1970, UTC when the offer was created.
*/
ir->inv->payer_key = tal_dup(ir->inv, struct pubkey,
ir->invreq->payer_key);
ir->inv->invoice_created_at = tal(ir->inv, u64);
*ir->inv->invoice_created_at = time_now().ts.tv_sec;
/* BOLT-offers #12:
* - MUST set (or not set) `payer_info` exactly as the invoice_request
* did.
* - MUST set `invoice_payment_hash` to the SHA256 hash of the
* `payment_preimage` that will be given in return for payment.
*/
ir->inv->payer_info
= tal_dup_talarr(ir->inv, u8, ir->invreq->payer_info);
/* BOLT-offers #12:
* - MUST set (or not set) `payer_note` exactly as the invoice_request
* did, or MUST not set it.
*/
/* i.e. we don't have to do anything, but we do. */
ir->inv->payer_note
= tal_dup_talarr(ir->inv, char, ir->invreq->payer_note);
randombytes_buf(&ir->preimage, sizeof(ir->preimage));
ir->inv->payment_hash = tal(ir->inv, struct sha256);
sha256(ir->inv->payment_hash, &ir->preimage, sizeof(ir->preimage));
ir->inv->invoice_payment_hash = tal(ir->inv, struct sha256);
sha256(ir->inv->invoice_payment_hash,
&ir->preimage, sizeof(ir->preimage));
ir->inv->cltv = tal_dup(ir->inv, u16, &cltv_final);
ir->inv->created_at = tal(ir->inv, u64);
*ir->inv->created_at = time_now().ts.tv_sec;
/* BOLT-offers #12:
* - or if it allows multiple parts to pay the invoice:
* - MUST set `invoice_features`.`features` bit `MPP/optional`
*/
ir->inv->invoice_features
= plugin_feature_set(cmd->plugin)->bits[BOLT12_INVOICE_FEATURE];
/* We may require currency lookup; if so, do it now. */
if (ir->offer->amount && ir->offer->currency)
if (ir->offer->offer_amount && ir->offer->offer_currency)
return convert_currency(cmd, ir);
err = invreq_base_amount_simple(cmd, ir, &amt);
@ -1023,25 +1002,19 @@ static struct command_result *listoffers_done(struct command *cmd,
return handle_amount_and_recurrence(cmd, ir, amt);
}
static struct command_result *handle_offerless_request(struct command *cmd,
struct invreq *ir)
{
/* FIXME: shut up and take their money! */
return fail_internalerr(cmd, ir, "FIXME: handle offerless req!");
}
struct command_result *handle_invoice_request(struct command *cmd,
const u8 *invreqbin,
struct blinded_path *reply_path)
{
size_t len = tal_count(invreqbin);
const u8 *cursor = invreqbin;
struct invreq *ir = tal(cmd, struct invreq);
struct out_req *req;
int bad_feature;
ir->reply_path = tal_steal(ir, reply_path);
ir->invreq = fromwire_tlv_invoice_request(cmd, &invreqbin, &len);
ir->invreq = fromwire_tlv_invoice_request(cmd, &cursor, &len);
if (!ir->invreq) {
return fail_invreq(cmd, ir,
"Invalid invreq %s",
@ -1050,13 +1023,39 @@ struct command_result *handle_invoice_request(struct command *cmd,
/* BOLT-offers #12:
*
* The reader of an invoice_request:
* The reader:
* - MUST fail the request if `invreq_payer_id` or `invreq_metadata`
* are not present.
*/
if (!ir->invreq->invreq_payer_id)
return fail_invreq(cmd, ir, "Missing invreq_payer_id");
if (!ir->invreq->invreq_metadata)
return fail_invreq(cmd, ir, "Missing invreq_metadata");
/* BOLT-offers #12:
* The reader:
* ...
* - MUST fail the request if any non-signature TLV fields greater or
* equal to 160.
*/
/* BOLT-offers #12:
* Each form is signed using one or more *signature TLV elements*:
* TLV types 240 through 1000 (inclusive)
*/
if (tlv_span(invreqbin, 0, 159, NULL)
+ tlv_span(invreqbin, 240, 1000, NULL) != tal_bytelen(invreqbin))
return fail_invreq(cmd, ir, "Fields beyond 160");
/* BOLT-offers #12:
*
* The reader of an invreq:
*...
* - MUST fail the request if `features` contains unknown even bits.
* - if `invreq_features` contains unknown _even_ bits that are non-zero:
* - MUST fail the request.
*/
bad_feature = features_unsupported(plugin_feature_set(cmd->plugin),
ir->invreq->features,
BOLT11_FEATURE);
ir->invreq->invreq_features,
BOLT12_INVREQ_FEATURE);
if (bad_feature != -1) {
return fail_invreq(cmd, ir,
"Unsupported invreq feature %i",
@ -1065,34 +1064,41 @@ struct command_result *handle_invoice_request(struct command *cmd,
/* BOLT-offers #12:
*
* The reader of an invoice_request:
* The reader:
*...
* - if `chain` is not present:
* - MUST fail the request if bitcoin is not a supported chain.
* - otherwise:
* - MUST fail the request if `chain` is not a supported chain.
* - if `invreq_chain` is not present:
* - MUST fail the request if bitcoin is not a supported chain.
* - otherwise:
* - MUST fail the request if `invreq_chain`.`chain` is not a
* supported chain.
*/
if (!bolt12_chain_matches(ir->invreq->chain, chainparams)) {
if (!bolt12_chain_matches(ir->invreq->invreq_chain, chainparams)) {
return fail_invreq(cmd, ir,
"Wrong chain %s",
tal_hex(tmpctx, ir->invreq->chain));
tal_hex(tmpctx, ir->invreq->invreq_chain));
}
/* BOLT-offers #12:
*
* The reader of an invoice_request:
* - MUST fail the request if `payer_key` is not present.
* - otherwise (no `offer_node_id`, not a response to our offer):
*/
if (!ir->invreq->payer_key)
return fail_invreq(cmd, ir, "Missing payer key");
/* FIXME-OFFERS: handle this! */
if (!ir->invreq->offer_node_id) {
return fail_invreq(cmd, ir, "Not based on an offer");
}
if (!ir->invreq->offer_id)
return handle_offerless_request(cmd, ir);
/* BOLT-offers #12:
*
* - if `offer_node_id` is present (response to an offer):
* - MUST fail the request if the offer fields do not exactly match a
* valid, unexpired offer.
*/
invreq_offer_id(ir->invreq, &ir->offer_id);
/* Now, look up offer */
req = jsonrpc_request_start(cmd->plugin, cmd, "listoffers",
listoffers_done, error, ir);
json_add_sha256(req->js, "offer_id", ir->invreq->offer_id);
json_add_sha256(req->js, "offer_id", &ir->offer_id);
return send_outreq(cmd->plugin, req);
}

View File

@ -21,7 +21,7 @@ static bool msat_or_any(const char *buffer,
buffer + tok->start, tok->end - tok->start))
return false;
offer->amount = tal_dup(offer, u64,
offer->offer_amount = tal_dup(offer, u64,
&msat.millisatoshis); /* Raw: other currencies */
return true;
}
@ -39,7 +39,7 @@ static struct command_result *param_amount(struct command *cmd,
if (msat_or_any(buffer, tok, offer))
return NULL;
offer->amount = tal(offer, u64);
offer->offer_amount = tal(offer, u64);
/* BOLT-offers #12:
*
@ -58,7 +58,7 @@ static struct command_result *param_amount(struct command *cmd,
ISO4217_NAMELEN,
buffer + tok->end - ISO4217_NAMELEN);
offer->currency
offer->offer_currency
= tal_dup_arr(offer, utf8, isocode->name, ISO4217_NAMELEN, 0);
number = *tok;
@ -77,19 +77,19 @@ static struct command_result *param_amount(struct command *cmd,
"Bad minor units");
}
if (!json_to_u64(buffer, &whole, offer->amount))
if (!json_to_u64(buffer, &whole, offer->offer_amount))
return command_fail_badparam(cmd, name, buffer, tok,
"should be 'any', msatoshis or <ISO-4712><amount>[.<amount>]");
for (size_t i = 0; i < isocode->minor_unit; i++) {
if (mul_overflows_u64(*offer->amount, 10))
if (mul_overflows_u64(*offer->offer_amount, 10))
return command_fail_badparam(cmd, name, buffer,
&whole,
"excessively large value");
*offer->amount *= 10;
*offer->offer_amount *= 10;
}
*offer->amount += cents;
*offer->offer_amount += cents;
return NULL;
}
@ -139,8 +139,7 @@ static struct command_result *param_recurrence(struct command *cmd,
const char *name,
const char *buffer,
const jsmntok_t *tok,
struct tlv_offer_recurrence
**recurrence)
struct recurrence **recurrence)
{
u32 mul;
const struct time_string *ts;
@ -150,7 +149,7 @@ static struct command_result *param_recurrence(struct command *cmd,
return command_fail_badparam(cmd, name, buffer, tok,
"not a valid time");
*recurrence = tal(cmd, struct tlv_offer_recurrence);
*recurrence = tal(cmd, struct recurrence);
(*recurrence)->time_unit = ts->unit;
(*recurrence)->period = ts->mul * mul;
return NULL;
@ -160,12 +159,12 @@ static struct command_result *param_recurrence_base(struct command *cmd,
const char *name,
const char *buffer,
const jsmntok_t *tok,
struct tlv_offer_recurrence_base **base)
struct recurrence_base **base)
{
/* Make copy so we can manipulate it */
jsmntok_t t = *tok;
*base = tal(cmd, struct tlv_offer_recurrence_base);
*base = tal(cmd, struct recurrence_base);
if (json_tok_startswith(buffer, &t, "@")) {
t.start++;
(*base)->start_any_period = false;
@ -183,12 +182,12 @@ static struct command_result *param_recurrence_paywindow(struct command *cmd,
const char *name,
const char *buffer,
const jsmntok_t *tok,
struct tlv_offer_recurrence_paywindow
struct recurrence_paywindow
**paywindow)
{
jsmntok_t t, before, after;
*paywindow = tal(cmd, struct tlv_offer_recurrence_paywindow);
*paywindow = tal(cmd, struct recurrence_paywindow);
t = *tok;
if (json_tok_endswith(buffer, &t, "%")) {
(*paywindow)->proportional_amount = true;
@ -231,7 +230,7 @@ static struct command_result *check_result(struct command *cmd,
&active)) {
return command_fail(cmd,
LIGHTNINGD,
"Bad creaoffer status reply %.*s",
"Bad createoffer status reply %.*s",
json_tok_full_len(result),
json_tok_full(buf, result));
}
@ -289,19 +288,18 @@ struct command_result *json_offer(struct command *cmd,
p_req("description", param_escaped_string, &desc),
p_opt("issuer", param_escaped_string, &issuer),
p_opt("label", param_escaped_string, &offinfo->label),
p_opt("quantity_min", param_u64, &offer->quantity_min),
p_opt("quantity_max", param_u64, &offer->quantity_max),
p_opt("absolute_expiry", param_u64, &offer->absolute_expiry),
p_opt("recurrence", param_recurrence, &offer->recurrence),
p_opt("quantity_max", param_u64, &offer->offer_quantity_max),
p_opt("absolute_expiry", param_u64, &offer->offer_absolute_expiry),
p_opt("recurrence", param_recurrence, &offer->offer_recurrence),
p_opt("recurrence_base",
param_recurrence_base,
&offer->recurrence_base),
&offer->offer_recurrence_base),
p_opt("recurrence_paywindow",
param_recurrence_paywindow,
&offer->recurrence_paywindow),
&offer->offer_recurrence_paywindow),
p_opt("recurrence_limit",
param_number,
&offer->recurrence_limit),
&offer->offer_recurrence_limit),
p_opt_def("single_use", param_bool,
&offinfo->single_use, false),
/* FIXME: hints support! */
@ -312,66 +310,66 @@ struct command_result *json_offer(struct command *cmd,
return command_fail(cmd, LIGHTNINGD,
"experimental-offers not enabled");
/* BOLT-offers #12:
* - MUST NOT set `quantity_min` or `quantity_max` less than 1.
*/
if (offer->quantity_min && *offer->quantity_min < 1)
return command_fail_badparam(cmd, "quantity_min",
buffer, params,
"must be >= 1");
if (offer->quantity_max && *offer->quantity_max < 1)
/* Doesn't make sense to have max quantity 1. */
if (offer->offer_quantity_max && *offer->offer_quantity_max == 1)
return command_fail_badparam(cmd, "quantity_max",
buffer, params,
"must be >= 1");
/* BOLT-offers #12:
* - if both:
* - MUST set `quantity_min` less than or equal to `quantity_max`.
*/
if (offer->quantity_min && offer->quantity_max) {
if (*offer->quantity_min > *offer->quantity_max)
return command_fail_badparam(cmd, "quantity_min",
buffer, params,
"must be <= quantity_max");
}
"must be 0 or > 1");
/* BOLT-offers #12:
*
* - if the chain for the invoice is not solely bitcoin:
* - MUST specify `chains` the offer is valid for.
* - MUST specify `offer_chains` the offer is valid for.
* - otherwise:
* - the bitcoin chain is implied as the first and only entry.
* - MAY omit `offer_chains`, implying that bitcoin is only chain.
*/
if (!streq(chainparams->network_name, "bitcoin")) {
offer->chains = tal_arr(offer, struct bitcoin_blkid, 1);
offer->chains[0] = chainparams->genesis_blockhash;
offer->offer_chains = tal_arr(offer, struct bitcoin_blkid, 1);
offer->offer_chains[0] = chainparams->genesis_blockhash;
}
if (!offer->recurrence) {
if (offer->recurrence_limit)
if (!offer->offer_recurrence) {
if (offer->offer_recurrence_limit)
return command_fail_badparam(cmd, "recurrence_limit",
buffer, params,
"needs recurrence");
if (offer->recurrence_base)
if (offer->offer_recurrence_base)
return command_fail_badparam(cmd, "recurrence_base",
buffer, params,
"needs recurrence");
if (offer->recurrence_paywindow)
if (offer->offer_recurrence_paywindow)
return command_fail_badparam(cmd, "recurrence_paywindow",
buffer, params,
"needs recurrence");
}
offer->description = tal_dup_arr(offer, char, desc, strlen(desc), 0);
/* BOLT-offers #12:
* - MUST set `offer_description` to a complete description of the
* purpose of the payment.
*/
offer->offer_description
= tal_dup_arr(offer, char, desc, strlen(desc), 0);
/* BOLT-offers #12:
* - if it sets `offer_issuer`:
* - SHOULD set it to identify the issuer of the invoice clearly.
* - if it includes a domain name:
* - SHOULD begin it with either user@domain or domain
* - MAY follow with a space and more text
*/
if (issuer) {
offer->issuer
offer->offer_issuer
= tal_dup_arr(offer, char, issuer, strlen(issuer), 0);
}
offer->node_id = tal_dup(offer, struct pubkey, &id);
/* BOLT-offers #12:
* - MUST set `offer_node_id` to the node's public key to request the
* invoice from.
*/
offer->offer_node_id = tal_dup(offer, struct pubkey, &id);
/* If they specify a different currency, warn if we can't
* convert it! */
if (offer->currency) {
if (offer->offer_currency) {
struct out_req *req;
req = jsonrpc_request_start(cmd->plugin, cmd, "currencyconvert",
@ -379,8 +377,8 @@ struct command_result *json_offer(struct command *cmd,
offinfo);
json_add_u32(req->js, "amount", 1);
json_add_stringn(req->js, "currency",
(const char *)offer->currency,
tal_bytelen(offer->currency));
(const char *)offer->offer_currency,
tal_bytelen(offer->offer_currency));
return send_outreq(cmd->plugin, req);
}

View File

@ -1092,83 +1092,73 @@ static struct command_result *json_pay(struct command *cmd,
/* p->features = tal_steal(p, b12->features); */
p->features = NULL;
if (!b12->node_id)
if (!b12->invoice_node_id)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"invoice missing node_id");
if (!b12->payment_hash)
if (!b12->invoice_payment_hash)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"invoice missing payment_hash");
if (!b12->created_at)
if (!b12->invoice_created_at)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"invoice missing created_at");
if (b12->amount) {
invmsat = tal(cmd, struct amount_msat);
*invmsat = amount_msat(*b12->amount);
} else
invmsat = NULL;
if (!b12->invoice_amount)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"invoice missing invoice_amount");
invmsat = tal(cmd, struct amount_msat);
*invmsat = amount_msat(*b12->invoice_amount);
p->destination = tal(p, struct node_id);
node_id_from_pubkey(p->destination, b12->node_id);
p->payment_hash = tal_dup(p, struct sha256, b12->payment_hash);
if (b12->recurrence_counter && !label)
node_id_from_pubkey(p->destination, b12->invoice_node_id);
p->payment_hash = tal_dup(p, struct sha256,
b12->invoice_payment_hash);
if (b12->invreq_recurrence_counter && !label)
return command_fail(
cmd, JSONRPC2_INVALID_PARAMS,
"recurring invoice requires a label");
/* BOLT-offers #12:
* - MUST reject the invoice if `blindedpay` is not present.
* - MUST reject the invoice if `invoice_paths` is not present
* or is empty.
*/
/* FIXME: We allow this for now. */
if (tal_count(b12->invoice_paths) == 0)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"invoice missing invoice_paths");
if (tal_count(b12->paths) != 0) {
/* BOLT-offers #12: - MUST reject the invoice if
* `blindedpay` does not contain exactly one
* `blinded_payinfo` per `blinded_path`.
*/
if (tal_count(b12->paths) != tal_count(b12->blindedpay)) {
return command_fail(
cmd, JSONRPC2_INVALID_PARAMS,
"Wrong blinding info: %zu paths, %zu payinfo",
tal_count(b12->paths),
tal_count(b12->blindedpay));
}
/* FIXME: do MPP across these! We choose first one. */
p->blindedpath = tal_steal(p, b12->paths[0]);
p->blindedpay = tal_steal(p, b12->blindedpay[0]);
/* Set destination to introduction point */
node_id_from_pubkey(p->destination, &p->blindedpath->first_node_id);
} else {
/* FIXME payment_secret should be signature! */
struct sha256 merkle;
p->payment_secret = tal(p, struct secret);
merkle_tlv(b12->fields, &merkle);
memcpy(p->payment_secret, &merkle, sizeof(merkle));
BUILD_ASSERT(sizeof(*p->payment_secret) ==
sizeof(merkle));
/* BOLT-offers #12:
* - MUST reject the invoice if `invoice_blindedpay` does not
* contain exactly one `blinded_payinfo` per
* `invoice_paths`.`blinded_path`. */
if (tal_count(b12->invoice_paths)
!= tal_count(b12->invoice_blindedpay)) {
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"Wrong blinding info: %zu paths, %zu payinfo",
tal_count(b12->invoice_paths),
tal_count(b12->invoice_blindedpay));
}
/* FIXME: do MPP across these! We choose first one. */
p->blindedpath = tal_steal(p, b12->invoice_paths[0]);
p->blindedpay = tal_steal(p, b12->invoice_blindedpay[0]);
p->min_final_cltv_expiry = p->blindedpay->cltv_expiry_delta;
/* Set destination to introduction point */
node_id_from_pubkey(p->destination, &p->blindedpath->first_node_id);
p->payment_metadata = NULL;
p->routes = NULL;
if (b12->cltv)
p->min_final_cltv_expiry = *b12->cltv;
else
p->min_final_cltv_expiry = 18;
/* BOLT-offers #12:
* - if `relative_expiry` is present:
* - if `invoice_relative_expiry` is present:
* - MUST reject the invoice if the current time since
* 1970-01-01 UTC is greater than `created_at` plus
* 1970-01-01 UTC is greater than `invoice_created_at` plus
* `seconds_from_creation`.
* - otherwise:
* - MUST reject the invoice if the current time since
* 1970-01-01 UTC is greater than `created_at` plus
* 7200.
* 1970-01-01 UTC is greater than `invoice_created_at` plus
* 7200.
*/
if (b12->relative_expiry)
invexpiry = *b12->created_at + *b12->relative_expiry;
if (b12->invoice_relative_expiry)
invexpiry = *b12->invoice_created_at + *b12->invoice_relative_expiry;
else
invexpiry = *b12->created_at + BOLT12_DEFAULT_REL_EXPIRY;
invexpiry = *b12->invoice_created_at + BOLT12_DEFAULT_REL_EXPIRY;
p->local_offer_id = tal_steal(p, local_offer_id);
}

View File

@ -4383,7 +4383,7 @@ def test_offer_needs_option(node_factory):
l1.rpc.call('fetchinvoice', {'offer': 'aaaa'})
# Decode still works though
assert l1.rpc.decode('lno1qgsqvgnwgcg35z6ee2h3yczraddm72xrfua9uve2rlrm9deu7xyfzrcgqyys5qq7yypxdeze35wncs2l2u4gfzyrpds00e6yakfrt6ctrw5n9qanzhqr2x8sgp3lqxpxd82j87j67wyff9cd9msgagq8hveftdkx5t3e98gj2x7ac99hhwlpj9yvj79yz3l8gdlmdmhq47ct9pkedfd8naksd8f8gpar')['valid']
assert l1.rpc.decode('lno1qgsqvgnwgcg35z6ee2h3yczraddm72xrfua9uve2rlrm9deu7xyfzrcgqyqs5pr5v4ehg93pqfnwgkvdr57yzh6h92zg3qctvrm7w38djg67kzcm4yeg8vc4cq63s')['valid']
def test_offer(node_factory, bitcoind):
@ -4449,14 +4449,14 @@ def test_offer(node_factory, bitcoind):
offer['bolt12']]).decode('UTF-8')
assert 'issuer: ' + weird_issuer in output
# Test quantity min/max
# Test quantity
ret = l1.rpc.call('offer', {'amount': '100000sat',
'description': 'quantity_min test',
'quantity_min': 1})
'description': 'quantity_max existence test',
'quantity_max': 0})
offer = only_one(l1.rpc.call('listoffers', [ret['offer_id']])['offers'])
output = subprocess.check_output([bolt12tool, 'decode',
offer['bolt12']]).decode('UTF-8')
assert 'quantity_min: 1' in output
assert 'quantity_max: 0' in output
ret = l1.rpc.call('offer', {'amount': '100000sat',
'description': 'quantity_max test',
@ -4466,26 +4466,6 @@ def test_offer(node_factory, bitcoind):
offer['bolt12']]).decode('UTF-8')
assert 'quantity_max: 2' in output
# BOLT-offers #12:
# * - MUST NOT set `quantity_min` or `quantity_max` less than 1.
with pytest.raises(RpcError, match='quantity_min: must be >= 1'):
ret = l1.rpc.call('offer', {'amount': '100000sat',
'description': 'quantity_min test',
'quantity_min': 0})
with pytest.raises(RpcError, match='quantity_max: must be >= 1'):
ret = l1.rpc.call('offer', {'amount': '100000sat',
'description': 'quantity_max test',
'quantity_max': 0})
# BOLT-offers #12:
# - if both:
# - MUST set `quantity_min` greater or equal to `quantity_max`.
with pytest.raises(RpcError, match='quantity_min: must be <= quantity_max'):
ret = l1.rpc.call('offer', {'amount': '100000sat',
'description': 'quantity_max test',
'quantity_min': 10,
'quantity_max': 9})
# Test absolute_expiry
exp = int(time.time() + 2)
ret = l1.rpc.call('offer', {'amount': '100000sat',
@ -5241,5 +5221,5 @@ def test_payerkey(node_factory):
"03a3bbda0137722ba62207b9d3e5e6cc2a11e58480f801892093e01383aacb7fb2"]
for n, k in zip(nodes, expected_keys):
b12 = n.rpc.createinvoicerequest('lnr1qvsqvgnwgcg35z6ee2h3yczraddm72xrfua9uve2rlrm9deu7xyfzrcyyrjjthf4rh99n7equvlrzrlalcacxj4y9hgzxc79yrntrth6mp3nkvssy5mac4pkfq2m3gq4ttajwh097s')['bolt12']
b12 = n.rpc.createinvoicerequest('lnr1qqgz2d7u2smys9dc5q2447e8thjlgq3qqc3xu3s3rg94nj40zfsy866mhu5vxne6tcej5878k2mneuvgjy8ssqvepgz5zsjrg3z3vggzvkm2khkgvrxj27r96c00pwl4kveecdktm29jdd6w0uwu5jgtv5v9qgqxyfhyvyg6pdvu4tcjvpp7kkal9rp57wj7xv4pl3ajku70rzy3pu')['bolt12']
assert n.rpc.decode(b12)['payer_key'] == k

View File

@ -1,119 +1,163 @@
tlvtype,offer,chains,2
tlvdata,offer,chains,chains,chain_hash,...
tlvtype,offer,currency,6
tlvdata,offer,currency,iso4217,utf8,...
tlvtype,offer,amount,8
tlvdata,offer,amount,amount,tu64,
tlvtype,offer,description,10
tlvdata,offer,description,description,utf8,...
tlvtype,offer,features,12
tlvdata,offer,features,features,byte,...
tlvtype,offer,absolute_expiry,14
tlvdata,offer,absolute_expiry,seconds_from_epoch,tu64,
tlvtype,offer,paths,16
tlvdata,offer,paths,paths,blinded_path,...
tlvtype,offer,issuer,20
tlvdata,offer,issuer,issuer,utf8,...
tlvtype,offer,quantity_min,22
tlvdata,offer,quantity_min,min,tu64,
tlvtype,offer,quantity_max,24
tlvdata,offer,quantity_max,max,tu64,
tlvtype,offer,recurrence,26
tlvdata,offer,recurrence,time_unit,byte,
tlvdata,offer,recurrence,period,tu32,
tlvtype,offer,recurrence_paywindow,64
tlvdata,offer,recurrence_paywindow,seconds_before,u32,
tlvdata,offer,recurrence_paywindow,proportional_amount,byte,
tlvdata,offer,recurrence_paywindow,seconds_after,tu32,
tlvtype,offer,recurrence_limit,66
tlvdata,offer,recurrence_limit,max_period,tu32,
tlvtype,offer,recurrence_base,28
tlvdata,offer,recurrence_base,start_any_period,byte,
tlvdata,offer,recurrence_base,basetime,tu64,
tlvtype,offer,node_id,30
tlvdata,offer,node_id,node_id,point,
tlvtype,offer,send_invoice,54
tlvtype,offer,refund_for,34
tlvdata,offer,refund_for,refunded_payment_hash,sha256,
tlvtype,offer,signature,240
tlvdata,offer,signature,sig,bip340sig,
tlvtype,invoice_request,chain,3
tlvdata,invoice_request,chain,chain,chain_hash,
tlvtype,invoice_request,offer_id,4
tlvdata,invoice_request,offer_id,offer_id,sha256,
tlvtype,invoice_request,amount,8
tlvdata,invoice_request,amount,msat,tu64,
tlvtype,invoice_request,features,12
tlvdata,invoice_request,features,features,byte,...
tlvtype,invoice_request,quantity,32
tlvdata,invoice_request,quantity,quantity,tu64,
tlvtype,invoice_request,recurrence_counter,36
tlvdata,invoice_request,recurrence_counter,counter,tu32,
tlvtype,invoice_request,recurrence_start,68
tlvdata,invoice_request,recurrence_start,period_offset,tu32,
tlvtype,invoice_request,payer_key,38
tlvdata,invoice_request,payer_key,key,point,
tlvtype,invoice_request,payer_note,39
tlvdata,invoice_request,payer_note,note,utf8,...
tlvtype,invoice_request,payer_info,50
tlvdata,invoice_request,payer_info,blob,byte,...
tlvtype,invoice_request,replace_invoice,56
tlvdata,invoice_request,replace_invoice,payment_hash,sha256,
tlvtype,offer,offer_chains,2
tlvdata,offer,offer_chains,chains,chain_hash,...
tlvtype,offer,offer_metadata,4
tlvdata,offer,offer_metadata,data,byte,...
tlvtype,offer,offer_currency,6
tlvdata,offer,offer_currency,iso4217,utf8,...
tlvtype,offer,offer_amount,8
tlvdata,offer,offer_amount,amount,tu64,
tlvtype,offer,offer_description,10
tlvdata,offer,offer_description,description,utf8,...
tlvtype,offer,offer_features,12
tlvdata,offer,offer_features,features,byte,...
tlvtype,offer,offer_absolute_expiry,14
tlvdata,offer,offer_absolute_expiry,seconds_from_epoch,tu64,
tlvtype,offer,offer_paths,16
tlvdata,offer,offer_paths,paths,blinded_path,...
tlvtype,offer,offer_issuer,18
tlvdata,offer,offer_issuer,issuer,utf8,...
tlvtype,offer,offer_quantity_max,20
tlvdata,offer,offer_quantity_max,max,tu64,
tlvtype,offer,offer_node_id,22
tlvdata,offer,offer_node_id,node_id,point,
tlvtype,offer,offer_recurrence,26
tlvdata,offer,offer_recurrence,recurrence,recurrence,
tlvtype,offer,offer_recurrence_paywindow,28
tlvdata,offer,offer_recurrence_paywindow,paywindow,recurrence_paywindow,
tlvtype,offer,offer_recurrence_limit,30
tlvdata,offer,offer_recurrence_limit,max_period,tu32,
tlvtype,offer,offer_recurrence_base,32
tlvdata,offer,offer_recurrence_base,base,recurrence_base,
tlvtype,invoice_request,invreq_metadata,0
tlvdata,invoice_request,invreq_metadata,blob,byte,...
tlvtype,invoice_request,offer_chains,2
tlvdata,invoice_request,offer_chains,chains,chain_hash,...
tlvtype,invoice_request,offer_metadata,4
tlvdata,invoice_request,offer_metadata,data,byte,...
tlvtype,invoice_request,offer_currency,6
tlvdata,invoice_request,offer_currency,iso4217,utf8,...
tlvtype,invoice_request,offer_amount,8
tlvdata,invoice_request,offer_amount,amount,tu64,
tlvtype,invoice_request,offer_description,10
tlvdata,invoice_request,offer_description,description,utf8,...
tlvtype,invoice_request,offer_features,12
tlvdata,invoice_request,offer_features,features,byte,...
tlvtype,invoice_request,offer_absolute_expiry,14
tlvdata,invoice_request,offer_absolute_expiry,seconds_from_epoch,tu64,
tlvtype,invoice_request,offer_paths,16
tlvdata,invoice_request,offer_paths,paths,blinded_path,...
tlvtype,invoice_request,offer_issuer,18
tlvdata,invoice_request,offer_issuer,issuer,utf8,...
tlvtype,invoice_request,offer_quantity_max,20
tlvdata,invoice_request,offer_quantity_max,max,tu64,
tlvtype,invoice_request,offer_node_id,22
tlvdata,invoice_request,offer_node_id,node_id,point,
tlvtype,invoice_request,offer_recurrence,26
tlvdata,invoice_request,offer_recurrence,recurrence,recurrence,
tlvtype,invoice_request,offer_recurrence_paywindow,28
tlvdata,invoice_request,offer_recurrence_paywindow,paywindow,recurrence_paywindow,
tlvtype,invoice_request,offer_recurrence_limit,30
tlvdata,invoice_request,offer_recurrence_limit,max_period,tu32,
tlvtype,invoice_request,offer_recurrence_base,32
tlvdata,invoice_request,offer_recurrence_base,base,recurrence_base,
tlvtype,invoice_request,invreq_chain,80
tlvdata,invoice_request,invreq_chain,chain,chain_hash,
tlvtype,invoice_request,invreq_amount,82
tlvdata,invoice_request,invreq_amount,msat,tu64,
tlvtype,invoice_request,invreq_features,84
tlvdata,invoice_request,invreq_features,features,byte,...
tlvtype,invoice_request,invreq_quantity,86
tlvdata,invoice_request,invreq_quantity,quantity,tu64,
tlvtype,invoice_request,invreq_payer_id,88
tlvdata,invoice_request,invreq_payer_id,key,point,
tlvtype,invoice_request,invreq_payer_note,89
tlvdata,invoice_request,invreq_payer_note,note,utf8,...
tlvtype,invoice_request,invreq_recurrence_counter,90
tlvdata,invoice_request,invreq_recurrence_counter,counter,tu32,
tlvtype,invoice_request,invreq_recurrence_start,92
tlvdata,invoice_request,invreq_recurrence_start,period_offset,tu32,
tlvtype,invoice_request,signature,240
tlvdata,invoice_request,signature,sig,bip340sig,
tlvtype,invoice,chain,3
tlvdata,invoice,chain,chain,chain_hash,
tlvtype,invoice,offer_id,4
tlvdata,invoice,offer_id,offer_id,sha256,
tlvtype,invoice,amount,8
tlvdata,invoice,amount,msat,tu64,
tlvtype,invoice,description,10
tlvdata,invoice,description,description,utf8,...
tlvtype,invoice,features,12
tlvdata,invoice,features,features,byte,...
tlvtype,invoice,paths,16
tlvdata,invoice,paths,paths,blinded_path,...
tlvtype,invoice,blindedpay,18
tlvdata,invoice,blindedpay,payinfo,blinded_payinfo,...
tlvtype,invoice,blinded_capacities,19
tlvdata,invoice,blinded_capacities,incoming_msat,u64,...
tlvtype,invoice,issuer,20
tlvdata,invoice,issuer,issuer,utf8,...
tlvtype,invoice,node_id,30
tlvdata,invoice,node_id,node_id,point,
tlvtype,invoice,quantity,32
tlvdata,invoice,quantity,quantity,tu64,
tlvtype,invoice,refund_for,34
tlvdata,invoice,refund_for,refunded_payment_hash,sha256,
tlvtype,invoice,recurrence_counter,36
tlvdata,invoice,recurrence_counter,counter,tu32,
tlvtype,invoice,recurrence_start,68
tlvdata,invoice,recurrence_start,period_offset,tu32,
tlvtype,invoice,recurrence_basetime,64
tlvdata,invoice,recurrence_basetime,basetime,tu64,
tlvtype,invoice,payer_key,38
tlvdata,invoice,payer_key,key,point,
tlvtype,invoice,payer_note,39
tlvdata,invoice,payer_note,note,utf8,...
tlvtype,invoice,created_at,40
tlvdata,invoice,created_at,timestamp,tu64,
tlvtype,invoice,payment_hash,42
tlvdata,invoice,payment_hash,payment_hash,sha256,
tlvtype,invoice,relative_expiry,44
tlvdata,invoice,relative_expiry,seconds_from_creation,tu32,
tlvtype,invoice,cltv,46
tlvdata,invoice,cltv,min_final_cltv_expiry,tu16,
tlvtype,invoice,fallbacks,48
tlvdata,invoice,fallbacks,fallbacks,fallback_address,...
tlvtype,invoice,payer_info,50
tlvdata,invoice,payer_info,blob,byte,...
tlvtype,invoice,refund_signature,52
tlvdata,invoice,refund_signature,payer_signature,bip340sig,
tlvtype,invoice,send_invoice,54
tlvtype,invoice,replace_invoice,56
tlvdata,invoice,replace_invoice,payment_hash,sha256,
tlvtype,invoice,invreq_metadata,0
tlvdata,invoice,invreq_metadata,blob,byte,...
tlvtype,invoice,offer_chains,2
tlvdata,invoice,offer_chains,chains,chain_hash,...
tlvtype,invoice,offer_metadata,4
tlvdata,invoice,offer_metadata,data,byte,...
tlvtype,invoice,offer_currency,6
tlvdata,invoice,offer_currency,iso4217,utf8,...
tlvtype,invoice,offer_amount,8
tlvdata,invoice,offer_amount,amount,tu64,
tlvtype,invoice,offer_description,10
tlvdata,invoice,offer_description,description,utf8,...
tlvtype,invoice,offer_features,12
tlvdata,invoice,offer_features,features,byte,...
tlvtype,invoice,offer_absolute_expiry,14
tlvdata,invoice,offer_absolute_expiry,seconds_from_epoch,tu64,
tlvtype,invoice,offer_paths,16
tlvdata,invoice,offer_paths,paths,blinded_path,...
tlvtype,invoice,offer_issuer,18
tlvdata,invoice,offer_issuer,issuer,utf8,...
tlvtype,invoice,offer_quantity_max,20
tlvdata,invoice,offer_quantity_max,max,tu64,
tlvtype,invoice,offer_node_id,22
tlvdata,invoice,offer_node_id,node_id,point,
tlvtype,invoice,offer_recurrence,26
tlvdata,invoice,offer_recurrence,recurrence,recurrence,
tlvtype,invoice,offer_recurrence_paywindow,28
tlvdata,invoice,offer_recurrence_paywindow,paywindow,recurrence_paywindow,
tlvtype,invoice,offer_recurrence_limit,30
tlvdata,invoice,offer_recurrence_limit,max_period,tu32,
tlvtype,invoice,offer_recurrence_base,32
tlvdata,invoice,offer_recurrence_base,base,recurrence_base,
tlvtype,invoice,invreq_chain,80
tlvdata,invoice,invreq_chain,chain,chain_hash,
tlvtype,invoice,invreq_amount,82
tlvdata,invoice,invreq_amount,msat,tu64,
tlvtype,invoice,invreq_features,84
tlvdata,invoice,invreq_features,features,byte,...
tlvtype,invoice,invreq_quantity,86
tlvdata,invoice,invreq_quantity,quantity,tu64,
tlvtype,invoice,invreq_payer_id,88
tlvdata,invoice,invreq_payer_id,key,point,
tlvtype,invoice,invreq_payer_note,89
tlvdata,invoice,invreq_payer_note,note,utf8,...
tlvtype,invoice,invreq_recurrence_counter,90
tlvdata,invoice,invreq_recurrence_counter,counter,tu32,
tlvtype,invoice,invreq_recurrence_start,92
tlvdata,invoice,invreq_recurrence_start,period_offset,tu32,
tlvtype,invoice,invoice_paths,160
tlvdata,invoice,invoice_paths,paths,blinded_path,...
tlvtype,invoice,invoice_blindedpay,162
tlvdata,invoice,invoice_blindedpay,payinfo,blinded_payinfo,...
tlvtype,invoice,invoice_created_at,164
tlvdata,invoice,invoice_created_at,timestamp,tu64,
tlvtype,invoice,invoice_relative_expiry,166
tlvdata,invoice,invoice_relative_expiry,seconds_from_creation,tu32,
tlvtype,invoice,invoice_payment_hash,168
tlvdata,invoice,invoice_payment_hash,payment_hash,sha256,
tlvtype,invoice,invoice_amount,170
tlvdata,invoice,invoice_amount,msat,tu64,
tlvtype,invoice,invoice_fallbacks,172
tlvdata,invoice,invoice_fallbacks,fallbacks,fallback_address,...
tlvtype,invoice,invoice_features,174
tlvdata,invoice,invoice_features,features,byte,...
tlvtype,invoice,invoice_node_id,176
tlvdata,invoice,invoice_node_id,node_id,point,
tlvtype,invoice,invoice_recurrence_basetime,178
tlvdata,invoice,invoice_recurrence_basetime,basetime,tu64,
tlvtype,invoice,signature,240
tlvdata,invoice,signature,sig,bip340sig,
subtype,recurrence
subtypedata,recurrence,time_unit,byte,
subtypedata,recurrence,period,tu32,
subtype,recurrence_paywindow
subtypedata,recurrence_paywindow,seconds_before,u32,
subtypedata,recurrence_paywindow,proportional_amount,byte,
subtypedata,recurrence_paywindow,seconds_after,tu32,
subtype,recurrence_base
subtypedata,recurrence_base,start_any_period,byte,
subtypedata,recurrence_base,basetime,tu64,
subtype,blinded_payinfo
subtypedata,blinded_payinfo,fee_base_msat,u32,
subtypedata,blinded_payinfo,fee_proportional_millionths,u32,

1 tlvtype,offer,chains,2 tlvtype,offer,offer_chains,2
2 tlvdata,offer,chains,chains,chain_hash,... tlvdata,offer,offer_chains,chains,chain_hash,...
3 tlvtype,offer,currency,6 tlvtype,offer,offer_metadata,4
4 tlvdata,offer,currency,iso4217,utf8,... tlvdata,offer,offer_metadata,data,byte,...
5 tlvtype,offer,amount,8 tlvtype,offer,offer_currency,6
6 tlvdata,offer,amount,amount,tu64, tlvdata,offer,offer_currency,iso4217,utf8,...
7 tlvtype,offer,description,10 tlvtype,offer,offer_amount,8
8 tlvdata,offer,description,description,utf8,... tlvdata,offer,offer_amount,amount,tu64,
9 tlvtype,offer,features,12 tlvtype,offer,offer_description,10
10 tlvdata,offer,features,features,byte,... tlvdata,offer,offer_description,description,utf8,...
11 tlvtype,offer,absolute_expiry,14 tlvtype,offer,offer_features,12
12 tlvdata,offer,absolute_expiry,seconds_from_epoch,tu64, tlvdata,offer,offer_features,features,byte,...
13 tlvtype,offer,paths,16 tlvtype,offer,offer_absolute_expiry,14
14 tlvdata,offer,paths,paths,blinded_path,... tlvdata,offer,offer_absolute_expiry,seconds_from_epoch,tu64,
15 tlvtype,offer,issuer,20 tlvtype,offer,offer_paths,16
16 tlvdata,offer,issuer,issuer,utf8,... tlvdata,offer,offer_paths,paths,blinded_path,...
17 tlvtype,offer,quantity_min,22 tlvtype,offer,offer_issuer,18
18 tlvdata,offer,quantity_min,min,tu64, tlvdata,offer,offer_issuer,issuer,utf8,...
19 tlvtype,offer,quantity_max,24 tlvtype,offer,offer_quantity_max,20
20 tlvdata,offer,quantity_max,max,tu64, tlvdata,offer,offer_quantity_max,max,tu64,
21 tlvtype,offer,recurrence,26 tlvtype,offer,offer_node_id,22
22 tlvdata,offer,recurrence,time_unit,byte, tlvdata,offer,offer_node_id,node_id,point,
23 tlvdata,offer,recurrence,period,tu32, tlvtype,offer,offer_recurrence,26
24 tlvtype,offer,recurrence_paywindow,64 tlvdata,offer,offer_recurrence,recurrence,recurrence,
25 tlvdata,offer,recurrence_paywindow,seconds_before,u32, tlvtype,offer,offer_recurrence_paywindow,28
26 tlvdata,offer,recurrence_paywindow,proportional_amount,byte, tlvdata,offer,offer_recurrence_paywindow,paywindow,recurrence_paywindow,
27 tlvdata,offer,recurrence_paywindow,seconds_after,tu32, tlvtype,offer,offer_recurrence_limit,30
28 tlvtype,offer,recurrence_limit,66 tlvdata,offer,offer_recurrence_limit,max_period,tu32,
29 tlvdata,offer,recurrence_limit,max_period,tu32, tlvtype,offer,offer_recurrence_base,32
30 tlvtype,offer,recurrence_base,28 tlvdata,offer,offer_recurrence_base,base,recurrence_base,
31 tlvdata,offer,recurrence_base,start_any_period,byte, tlvtype,invoice_request,invreq_metadata,0
32 tlvdata,offer,recurrence_base,basetime,tu64, tlvdata,invoice_request,invreq_metadata,blob,byte,...
33 tlvtype,offer,node_id,30 tlvtype,invoice_request,offer_chains,2
34 tlvdata,offer,node_id,node_id,point, tlvdata,invoice_request,offer_chains,chains,chain_hash,...
35 tlvtype,offer,send_invoice,54 tlvtype,invoice_request,offer_metadata,4
36 tlvtype,offer,refund_for,34 tlvdata,invoice_request,offer_metadata,data,byte,...
37 tlvdata,offer,refund_for,refunded_payment_hash,sha256, tlvtype,invoice_request,offer_currency,6
38 tlvtype,offer,signature,240 tlvdata,invoice_request,offer_currency,iso4217,utf8,...
39 tlvdata,offer,signature,sig,bip340sig, tlvtype,invoice_request,offer_amount,8
40 tlvtype,invoice_request,chain,3 tlvdata,invoice_request,offer_amount,amount,tu64,
41 tlvdata,invoice_request,chain,chain,chain_hash, tlvtype,invoice_request,offer_description,10
42 tlvtype,invoice_request,offer_id,4 tlvdata,invoice_request,offer_description,description,utf8,...
43 tlvdata,invoice_request,offer_id,offer_id,sha256, tlvtype,invoice_request,offer_features,12
44 tlvtype,invoice_request,amount,8 tlvdata,invoice_request,offer_features,features,byte,...
45 tlvdata,invoice_request,amount,msat,tu64, tlvtype,invoice_request,offer_absolute_expiry,14
46 tlvtype,invoice_request,features,12 tlvdata,invoice_request,offer_absolute_expiry,seconds_from_epoch,tu64,
47 tlvdata,invoice_request,features,features,byte,... tlvtype,invoice_request,offer_paths,16
48 tlvtype,invoice_request,quantity,32 tlvdata,invoice_request,offer_paths,paths,blinded_path,...
49 tlvdata,invoice_request,quantity,quantity,tu64, tlvtype,invoice_request,offer_issuer,18
50 tlvtype,invoice_request,recurrence_counter,36 tlvdata,invoice_request,offer_issuer,issuer,utf8,...
51 tlvdata,invoice_request,recurrence_counter,counter,tu32, tlvtype,invoice_request,offer_quantity_max,20
52 tlvtype,invoice_request,recurrence_start,68 tlvdata,invoice_request,offer_quantity_max,max,tu64,
53 tlvdata,invoice_request,recurrence_start,period_offset,tu32, tlvtype,invoice_request,offer_node_id,22
54 tlvtype,invoice_request,payer_key,38 tlvdata,invoice_request,offer_node_id,node_id,point,
55 tlvdata,invoice_request,payer_key,key,point, tlvtype,invoice_request,offer_recurrence,26
56 tlvtype,invoice_request,payer_note,39 tlvdata,invoice_request,offer_recurrence,recurrence,recurrence,
57 tlvdata,invoice_request,payer_note,note,utf8,... tlvtype,invoice_request,offer_recurrence_paywindow,28
58 tlvtype,invoice_request,payer_info,50 tlvdata,invoice_request,offer_recurrence_paywindow,paywindow,recurrence_paywindow,
59 tlvdata,invoice_request,payer_info,blob,byte,... tlvtype,invoice_request,offer_recurrence_limit,30
60 tlvtype,invoice_request,replace_invoice,56 tlvdata,invoice_request,offer_recurrence_limit,max_period,tu32,
61 tlvdata,invoice_request,replace_invoice,payment_hash,sha256, tlvtype,invoice_request,offer_recurrence_base,32
62 tlvdata,invoice_request,offer_recurrence_base,base,recurrence_base,
63 tlvtype,invoice_request,invreq_chain,80
64 tlvdata,invoice_request,invreq_chain,chain,chain_hash,
65 tlvtype,invoice_request,invreq_amount,82
66 tlvdata,invoice_request,invreq_amount,msat,tu64,
67 tlvtype,invoice_request,invreq_features,84
68 tlvdata,invoice_request,invreq_features,features,byte,...
69 tlvtype,invoice_request,invreq_quantity,86
70 tlvdata,invoice_request,invreq_quantity,quantity,tu64,
71 tlvtype,invoice_request,invreq_payer_id,88
72 tlvdata,invoice_request,invreq_payer_id,key,point,
73 tlvtype,invoice_request,invreq_payer_note,89
74 tlvdata,invoice_request,invreq_payer_note,note,utf8,...
75 tlvtype,invoice_request,invreq_recurrence_counter,90
76 tlvdata,invoice_request,invreq_recurrence_counter,counter,tu32,
77 tlvtype,invoice_request,invreq_recurrence_start,92
78 tlvdata,invoice_request,invreq_recurrence_start,period_offset,tu32,
79 tlvtype,invoice_request,signature,240 tlvtype,invoice_request,signature,240
80 tlvdata,invoice_request,signature,sig,bip340sig, tlvdata,invoice_request,signature,sig,bip340sig,
81 tlvtype,invoice,chain,3 tlvtype,invoice,invreq_metadata,0
82 tlvdata,invoice,chain,chain,chain_hash, tlvdata,invoice,invreq_metadata,blob,byte,...
83 tlvtype,invoice,offer_id,4 tlvtype,invoice,offer_chains,2
84 tlvdata,invoice,offer_id,offer_id,sha256, tlvdata,invoice,offer_chains,chains,chain_hash,...
85 tlvtype,invoice,amount,8 tlvtype,invoice,offer_metadata,4
86 tlvdata,invoice,amount,msat,tu64, tlvdata,invoice,offer_metadata,data,byte,...
87 tlvtype,invoice,description,10 tlvtype,invoice,offer_currency,6
88 tlvdata,invoice,description,description,utf8,... tlvdata,invoice,offer_currency,iso4217,utf8,...
89 tlvtype,invoice,features,12 tlvtype,invoice,offer_amount,8
90 tlvdata,invoice,features,features,byte,... tlvdata,invoice,offer_amount,amount,tu64,
91 tlvtype,invoice,paths,16 tlvtype,invoice,offer_description,10
92 tlvdata,invoice,paths,paths,blinded_path,... tlvdata,invoice,offer_description,description,utf8,...
93 tlvtype,invoice,blindedpay,18 tlvtype,invoice,offer_features,12
94 tlvdata,invoice,blindedpay,payinfo,blinded_payinfo,... tlvdata,invoice,offer_features,features,byte,...
95 tlvtype,invoice,blinded_capacities,19 tlvtype,invoice,offer_absolute_expiry,14
96 tlvdata,invoice,blinded_capacities,incoming_msat,u64,... tlvdata,invoice,offer_absolute_expiry,seconds_from_epoch,tu64,
97 tlvtype,invoice,issuer,20 tlvtype,invoice,offer_paths,16
98 tlvdata,invoice,issuer,issuer,utf8,... tlvdata,invoice,offer_paths,paths,blinded_path,...
99 tlvtype,invoice,node_id,30 tlvtype,invoice,offer_issuer,18
100 tlvdata,invoice,node_id,node_id,point, tlvdata,invoice,offer_issuer,issuer,utf8,...
101 tlvtype,invoice,quantity,32 tlvtype,invoice,offer_quantity_max,20
102 tlvdata,invoice,quantity,quantity,tu64, tlvdata,invoice,offer_quantity_max,max,tu64,
103 tlvtype,invoice,refund_for,34 tlvtype,invoice,offer_node_id,22
104 tlvdata,invoice,refund_for,refunded_payment_hash,sha256, tlvdata,invoice,offer_node_id,node_id,point,
105 tlvtype,invoice,recurrence_counter,36 tlvtype,invoice,offer_recurrence,26
106 tlvdata,invoice,recurrence_counter,counter,tu32, tlvdata,invoice,offer_recurrence,recurrence,recurrence,
107 tlvtype,invoice,recurrence_start,68 tlvtype,invoice,offer_recurrence_paywindow,28
108 tlvdata,invoice,recurrence_start,period_offset,tu32, tlvdata,invoice,offer_recurrence_paywindow,paywindow,recurrence_paywindow,
109 tlvtype,invoice,recurrence_basetime,64 tlvtype,invoice,offer_recurrence_limit,30
110 tlvdata,invoice,recurrence_basetime,basetime,tu64, tlvdata,invoice,offer_recurrence_limit,max_period,tu32,
111 tlvtype,invoice,payer_key,38 tlvtype,invoice,offer_recurrence_base,32
112 tlvdata,invoice,payer_key,key,point, tlvdata,invoice,offer_recurrence_base,base,recurrence_base,
113 tlvtype,invoice,payer_note,39 tlvtype,invoice,invreq_chain,80
114 tlvdata,invoice,payer_note,note,utf8,... tlvdata,invoice,invreq_chain,chain,chain_hash,
115 tlvtype,invoice,created_at,40 tlvtype,invoice,invreq_amount,82
116 tlvdata,invoice,created_at,timestamp,tu64, tlvdata,invoice,invreq_amount,msat,tu64,
117 tlvtype,invoice,payment_hash,42 tlvtype,invoice,invreq_features,84
118 tlvdata,invoice,payment_hash,payment_hash,sha256, tlvdata,invoice,invreq_features,features,byte,...
119 tlvtype,invoice,relative_expiry,44 tlvtype,invoice,invreq_quantity,86
120 tlvdata,invoice,relative_expiry,seconds_from_creation,tu32, tlvdata,invoice,invreq_quantity,quantity,tu64,
121 tlvtype,invoice,cltv,46 tlvtype,invoice,invreq_payer_id,88
122 tlvdata,invoice,cltv,min_final_cltv_expiry,tu16, tlvdata,invoice,invreq_payer_id,key,point,
123 tlvtype,invoice,fallbacks,48 tlvtype,invoice,invreq_payer_note,89
124 tlvdata,invoice,fallbacks,fallbacks,fallback_address,... tlvdata,invoice,invreq_payer_note,note,utf8,...
125 tlvtype,invoice,payer_info,50 tlvtype,invoice,invreq_recurrence_counter,90
126 tlvdata,invoice,payer_info,blob,byte,... tlvdata,invoice,invreq_recurrence_counter,counter,tu32,
127 tlvtype,invoice,refund_signature,52 tlvtype,invoice,invreq_recurrence_start,92
128 tlvdata,invoice,refund_signature,payer_signature,bip340sig, tlvdata,invoice,invreq_recurrence_start,period_offset,tu32,
129 tlvtype,invoice,send_invoice,54 tlvtype,invoice,invoice_paths,160
130 tlvtype,invoice,replace_invoice,56 tlvdata,invoice,invoice_paths,paths,blinded_path,...
131 tlvdata,invoice,replace_invoice,payment_hash,sha256, tlvtype,invoice,invoice_blindedpay,162
132 tlvdata,invoice,invoice_blindedpay,payinfo,blinded_payinfo,...
133 tlvtype,invoice,invoice_created_at,164
134 tlvdata,invoice,invoice_created_at,timestamp,tu64,
135 tlvtype,invoice,invoice_relative_expiry,166
136 tlvdata,invoice,invoice_relative_expiry,seconds_from_creation,tu32,
137 tlvtype,invoice,invoice_payment_hash,168
138 tlvdata,invoice,invoice_payment_hash,payment_hash,sha256,
139 tlvtype,invoice,invoice_amount,170
140 tlvdata,invoice,invoice_amount,msat,tu64,
141 tlvtype,invoice,invoice_fallbacks,172
142 tlvdata,invoice,invoice_fallbacks,fallbacks,fallback_address,...
143 tlvtype,invoice,invoice_features,174
144 tlvdata,invoice,invoice_features,features,byte,...
145 tlvtype,invoice,invoice_node_id,176
146 tlvdata,invoice,invoice_node_id,node_id,point,
147 tlvtype,invoice,invoice_recurrence_basetime,178
148 tlvdata,invoice,invoice_recurrence_basetime,basetime,tu64,
149 tlvtype,invoice,signature,240 tlvtype,invoice,signature,240
150 tlvdata,invoice,signature,sig,bip340sig, tlvdata,invoice,signature,sig,bip340sig,
151 subtype,recurrence
152 subtypedata,recurrence,time_unit,byte,
153 subtypedata,recurrence,period,tu32,
154 subtype,recurrence_paywindow
155 subtypedata,recurrence_paywindow,seconds_before,u32,
156 subtypedata,recurrence_paywindow,proportional_amount,byte,
157 subtypedata,recurrence_paywindow,seconds_after,tu32,
158 subtype,recurrence_base
159 subtypedata,recurrence_base,start_any_period,byte,
160 subtypedata,recurrence_base,basetime,tu64,
161 subtype,blinded_payinfo subtype,blinded_payinfo
162 subtypedata,blinded_payinfo,fee_base_msat,u32, subtypedata,blinded_payinfo,fee_base_msat,u32,
163 subtypedata,blinded_payinfo,fee_proportional_millionths,u32, subtypedata,blinded_payinfo,fee_proportional_millionths,u32,

View File

@ -1,119 +1,163 @@
tlvtype,offer,chains,2
tlvdata,offer,chains,chains,chain_hash,...
tlvtype,offer,currency,6
tlvdata,offer,currency,iso4217,utf8,...
tlvtype,offer,amount,8
tlvdata,offer,amount,amount,tu64,
tlvtype,offer,description,10
tlvdata,offer,description,description,utf8,...
tlvtype,offer,features,12
tlvdata,offer,features,features,byte,...
tlvtype,offer,absolute_expiry,14
tlvdata,offer,absolute_expiry,seconds_from_epoch,tu64,
tlvtype,offer,paths,16
tlvdata,offer,paths,paths,blinded_path,...
tlvtype,offer,issuer,20
tlvdata,offer,issuer,issuer,utf8,...
tlvtype,offer,quantity_min,22
tlvdata,offer,quantity_min,min,tu64,
tlvtype,offer,quantity_max,24
tlvdata,offer,quantity_max,max,tu64,
tlvtype,offer,recurrence,26
tlvdata,offer,recurrence,time_unit,byte,
tlvdata,offer,recurrence,period,tu32,
tlvtype,offer,recurrence_paywindow,64
tlvdata,offer,recurrence_paywindow,seconds_before,u32,
tlvdata,offer,recurrence_paywindow,proportional_amount,byte,
tlvdata,offer,recurrence_paywindow,seconds_after,tu32,
tlvtype,offer,recurrence_limit,66
tlvdata,offer,recurrence_limit,max_period,tu32,
tlvtype,offer,recurrence_base,28
tlvdata,offer,recurrence_base,start_any_period,byte,
tlvdata,offer,recurrence_base,basetime,tu64,
tlvtype,offer,node_id,30
tlvdata,offer,node_id,node_id,point,
tlvtype,offer,send_invoice,54
tlvtype,offer,refund_for,34
tlvdata,offer,refund_for,refunded_payment_hash,sha256,
tlvtype,offer,signature,240
tlvdata,offer,signature,sig,bip340sig,
tlvtype,invoice_request,chain,3
tlvdata,invoice_request,chain,chain,chain_hash,
tlvtype,invoice_request,offer_id,4
tlvdata,invoice_request,offer_id,offer_id,sha256,
tlvtype,invoice_request,amount,8
tlvdata,invoice_request,amount,msat,tu64,
tlvtype,invoice_request,features,12
tlvdata,invoice_request,features,features,byte,...
tlvtype,invoice_request,quantity,32
tlvdata,invoice_request,quantity,quantity,tu64,
tlvtype,invoice_request,recurrence_counter,36
tlvdata,invoice_request,recurrence_counter,counter,tu32,
tlvtype,invoice_request,recurrence_start,68
tlvdata,invoice_request,recurrence_start,period_offset,tu32,
tlvtype,invoice_request,payer_key,38
tlvdata,invoice_request,payer_key,key,point,
tlvtype,invoice_request,payer_note,39
tlvdata,invoice_request,payer_note,note,utf8,...
tlvtype,invoice_request,payer_info,50
tlvdata,invoice_request,payer_info,blob,byte,...
tlvtype,invoice_request,replace_invoice,56
tlvdata,invoice_request,replace_invoice,payment_hash,sha256,
tlvtype,offer,offer_chains,2
tlvdata,offer,offer_chains,chains,chain_hash,...
tlvtype,offer,offer_metadata,4
tlvdata,offer,offer_metadata,data,byte,...
tlvtype,offer,offer_currency,6
tlvdata,offer,offer_currency,iso4217,utf8,...
tlvtype,offer,offer_amount,8
tlvdata,offer,offer_amount,amount,tu64,
tlvtype,offer,offer_description,10
tlvdata,offer,offer_description,description,utf8,...
tlvtype,offer,offer_features,12
tlvdata,offer,offer_features,features,byte,...
tlvtype,offer,offer_absolute_expiry,14
tlvdata,offer,offer_absolute_expiry,seconds_from_epoch,tu64,
tlvtype,offer,offer_paths,16
tlvdata,offer,offer_paths,paths,blinded_path,...
tlvtype,offer,offer_issuer,18
tlvdata,offer,offer_issuer,issuer,utf8,...
tlvtype,offer,offer_quantity_max,20
tlvdata,offer,offer_quantity_max,max,tu64,
tlvtype,offer,offer_node_id,22
tlvdata,offer,offer_node_id,node_id,point,
tlvtype,offer,offer_recurrence,26
tlvdata,offer,offer_recurrence,recurrence,recurrence,
tlvtype,offer,offer_recurrence_paywindow,28
tlvdata,offer,offer_recurrence_paywindow,paywindow,recurrence_paywindow,
tlvtype,offer,offer_recurrence_limit,30
tlvdata,offer,offer_recurrence_limit,max_period,tu32,
tlvtype,offer,offer_recurrence_base,32
tlvdata,offer,offer_recurrence_base,base,recurrence_base,
tlvtype,invoice_request,invreq_metadata,0
tlvdata,invoice_request,invreq_metadata,blob,byte,...
tlvtype,invoice_request,offer_chains,2
tlvdata,invoice_request,offer_chains,chains,chain_hash,...
tlvtype,invoice_request,offer_metadata,4
tlvdata,invoice_request,offer_metadata,data,byte,...
tlvtype,invoice_request,offer_currency,6
tlvdata,invoice_request,offer_currency,iso4217,utf8,...
tlvtype,invoice_request,offer_amount,8
tlvdata,invoice_request,offer_amount,amount,tu64,
tlvtype,invoice_request,offer_description,10
tlvdata,invoice_request,offer_description,description,utf8,...
tlvtype,invoice_request,offer_features,12
tlvdata,invoice_request,offer_features,features,byte,...
tlvtype,invoice_request,offer_absolute_expiry,14
tlvdata,invoice_request,offer_absolute_expiry,seconds_from_epoch,tu64,
tlvtype,invoice_request,offer_paths,16
tlvdata,invoice_request,offer_paths,paths,blinded_path,...
tlvtype,invoice_request,offer_issuer,18
tlvdata,invoice_request,offer_issuer,issuer,utf8,...
tlvtype,invoice_request,offer_quantity_max,20
tlvdata,invoice_request,offer_quantity_max,max,tu64,
tlvtype,invoice_request,offer_node_id,22
tlvdata,invoice_request,offer_node_id,node_id,point,
tlvtype,invoice_request,offer_recurrence,26
tlvdata,invoice_request,offer_recurrence,recurrence,recurrence,
tlvtype,invoice_request,offer_recurrence_paywindow,28
tlvdata,invoice_request,offer_recurrence_paywindow,paywindow,recurrence_paywindow,
tlvtype,invoice_request,offer_recurrence_limit,30
tlvdata,invoice_request,offer_recurrence_limit,max_period,tu32,
tlvtype,invoice_request,offer_recurrence_base,32
tlvdata,invoice_request,offer_recurrence_base,base,recurrence_base,
tlvtype,invoice_request,invreq_chain,80
tlvdata,invoice_request,invreq_chain,chain,chain_hash,
tlvtype,invoice_request,invreq_amount,82
tlvdata,invoice_request,invreq_amount,msat,tu64,
tlvtype,invoice_request,invreq_features,84
tlvdata,invoice_request,invreq_features,features,byte,...
tlvtype,invoice_request,invreq_quantity,86
tlvdata,invoice_request,invreq_quantity,quantity,tu64,
tlvtype,invoice_request,invreq_payer_id,88
tlvdata,invoice_request,invreq_payer_id,key,point,
tlvtype,invoice_request,invreq_payer_note,89
tlvdata,invoice_request,invreq_payer_note,note,utf8,...
tlvtype,invoice_request,invreq_recurrence_counter,90
tlvdata,invoice_request,invreq_recurrence_counter,counter,tu32,
tlvtype,invoice_request,invreq_recurrence_start,92
tlvdata,invoice_request,invreq_recurrence_start,period_offset,tu32,
tlvtype,invoice_request,signature,240
tlvdata,invoice_request,signature,sig,bip340sig,
tlvtype,invoice,chain,3
tlvdata,invoice,chain,chain,chain_hash,
tlvtype,invoice,offer_id,4
tlvdata,invoice,offer_id,offer_id,sha256,
tlvtype,invoice,amount,8
tlvdata,invoice,amount,msat,tu64,
tlvtype,invoice,description,10
tlvdata,invoice,description,description,utf8,...
tlvtype,invoice,features,12
tlvdata,invoice,features,features,byte,...
tlvtype,invoice,paths,16
tlvdata,invoice,paths,paths,blinded_path,...
tlvtype,invoice,blindedpay,18
tlvdata,invoice,blindedpay,payinfo,blinded_payinfo,...
tlvtype,invoice,blinded_capacities,19
tlvdata,invoice,blinded_capacities,incoming_msat,u64,...
tlvtype,invoice,issuer,20
tlvdata,invoice,issuer,issuer,utf8,...
tlvtype,invoice,node_id,30
tlvdata,invoice,node_id,node_id,point,
tlvtype,invoice,quantity,32
tlvdata,invoice,quantity,quantity,tu64,
tlvtype,invoice,refund_for,34
tlvdata,invoice,refund_for,refunded_payment_hash,sha256,
tlvtype,invoice,recurrence_counter,36
tlvdata,invoice,recurrence_counter,counter,tu32,
tlvtype,invoice,recurrence_start,68
tlvdata,invoice,recurrence_start,period_offset,tu32,
tlvtype,invoice,recurrence_basetime,64
tlvdata,invoice,recurrence_basetime,basetime,tu64,
tlvtype,invoice,payer_key,38
tlvdata,invoice,payer_key,key,point,
tlvtype,invoice,payer_note,39
tlvdata,invoice,payer_note,note,utf8,...
tlvtype,invoice,created_at,40
tlvdata,invoice,created_at,timestamp,tu64,
tlvtype,invoice,payment_hash,42
tlvdata,invoice,payment_hash,payment_hash,sha256,
tlvtype,invoice,relative_expiry,44
tlvdata,invoice,relative_expiry,seconds_from_creation,tu32,
tlvtype,invoice,cltv,46
tlvdata,invoice,cltv,min_final_cltv_expiry,tu16,
tlvtype,invoice,fallbacks,48
tlvdata,invoice,fallbacks,fallbacks,fallback_address,...
tlvtype,invoice,payer_info,50
tlvdata,invoice,payer_info,blob,byte,...
tlvtype,invoice,refund_signature,52
tlvdata,invoice,refund_signature,payer_signature,bip340sig,
tlvtype,invoice,send_invoice,54
tlvtype,invoice,replace_invoice,56
tlvdata,invoice,replace_invoice,payment_hash,sha256,
tlvtype,invoice,invreq_metadata,0
tlvdata,invoice,invreq_metadata,blob,byte,...
tlvtype,invoice,offer_chains,2
tlvdata,invoice,offer_chains,chains,chain_hash,...
tlvtype,invoice,offer_metadata,4
tlvdata,invoice,offer_metadata,data,byte,...
tlvtype,invoice,offer_currency,6
tlvdata,invoice,offer_currency,iso4217,utf8,...
tlvtype,invoice,offer_amount,8
tlvdata,invoice,offer_amount,amount,tu64,
tlvtype,invoice,offer_description,10
tlvdata,invoice,offer_description,description,utf8,...
tlvtype,invoice,offer_features,12
tlvdata,invoice,offer_features,features,byte,...
tlvtype,invoice,offer_absolute_expiry,14
tlvdata,invoice,offer_absolute_expiry,seconds_from_epoch,tu64,
tlvtype,invoice,offer_paths,16
tlvdata,invoice,offer_paths,paths,blinded_path,...
tlvtype,invoice,offer_issuer,18
tlvdata,invoice,offer_issuer,issuer,utf8,...
tlvtype,invoice,offer_quantity_max,20
tlvdata,invoice,offer_quantity_max,max,tu64,
tlvtype,invoice,offer_node_id,22
tlvdata,invoice,offer_node_id,node_id,point,
tlvtype,invoice,offer_recurrence,26
tlvdata,invoice,offer_recurrence,recurrence,recurrence,
tlvtype,invoice,offer_recurrence_paywindow,28
tlvdata,invoice,offer_recurrence_paywindow,paywindow,recurrence_paywindow,
tlvtype,invoice,offer_recurrence_limit,30
tlvdata,invoice,offer_recurrence_limit,max_period,tu32,
tlvtype,invoice,offer_recurrence_base,32
tlvdata,invoice,offer_recurrence_base,base,recurrence_base,
tlvtype,invoice,invreq_chain,80
tlvdata,invoice,invreq_chain,chain,chain_hash,
tlvtype,invoice,invreq_amount,82
tlvdata,invoice,invreq_amount,msat,tu64,
tlvtype,invoice,invreq_features,84
tlvdata,invoice,invreq_features,features,byte,...
tlvtype,invoice,invreq_quantity,86
tlvdata,invoice,invreq_quantity,quantity,tu64,
tlvtype,invoice,invreq_payer_id,88
tlvdata,invoice,invreq_payer_id,key,point,
tlvtype,invoice,invreq_payer_note,89
tlvdata,invoice,invreq_payer_note,note,utf8,...
tlvtype,invoice,invreq_recurrence_counter,90
tlvdata,invoice,invreq_recurrence_counter,counter,tu32,
tlvtype,invoice,invreq_recurrence_start,92
tlvdata,invoice,invreq_recurrence_start,period_offset,tu32,
tlvtype,invoice,invoice_paths,160
tlvdata,invoice,invoice_paths,paths,blinded_path,...
tlvtype,invoice,invoice_blindedpay,162
tlvdata,invoice,invoice_blindedpay,payinfo,blinded_payinfo,...
tlvtype,invoice,invoice_created_at,164
tlvdata,invoice,invoice_created_at,timestamp,tu64,
tlvtype,invoice,invoice_relative_expiry,166
tlvdata,invoice,invoice_relative_expiry,seconds_from_creation,tu32,
tlvtype,invoice,invoice_payment_hash,168
tlvdata,invoice,invoice_payment_hash,payment_hash,sha256,
tlvtype,invoice,invoice_amount,170
tlvdata,invoice,invoice_amount,msat,tu64,
tlvtype,invoice,invoice_fallbacks,172
tlvdata,invoice,invoice_fallbacks,fallbacks,fallback_address,...
tlvtype,invoice,invoice_features,174
tlvdata,invoice,invoice_features,features,byte,...
tlvtype,invoice,invoice_node_id,176
tlvdata,invoice,invoice_node_id,node_id,point,
tlvtype,invoice,invoice_recurrence_basetime,178
tlvdata,invoice,invoice_recurrence_basetime,basetime,tu64,
tlvtype,invoice,signature,240
tlvdata,invoice,signature,sig,bip340sig,
subtype,recurrence
subtypedata,recurrence,time_unit,byte,
subtypedata,recurrence,period,tu32,
subtype,recurrence_paywindow
subtypedata,recurrence_paywindow,seconds_before,u32,
subtypedata,recurrence_paywindow,proportional_amount,byte,
subtypedata,recurrence_paywindow,seconds_after,tu32,
subtype,recurrence_base
subtypedata,recurrence_base,start_any_period,byte,
subtypedata,recurrence_base,basetime,tu64,
subtype,blinded_payinfo
subtypedata,blinded_payinfo,fee_base_msat,u32,
subtypedata,blinded_payinfo,fee_proportional_millionths,u32,

1 tlvtype,offer,chains,2 tlvtype,offer,offer_chains,2
2 tlvdata,offer,chains,chains,chain_hash,... tlvdata,offer,offer_chains,chains,chain_hash,...
3 tlvtype,offer,currency,6 tlvtype,offer,offer_metadata,4
4 tlvdata,offer,currency,iso4217,utf8,... tlvdata,offer,offer_metadata,data,byte,...
5 tlvtype,offer,amount,8 tlvtype,offer,offer_currency,6
6 tlvdata,offer,amount,amount,tu64, tlvdata,offer,offer_currency,iso4217,utf8,...
7 tlvtype,offer,description,10 tlvtype,offer,offer_amount,8
8 tlvdata,offer,description,description,utf8,... tlvdata,offer,offer_amount,amount,tu64,
9 tlvtype,offer,features,12 tlvtype,offer,offer_description,10
10 tlvdata,offer,features,features,byte,... tlvdata,offer,offer_description,description,utf8,...
11 tlvtype,offer,absolute_expiry,14 tlvtype,offer,offer_features,12
12 tlvdata,offer,absolute_expiry,seconds_from_epoch,tu64, tlvdata,offer,offer_features,features,byte,...
13 tlvtype,offer,paths,16 tlvtype,offer,offer_absolute_expiry,14
14 tlvdata,offer,paths,paths,blinded_path,... tlvdata,offer,offer_absolute_expiry,seconds_from_epoch,tu64,
15 tlvtype,offer,issuer,20 tlvtype,offer,offer_paths,16
16 tlvdata,offer,issuer,issuer,utf8,... tlvdata,offer,offer_paths,paths,blinded_path,...
17 tlvtype,offer,quantity_min,22 tlvtype,offer,offer_issuer,18
18 tlvdata,offer,quantity_min,min,tu64, tlvdata,offer,offer_issuer,issuer,utf8,...
19 tlvtype,offer,quantity_max,24 tlvtype,offer,offer_quantity_max,20
20 tlvdata,offer,quantity_max,max,tu64, tlvdata,offer,offer_quantity_max,max,tu64,
21 tlvtype,offer,recurrence,26 tlvtype,offer,offer_node_id,22
22 tlvdata,offer,recurrence,time_unit,byte, tlvdata,offer,offer_node_id,node_id,point,
23 tlvdata,offer,recurrence,period,tu32, tlvtype,offer,offer_recurrence,26
24 tlvtype,offer,recurrence_paywindow,64 tlvdata,offer,offer_recurrence,recurrence,recurrence,
25 tlvdata,offer,recurrence_paywindow,seconds_before,u32, tlvtype,offer,offer_recurrence_paywindow,28
26 tlvdata,offer,recurrence_paywindow,proportional_amount,byte, tlvdata,offer,offer_recurrence_paywindow,paywindow,recurrence_paywindow,
27 tlvdata,offer,recurrence_paywindow,seconds_after,tu32, tlvtype,offer,offer_recurrence_limit,30
28 tlvtype,offer,recurrence_limit,66 tlvdata,offer,offer_recurrence_limit,max_period,tu32,
29 tlvdata,offer,recurrence_limit,max_period,tu32, tlvtype,offer,offer_recurrence_base,32
30 tlvtype,offer,recurrence_base,28 tlvdata,offer,offer_recurrence_base,base,recurrence_base,
31 tlvdata,offer,recurrence_base,start_any_period,byte, tlvtype,invoice_request,invreq_metadata,0
32 tlvdata,offer,recurrence_base,basetime,tu64, tlvdata,invoice_request,invreq_metadata,blob,byte,...
33 tlvtype,offer,node_id,30 tlvtype,invoice_request,offer_chains,2
34 tlvdata,offer,node_id,node_id,point, tlvdata,invoice_request,offer_chains,chains,chain_hash,...
35 tlvtype,offer,send_invoice,54 tlvtype,invoice_request,offer_metadata,4
36 tlvtype,offer,refund_for,34 tlvdata,invoice_request,offer_metadata,data,byte,...
37 tlvdata,offer,refund_for,refunded_payment_hash,sha256, tlvtype,invoice_request,offer_currency,6
38 tlvtype,offer,signature,240 tlvdata,invoice_request,offer_currency,iso4217,utf8,...
39 tlvdata,offer,signature,sig,bip340sig, tlvtype,invoice_request,offer_amount,8
40 tlvtype,invoice_request,chain,3 tlvdata,invoice_request,offer_amount,amount,tu64,
41 tlvdata,invoice_request,chain,chain,chain_hash, tlvtype,invoice_request,offer_description,10
42 tlvtype,invoice_request,offer_id,4 tlvdata,invoice_request,offer_description,description,utf8,...
43 tlvdata,invoice_request,offer_id,offer_id,sha256, tlvtype,invoice_request,offer_features,12
44 tlvtype,invoice_request,amount,8 tlvdata,invoice_request,offer_features,features,byte,...
45 tlvdata,invoice_request,amount,msat,tu64, tlvtype,invoice_request,offer_absolute_expiry,14
46 tlvtype,invoice_request,features,12 tlvdata,invoice_request,offer_absolute_expiry,seconds_from_epoch,tu64,
47 tlvdata,invoice_request,features,features,byte,... tlvtype,invoice_request,offer_paths,16
48 tlvtype,invoice_request,quantity,32 tlvdata,invoice_request,offer_paths,paths,blinded_path,...
49 tlvdata,invoice_request,quantity,quantity,tu64, tlvtype,invoice_request,offer_issuer,18
50 tlvtype,invoice_request,recurrence_counter,36 tlvdata,invoice_request,offer_issuer,issuer,utf8,...
51 tlvdata,invoice_request,recurrence_counter,counter,tu32, tlvtype,invoice_request,offer_quantity_max,20
52 tlvtype,invoice_request,recurrence_start,68 tlvdata,invoice_request,offer_quantity_max,max,tu64,
53 tlvdata,invoice_request,recurrence_start,period_offset,tu32, tlvtype,invoice_request,offer_node_id,22
54 tlvtype,invoice_request,payer_key,38 tlvdata,invoice_request,offer_node_id,node_id,point,
55 tlvdata,invoice_request,payer_key,key,point, tlvtype,invoice_request,offer_recurrence,26
56 tlvtype,invoice_request,payer_note,39 tlvdata,invoice_request,offer_recurrence,recurrence,recurrence,
57 tlvdata,invoice_request,payer_note,note,utf8,... tlvtype,invoice_request,offer_recurrence_paywindow,28
58 tlvtype,invoice_request,payer_info,50 tlvdata,invoice_request,offer_recurrence_paywindow,paywindow,recurrence_paywindow,
59 tlvdata,invoice_request,payer_info,blob,byte,... tlvtype,invoice_request,offer_recurrence_limit,30
60 tlvtype,invoice_request,replace_invoice,56 tlvdata,invoice_request,offer_recurrence_limit,max_period,tu32,
61 tlvdata,invoice_request,replace_invoice,payment_hash,sha256, tlvtype,invoice_request,offer_recurrence_base,32
62 tlvdata,invoice_request,offer_recurrence_base,base,recurrence_base,
63 tlvtype,invoice_request,invreq_chain,80
64 tlvdata,invoice_request,invreq_chain,chain,chain_hash,
65 tlvtype,invoice_request,invreq_amount,82
66 tlvdata,invoice_request,invreq_amount,msat,tu64,
67 tlvtype,invoice_request,invreq_features,84
68 tlvdata,invoice_request,invreq_features,features,byte,...
69 tlvtype,invoice_request,invreq_quantity,86
70 tlvdata,invoice_request,invreq_quantity,quantity,tu64,
71 tlvtype,invoice_request,invreq_payer_id,88
72 tlvdata,invoice_request,invreq_payer_id,key,point,
73 tlvtype,invoice_request,invreq_payer_note,89
74 tlvdata,invoice_request,invreq_payer_note,note,utf8,...
75 tlvtype,invoice_request,invreq_recurrence_counter,90
76 tlvdata,invoice_request,invreq_recurrence_counter,counter,tu32,
77 tlvtype,invoice_request,invreq_recurrence_start,92
78 tlvdata,invoice_request,invreq_recurrence_start,period_offset,tu32,
79 tlvtype,invoice_request,signature,240 tlvtype,invoice_request,signature,240
80 tlvdata,invoice_request,signature,sig,bip340sig, tlvdata,invoice_request,signature,sig,bip340sig,
81 tlvtype,invoice,chain,3 tlvtype,invoice,invreq_metadata,0
82 tlvdata,invoice,chain,chain,chain_hash, tlvdata,invoice,invreq_metadata,blob,byte,...
83 tlvtype,invoice,offer_id,4 tlvtype,invoice,offer_chains,2
84 tlvdata,invoice,offer_id,offer_id,sha256, tlvdata,invoice,offer_chains,chains,chain_hash,...
85 tlvtype,invoice,amount,8 tlvtype,invoice,offer_metadata,4
86 tlvdata,invoice,amount,msat,tu64, tlvdata,invoice,offer_metadata,data,byte,...
87 tlvtype,invoice,description,10 tlvtype,invoice,offer_currency,6
88 tlvdata,invoice,description,description,utf8,... tlvdata,invoice,offer_currency,iso4217,utf8,...
89 tlvtype,invoice,features,12 tlvtype,invoice,offer_amount,8
90 tlvdata,invoice,features,features,byte,... tlvdata,invoice,offer_amount,amount,tu64,
91 tlvtype,invoice,paths,16 tlvtype,invoice,offer_description,10
92 tlvdata,invoice,paths,paths,blinded_path,... tlvdata,invoice,offer_description,description,utf8,...
93 tlvtype,invoice,blindedpay,18 tlvtype,invoice,offer_features,12
94 tlvdata,invoice,blindedpay,payinfo,blinded_payinfo,... tlvdata,invoice,offer_features,features,byte,...
95 tlvtype,invoice,blinded_capacities,19 tlvtype,invoice,offer_absolute_expiry,14
96 tlvdata,invoice,blinded_capacities,incoming_msat,u64,... tlvdata,invoice,offer_absolute_expiry,seconds_from_epoch,tu64,
97 tlvtype,invoice,issuer,20 tlvtype,invoice,offer_paths,16
98 tlvdata,invoice,issuer,issuer,utf8,... tlvdata,invoice,offer_paths,paths,blinded_path,...
99 tlvtype,invoice,node_id,30 tlvtype,invoice,offer_issuer,18
100 tlvdata,invoice,node_id,node_id,point, tlvdata,invoice,offer_issuer,issuer,utf8,...
101 tlvtype,invoice,quantity,32 tlvtype,invoice,offer_quantity_max,20
102 tlvdata,invoice,quantity,quantity,tu64, tlvdata,invoice,offer_quantity_max,max,tu64,
103 tlvtype,invoice,refund_for,34 tlvtype,invoice,offer_node_id,22
104 tlvdata,invoice,refund_for,refunded_payment_hash,sha256, tlvdata,invoice,offer_node_id,node_id,point,
105 tlvtype,invoice,recurrence_counter,36 tlvtype,invoice,offer_recurrence,26
106 tlvdata,invoice,recurrence_counter,counter,tu32, tlvdata,invoice,offer_recurrence,recurrence,recurrence,
107 tlvtype,invoice,recurrence_start,68 tlvtype,invoice,offer_recurrence_paywindow,28
108 tlvdata,invoice,recurrence_start,period_offset,tu32, tlvdata,invoice,offer_recurrence_paywindow,paywindow,recurrence_paywindow,
109 tlvtype,invoice,recurrence_basetime,64 tlvtype,invoice,offer_recurrence_limit,30
110 tlvdata,invoice,recurrence_basetime,basetime,tu64, tlvdata,invoice,offer_recurrence_limit,max_period,tu32,
111 tlvtype,invoice,payer_key,38 tlvtype,invoice,offer_recurrence_base,32
112 tlvdata,invoice,payer_key,key,point, tlvdata,invoice,offer_recurrence_base,base,recurrence_base,
113 tlvtype,invoice,payer_note,39 tlvtype,invoice,invreq_chain,80
114 tlvdata,invoice,payer_note,note,utf8,... tlvdata,invoice,invreq_chain,chain,chain_hash,
115 tlvtype,invoice,created_at,40 tlvtype,invoice,invreq_amount,82
116 tlvdata,invoice,created_at,timestamp,tu64, tlvdata,invoice,invreq_amount,msat,tu64,
117 tlvtype,invoice,payment_hash,42 tlvtype,invoice,invreq_features,84
118 tlvdata,invoice,payment_hash,payment_hash,sha256, tlvdata,invoice,invreq_features,features,byte,...
119 tlvtype,invoice,relative_expiry,44 tlvtype,invoice,invreq_quantity,86
120 tlvdata,invoice,relative_expiry,seconds_from_creation,tu32, tlvdata,invoice,invreq_quantity,quantity,tu64,
121 tlvtype,invoice,cltv,46 tlvtype,invoice,invreq_payer_id,88
122 tlvdata,invoice,cltv,min_final_cltv_expiry,tu16, tlvdata,invoice,invreq_payer_id,key,point,
123 tlvtype,invoice,fallbacks,48 tlvtype,invoice,invreq_payer_note,89
124 tlvdata,invoice,fallbacks,fallbacks,fallback_address,... tlvdata,invoice,invreq_payer_note,note,utf8,...
125 tlvtype,invoice,payer_info,50 tlvtype,invoice,invreq_recurrence_counter,90
126 tlvdata,invoice,payer_info,blob,byte,... tlvdata,invoice,invreq_recurrence_counter,counter,tu32,
127 tlvtype,invoice,refund_signature,52 tlvtype,invoice,invreq_recurrence_start,92
128 tlvdata,invoice,refund_signature,payer_signature,bip340sig, tlvdata,invoice,invreq_recurrence_start,period_offset,tu32,
129 tlvtype,invoice,send_invoice,54 tlvtype,invoice,invoice_paths,160
130 tlvtype,invoice,replace_invoice,56 tlvdata,invoice,invoice_paths,paths,blinded_path,...
131 tlvdata,invoice,replace_invoice,payment_hash,sha256, tlvtype,invoice,invoice_blindedpay,162
132 tlvdata,invoice,invoice_blindedpay,payinfo,blinded_payinfo,...
133 tlvtype,invoice,invoice_created_at,164
134 tlvdata,invoice,invoice_created_at,timestamp,tu64,
135 tlvtype,invoice,invoice_relative_expiry,166
136 tlvdata,invoice,invoice_relative_expiry,seconds_from_creation,tu32,
137 tlvtype,invoice,invoice_payment_hash,168
138 tlvdata,invoice,invoice_payment_hash,payment_hash,sha256,
139 tlvtype,invoice,invoice_amount,170
140 tlvdata,invoice,invoice_amount,msat,tu64,
141 tlvtype,invoice,invoice_fallbacks,172
142 tlvdata,invoice,invoice_fallbacks,fallbacks,fallback_address,...
143 tlvtype,invoice,invoice_features,174
144 tlvdata,invoice,invoice_features,features,byte,...
145 tlvtype,invoice,invoice_node_id,176
146 tlvdata,invoice,invoice_node_id,node_id,point,
147 tlvtype,invoice,invoice_recurrence_basetime,178
148 tlvdata,invoice,invoice_recurrence_basetime,basetime,tu64,
149 tlvtype,invoice,signature,240 tlvtype,invoice,signature,240
150 tlvdata,invoice,signature,sig,bip340sig, tlvdata,invoice,signature,sig,bip340sig,
151 subtype,recurrence
152 subtypedata,recurrence,time_unit,byte,
153 subtypedata,recurrence,period,tu32,
154 subtype,recurrence_paywindow
155 subtypedata,recurrence_paywindow,seconds_before,u32,
156 subtypedata,recurrence_paywindow,proportional_amount,byte,
157 subtypedata,recurrence_paywindow,seconds_after,tu32,
158 subtype,recurrence_base
159 subtypedata,recurrence_base,start_any_period,byte,
160 subtypedata,recurrence_base,basetime,tu64,
161 subtype,blinded_payinfo subtype,blinded_payinfo
162 subtypedata,blinded_payinfo,fee_base_msat,u32, subtypedata,blinded_payinfo,fee_base_msat,u32,
163 subtypedata,blinded_payinfo,fee_proportional_millionths,u32, subtypedata,blinded_payinfo,fee_proportional_millionths,u32,

View File

@ -1,47 +1,87 @@
diff --git b/wire/bolt12_wire.csv a/wire/bolt12_wire.csv
index 726c3c0a1..a53ca3cdf 100644
--- b/wire/bolt12_wire.csv
+++ a/wire/bolt12_wire.csv
@@ -18,6 +18,18 @@ tlvtype,offer,quantity_min,22
tlvdata,offer,quantity_min,min,tu64,
tlvtype,offer,quantity_max,24
tlvdata,offer,quantity_max,max,tu64,
+tlvtype,offer,recurrence,26
+tlvdata,offer,recurrence,time_unit,byte,
+tlvdata,offer,recurrence,period,tu32,
+tlvtype,offer,recurrence_paywindow,64
+tlvdata,offer,recurrence_paywindow,seconds_before,u32,
+tlvdata,offer,recurrence_paywindow,proportional_amount,byte,
+tlvdata,offer,recurrence_paywindow,seconds_after,tu32,
+tlvtype,offer,recurrence_limit,66
+tlvdata,offer,recurrence_limit,max_period,tu32,
+tlvtype,offer,recurrence_base,28
+tlvdata,offer,recurrence_base,start_any_period,byte,
+tlvdata,offer,recurrence_base,basetime,tu64,
tlvtype,offer,node_id,30
tlvdata,offer,node_id,node_id,pubkey,
tlvtype,offer,send_invoice,54
@@ -40,6 +54,10 @@ tlvtype,invoice_request,features,12
tlvdata,invoice_request,features,features,byte,...
tlvtype,invoice_request,quantity,32
tlvdata,invoice_request,quantity,quantity,tu64,
+tlvtype,invoice_request,recurrence_counter,36
+tlvdata,invoice_request,recurrence_counter,counter,tu32,
+tlvtype,invoice_request,recurrence_start,68
+tlvdata,invoice_request,recurrence_start,period_offset,tu32,
tlvtype,invoice_request,payer_key,38
tlvdata,invoice_request,payer_key,key,pubkey,
tlvtype,invoice_request,payer_note,39
@@ -74,6 +94,12 @@ tlvtype,invoice,quantity,32
tlvdata,invoice,quantity,quantity,tu64,
tlvtype,invoice,refund_for,34
tlvdata,invoice,refund_for,refunded_payment_hash,sha256,
+tlvtype,invoice,recurrence_counter,36
+tlvdata,invoice,recurrence_counter,counter,tu32,
+tlvtype,invoice,recurrence_start,68
+tlvdata,invoice,recurrence_start,period_offset,tu32,
+tlvtype,invoice,recurrence_basetime,64
+tlvdata,invoice,recurrence_basetime,basetime,tu64,
tlvtype,invoice,payer_key,38
tlvdata,invoice,payer_key,key,pubkey,
tlvtype,invoice,payer_note,39
--- wire/bolt12_wire.csv.raw 2022-10-04 13:26:18.105307201 +1030
+++ wire/bolt12_wire.csv 2022-10-04 13:25:59.617242667 +1030
@@ -22,6 +22,14 @@
tlvdata,offer,offer_quantity_max,max,tu64,
tlvtype,offer,offer_node_id,24
tlvdata,offer,offer_node_id,node_id,point,
+tlvtype,offer,offer_recurrence,26
+tlvdata,offer,offer_recurrence,recurrence,recurrence,
+tlvtype,offer,offer_recurrence_paywindow,28
+tlvdata,offer,offer_recurrence_paywindow,paywindow,recurrence_paywindow,
+tlvtype,offer,offer_recurrence_limit,30
+tlvdata,offer,offer_recurrence_limit,max_period,tu32,
+tlvtype,offer,offer_recurrence_base,32
+tlvdata,offer,offer_recurrence_base,base,recurrence_base,
tlvtype,invoice_request,invreq_metadata,0
tlvdata,invoice_request,invreq_metadata,blob,byte,...
tlvtype,invoice_request,offer_chains,2
@@ -48,6 +60,14 @@
tlvdata,invoice_request,offer_quantity_max,max,tu64,
tlvtype,invoice_request,offer_node_id,24
tlvdata,invoice_request,offer_node_id,node_id,point,
+tlvtype,invoice_request,offer_recurrence,26
+tlvdata,invoice_request,offer_recurrence,recurrence,recurrence,
+tlvtype,invoice_request,offer_recurrence_paywindow,28
+tlvdata,invoice_request,offer_recurrence_paywindow,paywindow,recurrence_paywindow,
+tlvtype,invoice_request,offer_recurrence_limit,30
+tlvdata,invoice_request,offer_recurrence_limit,max_period,tu32,
+tlvtype,invoice_request,offer_recurrence_base,32
+tlvdata,invoice_request,offer_recurrence_base,base,recurrence_base,
tlvtype,invoice_request,invreq_chain,80
tlvdata,invoice_request,invreq_chain,chain,chain_hash,
tlvtype,invoice_request,invreq_amount,82
@@ -60,6 +84,10 @@
tlvdata,invoice_request,invreq_payer_id,key,point,
tlvtype,invoice_request,invreq_payer_note,89
tlvdata,invoice_request,invreq_payer_note,note,utf8,...
+tlvtype,invoice_request,invreq_recurrence_counter,90
+tlvdata,invoice_request,invreq_recurrence_counter,counter,tu32,
+tlvtype,invoice_request,invreq_recurrence_start,92
+tlvdata,invoice_request,invreq_recurrence_start,period_offset,tu32,
tlvtype,invoice_request,signature,240
tlvdata,invoice_request,signature,sig,bip340sig,
tlvtype,invoice,invreq_metadata,0
@@ -89,5 +117,13 @@
tlvtype,invoice,offer_node_id,24
tlvdata,invoice,offer_node_id,node_id,point,
+tlvtype,invoice,offer_recurrence,26
+tlvdata,invoice,offer_recurrence,recurrence,recurrence,
+tlvtype,invoice,offer_recurrence_paywindow,28
+tlvdata,invoice,offer_recurrence_paywindow,paywindow,recurrence_paywindow,
+tlvtype,invoice,offer_recurrence_limit,30
+tlvdata,invoice,offer_recurrence_limit,max_period,tu32,
+tlvtype,invoice,offer_recurrence_base,32
+tlvdata,invoice,offer_recurrence_base,base,recurrence_base,
tlvtype,invoice,invreq_chain,80
tlvdata,invoice,invreq_chain,chain,chain_hash,
tlvtype,invoice,invreq_amount,82
@@ -101,6 +141,10 @@
tlvdata,invoice,invreq_payer_id,key,point,
tlvtype,invoice,invreq_payer_note,89
tlvdata,invoice,invreq_payer_note,note,utf8,...
+tlvtype,invoice,invreq_recurrence_counter,90
+tlvdata,invoice,invreq_recurrence_counter,counter,tu32,
+tlvtype,invoice,invreq_recurrence_start,92
+tlvdata,invoice,invreq_recurrence_start,period_offset,tu32,
tlvtype,invoice,invoice_paths,160
tlvdata,invoice,invoice_paths,paths,blinded_path,...
tlvtype,invoice,invoice_blindedpay,162
@@ -119,6 +163,18 @@
tlvdata,invoice,invoice_features,features,byte,...
tlvtype,invoice,invoice_node_id,176
tlvdata,invoice,invoice_node_id,node_id,point,
+tlvtype,invoice,invoice_recurrence_basetime,178
+tlvdata,invoice,invoice_recurrence_basetime,basetime,tu64,
tlvtype,invoice,signature,240
tlvdata,invoice,signature,sig,bip340sig,
+subtype,recurrence
+subtypedata,recurrence,time_unit,byte,
+subtypedata,recurrence,period,tu32,
+subtype,recurrence_paywindow
+subtypedata,recurrence_paywindow,seconds_before,u32,
+subtypedata,recurrence_paywindow,proportional_amount,byte,
+subtypedata,recurrence_paywindow,seconds_after,tu32,
+subtype,recurrence_base
+subtypedata,recurrence_base,start_any_period,byte,
+subtypedata,recurrence_base,basetime,tu64,
subtype,blinded_payinfo