Skip to content

Commit ef3fbab

Browse files
committed
invoice: don't allow zero-value invoices.
You can't pay them anyway, and at least one person used 0 instead of "any". Closes: ElementsProject#3808 Signed-off-by: Rusty Russell <[email protected]> Changelog-Changed: JSON-RPC: `invoice` no longer accepts zero amounts (did you mean "any"?)
1 parent 2e51f23 commit ef3fbab

File tree

4 files changed

+32
-10
lines changed

4 files changed

+32
-10
lines changed

doc/lightning-invoice.7

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

doc/lightning-invoice.7.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ lightning daemon can use to pay this invoice. This token includes a
1717
invoice, if any exists.
1818

1919
The *msatoshi* parameter can be the string "any", which creates an
20-
invoice that can be paid with any amount. Otherwise it is in
20+
invoice that can be paid with any amount. Otherwise it is a positive value in
2121
millisatoshi precision; it can be a whole number, or a whole number
2222
ending in *msat* or *sat*, or a number with three decimal places ending
2323
in *sat*, or a number with 1 to 11 decimal places ending in *btc*.

lightningd/invoice.c

+10-8
Original file line numberDiff line numberDiff line change
@@ -826,22 +826,24 @@ static struct route_info **unpack_routes(const tal_t *ctx,
826826
}
827827
#endif /* DEVELOPER */
828828

829-
static struct command_result *param_msat_or_any(struct command *cmd,
830-
const char *name,
831-
const char *buffer,
832-
const jsmntok_t *tok,
833-
struct amount_msat **msat)
829+
static struct command_result *param_positive_msat_or_any(struct command *cmd,
830+
const char *name,
831+
const char *buffer,
832+
const jsmntok_t *tok,
833+
struct amount_msat **msat)
834834
{
835835
if (json_tok_streq(buffer, tok, "any")) {
836836
*msat = NULL;
837837
return NULL;
838838
}
839839
*msat = tal(cmd, struct amount_msat);
840-
if (parse_amount_msat(*msat, buffer + tok->start, tok->end - tok->start))
840+
if (parse_amount_msat(*msat, buffer + tok->start, tok->end - tok->start)
841+
&& !amount_msat_eq(**msat, AMOUNT_MSAT(0)))
841842
return NULL;
842843

843844
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
844-
"'%s' should be millisatoshis or 'any', not '%.*s'",
845+
"'%s' should be positive millisatoshis or 'any',"
846+
" not '%.*s'",
845847
name,
846848
tok->end - tok->start,
847849
buffer + tok->start);
@@ -963,7 +965,7 @@ static struct command_result *json_invoice(struct command *cmd,
963965
info->cmd = cmd;
964966

965967
if (!param(cmd, buffer, params,
966-
p_req("msatoshi", param_msat_or_any, &msatoshi_val),
968+
p_req("msatoshi", param_positive_msat_or_any, &msatoshi_val),
967969
p_req("label", param_label, &info->label),
968970
p_req("description", param_escaped_string, &desc_val),
969971
p_opt_def("expiry", param_time, &expiry, 3600*24*7),

tests/test_invoices.py

+20
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,26 @@ def test_invoice(node_factory, chainparams):
5959
l2.rpc.invoice(4294967295, 'inv3', '?')
6060

6161

62+
def test_invoice_zeroval(node_factory):
63+
"""A zero value invoice is unpayable, did you mean 'any'?"""
64+
l1 = node_factory.get_node()
65+
66+
with pytest.raises(RpcError, match=r"positive .* not '0'"):
67+
l1.rpc.invoice(0, 'inv', '?')
68+
69+
with pytest.raises(RpcError, match=r"positive .* not '0msat'"):
70+
l1.rpc.invoice('0msat', 'inv', '?')
71+
72+
with pytest.raises(RpcError, match=r"positive .* not '0sat'"):
73+
l1.rpc.invoice('0sat', 'inv', '?')
74+
75+
with pytest.raises(RpcError, match=r"positive .* not '0.00000000btc'"):
76+
l1.rpc.invoice('0.00000000btc', 'inv', '?')
77+
78+
with pytest.raises(RpcError, match=r"positive .* not '0.00000000000btc'"):
79+
l1.rpc.invoice('0.00000000000btc', 'inv', '?')
80+
81+
6282
def test_invoice_weirdstring(node_factory):
6383
l1 = node_factory.get_node()
6484

0 commit comments

Comments
 (0)