mirror of
https://github.com/ElementsProject/lightning.git
synced 2024-11-19 09:54:16 +01:00
723c16072a
1. Hoist 7200 constant into the bolt12 heade2. 2. Make preimage the last createinvoice arg, so we could make it optional. 3. Check the validity of the preimage in createinvoice. 4. Always output used flag in listoffers. 5. Rename wallet offer iterators to offer_id iterators. 6. Fix paramter typos. 7. Rename `local_offer_id` parameter to `localofferid`. 8. Add reference constraints on local_offer_id db fields. 9. Remove cut/paste comment. 10. Clarify source of fatal() messages in wallet. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
647 lines
18 KiB
C
647 lines
18 KiB
C
#include <bitcoin/chainparams.h>
|
|
#include <ccan/cast/cast.h>
|
|
#include <ccan/err/err.h>
|
|
#include <ccan/tal/str/str.h>
|
|
#include <common/bech32_util.h>
|
|
#include <common/bolt12.h>
|
|
#include <common/bolt12_merkle.h>
|
|
#include <common/features.h>
|
|
#include <common/iso4217.h>
|
|
#include <common/setup.h>
|
|
#include <common/type_to_string.h>
|
|
#include <common/version.h>
|
|
#include <inttypes.h>
|
|
#include <secp256k1_schnorrsig.h>
|
|
#include <stdio.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/types.h>
|
|
#include <sys/un.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#define NO_ERROR 0
|
|
#define ERROR_BAD_DECODE 1
|
|
#define ERROR_USAGE 3
|
|
|
|
static bool well_formed = true;
|
|
|
|
/* Tal wrappers for opt. */
|
|
static void *opt_allocfn(size_t size)
|
|
{
|
|
return tal_arr_label(NULL, char, size, TAL_LABEL("opt_allocfn", ""));
|
|
}
|
|
|
|
static void *tal_reallocfn(void *ptr, size_t size)
|
|
{
|
|
if (!ptr)
|
|
return opt_allocfn(size);
|
|
tal_resize_(&ptr, 1, size, false);
|
|
return ptr;
|
|
}
|
|
|
|
static void tal_freefn(void *ptr)
|
|
{
|
|
tal_free(ptr);
|
|
}
|
|
|
|
static char *fmt_time(const tal_t *ctx, u64 time)
|
|
{
|
|
/* ctime is not sane. Take pointer, returns \n in string. */
|
|
time_t t = time;
|
|
const char *p = ctime(&t);
|
|
|
|
return tal_fmt(ctx, "%.*s", (int)strcspn(p, "\n"), p);
|
|
}
|
|
|
|
static bool must_str(bool expected, const char *complaint, const char *fieldname)
|
|
{
|
|
if (!expected) {
|
|
fprintf(stderr, "%s %s\n", complaint, fieldname);
|
|
well_formed = false;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
#define must_have(obj, field) \
|
|
must_str((obj)->field != NULL, "Missing", stringify(field))
|
|
#define must_not_have(obj, field) \
|
|
must_str((obj)->field == NULL, "Unnecessary", stringify(field))
|
|
|
|
static void print_chains(const struct bitcoin_blkid *chains)
|
|
{
|
|
printf("chains:");
|
|
for (size_t i = 0; i < tal_count(chains); i++) {
|
|
printf(" %s", type_to_string(tmpctx, struct bitcoin_blkid, &chains[i]));
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
static bool print_amount(const struct bitcoin_blkid *chains,
|
|
const char *iso4217, u64 amount)
|
|
{
|
|
const char *currency;
|
|
unsigned int minor_unit;
|
|
bool ok = true;
|
|
|
|
/* BOLT-offers #12:
|
|
* - if the currency for `amount` is that of the first entry in `chains`:
|
|
* - MUST specify `amount` in multiples of the minimum
|
|
* lightning-payable unit (e.g. milli-satoshis for bitcoin).
|
|
* - otherwise:
|
|
* - MUST specify `iso4217` as an ISO 4712 three-letter code.
|
|
* - MUST specify `amount` in the currency unit adjusted by the
|
|
* ISO 4712 exponent (e.g. USD cents).
|
|
*/
|
|
if (!iso4217) {
|
|
if (tal_count(chains) == 0)
|
|
currency = "bc";
|
|
else {
|
|
const struct chainparams *ch;
|
|
|
|
ch = chainparams_by_chainhash(&chains[0]);
|
|
if (!ch) {
|
|
currency = tal_fmt(tmpctx, "UNKNOWN CHAINHASH %s",
|
|
type_to_string(tmpctx,
|
|
struct bitcoin_blkid,
|
|
&chains[0]));
|
|
ok = false;
|
|
} else
|
|
currency = ch->bip173_name;
|
|
}
|
|
minor_unit = 11;
|
|
} else {
|
|
const struct iso4217_name_and_divisor *iso;
|
|
iso = find_iso4217(iso4217, tal_bytelen(iso4217));
|
|
if (iso) {
|
|
minor_unit = iso->minor_unit;
|
|
currency = iso->name;
|
|
} else {
|
|
minor_unit = 0;
|
|
currency = tal_fmt(tmpctx, "%.*s (UNKNOWN CURRENCY)",
|
|
(int)tal_bytelen(iso4217), iso4217);
|
|
ok = false;
|
|
}
|
|
}
|
|
|
|
if (!minor_unit)
|
|
printf("amount: %"PRIu64"%s\n", amount, currency);
|
|
else {
|
|
u64 minor_div = 1;
|
|
for (size_t i = 0; i < minor_unit; i++)
|
|
minor_div *= 10;
|
|
printf("amount: %"PRIu64".%.*"PRIu64"%s\n",
|
|
amount / minor_div, minor_unit, amount % minor_div,
|
|
currency);
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
static void print_description(const char *description)
|
|
{
|
|
printf("description: %.*s\n",
|
|
(int)tal_bytelen(description), description);
|
|
}
|
|
|
|
static void print_vendor(const char *vendor)
|
|
{
|
|
printf("vendor: %.*s\n", (int)tal_bytelen(vendor), vendor);
|
|
}
|
|
|
|
static void print_node_id(const struct pubkey32 *node_id)
|
|
{
|
|
printf("node_id: %s\n", type_to_string(tmpctx, struct pubkey32, node_id));
|
|
}
|
|
|
|
static void print_quantity_min(u64 min)
|
|
{
|
|
printf("quantity_min: %"PRIu64"\n", min);
|
|
}
|
|
|
|
static void print_quantity_max(u64 max)
|
|
{
|
|
printf("quantity_max: %"PRIu64"\n", max);
|
|
}
|
|
|
|
static bool print_recurrance(const struct tlv_offer_recurrence *recurrence,
|
|
const struct tlv_offer_recurrence_paywindow *paywindow,
|
|
const u32 *limit,
|
|
const struct tlv_offer_recurrence_base *base)
|
|
{
|
|
const char *unit;
|
|
bool ok = true;
|
|
|
|
/* BOLT-offers #12:
|
|
* Thus, each payment has:
|
|
* 1. A `time_unit` defining 0 (seconds), 1 (days), 2 (months),
|
|
* 3 (years).
|
|
* 2. A `period`, defining how often (in `time_unit`) it has to be paid.
|
|
* 3. An optional `recurrence_limit` of total payments to be paid.
|
|
* 4. An optional `recurrence_base`:
|
|
* * `basetime`, defining when the first period starts
|
|
* in seconds since 1970-01-01 UTC.
|
|
* * `start_any_period` if non-zero, meaning you don't have to start
|
|
* paying at the period indicated by `basetime`, but can use
|
|
* `recurrence_start` to indicate what period you are starting at.
|
|
* 5. An optional `recurrence_paywindow`:
|
|
* * `seconds_before`, defining how many seconds prior to the start of
|
|
* the period a payment will be accepted.
|
|
* * `proportional_amount`, if set indicating that a payment made
|
|
* during the period itself will be charged proportionally to the
|
|
* remaining time in the period (e.g. 150 seconds into a 1500
|
|
* second period gives a 10% discount).
|
|
* * `seconds_after`, defining how many seconds after the start of the
|
|
* period a payment will be accepted.
|
|
* If this field is missing, payment will be accepted during the prior
|
|
* period and the paid-for period.
|
|
*/
|
|
switch (recurrence->time_unit) {
|
|
case 0:
|
|
unit = "seconds";
|
|
break;
|
|
case 1:
|
|
unit = "days";
|
|
break;
|
|
case 2:
|
|
unit = "months";
|
|
break;
|
|
case 3:
|
|
unit = "years";
|
|
break;
|
|
default:
|
|
fprintf(stderr, "recurrence: unknown time_unit %u", recurrence->time_unit);
|
|
unit = "";
|
|
ok = false;
|
|
}
|
|
printf("recurrence: every %u %s", recurrence->period, unit);
|
|
if (limit)
|
|
printf(" limit %u", *limit);
|
|
if (base) {
|
|
printf(" start %"PRIu64" (%s)",
|
|
base->basetime,
|
|
fmt_time(tmpctx, base->basetime));
|
|
if (base->start_any_period)
|
|
printf(" (can start any period)");
|
|
}
|
|
if (paywindow) {
|
|
printf(" paywindow -%u to +%u",
|
|
paywindow->seconds_before, paywindow->seconds_after);
|
|
if (paywindow->proportional_amount)
|
|
printf(" (pay proportional)");
|
|
}
|
|
printf("\n");
|
|
|
|
return ok;
|
|
}
|
|
|
|
static void print_absolute_expiry(u64 expiry)
|
|
{
|
|
printf("absolute_expiry: %"PRIu64" (%s)\n",
|
|
expiry, fmt_time(tmpctx, expiry));
|
|
}
|
|
|
|
static void print_features(const u8 *features)
|
|
{
|
|
printf("features:");
|
|
for (size_t i = 0; i < tal_bytelen(features) * CHAR_BIT; i++) {
|
|
if (feature_is_set(features, i))
|
|
printf(" %zu", i);
|
|
}
|
|
printf("\n");
|
|
}
|
|
|
|
static bool print_blindedpaths(struct blinded_path **paths,
|
|
struct blinded_payinfo **blindedpay)
|
|
{
|
|
size_t bp_idx = 0;
|
|
|
|
for (size_t i = 0; i < tal_count(paths); i++) {
|
|
struct onionmsg_path **p = paths[i]->path;
|
|
printf("blindedpath %zu/%zu: blinding %s",
|
|
i, tal_count(paths),
|
|
type_to_string(tmpctx, struct pubkey,
|
|
&paths[i]->blinding));
|
|
printf("blindedpath %zu/%zu: path ",
|
|
i, tal_count(paths));
|
|
for (size_t j = 0; j < tal_count(p); j++) {
|
|
printf(" %s:%s",
|
|
type_to_string(tmpctx, struct pubkey,
|
|
&p[j]->node_id),
|
|
tal_hex(tmpctx, p[j]->enctlv));
|
|
if (blindedpay) {
|
|
if (bp_idx < tal_count(blindedpay))
|
|
printf("fee=%u/%u,cltv=%u,features=%s",
|
|
blindedpay[bp_idx]->fee_base_msat,
|
|
blindedpay[bp_idx]->fee_proportional_millionths,
|
|
blindedpay[bp_idx]->cltv_expiry_delta,
|
|
tal_hex(tmpctx,
|
|
blindedpay[bp_idx]->features));
|
|
bp_idx++;
|
|
}
|
|
}
|
|
printf("\n");
|
|
}
|
|
if (blindedpay && tal_count(blindedpay) != bp_idx) {
|
|
fprintf(stderr, "Expected %zu blindedpay fields, got %zu\n",
|
|
bp_idx, tal_count(blindedpay));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static void print_send_invoice(void)
|
|
{
|
|
printf("send_invoice\n");
|
|
}
|
|
|
|
static void print_refund_for(const struct sha256 *payment_hash)
|
|
{
|
|
printf("refund_for: %s\n",
|
|
type_to_string(tmpctx, struct sha256, payment_hash));
|
|
}
|
|
|
|
static bool print_signature(const char *messagename,
|
|
const char *fieldname,
|
|
const struct tlv_field *fields,
|
|
const struct pubkey32 *node_id,
|
|
const struct bip340sig *sig)
|
|
{
|
|
struct sha256 m, shash;
|
|
|
|
/* No key, it's already invalid */
|
|
if (!node_id)
|
|
return false;
|
|
|
|
merkle_tlv(fields, &m);
|
|
sighash_from_merkle(messagename, fieldname, &m, &shash);
|
|
if (secp256k1_schnorrsig_verify(secp256k1_ctx,
|
|
sig->u8,
|
|
shash.u.u8,
|
|
&node_id->pubkey) != 1) {
|
|
fprintf(stderr, "%s: INVALID\n", fieldname);
|
|
return false;
|
|
}
|
|
printf("%s: %s\n",
|
|
fieldname,
|
|
type_to_string(tmpctx, struct bip340sig, sig));
|
|
return true;
|
|
}
|
|
|
|
static void print_offer_id(const struct sha256 *offer_id)
|
|
{
|
|
printf("offer_id: %s\n",
|
|
type_to_string(tmpctx, struct sha256, offer_id));
|
|
}
|
|
|
|
static void print_quantity(u64 q)
|
|
{
|
|
printf("quantity: %"PRIu64"\n", q);
|
|
}
|
|
|
|
static void print_recurrence_counter(const u32 *recurrence_counter,
|
|
const u32 *recurrence_start)
|
|
{
|
|
printf("recurrence_counter: %u", *recurrence_counter);
|
|
if (recurrence_start)
|
|
printf(" (start +%u)", *recurrence_start);
|
|
printf("\n");
|
|
}
|
|
|
|
static bool print_recurrence_counter_with_base(const u32 *recurrence_counter,
|
|
const u32 *recurrence_start,
|
|
const u64 *recurrence_base)
|
|
{
|
|
if (!recurrence_base) {
|
|
fprintf(stderr, "Missing recurrence_base\n");
|
|
return false;
|
|
}
|
|
printf("recurrence_counter: %u", *recurrence_counter);
|
|
if (recurrence_start)
|
|
printf(" (start +%u)", *recurrence_start);
|
|
printf(" (base %"PRIu64")\n", *recurrence_base);
|
|
return true;
|
|
}
|
|
|
|
static void print_payer_key(const struct pubkey32 *payer_key,
|
|
const u8 *payer_info)
|
|
{
|
|
printf("payer_key: %s",
|
|
type_to_string(tmpctx, struct pubkey32, payer_key));
|
|
if (payer_info)
|
|
printf(" (payer_info %s)", tal_hex(tmpctx, payer_info));
|
|
printf("\n");
|
|
}
|
|
|
|
static void print_timestamp(u64 timestamp)
|
|
{
|
|
printf("timestamp: %"PRIu64" (%s)\n",
|
|
timestamp, fmt_time(tmpctx, timestamp));
|
|
}
|
|
|
|
static void print_payment_hash(const struct sha256 *payment_hash)
|
|
{
|
|
printf("payment_hash: %s\n",
|
|
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 *timestamp, u32 *relative)
|
|
{
|
|
/* Ignore if already malformed */
|
|
if (!timestamp)
|
|
return;
|
|
|
|
/* BOLT-offers #12:
|
|
* - if `relative_expiry` is present:
|
|
* - MUST reject the invoice if the current time since 1970-01-01 UTC
|
|
* is greater than `timestamp` plus `seconds_from_timestamp`.
|
|
* - otherwise:
|
|
* - MUST reject the invoice if the current time since 1970-01-01 UTC
|
|
* is greater than `timestamp` plus 7200.
|
|
*/
|
|
if (!relative)
|
|
printf("relative_expiry: %u (%s) (default)\n",
|
|
BOLT12_DEFAULT_REL_EXPIRY,
|
|
fmt_time(tmpctx, *timestamp + BOLT12_DEFAULT_REL_EXPIRY));
|
|
else
|
|
printf("relative_expiry: %u (%s)\n", *relative,
|
|
fmt_time(tmpctx, *timestamp + *relative));
|
|
}
|
|
|
|
static void print_fallbacks(const struct tlv_invoice_fallbacks *fallbacks)
|
|
{
|
|
for (size_t i = 0; i < tal_count(fallbacks->fallbacks); i++) {
|
|
/* FIXME: format properly! */
|
|
printf("fallback: %u %s\n",
|
|
fallbacks->fallbacks[i]->version,
|
|
tal_hex(tmpctx, fallbacks->fallbacks[i]->address));
|
|
}
|
|
}
|
|
|
|
static bool print_extra_fields(const struct tlv_field *fields)
|
|
{
|
|
bool ok = true;
|
|
|
|
for (size_t i = 0; i < tal_count(fields); i++) {
|
|
if (fields[i].meta)
|
|
continue;
|
|
if (fields[i].numtype % 2) {
|
|
printf("UNKNOWN EVEN field %"PRIu64": %s\n",
|
|
fields[i].numtype,
|
|
tal_hexstr(tmpctx, fields[i].value, fields[i].length));
|
|
ok = false;
|
|
} else {
|
|
printf("Unknown field %"PRIu64": %s\n",
|
|
fields[i].numtype,
|
|
tal_hexstr(tmpctx, fields[i].value, fields[i].length));
|
|
}
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
const tal_t *ctx = tal(NULL, char);
|
|
const char *method;
|
|
char *hrp;
|
|
u8 *data;
|
|
char *fail;
|
|
|
|
common_setup(argv[0]);
|
|
|
|
opt_set_alloc(opt_allocfn, tal_reallocfn, tal_freefn);
|
|
opt_register_noarg("--help|-h", opt_usage_and_exit,
|
|
"<decode> <bolt12>", "Show this message");
|
|
opt_register_version();
|
|
|
|
opt_early_parse(argc, argv, opt_log_stderr_exit);
|
|
opt_parse(&argc, argv, opt_log_stderr_exit);
|
|
|
|
method = argv[1];
|
|
if (!method)
|
|
errx(ERROR_USAGE, "Need at least one argument\n%s",
|
|
opt_usage(argv[0], NULL));
|
|
|
|
if (!streq(method, "decode"))
|
|
errx(ERROR_USAGE, "Need decode argument\n%s",
|
|
opt_usage(argv[0], NULL));
|
|
|
|
if (!argv[2])
|
|
errx(ERROR_USAGE, "Need argument\n%s",
|
|
opt_usage(argv[0], NULL));
|
|
|
|
if (!from_bech32_charset(ctx, argv[2], strlen(argv[2]), &hrp, &data))
|
|
errx(ERROR_USAGE, "Bad bech32 string\n%s",
|
|
opt_usage(argv[0], NULL));
|
|
|
|
if (streq(hrp, "lno")) {
|
|
const struct tlv_offer *offer
|
|
= offer_decode_nosig(ctx, argv[2], strlen(argv[2]),
|
|
NULL, NULL, &fail);
|
|
if (!offer)
|
|
errx(ERROR_BAD_DECODE, "Bad offer: %s", fail);
|
|
|
|
if (offer->send_invoice)
|
|
print_send_invoice();
|
|
if (offer->chains)
|
|
print_chains(offer->chains);
|
|
if (offer->refund_for)
|
|
print_refund_for(offer->refund_for);
|
|
if (offer->amount)
|
|
well_formed &= print_amount(offer->chains,
|
|
offer->currency,
|
|
*offer->amount);
|
|
if (must_have(offer, description))
|
|
print_description(offer->description);
|
|
if (offer->vendor)
|
|
print_vendor(offer->vendor);
|
|
if (must_have(offer, node_id))
|
|
print_node_id(offer->node_id);
|
|
if (offer->quantity_min)
|
|
print_quantity_min(*offer->quantity_min);
|
|
if (offer->quantity_max)
|
|
print_quantity_max(*offer->quantity_max);
|
|
if (offer->recurrence)
|
|
well_formed &= print_recurrance(offer->recurrence,
|
|
offer->recurrence_paywindow,
|
|
offer->recurrence_limit,
|
|
offer->recurrence_base);
|
|
if (offer->absolute_expiry)
|
|
print_absolute_expiry(*offer->absolute_expiry);
|
|
if (offer->features)
|
|
print_features(offer->features);
|
|
if (offer->paths)
|
|
print_blindedpaths(offer->paths, NULL);
|
|
if (must_have(offer, signature) && offer->node_id)
|
|
well_formed &= print_signature("offer", "signature",
|
|
offer->fields,
|
|
offer->node_id,
|
|
offer->signature);
|
|
if (!print_extra_fields(offer->fields))
|
|
well_formed = false;
|
|
} else if (streq(hrp, "lnr")) {
|
|
const struct tlv_invoice_request *invreq
|
|
= invrequest_decode(ctx, argv[2], strlen(argv[2]),
|
|
NULL, NULL, &fail);
|
|
if (!invreq)
|
|
errx(ERROR_BAD_DECODE, "Bad invoice_request: %s", fail);
|
|
|
|
if (invreq->chains)
|
|
print_chains(invreq->chains);
|
|
if (must_have(invreq, payer_key))
|
|
print_payer_key(invreq->payer_key, invreq->payer_info);
|
|
if (must_have(invreq, offer_id))
|
|
print_offer_id(invreq->offer_id);
|
|
if (must_have(invreq, amount))
|
|
well_formed &= print_amount(invreq->chains,
|
|
NULL,
|
|
*invreq->amount);
|
|
if (invreq->features)
|
|
print_features(invreq->features);
|
|
if (invreq->quantity)
|
|
print_quantity(*invreq->quantity);
|
|
if (invreq->recurrence_counter) {
|
|
print_recurrence_counter(invreq->recurrence_counter,
|
|
invreq->recurrence_start);
|
|
if (must_have(invreq, recurrence_signature)) {
|
|
well_formed &= print_signature("invoice_request",
|
|
"recurrence_signature",
|
|
invreq->fields,
|
|
invreq->payer_key,
|
|
invreq->recurrence_signature);
|
|
}
|
|
} else {
|
|
must_not_have(invreq, recurrence_start);
|
|
must_not_have(invreq, recurrence_signature);
|
|
}
|
|
if (!print_extra_fields(invreq->fields))
|
|
well_formed = false;
|
|
} else if (streq(hrp, "lni")) {
|
|
const struct tlv_invoice *invoice
|
|
= invoice_decode(ctx, argv[2], strlen(argv[2]),
|
|
NULL, NULL, &fail);
|
|
if (!invoice)
|
|
errx(ERROR_BAD_DECODE, "Bad invoice: %s", fail);
|
|
|
|
if (invoice->chains)
|
|
print_chains(invoice->chains);
|
|
|
|
if (invoice->offer_id) {
|
|
print_offer_id(invoice->offer_id);
|
|
}
|
|
if (must_have(invoice, amount))
|
|
well_formed &= print_amount(invoice->chains,
|
|
NULL,
|
|
*invoice->amount);
|
|
if (must_have(invoice, description))
|
|
print_description(invoice->description);
|
|
if (invoice->features)
|
|
print_features(invoice->features);
|
|
if (invoice->paths) {
|
|
must_have(invoice, blindedpay);
|
|
well_formed &= print_blindedpaths(invoice->paths,
|
|
invoice->blindedpay);
|
|
} else
|
|
must_not_have(invoice, blindedpay);
|
|
if (invoice->vendor)
|
|
print_vendor(invoice->vendor);
|
|
if (must_have(invoice, node_id))
|
|
print_node_id(invoice->node_id);
|
|
if (invoice->quantity)
|
|
print_quantity(*invoice->quantity);
|
|
if (invoice->refund_for) {
|
|
print_refund_for(invoice->refund_for);
|
|
if (must_have(invoice, refund_signature))
|
|
well_formed &= print_signature("invoice",
|
|
"refund_signature",
|
|
invoice->fields,
|
|
invoice->payer_key,
|
|
invoice->refund_signature);
|
|
} else {
|
|
must_not_have(invoice, refund_signature);
|
|
}
|
|
if (invoice->recurrence_counter) {
|
|
well_formed &=
|
|
print_recurrence_counter_with_base(invoice->recurrence_counter,
|
|
invoice->recurrence_start,
|
|
invoice->recurrence_basetime);
|
|
} else {
|
|
must_not_have(invoice, recurrence_start);
|
|
must_not_have(invoice, recurrence_basetime);
|
|
}
|
|
if (must_have(invoice, payer_key))
|
|
print_payer_key(invoice->payer_key, invoice->payer_info);
|
|
if (must_have(invoice, timestamp))
|
|
print_timestamp(*invoice->timestamp);
|
|
print_relative_expiry(invoice->timestamp,
|
|
invoice->relative_expiry);
|
|
if (must_have(invoice, payment_hash))
|
|
print_payment_hash(invoice->payment_hash);
|
|
if (must_have(invoice, cltv))
|
|
print_cltv(*invoice->cltv);
|
|
if (invoice->fallbacks)
|
|
print_fallbacks(invoice->fallbacks);
|
|
if (must_have(invoice, signature))
|
|
well_formed &= print_signature("invoice", "signature",
|
|
invoice->fields,
|
|
invoice->node_id,
|
|
invoice->signature);
|
|
if (!print_extra_fields(invoice->fields))
|
|
well_formed = false;
|
|
} else
|
|
errx(ERROR_BAD_DECODE, "Unknown prefix %s", hrp);
|
|
|
|
tal_free(ctx);
|
|
common_shutdown();
|
|
|
|
if (well_formed)
|
|
return NO_ERROR;
|
|
else
|
|
return ERROR_BAD_DECODE;
|
|
}
|