Skip to content

Commit a90de8d

Browse files
Lagrang3rustyrussell
authored andcommitted
sendonion: add total_amount_msat parameter
Changelog-Added: sendonion: a new paramter total_amount_msat to make MPP payments with sendpay and sendonion compatible. [ Reordered to put new parameter at the end --RR ] Signed-off-by: Lagrang3 <lagrang3@protonmail.com>
1 parent 5d16bb0 commit a90de8d

File tree

9 files changed

+1016
-911
lines changed

9 files changed

+1016
-911
lines changed

.msggen.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3400,7 +3400,8 @@
34003400
"SendOnion.onion": 1,
34013401
"SendOnion.partid": 6,
34023402
"SendOnion.payment_hash": 3,
3403-
"SendOnion.shared_secrets[]": 5
3403+
"SendOnion.shared_secrets[]": 5,
3404+
"SendOnion.total_amount_msat": 15
34043405
},
34053406
"SendonionResponse": {
34063407
"SendOnion.amount_msat": 4,
@@ -12077,6 +12078,10 @@
1207712078
"added": "pre-v0.10.1",
1207812079
"deprecated": null
1207912080
},
12081+
"SendOnion.total_amount_msat": {
12082+
"added": "v25.05",
12083+
"deprecated": null
12084+
},
1208012085
"SendOnion.updated_index": {
1208112086
"added": "v23.11",
1208212087
"deprecated": null

cln-grpc/proto/node.proto

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cln-grpc/src/convert.rs

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cln-rpc/src/model.rs

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

contrib/msggen/msggen/schema.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30975,6 +30975,15 @@
3097530975
"description": [
3097630976
"If provided, it will be returned in *waitsendpay* and *listsendpays* results."
3097730977
]
30978+
},
30979+
"total_amount_msat": {
30980+
"type": "msat",
30981+
"description": [
30982+
"This is the full amount requested by the destination in the invoice. It is needed internally for multi-part payments.",
30983+
"Its default value is 0 msat for backwards compatibility."
30984+
],
30985+
"default": "0",
30986+
"added": "v25.05"
3097830987
}
3097930988
}
3098030989
},

contrib/pyln-grpc-proto/pyln/grpc/node_pb2.py

Lines changed: 908 additions & 908 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

doc/schemas/sendonion.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,15 @@
125125
"description": [
126126
"If provided, it will be returned in *waitsendpay* and *listsendpays* results."
127127
]
128+
},
129+
"total_amount_msat": {
130+
"type": "msat",
131+
"description": [
132+
"This is the full amount requested by the destination in the invoice. It is needed internally for multi-part payments.",
133+
"Its default value is 0 msat for backwards compatibility."
134+
],
135+
"default": "0",
136+
"added": "v25.05"
128137
}
129138
}
130139
},

lightningd/pay.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1329,7 +1329,7 @@ static struct command_result *json_sendonion(struct command *cmd,
13291329
const char *label, *invstring, *description;
13301330
struct node_id *destination;
13311331
struct secret *path_secrets;
1332-
struct amount_msat *msat;
1332+
struct amount_msat *msat, *total_msat;
13331333
u64 *partid, *group;
13341334
struct sha256 *local_invreq_id = NULL;
13351335

@@ -1347,6 +1347,7 @@ static struct command_result *json_sendonion(struct command *cmd,
13471347
p_opt("localinvreqid", param_sha256, &local_invreq_id),
13481348
p_opt("groupid", param_u64, &group),
13491349
p_opt("description", param_string, &description),
1350+
p_opt_def("total_amount_msat", param_msat, &total_msat, AMOUNT_MSAT(0)),
13501351
NULL))
13511352
return command_param_failed();
13521353

@@ -1370,7 +1371,7 @@ static struct command_result *json_sendonion(struct command *cmd,
13701371
return command_check_done(cmd);
13711372

13721373
return send_payment_core(ld, cmd, payment_hash, *partid, *group,
1373-
first_hop, *msat, AMOUNT_MSAT(0),
1374+
first_hop, *msat, *total_msat,
13741375
label, invstring, description,
13751376
packet, destination, NULL, NULL,
13761377
path_secrets, local_invreq_id);

tests/test_pay.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6862,3 +6862,79 @@ def test_decode_expired_bolt12(node_factory):
68626862
'type': 'bolt12 invoice',
68636863
'valid': True,
68646864
}
6865+
6866+
6867+
def test_sendonion_sendpay(node_factory, bitcoind):
6868+
"""
6869+
Check that a payment initiated with sendpay can be completed by sendonion.
6870+
"""
6871+
opts = [
6872+
{"disable-mpp": None, "fee-base": 1000, "fee-per-satoshi": 1000},
6873+
]
6874+
l1, l2, l3 = node_factory.line_graph(3, wait_for_announce=True, opts=opts * 3)
6875+
6876+
total_amount = "10000sat"
6877+
# First case, do not overpay a pending MPP payment
6878+
invstr = l3.rpc.invoice("10000sat", "inv", "description")["bolt11"]
6879+
inv = l1.rpc.decode(invstr)
6880+
route2 = l1.rpc.getroute(inv["payee"], "2000sat", 10)["route"]
6881+
route8 = l1.rpc.getroute(inv["payee"], "8000sat", 10)["route"]
6882+
6883+
def pay_with_sendpay(invoice, route, groupid, partid):
6884+
l1.rpc.sendpay(
6885+
route=route,
6886+
payment_hash=invoice["payment_hash"],
6887+
payment_secret=invoice["payment_secret"],
6888+
amount_msat=invoice["amount_msat"],
6889+
groupid=groupid,
6890+
partid=partid,
6891+
)
6892+
6893+
def pay_with_sendonion(invoice, route, groupid, partid):
6894+
blockheight = l1.rpc.getinfo()["blockheight"]
6895+
# Need to shift the parameters by one hop
6896+
hops = []
6897+
for h, n in zip(route[:-1], route[1:]):
6898+
# We tell the node h about the parameters to use for n (a.k.a. h + 1)
6899+
hops.append(
6900+
{
6901+
"pubkey": h["id"],
6902+
"payload": serialize_payload_tlv(
6903+
n["amount_msat"], n["delay"], n["channel"], blockheight
6904+
).hex(),
6905+
}
6906+
)
6907+
# The last hop has a special payload:
6908+
hops.append(
6909+
{
6910+
"pubkey": route[-1]["id"],
6911+
"payload": serialize_payload_final_tlv(
6912+
route[-1]["amount_msat"],
6913+
route[-1]["delay"],
6914+
invoice["amount_msat"],
6915+
blockheight,
6916+
invoice["payment_secret"],
6917+
).hex(),
6918+
}
6919+
)
6920+
onion = l1.rpc.createonion(hops=hops, assocdata=invoice["payment_hash"])
6921+
l1.rpc.call(
6922+
"sendonion",
6923+
{
6924+
"onion": onion["onion"],
6925+
"shared_secrets": onion["shared_secrets"],
6926+
"first_hop": route[0],
6927+
"payment_hash": invoice["payment_hash"],
6928+
"total_amount_msat": invoice["amount_msat"],
6929+
"groupid": groupid,
6930+
"partid": partid,
6931+
},
6932+
)
6933+
6934+
pay_with_sendpay(inv, route2, 1, 1)
6935+
pay_with_sendonion(inv, route8, 1, 2)
6936+
6937+
l1.wait_for_htlcs()
6938+
invoice = only_one(l3.rpc.listinvoices("inv")["invoices"])
6939+
# the receive amount should be exact
6940+
assert invoice["amount_received_msat"] == Millisatoshi(total_amount)

0 commit comments

Comments
 (0)