mirror of
https://github.com/aljazceru/lightning.git
synced 2026-02-23 15:04:19 +01:00
gossipd: handle receipt of modern onion message.
And wire it through to the hook; update the plugins to recognize modern vs obs2 onions. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
@@ -458,6 +458,7 @@ static u8 *handle_obs2_onion_message(struct peer *peer, const u8 *msg)
|
||||
towire_tlvstream_raw(&omsg, om->fields);
|
||||
daemon_conn_send(peer->daemon->master,
|
||||
take(towire_gossipd_got_onionmsg_to_us(NULL,
|
||||
true, /* obs2 */
|
||||
&alias, self_id,
|
||||
reply_blinding,
|
||||
first_node_id,
|
||||
@@ -527,7 +528,148 @@ static struct io_plan *onionmsg_req(struct io_conn *conn, struct daemon *daemon,
|
||||
/* Peer sends an onion msg. */
|
||||
static u8 *handle_onion_message(struct peer *peer, const u8 *msg)
|
||||
{
|
||||
/* FIXME! */
|
||||
enum onion_wire badreason;
|
||||
struct onionpacket *op;
|
||||
struct pubkey blinding, ephemeral;
|
||||
struct route_step *rs;
|
||||
u8 *onion;
|
||||
struct tlv_onionmsg_payload *om;
|
||||
struct secret ss, onion_ss;
|
||||
const u8 *cursor;
|
||||
size_t max, maxlen;
|
||||
|
||||
/* Ignore unless explicitly turned on. */
|
||||
if (!feature_offered(peer->daemon->our_features->bits[NODE_ANNOUNCE_FEATURE],
|
||||
OPT_ONION_MESSAGES))
|
||||
return NULL;
|
||||
|
||||
/* FIXME: ratelimit! */
|
||||
if (!fromwire_onion_message(msg, msg, &blinding, &onion))
|
||||
return towire_warningfmt(peer, NULL, "Bad onion_message");
|
||||
|
||||
/* We unwrap the onion now. */
|
||||
op = parse_onionpacket(tmpctx, onion, tal_bytelen(onion), &badreason);
|
||||
if (!op) {
|
||||
status_peer_debug(&peer->id, "onion msg: can't parse onionpacket: %s",
|
||||
onion_wire_name(badreason));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ephemeral = op->ephemeralkey;
|
||||
if (!unblind_onion(&blinding, ecdh, &ephemeral, &ss)) {
|
||||
status_peer_debug(&peer->id, "onion msg: can't unblind onionpacket");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Now get onion shared secret and parse it. */
|
||||
ecdh(&ephemeral, &onion_ss);
|
||||
rs = process_onionpacket(tmpctx, op, &onion_ss, NULL, 0, false);
|
||||
if (!rs) {
|
||||
status_peer_debug(&peer->id,
|
||||
"onion msg: can't process onionpacket ss=%s",
|
||||
type_to_string(tmpctx, struct secret, &onion_ss));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* The raw payload is prepended with length in the modern onion. */
|
||||
cursor = rs->raw_payload;
|
||||
max = tal_bytelen(rs->raw_payload);
|
||||
maxlen = fromwire_bigsize(&cursor, &max);
|
||||
if (!cursor) {
|
||||
status_peer_debug(&peer->id, "onion msg: Invalid hop payload %s",
|
||||
tal_hex(tmpctx, rs->raw_payload));
|
||||
return NULL;
|
||||
}
|
||||
if (maxlen > max) {
|
||||
status_peer_debug(&peer->id, "onion msg: overlong hop payload %s",
|
||||
tal_hex(tmpctx, rs->raw_payload));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
om = tlv_onionmsg_payload_new(msg);
|
||||
if (!fromwire_onionmsg_payload(&cursor, &maxlen, om)) {
|
||||
status_peer_debug(&peer->id, "onion msg: invalid onionmsg_payload %s",
|
||||
tal_hex(tmpctx, rs->raw_payload));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (rs->nextcase == ONION_END) {
|
||||
struct pubkey *reply_blinding, *first_node_id, me, alias;
|
||||
const struct onionmsg_path **reply_path;
|
||||
struct secret *self_id;
|
||||
u8 *omsg;
|
||||
|
||||
if (!pubkey_from_node_id(&me, &peer->daemon->id)) {
|
||||
status_broken("Failed to convert own id");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Final enctlv is actually optional */
|
||||
if (!om->encrypted_data_tlv) {
|
||||
alias = me;
|
||||
self_id = NULL;
|
||||
} else if (!decrypt_final_enctlv(tmpctx, &blinding, &ss,
|
||||
om->encrypted_data_tlv, &me, &alias,
|
||||
&self_id)) {
|
||||
status_peer_debug(&peer->id,
|
||||
"onion msg: failed to decrypt enctlv"
|
||||
" %s", tal_hex(tmpctx, om->encrypted_data_tlv));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (om->reply_path) {
|
||||
first_node_id = &om->reply_path->first_node_id;
|
||||
reply_blinding = &om->reply_path->blinding;
|
||||
reply_path = cast_const2(const struct onionmsg_path **,
|
||||
om->reply_path->path);
|
||||
} else {
|
||||
first_node_id = NULL;
|
||||
reply_blinding = NULL;
|
||||
reply_path = NULL;
|
||||
}
|
||||
|
||||
/* We re-marshall here by policy, before handing to lightningd */
|
||||
omsg = tal_arr(tmpctx, u8, 0);
|
||||
towire_tlvstream_raw(&omsg, om->fields);
|
||||
daemon_conn_send(peer->daemon->master,
|
||||
take(towire_gossipd_got_onionmsg_to_us(NULL,
|
||||
false, /* !obs2 */
|
||||
&alias, self_id,
|
||||
reply_blinding,
|
||||
first_node_id,
|
||||
reply_path,
|
||||
omsg)));
|
||||
} else {
|
||||
struct pubkey next_node, next_blinding;
|
||||
struct peer *next_peer;
|
||||
struct node_id next_node_id;
|
||||
|
||||
/* This fails as expected if no enctlv. */
|
||||
if (!decrypt_enctlv(&blinding, &ss, om->encrypted_data_tlv, &next_node,
|
||||
&next_blinding)) {
|
||||
status_peer_debug(&peer->id,
|
||||
"onion msg: invalid enctlv %s",
|
||||
tal_hex(tmpctx, om->encrypted_data_tlv));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* FIXME: Handle short_channel_id! */
|
||||
node_id_from_pubkey(&next_node_id, &next_node);
|
||||
next_peer = find_peer(peer->daemon, &next_node_id);
|
||||
if (!next_peer) {
|
||||
status_peer_debug(&peer->id,
|
||||
"onion msg: unknown next peer %s",
|
||||
type_to_string(tmpctx,
|
||||
struct pubkey,
|
||||
&next_node));
|
||||
return NULL;
|
||||
}
|
||||
queue_peer_msg(next_peer,
|
||||
take(towire_onion_message(NULL,
|
||||
&next_blinding,
|
||||
serialize_onionpacket(tmpctx, rs->next))));
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -75,6 +75,7 @@ msgtype,gossipd_new_blockheight,3026
|
||||
msgdata,gossipd_new_blockheight,blockheight,u32,
|
||||
|
||||
msgtype,gossipd_got_onionmsg_to_us,3145
|
||||
msgdata,gossipd_got_onionmsg_to_us,obs2,bool,
|
||||
msgdata,gossipd_got_onionmsg_to_us,node_alias,pubkey,
|
||||
msgdata,gossipd_got_onionmsg_to_us,self_id,?secret,
|
||||
msgdata,gossipd_got_onionmsg_to_us,reply_blinding,?pubkey,
|
||||
|
||||
|
@@ -21,7 +21,9 @@ struct onion_message_hook_payload {
|
||||
struct pubkey *reply_first_node;
|
||||
struct pubkey *our_alias;
|
||||
|
||||
struct tlv_obs2_onionmsg_payload *om;
|
||||
/* Exactly one of these is set! */
|
||||
struct tlv_onionmsg_payload *om;
|
||||
struct tlv_obs2_onionmsg_payload *obs2_om;
|
||||
};
|
||||
|
||||
static void json_add_blindedpath(struct json_stream *stream,
|
||||
@@ -61,28 +63,55 @@ static void onion_message_serialize(struct onion_message_hook_payload *payload,
|
||||
}
|
||||
|
||||
/* Common convenience fields */
|
||||
if (payload->om->invoice_request)
|
||||
json_add_hex_talarr(stream, "invoice_request",
|
||||
payload->om->invoice_request);
|
||||
if (payload->om->invoice)
|
||||
json_add_hex_talarr(stream, "invoice", payload->om->invoice);
|
||||
if (payload->obs2_om) {
|
||||
json_add_bool(stream, "obs2", true);
|
||||
if (payload->obs2_om->invoice_request)
|
||||
json_add_hex_talarr(stream, "invoice_request",
|
||||
payload->obs2_om->invoice_request);
|
||||
if (payload->obs2_om->invoice)
|
||||
json_add_hex_talarr(stream, "invoice", payload->obs2_om->invoice);
|
||||
|
||||
if (payload->om->invoice_error)
|
||||
json_add_hex_talarr(stream, "invoice_error",
|
||||
payload->om->invoice_error);
|
||||
if (payload->obs2_om->invoice_error)
|
||||
json_add_hex_talarr(stream, "invoice_error",
|
||||
payload->obs2_om->invoice_error);
|
||||
|
||||
json_array_start(stream, "unknown_fields");
|
||||
for (size_t i = 0; i < tal_count(payload->om->fields); i++) {
|
||||
if (payload->om->fields[i].meta)
|
||||
continue;
|
||||
json_object_start(stream, NULL);
|
||||
json_add_u64(stream, "number", payload->om->fields[i].numtype);
|
||||
json_add_hex(stream, "value",
|
||||
payload->om->fields[i].value,
|
||||
payload->om->fields[i].length);
|
||||
json_object_end(stream);
|
||||
json_array_start(stream, "unknown_fields");
|
||||
for (size_t i = 0; i < tal_count(payload->obs2_om->fields); i++) {
|
||||
if (payload->obs2_om->fields[i].meta)
|
||||
continue;
|
||||
json_object_start(stream, NULL);
|
||||
json_add_u64(stream, "number", payload->obs2_om->fields[i].numtype);
|
||||
json_add_hex(stream, "value",
|
||||
payload->obs2_om->fields[i].value,
|
||||
payload->obs2_om->fields[i].length);
|
||||
json_object_end(stream);
|
||||
}
|
||||
json_array_end(stream);
|
||||
} else {
|
||||
json_add_bool(stream, "obs2", false);
|
||||
if (payload->om->invoice_request)
|
||||
json_add_hex_talarr(stream, "invoice_request",
|
||||
payload->om->invoice_request);
|
||||
if (payload->om->invoice)
|
||||
json_add_hex_talarr(stream, "invoice", payload->om->invoice);
|
||||
|
||||
if (payload->om->invoice_error)
|
||||
json_add_hex_talarr(stream, "invoice_error",
|
||||
payload->om->invoice_error);
|
||||
|
||||
json_array_start(stream, "unknown_fields");
|
||||
for (size_t i = 0; i < tal_count(payload->om->fields); i++) {
|
||||
if (payload->om->fields[i].meta)
|
||||
continue;
|
||||
json_object_start(stream, NULL);
|
||||
json_add_u64(stream, "number", payload->om->fields[i].numtype);
|
||||
json_add_hex(stream, "value",
|
||||
payload->om->fields[i].value,
|
||||
payload->om->fields[i].length);
|
||||
json_object_end(stream);
|
||||
}
|
||||
json_array_end(stream);
|
||||
}
|
||||
json_array_end(stream);
|
||||
json_object_end(stream);
|
||||
}
|
||||
|
||||
@@ -113,14 +142,15 @@ void handle_onionmsg_to_us(struct lightningd *ld, const u8 *msg)
|
||||
struct onion_message_hook_payload *payload;
|
||||
u8 *submsg;
|
||||
struct secret *self_id;
|
||||
bool obs2;
|
||||
size_t submsglen;
|
||||
const u8 *subptr;
|
||||
|
||||
payload = tal(ld, struct onion_message_hook_payload);
|
||||
payload->om = tlv_obs2_onionmsg_payload_new(payload);
|
||||
payload->our_alias = tal(payload, struct pubkey);
|
||||
|
||||
if (!fromwire_gossipd_got_onionmsg_to_us(payload, msg,
|
||||
&obs2,
|
||||
payload->our_alias,
|
||||
&self_id,
|
||||
&payload->reply_blinding,
|
||||
@@ -139,12 +169,25 @@ void handle_onionmsg_to_us(struct lightningd *ld, const u8 *msg)
|
||||
|
||||
submsglen = tal_bytelen(submsg);
|
||||
subptr = submsg;
|
||||
if (!fromwire_obs2_onionmsg_payload(&subptr,
|
||||
&submsglen, payload->om)) {
|
||||
tal_free(payload);
|
||||
log_broken(ld->log, "bad got_onionmsg_tous om: %s",
|
||||
tal_hex(tmpctx, msg));
|
||||
return;
|
||||
if (obs2) {
|
||||
payload->om = NULL;
|
||||
payload->obs2_om = tlv_obs2_onionmsg_payload_new(payload);
|
||||
if (!fromwire_obs2_onionmsg_payload(&subptr,
|
||||
&submsglen, payload->obs2_om)) {
|
||||
tal_free(payload);
|
||||
log_broken(ld->log, "bad got_onionmsg_tous obs2 om: %s",
|
||||
tal_hex(tmpctx, msg));
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
payload->obs2_om = NULL;
|
||||
payload->om = tlv_onionmsg_payload_new(payload);
|
||||
if (!fromwire_onionmsg_payload(&subptr, &submsglen, payload->om)) {
|
||||
tal_free(payload);
|
||||
log_broken(ld->log, "bad got_onionmsg_tous om: %s",
|
||||
tal_hex(tmpctx, msg));
|
||||
return;
|
||||
}
|
||||
}
|
||||
tal_free(submsg);
|
||||
|
||||
|
||||
@@ -140,7 +140,8 @@ static struct command_result *onion_message_modern_call(struct command *cmd,
|
||||
const jsmntok_t *params)
|
||||
{
|
||||
const jsmntok_t *om, *replytok, *invreqtok, *invtok;
|
||||
struct tlv_obs2_onionmsg_payload_reply_path *reply_path;
|
||||
struct tlv_obs2_onionmsg_payload_reply_path *obs2_reply_path = NULL;
|
||||
struct tlv_onionmsg_payload_reply_path *reply_path = NULL;
|
||||
|
||||
if (!offers_enabled)
|
||||
return command_hook_success(cmd);
|
||||
@@ -148,21 +149,31 @@ static struct command_result *onion_message_modern_call(struct command *cmd,
|
||||
om = json_get_member(buf, params, "onion_message");
|
||||
replytok = json_get_member(buf, om, "reply_blindedpath");
|
||||
if (replytok) {
|
||||
reply_path = json_to_obs2_reply_path(cmd, buf, replytok);
|
||||
if (!reply_path)
|
||||
plugin_err(cmd->plugin, "Invalid reply path %.*s?",
|
||||
json_tok_full_len(replytok),
|
||||
json_tok_full(buf, replytok));
|
||||
} else
|
||||
reply_path = NULL;
|
||||
bool obs2;
|
||||
json_to_bool(buf, json_get_member(buf, om, "obs2"), &obs2);
|
||||
if (obs2) {
|
||||
obs2_reply_path = json_to_obs2_reply_path(cmd, buf, replytok);
|
||||
if (!obs2_reply_path)
|
||||
plugin_err(cmd->plugin, "Invalid obs2 reply path %.*s?",
|
||||
json_tok_full_len(replytok),
|
||||
json_tok_full(buf, replytok));
|
||||
} else {
|
||||
reply_path = json_to_reply_path(cmd, buf, replytok);
|
||||
if (!reply_path)
|
||||
plugin_err(cmd->plugin, "Invalid reply path %.*s?",
|
||||
json_tok_full_len(replytok),
|
||||
json_tok_full(buf, replytok));
|
||||
}
|
||||
}
|
||||
|
||||
invreqtok = json_get_member(buf, om, "invoice_request");
|
||||
if (invreqtok) {
|
||||
const u8 *invreqbin = json_tok_bin_from_hex(tmpctx, buf, invreqtok);
|
||||
if (reply_path)
|
||||
if (reply_path || obs2_reply_path)
|
||||
return handle_invoice_request(cmd,
|
||||
invreqbin,
|
||||
reply_path);
|
||||
reply_path,
|
||||
obs2_reply_path);
|
||||
else
|
||||
plugin_log(cmd->plugin, LOG_DBG,
|
||||
"invoice_request without reply_path");
|
||||
@@ -172,7 +183,7 @@ static struct command_result *onion_message_modern_call(struct command *cmd,
|
||||
if (invtok) {
|
||||
const u8 *invbin = json_tok_bin_from_hex(tmpctx, buf, invtok);
|
||||
if (invbin)
|
||||
return handle_invoice(cmd, invbin, reply_path);
|
||||
return handle_invoice(cmd, invbin, reply_path, obs2_reply_path);
|
||||
}
|
||||
|
||||
return command_hook_success(cmd);
|
||||
|
||||
@@ -315,7 +315,8 @@ static struct command_result *listoffers_error(struct command *cmd,
|
||||
|
||||
struct command_result *handle_invoice(struct command *cmd,
|
||||
const u8 *invbin,
|
||||
struct tlv_obs2_onionmsg_payload_reply_path *reply_path STEALS)
|
||||
struct tlv_onionmsg_payload_reply_path *reply_path STEALS,
|
||||
struct tlv_obs2_onionmsg_payload_reply_path *obs2_reply_path STEALS)
|
||||
{
|
||||
size_t len = tal_count(invbin);
|
||||
struct inv *inv = tal(cmd, struct inv);
|
||||
@@ -324,8 +325,8 @@ struct command_result *handle_invoice(struct command *cmd,
|
||||
int bad_feature;
|
||||
struct sha256 m, shash;
|
||||
|
||||
inv->obs2_reply_path = tal_steal(inv, reply_path);
|
||||
inv->reply_path = NULL;
|
||||
inv->obs2_reply_path = tal_steal(inv, obs2_reply_path);
|
||||
inv->reply_path = tal_steal(inv, reply_path);
|
||||
|
||||
inv->inv = tlv_invoice_new(cmd);
|
||||
if (!fromwire_invoice(&invbin, &len, inv->inv)) {
|
||||
|
||||
@@ -6,5 +6,6 @@
|
||||
/* We got an onionmessage with an invoice! reply_path could be NULL. */
|
||||
struct command_result *handle_invoice(struct command *cmd,
|
||||
const u8 *invbin,
|
||||
struct tlv_obs2_onionmsg_payload_reply_path *reply_path STEALS);
|
||||
struct tlv_onionmsg_payload_reply_path *reply_path STEALS,
|
||||
struct tlv_obs2_onionmsg_payload_reply_path *obs2_reply_path STEALS);
|
||||
#endif /* LIGHTNING_PLUGINS_OFFERS_INV_HOOK_H */
|
||||
|
||||
@@ -833,15 +833,16 @@ static struct command_result *handle_offerless_request(struct command *cmd,
|
||||
|
||||
struct command_result *handle_invoice_request(struct command *cmd,
|
||||
const u8 *invreqbin,
|
||||
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)
|
||||
{
|
||||
size_t len = tal_count(invreqbin);
|
||||
struct invreq *ir = tal(cmd, struct invreq);
|
||||
struct out_req *req;
|
||||
int bad_feature;
|
||||
|
||||
ir->obs2_reply_path = tal_steal(ir, reply_path);
|
||||
ir->reply_path = NULL;
|
||||
ir->obs2_reply_path = tal_steal(ir, obs2_reply_path);
|
||||
ir->reply_path = tal_steal(ir, reply_path);
|
||||
|
||||
ir->invreq = tlv_invoice_request_new(cmd);
|
||||
if (!fromwire_invoice_request(&invreqbin, &len, ir->invreq)) {
|
||||
|
||||
@@ -8,5 +8,6 @@ extern u32 cltv_final;
|
||||
/* We got an onionmessage with an invreq! */
|
||||
struct command_result *handle_invoice_request(struct command *cmd,
|
||||
const u8 *invreqbin,
|
||||
struct tlv_obs2_onionmsg_payload_reply_path *reply_path STEALS);
|
||||
struct tlv_onionmsg_payload_reply_path *reply_path STEALS,
|
||||
struct tlv_obs2_onionmsg_payload_reply_path *obs2_reply_path STEALS);
|
||||
#endif /* LIGHTNING_PLUGINS_OFFERS_INVREQ_HOOK_H */
|
||||
|
||||
Reference in New Issue
Block a user