Skip to content

Commit 7f1e555

Browse files
committed
lightningd: honor payment-fronting-node when making bolt11 invoices.
We use all the fronting nodes when creating invoices. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
1 parent 3caad8e commit 7f1e555

File tree

3 files changed

+78
-5
lines changed

3 files changed

+78
-5
lines changed

lightningd/invoice.c

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -662,6 +662,25 @@ static struct route_info **select_inchan_mpp(const tal_t *ctx,
662662
return routehints;
663663
}
664664

665+
static struct route_info **select_inchan_all(const tal_t *ctx,
666+
struct lightningd *ld,
667+
struct routehint_candidate
668+
*candidates)
669+
{
670+
struct route_info **routehints;
671+
672+
log_debug(ld->log, "Selecting all %zu candidates",
673+
tal_count(candidates));
674+
675+
routehints = tal_arr(ctx, struct route_info *, tal_count(candidates));
676+
for (size_t i = 0; i < tal_count(candidates); i++) {
677+
routehints[i] = tal_dup(routehints, struct route_info,
678+
candidates[i].r);
679+
}
680+
681+
return routehints;
682+
}
683+
665684
/* Encapsulating struct while we wait for gossipd to give us incoming channels */
666685
struct chanhints {
667686
bool expose_all_private;
@@ -723,6 +742,10 @@ add_routehints(struct invoice_info *info,
723742

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

745+
/* --payment-fronting-node means use all candidates. */
746+
if (tal_count(info->cmd->ld->fronting_nodes))
747+
info->b11->routes = select_inchan_all(info->b11, info->cmd->ld, candidates);
748+
726749
/* If we are not completely unpublished, try with reservoir
727750
* sampling first.
728751
*
@@ -738,7 +761,7 @@ add_routehints(struct invoice_info *info,
738761
* should make an effort to avoid overlapping incoming
739762
* channels, which is done by select_inchan_mpp.
740763
*/
741-
if (!node_unpublished)
764+
else if (!node_unpublished)
742765
info->b11->routes = select_inchan(info->b11,
743766
info->cmd->ld,
744767
needed,

lightningd/routehint.c

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,16 @@ static bool scid_in_arr(const struct short_channel_id *scidarr,
1717
return false;
1818
}
1919

20+
static bool is_fronting_node(const struct lightningd *ld,
21+
const struct node_id *node)
22+
{
23+
for (size_t i = 0; i < tal_count(ld->fronting_nodes); i++) {
24+
if (node_id_eq(&ld->fronting_nodes[i], node))
25+
return true;
26+
}
27+
return false;
28+
}
29+
2030
struct routehint_candidate *
2131
routehint_candidates(const tal_t *ctx,
2232
struct lightningd *ld,
@@ -62,7 +72,7 @@ routehint_candidates(const tal_t *ctx,
6272
struct routehint_candidate candidate;
6373
struct amount_msat fee_base, htlc_max;
6474
struct route_info *r;
65-
bool is_public;
75+
bool is_public, is_fronting;
6676

6777
r = tal(tmpctx, struct route_info);
6878

@@ -92,6 +102,17 @@ routehint_candidates(const tal_t *ctx,
92102
json_tok_full(buf, toks));
93103
}
94104

105+
/* If they specify fronting nodes, always use them. */
106+
if (tal_count(ld->fronting_nodes)) {
107+
if (!is_fronting_node(ld, &r->pubkey)) {
108+
log_debug(ld->log, "%s: not a fronting node",
109+
fmt_node_id(tmpctx, &r->pubkey));
110+
continue;
111+
}
112+
is_fronting = true;
113+
} else
114+
is_fronting = false;
115+
95116
/* Note: listincoming returns real scid or local alias if no real scid. */
96117
candidate.c = any_channel_by_scid(ld, r->short_channel_id, true);
97118
if (!candidate.c) {
@@ -133,6 +154,10 @@ routehint_candidates(const tal_t *ctx,
133154
if (expose_all_private != NULL && *expose_all_private)
134155
is_public = true;
135156

157+
/* Also, consider fronting nodes public */
158+
if (is_fronting)
159+
is_public = true;
160+
136161
r->fee_base_msat = fee_base.millisatoshis; /* Raw: route_info */
137162
/* Could wrap: if so ignore */
138163
if (!amount_msat_eq(amount_msat(r->fee_base_msat), fee_base)) {
@@ -156,7 +181,7 @@ routehint_candidates(const tal_t *ctx,
156181
continue;
157182
}
158183
/* If they give us a hint, we use even if capacity 0 */
159-
} else if (amount_msat_is_zero(capacity)) {
184+
} else if (!is_fronting && amount_msat_is_zero(capacity)) {
160185
log_debug(ld->log, "%s: deadend",
161186
fmt_short_channel_id(tmpctx,
162187
r->short_channel_id));
@@ -166,8 +191,8 @@ routehint_candidates(const tal_t *ctx,
166191
continue;
167192
}
168193

169-
/* Is it offline? */
170-
if (candidate.c->owner == NULL) {
194+
/* Is it offline? Leave it if it's fronting. */
195+
if (!is_fronting && candidate.c->owner == NULL) {
171196
log_debug(ld->log, "%s: offline",
172197
fmt_short_channel_id(tmpctx,
173198
r->short_channel_id));

tests/test_invoices.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -937,3 +937,28 @@ def test_invoice_botched_migration(node_factory, chainparams):
937937
assert ([(i['created_index'], i['label']) for i in l1.rpc.listinvoices()["invoices"]]
938938
== [(1, "made_after_bad_migration"), (2, "label1")])
939939
assert l1.rpc.invoice(100, "test", "test")["created_index"] == 3
940+
941+
942+
def test_payment_fronting(node_factory):
943+
l1, l2 = node_factory.get_nodes(2)
944+
l3, l4 = node_factory.get_nodes(2, opts=[{'payment-fronting-node': l1.info['id']},
945+
{'payment-fronting-node': [l1.info['id'], l2.info['id']]}])
946+
947+
assert l3.rpc.listconfigs('payment-fronting-node') == {'configs': {'payment-fronting-node': {'sources': ['cmdline'], 'values_str': [l1.info['id']]}}}
948+
assert l4.rpc.listconfigs('payment-fronting-node') == {'configs': {'payment-fronting-node': {'sources': ['cmdline', 'cmdline'], 'values_str': [l1.info['id'], l2.info['id']]}}}
949+
950+
# l1 <----> l3
951+
# \
952+
# \-----> l4 <----> l2
953+
node_factory.join_nodes([l1, l3], wait_for_announce=True)
954+
node_factory.join_nodes([l1, l4], wait_for_announce=True)
955+
node_factory.join_nodes([l2, l4], wait_for_announce=True)
956+
957+
l3inv = l3.rpc.invoice(1000, 'l3inv', 'l3inv')['bolt11']
958+
assert only_one(only_one(l3.rpc.decode(l3inv)['routes']))['pubkey'] == l1.info['id']
959+
960+
l4inv = l4.rpc.invoice(1000, 'l4inv', 'l4inv')['bolt11']
961+
assert [only_one(r)['pubkey'] for r in l4.rpc.decode(l4inv)['routes']] == [l1.info['id'], l2.info['id']]
962+
963+
l1.rpc.xpay(l3inv)
964+
l1.rpc.xpay(l4inv)

0 commit comments

Comments
 (0)