Skip to content

Commit 68241bf

Browse files
committed
lightningd: don't allow invoices with 640 byte descriptions.
They are invalid! This is because our BOLT11_FIELD_BYTE_LIMIT is not the limit, it's one greater than the limit. Reported-by: https://github.com/noblepayne Signed-off-by: Rusty Russell <[email protected]> Changelog-Fixed: JSON-RPC: `invoice` no longer accepts 640-byte descriptions (it would produce malformed invoices).
1 parent 1d3e473 commit 68241bf

File tree

4 files changed

+28
-4
lines changed

4 files changed

+28
-4
lines changed

common/bolt11.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
#include <secp256k1_recovery.h>
1010

1111
/* We only have 10 bits for the field length, meaning < 640 bytes */
12-
#define BOLT11_FIELD_BYTE_LIMIT ((1 << 10) * 5 / 8)
12+
#define BOLT11_FIELD_BYTE_LIMIT (((1 << 10) * 5 / 8) - 1)
1313

1414
/* BOLT #11:
1515
* * `c` (24): `data_length` variable.

lightningd/invoice.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1137,8 +1137,8 @@ static struct command_result *json_invoice(struct command *cmd,
11371137

11381138
if (strlen(desc_val) > BOLT11_FIELD_BYTE_LIMIT && !*hashonly) {
11391139
return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
1140-
"Descriptions greater than %d bytes "
1141-
"not yet supported "
1140+
"Description greater than %d bytes "
1141+
"invalid "
11421142
"(description length %zu)",
11431143
BOLT11_FIELD_BYTE_LIMIT,
11441144
strlen(desc_val));

plugins/keysend.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -564,7 +564,7 @@ static struct command_result *htlc_accepted_call(struct command *cmd,
564564
(const char *)desc_field->value);
565565
json_add_string(req->js, "description", desc);
566566
/* Don't exceed max possible desc length! */
567-
if (strlen(desc) >= BOLT11_FIELD_BYTE_LIMIT)
567+
if (strlen(desc) > BOLT11_FIELD_BYTE_LIMIT)
568568
json_add_bool(req->js, "deschashonly", true);
569569
} else {
570570
json_add_string(req->js, "description", "keysend");

tests/test_invoices.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -937,3 +937,27 @@ def test_invoice_botched_migration(node_factory, chainparams):
937937
assert ([(i['created_index'], i['label']) for i in l1.rpc.listinvoices()["invoices"]]
938938
== [(1, "made_after_bad_migration"), (2, "label1")])
939939
assert l1.rpc.invoice(100, "test", "test")["created_index"] == 3
940+
941+
942+
def test_invoice_maxdesc(node_factory, chainparams):
943+
l1, l2 = node_factory.line_graph(2)
944+
945+
# BOLT #11:
946+
#
947+
# Note that the maximum length of a Tagged Field's `data` is constricted
948+
# by the maximum value of `data_length`. This is 1023 x 5 bits, or 639
949+
# bytes.
950+
maxdesc = "x" * 639
951+
952+
# This should fail!
953+
with pytest.raises(RpcError, match=r'Description greater than 639 bytes invalid \(description length 641\)'):
954+
l1.rpc.invoice(123000, 'test_invoice_maxdesc', maxdesc + 'xx')
955+
956+
# This should also fail, but used to produce
957+
# lnbcrt1230n1p5dm097sp545trjl795r3mm86mk4ln5jpjvnh04x8aryl3qadjt99vspu646zspp52hf43ln8vg0564ljwccs8d84xc70ls8n7wdmp75ygp7ll8rprqzsdqq0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rc0pu8s7rcxqyjw5qcqp99qxpqysgqr6l8swzm6jc42ehy4v7s83jrggtwa9ua39cvy46c46tmqwn97mn43ycww7e9cf4w5ws8lxnef2k3m5nfa5c34nz54jaxhzc5e72q0ccq26n9fx
958+
with pytest.raises(RpcError, match=r'Description greater than 639 bytes invalid \(description length 640\)'):
959+
l1.rpc.invoice(123000, 'test_invoice_maxdesc2', maxdesc + 'x')
960+
961+
# This should succeed.
962+
inv = l1.rpc.invoice(123000, 'test_invoice_maxdesc3', maxdesc)
963+
assert l1.rpc.decode(inv['bolt11'])['description'] == maxdesc

0 commit comments

Comments
 (0)