From 220449e1cd1427cf4d31f01659abd8961f8b87a0 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 12 Jun 2019 10:08:54 +0930 Subject: [PATCH] ccan: import ccan/json_out and ccan/json_escape. These are generalized from our internal implementations. The main difference is that 'struct json_escaped' is now 'struct json_escape', so we replace that immediately. The difference between lightningd's json-writing ringbuffer and the more generic ccan/json_out is that the latter has a better API and handles escaping transparently if something slips through (though it does offer direct accessors so you can mess things up yourself!). Signed-off-by: Rusty Russell --- Makefile | 10 +- ccan/ccan/json_escape/LICENSE | 1 + ccan/ccan/json_escape/_info | 39 ++ .../ccan/json_escape/json_escape.c | 73 ++-- ccan/ccan/json_escape/json_escape.h | 44 +++ ccan/ccan/json_escape/test/run-partial.c | 41 ++ ccan/ccan/json_escape/test/run-take.c | 35 ++ ccan/ccan/json_escape/test/run.c | 44 +++ ccan/ccan/json_out/LICENSE | 1 + ccan/ccan/json_out/_info | 82 ++++ ccan/ccan/json_out/json_out.c | 356 ++++++++++++++++++ ccan/ccan/json_out/json_out.h | 200 ++++++++++ ccan/ccan/json_out/test/run-debugging.c | 2 + ccan/ccan/json_out/test/run-move_cb.c | 48 +++ ccan/ccan/json_out/test/run.c | 141 +++++++ cli/Makefile | 1 - cli/lightning-cli.c | 2 +- cli/test/Makefile | 1 - common/Makefile | 1 - common/json_escaped.h | 35 -- common/json_tok.c | 15 +- common/json_tok.h | 4 +- common/test/run-json_escaped.c | 46 --- common/test/run-param.c | 7 +- lightningd/Makefile | 1 - lightningd/gossip_control.c | 4 +- lightningd/invoice.c | 16 +- lightningd/json.c | 6 +- lightningd/json.h | 4 +- lightningd/jsonrpc.c | 6 +- lightningd/lightningd.c | 2 +- lightningd/options.c | 4 +- lightningd/peer_htlcs.c | 1 - lightningd/test/Makefile | 1 - lightningd/test/run-invoice-select-inchan.c | 8 +- lightningd/test/run-jsonrpc.c | 2 +- plugins/Makefile | 1 - wallet/db.c | 16 +- wallet/db.h | 8 +- wallet/invoices.c | 10 +- wallet/invoices.h | 6 +- wallet/test/run-db.c | 4 - wallet/test/run-wallet.c | 8 +- wallet/wallet.c | 4 +- wallet/wallet.h | 6 +- 45 files changed, 1156 insertions(+), 191 deletions(-) create mode 120000 ccan/ccan/json_escape/LICENSE create mode 100644 ccan/ccan/json_escape/_info rename common/json_escaped.c => ccan/ccan/json_escape/json_escape.c (57%) create mode 100644 ccan/ccan/json_escape/json_escape.h create mode 100644 ccan/ccan/json_escape/test/run-partial.c create mode 100644 ccan/ccan/json_escape/test/run-take.c create mode 100644 ccan/ccan/json_escape/test/run.c create mode 120000 ccan/ccan/json_out/LICENSE create mode 100644 ccan/ccan/json_out/_info create mode 100644 ccan/ccan/json_out/json_out.c create mode 100644 ccan/ccan/json_out/json_out.h create mode 100644 ccan/ccan/json_out/test/run-debugging.c create mode 100644 ccan/ccan/json_out/test/run-move_cb.c create mode 100644 ccan/ccan/json_out/test/run.c delete mode 100644 common/json_escaped.h delete mode 100644 common/test/run-json_escaped.c diff --git a/Makefile b/Makefile index a2691e93e..22152da88 100644 --- a/Makefile +++ b/Makefile @@ -31,7 +31,7 @@ SANITIZER_FLAGS= endif ifeq ($(DEVELOPER),1) -DEV_CFLAGS=-DCCAN_TAKE_DEBUG=1 -DCCAN_TAL_DEBUG=1 +DEV_CFLAGS=-DCCAN_TAKE_DEBUG=1 -DCCAN_TAL_DEBUG=1 -DCCAN_JSON_OUT_DEBUG=1 else DEV_CFLAGS= endif @@ -81,6 +81,8 @@ CCAN_OBJS := \ ccan-io-fdpass.o \ ccan-isaac.o \ ccan-isaac64.o \ + ccan-json_escape.o \ + ccan-json_out.o \ ccan-list.o \ ccan-mem.o \ ccan-membuf.o \ @@ -144,6 +146,8 @@ CCAN_HEADERS := \ $(CCANDIR)/ccan/io/io_plan.h \ $(CCANDIR)/ccan/isaac/isaac.h \ $(CCANDIR)/ccan/isaac/isaac64.h \ + $(CCANDIR)/ccan/json_escape/json_escape.h \ + $(CCANDIR)/ccan/json_out/json_out.h \ $(CCANDIR)/ccan/likely/likely.h \ $(CCANDIR)/ccan/list/list.h \ $(CCANDIR)/ccan/mem/mem.h \ @@ -657,3 +661,7 @@ ccan-bitmap.o: $(CCANDIR)/ccan/bitmap/bitmap.c $(CC) $(CFLAGS) -c -o $@ $< ccan-membuf.o: $(CCANDIR)/ccan/membuf/membuf.c $(CC) $(CFLAGS) -c -o $@ $< +ccan-json_escape.o: $(CCANDIR)/ccan/json_escape/json_escape.c + $(CC) $(CFLAGS) -c -o $@ $< +ccan-json_out.o: $(CCANDIR)/ccan/json_out/json_out.c + $(CC) $(CFLAGS) -c -o $@ $< diff --git a/ccan/ccan/json_escape/LICENSE b/ccan/ccan/json_escape/LICENSE new file mode 120000 index 000000000..2354d1294 --- /dev/null +++ b/ccan/ccan/json_escape/LICENSE @@ -0,0 +1 @@ +../../licenses/BSD-MIT \ No newline at end of file diff --git a/ccan/ccan/json_escape/_info b/ccan/ccan/json_escape/_info new file mode 100644 index 000000000..934857f8b --- /dev/null +++ b/ccan/ccan/json_escape/_info @@ -0,0 +1,39 @@ +#include "config.h" +#include +#include + +/** + * json_escape - Escape sequences for JSON strings + * + * This code helps you format strings into forms useful for JSON. + * + * Author: Rusty Russell + * License: BSD-MIT + * Example: + * // Print arguments as a JSON array. + * #include + * + * int main(int argc, char *argv[]) + * { + * printf("["); + * for (int i = 1; i < argc; i++) { + * struct json_escape *e = json_escape(NULL, argv[i]); + * printf("%s\"%s\"", i == 1 ? "" : ",", e->s); + * } + * printf("]\n"); + * return 0; + * } + */ +int main(int argc, char *argv[]) +{ + /* Expect exactly one argument */ + if (argc != 2) + return 1; + + if (strcmp(argv[1], "depends") == 0) { + printf("ccan/tal\n"); + return 0; + } + + return 1; +} diff --git a/common/json_escaped.c b/ccan/ccan/json_escape/json_escape.c similarity index 57% rename from common/json_escaped.c rename to ccan/ccan/json_escape/json_escape.c index d60654b92..ed4bfab94 100644 --- a/common/json_escaped.c +++ b/ccan/ccan/json_escape/json_escape.c @@ -1,46 +1,58 @@ -#include +/* MIT (BSD) license - see LICENSE file for details */ +#include #include -struct json_escaped *json_escaped_string_(const tal_t *ctx, - const void *bytes, size_t len) +struct json_escape *json_escape_string_(const tal_t *ctx, + const void *bytes, size_t len) { - struct json_escaped *esc; + struct json_escape *esc; esc = (void *)tal_arr_label(ctx, char, len + 1, - TAL_LABEL(struct json_escaped, "")); + TAL_LABEL(struct json_escape, "")); memcpy(esc->s, bytes, len); esc->s[len] = '\0'; return esc; } -struct json_escaped *json_to_escaped_string(const tal_t *ctx, - const char *buffer, - const jsmntok_t *tok) +bool json_escape_eq(const struct json_escape *a, const struct json_escape *b) { - if (tok->type != JSMN_STRING) - return NULL; - /* jsmn always gives us ~ well-formed strings. */ - return json_escaped_string_(ctx, buffer + tok->start, - tok->end - tok->start); + return streq(a->s, b->s); } -bool json_escaped_eq(const struct json_escaped *a, - const struct json_escaped *b) +bool json_escape_needed(const char *str, size_t len) { - return streq(a->s, b->s); + for (size_t i = 0; i < len; i++) { + if ((unsigned)str[i] < ' ' + || str[i] == 127 + || str[i] == '"' + || str[i] == '\\') + return true; + } + return false; } -static struct json_escaped *escape(const tal_t *ctx, - const char *str TAKES, - bool partial) +static struct json_escape *escape(const tal_t *ctx, + const char *str TAKES, + size_t len, + bool partial) { - struct json_escaped *esc; + struct json_escape *esc; size_t i, n; + /* Fast path: can steal, and nothing to escape. */ + if (is_taken(str) + && tal_count(str) > len + && !json_escape_needed(str, len)) { + taken(str); + esc = (struct json_escape *)tal_steal(ctx, str); + esc->s[len] = '\0'; + return esc; + } + /* Worst case: all \uXXXX */ - esc = (struct json_escaped *)tal_arr(ctx, char, strlen(str) * 6 + 1); + esc = (struct json_escape *)tal_arr(ctx, char, len * 6 + 1); - for (i = n = 0; str[i]; i++, n++) { + for (i = n = 0; i < len; i++, n++) { char escape = 0; switch (str[i]) { case '\n': @@ -107,19 +119,24 @@ static struct json_escaped *escape(const tal_t *ctx, return esc; } -struct json_escaped *json_partial_escape(const tal_t *ctx, const char *str TAKES) +struct json_escape *json_partial_escape(const tal_t *ctx, const char *str TAKES) +{ + return escape(ctx, str, strlen(str), true); +} + +struct json_escape *json_escape(const tal_t *ctx, const char *str TAKES) { - return escape(ctx, str, true); + return escape(ctx, str, strlen(str), false); } -struct json_escaped *json_escape(const tal_t *ctx, const char *str TAKES) +struct json_escape *json_escape_len(const tal_t *ctx, const char *str TAKES, + size_t len) { - return escape(ctx, str, false); + return escape(ctx, str, len, false); } /* By policy, we don't handle \u. Use UTF-8. */ -const char *json_escaped_unescape(const tal_t *ctx, - const struct json_escaped *esc) +const char *json_escape_unescape(const tal_t *ctx, const struct json_escape *esc) { char *unesc = tal_arr(ctx, char, strlen(esc->s) + 1); size_t i, n; diff --git a/ccan/ccan/json_escape/json_escape.h b/ccan/ccan/json_escape/json_escape.h new file mode 100644 index 000000000..5b33432f8 --- /dev/null +++ b/ccan/ccan/json_escape/json_escape.h @@ -0,0 +1,44 @@ +/* MIT (BSD) license - see LICENSE file for details */ +#ifndef CCAN_JSON_ESCAPE_H +#define CCAN_JSON_ESCAPE_H +#include "config.h" +#include + +/* Type differentiation for a correctly-escaped JSON string */ +struct json_escape { + /* NUL terminated string. */ + char s[1]; +}; + +/** + * json_escape - escape a valid UTF-8 string. + * @ctx: tal context to allocate from. + * @str: the string to escape. + * + * Allocates and returns a valid JSON string (without surrounding quotes). + */ +struct json_escape *json_escape(const tal_t *ctx, const char *str TAKES); + +/* Version with @len */ +struct json_escape *json_escape_len(const tal_t *ctx, + const char *str TAKES, size_t len); + +/* @str is a valid UTF-8 string which may already contain escapes. */ +struct json_escape *json_partial_escape(const tal_t *ctx, + const char *str TAKES); + +/* Do we need to escape this str? */ +bool json_escape_needed(const char *str, size_t len); + +/* Are two escape json strings identical? */ +bool json_escape_eq(const struct json_escape *a, + const struct json_escape *b); + +/* Internal routine for creating json_escape from bytes. */ +struct json_escape *json_escape_string_(const tal_t *ctx, + const void *bytes, size_t len); + +/* Be very careful here! Can fail! Doesn't handle \u: use UTF-8 please. */ +const char *json_escape_unescape(const tal_t *ctx, + const struct json_escape *esc); +#endif /* CCAN_JSON_ESCAPE_H */ diff --git a/ccan/ccan/json_escape/test/run-partial.c b/ccan/ccan/json_escape/test/run-partial.c new file mode 100644 index 000000000..25764df03 --- /dev/null +++ b/ccan/ccan/json_escape/test/run-partial.c @@ -0,0 +1,41 @@ +#include +/* Include the C files directly. */ +#include +#include + +int main(void) +{ + const tal_t *ctx = tal(NULL, char); + + /* This is how many tests you plan to run */ + plan_tests(21); + + ok1(!strcmp(json_partial_escape(ctx, "\\")->s, "\\\\")); + ok1(!strcmp(json_partial_escape(ctx, "\\\\")->s, "\\\\")); + ok1(!strcmp(json_partial_escape(ctx, "\\\\\\")->s, "\\\\\\\\")); + ok1(!strcmp(json_partial_escape(ctx, "\\\\\\\\")->s, "\\\\\\\\")); + ok1(!strcmp(json_partial_escape(ctx, "\\n")->s, "\\n")); + ok1(!strcmp(json_partial_escape(ctx, "\n")->s, "\\n")); + ok1(!strcmp(json_partial_escape(ctx, "\\\"")->s, "\\\"")); + ok1(!strcmp(json_partial_escape(ctx, "\"")->s, "\\\"")); + ok1(!strcmp(json_partial_escape(ctx, "\\t")->s, "\\t")); + ok1(!strcmp(json_partial_escape(ctx, "\t")->s, "\\t")); + ok1(!strcmp(json_partial_escape(ctx, "\\b")->s, "\\b")); + ok1(!strcmp(json_partial_escape(ctx, "\b")->s, "\\b")); + ok1(!strcmp(json_partial_escape(ctx, "\\r")->s, "\\r")); + ok1(!strcmp(json_partial_escape(ctx, "\r")->s, "\\r")); + ok1(!strcmp(json_partial_escape(ctx, "\\f")->s, "\\f")); + ok1(!strcmp(json_partial_escape(ctx, "\f")->s, "\\f")); + /* You're allowed to escape / according to json.org. */ + ok1(!strcmp(json_partial_escape(ctx, "\\/")->s, "\\/")); + ok1(!strcmp(json_partial_escape(ctx, "/")->s, "/")); + + ok1(!strcmp(json_partial_escape(ctx, "\\u0FFF")->s, "\\u0FFF")); + ok1(!strcmp(json_partial_escape(ctx, "\\u0FFFx")->s, "\\u0FFFx")); + + /* Unknown escapes should be escaped. */ + ok1(!strcmp(json_partial_escape(ctx, "\\x")->s, "\\\\x")); + tal_free(ctx); + + return 0; +} diff --git a/ccan/ccan/json_escape/test/run-take.c b/ccan/ccan/json_escape/test/run-take.c new file mode 100644 index 000000000..f3b62a794 --- /dev/null +++ b/ccan/ccan/json_escape/test/run-take.c @@ -0,0 +1,35 @@ +#include +/* Include the C files directly. */ +#include +#include + +int main(void) +{ + const tal_t *ctx = tal(NULL, char); + struct json_escape *e; + char *p; + + /* This is how many tests you plan to run */ + plan_tests(5); + + /* This should simply be tal_steal */ + p = tal_dup_arr(NULL, char, "Hello", 6, 0); + e = json_escape(ctx, take(p)); + ok1(!strcmp(e->s, "Hello")); + ok1((void *)e == (void *)p); + ok1(tal_parent(e) == ctx); + + /* This can't be tal_steal, but still should be freed. */ + p = tal_dup_arr(NULL, char, + "\\\b\f\n\r\t\"" + "\\\\\\b\\f\\n\\r\\t\\\"", 22, 0); + e = json_escape(ctx, take(p)); + ok1(tal_parent(e) == ctx); + ok1(!strcmp(e->s, + "\\\\\\b\\f\\n\\r\\t\\\"" + "\\\\\\\\\\\\b\\\\f\\\\n\\\\r\\\\t\\\\\\\"")); + tal_free(ctx); + + /* This exits depending on whether all tests passed */ + return exit_status(); +} diff --git a/ccan/ccan/json_escape/test/run.c b/ccan/ccan/json_escape/test/run.c new file mode 100644 index 000000000..9d8b049c3 --- /dev/null +++ b/ccan/ccan/json_escape/test/run.c @@ -0,0 +1,44 @@ +#include +/* Include the C files directly. */ +#include +#include + +int main(void) +{ + const tal_t *ctx = tal(NULL, char); + struct json_escape *e; + + /* This is how many tests you plan to run */ + plan_tests(6); + + e = json_escape(ctx, "Hello"); + ok1(!strcmp(e->s, "Hello")); + ok1(!strcmp(json_escape_unescape(ctx, e), + "Hello")); + + e = json_escape(ctx, + "\\\b\f\n\r\t\"" + "\\\\\\b\\f\\n\\r\\t\\\""); + ok1(!strcmp(e->s, + "\\\\\\b\\f\\n\\r\\t\\\"" + "\\\\\\\\\\\\b\\\\f\\\\n\\\\r\\\\t\\\\\\\"")); + ok1(!strcmp(json_escape_unescape(ctx, e), + "\\\b\f\n\r\t\"" + "\\\\\\b\\f\\n\\r\\t\\\"")); + + /* This one doesn't escape the already-escaped chars */ + e = json_partial_escape(ctx, + "\\\b\f\n\r\t\"" + "\\\\\\b\\f\\n\\r\\t\\\""); + ok1(!strcmp(e->s, + "\\\\\\b\\f\\n\\r\\t\\\"" + "\\\\\\b\\f\\n\\r\\t\\\"")); + ok1(!strcmp(json_escape_unescape(ctx, e), + "\\\b\f\n\r\t\"" + "\\\b\f\n\r\t\"")); + + tal_free(ctx); + + /* This exits depending on whether all tests passed */ + return exit_status(); +} diff --git a/ccan/ccan/json_out/LICENSE b/ccan/ccan/json_out/LICENSE new file mode 120000 index 000000000..2354d1294 --- /dev/null +++ b/ccan/ccan/json_out/LICENSE @@ -0,0 +1 @@ +../../licenses/BSD-MIT \ No newline at end of file diff --git a/ccan/ccan/json_out/_info b/ccan/ccan/json_out/_info new file mode 100644 index 000000000..fbb4fa567 --- /dev/null +++ b/ccan/ccan/json_out/_info @@ -0,0 +1,82 @@ +#include "config.h" +#include +#include + +/** + * json_out - Code for creating simple JSON output. + * + * This code helps you create well-formed JSON strings. + * + * Author: Rusty Russell + * License: BSD-MIT + * + * Example: + * // Given "a 1 true" outputs {"argv1":"a","argv2":1,"argv3":true} + * // Print arguments as a JSON array. + * #include + * #include + * #include + * #include + * + * // Simplistic test to see if str needs quotes. + * static bool can_be_json_literal(const char *str) + * { + * char *endp; + * if (strtol(str, &endp, 10) != LONG_MIN + * && endp != str + * && *endp == '\0') + * return true; + * return !strcmp(str, "true") + * || !strcmp(str, "false") + * || !strcmp(str, "null"); + * } + * + * int main(int argc, char *argv[]) + * { + * struct json_out *jout = json_out_new(NULL); + * size_t len; + * const char *p; + * + * json_out_start(jout, NULL, '{'); + * for (int i = 1; i < argc; i++) { + * char fieldname[80]; + * sprintf(fieldname, "argv%i", i); + * json_out_add(jout, fieldname, + * !can_be_json_literal(argv[i]), + * "%s", argv[i]); + * } + * json_out_end(jout, '}'); + * // Force appending of \n + * json_out_direct(jout, 1)[0] = '\n'; + * json_out_finished(jout); + * + * // Now write it out. + * while ((p = json_out_contents(jout, &len)) != NULL) { + * int i = write(STDOUT_FILENO, p, len); + * if (i <= 0) + * exit(1); + * json_out_consume(jout, i); + * } + * + * tal_free(jout); + * return 0; + * } + * + */ +int main(int argc, char *argv[]) +{ + /* Expect exactly one argument */ + if (argc != 2) + return 1; + + if (strcmp(argv[1], "depends") == 0) { + printf("ccan/compiler\n"); + printf("ccan/json_escape\n"); + printf("ccan/membuf\n"); + printf("ccan/tal\n"); + printf("ccan/typesafe_cb\n"); + return 0; + } + + return 1; +} diff --git a/ccan/ccan/json_out/json_out.c b/ccan/ccan/json_out/json_out.c new file mode 100644 index 000000000..87deaca85 --- /dev/null +++ b/ccan/ccan/json_out/json_out.c @@ -0,0 +1,356 @@ +/* MIT (BSD) license - see LICENSE file for details */ +#include +#include +#include +#include +#include + +struct json_out { + /* Callback if we reallocate. */ + void (*move_cb)(struct json_out *jout, ptrdiff_t delta, void *arg); + void *cb_arg; + +#ifdef CCAN_JSON_OUT_DEBUG + /* tal_arr of types ( or [ we're enclosed in. NULL if oom. */ + char *wrapping; +#endif + /* True if we haven't yet put an element in current wrapping */ + bool empty; + + /* Output. */ + MEMBUF(char) outbuf; +}; + +/* Realloc helper for tal membufs */ +static void *membuf_tal_realloc(struct membuf *mb, + void *rawelems, size_t newsize) +{ + char *p = rawelems; + + if (!tal_resize(&p, newsize)) + return NULL; + return p; +} + +struct json_out *json_out_new(const tal_t *ctx) +{ + struct json_out *jout = tal(ctx, struct json_out); + char *pool; + + if (!jout) + return NULL; + pool = tal_arr(jout, char, 64); + if (!pool) + return tal_free(jout); + + membuf_init(&jout->outbuf, pool, tal_count(pool), membuf_tal_realloc); +#ifdef CCAN_JSON_OUT_DEBUG + jout->wrapping = tal_arr(jout, char, 0); +#endif + jout->empty = true; + jout->move_cb = NULL; + return jout; +} + +void json_out_call_on_move_(struct json_out *jout, + void (*cb)(struct json_out *jout, ptrdiff_t delta, + void *arg), + void *arg) +{ + if (cb) + assert(!jout->move_cb); + jout->move_cb = cb; + jout->cb_arg = arg; +} + +struct json_out *json_out_dup(const tal_t *ctx, const struct json_out *src) +{ + size_t num_elems = membuf_num_elems(&src->outbuf); + char *elems = membuf_elems(&src->outbuf); + struct json_out *jout = tal_dup(ctx, struct json_out, src); + char *pool; + + if (!jout) + return NULL; + pool = tal_dup_arr(jout, char, elems, num_elems, 0); + if (!pool) + return tal_free(jout); + membuf_init(&jout->outbuf, pool, num_elems, membuf_tal_realloc); + membuf_added(&jout->outbuf, num_elems); +#ifdef CCAN_JSON_OUT_DEBUG + jout->wrapping = tal_dup_arr(jout, char, + jout->wrapping, tal_count(jout->wrapping), + 0); +#endif + return jout; +} + +static void indent(struct json_out *jout, char type) +{ +#ifdef CCAN_JSON_OUT_DEBUG + /* Can't check if we ran out of memory. */ + if (jout->wrapping) { + size_t n = tal_count(jout->wrapping); + if (!tal_resize(&jout->wrapping, n+1)) + jout->wrapping = tal_free(jout->wrapping); + else + jout->wrapping[n] = type; + } +#endif + jout->empty = true; +} + +static void unindent(struct json_out *jout, char type) +{ +#ifdef CCAN_JSON_OUT_DEBUG + /* Can't check if we ran out of memory. */ + if (jout->wrapping) { + size_t indent = tal_count(jout->wrapping); + assert(indent > 0); + /* Both [ and ] and { and } are two apart in ASCII */ + assert(jout->wrapping[indent-1] == type - 2); + tal_resize(&jout->wrapping, indent-1); + } +#endif + jout->empty = false; +} + +/* Make sure jout->outbuf has room for len: return pointer */ +static char *mkroom(struct json_out *jout, size_t len) +{ + ptrdiff_t delta = membuf_prepare_space(&jout->outbuf, len); + + if (delta && jout->move_cb) + jout->move_cb(jout, delta, jout->cb_arg); + + return membuf_space(&jout->outbuf); +} + +static void check_fieldname(const struct json_out *jout, + const char *fieldname) +{ +#ifdef CCAN_JSON_OUT_DEBUG + /* We don't escape this for you */ + assert(!fieldname || !json_escape_needed(fieldname, strlen(fieldname))); + + /* Can't check anything else if we ran out of memory. */ + if (jout->wrapping) { + size_t n = tal_count(jout->wrapping); + if (n == 0) + /* Can't have a fieldname if not in anything! */ + assert(!fieldname); + else if (jout->wrapping[n-1] == '[') + /* No fieldnames in arrays. */ + assert(!fieldname); + else { + /* Must have fieldnames in objects. */ + assert(fieldname); + } + } +#endif +} + +char *json_out_member_direct(struct json_out *jout, + const char *fieldname, size_t extra) +{ + char *dest; + + /* Prepend comma if required. */ + if (!jout->empty) + extra++; + + check_fieldname(jout, fieldname); + if (fieldname) + extra += 1 + strlen(fieldname) + 2; + + dest = mkroom(jout, extra); + if (!dest) + goto out; + + if (!jout->empty) + *(dest++) = ','; + if (fieldname) { + *(dest++) = '"'; + memcpy(dest, fieldname, strlen(fieldname)); + dest += strlen(fieldname); + *(dest++) = '"'; + *(dest++) = ':'; + } + membuf_added(&jout->outbuf, extra); + +out: + jout->empty = false; + return dest; +} + +bool json_out_start(struct json_out *jout, const char *fieldname, char type) +{ + char *p; + + assert(type == '[' || type == '{'); + p = json_out_member_direct(jout, fieldname, 1); + if (p) + p[0] = type; + indent(jout, type); + + return p != NULL; +} + +bool json_out_end(struct json_out *jout, char type) +{ + char *p; + + assert(type == '}' || type == ']'); + p = json_out_direct(jout, 1); + if (p) + p[0] = type; + unindent(jout, type); + + return p != NULL; +} + +bool json_out_addv(struct json_out *jout, + const char *fieldname, + bool quote, + const char *fmt, + va_list ap) +{ + size_t fmtlen, avail; + va_list ap2; + char *dst; + + if (!json_out_member_direct(jout, fieldname, 0)) + return false; + + /* Make a copy in case we need it below. */ + va_copy(ap2, ap); + + /* We can use any additional space, but need room for ". */ + avail = membuf_num_space(&jout->outbuf); + if (quote) { + if (avail < 2) + avail = 0; + else + avail -= 2; + } + + /* Try printing in place first. */ + dst = membuf_space(&jout->outbuf); + fmtlen = vsnprintf(dst + quote, avail, fmt, ap); + + /* Horrible subtlety: vsnprintf *will* NUL terminate, even if it means + * chopping off the last character. So if fmtlen == + * membuf_num_space(&jout->outbuf), the result was truncated! */ + if (fmtlen + (int)quote*2 >= membuf_num_space(&jout->outbuf)) { + /* Make room for NUL terminator, even though we don't want it */ + dst = mkroom(jout, fmtlen + 1 + (int)quote*2); + if (!dst) + goto out; + vsprintf(dst + quote, fmt, ap2); + } + +#ifdef CCAN_JSON_OUT_DEBUG + /* You're not inserting junk here, are you? */ + assert(quote || !json_escape_needed(dst, fmtlen)); +#endif + + /* Of course, if we need to escape it, we have to redo it all. */ + if (quote) { + if (json_escape_needed(dst + quote, fmtlen)) { + struct json_escape *e; + e = json_escape_len(NULL, dst + quote, fmtlen); + fmtlen = strlen(e->s); + dst = mkroom(jout, fmtlen + (int)quote*2); + if (!dst) + goto out; + memcpy(dst + quote, e, fmtlen); + tal_free(e); + } + dst[0] = '"'; + dst[fmtlen+1] = '"'; + } + membuf_added(&jout->outbuf, fmtlen + (int)quote*2); + +out: + va_end(ap2); + return dst != NULL; +} + +bool json_out_add(struct json_out *jout, + const char *fieldname, + bool quote, + const char *fmt, ...) +{ + va_list ap; + bool ret; + + va_start(ap, fmt); + ret = json_out_addv(jout, fieldname, quote, fmt, ap); + va_end(ap); + return ret; +} + +bool json_out_addstr(struct json_out *jout, + const char *fieldname, + const char *str) +{ + size_t len = strlen(str); + char *p; + struct json_escape *e; + + if (json_escape_needed(str, len)) { + e = json_escape(NULL, str); + str = e->s; + len = strlen(str); + } else + e = NULL; + + p = json_out_member_direct(jout, fieldname, len + 2); + if (p) { + p[0] = p[1+len] = '"'; + memcpy(p+1, str, len); + } + tal_free(e); + + return p != NULL; +} + +bool json_out_add_splice(struct json_out *jout, + const char *fieldname, + const struct json_out *src) +{ + const char *p; + size_t len; + + p = json_out_contents(src, &len); + if (!p) + return false; + memcpy(json_out_member_direct(jout, fieldname, len), p, len); + return true; +} + +char *json_out_direct(struct json_out *jout, size_t len) +{ + char *p = mkroom(jout, len); + if (p) + membuf_added(&jout->outbuf, len); + return p; +} + +void json_out_finished(const struct json_out *jout) +{ +#ifdef CCAN_JSON_OUT_DEBUG + assert(tal_count(jout->wrapping) == 0); +#endif +} + +const char *json_out_contents(const struct json_out *jout, size_t *len) +{ + *len = membuf_num_elems(&jout->outbuf); + return *len ? membuf_elems(&jout->outbuf) : NULL; +} + +void json_out_consume(struct json_out *jout, size_t len) +{ + membuf_consume(&jout->outbuf, len); +} diff --git a/ccan/ccan/json_out/json_out.h b/ccan/ccan/json_out/json_out.h new file mode 100644 index 000000000..2911ff247 --- /dev/null +++ b/ccan/ccan/json_out/json_out.h @@ -0,0 +1,200 @@ +/* MIT (BSD) license - see LICENSE file for details */ +#ifndef CCAN_JSON_OUT_H +#define CCAN_JSON_OUT_H +#include +#include +#include +#include + +struct json_out; + +/** + * json_out_new - allocate a json_out stream. + * @ctx: the tal_context to allocate from, or NULL + * + * Returns NULL if tal allocation fails. + */ +struct json_out *json_out_new(const tal_t *ctx); + +/** + * json_out_call_on_move - callback for when buffer is reallocated. + * @jout: the json_out object to attach to. + * @cb: the callback to call. + * @arg: the argument to @cb (must match type). + * + * A NULL @cb disables. You can't currently have more than one callback. + * The @delta argument to @cb is the difference between the old location + * and the new one, and is never zero. + */ +#define json_out_call_on_move(jout, cb, arg) \ + json_out_call_on_move_((jout), \ + typesafe_cb_preargs(void, void *, \ + (cb), (arg), \ + struct json_out *, \ + ptrdiff_t), \ + (arg)) + +void json_out_call_on_move_(struct json_out *jout, + void (*cb)(struct json_out *jout, ptrdiff_t delta, + void *arg), + void *arg); + +/** + * json_out_dup - duplicate a json_out stream. + * @ctx: the tal_context to allocate from, or NULL + * @src: the json_out to copy. + */ +struct json_out *json_out_dup(const tal_t *ctx, const struct json_out *src); + +/** + * json_out_start - start an array or object. + * @jout: the json_out object to write into. + * @fieldname: the fieldname, if inside an object, or NULL if inside an array. + * @type: '[' or '{' to start an array or object, respectively. + * + * Returns true unless tal_resize() fails. + * Literally writes '"@fieldname": @type' or '@type ' if fieldname is NULL. + * @fieldname must not need JSON escaping. + */ +bool json_out_start(struct json_out *jout, const char *fieldname, char type); + +/** + * json_out_end - end an array or object. + * @jout: the json_out object to write into. + * @type: '}' or ']' to end an array or object, respectively. + * + * Returns true unless tal_resize() fails. + * + * Literally writes ']' or '}', keeping track of whether we need to append + * a comma. + */ +bool json_out_end(struct json_out *jout, char type); + +/** + * json_out_add - add a formatted member. + * @jout: the json_out object to write into. + * @fieldname: optional fieldname to prepend (must not need escaping). + * @quote: if true, surround fmt by " and ". + * @fmt...: the printf-style format + * + * Returns true unless tal_resize() fails. + * + * If you're in an array, @fieldname must be NULL. If you're in an + * object, @fieldname must be non-NULL. This is checked if + * CCAN_JSON_OUT_DEBUG is defined. + * @fieldname must not need JSON escaping. + * + * If the resulting string requires escaping, and @quote is true, we + * call json_escape(). + */ +PRINTF_FMT(4,5) +bool json_out_add(struct json_out *jout, + const char *fieldname, + bool quote, + const char *fmt, ...); + +/** + * json_out_addv - add a formatted member (vararg variant) + * @jout: the json_out object to write into. + * @fieldname: optional fieldname to prepend. + * @quote: if true, surround fmt by " and ". + * @fmt: the printf-style format + * @ap: the argument list. + * + * See json_out_add() above. + */ +bool json_out_addv(struct json_out *jout, + const char *fieldname, + bool quote, + const char *fmt, + va_list ap); + +/** + * json_out_addstr - convenience helper to add a string field. + * @jout: the json_out object to write into. + * @fieldname: optional fieldname to prepend. + * @str: the string to add (must not be NULL). + * + * Equivalent to json_out_add(@jout, @fieldname, true, "%s", @str); + */ +bool json_out_addstr(struct json_out *jout, + const char *fieldname, + const char *str); + +/** + * json_out_member_direct - add a field, with direct access. + * @jout: the json_out object to write into. + * @fieldname: optional fieldname to prepend. + * @extra: how many bytes to allocate. + * + * @fieldname must not need JSON escaping. Returns a direct pointer into + * the @extra bytes, or NULL if tal_resize() fails. + * + * This allows you to write your own efficient type-specific helpers. + */ +char *json_out_member_direct(struct json_out *jout, + const char *fieldname, size_t extra); + +/** + * json_out_direct - make room in output and access directly. + * @jout: the json_out object to write into. + * @len: the length to allocate. + * + * This lets you access the json_out stream directly, to save a copy, + * if you know exactly how much you will write. + * + * Returns a pointer to @len bytes at the end of @jout, or NULL if + * tal_resize() fails. + * + * This is dangerous, since it doesn't automatically prepend a "," + * like the internal logic does, but can be used (carefully) to add + * entire objects, or whitespace. + */ +char *json_out_direct(struct json_out *jout, size_t extra); + +/** + * json_out_add_splice - copy a field from another json_out. + * @jout: the json_out object to write into. + * @fieldname: optional fieldname to prepend. + * @src: the json_out object to copy from. + * + * This asserts that @src is well-formed (as per json_out_finished()), + * then places it into @jout with optional @fieldname prepended. This + * can be used to assemble sub-objects for your JSON and then copy + * them in. + * + * Note that it will call json_out_contents(@src), so it expects that + * object to be unconsumed. + * + * Returns false if tal_resize() fails. + */ +bool json_out_add_splice(struct json_out *jout, + const char *fieldname, + const struct json_out *src); + +/** + * json_out_finished - assert that the json buffer is finished. + * @jout: the json_out object written to. + * + * This simply causes internal assertions that all arrays and objects are + * finished. It needs CCAN_JSON_OUT_DEBUG defined to have any effect. + */ +void json_out_finished(const struct json_out *jout); + +/** + * json_out_contents - read contents from json_out stream. + * @jout: the json_out object we want to read from. + * @len: set to the length of the buffer returned. + * + * This returns a pointer into the JSON written so far. Returns NULL + * and sets @len to 0 if there's nothing left in the buffer. + */ +const char *json_out_contents(const struct json_out *jout, size_t *len); + +/** + * json_out_consume - discard contents from json_out stream. + * @jout: the json_out object we read from. + * @len: the length to consume (must be <= @len from json_out_contents) + */ +void json_out_consume(struct json_out *jout, size_t len); +#endif /* CCAN_JSON_OUT_H */ diff --git a/ccan/ccan/json_out/test/run-debugging.c b/ccan/ccan/json_out/test/run-debugging.c new file mode 100644 index 000000000..e239d8e1f --- /dev/null +++ b/ccan/ccan/json_out/test/run-debugging.c @@ -0,0 +1,2 @@ +#define CCAN_JSON_OUT_DEBUG 1 +#include "run.c" diff --git a/ccan/ccan/json_out/test/run-move_cb.c b/ccan/ccan/json_out/test/run-move_cb.c new file mode 100644 index 000000000..445b42623 --- /dev/null +++ b/ccan/ccan/json_out/test/run-move_cb.c @@ -0,0 +1,48 @@ +#include +/* Include the C files directly. */ +#include +#include + +static const char *ptr; +static bool called = false; + +static void move_cb(struct json_out *jout, ptrdiff_t delta, + struct json_out *arg) +{ + ptr += delta; + called = true; + ok1(arg == jout); +} + +int main(void) +{ + const tal_t *ctx = tal(NULL, char); + struct json_out *jout; + char *p; + size_t len; + + /* This is how many tests you plan to run */ + plan_tests(3); + + /* Test nested arrays. */ + jout = json_out_new(ctx); + json_out_call_on_move(jout, move_cb, jout); + + json_out_start(jout, NULL, '{'); + ptr = json_out_contents(jout, &len); + + p = json_out_member_direct(jout, "fieldname", 102); + p[0] = '"'; + p[101] = '"'; + memset(p+1, 'p', 100); + + json_out_finished(jout); + ok1(called); + /* Contents should have moved correctly. */ + ok1(json_out_contents(jout, &len) == ptr); + + tal_free(ctx); + + /* This exits depending on whether all tests passed */ + return exit_status(); +} diff --git a/ccan/ccan/json_out/test/run.c b/ccan/ccan/json_out/test/run.c new file mode 100644 index 000000000..f6f9d41ee --- /dev/null +++ b/ccan/ccan/json_out/test/run.c @@ -0,0 +1,141 @@ +#include +/* Include the C files directly. */ +#include +#include + +static void test_json_out_add(const tal_t *ctx, + char c, bool quote, const char *escaped) +{ + /* 64 is the size of the initial buf, so we test that. */ + for (size_t i = 1; i < 64; i++) { + struct json_out *jout; + char str[64 + 1]; + const char *r; + size_t len; + char fieldname[64 + 1]; + + jout = json_out_new(ctx); + json_out_start(jout, NULL, '{'); + memset(str, c, i); + str[i] = '\0'; + memset(fieldname, 'f', i); + fieldname[i] = '\0'; + json_out_add(jout, fieldname, quote, "%s", str); + json_out_end(jout, '}'); + json_out_finished(jout); + + r = json_out_contents(jout, &len); + ok1(len == strlen("{\"") + i + strlen("\":") + + quote * 2 + strlen(escaped) * i + strlen("}")); + + ok1(len > strlen("{\"")); + ok1(memcmp(r, "{\"", strlen("{\"")) == 0); + json_out_consume(jout, strlen("{\"")); + + r = json_out_contents(jout, &len); + ok1(len > strlen(fieldname)); + ok1(memcmp(r, fieldname, strlen(fieldname)) == 0); + json_out_consume(jout, strlen(fieldname)); + + r = json_out_contents(jout, &len); + ok1(len > strlen("\":")); + ok1(memcmp(r, "\":", strlen("\":")) == 0); + json_out_consume(jout, strlen("\":")); + + r = json_out_contents(jout, &len); + if (quote) { + ok1(len > 0); + ok1(r[0] == '"'); + json_out_consume(jout, 1); + } + for (size_t n = 0; n < i; n++) { + r = json_out_contents(jout, &len); + ok1(len > strlen(escaped)); + ok1(memcmp(r, escaped, strlen(escaped)) == 0); + json_out_consume(jout, strlen(escaped)); + } + r = json_out_contents(jout, &len); + if (quote) { + ok1(len > 0); + ok1(r[0] == '"'); + json_out_consume(jout, 1); + } + r = json_out_contents(jout, &len); + ok1(len == 1); + ok1(memcmp(r, "}", 1) == 0); + json_out_consume(jout, 1); + ok1(!json_out_contents(jout, &len)); + ok1(len == 0); + } +} + +static void json_eq(const struct json_out *jout, const char *expect) +{ + size_t len; + const char *p; + + json_out_finished(jout); + p = json_out_contents(jout, &len); + ok1(len == strlen(expect)); + ok1(memcmp(expect, p, len) == 0); +} + +int main(void) +{ + const tal_t *ctx = tal(NULL, char); + struct json_out *jout; + char *p; + + /* This is how many tests you plan to run */ + plan_tests(14689); + + /* Simple tests */ + test_json_out_add(ctx, '1', false, "1"); + test_json_out_add(ctx, 'x', true, "x"); + test_json_out_add(ctx, '\n', true, "\\n"); + + /* Test nested arrays. */ + jout = json_out_new(ctx); + for (size_t i = 0; i < 64; i++) + json_out_start(jout, NULL, '['); + for (size_t i = 0; i < 64; i++) + json_out_end(jout, ']'); + json_eq(jout, "[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]"); + + /* Test nested objects. */ + jout = json_out_new(ctx); + json_out_start(jout, NULL, '{'); + for (size_t i = 0; i < 63; i++) + json_out_start(jout, "x", '{'); + for (size_t i = 0; i < 64; i++) + json_out_end(jout, '}'); + json_eq(jout, "{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{\"x\":{}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}"); + + jout = json_out_new(ctx); + json_out_start(jout, NULL, '{'); + p = json_out_member_direct(jout, "x", 7); + memcpy(p, "\"hello\"", 7); + json_out_end(jout, '}'); + json_eq(jout, "{\"x\":\"hello\"}"); + + jout = json_out_new(ctx); + p = json_out_direct(jout, strlen("{\"x\":\"hello\"}\n")); + memcpy(p, "{\"x\":\"hello\"}\n", strlen("{\"x\":\"hello\"}\n")); + json_eq(jout, "{\"x\":\"hello\"}\n"); + + jout = json_out_new(ctx); + json_out_start(jout, NULL, '{'); + struct json_out *jout2 = json_out_new(ctx); + json_out_start(jout2, NULL, '{'); + json_out_addstr(jout2, "x", "hello"); + json_out_end(jout2, '}'); + json_out_finished(jout2); + json_out_add_splice(jout, "inner", jout2); + json_out_end(jout, '}'); + json_eq(jout, "{\"inner\":{\"x\":\"hello\"}}"); + + tal_free(ctx); + + /* This exits depending on whether all tests passed */ + return exit_status(); +} diff --git a/cli/Makefile b/cli/Makefile index 1c0cf09cd..1bd4d43ab 100644 --- a/cli/Makefile +++ b/cli/Makefile @@ -4,7 +4,6 @@ LIGHTNING_CLI_OBJS := $(LIGHTNING_CLI_SRC:.c=.o) LIGHTNING_CLI_COMMON_OBJS := \ common/configdir.o \ common/json.o \ - common/json_escaped.o \ common/memleak.o \ common/utils.o \ common/version.o diff --git a/cli/lightning-cli.c b/cli/lightning-cli.c index ecb9c03f9..99fd8ed0a 100644 --- a/cli/lightning-cli.c +++ b/cli/lightning-cli.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -12,7 +13,6 @@ #include #include #include -#include #include #include #include diff --git a/cli/test/Makefile b/cli/test/Makefile index 6758a6992..2f5889f63 100644 --- a/cli/test/Makefile +++ b/cli/test/Makefile @@ -12,7 +12,6 @@ CLI_TEST_COMMON_OBJS := \ common/daemon_conn.o \ common/htlc_state.o \ common/json.o \ - common/json_escaped.o \ common/pseudorand.o \ common/memleak.o \ common/msg_queue.o \ diff --git a/common/Makefile b/common/Makefile index f53a7710c..4ad5602b7 100644 --- a/common/Makefile +++ b/common/Makefile @@ -28,7 +28,6 @@ COMMON_SRC_NOGEN := \ common/initial_commit_tx.c \ common/io_lock.c \ common/json.c \ - common/json_escaped.c \ common/json_helpers.c \ common/json_tok.c \ common/key_derive.c \ diff --git a/common/json_escaped.h b/common/json_escaped.h deleted file mode 100644 index 0f742a86f..000000000 --- a/common/json_escaped.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef LIGHTNING_COMMON_JSON_ESCAPED_H -#define LIGHTNING_COMMON_JSON_ESCAPED_H -#include "config.h" -#include - -/* Type differentiation for a correctly-escaped JSON string */ -struct json_escaped { - /* NUL terminated string. */ - char s[1]; -}; - -/* @str be a valid UTF-8 string */ -struct json_escaped *json_escape(const tal_t *ctx, const char *str TAKES); - -/* @str is a valid UTF-8 string which may already contain escapes. */ -struct json_escaped *json_partial_escape(const tal_t *ctx, - const char *str TAKES); - -/* Extract a JSON-escaped string. */ -struct json_escaped *json_to_escaped_string(const tal_t *ctx, - const char *buffer, - const jsmntok_t *tok); - -/* Are two escaped json strings identical? */ -bool json_escaped_eq(const struct json_escaped *a, - const struct json_escaped *b); - -/* Internal routine for creating json_escaped from bytes. */ -struct json_escaped *json_escaped_string_(const tal_t *ctx, - const void *bytes, size_t len); - -/* Be very careful here! Can fail! Doesn't handle \u: use UTF-8 please. */ -const char *json_escaped_unescape(const tal_t *ctx, - const struct json_escaped *esc); -#endif /* LIGHTNING_COMMON_JSON_ESCAPED_H */ diff --git a/common/json_tok.c b/common/json_tok.c index faff0abc3..eab00fa36 100644 --- a/common/json_tok.c +++ b/common/json_tok.c @@ -1,9 +1,9 @@ #include +#include #include #include #include #include -#include #include #include #include @@ -53,9 +53,12 @@ struct command_result *param_escaped_string(struct command *cmd, const jsmntok_t *tok, const char **str) { - struct json_escaped *esc = json_to_escaped_string(cmd, buffer, tok); - if (esc) { - *str = json_escaped_unescape(cmd, esc); + if (tok->type == JSMN_STRING) { + struct json_escape *esc; + /* jsmn always gives us ~ well-formed strings. */ + esc = json_escape_string_(cmd, buffer + tok->start, + tok->end - tok->start); + *str = json_escape_unescape(cmd, esc); if (*str) return NULL; } @@ -77,10 +80,10 @@ struct command_result *param_string(struct command *cmd, const char *name, struct command_result *param_label(struct command *cmd, const char *name, const char * buffer, const jsmntok_t *tok, - struct json_escaped **label) + struct json_escape **label) { /* We accept both strings and number literals here. */ - *label = json_escaped_string_(cmd, buffer + tok->start, tok->end - tok->start); + *label = json_escape_string_(cmd, buffer + tok->start, tok->end - tok->start); if (*label && (tok->type == JSMN_STRING || json_tok_is_num(buffer, tok))) return NULL; diff --git a/common/json_tok.h b/common/json_tok.h index b89a81595..8473eda88 100644 --- a/common/json_tok.h +++ b/common/json_tok.h @@ -9,7 +9,7 @@ struct amount_msat; struct amount_sat; struct command; struct command_result; -struct json_escaped; +struct json_escape; struct sha256; /* Extract json array token */ @@ -42,7 +42,7 @@ struct command_result *param_string(struct command *cmd, const char *name, /* Extract a label. It is either an escaped string or a number. */ struct command_result *param_label(struct command *cmd, const char *name, const char * buffer, const jsmntok_t *tok, - struct json_escaped **label); + struct json_escape **label); /* Extract number from this (may be a string, or a number literal) */ struct command_result *param_number(struct command *cmd, const char *name, diff --git a/common/test/run-json_escaped.c b/common/test/run-json_escaped.c deleted file mode 100644 index 03feab5ad..000000000 --- a/common/test/run-json_escaped.c +++ /dev/null @@ -1,46 +0,0 @@ -#include "../json_escaped.c" -#include -#include -#include - -static void test_json_partial(void) -{ - const tal_t *ctx = tal(NULL, char); - - assert(streq(json_partial_escape(ctx, "\\")->s, "\\\\")); - assert(streq(json_partial_escape(ctx, "\\\\")->s, "\\\\")); - assert(streq(json_partial_escape(ctx, "\\\\\\")->s, "\\\\\\\\")); - assert(streq(json_partial_escape(ctx, "\\\\\\\\")->s, "\\\\\\\\")); - assert(streq(json_partial_escape(ctx, "\\n")->s, "\\n")); - assert(streq(json_partial_escape(ctx, "\n")->s, "\\n")); - assert(streq(json_partial_escape(ctx, "\\\"")->s, "\\\"")); - assert(streq(json_partial_escape(ctx, "\"")->s, "\\\"")); - assert(streq(json_partial_escape(ctx, "\\t")->s, "\\t")); - assert(streq(json_partial_escape(ctx, "\t")->s, "\\t")); - assert(streq(json_partial_escape(ctx, "\\b")->s, "\\b")); - assert(streq(json_partial_escape(ctx, "\b")->s, "\\b")); - assert(streq(json_partial_escape(ctx, "\\r")->s, "\\r")); - assert(streq(json_partial_escape(ctx, "\r")->s, "\\r")); - assert(streq(json_partial_escape(ctx, "\\f")->s, "\\f")); - assert(streq(json_partial_escape(ctx, "\f")->s, "\\f")); - /* You're allowed to escape / according to json.org. */ - assert(streq(json_partial_escape(ctx, "\\/")->s, "\\/")); - assert(streq(json_partial_escape(ctx, "/")->s, "/")); - - assert(streq(json_partial_escape(ctx, "\\u0FFF")->s, "\\u0FFF")); - assert(streq(json_partial_escape(ctx, "\\u0FFFx")->s, "\\u0FFFx")); - - /* Unknown escapes should be escaped. */ - assert(streq(json_partial_escape(ctx, "\\x")->s, "\\\\x")); - tal_free(ctx); -} - - -int main(void) -{ - setup_locale(); - - test_json_partial(); - assert(!taken_any()); - take_cleanup(); -} diff --git a/common/test/run-param.c b/common/test/run-param.c index 57c44d265..5d776a061 100644 --- a/common/test/run-param.c +++ b/common/test/run-param.c @@ -1,7 +1,6 @@ #include "config.h" #include "../amount.c" #include "../json.c" -#include "../json_escaped.c" #include "../json_tok.c" #include "../param.c" #include @@ -438,7 +437,7 @@ static void advanced(void) { struct json *j = json_parse(cmd, "[ 'lightning', 24, 'tok', 543 ]"); - struct json_escaped *label; + struct json_escape *label; u64 *msat; u64 *msat_opt1, *msat_opt2; const jsmntok_t *tok; @@ -460,7 +459,7 @@ static void advanced(void) } { struct json *j = json_parse(cmd, "[ 3, 'foo' ]"); - struct json_escaped *label, *foo; + struct json_escape *label, *foo; assert(param(cmd, j->buffer, j->toks, p_req("label", param_label, &label), p_opt("foo", param_label, &foo), @@ -532,7 +531,7 @@ static void test_invoice(struct command *cmd, const jsmntok_t *params) { u64 *msatoshi_val; - struct json_escaped *label_val; + struct json_escape *label_val; const char *desc_val; u64 *expiry; const jsmntok_t *fallbacks; diff --git a/lightningd/Makefile b/lightningd/Makefile index 115f2eb90..a7957b69a 100644 --- a/lightningd/Makefile +++ b/lightningd/Makefile @@ -36,7 +36,6 @@ LIGHTNINGD_COMMON_OBJS := \ common/key_derive.o \ common/io_lock.o \ common/json.o \ - common/json_escaped.o \ common/json_helpers.o \ common/json_tok.o \ common/memleak.o \ diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index b8d1bdba6..fb48ea0d4 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -8,12 +8,12 @@ #include #include #include +#include #include #include #include #include #include -#include #include #include #include @@ -202,7 +202,7 @@ static void json_getnodes_reply(struct subd *gossip UNUSED, const u8 *reply, json_array_start(response, "nodes"); for (i = 0; i < tal_count(nodes); i++) { - struct json_escaped *esc; + struct json_escape *esc; json_object_start(response, NULL); json_add_node_id(response, "nodeid", &nodes[i]->nodeid); diff --git a/lightningd/invoice.c b/lightningd/invoice.c index 5d11cff53..5b50630d3 100644 --- a/lightningd/invoice.c +++ b/lightningd/invoice.c @@ -6,13 +6,13 @@ #include #include #include +#include #include #include #include #include #include #include -#include #include #include #include @@ -108,7 +108,7 @@ struct invoice_payment_hook_payload { /* Set to NULL if it is deleted while waiting for plugin */ struct htlc_in *hin; /* What invoice it's trying to pay. */ - const struct json_escaped *label; + const struct json_escape *label; /* Amount it's offering. */ struct amount_msat msat; /* Preimage we'll give it if succeeds. */ @@ -448,7 +448,7 @@ struct invoice_info { struct command *cmd; struct preimage payment_preimage; struct bolt11 *b11; - struct json_escaped *label; + struct json_escape *label; }; static void gossipd_incoming_channels_reply(struct subd *gossipd, @@ -799,7 +799,7 @@ AUTODATA(json_command, &invoice_command); static void json_add_invoices(struct json_stream *response, struct wallet *wallet, - const struct json_escaped *label) + const struct json_escape *label) { struct invoice_iterator it; const struct invoice_details *details; @@ -826,7 +826,7 @@ static struct command_result *json_listinvoices(struct command *cmd, const jsmntok_t *obj UNNEEDED, const jsmntok_t *params) { - struct json_escaped *label; + struct json_escape *label; struct json_stream *response; struct wallet *wallet = cmd->ld->wallet; if (!param(cmd, buffer, params, @@ -859,7 +859,7 @@ static struct command_result *json_delinvoice(struct command *cmd, const struct invoice_details *details; struct json_stream *response; const char *status, *actual_status; - struct json_escaped *label; + struct json_escape *label; struct wallet *wallet = cmd->ld->wallet; if (!param(cmd, buffer, params, @@ -978,7 +978,7 @@ static struct command_result *json_waitinvoice(struct command *cmd, struct invoice i; const struct invoice_details *details; struct wallet *wallet = cmd->ld->wallet; - struct json_escaped *label; + struct json_escape *label; if (!param(cmd, buffer, params, p_req("label", param_label, &label), @@ -1079,7 +1079,7 @@ static struct command_result *json_decodepay(struct command *cmd, json_add_amount_msat_compat(response, *b11->msat, "msatoshi", "amount_msat"); if (b11->description) { - struct json_escaped *esc = json_escape(NULL, b11->description); + struct json_escape *esc = json_escape(NULL, b11->description); json_add_escaped_string(response, "description", take(esc)); } if (b11->description_hash) diff --git a/lightningd/json.c b/lightningd/json.c index 11a661d88..582b66e93 100644 --- a/lightningd/json.c +++ b/lightningd/json.c @@ -1,10 +1,10 @@ #include +#include #include #include #include #include #include -#include #include #include #include @@ -349,7 +349,7 @@ void json_add_literal(struct json_stream *result, const char *fieldname, void json_add_string(struct json_stream *result, const char *fieldname, const char *value TAKES) { - struct json_escaped *esc = json_partial_escape(NULL, value); + struct json_escape *esc = json_partial_escape(NULL, value); json_add_member(result, fieldname, "\"%s\"", esc->s); tal_free(esc); @@ -380,7 +380,7 @@ void json_add_tx(struct json_stream *result, } void json_add_escaped_string(struct json_stream *result, const char *fieldname, - const struct json_escaped *esc TAKES) + const struct json_escape *esc TAKES) { json_add_member(result, fieldname, "\"%s\"", esc->s); if (taken(esc)) diff --git a/lightningd/json.h b/lightningd/json.h index ee4c9a447..52183ccdf 100644 --- a/lightningd/json.h +++ b/lightningd/json.h @@ -22,7 +22,7 @@ struct bitcoin_txid; struct chainparams; struct channel_id; struct command; -struct json_escaped; +struct json_escape; struct json_stream; struct pubkey; struct node_id; @@ -123,7 +123,7 @@ void json_add_string(struct json_stream *result, const char *fieldname, const ch * already be JSON escaped as necessary. */ void json_add_escaped_string(struct json_stream *result, const char *fieldname, - const struct json_escaped *esc TAKES); + const struct json_escape *esc TAKES); /* '"fieldname" : literal' or 'literal' if fieldname is NULL*/ void json_add_literal(struct json_stream *result, const char *fieldname, diff --git a/lightningd/jsonrpc.c b/lightningd/jsonrpc.c index 3a3eef7f2..cf3b06b5c 100644 --- a/lightningd/jsonrpc.c +++ b/lightningd/jsonrpc.c @@ -22,12 +22,12 @@ #include #include #include +#include #include #include #include #include #include -#include #include #include #include @@ -332,7 +332,7 @@ static void json_add_help_command(struct command *cmd, " a description for this" " json_command!"); } else { - struct json_escaped *esc; + struct json_escape *esc; esc = json_escape(NULL, json_command->verbose); json_add_escaped_string(response, "verbose", take(esc)); @@ -551,7 +551,7 @@ struct json_stream *json_stream_fail_nodata(struct command *cmd, const char *errmsg) { struct json_stream *r = json_start(cmd); - struct json_escaped *e = json_partial_escape(tmpctx, errmsg); + struct json_escape *e = json_partial_escape(tmpctx, errmsg); assert(code); diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c index bc8e31ac2..214346943 100644 --- a/lightningd/lightningd.c +++ b/lightningd/lightningd.c @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -57,7 +58,6 @@ /*~ This is common code: routines shared by one or more executables * (separate daemons, or the lightning-cli program). */ #include -#include #include #include #include diff --git a/lightningd/options.c b/lightningd/options.c index cca3711a7..4ab8b4ad6 100644 --- a/lightningd/options.c +++ b/lightningd/options.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -11,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -1044,7 +1044,7 @@ static void add_config(struct lightningd *ld, } if (answer) { - struct json_escaped *esc = json_escape(NULL, answer); + struct json_escape *esc = json_escape(NULL, answer); json_add_escaped_string(response, name0, take(esc)); } tal_free(name0); diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index aaea0484a..933d60024 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include diff --git a/lightningd/test/Makefile b/lightningd/test/Makefile index c64f70e8b..b0334987a 100644 --- a/lightningd/test/Makefile +++ b/lightningd/test/Makefile @@ -14,7 +14,6 @@ LIGHTNINGD_TEST_COMMON_OBJS := \ common/htlc_state.o \ common/io_lock.o \ common/json.o \ - common/json_escaped.o \ common/key_derive.o \ common/pseudorand.o \ common/memleak.o \ diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index fb21f5bb6..7787c37c7 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -154,7 +154,7 @@ void json_add_bool(struct json_stream *result UNNEEDED, const char *fieldname UN /* Generated stub for json_add_escaped_string */ void json_add_escaped_string(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, - const struct json_escaped *esc TAKES UNNEEDED) + const struct json_escape *esc TAKES UNNEEDED) { fprintf(stderr, "json_add_escaped_string called!\n"); abort(); } /* Generated stub for json_add_hex */ void json_add_hex(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, @@ -323,7 +323,7 @@ struct command_result *param_escaped_string(struct command *cmd UNNEEDED, /* Generated stub for param_label */ struct command_result *param_label(struct command *cmd UNNEEDED, const char *name UNNEEDED, const char * buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, - struct json_escaped **label UNNEEDED) + struct json_escape **label UNNEEDED) { fprintf(stderr, "param_label called!\n"); abort(); } /* Generated stub for param_loglevel */ struct command_result *param_loglevel(struct command *cmd UNNEEDED, @@ -495,7 +495,7 @@ bool wallet_htlcs_load_for_channel(struct wallet *wallet UNNEEDED, bool wallet_invoice_create(struct wallet *wallet UNNEEDED, struct invoice *pinvoice UNNEEDED, const struct amount_msat *msat TAKES UNNEEDED, - const struct json_escaped *label TAKES UNNEEDED, + const struct json_escape *label TAKES UNNEEDED, u64 expiry UNNEEDED, const char *b11enc UNNEEDED, const char *description UNNEEDED, @@ -518,7 +518,7 @@ const struct invoice_details *wallet_invoice_details(const tal_t *ctx UNNEEDED, /* Generated stub for wallet_invoice_find_by_label */ bool wallet_invoice_find_by_label(struct wallet *wallet UNNEEDED, struct invoice *pinvoice UNNEEDED, - const struct json_escaped *label UNNEEDED) + const struct json_escape *label UNNEEDED) { fprintf(stderr, "wallet_invoice_find_by_label called!\n"); abort(); } /* Generated stub for wallet_invoice_find_by_rhash */ bool wallet_invoice_find_by_rhash(struct wallet *wallet UNNEEDED, diff --git a/lightningd/test/run-jsonrpc.c b/lightningd/test/run-jsonrpc.c index 7d7b51c56..9bdc76bb9 100644 --- a/lightningd/test/run-jsonrpc.c +++ b/lightningd/test/run-jsonrpc.c @@ -141,7 +141,7 @@ static void test_json_escape(void) for (i = 1; i < 256; i++) { char badstr[2]; struct json_stream *result = new_json_stream(NULL, NULL, NULL); - struct json_escaped *esc; + struct json_escape *esc; badstr[0] = i; badstr[1] = 0; diff --git a/plugins/Makefile b/plugins/Makefile index de6f47a27..50106c2b6 100644 --- a/plugins/Makefile +++ b/plugins/Makefile @@ -25,7 +25,6 @@ PLUGIN_COMMON_OBJS := \ common/daemon.o \ common/hash_u5.o \ common/json.o \ - common/json_escaped.o \ common/json_helpers.o \ common/json_tok.o \ common/memleak.o \ diff --git a/wallet/db.c b/wallet/db.c index d82c28281..c8801e2bb 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -1,8 +1,8 @@ #include "db.h" #include +#include #include -#include #include #include #include @@ -1121,16 +1121,16 @@ bool sqlite3_bind_sha256_double(sqlite3_stmt *stmt, int col, const struct sha256 return err == SQLITE_OK; } -struct json_escaped *sqlite3_column_json_escaped(const tal_t *ctx, - sqlite3_stmt *stmt, int col) +struct json_escape *sqlite3_column_json_escape(const tal_t *ctx, + sqlite3_stmt *stmt, int col) { - return json_escaped_string_(ctx, - sqlite3_column_blob(stmt, col), - sqlite3_column_bytes(stmt, col)); + return json_escape_string_(ctx, + sqlite3_column_blob(stmt, col), + sqlite3_column_bytes(stmt, col)); } -bool sqlite3_bind_json_escaped(sqlite3_stmt *stmt, int col, - const struct json_escaped *esc) +bool sqlite3_bind_json_escape(sqlite3_stmt *stmt, int col, + const struct json_escape *esc) { int err = sqlite3_bind_text(stmt, col, esc->s, strlen(esc->s), SQLITE_TRANSIENT); return err == SQLITE_OK; diff --git a/wallet/db.h b/wallet/db.h index 5656d3986..45d8c7f3c 100644 --- a/wallet/db.h +++ b/wallet/db.h @@ -202,10 +202,10 @@ bool sqlite3_bind_sha256_double(sqlite3_stmt *stmt, int col, const struct sha256 struct secret *sqlite3_column_secrets(const tal_t *ctx, sqlite3_stmt *stmt, int col); -struct json_escaped *sqlite3_column_json_escaped(const tal_t *ctx, - sqlite3_stmt *stmt, int col); -bool sqlite3_bind_json_escaped(sqlite3_stmt *stmt, int col, - const struct json_escaped *esc); +struct json_escape *sqlite3_column_json_escape(const tal_t *ctx, + sqlite3_stmt *stmt, int col); +bool sqlite3_bind_json_escape(sqlite3_stmt *stmt, int col, + const struct json_escape *esc); struct amount_msat sqlite3_column_amount_msat(sqlite3_stmt *stmt, int col); struct amount_sat sqlite3_column_amount_sat(sqlite3_stmt *stmt, int col); diff --git a/wallet/invoices.c b/wallet/invoices.c index aa1d7bfa3..3c61f6101 100644 --- a/wallet/invoices.c +++ b/wallet/invoices.c @@ -96,7 +96,7 @@ static struct invoice_details *wallet_stmt2invoice_details(const tal_t *ctx, sqlite3_column_sha256(stmt, 2, &dtl->rhash); - dtl->label = sqlite3_column_json_escaped(dtl, stmt, 3); + dtl->label = sqlite3_column_json_escape(dtl, stmt, 3); if (sqlite3_column_type(stmt, 4) != SQLITE_NULL) { dtl->msat = tal(dtl, struct amount_msat); @@ -255,7 +255,7 @@ static void install_expiration_timer(struct invoices *invoices) bool invoices_create(struct invoices *invoices, struct invoice *pinvoice, const struct amount_msat *msat TAKES, - const struct json_escaped *label TAKES, + const struct json_escape *label TAKES, u64 expiry, const char *b11enc, const char *description, @@ -300,7 +300,7 @@ bool invoices_create(struct invoices *invoices, sqlite3_bind_amount_msat(stmt, 4, *msat); else sqlite3_bind_null(stmt, 4); - sqlite3_bind_json_escaped(stmt, 5, label); + sqlite3_bind_json_escape(stmt, 5, label); sqlite3_bind_int64(stmt, 6, expiry_time); sqlite3_bind_text(stmt, 7, b11enc, strlen(b11enc), SQLITE_TRANSIENT); sqlite3_bind_text(stmt, 8, description, strlen(description), SQLITE_TRANSIENT); @@ -327,7 +327,7 @@ bool invoices_create(struct invoices *invoices, bool invoices_find_by_label(struct invoices *invoices, struct invoice *pinvoice, - const struct json_escaped *label) + const struct json_escape *label) { sqlite3_stmt *stmt; @@ -335,7 +335,7 @@ bool invoices_find_by_label(struct invoices *invoices, "id" " FROM invoices" " WHERE label = ?;"); - sqlite3_bind_json_escaped(stmt, 1, label); + sqlite3_bind_json_escape(stmt, 1, label); if (!db_select_step(invoices->db, stmt)) return false; diff --git a/wallet/invoices.h b/wallet/invoices.h index 41a250f26..9bc0bc97e 100644 --- a/wallet/invoices.h +++ b/wallet/invoices.h @@ -8,7 +8,7 @@ struct amount_msat; struct db; -struct json_escaped; +struct json_escape; struct invoice; struct invoice_details; struct invoice_iterator; @@ -49,7 +49,7 @@ struct invoices *invoices_new(const tal_t *ctx, bool invoices_create(struct invoices *invoices, struct invoice *pinvoice, const struct amount_msat *msat TAKES, - const struct json_escaped *label TAKES, + const struct json_escape *label TAKES, u64 expiry, const char *b11enc, const char *description, @@ -68,7 +68,7 @@ bool invoices_create(struct invoices *invoices, */ bool invoices_find_by_label(struct invoices *invoices, struct invoice *pinvoice, - const struct json_escaped *label); + const struct json_escape *label); /** * invoices_find_by_rhash - Search for an invoice by diff --git a/wallet/test/run-db.c b/wallet/test/run-db.c index 63b37fe25..20cc404c6 100644 --- a/wallet/test/run-db.c +++ b/wallet/test/run-db.c @@ -18,10 +18,6 @@ static void db_log_(struct log *log UNUSED, enum log_level level UNUSED, bool ca #include /* AUTOGENERATED MOCKS START */ -/* Generated stub for json_escaped_string_ */ -struct json_escaped *json_escaped_string_(const tal_t *ctx UNNEEDED, - const void *bytes UNNEEDED, size_t len UNNEEDED) -{ fprintf(stderr, "json_escaped_string_ called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ static char *db_err; diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index c207b3fa3..85fcbcd83 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -123,7 +123,7 @@ bool htlc_is_trimmed(enum side htlc_owner UNNEEDED, bool invoices_create(struct invoices *invoices UNNEEDED, struct invoice *pinvoice UNNEEDED, const struct amount_msat *msat TAKES UNNEEDED, - const struct json_escaped *label TAKES UNNEEDED, + const struct json_escape *label TAKES UNNEEDED, u64 expiry UNNEEDED, const char *b11enc UNNEEDED, const char *description UNNEEDED, @@ -141,7 +141,7 @@ void invoices_delete_expired(struct invoices *invoices UNNEEDED, /* Generated stub for invoices_find_by_label */ bool invoices_find_by_label(struct invoices *invoices UNNEEDED, struct invoice *pinvoice UNNEEDED, - const struct json_escaped *label UNNEEDED) + const struct json_escape *label UNNEEDED) { fprintf(stderr, "invoices_find_by_label called!\n"); abort(); } /* Generated stub for invoices_find_by_rhash */ bool invoices_find_by_rhash(struct invoices *invoices UNNEEDED, @@ -305,10 +305,6 @@ void json_array_end(struct json_stream *js UNNEEDED) /* Generated stub for json_array_start */ void json_array_start(struct json_stream *js UNNEEDED, const char *fieldname UNNEEDED) { fprintf(stderr, "json_array_start called!\n"); abort(); } -/* Generated stub for json_escaped_string_ */ -struct json_escaped *json_escaped_string_(const tal_t *ctx UNNEEDED, - const void *bytes UNNEEDED, size_t len UNNEEDED) -{ fprintf(stderr, "json_escaped_string_ called!\n"); abort(); } /* Generated stub for json_get_member */ const jsmntok_t *json_get_member(const char *buffer UNNEEDED, const jsmntok_t tok[] UNNEEDED, const char *label UNNEEDED) diff --git a/wallet/wallet.c b/wallet/wallet.c index 01391463c..bbc040d8e 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -1629,7 +1629,7 @@ bool wallet_htlcs_load_for_channel(struct wallet *wallet, bool wallet_invoice_create(struct wallet *wallet, struct invoice *pinvoice, const struct amount_msat *msat TAKES, - const struct json_escaped *label TAKES, + const struct json_escape *label TAKES, u64 expiry, const char *b11enc, const char *description, @@ -1640,7 +1640,7 @@ bool wallet_invoice_create(struct wallet *wallet, } bool wallet_invoice_find_by_label(struct wallet *wallet, struct invoice *pinvoice, - const struct json_escaped *label) + const struct json_escape *label) { return invoices_find_by_label(wallet->invoices, pinvoice, label); } diff --git a/wallet/wallet.h b/wallet/wallet.h index dc8483f64..23c91e7ae 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -641,7 +641,7 @@ struct invoice_details { /* Hash of preimage r */ struct sha256 rhash; /* Label assigned by user */ - const struct json_escaped *label; + const struct json_escape *label; /* NULL if they specified "any" */ struct amount_msat *msat; /* Absolute UNIX epoch time this will expire */ @@ -693,7 +693,7 @@ struct invoice { bool wallet_invoice_create(struct wallet *wallet, struct invoice *pinvoice, const struct amount_msat *msat TAKES, - const struct json_escaped *label TAKES, + const struct json_escape *label TAKES, u64 expiry, const char *b11enc, const char *description, @@ -712,7 +712,7 @@ bool wallet_invoice_create(struct wallet *wallet, */ bool wallet_invoice_find_by_label(struct wallet *wallet, struct invoice *pinvoice, - const struct json_escaped *label); + const struct json_escape *label); /** * wallet_invoice_find_by_rhash - Search for an invoice by payment_hash