mirror of
https://github.com/ElementsProject/lightning.git
synced 2025-01-18 21:35:11 +01:00
invoice: allow suffixes.
Makes it much easier to set it to 6 hours, for example. Fixes: #2551 Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
parent
1d6584e733
commit
ba41238df9
@ -15,7 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
### Changed
|
||||
|
||||
- JSON API: `invoice` expiry defaults to 7 days.
|
||||
- JSON API: `invoice` expiry defaults to 7 days, and can have s/m/h/d/w suffixes.
|
||||
|
||||
### Deprecated
|
||||
|
||||
|
@ -42,7 +42,7 @@ The \fIlabel\fR must be a unique string or number (which is treated as a string,
|
||||
.sp
|
||||
The \fIdescription\fR is a short description of purpose of payment, e\&.g\&. \fI1 cup of coffee\fR\&. 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 cannot use \fI\eu\fR JSON escape codes\&.
|
||||
.sp
|
||||
The \fIexpiry\fR is optionally the number of seconds the invoice is valid for\&. If no value is provided the default of 604800 (1 week) is used\&.
|
||||
The \fIexpiry\fR is optionally the time the invoice is valid for; without a suffix it is interpreted as seconds, otherwise suffixes \fIs\fR, \fIm\fR, \fIh\fR, \fId\fR, \fIw\fR indicate seconds, minutes, hours, days and weeks respectively\&. If no value is provided the default of 604800 (1w) is used\&.
|
||||
.sp
|
||||
The \fIfallbacks\fR array is one or more fallback addresses to include in the invoice (in order from most\-preferred to least): note that these arrays are not currently tracked to fulfill the invoice\&.
|
||||
.sp
|
||||
|
@ -34,8 +34,11 @@ e.g. '1 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 cannot use '\u' JSON escape codes.
|
||||
|
||||
The 'expiry' is optionally the number of seconds the invoice is valid for.
|
||||
If no value is provided the default of 604800 (1 week) is used.
|
||||
The 'expiry' is optionally the time the invoice is valid for; without
|
||||
a suffix it is interpreted as seconds, otherwise suffixes 's', 'm',
|
||||
'h', 'd', 'w' indicate seconds, minutes, hours, days and weeks
|
||||
respectively. If no value is provided the default of 604800 (1w)
|
||||
is used.
|
||||
|
||||
The 'fallbacks' array is one or more fallback addresses to include in
|
||||
the invoice (in order from most-preferred to least): note that these
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <bitcoin/address.h>
|
||||
#include <bitcoin/base58.h>
|
||||
#include <bitcoin/script.h>
|
||||
#include <ccan/array_size/array_size.h>
|
||||
#include <ccan/str/hex/hex.h>
|
||||
#include <ccan/tal/str/str.h>
|
||||
#include <common/amount.h>
|
||||
@ -14,6 +15,7 @@
|
||||
#include <common/json_escaped.h>
|
||||
#include <common/json_helpers.h>
|
||||
#include <common/jsonrpc_errors.h>
|
||||
#include <common/overflows.h>
|
||||
#include <common/param.h>
|
||||
#include <common/pseudorand.h>
|
||||
#include <common/utils.h>
|
||||
@ -389,6 +391,56 @@ static struct command_result *param_msat_or_any(struct command *cmd,
|
||||
buffer + tok->start);
|
||||
}
|
||||
|
||||
/* Parse time with optional suffix, return seconds */
|
||||
static struct command_result *param_time(struct command *cmd, const char *name,
|
||||
const char *buffer,
|
||||
const jsmntok_t *tok,
|
||||
uint64_t **secs)
|
||||
{
|
||||
/* We need to manipulate this, so make copy */
|
||||
jsmntok_t timetok = *tok;
|
||||
u64 mul;
|
||||
char s;
|
||||
struct {
|
||||
char suffix;
|
||||
u64 mul;
|
||||
} suffixes[] = {
|
||||
{ 's', 1 },
|
||||
{ 'm', 60 },
|
||||
{ 'h', 60*60 },
|
||||
{ 'd', 24*60*60 },
|
||||
{ 'w', 7*24*60*60 } };
|
||||
|
||||
mul = 1;
|
||||
if (timetok.end == timetok.start)
|
||||
s = '\0';
|
||||
else
|
||||
s = buffer[timetok.end - 1];
|
||||
for (size_t i = 0; i < ARRAY_SIZE(suffixes); i++) {
|
||||
if (s == suffixes[i].suffix) {
|
||||
mul = suffixes[i].mul;
|
||||
timetok.end--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*secs = tal(cmd, uint64_t);
|
||||
if (json_to_u64(buffer, &timetok, *secs)) {
|
||||
if (mul_overflows_u64(**secs, mul)) {
|
||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||
"'%s' string '%.*s' is too large",
|
||||
name, tok->end - tok->start,
|
||||
buffer + tok->start);
|
||||
}
|
||||
**secs *= mul;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
|
||||
"'%s' should be a number with optional {s,m,h,d,w} suffix, not '%.*s'",
|
||||
name, tok->end - tok->start, buffer + tok->start);
|
||||
}
|
||||
|
||||
static struct command_result *json_invoice(struct command *cmd,
|
||||
const char *buffer,
|
||||
const jsmntok_t *obj UNNEEDED,
|
||||
@ -415,7 +467,7 @@ static struct command_result *json_invoice(struct command *cmd,
|
||||
p_req("msatoshi", param_msat_or_any, &msatoshi_val),
|
||||
p_req("label", param_label, &info->label),
|
||||
p_req("description", param_escaped_string, &desc_val),
|
||||
p_opt_def("expiry", param_u64, &expiry, 3600*24*7),
|
||||
p_opt_def("expiry", param_time, &expiry, 3600*24*7),
|
||||
p_opt("fallbacks", param_array, &fallbacks),
|
||||
p_opt("preimage", param_tok, &preimagetok),
|
||||
p_opt("exposeprivatechannels", param_bool, &exposeprivate),
|
||||
|
@ -297,6 +297,37 @@ def test_invoice_expiry(node_factory, executor):
|
||||
# all invoices are expired and should be deleted
|
||||
assert len(l2.rpc.listinvoices()['invoices']) == 0
|
||||
|
||||
# Test expiry suffixes.
|
||||
start = int(time.time())
|
||||
inv = l2.rpc.invoice(msatoshi=123000, label='inv_s', description='description', expiry='1s')['bolt11']
|
||||
end = int(time.time())
|
||||
expiry = only_one(l2.rpc.listinvoices('inv_s')['invoices'])['expires_at']
|
||||
assert expiry >= start + 1 and expiry <= end + 1
|
||||
|
||||
start = int(time.time())
|
||||
inv = l2.rpc.invoice(msatoshi=123000, label='inv_m', description='description', expiry='1m')['bolt11']
|
||||
end = int(time.time())
|
||||
expiry = only_one(l2.rpc.listinvoices('inv_m')['invoices'])['expires_at']
|
||||
assert expiry >= start + 60 and expiry <= end + 60
|
||||
|
||||
start = int(time.time())
|
||||
inv = l2.rpc.invoice(msatoshi=123000, label='inv_h', description='description', expiry='1h')['bolt11']
|
||||
end = int(time.time())
|
||||
expiry = only_one(l2.rpc.listinvoices('inv_h')['invoices'])['expires_at']
|
||||
assert expiry >= start + 3600 and expiry <= end + 3600
|
||||
|
||||
start = int(time.time())
|
||||
inv = l2.rpc.invoice(msatoshi=123000, label='inv_d', description='description', expiry='1d')['bolt11']
|
||||
end = int(time.time())
|
||||
expiry = only_one(l2.rpc.listinvoices('inv_d')['invoices'])['expires_at']
|
||||
assert expiry >= start + 24 * 3600 and expiry <= end + 24 * 3600
|
||||
|
||||
start = int(time.time())
|
||||
inv = l2.rpc.invoice(msatoshi=123000, label='inv_w', description='description', expiry='1w')['bolt11']
|
||||
end = int(time.time())
|
||||
expiry = only_one(l2.rpc.listinvoices('inv_w')['invoices'])['expires_at']
|
||||
assert expiry >= start + 7 * 24 * 3600 and expiry <= end + 7 * 24 * 3600
|
||||
|
||||
|
||||
@unittest.skipIf(not DEVELOPER, "Too slow without --dev-bitcoind-poll")
|
||||
def test_waitinvoice(node_factory, executor):
|
||||
|
Loading…
Reference in New Issue
Block a user