plugins/libplugin-pay: hack in blinded path support.

We simply take the first one, and route to the start of that.  Then we
append the blinded path to the onion construction.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell 2022-11-09 12:00:10 +10:30 committed by Christian Decker
parent 8720bbedae
commit 01a47720c3
6 changed files with 114 additions and 15 deletions

View file

@ -172,7 +172,7 @@ ALL_PROGRAMS += $(C_PLUGINS)
# Make all plugins depend on all plugin headers, for simplicity.
$(PLUGIN_ALL_OBJS): $(PLUGIN_ALL_HEADER)
plugins/pay: $(PLUGIN_PAY_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_PAY_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) common/gossmap.o common/fp16.o common/route.o common/dijkstra.o common/bolt12.o common/bolt12_merkle.o wire/bolt12$(EXP)_wiregen.o bitcoin/block.o
plugins/pay: $(PLUGIN_PAY_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_PAY_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) common/gossmap.o common/fp16.o common/route.o common/dijkstra.o common/bolt12.o common/bolt12_merkle.o wire/bolt12$(EXP)_wiregen.o bitcoin/block.o common/blindedpay.o common/blindedpath.o common/hmac.o common/blinding.o common/onion_encode.o
plugins/autoclean: $(PLUGIN_AUTOCLEAN_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS)
@ -188,7 +188,7 @@ plugins/txprepare: $(PLUGIN_TXPREPARE_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_O
plugins/bcli: $(PLUGIN_BCLI_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS)
plugins/keysend: wire/tlvstream.o wire/onion$(EXP)_wiregen.o $(PLUGIN_KEYSEND_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_PAY_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) common/gossmap.o common/fp16.o common/route.o common/dijkstra.o
plugins/keysend: wire/tlvstream.o wire/onion$(EXP)_wiregen.o $(PLUGIN_KEYSEND_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_PAY_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) common/gossmap.o common/fp16.o common/route.o common/dijkstra.o common/blindedpay.o common/blindedpath.o common/hmac.o common/blinding.o common/onion_encode.o
$(PLUGIN_KEYSEND_OBJS): $(PLUGIN_PAY_LIB_HEADER)
plugins/spenderp: bitcoin/block.o bitcoin/preimage.o bitcoin/psbt.o common/psbt_open.o wire/peer${EXP}_wiregen.o $(PLUGIN_SPENDER_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS)

View file

@ -174,6 +174,8 @@ static struct command_result *json_keysend(struct command *cmd, const char *buf,
p->destination = tal_steal(p, destination);
p->payment_secret = NULL;
p->payment_metadata = NULL;
p->blindedpath = NULL;
p->blindedpay = NULL;
p->amount = *msat;
p->routes = tal_steal(p, hints);
// 22 is the Rust-Lightning default and the highest minimum we know of.

View file

@ -1,6 +1,7 @@
#include "config.h"
#include <ccan/array_size/array_size.h>
#include <ccan/tal/str/str.h>
#include <common/blindedpay.h>
#include <common/dijkstra.h>
#include <common/gossmap.h>
#include <common/json_stream.h>
@ -1662,6 +1663,33 @@ static void payment_add_hop_onion_payload(struct payment *p,
}
}
static void payment_add_blindedpath(const tal_t *ctx,
struct createonion_hop *hops,
const struct blinded_path *bpath,
struct amount_msat final_amt,
u32 final_cltv)
{
/* It's a bit of a weird API for us, so we convert it back to
* the struct tlv_tlv_payload */
u8 **tlvs = blinded_onion_hops(tmpctx, final_amt, final_cltv, bpath);
for (size_t i = 0; i < tal_count(tlvs); i++) {
const u8 *cursor = tlvs[i];
size_t max = tal_bytelen(tlvs[i]);
/* First one has to use real node_id */
if (i == 0)
node_id_from_pubkey(&hops[i].pubkey,
&bpath->first_node_id);
else
node_id_from_pubkey(&hops[i].pubkey,
&bpath->path[i]->blinded_node_id);
/* Length is prepended, discard that first! */
fromwire_bigsize(&cursor, &max);
hops[i].tlv_payload = fromwire_tlv_tlv_payload(ctx, &cursor, &max);
}
}
static void payment_compute_onion_payloads(struct payment *p)
{
struct createonion_request *cr;
@ -1690,7 +1718,9 @@ static void payment_compute_onion_payloads(struct payment *p)
cr->assocdata = tal_arr(cr, u8, 0);
towire_sha256(&cr->assocdata, p->payment_hash);
cr->session_key = NULL;
cr->hops = tal_arr(cr, struct createonion_hop, tal_count(p->route));
cr->hops = tal_arr(cr, struct createonion_hop,
tal_count(p->route)
+ (root->blindedpath ? tal_count(root->blindedpath->path) - 1: 0));
/* Non-final hops */
for (size_t i = 0; i < hopcount - 1; i++) {
@ -1704,14 +1734,27 @@ static void payment_compute_onion_payloads(struct payment *p)
&p->route[i].scid));
}
/* Final hop */
payment_add_hop_onion_payload(
p, &cr->hops[hopcount - 1], &p->route[hopcount - 1],
&p->route[hopcount - 1], true,
root->payment_secret, root->payment_metadata);
tal_append_fmt(&routetxt, "%s",
type_to_string(tmpctx, struct short_channel_id,
&p->route[hopcount - 1].scid));
/* If we're headed to a blinded path, connect that now. */
if (root->blindedpath) {
payment_add_blindedpath(cr->hops, cr->hops + hopcount - 1,
root->blindedpath,
root->blindedfinalamount,
root->blindedfinalcltv);
tal_append_fmt(&routetxt, "%s -> blinded path (%zu hops)",
type_to_string(tmpctx, struct short_channel_id,
&p->route[hopcount-1].scid),
tal_count(root->blindedpath->path));
} else {
/* Final hop */
payment_add_hop_onion_payload(
p, &cr->hops[hopcount - 1], &p->route[hopcount - 1],
&p->route[hopcount - 1], true,
root->payment_secret,
root->payment_metadata);
tal_append_fmt(&routetxt, "%s",
type_to_string(tmpctx, struct short_channel_id,
&p->route[hopcount - 1].scid));
}
paymod_log(p, LOG_DBG,
"Created outgoing onion for route: %s", routetxt);

View file

@ -188,6 +188,12 @@ struct payment {
/* Payment metadata, from the invoice if any. */
u8 *payment_metadata;
/* Blinded path (for bolt12) */
struct blinded_path *blindedpath;
struct blinded_payinfo *blindedpay;
struct amount_msat blindedfinalamount;
u32 blindedfinalcltv;
u64 groupid;
u32 partid;
u32 next_partid;

View file

@ -1024,6 +1024,9 @@ static struct command_result *json_pay(struct command *cmd,
p = payment_new(cmd, cmd, NULL /* No parent */, paymod_mods);
p->invstring = tal_steal(p, b11str);
p->description = tal_steal(p, description);
/* Overridded by bolt12 if present */
p->blindedpath = NULL;
p->blindedpay = NULL;
if (!bolt12_has_prefix(b11str)) {
b11 =
@ -1085,7 +1088,9 @@ static struct command_result *json_pay(struct command *cmd,
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"experimental-offers disabled");
p->features = tal_steal(p, b12->features);
/* FIXME: We disable MPP for now */
/* p->features = tal_steal(p, b12->features); */
p->features = NULL;
if (!b12->node_id)
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
@ -1109,8 +1114,33 @@ static struct command_result *json_pay(struct command *cmd,
return command_fail(
cmd, JSONRPC2_INVALID_PARAMS,
"recurring invoice requires a label");
/* FIXME payment_secret should be signature! */
{
/* BOLT-offers #12:
* - MUST reject the invoice if `blindedpay` is not present.
*/
/* FIXME: We allow this for now. */
if (tal_count(b12->paths) != 0) {
/* BOLT-offers #12: - MUST reject the invoice if
* `blindedpay` does not contain exactly one
* `blinded_payinfo` per `blinded_path`.
*/
if (tal_count(b12->paths) != tal_count(b12->blindedpay)) {
return command_fail(
cmd, JSONRPC2_INVALID_PARAMS,
"Wrong blinding info: %zu paths, %zu payinfo",
tal_count(b12->paths),
tal_count(b12->blindedpay));
}
/* FIXME: do MPP across these! We choose first one. */
p->blindedpath = tal_steal(p, b12->paths[0]);
p->blindedpay = tal_steal(p, b12->blindedpay[0]);
/* Set destination to introduction point */
node_id_from_pubkey(p->destination, &p->blindedpath->first_node_id);
} else {
/* FIXME payment_secret should be signature! */
struct sha256 merkle;
p->payment_secret = tal(p, struct secret);
@ -1121,7 +1151,6 @@ static struct command_result *json_pay(struct command *cmd,
}
p->payment_metadata = NULL;
p->routes = NULL;
/* FIXME: paths! */
if (b12->cltv)
p->min_final_cltv_expiry = *b12->cltv;
else
@ -1161,6 +1190,19 @@ static struct command_result *json_pay(struct command *cmd,
p->amount = *msat;
}
/* We replace real final values if we're using a blinded path */
if (p->blindedpath) {
p->blindedfinalcltv = p->min_final_cltv_expiry;
p->blindedfinalamount = p->amount;
p->min_final_cltv_expiry += p->blindedpay->cltv_expiry_delta;
if (!amount_msat_add_fee(&p->amount,
p->blindedpay->fee_base_msat,
p->blindedpay->fee_proportional_millionths))
return command_fail(cmd, PAY_ROUTE_TOO_EXPENSIVE,
"This payment blinded path fee overflows!");
}
if (node_id_eq(&my_id, p->destination))
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
"This payment is destined for ourselves. "

View file

@ -8,6 +8,12 @@
#include <unistd.h>
/* AUTOGENERATED MOCKS START */
/* Generated stub for blinded_onion_hops */
u8 **blinded_onion_hops(const tal_t *ctx UNNEEDED,
struct amount_msat final_amount UNNEEDED,
u32 final_cltv UNNEEDED,
const struct blinded_path *path UNNEEDED)
{ fprintf(stderr, "blinded_onion_hops called!\n"); abort(); }
/* Generated stub for command_finished */
struct command_result *command_finished(struct command *cmd UNNEEDED, struct json_stream *response UNNEEDED)
{ fprintf(stderr, "command_finished called!\n"); abort(); }