Skip to content

Commit 7160541

Browse files
committed
lightningd: honor payment-fronting-node when making bolt12 offers.
We use all the fronting nodes when creating offers. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
1 parent 0f52dc1 commit 7160541

File tree

5 files changed

+114
-33
lines changed

5 files changed

+114
-33
lines changed

plugins/offers.c

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,10 @@ bool we_want_blinded_path(struct plugin *plugin, bool for_payment)
7676
u8 rgb_color[3], alias[32];
7777
struct tlv_node_ann_tlvs *na_tlvs;
7878

79+
/* If we're fronting, we always want a blinded path */
80+
if (od->fronting_nodes)
81+
return true;
82+
7983
node_id_from_pubkey(&local_nodeid, &od->id);
8084

8185
node = gossmap_find_node(gossmap, &local_nodeid);
@@ -1509,6 +1513,25 @@ static struct command_result *json_decode(struct command *cmd,
15091513
return command_finished(cmd, response);
15101514
}
15111515

1516+
static struct pubkey *json_to_pubkeys(const tal_t *ctx,
1517+
const char *buffer,
1518+
const jsmntok_t *tok)
1519+
{
1520+
size_t i;
1521+
const jsmntok_t *t;
1522+
struct pubkey *arr;
1523+
1524+
if (tok->type != JSMN_ARRAY)
1525+
return NULL;
1526+
1527+
arr = tal_arr(ctx, struct pubkey, tok->size);
1528+
json_for_each_arr(i, t, tok) {
1529+
if (!json_to_pubkey(buffer, t, &arr[i]))
1530+
return tal_free(arr);
1531+
}
1532+
return arr;
1533+
}
1534+
15121535
static const char *init(struct command *init_cmd,
15131536
const char *buf UNUSED,
15141537
const jsmntok_t *config UNUSED)
@@ -1526,8 +1549,10 @@ static const char *init(struct command *init_cmd,
15261549
rpc_scan(init_cmd, "listconfigs",
15271550
take(json_out_obj(NULL, NULL, NULL)),
15281551
"{configs:"
1529-
"{cltv-final:{value_int:%}}}",
1530-
JSON_SCAN(json_to_u16, &od->cltv_final));
1552+
"{cltv-final:{value_int:%},"
1553+
"payment-fronting-node?:{values_str:%}}}",
1554+
JSON_SCAN(json_to_u16, &od->cltv_final),
1555+
JSON_SCAN_TAL(od, json_to_pubkeys, &od->fronting_nodes));
15311556

15321557
rpc_scan(init_cmd, "makesecret",
15331558
take(json_out_obj(NULL, "string", BOLT12_ID_BASE_STRING)),
@@ -1604,6 +1629,7 @@ int main(int argc, char *argv[])
16041629
od->disable_connect = false;
16051630
od->dev_invoice_bpath_scid = false;
16061631
od->dev_invoice_internal_scid = NULL;
1632+
od->fronting_nodes = NULL;
16071633

16081634
/* We deal in UTC; mktime() uses local time */
16091635
setenv("TZ", "", 1);

plugins/offers.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ struct offers_data {
2323
struct secret offerblinding_base;
2424
/* Base for node aliases for invoice requests */
2525
struct secret nodealias_base;
26+
/* Any --payment-fronting-node specified */
27+
struct pubkey *fronting_nodes;
2628
/* --dev-invoice-bpath-scid */
2729
bool dev_invoice_bpath_scid;
2830
/* --dev-invoice-internal-scid */

plugins/offers_invreq_hook.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,10 @@ static struct command_result *create_invoicereq(struct command *cmd,
237237
return send_outreq(req);
238238
}
239239

240+
/* FIXME: Allow multihop! */
241+
/* FIXME: And add padding! */
242+
243+
240244
/* FIXME: This is naive:
241245
* - Only creates if we have no public channels.
242246
* - Always creates a path from direct neighbor.

plugins/offers_offer.c

Lines changed: 70 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,50 @@ static struct command_result *create_offer(struct command *cmd,
278278
return send_outreq(req);
279279
}
280280

281+
/* Create num_node_ids paths from these node_ids to us (one hop each) */
282+
static struct blinded_path **offer_onehop_paths(const tal_t *ctx,
283+
const struct offers_data *od,
284+
const struct tlv_offer *offer,
285+
const struct pubkey *neighbors,
286+
size_t num_neigbors)
287+
{
288+
struct pubkey *ids = tal_arr(tmpctx, struct pubkey, 2);
289+
struct secret blinding_path_secret;
290+
struct sha256 offer_id;
291+
struct blinded_path **offer_paths;
292+
293+
/* Note: "id" of offer minus paths */
294+
assert(!offer->offer_paths);
295+
offer_offer_id(offer, &offer_id);
296+
297+
offer_paths = tal_arr(ctx, struct blinded_path *, num_neigbors);
298+
for (size_t i = 0; i < num_neigbors; i++) {
299+
ids[0] = neighbors[i];
300+
ids[1] = od->id;
301+
302+
/* So we recognize this */
303+
/* We can check this when they try to take up offer. */
304+
bolt12_path_secret(&od->offerblinding_base, &offer_id,
305+
&blinding_path_secret);
306+
307+
offer_paths[i]
308+
= incoming_message_blinded_path(offer_paths,
309+
ids,
310+
NULL,
311+
&blinding_path_secret);
312+
}
313+
return offer_paths;
314+
}
315+
316+
/* Common case of making a single path */
317+
static struct blinded_path **offer_onehop_path(const tal_t *ctx,
318+
const struct offers_data *od,
319+
const struct tlv_offer *offer,
320+
const struct pubkey *neighbor)
321+
{
322+
return offer_onehop_paths(ctx, od, offer, neighbor, 1);
323+
}
324+
281325
static struct command_result *found_best_peer(struct command *cmd,
282326
const struct chaninfo *best,
283327
struct offer_info *offinfo)
@@ -294,29 +338,9 @@ static struct command_result *found_best_peer(struct command *cmd,
294338
plugin_log(cmd->plugin, LOG_UNUSUAL,
295339
"No incoming channel to public peer, so no blinded path");
296340
} else {
297-
struct pubkey *ids;
298-
struct secret blinding_path_secret;
299-
struct sha256 offer_id;
300-
301-
/* Note: "id" of offer minus paths */
302-
offer_offer_id(offinfo->offer, &offer_id);
303-
304-
/* Make a small 1-hop path to us */
305-
ids = tal_arr(tmpctx, struct pubkey, 2);
306-
ids[0] = best->id;
307-
ids[1] = od->id;
308-
309-
/* So we recognize this */
310-
/* We can check this when they try to take up offer. */
311-
bolt12_path_secret(&od->offerblinding_base, &offer_id,
312-
&blinding_path_secret);
313-
314-
offinfo->offer->offer_paths = tal_arr(offinfo->offer, struct blinded_path *, 1);
315-
offinfo->offer->offer_paths[0]
316-
= incoming_message_blinded_path(offinfo->offer->offer_paths,
317-
ids,
318-
NULL,
319-
&blinding_path_secret);
341+
offinfo->offer->offer_paths
342+
= offer_onehop_path(offinfo->offer, od,
343+
offinfo->offer, &best->id);
320344
}
321345

322346
return create_offer(cmd, offinfo);
@@ -325,16 +349,31 @@ static struct command_result *found_best_peer(struct command *cmd,
325349
static struct command_result *maybe_add_path(struct command *cmd,
326350
struct offer_info *offinfo)
327351
{
328-
/* BOLT #12:
329-
* - if it is connected only by private channels:
330-
* - MUST include `offer_paths` containing one or more paths to the node from
331-
* publicly reachable nodes.
332-
*/
352+
const struct offers_data *od = get_offers_data(cmd->plugin);
353+
354+
/* Populate paths assuming not already set by dev_paths */
333355
if (!offinfo->offer->offer_paths) {
334-
if (we_want_blinded_path(cmd->plugin, false))
335-
return find_best_peer(cmd, OPT_ONION_MESSAGES,
336-
found_best_peer, offinfo);
356+
/* BOLT #12:
357+
* - if it is connected only by private channels:
358+
* - MUST include `offer_paths` containing one or more paths to the node from
359+
* publicly reachable nodes.
360+
*/
361+
if (we_want_blinded_path(cmd->plugin, false)) {
362+
/* We use *all* fronting nodes (not just "best" one)
363+
* for offers */
364+
if (od->fronting_nodes) {
365+
offinfo->offer->offer_paths
366+
= offer_onehop_paths(offinfo->offer, od,
367+
offinfo->offer,
368+
od->fronting_nodes,
369+
tal_count(od->fronting_nodes));
370+
} else {
371+
return find_best_peer(cmd, OPT_ONION_MESSAGES,
372+
found_best_peer, offinfo);
373+
}
374+
}
337375
}
376+
338377
return create_offer(cmd, offinfo);
339378
}
340379

tests/test_invoices.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -962,3 +962,13 @@ def test_payment_fronting(node_factory):
962962

963963
l1.rpc.xpay(l3inv)
964964
l1.rpc.xpay(l4inv)
965+
966+
# Now test offers.
967+
l3offer = l3.rpc.offer(1000, 'l3offer', 'l3offer')['bolt12']
968+
assert only_one(l3.rpc.decode(l3offer)['offer_paths'])['first_node_id'] == l1.info['id']
969+
970+
l4offer = l4.rpc.offer(1000, 'l4offer', 'l4offer')['bolt12']
971+
assert [r['first_node_id'] for r in l4.rpc.decode(l4offer)['offer_paths']] == [l1.info['id'], l2.info['id']]
972+
973+
l3invb12 = l1.rpc.fetchinvoice(l3offer)['invoice']
974+
l4invb12 = l1.rpc.fetchinvoice(l4offer)['invoice']

0 commit comments

Comments
 (0)