mirror of
https://github.com/aljazceru/lightning.git
synced 2025-12-20 15:44:21 +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:
committed by
Christian Decker
parent
846a520bc2
commit
1e3cb01546
@@ -11,7 +11,7 @@
|
|||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
/* If chains is NULL, max_num_chains is ignored */
|
/* If chains is NULL, max_num_chains is ignored */
|
||||||
static bool bolt12_chains_match(const struct bitcoin_blkid *chains,
|
bool bolt12_chains_match(const struct bitcoin_blkid *chains,
|
||||||
size_t max_num_chains,
|
size_t max_num_chains,
|
||||||
const struct chainparams *must_be_chain)
|
const struct chainparams *must_be_chain)
|
||||||
{
|
{
|
||||||
@@ -181,26 +181,13 @@ struct tlv_offer *offer_decode(const tal_t *ctx,
|
|||||||
|
|
||||||
*fail = check_features_and_chain(ctx,
|
*fail = check_features_and_chain(ctx,
|
||||||
our_features, must_be_chain,
|
our_features, must_be_chain,
|
||||||
offer->features,
|
offer->offer_features,
|
||||||
BOLT12_OFFER_FEATURE,
|
BOLT12_OFFER_FEATURE,
|
||||||
offer->chains,
|
offer->offer_chains,
|
||||||
tal_count(offer->chains));
|
tal_count(offer->offer_chains));
|
||||||
if (*fail)
|
if (*fail)
|
||||||
return tal_free(offer);
|
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;
|
return offer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,15 +217,15 @@ struct tlv_invoice_request *invrequest_decode(const tal_t *ctx,
|
|||||||
|
|
||||||
invrequest = fromwire_tlv_invoice_request(ctx, &data, &dlen);
|
invrequest = fromwire_tlv_invoice_request(ctx, &data, &dlen);
|
||||||
if (!invrequest) {
|
if (!invrequest) {
|
||||||
*fail = tal_fmt(ctx, "invalid invoice_request data");
|
*fail = tal_fmt(ctx, "invalid invreq data");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
*fail = check_features_and_chain(ctx,
|
*fail = check_features_and_chain(ctx,
|
||||||
our_features, must_be_chain,
|
our_features, must_be_chain,
|
||||||
invrequest->features,
|
invrequest->invreq_features,
|
||||||
BOLT12_INVREQ_FEATURE,
|
BOLT12_INVREQ_FEATURE,
|
||||||
invrequest->chain, 1);
|
invrequest->invreq_chain, 1);
|
||||||
if (*fail)
|
if (*fail)
|
||||||
return tal_free(invrequest);
|
return tal_free(invrequest);
|
||||||
|
|
||||||
@@ -277,9 +264,9 @@ struct tlv_invoice *invoice_decode_nosig(const tal_t *ctx,
|
|||||||
|
|
||||||
*fail = check_features_and_chain(ctx,
|
*fail = check_features_and_chain(ctx,
|
||||||
our_features, must_be_chain,
|
our_features, must_be_chain,
|
||||||
invoice->features,
|
invoice->invoice_features,
|
||||||
BOLT12_INVOICE_FEATURE,
|
BOLT12_INVOICE_FEATURE,
|
||||||
invoice->chain, 1);
|
invoice->invreq_chain, 1);
|
||||||
if (*fail)
|
if (*fail)
|
||||||
return tal_free(invoice);
|
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,
|
u64 offer_period_start(u64 basetime, size_t n,
|
||||||
const struct tlv_offer_recurrence *recur)
|
const struct recurrence *recur)
|
||||||
{
|
{
|
||||||
/* BOLT-offers-recurrence #12:
|
/* BOLT-offers-recurrence #12:
|
||||||
* 1. A `time_unit` defining 0 (seconds), 1 (days), 2 (months),
|
* 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,
|
void offer_period_paywindow(const struct recurrence *recurrence,
|
||||||
const struct tlv_offer_recurrence_paywindow *recurrence_paywindow,
|
const struct recurrence_paywindow *recurrence_paywindow,
|
||||||
const struct tlv_offer_recurrence_base *recurrence_base,
|
const struct recurrence_base *recurrence_base,
|
||||||
u64 basetime, u64 period_idx,
|
u64 basetime, u64 period_idx,
|
||||||
u64 *start, u64 *end)
|
u64 *start, u64 *end)
|
||||||
{
|
{
|
||||||
@@ -364,9 +351,9 @@ void offer_period_paywindow(const struct tlv_offer_recurrence *recurrence,
|
|||||||
/* BOLT-offers-recurrence #12:
|
/* BOLT-offers-recurrence #12:
|
||||||
* - if the offer has a `recurrence_basetime` or the
|
* - if the offer has a `recurrence_basetime` or the
|
||||||
* `recurrence_counter` is non-zero:
|
* `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.
|
* `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.
|
* than `seconds_after` seconds past that period start.
|
||||||
*/
|
*/
|
||||||
*start = pstart - recurrence_paywindow->seconds_before;
|
*start = pstart - recurrence_paywindow->seconds_before;
|
||||||
@@ -381,7 +368,7 @@ void offer_period_paywindow(const struct tlv_offer_recurrence *recurrence,
|
|||||||
} else {
|
} else {
|
||||||
/* BOLT-offers-recurrence #12:
|
/* BOLT-offers-recurrence #12:
|
||||||
* - otherwise:
|
* - otherwise:
|
||||||
* - SHOULD NOT send an `invoice_request` with
|
* - SHOULD NOT send an `invreq` with
|
||||||
* `recurrence_counter` is non-zero for a period whose
|
* `recurrence_counter` is non-zero for a period whose
|
||||||
* immediate predecessor has not yet begun.
|
* immediate predecessor has not yet begun.
|
||||||
*/
|
*/
|
||||||
@@ -392,7 +379,7 @@ void offer_period_paywindow(const struct tlv_offer_recurrence *recurrence,
|
|||||||
recurrence);
|
recurrence);
|
||||||
|
|
||||||
/* BOLT-offers-recurrence #12:
|
/* 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.
|
* has already passed.
|
||||||
*/
|
*/
|
||||||
*end = offer_period_start(basetime, period_idx+1,
|
*end = offer_period_start(basetime, period_idx+1,
|
||||||
@@ -413,7 +400,8 @@ struct tlv_invoice *invoice_decode(const tal_t *ctx,
|
|||||||
if (invoice) {
|
if (invoice) {
|
||||||
*fail = check_signature(ctx, invoice->fields,
|
*fail = check_signature(ctx, invoice->fields,
|
||||||
"invoice", "signature",
|
"invoice", "signature",
|
||||||
invoice->node_id, invoice->signature);
|
invoice->invoice_node_id,
|
||||||
|
invoice->signature);
|
||||||
if (*fail)
|
if (*fail)
|
||||||
invoice = tal_free(invoice);
|
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.
|
* invrequest_decode - decode this complete bolt12 text into a TLV.
|
||||||
* @ctx: the context to allocate return or *@fail off.
|
* @ctx: the context to allocate return or *@fail off.
|
||||||
* @b12: the invoice_request string
|
* @b12: the invreq string
|
||||||
* @b12len: the invoice_request string length
|
* @b12len: the invreq string length
|
||||||
* @our_features: if non-NULL, feature set to check against.
|
* @our_features: if non-NULL, feature set to check against.
|
||||||
* @must_be_chain: if non-NULL, chain to enforce.
|
* @must_be_chain: if non-NULL, chain to enforce.
|
||||||
* @fail: pointer to descriptive error string, set if this returns NULL.
|
* @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,
|
struct tlv_invoice_request *invrequest_decode(const tal_t *ctx,
|
||||||
const char *b12, size_t b12len,
|
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,
|
bool bolt12_chain_matches(const struct bitcoin_blkid *chain,
|
||||||
const struct chainparams *must_be_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? */
|
/* Given a basetime, when does period N start? */
|
||||||
u64 offer_period_start(u64 basetime, size_t n,
|
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. */
|
/* Get the start and end of the payment window for period N. */
|
||||||
void offer_period_paywindow(const struct tlv_offer_recurrence *recurrence,
|
void offer_period_paywindow(const struct recurrence *recurrence,
|
||||||
const struct tlv_offer_recurrence_paywindow *recurrence_paywindow,
|
const struct recurrence_paywindow *recurrence_paywindow,
|
||||||
const struct tlv_offer_recurrence_base *recurrence_base,
|
const struct recurrence_base *recurrence_base,
|
||||||
u64 basetime, u64 period_idx,
|
u64 basetime, u64 period_idx,
|
||||||
u64 *period_start, u64 *period_end);
|
u64 *period_start, u64 *period_end);
|
||||||
|
|
||||||
|
|||||||
@@ -192,7 +192,7 @@ int main(int argc, char *argv[])
|
|||||||
/* We deal in UTC; mktime() uses local time */
|
/* We deal in UTC; mktime() uses local time */
|
||||||
setenv("TZ", "", 1);
|
setenv("TZ", "", 1);
|
||||||
json_for_each_arr(i, t, toks) {
|
json_for_each_arr(i, t, toks) {
|
||||||
struct tlv_offer_recurrence recurrence;
|
struct recurrence recurrence;
|
||||||
int unit;
|
int unit;
|
||||||
const jsmntok_t *exp;
|
const jsmntok_t *exp;
|
||||||
u64 base, secs, n;
|
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));
|
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)
|
static void print_quantity_max(u64 max)
|
||||||
{
|
{
|
||||||
printf("quantity_max: %"PRIu64"\n", max);
|
printf("quantity_max: %"PRIu64"\n", max);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool print_recurrance(const struct tlv_offer_recurrence *recurrence,
|
static bool print_recurrance(const struct recurrence *recurrence,
|
||||||
const struct tlv_offer_recurrence_paywindow *paywindow,
|
const struct recurrence_paywindow *paywindow,
|
||||||
const u32 *limit,
|
const u32 *limit,
|
||||||
const struct tlv_offer_recurrence_base *base)
|
const struct recurrence_base *base)
|
||||||
{
|
{
|
||||||
const char *unit;
|
const char *unit;
|
||||||
bool ok = true;
|
bool ok = true;
|
||||||
@@ -293,17 +288,6 @@ static bool print_blindedpaths(struct blinded_path **paths,
|
|||||||
return true;
|
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,
|
static bool print_signature(const char *messagename,
|
||||||
const char *fieldname,
|
const char *fieldname,
|
||||||
const struct tlv_field *fields,
|
const struct tlv_field *fields,
|
||||||
@@ -328,12 +312,6 @@ static bool print_signature(const char *messagename,
|
|||||||
return true;
|
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)
|
static void print_quantity(u64 q)
|
||||||
{
|
{
|
||||||
printf("quantity: %"PRIu64"\n", q);
|
printf("quantity: %"PRIu64"\n", q);
|
||||||
@@ -363,13 +341,14 @@ static bool print_recurrence_counter_with_base(const u32 *recurrence_counter,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void print_payer_key(const struct pubkey *payer_key,
|
static void print_payer_id(const struct pubkey *payer_id,
|
||||||
const u8 *payer_info)
|
const u8 *metadata)
|
||||||
{
|
{
|
||||||
printf("payer_key: %s",
|
printf("invreq_payer_id: %s",
|
||||||
type_to_string(tmpctx, struct pubkey, payer_key));
|
type_to_string(tmpctx, struct pubkey, payer_id));
|
||||||
if (payer_info)
|
if (metadata)
|
||||||
printf(" (payer_info %s)", tal_hex(tmpctx, payer_info));
|
printf(" (invreq_metadata %s)",
|
||||||
|
tal_hex(tmpctx, metadata));
|
||||||
printf("\n");
|
printf("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -391,11 +370,6 @@ static void print_payment_hash(const struct sha256 *payment_hash)
|
|||||||
type_to_string(tmpctx, 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)
|
static void print_relative_expiry(u64 *created_at, u32 *relative)
|
||||||
{
|
{
|
||||||
/* Ignore if already malformed */
|
/* Ignore if already malformed */
|
||||||
@@ -553,42 +527,31 @@ int main(int argc, char *argv[])
|
|||||||
if (!offer)
|
if (!offer)
|
||||||
errx(ERROR_BAD_DECODE, "Bad offer: %s", fail);
|
errx(ERROR_BAD_DECODE, "Bad offer: %s", fail);
|
||||||
|
|
||||||
if (offer->send_invoice)
|
if (offer->offer_chains)
|
||||||
print_send_invoice();
|
print_chains(offer->offer_chains);
|
||||||
if (offer->chains)
|
if (offer->offer_amount)
|
||||||
print_chains(offer->chains);
|
well_formed &= print_amount(offer->offer_chains,
|
||||||
if (offer->refund_for)
|
offer->offer_currency,
|
||||||
print_refund_for(offer->refund_for);
|
*offer->offer_amount);
|
||||||
if (offer->amount)
|
if (must_have(offer, offer_description))
|
||||||
well_formed &= print_amount(offer->chains,
|
print_description(offer->offer_description);
|
||||||
offer->currency,
|
if (offer->offer_issuer)
|
||||||
*offer->amount);
|
print_issuer(offer->offer_issuer);
|
||||||
if (must_have(offer, description))
|
if (must_have(offer, offer_node_id))
|
||||||
print_description(offer->description);
|
print_node_id(offer->offer_node_id);
|
||||||
if (offer->issuer)
|
if (offer->offer_quantity_max)
|
||||||
print_issuer(offer->issuer);
|
print_quantity_max(*offer->offer_quantity_max);
|
||||||
if (must_have(offer, node_id))
|
if (offer->offer_recurrence)
|
||||||
print_node_id(offer->node_id);
|
well_formed &= print_recurrance(offer->offer_recurrence,
|
||||||
if (offer->quantity_min)
|
offer->offer_recurrence_paywindow,
|
||||||
print_quantity_min(*offer->quantity_min);
|
offer->offer_recurrence_limit,
|
||||||
if (offer->quantity_max)
|
offer->offer_recurrence_base);
|
||||||
print_quantity_max(*offer->quantity_max);
|
if (offer->offer_absolute_expiry)
|
||||||
if (offer->recurrence)
|
print_absolute_expiry(*offer->offer_absolute_expiry);
|
||||||
well_formed &= print_recurrance(offer->recurrence,
|
if (offer->offer_features)
|
||||||
offer->recurrence_paywindow,
|
print_features(offer->offer_features);
|
||||||
offer->recurrence_limit,
|
if (offer->offer_paths)
|
||||||
offer->recurrence_base);
|
print_blindedpaths(offer->offer_paths, NULL);
|
||||||
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 (!print_extra_fields(offer->fields))
|
if (!print_extra_fields(offer->fields))
|
||||||
well_formed = false;
|
well_formed = false;
|
||||||
} else if (streq(hrp, "lnr")) {
|
} else if (streq(hrp, "lnr")) {
|
||||||
@@ -596,43 +559,35 @@ int main(int argc, char *argv[])
|
|||||||
= invrequest_decode(ctx, argv[2], strlen(argv[2]),
|
= invrequest_decode(ctx, argv[2], strlen(argv[2]),
|
||||||
NULL, NULL, &fail);
|
NULL, NULL, &fail);
|
||||||
if (!invreq)
|
if (!invreq)
|
||||||
errx(ERROR_BAD_DECODE, "Bad invoice_request: %s", fail);
|
errx(ERROR_BAD_DECODE, "Bad invreq: %s", fail);
|
||||||
|
|
||||||
if (invreq->chain)
|
if (invreq->invreq_chain)
|
||||||
print_chain(invreq->chain);
|
print_chain(invreq->invreq_chain);
|
||||||
if (must_have(invreq, payer_key))
|
if (must_have(invreq, invreq_payer_id))
|
||||||
print_payer_key(invreq->payer_key, invreq->payer_info);
|
print_payer_id(invreq->invreq_payer_id,
|
||||||
if (invreq->payer_note)
|
invreq->invreq_metadata);
|
||||||
print_payer_note(invreq->payer_note);
|
if (invreq->invreq_payer_note)
|
||||||
if (must_have(invreq, offer_id))
|
print_payer_note(invreq->invreq_payer_note);
|
||||||
print_offer_id(invreq->offer_id);
|
if (must_have(invreq, invreq_amount))
|
||||||
if (must_have(invreq, amount))
|
well_formed &= print_amount(invreq->invreq_chain,
|
||||||
well_formed &= print_amount(invreq->chain,
|
|
||||||
NULL,
|
NULL,
|
||||||
*invreq->amount);
|
*invreq->invreq_amount);
|
||||||
if (invreq->features)
|
if (invreq->invreq_features)
|
||||||
print_features(invreq->features);
|
print_features(invreq->invreq_features);
|
||||||
if (invreq->quantity)
|
if (invreq->invreq_quantity)
|
||||||
print_quantity(*invreq->quantity);
|
print_quantity(*invreq->invreq_quantity);
|
||||||
if (must_have(invreq, signature)) {
|
if (must_have(invreq, signature)) {
|
||||||
if (!print_signature("invoice_request",
|
well_formed = print_signature("invoice_request",
|
||||||
"signature",
|
"signature",
|
||||||
invreq->fields,
|
invreq->fields,
|
||||||
invreq->payer_key,
|
invreq->invreq_payer_id,
|
||||||
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);
|
invreq->signature);
|
||||||
}
|
}
|
||||||
}
|
if (invreq->invreq_recurrence_counter) {
|
||||||
if (invreq->recurrence_counter) {
|
print_recurrence_counter(invreq->invreq_recurrence_counter,
|
||||||
print_recurrence_counter(invreq->recurrence_counter,
|
invreq->invreq_recurrence_start);
|
||||||
invreq->recurrence_start);
|
|
||||||
} else {
|
} else {
|
||||||
must_not_have(invreq, recurrence_start);
|
must_not_have(invreq, invreq_recurrence_start);
|
||||||
}
|
}
|
||||||
if (!print_extra_fields(invreq->fields))
|
if (!print_extra_fields(invreq->fields))
|
||||||
well_formed = false;
|
well_formed = false;
|
||||||
@@ -643,70 +598,55 @@ int main(int argc, char *argv[])
|
|||||||
if (!invoice)
|
if (!invoice)
|
||||||
errx(ERROR_BAD_DECODE, "Bad invoice: %s", fail);
|
errx(ERROR_BAD_DECODE, "Bad invoice: %s", fail);
|
||||||
|
|
||||||
if (invoice->chain)
|
if (invoice->invreq_chain)
|
||||||
print_chain(invoice->chain);
|
print_chain(invoice->invreq_chain);
|
||||||
|
|
||||||
if (invoice->offer_id) {
|
if (must_have(invoice, invoice_amount))
|
||||||
print_offer_id(invoice->offer_id);
|
well_formed &= print_amount(invoice->invreq_chain,
|
||||||
}
|
|
||||||
if (must_have(invoice, amount))
|
|
||||||
well_formed &= print_amount(invoice->chain,
|
|
||||||
NULL,
|
NULL,
|
||||||
*invoice->amount);
|
*invoice->invoice_amount);
|
||||||
if (must_have(invoice, description))
|
if (must_have(invoice, offer_description))
|
||||||
print_description(invoice->description);
|
print_description(invoice->offer_description);
|
||||||
if (invoice->features)
|
if (invoice->invoice_features)
|
||||||
print_features(invoice->features);
|
print_features(invoice->invoice_features);
|
||||||
if (invoice->paths) {
|
if (invoice->invoice_paths) {
|
||||||
must_have(invoice, blindedpay);
|
must_have(invoice, invoice_blindedpay);
|
||||||
well_formed &= print_blindedpaths(invoice->paths,
|
well_formed &= print_blindedpaths(invoice->invoice_paths,
|
||||||
invoice->blindedpay);
|
invoice->invoice_blindedpay);
|
||||||
} else
|
} else
|
||||||
must_not_have(invoice, blindedpay);
|
must_not_have(invoice, invoice_blindedpay);
|
||||||
if (invoice->issuer)
|
if (invoice->offer_issuer)
|
||||||
print_issuer(invoice->issuer);
|
print_issuer(invoice->offer_issuer);
|
||||||
if (must_have(invoice, node_id))
|
if (must_have(invoice, offer_node_id))
|
||||||
print_node_id(invoice->node_id);
|
print_node_id(invoice->offer_node_id);
|
||||||
if (invoice->quantity)
|
if (invoice->invreq_quantity)
|
||||||
print_quantity(*invoice->quantity);
|
print_quantity(*invoice->invreq_quantity);
|
||||||
if (invoice->refund_for) {
|
if (invoice->invreq_recurrence_counter) {
|
||||||
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) {
|
|
||||||
well_formed &=
|
well_formed &=
|
||||||
print_recurrence_counter_with_base(invoice->recurrence_counter,
|
print_recurrence_counter_with_base(invoice->invreq_recurrence_counter,
|
||||||
invoice->recurrence_start,
|
invoice->invreq_recurrence_start,
|
||||||
invoice->recurrence_basetime);
|
invoice->invoice_recurrence_basetime);
|
||||||
} else {
|
} else {
|
||||||
must_not_have(invoice, recurrence_start);
|
must_not_have(invoice, invreq_recurrence_start);
|
||||||
must_not_have(invoice, recurrence_basetime);
|
must_not_have(invoice, invoice_recurrence_basetime);
|
||||||
}
|
}
|
||||||
if (must_have(invoice, payer_key))
|
if (must_have(invoice, invreq_payer_id))
|
||||||
print_payer_key(invoice->payer_key, invoice->payer_info);
|
print_payer_id(invoice->invreq_payer_id,
|
||||||
if (must_have(invoice, created_at))
|
invoice->invreq_metadata);
|
||||||
print_created_at(*invoice->created_at);
|
if (must_have(invoice, invoice_created_at))
|
||||||
if (invoice->payer_note)
|
print_created_at(*invoice->invoice_created_at);
|
||||||
print_payer_note(invoice->payer_note);
|
if (invoice->invreq_payer_note)
|
||||||
print_relative_expiry(invoice->created_at,
|
print_payer_note(invoice->invreq_payer_note);
|
||||||
invoice->relative_expiry);
|
print_relative_expiry(invoice->invoice_created_at,
|
||||||
if (must_have(invoice, payment_hash))
|
invoice->invoice_relative_expiry);
|
||||||
print_payment_hash(invoice->payment_hash);
|
if (must_have(invoice, invoice_payment_hash))
|
||||||
if (must_have(invoice, cltv))
|
print_payment_hash(invoice->invoice_payment_hash);
|
||||||
print_cltv(*invoice->cltv);
|
if (invoice->invoice_fallbacks)
|
||||||
if (invoice->fallbacks)
|
print_fallbacks(invoice->invoice_fallbacks);
|
||||||
print_fallbacks(invoice->fallbacks);
|
|
||||||
if (must_have(invoice, signature))
|
if (must_have(invoice, signature))
|
||||||
well_formed &= print_signature("invoice", "signature",
|
well_formed &= print_signature("invoice", "signature",
|
||||||
invoice->fields,
|
invoice->fields,
|
||||||
invoice->node_id,
|
invoice->offer_node_id,
|
||||||
invoice->signature);
|
invoice->signature);
|
||||||
if (!print_extra_fields(invoice->fields))
|
if (!print_extra_fields(invoice->fields))
|
||||||
well_formed = false;
|
well_formed = false;
|
||||||
|
|||||||
@@ -50,8 +50,7 @@ If **type** is "bolt12 offer", and **valid** is *true*:
|
|||||||
- **path** (array of objects): an individual path:
|
- **path** (array of objects): an individual path:
|
||||||
- **blinded\_node\_id** (pubkey): node_id of the hop
|
- **blinded\_node\_id** (pubkey): node_id of the hop
|
||||||
- **encrypted\_recipient\_data** (hex): encrypted TLV entry for this 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 (or, if 0, means any quantity)
|
||||||
- **quantity\_max** (u64, optional): the maximum quantity
|
|
||||||
- **recurrence** (object, optional): how often to this offer should be used:
|
- **recurrence** (object, optional): how often to this offer should be used:
|
||||||
- **time\_unit** (u32): the BOLT12 time unit
|
- **time\_unit** (u32): the BOLT12 time unit
|
||||||
- **period** (u32): how many *time_unit* per payment period
|
- **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
|
- **created\_at** (u64): the UNIX timestamp of invoice creation
|
||||||
- **payment\_hash** (hex): the hash of the *payment_preimage* (always 64 characters)
|
- **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
|
- **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)
|
- **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)
|
- **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*)
|
- **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\_start** (u32, optional): the optional start period for a recurring payment
|
||||||
- **recurrence\_basetime** (u32, optional): the UNIX timestamp of the first recurrence period start
|
- **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\_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:
|
- **fallbacks** (array of objects, optional): onchain addresses:
|
||||||
- **version** (u8): Segwit address version
|
- **version** (u8): Segwit address version
|
||||||
- **hex** (hex): Raw encoded segwit address
|
- **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
|
- **quantity** (u64, optional): the quantity ordered
|
||||||
- **recurrence\_counter** (u32, optional): the 0-based counter for a recurring payment
|
- **recurrence\_counter** (u32, optional): the 0-based counter for a recurring payment
|
||||||
- **recurrence\_start** (u32, optional): the optional start period 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
|
- **recurrence\_signature** (bip340sig, optional): the payer key signature
|
||||||
|
|
||||||
If **type** is "bolt12 invoice_request", and **valid** is *false*:
|
If **type** is "bolt12 invoice_request", and **valid** is *false*:
|
||||||
@@ -217,4 +215,4 @@ RESOURCES
|
|||||||
|
|
||||||
Main web site: <https://github.com/ElementsProject/lightning>
|
Main web site: <https://github.com/ElementsProject/lightning>
|
||||||
|
|
||||||
[comment]: # ( SHA256STAMP:bbe57fd87e729e1203055d983a72757b9647ea67dca23c254a05b38b7b7020d9)
|
[comment]: # ( SHA256STAMP:3f0c78dd665bff6749352801b0fdd958ee138fda6ede5b3631d9cb136fc45d76)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ SYNOPSIS
|
|||||||
|
|
||||||
**(WARNING: experimental-offers only)**
|
**(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
|
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
|
The *label* field is an internal-use name for the offer, which can
|
||||||
be any UTF-8 string.
|
be any UTF-8 string.
|
||||||
|
|
||||||
The present of *quantity_min* or *quantity_max* indicates that the
|
The presence of *quantity_max* indicates that the
|
||||||
invoice can specify more than one of the items within this (inclusive)
|
invoice can specify more than one of the items up (and including)
|
||||||
range. The *amount* for the invoice will need to be multiplied
|
this maximum: 0 is a special value meaning "no maximuim".
|
||||||
accordingly. These are encoded in the offer.
|
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,
|
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
|
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": {
|
"quantity_max": {
|
||||||
"type": "u64",
|
"type": "u64",
|
||||||
"description": "the maximum quantity"
|
"description": "the maximum quantity (or, if 0, means any quantity)"
|
||||||
},
|
},
|
||||||
"recurrence": {
|
"recurrence": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
@@ -281,7 +277,6 @@
|
|||||||
"features": {},
|
"features": {},
|
||||||
"absolute_expiry": {},
|
"absolute_expiry": {},
|
||||||
"paths": {},
|
"paths": {},
|
||||||
"quantity_min": {},
|
|
||||||
"quantity_max": {},
|
"quantity_max": {},
|
||||||
"recurrence": {},
|
"recurrence": {},
|
||||||
"warning_offer_missing_description": {
|
"warning_offer_missing_description": {
|
||||||
@@ -316,8 +311,7 @@
|
|||||||
"description",
|
"description",
|
||||||
"created_at",
|
"created_at",
|
||||||
"payment_hash",
|
"payment_hash",
|
||||||
"relative_expiry",
|
"relative_expiry"
|
||||||
"min_final_cltv_expiry"
|
|
||||||
],
|
],
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"properties": {
|
"properties": {
|
||||||
@@ -453,7 +447,7 @@
|
|||||||
"type": "pubkey",
|
"type": "pubkey",
|
||||||
"description": "the transient key which identifies the payer"
|
"description": "the transient key which identifies the payer"
|
||||||
},
|
},
|
||||||
"payer_info": {
|
"invreq_metadata": {
|
||||||
"type": "hex",
|
"type": "hex",
|
||||||
"description": "the payer-provided blob to derive payer_key"
|
"description": "the payer-provided blob to derive payer_key"
|
||||||
},
|
},
|
||||||
@@ -474,10 +468,6 @@
|
|||||||
"type": "u32",
|
"type": "u32",
|
||||||
"description": "the number of seconds after *created_at* when this expires"
|
"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": {
|
"fallbacks": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"description": "onchain addresses",
|
"description": "onchain addresses",
|
||||||
@@ -550,12 +540,11 @@
|
|||||||
"recurrence_start": {},
|
"recurrence_start": {},
|
||||||
"recurrence_basetime": {},
|
"recurrence_basetime": {},
|
||||||
"payer_key": {},
|
"payer_key": {},
|
||||||
"payer_info": {},
|
"invreq_metadata": {},
|
||||||
"timestamp": {},
|
"timestamp": {},
|
||||||
"created_at": {},
|
"created_at": {},
|
||||||
"payment_hash": {},
|
"payment_hash": {},
|
||||||
"relative_expiry": {},
|
"relative_expiry": {},
|
||||||
"min_final_cltv_expiry": {},
|
|
||||||
"fallbacks": {
|
"fallbacks": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {
|
"items": {
|
||||||
@@ -681,7 +670,7 @@
|
|||||||
"type": "pubkey",
|
"type": "pubkey",
|
||||||
"description": "the transient key which identifies the payer"
|
"description": "the transient key which identifies the payer"
|
||||||
},
|
},
|
||||||
"payer_info": {
|
"invreq_metadata": {
|
||||||
"type": "hex",
|
"type": "hex",
|
||||||
"description": "the payer-provided blob to derive payer_key"
|
"description": "the payer-provided blob to derive payer_key"
|
||||||
},
|
},
|
||||||
@@ -723,7 +712,7 @@
|
|||||||
"recurrence_counter": {},
|
"recurrence_counter": {},
|
||||||
"recurrence_start": {},
|
"recurrence_start": {},
|
||||||
"payer_key": {},
|
"payer_key": {},
|
||||||
"payer_info": {},
|
"invreq_metadata": {},
|
||||||
"recurrence_signature": {},
|
"recurrence_signature": {},
|
||||||
"warning_invoice_request_missing_offer_id": {
|
"warning_invoice_request_missing_offer_id": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|||||||
@@ -70,10 +70,11 @@ static void json_add_invoice_fields(struct json_stream *response,
|
|||||||
tinv = invoice_decode(tmpctx,
|
tinv = invoice_decode(tmpctx,
|
||||||
inv->invstring, strlen(inv->invstring),
|
inv->invstring, strlen(inv->invstring),
|
||||||
NULL, NULL, &fail);
|
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",
|
json_add_stringn(response, "payer_note",
|
||||||
tinv->payer_note,
|
tinv->invreq_payer_note,
|
||||||
tal_bytelen(tinv->payer_note));
|
tal_bytelen(tinv->invreq_payer_note));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1352,11 +1353,11 @@ static struct command_result *json_listinvoices(struct command *cmd,
|
|||||||
strlen(invstring),
|
strlen(invstring),
|
||||||
cmd->ld->our_features, NULL,
|
cmd->ld->our_features, NULL,
|
||||||
&fail);
|
&fail);
|
||||||
if (!b12 || !b12->payment_hash) {
|
if (!b12 || !b12->invoice_payment_hash) {
|
||||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||||
"Invalid invstring");
|
"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 = tlv_encrypted_data_tlv_new(tmpctx);
|
||||||
tlv->path_id = invoice_path_id(inv,
|
tlv->path_id = invoice_path_id(inv,
|
||||||
&ld->invoicesecret_base,
|
&ld->invoicesecret_base,
|
||||||
inv->payment_hash);
|
inv->invoice_payment_hash);
|
||||||
|
|
||||||
path->path[0]->encrypted_recipient_data
|
path->path[0]->encrypted_recipient_data
|
||||||
= encrypt_tlv_encrypted_data(path->path[0],
|
= encrypt_tlv_encrypted_data(path->path[0],
|
||||||
@@ -1668,22 +1669,23 @@ static struct tlv_invoice *add_stub_blindedpath(const tal_t *ctx,
|
|||||||
NULL,
|
NULL,
|
||||||
&path->path[0]->blinded_node_id);
|
&path->path[0]->blinded_node_id);
|
||||||
|
|
||||||
inv->paths = tal_arr(inv, struct blinded_path *, 1);
|
inv->invoice_paths = tal_arr(inv, struct blinded_path *, 1);
|
||||||
inv->paths[0] = tal_steal(inv->paths, path);
|
inv->invoice_paths[0] = tal_steal(inv->invoice_paths, path);
|
||||||
|
|
||||||
/* BOLT-offers #12:
|
/* BOLT-offers #12:
|
||||||
* - MUST include `invoice_paths` containing one or more paths to the node.
|
* - 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 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.
|
* - 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->invoice_blindedpay = tal_arr(inv, struct blinded_payinfo *, 1);
|
||||||
inv->blindedpay[0] = tal(inv->blindedpay, struct blinded_payinfo);
|
inv->invoice_blindedpay[0] = tal(inv->invoice_blindedpay,
|
||||||
inv->blindedpay[0]->fee_base_msat = 0;
|
struct blinded_payinfo);
|
||||||
inv->blindedpay[0]->fee_proportional_millionths = 0;
|
inv->invoice_blindedpay[0]->fee_base_msat = 0;
|
||||||
inv->blindedpay[0]->cltv_expiry_delta = ld->config.cltv_final;
|
inv->invoice_blindedpay[0]->fee_proportional_millionths = 0;
|
||||||
inv->blindedpay[0]->htlc_minimum_msat = AMOUNT_MSAT(0);
|
inv->invoice_blindedpay[0]->cltv_expiry_delta = ld->config.cltv_final;
|
||||||
inv->blindedpay[0]->htlc_maximum_msat = AMOUNT_MSAT(21000000 * MSAT_PER_BTC);
|
inv->invoice_blindedpay[0]->htlc_minimum_msat = AMOUNT_MSAT(0);
|
||||||
inv->blindedpay[0]->features = NULL;
|
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 */
|
/* But we need to update ->fields, so re-linearize */
|
||||||
wire = tal_arr(tmpctx, u8, 0);
|
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);
|
notify_invoice_creation(cmd->ld, b11->msat, *preimage, label);
|
||||||
} else {
|
} else {
|
||||||
struct tlv_invoice *inv;
|
struct tlv_invoice *inv;
|
||||||
struct sha256 *local_offer_id;
|
struct sha256 offer_id, *local_offer_id;
|
||||||
char *b12enc;
|
char *b12enc;
|
||||||
struct amount_msat msat;
|
struct amount_msat msat;
|
||||||
const char *desc;
|
const char *desc;
|
||||||
@@ -1771,10 +1773,16 @@ static struct command_result *json_createinvoice(struct command *cmd,
|
|||||||
"Unparsable invoice '%s': %s",
|
"Unparsable invoice '%s': %s",
|
||||||
invstring, fail);
|
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
|
/* If they don't create a blinded path, add a simple one so we
|
||||||
* can recognize payments (bolt12 doesn't use
|
* can recognize payments (bolt12 doesn't use
|
||||||
* payment_secret) */
|
* payment_secret) */
|
||||||
if (!inv->paths)
|
if (!inv->invoice_paths)
|
||||||
inv = add_stub_blindedpath(cmd, cmd->ld, inv);
|
inv = add_stub_blindedpath(cmd, cmd->ld, inv);
|
||||||
|
|
||||||
if (inv->signature)
|
if (inv->signature)
|
||||||
@@ -1783,54 +1791,65 @@ static struct command_result *json_createinvoice(struct command *cmd,
|
|||||||
hsm_sign_b12_invoice(cmd->ld, inv);
|
hsm_sign_b12_invoice(cmd->ld, inv);
|
||||||
b12enc = invoice_encode(cmd, inv);
|
b12enc = invoice_encode(cmd, inv);
|
||||||
|
|
||||||
if (inv->offer_id
|
if (inv->offer_node_id) {
|
||||||
&& wallet_offer_find(tmpctx, cmd->ld->wallet,
|
invoice_offer_id(inv, &offer_id);
|
||||||
inv->offer_id, NULL, &status)) {
|
if (wallet_offer_find(tmpctx, cmd->ld->wallet,
|
||||||
|
&offer_id, NULL, &status)) {
|
||||||
if (!offer_status_active(status))
|
if (!offer_status_active(status))
|
||||||
return command_fail(cmd, INVOICE_OFFER_INACTIVE,
|
return command_fail(cmd,
|
||||||
|
INVOICE_OFFER_INACTIVE,
|
||||||
"offer not active");
|
"offer not active");
|
||||||
local_offer_id = inv->offer_id;
|
local_offer_id = &offer_id;
|
||||||
|
} else
|
||||||
|
local_offer_id = NULL;
|
||||||
} else
|
} else
|
||||||
local_offer_id = NULL;
|
local_offer_id = NULL;
|
||||||
|
|
||||||
if (inv->amount)
|
/* BOLT-offers #12:
|
||||||
msat = amount_msat(*inv->amount);
|
* 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)
|
if (inv->invoice_relative_expiry)
|
||||||
expiry = *inv->relative_expiry;
|
expiry = *inv->invoice_relative_expiry;
|
||||||
else
|
else
|
||||||
expiry = BOLT12_DEFAULT_REL_EXPIRY;
|
expiry = BOLT12_DEFAULT_REL_EXPIRY;
|
||||||
|
|
||||||
if (!inv->payment_hash)
|
if (!inv->invoice_payment_hash)
|
||||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||||
"Missing payment_hash in invoice");
|
"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,
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||||
"Incorrect preimage");
|
"Incorrect preimage");
|
||||||
|
|
||||||
if (!inv->description)
|
if (!inv->offer_description)
|
||||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||||
"Missing description in invoice");
|
"Missing description in invoice");
|
||||||
desc = tal_strndup(cmd,
|
desc = tal_strndup(cmd,
|
||||||
cast_signed(char *, inv->description),
|
inv->offer_description,
|
||||||
tal_bytelen(inv->description));
|
tal_bytelen(inv->offer_description));
|
||||||
|
|
||||||
if (!wallet_invoice_create(cmd->ld->wallet,
|
if (!wallet_invoice_create(cmd->ld->wallet,
|
||||||
&invoice,
|
&invoice,
|
||||||
inv->amount ? &msat : NULL,
|
&msat,
|
||||||
label,
|
label,
|
||||||
expiry,
|
expiry,
|
||||||
b12enc,
|
b12enc,
|
||||||
desc,
|
desc,
|
||||||
inv->features,
|
inv->invoice_features,
|
||||||
preimage,
|
preimage,
|
||||||
&payment_hash,
|
&payment_hash,
|
||||||
local_offer_id))
|
local_offer_id))
|
||||||
return fail_exists(cmd, label);
|
return fail_exists(cmd, label);
|
||||||
|
|
||||||
notify_invoice_creation(cmd->ld,
|
notify_invoice_creation(cmd->ld, &msat, *preimage, label);
|
||||||
inv->amount ? &msat : NULL,
|
|
||||||
*preimage, label);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
response = json_stream_success(cmd);
|
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);
|
cmd->ld->our_features, chainparams, &fail);
|
||||||
if (!*offer)
|
if (!*offer)
|
||||||
return command_fail_badparam(cmd, name, buffer, tok, fail);
|
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;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +83,7 @@ static struct command_result *json_createoffer(struct command *cmd,
|
|||||||
struct json_stream *response;
|
struct json_stream *response;
|
||||||
struct json_escape *label;
|
struct json_escape *label;
|
||||||
struct tlv_offer *offer;
|
struct tlv_offer *offer;
|
||||||
struct sha256 merkle;
|
struct sha256 offer_id;
|
||||||
const char *b12str;
|
const char *b12str;
|
||||||
bool *single_use;
|
bool *single_use;
|
||||||
enum offer_status status;
|
enum offer_status status;
|
||||||
@@ -103,15 +100,14 @@ static struct command_result *json_createoffer(struct command *cmd,
|
|||||||
status = OFFER_SINGLE_USE_UNUSED;
|
status = OFFER_SINGLE_USE_UNUSED;
|
||||||
else
|
else
|
||||||
status = OFFER_MULTIPLE_USE_UNUSED;
|
status = OFFER_MULTIPLE_USE_UNUSED;
|
||||||
merkle_tlv(offer->fields, &merkle);
|
|
||||||
offer->signature = NULL;
|
|
||||||
b12str = offer_encode(cmd, offer);
|
b12str = offer_encode(cmd, offer);
|
||||||
|
offer_offer_id(offer, &offer_id);
|
||||||
|
|
||||||
/* If it already exists, we use that one instead (and then
|
/* If it already exists, we use that one instead (and then
|
||||||
* the offer plugin will complain if it's inactive or expired) */
|
* 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)) {
|
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 **,
|
cast_const2(const struct json_escape **,
|
||||||
&label),
|
&label),
|
||||||
&status)) {
|
&status)) {
|
||||||
@@ -122,10 +118,8 @@ static struct command_result *json_createoffer(struct command *cmd,
|
|||||||
} else
|
} else
|
||||||
created = true;
|
created = true;
|
||||||
|
|
||||||
offer->signature = tal_free(offer->signature);
|
|
||||||
|
|
||||||
response = json_stream_success(cmd);
|
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);
|
json_add_bool(response, "created", created);
|
||||||
return command_success(cmd, response);
|
return command_success(cmd, response);
|
||||||
}
|
}
|
||||||
@@ -240,7 +234,7 @@ static const struct json_command disableoffer_command = {
|
|||||||
AUTODATA(json_command, &disableoffer_command);
|
AUTODATA(json_command, &disableoffer_command);
|
||||||
|
|
||||||
/* We do some sanity checks now, since we're looking up prev payment anyway,
|
/* 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,
|
static struct command_result *prev_payment(struct command *cmd,
|
||||||
const char *label,
|
const char *label,
|
||||||
struct tlv_invoice_request *invreq,
|
struct tlv_invoice_request *invreq,
|
||||||
@@ -248,13 +242,16 @@ static struct command_result *prev_payment(struct command *cmd,
|
|||||||
{
|
{
|
||||||
const struct wallet_payment **payments;
|
const struct wallet_payment **payments;
|
||||||
bool prev_paid = false;
|
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);
|
payments = wallet_payment_list(cmd, cmd->ld->wallet, NULL);
|
||||||
|
|
||||||
for (size_t i = 0; i < tal_count(payments); i++) {
|
for (size_t i = 0; i < tal_count(payments); i++) {
|
||||||
const struct tlv_invoice *inv;
|
const struct tlv_invoice *inv;
|
||||||
char *fail;
|
char *fail;
|
||||||
|
struct sha256 inv_oid;
|
||||||
|
|
||||||
/* FIXME: Restrict db queries instead */
|
/* FIXME: Restrict db queries instead */
|
||||||
if (!payments[i]->label || !streq(label, payments[i]->label))
|
if (!payments[i]->label || !streq(label, payments[i]->label))
|
||||||
@@ -270,12 +267,13 @@ static struct command_result *prev_payment(struct command *cmd,
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* They can reuse labels across different offers. */
|
/* 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;
|
continue;
|
||||||
|
|
||||||
/* Be paranoid, in case someone inserts their own
|
/* Be paranoid, in case someone inserts their own
|
||||||
* clashing label! */
|
* clashing label! */
|
||||||
if (!inv->recurrence_counter)
|
if (!inv->invreq_recurrence_counter)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* BOLT-offers-recurrence #12:
|
/* 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
|
* - MUST set `period_offset` to the same value on all
|
||||||
* following requests.
|
* following requests.
|
||||||
*/
|
*/
|
||||||
if (invreq->recurrence_start) {
|
if (invreq->invreq_recurrence_start) {
|
||||||
if (!inv->recurrence_start)
|
if (!inv->invreq_recurrence_start)
|
||||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||||
"unexpected"
|
"unexpected"
|
||||||
" recurrence_start");
|
" 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,
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||||
"recurrence_start was"
|
"recurrence_start was"
|
||||||
" previously %u",
|
" previously %u",
|
||||||
*inv->recurrence_start);
|
*inv->invreq_recurrence_start);
|
||||||
} else {
|
} else {
|
||||||
if (inv->recurrence_start)
|
if (inv->invreq_recurrence_start)
|
||||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||||
"missing"
|
"missing"
|
||||||
" recurrence_start");
|
" 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)
|
if (payments[i]->status == PAYMENT_COMPLETE)
|
||||||
prev_paid = true;
|
prev_paid = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inv->payer_info) {
|
if (inv->invreq_metadata) {
|
||||||
invreq->payer_info
|
invreq->invreq_metadata
|
||||||
= tal_dup_talarr(invreq, u8, inv->payer_info);
|
= tal_dup_talarr(invreq, u8, inv->invreq_metadata);
|
||||||
*prev_basetime = tal_dup(cmd, u64,
|
*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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!invreq->payer_info)
|
if (!invreq->invreq_metadata)
|
||||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||||
"No previous payment attempted for this"
|
"No previous payment attempted for this"
|
||||||
" label and offer");
|
" 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);
|
return command_fail_badparam(cmd, name, buffer, tok, fail);
|
||||||
#if !DEVELOPER
|
#if !DEVELOPER
|
||||||
/* We use this for testing with known payer_info */
|
/* 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,
|
return command_fail_badparam(cmd, name, buffer, tok,
|
||||||
"must not have payer_info");
|
"must not have invreq_metadata");
|
||||||
#endif
|
#endif
|
||||||
if ((*invreq)->payer_key)
|
if ((*invreq)->invreq_payer_id)
|
||||||
return command_fail_badparam(cmd, name, buffer, tok,
|
return command_fail_badparam(cmd, name, buffer, tok,
|
||||||
"must not have payer_key");
|
"must not have invreq_payer_id");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -389,12 +387,14 @@ static struct command_result *json_createinvoicerequest(struct command *cmd,
|
|||||||
NULL))
|
NULL))
|
||||||
return command_param_failed();
|
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)
|
if (!label)
|
||||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||||
"Need payment label for recurring payments");
|
"Need payment label for recurring payments");
|
||||||
|
|
||||||
if (*invreq->recurrence_counter != 0) {
|
if (*invreq->invreq_recurrence_counter != 0) {
|
||||||
struct command_result *err
|
struct command_result *err
|
||||||
= prev_payment(cmd, label, invreq,
|
= prev_payment(cmd, label, invreq,
|
||||||
&prev_basetime);
|
&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:
|
/* BOLT-offers #12:
|
||||||
* `payer_info` might typically contain information about the
|
*
|
||||||
* derivation of the `payer_key`. This should not leak any
|
* `invreq_metadata` might typically contain information about
|
||||||
* information (such as using a simple BIP-32 derivation
|
* the derivation of the `invreq_payer_id`. This should not
|
||||||
* path); a valid system might be for a node to maintain a
|
* leak any information (such as using a simple BIP-32
|
||||||
* base payer key, and encode a 128-bit tweak here. The
|
* derivation path); a valid system might be for a node to
|
||||||
* payer_key would be derived by tweaking the base key with
|
* maintain a base payer key and encode a 128-bit tweak here.
|
||||||
* SHA256(payer_base_pubkey || tweak).
|
* 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);
|
invreq->invreq_metadata = tal_arr(invreq, u8, 16);
|
||||||
randombytes_buf(invreq->payer_info,
|
randombytes_buf(invreq->invreq_metadata,
|
||||||
tal_bytelen(invreq->payer_info));
|
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,
|
if (!payer_key(cmd->ld,
|
||||||
invreq->payer_info, tal_bytelen(invreq->payer_info),
|
invreq->invreq_metadata,
|
||||||
invreq->payer_key)) {
|
tal_bytelen(invreq->invreq_metadata),
|
||||||
|
invreq->invreq_payer_id)) {
|
||||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||||
"Invalid tweak");
|
"Invalid tweak");
|
||||||
}
|
}
|
||||||
@@ -435,7 +439,7 @@ static struct command_result *json_createinvoicerequest(struct command *cmd,
|
|||||||
merkle_tlv(invreq->fields, &merkle);
|
merkle_tlv(invreq->fields, &merkle);
|
||||||
invreq->signature = tal(invreq, struct bip340sig);
|
invreq->signature = tal(invreq, struct bip340sig);
|
||||||
hsm_sign_b12(cmd->ld, "invoice_request", "signature",
|
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);
|
invreq->signature);
|
||||||
|
|
||||||
response = json_stream_success(cmd);
|
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),
|
b12 = invoice_decode(cmd, invstring, strlen(invstring),
|
||||||
cmd->ld->our_features,
|
cmd->ld->our_features,
|
||||||
chainparams, &fail);
|
chainparams, &fail);
|
||||||
if (b12 && b12->payment_hash)
|
if (b12 && b12->invoice_payment_hash)
|
||||||
rhash = b12->payment_hash;
|
rhash = b12->invoice_payment_hash;
|
||||||
else
|
else
|
||||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||||
"Invalid invstring: %s", fail);
|
"Invalid invstring: %s", fail);
|
||||||
|
|||||||
@@ -315,6 +315,9 @@ struct tlv_invoice *invoice_decode_nosig(const tal_t *ctx UNNEEDED,
|
|||||||
/* Generated stub for invoice_encode */
|
/* Generated stub for invoice_encode */
|
||||||
char *invoice_encode(const tal_t *ctx UNNEEDED, const struct tlv_invoice *bolt12_tlv UNNEEDED)
|
char *invoice_encode(const tal_t *ctx UNNEEDED, const struct tlv_invoice *bolt12_tlv UNNEEDED)
|
||||||
{ fprintf(stderr, "invoice_encode called!\n"); abort(); }
|
{ 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 */
|
/* Generated stub for invoice_path_id */
|
||||||
u8 *invoice_path_id(const tal_t *ctx UNNEEDED,
|
u8 *invoice_path_id(const tal_t *ctx UNNEEDED,
|
||||||
const struct secret *base_secret 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;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bolt12->description)
|
if (bolt12->offer_description)
|
||||||
desc = tal_strndup(ctx,
|
desc = tal_strndup(ctx,
|
||||||
cast_signed(char *, bolt12->description),
|
cast_signed(char *, bolt12->offer_description),
|
||||||
tal_bytelen(bolt12->description));
|
tal_bytelen(bolt12->offer_description));
|
||||||
else
|
else
|
||||||
desc = NULL;
|
desc = NULL;
|
||||||
} else
|
} else
|
||||||
|
|||||||
@@ -59,37 +59,6 @@ static struct sent *find_sent_by_secret(const struct secret *pathsecret)
|
|||||||
return NULL;
|
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 */
|
/* Hack to suppress warnings when we finish a different command */
|
||||||
static void discard_result(struct command_result *ret)
|
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);
|
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,
|
static struct command_result *handle_invreq_response(struct command *cmd,
|
||||||
struct sent *sent,
|
struct sent *sent,
|
||||||
const char *buf,
|
const char *buf,
|
||||||
const jsmntok_t *om)
|
const jsmntok_t *om)
|
||||||
{
|
{
|
||||||
const u8 *invbin;
|
const u8 *invbin, *cursor;
|
||||||
const jsmntok_t *invtok;
|
const jsmntok_t *invtok;
|
||||||
size_t len;
|
size_t len;
|
||||||
struct tlv_invoice *inv;
|
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);
|
invbin = json_tok_bin_from_hex(cmd, buf, invtok);
|
||||||
|
cursor = invbin;
|
||||||
len = tal_bytelen(invbin);
|
len = tal_bytelen(invbin);
|
||||||
inv = fromwire_tlv_invoice(cmd, &invbin, &len);
|
inv = fromwire_tlv_invoice(cmd, &cursor, &len);
|
||||||
if (!inv) {
|
if (!inv) {
|
||||||
badfield = "invoice";
|
badfield = "invoice";
|
||||||
goto badinv;
|
goto badinv;
|
||||||
@@ -202,88 +193,64 @@ static struct command_result *handle_invreq_response(struct command *cmd,
|
|||||||
#endif /* DEVELOPER */
|
#endif /* DEVELOPER */
|
||||||
|
|
||||||
/* BOLT-offers #12:
|
/* 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)) {
|
if (!invoice_matches_request(cmd, invbin, sent->invreq)) {
|
||||||
badfield = "node_id";
|
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;
|
goto badinv;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* BOLT-offers #12:
|
/* BOLT-offers #12:
|
||||||
* - MUST reject the invoice if `signature` is not a valid signature
|
* - 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);
|
merkle_tlv(inv->fields, &merkle);
|
||||||
sighash_from_merkle("invoice", "signature", &merkle, &sighash);
|
sighash_from_merkle("invoice", "signature", &merkle, &sighash);
|
||||||
|
|
||||||
if (!inv->signature
|
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";
|
badfield = "signature";
|
||||||
goto badinv;
|
goto badinv;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* BOLT-offers #12:
|
/* 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) {
|
if (!inv->invoice_amount) {
|
||||||
badfield = "amount";
|
badfield = "invoice_amount";
|
||||||
goto badinv;
|
goto badinv;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* BOLT-offers #12:
|
/* FIXME-OFFERS: Examine fields in inv directly! */
|
||||||
* - 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;
|
|
||||||
|
|
||||||
/* Get the amount we expected: firstly, if that's what we sent,
|
/* Get the amount we expected: firstly, if that's what we sent,
|
||||||
* secondly, if specified in the invoice. */
|
* secondly, if specified in the invoice. */
|
||||||
if (sent->invreq->amount) {
|
if (sent->invreq->invreq_amount) {
|
||||||
expected_amount = tal_dup(tmpctx, u64, sent->invreq->amount);
|
expected_amount = tal_dup(tmpctx, u64, sent->invreq->invreq_amount);
|
||||||
} else if (sent->offer->amount && !sent->offer->currency) {
|
} else if (sent->offer->offer_amount && !sent->offer->offer_currency) {
|
||||||
expected_amount = tal(tmpctx, u64);
|
expected_amount = tal(tmpctx, u64);
|
||||||
|
|
||||||
*expected_amount = *sent->offer->amount;
|
*expected_amount = *sent->offer->offer_amount;
|
||||||
if (sent->invreq->quantity) {
|
if (sent->invreq->invreq_quantity) {
|
||||||
/* We should never have sent this! */
|
/* We should never have sent this! */
|
||||||
if (mul_overflows_u64(*expected_amount,
|
if (mul_overflows_u64(*expected_amount,
|
||||||
*sent->invreq->quantity)) {
|
*sent->invreq->invreq_quantity)) {
|
||||||
badfield = "quantity overflow";
|
badfield = "quantity overflow";
|
||||||
goto badinv;
|
goto badinv;
|
||||||
}
|
}
|
||||||
*expected_amount *= *sent->invreq->quantity;
|
*expected_amount *= *sent->invreq->invreq_quantity;
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
expected_amount = NULL;
|
expected_amount = NULL;
|
||||||
@@ -292,97 +259,56 @@ static struct command_result *handle_invreq_response(struct command *cmd,
|
|||||||
* - if the offer contained `recurrence`:
|
* - if the offer contained `recurrence`:
|
||||||
* - MUST reject the invoice if `recurrence_basetime` is not set.
|
* - 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";
|
badfield = "recurrence_basetime";
|
||||||
goto badinv;
|
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);
|
out = jsonrpc_stream_success(sent->cmd);
|
||||||
json_add_string(out, "invoice", invoice_encode(tmpctx, inv));
|
json_add_string(out, "invoice", invoice_encode(tmpctx, inv));
|
||||||
json_object_start(out, "changes");
|
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:
|
/* BOLT-offers #12:
|
||||||
* - SHOULD confirm authorization if `msat` is not within the amount
|
* - SHOULD confirm authorization if `msat` is not within the amount
|
||||||
* range authorized.
|
* range authorized.
|
||||||
*/
|
*/
|
||||||
/* We always tell them this unless it's trivial to calc and
|
/* We always tell them this unless it's trivial to calc and
|
||||||
* exactly as expected. */
|
* exactly as expected. */
|
||||||
if (!expected_amount || *inv->amount != *expected_amount) {
|
if (!expected_amount || *inv->invoice_amount != *expected_amount) {
|
||||||
if (deprecated_apis)
|
|
||||||
json_add_amount_msat_only(out, "msat",
|
|
||||||
amount_msat(*inv->amount));
|
|
||||||
json_add_amount_msat_only(out, "amount_msat",
|
json_add_amount_msat_only(out, "amount_msat",
|
||||||
amount_msat(*inv->amount));
|
amount_msat(*inv->invoice_amount));
|
||||||
}
|
}
|
||||||
json_object_end(out);
|
json_object_end(out);
|
||||||
|
|
||||||
/* We tell them about next period at this point, if any. */
|
/* 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 next_counter, next_period_idx;
|
||||||
u64 paywindow_start, paywindow_end;
|
u64 paywindow_start, paywindow_end;
|
||||||
|
|
||||||
next_counter = *sent->invreq->recurrence_counter + 1;
|
next_counter = *sent->invreq->invreq_recurrence_counter + 1;
|
||||||
if (sent->invreq->recurrence_start)
|
if (sent->invreq->invreq_recurrence_start)
|
||||||
next_period_idx = *sent->invreq->recurrence_start
|
next_period_idx = *sent->invreq->invreq_recurrence_start
|
||||||
+ next_counter;
|
+ next_counter;
|
||||||
else
|
else
|
||||||
next_period_idx = next_counter;
|
next_period_idx = next_counter;
|
||||||
|
|
||||||
/* If this was the last, don't tell them about a next! */
|
/* If this was the last, don't tell them about a next! */
|
||||||
if (!sent->offer->recurrence_limit
|
if (!sent->offer->offer_recurrence_limit
|
||||||
|| next_period_idx <= *sent->offer->recurrence_limit) {
|
|| next_period_idx <= *sent->offer->offer_recurrence_limit) {
|
||||||
json_object_start(out, "next_period");
|
json_object_start(out, "next_period");
|
||||||
json_add_u64(out, "counter", next_counter);
|
json_add_u64(out, "counter", next_counter);
|
||||||
json_add_u64(out, "starttime",
|
json_add_u64(out, "starttime",
|
||||||
offer_period_start(*inv->recurrence_basetime,
|
offer_period_start(*inv->invoice_recurrence_basetime,
|
||||||
next_period_idx,
|
next_period_idx,
|
||||||
sent->offer->recurrence));
|
sent->offer->offer_recurrence));
|
||||||
json_add_u64(out, "endtime",
|
json_add_u64(out, "endtime",
|
||||||
offer_period_start(*inv->recurrence_basetime,
|
offer_period_start(*inv->invoice_recurrence_basetime,
|
||||||
next_period_idx + 1,
|
next_period_idx + 1,
|
||||||
sent->offer->recurrence) - 1);
|
sent->offer->offer_recurrence) - 1);
|
||||||
|
|
||||||
offer_period_paywindow(sent->offer->recurrence,
|
offer_period_paywindow(sent->offer->offer_recurrence,
|
||||||
sent->offer->recurrence_paywindow,
|
sent->offer->offer_recurrence_paywindow,
|
||||||
sent->offer->recurrence_base,
|
sent->offer->offer_recurrence_base,
|
||||||
*inv->recurrence_basetime,
|
*inv->invoice_recurrence_basetime,
|
||||||
next_period_idx,
|
next_period_idx,
|
||||||
&paywindow_start, &paywindow_end);
|
&paywindow_start, &paywindow_end);
|
||||||
json_add_u64(out, "paywindow_start", paywindow_start);
|
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)
|
struct tlv_offer **offer)
|
||||||
{
|
{
|
||||||
char *fail;
|
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,
|
*offer = offer_decode(cmd, buffer + tok->start, tok->end - tok->start,
|
||||||
plugin_feature_set(cmd->plugin), chainparams,
|
plugin_feature_set(cmd->plugin), chainparams,
|
||||||
&fail);
|
&fail);
|
||||||
@@ -520,19 +436,61 @@ static struct command_result *param_offer(struct command *cmd,
|
|||||||
tal_fmt(cmd,
|
tal_fmt(cmd,
|
||||||
"Unparsable offer: %s",
|
"Unparsable offer: %s",
|
||||||
fail));
|
fail));
|
||||||
|
|
||||||
/* BOLT-offers #12:
|
/* BOLT-offers #12:
|
||||||
*
|
* A reader of an offer:
|
||||||
* - if `node_id` or `description` is not set:
|
* - 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.
|
* - 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,
|
return command_fail_badparam(cmd, name, buffer, tok,
|
||||||
"Offer does not contain a node_id");
|
"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,
|
return command_fail_badparam(cmd, name, buffer, tok,
|
||||||
"Offer does not contain a description");
|
"Offer for wrong chains");
|
||||||
|
}
|
||||||
|
|
||||||
return NULL;
|
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
|
/* Now that's given us the previous base, check this is an OK time
|
||||||
* to request an invoice. */
|
* to request an invoice. */
|
||||||
if (sent->invreq->recurrence_counter) {
|
if (sent->invreq->invreq_recurrence_counter) {
|
||||||
u64 *base;
|
u64 *base;
|
||||||
const jsmntok_t *pbtok;
|
const jsmntok_t *pbtok;
|
||||||
u64 period_idx = *sent->invreq->recurrence_counter;
|
u64 period_idx = *sent->invreq->invreq_recurrence_counter;
|
||||||
|
|
||||||
if (sent->invreq->recurrence_start)
|
if (sent->invreq->invreq_recurrence_start)
|
||||||
period_idx += *sent->invreq->recurrence_start;
|
period_idx += *sent->invreq->invreq_recurrence_start;
|
||||||
|
|
||||||
/* BOLT-offers-recurrence #12:
|
/* BOLT-offers-recurrence #12:
|
||||||
* - if the offer contained `recurrence_limit`:
|
* - if the offer contained `recurrence_limit`:
|
||||||
* - MUST NOT send an `invoice_request` for a period greater
|
* - MUST NOT send an `invoice_request` for a period greater
|
||||||
* than `max_period`
|
* than `max_period`
|
||||||
*/
|
*/
|
||||||
if (sent->offer->recurrence_limit
|
if (sent->offer->offer_recurrence_limit
|
||||||
&& period_idx > *sent->offer->recurrence_limit)
|
&& period_idx > *sent->offer->offer_recurrence_limit)
|
||||||
return command_fail(cmd, LIGHTNINGD,
|
return command_fail(cmd, LIGHTNINGD,
|
||||||
"Can't send invreq for period %"
|
"Can't send invreq for period %"
|
||||||
PRIu64" (limit %u)",
|
PRIu64" (limit %u)",
|
||||||
period_idx,
|
period_idx,
|
||||||
*sent->offer->recurrence_limit);
|
*sent->offer->offer_recurrence_limit);
|
||||||
|
|
||||||
/* BOLT-offers-recurrence #12:
|
/* BOLT-offers-recurrence #12:
|
||||||
* - SHOULD NOT send an `invoice_request` for a period which has
|
* - 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) {
|
if (pbtok) {
|
||||||
base = tal(tmpctx, u64);
|
base = tal(tmpctx, u64);
|
||||||
json_to_u64(buf, pbtok, base);
|
json_to_u64(buf, pbtok, base);
|
||||||
} else if (sent->offer->recurrence_base)
|
} else if (sent->offer->offer_recurrence_base)
|
||||||
base = &sent->offer->recurrence_base->basetime;
|
base = &sent->offer->offer_recurrence_base->basetime;
|
||||||
else {
|
else {
|
||||||
/* happens with *recurrence_base == 0 */
|
/* happens with *recurrence_base == 0 */
|
||||||
assert(*sent->invreq->recurrence_counter == 0);
|
assert(*sent->invreq->invreq_recurrence_counter == 0);
|
||||||
base = NULL;
|
base = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (base) {
|
if (base) {
|
||||||
u64 period_start, period_end, now = time_now().ts.tv_sec;
|
u64 period_start, period_end, now = time_now().ts.tv_sec;
|
||||||
offer_period_paywindow(sent->offer->recurrence,
|
offer_period_paywindow(sent->offer->offer_recurrence,
|
||||||
sent->offer->recurrence_paywindow,
|
sent->offer->offer_recurrence_paywindow,
|
||||||
sent->offer->recurrence_base,
|
sent->offer->offer_recurrence_base,
|
||||||
*base, period_idx,
|
*base, period_idx,
|
||||||
&period_start, &period_end);
|
&period_start, &period_end);
|
||||||
if (now < period_start)
|
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->path = path_to_node(sent, cmd->plugin,
|
||||||
sent->offer->node_id);
|
sent->offer->offer_node_id);
|
||||||
if (!sent->path)
|
if (!sent->path)
|
||||||
return connect_direct(cmd, sent->offer->node_id,
|
return connect_direct(cmd, sent->offer->offer_node_id,
|
||||||
sendinvreq_after_connect, sent);
|
sendinvreq_after_connect, sent);
|
||||||
|
|
||||||
return sendinvreq_after_connect(cmd, NULL, NULL, 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)
|
if (secp256k1_keypair_create(secp256k1_ctx, &kp, payer_secret->data) != 1)
|
||||||
return command_fail(cmd, LIGHTNINGD, "Bad payer_secret");
|
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! */
|
/* Docs say this only happens if arguments are invalid! */
|
||||||
if (secp256k1_keypair_pub(secp256k1_ctx,
|
if (secp256k1_keypair_pub(secp256k1_ctx,
|
||||||
&invreq->payer_key->pubkey,
|
&invreq->invreq_payer_id->pubkey,
|
||||||
&kp) != 1)
|
&kp) != 1)
|
||||||
plugin_err(cmd->plugin,
|
plugin_err(cmd->plugin,
|
||||||
"secp256k1_keypair_pub failed on %s?",
|
"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->path = path_to_node(sent, cmd->plugin,
|
||||||
sent->offer->node_id);
|
sent->offer->offer_node_id);
|
||||||
if (!sent->path)
|
if (!sent->path)
|
||||||
return connect_direct(cmd, sent->offer->node_id,
|
return connect_direct(cmd, sent->offer->offer_node_id,
|
||||||
sendinvreq_after_connect, sent);
|
sendinvreq_after_connect, sent);
|
||||||
|
|
||||||
return sendinvreq_after_connect(cmd, NULL, NULL, 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 sent *sent = tal(cmd, struct sent);
|
||||||
struct secret *payer_secret = NULL;
|
struct secret *payer_secret = NULL;
|
||||||
u32 *timeout;
|
u32 *timeout;
|
||||||
|
u64 *quantity;
|
||||||
|
u32 *recurrence_counter, *recurrence_start;
|
||||||
|
|
||||||
invreq = tlv_invoice_request_new(sent);
|
|
||||||
if (!param(cmd, buffer, params,
|
if (!param(cmd, buffer, params,
|
||||||
p_req("offer", param_offer, &sent->offer),
|
p_req("offer", param_offer, &sent->offer),
|
||||||
p_opt("amount_msat|msatoshi", param_msat, &msat),
|
p_opt("amount_msat|msatoshi", param_msat, &msat),
|
||||||
p_opt("quantity", param_u64, &invreq->quantity),
|
p_opt("quantity", param_u64, &quantity),
|
||||||
p_opt("recurrence_counter", param_number,
|
p_opt("recurrence_counter", param_number, &recurrence_counter),
|
||||||
&invreq->recurrence_counter),
|
p_opt("recurrence_start", param_number, &recurrence_start),
|
||||||
p_opt("recurrence_start", param_number,
|
|
||||||
&invreq->recurrence_start),
|
|
||||||
p_opt("recurrence_label", param_string, &rec_label),
|
p_opt("recurrence_label", param_string, &rec_label),
|
||||||
p_opt_def("timeout", param_number, &timeout, 60),
|
p_opt_def("timeout", param_number, &timeout, 60),
|
||||||
p_opt("payer_note", param_string, &payer_note),
|
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;
|
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:
|
/* BOLT-offers #12:
|
||||||
* - SHOULD not respond to an offer if the current time is after
|
* - SHOULD not respond to an offer if the current time is after
|
||||||
* `absolute_expiry`.
|
* `absolute_expiry`.
|
||||||
*/
|
*/
|
||||||
if (sent->offer->absolute_expiry
|
if (sent->offer->offer_absolute_expiry
|
||||||
&& time_now().ts.tv_sec > *sent->offer->absolute_expiry)
|
&& time_now().ts.tv_sec > *sent->offer->offer_absolute_expiry)
|
||||||
return command_fail(cmd, OFFER_EXPIRED, "Offer expired");
|
return command_fail(cmd, OFFER_EXPIRED, "Offer expired");
|
||||||
|
|
||||||
/* BOLT-offers-recurrence #12:
|
/* BOLT-offers #12:
|
||||||
* - if the offer did not specify `amount`:
|
* The writer:
|
||||||
* - MUST specify `amount`.`msat` in multiples of the minimum
|
* - if it is responding to an offer:
|
||||||
* lightning-payable unit (e.g. milli-satoshis for bitcoin) for
|
* - MUST copy all fields from the offer (including unknown fields).
|
||||||
* `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).
|
|
||||||
*/
|
*/
|
||||||
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? */
|
/* FIXME: Check after quantity? */
|
||||||
if (msat) {
|
if (msat) {
|
||||||
invreq->amount = tal_dup(invreq, u64,
|
invreq->invreq_amount = tal_dup(invreq, u64,
|
||||||
&msat->millisatoshis); /* Raw: tu64 */
|
&msat->millisatoshis); /* Raw: tu64 */
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!msat)
|
if (!msat)
|
||||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||||
"msatoshi parameter required");
|
"msatoshi parameter required");
|
||||||
invreq->amount = tal_dup(invreq, u64,
|
invreq->invreq_amount = tal_dup(invreq, u64,
|
||||||
&msat->millisatoshis); /* Raw: tu64 */
|
&msat->millisatoshis); /* Raw: tu64 */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* FIXME-OFFERS: Examine fields in inv directly! */
|
||||||
/* BOLT-offers #12:
|
/* BOLT-offers #12:
|
||||||
* - if the offer had a `quantity_min` or `quantity_max` field:
|
* - if `offer_quantity_max` is present:
|
||||||
* - MUST set `quantity`
|
* - MUST set `invreq_quantity`
|
||||||
* - MUST set it within that (inclusive) range.
|
* - if `offer_quantity_max` is non-zero:
|
||||||
* - otherwise:
|
* - MUST set `invreq_quantity` less than or equal to
|
||||||
* - MUST NOT set `quantity`
|
* `offer_quantity_max`.
|
||||||
*/
|
*/
|
||||||
if (sent->offer->quantity_min || sent->offer->quantity_max) {
|
if (sent->offer->offer_quantity_max) {
|
||||||
if (!invreq->quantity)
|
if (!invreq->invreq_quantity)
|
||||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||||
"quantity parameter required");
|
"quantity parameter required");
|
||||||
if (sent->offer->quantity_min
|
if (*sent->offer->offer_quantity_max
|
||||||
&& *invreq->quantity < *sent->offer->quantity_min)
|
&& *invreq->invreq_quantity > *sent->offer->offer_quantity_max)
|
||||||
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)
|
|
||||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||||
"quantity must be <= %"PRIu64,
|
"quantity must be <= %"PRIu64,
|
||||||
*sent->offer->quantity_max);
|
*sent->offer->offer_quantity_max);
|
||||||
} else {
|
} else {
|
||||||
if (invreq->quantity)
|
if (invreq->invreq_quantity)
|
||||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||||
"quantity parameter unnecessary");
|
"quantity parameter unnecessary");
|
||||||
}
|
}
|
||||||
@@ -1112,7 +1061,7 @@ static struct command_result *json_fetchinvoice(struct command *cmd,
|
|||||||
/* BOLT-offers-recurrence #12:
|
/* BOLT-offers-recurrence #12:
|
||||||
* - if the offer contained `recurrence`:
|
* - if the offer contained `recurrence`:
|
||||||
*/
|
*/
|
||||||
if (sent->offer->recurrence) {
|
if (sent->offer->offer_recurrence) {
|
||||||
/* BOLT-offers-recurrence #12:
|
/* BOLT-offers-recurrence #12:
|
||||||
* - for the initial request:
|
* - 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
|
* - MUST set `recurrence_counter` `counter` to one greater
|
||||||
* than the highest-paid invoice.
|
* than the highest-paid invoice.
|
||||||
*/
|
*/
|
||||||
if (!invreq->recurrence_counter)
|
if (!invreq->invreq_recurrence_counter)
|
||||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||||
"needs recurrence_counter");
|
"needs recurrence_counter");
|
||||||
|
|
||||||
@@ -1136,13 +1085,13 @@ static struct command_result *json_fetchinvoice(struct command *cmd,
|
|||||||
* - otherwise:
|
* - otherwise:
|
||||||
* - MUST NOT include `recurrence_start`
|
* - MUST NOT include `recurrence_start`
|
||||||
*/
|
*/
|
||||||
if (sent->offer->recurrence_base
|
if (sent->offer->offer_recurrence_base
|
||||||
&& sent->offer->recurrence_base->start_any_period) {
|
&& sent->offer->offer_recurrence_base->start_any_period) {
|
||||||
if (!invreq->recurrence_start)
|
if (!invreq->invreq_recurrence_start)
|
||||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||||
"needs recurrence_start");
|
"needs recurrence_start");
|
||||||
} else {
|
} else {
|
||||||
if (invreq->recurrence_start)
|
if (invreq->invreq_recurrence_start)
|
||||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||||
"unnecessary recurrence_start");
|
"unnecessary recurrence_start");
|
||||||
}
|
}
|
||||||
@@ -1158,33 +1107,40 @@ static struct command_result *json_fetchinvoice(struct command *cmd,
|
|||||||
* - MUST NOT set `recurrence_counter`.
|
* - MUST NOT set `recurrence_counter`.
|
||||||
* - MUST NOT set `recurrence_start`
|
* - MUST NOT set `recurrence_start`
|
||||||
*/
|
*/
|
||||||
if (invreq->recurrence_counter)
|
if (invreq->invreq_recurrence_counter)
|
||||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||||
"unnecessary recurrence_counter");
|
"unnecessary recurrence_counter");
|
||||||
if (invreq->recurrence_start)
|
if (invreq->invreq_recurrence_start)
|
||||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||||
"unnecessary recurrence_start");
|
"unnecessary recurrence_start");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* BOLT-offers #12:
|
/* BOLT-offers #12:
|
||||||
*
|
*
|
||||||
* - if the chain for the invoice is not solely bitcoin:
|
* - if `offer_chains` is set:
|
||||||
* - MUST specify `chains` the offer is valid for.
|
* - MUST set `invreq_chain` to one of `offer_chains` unless that
|
||||||
|
* chain is bitcoin, in which case it MAY omit `invreq_chain`.
|
||||||
* - otherwise:
|
* - 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")) {
|
if (!streq(chainparams->network_name, "bitcoin")) {
|
||||||
invreq->chain = tal_dup(invreq, struct bitcoin_blkid,
|
invreq->invreq_chain = tal_dup(invreq, struct bitcoin_blkid,
|
||||||
&chainparams->genesis_blockhash);
|
&chainparams->genesis_blockhash);
|
||||||
}
|
}
|
||||||
|
|
||||||
invreq->features
|
/* BOLT-offers #12:
|
||||||
= plugin_feature_set(cmd->plugin)->bits[BOLT11_FEATURE];
|
* - 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! */
|
/* invreq->payer_note is not a nul-terminated string! */
|
||||||
if (payer_note)
|
if (payer_note)
|
||||||
invreq->payer_note = tal_dup_arr(invreq, utf8,
|
invreq->invreq_payer_note = tal_dup_arr(invreq, utf8,
|
||||||
payer_note, strlen(payer_note),
|
payer_note,
|
||||||
|
strlen(payer_note),
|
||||||
0);
|
0);
|
||||||
|
|
||||||
/* They can provide a secret, and we don't assume it's our job
|
/* They can provide a secret, and we don't assume it's our job
|
||||||
|
|||||||
376
plugins/offers.c
376
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;
|
struct sha256 offer_id;
|
||||||
bool valid = true;
|
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);
|
json_add_sha256(js, "offer_id", &offer_id);
|
||||||
if (offer->chains)
|
if (offer->offer_chains)
|
||||||
json_add_chains(js, offer->chains);
|
json_add_chains(js, offer->offer_chains);
|
||||||
if (offer->currency) {
|
if (offer->offer_currency) {
|
||||||
const struct iso4217_name_and_divisor *iso4217;
|
const struct iso4217_name_and_divisor *iso4217;
|
||||||
json_add_stringn(js, "currency",
|
json_add_stringn(js, "currency",
|
||||||
offer->currency, tal_bytelen(offer->currency));
|
offer->offer_currency,
|
||||||
if (offer->amount)
|
tal_bytelen(offer->offer_currency));
|
||||||
json_add_u64(js, "amount", *offer->amount);
|
if (offer->offer_amount)
|
||||||
iso4217 = find_iso4217(offer->currency,
|
json_add_u64(js, "amount", *offer->offer_amount);
|
||||||
tal_bytelen(offer->currency));
|
iso4217 = find_iso4217(offer->offer_currency,
|
||||||
|
tal_bytelen(offer->offer_currency));
|
||||||
if (iso4217)
|
if (iso4217)
|
||||||
json_add_num(js, "minor_unit", iso4217->minor_unit);
|
json_add_num(js, "minor_unit", iso4217->minor_unit);
|
||||||
else
|
else
|
||||||
json_add_string(js, "warning_offer_unknown_currency",
|
json_add_string(js, "warning_offer_unknown_currency",
|
||||||
"unknown currency code");
|
"unknown currency code");
|
||||||
} else if (offer->amount)
|
} else if (offer->offer_amount)
|
||||||
json_add_amount_msat_only(js, "amount_msat",
|
json_add_amount_msat_only(js, "amount_msat",
|
||||||
amount_msat(*offer->amount));
|
amount_msat(*offer->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);
|
|
||||||
|
|
||||||
/* BOLT-offers #12:
|
/* BOLT-offers #12:
|
||||||
* A reader of an offer:
|
* A reader of an offer:
|
||||||
*...
|
*...
|
||||||
* - if `node_id` or `description` is not set:
|
* - if `offer_description` is not set:
|
||||||
* - MUST NOT respond to the offer.
|
* - MUST NOT respond to the offer.
|
||||||
*/
|
*/
|
||||||
if (offer->description)
|
if (offer->offer_description)
|
||||||
json_add_stringn(js, "description",
|
json_add_stringn(js, "description",
|
||||||
offer->description,
|
offer->offer_description,
|
||||||
tal_bytelen(offer->description));
|
tal_bytelen(offer->offer_description));
|
||||||
else {
|
else {
|
||||||
json_add_string(js, "warning_offer_missing_description",
|
json_add_string(js, "warning_offer_missing_description",
|
||||||
"offers without a description are invalid");
|
"offers without a description are invalid");
|
||||||
valid = false;
|
valid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (offer->issuer)
|
if (offer->offer_issuer)
|
||||||
json_add_stringn(js, "issuer", offer->issuer,
|
json_add_stringn(js, "issuer", offer->offer_issuer,
|
||||||
tal_bytelen(offer->issuer));
|
tal_bytelen(offer->offer_issuer));
|
||||||
if (offer->features)
|
if (offer->offer_features)
|
||||||
json_add_hex_talarr(js, "features", offer->features);
|
json_add_hex_talarr(js, "features", offer->offer_features);
|
||||||
if (offer->absolute_expiry)
|
if (offer->offer_absolute_expiry)
|
||||||
json_add_u64(js, "absolute_expiry",
|
json_add_u64(js, "absolute_expiry",
|
||||||
*offer->absolute_expiry);
|
*offer->offer_absolute_expiry);
|
||||||
if (offer->paths)
|
if (offer->offer_paths)
|
||||||
valid &= json_add_blinded_paths(js, offer->paths, NULL);
|
valid &= json_add_blinded_paths(js, offer->offer_paths, NULL);
|
||||||
|
|
||||||
if (offer->quantity_min)
|
if (offer->offer_quantity_max)
|
||||||
json_add_u64(js, "quantity_min", *offer->quantity_min);
|
json_add_u64(js, "quantity_max", *offer->offer_quantity_max);
|
||||||
if (offer->quantity_max)
|
if (offer->offer_recurrence) {
|
||||||
json_add_u64(js, "quantity_max", *offer->quantity_max);
|
|
||||||
if (offer->recurrence) {
|
|
||||||
const char *name;
|
const char *name;
|
||||||
json_object_start(js, "recurrence");
|
json_object_start(js, "recurrence");
|
||||||
json_add_num(js, "time_unit", offer->recurrence->time_unit);
|
json_add_num(js, "time_unit", offer->offer_recurrence->time_unit);
|
||||||
name = recurrence_time_unit_name(offer->recurrence->time_unit);
|
name = recurrence_time_unit_name(offer->offer_recurrence->time_unit);
|
||||||
if (name)
|
if (name)
|
||||||
json_add_string(js, "time_unit_name", name);
|
json_add_string(js, "time_unit_name", name);
|
||||||
json_add_num(js, "period", offer->recurrence->period);
|
json_add_num(js, "period", offer->offer_recurrence->period);
|
||||||
if (offer->recurrence_base) {
|
if (offer->offer_recurrence_base) {
|
||||||
json_add_u64(js, "basetime",
|
json_add_u64(js, "basetime",
|
||||||
offer->recurrence_base->basetime);
|
offer->offer_recurrence_base->basetime);
|
||||||
if (offer->recurrence_base->start_any_period)
|
if (offer->offer_recurrence_base->start_any_period)
|
||||||
json_add_bool(js, "start_any_period", true);
|
json_add_bool(js, "start_any_period", true);
|
||||||
}
|
}
|
||||||
if (offer->recurrence_limit)
|
if (offer->offer_recurrence_limit)
|
||||||
json_add_u32(js, "limit", *offer->recurrence_limit);
|
json_add_u32(js, "limit", *offer->offer_recurrence_limit);
|
||||||
if (offer->recurrence_paywindow) {
|
if (offer->offer_recurrence_paywindow) {
|
||||||
json_object_start(js, "paywindow");
|
json_object_start(js, "paywindow");
|
||||||
json_add_u32(js, "seconds_before",
|
json_add_u32(js, "seconds_before",
|
||||||
offer->recurrence_paywindow->seconds_before);
|
offer->offer_recurrence_paywindow->seconds_before);
|
||||||
json_add_u32(js, "seconds_after",
|
json_add_u32(js, "seconds_after",
|
||||||
offer->recurrence_paywindow->seconds_after);
|
offer->offer_recurrence_paywindow->seconds_after);
|
||||||
if (offer->recurrence_paywindow->proportional_amount)
|
if (offer->offer_recurrence_paywindow->proportional_amount)
|
||||||
json_add_bool(js, "proportional_amount", true);
|
json_add_bool(js, "proportional_amount", true);
|
||||||
json_object_end(js);
|
json_object_end(js);
|
||||||
}
|
}
|
||||||
json_object_end(js);
|
json_object_end(js);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (offer->node_id)
|
/* BOLT-offers #12:
|
||||||
json_add_pubkey(js, "node_id", offer->node_id);
|
* - 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
|
else
|
||||||
valid = false;
|
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);
|
json_add_bool(js, "valid", valid);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -491,41 +488,83 @@ static void json_add_b12_invoice(struct json_stream *js,
|
|||||||
{
|
{
|
||||||
bool valid = true;
|
bool valid = true;
|
||||||
|
|
||||||
if (invoice->chain)
|
/* If there's an offer_node_id, then there's an offer. */
|
||||||
json_add_sha256(js, "chain", &invoice->chain->shad.sha);
|
if (invoice->offer_node_id) {
|
||||||
if (invoice->offer_id)
|
struct sha256 offer_id;
|
||||||
json_add_sha256(js, "offer_id", invoice->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:
|
/* 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",
|
json_add_amount_msat_only(js, "amount_msat",
|
||||||
amount_msat(*invoice->amount));
|
amount_msat(*invoice->invoice_amount));
|
||||||
else {
|
else {
|
||||||
json_add_string(js, "warning_invoice_missing_amount",
|
json_add_string(js, "warning_invoice_missing_amount",
|
||||||
"invoices without an amount are invalid");
|
"invoices without an amount are invalid");
|
||||||
valid = false;
|
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:
|
/* 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)
|
if (invoice->offer_description)
|
||||||
json_add_stringn(js, "description", invoice->description,
|
json_add_stringn(js, "description", invoice->offer_description,
|
||||||
tal_bytelen(invoice->description));
|
tal_bytelen(invoice->offer_description));
|
||||||
else {
|
else {
|
||||||
json_add_string(js, "warning_invoice_missing_description",
|
json_add_string(js, "warning_invoice_missing_description",
|
||||||
"invoices without a description are invalid");
|
"invoices without a description are invalid");
|
||||||
valid = false;
|
valid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (invoice->issuer)
|
if (invoice->invoice_created_at) {
|
||||||
json_add_stringn(js, "issuer", invoice->issuer,
|
json_add_u64(js, "created_at", *invoice->invoice_created_at);
|
||||||
tal_bytelen(invoice->issuer));
|
} else {
|
||||||
if (invoice->features)
|
json_add_string(js, "warning_invoice_missing_created_at",
|
||||||
json_add_hex_talarr(js, "features", invoice->features);
|
"invoices without created_at are invalid");
|
||||||
if (invoice->paths) {
|
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:
|
/* BOLT-offers #12:
|
||||||
* - if `blinded_path` is present:
|
* - if `blinded_path` is present:
|
||||||
* - MUST reject the invoice if `blinded_payinfo` is not
|
* - 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`
|
* contain exactly as many `payinfo` as total `onionmsg_path`
|
||||||
* in `blinded_path`.
|
* in `blinded_path`.
|
||||||
*/
|
*/
|
||||||
if (!invoice->blindedpay) {
|
if (!invoice->invoice_blindedpay) {
|
||||||
json_add_string(js, "warning_invoice_missing_blinded_payinfo",
|
json_add_string(js, "warning_invoice_missing_blinded_payinfo",
|
||||||
"invoices with blinded_path without blinded_payinfo are invalid");
|
"invoices with blinded_path without blinded_payinfo are invalid");
|
||||||
valid = false;
|
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->invreq_quantity)
|
||||||
if (invoice->send_invoice)
|
json_add_u64(js, "quantity", *invoice->invreq_quantity);
|
||||||
json_add_bool(js, "send_invoice", true);
|
if (invoice->invreq_recurrence_counter) {
|
||||||
if (invoice->refund_for)
|
|
||||||
json_add_sha256(js, "refund_for", invoice->refund_for);
|
|
||||||
if (invoice->recurrence_counter) {
|
|
||||||
json_add_u32(js, "recurrence_counter",
|
json_add_u32(js, "recurrence_counter",
|
||||||
*invoice->recurrence_counter);
|
*invoice->invreq_recurrence_counter);
|
||||||
if (invoice->recurrence_start)
|
if (invoice->invreq_recurrence_start)
|
||||||
json_add_u32(js, "recurrence_start",
|
json_add_u32(js, "recurrence_start",
|
||||||
*invoice->recurrence_start);
|
*invoice->invreq_recurrence_start);
|
||||||
/* BOLT-offers-recurrence #12:
|
/* BOLT-offers-recurrence #12:
|
||||||
* - if the offer contained `recurrence`:
|
* - if the offer contained `recurrence`:
|
||||||
* - MUST reject the invoice if `recurrence_basetime` is not
|
* - MUST reject the invoice if `recurrence_basetime` is not
|
||||||
* set.
|
* set.
|
||||||
*/
|
*/
|
||||||
if (invoice->recurrence_basetime)
|
if (invoice->invoice_recurrence_basetime)
|
||||||
json_add_u64(js, "recurrence_basetime",
|
json_add_u64(js, "recurrence_basetime",
|
||||||
*invoice->recurrence_basetime);
|
*invoice->invoice_recurrence_basetime);
|
||||||
else {
|
else {
|
||||||
json_add_string(js, "warning_invoice_missing_recurrence_basetime",
|
json_add_string(js, "warning_invoice_missing_recurrence_basetime",
|
||||||
"recurring invoices without a recurrence_basetime are invalid");
|
"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)
|
if (invoice->invreq_metadata)
|
||||||
json_add_pubkey(js, "payer_key", invoice->payer_key);
|
json_add_hex_talarr(js, "invreq_metadata",
|
||||||
if (invoice->payer_info)
|
invoice->invreq_metadata);
|
||||||
json_add_hex_talarr(js, "payer_info", invoice->payer_info);
|
if (invoice->invreq_payer_note)
|
||||||
if (invoice->payer_note)
|
json_add_stringn(js, "payer_note", invoice->invreq_payer_note,
|
||||||
json_add_stringn(js, "payer_note", invoice->payer_note,
|
tal_bytelen(invoice->invreq_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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* BOLT-offers #12:
|
/* BOLT-offers #12:
|
||||||
*
|
*
|
||||||
* - if the expiry for accepting payment is not 7200 seconds after
|
* - if `invoice_relative_expiry` is present:
|
||||||
* `created_at`:
|
* - MUST reject the invoice if the current time since 1970-01-01 UTC
|
||||||
* - MUST set `relative_expiry`
|
* 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)
|
if (invoice->invoice_relative_expiry)
|
||||||
json_add_u32(js, "relative_expiry", *invoice->relative_expiry);
|
json_add_u32(js, "relative_expiry", *invoice->invoice_relative_expiry);
|
||||||
else
|
else
|
||||||
json_add_u32(js, "relative_expiry", 7200);
|
json_add_u32(js, "relative_expiry", 7200);
|
||||||
|
|
||||||
/* BOLT-offers #12:
|
if (invoice->invoice_fallbacks)
|
||||||
* - 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)
|
|
||||||
valid &= json_add_fallbacks(js,
|
valid &= json_add_fallbacks(js,
|
||||||
invoice->chain,
|
invoice->invreq_chain,
|
||||||
invoice->fallbacks);
|
invoice->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_decode checked these */
|
/* 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_bip340sig(js, "signature", invoice->signature);
|
||||||
|
|
||||||
json_add_bool(js, "valid", valid);
|
json_add_bool(js, "valid", valid);
|
||||||
@@ -668,8 +647,17 @@ static void json_add_invoice_request(struct json_stream *js,
|
|||||||
{
|
{
|
||||||
bool valid = true;
|
bool valid = true;
|
||||||
|
|
||||||
if (invreq->chain)
|
/* If there's an offer_node_id, then there's an offer. */
|
||||||
json_add_sha256(js, "chain", &invreq->chain->shad.sha);
|
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:
|
/* BOLT-offers #12:
|
||||||
* - MUST fail the request if `payer_key` is not present.
|
* - 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 `features` contains unknown even bits.
|
||||||
* - MUST fail the request if `offer_id` is not present.
|
* - MUST fail the request if `offer_id` is not present.
|
||||||
*/
|
*/
|
||||||
if (invreq->offer_id)
|
if (invreq->invreq_amount)
|
||||||
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)
|
|
||||||
json_add_amount_msat_only(js, "amount_msat",
|
json_add_amount_msat_only(js, "amount_msat",
|
||||||
amount_msat(*invreq->amount));
|
amount_msat(*invreq->invreq_amount));
|
||||||
if (invreq->features)
|
if (invreq->invreq_features)
|
||||||
json_add_hex_talarr(js, "features", invreq->features);
|
json_add_hex_talarr(js, "features", invreq->invreq_features);
|
||||||
if (invreq->quantity)
|
if (invreq->invreq_quantity)
|
||||||
json_add_u64(js, "quantity", *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",
|
json_add_u32(js, "recurrence_counter",
|
||||||
*invreq->recurrence_counter);
|
*invreq->invreq_recurrence_counter);
|
||||||
if (invreq->recurrence_start)
|
if (invreq->invreq_recurrence_start)
|
||||||
json_add_u32(js, "recurrence_start",
|
json_add_u32(js, "recurrence_start",
|
||||||
*invreq->recurrence_start);
|
*invreq->invreq_recurrence_start);
|
||||||
if (invreq->payer_key)
|
/* BOLT-offers #12:
|
||||||
json_add_pubkey(js, "payer_key", invreq->payer_key);
|
* - 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 {
|
else {
|
||||||
json_add_string(js, "warning_invoice_request_missing_payer_key",
|
json_add_string(js, "warning_invoice_request_missing_payer_key",
|
||||||
"invoice_request requires payer_key");
|
"invoice_request requires payer_key");
|
||||||
valid = false;
|
valid = false;
|
||||||
}
|
}
|
||||||
if (invreq->payer_info)
|
if (invreq->invreq_metadata)
|
||||||
json_add_hex_talarr(js, "payer_info", invreq->payer_info);
|
json_add_hex_talarr(js, "invreq_metadata", invreq->invreq_metadata);
|
||||||
if (invreq->payer_note)
|
else {
|
||||||
json_add_stringn(js, "payer_note", invreq->payer_note,
|
json_add_string(js, "warning_invoice_request_missing_invreq_metadata",
|
||||||
tal_bytelen(invreq->payer_note));
|
"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:
|
/* BOLT-offers #12:
|
||||||
* - MUST fail the request if there is no `signature` field.
|
* - MUST fail the request if `signature` is not correct as detailed
|
||||||
* - MUST fail the request if `signature` is not correct.
|
* in [Signature Calculation](#signature-calculation) using the
|
||||||
|
* `invreq_payer_id`.
|
||||||
*/
|
*/
|
||||||
if (invreq->signature) {
|
if (invreq->signature) {
|
||||||
if (invreq->payer_key
|
if (invreq->invreq_payer_id
|
||||||
&& !bolt12_check_signature(invreq->fields,
|
&& !bolt12_check_signature(invreq->fields,
|
||||||
"invoice_request",
|
"invoice_request",
|
||||||
"signature",
|
"signature",
|
||||||
invreq->payer_key,
|
invreq->invreq_payer_id,
|
||||||
invreq->signature)) {
|
invreq->signature)) {
|
||||||
json_add_string(js, "warning_invoice_request_invalid_signature",
|
json_add_string(js, "warning_invoice_request_invalid_signature",
|
||||||
"Bad signature");
|
"Bad signature");
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ struct invreq {
|
|||||||
|
|
||||||
/* The offer, once we've looked it up. */
|
/* The offer, once we've looked it up. */
|
||||||
struct tlv_offer *offer;
|
struct tlv_offer *offer;
|
||||||
|
/* The offer id */
|
||||||
|
struct sha256 offer_id;
|
||||||
|
|
||||||
/* The invoice we're preparing (can require additional lookups) */
|
/* The invoice we're preparing (can require additional lookups) */
|
||||||
struct tlv_invoice *inv;
|
struct tlv_invoice *inv;
|
||||||
@@ -40,14 +42,10 @@ fail_invreq_level(struct command *cmd,
|
|||||||
struct tlv_onionmsg_tlv *payload;
|
struct tlv_onionmsg_tlv *payload;
|
||||||
struct tlv_invoice_error *err;
|
struct tlv_invoice_error *err;
|
||||||
|
|
||||||
full_fmt = tal_fmt(tmpctx, "Failed invoice_request");
|
full_fmt = tal_fmt(tmpctx, "Failed invreq");
|
||||||
if (invreq->invreq) {
|
if (invreq->invreq) {
|
||||||
tal_append_fmt(&full_fmt, " %s",
|
tal_append_fmt(&full_fmt, " %s",
|
||||||
invrequest_encode(tmpctx, invreq->invreq));
|
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);
|
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)
|
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. */
|
/* Don't give them a 0 second invoice, even if it's true. */
|
||||||
if (last_pay <= *inv->created_at)
|
if (last_pay <= *inv->invoice_created_at)
|
||||||
*inv->relative_expiry = 1;
|
*inv->invoice_relative_expiry = 1;
|
||||||
else
|
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! */
|
/* 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_string(req->js, "invstring", invoice_encode(tmpctx, ir->inv));
|
||||||
json_add_preimage(req->js, "preimage", &ir->preimage);
|
json_add_preimage(req->js, "preimage", &ir->preimage);
|
||||||
json_add_label(req->js, ir->inv->offer_id, ir->inv->payer_key,
|
json_add_label(req->js, &ir->offer_id, ir->inv->invreq_payer_id,
|
||||||
ir->inv->recurrence_counter
|
ir->inv->invreq_recurrence_counter
|
||||||
? *ir->inv->recurrence_counter : 0);
|
? *ir->inv->invreq_recurrence_counter : 0);
|
||||||
return send_outreq(cmd->plugin, req);
|
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))
|
if (!feature_offered(features, OPT_ROUTE_BLINDING))
|
||||||
continue;
|
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;
|
continue;
|
||||||
|
|
||||||
/* Only pick a private one if no public candidates. */
|
/* 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. */
|
/* Note: since we don't make one, createinvoice adds a dummy. */
|
||||||
plugin_log(cmd->plugin, LOG_UNUSUAL,
|
plugin_log(cmd->plugin, LOG_UNUSUAL,
|
||||||
"No incoming channel for %s, so no blinded path",
|
"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 {
|
} else {
|
||||||
struct privkey blinding;
|
struct privkey blinding;
|
||||||
struct tlv_encrypted_data_tlv_payment_relay relay;
|
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_base_msat = best->feebase;
|
||||||
relay.fee_proportional_millionths = best->feeppm;
|
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. */
|
/* Give them 6 blocks, plus one per 10 minutes until expiry. */
|
||||||
if (ir->inv->relative_expiry)
|
if (ir->inv->invoice_relative_expiry)
|
||||||
base = blockheight + 6 + *ir->inv->relative_expiry / 600;
|
base = blockheight + 6 + *ir->inv->invoice_relative_expiry / 600;
|
||||||
else
|
else
|
||||||
base = blockheight + 6 + 7200 / 600;
|
base = blockheight + 6 + 7200 / 600;
|
||||||
constraints.max_cltv_expiry = base + best->cltv + cltv_final;
|
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));
|
randombytes_buf(&blinding, sizeof(blinding));
|
||||||
|
|
||||||
ir->inv->paths = tal_arr(ir->inv, struct blinded_path *, 1);
|
ir->inv->invoice_paths = tal_arr(ir->inv, struct blinded_path *, 1);
|
||||||
ir->inv->paths[0] = tal(ir->inv->paths, struct blinded_path);
|
ir->inv->invoice_paths[0] = tal(ir->inv->invoice_paths, struct blinded_path);
|
||||||
ir->inv->paths[0]->first_node_id = best->id;
|
ir->inv->invoice_paths[0]->first_node_id = best->id;
|
||||||
if (!pubkey_from_privkey(&blinding,
|
if (!pubkey_from_privkey(&blinding,
|
||||||
&ir->inv->paths[0]->blinding))
|
&ir->inv->invoice_paths[0]->blinding))
|
||||||
abort();
|
abort();
|
||||||
hops = tal_arr(ir->inv->paths[0], struct onionmsg_hop *, 2);
|
hops = tal_arr(ir->inv->invoice_paths[0], struct onionmsg_hop *, 2);
|
||||||
ir->inv->paths[0]->path = hops;
|
ir->inv->invoice_paths[0]->path = hops;
|
||||||
|
|
||||||
/* First hop is the peer */
|
/* First hop is the peer */
|
||||||
hops[0] = tal(hops, struct onionmsg_hop);
|
hops[0] = tal(hops, struct onionmsg_hop);
|
||||||
@@ -395,18 +400,18 @@ static struct command_result *listincoming_done(struct command *cmd,
|
|||||||
&id,
|
&id,
|
||||||
NULL, NULL, NULL,
|
NULL, NULL, NULL,
|
||||||
invoice_path_id(tmpctx, &invoicesecret_base,
|
invoice_path_id(tmpctx, &invoicesecret_base,
|
||||||
ir->inv->payment_hash),
|
ir->inv->invoice_payment_hash),
|
||||||
&hops[1]->blinded_node_id);
|
&hops[1]->blinded_node_id);
|
||||||
|
|
||||||
/* FIXME: This should be a "normal" feerate and range. */
|
/* FIXME: This should be a "normal" feerate and range. */
|
||||||
ir->inv->blindedpay = tal_arr(ir->inv, struct blinded_payinfo *, 1);
|
ir->inv->invoice_blindedpay = tal_arr(ir->inv, struct blinded_payinfo *, 1);
|
||||||
ir->inv->blindedpay[0] = tal(ir->inv->blindedpay, struct blinded_payinfo);
|
ir->inv->invoice_blindedpay[0] = tal(ir->inv->invoice_blindedpay, struct blinded_payinfo);
|
||||||
ir->inv->blindedpay[0]->fee_base_msat = best->feebase;
|
ir->inv->invoice_blindedpay[0]->fee_base_msat = best->feebase;
|
||||||
ir->inv->blindedpay[0]->fee_proportional_millionths = best->feeppm;
|
ir->inv->invoice_blindedpay[0]->fee_proportional_millionths = best->feeppm;
|
||||||
ir->inv->blindedpay[0]->cltv_expiry_delta = best->cltv;
|
ir->inv->invoice_blindedpay[0]->cltv_expiry_delta = best->cltv;
|
||||||
ir->inv->blindedpay[0]->htlc_minimum_msat = best->htlc_min;
|
ir->inv->invoice_blindedpay[0]->htlc_minimum_msat = best->htlc_min;
|
||||||
ir->inv->blindedpay[0]->htlc_maximum_msat = best->htlc_max;
|
ir->inv->invoice_blindedpay[0]->htlc_maximum_msat = best->htlc_max;
|
||||||
ir->inv->blindedpay[0]->features = NULL;
|
ir->inv->invoice_blindedpay[0]->features = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
done:
|
done:
|
||||||
@@ -432,17 +437,17 @@ static struct command_result *check_period(struct command *cmd,
|
|||||||
struct command_result *err;
|
struct command_result *err;
|
||||||
|
|
||||||
/* If we have a recurrence base, that overrides. */
|
/* If we have a recurrence base, that overrides. */
|
||||||
if (ir->offer->recurrence_base)
|
if (ir->offer->offer_recurrence_base)
|
||||||
basetime = ir->offer->recurrence_base->basetime;
|
basetime = ir->offer->offer_recurrence_base->basetime;
|
||||||
|
|
||||||
/* BOLT-offers-recurrence #12:
|
/* BOLT-offers-recurrence #12:
|
||||||
* - if the invoice corresponds to an offer with `recurrence`:
|
* - if the invoice corresponds to an offer with `recurrence`:
|
||||||
* - MUST set `recurrence_basetime` to the start of period #0 as
|
* - MUST set `recurrence_basetime` to the start of period #0 as
|
||||||
* calculated by [Period Calculation](#offer-period-calculation).
|
* 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:
|
/* BOLT-offers-recurrence #12:
|
||||||
* - if the offer had `recurrence_base` and `start_any_period`
|
* - 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`
|
* `recurrence_start` field plus the `recurrence_counter`
|
||||||
* `counter` field.
|
* `counter` field.
|
||||||
*/
|
*/
|
||||||
if (ir->offer->recurrence_base
|
if (ir->offer->offer_recurrence_base
|
||||||
&& ir->offer->recurrence_base->start_any_period) {
|
&& ir->offer->offer_recurrence_base->start_any_period) {
|
||||||
err = invreq_must_have(cmd, ir, recurrence_start);
|
err = invreq_must_have(cmd, ir, invreq_recurrence_start);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
period_idx += *ir->invreq->recurrence_start;
|
period_idx += *ir->invreq->invreq_recurrence_start;
|
||||||
|
|
||||||
/* BOLT-offers-recurrence #12:
|
/* BOLT-offers-recurrence #12:
|
||||||
* - MUST set (or not set) `recurrence_start` exactly as the
|
* - MUST set (or not set) `recurrence_start` exactly as the
|
||||||
* invoice_request did.
|
* invreq did.
|
||||||
*/
|
*/
|
||||||
ir->inv->recurrence_start
|
ir->inv->invreq_recurrence_start
|
||||||
= tal_dup(ir->inv, u32, ir->invreq->recurrence_start);
|
= tal_dup(ir->inv, u32, ir->invreq->invreq_recurrence_start);
|
||||||
} else {
|
} else {
|
||||||
/* BOLT-offers-recurrence #12:
|
/* 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
|
* - MUST consider the period index for this request to be the
|
||||||
* `recurrence_counter` `counter` field.
|
* `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)
|
if (err)
|
||||||
return 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
|
* - MUST fail the request if the period index is greater than
|
||||||
* `max_period`.
|
* `max_period`.
|
||||||
*/
|
*/
|
||||||
if (ir->offer->recurrence_limit
|
if (ir->offer->offer_recurrence_limit
|
||||||
&& period_idx > *ir->offer->recurrence_limit) {
|
&& period_idx > *ir->offer->offer_recurrence_limit) {
|
||||||
return fail_invreq(cmd, ir,
|
return fail_invreq(cmd, ir,
|
||||||
"period_index %"PRIu64" too great",
|
"period_index %"PRIu64" too great",
|
||||||
period_idx);
|
period_idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
offer_period_paywindow(ir->offer->recurrence,
|
offer_period_paywindow(ir->offer->offer_recurrence,
|
||||||
ir->offer->recurrence_paywindow,
|
ir->offer->offer_recurrence_paywindow,
|
||||||
ir->offer->recurrence_base,
|
ir->offer->offer_recurrence_base,
|
||||||
basetime, period_idx,
|
basetime, period_idx,
|
||||||
&paywindow_start, &paywindow_end);
|
&paywindow_start, &paywindow_end);
|
||||||
if (*ir->inv->created_at < paywindow_start) {
|
if (*ir->inv->invoice_created_at < paywindow_start) {
|
||||||
return fail_invreq(cmd, ir,
|
return fail_invreq(cmd, ir,
|
||||||
"period_index %"PRIu64
|
"period_index %"PRIu64
|
||||||
" too early (start %"PRIu64")",
|
" too early (start %"PRIu64")",
|
||||||
period_idx,
|
period_idx,
|
||||||
paywindow_start);
|
paywindow_start);
|
||||||
}
|
}
|
||||||
if (*ir->inv->created_at > paywindow_end) {
|
if (*ir->inv->invoice_created_at > paywindow_end) {
|
||||||
return fail_invreq(cmd, ir,
|
return fail_invreq(cmd, ir,
|
||||||
"period_index %"PRIu64
|
"period_index %"PRIu64
|
||||||
" too late (ended %"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
|
* - MUST adjust the *base invoice amount* proportional to time
|
||||||
* remaining in the period.
|
* remaining in the period.
|
||||||
*/
|
*/
|
||||||
if (*ir->invreq->recurrence_counter != 0
|
if (*ir->invreq->invreq_recurrence_counter != 0
|
||||||
&& ir->offer->recurrence_paywindow
|
&& ir->offer->offer_recurrence_paywindow
|
||||||
&& ir->offer->recurrence_paywindow->proportional_amount == 1) {
|
&& ir->offer->offer_recurrence_paywindow->proportional_amount == 1) {
|
||||||
u64 start = offer_period_start(basetime, period_idx,
|
u64 start = offer_period_start(basetime, period_idx,
|
||||||
ir->offer->recurrence);
|
ir->offer->offer_recurrence);
|
||||||
u64 end = offer_period_start(basetime, period_idx + 1,
|
u64 end = offer_period_start(basetime, period_idx + 1,
|
||||||
ir->offer->recurrence);
|
ir->offer->offer_recurrence);
|
||||||
|
|
||||||
if (*ir->inv->created_at > start) {
|
if (*ir->inv->invoice_created_at > start) {
|
||||||
*ir->inv->amount
|
*ir->inv->invoice_amount
|
||||||
*= (double)((*ir->inv->created_at - start)
|
*= (double)((*ir->inv->invoice_created_at - start)
|
||||||
/ (end - start));
|
/ (end - start));
|
||||||
/* Round up to make it non-zero if necessary. */
|
/* Round up to make it non-zero if necessary. */
|
||||||
if (*ir->inv->amount == 0)
|
if (*ir->inv->invoice_amount == 0)
|
||||||
*ir->inv->amount = 1;
|
*ir->inv->invoice_amount = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -559,7 +564,7 @@ static struct command_result *prev_invoice_done(struct command *cmd,
|
|||||||
if (arr->size == 0) {
|
if (arr->size == 0) {
|
||||||
return fail_invreq(cmd, ir,
|
return fail_invreq(cmd, ir,
|
||||||
"No previous invoice #%u",
|
"No previous invoice #%u",
|
||||||
*ir->inv->recurrence_counter - 1);
|
*ir->inv->invreq_recurrence_counter - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Was it paid? */
|
/* Was it paid? */
|
||||||
@@ -567,7 +572,7 @@ static struct command_result *prev_invoice_done(struct command *cmd,
|
|||||||
if (!json_tok_streq(buf, status, "paid")) {
|
if (!json_tok_streq(buf, status, "paid")) {
|
||||||
return fail_invreq(cmd, ir,
|
return fail_invreq(cmd, ir,
|
||||||
"Previous invoice #%u status %.*s",
|
"Previous invoice #%u status %.*s",
|
||||||
*ir->inv->recurrence_counter - 1,
|
*ir->inv->invreq_recurrence_counter - 1,
|
||||||
json_tok_full_len(status),
|
json_tok_full_len(status),
|
||||||
json_tok_full(buf, status));
|
json_tok_full(buf, status));
|
||||||
}
|
}
|
||||||
@@ -577,7 +582,7 @@ static struct command_result *prev_invoice_done(struct command *cmd,
|
|||||||
if (!b12) {
|
if (!b12) {
|
||||||
return fail_internalerr(cmd, ir,
|
return fail_internalerr(cmd, ir,
|
||||||
"Previous invoice #%u no bolt12 (%.*s)",
|
"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_len(arr + 1),
|
||||||
json_tok_full(buf, 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_len(b12),
|
||||||
json_tok_full(buf, b12));
|
json_tok_full(buf, b12));
|
||||||
}
|
}
|
||||||
if (!previnv->recurrence_basetime) {
|
if (!previnv->invoice_recurrence_basetime) {
|
||||||
return fail_internalerr(cmd, ir,
|
return fail_internalerr(cmd, ir,
|
||||||
"Previous invoice %.*s no recurrence_basetime?",
|
"Previous invoice %.*s no recurrence_basetime?",
|
||||||
json_tok_full_len(b12), json_tok_full(buf, b12));
|
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 */
|
/* 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;
|
struct out_req *req;
|
||||||
|
|
||||||
/* No previous? Just pass through */
|
/* No previous? Just pass through */
|
||||||
if (*ir->invreq->recurrence_counter == 0)
|
if (*ir->invreq->invreq_recurrence_counter == 0)
|
||||||
return check_period(cmd, ir, *ir->inv->created_at);
|
return check_period(cmd, ir, *ir->inv->invoice_created_at);
|
||||||
|
|
||||||
req = jsonrpc_request_start(cmd->plugin, cmd,
|
req = jsonrpc_request_start(cmd->plugin, cmd,
|
||||||
"listinvoices",
|
"listinvoices",
|
||||||
@@ -614,9 +619,9 @@ static struct command_result *check_previous_invoice(struct command *cmd,
|
|||||||
error,
|
error,
|
||||||
ir);
|
ir);
|
||||||
json_add_label(req->js,
|
json_add_label(req->js,
|
||||||
ir->invreq->offer_id,
|
&ir->offer_id,
|
||||||
ir->invreq->payer_key,
|
ir->invreq->invreq_payer_id,
|
||||||
*ir->invreq->recurrence_counter - 1);
|
*ir->invreq->invreq_recurrence_counter - 1);
|
||||||
return send_outreq(cmd->plugin, req);
|
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,
|
const struct invreq *ir,
|
||||||
u64 *raw_amt)
|
u64 *raw_amt)
|
||||||
{
|
{
|
||||||
assert(ir->offer->amount);
|
assert(ir->offer->offer_amount);
|
||||||
|
|
||||||
/* BOLT-offers #12:
|
/* BOLT-offers #12:
|
||||||
* - MUST calculate the *base invoice amount* using the offer `amount`:
|
* - MUST calculate the *base invoice amount* using the offer `amount`:
|
||||||
*/
|
*/
|
||||||
*raw_amt = *ir->offer->amount;
|
*raw_amt = *ir->offer->offer_amount;
|
||||||
|
|
||||||
/* BOLT-offers #12:
|
/* BOLT-offers #12:
|
||||||
* - if request contains `quantity`, multiply by `quantity`.
|
* - if request contains `quantity`, multiply by `quantity`.
|
||||||
*/
|
*/
|
||||||
if (ir->invreq->quantity) {
|
if (ir->invreq->invreq_quantity) {
|
||||||
if (mul_overflows_u64(*ir->invreq->quantity, *raw_amt)) {
|
if (mul_overflows_u64(*ir->invreq->invreq_quantity, *raw_amt)) {
|
||||||
return fail_invreq(cmd, ir,
|
return fail_invreq(cmd, ir,
|
||||||
"quantity %"PRIu64
|
"quantity %"PRIu64
|
||||||
" causes overflow",
|
" causes overflow",
|
||||||
*ir->invreq->quantity);
|
*ir->invreq->invreq_quantity);
|
||||||
}
|
}
|
||||||
*raw_amt *= *ir->invreq->quantity;
|
*raw_amt *= *ir->invreq->invreq_quantity;
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -669,28 +674,28 @@ static struct command_result *invreq_base_amount_simple(struct command *cmd,
|
|||||||
{
|
{
|
||||||
struct command_result *err;
|
struct command_result *err;
|
||||||
|
|
||||||
if (ir->offer->amount) {
|
if (ir->offer->offer_amount) {
|
||||||
u64 raw_amount;
|
u64 raw_amount;
|
||||||
assert(!ir->offer->currency);
|
assert(!ir->offer->offer_currency);
|
||||||
err = invreq_amount_by_quantity(cmd, ir, &raw_amount);
|
err = invreq_amount_by_quantity(cmd, ir, &raw_amount);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
*amt = amount_msat(raw_amount);
|
*amt = amount_msat(raw_amount);
|
||||||
} else {
|
} else {
|
||||||
/* BOLT-offers-recurrence #12:
|
/* BOLT-offers #12:
|
||||||
*
|
*
|
||||||
* - otherwise:
|
* The reader:
|
||||||
* - MUST fail the request if it does not contain `amount`.
|
*...
|
||||||
* - MUST use the request `amount` as the *base invoice amount*.
|
* - otherwise (no `offer_amount`):
|
||||||
* (Note: invoice amount can be further modified by recurrence
|
* - MUST fail the request if it does not contain
|
||||||
* below)
|
* `invreq_amount`.
|
||||||
*/
|
*/
|
||||||
err = invreq_must_have(cmd, ir, amount);
|
err = invreq_must_have(cmd, ir, invreq_amount);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
*amt = amount_msat(*ir->invreq->amount);
|
*amt = amount_msat(*ir->invreq->invreq_amount);
|
||||||
}
|
}
|
||||||
return NULL;
|
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
|
* - MUST fail the request if its `amount` is less than the
|
||||||
* *base invoice amount*.
|
* *base invoice amount*.
|
||||||
*/
|
*/
|
||||||
if (ir->offer->amount && ir->invreq->amount) {
|
if (ir->offer->offer_amount && ir->invreq->invreq_amount) {
|
||||||
if (amount_msat_less(amount_msat(*ir->invreq->amount), base_inv_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",
|
return fail_invreq(cmd, ir, "Amount must be at least %s",
|
||||||
type_to_string(tmpctx, struct amount_msat,
|
type_to_string(tmpctx, struct amount_msat,
|
||||||
&base_inv_amount));
|
&base_inv_amount));
|
||||||
@@ -717,7 +722,7 @@ static struct command_result *handle_amount_and_recurrence(struct command *cmd,
|
|||||||
* the *base invoice amount*.
|
* the *base invoice amount*.
|
||||||
*/
|
*/
|
||||||
/* Much == 5? Easier to divide and compare, than multiply. */
|
/* 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)) {
|
base_inv_amount)) {
|
||||||
return fail_invreq(cmd, ir, "Amount vastly exceeds %s",
|
return fail_invreq(cmd, ir, "Amount vastly exceeds %s",
|
||||||
type_to_string(tmpctx, struct amount_msat,
|
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
|
* - MUST use the request's `amount` as the *base invoice
|
||||||
* amount*.
|
* 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 */
|
/* This may be adjusted by recurrence if proportional_amount set */
|
||||||
ir->inv->amount = tal_dup(ir->inv, u64,
|
ir->inv->invoice_amount = tal_dup(ir->inv, u64,
|
||||||
&base_inv_amount.millisatoshis); /* Raw: wire protocol */
|
&base_inv_amount.millisatoshis); /* Raw: wire protocol */
|
||||||
|
|
||||||
/* Last of all, we handle recurrence details, which often requires
|
/* Last of all, we handle recurrence details, which often requires
|
||||||
* further lookups. */
|
* further lookups. */
|
||||||
|
if (ir->inv->invreq_recurrence_counter) {
|
||||||
/* 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;
|
|
||||||
return check_previous_invoice(cmd, ir);
|
return check_previous_invoice(cmd, ir);
|
||||||
}
|
}
|
||||||
/* We're happy with 2 hours timeout (default): they can always
|
/* 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)
|
if (!msat)
|
||||||
return fail_internalerr(cmd, ir,
|
return fail_internalerr(cmd, ir,
|
||||||
"Cannot convert currency %.*s: %.*s",
|
"Cannot convert currency %.*s: %.*s",
|
||||||
(int)tal_bytelen(ir->offer->currency),
|
(int)tal_bytelen(ir->offer->offer_currency),
|
||||||
(const char *)ir->offer->currency,
|
(const char *)ir->offer->offer_currency,
|
||||||
json_tok_full_len(result),
|
json_tok_full_len(result),
|
||||||
json_tok_full(buf, result));
|
json_tok_full(buf, result));
|
||||||
|
|
||||||
if (!json_to_msat(buf, msat, &amount))
|
if (!json_to_msat(buf, msat, &amount))
|
||||||
return fail_internalerr(cmd, ir,
|
return fail_internalerr(cmd, ir,
|
||||||
"Bad convert for currency %.*s: %.*s",
|
"Bad convert for currency %.*s: %.*s",
|
||||||
(int)tal_bytelen(ir->offer->currency),
|
(int)tal_bytelen(ir->offer->offer_currency),
|
||||||
(const char *)ir->offer->currency,
|
(const char *)ir->offer->offer_currency,
|
||||||
json_tok_full_len(msat),
|
json_tok_full_len(msat),
|
||||||
json_tok_full(buf, msat));
|
json_tok_full(buf, msat));
|
||||||
|
|
||||||
@@ -789,7 +788,7 @@ static struct command_result *convert_currency(struct command *cmd,
|
|||||||
struct command_result *err;
|
struct command_result *err;
|
||||||
const struct iso4217_name_and_divisor *iso4217;
|
const struct iso4217_name_and_divisor *iso4217;
|
||||||
|
|
||||||
assert(ir->offer->currency);
|
assert(ir->offer->offer_currency);
|
||||||
|
|
||||||
/* Multiply by quantity *first*, for best precision */
|
/* Multiply by quantity *first*, for best precision */
|
||||||
err = invreq_amount_by_quantity(cmd, ir, &raw_amount);
|
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
|
* - if offer `currency` is not the invoice currency, convert
|
||||||
* to the invoice currency.
|
* to the invoice currency.
|
||||||
*/
|
*/
|
||||||
iso4217 = find_iso4217(ir->offer->currency,
|
iso4217 = find_iso4217(ir->offer->offer_currency,
|
||||||
tal_bytelen(ir->offer->currency));
|
tal_bytelen(ir->offer->offer_currency));
|
||||||
/* We should not create offer with unknown currency! */
|
/* We should not create offer with unknown currency! */
|
||||||
if (!iso4217)
|
if (!iso4217)
|
||||||
return fail_internalerr(cmd, ir,
|
return fail_internalerr(cmd, ir,
|
||||||
"Unknown offer currency %.*s",
|
"Unknown offer currency %.*s",
|
||||||
(int)tal_bytelen(ir->offer->currency),
|
(int)tal_bytelen(ir->offer->offer_currency),
|
||||||
ir->offer->currency);
|
ir->offer->offer_currency);
|
||||||
double_amount = (double)raw_amount;
|
double_amount = (double)raw_amount;
|
||||||
for (size_t i = 0; i < iso4217->minor_unit; i++)
|
for (size_t i = 0; i < iso4217->minor_unit; i++)
|
||||||
double_amount /= 10;
|
double_amount /= 10;
|
||||||
@@ -817,8 +816,8 @@ static struct command_result *convert_currency(struct command *cmd,
|
|||||||
req = jsonrpc_request_start(cmd->plugin, cmd, "currencyconvert",
|
req = jsonrpc_request_start(cmd->plugin, cmd, "currencyconvert",
|
||||||
currency_done, error, ir);
|
currency_done, error, ir);
|
||||||
json_add_stringn(req->js, "currency",
|
json_add_stringn(req->js, "currency",
|
||||||
(const char *)ir->offer->currency,
|
(const char *)ir->offer->offer_currency,
|
||||||
tal_bytelen(ir->offer->currency));
|
tal_bytelen(ir->offer->offer_currency));
|
||||||
json_add_primitive_fmt(req->js, "amount", "%f", double_amount);
|
json_add_primitive_fmt(req->js, "amount", "%f", double_amount);
|
||||||
return send_outreq(cmd->plugin, req);
|
return send_outreq(cmd->plugin, req);
|
||||||
}
|
}
|
||||||
@@ -837,8 +836,8 @@ static struct command_result *listoffers_done(struct command *cmd,
|
|||||||
|
|
||||||
/* BOLT-offers #12:
|
/* BOLT-offers #12:
|
||||||
*
|
*
|
||||||
* - MUST fail the request if the `offer_id` does not refer to an
|
* - MUST fail the request if the offer fields do not exactly match a
|
||||||
* unexpired offer.
|
* valid, unexpired offer.
|
||||||
*/
|
*/
|
||||||
if (arr->size == 0)
|
if (arr->size == 0)
|
||||||
return fail_invreq(cmd, ir, "Unknown offer");
|
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_len(offertok),
|
||||||
json_tok_full(buf, offertok));
|
json_tok_full(buf, offertok));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* FIXME-OFFERS: we have these fields in invreq! */
|
||||||
ir->offer = offer_decode(ir,
|
ir->offer = offer_decode(ir,
|
||||||
buf + b12tok->start,
|
buf + b12tok->start,
|
||||||
b12tok->end - b12tok->start,
|
b12tok->end - b12tok->start,
|
||||||
@@ -876,63 +877,65 @@ static struct command_result *listoffers_done(struct command *cmd,
|
|||||||
json_tok_full(buf, offertok));
|
json_tok_full(buf, offertok));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ir->offer->absolute_expiry
|
if (ir->offer->offer_absolute_expiry
|
||||||
&& time_now().ts.tv_sec >= *ir->offer->absolute_expiry) {
|
&& time_now().ts.tv_sec >= *ir->offer->offer_absolute_expiry) {
|
||||||
/* FIXME: do deloffer to disable it */
|
/* FIXME: do deloffer to disable it */
|
||||||
return fail_invreq(cmd, ir, "Offer expired");
|
return fail_invreq(cmd, ir, "Offer expired");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* BOLT-offers #12:
|
/* BOLT-offers #12:
|
||||||
* - if the offer had a `quantity_min` or `quantity_max` field:
|
* - if `offer_quantity_max` is present:
|
||||||
* - MUST fail the request if there is no `quantity` field.
|
* - MUST fail the request if there is no `invreq_quantity` field.
|
||||||
* - MUST fail the request if there is `quantity` is not within
|
* - if `offer_quantity_max` is non-zero:
|
||||||
* that (inclusive) range.
|
* - MUST fail the request if `invreq_quantity` is zero, OR greater than
|
||||||
|
* `offer_quantity_max`.
|
||||||
* - otherwise:
|
* - 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) {
|
if (ir->offer->offer_quantity_max) {
|
||||||
err = invreq_must_have(cmd, ir, quantity);
|
err = invreq_must_have(cmd, ir, invreq_quantity);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
if (ir->offer->quantity_min &&
|
if (*ir->invreq->invreq_quantity == 0)
|
||||||
*ir->invreq->quantity < *ir->offer->quantity_min) {
|
|
||||||
return fail_invreq(cmd, ir,
|
return fail_invreq(cmd, ir,
|
||||||
"quantity %"PRIu64 " < %"PRIu64,
|
"quantity zero invalid");
|
||||||
*ir->invreq->quantity,
|
|
||||||
*ir->offer->quantity_min);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ir->offer->quantity_max &&
|
if (*ir->offer->offer_quantity_max &&
|
||||||
*ir->invreq->quantity > *ir->offer->quantity_max) {
|
*ir->invreq->invreq_quantity > *ir->offer->offer_quantity_max) {
|
||||||
return fail_invreq(cmd, ir,
|
return fail_invreq(cmd, ir,
|
||||||
"quantity %"PRIu64" > %"PRIu64,
|
"quantity %"PRIu64" > %"PRIu64,
|
||||||
*ir->invreq->quantity,
|
*ir->invreq->invreq_quantity,
|
||||||
*ir->offer->quantity_max);
|
*ir->offer->offer_quantity_max);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err = invreq_must_not_have(cmd, ir, quantity);
|
err = invreq_must_not_have(cmd, ir, invreq_quantity);
|
||||||
if (err)
|
if (err)
|
||||||
return 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);
|
err = invreq_must_have(cmd, ir, signature);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
if (!check_payer_sig(cmd, ir->invreq,
|
if (!check_payer_sig(cmd, ir->invreq,
|
||||||
ir->invreq->payer_key,
|
ir->invreq->invreq_payer_id,
|
||||||
ir->invreq->signature)) {
|
ir->invreq->signature)) {
|
||||||
return fail_invreq(cmd, ir, "bad signature");
|
return fail_invreq(cmd, ir, "bad signature");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ir->offer->recurrence) {
|
if (ir->offer->offer_recurrence) {
|
||||||
/* BOLT-offers-recurrence #12:
|
/* BOLT-offers-recurrence #12:
|
||||||
*
|
*
|
||||||
* - if the offer had a `recurrence`:
|
* - if the offer had a `recurrence`:
|
||||||
* - MUST fail the request if there is no `recurrence_counter`
|
* - MUST fail the request if there is no `recurrence_counter`
|
||||||
* field.
|
* field.
|
||||||
*/
|
*/
|
||||||
err = invreq_must_have(cmd, ir, recurrence_counter);
|
err = invreq_must_have(cmd, ir, invreq_recurrence_counter);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
} else {
|
} else {
|
||||||
@@ -943,78 +946,54 @@ static struct command_result *listoffers_done(struct command *cmd,
|
|||||||
* - MUST fail the request if there is a `recurrence_start`
|
* - MUST fail the request if there is a `recurrence_start`
|
||||||
* field.
|
* field.
|
||||||
*/
|
*/
|
||||||
err = invreq_must_not_have(cmd, ir, recurrence_counter);
|
err = invreq_must_not_have(cmd, ir, invreq_recurrence_counter);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
err = invreq_must_not_have(cmd, ir, recurrence_start);
|
err = invreq_must_not_have(cmd, ir, invreq_recurrence_start);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
ir->inv = tlv_invoice_new(cmd);
|
|
||||||
/* BOLT-offers #12:
|
/* BOLT-offers #12:
|
||||||
* - if the chain for the invoice is not solely bitcoin:
|
* The writer:
|
||||||
* - MUST specify `chains` the offer is valid for.
|
* - MUST copy all non-signature fields from the invreq (including
|
||||||
|
* unknown fields).
|
||||||
*/
|
*/
|
||||||
if (!streq(chainparams->network_name, "bitcoin")) {
|
ir->inv = invoice_for_invreq(cmd, ir->invreq);
|
||||||
ir->inv->chain = tal_dup(ir->inv, struct bitcoin_blkid,
|
assert(ir->inv->invreq_payer_id);
|
||||||
&chainparams->genesis_blockhash);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* BOLT-offers #12:
|
/* 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 */
|
/* We always provide an offer_node_id! */
|
||||||
ir->inv->offer_id = tal_dup(ir->inv, struct sha256,
|
ir->inv->invoice_node_id = ir->inv->offer_node_id;
|
||||||
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);
|
|
||||||
|
|
||||||
/* BOLT-offers #12:
|
/* 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->inv->invoice_created_at = tal(ir->inv, u64);
|
||||||
ir->invreq->payer_key);
|
*ir->inv->invoice_created_at = time_now().ts.tv_sec;
|
||||||
|
|
||||||
/* BOLT-offers #12:
|
/* BOLT-offers #12:
|
||||||
* - MUST set (or not set) `payer_info` exactly as the invoice_request
|
* - MUST set `invoice_payment_hash` to the SHA256 hash of the
|
||||||
* did.
|
* `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));
|
randombytes_buf(&ir->preimage, sizeof(ir->preimage));
|
||||||
ir->inv->payment_hash = tal(ir->inv, struct sha256);
|
ir->inv->invoice_payment_hash = tal(ir->inv, struct sha256);
|
||||||
sha256(ir->inv->payment_hash, &ir->preimage, sizeof(ir->preimage));
|
sha256(ir->inv->invoice_payment_hash,
|
||||||
|
&ir->preimage, sizeof(ir->preimage));
|
||||||
|
|
||||||
ir->inv->cltv = tal_dup(ir->inv, u16, &cltv_final);
|
/* BOLT-offers #12:
|
||||||
|
* - or if it allows multiple parts to pay the invoice:
|
||||||
ir->inv->created_at = tal(ir->inv, u64);
|
* - MUST set `invoice_features`.`features` bit `MPP/optional`
|
||||||
*ir->inv->created_at = time_now().ts.tv_sec;
|
*/
|
||||||
|
ir->inv->invoice_features
|
||||||
|
= plugin_feature_set(cmd->plugin)->bits[BOLT12_INVOICE_FEATURE];
|
||||||
|
|
||||||
/* We may require currency lookup; if so, do it now. */
|
/* 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);
|
return convert_currency(cmd, ir);
|
||||||
|
|
||||||
err = invreq_base_amount_simple(cmd, ir, &amt);
|
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);
|
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,
|
struct command_result *handle_invoice_request(struct command *cmd,
|
||||||
const u8 *invreqbin,
|
const u8 *invreqbin,
|
||||||
struct blinded_path *reply_path)
|
struct blinded_path *reply_path)
|
||||||
{
|
{
|
||||||
size_t len = tal_count(invreqbin);
|
size_t len = tal_count(invreqbin);
|
||||||
|
const u8 *cursor = invreqbin;
|
||||||
struct invreq *ir = tal(cmd, struct invreq);
|
struct invreq *ir = tal(cmd, struct invreq);
|
||||||
struct out_req *req;
|
struct out_req *req;
|
||||||
int bad_feature;
|
int bad_feature;
|
||||||
|
|
||||||
ir->reply_path = tal_steal(ir, reply_path);
|
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) {
|
if (!ir->invreq) {
|
||||||
return fail_invreq(cmd, ir,
|
return fail_invreq(cmd, ir,
|
||||||
"Invalid invreq %s",
|
"Invalid invreq %s",
|
||||||
@@ -1050,13 +1023,39 @@ struct command_result *handle_invoice_request(struct command *cmd,
|
|||||||
|
|
||||||
/* BOLT-offers #12:
|
/* 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),
|
bad_feature = features_unsupported(plugin_feature_set(cmd->plugin),
|
||||||
ir->invreq->features,
|
ir->invreq->invreq_features,
|
||||||
BOLT11_FEATURE);
|
BOLT12_INVREQ_FEATURE);
|
||||||
if (bad_feature != -1) {
|
if (bad_feature != -1) {
|
||||||
return fail_invreq(cmd, ir,
|
return fail_invreq(cmd, ir,
|
||||||
"Unsupported invreq feature %i",
|
"Unsupported invreq feature %i",
|
||||||
@@ -1065,34 +1064,41 @@ struct command_result *handle_invoice_request(struct command *cmd,
|
|||||||
|
|
||||||
/* BOLT-offers #12:
|
/* BOLT-offers #12:
|
||||||
*
|
*
|
||||||
* The reader of an invoice_request:
|
* The reader:
|
||||||
*...
|
*...
|
||||||
* - if `chain` is not present:
|
* - if `invreq_chain` is not present:
|
||||||
* - MUST fail the request if bitcoin is not a supported chain.
|
* - MUST fail the request if bitcoin is not a supported chain.
|
||||||
* - otherwise:
|
* - otherwise:
|
||||||
* - MUST fail the request if `chain` is not a supported chain.
|
* - 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,
|
return fail_invreq(cmd, ir,
|
||||||
"Wrong chain %s",
|
"Wrong chain %s",
|
||||||
tal_hex(tmpctx, ir->invreq->chain));
|
tal_hex(tmpctx, ir->invreq->invreq_chain));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* BOLT-offers #12:
|
/* BOLT-offers #12:
|
||||||
*
|
*
|
||||||
* The reader of an invoice_request:
|
* - otherwise (no `offer_node_id`, not a response to our offer):
|
||||||
* - MUST fail the request if `payer_key` is not present.
|
|
||||||
*/
|
*/
|
||||||
if (!ir->invreq->payer_key)
|
/* FIXME-OFFERS: handle this! */
|
||||||
return fail_invreq(cmd, ir, "Missing payer key");
|
if (!ir->invreq->offer_node_id) {
|
||||||
|
return fail_invreq(cmd, ir, "Not based on an offer");
|
||||||
|
}
|
||||||
|
|
||||||
if (!ir->invreq->offer_id)
|
/* BOLT-offers #12:
|
||||||
return handle_offerless_request(cmd, ir);
|
*
|
||||||
|
* - 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 */
|
/* Now, look up offer */
|
||||||
req = jsonrpc_request_start(cmd->plugin, cmd, "listoffers",
|
req = jsonrpc_request_start(cmd->plugin, cmd, "listoffers",
|
||||||
listoffers_done, error, ir);
|
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);
|
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))
|
buffer + tok->start, tok->end - tok->start))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
offer->amount = tal_dup(offer, u64,
|
offer->offer_amount = tal_dup(offer, u64,
|
||||||
&msat.millisatoshis); /* Raw: other currencies */
|
&msat.millisatoshis); /* Raw: other currencies */
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -39,7 +39,7 @@ static struct command_result *param_amount(struct command *cmd,
|
|||||||
if (msat_or_any(buffer, tok, offer))
|
if (msat_or_any(buffer, tok, offer))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
offer->amount = tal(offer, u64);
|
offer->offer_amount = tal(offer, u64);
|
||||||
|
|
||||||
/* BOLT-offers #12:
|
/* BOLT-offers #12:
|
||||||
*
|
*
|
||||||
@@ -58,7 +58,7 @@ static struct command_result *param_amount(struct command *cmd,
|
|||||||
ISO4217_NAMELEN,
|
ISO4217_NAMELEN,
|
||||||
buffer + tok->end - ISO4217_NAMELEN);
|
buffer + tok->end - ISO4217_NAMELEN);
|
||||||
|
|
||||||
offer->currency
|
offer->offer_currency
|
||||||
= tal_dup_arr(offer, utf8, isocode->name, ISO4217_NAMELEN, 0);
|
= tal_dup_arr(offer, utf8, isocode->name, ISO4217_NAMELEN, 0);
|
||||||
|
|
||||||
number = *tok;
|
number = *tok;
|
||||||
@@ -77,19 +77,19 @@ static struct command_result *param_amount(struct command *cmd,
|
|||||||
"Bad minor units");
|
"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,
|
return command_fail_badparam(cmd, name, buffer, tok,
|
||||||
"should be 'any', msatoshis or <ISO-4712><amount>[.<amount>]");
|
"should be 'any', msatoshis or <ISO-4712><amount>[.<amount>]");
|
||||||
|
|
||||||
for (size_t i = 0; i < isocode->minor_unit; i++) {
|
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,
|
return command_fail_badparam(cmd, name, buffer,
|
||||||
&whole,
|
&whole,
|
||||||
"excessively large value");
|
"excessively large value");
|
||||||
*offer->amount *= 10;
|
*offer->offer_amount *= 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
*offer->amount += cents;
|
*offer->offer_amount += cents;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,8 +139,7 @@ static struct command_result *param_recurrence(struct command *cmd,
|
|||||||
const char *name,
|
const char *name,
|
||||||
const char *buffer,
|
const char *buffer,
|
||||||
const jsmntok_t *tok,
|
const jsmntok_t *tok,
|
||||||
struct tlv_offer_recurrence
|
struct recurrence **recurrence)
|
||||||
**recurrence)
|
|
||||||
{
|
{
|
||||||
u32 mul;
|
u32 mul;
|
||||||
const struct time_string *ts;
|
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,
|
return command_fail_badparam(cmd, name, buffer, tok,
|
||||||
"not a valid time");
|
"not a valid time");
|
||||||
|
|
||||||
*recurrence = tal(cmd, struct tlv_offer_recurrence);
|
*recurrence = tal(cmd, struct recurrence);
|
||||||
(*recurrence)->time_unit = ts->unit;
|
(*recurrence)->time_unit = ts->unit;
|
||||||
(*recurrence)->period = ts->mul * mul;
|
(*recurrence)->period = ts->mul * mul;
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -160,12 +159,12 @@ static struct command_result *param_recurrence_base(struct command *cmd,
|
|||||||
const char *name,
|
const char *name,
|
||||||
const char *buffer,
|
const char *buffer,
|
||||||
const jsmntok_t *tok,
|
const jsmntok_t *tok,
|
||||||
struct tlv_offer_recurrence_base **base)
|
struct recurrence_base **base)
|
||||||
{
|
{
|
||||||
/* Make copy so we can manipulate it */
|
/* Make copy so we can manipulate it */
|
||||||
jsmntok_t t = *tok;
|
jsmntok_t t = *tok;
|
||||||
|
|
||||||
*base = tal(cmd, struct tlv_offer_recurrence_base);
|
*base = tal(cmd, struct recurrence_base);
|
||||||
if (json_tok_startswith(buffer, &t, "@")) {
|
if (json_tok_startswith(buffer, &t, "@")) {
|
||||||
t.start++;
|
t.start++;
|
||||||
(*base)->start_any_period = false;
|
(*base)->start_any_period = false;
|
||||||
@@ -183,12 +182,12 @@ static struct command_result *param_recurrence_paywindow(struct command *cmd,
|
|||||||
const char *name,
|
const char *name,
|
||||||
const char *buffer,
|
const char *buffer,
|
||||||
const jsmntok_t *tok,
|
const jsmntok_t *tok,
|
||||||
struct tlv_offer_recurrence_paywindow
|
struct recurrence_paywindow
|
||||||
**paywindow)
|
**paywindow)
|
||||||
{
|
{
|
||||||
jsmntok_t t, before, after;
|
jsmntok_t t, before, after;
|
||||||
|
|
||||||
*paywindow = tal(cmd, struct tlv_offer_recurrence_paywindow);
|
*paywindow = tal(cmd, struct recurrence_paywindow);
|
||||||
t = *tok;
|
t = *tok;
|
||||||
if (json_tok_endswith(buffer, &t, "%")) {
|
if (json_tok_endswith(buffer, &t, "%")) {
|
||||||
(*paywindow)->proportional_amount = true;
|
(*paywindow)->proportional_amount = true;
|
||||||
@@ -231,7 +230,7 @@ static struct command_result *check_result(struct command *cmd,
|
|||||||
&active)) {
|
&active)) {
|
||||||
return command_fail(cmd,
|
return command_fail(cmd,
|
||||||
LIGHTNINGD,
|
LIGHTNINGD,
|
||||||
"Bad creaoffer status reply %.*s",
|
"Bad createoffer status reply %.*s",
|
||||||
json_tok_full_len(result),
|
json_tok_full_len(result),
|
||||||
json_tok_full(buf, 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_req("description", param_escaped_string, &desc),
|
||||||
p_opt("issuer", param_escaped_string, &issuer),
|
p_opt("issuer", param_escaped_string, &issuer),
|
||||||
p_opt("label", param_escaped_string, &offinfo->label),
|
p_opt("label", param_escaped_string, &offinfo->label),
|
||||||
p_opt("quantity_min", param_u64, &offer->quantity_min),
|
p_opt("quantity_max", param_u64, &offer->offer_quantity_max),
|
||||||
p_opt("quantity_max", param_u64, &offer->quantity_max),
|
p_opt("absolute_expiry", param_u64, &offer->offer_absolute_expiry),
|
||||||
p_opt("absolute_expiry", param_u64, &offer->absolute_expiry),
|
p_opt("recurrence", param_recurrence, &offer->offer_recurrence),
|
||||||
p_opt("recurrence", param_recurrence, &offer->recurrence),
|
|
||||||
p_opt("recurrence_base",
|
p_opt("recurrence_base",
|
||||||
param_recurrence_base,
|
param_recurrence_base,
|
||||||
&offer->recurrence_base),
|
&offer->offer_recurrence_base),
|
||||||
p_opt("recurrence_paywindow",
|
p_opt("recurrence_paywindow",
|
||||||
param_recurrence_paywindow,
|
param_recurrence_paywindow,
|
||||||
&offer->recurrence_paywindow),
|
&offer->offer_recurrence_paywindow),
|
||||||
p_opt("recurrence_limit",
|
p_opt("recurrence_limit",
|
||||||
param_number,
|
param_number,
|
||||||
&offer->recurrence_limit),
|
&offer->offer_recurrence_limit),
|
||||||
p_opt_def("single_use", param_bool,
|
p_opt_def("single_use", param_bool,
|
||||||
&offinfo->single_use, false),
|
&offinfo->single_use, false),
|
||||||
/* FIXME: hints support! */
|
/* FIXME: hints support! */
|
||||||
@@ -312,66 +310,66 @@ struct command_result *json_offer(struct command *cmd,
|
|||||||
return command_fail(cmd, LIGHTNINGD,
|
return command_fail(cmd, LIGHTNINGD,
|
||||||
"experimental-offers not enabled");
|
"experimental-offers not enabled");
|
||||||
|
|
||||||
/* BOLT-offers #12:
|
/* Doesn't make sense to have max quantity 1. */
|
||||||
* - MUST NOT set `quantity_min` or `quantity_max` less than 1.
|
if (offer->offer_quantity_max && *offer->offer_quantity_max == 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)
|
|
||||||
return command_fail_badparam(cmd, "quantity_max",
|
return command_fail_badparam(cmd, "quantity_max",
|
||||||
buffer, params,
|
buffer, params,
|
||||||
"must be >= 1");
|
"must be 0 or > 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");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* BOLT-offers #12:
|
/* BOLT-offers #12:
|
||||||
*
|
*
|
||||||
* - if the chain for the invoice is not solely bitcoin:
|
* - 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:
|
* - 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")) {
|
if (!streq(chainparams->network_name, "bitcoin")) {
|
||||||
offer->chains = tal_arr(offer, struct bitcoin_blkid, 1);
|
offer->offer_chains = tal_arr(offer, struct bitcoin_blkid, 1);
|
||||||
offer->chains[0] = chainparams->genesis_blockhash;
|
offer->offer_chains[0] = chainparams->genesis_blockhash;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!offer->recurrence) {
|
if (!offer->offer_recurrence) {
|
||||||
if (offer->recurrence_limit)
|
if (offer->offer_recurrence_limit)
|
||||||
return command_fail_badparam(cmd, "recurrence_limit",
|
return command_fail_badparam(cmd, "recurrence_limit",
|
||||||
buffer, params,
|
buffer, params,
|
||||||
"needs recurrence");
|
"needs recurrence");
|
||||||
if (offer->recurrence_base)
|
if (offer->offer_recurrence_base)
|
||||||
return command_fail_badparam(cmd, "recurrence_base",
|
return command_fail_badparam(cmd, "recurrence_base",
|
||||||
buffer, params,
|
buffer, params,
|
||||||
"needs recurrence");
|
"needs recurrence");
|
||||||
if (offer->recurrence_paywindow)
|
if (offer->offer_recurrence_paywindow)
|
||||||
return command_fail_badparam(cmd, "recurrence_paywindow",
|
return command_fail_badparam(cmd, "recurrence_paywindow",
|
||||||
buffer, params,
|
buffer, params,
|
||||||
"needs recurrence");
|
"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) {
|
if (issuer) {
|
||||||
offer->issuer
|
offer->offer_issuer
|
||||||
= tal_dup_arr(offer, char, issuer, strlen(issuer), 0);
|
= 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
|
/* If they specify a different currency, warn if we can't
|
||||||
* convert it! */
|
* convert it! */
|
||||||
if (offer->currency) {
|
if (offer->offer_currency) {
|
||||||
struct out_req *req;
|
struct out_req *req;
|
||||||
|
|
||||||
req = jsonrpc_request_start(cmd->plugin, cmd, "currencyconvert",
|
req = jsonrpc_request_start(cmd->plugin, cmd, "currencyconvert",
|
||||||
@@ -379,8 +377,8 @@ struct command_result *json_offer(struct command *cmd,
|
|||||||
offinfo);
|
offinfo);
|
||||||
json_add_u32(req->js, "amount", 1);
|
json_add_u32(req->js, "amount", 1);
|
||||||
json_add_stringn(req->js, "currency",
|
json_add_stringn(req->js, "currency",
|
||||||
(const char *)offer->currency,
|
(const char *)offer->offer_currency,
|
||||||
tal_bytelen(offer->currency));
|
tal_bytelen(offer->offer_currency));
|
||||||
return send_outreq(cmd->plugin, req);
|
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 = tal_steal(p, b12->features); */
|
||||||
p->features = NULL;
|
p->features = NULL;
|
||||||
|
|
||||||
if (!b12->node_id)
|
if (!b12->invoice_node_id)
|
||||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||||
"invoice missing node_id");
|
"invoice missing node_id");
|
||||||
if (!b12->payment_hash)
|
if (!b12->invoice_payment_hash)
|
||||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||||
"invoice missing payment_hash");
|
"invoice missing payment_hash");
|
||||||
if (!b12->created_at)
|
if (!b12->invoice_created_at)
|
||||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||||
"invoice missing created_at");
|
"invoice missing created_at");
|
||||||
if (b12->amount) {
|
if (!b12->invoice_amount)
|
||||||
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||||
|
"invoice missing invoice_amount");
|
||||||
invmsat = tal(cmd, struct amount_msat);
|
invmsat = tal(cmd, struct amount_msat);
|
||||||
*invmsat = amount_msat(*b12->amount);
|
*invmsat = amount_msat(*b12->invoice_amount);
|
||||||
} else
|
|
||||||
invmsat = NULL;
|
|
||||||
|
|
||||||
p->destination = tal(p, struct node_id);
|
p->destination = tal(p, struct node_id);
|
||||||
node_id_from_pubkey(p->destination, b12->node_id);
|
node_id_from_pubkey(p->destination, b12->invoice_node_id);
|
||||||
p->payment_hash = tal_dup(p, struct sha256, b12->payment_hash);
|
p->payment_hash = tal_dup(p, struct sha256,
|
||||||
if (b12->recurrence_counter && !label)
|
b12->invoice_payment_hash);
|
||||||
|
if (b12->invreq_recurrence_counter && !label)
|
||||||
return command_fail(
|
return command_fail(
|
||||||
cmd, JSONRPC2_INVALID_PARAMS,
|
cmd, JSONRPC2_INVALID_PARAMS,
|
||||||
"recurring invoice requires a label");
|
"recurring invoice requires a label");
|
||||||
|
|
||||||
/* BOLT-offers #12:
|
/* 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:
|
||||||
/* BOLT-offers #12: - MUST reject the invoice if
|
* - MUST reject the invoice if `invoice_blindedpay` does not
|
||||||
* `blindedpay` does not contain exactly one
|
* contain exactly one `blinded_payinfo` per
|
||||||
* `blinded_payinfo` per `blinded_path`.
|
* `invoice_paths`.`blinded_path`. */
|
||||||
*/
|
if (tal_count(b12->invoice_paths)
|
||||||
if (tal_count(b12->paths) != tal_count(b12->blindedpay)) {
|
!= tal_count(b12->invoice_blindedpay)) {
|
||||||
return command_fail(
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||||
cmd, JSONRPC2_INVALID_PARAMS,
|
|
||||||
"Wrong blinding info: %zu paths, %zu payinfo",
|
"Wrong blinding info: %zu paths, %zu payinfo",
|
||||||
tal_count(b12->paths),
|
tal_count(b12->invoice_paths),
|
||||||
tal_count(b12->blindedpay));
|
tal_count(b12->invoice_blindedpay));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* FIXME: do MPP across these! We choose first one. */
|
/* FIXME: do MPP across these! We choose first one. */
|
||||||
p->blindedpath = tal_steal(p, b12->paths[0]);
|
p->blindedpath = tal_steal(p, b12->invoice_paths[0]);
|
||||||
p->blindedpay = tal_steal(p, b12->blindedpay[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 */
|
/* Set destination to introduction point */
|
||||||
node_id_from_pubkey(p->destination, &p->blindedpath->first_node_id);
|
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));
|
|
||||||
}
|
|
||||||
p->payment_metadata = NULL;
|
p->payment_metadata = NULL;
|
||||||
p->routes = NULL;
|
p->routes = NULL;
|
||||||
if (b12->cltv)
|
|
||||||
p->min_final_cltv_expiry = *b12->cltv;
|
|
||||||
else
|
|
||||||
p->min_final_cltv_expiry = 18;
|
|
||||||
/* BOLT-offers #12:
|
/* BOLT-offers #12:
|
||||||
* - if `relative_expiry` is present:
|
* - if `invoice_relative_expiry` is present:
|
||||||
* - MUST reject the invoice if the current time since
|
* - 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`.
|
* `seconds_from_creation`.
|
||||||
* - otherwise:
|
* - otherwise:
|
||||||
* - MUST reject the invoice if the current time since
|
* - 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
|
||||||
* 7200.
|
* 7200.
|
||||||
*/
|
*/
|
||||||
if (b12->relative_expiry)
|
if (b12->invoice_relative_expiry)
|
||||||
invexpiry = *b12->created_at + *b12->relative_expiry;
|
invexpiry = *b12->invoice_created_at + *b12->invoice_relative_expiry;
|
||||||
else
|
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);
|
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'})
|
l1.rpc.call('fetchinvoice', {'offer': 'aaaa'})
|
||||||
|
|
||||||
# Decode still works though
|
# Decode still works though
|
||||||
assert l1.rpc.decode('lno1qgsqvgnwgcg35z6ee2h3yczraddm72xrfua9uve2rlrm9deu7xyfzrcgqyys5qq7yypxdeze35wncs2l2u4gfzyrpds00e6yakfrt6ctrw5n9qanzhqr2x8sgp3lqxpxd82j87j67wyff9cd9msgagq8hveftdkx5t3e98gj2x7ac99hhwlpj9yvj79yz3l8gdlmdmhq47ct9pkedfd8naksd8f8gpar')['valid']
|
assert l1.rpc.decode('lno1qgsqvgnwgcg35z6ee2h3yczraddm72xrfua9uve2rlrm9deu7xyfzrcgqyqs5pr5v4ehg93pqfnwgkvdr57yzh6h92zg3qctvrm7w38djg67kzcm4yeg8vc4cq63s')['valid']
|
||||||
|
|
||||||
|
|
||||||
def test_offer(node_factory, bitcoind):
|
def test_offer(node_factory, bitcoind):
|
||||||
@@ -4449,14 +4449,14 @@ def test_offer(node_factory, bitcoind):
|
|||||||
offer['bolt12']]).decode('UTF-8')
|
offer['bolt12']]).decode('UTF-8')
|
||||||
assert 'issuer: ' + weird_issuer in output
|
assert 'issuer: ' + weird_issuer in output
|
||||||
|
|
||||||
# Test quantity min/max
|
# Test quantity
|
||||||
ret = l1.rpc.call('offer', {'amount': '100000sat',
|
ret = l1.rpc.call('offer', {'amount': '100000sat',
|
||||||
'description': 'quantity_min test',
|
'description': 'quantity_max existence test',
|
||||||
'quantity_min': 1})
|
'quantity_max': 0})
|
||||||
offer = only_one(l1.rpc.call('listoffers', [ret['offer_id']])['offers'])
|
offer = only_one(l1.rpc.call('listoffers', [ret['offer_id']])['offers'])
|
||||||
output = subprocess.check_output([bolt12tool, 'decode',
|
output = subprocess.check_output([bolt12tool, 'decode',
|
||||||
offer['bolt12']]).decode('UTF-8')
|
offer['bolt12']]).decode('UTF-8')
|
||||||
assert 'quantity_min: 1' in output
|
assert 'quantity_max: 0' in output
|
||||||
|
|
||||||
ret = l1.rpc.call('offer', {'amount': '100000sat',
|
ret = l1.rpc.call('offer', {'amount': '100000sat',
|
||||||
'description': 'quantity_max test',
|
'description': 'quantity_max test',
|
||||||
@@ -4466,26 +4466,6 @@ def test_offer(node_factory, bitcoind):
|
|||||||
offer['bolt12']]).decode('UTF-8')
|
offer['bolt12']]).decode('UTF-8')
|
||||||
assert 'quantity_max: 2' in output
|
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
|
# Test absolute_expiry
|
||||||
exp = int(time.time() + 2)
|
exp = int(time.time() + 2)
|
||||||
ret = l1.rpc.call('offer', {'amount': '100000sat',
|
ret = l1.rpc.call('offer', {'amount': '100000sat',
|
||||||
@@ -5241,5 +5221,5 @@ def test_payerkey(node_factory):
|
|||||||
"03a3bbda0137722ba62207b9d3e5e6cc2a11e58480f801892093e01383aacb7fb2"]
|
"03a3bbda0137722ba62207b9d3e5e6cc2a11e58480f801892093e01383aacb7fb2"]
|
||||||
|
|
||||||
for n, k in zip(nodes, expected_keys):
|
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
|
assert n.rpc.decode(b12)['payer_key'] == k
|
||||||
|
|||||||
@@ -1,119 +1,163 @@
|
|||||||
tlvtype,offer,chains,2
|
tlvtype,offer,offer_chains,2
|
||||||
tlvdata,offer,chains,chains,chain_hash,...
|
tlvdata,offer,offer_chains,chains,chain_hash,...
|
||||||
tlvtype,offer,currency,6
|
tlvtype,offer,offer_metadata,4
|
||||||
tlvdata,offer,currency,iso4217,utf8,...
|
tlvdata,offer,offer_metadata,data,byte,...
|
||||||
tlvtype,offer,amount,8
|
tlvtype,offer,offer_currency,6
|
||||||
tlvdata,offer,amount,amount,tu64,
|
tlvdata,offer,offer_currency,iso4217,utf8,...
|
||||||
tlvtype,offer,description,10
|
tlvtype,offer,offer_amount,8
|
||||||
tlvdata,offer,description,description,utf8,...
|
tlvdata,offer,offer_amount,amount,tu64,
|
||||||
tlvtype,offer,features,12
|
tlvtype,offer,offer_description,10
|
||||||
tlvdata,offer,features,features,byte,...
|
tlvdata,offer,offer_description,description,utf8,...
|
||||||
tlvtype,offer,absolute_expiry,14
|
tlvtype,offer,offer_features,12
|
||||||
tlvdata,offer,absolute_expiry,seconds_from_epoch,tu64,
|
tlvdata,offer,offer_features,features,byte,...
|
||||||
tlvtype,offer,paths,16
|
tlvtype,offer,offer_absolute_expiry,14
|
||||||
tlvdata,offer,paths,paths,blinded_path,...
|
tlvdata,offer,offer_absolute_expiry,seconds_from_epoch,tu64,
|
||||||
tlvtype,offer,issuer,20
|
tlvtype,offer,offer_paths,16
|
||||||
tlvdata,offer,issuer,issuer,utf8,...
|
tlvdata,offer,offer_paths,paths,blinded_path,...
|
||||||
tlvtype,offer,quantity_min,22
|
tlvtype,offer,offer_issuer,18
|
||||||
tlvdata,offer,quantity_min,min,tu64,
|
tlvdata,offer,offer_issuer,issuer,utf8,...
|
||||||
tlvtype,offer,quantity_max,24
|
tlvtype,offer,offer_quantity_max,20
|
||||||
tlvdata,offer,quantity_max,max,tu64,
|
tlvdata,offer,offer_quantity_max,max,tu64,
|
||||||
tlvtype,offer,recurrence,26
|
tlvtype,offer,offer_node_id,22
|
||||||
tlvdata,offer,recurrence,time_unit,byte,
|
tlvdata,offer,offer_node_id,node_id,point,
|
||||||
tlvdata,offer,recurrence,period,tu32,
|
tlvtype,offer,offer_recurrence,26
|
||||||
tlvtype,offer,recurrence_paywindow,64
|
tlvdata,offer,offer_recurrence,recurrence,recurrence,
|
||||||
tlvdata,offer,recurrence_paywindow,seconds_before,u32,
|
tlvtype,offer,offer_recurrence_paywindow,28
|
||||||
tlvdata,offer,recurrence_paywindow,proportional_amount,byte,
|
tlvdata,offer,offer_recurrence_paywindow,paywindow,recurrence_paywindow,
|
||||||
tlvdata,offer,recurrence_paywindow,seconds_after,tu32,
|
tlvtype,offer,offer_recurrence_limit,30
|
||||||
tlvtype,offer,recurrence_limit,66
|
tlvdata,offer,offer_recurrence_limit,max_period,tu32,
|
||||||
tlvdata,offer,recurrence_limit,max_period,tu32,
|
tlvtype,offer,offer_recurrence_base,32
|
||||||
tlvtype,offer,recurrence_base,28
|
tlvdata,offer,offer_recurrence_base,base,recurrence_base,
|
||||||
tlvdata,offer,recurrence_base,start_any_period,byte,
|
tlvtype,invoice_request,invreq_metadata,0
|
||||||
tlvdata,offer,recurrence_base,basetime,tu64,
|
tlvdata,invoice_request,invreq_metadata,blob,byte,...
|
||||||
tlvtype,offer,node_id,30
|
tlvtype,invoice_request,offer_chains,2
|
||||||
tlvdata,offer,node_id,node_id,point,
|
tlvdata,invoice_request,offer_chains,chains,chain_hash,...
|
||||||
tlvtype,offer,send_invoice,54
|
tlvtype,invoice_request,offer_metadata,4
|
||||||
tlvtype,offer,refund_for,34
|
tlvdata,invoice_request,offer_metadata,data,byte,...
|
||||||
tlvdata,offer,refund_for,refunded_payment_hash,sha256,
|
tlvtype,invoice_request,offer_currency,6
|
||||||
tlvtype,offer,signature,240
|
tlvdata,invoice_request,offer_currency,iso4217,utf8,...
|
||||||
tlvdata,offer,signature,sig,bip340sig,
|
tlvtype,invoice_request,offer_amount,8
|
||||||
tlvtype,invoice_request,chain,3
|
tlvdata,invoice_request,offer_amount,amount,tu64,
|
||||||
tlvdata,invoice_request,chain,chain,chain_hash,
|
tlvtype,invoice_request,offer_description,10
|
||||||
tlvtype,invoice_request,offer_id,4
|
tlvdata,invoice_request,offer_description,description,utf8,...
|
||||||
tlvdata,invoice_request,offer_id,offer_id,sha256,
|
tlvtype,invoice_request,offer_features,12
|
||||||
tlvtype,invoice_request,amount,8
|
tlvdata,invoice_request,offer_features,features,byte,...
|
||||||
tlvdata,invoice_request,amount,msat,tu64,
|
tlvtype,invoice_request,offer_absolute_expiry,14
|
||||||
tlvtype,invoice_request,features,12
|
tlvdata,invoice_request,offer_absolute_expiry,seconds_from_epoch,tu64,
|
||||||
tlvdata,invoice_request,features,features,byte,...
|
tlvtype,invoice_request,offer_paths,16
|
||||||
tlvtype,invoice_request,quantity,32
|
tlvdata,invoice_request,offer_paths,paths,blinded_path,...
|
||||||
tlvdata,invoice_request,quantity,quantity,tu64,
|
tlvtype,invoice_request,offer_issuer,18
|
||||||
tlvtype,invoice_request,recurrence_counter,36
|
tlvdata,invoice_request,offer_issuer,issuer,utf8,...
|
||||||
tlvdata,invoice_request,recurrence_counter,counter,tu32,
|
tlvtype,invoice_request,offer_quantity_max,20
|
||||||
tlvtype,invoice_request,recurrence_start,68
|
tlvdata,invoice_request,offer_quantity_max,max,tu64,
|
||||||
tlvdata,invoice_request,recurrence_start,period_offset,tu32,
|
tlvtype,invoice_request,offer_node_id,22
|
||||||
tlvtype,invoice_request,payer_key,38
|
tlvdata,invoice_request,offer_node_id,node_id,point,
|
||||||
tlvdata,invoice_request,payer_key,key,point,
|
tlvtype,invoice_request,offer_recurrence,26
|
||||||
tlvtype,invoice_request,payer_note,39
|
tlvdata,invoice_request,offer_recurrence,recurrence,recurrence,
|
||||||
tlvdata,invoice_request,payer_note,note,utf8,...
|
tlvtype,invoice_request,offer_recurrence_paywindow,28
|
||||||
tlvtype,invoice_request,payer_info,50
|
tlvdata,invoice_request,offer_recurrence_paywindow,paywindow,recurrence_paywindow,
|
||||||
tlvdata,invoice_request,payer_info,blob,byte,...
|
tlvtype,invoice_request,offer_recurrence_limit,30
|
||||||
tlvtype,invoice_request,replace_invoice,56
|
tlvdata,invoice_request,offer_recurrence_limit,max_period,tu32,
|
||||||
tlvdata,invoice_request,replace_invoice,payment_hash,sha256,
|
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
|
tlvtype,invoice_request,signature,240
|
||||||
tlvdata,invoice_request,signature,sig,bip340sig,
|
tlvdata,invoice_request,signature,sig,bip340sig,
|
||||||
tlvtype,invoice,chain,3
|
tlvtype,invoice,invreq_metadata,0
|
||||||
tlvdata,invoice,chain,chain,chain_hash,
|
tlvdata,invoice,invreq_metadata,blob,byte,...
|
||||||
tlvtype,invoice,offer_id,4
|
tlvtype,invoice,offer_chains,2
|
||||||
tlvdata,invoice,offer_id,offer_id,sha256,
|
tlvdata,invoice,offer_chains,chains,chain_hash,...
|
||||||
tlvtype,invoice,amount,8
|
tlvtype,invoice,offer_metadata,4
|
||||||
tlvdata,invoice,amount,msat,tu64,
|
tlvdata,invoice,offer_metadata,data,byte,...
|
||||||
tlvtype,invoice,description,10
|
tlvtype,invoice,offer_currency,6
|
||||||
tlvdata,invoice,description,description,utf8,...
|
tlvdata,invoice,offer_currency,iso4217,utf8,...
|
||||||
tlvtype,invoice,features,12
|
tlvtype,invoice,offer_amount,8
|
||||||
tlvdata,invoice,features,features,byte,...
|
tlvdata,invoice,offer_amount,amount,tu64,
|
||||||
tlvtype,invoice,paths,16
|
tlvtype,invoice,offer_description,10
|
||||||
tlvdata,invoice,paths,paths,blinded_path,...
|
tlvdata,invoice,offer_description,description,utf8,...
|
||||||
tlvtype,invoice,blindedpay,18
|
tlvtype,invoice,offer_features,12
|
||||||
tlvdata,invoice,blindedpay,payinfo,blinded_payinfo,...
|
tlvdata,invoice,offer_features,features,byte,...
|
||||||
tlvtype,invoice,blinded_capacities,19
|
tlvtype,invoice,offer_absolute_expiry,14
|
||||||
tlvdata,invoice,blinded_capacities,incoming_msat,u64,...
|
tlvdata,invoice,offer_absolute_expiry,seconds_from_epoch,tu64,
|
||||||
tlvtype,invoice,issuer,20
|
tlvtype,invoice,offer_paths,16
|
||||||
tlvdata,invoice,issuer,issuer,utf8,...
|
tlvdata,invoice,offer_paths,paths,blinded_path,...
|
||||||
tlvtype,invoice,node_id,30
|
tlvtype,invoice,offer_issuer,18
|
||||||
tlvdata,invoice,node_id,node_id,point,
|
tlvdata,invoice,offer_issuer,issuer,utf8,...
|
||||||
tlvtype,invoice,quantity,32
|
tlvtype,invoice,offer_quantity_max,20
|
||||||
tlvdata,invoice,quantity,quantity,tu64,
|
tlvdata,invoice,offer_quantity_max,max,tu64,
|
||||||
tlvtype,invoice,refund_for,34
|
tlvtype,invoice,offer_node_id,22
|
||||||
tlvdata,invoice,refund_for,refunded_payment_hash,sha256,
|
tlvdata,invoice,offer_node_id,node_id,point,
|
||||||
tlvtype,invoice,recurrence_counter,36
|
tlvtype,invoice,offer_recurrence,26
|
||||||
tlvdata,invoice,recurrence_counter,counter,tu32,
|
tlvdata,invoice,offer_recurrence,recurrence,recurrence,
|
||||||
tlvtype,invoice,recurrence_start,68
|
tlvtype,invoice,offer_recurrence_paywindow,28
|
||||||
tlvdata,invoice,recurrence_start,period_offset,tu32,
|
tlvdata,invoice,offer_recurrence_paywindow,paywindow,recurrence_paywindow,
|
||||||
tlvtype,invoice,recurrence_basetime,64
|
tlvtype,invoice,offer_recurrence_limit,30
|
||||||
tlvdata,invoice,recurrence_basetime,basetime,tu64,
|
tlvdata,invoice,offer_recurrence_limit,max_period,tu32,
|
||||||
tlvtype,invoice,payer_key,38
|
tlvtype,invoice,offer_recurrence_base,32
|
||||||
tlvdata,invoice,payer_key,key,point,
|
tlvdata,invoice,offer_recurrence_base,base,recurrence_base,
|
||||||
tlvtype,invoice,payer_note,39
|
tlvtype,invoice,invreq_chain,80
|
||||||
tlvdata,invoice,payer_note,note,utf8,...
|
tlvdata,invoice,invreq_chain,chain,chain_hash,
|
||||||
tlvtype,invoice,created_at,40
|
tlvtype,invoice,invreq_amount,82
|
||||||
tlvdata,invoice,created_at,timestamp,tu64,
|
tlvdata,invoice,invreq_amount,msat,tu64,
|
||||||
tlvtype,invoice,payment_hash,42
|
tlvtype,invoice,invreq_features,84
|
||||||
tlvdata,invoice,payment_hash,payment_hash,sha256,
|
tlvdata,invoice,invreq_features,features,byte,...
|
||||||
tlvtype,invoice,relative_expiry,44
|
tlvtype,invoice,invreq_quantity,86
|
||||||
tlvdata,invoice,relative_expiry,seconds_from_creation,tu32,
|
tlvdata,invoice,invreq_quantity,quantity,tu64,
|
||||||
tlvtype,invoice,cltv,46
|
tlvtype,invoice,invreq_payer_id,88
|
||||||
tlvdata,invoice,cltv,min_final_cltv_expiry,tu16,
|
tlvdata,invoice,invreq_payer_id,key,point,
|
||||||
tlvtype,invoice,fallbacks,48
|
tlvtype,invoice,invreq_payer_note,89
|
||||||
tlvdata,invoice,fallbacks,fallbacks,fallback_address,...
|
tlvdata,invoice,invreq_payer_note,note,utf8,...
|
||||||
tlvtype,invoice,payer_info,50
|
tlvtype,invoice,invreq_recurrence_counter,90
|
||||||
tlvdata,invoice,payer_info,blob,byte,...
|
tlvdata,invoice,invreq_recurrence_counter,counter,tu32,
|
||||||
tlvtype,invoice,refund_signature,52
|
tlvtype,invoice,invreq_recurrence_start,92
|
||||||
tlvdata,invoice,refund_signature,payer_signature,bip340sig,
|
tlvdata,invoice,invreq_recurrence_start,period_offset,tu32,
|
||||||
tlvtype,invoice,send_invoice,54
|
tlvtype,invoice,invoice_paths,160
|
||||||
tlvtype,invoice,replace_invoice,56
|
tlvdata,invoice,invoice_paths,paths,blinded_path,...
|
||||||
tlvdata,invoice,replace_invoice,payment_hash,sha256,
|
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
|
tlvtype,invoice,signature,240
|
||||||
tlvdata,invoice,signature,sig,bip340sig,
|
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
|
subtype,blinded_payinfo
|
||||||
subtypedata,blinded_payinfo,fee_base_msat,u32,
|
subtypedata,blinded_payinfo,fee_base_msat,u32,
|
||||||
subtypedata,blinded_payinfo,fee_proportional_millionths,u32,
|
subtypedata,blinded_payinfo,fee_proportional_millionths,u32,
|
||||||
|
|||||||
|
@@ -1,119 +1,163 @@
|
|||||||
tlvtype,offer,chains,2
|
tlvtype,offer,offer_chains,2
|
||||||
tlvdata,offer,chains,chains,chain_hash,...
|
tlvdata,offer,offer_chains,chains,chain_hash,...
|
||||||
tlvtype,offer,currency,6
|
tlvtype,offer,offer_metadata,4
|
||||||
tlvdata,offer,currency,iso4217,utf8,...
|
tlvdata,offer,offer_metadata,data,byte,...
|
||||||
tlvtype,offer,amount,8
|
tlvtype,offer,offer_currency,6
|
||||||
tlvdata,offer,amount,amount,tu64,
|
tlvdata,offer,offer_currency,iso4217,utf8,...
|
||||||
tlvtype,offer,description,10
|
tlvtype,offer,offer_amount,8
|
||||||
tlvdata,offer,description,description,utf8,...
|
tlvdata,offer,offer_amount,amount,tu64,
|
||||||
tlvtype,offer,features,12
|
tlvtype,offer,offer_description,10
|
||||||
tlvdata,offer,features,features,byte,...
|
tlvdata,offer,offer_description,description,utf8,...
|
||||||
tlvtype,offer,absolute_expiry,14
|
tlvtype,offer,offer_features,12
|
||||||
tlvdata,offer,absolute_expiry,seconds_from_epoch,tu64,
|
tlvdata,offer,offer_features,features,byte,...
|
||||||
tlvtype,offer,paths,16
|
tlvtype,offer,offer_absolute_expiry,14
|
||||||
tlvdata,offer,paths,paths,blinded_path,...
|
tlvdata,offer,offer_absolute_expiry,seconds_from_epoch,tu64,
|
||||||
tlvtype,offer,issuer,20
|
tlvtype,offer,offer_paths,16
|
||||||
tlvdata,offer,issuer,issuer,utf8,...
|
tlvdata,offer,offer_paths,paths,blinded_path,...
|
||||||
tlvtype,offer,quantity_min,22
|
tlvtype,offer,offer_issuer,18
|
||||||
tlvdata,offer,quantity_min,min,tu64,
|
tlvdata,offer,offer_issuer,issuer,utf8,...
|
||||||
tlvtype,offer,quantity_max,24
|
tlvtype,offer,offer_quantity_max,20
|
||||||
tlvdata,offer,quantity_max,max,tu64,
|
tlvdata,offer,offer_quantity_max,max,tu64,
|
||||||
tlvtype,offer,recurrence,26
|
tlvtype,offer,offer_node_id,22
|
||||||
tlvdata,offer,recurrence,time_unit,byte,
|
tlvdata,offer,offer_node_id,node_id,point,
|
||||||
tlvdata,offer,recurrence,period,tu32,
|
tlvtype,offer,offer_recurrence,26
|
||||||
tlvtype,offer,recurrence_paywindow,64
|
tlvdata,offer,offer_recurrence,recurrence,recurrence,
|
||||||
tlvdata,offer,recurrence_paywindow,seconds_before,u32,
|
tlvtype,offer,offer_recurrence_paywindow,28
|
||||||
tlvdata,offer,recurrence_paywindow,proportional_amount,byte,
|
tlvdata,offer,offer_recurrence_paywindow,paywindow,recurrence_paywindow,
|
||||||
tlvdata,offer,recurrence_paywindow,seconds_after,tu32,
|
tlvtype,offer,offer_recurrence_limit,30
|
||||||
tlvtype,offer,recurrence_limit,66
|
tlvdata,offer,offer_recurrence_limit,max_period,tu32,
|
||||||
tlvdata,offer,recurrence_limit,max_period,tu32,
|
tlvtype,offer,offer_recurrence_base,32
|
||||||
tlvtype,offer,recurrence_base,28
|
tlvdata,offer,offer_recurrence_base,base,recurrence_base,
|
||||||
tlvdata,offer,recurrence_base,start_any_period,byte,
|
tlvtype,invoice_request,invreq_metadata,0
|
||||||
tlvdata,offer,recurrence_base,basetime,tu64,
|
tlvdata,invoice_request,invreq_metadata,blob,byte,...
|
||||||
tlvtype,offer,node_id,30
|
tlvtype,invoice_request,offer_chains,2
|
||||||
tlvdata,offer,node_id,node_id,point,
|
tlvdata,invoice_request,offer_chains,chains,chain_hash,...
|
||||||
tlvtype,offer,send_invoice,54
|
tlvtype,invoice_request,offer_metadata,4
|
||||||
tlvtype,offer,refund_for,34
|
tlvdata,invoice_request,offer_metadata,data,byte,...
|
||||||
tlvdata,offer,refund_for,refunded_payment_hash,sha256,
|
tlvtype,invoice_request,offer_currency,6
|
||||||
tlvtype,offer,signature,240
|
tlvdata,invoice_request,offer_currency,iso4217,utf8,...
|
||||||
tlvdata,offer,signature,sig,bip340sig,
|
tlvtype,invoice_request,offer_amount,8
|
||||||
tlvtype,invoice_request,chain,3
|
tlvdata,invoice_request,offer_amount,amount,tu64,
|
||||||
tlvdata,invoice_request,chain,chain,chain_hash,
|
tlvtype,invoice_request,offer_description,10
|
||||||
tlvtype,invoice_request,offer_id,4
|
tlvdata,invoice_request,offer_description,description,utf8,...
|
||||||
tlvdata,invoice_request,offer_id,offer_id,sha256,
|
tlvtype,invoice_request,offer_features,12
|
||||||
tlvtype,invoice_request,amount,8
|
tlvdata,invoice_request,offer_features,features,byte,...
|
||||||
tlvdata,invoice_request,amount,msat,tu64,
|
tlvtype,invoice_request,offer_absolute_expiry,14
|
||||||
tlvtype,invoice_request,features,12
|
tlvdata,invoice_request,offer_absolute_expiry,seconds_from_epoch,tu64,
|
||||||
tlvdata,invoice_request,features,features,byte,...
|
tlvtype,invoice_request,offer_paths,16
|
||||||
tlvtype,invoice_request,quantity,32
|
tlvdata,invoice_request,offer_paths,paths,blinded_path,...
|
||||||
tlvdata,invoice_request,quantity,quantity,tu64,
|
tlvtype,invoice_request,offer_issuer,18
|
||||||
tlvtype,invoice_request,recurrence_counter,36
|
tlvdata,invoice_request,offer_issuer,issuer,utf8,...
|
||||||
tlvdata,invoice_request,recurrence_counter,counter,tu32,
|
tlvtype,invoice_request,offer_quantity_max,20
|
||||||
tlvtype,invoice_request,recurrence_start,68
|
tlvdata,invoice_request,offer_quantity_max,max,tu64,
|
||||||
tlvdata,invoice_request,recurrence_start,period_offset,tu32,
|
tlvtype,invoice_request,offer_node_id,22
|
||||||
tlvtype,invoice_request,payer_key,38
|
tlvdata,invoice_request,offer_node_id,node_id,point,
|
||||||
tlvdata,invoice_request,payer_key,key,point,
|
tlvtype,invoice_request,offer_recurrence,26
|
||||||
tlvtype,invoice_request,payer_note,39
|
tlvdata,invoice_request,offer_recurrence,recurrence,recurrence,
|
||||||
tlvdata,invoice_request,payer_note,note,utf8,...
|
tlvtype,invoice_request,offer_recurrence_paywindow,28
|
||||||
tlvtype,invoice_request,payer_info,50
|
tlvdata,invoice_request,offer_recurrence_paywindow,paywindow,recurrence_paywindow,
|
||||||
tlvdata,invoice_request,payer_info,blob,byte,...
|
tlvtype,invoice_request,offer_recurrence_limit,30
|
||||||
tlvtype,invoice_request,replace_invoice,56
|
tlvdata,invoice_request,offer_recurrence_limit,max_period,tu32,
|
||||||
tlvdata,invoice_request,replace_invoice,payment_hash,sha256,
|
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
|
tlvtype,invoice_request,signature,240
|
||||||
tlvdata,invoice_request,signature,sig,bip340sig,
|
tlvdata,invoice_request,signature,sig,bip340sig,
|
||||||
tlvtype,invoice,chain,3
|
tlvtype,invoice,invreq_metadata,0
|
||||||
tlvdata,invoice,chain,chain,chain_hash,
|
tlvdata,invoice,invreq_metadata,blob,byte,...
|
||||||
tlvtype,invoice,offer_id,4
|
tlvtype,invoice,offer_chains,2
|
||||||
tlvdata,invoice,offer_id,offer_id,sha256,
|
tlvdata,invoice,offer_chains,chains,chain_hash,...
|
||||||
tlvtype,invoice,amount,8
|
tlvtype,invoice,offer_metadata,4
|
||||||
tlvdata,invoice,amount,msat,tu64,
|
tlvdata,invoice,offer_metadata,data,byte,...
|
||||||
tlvtype,invoice,description,10
|
tlvtype,invoice,offer_currency,6
|
||||||
tlvdata,invoice,description,description,utf8,...
|
tlvdata,invoice,offer_currency,iso4217,utf8,...
|
||||||
tlvtype,invoice,features,12
|
tlvtype,invoice,offer_amount,8
|
||||||
tlvdata,invoice,features,features,byte,...
|
tlvdata,invoice,offer_amount,amount,tu64,
|
||||||
tlvtype,invoice,paths,16
|
tlvtype,invoice,offer_description,10
|
||||||
tlvdata,invoice,paths,paths,blinded_path,...
|
tlvdata,invoice,offer_description,description,utf8,...
|
||||||
tlvtype,invoice,blindedpay,18
|
tlvtype,invoice,offer_features,12
|
||||||
tlvdata,invoice,blindedpay,payinfo,blinded_payinfo,...
|
tlvdata,invoice,offer_features,features,byte,...
|
||||||
tlvtype,invoice,blinded_capacities,19
|
tlvtype,invoice,offer_absolute_expiry,14
|
||||||
tlvdata,invoice,blinded_capacities,incoming_msat,u64,...
|
tlvdata,invoice,offer_absolute_expiry,seconds_from_epoch,tu64,
|
||||||
tlvtype,invoice,issuer,20
|
tlvtype,invoice,offer_paths,16
|
||||||
tlvdata,invoice,issuer,issuer,utf8,...
|
tlvdata,invoice,offer_paths,paths,blinded_path,...
|
||||||
tlvtype,invoice,node_id,30
|
tlvtype,invoice,offer_issuer,18
|
||||||
tlvdata,invoice,node_id,node_id,point,
|
tlvdata,invoice,offer_issuer,issuer,utf8,...
|
||||||
tlvtype,invoice,quantity,32
|
tlvtype,invoice,offer_quantity_max,20
|
||||||
tlvdata,invoice,quantity,quantity,tu64,
|
tlvdata,invoice,offer_quantity_max,max,tu64,
|
||||||
tlvtype,invoice,refund_for,34
|
tlvtype,invoice,offer_node_id,22
|
||||||
tlvdata,invoice,refund_for,refunded_payment_hash,sha256,
|
tlvdata,invoice,offer_node_id,node_id,point,
|
||||||
tlvtype,invoice,recurrence_counter,36
|
tlvtype,invoice,offer_recurrence,26
|
||||||
tlvdata,invoice,recurrence_counter,counter,tu32,
|
tlvdata,invoice,offer_recurrence,recurrence,recurrence,
|
||||||
tlvtype,invoice,recurrence_start,68
|
tlvtype,invoice,offer_recurrence_paywindow,28
|
||||||
tlvdata,invoice,recurrence_start,period_offset,tu32,
|
tlvdata,invoice,offer_recurrence_paywindow,paywindow,recurrence_paywindow,
|
||||||
tlvtype,invoice,recurrence_basetime,64
|
tlvtype,invoice,offer_recurrence_limit,30
|
||||||
tlvdata,invoice,recurrence_basetime,basetime,tu64,
|
tlvdata,invoice,offer_recurrence_limit,max_period,tu32,
|
||||||
tlvtype,invoice,payer_key,38
|
tlvtype,invoice,offer_recurrence_base,32
|
||||||
tlvdata,invoice,payer_key,key,point,
|
tlvdata,invoice,offer_recurrence_base,base,recurrence_base,
|
||||||
tlvtype,invoice,payer_note,39
|
tlvtype,invoice,invreq_chain,80
|
||||||
tlvdata,invoice,payer_note,note,utf8,...
|
tlvdata,invoice,invreq_chain,chain,chain_hash,
|
||||||
tlvtype,invoice,created_at,40
|
tlvtype,invoice,invreq_amount,82
|
||||||
tlvdata,invoice,created_at,timestamp,tu64,
|
tlvdata,invoice,invreq_amount,msat,tu64,
|
||||||
tlvtype,invoice,payment_hash,42
|
tlvtype,invoice,invreq_features,84
|
||||||
tlvdata,invoice,payment_hash,payment_hash,sha256,
|
tlvdata,invoice,invreq_features,features,byte,...
|
||||||
tlvtype,invoice,relative_expiry,44
|
tlvtype,invoice,invreq_quantity,86
|
||||||
tlvdata,invoice,relative_expiry,seconds_from_creation,tu32,
|
tlvdata,invoice,invreq_quantity,quantity,tu64,
|
||||||
tlvtype,invoice,cltv,46
|
tlvtype,invoice,invreq_payer_id,88
|
||||||
tlvdata,invoice,cltv,min_final_cltv_expiry,tu16,
|
tlvdata,invoice,invreq_payer_id,key,point,
|
||||||
tlvtype,invoice,fallbacks,48
|
tlvtype,invoice,invreq_payer_note,89
|
||||||
tlvdata,invoice,fallbacks,fallbacks,fallback_address,...
|
tlvdata,invoice,invreq_payer_note,note,utf8,...
|
||||||
tlvtype,invoice,payer_info,50
|
tlvtype,invoice,invreq_recurrence_counter,90
|
||||||
tlvdata,invoice,payer_info,blob,byte,...
|
tlvdata,invoice,invreq_recurrence_counter,counter,tu32,
|
||||||
tlvtype,invoice,refund_signature,52
|
tlvtype,invoice,invreq_recurrence_start,92
|
||||||
tlvdata,invoice,refund_signature,payer_signature,bip340sig,
|
tlvdata,invoice,invreq_recurrence_start,period_offset,tu32,
|
||||||
tlvtype,invoice,send_invoice,54
|
tlvtype,invoice,invoice_paths,160
|
||||||
tlvtype,invoice,replace_invoice,56
|
tlvdata,invoice,invoice_paths,paths,blinded_path,...
|
||||||
tlvdata,invoice,replace_invoice,payment_hash,sha256,
|
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
|
tlvtype,invoice,signature,240
|
||||||
tlvdata,invoice,signature,sig,bip340sig,
|
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
|
subtype,blinded_payinfo
|
||||||
subtypedata,blinded_payinfo,fee_base_msat,u32,
|
subtypedata,blinded_payinfo,fee_base_msat,u32,
|
||||||
subtypedata,blinded_payinfo,fee_proportional_millionths,u32,
|
subtypedata,blinded_payinfo,fee_proportional_millionths,u32,
|
||||||
|
|||||||
|
@@ -1,47 +1,87 @@
|
|||||||
diff --git b/wire/bolt12_wire.csv a/wire/bolt12_wire.csv
|
--- wire/bolt12_wire.csv.raw 2022-10-04 13:26:18.105307201 +1030
|
||||||
index 726c3c0a1..a53ca3cdf 100644
|
+++ wire/bolt12_wire.csv 2022-10-04 13:25:59.617242667 +1030
|
||||||
--- b/wire/bolt12_wire.csv
|
@@ -22,6 +22,14 @@
|
||||||
+++ a/wire/bolt12_wire.csv
|
tlvdata,offer,offer_quantity_max,max,tu64,
|
||||||
@@ -18,6 +18,18 @@ tlvtype,offer,quantity_min,22
|
tlvtype,offer,offer_node_id,24
|
||||||
tlvdata,offer,quantity_min,min,tu64,
|
tlvdata,offer,offer_node_id,node_id,point,
|
||||||
tlvtype,offer,quantity_max,24
|
+tlvtype,offer,offer_recurrence,26
|
||||||
tlvdata,offer,quantity_max,max,tu64,
|
+tlvdata,offer,offer_recurrence,recurrence,recurrence,
|
||||||
+tlvtype,offer,recurrence,26
|
+tlvtype,offer,offer_recurrence_paywindow,28
|
||||||
+tlvdata,offer,recurrence,time_unit,byte,
|
+tlvdata,offer,offer_recurrence_paywindow,paywindow,recurrence_paywindow,
|
||||||
+tlvdata,offer,recurrence,period,tu32,
|
+tlvtype,offer,offer_recurrence_limit,30
|
||||||
+tlvtype,offer,recurrence_paywindow,64
|
+tlvdata,offer,offer_recurrence_limit,max_period,tu32,
|
||||||
+tlvdata,offer,recurrence_paywindow,seconds_before,u32,
|
+tlvtype,offer,offer_recurrence_base,32
|
||||||
+tlvdata,offer,recurrence_paywindow,proportional_amount,byte,
|
+tlvdata,offer,offer_recurrence_base,base,recurrence_base,
|
||||||
+tlvdata,offer,recurrence_paywindow,seconds_after,tu32,
|
tlvtype,invoice_request,invreq_metadata,0
|
||||||
+tlvtype,offer,recurrence_limit,66
|
tlvdata,invoice_request,invreq_metadata,blob,byte,...
|
||||||
+tlvdata,offer,recurrence_limit,max_period,tu32,
|
tlvtype,invoice_request,offer_chains,2
|
||||||
+tlvtype,offer,recurrence_base,28
|
@@ -48,6 +60,14 @@
|
||||||
+tlvdata,offer,recurrence_base,start_any_period,byte,
|
tlvdata,invoice_request,offer_quantity_max,max,tu64,
|
||||||
+tlvdata,offer,recurrence_base,basetime,tu64,
|
tlvtype,invoice_request,offer_node_id,24
|
||||||
tlvtype,offer,node_id,30
|
tlvdata,invoice_request,offer_node_id,node_id,point,
|
||||||
tlvdata,offer,node_id,node_id,pubkey,
|
+tlvtype,invoice_request,offer_recurrence,26
|
||||||
tlvtype,offer,send_invoice,54
|
+tlvdata,invoice_request,offer_recurrence,recurrence,recurrence,
|
||||||
@@ -40,6 +54,10 @@ tlvtype,invoice_request,features,12
|
+tlvtype,invoice_request,offer_recurrence_paywindow,28
|
||||||
tlvdata,invoice_request,features,features,byte,...
|
+tlvdata,invoice_request,offer_recurrence_paywindow,paywindow,recurrence_paywindow,
|
||||||
tlvtype,invoice_request,quantity,32
|
+tlvtype,invoice_request,offer_recurrence_limit,30
|
||||||
tlvdata,invoice_request,quantity,quantity,tu64,
|
+tlvdata,invoice_request,offer_recurrence_limit,max_period,tu32,
|
||||||
+tlvtype,invoice_request,recurrence_counter,36
|
+tlvtype,invoice_request,offer_recurrence_base,32
|
||||||
+tlvdata,invoice_request,recurrence_counter,counter,tu32,
|
+tlvdata,invoice_request,offer_recurrence_base,base,recurrence_base,
|
||||||
+tlvtype,invoice_request,recurrence_start,68
|
tlvtype,invoice_request,invreq_chain,80
|
||||||
+tlvdata,invoice_request,recurrence_start,period_offset,tu32,
|
tlvdata,invoice_request,invreq_chain,chain,chain_hash,
|
||||||
tlvtype,invoice_request,payer_key,38
|
tlvtype,invoice_request,invreq_amount,82
|
||||||
tlvdata,invoice_request,payer_key,key,pubkey,
|
@@ -60,6 +84,10 @@
|
||||||
tlvtype,invoice_request,payer_note,39
|
tlvdata,invoice_request,invreq_payer_id,key,point,
|
||||||
@@ -74,6 +94,12 @@ tlvtype,invoice,quantity,32
|
tlvtype,invoice_request,invreq_payer_note,89
|
||||||
tlvdata,invoice,quantity,quantity,tu64,
|
tlvdata,invoice_request,invreq_payer_note,note,utf8,...
|
||||||
tlvtype,invoice,refund_for,34
|
+tlvtype,invoice_request,invreq_recurrence_counter,90
|
||||||
tlvdata,invoice,refund_for,refunded_payment_hash,sha256,
|
+tlvdata,invoice_request,invreq_recurrence_counter,counter,tu32,
|
||||||
+tlvtype,invoice,recurrence_counter,36
|
+tlvtype,invoice_request,invreq_recurrence_start,92
|
||||||
+tlvdata,invoice,recurrence_counter,counter,tu32,
|
+tlvdata,invoice_request,invreq_recurrence_start,period_offset,tu32,
|
||||||
+tlvtype,invoice,recurrence_start,68
|
tlvtype,invoice_request,signature,240
|
||||||
+tlvdata,invoice,recurrence_start,period_offset,tu32,
|
tlvdata,invoice_request,signature,sig,bip340sig,
|
||||||
+tlvtype,invoice,recurrence_basetime,64
|
tlvtype,invoice,invreq_metadata,0
|
||||||
+tlvdata,invoice,recurrence_basetime,basetime,tu64,
|
@@ -89,5 +117,13 @@
|
||||||
tlvtype,invoice,payer_key,38
|
tlvtype,invoice,offer_node_id,24
|
||||||
tlvdata,invoice,payer_key,key,pubkey,
|
tlvdata,invoice,offer_node_id,node_id,point,
|
||||||
tlvtype,invoice,payer_note,39
|
+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
|
||||||
|
|||||||
Reference in New Issue
Block a user