Skip to content

Commit fdd60db

Browse files
committed
keysend: Add maxfee to keysend for consistency with pay. ([#7227])
Changelog-Added: keysend: Add `maxfee` to keysend for consistency with pay. ([#7227])
1 parent 21fcd45 commit fdd60db

File tree

11 files changed

+789
-707
lines changed

11 files changed

+789
-707
lines changed

.msggen.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1521,6 +1521,7 @@
15211521
"KeySend.extratlvs": 9,
15221522
"KeySend.label": 3,
15231523
"KeySend.maxdelay": 6,
1524+
"KeySend.maxfee": 11,
15241525
"KeySend.maxfeepercent": 4,
15251526
"KeySend.msatoshi": 2,
15261527
"KeySend.retry_for": 5,
@@ -6362,6 +6363,10 @@
63626363
"added": "pre-v0.10.1",
63636364
"deprecated": null
63646365
},
6366+
"KeySend.maxfee": {
6367+
"added": "v24.11",
6368+
"deprecated": null
6369+
},
63656370
"KeySend.maxfeepercent": {
63666371
"added": "pre-v0.10.1",
63676372
"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-grpc/src/test.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,7 @@ fn test_keysend() {
293293
}],
294294
}),
295295
extratlvs: None,
296+
maxfee: None
296297
};
297298

298299
let u: cln_rpc::model::requests::KeysendRequest = g.into();

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: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15048,6 +15048,13 @@
1504815048
"description": [
1504915049
"Dictionary of additional fields to insert into the final tlv. The format is 'fieldnumber': 'hexstring'."
1505015050
]
15051+
},
15052+
"maxfee": {
15053+
"added": "v24.11",
15054+
"type": "msat",
15055+
"description": [
15056+
"*maxfee* overrides both *maxfeepercent* and *exemptfee* defaults (and if you specify *maxfee* you cannot specify either of those), and creates an absolute limit on what fee we will pay. This allows you to implement your own heuristics rather than the primitive ones used here."
15057+
]
1505115058
}
1505215059
}
1505315060
},
@@ -15180,12 +15187,12 @@
1518015187
},
1518115188
"response": {
1518215189
"destination": "035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d",
15183-
"payment_hash": "80ff407792947a23f193f9a1968e9a437b071364ae3159f83631335c9a453c1b",
15184-
"created_at": 1722303677.1300898,
15190+
"payment_hash": "bf2806c76e67fbc07bccd878a2c583664f97ba49eebb707939bacaa9d6813858",
15191+
"created_at": 1726169170.7366238,
1518515192
"parts": 1,
1518615193
"amount_msat": 10000,
1518715194
"amount_sent_msat": 10001,
15188-
"payment_preimage": "0d802c9c611bae611d51afa8ddf396df8ba4e0580a2eccfd1120da97e70482a0",
15195+
"payment_preimage": "8e9f92f7f4928fde1469b199680aa09ca2690fa4e1c0b21ea60fd87e962ecce4",
1518915196
"status": "complete"
1519015197
}
1519115198
},
@@ -15204,12 +15211,12 @@
1520415211
},
1520515212
"response": {
1520615213
"destination": "0382ce59ebf18be7d84677c2e35f23294b9992ceca95491fcf8a56c6cb2d9de199",
15207-
"payment_hash": "3b80a3028343b16f8ab7261343eae40ff73ba833b0b7d4dcbfd42a3078dc322b",
15208-
"created_at": 1722303679.3164163,
15214+
"payment_hash": "06f861fa426d342ca144785243c44093654ff8dfc7cec7db346286c20010cffc",
15215+
"created_at": 1726169172.886992,
1520915216
"parts": 1,
1521015217
"amount_msat": 10000000,
1521115218
"amount_sent_msat": 10000202,
15212-
"payment_preimage": "f76d6b7ef362f33e25eb5571e616f6e539a2c77caf0afa4227d1351546823664",
15219+
"payment_preimage": "9416f0889f0dfe6316e047aee01a5451e29753a51b10ddda83fe6b8ccad2eb72",
1521315220
"status": "complete"
1521415221
}
1521515222
},
@@ -15223,7 +15230,7 @@
1522315230
"routehints": [
1522415231
[
1522515232
{
15226-
"scid": "6250403x3681116x19863",
15233+
"scid": "6974878x14919575x17443",
1522715234
"id": "022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59",
1522815235
"feebase": "1msat",
1522915236
"feeprop": 10,
@@ -15235,12 +15242,12 @@
1523515242
},
1523615243
"response": {
1523715244
"destination": "035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d",
15238-
"payment_hash": "0458c01fdd3aa0b9829002390301f92083e78dc27bb293bc3e7caee5d4ed7259",
15239-
"created_at": 1722303682.5805738,
15245+
"payment_hash": "c9e6b85aafb27fbaceb3f0eca6d0940d6e1d93a4be2d6da981aed9503fcec92c",
15246+
"created_at": 1726169175.977477,
1524015247
"parts": 2,
1524115248
"amount_msat": 10000,
1524215249
"amount_sent_msat": 10001,
15243-
"payment_preimage": "4dad6dcf625f650a35a8199fbda18ea4f6717cdfadb40e6bed2bf5f96a4742b0",
15250+
"payment_preimage": "1cd3749122f3a1b8c1cbd9f4585ec820ec4e1b8f2aa3514a4dd9deda170bdf5d",
1524415251
"status": "complete"
1524515252
}
1524615253
}

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

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

doc/schemas/lightning-keysend.json

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,13 @@
103103
"description": [
104104
"Dictionary of additional fields to insert into the final tlv. The format is 'fieldnumber': 'hexstring'."
105105
]
106+
},
107+
"maxfee": {
108+
"added": "v24.11",
109+
"type": "msat",
110+
"description": [
111+
"*maxfee* overrides both *maxfeepercent* and *exemptfee* defaults (and if you specify *maxfee* you cannot specify either of those), and creates an absolute limit on what fee we will pay. This allows you to implement your own heuristics rather than the primitive ones used here."
112+
]
106113
}
107114
}
108115
},
@@ -235,12 +242,12 @@
235242
},
236243
"response": {
237244
"destination": "035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d",
238-
"payment_hash": "80ff407792947a23f193f9a1968e9a437b071364ae3159f83631335c9a453c1b",
239-
"created_at": 1722303677.1300898,
245+
"payment_hash": "bf2806c76e67fbc07bccd878a2c583664f97ba49eebb707939bacaa9d6813858",
246+
"created_at": 1726169170.7366238,
240247
"parts": 1,
241248
"amount_msat": 10000,
242249
"amount_sent_msat": 10001,
243-
"payment_preimage": "0d802c9c611bae611d51afa8ddf396df8ba4e0580a2eccfd1120da97e70482a0",
250+
"payment_preimage": "8e9f92f7f4928fde1469b199680aa09ca2690fa4e1c0b21ea60fd87e962ecce4",
244251
"status": "complete"
245252
}
246253
},
@@ -259,12 +266,12 @@
259266
},
260267
"response": {
261268
"destination": "0382ce59ebf18be7d84677c2e35f23294b9992ceca95491fcf8a56c6cb2d9de199",
262-
"payment_hash": "3b80a3028343b16f8ab7261343eae40ff73ba833b0b7d4dcbfd42a3078dc322b",
263-
"created_at": 1722303679.3164163,
269+
"payment_hash": "06f861fa426d342ca144785243c44093654ff8dfc7cec7db346286c20010cffc",
270+
"created_at": 1726169172.886992,
264271
"parts": 1,
265272
"amount_msat": 10000000,
266273
"amount_sent_msat": 10000202,
267-
"payment_preimage": "f76d6b7ef362f33e25eb5571e616f6e539a2c77caf0afa4227d1351546823664",
274+
"payment_preimage": "9416f0889f0dfe6316e047aee01a5451e29753a51b10ddda83fe6b8ccad2eb72",
268275
"status": "complete"
269276
}
270277
},
@@ -278,7 +285,7 @@
278285
"routehints": [
279286
[
280287
{
281-
"scid": "6250403x3681116x19863",
288+
"scid": "6974878x14919575x17443",
282289
"id": "022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59",
283290
"feebase": "1msat",
284291
"feeprop": 10,
@@ -290,12 +297,12 @@
290297
},
291298
"response": {
292299
"destination": "035d2b1192dfba134e10e540875d366ebc8bc353d5aa766b80c090b39c3a5d885d",
293-
"payment_hash": "0458c01fdd3aa0b9829002390301f92083e78dc27bb293bc3e7caee5d4ed7259",
294-
"created_at": 1722303682.5805738,
300+
"payment_hash": "c9e6b85aafb27fbaceb3f0eca6d0940d6e1d93a4be2d6da981aed9503fcec92c",
301+
"created_at": 1726169175.977477,
295302
"parts": 2,
296303
"amount_msat": 10000,
297304
"amount_sent_msat": 10001,
298-
"payment_preimage": "4dad6dcf625f650a35a8199fbda18ea4f6717cdfadb40e6bed2bf5f96a4742b0",
305+
"payment_preimage": "1cd3749122f3a1b8c1cbd9f4585ec820ec4e1b8f2aa3514a4dd9deda170bdf5d",
299306
"status": "complete"
300307
}
301308
}

plugins/keysend.c

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ static u64 *accepted_extra_tlvs;
2323
* ================
2424
*
2525
* The keysend modifier adds the payment preimage to the TLV payload. This
26-
* enables the recipient to accept the payment despite it not correspondin to
26+
* enables the recipient to accept the payment despite it not corresponding to
2727
* an invoice that the recipient created. Keysend does not provide any proof
2828
* or payment, but does not require an out-of-band communication round to get
2929
* an invoice first.
@@ -215,7 +215,7 @@ static struct command_result *json_keysend(struct command *cmd, const char *buf,
215215
{
216216
struct payment *p;
217217
const char *label;
218-
struct amount_msat *exemptfee, *msat;
218+
struct amount_msat *exemptfee, *msat, *maxfee;
219219
struct node_id *destination;
220220
u64 *maxfee_pct_millionths;
221221
u32 *maxdelay;
@@ -229,14 +229,15 @@ static struct command_result *json_keysend(struct command *cmd, const char *buf,
229229
p_req("destination", param_node_id, &destination),
230230
p_req("amount_msat", param_msat, &msat),
231231
p_opt("label", param_string, &label),
232-
p_opt_def("maxfeepercent", param_millionths,
233-
&maxfee_pct_millionths, 500000),
232+
p_opt("maxfeepercent", param_millionths,
233+
&maxfee_pct_millionths),
234234
p_opt_def("retry_for", param_number, &retryfor, 60),
235235
p_opt_def("maxdelay", param_number, &maxdelay,
236236
maxdelay_default),
237-
p_opt_def("exemptfee", param_msat, &exemptfee, AMOUNT_MSAT(5000)),
237+
p_opt("exemptfee", param_msat, &exemptfee),
238238
p_opt("extratlvs", param_extra_tlvs, &extra_fields),
239239
p_opt("routehints", param_routehint_array, &hints),
240+
p_opt("maxfee", param_msat, &maxfee),
240241
p_opt_dev("dev_use_shadow", param_bool, &dev_use_shadow, true),
241242
NULL))
242243
return command_param_failed();
@@ -272,19 +273,36 @@ static struct command_result *json_keysend(struct command *cmd, const char *buf,
272273
"We are the destination. Keysend cannot be used to send funds to yourself");
273274
}
274275

275-
if (!amount_msat_fee(&p->constraints.fee_budget, p->our_amount, 0,
276-
*maxfee_pct_millionths / 100)) {
277-
return command_fail(
278-
cmd, JSONRPC2_INVALID_PARAMS,
279-
"Overflow when computing fee budget, fee rate too high.");
280-
}
281-
282276
p->constraints.cltv_budget = *maxdelay;
283277

284278
payment_mod_keysend_get_data(p)->extra_tlvs =
285279
tal_steal(p, extra_fields);
286280

287-
payment_mod_exemptfee_get_data(p)->amount = *exemptfee;
281+
if (maxfee) {
282+
if (maxfee_pct_millionths || exemptfee) {
283+
return command_fail(
284+
cmd, JSONRPC2_INVALID_PARAMS,
285+
"If you specify maxfee, cannot specify maxfeepercent or exemptfee.");
286+
}
287+
p->constraints.fee_budget = *maxfee;
288+
payment_mod_exemptfee_get_data(p)->amount = AMOUNT_MSAT(0);
289+
} else {
290+
u64 maxppm;
291+
292+
if (maxfee_pct_millionths)
293+
maxppm = *maxfee_pct_millionths / 100;
294+
else
295+
maxppm = 500000 / 100;
296+
if (!amount_msat_fee(&p->constraints.fee_budget, p->our_amount, 0,
297+
maxppm)) {
298+
return command_fail(
299+
cmd, JSONRPC2_INVALID_PARAMS,
300+
"Overflow when computing fee budget, fee rate too high.");
301+
}
302+
payment_mod_exemptfee_get_data(p)->amount
303+
= exemptfee ? *exemptfee : AMOUNT_MSAT(5000);
304+
}
305+
288306
payment_mod_shadowroute_get_data(p)->use_shadow = *dev_use_shadow;
289307
p->label = tal_steal(p, label);
290308

tests/test_pay.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3655,6 +3655,31 @@ def test_keysend_routehint(node_factory):
36553655
assert(inv['amount_received_msat'] >= Millisatoshi(amt))
36563656

36573657

3658+
def test_keysend_maxfee(node_factory):
3659+
l1, l2, l3 = node_factory.line_graph(
3660+
3,
3661+
wait_for_announce=True,
3662+
opts=[{}, {'fee-base': 50, 'fee-per-satoshi': 0}, {}]
3663+
)
3664+
3665+
# We should fail because maxfee and exemptfee cannot be set simultaneously.
3666+
with pytest.raises(RpcError):
3667+
l1.rpc.call("keysend", payload={'destination': l3.info['id'], 'amount_msat': 1, 'maxfee': 1, 'exemptfee': 5000})
3668+
3669+
# We should fail because maxfee and maxfeepercent cannot be set simultaneously.
3670+
with pytest.raises(RpcError):
3671+
l1.rpc.call("keysend", payload={'destination': l3.info['id'], 'amount_msat': 1, 'maxfee': 1, 'maxfeepercent': 0.0001})
3672+
3673+
# We should fail because 50msat base fee on l2 exceeds maxfee of 1msat.
3674+
with pytest.raises(RpcError):
3675+
l1.rpc.call("keysend", payload={'destination': l3.info['id'], 'amount_msat': 1, 'maxfee': 1})
3676+
assert len(l3.rpc.listinvoices()['invoices']) == 0
3677+
3678+
# Perform a normal keysend with maxfee.
3679+
l1.rpc.call("keysend", payload={'destination': l3.info['id'], 'amount_msat': 1, 'maxfee': 50})
3680+
assert len(l3.rpc.listinvoices()['invoices']) == 1
3681+
3682+
36583683
def test_invalid_onion_channel_update(node_factory):
36593684
'''
36603685
Some onion failures "should" send a `channel_update`.

0 commit comments

Comments
 (0)