mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-02-22 22:45:27 +01:00
plugins/offers: add dev-paths option for specifying offers blinded paths.
This will let us test, at least, as we implement fetching invoices from them. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
92eb84d45f
commit
27578d5c1d
4 changed files with 159 additions and 2 deletions
|
@ -36,6 +36,7 @@ u16 cltv_final;
|
|||
bool offers_enabled;
|
||||
bool disable_connect;
|
||||
struct secret invoicesecret_base;
|
||||
struct secret offerblinding_base;
|
||||
static struct gossmap *global_gossmap;
|
||||
|
||||
static void init_gossmap(struct plugin *plugin)
|
||||
|
@ -1324,6 +1325,11 @@ static const char *init(struct plugin *p,
|
|||
"{secret:%}",
|
||||
JSON_SCAN(json_to_secret, &invoicesecret_base));
|
||||
|
||||
rpc_scan(p, "makesecret",
|
||||
take(json_out_obj(NULL, "string", "offer-blinded-path")),
|
||||
"{secret:%}",
|
||||
JSON_SCAN(json_to_secret, &offerblinding_base));
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,8 +17,13 @@ extern bool disable_connect;
|
|||
extern u16 cltv_final;
|
||||
/* Current header_count */
|
||||
extern u32 blockheight;
|
||||
/* Basis for invoice secrets */
|
||||
/* Basis for invoice path_secrets */
|
||||
extern struct secret invoicesecret_base;
|
||||
/* Base for offers path_secrets */
|
||||
extern struct secret offerblinding_base;
|
||||
|
||||
/* This is me. */
|
||||
extern struct pubkey id;
|
||||
|
||||
/* If they give us an scid, do a lookup */
|
||||
bool convert_to_scidd(struct command *cmd,
|
||||
|
|
|
@ -3,9 +3,11 @@
|
|||
#include <ccan/array_size/array_size.h>
|
||||
#include <ccan/tal/str/str.h>
|
||||
#include <common/bolt12.h>
|
||||
#include <common/invoice_path_id.h>
|
||||
#include <common/iso4217.h>
|
||||
#include <common/json_param.h>
|
||||
#include <common/json_stream.h>
|
||||
#include <common/onion_message.h>
|
||||
#include <common/overflows.h>
|
||||
#include <plugins/offers.h>
|
||||
#include <plugins/offers_offer.h>
|
||||
|
@ -286,6 +288,105 @@ static struct command_result *currency_done(struct command *cmd,
|
|||
return create_offer(cmd, offinfo);
|
||||
}
|
||||
|
||||
static bool json_to_short_channel_id_dir(const char *buffer, const jsmntok_t *tok,
|
||||
struct short_channel_id_dir *scidd)
|
||||
{
|
||||
jsmntok_t scidtok, numtok;
|
||||
u32 dir;
|
||||
|
||||
if (!split_tok(buffer, tok, '/', &scidtok, &numtok))
|
||||
return false;
|
||||
|
||||
if (!json_to_short_channel_id(buffer, &scidtok, &scidd->scid))
|
||||
return false;
|
||||
|
||||
if (!json_to_u32(buffer, &numtok, &dir) || (dir > 1))
|
||||
return false;
|
||||
|
||||
scidd->dir = dir;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool json_to_sciddir_or_pubkey(const char *buffer, const jsmntok_t *tok,
|
||||
struct sciddir_or_pubkey *sciddir_or_pubkey)
|
||||
{
|
||||
struct pubkey pk;
|
||||
struct short_channel_id_dir scidd;
|
||||
|
||||
if (json_to_pubkey(buffer, tok, &pk)) {
|
||||
sciddir_or_pubkey_from_pubkey(sciddir_or_pubkey, &pk);
|
||||
return true;
|
||||
} else if (json_to_short_channel_id_dir(buffer, tok, &scidd)) {
|
||||
sciddir_or_pubkey_from_scidd(sciddir_or_pubkey, &scidd);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
struct path {
|
||||
/* Optional: a scid as the entry point */
|
||||
struct short_channel_id_dir *first_scidd;
|
||||
/* A node id for every element on the path */
|
||||
struct pubkey *path;
|
||||
};
|
||||
|
||||
static struct command_result *param_paths(struct command *cmd, const char *name,
|
||||
const char *buffer, const jsmntok_t *tok,
|
||||
struct path ***paths)
|
||||
{
|
||||
size_t i;
|
||||
const jsmntok_t *t;
|
||||
|
||||
if (tok->type != JSMN_ARRAY)
|
||||
return command_fail_badparam(cmd, name, buffer, tok, "Must be array");
|
||||
|
||||
*paths = tal_arr(cmd, struct path *, tok->size);
|
||||
json_for_each_arr(i, t, tok) {
|
||||
size_t j;
|
||||
const jsmntok_t *p;
|
||||
|
||||
if (t->type != JSMN_ARRAY || t->size == 0) {
|
||||
return command_fail_badparam(cmd, name, buffer, t,
|
||||
"Must be array of non-empty arrays");
|
||||
}
|
||||
|
||||
(*paths)[i] = tal(*paths, struct path);
|
||||
(*paths)[i]->path = tal_arr((*paths)[i], struct pubkey, t->size);
|
||||
json_for_each_arr(j, p, t) {
|
||||
struct pubkey pk;
|
||||
if (j == 0) {
|
||||
struct sciddir_or_pubkey init;
|
||||
if (!json_to_sciddir_or_pubkey(buffer, p, &init)) {
|
||||
return command_fail_badparam(cmd, name, buffer, p,
|
||||
"invalid pubkey/sciddir");
|
||||
}
|
||||
if (!init.is_pubkey) {
|
||||
(*paths)[i]->first_scidd = tal_dup((*paths)[i],
|
||||
struct short_channel_id_dir,
|
||||
&init.scidd);
|
||||
if (!convert_to_scidd(cmd, &init))
|
||||
return command_fail_badparam(cmd, name, buffer, p,
|
||||
"unknown sciddir");
|
||||
} else {
|
||||
(*paths)[i]->first_scidd = NULL;
|
||||
}
|
||||
pk = init.pubkey;
|
||||
} else {
|
||||
if (!json_to_pubkey(buffer, p, &pk)) {
|
||||
return command_fail_badparam(cmd, name, buffer, p,
|
||||
"invalid pubkey");
|
||||
}
|
||||
}
|
||||
if (j == t->size - 1 && !pubkey_eq(&pk, &id))
|
||||
return command_fail_badparam(cmd, name, buffer, p,
|
||||
"final pubkey must be this node");
|
||||
(*paths)[i]->path[j] = pk;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct command_result *json_offer(struct command *cmd,
|
||||
const char *buffer,
|
||||
const jsmntok_t *params)
|
||||
|
@ -293,6 +394,7 @@ struct command_result *json_offer(struct command *cmd,
|
|||
const char *desc, *issuer;
|
||||
struct tlv_offer *offer;
|
||||
struct offer_info *offinfo = tal(cmd, struct offer_info);
|
||||
struct path **paths;
|
||||
|
||||
offinfo->offer = offer = tlv_offer_new(offinfo);
|
||||
|
||||
|
@ -318,7 +420,7 @@ struct command_result *json_offer(struct command *cmd,
|
|||
p_opt("recurrence_start_any_period",
|
||||
param_recurrence_start_any_period,
|
||||
&offer->offer_recurrence_base),
|
||||
/* FIXME: hints support! */
|
||||
p_opt("dev_paths", param_paths, &paths),
|
||||
NULL))
|
||||
return command_param_failed();
|
||||
|
||||
|
@ -389,6 +491,32 @@ struct command_result *json_offer(struct command *cmd,
|
|||
*/
|
||||
offer->offer_node_id = tal_dup(offer, struct pubkey, &id);
|
||||
|
||||
/* Now rest of offer will not change: we use pathless offer to create secret. */
|
||||
if (paths) {
|
||||
const u8 *path_secret;
|
||||
struct secret blinding_path_secret;
|
||||
struct sha256 offer_id;
|
||||
/* Note: "id" of offer minus paths */
|
||||
offer_offer_id(offer, &offer_id);
|
||||
|
||||
/* We can check this when they try to take up offer. */
|
||||
path_secret = invoice_path_id(tmpctx, &offerblinding_base, &offer_id);
|
||||
assert(tal_count(path_secret) == sizeof(blinding_path_secret));
|
||||
memcpy(&blinding_path_secret, path_secret, sizeof(blinding_path_secret));
|
||||
|
||||
offer->offer_paths = tal_arr(offer, struct blinded_path *, tal_count(paths));
|
||||
for (size_t i = 0; i < tal_count(paths); i++) {
|
||||
offer->offer_paths[i] = incoming_message_blinded_path(offer->offer_paths,
|
||||
paths[i]->path,
|
||||
NULL,
|
||||
&blinding_path_secret);
|
||||
/* Override entry point if they said to */
|
||||
if (paths[i]->first_scidd)
|
||||
sciddir_or_pubkey_from_scidd(&offer->offer_paths[i]->first_node_id,
|
||||
paths[i]->first_scidd);
|
||||
}
|
||||
}
|
||||
|
||||
/* If they specify a different currency, warn if we can't
|
||||
* convert it! */
|
||||
if (offer->offer_currency) {
|
||||
|
|
|
@ -5674,6 +5674,24 @@ def test_pay_while_opening_channel(node_factory, bitcoind, executor):
|
|||
l1.rpc.pay(inv['bolt11'])
|
||||
|
||||
|
||||
def test_offer_paths(node_factory):
|
||||
# Need to announce channels to use their scid in offers anyway!
|
||||
l1, l2, l3 = node_factory.line_graph(3,
|
||||
opts={'experimental-offers': None},
|
||||
wait_for_announce=True)
|
||||
|
||||
chan = only_one(l1.rpc.listpeerchannels()['channels'])
|
||||
scidd = "{}/{}".format(chan['short_channel_id'], chan['direction'])
|
||||
offer = l2.rpc.offer(amount='100sat', description='test_offer_paths',
|
||||
dev_paths=[[scidd, l2.info['id']], [l3.info['id'], l2.info['id']]])
|
||||
|
||||
paths = l1.rpc.decode(offer['bolt12'])['offer_paths']
|
||||
assert len(paths) == 2
|
||||
assert paths[0]['first_scid'] == chan['short_channel_id']
|
||||
assert paths[0]['first_scid_dir'] == chan['direction']
|
||||
assert paths[1]['first_node_id'] == l3.info['id']
|
||||
|
||||
|
||||
def test_pay_legacy_forward(node_factory, bitcoind, executor):
|
||||
"""We removed legacy in 22.11, and LND will still send them for
|
||||
route hints! See
|
||||
|
|
Loading…
Add table
Reference in a new issue