Skip to content

Commit b8498b6

Browse files
committed
lightningd: blindedpath helper to create a blinded path to ourselves.
Currently it will be used for onion replies, but we can use it for offers and invoices in future, if we want to avoid revealing our node_id. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
1 parent b87e0eb commit b8498b6

File tree

2 files changed

+123
-0
lines changed

2 files changed

+123
-0
lines changed

lightningd/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ LIGHTNINGD_COMMON_OBJS := \
7070
common/bech32_util.o \
7171
common/bigsize.o \
7272
common/bip32.o \
73+
common/blindedpath.o \
7374
common/blinding.o \
7475
common/blockheight_states.o \
7576
common/bolt11.o \

lightningd/onion_message.c

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#include <common/blindedpath.h>
12
#include <common/json_command.h>
23
#include <common/json_helpers.h>
34
#include <common/json_tok.h>
@@ -10,6 +11,7 @@
1011
#include <lightningd/peer_control.h>
1112
#include <lightningd/plugin_hook.h>
1213
#include <lightningd/subd.h>
14+
#include <sodium/randombytes.h>
1315

1416
struct onion_message_hook_payload {
1517
/* Optional */
@@ -20,6 +22,26 @@ struct onion_message_hook_payload {
2022
struct tlv_onionmsg_payload *om;
2123
};
2224

25+
static void json_add_blindedpath(struct json_stream *stream,
26+
const char *fieldname,
27+
const struct pubkey *blinding,
28+
const struct pubkey *first_node_id,
29+
struct onionmsg_path **path)
30+
{
31+
json_object_start(stream, fieldname);
32+
json_add_pubkey(stream, "blinding", blinding);
33+
json_add_pubkey(stream, "first_node_id", first_node_id);
34+
json_array_start(stream, "hops");
35+
for (size_t i = 0; i < tal_count(path); i++) {
36+
json_object_start(stream, NULL);
37+
json_add_pubkey(stream, "id", &path[i]->node_id);
38+
json_add_hex_talarr(stream, "enctlv", path[i]->enctlv);
39+
json_object_end(stream);
40+
};
41+
json_array_end(stream);
42+
json_object_end(stream);
43+
}
44+
2345
static void onion_message_serialize(struct onion_message_hook_payload *payload,
2446
struct json_stream *stream,
2547
struct plugin *plugin)
@@ -559,3 +581,103 @@ static const struct json_command sendonionmessage_command = {
559581
"Send message to {first_id}, using {blinding}, encoded over {hops} (id, tlv)"
560582
};
561583
AUTODATA(json_command, &sendonionmessage_command);
584+
585+
static struct command_result *param_pubkeys(struct command *cmd,
586+
const char *name,
587+
const char *buffer,
588+
const jsmntok_t *tok,
589+
struct pubkey **pubkeys)
590+
{
591+
size_t i;
592+
const jsmntok_t *t;
593+
594+
if (tok->type != JSMN_ARRAY || tok->size == 0)
595+
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
596+
"%s must be an (non-empty) array", name);
597+
598+
*pubkeys = tal_arr(cmd, struct pubkey, tok->size);
599+
json_for_each_arr(i, t, tok) {
600+
if (!json_to_pubkey(buffer, t, &(*pubkeys)[i]))
601+
return command_fail_badparam(cmd, name, buffer, t,
602+
"should be a compressed pubkey");
603+
}
604+
return NULL;
605+
}
606+
607+
static struct command_result *json_blindedpath(struct command *cmd,
608+
const char *buffer,
609+
const jsmntok_t *obj UNNEEDED,
610+
const jsmntok_t *params)
611+
{
612+
struct pubkey *ids;
613+
struct onionmsg_path **path;
614+
struct privkey blinding_iter;
615+
struct pubkey first_blinding, first_node, me;
616+
size_t nhops;
617+
struct json_stream *response;
618+
619+
if (!param(cmd, buffer, params,
620+
p_req("ids", param_pubkeys, &ids),
621+
NULL))
622+
return command_param_failed();
623+
624+
nhops = tal_count(ids);
625+
626+
/* Final id should be us! */
627+
if (!pubkey_from_node_id(&me, &cmd->ld->id))
628+
fatal("My id %s is invalid?",
629+
type_to_string(tmpctx, struct node_id, &cmd->ld->id));
630+
631+
first_node = ids[0];
632+
if (!pubkey_eq(&ids[nhops-1], &me))
633+
return command_fail(cmd, LIGHTNINGD,
634+
"Final of ids must be this node (%s), not %s",
635+
type_to_string(tmpctx, struct pubkey, &me),
636+
type_to_string(tmpctx, struct pubkey,
637+
&ids[nhops-1]));
638+
639+
randombytes_buf(&blinding_iter, sizeof(blinding_iter));
640+
if (!pubkey_from_privkey(&blinding_iter, &first_blinding))
641+
/* Should not happen! */
642+
return command_fail(cmd, LIGHTNINGD,
643+
"Could not convert blinding to pubkey!");
644+
645+
/* We convert ids into aliases as we go. */
646+
path = tal_arr(cmd, struct onionmsg_path *, nhops);
647+
648+
for (size_t i = 0; i < nhops - 1; i++) {
649+
path[i] = tal(path, struct onionmsg_path);
650+
path[i]->enctlv = create_enctlv(path[i],
651+
&blinding_iter,
652+
&ids[i],
653+
&ids[i+1],
654+
/* FIXME: Pad? */
655+
0,
656+
NULL,
657+
&blinding_iter,
658+
&path[i]->node_id);
659+
}
660+
661+
/* FIXME: Add padding! */
662+
path[nhops-1] = tal(path, struct onionmsg_path);
663+
path[nhops-1]->enctlv = create_final_enctlv(path[nhops-1],
664+
&blinding_iter,
665+
&ids[nhops-1],
666+
/* FIXME: Pad? */
667+
0,
668+
&cmd->ld->onion_reply_secret,
669+
&path[nhops-1]->node_id);
670+
671+
response = json_stream_success(cmd);
672+
json_add_blindedpath(response, "blindedpath",
673+
&first_blinding, &first_node, path);
674+
return command_success(cmd, response);
675+
}
676+
677+
static const struct json_command blindedpath_command = {
678+
"blindedpath",
679+
"utility",
680+
json_blindedpath,
681+
"Create blinded path to us along {ids} (pubkey array ending in our id)"
682+
};
683+
AUTODATA(json_command, &blindedpath_command);

0 commit comments

Comments
 (0)