mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-03-26 20:30:59 +01:00
offers: handle scid in blinded reply path first_node_id
field.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au> Changelog-EXPERIMENTAL: offers: we now understand blinded paths which use a short-channel-id(+direction) as entry point.
This commit is contained in:
parent
cb2c4963f2
commit
e338452c19
4 changed files with 106 additions and 10 deletions
|
@ -212,7 +212,7 @@ $(PLUGIN_KEYSEND_OBJS): $(PLUGIN_PAY_LIB_HEADER)
|
|||
|
||||
plugins/spenderp: bitcoin/block.o bitcoin/preimage.o bitcoin/psbt.o common/psbt_open.o common/json_channel_type.o common/channel_type.o common/features.o wire/peer_wiregen.o $(PLUGIN_SPENDER_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS)
|
||||
|
||||
plugins/offers: $(PLUGIN_OFFERS_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) common/addr.o common/bolt12.o common/bolt12_merkle.o common/bolt11_json.o common/iso4217.o $(WIRE_OBJS) $(WIRE_BOLT12_OBJS) bitcoin/block.o common/channel_id.o bitcoin/preimage.o common/blindedpath.o common/invoice_path_id.o common/blinding.o common/hmac.o common/json_blinded_path.o $(JSMN_OBJS)
|
||||
plugins/offers: $(PLUGIN_OFFERS_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) common/addr.o common/bolt12.o common/bolt12_merkle.o common/bolt11_json.o common/iso4217.o $(WIRE_OBJS) $(WIRE_BOLT12_OBJS) bitcoin/block.o common/channel_id.o bitcoin/preimage.o common/blindedpath.o common/invoice_path_id.o common/blinding.o common/hmac.o common/json_blinded_path.o common/gossmap.o common/fp16.o $(JSMN_OBJS)
|
||||
|
||||
plugins/fetchinvoice: $(PLUGIN_FETCHINVOICE_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) common/bolt12.o common/bolt12_merkle.o common/iso4217.o $(WIRE_OBJS) $(WIRE_BOLT12_OBJS) bitcoin/block.o common/channel_id.o bitcoin/preimage.o $(JSMN_OBJS) common/gossmap.o common/fp16.o common/dijkstra.o common/route.o common/blindedpath.o common/hmac.o common/blinding.o common/gossmods_listpeerchannels.o
|
||||
|
||||
|
|
|
@ -38,6 +38,9 @@ struct sent {
|
|||
/* Path to use (including self) */
|
||||
struct pubkey *path;
|
||||
|
||||
/* When creating blinded return path, use scid not pubkey for intro node. */
|
||||
struct short_channel_id_dir *dev_path_use_scidd;
|
||||
|
||||
/* The invreq we sent, OR the invoice we sent */
|
||||
struct tlv_invoice_request *invreq;
|
||||
|
||||
|
@ -597,6 +600,7 @@ send_modern_message(struct command *cmd,
|
|||
static struct blinded_path *blinded_path(const tal_t *ctx,
|
||||
struct command *cmd,
|
||||
const struct pubkey *ids,
|
||||
const struct short_channel_id_dir *first_scidd,
|
||||
const struct secret *pathsecret)
|
||||
{
|
||||
struct privkey first_blinding, blinding_iter;
|
||||
|
@ -608,7 +612,10 @@ static struct blinded_path *blinded_path(const tal_t *ctx,
|
|||
nhops = tal_count(ids);
|
||||
|
||||
assert(nhops > 0);
|
||||
sciddir_or_pubkey_from_pubkey(&path->first_node_id, &ids[0]);
|
||||
if (first_scidd)
|
||||
sciddir_or_pubkey_from_scidd(&path->first_node_id, first_scidd);
|
||||
else
|
||||
sciddir_or_pubkey_from_pubkey(&path->first_node_id, &ids[0]);
|
||||
assert(pubkey_eq(&ids[nhops-1], &local_id));
|
||||
|
||||
randombytes_buf(&first_blinding, sizeof(first_blinding));
|
||||
|
@ -673,7 +680,8 @@ static struct command_result *make_reply_path(struct command *cmd,
|
|||
for (int i = nhops - 2; i >= 0; i--)
|
||||
ids[nhops - 2 - i] = sending->sent->path[i];
|
||||
|
||||
rpath = blinded_path(cmd, cmd, ids, sending->sent->reply_secret);
|
||||
rpath = blinded_path(cmd, cmd, ids, sending->sent->dev_path_use_scidd,
|
||||
sending->sent->reply_secret);
|
||||
return send_modern_message(cmd, rpath, sending);
|
||||
}
|
||||
|
||||
|
@ -927,6 +935,22 @@ static struct command_result *invreq_done(struct command *cmd,
|
|||
return send_outreq(cmd->plugin, req);
|
||||
}
|
||||
|
||||
static struct command_result *param_dev_scidd(struct command *cmd, const char *name,
|
||||
const char *buffer, const jsmntok_t *tok,
|
||||
struct short_channel_id_dir **scidd)
|
||||
{
|
||||
if (!plugin_developer_mode(cmd->plugin))
|
||||
return command_fail_badparam(cmd, name, buffer, tok,
|
||||
"not available outside --developer mode");
|
||||
|
||||
*scidd = tal(cmd, struct short_channel_id_dir);
|
||||
if (short_channel_id_dir_from_str(buffer + tok->start, tok->end - tok->start, *scidd))
|
||||
return NULL;
|
||||
|
||||
return command_fail_badparam(cmd, name, buffer, tok,
|
||||
"should be a short_channel_id of form NxNxN/dir");
|
||||
}
|
||||
|
||||
/* Fetches an invoice for this offer, and makes sure it corresponds. */
|
||||
static struct command_result *json_fetchinvoice(struct command *cmd,
|
||||
const char *buffer,
|
||||
|
@ -950,6 +974,7 @@ static struct command_result *json_fetchinvoice(struct command *cmd,
|
|||
p_opt("recurrence_label", param_string, &rec_label),
|
||||
p_opt_def("timeout", param_number, &timeout, 60),
|
||||
p_opt("payer_note", param_string, &payer_note),
|
||||
p_opt("dev_path_use_scidd", param_dev_scidd, &sent->dev_path_use_scidd),
|
||||
NULL))
|
||||
return command_param_failed();
|
||||
|
||||
|
@ -1405,6 +1430,8 @@ static struct command_result *json_sendinvoice(struct command *cmd,
|
|||
NULL))
|
||||
return command_param_failed();
|
||||
|
||||
sent->dev_path_use_scidd = NULL;
|
||||
|
||||
/* BOLT-offers #12:
|
||||
* - if the invoice is in response to an `invoice_request`:
|
||||
* - MUST copy all non-signature fields from the `invoice_request`
|
||||
|
@ -1539,6 +1566,7 @@ static struct command_result *json_dev_rawrequest(struct command *cmd,
|
|||
sent->wait_timeout = *timeout;
|
||||
sent->cmd = cmd;
|
||||
sent->offer = NULL;
|
||||
sent->dev_path_use_scidd = NULL;
|
||||
|
||||
/* We temporarily abuse ->path to store nodeid! */
|
||||
sent->path = node_id;
|
||||
|
|
|
@ -10,11 +10,14 @@
|
|||
#include <common/bolt11.h>
|
||||
#include <common/bolt11_json.h>
|
||||
#include <common/bolt12_merkle.h>
|
||||
#include <common/gossmap.h>
|
||||
#include <common/invoice_path_id.h>
|
||||
#include <common/iso4217.h>
|
||||
#include <common/json_blinded_path.h>
|
||||
#include <common/json_param.h>
|
||||
#include <common/json_stream.h>
|
||||
#include <common/memleak.h>
|
||||
#include <errno.h>
|
||||
#include <plugins/offers.h>
|
||||
#include <plugins/offers_inv_hook.h>
|
||||
#include <plugins/offers_invreq_hook.h>
|
||||
|
@ -29,6 +32,32 @@ u32 blockheight;
|
|||
u16 cltv_final;
|
||||
bool offers_enabled;
|
||||
struct secret invoicesecret_base;
|
||||
static struct gossmap *global_gossmap;
|
||||
|
||||
static void init_gossmap(struct plugin *plugin)
|
||||
{
|
||||
size_t num_cupdates_rejected;
|
||||
global_gossmap
|
||||
= notleak_with_children(gossmap_load(plugin,
|
||||
GOSSIP_STORE_FILENAME,
|
||||
&num_cupdates_rejected));
|
||||
if (!global_gossmap)
|
||||
plugin_err(plugin, "Could not load gossmap %s: %s",
|
||||
GOSSIP_STORE_FILENAME, strerror(errno));
|
||||
if (num_cupdates_rejected)
|
||||
plugin_log(plugin, LOG_DBG,
|
||||
"gossmap ignored %zu channel updates",
|
||||
num_cupdates_rejected);
|
||||
}
|
||||
|
||||
static struct gossmap *get_gossmap(struct plugin *plugin)
|
||||
{
|
||||
if (!global_gossmap)
|
||||
init_gossmap(plugin);
|
||||
else
|
||||
gossmap_refresh(global_gossmap, NULL);
|
||||
return global_gossmap;
|
||||
}
|
||||
|
||||
static struct command_result *finished(struct command *cmd,
|
||||
const char *buf,
|
||||
|
@ -52,6 +81,32 @@ static struct command_result *sendonionmessage_error(struct command *cmd,
|
|||
return command_hook_success(cmd);
|
||||
}
|
||||
|
||||
/* So, you gave us a reply scid? Let's do the lookup then! And no,
|
||||
* we won't accept private channels, just public ones.
|
||||
*/
|
||||
static bool convert_to_scidd(struct command *cmd,
|
||||
struct sciddir_or_pubkey *sciddpk)
|
||||
{
|
||||
struct gossmap *gossmap = get_gossmap(cmd->plugin);
|
||||
struct gossmap_chan *chan;
|
||||
struct gossmap_node *node;
|
||||
struct node_id id;
|
||||
|
||||
chan = gossmap_find_chan(gossmap, &sciddpk->scidd.scid);
|
||||
if (!chan)
|
||||
return false;
|
||||
|
||||
node = gossmap_nth_node(gossmap, chan, sciddpk->scidd.dir);
|
||||
gossmap_node_get_id(gossmap, node, &id);
|
||||
if (!sciddir_or_pubkey_from_node_id(sciddpk, &id)) {
|
||||
plugin_log(cmd->plugin, LOG_BROKEN,
|
||||
"Could not convert node %s to pubkey?",
|
||||
fmt_node_id(tmpctx, &id));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
struct command_result *
|
||||
send_onion_reply(struct command *cmd,
|
||||
struct blinded_path *reply_path,
|
||||
|
@ -60,6 +115,13 @@ send_onion_reply(struct command *cmd,
|
|||
struct out_req *req;
|
||||
size_t nhops;
|
||||
|
||||
if (!reply_path->first_node_id.is_pubkey
|
||||
&& !convert_to_scidd(cmd, &reply_path->first_node_id)) {
|
||||
plugin_log(cmd->plugin, LOG_INFORM, "Unknown reply scid %s: cannot send reply",
|
||||
fmt_short_channel_id_dir(tmpctx, &reply_path->first_node_id.scidd));
|
||||
return command_hook_success(cmd);
|
||||
}
|
||||
|
||||
req = jsonrpc_request_start(cmd->plugin, cmd, "sendonionmessage",
|
||||
finished, sendonionmessage_error, NULL);
|
||||
|
||||
|
@ -111,13 +173,6 @@ static struct command_result *onion_message_modern_call(struct command *cmd,
|
|||
plugin_err(cmd->plugin, "Invalid reply path %.*s?",
|
||||
json_tok_full_len(replytok),
|
||||
json_tok_full(buf, replytok));
|
||||
|
||||
/* FIXME: support this! */
|
||||
if (!reply_path->first_node_id.is_pubkey) {
|
||||
plugin_log(cmd->plugin, LOG_DBG,
|
||||
"reply_blindedpath uses scid");
|
||||
return command_hook_success(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
invreqtok = json_get_member(buf, om, "invoice_request");
|
||||
|
|
|
@ -5577,6 +5577,19 @@ def test_pay_partial_msat(node_factory, executor):
|
|||
l3pay.result(TIMEOUT)
|
||||
|
||||
|
||||
def test_blinded_reply_path_scid(node_factory):
|
||||
"""Check that we handle a blinded path which begins with a scid instead of a nodeid"""
|
||||
l1, l2 = node_factory.line_graph(2, wait_for_announce=True,
|
||||
opts={'experimental-offers': None})
|
||||
offer = l2.rpc.offer(amount='2msat', description='test_blinded_reply_path_scid')
|
||||
|
||||
chan = only_one(l1.rpc.listpeerchannels()['channels'])
|
||||
scidd = "{}/{}".format(chan['short_channel_id'], chan['direction'])
|
||||
inv = l1.rpc.fetchinvoice(offer=offer['bolt12'], dev_path_use_scidd=scidd)['invoice']
|
||||
|
||||
l1.rpc.pay(inv)
|
||||
|
||||
|
||||
def test_pay_while_opening_channel(node_factory, bitcoind, executor):
|
||||
delay_plugin = {'plugin': os.path.join(os.getcwd(),
|
||||
'tests/plugins/openchannel_hook_delay.py'),
|
||||
|
|
Loading…
Add table
Reference in a new issue