BOLT 12: switch invoice_request/invoice to singular chain field.

We keep the now-removed chains field, and in deprecated mode, we set it.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-EXPERIMENTAL: bolt12: `chains` in invoice_request and invoice is deprecated, `chain` is used instead.
This commit is contained in:
Rusty Russell
2021-10-08 13:57:29 +10:30
committed by Christian Decker
parent 45bf7a3974
commit c92ce59892
13 changed files with 98 additions and 41 deletions

View File

@@ -3,6 +3,7 @@
#include <common/bech32_util.h> #include <common/bech32_util.h>
#include <common/bolt12.h> #include <common/bolt12.h>
#include <common/bolt12_merkle.h> #include <common/bolt12_merkle.h>
#include <common/configdir.h>
#include <common/features.h> #include <common/features.h>
#include <secp256k1_schnorrsig.h> #include <secp256k1_schnorrsig.h>
#include <time.h> #include <time.h>
@@ -46,6 +47,21 @@ bool bolt12_chains_match(const struct bitcoin_blkid *chains,
return false; return false;
} }
bool bolt12_chain_matches(const struct bitcoin_blkid *chain,
const struct chainparams *must_be_chain,
const struct bitcoin_blkid *deprecated_chains)
{
/* Obsolete: We used to put an array in here, but we only ever
* used a single value */
if (deprecated_apis && !chain)
chain = deprecated_chains;
if (!chain)
chain = &chainparams_for_network("bitcoin")->genesis_blockhash;
return bitcoin_blkid_eq(chain, &must_be_chain->genesis_blockhash);
}
static char *check_features_and_chain(const tal_t *ctx, static char *check_features_and_chain(const tal_t *ctx,
const struct feature_set *our_features, const struct feature_set *our_features,
const struct chainparams *must_be_chain, const struct chainparams *must_be_chain,
@@ -231,7 +247,9 @@ struct tlv_invoice_request *invrequest_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,
invrequest->features, invrequest->features,
invrequest->chains); invrequest->chain
? invrequest->chain
: invrequest->chains);
if (*fail) if (*fail)
return tal_free(invrequest); return tal_free(invrequest);
@@ -270,7 +288,8 @@ 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->features,
invoice->chains); invoice->chain
? invoice->chain : invoice->chains);
if (*fail) if (*fail)
return tal_free(invoice); return tal_free(invoice);

View File

@@ -103,6 +103,11 @@ bool bolt12_check_signature(const struct tlv_field *fields,
bool bolt12_chains_match(const struct bitcoin_blkid *chains, bool bolt12_chains_match(const struct bitcoin_blkid *chains,
const struct chainparams *must_be_chain); const struct chainparams *must_be_chain);
/* Given a single bolt12 chain, does it match? (NULL == bitcoin) */
bool bolt12_chain_matches(const struct bitcoin_blkid *chain,
const struct chainparams *must_be_chain,
const struct bitcoin_blkid *deprecated_chains);
/* 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 tlv_offer_recurrence *recurrence);

View File

@@ -7,6 +7,8 @@
#include <ccan/tal/grab_file/grab_file.h> #include <ccan/tal/grab_file/grab_file.h>
#include <ccan/tal/path/path.h> #include <ccan/tal/path/path.h>
bool deprecated_apis = false;
/* AUTOGENERATED MOCKS START */ /* AUTOGENERATED MOCKS START */
/* Generated stub for amount_asset_is_main */ /* Generated stub for amount_asset_is_main */
bool amount_asset_is_main(struct amount_asset *asset UNNEEDED) bool amount_asset_is_main(struct amount_asset *asset UNNEEDED)

View File

@@ -12,6 +12,8 @@
/* Definition of n1 from the spec */ /* Definition of n1 from the spec */
#include <wire/peer_wire.h> #include <wire/peer_wire.h>
bool deprecated_apis = false;
/* AUTOGENERATED MOCKS START */ /* AUTOGENERATED MOCKS START */
/* Generated stub for features_unsupported */ /* Generated stub for features_unsupported */
int features_unsupported(const struct feature_set *our_features UNNEEDED, int features_unsupported(const struct feature_set *our_features UNNEEDED,

View File

@@ -5,6 +5,8 @@
#include <ccan/tal/path/path.h> #include <ccan/tal/path/path.h>
#include <common/setup.h> #include <common/setup.h>
bool deprecated_apis = false;
/* AUTOGENERATED MOCKS START */ /* AUTOGENERATED MOCKS START */
/* Generated stub for amount_asset_is_main */ /* Generated stub for amount_asset_is_main */
bool amount_asset_is_main(struct amount_asset *asset UNNEEDED) bool amount_asset_is_main(struct amount_asset *asset UNNEEDED)

View File

@@ -19,6 +19,7 @@
#define ERROR_USAGE 3 #define ERROR_USAGE 3
static bool well_formed = true; static bool well_formed = true;
bool deprecated_apis = true;
/* Tal wrappers for opt. */ /* Tal wrappers for opt. */
static void *opt_allocfn(size_t size) static void *opt_allocfn(size_t size)
@@ -72,6 +73,12 @@ static void print_chains(const struct bitcoin_blkid *chains)
printf("\n"); printf("\n");
} }
static void print_chain(const struct bitcoin_blkid *chain)
{
printf("chain: %s\n",
type_to_string(tmpctx, struct bitcoin_blkid, chain));
}
static bool print_amount(const struct bitcoin_blkid *chains, static bool print_amount(const struct bitcoin_blkid *chains,
const char *iso4217, u64 amount) const char *iso4217, u64 amount)
{ {
@@ -532,8 +539,8 @@ int main(int argc, char *argv[])
if (!invreq) if (!invreq)
errx(ERROR_BAD_DECODE, "Bad invoice_request: %s", fail); errx(ERROR_BAD_DECODE, "Bad invoice_request: %s", fail);
if (invreq->chains) if (invreq->chain)
print_chains(invreq->chains); print_chain(invreq->chain);
if (must_have(invreq, payer_key)) if (must_have(invreq, payer_key))
print_payer_key(invreq->payer_key, invreq->payer_info); print_payer_key(invreq->payer_key, invreq->payer_info);
if (invreq->payer_note) if (invreq->payer_note)
@@ -541,7 +548,7 @@ int main(int argc, char *argv[])
if (must_have(invreq, offer_id)) if (must_have(invreq, offer_id))
print_offer_id(invreq->offer_id); print_offer_id(invreq->offer_id);
if (must_have(invreq, amount)) if (must_have(invreq, amount))
well_formed &= print_amount(invreq->chains, well_formed &= print_amount(invreq->chain,
NULL, NULL,
*invreq->amount); *invreq->amount);
if (invreq->features) if (invreq->features)
@@ -569,14 +576,14 @@ 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->chains) if (invoice->chain)
print_chains(invoice->chains); print_chain(invoice->chain);
if (invoice->offer_id) { if (invoice->offer_id) {
print_offer_id(invoice->offer_id); print_offer_id(invoice->offer_id);
} }
if (must_have(invoice, amount)) if (must_have(invoice, amount))
well_formed &= print_amount(invoice->chains, well_formed &= print_amount(invoice->chain,
NULL, NULL,
*invoice->amount); *invoice->amount);
if (must_have(invoice, description)) if (must_have(invoice, description))

View File

@@ -282,15 +282,11 @@
"type": "bip340sig", "type": "bip340sig",
"description": "BIP-340 signature of the *node_id* on this offer" "description": "BIP-340 signature of the *node_id* on this offer"
}, },
"chains": { "chain": {
"type": "array", "type": "hex",
"description": "which blockchains this offer is for (missing implies bitcoin mainnet only)", "description": "which blockchain this invoice is for (missing implies bitcoin mainnet only)",
"items": { "maxLength": 64,
"type": "hex", "minLength": 64
"description": "the genesis blockhash",
"maxLength": 64,
"minLength": 64
}
}, },
"amount_msat": { "amount_msat": {
"type": "msat", "type": "msat",
@@ -450,7 +446,7 @@
"offer_id": { }, "offer_id": { },
"node_id": { }, "node_id": { },
"signature": { }, "signature": { },
"chains": { }, "chain": { },
"amount_msat": { }, "amount_msat": { },
"send_invoice": { }, "send_invoice": { },
"refund_for": { }, "refund_for": { },
@@ -551,19 +547,15 @@
"valid": { }, "valid": { },
"offer_id": { "offer_id": {
"type": "hex", "type": "hex",
"description": "the id of this offer (merkle hash of non-signature fields)", "description": "the id of the offer this is requesting (merkle hash of non-signature fields)",
"maxLength": 64, "maxLength": 64,
"minLength": 64 "minLength": 64
}, },
"chains": { "chain": {
"type": "array", "type": "hex",
"description": "which blockchains this offer is for (missing implies bitcoin mainnet only)", "description": "which blockchain this invoice_request is for (missing implies bitcoin mainnet only)",
"items": { "maxLength": 64,
"type": "hex", "minLength": 64
"description": "the genesis blockhash",
"maxLength": 64,
"minLength": 64
}
}, },
"amount_msat": { "amount_msat": {
"type": "msat", "type": "msat",
@@ -620,7 +612,7 @@
"type": { }, "type": { },
"valid": { }, "valid": { },
"offer_id": { }, "offer_id": { },
"chains": { }, "chain": { },
"amount_msat": { }, "amount_msat": { },
"features": { }, "features": { },
"quantity": { }, "quantity": { },

View File

@@ -1395,8 +1395,12 @@ static struct command_result *json_fetchinvoice(struct command *cmd,
* - the bitcoin chain is implied as the first and only entry. * - the bitcoin chain is implied as the first and only entry.
*/ */
if (!streq(chainparams->network_name, "bitcoin")) { if (!streq(chainparams->network_name, "bitcoin")) {
invreq->chains = tal_arr(invreq, struct bitcoin_blkid, 1); if (deprecated_apis) {
invreq->chains[0] = chainparams->genesis_blockhash; invreq->chains = tal_arr(invreq, struct bitcoin_blkid, 1);
invreq->chains[0] = chainparams->genesis_blockhash;
}
invreq->chain = tal_dup(invreq, struct bitcoin_blkid,
&chainparams->genesis_blockhash);
} }
invreq->features invreq->features
@@ -1790,8 +1794,12 @@ static struct command_result *json_sendinvoice(struct command *cmd,
* - the bitcoin chain is implied as the first and only entry. * - the bitcoin chain is implied as the first and only entry.
*/ */
if (!streq(chainparams->network_name, "bitcoin")) { if (!streq(chainparams->network_name, "bitcoin")) {
sent->inv->chains = tal_arr(sent->inv, struct bitcoin_blkid, 1); if (deprecated_apis) {
sent->inv->chains[0] = chainparams->genesis_blockhash; sent->inv->chains = tal_arr(sent->inv, struct bitcoin_blkid, 1);
sent->inv->chains[0] = chainparams->genesis_blockhash;
}
sent->inv->chain = tal_dup(sent->inv, struct bitcoin_blkid,
&chainparams->genesis_blockhash);
} }
sent->inv->features sent->inv->features

View File

@@ -565,6 +565,8 @@ static void json_add_b12_invoice(struct json_stream *js,
if (invoice->chains) if (invoice->chains)
json_add_chains(js, invoice->chains); json_add_chains(js, invoice->chains);
if (invoice->chain)
json_add_sha256(js, "chain", &invoice->chain->shad.sha);
if (invoice->offer_id) if (invoice->offer_id)
json_add_sha256(js, "offer_id", invoice->offer_id); json_add_sha256(js, "offer_id", invoice->offer_id);
@@ -700,7 +702,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)
valid &= json_add_fallbacks(js, invoice->chains, valid &= json_add_fallbacks(js,
invoice->chain ? invoice->chain : invoice->chains,
invoice->fallbacks->fallbacks); invoice->fallbacks->fallbacks);
/* BOLT-offers #12: /* BOLT-offers #12:
@@ -749,6 +752,9 @@ static void json_add_invoice_request(struct json_stream *js,
if (invreq->chains) if (invreq->chains)
json_add_chains(js, invreq->chains); json_add_chains(js, invreq->chains);
if (invreq->chain)
json_add_sha256(js, "chain", &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.
* - MUST fail the request if `chains` does not include (or imply) a supported chain. * - MUST fail the request if `chains` does not include (or imply) a supported chain.

View File

@@ -369,10 +369,10 @@ struct command_result *handle_invoice(struct command *cmd,
* - MUST fail the request if `chains` does not include (or imply) a * - MUST fail the request if `chains` does not include (or imply) a
* supported chain. * supported chain.
*/ */
if (!bolt12_chains_match(inv->inv->chains, chainparams)) { if (!bolt12_chain_matches(inv->inv->chain, chainparams, inv->inv->chains)) {
return fail_inv(cmd, inv, return fail_inv(cmd, inv,
"Wrong chains %s", "Wrong chains %s",
tal_hex(tmpctx, inv->inv->chains)); tal_hex(tmpctx, inv->inv->chain));
} }
/* BOLT-offers #12: /* BOLT-offers #12:

View File

@@ -758,9 +758,14 @@ static struct command_result *listoffers_done(struct command *cmd,
* - MUST specify `chains` the offer is valid for. * - MUST specify `chains` the offer is valid for.
*/ */
if (!streq(chainparams->network_name, "bitcoin")) { if (!streq(chainparams->network_name, "bitcoin")) {
ir->inv->chains = tal_arr(ir->inv, struct bitcoin_blkid, 1); if (deprecated_apis) {
ir->inv->chains[0] = chainparams->genesis_blockhash; ir->inv->chains = tal_arr(ir->inv, struct bitcoin_blkid, 1);
ir->inv->chains[0] = chainparams->genesis_blockhash;
}
ir->inv->chain = tal_dup(ir->inv, struct bitcoin_blkid,
&chainparams->genesis_blockhash);
} }
/* BOLT-offers #12: /* BOLT-offers #12:
* - MUST set `offer_id` to the id of the offer. * - MUST set `offer_id` to the id of the offer.
*/ */
@@ -883,10 +888,11 @@ struct command_result *handle_invoice_request(struct command *cmd,
* - MUST fail the request if `chains` does not include (or imply) a * - MUST fail the request if `chains` does not include (or imply) a
* supported chain. * supported chain.
*/ */
if (!bolt12_chains_match(ir->invreq->chains, chainparams)) { if (!bolt12_chain_matches(ir->invreq->chain, chainparams,
ir->invreq->chains)) {
return fail_invreq(cmd, ir, return fail_invreq(cmd, ir,
"Wrong chains %s", "Wrong chain %s",
tal_hex(tmpctx, ir->invreq->chains)); tal_hex(tmpctx, ir->invreq->chain));
} }
/* BOLT-offers #12: /* BOLT-offers #12:

View File

@@ -44,6 +44,8 @@ subtypedata,blinded_path,num_hops,byte,
subtypedata,blinded_path,path,onionmsg_path,num_hops subtypedata,blinded_path,path,onionmsg_path,num_hops
tlvtype,invoice_request,chains,2 tlvtype,invoice_request,chains,2
tlvdata,invoice_request,chains,chains,chain_hash,... tlvdata,invoice_request,chains,chains,chain_hash,...
tlvtype,invoice_request,chain,3
tlvdata,invoice_request,chain,chain,chain_hash,
tlvtype,invoice_request,offer_id,4 tlvtype,invoice_request,offer_id,4
tlvdata,invoice_request,offer_id,offer_id,sha256, tlvdata,invoice_request,offer_id,offer_id,sha256,
tlvtype,invoice_request,amount,8 tlvtype,invoice_request,amount,8
@@ -68,6 +70,8 @@ tlvtype,invoice_request,payer_signature,240
tlvdata,invoice_request,payer_signature,sig,bip340sig, tlvdata,invoice_request,payer_signature,sig,bip340sig,
tlvtype,invoice,chains,2 tlvtype,invoice,chains,2
tlvdata,invoice,chains,chains,chain_hash,... tlvdata,invoice,chains,chains,chain_hash,...
tlvtype,invoice,chain,3
tlvdata,invoice,chain,chain,chain_hash,
tlvtype,invoice,offer_id,4 tlvtype,invoice,offer_id,4
tlvdata,invoice,offer_id,offer_id,sha256, tlvdata,invoice,offer_id,offer_id,sha256,
tlvtype,invoice,amount,8 tlvtype,invoice,amount,8
1 tlvtype,offer,chains,2
44 subtypedata,blinded_path,path,onionmsg_path,num_hops
45 tlvtype,invoice_request,chains,2
46 tlvdata,invoice_request,chains,chains,chain_hash,...
47 tlvtype,invoice_request,chain,3
48 tlvdata,invoice_request,chain,chain,chain_hash,
49 tlvtype,invoice_request,offer_id,4
50 tlvdata,invoice_request,offer_id,offer_id,sha256,
51 tlvtype,invoice_request,amount,8
70 tlvdata,invoice_request,payer_signature,sig,bip340sig,
71 tlvtype,invoice,chains,2
72 tlvdata,invoice,chains,chains,chain_hash,...
73 tlvtype,invoice,chain,3
74 tlvdata,invoice,chain,chain,chain_hash,
75 tlvtype,invoice,offer_id,4
76 tlvdata,invoice,offer_id,offer_id,sha256,
77 tlvtype,invoice,amount,8

View File

@@ -44,6 +44,8 @@ subtypedata,blinded_path,num_hops,byte,
subtypedata,blinded_path,path,onionmsg_path,num_hops subtypedata,blinded_path,path,onionmsg_path,num_hops
tlvtype,invoice_request,chains,2 tlvtype,invoice_request,chains,2
tlvdata,invoice_request,chains,chains,chain_hash,... tlvdata,invoice_request,chains,chains,chain_hash,...
tlvtype,invoice_request,chain,3
tlvdata,invoice_request,chain,chain,chain_hash,
tlvtype,invoice_request,offer_id,4 tlvtype,invoice_request,offer_id,4
tlvdata,invoice_request,offer_id,offer_id,sha256, tlvdata,invoice_request,offer_id,offer_id,sha256,
tlvtype,invoice_request,amount,8 tlvtype,invoice_request,amount,8
@@ -68,6 +70,8 @@ tlvtype,invoice_request,payer_signature,240
tlvdata,invoice_request,payer_signature,sig,bip340sig, tlvdata,invoice_request,payer_signature,sig,bip340sig,
tlvtype,invoice,chains,2 tlvtype,invoice,chains,2
tlvdata,invoice,chains,chains,chain_hash,... tlvdata,invoice,chains,chains,chain_hash,...
tlvtype,invoice,chain,3
tlvdata,invoice,chain,chain,chain_hash,
tlvtype,invoice,offer_id,4 tlvtype,invoice,offer_id,4
tlvdata,invoice,offer_id,offer_id,sha256, tlvdata,invoice,offer_id,offer_id,sha256,
tlvtype,invoice,amount,8 tlvtype,invoice,amount,8
1 tlvtype,offer,chains,2
44 subtypedata,blinded_path,path,onionmsg_path,num_hops
45 tlvtype,invoice_request,chains,2
46 tlvdata,invoice_request,chains,chains,chain_hash,...
47 tlvtype,invoice_request,chain,3
48 tlvdata,invoice_request,chain,chain,chain_hash,
49 tlvtype,invoice_request,offer_id,4
50 tlvdata,invoice_request,offer_id,offer_id,sha256,
51 tlvtype,invoice_request,amount,8
70 tlvdata,invoice_request,payer_signature,sig,bip340sig,
71 tlvtype,invoice,chains,2
72 tlvdata,invoice,chains,chains,chain_hash,...
73 tlvtype,invoice,chain,3
74 tlvdata,invoice,chain,chain,chain_hash,
75 tlvtype,invoice,offer_id,4
76 tlvdata,invoice,offer_id,offer_id,sha256,
77 tlvtype,invoice,amount,8