Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .msggen.json
Original file line number Diff line number Diff line change
Expand Up @@ -3337,6 +3337,7 @@
"Offer.absolute_expiry": 6,
"Offer.amount": 1,
"Offer.description": 2,
"Offer.fronting_nodes[]": 15,
"Offer.issuer": 3,
"Offer.label": 4,
"Offer.optional_recurrence": 14,
Expand Down Expand Up @@ -11988,6 +11989,10 @@
"added": "pre-v0.10.1",
"deprecated": null
},
"Offer.fronting_nodes[]": {
"added": "v26.04",
"deprecated": null
},
"Offer.issuer": {
"added": "pre-v0.10.1",
"deprecated": null
Expand Down
1 change: 1 addition & 0 deletions cln-grpc/proto/node.proto

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions cln-grpc/src/convert.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions cln-rpc/src/model.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 0 additions & 6 deletions common/test/run-sphinx-xor_cipher_stream.c
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,6 @@ void subkey_from_hmac(const char *prefix UNNEEDED,
const struct secret *base UNNEEDED,
struct secret *key UNNEEDED)
{ fprintf(stderr, "subkey_from_hmac called!\n"); abort(); }
/* Generated stub for tlv_payload_new */
struct tlv_payload *tlv_payload_new(const tal_t *ctx UNNEEDED)
{ fprintf(stderr, "tlv_payload_new called!\n"); abort(); }
/* Generated stub for towire */
void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED)
{ fprintf(stderr, "towire called!\n"); abort(); }
Expand All @@ -136,9 +133,6 @@ void towire_secp256k1_ecdsa_signature(u8 **pptr UNNEEDED,
/* Generated stub for towire_sha256 */
void towire_sha256(u8 **pptr UNNEEDED, const struct sha256 *sha256 UNNEEDED)
{ fprintf(stderr, "towire_sha256 called!\n"); abort(); }
/* Generated stub for towire_tlv_payload */
void towire_tlv_payload(u8 **pptr UNNEEDED, const struct tlv_payload *record UNNEEDED)
{ fprintf(stderr, "towire_tlv_payload called!\n"); abort(); }
/* Generated stub for towire_u16 */
void towire_u16(u8 **pptr UNNEEDED, u16 v UNNEEDED)
{ fprintf(stderr, "towire_u16 called!\n"); abort(); }
Expand Down
3 changes: 0 additions & 3 deletions connectd/test/run-crc32_of_update.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,6 @@ int unused_main(int argc, char *argv[]);
/* Generated stub for fromwire_connectd_dev_set_max_scids_encode_size */
bool fromwire_connectd_dev_set_max_scids_encode_size(const void *p UNNEEDED, u32 *max UNNEEDED)
{ fprintf(stderr, "fromwire_connectd_dev_set_max_scids_encode_size called!\n"); abort(); }
/* Generated stub for fromwire_gossip_store_chan_dying */
bool fromwire_gossip_store_chan_dying(const void *p UNNEEDED, struct short_channel_id *scid UNNEEDED, u32 *blockheight UNNEEDED)
{ fprintf(stderr, "fromwire_gossip_store_chan_dying called!\n"); abort(); }
/* Generated stub for get_gossmap */
struct gossmap *get_gossmap(struct daemon *daemon UNNEEDED)
{ fprintf(stderr, "get_gossmap called!\n"); abort(); }
Expand Down
11 changes: 11 additions & 0 deletions contrib/msggen/msggen/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -27735,6 +27735,17 @@
"Make recurrence optional, for backwards compatibility (older payers will only pay once)."
]
},
"fronting_nodes": {
"added": "v26.04",
"type": "array",
"items": {
"type": "pubkey"
},
"description": [
"An optional array of peer nodes to create blinded paths from. One of these blinded paths will also be used for the invoice, when they request it. This overrides the `payment-fronting-node` configuration setting. If set to the empty array, this means *no fronting nodes*."
],
"default": "The `payment-fronting-node` configuration setting"
},
"dev_paths": {
"hidden": true
}
Expand Down
3 changes: 2 additions & 1 deletion contrib/pyln-client/pyln/client/lightning.py
Original file line number Diff line number Diff line change
Expand Up @@ -1120,7 +1120,7 @@ def newaddr(self, addresstype=None):

def offer(self, amount, description=None, issuer=None, label=None, quantity_max=None, absolute_expiry=None,
recurrence=None, recurrence_base=None, recurrence_paywindow=None, recurrence_limit=None,
single_use=None):
single_use=None, fronting_nodes=None):
"""
Create an offer (or returns an existing one), which is a precursor to creating one or more invoices.
It automatically enables the processing of an incoming invoice_request, and issuing of invoices.
Expand All @@ -1137,6 +1137,7 @@ def offer(self, amount, description=None, issuer=None, label=None, quantity_max=
"recurrence_paywindow": recurrence_paywindow,
"recurrence_limit": recurrence_limit,
"single_use": single_use,
"fronting_nodes": fronting_nodes,
}
return self.call("offer", payload)

Expand Down
1,216 changes: 608 additions & 608 deletions contrib/pyln-grpc-proto/pyln/grpc/node_pb2.py

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions doc/lightningd-config.5.md
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,10 @@ delete the others.

### Payment and invoice control options:

* **payment-fronting-node**=*nodeid*

Always use this *nodeid* as the entry point when we generate invoices or offers: currently, the node must be a neighbor we have a channel with. For BOLT11 invoices we will use a routehint with the alias for the short channel id to provide limited privacy (we still reveal our node id). For BOLT12 invoices and offers, we provide a blinded path from the node to us which provides better privacy. This can be specified multiple times for multiple fronting nodes.

* **disable-mpp** [plugin `pay`]

Disable the multi-part payment sending support in the `pay` plugin. By default
Expand Down
11 changes: 11 additions & 0 deletions doc/schemas/offer.json
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,17 @@
"Make recurrence optional, for backwards compatibility (older payers will only pay once)."
]
},
"fronting_nodes": {
"added": "v26.04",
"type": "array",
"items": {
"type": "pubkey"
},
"description": [
"An optional array of peer nodes to create blinded paths from. One of these blinded paths will also be used for the invoice, when they request it. This overrides the `payment-fronting-node` configuration setting. If set to the empty array, this means *no fronting nodes*."
],
"default": "The `payment-fronting-node` configuration setting"
},
"dev_paths": {
"hidden": true
}
Expand Down
3 changes: 0 additions & 3 deletions gossipd/test/run-next_block_range.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,6 @@ struct peer *find_peer(struct daemon *daemon UNNEEDED, const struct node_id *id
struct peer *first_random_peer(struct daemon *daemon UNNEEDED,
struct peer_node_id_map_iter *it UNNEEDED)
{ fprintf(stderr, "first_random_peer called!\n"); abort(); }
/* Generated stub for fromwire_gossip_store_chan_dying */
bool fromwire_gossip_store_chan_dying(const void *p UNNEEDED, struct short_channel_id *scid UNNEEDED, u32 *blockheight UNNEEDED)
{ fprintf(stderr, "fromwire_gossip_store_chan_dying called!\n"); abort(); }
/* Generated stub for gossmap_manage_get_gossmap */
struct gossmap *gossmap_manage_get_gossmap(struct gossmap_manage *gm UNNEEDED)
{ fprintf(stderr, "gossmap_manage_get_gossmap called!\n"); abort(); }
Expand Down
25 changes: 24 additions & 1 deletion lightningd/invoice.c
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,25 @@ static struct route_info **select_inchan_mpp(const tal_t *ctx,
return routehints;
}

static struct route_info **select_inchan_all(const tal_t *ctx,
struct lightningd *ld,
struct routehint_candidate
*candidates)
{
struct route_info **routehints;

log_debug(ld->log, "Selecting all %zu candidates",
tal_count(candidates));

routehints = tal_arr(ctx, struct route_info *, tal_count(candidates));
for (size_t i = 0; i < tal_count(candidates); i++) {
routehints[i] = tal_dup(routehints, struct route_info,
candidates[i].r);
}

return routehints;
}

/* Encapsulating struct while we wait for gossipd to give us incoming channels */
struct chanhints {
bool expose_all_private;
Expand Down Expand Up @@ -721,6 +740,10 @@ add_routehints(struct invoice_info *info,

needed = info->b11->msat ? *info->b11->msat : AMOUNT_MSAT(1);

/* --payment-fronting-node means use all candidates. */
if (tal_count(info->cmd->ld->fronting_nodes))
info->b11->routes = select_inchan_all(info->b11, info->cmd->ld, candidates);

/* If we are not completely unpublished, try with reservoir
* sampling first.
*
Expand All @@ -736,7 +759,7 @@ add_routehints(struct invoice_info *info,
* should make an effort to avoid overlapping incoming
* channels, which is done by select_inchan_mpp.
*/
if (!node_unpublished)
else if (!node_unpublished)
info->b11->routes = select_inchan(info->b11,
info->cmd->ld,
needed,
Expand Down
1 change: 1 addition & 0 deletions lightningd/lightningd.c
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,7 @@ static struct lightningd *new_lightningd(const tal_t *ctx)
/* The gossip seeker automatically connects to a this many peers */
ld->autoconnect_seeker_peers = 10;

ld->fronting_nodes = tal_arr(ld, struct node_id, 0);
return ld;
}

Expand Down
3 changes: 3 additions & 0 deletions lightningd/lightningd.h
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,9 @@ struct lightningd {

/* Minimum number of peers seeker should maintain. */
u32 autoconnect_seeker_peers;

/* Nodes to use for invoices / offers */
struct node_id *fronting_nodes;
};

/* Turning this on allows a tal allocation to return NULL, rather than aborting.
Expand Down
15 changes: 15 additions & 0 deletions lightningd/options.c
Original file line number Diff line number Diff line change
Expand Up @@ -1278,6 +1278,16 @@ static char *opt_add_api_beg(const char *arg, struct lightningd *ld)
return NULL;
}

static char *opt_add_node_id(const char *arg, struct node_id **arr)
{
struct node_id n;
if (!node_id_from_hexstr(arg, strlen(arg), &n))
return "Unparsable nodeid";

tal_arr_expand(arr, n);
return NULL;
}

char *hsm_secret_arg(const tal_t *ctx,
const char *arg,
const struct hsm_secret **hsm_secret)
Expand Down Expand Up @@ -1632,6 +1642,10 @@ static void register_opts(struct lightningd *ld)
ld,
"Re-enable a long-deprecated API (which will be removed entirely next version!)");
opt_register_logging(ld);
clnopt_witharg("--payment-fronting-node",
OPT_MULTI,
opt_add_node_id, NULL,
&ld->fronting_nodes, "Put this node in all invoices and offers, and use blinded path (bolt12) or route hints (bolt11) to route to this node. Must be a neighboring node. Can be specified multiple times.");

/* Old bookkeeper migration flags. */
opt_register_early_arg("--bookkeeper-dir",
Expand Down Expand Up @@ -1898,6 +1912,7 @@ bool is_known_opt_cb_arg(char *(*cb_arg)(const char *, void *))
|| cb_arg == (void *)opt_subd_dev_disconnect
|| cb_arg == (void *)opt_set_crash_timeout
|| cb_arg == (void *)opt_add_api_beg
|| cb_arg == (void *)opt_add_node_id
|| cb_arg == (void *)opt_force_featureset
|| cb_arg == (void *)opt_force_privkey
|| cb_arg == (void *)opt_force_bip32_seed
Expand Down
8 changes: 2 additions & 6 deletions lightningd/plugin.c
Original file line number Diff line number Diff line change
Expand Up @@ -2062,12 +2062,8 @@ void plugins_init(struct plugins *plugins)
setenv("LIGHTNINGD_PLUGIN", "1", 1);
setenv("LIGHTNINGD_VERSION", version(), 1);

if (plugins_send_getmanifest(plugins, NULL)) {
void *ret;
ret = io_loop_with_timers(plugins->ld);
log_debug(plugins->ld->log, "io_loop_with_timers: %s", __func__);
assert(ret == plugins);
}
if (plugins_send_getmanifest(plugins, NULL))
io_loop_with_timers(plugins->ld);
}

static void plugin_config_cb(const char *buffer,
Expand Down
33 changes: 29 additions & 4 deletions lightningd/routehint.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,16 @@ static bool scid_in_arr(const struct short_channel_id *scidarr,
return false;
}

static bool is_fronting_node(const struct lightningd *ld,
const struct node_id *node)
{
for (size_t i = 0; i < tal_count(ld->fronting_nodes); i++) {
if (node_id_eq(&ld->fronting_nodes[i], node))
return true;
}
return false;
}

struct routehint_candidate *
routehint_candidates(const tal_t *ctx,
struct lightningd *ld,
Expand Down Expand Up @@ -58,7 +68,7 @@ routehint_candidates(const tal_t *ctx,
struct routehint_candidate candidate;
struct amount_msat fee_base, htlc_max;
struct route_info *r;
bool is_public;
bool is_public, is_fronting;

r = tal(tmpctx, struct route_info);

Expand Down Expand Up @@ -88,6 +98,17 @@ routehint_candidates(const tal_t *ctx,
json_tok_full(buf, toks));
}

/* If they specify fronting nodes, always use them. */
if (tal_count(ld->fronting_nodes)) {
if (!is_fronting_node(ld, &r->pubkey)) {
log_debug(ld->log, "%s: not a fronting node",
fmt_node_id(tmpctx, &r->pubkey));
continue;
}
is_fronting = true;
} else
is_fronting = false;

/* Note: listincoming returns real scid or local alias if no real scid. */
candidate.c = any_channel_by_scid(ld, r->short_channel_id, true);
if (!candidate.c) {
Expand Down Expand Up @@ -129,6 +150,10 @@ routehint_candidates(const tal_t *ctx,
if (expose_all_private != NULL && *expose_all_private)
is_public = true;

/* Also, consider fronting nodes public */
if (is_fronting)
is_public = true;

r->fee_base_msat = fee_base.millisatoshis; /* Raw: route_info */
/* Could wrap: if so ignore */
if (!amount_msat_eq(amount_msat(r->fee_base_msat), fee_base)) {
Expand All @@ -152,7 +177,7 @@ routehint_candidates(const tal_t *ctx,
continue;
}
/* If they give us a hint, we use even if capacity 0 */
} else if (amount_msat_is_zero(capacity)) {
} else if (!is_fronting && amount_msat_is_zero(capacity)) {
log_debug(ld->log, "%s: deadend",
fmt_short_channel_id(tmpctx,
r->short_channel_id));
Expand All @@ -162,8 +187,8 @@ routehint_candidates(const tal_t *ctx,
continue;
}

/* Is it offline? */
if (candidate.c->owner == NULL) {
/* Is it offline? Leave it if it's fronting. */
if (!is_fronting && candidate.c->owner == NULL) {
log_debug(ld->log, "%s: offline",
fmt_short_channel_id(tmpctx,
r->short_channel_id));
Expand Down
Loading
Loading