mirror of
https://github.com/aljazceru/lightning.git
synced 2025-12-19 07:04:22 +01:00
pay: require description if bolt11 only has hash.
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au> Changelog-Added: JSON-RPC: `pay` has `description` parameter, will be required if bolt11 only has a hash. Changelog-Deprecated: JSON-RPC: `pay` for a bolt11 which uses a `description_hash`, without setting `description`.
This commit is contained in:
@@ -998,7 +998,7 @@ class LightningRpc(UnixDomainSocketRpc):
|
||||
def pay(self, bolt11, msatoshi=None, label=None, riskfactor=None,
|
||||
maxfeepercent=None, retry_for=None,
|
||||
maxdelay=None, exemptfee=None, localofferid=None, exclude=None,
|
||||
maxfee=None):
|
||||
maxfee=None, description=None):
|
||||
"""
|
||||
Send payment specified by {bolt11} with {msatoshi}
|
||||
(ignored if {bolt11} has an amount), optional {label}
|
||||
@@ -1016,6 +1016,7 @@ class LightningRpc(UnixDomainSocketRpc):
|
||||
"localofferid": localofferid,
|
||||
"exclude": exclude,
|
||||
"maxfee": maxfee,
|
||||
"description": description,
|
||||
}
|
||||
return self.call("pay", payload)
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ SYNOPSIS
|
||||
|
||||
**pay** *bolt11* [*msatoshi*] [*label*] [*riskfactor*]
|
||||
[*maxfeepercent*] [*retry_for*] [*maxdelay*] [*exemptfee*]
|
||||
[*localofferid*] [*exclude*] [*maxfee*]
|
||||
[*localofferid*] [*exclude*] [*maxfee*] [*description*]
|
||||
|
||||
DESCRIPTION
|
||||
-----------
|
||||
@@ -43,6 +43,11 @@ 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.
|
||||
|
||||
*description* is only required for bolt11 invoices which do not
|
||||
contain a description themselves, but contain a description hash.
|
||||
*description* is then checked against the hash inside the invoice
|
||||
before it will be paid.
|
||||
|
||||
The response will occur when the payment fails or succeeds. Once a
|
||||
payment has succeeded, calls to **pay** with the same *bolt11* will
|
||||
succeed immediately.
|
||||
|
||||
@@ -48,6 +48,9 @@
|
||||
},
|
||||
"maxfee": {
|
||||
"type": "msat"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -926,7 +926,7 @@ static struct command_result *json_pay(struct command *cmd,
|
||||
u64 *maxfee_pct_millionths;
|
||||
u32 *maxdelay;
|
||||
struct amount_msat *exemptfee, *msat, *maxfee;
|
||||
const char *label;
|
||||
const char *label, *description;
|
||||
unsigned int *retryfor;
|
||||
u64 *riskfactor_millionths;
|
||||
struct shadow_route_data *shadow_route;
|
||||
@@ -959,6 +959,7 @@ static struct command_result *json_pay(struct command *cmd,
|
||||
p_opt("localofferid", param_sha256, &local_offer_id),
|
||||
p_opt("exclude", param_route_exclusion_array, &exclusions),
|
||||
p_opt("maxfee", param_msat, &maxfee),
|
||||
p_opt("description", param_string, &description),
|
||||
#if DEVELOPER
|
||||
p_opt_def("use_shadow", param_bool, &use_shadow, true),
|
||||
#endif
|
||||
@@ -971,7 +972,7 @@ static struct command_result *json_pay(struct command *cmd,
|
||||
if (!bolt12_has_prefix(b11str)) {
|
||||
b11 =
|
||||
bolt11_decode(tmpctx, b11str, plugin_feature_set(cmd->plugin),
|
||||
NULL, chainparams, &b11_fail);
|
||||
description, chainparams, &b11_fail);
|
||||
if (b11 == NULL)
|
||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||
"Invalid bolt11: %s", b11_fail);
|
||||
@@ -999,6 +1000,24 @@ static struct command_result *json_pay(struct command *cmd,
|
||||
cmd, JSONRPC2_INVALID_PARAMS,
|
||||
"Invalid bolt11:"
|
||||
" sets feature var_onion with no secret");
|
||||
|
||||
/* BOLT #11:
|
||||
* A reader:
|
||||
*...
|
||||
* - MUST check that the SHA2 256-bit hash in the `h` field
|
||||
* exactly matches the hashed description.
|
||||
*/
|
||||
if (!b11->description && !deprecated_apis) {
|
||||
if (!b11->description_hash) {
|
||||
return command_fail(cmd,
|
||||
JSONRPC2_INVALID_PARAMS,
|
||||
"Invalid bolt11: missing description");
|
||||
}
|
||||
if (!description)
|
||||
return command_fail(cmd,
|
||||
JSONRPC2_INVALID_PARAMS,
|
||||
"bolt11 uses description_hash, but you did not provide description parameter");
|
||||
}
|
||||
} else {
|
||||
b12 = invoice_decode(tmpctx, b11str, strlen(b11str),
|
||||
plugin_feature_set(cmd->plugin),
|
||||
|
||||
@@ -707,8 +707,14 @@ def test_invoice_deschash(node_factory, chainparams):
|
||||
listinv = only_one(l2.rpc.listinvoices()['invoices'])
|
||||
assert listinv['description'] == 'One piece of chocolate cake, one icecream cone, one pickle, one slice of swiss cheese, one slice of salami, one lollypop, one piece of cherry pie, one sausage, one cupcake, and one slice of watermelon'
|
||||
|
||||
# Make sure we can pay it!
|
||||
l1.rpc.pay(inv['bolt11'])
|
||||
# To pay it we need to provide the (correct!) description.
|
||||
with pytest.raises(RpcError, match=r'you did not provide description parameter'):
|
||||
l1.rpc.pay(inv['bolt11'])
|
||||
|
||||
with pytest.raises(RpcError, match=r'does not match description'):
|
||||
l1.rpc.pay(inv['bolt11'], description=listinv['description'][:-1])
|
||||
|
||||
l1.rpc.pay(inv['bolt11'], description=listinv['description'])
|
||||
|
||||
# Try removing description.
|
||||
l2.rpc.delinvoice('label', "paid", desconly=True)
|
||||
|
||||
@@ -398,7 +398,7 @@ def test_pay_plugin(node_factory):
|
||||
# Make sure usage messages are present.
|
||||
msg = 'pay bolt11 [msatoshi] [label] [riskfactor] [maxfeepercent] '\
|
||||
'[retry_for] [maxdelay] [exemptfee] [localofferid] [exclude] '\
|
||||
'[maxfee]'
|
||||
'[maxfee] [description]'
|
||||
if DEVELOPER:
|
||||
msg += ' [use_shadow]'
|
||||
assert only_one(l1.rpc.help('pay')['help'])['command'] == msg
|
||||
|
||||
Reference in New Issue
Block a user