mirror of
https://github.com/aljazceru/lightning.git
synced 2025-12-19 23:24:27 +01:00
invoice: add deschashonly parameter.
LNURL wants this so they can include images etc in descriptions. Replaces: #4892 Changelog-Added: JSON-RPC: `invoice` has a new parameter `deschashonly` to put hash of description in bolt11. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
@@ -1119,11 +1119,16 @@ char *bolt11_encode_(const tal_t *ctx,
|
|||||||
/* Thus we do built-in fields, then extras last. */
|
/* Thus we do built-in fields, then extras last. */
|
||||||
encode_p(&data, &b11->payment_hash);
|
encode_p(&data, &b11->payment_hash);
|
||||||
|
|
||||||
if (b11->description)
|
/* BOLT #11:
|
||||||
encode_d(&data, b11->description);
|
* A writer:
|
||||||
|
*...
|
||||||
|
* - MUST include either exactly one `d` or exactly one `h` field.
|
||||||
|
*/
|
||||||
|
/* We sometimes keep description around (to put in db), so prefer hash */
|
||||||
if (b11->description_hash)
|
if (b11->description_hash)
|
||||||
encode_h(&data, b11->description_hash);
|
encode_h(&data, b11->description_hash);
|
||||||
|
else if (b11->description)
|
||||||
|
encode_d(&data, b11->description);
|
||||||
|
|
||||||
if (n_field)
|
if (n_field)
|
||||||
encode_n(&data, &b11->receiver_id);
|
encode_n(&data, &b11->receiver_id);
|
||||||
|
|||||||
@@ -828,7 +828,7 @@ class LightningRpc(UnixDomainSocketRpc):
|
|||||||
return self.call("help", payload)
|
return self.call("help", payload)
|
||||||
|
|
||||||
def invoice(self, msatoshi, label, description, expiry=None, fallbacks=None,
|
def invoice(self, msatoshi, label, description, expiry=None, fallbacks=None,
|
||||||
preimage=None, exposeprivatechannels=None, cltv=None):
|
preimage=None, exposeprivatechannels=None, cltv=None, deschashonly=None):
|
||||||
"""
|
"""
|
||||||
Create an invoice for {msatoshi} with {label} and {description} with
|
Create an invoice for {msatoshi} with {label} and {description} with
|
||||||
optional {expiry} seconds (default 1 week).
|
optional {expiry} seconds (default 1 week).
|
||||||
@@ -842,6 +842,7 @@ class LightningRpc(UnixDomainSocketRpc):
|
|||||||
"preimage": preimage,
|
"preimage": preimage,
|
||||||
"exposeprivatechannels": exposeprivatechannels,
|
"exposeprivatechannels": exposeprivatechannels,
|
||||||
"cltv": cltv,
|
"cltv": cltv,
|
||||||
|
"deschashonly": deschashonly,
|
||||||
}
|
}
|
||||||
return self.call("invoice", payload)
|
return self.call("invoice", payload)
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ SYNOPSIS
|
|||||||
--------
|
--------
|
||||||
|
|
||||||
**invoice** *msatoshi* *label* *description* [*expiry*]
|
**invoice** *msatoshi* *label* *description* [*expiry*]
|
||||||
[*fallbacks*] [*preimage*] [*exposeprivatechannels*] [*cltv*]
|
[*fallbacks*] [*preimage*] [*exposeprivatechannels*] [*cltv*] [*deschashonly*]
|
||||||
|
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
-----------
|
-----------
|
||||||
@@ -29,8 +29,9 @@ of this invoice.
|
|||||||
|
|
||||||
The *description* is a short description of purpose of payment, e.g. *1
|
The *description* is a short description of purpose of payment, e.g. *1
|
||||||
cup of coffee*. This value is encoded into the BOLT11 invoice and is
|
cup of coffee*. This value is encoded into the BOLT11 invoice and is
|
||||||
viewable by any node you send this invoice to. It must be UTF-8, and
|
viewable by any node you send this invoice to (unless *deschashonly* is
|
||||||
cannot use *\\u* JSON escape codes.
|
true as described below). It must be UTF-8, and cannot use *\\u* JSON
|
||||||
|
escape codes.
|
||||||
|
|
||||||
The *expiry* is optionally the time the invoice is valid for; without a
|
The *expiry* is optionally the time the invoice is valid for; without a
|
||||||
suffix it is interpreted as seconds, otherwise suffixes *s*, *m*, *h*,
|
suffix it is interpreted as seconds, otherwise suffixes *s*, *m*, *h*,
|
||||||
@@ -68,6 +69,11 @@ payment.
|
|||||||
If specified, *cltv* sets the *min_final_cltv_expiry* for the invoice.
|
If specified, *cltv* sets the *min_final_cltv_expiry* for the invoice.
|
||||||
Otherwise, it's set to the parameter **cltv-final**.
|
Otherwise, it's set to the parameter **cltv-final**.
|
||||||
|
|
||||||
|
If *deschash* is true (default false), then the bolt11 returned
|
||||||
|
contains a hash of the *description*, rather than the *description*
|
||||||
|
itself: this allows much longer descriptions, but they must be
|
||||||
|
communicated via some other mechanism.
|
||||||
|
|
||||||
RETURN VALUE
|
RETURN VALUE
|
||||||
------------
|
------------
|
||||||
|
|
||||||
|
|||||||
@@ -1135,6 +1135,7 @@ static struct command_result *json_invoice(struct command *cmd,
|
|||||||
u32 *cltv;
|
u32 *cltv;
|
||||||
struct jsonrpc_request *req;
|
struct jsonrpc_request *req;
|
||||||
struct plugin *plugin;
|
struct plugin *plugin;
|
||||||
|
bool *hashonly;
|
||||||
#if DEVELOPER
|
#if DEVELOPER
|
||||||
const jsmntok_t *routes;
|
const jsmntok_t *routes;
|
||||||
#endif
|
#endif
|
||||||
@@ -1153,6 +1154,7 @@ static struct command_result *json_invoice(struct command *cmd,
|
|||||||
&info->chanhints),
|
&info->chanhints),
|
||||||
p_opt_def("cltv", param_number, &cltv,
|
p_opt_def("cltv", param_number, &cltv,
|
||||||
cmd->ld->config.cltv_final),
|
cmd->ld->config.cltv_final),
|
||||||
|
p_opt_def("deschashonly", param_bool, &hashonly, false),
|
||||||
#if DEVELOPER
|
#if DEVELOPER
|
||||||
p_opt("dev-routes", param_array, &routes),
|
p_opt("dev-routes", param_array, &routes),
|
||||||
#endif
|
#endif
|
||||||
@@ -1165,7 +1167,7 @@ static struct command_result *json_invoice(struct command *cmd,
|
|||||||
INVOICE_MAX_LABEL_LEN);
|
INVOICE_MAX_LABEL_LEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strlen(desc_val) > BOLT11_FIELD_BYTE_LIMIT) {
|
if (strlen(desc_val) > BOLT11_FIELD_BYTE_LIMIT && !*hashonly) {
|
||||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||||
"Descriptions greater than %d bytes "
|
"Descriptions greater than %d bytes "
|
||||||
"not yet supported "
|
"not yet supported "
|
||||||
@@ -1207,6 +1209,17 @@ static struct command_result *json_invoice(struct command *cmd,
|
|||||||
info->b11->min_final_cltv_expiry = *cltv;
|
info->b11->min_final_cltv_expiry = *cltv;
|
||||||
info->b11->expiry = *expiry;
|
info->b11->expiry = *expiry;
|
||||||
info->b11->description = tal_steal(info->b11, desc_val);
|
info->b11->description = tal_steal(info->b11, desc_val);
|
||||||
|
/* BOLT #11:
|
||||||
|
* * `h` (23): `data_length` 52. 256-bit description of purpose of payment (SHA256).
|
||||||
|
*...
|
||||||
|
* A writer:
|
||||||
|
*...
|
||||||
|
* - MUST include either exactly one `d` or exactly one `h` field.
|
||||||
|
*/
|
||||||
|
if (*hashonly) {
|
||||||
|
info->b11->description_hash = tal(info->b11, struct sha256);
|
||||||
|
sha256(info->b11->description_hash, desc_val, strlen(desc_val));
|
||||||
|
} else
|
||||||
info->b11->description_hash = NULL;
|
info->b11->description_hash = NULL;
|
||||||
info->b11->payment_secret = tal_dup(info->b11, struct secret,
|
info->b11->payment_secret = tal_dup(info->b11, struct secret,
|
||||||
&payment_secret);
|
&payment_secret);
|
||||||
|
|||||||
@@ -712,3 +712,24 @@ def test_listinvoices_filter(node_factory):
|
|||||||
for q in queries:
|
for q in queries:
|
||||||
r = l1.rpc.listinvoices(**q)
|
r = l1.rpc.listinvoices(**q)
|
||||||
assert len(r['invoices']) == 0
|
assert len(r['invoices']) == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_invoice_deschash(node_factory, chainparams):
|
||||||
|
l1, l2 = node_factory.line_graph(2)
|
||||||
|
|
||||||
|
# BOLT #11:
|
||||||
|
# * `h`: tagged field: hash of description
|
||||||
|
# * `p5`: `data_length` (`p` = 1, `5` = 20; 1 * 32 + 20 == 52)
|
||||||
|
# * `8yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs`: SHA256 of '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'
|
||||||
|
inv = l2.rpc.invoice(42, 'label', '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', deschashonly=True)
|
||||||
|
assert '8yjmdan79s6qqdhdzgynm4zwqd5d7xmw5fk98klysy043l2ahrqs' in inv['bolt11']
|
||||||
|
|
||||||
|
b11 = l2.rpc.decodepay(inv['bolt11'])
|
||||||
|
assert 'description' not in b11
|
||||||
|
assert b11['description_hash'] == '3925b6f67e2c340036ed12093dd44e0368df1b6ea26c53dbe4811f58fd5db8c1'
|
||||||
|
|
||||||
|
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'])
|
||||||
|
|||||||
Reference in New Issue
Block a user