Skip to content

Commit

Permalink
lightningd: Send updated onion spec messages.
Browse files Browse the repository at this point in the history
It's very similar to the previous, but there are a few changes:

1. The enctlv fields are numbered differently.
2. The message itself is a different number.

The onionmsg_path type is the same, however, so we keep that constant
at least.

The result is a lot of cut & paste, but we will delete the old one
next release.

Signed-off-by: Rusty Russell <[email protected]>
  • Loading branch information
rustyrussell committed Nov 30, 2021
1 parent 5cf2c2f commit e7b2633
Show file tree
Hide file tree
Showing 8 changed files with 213 additions and 32 deletions.
12 changes: 8 additions & 4 deletions gossipd/gossipd.c
Original file line number Diff line number Diff line change
Expand Up @@ -505,17 +505,21 @@ static struct io_plan *onionmsg_req(struct io_conn *conn, struct daemon *daemon,
u8 *onionmsg;
struct pubkey blinding;
struct peer *peer;
bool obs2;

if (!fromwire_gossipd_send_onionmsg(msg, msg, &id, &onionmsg, &blinding))
if (!fromwire_gossipd_send_onionmsg(msg, msg, &obs2, &id, &onionmsg, &blinding))
master_badmsg(WIRE_GOSSIPD_SEND_ONIONMSG, msg);

/* Even though lightningd checks for valid ids, there's a race
* where it might vanish before we read this command. */
peer = find_peer(daemon, &id);
if (peer) {
queue_peer_msg(peer,
take(towire_obs2_onion_message(NULL,
&blinding, onionmsg)));
u8 *omsg;
if (obs2)
omsg = towire_obs2_onion_message(NULL, &blinding, onionmsg);
else
omsg = towire_onion_message(NULL, &blinding, onionmsg);
queue_peer_msg(peer, take(omsg));
}
return daemon_conn_read_next(conn, daemon->master);
}
Expand Down
1 change: 1 addition & 0 deletions gossipd/gossipd_wire.csv
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ msgdata,gossipd_got_onionmsg_to_us,rawmsg,u8,rawmsg_len

# Lightningd tells us to send an onion message.
msgtype,gossipd_send_onionmsg,3041
msgdata,gossipd_send_onionmsg,obs2,bool,
msgdata,gossipd_send_onionmsg,id,node_id,
msgdata,gossipd_send_onionmsg,onion_len,u16,
msgdata,gossipd_send_onionmsg,onion,u8,onion_len
Expand Down
35 changes: 30 additions & 5 deletions lightningd/onion_message.c
Original file line number Diff line number Diff line change
Expand Up @@ -199,10 +199,11 @@ static struct command_result *param_onion_hops(struct command *cmd,
return NULL;
}

static struct command_result *json_sendonionmessage(struct command *cmd,
const char *buffer,
const jsmntok_t *obj UNNEEDED,
const jsmntok_t *params)
static struct command_result *json_sendonionmessage2(struct command *cmd,
const char *buffer,
const jsmntok_t *obj UNNEEDED,
const jsmntok_t *params,
bool obs2)
{
struct onion_hop *hops;
struct node_id *first_id;
Expand Down Expand Up @@ -248,13 +249,29 @@ static struct command_result *json_sendonionmessage(struct command *cmd,
"Creating onion failed (tlvs too long?)");

subd_send_msg(cmd->ld->gossip,
take(towire_gossipd_send_onionmsg(NULL, first_id,
take(towire_gossipd_send_onionmsg(NULL, obs2, first_id,
serialize_onionpacket(tmpctx, op),
blinding)));

return command_success(cmd, json_stream_success(cmd));
}

static struct command_result *json_sendonionmessage(struct command *cmd,
const char *buffer,
const jsmntok_t *obj,
const jsmntok_t *params)
{
return json_sendonionmessage2(cmd, buffer, obj, params, false);
}

static struct command_result *json_sendobs2onionmessage(struct command *cmd,
const char *buffer,
const jsmntok_t *obj,
const jsmntok_t *params)
{
return json_sendonionmessage2(cmd, buffer, obj, params, true);
}

static const struct json_command sendonionmessage_command = {
"sendonionmessage",
"utility",
Expand All @@ -263,6 +280,14 @@ static const struct json_command sendonionmessage_command = {
};
AUTODATA(json_command, &sendonionmessage_command);

static const struct json_command sendobs2onionmessage_command = {
"sendobs2onionmessage",
"utility",
json_sendobs2onionmessage,
"Send obsolete message to {first_id}, using {blinding}, encoded over {hops} (id, tlv)"
};
AUTODATA(json_command, &sendobs2onionmessage_command);

static struct command_result *param_pubkeys(struct command *cmd,
const char *name,
const char *buffer,
Expand Down
108 changes: 100 additions & 8 deletions plugins/fetchinvoice.c
Original file line number Diff line number Diff line change
Expand Up @@ -631,16 +631,18 @@ struct sending {
struct sent *sent;
const char *msgfield;
const u8 *msgval;
struct tlv_obs2_onionmsg_payload_reply_path *obs2_reply_path;
struct command_result *(*done)(struct command *cmd,
const char *buf UNUSED,
const jsmntok_t *result UNUSED,
struct sent *sent);
};

static struct command_result *
send_modern_message(struct command *cmd,
struct tlv_obs2_onionmsg_payload_reply_path *reply_path,
struct sending *sending)
send_obs2_message(struct command *cmd,
const char *buf,
const jsmntok_t *result,
struct sending *sending)
{
struct sent *sent = sending->sent;
struct privkey blinding_iter;
Expand Down Expand Up @@ -695,9 +697,9 @@ send_modern_message(struct command *cmd,
payloads[nhops-1]->invoice
= cast_const(u8 *, sending->msgval);
}
payloads[nhops-1]->reply_path = reply_path;
payloads[nhops-1]->reply_path = sending->obs2_reply_path;

req = jsonrpc_request_start(cmd->plugin, cmd, "sendonionmessage",
req = jsonrpc_request_start(cmd->plugin, cmd, "sendobs2onionmessage",
sending->done,
forward_error,
sending->sent);
Expand All @@ -717,23 +719,113 @@ send_modern_message(struct command *cmd,
return send_outreq(cmd->plugin, req);
}

static struct command_result *
send_modern_message(struct command *cmd,
struct tlv_onionmsg_payload_reply_path *reply_path,
struct sending *sending)
{
struct sent *sent = sending->sent;
struct privkey blinding_iter;
struct pubkey fwd_blinding, *node_alias;
size_t nhops = tal_count(sent->path);
struct tlv_onionmsg_payload **payloads;
struct out_req *req;

/* Now create enctlvs for *forward* path. */
randombytes_buf(&blinding_iter, sizeof(blinding_iter));
if (!pubkey_from_privkey(&blinding_iter, &fwd_blinding))
return command_fail(cmd, LIGHTNINGD,
"Could not convert blinding %s to pubkey!",
type_to_string(tmpctx, struct privkey,
&blinding_iter));

/* We overallocate: this node (0) doesn't have payload or alias */
payloads = tal_arr(cmd, struct tlv_onionmsg_payload *, nhops);
node_alias = tal_arr(cmd, struct pubkey, nhops);

for (size_t i = 1; i < nhops - 1; i++) {
payloads[i] = tlv_onionmsg_payload_new(payloads);
payloads[i]->encrypted_data_tlv = create_enctlv(payloads[i],
&blinding_iter,
&sent->path[i],
&sent->path[i+1],
/* FIXME: Pad? */
0,
NULL,
&blinding_iter,
&node_alias[i]);
}
/* Final payload contains the actual data. */
payloads[nhops-1] = tlv_onionmsg_payload_new(payloads);

/* We don't include enctlv in final, but it gives us final alias */
if (!create_final_enctlv(tmpctx, &blinding_iter, &sent->path[nhops-1],
/* FIXME: Pad? */ 0,
NULL,
&node_alias[nhops-1])) {
/* Should not happen! */
return command_fail(cmd, LIGHTNINGD,
"Could create final enctlv");
}

/* FIXME: This interface is a string for sendobsonionmessage! */
if (streq(sending->msgfield, "invoice_request")) {
payloads[nhops-1]->invoice_request
= cast_const(u8 *, sending->msgval);
} else {
assert(streq(sending->msgfield, "invoice"));
payloads[nhops-1]->invoice
= cast_const(u8 *, sending->msgval);
}
payloads[nhops-1]->reply_path = reply_path;

req = jsonrpc_request_start(cmd->plugin, cmd, "sendonionmessage",
/* Try sending older version next */
send_obs2_message,
forward_error,
sending);
json_add_pubkey(req->js, "first_id", &sent->path[1]);
json_add_pubkey(req->js, "blinding", &fwd_blinding);
json_array_start(req->js, "hops");
for (size_t i = 1; i < nhops; i++) {
u8 *tlv;
json_object_start(req->js, NULL);
json_add_pubkey(req->js, "id", &node_alias[i]);
tlv = tal_arr(tmpctx, u8, 0);
towire_onionmsg_payload(&tlv, payloads[i]);
json_add_hex_talarr(req->js, "tlv", tlv);
json_object_end(req->js);
}
json_array_end(req->js);
return send_outreq(cmd->plugin, req);
}

/* Lightningd gives us reply path, since we don't know secret to put
* in final so it will recognize it. */
static struct command_result *use_reply_path(struct command *cmd,
const char *buf,
const jsmntok_t *result,
struct sending *sending)
{
struct tlv_obs2_onionmsg_payload_reply_path *rpath;
struct tlv_onionmsg_payload_reply_path *rpath;

rpath = json_to_obs2_reply_path(cmd, buf,
json_get_member(buf, result, "obs2blindedpath"));
rpath = json_to_reply_path(cmd, buf,
json_get_member(buf, result, "blindedpath"));
if (!rpath)
plugin_err(cmd->plugin,
"could not parse reply path %.*s?",
json_tok_full_len(result),
json_tok_full(buf, result));

sending->obs2_reply_path = json_to_obs2_reply_path(cmd, buf,
json_get_member(buf, result,
"obs2blindedpath"));
if (!sending->obs2_reply_path)
plugin_err(cmd->plugin,
"could not parse obs2 reply path %.*s?",
json_tok_full_len(result),
json_tok_full(buf, result));

/* Remember our alias we used so we can recognize reply */
sending->sent->reply_alias
= tal_dup(sending->sent, struct pubkey,
Expand Down
63 changes: 57 additions & 6 deletions plugins/offers.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,16 +41,16 @@ static struct command_result *sendonionmessage_error(struct command *cmd,
}

/* FIXME: replyfield string interface is to accomodate obsolete API */
struct command_result *
send_onion_reply(struct command *cmd,
struct tlv_obs2_onionmsg_payload_reply_path *reply_path,
const char *replyfield,
const u8 *replydata)
static struct command_result *
send_obs2_onion_reply(struct command *cmd,
struct tlv_obs2_onionmsg_payload_reply_path *reply_path,
const char *replyfield,
const u8 *replydata)
{
struct out_req *req;
size_t nhops = tal_count(reply_path->path);

req = jsonrpc_request_start(cmd->plugin, cmd, "sendonionmessage",
req = jsonrpc_request_start(cmd->plugin, cmd, "sendobs2onionmessage",
finished, sendonionmessage_error, NULL);

json_add_pubkey(req->js, "first_id", &reply_path->first_node_id);
Expand Down Expand Up @@ -84,6 +84,57 @@ send_onion_reply(struct command *cmd,
return send_outreq(cmd->plugin, req);
}

struct command_result *
send_onion_reply(struct command *cmd,
struct tlv_onionmsg_payload_reply_path *reply_path,
struct tlv_obs2_onionmsg_payload_reply_path *obs2_reply_path,
const char *replyfield,
const u8 *replydata)
{
struct out_req *req;
size_t nhops;

/* Exactly one must be set! */
assert(!reply_path != !obs2_reply_path);
if (obs2_reply_path)
return send_obs2_onion_reply(cmd, obs2_reply_path, replyfield, replydata);

req = jsonrpc_request_start(cmd->plugin, cmd, "sendonionmessage",
finished, sendonionmessage_error, NULL);

json_add_pubkey(req->js, "first_id", &reply_path->first_node_id);
json_add_pubkey(req->js, "blinding", &reply_path->blinding);
json_array_start(req->js, "hops");

nhops = tal_count(reply_path->path);
for (size_t i = 0; i < nhops; i++) {
struct tlv_onionmsg_payload *omp;
u8 *tlv;

json_object_start(req->js, NULL);
json_add_pubkey(req->js, "id", &reply_path->path[i]->node_id);

omp = tlv_onionmsg_payload_new(tmpctx);
omp->encrypted_data_tlv = reply_path->path[i]->encrypted_recipient_data;

/* Put payload in last hop. */
if (i == nhops - 1) {
if (streq(replyfield, "invoice")) {
omp->invoice = cast_const(u8 *, replydata);
} else {
assert(streq(replyfield, "invoice_error"));
omp->invoice_error = cast_const(u8 *, replydata);
}
}
tlv = tal_arr(tmpctx, u8, 0);
towire_onionmsg_payload(&tlv, omp);
json_add_hex_talarr(req->js, "tlv", tlv);
json_object_end(req->js);
}
json_array_end(req->js);
return send_outreq(cmd->plugin, req);
}

static struct command_result *onion_message_modern_call(struct command *cmd,
const char *buf,
const jsmntok_t *params)
Expand Down
3 changes: 2 additions & 1 deletion plugins/offers.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ struct command;
/* Helper to send a reply */
struct command_result *WARN_UNUSED_RESULT
send_onion_reply(struct command *cmd,
struct tlv_obs2_onionmsg_payload_reply_path *reply_path,
struct tlv_onionmsg_payload_reply_path *reply_path,
struct tlv_obs2_onionmsg_payload_reply_path *obs2_reply_path,
const char *replyfield,
const u8 *replydata);
#endif /* LIGHTNING_PLUGINS_OFFERS_H */
11 changes: 7 additions & 4 deletions plugins/offers_inv_hook.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ struct inv {
struct tlv_invoice *inv;

/* May be NULL */
struct tlv_obs2_onionmsg_payload_reply_path *reply_path;
struct tlv_obs2_onionmsg_payload_reply_path *obs2_reply_path;
struct tlv_onionmsg_payload_reply_path *reply_path;

/* The offer, once we've looked it up. */
struct tlv_offer *offer;
Expand Down Expand Up @@ -39,7 +40,7 @@ fail_inv_level(struct command *cmd,
plugin_log(cmd->plugin, l, "%s", msg);

/* Only reply if they gave us a path */
if (!inv->reply_path)
if (!inv->reply_path && !inv->obs2_reply_path)
return command_hook_success(cmd);

/* Don't send back internal error details. */
Expand All @@ -53,7 +54,8 @@ fail_inv_level(struct command *cmd,

errdata = tal_arr(cmd, u8, 0);
towire_invoice_error(&errdata, err);
return send_onion_reply(cmd, inv->reply_path, "invoice_error", errdata);
return send_onion_reply(cmd, inv->reply_path, inv->obs2_reply_path,
"invoice_error", errdata);
}

static struct command_result *WARN_UNUSED_RESULT
Expand Down Expand Up @@ -322,7 +324,8 @@ struct command_result *handle_invoice(struct command *cmd,
int bad_feature;
struct sha256 m, shash;

inv->reply_path = tal_steal(inv, reply_path);
inv->obs2_reply_path = tal_steal(inv, reply_path);
inv->reply_path = NULL;

inv->inv = tlv_invoice_new(cmd);
if (!fromwire_invoice(&invbin, &len, inv->inv)) {
Expand Down
Loading

0 comments on commit e7b2633

Please sign in to comment.