lightningd: add routine to directly inject an onion message.

Unlike "sendonionmessage" which instructs us to send to a peer, this
process it locally (presumably, it contains the next hop).  This is
useful because it allows us to process an onion message which starts
with us (a legal case for a blinded path supplied by someone else!).
It also opens the door to bolt12 self-pay.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell 2024-07-10 12:23:19 +09:30 committed by Vincenzo Palazzo
parent ba82592196
commit b5f921ce0a
6 changed files with 149 additions and 26 deletions

View File

@ -2174,6 +2174,10 @@ static struct io_plan *recv_req(struct io_conn *conn,
set_custommsgs(daemon, msg);
goto out;
case WIRE_CONNECTD_INJECT_ONIONMSG:
inject_onionmsg_req(daemon, msg);
goto out;
case WIRE_CONNECTD_DEV_MEMLEAK:
if (daemon->developer) {
dev_connect_memleak(daemon, msg);
@ -2216,6 +2220,7 @@ static struct io_plan *recv_req(struct io_conn *conn,
case WIRE_CONNECTD_CUSTOMMSG_IN:
case WIRE_CONNECTD_PEER_DISCONNECT_DONE:
case WIRE_CONNECTD_START_SHUTDOWN_REPLY:
case WIRE_CONNECTD_INJECT_ONIONMSG_REPLY:
break;
}

View File

@ -144,6 +144,16 @@ msgdata,connectd_send_onionmsg,onion_len,u16,
msgdata,connectd_send_onionmsg,onion,u8,onion_len
msgdata,connectd_send_onionmsg,blinding,pubkey,
# Lightningd tells us to digest an onion message.
msgtype,connectd_inject_onionmsg,2042
msgdata,connectd_inject_onionmsg,blinding,pubkey,
msgdata,connectd_inject_onionmsg,onion_len,u16,
msgdata,connectd_inject_onionmsg,onion,u8,onion_len
# Reply. If error isn't empty, something went wrong.
msgtype,connectd_inject_onionmsg_reply,2142
msgdata,connectd_inject_onionmsg_reply,err,wirestring,
# A custom message that we got from a peer and don't know how to handle, so we
# forward it to the master for further handling.
msgtype,connectd_custommsg_in,2110

1 #include <bitcoin/block.h>
144 msgtype,connectd_dev_exhaust_fds,2036 msgdata,connectd_custommsg_out,msg,u8,msg_len
145 # master -> connect: set artificial maximum reply_channel_range size. # master -> connectd: we're shutting down, no new connections.
146 msgtype,connectd_dev_set_max_scids_encode_size,2035 msgtype,connectd_start_shutdown,2031
147 # connect - >master: acknowledged.
148 msgtype,connectd_start_shutdown_reply,2131
149 # master -> connect: stop sending gossip.
150 msgtype,connectd_dev_suppress_gossip,2032
151 # master -> connect: waste all your fds.
152 msgtype,connectd_dev_exhaust_fds,2036
153 # master -> connect: set artificial maximum reply_channel_range size.
154 msgtype,connectd_dev_set_max_scids_encode_size,2035
155 msgdata,connectd_dev_set_max_scids_encode_size,max,u32,
156
157
158
159

View File

@ -1,6 +1,7 @@
/*~ This contains all the code to handle onion messages. */
#include "config.h"
#include <ccan/cast/cast.h>
#include <ccan/tal/str/str.h>
#include <common/blindedpath.h>
#include <common/blinding.h>
#include <common/daemon_conn.h>
@ -35,12 +36,11 @@ void onionmsg_req(struct daemon *daemon, const u8 *msg)
}
}
/* Peer sends an onion msg. */
void handle_onion_message(struct daemon *daemon,
struct peer *peer, const u8 *msg)
static const char *handle_onion(const tal_t *ctx,
struct daemon *daemon,
const struct pubkey *blinding,
const u8 *onion)
{
struct pubkey blinding;
u8 *onion;
u8 *next_onion_msg;
struct pubkey next_node;
struct tlv_onionmsg_tlv *final_om;
@ -48,26 +48,12 @@ void handle_onion_message(struct daemon *daemon,
struct secret *final_path_id;
const char *err;
/* Ignore unless explicitly turned on. */
if (!feature_offered(daemon->our_features->bits[NODE_ANNOUNCE_FEATURE],
OPT_ONION_MESSAGES))
return;
/* FIXME: ratelimit! */
if (!fromwire_onion_message(msg, msg, &blinding, &onion)) {
inject_peer_msg(peer,
towire_warningfmt(NULL, NULL,
"Bad onion_message"));
return;
}
err = onion_message_parse(tmpctx, onion, &blinding,
err = onion_message_parse(tmpctx, onion, blinding,
&daemon->mykey,
&next_onion_msg, &next_node,
&final_om, &final_alias, &final_path_id);
if (err) {
status_peer_debug(&peer->id, "%s", err);
return;
return tal_steal(ctx, err);
}
if (final_om) {
@ -91,12 +77,50 @@ void handle_onion_message(struct daemon *daemon,
node_id_from_pubkey(&next_node_id, &next_node);
next_peer = peer_htable_get(daemon->peers, &next_node_id);
if (!next_peer) {
status_peer_debug(&peer->id,
"onion msg: unknown next peer %s",
fmt_pubkey(tmpctx, &next_node));
return;
return tal_fmt(ctx, "onion msg: unknown next peer %s",
fmt_pubkey(tmpctx, &next_node));
}
inject_peer_msg(next_peer, take(next_onion_msg));
}
return NULL;
}
/* Peer sends an onion msg, or (if peer NULL) lightningd injects one. */
void handle_onion_message(struct daemon *daemon,
struct peer *peer, const u8 *msg)
{
struct pubkey blinding;
u8 *onion;
/* Ignore unless explicitly turned on. */
if (!feature_offered(daemon->our_features->bits[NODE_ANNOUNCE_FEATURE],
OPT_ONION_MESSAGES))
return;
/* FIXME: ratelimit! */
if (!fromwire_onion_message(msg, msg, &blinding, &onion)) {
inject_peer_msg(peer,
towire_warningfmt(NULL, NULL,
"Bad onion_message"));
return;
}
handle_onion(tmpctx, daemon, &blinding, onion);
}
void inject_onionmsg_req(struct daemon *daemon, const u8 *msg)
{
u8 *onionmsg;
struct pubkey blinding;
const char *err;
if (!fromwire_connectd_inject_onionmsg(msg, msg, &blinding, &onionmsg))
master_badmsg(WIRE_CONNECTD_INJECT_ONIONMSG, msg);
err = handle_onion(tmpctx, daemon, &blinding, onionmsg);
daemon_conn_send(daemon->master,
take(towire_connectd_inject_onionmsg_reply(NULL, err ? err : "")));
}

View File

@ -3,11 +3,14 @@
#include "config.h"
#include <ccan/short_types/short_types.h>
/* Onion message comes in from peer */
/* Onion message comes in from peer. */
void handle_onion_message(struct daemon *daemon,
struct peer *peer, const u8 *msg);
/* Lightningd tells us to send an onion message */
void onionmsg_req(struct daemon *daemon, const u8 *msg);
/* Lightning tells us unwrap onion message as if from peer */
void inject_onionmsg_req(struct daemon *daemon, const u8 *msg);
#endif /* LIGHTNING_CONNECTD_ONION_MESSAGE_H */

View File

@ -608,6 +608,7 @@ static unsigned connectd_msg(struct subd *connectd, const u8 *msg, const int *fd
case WIRE_CONNECTD_PEER_CONNECT_SUBD:
case WIRE_CONNECTD_PING:
case WIRE_CONNECTD_SEND_ONIONMSG:
case WIRE_CONNECTD_INJECT_ONIONMSG:
case WIRE_CONNECTD_CUSTOMMSG_OUT:
case WIRE_CONNECTD_START_SHUTDOWN:
case WIRE_CONNECTD_SET_CUSTOMMSGS:
@ -619,6 +620,7 @@ static unsigned connectd_msg(struct subd *connectd, const u8 *msg, const int *fd
case WIRE_CONNECTD_DEV_MEMLEAK_REPLY:
case WIRE_CONNECTD_PING_REPLY:
case WIRE_CONNECTD_START_SHUTDOWN_REPLY:
case WIRE_CONNECTD_INJECT_ONIONMSG_REPLY:
break;
case WIRE_CONNECTD_PEER_CONNECTED:

View File

@ -249,3 +249,82 @@ static const struct json_command sendonionmessage_command = {
"Send message to {first_id}, using {blinding}, encoded over {hops} (id, tlv)"
};
AUTODATA(json_command, &sendonionmessage_command);
static void inject_onionmsg_reply(struct subd *connectd,
const u8 *reply,
const int *fds UNUSED,
struct command *cmd)
{
char *err;
if (!fromwire_connectd_inject_onionmsg_reply(cmd, reply, &err)) {
log_broken(connectd->ld->log, "bad onionmsg_reply: %s",
tal_hex(tmpctx, reply));
return;
}
if (strlen(err) == 0)
was_pending(command_success(cmd, json_stream_success(cmd)));
else
was_pending(command_fail(cmd, LIGHTNINGD, "%s", err));
}
static struct command_result *json_injectonionmessage(struct command *cmd,
const char *buffer,
const jsmntok_t *obj UNNEEDED,
const jsmntok_t *params)
{
struct onion_hop *hops;
struct pubkey *blinding;
struct sphinx_path *sphinx_path;
struct onionpacket *op;
struct secret *path_secrets;
size_t onion_size;
if (!param_check(cmd, buffer, params,
p_req("blinding", param_pubkey, &blinding),
p_req("hops", param_onion_hops, &hops),
NULL))
return command_param_failed();
if (!feature_offered(cmd->ld->our_features->bits[NODE_ANNOUNCE_FEATURE],
OPT_ONION_MESSAGES))
return command_fail(cmd, LIGHTNINGD,
"experimental-onion-messages not enabled");
/* Create an onion which encodes this. */
sphinx_path = sphinx_path_new(cmd, NULL);
for (size_t i = 0; i < tal_count(hops); i++)
sphinx_add_hop(sphinx_path, &hops[i].node, hops[i].tlv);
/* BOLT-onion-message #4:
* - SHOULD set `onion_message_packet` `len` to 1366 or 32834.
*/
if (sphinx_path_payloads_size(sphinx_path) <= ROUTING_INFO_SIZE)
onion_size = ROUTING_INFO_SIZE;
else
onion_size = 32768; /* VERSION_SIZE + HMAC_SIZE + PUBKEY_SIZE == 66 */
op = create_onionpacket(tmpctx, sphinx_path, onion_size, &path_secrets);
if (!op)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"Creating onion failed (tlvs too long?)");
if (command_check_only(cmd))
return command_check_done(cmd);
subd_req(cmd, cmd->ld->connectd,
take(towire_connectd_inject_onionmsg(NULL,
blinding,
serialize_onionpacket(tmpctx, op))),
-1, 0, inject_onionmsg_reply, cmd);
return command_still_pending(cmd);
}
static const struct json_command injectonionmessage_command = {
"injectonionmessage",
"utility",
json_injectonionmessage,
"Unwrap using {blinding}, encoded over {hops} (id, tlv)"
};
AUTODATA(json_command, &injectonionmessage_command);