mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-03-15 20:09:18 +01:00
offers: send a modern onion reply in response to a modern request.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au> Changelog-Experimental: Protocol: Updated onion_message support to match updated draft specification (with backwards compat for old version)
This commit is contained in:
parent
af7e0a1183
commit
368fc07d05
6 changed files with 148 additions and 19 deletions
105
plugins/offers.c
105
plugins/offers.c
|
@ -1,6 +1,7 @@
|
|||
/* This plugin covers both sending and receiving offers */
|
||||
#include <bitcoin/chainparams.h>
|
||||
#include <ccan/array_size/array_size.h>
|
||||
#include <ccan/cast/cast.h>
|
||||
#include <ccan/tal/str/str.h>
|
||||
#include <common/bech32.h>
|
||||
#include <common/bolt11.h>
|
||||
|
@ -37,8 +38,53 @@ static struct command_result *sendonionmessage_error(struct command *cmd,
|
|||
return command_hook_success(cmd);
|
||||
}
|
||||
|
||||
/* FIXME: replyfield string interface is to accomodate obsolete API */
|
||||
static struct command_result *
|
||||
send_modern_onion_reply(struct command *cmd,
|
||||
struct tlv_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",
|
||||
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");
|
||||
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->enctlv = reply_path->path[i]->enctlv;
|
||||
|
||||
/* 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);
|
||||
}
|
||||
|
||||
struct command_result *WARN_UNUSED_RESULT
|
||||
send_onion_reply(struct command *cmd,
|
||||
struct tlv_onionmsg_payload_reply_path *reply_path,
|
||||
const char *jsonbuf,
|
||||
const jsmntok_t *replytok,
|
||||
const char *replyfield,
|
||||
|
@ -48,7 +94,11 @@ send_onion_reply(struct command *cmd,
|
|||
size_t i;
|
||||
const jsmntok_t *t;
|
||||
|
||||
plugin_log(cmd->plugin, LOG_DBG, "sending reply %s = %s",
|
||||
if (reply_path)
|
||||
return send_modern_onion_reply(cmd, reply_path,
|
||||
replyfield, replydata);
|
||||
|
||||
plugin_log(cmd->plugin, LOG_DBG, "sending obs reply %s = %s",
|
||||
replyfield, tal_hex(tmpctx, replydata));
|
||||
|
||||
/* Send to requester, using return route. */
|
||||
|
@ -97,7 +147,7 @@ static struct command_result *onion_message_call(struct command *cmd,
|
|||
replytok = json_get_member(buf, om, "reply_path");
|
||||
if (replytok && replytok->size > 0)
|
||||
return handle_invoice_request(cmd, buf,
|
||||
invreqtok, replytok);
|
||||
invreqtok, replytok, NULL);
|
||||
else
|
||||
plugin_log(cmd->plugin, LOG_DBG,
|
||||
"invoice_request without reply_path");
|
||||
|
@ -108,7 +158,52 @@ static struct command_result *onion_message_call(struct command *cmd,
|
|||
const jsmntok_t *replytok;
|
||||
|
||||
replytok = json_get_member(buf, om, "reply_path");
|
||||
return handle_invoice(cmd, buf, invtok, replytok);
|
||||
return handle_invoice(cmd, buf, invtok, replytok, NULL);
|
||||
}
|
||||
|
||||
return command_hook_success(cmd);
|
||||
}
|
||||
|
||||
static struct command_result *onion_message_modern_call(struct command *cmd,
|
||||
const char *buf,
|
||||
const jsmntok_t *params)
|
||||
{
|
||||
const jsmntok_t *om, *replytok, *invreqtok, *invtok;
|
||||
bool obsolete;
|
||||
struct tlv_onionmsg_payload_reply_path *reply_path;
|
||||
|
||||
if (!offers_enabled)
|
||||
return command_hook_success(cmd);
|
||||
|
||||
om = json_get_member(buf, params, "onion_message");
|
||||
json_to_bool(buf, json_get_member(buf, om, "obsolete"), &obsolete);
|
||||
if (obsolete)
|
||||
return command_hook_success(cmd);
|
||||
|
||||
replytok = json_get_member(buf, om, "reply_blindedpath");
|
||||
if (replytok) {
|
||||
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));
|
||||
} else
|
||||
reply_path = NULL;
|
||||
|
||||
invreqtok = json_get_member(buf, om, "invoice_request");
|
||||
if (invreqtok) {
|
||||
if (reply_path)
|
||||
return handle_invoice_request(cmd, buf,
|
||||
invreqtok,
|
||||
NULL, reply_path);
|
||||
else
|
||||
plugin_log(cmd->plugin, LOG_DBG,
|
||||
"invoice_request without reply_path");
|
||||
}
|
||||
|
||||
invtok = json_get_member(buf, om, "invoice");
|
||||
if (invtok) {
|
||||
return handle_invoice(cmd, buf, invtok, NULL, reply_path);
|
||||
}
|
||||
|
||||
return command_hook_success(cmd);
|
||||
|
@ -119,6 +214,10 @@ static const struct plugin_hook hooks[] = {
|
|||
"onion_message",
|
||||
onion_message_call
|
||||
},
|
||||
{
|
||||
"onion_message_blinded",
|
||||
onion_message_modern_call
|
||||
},
|
||||
};
|
||||
|
||||
struct decodable {
|
||||
|
|
|
@ -9,6 +9,9 @@ struct command;
|
|||
/* Helper to send a reply */
|
||||
struct command_result *WARN_UNUSED_RESULT
|
||||
send_onion_reply(struct command *cmd,
|
||||
/* Preferred */
|
||||
struct tlv_onionmsg_payload_reply_path *reply_path,
|
||||
/* Used if reply_path is NULL */
|
||||
const char *jsonbuf,
|
||||
const jsmntok_t *replytok,
|
||||
const char *replyfield,
|
||||
|
|
|
@ -13,6 +13,7 @@ struct inv {
|
|||
const char *buf;
|
||||
/* May be NULL */
|
||||
const jsmntok_t *replytok;
|
||||
struct tlv_onionmsg_payload_reply_path *reply_path;
|
||||
|
||||
/* The offer, once we've looked it up. */
|
||||
struct tlv_offer *offer;
|
||||
|
@ -40,7 +41,7 @@ fail_inv_level(struct command *cmd,
|
|||
plugin_log(cmd->plugin, l, "%s", msg);
|
||||
|
||||
/* Only reply if they gave us a path */
|
||||
if (!inv->replytok)
|
||||
if (!inv->replytok && !inv->reply_path)
|
||||
return command_hook_success(cmd);
|
||||
|
||||
/* Don't send back internal error details. */
|
||||
|
@ -54,7 +55,8 @@ fail_inv_level(struct command *cmd,
|
|||
|
||||
errdata = tal_arr(cmd, u8, 0);
|
||||
towire_invoice_error(&errdata, err);
|
||||
return send_onion_reply(cmd, inv->buf, inv->replytok, "invoice_error", errdata);
|
||||
return send_onion_reply(cmd, inv->reply_path, inv->buf, inv->replytok,
|
||||
"invoice_error", errdata);
|
||||
}
|
||||
|
||||
static struct command_result *WARN_UNUSED_RESULT
|
||||
|
@ -315,7 +317,8 @@ static struct command_result *listoffers_error(struct command *cmd,
|
|||
struct command_result *handle_invoice(struct command *cmd,
|
||||
const char *buf,
|
||||
const jsmntok_t *invtok,
|
||||
const jsmntok_t *replytok)
|
||||
const jsmntok_t *replytok,
|
||||
struct tlv_onionmsg_payload_reply_path *reply_path)
|
||||
{
|
||||
const u8 *invbin = json_tok_bin_from_hex(cmd, buf, invtok);
|
||||
size_t len = tal_count(invbin);
|
||||
|
@ -325,10 +328,17 @@ struct command_result *handle_invoice(struct command *cmd,
|
|||
int bad_feature;
|
||||
struct sha256 m, shash;
|
||||
|
||||
/* Make a copy of entire buffer, for later. */
|
||||
inv->buf = tal_dup_arr(inv, char, buf, replytok->end, 0);
|
||||
inv->replytok = tal_dup_arr(inv, jsmntok_t, replytok,
|
||||
json_next(replytok) - replytok, 0);
|
||||
if (reply_path) {
|
||||
inv->buf = NULL;
|
||||
inv->replytok = NULL;
|
||||
inv->reply_path = reply_path;
|
||||
} else {
|
||||
/* Make a copy of entire buffer, for later. */
|
||||
inv->buf = tal_dup_arr(inv, char, buf, replytok->end, 0);
|
||||
inv->replytok = tal_dup_arr(inv, jsmntok_t, replytok,
|
||||
json_next(replytok) - replytok, 0);
|
||||
inv->reply_path = NULL;
|
||||
}
|
||||
|
||||
inv->inv = tlv_invoice_new(cmd);
|
||||
if (!fromwire_invoice(&invbin, &len, inv->inv)) {
|
||||
|
|
|
@ -3,9 +3,10 @@
|
|||
#include "config.h"
|
||||
#include <plugins/libplugin.h>
|
||||
|
||||
/* We got an onionmessage with an invoice! replytok could be NULL. */
|
||||
/* We got an onionmessage with an invoice! replytok/reply_path could be NULL. */
|
||||
struct command_result *handle_invoice(struct command *cmd,
|
||||
const char *buf,
|
||||
const jsmntok_t *invtok,
|
||||
const jsmntok_t *replytok);
|
||||
const jsmntok_t *replytok,
|
||||
struct tlv_onionmsg_payload_reply_path *reply_path);
|
||||
#endif /* LIGHTNING_PLUGINS_OFFERS_INV_HOOK_H */
|
||||
|
|
|
@ -16,7 +16,10 @@
|
|||
struct invreq {
|
||||
struct tlv_invoice_request *invreq;
|
||||
const char *buf;
|
||||
/* If obsolete style */
|
||||
const jsmntok_t *replytok;
|
||||
/* If modern style. */
|
||||
struct tlv_onionmsg_payload_reply_path *reply_path;
|
||||
|
||||
/* The offer, once we've looked it up. */
|
||||
struct tlv_offer *offer;
|
||||
|
@ -60,7 +63,8 @@ fail_invreq_level(struct command *cmd,
|
|||
|
||||
errdata = tal_arr(cmd, u8, 0);
|
||||
towire_invoice_error(&errdata, err);
|
||||
return send_onion_reply(cmd, invreq->buf, invreq->replytok,
|
||||
return send_onion_reply(cmd, invreq->reply_path,
|
||||
invreq->buf, invreq->replytok,
|
||||
"invoice_error", errdata);
|
||||
}
|
||||
|
||||
|
@ -180,7 +184,8 @@ static struct command_result *createinvoice_done(struct command *cmd,
|
|||
json_tok_full(buf, t));
|
||||
}
|
||||
|
||||
return send_onion_reply(cmd, ir->buf, ir->replytok, "invoice", rawinv);
|
||||
return send_onion_reply(cmd, ir->reply_path, ir->buf, ir->replytok,
|
||||
"invoice", rawinv);
|
||||
}
|
||||
|
||||
static struct command_result *createinvoice_error(struct command *cmd,
|
||||
|
@ -828,7 +833,8 @@ static struct command_result *handle_offerless_request(struct command *cmd,
|
|||
struct command_result *handle_invoice_request(struct command *cmd,
|
||||
const char *buf,
|
||||
const jsmntok_t *invreqtok,
|
||||
const jsmntok_t *replytok)
|
||||
const jsmntok_t *replytok,
|
||||
struct tlv_onionmsg_payload_reply_path *reply_path)
|
||||
{
|
||||
const u8 *invreqbin = json_tok_bin_from_hex(cmd, buf, invreqtok);
|
||||
size_t len = tal_count(invreqbin);
|
||||
|
@ -837,9 +843,16 @@ struct command_result *handle_invoice_request(struct command *cmd,
|
|||
int bad_feature;
|
||||
|
||||
/* Make a copy of entire buffer, for later. */
|
||||
ir->buf = tal_dup_arr(ir, char, buf, replytok->end, 0);
|
||||
ir->replytok = tal_dup_arr(ir, jsmntok_t, replytok,
|
||||
json_next(replytok) - replytok, 0);
|
||||
if (reply_path) {
|
||||
ir->buf = NULL;
|
||||
ir->replytok = NULL;
|
||||
ir->reply_path = reply_path;
|
||||
} else {
|
||||
ir->buf = tal_dup_arr(ir, char, buf, replytok->end, 0);
|
||||
ir->replytok = tal_dup_arr(ir, jsmntok_t, replytok,
|
||||
json_next(replytok) - replytok, 0);
|
||||
ir->reply_path = NULL;
|
||||
}
|
||||
|
||||
ir->invreq = tlv_invoice_request_new(cmd);
|
||||
if (!fromwire_invoice_request(&invreqbin, &len, ir->invreq)) {
|
||||
|
|
|
@ -9,5 +9,8 @@ extern u32 cltv_final;
|
|||
struct command_result *handle_invoice_request(struct command *cmd,
|
||||
const char *buf,
|
||||
const jsmntok_t *invreqtok,
|
||||
const jsmntok_t *replytok);
|
||||
/* Obsolete onion */
|
||||
const jsmntok_t *replytok,
|
||||
/* Modern onion */
|
||||
struct tlv_onionmsg_payload_reply_path *reply_path);
|
||||
#endif /* LIGHTNING_PLUGINS_OFFERS_INVREQ_HOOK_H */
|
||||
|
|
Loading…
Add table
Reference in a new issue