bolt12: use spec field names, update decode API.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell
2022-11-09 13:02:00 +10:30
committed by Christian Decker
parent fdb3d9186a
commit ef2f4a0648
18 changed files with 1466 additions and 912 deletions

View File

@@ -230,9 +230,10 @@ static struct command_result *param_decodable(struct command *cmd,
}
static void json_add_chains(struct json_stream *js,
const char *fieldname,
const struct bitcoin_blkid *chains)
{
json_array_start(js, "chains");
json_array_start(js, fieldname);
for (size_t i = 0; i < tal_count(chains); i++)
json_add_sha256(js, NULL, &chains[i].shad.sha);
json_array_end(js);
@@ -247,7 +248,8 @@ static void json_add_onionmsg_path(struct json_stream *js,
json_add_pubkey(js, "blinded_node_id", &hop->blinded_node_id);
json_add_hex_talarr(js, "encrypted_recipient_data", hop->encrypted_recipient_data);
if (payinfo) {
json_add_u32(js, "fee_base_msat", payinfo->fee_base_msat);
json_add_amount_msat_only(js, "fee_base_msat",
amount_msat(payinfo->fee_base_msat));
json_add_u32(js, "fee_proportional_millionths",
payinfo->fee_proportional_millionths);
json_add_u32(js, "cltv_expiry_delta",
@@ -259,11 +261,12 @@ static void json_add_onionmsg_path(struct json_stream *js,
/* Returns true if valid */
static bool json_add_blinded_paths(struct json_stream *js,
const char *fieldname,
struct blinded_path **paths,
struct blinded_payinfo **blindedpay)
{
size_t n = 0;
json_array_start(js, "paths");
json_array_start(js, fieldname);
for (size_t i = 0; i < tal_count(paths); i++) {
json_object_start(js, NULL);
json_add_pubkey(js, "first_node_id", &paths[i]->first_node_id);
@@ -286,7 +289,7 @@ static bool json_add_blinded_paths(struct json_stream *js,
* `blinded_path`.
*/
if (blindedpay && n != tal_count(blindedpay)) {
json_add_string(js, "warning_invoice_invalid_blinded_payinfo",
json_add_string(js, "warning_invalid_invoice_blindedpay",
"invoice does not have correct number of blinded_payinfo");
return false;
}
@@ -312,33 +315,57 @@ static const char *recurrence_time_unit_name(u8 time_unit)
return NULL;
}
static void json_add_offer(struct json_stream *js, const struct tlv_offer *offer)
static bool json_add_utf8(struct json_stream *js,
const char *fieldname,
const char *utf8str)
{
if (utf8_check(utf8str, tal_bytelen(utf8str))) {
json_add_stringn(js, fieldname, utf8str, tal_bytelen(utf8str));
return true;
}
json_add_string(js, tal_fmt(tmpctx, "warning_invalid_%s", fieldname),
"invalid UTF8");
return false;
}
static bool json_add_offer_fields(struct json_stream *js,
const struct bitcoin_blkid *offer_chains,
const u8 *offer_metadata,
const char *offer_currency,
const u64 *offer_amount,
const char *offer_description,
const u8 *offer_features,
const u64 *offer_absolute_expiry,
struct blinded_path **offer_paths,
const char *offer_issuer,
const u64 *offer_quantity_max,
const struct pubkey *offer_node_id,
const struct recurrence *offer_recurrence,
const struct recurrence_paywindow *offer_recurrence_paywindow,
const u32 *offer_recurrence_limit,
const struct recurrence_base *offer_recurrence_base)
{
struct sha256 offer_id;
bool valid = true;
/* FIXME-OFFERS: Rename all fields to offer_ as per spec */
offer_offer_id(offer, &offer_id);
json_add_sha256(js, "offer_id", &offer_id);
if (offer->offer_chains)
json_add_chains(js, offer->offer_chains);
if (offer->offer_currency) {
if (offer_chains)
json_add_chains(js, "offer_chains", offer_chains);
if (offer_metadata)
json_add_hex_talarr(js, "offer_metadata", offer_metadata);
if (offer_currency) {
const struct iso4217_name_and_divisor *iso4217;
json_add_stringn(js, "currency",
offer->offer_currency,
tal_bytelen(offer->offer_currency));
if (offer->offer_amount)
json_add_u64(js, "amount", *offer->offer_amount);
iso4217 = find_iso4217(offer->offer_currency,
tal_bytelen(offer->offer_currency));
valid &= json_add_utf8(js, "offer_currency", offer_currency);
if (offer_amount)
json_add_u64(js, "offer_amount", *offer_amount);
iso4217 = find_iso4217(offer_currency,
tal_bytelen(offer_currency));
if (iso4217)
json_add_num(js, "minor_unit", iso4217->minor_unit);
json_add_num(js, "currency_minor_unit", iso4217->minor_unit);
else
json_add_string(js, "warning_offer_unknown_currency",
json_add_string(js, "warning_unknown_offer_currency",
"unknown currency code");
} else if (offer->offer_amount)
json_add_amount_msat_only(js, "amount_msat",
amount_msat(*offer->offer_amount));
} else if (offer_amount)
json_add_amount_msat_only(js, "offer_amount_msat",
amount_msat(*offer_amount));
/* BOLT-offers #12:
* A reader of an offer:
@@ -346,71 +373,153 @@ static void json_add_offer(struct json_stream *js, const struct tlv_offer *offer
* - if `offer_description` is not set:
* - MUST NOT respond to the offer.
*/
if (offer->offer_description)
json_add_stringn(js, "description",
offer->offer_description,
tal_bytelen(offer->offer_description));
if (offer_description)
valid &= json_add_utf8(js, "offer_description",
offer_description);
else {
json_add_string(js, "warning_offer_missing_description",
json_add_string(js, "warning_missing_offer_description",
"offers without a description are invalid");
valid = false;
}
if (offer->offer_issuer)
json_add_stringn(js, "issuer", offer->offer_issuer,
tal_bytelen(offer->offer_issuer));
if (offer->offer_features)
json_add_hex_talarr(js, "features", offer->offer_features);
if (offer->offer_absolute_expiry)
json_add_u64(js, "absolute_expiry",
*offer->offer_absolute_expiry);
if (offer->offer_paths)
valid &= json_add_blinded_paths(js, offer->offer_paths, NULL);
if (offer_issuer)
valid &= json_add_utf8(js, "offer_issuer", offer_issuer);
if (offer_features)
json_add_hex_talarr(js, "offer_features", offer_features);
if (offer_absolute_expiry)
json_add_u64(js, "offer_absolute_expiry",
*offer_absolute_expiry);
if (offer_paths)
valid &= json_add_blinded_paths(js, "offer_paths",
offer_paths, NULL);
if (offer->offer_quantity_max)
json_add_u64(js, "quantity_max", *offer->offer_quantity_max);
if (offer->offer_recurrence) {
if (offer_quantity_max)
json_add_u64(js, "offer_quantity_max", *offer_quantity_max);
if (offer_recurrence) {
const char *name;
json_object_start(js, "recurrence");
json_add_num(js, "time_unit", offer->offer_recurrence->time_unit);
name = recurrence_time_unit_name(offer->offer_recurrence->time_unit);
json_object_start(js, "offer_recurrence");
json_add_num(js, "time_unit", offer_recurrence->time_unit);
name = recurrence_time_unit_name(offer_recurrence->time_unit);
if (name)
json_add_string(js, "time_unit_name", name);
json_add_num(js, "period", offer->offer_recurrence->period);
if (offer->offer_recurrence_base) {
json_add_num(js, "period", offer_recurrence->period);
if (offer_recurrence_base) {
json_add_u64(js, "basetime",
offer->offer_recurrence_base->basetime);
if (offer->offer_recurrence_base->start_any_period)
offer_recurrence_base->basetime);
if (offer_recurrence_base->start_any_period)
json_add_bool(js, "start_any_period", true);
}
if (offer->offer_recurrence_limit)
json_add_u32(js, "limit", *offer->offer_recurrence_limit);
if (offer->offer_recurrence_paywindow) {
if (offer_recurrence_limit)
json_add_u32(js, "limit", *offer_recurrence_limit);
if (offer_recurrence_paywindow) {
json_object_start(js, "paywindow");
json_add_u32(js, "seconds_before",
offer->offer_recurrence_paywindow->seconds_before);
offer_recurrence_paywindow->seconds_before);
json_add_u32(js, "seconds_after",
offer->offer_recurrence_paywindow->seconds_after);
if (offer->offer_recurrence_paywindow->proportional_amount)
offer_recurrence_paywindow->seconds_after);
if (offer_recurrence_paywindow->proportional_amount)
json_add_bool(js, "proportional_amount", true);
json_object_end(js);
}
json_object_end(js);
}
/* Required for offers, *not* for others! */
if (offer_node_id)
json_add_pubkey(js, "offer_node_id", offer_node_id);
return valid;
}
static void json_add_offer(struct json_stream *js, const struct tlv_offer *offer)
{
struct sha256 offer_id;
bool valid = true;
offer_offer_id(offer, &offer_id);
json_add_sha256(js, "offer_id", &offer_id);
valid &= json_add_offer_fields(js,
offer->offer_chains,
offer->offer_metadata,
offer->offer_currency,
offer->offer_amount,
offer->offer_description,
offer->offer_features,
offer->offer_absolute_expiry,
offer->offer_paths,
offer->offer_issuer,
offer->offer_quantity_max,
offer->offer_node_id,
offer->offer_recurrence,
offer->offer_recurrence_paywindow,
offer->offer_recurrence_limit,
offer->offer_recurrence_base);
/* BOLT-offers #12:
* - if `offer_node_id` is not set:
* - MUST NOT respond to the offer.
*/
/* FIXME-OFFERS: Rename all fields to offer_ as per spec */
if (offer->offer_node_id)
json_add_pubkey(js, "node_id", offer->offer_node_id);
else
if (!offer->offer_node_id) {
json_add_string(js, "warning_missing_offer_node_id",
"offers without a node_id are invalid");
valid = false;
}
json_add_bool(js, "valid", valid);
}
static bool json_add_invreq_fields(struct json_stream *js,
const u8 *invreq_metadata,
const struct bitcoin_blkid *invreq_chain,
const u64 *invreq_amount,
const u8 *invreq_features,
const u64 *invreq_quantity,
const struct pubkey *invreq_payer_id,
const utf8 *invreq_payer_note,
const u32 *invreq_recurrence_counter,
const u32 *invreq_recurrence_start)
{
bool valid = true;
/* BOLT-offers #12:
* - MUST fail the request if `invreq_payer_id` or `invreq_metadata` are not present.
*/
if (invreq_metadata)
json_add_hex_talarr(js, "invreq_metadata",
invreq_metadata);
else {
json_add_string(js, "warning_missing_invreq_metadata",
"invreq_metadata required");
valid = false;
}
/* This can be missing for an invoice though! */
if (invreq_payer_id)
json_add_pubkey(js, "invreq_payer_id", invreq_payer_id);
if (invreq_chain)
json_add_sha256(js, "invreq_chain", &invreq_chain->shad.sha);
if (invreq_amount)
json_add_amount_msat_only(js, "invreq_amount_msat",
amount_msat(*invreq_amount));
if (invreq_features)
json_add_hex_talarr(js, "invreq_features", invreq_features);
if (invreq_quantity)
json_add_u64(js, "invreq_quantity", *invreq_quantity);
if (invreq_payer_note)
valid &= json_add_utf8(js, "invreq_payer_note", invreq_payer_note);
if (invreq_recurrence_counter) {
json_add_u32(js, "invreq_recurrence_counter",
*invreq_recurrence_counter);
if (invreq_recurrence_start)
json_add_u32(js, "invreq_recurrence_start",
*invreq_recurrence_start);
}
return valid;
}
/* Returns true if valid */
static bool json_add_fallback_address(struct json_stream *js,
const struct chainparams *chain,
@@ -425,7 +534,7 @@ static bool json_add_fallback_address(struct json_stream *js,
return true;
}
json_add_string(js,
"warning_invoice_fallbacks_address_invalid",
"warning_invalid_invoice_fallbacks_address",
"invalid fallback address for this version");
return false;
}
@@ -444,7 +553,7 @@ static bool json_add_fallbacks(struct json_stream *js,
else
chain = chainparams_for_network("bitcoin");
json_array_start(js, "fallbacks");
json_array_start(js, "invoice_fallbacks");
for (size_t i = 0; i < tal_count(fallbacks); i++) {
size_t addrlen = tal_bytelen(fallbacks[i]->address);
@@ -463,12 +572,12 @@ static bool json_add_fallbacks(struct json_stream *js,
*/
if (fallbacks[i]->version > 16) {
json_add_string(js,
"warning_invoice_fallbacks_version_invalid",
"warning_invalid_invoice_fallbacks_version",
"invoice fallback version > 16");
valid = false;
} else if (addrlen < 2 || addrlen > 40) {
json_add_string(js,
"warning_invoice_fallbacks_address_invalid",
"warning_invalid_invoice_fallbacks_address",
"invoice fallback address bad length");
valid = false;
} else if (chain) {
@@ -483,6 +592,82 @@ static bool json_add_fallbacks(struct json_stream *js,
return valid;
}
static void json_add_invoice_request(struct json_stream *js,
const struct tlv_invoice_request *invreq)
{
bool valid = true;
/* If there's an offer_node_id, then there's an offer. */
if (invreq->offer_node_id) {
struct sha256 offer_id;
invreq_offer_id(invreq, &offer_id);
json_add_sha256(js, "offer_id", &offer_id);
}
valid &= json_add_offer_fields(js,
invreq->offer_chains,
invreq->offer_metadata,
invreq->offer_currency,
invreq->offer_amount,
invreq->offer_description,
invreq->offer_features,
invreq->offer_absolute_expiry,
invreq->offer_paths,
invreq->offer_issuer,
invreq->offer_quantity_max,
invreq->offer_node_id,
invreq->offer_recurrence,
invreq->offer_recurrence_paywindow,
invreq->offer_recurrence_limit,
invreq->offer_recurrence_base);
valid &= json_add_invreq_fields(js,
invreq->invreq_metadata,
invreq->invreq_chain,
invreq->invreq_amount,
invreq->invreq_features,
invreq->invreq_quantity,
invreq->invreq_payer_id,
invreq->invreq_payer_note,
invreq->invreq_recurrence_counter,
invreq->invreq_recurrence_start);
/* BOLT-offers #12:
* - MUST fail the request if `invreq_payer_id` or `invreq_metadata` are not present.
*/
if (!invreq->invreq_payer_id) {
json_add_string(js, "warning_missing_invreq_payer_id",
"invreq_payer_id required");
valid = false;
}
/* BOLT-offers #12:
* - MUST fail the request if `signature` is not correct as detailed
* in [Signature Calculation](#signature-calculation) using the
* `invreq_payer_id`.
*/
if (invreq->signature) {
if (invreq->invreq_payer_id
&& !bolt12_check_signature(invreq->fields,
"invoice_request",
"signature",
invreq->invreq_payer_id,
invreq->signature)) {
json_add_string(js, "warning_invalid_invoice_request_signature",
"Bad signature");
valid = false;
} else {
json_add_bip340sig(js, "signature", invreq->signature);
}
} else {
json_add_string(js, "warning_missing_invoice_request_signature",
"Missing signature");
valid = false;
}
json_add_bool(js, "valid", valid);
}
static void json_add_b12_invoice(struct json_stream *js,
const struct tlv_invoice *invoice)
{
@@ -496,66 +681,32 @@ static void json_add_b12_invoice(struct json_stream *js,
json_add_sha256(js, "offer_id", &offer_id);
}
/* FIXME-OFFERS: Rename all fields to invoice_ as per spec */
if (invoice->invreq_chain)
json_add_sha256(js, "chain", &invoice->invreq_chain->shad.sha);
/* BOLT-offers #12:
* - MUST reject the invoice if `invoice_amount` is not present.
* - MUST reject the invoice if `invreq_payer_id` is not present.
*/
if (invoice->invoice_amount)
json_add_amount_msat_only(js, "amount_msat",
amount_msat(*invoice->invoice_amount));
else {
json_add_string(js, "warning_invoice_missing_amount",
"invoices without an amount are invalid");
valid = false;
}
if (invoice->invreq_payer_id)
json_add_pubkey(js, "payer_key", invoice->invreq_payer_id);
else {
json_add_string(js, "warning_invoice_missing_invreq_payer_id",
"invoices without an invreq_payer_id are invald");
valid = false;
}
/* BOLT-offers #12:
* - MUST reject the invoice if `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->offer_description)
json_add_stringn(js, "description", invoice->offer_description,
tal_bytelen(invoice->offer_description));
else {
json_add_string(js, "warning_invoice_missing_description",
"invoices without a description are invalid");
valid = false;
}
if (invoice->invoice_created_at) {
json_add_u64(js, "created_at", *invoice->invoice_created_at);
} else {
json_add_string(js, "warning_invoice_missing_created_at",
"invoices without created_at are invalid");
valid = false;
}
if (invoice->invoice_payment_hash)
json_add_sha256(js, "payment_hash", invoice->invoice_payment_hash);
else {
json_add_string(js, "warning_invoice_missing_payment_hash",
"invoices without a payment_hash are invalid");
valid = false;
}
if (invoice->offer_issuer)
json_add_stringn(js, "issuer", invoice->offer_issuer,
tal_bytelen(invoice->offer_issuer));
if (invoice->invoice_features)
json_add_hex_talarr(js, "features", invoice->invoice_features);
valid &= json_add_offer_fields(js,
invoice->offer_chains,
invoice->offer_metadata,
invoice->offer_currency,
invoice->offer_amount,
invoice->offer_description,
invoice->offer_features,
invoice->offer_absolute_expiry,
invoice->offer_paths,
invoice->offer_issuer,
invoice->offer_quantity_max,
invoice->offer_node_id,
invoice->offer_recurrence,
invoice->offer_recurrence_paywindow,
invoice->offer_recurrence_limit,
invoice->offer_recurrence_base);
valid &= json_add_invreq_fields(js,
invoice->invreq_metadata,
invoice->invreq_chain,
invoice->invreq_amount,
invoice->invreq_features,
invoice->invreq_quantity,
invoice->invreq_payer_id,
invoice->invreq_payer_note,
invoice->invreq_recurrence_counter,
invoice->invreq_recurrence_start);
/* BOLT-offers #12:
* - MUST reject the invoice if `invoice_paths` is not present
@@ -574,48 +725,27 @@ static void json_add_b12_invoice(struct json_stream *js,
* in `blinded_path`.
*/
if (!invoice->invoice_blindedpay) {
json_add_string(js, "warning_invoice_missing_blinded_payinfo",
"invoices with blinded_path without blinded_payinfo are invalid");
json_add_string(js, "warning_missing_invoice_blindedpay",
"invoices with paths without blindedpay are invalid");
valid = false;
}
valid &= json_add_blinded_paths(js, invoice->invoice_paths,
valid &= json_add_blinded_paths(js, "invoice_paths",
invoice->invoice_paths,
invoice->invoice_blindedpay);
} else {
json_add_string(js, "warning_invoice_missing_blinded_path",
"invoices without a payment_hash are invalid");
json_add_string(js, "warning_missing_invoice_paths",
"invoices without a invoice_paths are invalid");
valid = false;
}
if (invoice->invreq_quantity)
json_add_u64(js, "quantity", *invoice->invreq_quantity);
if (invoice->invreq_recurrence_counter) {
json_add_u32(js, "recurrence_counter",
*invoice->invreq_recurrence_counter);
if (invoice->invreq_recurrence_start)
json_add_u32(js, "recurrence_start",
*invoice->invreq_recurrence_start);
/* BOLT-offers-recurrence #12:
* - if the offer contained `recurrence`:
* - MUST reject the invoice if `recurrence_basetime` is not
* set.
*/
if (invoice->invoice_recurrence_basetime)
json_add_u64(js, "recurrence_basetime",
*invoice->invoice_recurrence_basetime);
else {
json_add_string(js, "warning_invoice_missing_recurrence_basetime",
"recurring invoices without a recurrence_basetime are invalid");
valid = false;
}
if (invoice->invoice_created_at) {
json_add_u64(js, "invoice_created_at", *invoice->invoice_created_at);
} else {
json_add_string(js, "warning_missing_invoice_created_at",
"invoices without created_at are invalid");
valid = false;
}
if (invoice->invreq_metadata)
json_add_hex_talarr(js, "invreq_metadata",
invoice->invreq_metadata);
if (invoice->invreq_payer_note)
json_add_stringn(js, "payer_note", invoice->invreq_payer_note,
tal_bytelen(invoice->invreq_payer_note));
/* BOLT-offers #12:
*
* - if `invoice_relative_expiry` is present:
@@ -626,104 +756,65 @@ static void json_add_b12_invoice(struct json_stream *js,
* is greater than `invoice_created_at` plus 7200.
*/
if (invoice->invoice_relative_expiry)
json_add_u32(js, "relative_expiry", *invoice->invoice_relative_expiry);
json_add_u32(js, "invoice_relative_expiry", *invoice->invoice_relative_expiry);
else
json_add_u32(js, "relative_expiry", 7200);
json_add_u32(js, "invoice_relative_expiry", BOLT12_DEFAULT_REL_EXPIRY);
if (invoice->invoice_payment_hash)
json_add_sha256(js, "invoice_payment_hash", invoice->invoice_payment_hash);
else {
json_add_string(js, "warning_missing_invoice_payment_hash",
"invoices without a payment_hash are invalid");
valid = false;
}
/* BOLT-offers #12:
* - MUST reject the invoice if `invoice_amount` is not present.
*/
if (invoice->invoice_amount)
json_add_amount_msat_only(js, "invoice_amount_msat",
amount_msat(*invoice->invoice_amount));
else {
json_add_string(js, "warning_missing_invoice_amount",
"invoices without an amount are invalid");
valid = false;
}
if (invoice->invoice_fallbacks)
valid &= json_add_fallbacks(js,
invoice->invreq_chain,
invoice->invoice_fallbacks);
/* invoice_decode checked these */
json_add_pubkey(js, "node_id", invoice->offer_node_id);
json_add_bip340sig(js, "signature", invoice->signature);
if (invoice->invoice_features)
json_add_hex_talarr(js, "features", invoice->invoice_features);
json_add_bool(js, "valid", valid);
}
static void json_add_invoice_request(struct json_stream *js,
const struct tlv_invoice_request *invreq)
{
bool valid = true;
/* If there's an offer_node_id, then there's an offer. */
if (invreq->offer_node_id) {
struct sha256 offer_id;
invreq_offer_id(invreq, &offer_id);
json_add_sha256(js, "offer_id", &offer_id);
}
/* FIXME-OFFERS: Rename all fields to invreq_ as per spec */
if (invreq->invreq_chain)
json_add_sha256(js, "chain", &invreq->invreq_chain->shad.sha);
/* BOLT-offers #12:
* - MUST fail the request if `payer_key` is not present.
*...
* - MUST fail the request if `features` contains unknown even bits.
* - MUST fail the request if `offer_id` is not present.
*/
if (invreq->invreq_amount)
json_add_amount_msat_only(js, "amount_msat",
amount_msat(*invreq->invreq_amount));
if (invreq->invreq_features)
json_add_hex_talarr(js, "features", invreq->invreq_features);
if (invreq->invreq_quantity)
json_add_u64(js, "quantity", *invreq->invreq_quantity);
if (invreq->invreq_recurrence_counter)
json_add_u32(js, "recurrence_counter",
*invreq->invreq_recurrence_counter);
if (invreq->invreq_recurrence_start)
json_add_u32(js, "recurrence_start",
*invreq->invreq_recurrence_start);
/* BOLT-offers #12:
* - MUST fail the request if `invreq_payer_id` or `invreq_metadata`
* are not present.
*/
if (invreq->invreq_payer_id)
json_add_pubkey(js, "payer_key", invreq->invreq_payer_id);
if (invoice->invoice_node_id)
json_add_pubkey(js, "invoice_node_id", invoice->invoice_node_id);
else {
json_add_string(js, "warning_invoice_request_missing_payer_key",
"invoice_request requires payer_key");
valid = false;
}
if (invreq->invreq_metadata)
json_add_hex_talarr(js, "invreq_metadata", invreq->invreq_metadata);
else {
json_add_string(js, "warning_invoice_request_missing_invreq_metadata",
"invoice_request requires invreq_metadata");
json_add_string(js, "warning_missing_invoice_node_id",
"invoices without an invoice_node_id are invalid");
valid = false;
}
if (invreq->invreq_payer_note)
json_add_stringn(js, "payer_note", invreq->invreq_payer_note,
tal_bytelen(invreq->invreq_payer_note));
/* BOLT-offers #12:
* - MUST fail the request if `signature` is not correct as detailed
* in [Signature Calculation](#signature-calculation) using the
* `invreq_payer_id`.
/* BOLT-offers-recurrence #12:
* - if the offer contained `recurrence`:
* - MUST reject the invoice if `recurrence_basetime` is not
* set.
*/
if (invreq->signature) {
if (invreq->invreq_payer_id
&& !bolt12_check_signature(invreq->fields,
"invoice_request",
"signature",
invreq->invreq_payer_id,
invreq->signature)) {
json_add_string(js, "warning_invoice_request_invalid_signature",
"Bad signature");
if (invoice->offer_recurrence) {
if (invoice->invoice_recurrence_basetime)
json_add_u64(js, "invoice_recurrence_basetime",
*invoice->invoice_recurrence_basetime);
else {
json_add_string(js, "warning_missing_invoice_recurrence_basetime",
"recurring invoices without a recurrence_basetime are invalid");
valid = false;
}
} else {
json_add_string(js, "warning_invoice_request_missing_signature",
"Missing signature");
valid = false;
}
/* invoice_decode checked this */
json_add_bip340sig(js, "signature", invoice->signature);
json_add_bool(js, "valid", valid);
}