decode: always return "valid" field.

Otherwise our schema is pretty meaningless, since invalid decodes
can have missing "required" fields.

Also fix a typo "blinded_payindo".

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-Experimental: JSON-RPC: `decode` now gives a `valid` boolean (it does partial decodes of some invalid data).
This commit is contained in:
Rusty Russell 2021-05-26 15:16:01 +09:30
parent fc9b24a746
commit 8a67c4a1ba

View File

@ -224,7 +224,8 @@ static void json_add_onionmsg_path(struct json_stream *js,
json_object_end(js); json_object_end(js);
} }
static void json_add_blinded_paths(struct json_stream *js, /* Returns true if valid */
static bool json_add_blinded_paths(struct json_stream *js,
struct blinded_path **paths, struct blinded_path **paths,
struct blinded_payinfo **blindedpay) struct blinded_payinfo **blindedpay)
{ {
@ -250,9 +251,13 @@ static void json_add_blinded_paths(struct json_stream *js,
* exactly as many `payinfo` as total `onionmsg_path` in * exactly as many `payinfo` as total `onionmsg_path` in
* `blinded_path`. * `blinded_path`.
*/ */
if (blindedpay && n != tal_count(blindedpay)) if (blindedpay && n != tal_count(blindedpay)) {
json_add_string(js, "warning_invoice_invalid_blinded_payinfo", json_add_string(js, "warning_invoice_invalid_blinded_payinfo",
"invoice does not have correct number of blinded_payinfo"); "invoice does not have correct number of blinded_payinfo");
return false;
}
return true;
} }
static const char *recurrence_time_unit_name(u8 time_unit) static const char *recurrence_time_unit_name(u8 time_unit)
@ -276,6 +281,7 @@ static const char *recurrence_time_unit_name(u8 time_unit)
static void json_add_offer(struct json_stream *js, const struct tlv_offer *offer) 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;
merkle_tlv(offer->fields, &offer_id); merkle_tlv(offer->fields, &offer_id);
json_add_sha256(js, "offer_id", &offer_id); json_add_sha256(js, "offer_id", &offer_id);
@ -312,9 +318,11 @@ static void json_add_offer(struct json_stream *js, const struct tlv_offer *offer
json_add_stringn(js, "description", json_add_stringn(js, "description",
offer->description, offer->description,
tal_bytelen(offer->description)); tal_bytelen(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;
}
if (offer->vendor) if (offer->vendor)
json_add_stringn(js, "vendor", offer->vendor, json_add_stringn(js, "vendor", offer->vendor,
@ -325,7 +333,7 @@ static void json_add_offer(struct json_stream *js, const struct tlv_offer *offer
json_add_u64(js, "absolute_expiry", json_add_u64(js, "absolute_expiry",
*offer->absolute_expiry); *offer->absolute_expiry);
if (offer->paths) if (offer->paths)
json_add_blinded_paths(js, offer->paths, NULL); valid &= json_add_blinded_paths(js, offer->paths, NULL);
if (offer->quantity_min) if (offer->quantity_min)
json_add_u64(js, "quantity_min", *offer->quantity_min); json_add_u64(js, "quantity_min", *offer->quantity_min);
@ -363,9 +371,12 @@ static void json_add_offer(struct json_stream *js, const struct tlv_offer *offer
/* offer_decode fails if node_id or signature not set */ /* offer_decode fails if node_id or signature not set */
json_add_pubkey32(js, "node_id", offer->node_id); json_add_pubkey32(js, "node_id", offer->node_id);
json_add_bip340sig(js, "signature", offer->signature); json_add_bip340sig(js, "signature", offer->signature);
json_add_bool(js, "valid", valid);
} }
static void json_add_fallback_address(struct json_stream *js, /* Returns true if valid */
static bool json_add_fallback_address(struct json_stream *js,
const struct chainparams *chain, const struct chainparams *chain,
u8 version, const u8 *address) u8 version, const u8 *address)
{ {
@ -373,19 +384,23 @@ static void json_add_fallback_address(struct json_stream *js,
/* Does extra checks, in particular checks v0 sizes */ /* Does extra checks, in particular checks v0 sizes */
if (segwit_addr_encode(out, chain->bip173_name, version, if (segwit_addr_encode(out, chain->bip173_name, version,
address, tal_bytelen(address))) address, tal_bytelen(address))) {
json_add_string(js, "address", out); json_add_string(js, "address", out);
else return true;
json_add_string(js, }
"warning_invoice_fallbacks_address_invalid", json_add_string(js,
"invalid fallback address for this version"); "warning_invoice_fallbacks_address_invalid",
"invalid fallback address for this version");
return false;
} }
static void json_add_fallbacks(struct json_stream *js, /* Returns true if valid */
static bool json_add_fallbacks(struct json_stream *js,
const struct bitcoin_blkid *chains, const struct bitcoin_blkid *chains,
struct fallback_address **fallbacks) struct fallback_address **fallbacks)
{ {
const struct chainparams *chain; const struct chainparams *chain;
bool valid = true;
/* Present address as first chain mentioned. */ /* Present address as first chain mentioned. */
if (tal_count(chains) != 0) if (tal_count(chains) != 0)
@ -414,23 +429,29 @@ static void json_add_fallbacks(struct json_stream *js,
json_add_string(js, json_add_string(js,
"warning_invoice_fallbacks_version_invalid", "warning_invoice_fallbacks_version_invalid",
"invoice fallback version > 16"); "invoice fallback version > 16");
valid = false;
} else if (addrlen < 2 || addrlen > 40) { } else if (addrlen < 2 || addrlen > 40) {
json_add_string(js, json_add_string(js,
"warning_invoice_fallbacks_address_invalid", "warning_invoice_fallbacks_address_invalid",
"invoice fallback address bad length"); "invoice fallback address bad length");
valid = false;
} else if (chain) { } else if (chain) {
json_add_fallback_address(js, chain, valid &= json_add_fallback_address(js, chain,
fallbacks[i]->version, fallbacks[i]->version,
fallbacks[i]->address); fallbacks[i]->address);
} }
json_object_end(js); json_object_end(js);
} }
json_array_end(js); json_array_end(js);
return valid;
} }
static void json_add_b12_invoice(struct json_stream *js, static void json_add_b12_invoice(struct json_stream *js,
const struct tlv_invoice *invoice) const struct tlv_invoice *invoice)
{ {
bool valid = true;
if (invoice->chains) if (invoice->chains)
json_add_chains(js, invoice->chains); json_add_chains(js, invoice->chains);
if (invoice->offer_id) if (invoice->offer_id)
@ -442,9 +463,11 @@ static void json_add_b12_invoice(struct json_stream *js,
if (invoice->amount) if (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->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;
}
/* BOLT-offers #12: /* BOLT-offers #12:
* - MUST reject the invoice if `description` is not present. * - MUST reject the invoice if `description` is not present.
@ -452,9 +475,12 @@ static void json_add_b12_invoice(struct json_stream *js,
if (invoice->description) if (invoice->description)
json_add_stringn(js, "description", invoice->description, json_add_stringn(js, "description", invoice->description,
tal_bytelen(invoice->description)); tal_bytelen(invoice->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;
}
if (invoice->vendor) if (invoice->vendor)
json_add_stringn(js, "vendor", invoice->vendor, json_add_stringn(js, "vendor", invoice->vendor,
tal_bytelen(invoice->vendor)); tal_bytelen(invoice->vendor));
@ -469,10 +495,12 @@ 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->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_payindo are invalid"); "invoices with blinded_path without blinded_payinfo are invalid");
json_add_blinded_paths(js, invoice->paths, invoice->blindedpay); valid = false;
}
valid &= json_add_blinded_paths(js, invoice->paths, invoice->blindedpay);
} }
if (invoice->quantity) if (invoice->quantity)
json_add_u64(js, "quantity", *invoice->quantity); json_add_u64(js, "quantity", *invoice->quantity);
@ -494,9 +522,11 @@ static void json_add_b12_invoice(struct json_stream *js,
if (invoice->recurrence_basetime) if (invoice->recurrence_basetime)
json_add_u64(js, "recurrence_basetime", json_add_u64(js, "recurrence_basetime",
*invoice->recurrence_basetime); *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");
valid = false;
}
} }
if (invoice->payer_key) if (invoice->payer_key)
@ -509,18 +539,22 @@ static void json_add_b12_invoice(struct json_stream *js,
*/ */
if (invoice->timestamp) if (invoice->timestamp)
json_add_u64(js, "timestamp", *invoice->timestamp); json_add_u64(js, "timestamp", *invoice->timestamp);
else else {
json_add_string(js, "warning_invoice_missing_timestamp", json_add_string(js, "warning_invoice_missing_timestamp",
"invoices without a timestamp are invalid"); "invoices without a timestamp are invalid");
valid = false;
}
/* BOLT-offers #12: /* BOLT-offers #12:
* - MUST reject the invoice if `payment_hash` is not present. * - MUST reject the invoice if `payment_hash` is not present.
*/ */
if (invoice->payment_hash) if (invoice->payment_hash)
json_add_sha256(js, "payment_hash", invoice->payment_hash); json_add_sha256(js, "payment_hash", invoice->payment_hash);
else else {
json_add_string(js, "warning_invoice_missing_payment_hash", json_add_string(js, "warning_invoice_missing_payment_hash",
"invoices without a payment_hash are invalid"); "invoices without a payment_hash are invalid");
valid = false;
}
/* BOLT-offers #12: /* BOLT-offers #12:
* *
@ -544,8 +578,8 @@ static void json_add_b12_invoice(struct json_stream *js,
json_add_u32(js, "min_final_cltv_expiry", 18); json_add_u32(js, "min_final_cltv_expiry", 18);
if (invoice->fallbacks) if (invoice->fallbacks)
json_add_fallbacks(js, invoice->chains, valid &= json_add_fallbacks(js, invoice->chains,
invoice->fallbacks->fallbacks); invoice->fallbacks->fallbacks);
/* BOLT-offers #12: /* BOLT-offers #12:
* - if the offer contained `refund_for`: * - if the offer contained `refund_for`:
@ -560,28 +594,37 @@ static void json_add_b12_invoice(struct json_stream *js,
if (invoice->refund_signature) { if (invoice->refund_signature) {
json_add_bip340sig(js, "refund_signature", json_add_bip340sig(js, "refund_signature",
invoice->refund_signature); invoice->refund_signature);
if (!invoice->payer_key) if (!invoice->payer_key) {
json_add_string(js, "warning_invoice_refund_signature_missing_payer_key", json_add_string(js, "warning_invoice_refund_signature_missing_payer_key",
"Can't have refund_signature without payer key"); "Can't have refund_signature without payer key");
else if (!bolt12_check_signature(invoice->fields, valid = false;
"invoice", } else if (!bolt12_check_signature(invoice->fields,
"refund_signature", "invoice",
invoice->payer_key, "refund_signature",
invoice->refund_signature)) invoice->payer_key,
invoice->refund_signature)) {
json_add_string(js, "warning_invoice_refund_signature_invalid", json_add_string(js, "warning_invoice_refund_signature_invalid",
"refund_signature does not match"); "refund_signature does not match");
} else if (invoice->refund_for) valid = false;
}
} else if (invoice->refund_for) {
json_add_string(js, "warning_invoice_refund_missing_signature", json_add_string(js, "warning_invoice_refund_missing_signature",
"refund_for requires refund_signature"); "refund_for requires refund_signature");
valid = false;
}
/* invoice_decode checked these */ /* invoice_decode checked these */
json_add_pubkey32(js, "node_id", invoice->node_id); json_add_pubkey32(js, "node_id", invoice->node_id);
json_add_bip340sig(js, "signature", invoice->signature); json_add_bip340sig(js, "signature", invoice->signature);
json_add_bool(js, "valid", valid);
} }
static void json_add_invoice_request(struct json_stream *js, static void json_add_invoice_request(struct json_stream *js,
const struct tlv_invoice_request *invreq) const struct tlv_invoice_request *invreq)
{ {
bool valid = true;
if (invreq->chains) if (invreq->chains)
json_add_chains(js, invreq->chains); json_add_chains(js, invreq->chains);
/* BOLT-offers #12: /* BOLT-offers #12:
@ -592,9 +635,11 @@ static void json_add_invoice_request(struct json_stream *js,
*/ */
if (invreq->offer_id) if (invreq->offer_id)
json_add_sha256(js, "offer_id", invreq->offer_id); json_add_sha256(js, "offer_id", invreq->offer_id);
else else {
json_add_string(js, "warning_invoice_request_missing_offer_id", json_add_string(js, "warning_invoice_request_missing_offer_id",
"invoice_request requires offer_id"); "invoice_request requires offer_id");
valid = false;
}
if (invreq->amount) 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->amount));
@ -611,9 +656,11 @@ static void json_add_invoice_request(struct json_stream *js,
*invreq->recurrence_start); *invreq->recurrence_start);
if (invreq->payer_key) if (invreq->payer_key)
json_add_pubkey32(js, "payer_key", invreq->payer_key); json_add_pubkey32(js, "payer_key", invreq->payer_key);
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;
}
if (invreq->payer_info) if (invreq->payer_info)
json_add_hex_talarr(js, "payer_info", invreq->payer_info); json_add_hex_talarr(js, "payer_info", invreq->payer_info);
@ -631,13 +678,18 @@ static void json_add_invoice_request(struct json_stream *js,
"invoice_request", "invoice_request",
"recurrence_signature", "recurrence_signature",
invreq->payer_key, invreq->payer_key,
invreq->recurrence_signature)) invreq->recurrence_signature)) {
json_add_string(js, "warning_invoice_request_invalid_recurrence_signature", json_add_string(js, "warning_invoice_request_invalid_recurrence_signature",
"Bad recurrence_signature"); "Bad recurrence_signature");
valid = false;
}
} else if (invreq->recurrence_counter) { } else if (invreq->recurrence_counter) {
json_add_string(js, "warning_invoice_request_missing_recurrence_signature", json_add_string(js, "warning_invoice_request_missing_recurrence_signature",
"invoice_request requires recurrence_signature"); "invoice_request requires recurrence_signature");
valid = false;
} }
json_add_bool(js, "valid", valid);
} }
static struct command_result *json_decode(struct command *cmd, static struct command_result *json_decode(struct command *cmd,
@ -660,8 +712,11 @@ static struct command_result *json_decode(struct command *cmd,
json_add_invoice_request(response, decodable->invreq); json_add_invoice_request(response, decodable->invreq);
if (decodable->invoice) if (decodable->invoice)
json_add_b12_invoice(response, decodable->invoice); json_add_b12_invoice(response, decodable->invoice);
if (decodable->b11) if (decodable->b11) {
/* The bolt11 decoder simply refuses to decode bad invs. */
json_add_bolt11(response, decodable->b11); json_add_bolt11(response, decodable->b11);
json_add_bool(response, "valid", true);
}
return command_finished(cmd, response); return command_finished(cmd, response);
} }