From a85ead7058748d5c702812b6c36470b5814f0be3 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 28 Mar 2018 12:30:37 +1030 Subject: [PATCH] invoice: allow numeric labels again. Fixes: #1291 Signed-off-by: Rusty Russell --- doc/lightning-invoice.7 | 8 ++++---- doc/lightning-invoice.7.txt | 7 ++++--- external/libwally-core | 2 +- lightningd/invoice.c | 39 +++++++++++++++++++++++++++++-------- tests/test_lightningd.py | 23 ++++++++++++++++++++++ 5 files changed, 63 insertions(+), 16 deletions(-) diff --git a/doc/lightning-invoice.7 b/doc/lightning-invoice.7 index e038aea46..4358c4498 100644 --- a/doc/lightning-invoice.7 +++ b/doc/lightning-invoice.7 @@ -2,12 +2,12 @@ .\" Title: lightning-invoice .\" Author: [see the "AUTHOR" section] .\" Generator: DocBook XSL Stylesheets v1.79.1 -.\" Date: 01/16/2018 +.\" Date: 03/28/2018 .\" Manual: \ \& .\" Source: \ \& .\" Language: English .\" -.TH "LIGHTNING\-INVOICE" "7" "01/16/2018" "\ \&" "\ \&" +.TH "LIGHTNING\-INVOICE" "7" "03/28/2018" "\ \&" "\ \&" .\" ----------------------------------------------------------------- .\" * Define some portability stuff .\" ----------------------------------------------------------------- @@ -38,9 +38,9 @@ The \fBinvoice\fR RPC command creates the expectation of a payment of a given am .sp The \fImsatoshi\fR can be the string "any", which creates an invoice that can be paid with any amount\&. .sp -The \fIlabel\fR must be unique; it is never revealed to other nodes on the lightning network, but it can be used to query the status of this invoice\&. +The \fIlabel\fR must be a unique string or number (which is treated as a string, so "01" is different from "1"); it is never revealed to other nodes on the lightning network, but it can be used to query the status of this invoice\&. .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\&. +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 3600 (1 Hour) is used\&. .SH "RETURN VALUE" diff --git a/doc/lightning-invoice.7.txt b/doc/lightning-invoice.7.txt index 9f49cf358..3edae8d63 100644 --- a/doc/lightning-invoice.7.txt +++ b/doc/lightning-invoice.7.txt @@ -19,9 +19,10 @@ lightning daemon can use to pay this invoice. The 'msatoshi' can be the string "any", which creates an invoice that can be paid with any amount. -The 'label' must be a unique string; it is never revealed to other nodes on -the lightning network, but it can be used to query the status of this -invoice. +The 'label' must be a unique string or number (which is treated as a +string, so "01" is different from "1"); it is never revealed to other +nodes on the lightning network, but it can be used to query the status +of this invoice. The 'description' is a short description of purpose of payment, e.g. '1 cup of coffee'. This value is encoded into the BOLT11 invoice diff --git a/external/libwally-core b/external/libwally-core index 8e6d990b5..770c572db 160000 --- a/external/libwally-core +++ b/external/libwally-core @@ -1 +1 @@ -Subproject commit 8e6d990b5e62ff4d1c9bc68af29423cc98a232fa +Subproject commit 770c572db76e7730dd2c8481c78aaf02682b1c1a diff --git a/lightningd/invoice.c b/lightningd/invoice.c index ff97f9678..c774a27d1 100644 --- a/lightningd/invoice.c +++ b/lightningd/invoice.c @@ -104,6 +104,29 @@ static bool hsm_sign_b11(const u5 *u5bytes, return true; } +/* We allow a string, or a literal number, for labels */ +static struct json_escaped *json_tok_label(const tal_t *ctx, + const char *buffer, + const jsmntok_t *tok) +{ + struct json_escaped *label; + + label = json_tok_escaped_string(ctx, buffer, tok); + if (label) + return label; + + /* Allow literal numbers */ + if (tok->type != JSMN_PRIMITIVE) + return NULL; + + for (int i = tok->start; i < tok->end; i++) + if (!cisdigit(buffer[i])) + return NULL; + + return json_escaped_string_(ctx, buffer + tok->start, + tok->end - tok->start); +} + static void json_invoice(struct command *cmd, const char *buffer, const jsmntok_t *params) { @@ -148,9 +171,9 @@ static void json_invoice(struct command *cmd, } } /* label */ - label_val = json_tok_escaped_string(cmd, buffer, label); + label_val = json_tok_label(cmd, buffer, label); if (!label_val) { - command_fail(cmd, "label '%.*s' not a string", + command_fail(cmd, "label '%.*s' not a string or number", label->end - label->start, buffer + label->start); return; } @@ -306,9 +329,9 @@ static void json_listinvoice_internal(struct command *cmd, } if (labeltok) { - label = json_tok_escaped_string(cmd, buffer, labeltok); + label = json_tok_label(cmd, buffer, labeltok); if (!label) { - command_fail(cmd, "label '%.*s' is not a string", + command_fail(cmd, "label '%.*s' is not a string or number", labeltok->end - labeltok->start, buffer + labeltok->start); return; @@ -374,9 +397,9 @@ static void json_delinvoice(struct command *cmd, return; } - label = json_tok_escaped_string(cmd, buffer, labeltok); + label = json_tok_label(cmd, buffer, labeltok); if (!label) { - command_fail(cmd, "label '%.*s' not a string", + command_fail(cmd, "label '%.*s' is not a string or number", labeltok->end - labeltok->start, buffer + labeltok->start); return; @@ -567,9 +590,9 @@ static void json_waitinvoice(struct command *cmd, } /* Search for invoice */ - label = json_tok_escaped_string(cmd, buffer, labeltok); + label = json_tok_label(cmd, buffer, labeltok); if (!label) { - command_fail(cmd, "label '%.*s' is not a string", + command_fail(cmd, "label '%.*s' is not a string or number", labeltok->end - labeltok->start, buffer + labeltok->start); return; diff --git a/tests/test_lightningd.py b/tests/test_lightningd.py index 536d3a31a..3da507ecc 100644 --- a/tests/test_lightningd.py +++ b/tests/test_lightningd.py @@ -475,6 +475,29 @@ class LightningDTests(BaseLightningDTests): b11 = l1.rpc.decodepay(inv['bolt11']) assert b11['description'] == weird_desc + # Can delete by weird label. + l1.rpc.delinvoice(weird_label, "unpaid") + + # We can also use numbers as labels. + weird_label = 25 + weird_desc = '"' + l1.rpc.invoice(123000, weird_label, weird_desc) + # FIXME: invoice RPC should return label! + + # Can find by this label. + inv = l1.rpc.listinvoices(weird_label)['invoices'][0] + assert inv['label'] == str(weird_label) + + # Can find this in list. + inv = l1.rpc.listinvoices()['invoices'][0] + assert inv['label'] == str(weird_label) + + b11 = l1.rpc.decodepay(inv['bolt11']) + assert b11['description'] == weird_desc + + # Can delete by weird label. + l1.rpc.delinvoice(weird_label, "unpaid") + def test_invoice_expiry(self): l1, l2 = self.connect()