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
8 changes: 8 additions & 0 deletions .msggen.json
Original file line number Diff line number Diff line change
Expand Up @@ -6293,6 +6293,14 @@
"added": "v23.05",
"deprecated": null
},
"Decode.invoice_paths[].payinfo.htlc_maximum_msat": {
"added": "v26.04",
"deprecated": null
},
"Decode.invoice_paths[].payinfo.htlc_minimum_msat": {
"added": "v26.04",
"deprecated": null
},
"Decode.invoice_payment_hash": {
"added": "v23.05",
"deprecated": null
Expand Down
16 changes: 16 additions & 0 deletions contrib/msggen/msggen/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -9422,6 +9422,8 @@
"fee_base_msat",
"fee_proportional_millionths",
"cltv_expiry_delta",
"htlc_minimum_msat",
"htlc_maximum_msat",
"features"
],
"additionalProperties": false,
Expand All @@ -9444,6 +9446,20 @@
"CLTV delta for path."
]
},
"htlc_minimum_msat": {
"type": "msat",
"added": "v26.04",
"description": [
"Minimum amount which can be sent via path."
]
},
"htlc_maximum_msat": {
"type": "msat",
"added": "v26.04",
"description": [
"Maximum amount which can be sent via path."
]
},
"features": {
"type": "hex",
"description": [
Expand Down
2 changes: 2 additions & 0 deletions contrib/pyln-testing/pyln/testing/grpc2py.py
Original file line number Diff line number Diff line change
Expand Up @@ -1151,6 +1151,8 @@ def decode_invoice_paths_payinfo2py(m):
"features": hexlify(m.features), # PrimitiveField in generate_composite
"fee_base_msat": amount2msat(m.fee_base_msat), # PrimitiveField in generate_composite
"fee_proportional_millionths": m.fee_proportional_millionths, # PrimitiveField in generate_composite
"htlc_maximum_msat": amount2msat(m.htlc_maximum_msat), # PrimitiveField in generate_composite
"htlc_minimum_msat": amount2msat(m.htlc_minimum_msat), # PrimitiveField in generate_composite
})


Expand Down
4 changes: 3 additions & 1 deletion devtools/bolt12-cli.c
Original file line number Diff line number Diff line change
Expand Up @@ -276,10 +276,12 @@ static bool print_blindedpaths(const char *fieldname,
tal_hex(tmpctx, p[j]->encrypted_recipient_data));
}
if (i < tal_count(blindedpay)) {
printf(" blindedpay: fee=%u/%u,cltv=%u,features=%s",
printf(" blindedpay: fee=%u/%u,cltv=%u,min/max=%s/%s,features=%s",
blindedpay[i]->fee_base_msat,
blindedpay[i]->fee_proportional_millionths,
blindedpay[i]->cltv_expiry_delta,
fmt_amount_msat(tmpctx, blindedpay[i]->htlc_minimum_msat),
fmt_amount_msat(tmpctx, blindedpay[i]->htlc_maximum_msat),
tal_hex(tmpctx, blindedpay[i]->features));
}
printf("\n");
Expand Down
16 changes: 16 additions & 0 deletions doc/schemas/decode.json
Original file line number Diff line number Diff line change
Expand Up @@ -1624,6 +1624,8 @@
"fee_base_msat",
"fee_proportional_millionths",
"cltv_expiry_delta",
"htlc_minimum_msat",
"htlc_maximum_msat",
"features"
],
"additionalProperties": false,
Expand All @@ -1646,6 +1648,20 @@
"CLTV delta for path."
]
},
"htlc_minimum_msat": {
"type": "msat",
"added": "v26.04",
"description": [
"Minimum amount which can be sent via path."
]
},
"htlc_maximum_msat": {
"type": "msat",
"added": "v26.04",
"description": [
"Maximum amount which can be sent via path."
]
},
"features": {
"type": "hex",
"description": [
Expand Down
9 changes: 9 additions & 0 deletions plugins/offers.c
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,11 @@ static struct command_result *listincoming_done(struct command *cmd,
if (!enabled)
continue;

/* htlc_max is capped at the node's receivable amount,
* so may be 0 if we hold all the funds. */
if (amount_msat_is_zero(ci.htlc_max))
continue;

/* Not presented if there's no channel_announcement for peer:
* we could use listpeers, but if it's private we probably
* don't want to blinded route through it! */
Expand Down Expand Up @@ -754,6 +759,10 @@ static bool json_add_blinded_paths(struct command *cmd,
blindedpay[i]->fee_proportional_millionths);
json_add_u32(js, "cltv_expiry_delta",
blindedpay[i]->cltv_expiry_delta);
json_add_amount_msat(js, "htlc_minimum_msat",
blindedpay[i]->htlc_minimum_msat);
json_add_amount_msat(js, "htlc_maximum_msat",
blindedpay[i]->htlc_maximum_msat);
json_add_hex_talarr(js, "features", blindedpay[i]->features);
json_object_end(js);
}
Expand Down
11 changes: 8 additions & 3 deletions plugins/topology.c
Original file line number Diff line number Diff line change
Expand Up @@ -528,8 +528,14 @@ listpeerchannels_listincoming_done(struct command *cmd,
const u8 *peer_features;

ourchan = gossmap_nth_chan(gossmap, me, i, &dir);
/* Entirely missing? Ignore. */
if (ourchan->cupdate_off[!dir] == 0)

/* No update for incoming? Ignore. */
if (!gossmap_chan_set(ourchan, !dir))
continue;

/* No peer node_announcement? Ignore */
peer = gossmap_nth_node(gossmap, ourchan, !dir);
if (!gossmap_node_announced(peer))
continue;

if (randbytes_overridden() && i != deterministic_candidate)
Expand All @@ -538,7 +544,6 @@ listpeerchannels_listincoming_done(struct command *cmd,
/* We used to ignore if the peer said it was disabled,
* but we have a report of LND telling us our unannounced
* channel is disabled, so we still use them. */
peer = gossmap_nth_node(gossmap, ourchan, !dir);
scid = gossmap_chan_scid(gossmap, ourchan);

json_object_start(js, NULL);
Expand Down
12 changes: 12 additions & 0 deletions tests/test_pay.py
Original file line number Diff line number Diff line change
Expand Up @@ -6957,6 +6957,8 @@ def test_decode_expired_bolt12(node_factory):
'path': [{'blinded_node_id': '03f293aad04524af9d25e37e30975c9c48df7f46bb5707f5103c5f15477882ef0d',
'encrypted_recipient_data': 'ae4ae7ed0b821d8bbb394f8cd3013ed6c56d0b29baeddb1ce21fb8c34ce33ff59e2250758574924b03a1b567606479c7c9b0'}],
'payinfo': {'cltv_expiry_delta': 18,
'htlc_maximum_msat': 2100000000000000000,
'htlc_minimum_msat': 0,
'features': '',
'fee_base_msat': 0,
'fee_proportional_millionths': 0}}],
Expand Down Expand Up @@ -7155,3 +7157,13 @@ def test_offer_currency_no_amount(node_factory):
l1 = node_factory.get_node()
with pytest.raises(RpcError, match="currency with no amount"):
l1.rpc.decode("lno1qcp4256ypgx9getnwss8vetrw3hhyuckyypwa3eyt44h6txtxquqh7lz5djge4afgfjn7k4rgrkuag0jsd5xvxg")


def test_blinded_path_max(node_factory):
"""We have a bug where we can create a invoice_blindedpay with 0 htlc_maximum_msat."""
l1, l2, l3 = node_factory.line_graph(3, wait_for_announce=True)

# l2, having no advertized address, will use l1 or l3 as fronting nodes.
offer = l2.rpc.offer('any')['bolt12']
inv = l1.rpc.fetchinvoice(offer, '10000msat')['invoice']
assert only_one(l1.rpc.decode(inv)['invoice_paths'])['payinfo']['htlc_maximum_msat'] > 0, f"bad paths for offer = {offer}, invoice = {inv}"
12 changes: 11 additions & 1 deletion tests/test_xpay.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,8 @@ def test_pay_fakenet(node_factory):


def test_xpay_simple(node_factory):
l1, l2, l3, l4 = node_factory.get_nodes(4, opts={'may_reconnect': True})
# Setting dev-allow-localhost means no non-trivial bolt12 blinded paths.
l1, l2, l3, l4 = node_factory.get_nodes(4, opts={'may_reconnect': True, 'dev-allow-localhost': None})
node_factory.join_nodes([l1, l2, l3], wait_for_announce=True)
node_factory.join_nodes([l3, l4], announce_channels=False)

Expand Down Expand Up @@ -177,6 +178,15 @@ def test_xpay_simple(node_factory):
b12 = l1.rpc.fetchinvoice(offer, '100000msat')['invoice']
l1.rpc.xpay(invstring=b12, payer_note="Payment for a cup of coffee")

# BOLT 12, direct peer
offer = l2.rpc.offer('any')['bolt12']
b12 = l1.rpc.fetchinvoice(offer, '10000msat')['invoice']
ret = l1.rpc.xpay(invstring=b12)
assert ret['failed_parts'] == 0
assert ret['successful_parts'] == 1
assert ret['amount_msat'] == 10000
assert ret['amount_sent_msat'] == 10000

# Failure from l4.
b11 = l4.rpc.invoice('10000msat', 'test_xpay_simple2', 'test_xpay_simple2 bolt11')['bolt11']
l4.rpc.delinvoice('test_xpay_simple2', 'unpaid')
Expand Down
Loading