Skip to content

Commit ff05672

Browse files
committed
renepay: handle non-MPP invoices
Changelog-Fixed: renepay: Now able to handle invoices that don't support Multi-Path-Payments. Signed-off-by: Lagrang3 <[email protected]>
1 parent 2facae2 commit ff05672

File tree

7 files changed

+77
-5
lines changed

7 files changed

+77
-5
lines changed

plugins/renepay/json.c

+2-1
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,8 @@ void json_add_payment(struct json_stream *s, const struct payment *payment)
277277

278278
json_add_timeabs(s, "created_at", pinfo->start_time);
279279
json_add_u64(s, "groupid", payment->groupid);
280-
json_add_u64(s, "parts", payment->next_partid);
280+
// all tried parts, not all successful parts
281+
json_add_u64(s, "parts", payment_parts(payment));
281282

282283
switch (payment->status) {
283284
case PAYMENT_SUCCESS:

plugins/renepay/mods.c

+2
Original file line numberDiff line numberDiff line change
@@ -587,6 +587,8 @@ static struct command_result *getroutes_cb(struct payment *payment)
587587
json_add_string(req->js, NULL, "auto.sourcefree");
588588
json_add_string(req->js, NULL, payment->payment_layer);
589589
json_add_string(req->js, NULL, RENEPAY_LAYER);
590+
if (!payment->payment_info.use_mpp)
591+
json_add_string(req->js, NULL, "auto.no_mpp_support");
590592
json_array_end(req->js);
591593
// FIXME: add further constraints here if necessary when they become
592594
// available in getroutes

plugins/renepay/payment.c

+3-1
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,8 @@ bool payment_set_constraints(
108108
u64 min_prob_success_millionths,
109109
u64 base_prob_success_millionths,
110110
bool use_shadow,
111-
const struct route_exclusion **exclusions)
111+
const struct route_exclusion **exclusions,
112+
bool mpp_enabled)
112113
{
113114
// FIXME: add exclusions to a layer
114115
assert(p);
@@ -129,6 +130,7 @@ bool payment_set_constraints(
129130
pinfo->min_prob_success = min_prob_success_millionths / 1e6;
130131
pinfo->base_prob_success = base_prob_success_millionths / 1e6;
131132
pinfo->use_shadow = use_shadow;
133+
pinfo->use_mpp = mpp_enabled;
132134

133135
return true;
134136
}

plugins/renepay/payment.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,8 @@ bool payment_set_constraints(
122122
u64 min_prob_success_millionths,
123123
u64 base_prob_success_millionths,
124124
bool use_shadow,
125-
const struct route_exclusion **exclusions);
125+
const struct route_exclusion **exclusions,
126+
bool mpp_enabled);
126127

127128
bool payment_refresh(struct payment *p);
128129

plugins/renepay/payment_info.h

+3
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ struct payment_info {
8686

8787
/* --developer allows disabling shadow route */
8888
bool use_shadow;
89+
90+
/* Use Multi-Path-Payments feature. */
91+
bool use_mpp;
8992
};
9093

9194
#endif /* LIGHTNING_PLUGINS_RENEPAY_PAYMENT_INFO_H */

plugins/renepay/renepay.c

+8-2
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ static struct command_result *json_renepay(struct command *cmd, const char *buf,
181181
const char *description;
182182
const char *label;
183183
struct route_exclusion **exclusions;
184+
bool mpp_enabled = true;
184185

185186
// dev options
186187
bool *use_shadow;
@@ -262,6 +263,8 @@ static struct command_result *json_renepay(struct command *cmd, const char *buf,
262263
*inv_msat = amount_msat(*b12->invoice_amount);
263264
}
264265
payment_hash = b12->invoice_payment_hash;
266+
mpp_enabled =
267+
feature_offered(b12->invoice_features, OPT_BASIC_MPP);
265268
} else {
266269
b11 = bolt11_decode(tmpctx, invstr,
267270
plugin_feature_set(cmd->plugin),
@@ -280,6 +283,7 @@ static struct command_result *json_renepay(struct command *cmd, const char *buf,
280283
inv_msat = b11->msat;
281284
invexpiry = b11->timestamp + b11->expiry;
282285
payment_hash = &b11->payment_hash;
286+
mpp_enabled = feature_offered(b11->features, OPT_BASIC_MPP);
283287
}
284288

285289
/* === Set default values for non-trivial constraints === */
@@ -396,7 +400,8 @@ static struct command_result *json_renepay(struct command *cmd, const char *buf,
396400
*min_prob_success_millionths,
397401
*base_prob_success_millionths, use_shadow,
398402
cast_const2(const struct route_exclusion **,
399-
exclusions)) ||
403+
exclusions),
404+
mpp_enabled) ||
400405
!payment_refresh(payment))
401406
return command_fail(
402407
cmd, PLUGIN_ERROR,
@@ -431,7 +436,8 @@ static struct command_result *json_renepay(struct command *cmd, const char *buf,
431436
*min_prob_success_millionths,
432437
*base_prob_success_millionths, use_shadow,
433438
cast_const2(const struct route_exclusion **,
434-
exclusions)) ||
439+
exclusions),
440+
mpp_enabled) ||
435441
!payment_refresh(payment))
436442
return command_fail(
437443
cmd, PLUGIN_ERROR,

tests/test_renepay.py

+57
Original file line numberDiff line numberDiff line change
@@ -863,3 +863,60 @@ def test_unannounced(node_factory):
863863
b12 = l1.rpc.fetchinvoice(offer, "21sat")["invoice"]
864864
ret = l1.rpc.call("renepay", {"invstring": b12})
865865
assert ret["status"] == "complete"
866+
867+
868+
def test_renepay_no_mpp(node_factory, chainparams):
869+
"""Renepay should produce single route solutions for invoices that don't
870+
signal their support for MPP."""
871+
def test_bit(features, bit_pos):
872+
return (features[bit_pos // 8] & (1 << (bit_pos % 8))) != 0
873+
874+
# l4 does not support MPP
875+
l1, l2, l3, l4 = node_factory.get_nodes(
876+
4, opts=[{}, {}, {}, {"dev-force-features": -17}]
877+
)
878+
879+
# make it so such that it would be convenient to use MPP, eg. higher
880+
# probability of success
881+
amount = "900000sat"
882+
start_channels(
883+
[(l1, l2, 1000_000), (l2, l4, 1000_000), (l1, l3, 1000_000), (l3, l4, 1000_000)]
884+
)
885+
886+
# a normal call to getroutes returns two routes
887+
routes = l1.rpc.getroutes(
888+
source=l1.info["id"],
889+
destination=l4.info["id"],
890+
amount_msat=amount,
891+
layers=[],
892+
maxfee_msat="1000sat",
893+
final_cltv=0,
894+
)["routes"]
895+
assert len(routes) == 2
896+
897+
inv = l4.rpc.invoice(amount, "test_no_mpp", "test_no_mpp")["bolt11"]
898+
899+
# check that MPP feature is not present
900+
features = bytearray.fromhex(l1.rpc.decode(inv)["features"])
901+
features.reverse()
902+
# var_onion_optin is compulsory
903+
assert test_bit(features, 8)
904+
# payment_secret is compulsory
905+
assert test_bit(features, 14)
906+
# basic_mpp should not be present
907+
assert test_bit(features, 16) == False
908+
assert test_bit(features, 17) == False
909+
910+
# pay with renepay
911+
ret = l1.rpc.call("renepay", {"invstring": inv})
912+
l1.wait_for_htlcs()
913+
914+
# check that payment succeed
915+
# FIXME: lightningd on l4 will accept the MPP HTLCs (>1 routes) even if we
916+
# have disabled the feature.
917+
assert ret["status"] == "complete"
918+
# check that number of parts was 1
919+
assert ret["parts"] == 1
920+
# check that destination received the payment
921+
receipt = only_one(l4.rpc.listinvoices("test_no_mpp")["invoices"])
922+
assert receipt["amount_received_msat"] == Millisatoshi(amount)

0 commit comments

Comments
 (0)