BOLTs: update to more recent bolt12 spec.

It's 2b7ad577d7a790b302bd1aa044b22c809c76e49d, which reverts the
point32 changes.

It also restores send_invoice in `invoice`, which we had removed
from spec and put into the recurrence patch.

I originally had implemented compatibility, but other changes
which followed this are far too widespread.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Changelog-EXPERIMENTAL: offers: complete rework of spec from other teams (yay!) breaks previous compatibility (boo!)
This commit is contained in:
Rusty Russell
2022-10-17 11:07:05 +10:30
parent eac8401f84
commit e30ea91908
17 changed files with 89 additions and 193 deletions

View File

@@ -204,7 +204,7 @@ static struct command_result *handle_invreq_response(struct command *cmd,
/* BOLT-offers #12:
* - MUST reject the invoice unless `node_id` is equal to the offer.
*/
if (!point32_eq(sent->offer->node_id, inv->node_id)) {
if (!pubkey_eq(sent->offer->node_id, inv->node_id)) {
badfield = "node_id";
goto badinv;
}
@@ -556,41 +556,9 @@ static bool can_carry_onionmsg(const struct gossmap *map,
|| gossmap_node_get_feature(map, n, 102) != -1;
}
enum nodeid_parity {
nodeid_parity_even = SECP256K1_TAG_PUBKEY_EVEN,
nodeid_parity_odd = SECP256K1_TAG_PUBKEY_ODD,
nodeid_parity_unknown = 1,
};
static enum nodeid_parity node_parity(const struct gossmap *gossmap,
const struct gossmap_node *node)
{
struct node_id id;
gossmap_node_get_id(gossmap, node, &id);
return id.k[0];
}
static void node_id_from_point32(struct node_id *nid,
const struct point32 *node32_id,
enum nodeid_parity parity)
{
struct pubkey pk;
assert(parity == SECP256K1_TAG_PUBKEY_EVEN
|| parity == SECP256K1_TAG_PUBKEY_ODD);
pk.pubkey = node32_id->pubkey;
node_id_from_pubkey(nid, &pk);
nid->k[0] = parity;
}
/* Create path to node which can carry onion messages (including
* self); if it can't find one, returns NULL. Fills in nodeid_parity
* for 33rd nodeid byte. */
static struct pubkey *path_to_node(const tal_t *ctx,
struct plugin *plugin,
const struct point32 *node32_id,
enum nodeid_parity *parity)
const struct pubkey *node_id)
{
struct route_hop *r;
const struct dijkstra *dij;
@@ -600,21 +568,10 @@ static struct pubkey *path_to_node(const tal_t *ctx,
struct pubkey *nodes;
struct gossmap *gossmap = get_gossmap(plugin);
/* We try both parities. */
*parity = nodeid_parity_even;
node_id_from_point32(&dstid, node32_id, *parity);
node_id_from_pubkey(&dstid, node_id);
dst = gossmap_find_node(gossmap, &dstid);
if (!dst) {
*parity = nodeid_parity_odd;
node_id_from_point32(&dstid, node32_id, *parity);
dst = gossmap_find_node(gossmap, &dstid);
if (!dst) {
*parity = nodeid_parity_unknown;
return NULL;
}
}
*parity = node_parity(gossmap, dst);
if (!dst)
return NULL;
/* If we don't exist in gossip, routing can't happen. */
node_id_from_pubkey(&local_nodeid, &local_id);
@@ -857,41 +814,11 @@ static struct command_result *connect_failed(struct command *command,
NULL);
}
/* Offers contain only a 32-byte id. If we can't find the address, we
* don't know if it's 02 or 03, so we try both. If we're here, we
* failed 02. */
static struct command_result *try_other_parity(struct command *cmd,
const char *buf,
const jsmntok_t *result,
struct connect_attempt *ca)
{
struct out_req *req;
/* Flip parity */
ca->node_id.k[0] = SECP256K1_TAG_PUBKEY_ODD;
/* Path is us -> them, so they're second entry */
if (!pubkey_from_node_id(&ca->sent->path[1], &ca->node_id)) {
/* Should not happen!
* Pieter Wuille points out:
* y^2 = x^3 + 7 mod p
* negating y doesnt change the left hand side
*/
return command_done_err(cmd, LIGHTNINGD,
"Failed: could not convert inverted pubkey?",
NULL);
}
req = jsonrpc_request_start(cmd->plugin, cmd, "connect", connected,
connect_failed, ca);
json_add_node_id(req->js, "id", &ca->node_id);
return send_outreq(cmd->plugin, req);
}
/* We can't find a route, so we're going to try to connect, then just blast it
* to them. */
static struct command_result *
connect_direct(struct command *cmd,
const struct point32 *dst,
enum nodeid_parity parity,
const struct pubkey *dst,
struct command_result *(*cb)(struct command *command,
const char *buf,
const jsmntok_t *result,
@@ -903,20 +830,7 @@ connect_direct(struct command *cmd,
ca->cb = cb;
ca->sent = sent;
if (parity == nodeid_parity_unknown) {
plugin_notify_message(cmd, LOG_INFORM,
"Cannot find route, trying connect to 02/03%s directly",
type_to_string(tmpctx, struct point32, dst));
/* Try even first. */
node_id_from_point32(&ca->node_id, dst, SECP256K1_TAG_PUBKEY_EVEN);
} else {
plugin_notify_message(cmd, LOG_INFORM,
"Cannot find route, trying connect to %02x%s directly",
parity,
type_to_string(tmpctx, struct point32, dst));
node_id_from_point32(&ca->node_id, dst, parity);
}
node_id_from_pubkey(&ca->node_id, dst);
/* Make a direct path -> dst. */
sent->path = tal_arr(sent, struct pubkey, 2);
@@ -934,14 +848,13 @@ connect_direct(struct command *cmd,
"Cannot find route, but"
" fetchplugin-noconnect set:"
" trying direct anyway to %s",
type_to_string(tmpctx, struct point32,
type_to_string(tmpctx, struct pubkey,
dst));
return cb(cmd, NULL, NULL, sent);
}
req = jsonrpc_request_start(cmd->plugin, cmd, "connect", connected,
parity == nodeid_parity_unknown ?
try_other_parity : connect_failed, ca);
connect_failed, ca);
json_add_node_id(req->js, "id", &ca->node_id);
return send_outreq(cmd->plugin, req);
}
@@ -953,7 +866,6 @@ static struct command_result *invreq_done(struct command *cmd,
{
const jsmntok_t *t;
char *fail;
enum nodeid_parity parity;
/* Get invoice request */
t = json_get_member(buf, result, "bolt12");
@@ -1047,10 +959,9 @@ static struct command_result *invreq_done(struct command *cmd,
}
sent->path = path_to_node(sent, cmd->plugin,
sent->offer->node_id,
&parity);
sent->offer->node_id);
if (!sent->path)
return connect_direct(cmd, sent->offer->node_id, parity,
return connect_direct(cmd, sent->offer->node_id,
sendinvreq_after_connect, sent);
return sendinvreq_after_connect(cmd, NULL, NULL, sent);
@@ -1065,7 +976,6 @@ force_payer_secret(struct command *cmd,
const struct secret *payer_secret)
{
struct sha256 merkle, sha;
enum nodeid_parity parity;
secp256k1_keypair kp;
u8 *msg;
const u8 *p;
@@ -1074,7 +984,7 @@ force_payer_secret(struct command *cmd,
if (secp256k1_keypair_create(secp256k1_ctx, &kp, payer_secret->data) != 1)
return command_fail(cmd, LIGHTNINGD, "Bad payer_secret");
invreq->payer_key = tal(invreq, struct point32);
invreq->payer_key = tal(invreq, struct pubkey);
/* Docs say this only happens if arguments are invalid! */
if (secp256k1_keypair_pub(secp256k1_ctx,
&invreq->payer_key->pubkey,
@@ -1107,10 +1017,9 @@ force_payer_secret(struct command *cmd,
}
sent->path = path_to_node(sent, cmd->plugin,
sent->offer->node_id,
&parity);
sent->offer->node_id);
if (!sent->path)
return connect_direct(cmd, sent->offer->node_id, parity,
return connect_direct(cmd, sent->offer->node_id,
sendinvreq_after_connect, sent);
return sendinvreq_after_connect(cmd, NULL, NULL, sent);
@@ -1384,7 +1293,6 @@ static struct command_result *createinvoice_done(struct command *cmd,
{
const jsmntok_t *invtok = json_get_member(buf, result, "bolt12");
char *fail;
enum nodeid_parity parity;
/* Replace invoice with signed one */
tal_free(sent->inv);
@@ -1405,10 +1313,9 @@ static struct command_result *createinvoice_done(struct command *cmd,
}
sent->path = path_to_node(sent, cmd->plugin,
sent->offer->node_id,
&parity);
sent->offer->node_id);
if (!sent->path)
return connect_direct(cmd, sent->offer->node_id, parity,
return connect_direct(cmd, sent->offer->node_id,
sendinvoice_after_connect, sent);
return sendinvoice_after_connect(cmd, NULL, NULL, sent);
@@ -1585,7 +1492,7 @@ static struct command_result *json_sendinvoice(struct command *cmd,
* - MUST set `node_id` to the id of the node to send payment to.
* - MUST set `description` the same as the offer.
*/
sent->inv->node_id = tal(sent->inv, struct point32);
sent->inv->node_id = tal(sent->inv, struct pubkey);
sent->inv->node_id->pubkey = local_id.pubkey;
sent->inv->description
@@ -1738,34 +1645,23 @@ static struct command_result *json_rawrequest(struct command *cmd,
{
struct sent *sent = tal(cmd, struct sent);
u32 *timeout;
struct node_id *node_id;
struct point32 node_id32;
enum nodeid_parity parity;
struct pubkey *node_id;
if (!param(cmd, buffer, params,
p_req("invreq", param_invreq, &sent->invreq),
p_req("nodeid", param_node_id, &node_id),
p_req("nodeid", param_pubkey, &node_id),
p_opt_def("timeout", param_number, &timeout, 60),
NULL))
return command_param_failed();
if (!secp256k1_ec_pubkey_parse(secp256k1_ctx, &node_id32.pubkey,
node_id->k, sizeof(node_id->k)))
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"Invalid nodeid");
/* This is how long we'll wait for a reply for. */
sent->wait_timeout = *timeout;
sent->cmd = cmd;
sent->offer = NULL;
sent->path = path_to_node(sent, cmd->plugin,
&node_id32,
&parity);
sent->path = path_to_node(sent, cmd->plugin, node_id);
if (!sent->path) {
/* We *do* know parity: they gave it to us! */
parity = node_id->k[0];
return connect_direct(cmd, &node_id32, parity,
return connect_direct(cmd, node_id,
sendinvreq_after_connect, sent);
}

View File

@@ -17,7 +17,7 @@
#include <plugins/offers_invreq_hook.h>
#include <plugins/offers_offer.h>
struct point32 id;
struct pubkey id;
u16 cltv_final;
bool offers_enabled;
@@ -390,7 +390,7 @@ static void json_add_offer(struct json_stream *js, const struct tlv_offer *offer
}
if (offer->node_id)
json_add_point32(js, "node_id", offer->node_id);
json_add_pubkey(js, "node_id", offer->node_id);
else
valid = false;
@@ -556,7 +556,7 @@ static void json_add_b12_invoice(struct json_stream *js,
}
if (invoice->payer_key)
json_add_point32(js, "payer_key", invoice->payer_key);
json_add_pubkey(js, "payer_key", invoice->payer_key);
if (invoice->payer_info)
json_add_hex_talarr(js, "payer_info", invoice->payer_info);
if (invoice->payer_note)
@@ -644,7 +644,7 @@ static void json_add_b12_invoice(struct json_stream *js,
}
/* invoice_decode checked these */
json_add_point32(js, "node_id", invoice->node_id);
json_add_pubkey(js, "node_id", invoice->node_id);
json_add_bip340sig(js, "signature", invoice->signature);
json_add_bool(js, "valid", valid);
@@ -686,7 +686,7 @@ static void json_add_invoice_request(struct json_stream *js,
json_add_u32(js, "recurrence_start",
*invreq->recurrence_start);
if (invreq->payer_key)
json_add_point32(js, "payer_key", invreq->payer_key);
json_add_pubkey(js, "payer_key", invreq->payer_key);
else {
json_add_string(js, "warning_invoice_request_missing_payer_key",
"invoice_request requires payer_key");

View File

@@ -136,15 +136,14 @@ static void set_recurring_inv_expiry(struct tlv_invoice *inv, u64 last_pay)
/* We rely on label forms for uniqueness. */
static void json_add_label(struct json_stream *js,
const struct sha256 *offer_id,
const struct point32 *payer_key,
const struct pubkey *payer_key,
const u32 counter)
{
char *label;
label = tal_fmt(tmpctx, "%s-%s-%u",
type_to_string(tmpctx, struct sha256, offer_id),
type_to_string(tmpctx, struct point32,
payer_key),
type_to_string(tmpctx, struct pubkey, payer_key),
counter);
json_add_string(js, "label", label);
}
@@ -425,7 +424,7 @@ static struct command_result *check_previous_invoice(struct command *cmd,
*/
static bool check_payer_sig(struct command *cmd,
const struct tlv_invoice_request *invreq,
const struct point32 *payer_key,
const struct pubkey *payer_key,
const struct bip340sig *sig)
{
struct sha256 merkle, sighash;
@@ -775,7 +774,7 @@ static struct command_result *listoffers_done(struct command *cmd,
/* 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 point32, ir->offer->node_id);
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.
@@ -786,7 +785,7 @@ static struct command_result *listoffers_done(struct command *cmd,
/* BOLT-offers #12:
* - MUST set `payer_key` exactly as the invoice_request did.
*/
ir->inv->payer_key = tal_dup(ir->inv, struct point32,
ir->inv->payer_key = tal_dup(ir->inv, struct pubkey,
ir->invreq->payer_key);
/* BOLT-offers #12:

View File

@@ -405,7 +405,7 @@ struct command_result *json_offer(struct command *cmd,
= tal_dup_arr(offer, char, issuer, strlen(issuer), 0);
}
offer->node_id = tal_dup(offer, struct point32, &id);
offer->node_id = tal_dup(offer, struct pubkey, &id);
/* If they specify a different currency, warn if we can't
* convert it! */
@@ -469,7 +469,7 @@ struct command_result *json_offerout(struct command *cmd,
offer->issuer = tal_dup_arr(offer, char,
issuer, strlen(issuer), 0);
offer->node_id = tal_dup(offer, struct point32, &id);
offer->node_id = tal_dup(offer, struct pubkey, &id);
req = jsonrpc_request_start(cmd->plugin, cmd, "createoffer",
check_result, forward_error,

View File

@@ -3,7 +3,7 @@
#include "config.h"
#include <plugins/libplugin.h>
extern struct point32 id;
extern struct pubkey id;
extern bool offers_enabled;
struct command_result *json_offer(struct command *cmd,

View File

@@ -1103,10 +1103,8 @@ static struct command_result *json_pay(struct command *cmd,
} else
invmsat = NULL;
/* FIXME: gossmap should store as point32 */
p->destination = tal(p, struct node_id);
gossmap_guess_node_id(get_gossmap(cmd->plugin), b12->node_id,
p->destination);
node_id_from_pubkey(p->destination, b12->node_id);
p->payment_hash = tal_dup(p, struct sha256, b12->payment_hash);
if (b12->recurrence_counter && !label)
return command_fail(