mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-01-18 21:35:11 +01:00
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:
parent
846a520bc2
commit
1e3cb01546
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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",
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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. */
|
||||
|
378
plugins/offers.c
378
plugins/offers.c
@ -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");
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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,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,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
|
||||
|
Loading…
Reference in New Issue
Block a user