From 621bfe370e70e4a43d2e60cd726827aeb9fe0dda Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 10 Jul 2024 12:25:49 +0930 Subject: [PATCH] connectd: forward onion messages by scid as well as node_id. This is now permitted in the offers PR, so we should support it. But we can't just look up in the gossmap, since the "short_channel_id" could be an alias. So we get lightningd to tell us all scid->peer mappings, and look up in that. Changelog-Added: Protocol: onion messages can now be forwarded by short_channel_id. Signed-off-by: Rusty Russell --- common/onion_message_parse.c | 29 +++++++++++++++++++------- common/onion_message_parse.h | 6 +++--- common/test/run-onion-message-test.c | 22 +++++--------------- connectd/onion_message.c | 31 ++++++++++++++++++++++++---- 4 files changed, 56 insertions(+), 32 deletions(-) diff --git a/common/onion_message_parse.c b/common/onion_message_parse.c index defe167e7..71dad51e9 100644 --- a/common/onion_message_parse.c +++ b/common/onion_message_parse.c @@ -40,7 +40,7 @@ static bool decrypt_final_onionmsg(const tal_t *ctx, static bool decrypt_forwarding_onionmsg(const struct pubkey *blinding, const struct secret *ss, const u8 *enctlv, - struct pubkey *next_node, + struct sciddir_or_pubkey *next_node, struct pubkey *next_blinding) { struct tlv_encrypted_data_tlv *encmsg; @@ -58,14 +58,27 @@ static bool decrypt_forwarding_onionmsg(const struct pubkey *blinding, if (encmsg->path_id) return false; - /* BOLT #4: - * - SHOULD forward the message using `onion_message` to the next peer - * indicated by `next_node_id`. + /* BOLT-offers #4: + * - if it is not the final node according to the onion encryption: + *... + * - if `next_node_id` is present: + * - the *next peer* is the peer with that node id. + * - otherwise, if `short_channel_id` is present and corresponds to an announced short_channel_id or a local alias for a channel: + * - the *next peer* is the peer at the other end of that channel. + * - otherwise: + * - MUST ignore the message. */ - if (!encmsg->next_node_id) + if (encmsg->next_node_id) + sciddir_or_pubkey_from_pubkey(next_node, encmsg->next_node_id); + else if (encmsg->short_channel_id) { + /* This is actually scid, not sciddir, but the type is convenient! */ + struct short_channel_id_dir scidd; + scidd.scid = *encmsg->short_channel_id; + scidd.dir = 0; + sciddir_or_pubkey_from_scidd(next_node, &scidd); + } else return false; - *next_node = *encmsg->next_node_id; blindedpath_next_blinding(encmsg, blinding, ss, next_blinding); return true; } @@ -76,7 +89,7 @@ const char *onion_message_parse(const tal_t *ctx, const struct pubkey *blinding, const struct pubkey *me, u8 **next_onion_msg, - struct pubkey *next_node_id, + struct sciddir_or_pubkey *next_node, struct tlv_onionmsg_tlv **final_om, struct pubkey *final_alias, struct secret **final_path_id) @@ -161,7 +174,7 @@ const char *onion_message_parse(const tal_t *ctx, } /* This fails as expected if no enctlv. */ - if (!decrypt_forwarding_onionmsg(blinding, &ss, om->encrypted_recipient_data, next_node_id, + if (!decrypt_forwarding_onionmsg(blinding, &ss, om->encrypted_recipient_data, next_node, &next_blinding)) { return tal_fmt(ctx, "onion_message_parse: invalid encrypted_recipient_data %s", diff --git a/common/onion_message_parse.h b/common/onion_message_parse.h index f15da2aa6..fe0608a90 100644 --- a/common/onion_message_parse.h +++ b/common/onion_message_parse.h @@ -5,7 +5,7 @@ #include struct tlv_onionmsg_tlv; -struct node_id; +struct sciddir_or_pubkey; struct pubkey; /** @@ -15,7 +15,7 @@ struct pubkey; * @blinding: Blinding we were given for @onion_message_packet * @me: my pubkey * @next_onion_msg (out): set if we should forward, otherwise NULL. - * @next_node_id (out): set to node id to fwd to, iff *@next_onion_msg. + * @next_node (out): set to node id or scid to fwd to, iff *@next_onion_msg. * @final_om (out): set if we're the final hop, otherwise NULL. * @final_alias (out): our alias (if *@final_om), or our own ID * @final_path_id (out): secret enclosed, if any (iff *@final_om). @@ -27,7 +27,7 @@ const char *onion_message_parse(const tal_t *ctx, const struct pubkey *blinding, const struct pubkey *me, u8 **next_onion_msg, - struct pubkey *next_node_id, + struct sciddir_or_pubkey *next_node, struct tlv_onionmsg_tlv **final_om, struct pubkey *final_alias, struct secret **final_path_id); diff --git a/common/test/run-onion-message-test.c b/common/test/run-onion-message-test.c index ea6bee447..7017da3cf 100644 --- a/common/test/run-onion-message-test.c +++ b/common/test/run-onion-message-test.c @@ -14,6 +14,7 @@ static void maybe_print(const char *fmt, ...); #include "../hmac.c" #include "../onion_encode.c" #include "../onion_message_parse.c" +#include "../sciddir_or_pubkey.c" #include "../sphinx.c" #include "../../wire/onion_wiregen.c" #include "../../wire/peer_wiregen.c" @@ -29,32 +30,18 @@ bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, /* Generated stub for fromwire_node_id */ void fromwire_node_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct node_id *id UNNEEDED) { fprintf(stderr, "fromwire_node_id called!\n"); abort(); } -/* Generated stub for fromwire_sciddir_or_pubkey */ -void fromwire_sciddir_or_pubkey(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, - struct sciddir_or_pubkey *sciddpk UNNEEDED) -{ fprintf(stderr, "fromwire_sciddir_or_pubkey called!\n"); abort(); } /* Generated stub for new_onionreply */ struct onionreply *new_onionreply(const tal_t *ctx UNNEEDED, const u8 *contents TAKES UNNEEDED) { fprintf(stderr, "new_onionreply called!\n"); abort(); } /* Generated stub for pubkey_from_node_id */ bool pubkey_from_node_id(struct pubkey *key UNNEEDED, const struct node_id *id UNNEEDED) { fprintf(stderr, "pubkey_from_node_id called!\n"); abort(); } -/* Generated stub for status_fmt */ -void status_fmt(enum log_level level UNNEEDED, - const struct node_id *peer UNNEEDED, - const char *fmt UNNEEDED, ...) - -{ fprintf(stderr, "status_fmt called!\n"); abort(); } /* Generated stub for towire_channel_id */ void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "towire_channel_id called!\n"); abort(); } /* Generated stub for towire_node_id */ void towire_node_id(u8 **pptr UNNEEDED, const struct node_id *id UNNEEDED) { fprintf(stderr, "towire_node_id called!\n"); abort(); } -/* Generated stub for towire_sciddir_or_pubkey */ -void towire_sciddir_or_pubkey(u8 **pptr UNNEEDED, - const struct sciddir_or_pubkey *sciddpk UNNEEDED) -{ fprintf(stderr, "towire_sciddir_or_pubkey called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ static bool comma; @@ -355,7 +342,7 @@ int main(int argc, char *argv[]) json_start("hops", '['); for (size_t i = 0; i < ARRAY_SIZE(erd); i++) { - struct pubkey next_node_id; + struct sciddir_or_pubkey next_node; struct tlv_onionmsg_tlv *final_om; struct pubkey final_alias; struct secret *final_path_id; @@ -373,12 +360,13 @@ int main(int argc, char *argv[]) mykey = &privkey[i]; assert(onion_message_parse(tmpctx, onion_message_packet, &blinding_pub, &id[i], - &onion_message, &next_node_id, + &onion_message, &next_node, &final_om, &final_alias, &final_path_id) == NULL); if (onion_message) { - json_pubkey("next_node_id", &next_node_id); + assert(next_node.is_pubkey); + json_pubkey("next_node_id", &next_node.pubkey); } else { const struct tlv_field *hello; json_start("tlvs", '{'); diff --git a/connectd/onion_message.c b/connectd/onion_message.c index 86a531c7e..466f77284 100644 --- a/connectd/onion_message.c +++ b/connectd/onion_message.c @@ -42,7 +42,7 @@ static const char *handle_onion(const tal_t *ctx, const u8 *onion) { u8 *next_onion_msg; - struct pubkey next_node; + struct sciddir_or_pubkey next_node; struct tlv_onionmsg_tlv *final_om; struct pubkey final_alias; struct secret *final_path_id; @@ -73,12 +73,35 @@ static const char *handle_onion(const tal_t *ctx, assert(next_onion_msg); - /* FIXME: Handle short_channel_id! */ - node_id_from_pubkey(&next_node_id, &next_node); + /* BOLT-offers #4: + * - if it is not the final node according to the onion encryption: + *... + * - if `next_node_id` is present: + * - the *next peer* is the peer with that node id. + * - otherwise, if `short_channel_id` is present and corresponds to an announced short_channel_id or a local alias for a channel: + * - the *next peer* is the peer at the other end of that channel. + * - otherwise: + * - MUST ignore the message. + * - SHOULD forward the message using `onion_message` to the *next peer*. + */ + /* Since an alias is legal here, we can't simply lookup in gossmap. */ + if (!next_node.is_pubkey) { + struct scid_to_node_id *scid_to_node_id; + + scid_to_node_id = scid_htable_get(daemon->scid_htable, next_node.scidd.scid); + if (!scid_to_node_id) { + return tal_fmt(ctx, "onion msg: unknown next scid %s", + fmt_short_channel_id(tmpctx, next_node.scidd.scid)); + } + next_node_id = scid_to_node_id->node_id; + } else { + node_id_from_pubkey(&next_node_id, &next_node.pubkey); + } + next_peer = peer_htable_get(daemon->peers, &next_node_id); if (!next_peer) { return tal_fmt(ctx, "onion msg: unknown next peer %s", - fmt_pubkey(tmpctx, &next_node)); + fmt_sciddir_or_pubkey(tmpctx, &next_node)); } inject_peer_msg(next_peer, take(next_onion_msg)); }