mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-03-15 11:59:16 +01:00
lightningd: remove sendonionmesage parity hack.
offers contain an x-only pubkey: to route to them to need to know the 02 vs 03 prefix. If they're in the gossmap it's easy, but if they're a directly-connected peer it's harder. We used to have sendonionmessage tweak the key if it found a peer with the matching key, but this was always a hack. It turns out that we try to connect to the node anyway, which is a noop if it's already connected. So try connecting to the other parity if the first one fails. Also, this registers when we fail to connect, and returns an error rather than waiting for timeout. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
8b694907e6
commit
6aa520bb9b
2 changed files with 156 additions and 77 deletions
|
@ -417,17 +417,8 @@ static struct command_result *json_send_onion_message(struct command *cmd,
|
|||
|
||||
/* Sanity check first; gossipd doesn't bother telling us if peer
|
||||
* can't be reached. */
|
||||
if (!peer_by_id(cmd->ld, &first_id)) {
|
||||
/* Nasty hack: maybe we didn't know y-parity? */
|
||||
first_id.k[0] = SECP256K1_TAG_PUBKEY_ODD;
|
||||
if (!peer_by_id(cmd->ld, &first_id))
|
||||
return command_fail(cmd, LIGHTNINGD,
|
||||
"Unknown first peer");
|
||||
/* Fixup first hop parity. */
|
||||
if (!pubkey_from_node_id(&hops[0].id, &first_id))
|
||||
return command_fail(cmd, LIGHTNINGD,
|
||||
"Could not convert parity!");
|
||||
}
|
||||
if (!peer_by_id(cmd->ld, &first_id))
|
||||
return command_fail(cmd, LIGHTNINGD, "Unknown first peer");
|
||||
|
||||
/* Create an onion which encodes this. */
|
||||
populate_tlvs(hops, reply_path);
|
||||
|
|
|
@ -541,60 +541,76 @@ 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_pubkey32(struct node_id *nid,
|
||||
const struct pubkey32 *node32_id,
|
||||
enum nodeid_parity parity)
|
||||
{
|
||||
assert(parity == SECP256K1_TAG_PUBKEY_EVEN
|
||||
|| parity == SECP256K1_TAG_PUBKEY_ODD);
|
||||
nid->k[0] = parity;
|
||||
secp256k1_xonly_pubkey_serialize(secp256k1_ctx, nid->k+1,
|
||||
&node32_id->pubkey);
|
||||
}
|
||||
|
||||
/* Create path to node which can carry onion messages; if it can't find
|
||||
* one, create singleton path and sets @try_connect. */
|
||||
* one, returns NULL. Fills in nodeid_parity for 33rd nodeid byte. */
|
||||
static struct node_id *path_to_node(const tal_t *ctx,
|
||||
struct gossmap *gossmap,
|
||||
const struct pubkey32 *node32_id,
|
||||
bool *try_connect)
|
||||
enum nodeid_parity *parity)
|
||||
{
|
||||
struct route_hop *r;
|
||||
const struct dijkstra *dij;
|
||||
const struct gossmap_node *src;
|
||||
const struct gossmap_node *dst;
|
||||
struct node_id *nodes, dstid;
|
||||
|
||||
/* FIXME: Use blinded path if avail. */
|
||||
gossmap_guess_node_id(gossmap, node32_id, &dstid);
|
||||
/* We try both parities. */
|
||||
*parity = nodeid_parity_even;
|
||||
node_id_from_pubkey32(&dstid, node32_id, *parity);
|
||||
dst = gossmap_find_node(gossmap, &dstid);
|
||||
if (!dst) {
|
||||
nodes = tal_arr(ctx, struct node_id, 1);
|
||||
/* We don't know the pubkey y-sign, but sendonionmessage will
|
||||
* fix it up if we guess wrong. */
|
||||
nodes[0].k[0] = SECP256K1_TAG_PUBKEY_EVEN;
|
||||
secp256k1_xonly_pubkey_serialize(secp256k1_ctx,
|
||||
nodes[0].k+1,
|
||||
&node32_id->pubkey);
|
||||
/* Since it's not it gossmap, we don't know how to connect,
|
||||
* so don't try. */
|
||||
*try_connect = false;
|
||||
return nodes;
|
||||
} else {
|
||||
struct route_hop *r;
|
||||
const struct dijkstra *dij;
|
||||
const struct gossmap_node *src;
|
||||
|
||||
/* If we don't exist in gossip, routing can't happen. */
|
||||
src = gossmap_find_node(gossmap, &local_id);
|
||||
if (!src)
|
||||
goto go_direct_dst;
|
||||
|
||||
dij = dijkstra(tmpctx, gossmap, dst, AMOUNT_MSAT(0), 0,
|
||||
can_carry_onionmsg, route_score_shorter, NULL);
|
||||
|
||||
r = route_from_dijkstra(tmpctx, gossmap, dij, src, AMOUNT_MSAT(0), 0);
|
||||
if (!r)
|
||||
goto go_direct_dst;
|
||||
|
||||
*try_connect = false;
|
||||
nodes = tal_arr(ctx, struct node_id, tal_count(r));
|
||||
for (size_t i = 0; i < tal_count(r); i++)
|
||||
nodes[i] = r[i].node_id;
|
||||
return nodes;
|
||||
*parity = nodeid_parity_odd;
|
||||
node_id_from_pubkey32(&dstid, node32_id, *parity);
|
||||
dst = gossmap_find_node(gossmap, &dstid);
|
||||
if (!dst) {
|
||||
*parity = nodeid_parity_unknown;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
go_direct_dst:
|
||||
/* Try direct route, maybe it's connected? */
|
||||
nodes = tal_arr(ctx, struct node_id, 1);
|
||||
gossmap_node_get_id(gossmap, dst, &nodes[0]);
|
||||
*try_connect = true;
|
||||
*parity = node_parity(gossmap, dst);
|
||||
|
||||
/* If we don't exist in gossip, routing can't happen. */
|
||||
src = gossmap_find_node(gossmap, &local_id);
|
||||
if (!src)
|
||||
return NULL;
|
||||
|
||||
dij = dijkstra(tmpctx, gossmap, dst, AMOUNT_MSAT(0), 0,
|
||||
can_carry_onionmsg, route_score_shorter, NULL);
|
||||
|
||||
r = route_from_dijkstra(tmpctx, gossmap, dij, src, AMOUNT_MSAT(0), 0);
|
||||
if (!r)
|
||||
return NULL;
|
||||
|
||||
nodes = tal_arr(ctx, struct node_id, tal_count(r));
|
||||
for (size_t i = 0; i < tal_count(r); i++)
|
||||
nodes[i] = r[i].node_id;
|
||||
return nodes;
|
||||
}
|
||||
|
||||
|
@ -702,11 +718,58 @@ sendinvreq_after_connect(struct command *cmd,
|
|||
sendonionmsg_done);
|
||||
}
|
||||
|
||||
struct connect_attempt {
|
||||
struct node_id node_id;
|
||||
struct command_result *(*cb)(struct command *command,
|
||||
const char *buf,
|
||||
const jsmntok_t *result,
|
||||
struct sent *sent);
|
||||
struct sent *sent;
|
||||
};
|
||||
|
||||
static struct command_result *connected(struct command *command,
|
||||
const char *buf,
|
||||
const jsmntok_t *result,
|
||||
struct connect_attempt *ca)
|
||||
{
|
||||
return ca->cb(command, buf, result, ca->sent);
|
||||
}
|
||||
|
||||
static struct command_result *connect_failed(struct command *command,
|
||||
const char *buf,
|
||||
const jsmntok_t *result,
|
||||
struct connect_attempt *ca)
|
||||
{
|
||||
return command_done_err(command, OFFER_ROUTE_NOT_FOUND,
|
||||
"Failed: could not route, could not connect",
|
||||
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;
|
||||
ca->sent->path[0] = ca->node_id;
|
||||
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 node_id *dst,
|
||||
const struct pubkey32 *dst,
|
||||
enum nodeid_parity parity,
|
||||
struct command_result *(*cb)(struct command *command,
|
||||
const char *buf,
|
||||
const jsmntok_t *result,
|
||||
|
@ -714,23 +777,44 @@ connect_direct(struct command *cmd,
|
|||
struct sent *sent)
|
||||
{
|
||||
struct out_req *req;
|
||||
struct connect_attempt *ca = tal(cmd, struct connect_attempt);
|
||||
|
||||
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 pubkey32, dst));
|
||||
/* Try even first. */
|
||||
node_id_from_pubkey32(&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 pubkey32, dst));
|
||||
node_id_from_pubkey32(&ca->node_id, dst, parity);
|
||||
}
|
||||
|
||||
/* Make a direct path -> dst. */
|
||||
sent->path = tal_arr(sent, struct node_id, 1);
|
||||
sent->path[0] = ca->node_id;
|
||||
|
||||
if (disable_connect) {
|
||||
/* FIXME: This means we will fail if parity is wrong! */
|
||||
plugin_notify_message(cmd, LOG_UNUSUAL,
|
||||
"Cannot find route, but"
|
||||
" fetchplugin-noconnect set:"
|
||||
" trying direct anyway to %s",
|
||||
type_to_string(tmpctx, struct node_id,
|
||||
type_to_string(tmpctx, struct pubkey32,
|
||||
dst));
|
||||
return cb(cmd, NULL, NULL, sent);
|
||||
}
|
||||
|
||||
plugin_notify_message(cmd, LOG_INFORM,
|
||||
"Cannot find route, trying connect to %s directly",
|
||||
type_to_string(tmpctx, struct node_id, dst));
|
||||
|
||||
req = jsonrpc_request_start(cmd->plugin, cmd, "connect", cb, cb, sent);
|
||||
json_add_node_id(req->js, "id", dst);
|
||||
req = jsonrpc_request_start(cmd->plugin, cmd, "connect", connected,
|
||||
parity == nodeid_parity_unknown ?
|
||||
try_other_parity : connect_failed, ca);
|
||||
json_add_node_id(req->js, "id", &ca->node_id);
|
||||
return send_outreq(cmd->plugin, req);
|
||||
}
|
||||
|
||||
|
@ -741,7 +825,7 @@ static struct command_result *invreq_done(struct command *cmd,
|
|||
{
|
||||
const jsmntok_t *t;
|
||||
char *fail;
|
||||
bool try_connect;
|
||||
enum nodeid_parity parity;
|
||||
|
||||
/* Get invoice request */
|
||||
t = json_get_member(buf, result, "bolt12");
|
||||
|
@ -836,9 +920,9 @@ static struct command_result *invreq_done(struct command *cmd,
|
|||
|
||||
sent->path = path_to_node(sent, get_gossmap(cmd->plugin),
|
||||
sent->offer->node_id,
|
||||
&try_connect);
|
||||
if (try_connect)
|
||||
return connect_direct(cmd, &sent->path[0],
|
||||
&parity);
|
||||
if (!sent->path)
|
||||
return connect_direct(cmd, sent->offer->node_id, parity,
|
||||
sendinvreq_after_connect, sent);
|
||||
|
||||
return sendinvreq_after_connect(cmd, NULL, NULL, sent);
|
||||
|
@ -853,7 +937,7 @@ force_payer_secret(struct command *cmd,
|
|||
const struct secret *payer_secret)
|
||||
{
|
||||
struct sha256 merkle, sha;
|
||||
bool try_connect;
|
||||
enum nodeid_parity parity;
|
||||
secp256k1_keypair kp;
|
||||
u8 *msg;
|
||||
const u8 *p;
|
||||
|
@ -896,9 +980,9 @@ force_payer_secret(struct command *cmd,
|
|||
|
||||
sent->path = path_to_node(sent, get_gossmap(cmd->plugin),
|
||||
sent->offer->node_id,
|
||||
&try_connect);
|
||||
if (try_connect)
|
||||
return connect_direct(cmd, &sent->path[0],
|
||||
&parity);
|
||||
if (!sent->path)
|
||||
return connect_direct(cmd, sent->offer->node_id, parity,
|
||||
sendinvreq_after_connect, sent);
|
||||
|
||||
return sendinvreq_after_connect(cmd, NULL, NULL, sent);
|
||||
|
@ -1169,7 +1253,7 @@ static struct command_result *createinvoice_done(struct command *cmd,
|
|||
{
|
||||
const jsmntok_t *invtok = json_get_member(buf, result, "bolt12");
|
||||
char *fail;
|
||||
bool try_connect;
|
||||
enum nodeid_parity parity;
|
||||
|
||||
/* Replace invoice with signed one */
|
||||
tal_free(sent->inv);
|
||||
|
@ -1191,9 +1275,9 @@ static struct command_result *createinvoice_done(struct command *cmd,
|
|||
|
||||
sent->path = path_to_node(sent, get_gossmap(cmd->plugin),
|
||||
sent->offer->node_id,
|
||||
&try_connect);
|
||||
if (try_connect)
|
||||
return connect_direct(cmd, &sent->path[0],
|
||||
&parity);
|
||||
if (!sent->path)
|
||||
return connect_direct(cmd, sent->offer->node_id, parity,
|
||||
sendinvoice_after_connect, sent);
|
||||
|
||||
return sendinvoice_after_connect(cmd, NULL, NULL, sent);
|
||||
|
@ -1529,7 +1613,7 @@ static struct command_result *json_rawrequest(struct command *cmd,
|
|||
u32 *timeout;
|
||||
struct node_id *node_id;
|
||||
struct pubkey32 node_id32;
|
||||
bool try_connect;
|
||||
enum nodeid_parity parity;
|
||||
|
||||
if (!param(cmd, buffer, params,
|
||||
p_req("invreq", param_invreq, &sent->invreq),
|
||||
|
@ -1550,10 +1634,14 @@ static struct command_result *json_rawrequest(struct command *cmd,
|
|||
sent->offer = NULL;
|
||||
|
||||
sent->path = path_to_node(sent, get_gossmap(cmd->plugin),
|
||||
&node_id32, &try_connect);
|
||||
if (try_connect)
|
||||
return connect_direct(cmd, node_id,
|
||||
&node_id32,
|
||||
&parity);
|
||||
if (!sent->path) {
|
||||
/* We *do* know parity: they gave it to us! */
|
||||
parity = node_id->k[0];
|
||||
return connect_direct(cmd, &node_id32, parity,
|
||||
sendinvreq_after_connect, sent);
|
||||
}
|
||||
|
||||
return sendinvreq_after_connect(cmd, NULL, NULL, sent);
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue