From 3510c29e5dd641ba00b6a71a122baa9d52560773 Mon Sep 17 00:00:00 2001
From: darosior <darosior@protonmail.com>
Date: Sun, 26 Jan 2020 13:07:50 +0100
Subject: [PATCH] common: move json_stream helpers to common/json

Now that we have json_stream in common/, we can move all the related
helpers from lightningd/json to common/json. This way everyone can
benefit of them (including libplugin, the plugins themselves,
potentially lightning-cli), not lightningd alone!

Note that the Makefile of the common/test/ had to be modified, because
the new helpers make use of common/wireaddr... Which turns out to
\#include <lightingd/lightningd.h> ! So we couldnt just include the .c
and add mocks if we redefined some structs (hello run-param).
---
 cli/Makefile                                |  13 +-
 cli/test/run-large-input.c                  |  31 ++
 cli/test/run-remove-hint.c                  |  31 ++
 common/json.c                               | 337 +++++++++++++++++++-
 common/json.h                               | 154 ++++++++-
 common/test/Makefile                        |  13 +
 common/test/run-bigsize.c                   |  33 ++
 common/test/run-json.c                      |  32 +-
 common/test/run-json_remove.c               |  33 ++
 common/test/run-param.c                     |   5 -
 devtools/Makefile                           |   1 +
 gossipd/test/run-bench-find_route.c         |  31 +-
 gossipd/test/run-crc32_of_update.c          |  25 ++
 gossipd/test/run-extended-info.c            |  25 ++
 gossipd/test/run-find_route-specific.c      |  31 +-
 gossipd/test/run-find_route.c               |  31 +-
 gossipd/test/run-next_block_range.c         |  26 ++
 gossipd/test/run-overlong.c                 |  31 +-
 gossipd/test/run-txout_failure.c            |  25 ++
 lightningd/json.c                           | 333 -------------------
 lightningd/json.h                           | 138 --------
 lightningd/test/run-find_my_abspath.c       |  22 ++
 lightningd/test/run-invoice-select-inchan.c |  91 +-----
 lightningd/test/run-log-pruning.c           |  29 +-
 plugins/Makefile                            |   3 +
 25 files changed, 903 insertions(+), 621 deletions(-)

diff --git a/cli/Makefile b/cli/Makefile
index d594533bb..f2eefe2b8 100644
--- a/cli/Makefile
+++ b/cli/Makefile
@@ -3,11 +3,22 @@ LIGHTNING_CLI_OBJS := $(LIGHTNING_CLI_SRC:.c=.o)
 
 LIGHTNING_CLI_COMMON_OBJS :=			\
 	bitcoin/chainparams.o			\
+	bitcoin/pubkey.o			\
+	bitcoin/shadouble.o			\
+	bitcoin/tx.o				\
+	common/amount.o				\
+	common/base32.o				\
+	common/bigsize.o			\
 	common/configdir.o			\
 	common/json.o				\
+	common/json_stream.o			\
 	common/memleak.o			\
+	common/type_to_string.o			\
 	common/utils.o				\
-	common/version.o
+	common/version.o			\
+	common/wireaddr.o			\
+	wire/fromwire.o				\
+	wire/towire.o
 
 lightning-cli-all: cli/lightning-cli
 
diff --git a/cli/test/run-large-input.c b/cli/test/run-large-input.c
index 4af125b8c..d9c9fcd86 100644
--- a/cli/test/run-large-input.c
+++ b/cli/test/run-large-input.c
@@ -2,6 +2,8 @@
 #include <assert.h>
 #include <common/amount.h>
 #include <common/bigsize.h>
+#include <common/json_stream.h>
+#include <common/wireaddr.h>
 #include <fcntl.h>
 #include <sys/socket.h>
 #include <sys/stat.h>
@@ -48,12 +50,41 @@ bool amount_sat_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED)
 				       struct amount_sat a UNNEEDED,
 				       struct amount_sat b UNNEEDED)
 { fprintf(stderr, "amount_sat_sub called!\n"); abort(); }
+/* Generated stub for amount_sat_to_msat */
+ bool amount_sat_to_msat(struct amount_msat *msat UNNEEDED,
+					   struct amount_sat sat UNNEEDED)
+{ fprintf(stderr, "amount_sat_to_msat called!\n"); abort(); }
 /* Generated stub for bigsize_get */
 size_t bigsize_get(const u8 *p UNNEEDED, size_t max UNNEEDED, bigsize_t *val UNNEEDED)
 { fprintf(stderr, "bigsize_get called!\n"); abort(); }
 /* Generated stub for bigsize_put */
 size_t bigsize_put(u8 buf[BIGSIZE_MAX_LEN] UNNEEDED, bigsize_t v UNNEEDED)
 { fprintf(stderr, "bigsize_put called!\n"); abort(); }
+/* Generated stub for fmt_wireaddr_without_port */
+char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED)
+{ fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); }
+/* Generated stub for json_add_member */
+void json_add_member(struct json_stream *js UNNEEDED,
+		     const char *fieldname UNNEEDED,
+		     bool quote UNNEEDED,
+		     const char *fmt UNNEEDED, ...)
+{ fprintf(stderr, "json_add_member called!\n"); abort(); }
+/* Generated stub for json_array_end */
+void json_array_end(struct json_stream *js UNNEEDED)
+{ fprintf(stderr, "json_array_end called!\n"); abort(); }
+/* 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_member_direct */
+char *json_member_direct(struct json_stream *js UNNEEDED,
+			 const char *fieldname UNNEEDED, size_t extra UNNEEDED)
+{ fprintf(stderr, "json_member_direct called!\n"); abort(); }
+/* Generated stub for json_object_end */
+void json_object_end(struct json_stream *js UNNEEDED)
+{ fprintf(stderr, "json_object_end called!\n"); abort(); }
+/* Generated stub for json_object_start */
+void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED)
+{ fprintf(stderr, "json_object_start called!\n"); abort(); }
 /* Generated stub for version_and_exit */
 char *version_and_exit(const void *unused UNNEEDED)
 { fprintf(stderr, "version_and_exit called!\n"); abort(); }
diff --git a/cli/test/run-remove-hint.c b/cli/test/run-remove-hint.c
index 8db904c19..57541e6ca 100644
--- a/cli/test/run-remove-hint.c
+++ b/cli/test/run-remove-hint.c
@@ -2,6 +2,8 @@
 #include <assert.h>
 #include <common/amount.h>
 #include <common/bigsize.h>
+#include <common/json_stream.h>
+#include <common/wireaddr.h>
 #include <fcntl.h>
 #include <stdio.h>
 #include <sys/socket.h>
@@ -51,12 +53,41 @@ bool amount_sat_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED)
 				       struct amount_sat a UNNEEDED,
 				       struct amount_sat b UNNEEDED)
 { fprintf(stderr, "amount_sat_sub called!\n"); abort(); }
+/* Generated stub for amount_sat_to_msat */
+ bool amount_sat_to_msat(struct amount_msat *msat UNNEEDED,
+					   struct amount_sat sat UNNEEDED)
+{ fprintf(stderr, "amount_sat_to_msat called!\n"); abort(); }
 /* Generated stub for bigsize_get */
 size_t bigsize_get(const u8 *p UNNEEDED, size_t max UNNEEDED, bigsize_t *val UNNEEDED)
 { fprintf(stderr, "bigsize_get called!\n"); abort(); }
 /* Generated stub for bigsize_put */
 size_t bigsize_put(u8 buf[BIGSIZE_MAX_LEN] UNNEEDED, bigsize_t v UNNEEDED)
 { fprintf(stderr, "bigsize_put called!\n"); abort(); }
+/* Generated stub for fmt_wireaddr_without_port */
+char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED)
+{ fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); }
+/* Generated stub for json_add_member */
+void json_add_member(struct json_stream *js UNNEEDED,
+		     const char *fieldname UNNEEDED,
+		     bool quote UNNEEDED,
+		     const char *fmt UNNEEDED, ...)
+{ fprintf(stderr, "json_add_member called!\n"); abort(); }
+/* Generated stub for json_array_end */
+void json_array_end(struct json_stream *js UNNEEDED)
+{ fprintf(stderr, "json_array_end called!\n"); abort(); }
+/* 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_member_direct */
+char *json_member_direct(struct json_stream *js UNNEEDED,
+			 const char *fieldname UNNEEDED, size_t extra UNNEEDED)
+{ fprintf(stderr, "json_member_direct called!\n"); abort(); }
+/* Generated stub for json_object_end */
+void json_object_end(struct json_stream *js UNNEEDED)
+{ fprintf(stderr, "json_object_end called!\n"); abort(); }
+/* Generated stub for json_object_start */
+void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED)
+{ fprintf(stderr, "json_object_start called!\n"); abort(); }
 /* Generated stub for version_and_exit */
 char *version_and_exit(const void *unused UNNEEDED)
 { fprintf(stderr, "version_and_exit called!\n"); abort(); }
diff --git a/common/json.c b/common/json.c
index cd504a5f3..2f372044c 100644
--- a/common/json.c
+++ b/common/json.c
@@ -1,12 +1,20 @@
 /* JSON core and helpers */
-#include "json.h"
+#include <arpa/inet.h>
 #include <assert.h>
+#include <bitcoin/preimage.h>
+#include <bitcoin/privkey.h>
 #include <bitcoin/pubkey.h>
 #include <ccan/build_assert/build_assert.h>
 #include <ccan/mem/mem.h>
 #include <ccan/str/hex/hex.h>
 #include <ccan/tal/str/str.h>
+#include <ccan/time/time.h>
+#include <common/amount.h>
+#include <common/json.h>
+#include <common/json_stream.h>
+#include <common/node_id.h>
 #include <common/utils.h>
+#include <common/wireaddr.h>
 #include <errno.h>
 #include <inttypes.h>
 #include <stdarg.h>
@@ -427,3 +435,330 @@ const jsmntok_t *json_delve(const char *buffer,
 
        return tok;
 }
+
+void json_add_node_id(struct json_stream *response,
+		      const char *fieldname,
+		      const struct node_id *id)
+{
+	json_add_hex(response, fieldname, id->k, sizeof(id->k));
+}
+
+void json_add_pubkey(struct json_stream *response,
+		     const char *fieldname,
+		     const struct pubkey *key)
+{
+	u8 der[PUBKEY_CMPR_LEN];
+
+	pubkey_to_der(der, key);
+	json_add_hex(response, fieldname, der, sizeof(der));
+}
+
+void json_add_txid(struct json_stream *result, const char *fieldname,
+		   const struct bitcoin_txid *txid)
+{
+	char hex[hex_str_size(sizeof(*txid))];
+
+	bitcoin_txid_to_hex(txid, hex, sizeof(hex));
+	json_add_string(result, fieldname, hex);
+}
+
+void json_add_short_channel_id(struct json_stream *response,
+			       const char *fieldname,
+			       const struct short_channel_id *scid)
+{
+	json_add_member(response, fieldname, true, "%dx%dx%d",
+			short_channel_id_blocknum(scid),
+			short_channel_id_txnum(scid),
+			short_channel_id_outnum(scid));
+}
+
+void json_add_address(struct json_stream *response, const char *fieldname,
+		      const struct wireaddr *addr)
+{
+	json_object_start(response, fieldname);
+	char *addrstr = tal_arr(response, char, INET6_ADDRSTRLEN);
+	if (addr->type == ADDR_TYPE_IPV4) {
+		inet_ntop(AF_INET, addr->addr, addrstr, INET_ADDRSTRLEN);
+		json_add_string(response, "type", "ipv4");
+		json_add_string(response, "address", addrstr);
+		json_add_num(response, "port", addr->port);
+	} else if (addr->type == ADDR_TYPE_IPV6) {
+		inet_ntop(AF_INET6, addr->addr, addrstr, INET6_ADDRSTRLEN);
+		json_add_string(response, "type", "ipv6");
+		json_add_string(response, "address", addrstr);
+		json_add_num(response, "port", addr->port);
+	} else if (addr->type == ADDR_TYPE_TOR_V2) {
+		json_add_string(response, "type", "torv2");
+		json_add_string(response, "address", fmt_wireaddr_without_port(tmpctx, addr));
+		json_add_num(response, "port", addr->port);
+	} else if (addr->type == ADDR_TYPE_TOR_V3) {
+		json_add_string(response, "type", "torv3");
+		json_add_string(response, "address", fmt_wireaddr_without_port(tmpctx, addr));
+		json_add_num(response, "port", addr->port);
+	}
+	json_object_end(response);
+}
+
+void json_add_address_internal(struct json_stream *response,
+			       const char *fieldname,
+			       const struct wireaddr_internal *addr)
+{
+	switch (addr->itype) {
+	case ADDR_INTERNAL_SOCKNAME:
+		json_object_start(response, fieldname);
+		json_add_string(response, "type", "local socket");
+		json_add_string(response, "socket", addr->u.sockname);
+		json_object_end(response);
+		return;
+	case ADDR_INTERNAL_ALLPROTO:
+		json_object_start(response, fieldname);
+		json_add_string(response, "type", "any protocol");
+		json_add_num(response, "port", addr->u.port);
+		json_object_end(response);
+		return;
+	case ADDR_INTERNAL_AUTOTOR:
+		json_object_start(response, fieldname);
+		json_add_string(response, "type", "Tor generated address");
+		json_add_address(response, "service", &addr->u.torservice.address);
+		json_object_end(response);
+		return;
+	case ADDR_INTERNAL_STATICTOR:
+		json_object_start(response, fieldname);
+		json_add_string(response, "type", "Tor from blob generated static address");
+		json_add_address(response, "service", &addr->u.torservice.address);
+		json_object_end(response);
+		return;
+	case ADDR_INTERNAL_FORPROXY:
+		json_object_start(response, fieldname);
+		json_add_string(response, "type", "unresolved");
+		json_add_string(response, "name", addr->u.unresolved.name);
+		json_add_num(response, "port", addr->u.unresolved.port);
+		json_object_end(response);
+		return;
+	case ADDR_INTERNAL_WIREADDR:
+		json_add_address(response, fieldname, &addr->u.wireaddr);
+		return;
+	}
+	abort();
+}
+
+void json_add_num(struct json_stream *result, const char *fieldname, unsigned int value)
+{
+	json_add_member(result, fieldname, false, "%u", value);
+}
+
+void json_add_double(struct json_stream *result, const char *fieldname, double value)
+{
+	json_add_member(result, fieldname, false, "%f", value);
+}
+
+void json_add_u64(struct json_stream *result, const char *fieldname,
+		  uint64_t value)
+{
+	json_add_member(result, fieldname, false, "%"PRIu64, value);
+}
+
+void json_add_s64(struct json_stream *result, const char *fieldname,
+		  int64_t value)
+{
+	json_add_member(result, fieldname, false, "%"PRIi64, value);
+}
+
+void json_add_u32(struct json_stream *result, const char *fieldname,
+		  uint32_t value)
+{
+	json_add_member(result, fieldname, false, "%u", value);
+}
+
+void json_add_s32(struct json_stream *result, const char *fieldname,
+		  int32_t value)
+{
+	json_add_member(result, fieldname, false, "%d", value);
+}
+
+void json_add_literal(struct json_stream *result, const char *fieldname,
+		      const char *literal, int len)
+{
+	/* Literal may contain quotes, so bypass normal checks */
+	char *dest = json_member_direct(result, fieldname, strlen(literal));
+	if (dest)
+		memcpy(dest, literal, strlen(literal));
+}
+
+void json_add_string(struct json_stream *result, const char *fieldname, const char *value TAKES)
+{
+	json_add_member(result, fieldname, true, "%s", value);
+	if (taken(value))
+		tal_free(value);
+}
+
+void json_add_bool(struct json_stream *result, const char *fieldname, bool value)
+{
+	json_add_member(result, fieldname, false, value ? "true" : "false");
+}
+
+void json_add_null(struct json_stream *stream, const char *fieldname)
+{
+	json_add_member(stream, fieldname, false, "null");
+}
+
+void json_add_hex(struct json_stream *js, const char *fieldname,
+		  const void *data, size_t len)
+{
+	/* Size without NUL term */
+	size_t hexlen = hex_str_size(len) - 1;
+	char *dest;
+
+	dest = json_member_direct(js, fieldname, 1 + hexlen + 1);
+	if (dest) {
+		dest[0] = '"';
+		if (!hex_encode(data, len, dest + 1, hexlen + 1))
+			abort();
+		dest[1+hexlen] = '"';
+	}
+}
+
+void json_add_hex_talarr(struct json_stream *result,
+			 const char *fieldname,
+			 const tal_t *data)
+{
+	json_add_hex(result, fieldname, data, tal_bytelen(data));
+}
+
+void json_add_tx(struct json_stream *result,
+		 const char *fieldname,
+		 const struct bitcoin_tx *tx)
+{
+	json_add_hex_talarr(result, fieldname, linearize_tx(tmpctx, tx));
+}
+
+void json_add_escaped_string(struct json_stream *result, const char *fieldname,
+			     const struct json_escape *esc TAKES)
+{
+	/* Already escaped, don't re-escape! */
+	char *dest = json_member_direct(result,	fieldname,
+					1 + strlen(esc->s) + 1);
+
+	if (dest) {
+		dest[0] = '"';
+		memcpy(dest + 1, esc->s, strlen(esc->s));
+		dest[1+strlen(esc->s)] = '"';
+	}
+	if (taken(esc))
+		tal_free(esc);
+}
+
+void json_add_amount_msat_compat(struct json_stream *result,
+				 struct amount_msat msat,
+				 const char *rawfieldname,
+				 const char *msatfieldname)
+{
+	json_add_u64(result, rawfieldname, msat.millisatoshis); /* Raw: low-level helper */
+	json_add_amount_msat_only(result, msatfieldname, msat);
+}
+
+void json_add_amount_msat_only(struct json_stream *result,
+			  const char *msatfieldname,
+			  struct amount_msat msat)
+{
+	json_add_string(result, msatfieldname,
+			type_to_string(tmpctx, struct amount_msat, &msat));
+}
+
+void json_add_amount_sat_compat(struct json_stream *result,
+				struct amount_sat sat,
+				const char *rawfieldname,
+				const char *msatfieldname)
+{
+	json_add_u64(result, rawfieldname, sat.satoshis); /* Raw: low-level helper */
+	json_add_amount_sat_only(result, msatfieldname, sat);
+}
+
+void json_add_amount_sat_only(struct json_stream *result,
+			 const char *msatfieldname,
+			 struct amount_sat sat)
+{
+	struct amount_msat msat;
+	if (amount_sat_to_msat(&msat, sat))
+		json_add_string(result, msatfieldname,
+				type_to_string(tmpctx, struct amount_msat, &msat));
+}
+
+void json_add_timeabs(struct json_stream *result, const char *fieldname,
+		      struct timeabs t)
+{
+	json_add_member(result, fieldname, false, "%" PRIu64 ".%03" PRIu64,
+			(u64)t.ts.tv_sec, (u64)t.ts.tv_nsec / 1000000);
+}
+
+void json_add_time(struct json_stream *result, const char *fieldname,
+			  struct timespec ts)
+{
+	char timebuf[100];
+
+	snprintf(timebuf, sizeof(timebuf), "%lu.%09u",
+		(unsigned long)ts.tv_sec,
+		(unsigned)ts.tv_nsec);
+	json_add_string(result, fieldname, timebuf);
+}
+
+void json_add_secret(struct json_stream *response, const char *fieldname,
+		     const struct secret *secret)
+{
+	json_add_hex(response, fieldname, secret, sizeof(struct secret));
+}
+
+void json_add_sha256(struct json_stream *result, const char *fieldname,
+		     const struct sha256 *hash)
+{
+	json_add_hex(result, fieldname, hash, sizeof(*hash));
+}
+
+void json_add_preimage(struct json_stream *result, const char *fieldname,
+		     const struct preimage *preimage)
+{
+	json_add_hex(result, fieldname, preimage, sizeof(*preimage));
+}
+
+void json_add_tok(struct json_stream *result, const char *fieldname,
+                  const jsmntok_t *tok, const char *buffer)
+{
+	int i = 0;
+	const jsmntok_t *t;
+
+	switch (tok->type) {
+	case JSMN_PRIMITIVE:
+		if (json_tok_is_num(buffer, tok)) {
+			json_to_int(buffer, tok, &i);
+			json_add_num(result, fieldname, i);
+		}
+		return;
+
+	case JSMN_STRING:
+		if (json_tok_streq(buffer, tok, "true"))
+			json_add_bool(result, fieldname, true);
+		else if (json_tok_streq(buffer, tok, "false"))
+			json_add_bool(result, fieldname, false);
+		else
+			json_add_string(result, fieldname, json_strdup(tmpctx, buffer, tok));
+		return;
+
+	case JSMN_ARRAY:
+		json_array_start(result, fieldname);
+		json_for_each_arr(i, t, tok)
+			json_add_tok(result, NULL, t, buffer);
+		json_array_end(result);
+		return;
+
+	case JSMN_OBJECT:
+		json_object_start(result, fieldname);
+		json_for_each_obj(i, t, tok)
+			json_add_tok(result, json_strdup(tmpctx, buffer, t), t+1, buffer);
+		json_object_end(result);
+		return;
+
+	case JSMN_UNDEFINED:
+		break;
+	}
+	abort();
+}
diff --git a/common/json.h b/common/json.h
index 857686d25..4ac74bdd9 100644
--- a/common/json.h
+++ b/common/json.h
@@ -1,8 +1,6 @@
 #ifndef LIGHTNING_COMMON_JSON_H
 #define LIGHTNING_COMMON_JSON_H
 #include "config.h"
-#include <bitcoin/preimage.h>
-#include <bitcoin/privkey.h>
 #include <ccan/short_types/short_types.h>
 #include <ccan/tal/tal.h>
 #include <common/errcode.h>
@@ -13,6 +11,27 @@
 #define JSMN_STRICT 1
 # include <external/jsmn/jsmn.h>
 
+struct amount_sat;
+struct amount_msat;
+struct bitcoin_tx;
+struct bitcoin_txid;
+struct channel_id;
+struct json_escape;
+struct json_stream;
+struct pubkey;
+struct node_id;
+struct sha256;
+struct preimage;
+struct secret;
+struct short_channel_id;
+struct timeabs;
+struct timespec;
+struct wallet_payment;
+struct wallet_tx;
+struct wireaddr;
+struct wireaddr_internal;
+
+
 /* Include " if it's a string. */
 const char *json_tok_full(const char *buffer, const jsmntok_t *t);
 
@@ -115,4 +134,135 @@ const jsmntok_t *json_delve(const char *buffer,
 #define json_for_each_obj(i, t, obj) \
 	for (i = 0, t = (obj) + 1; i < (obj)->size; t = json_next(t+1), i++)
 
+
+/* Helpers for outputting JSON results */
+
+/* '"fieldname" : "0289abcdef..."' or "0289abcdef..." if fieldname is NULL */
+void json_add_pubkey(struct json_stream *response,
+		     const char *fieldname,
+		     const struct pubkey *key);
+
+/* '"fieldname" : "89abcdef..."' or "89abcdef..." if fieldname is NULL */
+void json_add_secret(struct json_stream *response,
+		     const char *fieldname,
+		     const struct secret *secret);
+
+/* '"fieldname" : "0289abcdef..."' or "0289abcdef..." if fieldname is NULL */
+void json_add_node_id(struct json_stream *response,
+				const char *fieldname,
+				const struct node_id *id);
+
+/* '"fieldname" : <hexrev>' or "<hexrev>" if fieldname is NULL */
+void json_add_txid(struct json_stream *result, const char *fieldname,
+		   const struct bitcoin_txid *txid);
+
+/* '"fieldname" : "1234:5:6"' */
+void json_add_short_channel_id(struct json_stream *response,
+			       const char *fieldname,
+			       const struct short_channel_id *id);
+
+/* JSON serialize a network address for a node */
+void json_add_address(struct json_stream *response, const char *fieldname,
+		      const struct wireaddr *addr);
+
+/* JSON serialize a network address for a node. */
+void json_add_address_internal(struct json_stream *response,
+			       const char *fieldname,
+			       const struct wireaddr_internal *addr);
+
+/* '"fieldname" : "value"' or '"value"' if fieldname is NULL.  Turns
+ * any non-printable chars into JSON escapes, but leaves existing escapes alone.
+ */
+void json_add_string(struct json_stream *result, const char *fieldname, const char *value);
+
+/* '"fieldname" : "value"' or '"value"' if fieldname is NULL.  String must
+ * already be JSON escaped as necessary. */
+void json_add_escaped_string(struct json_stream *result,
+			     const char *fieldname,
+			     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,
+		      const char *literal, int len);
+/* '"fieldname" : value' or 'value' if fieldname is NULL */
+void json_add_double(struct json_stream *result, const char *fieldname,
+		     double value);
+/* '"fieldname" : value' or 'value' if fieldname is NULL */
+void json_add_num(struct json_stream *result, const char *fieldname,
+		  unsigned int value);
+/* '"fieldname" : value' or 'value' if fieldname is NULL */
+void json_add_u64(struct json_stream *result, const char *fieldname,
+		  uint64_t value);
+/* '"fieldname" : value' or 'value' if fieldname is NULL */
+void json_add_s64(struct json_stream *result, const char *fieldname,
+		  int64_t value);
+/* '"fieldname" : value' or 'value' if fieldname is NULL */
+void json_add_u32(struct json_stream *result, const char *fieldname,
+		  uint32_t value);
+/* '"fieldname" : value' or 'value' if fieldname is NULL */
+void json_add_s32(struct json_stream *result, const char *fieldname,
+		  int32_t value);
+/* '"fieldname" : true|false' or 'true|false' if fieldname is NULL */
+void json_add_bool(struct json_stream *result, const char *fieldname,
+		   bool value);
+
+/* '"fieldname" : null' or 'null' if fieldname is NULL */
+void json_add_null(struct json_stream *stream, const char *fieldname);
+
+/* '"fieldname" : "0189abcdef..."' or "0189abcdef..." if fieldname is NULL */
+void json_add_hex(struct json_stream *result, const char *fieldname,
+		  const void *data, size_t len);
+/* '"fieldname" : "0189abcdef..."' or "0189abcdef..." if fieldname is NULL */
+void json_add_hex_talarr(struct json_stream *result,
+			 const char *fieldname,
+			 const tal_t *data);
+/* '"fieldname" : "010000000001..."' or "010000000001..." if fieldname is NULL */
+void json_add_tx(struct json_stream *result,
+		 const char *fieldname,
+		 const struct bitcoin_tx *tx);
+
+/* Adds both a 'raw' number field and an 'amount_msat' field */
+void json_add_amount_msat_compat(struct json_stream *result,
+				 struct amount_msat msat,
+				 const char *rawfieldname,
+				 const char *msatfieldname)
+	NO_NULL_ARGS;
+
+/* Adds both a 'raw' number field and an 'amount_msat' field */
+void json_add_amount_sat_compat(struct json_stream *result,
+				struct amount_sat sat,
+				const char *rawfieldname,
+				const char *msatfieldname)
+	NO_NULL_ARGS;
+
+/* Adds an 'msat' field */
+void json_add_amount_msat_only(struct json_stream *result,
+			  const char *msatfieldname,
+			  struct amount_msat msat)
+	NO_NULL_ARGS;
+
+/* Adds an 'msat' field */
+void json_add_amount_sat_only(struct json_stream *result,
+			 const char *msatfieldname,
+			 struct amount_sat sat)
+	NO_NULL_ARGS;
+
+void json_add_timeabs(struct json_stream *result, const char *fieldname,
+		      struct timeabs t);
+
+/* used in log.c and notification.c*/
+void json_add_time(struct json_stream *result, const char *fieldname,
+			  struct timespec ts);
+
+void json_add_sha256(struct json_stream *result, const char *fieldname,
+		     const struct sha256 *hash);
+
+void json_add_preimage(struct json_stream *result, const char *fieldname,
+		     const struct preimage *preimage);
+
+/* Add any json token */
+void json_add_tok(struct json_stream *result, const char *fieldname,
+                  const jsmntok_t *tok, const char *buffer);
+
+
 #endif /* LIGHTNING_COMMON_JSON_H */
diff --git a/common/test/Makefile b/common/test/Makefile
index d8c4eef78..aa52a2f7c 100644
--- a/common/test/Makefile
+++ b/common/test/Makefile
@@ -14,6 +14,19 @@ ALL_OBJS += $(COMMON_TEST_PROGRAMS:=.o)
 # Sphinx test wants to decode TLVs.
 common/test/run-sphinx: wire/gen_onion_wire.o wire/towire.o wire/fromwire.o
 
+common/test/run-param					\
+common/test/run-json:					\
+	common/amount.o					\
+	common/base32.o					\
+	common/bigsize.o				\
+	common/json.o					\
+	common/json_stream.o				\
+	common/wireaddr.o				\
+	common/type_to_string.o				\
+	wire/fromwire.o					\
+	wire/gen_onion_wire.o				\
+	wire/towire.o
+
 update-mocks: $(COMMON_TEST_SRC:%=update-mocks/%)
 
 check-units: $(COMMON_TEST_PROGRAMS:%=unittest/%)
diff --git a/common/test/run-bigsize.c b/common/test/run-bigsize.c
index b29bf0f0d..e833e3a96 100644
--- a/common/test/run-bigsize.c
+++ b/common/test/run-bigsize.c
@@ -31,9 +31,42 @@ bool amount_sat_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED)
 				       struct amount_sat a UNNEEDED,
 				       struct amount_sat b UNNEEDED)
 { fprintf(stderr, "amount_sat_sub called!\n"); abort(); }
+/* Generated stub for amount_sat_to_msat */
+ bool amount_sat_to_msat(struct amount_msat *msat UNNEEDED,
+					   struct amount_sat sat UNNEEDED)
+{ fprintf(stderr, "amount_sat_to_msat called!\n"); abort(); }
+/* Generated stub for fmt_wireaddr_without_port */
+char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED)
+{ fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); }
 /* Generated stub for fromwire_fail */
 const void *fromwire_fail(const u8 **cursor UNNEEDED, size_t *max UNNEEDED)
 { fprintf(stderr, "fromwire_fail called!\n"); abort(); }
+/* Generated stub for json_add_member */
+void json_add_member(struct json_stream *js UNNEEDED,
+		     const char *fieldname UNNEEDED,
+		     bool quote UNNEEDED,
+		     const char *fmt UNNEEDED, ...)
+{ fprintf(stderr, "json_add_member called!\n"); abort(); }
+/* Generated stub for json_array_end */
+void json_array_end(struct json_stream *js UNNEEDED)
+{ fprintf(stderr, "json_array_end called!\n"); abort(); }
+/* 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_member_direct */
+char *json_member_direct(struct json_stream *js UNNEEDED,
+			 const char *fieldname UNNEEDED, size_t extra UNNEEDED)
+{ fprintf(stderr, "json_member_direct called!\n"); abort(); }
+/* Generated stub for json_object_end */
+void json_object_end(struct json_stream *js UNNEEDED)
+{ fprintf(stderr, "json_object_end called!\n"); abort(); }
+/* Generated stub for json_object_start */
+void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED)
+{ fprintf(stderr, "json_object_start called!\n"); abort(); }
+/* Generated stub for type_to_string_ */
+const char *type_to_string_(const tal_t *ctx UNNEEDED, const char *typename UNNEEDED,
+			    union printable_types u UNNEEDED)
+{ fprintf(stderr, "type_to_string_ called!\n"); abort(); }
 /* AUTOGENERATED MOCKS END */
 
 /* BOLT #1:
diff --git a/common/test/run-json.c b/common/test/run-json.c
index a65c0cc4c..ed24dfd96 100644
--- a/common/test/run-json.c
+++ b/common/test/run-json.c
@@ -1,42 +1,16 @@
-#include "../json.c"
 #include "../json_helpers.c"
+#include <assert.h>
 #include <ccan/mem/mem.h>
+#include <common/json.h>
 #include <common/utils.h>
+#include <inttypes.h>
 #include <stdio.h>
 #include <wire/wire.h>
 
 /* AUTOGENERATED MOCKS START */
-/* Generated stub for amount_asset_is_main */
-bool amount_asset_is_main(struct amount_asset *asset UNNEEDED)
-{ fprintf(stderr, "amount_asset_is_main called!\n"); abort(); }
-/* Generated stub for amount_asset_to_sat */
-struct amount_sat amount_asset_to_sat(struct amount_asset *asset UNNEEDED)
-{ fprintf(stderr, "amount_asset_to_sat called!\n"); abort(); }
-/* Generated stub for amount_sat_add */
- bool amount_sat_add(struct amount_sat *val UNNEEDED,
-				       struct amount_sat a UNNEEDED,
-				       struct amount_sat b UNNEEDED)
-{ fprintf(stderr, "amount_sat_add called!\n"); abort(); }
-/* Generated stub for amount_sat_eq */
-bool amount_sat_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED)
-{ fprintf(stderr, "amount_sat_eq called!\n"); abort(); }
-/* Generated stub for amount_sat_sub */
- bool amount_sat_sub(struct amount_sat *val UNNEEDED,
-				       struct amount_sat a UNNEEDED,
-				       struct amount_sat b UNNEEDED)
-{ fprintf(stderr, "amount_sat_sub called!\n"); abort(); }
-/* Generated stub for fromwire_fail */
-const void *fromwire_fail(const u8 **cursor UNNEEDED, size_t *max UNNEEDED)
-{ fprintf(stderr, "fromwire_fail called!\n"); abort(); }
 /* Generated stub for node_id_from_hexstr */
 bool node_id_from_hexstr(const char *str UNNEEDED, size_t slen UNNEEDED, struct node_id *id UNNEEDED)
 { fprintf(stderr, "node_id_from_hexstr called!\n"); abort(); }
-/* Generated stub for parse_amount_msat */
-bool parse_amount_msat(struct amount_msat *msat UNNEEDED, const char *s UNNEEDED, size_t slen UNNEEDED)
-{ fprintf(stderr, "parse_amount_msat called!\n"); abort(); }
-/* Generated stub for parse_amount_sat */
-bool parse_amount_sat(struct amount_sat *sat UNNEEDED, const char *s UNNEEDED, size_t slen UNNEEDED)
-{ fprintf(stderr, "parse_amount_sat called!\n"); abort(); }
 /* AUTOGENERATED MOCKS END */
 
 
diff --git a/common/test/run-json_remove.c b/common/test/run-json_remove.c
index 202105a73..7d2816e43 100644
--- a/common/test/run-json_remove.c
+++ b/common/test/run-json_remove.c
@@ -23,9 +23,42 @@ bool amount_sat_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED)
 				       struct amount_sat a UNNEEDED,
 				       struct amount_sat b UNNEEDED)
 { fprintf(stderr, "amount_sat_sub called!\n"); abort(); }
+/* Generated stub for amount_sat_to_msat */
+ bool amount_sat_to_msat(struct amount_msat *msat UNNEEDED,
+					   struct amount_sat sat UNNEEDED)
+{ fprintf(stderr, "amount_sat_to_msat called!\n"); abort(); }
+/* Generated stub for fmt_wireaddr_without_port */
+char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED)
+{ fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); }
 /* Generated stub for fromwire_fail */
 const void *fromwire_fail(const u8 **cursor UNNEEDED, size_t *max UNNEEDED)
 { fprintf(stderr, "fromwire_fail called!\n"); abort(); }
+/* Generated stub for json_add_member */
+void json_add_member(struct json_stream *js UNNEEDED,
+		     const char *fieldname UNNEEDED,
+		     bool quote UNNEEDED,
+		     const char *fmt UNNEEDED, ...)
+{ fprintf(stderr, "json_add_member called!\n"); abort(); }
+/* Generated stub for json_array_end */
+void json_array_end(struct json_stream *js UNNEEDED)
+{ fprintf(stderr, "json_array_end called!\n"); abort(); }
+/* 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_member_direct */
+char *json_member_direct(struct json_stream *js UNNEEDED,
+			 const char *fieldname UNNEEDED, size_t extra UNNEEDED)
+{ fprintf(stderr, "json_member_direct called!\n"); abort(); }
+/* Generated stub for json_object_end */
+void json_object_end(struct json_stream *js UNNEEDED)
+{ fprintf(stderr, "json_object_end called!\n"); abort(); }
+/* Generated stub for json_object_start */
+void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED)
+{ fprintf(stderr, "json_object_start called!\n"); abort(); }
+/* Generated stub for type_to_string_ */
+const char *type_to_string_(const tal_t *ctx UNNEEDED, const char *typename UNNEEDED,
+			    union printable_types u UNNEEDED)
+{ fprintf(stderr, "type_to_string_ called!\n"); abort(); }
 /* AUTOGENERATED MOCKS END */
 
 struct json {
diff --git a/common/test/run-param.c b/common/test/run-param.c
index 93872d1f1..f55d87f66 100644
--- a/common/test/run-param.c
+++ b/common/test/run-param.c
@@ -1,6 +1,4 @@
 #include "config.h"
-#include "../amount.c"
-#include "../json.c"
 #include "../json_tok.c"
 #include "../param.c"
 #include <ccan/array_size/array_size.h>
@@ -40,9 +38,6 @@ struct command_result *command_fail(struct command *cmd,
 }
 
 /* AUTOGENERATED MOCKS START */
-/* Generated stub for fromwire_fail */
-const void *fromwire_fail(const u8 **cursor UNNEEDED, size_t *max UNNEEDED)
-{ fprintf(stderr, "fromwire_fail called!\n"); abort(); }
 /* Generated stub for json_to_channel_id */
 bool json_to_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED,
 			struct channel_id *cid UNNEEDED)
diff --git a/devtools/Makefile b/devtools/Makefile
index 1f9b3df91..7a8bbb22c 100644
--- a/devtools/Makefile
+++ b/devtools/Makefile
@@ -28,6 +28,7 @@ DEVTOOLS_COMMON_OBJS :=				\
 	common/pseudorand.o			\
 	common/json.o				\
 	common/json_helpers.o			\
+	common/json_stream.o			\
 	common/type_to_string.o			\
 	common/utils.o				\
 	common/version.o			\
diff --git a/gossipd/test/run-bench-find_route.c b/gossipd/test/run-bench-find_route.c
index e21b9b802..737fb573a 100644
--- a/gossipd/test/run-bench-find_route.c
+++ b/gossipd/test/run-bench-find_route.c
@@ -33,18 +33,43 @@ bool cupdate_different(struct gossip_store *gs UNNEEDED,
 		       const struct half_chan *hc UNNEEDED,
 		       const u8 *cupdate UNNEEDED)
 { fprintf(stderr, "cupdate_different called!\n"); abort(); }
+/* Generated stub for fmt_wireaddr_without_port */
+char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED)
+{ fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); }
+/* Generated stub for fromwire_gossipd_local_add_channel */
+bool fromwire_gossipd_local_add_channel(const void *p UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, struct node_id *remote_node_id UNNEEDED, struct amount_sat *satoshis UNNEEDED)
+{ fprintf(stderr, "fromwire_gossipd_local_add_channel called!\n"); abort(); }
 /* Generated stub for fromwire_gossip_store_channel_amount */
 bool fromwire_gossip_store_channel_amount(const void *p UNNEEDED, struct amount_sat *satoshis UNNEEDED)
 { fprintf(stderr, "fromwire_gossip_store_channel_amount called!\n"); abort(); }
 /* Generated stub for fromwire_gossip_store_private_update */
 bool fromwire_gossip_store_private_update(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u8 **update UNNEEDED)
 { fprintf(stderr, "fromwire_gossip_store_private_update called!\n"); abort(); }
-/* Generated stub for fromwire_gossipd_local_add_channel */
-bool fromwire_gossipd_local_add_channel(const void *p UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, struct node_id *remote_node_id UNNEEDED, struct amount_sat *satoshis UNNEEDED)
-{ fprintf(stderr, "fromwire_gossipd_local_add_channel called!\n"); abort(); }
 /* Generated stub for fromwire_wireaddr */
 bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED)
 { fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); }
+/* Generated stub for json_add_member */
+void json_add_member(struct json_stream *js UNNEEDED,
+		     const char *fieldname UNNEEDED,
+		     bool quote UNNEEDED,
+		     const char *fmt UNNEEDED, ...)
+{ fprintf(stderr, "json_add_member called!\n"); abort(); }
+/* Generated stub for json_array_end */
+void json_array_end(struct json_stream *js UNNEEDED)
+{ fprintf(stderr, "json_array_end called!\n"); abort(); }
+/* 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_member_direct */
+char *json_member_direct(struct json_stream *js UNNEEDED,
+			 const char *fieldname UNNEEDED, size_t extra UNNEEDED)
+{ fprintf(stderr, "json_member_direct called!\n"); abort(); }
+/* Generated stub for json_object_end */
+void json_object_end(struct json_stream *js UNNEEDED)
+{ fprintf(stderr, "json_object_end called!\n"); abort(); }
+/* Generated stub for json_object_start */
+void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED)
+{ fprintf(stderr, "json_object_start called!\n"); abort(); }
 /* Generated stub for memleak_add_helper_ */
 void memleak_add_helper_(const tal_t *p UNNEEDED, void (*cb)(struct htable *memtable UNNEEDED,
 						    const tal_t *)){ }
diff --git a/gossipd/test/run-crc32_of_update.c b/gossipd/test/run-crc32_of_update.c
index 5cc84938c..507edf284 100644
--- a/gossipd/test/run-crc32_of_update.c
+++ b/gossipd/test/run-crc32_of_update.c
@@ -27,6 +27,9 @@ struct short_channel_id *decode_short_ids(const tal_t *ctx UNNEEDED, const u8 *e
 /* Generated stub for find_peer */
 struct peer *find_peer(struct daemon *daemon UNNEEDED, const struct node_id *id UNNEEDED)
 { fprintf(stderr, "find_peer called!\n"); abort(); }
+/* Generated stub for fmt_wireaddr_without_port */
+char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED)
+{ fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); }
 /* Generated stub for fromwire_gossip_dev_set_max_scids_encode_size */
 bool fromwire_gossip_dev_set_max_scids_encode_size(const void *p UNNEEDED, u32 *max UNNEEDED)
 { fprintf(stderr, "fromwire_gossip_dev_set_max_scids_encode_size called!\n"); abort(); }
@@ -60,6 +63,28 @@ u8 *handle_channel_update(struct routing_state *rstate UNNEEDED, const u8 *updat
 u8 *handle_node_announcement(struct routing_state *rstate UNNEEDED, const u8 *node UNNEEDED,
 			     struct peer *peer UNNEEDED, bool *was_unknown UNNEEDED)
 { fprintf(stderr, "handle_node_announcement called!\n"); abort(); }
+/* Generated stub for json_add_member */
+void json_add_member(struct json_stream *js UNNEEDED,
+		     const char *fieldname UNNEEDED,
+		     bool quote UNNEEDED,
+		     const char *fmt UNNEEDED, ...)
+{ fprintf(stderr, "json_add_member called!\n"); abort(); }
+/* Generated stub for json_array_end */
+void json_array_end(struct json_stream *js UNNEEDED)
+{ fprintf(stderr, "json_array_end called!\n"); abort(); }
+/* 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_member_direct */
+char *json_member_direct(struct json_stream *js UNNEEDED,
+			 const char *fieldname UNNEEDED, size_t extra UNNEEDED)
+{ fprintf(stderr, "json_member_direct called!\n"); abort(); }
+/* Generated stub for json_object_end */
+void json_object_end(struct json_stream *js UNNEEDED)
+{ fprintf(stderr, "json_object_end called!\n"); abort(); }
+/* Generated stub for json_object_start */
+void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED)
+{ fprintf(stderr, "json_object_start called!\n"); abort(); }
 /* Generated stub for master_badmsg */
 void master_badmsg(u32 type_expected UNNEEDED, const u8 *msg)
 { fprintf(stderr, "master_badmsg called!\n"); abort(); }
diff --git a/gossipd/test/run-extended-info.c b/gossipd/test/run-extended-info.c
index d5efd9e8d..b8035df21 100644
--- a/gossipd/test/run-extended-info.c
+++ b/gossipd/test/run-extended-info.c
@@ -31,6 +31,9 @@ bigsize_t *decode_scid_query_flags(const tal_t *ctx UNNEEDED,
 /* Generated stub for decode_short_ids */
 struct short_channel_id *decode_short_ids(const tal_t *ctx UNNEEDED, const u8 *encoded UNNEEDED)
 { fprintf(stderr, "decode_short_ids called!\n"); abort(); }
+/* Generated stub for fmt_wireaddr_without_port */
+char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED)
+{ fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); }
 /* Generated stub for fromwire_gossip_dev_set_max_scids_encode_size */
 bool fromwire_gossip_dev_set_max_scids_encode_size(const void *p UNNEEDED, u32 *max UNNEEDED)
 { fprintf(stderr, "fromwire_gossip_dev_set_max_scids_encode_size called!\n"); abort(); }
@@ -48,6 +51,28 @@ const u8 *gossip_store_get(const tal_t *ctx UNNEEDED,
 			   struct gossip_store *gs UNNEEDED,
 			   u64 offset UNNEEDED)
 { fprintf(stderr, "gossip_store_get called!\n"); abort(); }
+/* Generated stub for json_add_member */
+void json_add_member(struct json_stream *js UNNEEDED,
+		     const char *fieldname UNNEEDED,
+		     bool quote UNNEEDED,
+		     const char *fmt UNNEEDED, ...)
+{ fprintf(stderr, "json_add_member called!\n"); abort(); }
+/* Generated stub for json_array_end */
+void json_array_end(struct json_stream *js UNNEEDED)
+{ fprintf(stderr, "json_array_end called!\n"); abort(); }
+/* 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_member_direct */
+char *json_member_direct(struct json_stream *js UNNEEDED,
+			 const char *fieldname UNNEEDED, size_t extra UNNEEDED)
+{ fprintf(stderr, "json_member_direct called!\n"); abort(); }
+/* Generated stub for json_object_end */
+void json_object_end(struct json_stream *js UNNEEDED)
+{ fprintf(stderr, "json_object_end called!\n"); abort(); }
+/* Generated stub for json_object_start */
+void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED)
+{ fprintf(stderr, "json_object_start called!\n"); abort(); }
 /* Generated stub for master_badmsg */
 void master_badmsg(u32 type_expected UNNEEDED, const u8 *msg)
 { fprintf(stderr, "master_badmsg called!\n"); abort(); }
diff --git a/gossipd/test/run-find_route-specific.c b/gossipd/test/run-find_route-specific.c
index 540e732c4..5cef77046 100644
--- a/gossipd/test/run-find_route-specific.c
+++ b/gossipd/test/run-find_route-specific.c
@@ -20,18 +20,43 @@ bool cupdate_different(struct gossip_store *gs UNNEEDED,
 		       const struct half_chan *hc UNNEEDED,
 		       const u8 *cupdate UNNEEDED)
 { fprintf(stderr, "cupdate_different called!\n"); abort(); }
+/* Generated stub for fmt_wireaddr_without_port */
+char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED)
+{ fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); }
+/* Generated stub for fromwire_gossipd_local_add_channel */
+bool fromwire_gossipd_local_add_channel(const void *p UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, struct node_id *remote_node_id UNNEEDED, struct amount_sat *satoshis UNNEEDED)
+{ fprintf(stderr, "fromwire_gossipd_local_add_channel called!\n"); abort(); }
 /* Generated stub for fromwire_gossip_store_channel_amount */
 bool fromwire_gossip_store_channel_amount(const void *p UNNEEDED, struct amount_sat *satoshis UNNEEDED)
 { fprintf(stderr, "fromwire_gossip_store_channel_amount called!\n"); abort(); }
 /* Generated stub for fromwire_gossip_store_private_update */
 bool fromwire_gossip_store_private_update(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u8 **update UNNEEDED)
 { fprintf(stderr, "fromwire_gossip_store_private_update called!\n"); abort(); }
-/* Generated stub for fromwire_gossipd_local_add_channel */
-bool fromwire_gossipd_local_add_channel(const void *p UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, struct node_id *remote_node_id UNNEEDED, struct amount_sat *satoshis UNNEEDED)
-{ fprintf(stderr, "fromwire_gossipd_local_add_channel called!\n"); abort(); }
 /* Generated stub for fromwire_wireaddr */
 bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED)
 { fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); }
+/* Generated stub for json_add_member */
+void json_add_member(struct json_stream *js UNNEEDED,
+		     const char *fieldname UNNEEDED,
+		     bool quote UNNEEDED,
+		     const char *fmt UNNEEDED, ...)
+{ fprintf(stderr, "json_add_member called!\n"); abort(); }
+/* Generated stub for json_array_end */
+void json_array_end(struct json_stream *js UNNEEDED)
+{ fprintf(stderr, "json_array_end called!\n"); abort(); }
+/* 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_member_direct */
+char *json_member_direct(struct json_stream *js UNNEEDED,
+			 const char *fieldname UNNEEDED, size_t extra UNNEEDED)
+{ fprintf(stderr, "json_member_direct called!\n"); abort(); }
+/* Generated stub for json_object_end */
+void json_object_end(struct json_stream *js UNNEEDED)
+{ fprintf(stderr, "json_object_end called!\n"); abort(); }
+/* Generated stub for json_object_start */
+void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED)
+{ fprintf(stderr, "json_object_start called!\n"); abort(); }
 /* Generated stub for memleak_add_helper_ */
 void memleak_add_helper_(const tal_t *p UNNEEDED, void (*cb)(struct htable *memtable UNNEEDED,
 						    const tal_t *)){ }
diff --git a/gossipd/test/run-find_route.c b/gossipd/test/run-find_route.c
index 0a91c3ca6..915901fd9 100644
--- a/gossipd/test/run-find_route.c
+++ b/gossipd/test/run-find_route.c
@@ -20,18 +20,43 @@ bool cupdate_different(struct gossip_store *gs UNNEEDED,
 		       const struct half_chan *hc UNNEEDED,
 		       const u8 *cupdate UNNEEDED)
 { fprintf(stderr, "cupdate_different called!\n"); abort(); }
+/* Generated stub for fmt_wireaddr_without_port */
+char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED)
+{ fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); }
+/* Generated stub for fromwire_gossipd_local_add_channel */
+bool fromwire_gossipd_local_add_channel(const void *p UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, struct node_id *remote_node_id UNNEEDED, struct amount_sat *satoshis UNNEEDED)
+{ fprintf(stderr, "fromwire_gossipd_local_add_channel called!\n"); abort(); }
 /* Generated stub for fromwire_gossip_store_channel_amount */
 bool fromwire_gossip_store_channel_amount(const void *p UNNEEDED, struct amount_sat *satoshis UNNEEDED)
 { fprintf(stderr, "fromwire_gossip_store_channel_amount called!\n"); abort(); }
 /* Generated stub for fromwire_gossip_store_private_update */
 bool fromwire_gossip_store_private_update(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u8 **update UNNEEDED)
 { fprintf(stderr, "fromwire_gossip_store_private_update called!\n"); abort(); }
-/* Generated stub for fromwire_gossipd_local_add_channel */
-bool fromwire_gossipd_local_add_channel(const void *p UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, struct node_id *remote_node_id UNNEEDED, struct amount_sat *satoshis UNNEEDED)
-{ fprintf(stderr, "fromwire_gossipd_local_add_channel called!\n"); abort(); }
 /* Generated stub for fromwire_wireaddr */
 bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED)
 { fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); }
+/* Generated stub for json_add_member */
+void json_add_member(struct json_stream *js UNNEEDED,
+		     const char *fieldname UNNEEDED,
+		     bool quote UNNEEDED,
+		     const char *fmt UNNEEDED, ...)
+{ fprintf(stderr, "json_add_member called!\n"); abort(); }
+/* Generated stub for json_array_end */
+void json_array_end(struct json_stream *js UNNEEDED)
+{ fprintf(stderr, "json_array_end called!\n"); abort(); }
+/* 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_member_direct */
+char *json_member_direct(struct json_stream *js UNNEEDED,
+			 const char *fieldname UNNEEDED, size_t extra UNNEEDED)
+{ fprintf(stderr, "json_member_direct called!\n"); abort(); }
+/* Generated stub for json_object_end */
+void json_object_end(struct json_stream *js UNNEEDED)
+{ fprintf(stderr, "json_object_end called!\n"); abort(); }
+/* Generated stub for json_object_start */
+void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED)
+{ fprintf(stderr, "json_object_start called!\n"); abort(); }
 /* Generated stub for memleak_add_helper_ */
 void memleak_add_helper_(const tal_t *p UNNEEDED, void (*cb)(struct htable *memtable UNNEEDED,
 						    const tal_t *)){ }
diff --git a/gossipd/test/run-next_block_range.c b/gossipd/test/run-next_block_range.c
index 85f7f1bb6..616586e73 100644
--- a/gossipd/test/run-next_block_range.c
+++ b/gossipd/test/run-next_block_range.c
@@ -1,9 +1,35 @@
 #include "../seeker.c"
 #include <ccan/err/err.h>
+#include <common/wireaddr.h>
 #include <stdarg.h>
 #include <stdio.h>
 
 /* AUTOGENERATED MOCKS START */
+/* Generated stub for fmt_wireaddr_without_port */
+char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED)
+{ fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); }
+/* Generated stub for json_add_member */
+void json_add_member(struct json_stream *js UNNEEDED,
+		     const char *fieldname UNNEEDED,
+		     bool quote UNNEEDED,
+		     const char *fmt UNNEEDED, ...)
+{ fprintf(stderr, "json_add_member called!\n"); abort(); }
+/* Generated stub for json_array_end */
+void json_array_end(struct json_stream *js UNNEEDED)
+{ fprintf(stderr, "json_array_end called!\n"); abort(); }
+/* 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_member_direct */
+char *json_member_direct(struct json_stream *js UNNEEDED,
+			 const char *fieldname UNNEEDED, size_t extra UNNEEDED)
+{ fprintf(stderr, "json_member_direct called!\n"); abort(); }
+/* Generated stub for json_object_end */
+void json_object_end(struct json_stream *js UNNEEDED)
+{ fprintf(stderr, "json_object_end called!\n"); abort(); }
+/* Generated stub for json_object_start */
+void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED)
+{ fprintf(stderr, "json_object_start called!\n"); abort(); }
 /* Generated stub for new_reltimer_ */
 struct oneshot *new_reltimer_(struct timers *timers UNNEEDED,
 			      const tal_t *ctx UNNEEDED,
diff --git a/gossipd/test/run-overlong.c b/gossipd/test/run-overlong.c
index 8ac52d35d..0d4e638aa 100644
--- a/gossipd/test/run-overlong.c
+++ b/gossipd/test/run-overlong.c
@@ -20,18 +20,43 @@ bool cupdate_different(struct gossip_store *gs UNNEEDED,
 		       const struct half_chan *hc UNNEEDED,
 		       const u8 *cupdate UNNEEDED)
 { fprintf(stderr, "cupdate_different called!\n"); abort(); }
+/* Generated stub for fmt_wireaddr_without_port */
+char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED)
+{ fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); }
+/* Generated stub for fromwire_gossipd_local_add_channel */
+bool fromwire_gossipd_local_add_channel(const void *p UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, struct node_id *remote_node_id UNNEEDED, struct amount_sat *satoshis UNNEEDED)
+{ fprintf(stderr, "fromwire_gossipd_local_add_channel called!\n"); abort(); }
 /* Generated stub for fromwire_gossip_store_channel_amount */
 bool fromwire_gossip_store_channel_amount(const void *p UNNEEDED, struct amount_sat *satoshis UNNEEDED)
 { fprintf(stderr, "fromwire_gossip_store_channel_amount called!\n"); abort(); }
 /* Generated stub for fromwire_gossip_store_private_update */
 bool fromwire_gossip_store_private_update(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u8 **update UNNEEDED)
 { fprintf(stderr, "fromwire_gossip_store_private_update called!\n"); abort(); }
-/* Generated stub for fromwire_gossipd_local_add_channel */
-bool fromwire_gossipd_local_add_channel(const void *p UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, struct node_id *remote_node_id UNNEEDED, struct amount_sat *satoshis UNNEEDED)
-{ fprintf(stderr, "fromwire_gossipd_local_add_channel called!\n"); abort(); }
 /* Generated stub for fromwire_wireaddr */
 bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED)
 { fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); }
+/* Generated stub for json_add_member */
+void json_add_member(struct json_stream *js UNNEEDED,
+		     const char *fieldname UNNEEDED,
+		     bool quote UNNEEDED,
+		     const char *fmt UNNEEDED, ...)
+{ fprintf(stderr, "json_add_member called!\n"); abort(); }
+/* Generated stub for json_array_end */
+void json_array_end(struct json_stream *js UNNEEDED)
+{ fprintf(stderr, "json_array_end called!\n"); abort(); }
+/* 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_member_direct */
+char *json_member_direct(struct json_stream *js UNNEEDED,
+			 const char *fieldname UNNEEDED, size_t extra UNNEEDED)
+{ fprintf(stderr, "json_member_direct called!\n"); abort(); }
+/* Generated stub for json_object_end */
+void json_object_end(struct json_stream *js UNNEEDED)
+{ fprintf(stderr, "json_object_end called!\n"); abort(); }
+/* Generated stub for json_object_start */
+void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED)
+{ fprintf(stderr, "json_object_start called!\n"); abort(); }
 /* Generated stub for memleak_add_helper_ */
 void memleak_add_helper_(const tal_t *p UNNEEDED, void (*cb)(struct htable *memtable UNNEEDED,
 						    const tal_t *)){ }
diff --git a/gossipd/test/run-txout_failure.c b/gossipd/test/run-txout_failure.c
index 19565593b..66506f8c9 100644
--- a/gossipd/test/run-txout_failure.c
+++ b/gossipd/test/run-txout_failure.c
@@ -8,6 +8,9 @@ bool cupdate_different(struct gossip_store *gs UNNEEDED,
 		       const struct half_chan *hc UNNEEDED,
 		       const u8 *cupdate UNNEEDED)
 { fprintf(stderr, "cupdate_different called!\n"); abort(); }
+/* Generated stub for fmt_wireaddr_without_port */
+char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED)
+{ fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); }
 /* Generated stub for fromwire_gossipd_local_add_channel */
 bool fromwire_gossipd_local_add_channel(const void *p UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, struct node_id *remote_node_id UNNEEDED, struct amount_sat *satoshis UNNEEDED)
 { fprintf(stderr, "fromwire_gossipd_local_add_channel called!\n"); abort(); }
@@ -36,6 +39,28 @@ const u8 *gossip_store_get_private_update(const tal_t *ctx UNNEEDED,
 					  struct gossip_store *gs UNNEEDED,
 					  u64 offset UNNEEDED)
 { fprintf(stderr, "gossip_store_get_private_update called!\n"); abort(); }
+/* Generated stub for json_add_member */
+void json_add_member(struct json_stream *js UNNEEDED,
+		     const char *fieldname UNNEEDED,
+		     bool quote UNNEEDED,
+		     const char *fmt UNNEEDED, ...)
+{ fprintf(stderr, "json_add_member called!\n"); abort(); }
+/* Generated stub for json_array_end */
+void json_array_end(struct json_stream *js UNNEEDED)
+{ fprintf(stderr, "json_array_end called!\n"); abort(); }
+/* 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_member_direct */
+char *json_member_direct(struct json_stream *js UNNEEDED,
+			 const char *fieldname UNNEEDED, size_t extra UNNEEDED)
+{ fprintf(stderr, "json_member_direct called!\n"); abort(); }
+/* Generated stub for json_object_end */
+void json_object_end(struct json_stream *js UNNEEDED)
+{ fprintf(stderr, "json_object_end called!\n"); abort(); }
+/* Generated stub for json_object_start */
+void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED)
+{ fprintf(stderr, "json_object_start called!\n"); abort(); }
 /* Generated stub for memleak_add_helper_ */
 void memleak_add_helper_(const tal_t *p UNNEEDED, void (*cb)(struct htable *memtable UNNEEDED,
 						    const tal_t *)){ }
diff --git a/lightningd/json.c b/lightningd/json.c
index 90c129684..9d1146fcb 100644
--- a/lightningd/json.c
+++ b/lightningd/json.c
@@ -10,50 +10,18 @@
 #include <common/json.h>
 #include <common/json_command.h>
 #include <common/json_helpers.h>
-#include <common/json_stream.h>
 #include <common/jsonrpc_errors.h>
 #include <common/memleak.h>
-#include <common/node_id.h>
 #include <common/param.h>
 #include <common/type_to_string.h>
 #include <common/utils.h>
-#include <common/wallet_tx.h>
-#include <common/wireaddr.h>
 #include <gossipd/routing.h>
 #include <lightningd/chaintopology.h>
-#include <lightningd/json.h>
 #include <lightningd/jsonrpc.h>
 #include <lightningd/options.h>
 #include <sys/socket.h>
-#include <wallet/wallet.h>
 #include <wire/wire.h>
 
-void json_add_node_id(struct json_stream *response,
-		      const char *fieldname,
-		      const struct node_id *id)
-{
-	json_add_hex(response, fieldname, id->k, sizeof(id->k));
-}
-
-void json_add_pubkey(struct json_stream *response,
-		     const char *fieldname,
-		     const struct pubkey *key)
-{
-	u8 der[PUBKEY_CMPR_LEN];
-
-	pubkey_to_der(der, key);
-	json_add_hex(response, fieldname, der, sizeof(der));
-}
-
-void json_add_txid(struct json_stream *result, const char *fieldname,
-		   const struct bitcoin_txid *txid)
-{
-	char hex[hex_str_size(sizeof(*txid))];
-
-	bitcoin_txid_to_hex(txid, hex, sizeof(hex));
-	json_add_string(result, fieldname, hex);
-}
-
 struct command_result *param_pubkey(struct command *cmd, const char *name,
 				    const char *buffer, const jsmntok_t *tok,
 				    struct pubkey **pubkey)
@@ -83,16 +51,6 @@ struct command_result *param_txid(struct command *cmd,
 			    json_tok_full(buffer, tok));
 }
 
-void json_add_short_channel_id(struct json_stream *response,
-			       const char *fieldname,
-			       const struct short_channel_id *scid)
-{
-	json_add_member(response, fieldname, true, "%dx%dx%d",
-			short_channel_id_blocknum(scid),
-			short_channel_id_txnum(scid),
-			short_channel_id_outnum(scid));
-}
-
 struct command_result *param_short_channel_id(struct command *cmd,
 					      const char *name,
 					      const char *buffer,
@@ -202,254 +160,6 @@ json_tok_channel_id(const char *buffer, const jsmntok_t *tok,
 			  cid, sizeof(*cid));
 }
 
-void json_add_address(struct json_stream *response, const char *fieldname,
-		      const struct wireaddr *addr)
-{
-	json_object_start(response, fieldname);
-	char *addrstr = tal_arr(response, char, INET6_ADDRSTRLEN);
-	if (addr->type == ADDR_TYPE_IPV4) {
-		inet_ntop(AF_INET, addr->addr, addrstr, INET_ADDRSTRLEN);
-		json_add_string(response, "type", "ipv4");
-		json_add_string(response, "address", addrstr);
-		json_add_num(response, "port", addr->port);
-	} else if (addr->type == ADDR_TYPE_IPV6) {
-		inet_ntop(AF_INET6, addr->addr, addrstr, INET6_ADDRSTRLEN);
-		json_add_string(response, "type", "ipv6");
-		json_add_string(response, "address", addrstr);
-		json_add_num(response, "port", addr->port);
-	} else if (addr->type == ADDR_TYPE_TOR_V2) {
-		json_add_string(response, "type", "torv2");
-		json_add_string(response, "address", fmt_wireaddr_without_port(tmpctx, addr));
-		json_add_num(response, "port", addr->port);
-	} else if (addr->type == ADDR_TYPE_TOR_V3) {
-		json_add_string(response, "type", "torv3");
-		json_add_string(response, "address", fmt_wireaddr_without_port(tmpctx, addr));
-		json_add_num(response, "port", addr->port);
-	}
-	json_object_end(response);
-}
-
-void json_add_address_internal(struct json_stream *response,
-			       const char *fieldname,
-			       const struct wireaddr_internal *addr)
-{
-	switch (addr->itype) {
-	case ADDR_INTERNAL_SOCKNAME:
-		json_object_start(response, fieldname);
-		json_add_string(response, "type", "local socket");
-		json_add_string(response, "socket", addr->u.sockname);
-		json_object_end(response);
-		return;
-	case ADDR_INTERNAL_ALLPROTO:
-		json_object_start(response, fieldname);
-		json_add_string(response, "type", "any protocol");
-		json_add_num(response, "port", addr->u.port);
-		json_object_end(response);
-		return;
-	case ADDR_INTERNAL_AUTOTOR:
-		json_object_start(response, fieldname);
-		json_add_string(response, "type", "Tor generated address");
-		json_add_address(response, "service", &addr->u.torservice.address);
-		json_object_end(response);
-		return;
-	case ADDR_INTERNAL_STATICTOR:
-		json_object_start(response, fieldname);
-		json_add_string(response, "type", "Tor from blob generated static address");
-		json_add_address(response, "service", &addr->u.torservice.address);
-		json_object_end(response);
-		return;
-	case ADDR_INTERNAL_FORPROXY:
-		json_object_start(response, fieldname);
-		json_add_string(response, "type", "unresolved");
-		json_add_string(response, "name", addr->u.unresolved.name);
-		json_add_num(response, "port", addr->u.unresolved.port);
-		json_object_end(response);
-		return;
-	case ADDR_INTERNAL_WIREADDR:
-		json_add_address(response, fieldname, &addr->u.wireaddr);
-		return;
-	}
-	abort();
-}
-
-void json_add_num(struct json_stream *result, const char *fieldname, unsigned int value)
-{
-	json_add_member(result, fieldname, false, "%u", value);
-}
-
-void json_add_double(struct json_stream *result, const char *fieldname, double value)
-{
-	json_add_member(result, fieldname, false, "%f", value);
-}
-
-void json_add_u64(struct json_stream *result, const char *fieldname,
-		  uint64_t value)
-{
-	json_add_member(result, fieldname, false, "%"PRIu64, value);
-}
-
-void json_add_s64(struct json_stream *result, const char *fieldname,
-		  int64_t value)
-{
-	json_add_member(result, fieldname, false, "%"PRIi64, value);
-}
-
-void json_add_u32(struct json_stream *result, const char *fieldname,
-		  uint32_t value)
-{
-	json_add_member(result, fieldname, false, "%u", value);
-}
-
-void json_add_s32(struct json_stream *result, const char *fieldname,
-		  int32_t value)
-{
-	json_add_member(result, fieldname, false, "%d", value);
-}
-
-void json_add_literal(struct json_stream *result, const char *fieldname,
-		      const char *literal, int len)
-{
-	/* Literal may contain quotes, so bypass normal checks */
-	char *dest = json_member_direct(result, fieldname, strlen(literal));
-	if (dest)
-		memcpy(dest, literal, strlen(literal));
-}
-
-void json_add_string(struct json_stream *result, const char *fieldname, const char *value TAKES)
-{
-	json_add_member(result, fieldname, true, "%s", value);
-	if (taken(value))
-		tal_free(value);
-}
-
-void json_add_bool(struct json_stream *result, const char *fieldname, bool value)
-{
-	json_add_member(result, fieldname, false, value ? "true" : "false");
-}
-
-void json_add_null(struct json_stream *stream, const char *fieldname)
-{
-	json_add_member(stream, fieldname, false, "null");
-}
-
-void json_add_hex(struct json_stream *js, const char *fieldname,
-		  const void *data, size_t len)
-{
-	/* Size without NUL term */
-	size_t hexlen = hex_str_size(len) - 1;
-	char *dest;
-
-	dest = json_member_direct(js, fieldname, 1 + hexlen + 1);
-	if (dest) {
-		dest[0] = '"';
-		if (!hex_encode(data, len, dest + 1, hexlen + 1))
-			abort();
-		dest[1+hexlen] = '"';
-	}
-}
-
-void json_add_hex_talarr(struct json_stream *result,
-			 const char *fieldname,
-			 const tal_t *data)
-{
-	json_add_hex(result, fieldname, data, tal_bytelen(data));
-}
-
-void json_add_tx(struct json_stream *result,
-		 const char *fieldname,
-		 const struct bitcoin_tx *tx)
-{
-	json_add_hex_talarr(result, fieldname, linearize_tx(tmpctx, tx));
-}
-
-void json_add_escaped_string(struct json_stream *result, const char *fieldname,
-			     const struct json_escape *esc TAKES)
-{
-	/* Already escaped, don't re-escape! */
-	char *dest = json_member_direct(result,	fieldname,
-					1 + strlen(esc->s) + 1);
-
-	if (dest) {
-		dest[0] = '"';
-		memcpy(dest + 1, esc->s, strlen(esc->s));
-		dest[1+strlen(esc->s)] = '"';
-	}
-	if (taken(esc))
-		tal_free(esc);
-}
-
-void json_add_amount_msat_compat(struct json_stream *result,
-				 struct amount_msat msat,
-				 const char *rawfieldname,
-				 const char *msatfieldname)
-{
-	json_add_u64(result, rawfieldname, msat.millisatoshis); /* Raw: low-level helper */
-	json_add_amount_msat_only(result, msatfieldname, msat);
-}
-
-void json_add_amount_msat_only(struct json_stream *result,
-			  const char *msatfieldname,
-			  struct amount_msat msat)
-{
-	json_add_string(result, msatfieldname,
-			type_to_string(tmpctx, struct amount_msat, &msat));
-}
-
-void json_add_amount_sat_compat(struct json_stream *result,
-				struct amount_sat sat,
-				const char *rawfieldname,
-				const char *msatfieldname)
-{
-	json_add_u64(result, rawfieldname, sat.satoshis); /* Raw: low-level helper */
-	json_add_amount_sat_only(result, msatfieldname, sat);
-}
-
-void json_add_amount_sat_only(struct json_stream *result,
-			 const char *msatfieldname,
-			 struct amount_sat sat)
-{
-	struct amount_msat msat;
-	if (amount_sat_to_msat(&msat, sat))
-		json_add_string(result, msatfieldname,
-				type_to_string(tmpctx, struct amount_msat, &msat));
-}
-
-void json_add_timeabs(struct json_stream *result, const char *fieldname,
-		      struct timeabs t)
-{
-	json_add_member(result, fieldname, false, "%" PRIu64 ".%03" PRIu64,
-			(u64)t.ts.tv_sec, (u64)t.ts.tv_nsec / 1000000);
-}
-
-void json_add_time(struct json_stream *result, const char *fieldname,
-			  struct timespec ts)
-{
-	char timebuf[100];
-
-	snprintf(timebuf, sizeof(timebuf), "%lu.%09u",
-		(unsigned long)ts.tv_sec,
-		(unsigned)ts.tv_nsec);
-	json_add_string(result, fieldname, timebuf);
-}
-
-void json_add_secret(struct json_stream *response, const char *fieldname,
-		     const struct secret *secret)
-{
-	json_add_hex(response, fieldname, secret, sizeof(struct secret));
-}
-
-void json_add_sha256(struct json_stream *result, const char *fieldname,
-		     const struct sha256 *hash)
-{
-	json_add_hex(result, fieldname, hash, sizeof(*hash));
-}
-
-void json_add_preimage(struct json_stream *result, const char *fieldname,
-		     const struct preimage *preimage)
-{
-	json_add_hex(result, fieldname, preimage, sizeof(*preimage));
-}
-
 /**
  * segwit_addr_net_decode - Try to decode a Bech32 address and detect
  * testnet/mainnet/regtest/signet
@@ -582,46 +292,3 @@ struct command_result *param_bitcoin_address(struct command *cmd,
 	}
 	abort();
 }
-
-void json_add_tok(struct json_stream *result, const char *fieldname,
-                  const jsmntok_t *tok, const char *buffer)
-{
-	int i = 0;
-	const jsmntok_t *t;
-
-	switch (tok->type) {
-	case JSMN_PRIMITIVE:
-		if (json_tok_is_num(buffer, tok)) {
-			json_to_int(buffer, tok, &i);
-			json_add_num(result, fieldname, i);
-		}
-		return;
-
-	case JSMN_STRING:
-		if (json_tok_streq(buffer, tok, "true"))
-			json_add_bool(result, fieldname, true);
-		else if (json_tok_streq(buffer, tok, "false"))
-			json_add_bool(result, fieldname, false);
-		else
-			json_add_string(result, fieldname, json_strdup(tmpctx, buffer, tok));
-		return;
-
-	case JSMN_ARRAY:
-		json_array_start(result, fieldname);
-		json_for_each_arr(i, t, tok)
-			json_add_tok(result, NULL, t, buffer);
-		json_array_end(result);
-		return;
-
-	case JSMN_OBJECT:
-		json_object_start(result, fieldname);
-		json_for_each_obj(i, t, tok)
-			json_add_tok(result, json_strdup(tmpctx, buffer, t), t+1, buffer);
-		json_object_end(result);
-		return;
-
-	case JSMN_UNDEFINED:
-		break;
-	}
-	abort();
-}
diff --git a/lightningd/json.h b/lightningd/json.h
index da64c384a..5055c3c06 100644
--- a/lightningd/json.h
+++ b/lightningd/json.h
@@ -8,8 +8,6 @@
 #include <bitcoin/privkey.h>
 #include <ccan/short_types/short_types.h>
 #include <ccan/tal/tal.h>
-#include <ccan/time/time.h>
-#include <common/amount.h>
 #include <stdbool.h>
 #include <stddef.h>
 #include <stdint.h>
@@ -17,41 +15,14 @@
 #define JSMN_STRICT 1
 # include <external/jsmn/jsmn.h>
 
-struct bitcoin_tx;
 struct bitcoin_txid;
 struct chainparams;
 struct channel_id;
 struct command;
 struct json_escape;
-struct json_stream;
 struct pubkey;
 struct node_id;
-struct sha256;
-struct preimage;
 struct short_channel_id;
-struct wallet_payment;
-struct wallet_tx;
-struct wireaddr;
-struct wireaddr_internal;
-
-/* '"fieldname" : "0289abcdef..."' or "0289abcdef..." if fieldname is NULL */
-void json_add_pubkey(struct json_stream *response,
-		     const char *fieldname,
-		     const struct pubkey *key);
-
-/* '"fieldname" : "89abcdef..."' or "89abcdef..." if fieldname is NULL */
-void json_add_secret(struct json_stream *response,
-		     const char *fieldname,
-		     const struct secret *secret);
-
-/* '"fieldname" : "0289abcdef..."' or "0289abcdef..." if fieldname is NULL */
-void json_add_node_id(struct json_stream *response,
-				const char *fieldname,
-				const struct node_id *id);
-
-/* '"fieldname" : <hexrev>' or "<hexrev>" if fieldname is NULL */
-void json_add_txid(struct json_stream *result, const char *fieldname,
-		   const struct bitcoin_txid *txid);
 
 struct command_result *param_pubkey(struct command *cmd, const char *name,
 				    const char *buffer, const jsmntok_t *tok,
@@ -86,101 +57,9 @@ struct command_result *param_feerate(struct command *cmd, const char *name,
 				     const char *buffer, const jsmntok_t *tok,
 				     u32 **feerate);
 
-/* '"fieldname" : "1234:5:6"' */
-void json_add_short_channel_id(struct json_stream *response,
-			       const char *fieldname,
-			       const struct short_channel_id *id);
-
 bool json_tok_channel_id(const char *buffer, const jsmntok_t *tok,
 			 struct channel_id *cid);
 
-/* JSON serialize a network address for a node */
-void json_add_address(struct json_stream *response, const char *fieldname,
-		      const struct wireaddr *addr);
-
-/* JSON serialize a network address for a node. */
-void json_add_address_internal(struct json_stream *response,
-			       const char *fieldname,
-			       const struct wireaddr_internal *addr);
-
-
-/* '"fieldname" : "value"' or '"value"' if fieldname is NULL.  Turns
- * any non-printable chars into JSON escapes, but leaves existing escapes alone.
- */
-void json_add_string(struct json_stream *result, const char *fieldname, const char *value);
-
-/* '"fieldname" : "value"' or '"value"' if fieldname is NULL.  String must
- * already be JSON escaped as necessary. */
-void json_add_escaped_string(struct json_stream *result,
-			     const char *fieldname,
-			     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,
-		      const char *literal, int len);
-/* '"fieldname" : value' or 'value' if fieldname is NULL */
-void json_add_double(struct json_stream *result, const char *fieldname,
-		     double value);
-/* '"fieldname" : value' or 'value' if fieldname is NULL */
-void json_add_num(struct json_stream *result, const char *fieldname,
-		  unsigned int value);
-/* '"fieldname" : value' or 'value' if fieldname is NULL */
-void json_add_u64(struct json_stream *result, const char *fieldname,
-		  uint64_t value);
-/* '"fieldname" : value' or 'value' if fieldname is NULL */
-void json_add_s64(struct json_stream *result, const char *fieldname,
-		  int64_t value);
-/* '"fieldname" : value' or 'value' if fieldname is NULL */
-void json_add_u32(struct json_stream *result, const char *fieldname,
-		  uint32_t value);
-/* '"fieldname" : value' or 'value' if fieldname is NULL */
-void json_add_s32(struct json_stream *result, const char *fieldname,
-		  int32_t value);
-/* '"fieldname" : true|false' or 'true|false' if fieldname is NULL */
-void json_add_bool(struct json_stream *result, const char *fieldname,
-		   bool value);
-
-/* '"fieldname" : null' or 'null' if fieldname is NULL */
-void json_add_null(struct json_stream *stream, const char *fieldname);
-
-/* '"fieldname" : "0189abcdef..."' or "0189abcdef..." if fieldname is NULL */
-void json_add_hex(struct json_stream *result, const char *fieldname,
-		  const void *data, size_t len);
-/* '"fieldname" : "0189abcdef..."' or "0189abcdef..." if fieldname is NULL */
-void json_add_hex_talarr(struct json_stream *result,
-			 const char *fieldname,
-			 const tal_t *data);
-/* '"fieldname" : "010000000001..."' or "010000000001..." if fieldname is NULL */
-void json_add_tx(struct json_stream *result,
-		 const char *fieldname,
-		 const struct bitcoin_tx *tx);
-
-/* Adds both a 'raw' number field and an 'amount_msat' field */
-void json_add_amount_msat_compat(struct json_stream *result,
-				 struct amount_msat msat,
-				 const char *rawfieldname,
-				 const char *msatfieldname)
-	NO_NULL_ARGS;
-
-/* Adds both a 'raw' number field and an 'amount_msat' field */
-void json_add_amount_sat_compat(struct json_stream *result,
-				struct amount_sat sat,
-				const char *rawfieldname,
-				const char *msatfieldname)
-	NO_NULL_ARGS;
-
-/* Adds an 'msat' field */
-void json_add_amount_msat_only(struct json_stream *result,
-			  const char *msatfieldname,
-			  struct amount_msat msat)
-	NO_NULL_ARGS;
-
-/* Adds an 'msat' field */
-void json_add_amount_sat_only(struct json_stream *result,
-			 const char *msatfieldname,
-			 struct amount_sat sat)
-	NO_NULL_ARGS;
-
 enum address_parse_result {
 	/* Not recognized as an onchain address */
 	ADDRESS_PARSE_UNRECOGNIZED,
@@ -197,27 +76,10 @@ enum address_parse_result json_to_address_scriptpubkey(const tal_t *ctx,
 			     const char *buffer,
 			     const jsmntok_t *tok, const u8 **scriptpubkey);
 
-void json_add_timeabs(struct json_stream *result, const char *fieldname,
-		      struct timeabs t);
-
-/* used in log.c and notification.c*/
-void json_add_time(struct json_stream *result, const char *fieldname,
-			  struct timespec ts);
-
-void json_add_sha256(struct json_stream *result, const char *fieldname,
-		     const struct sha256 *hash);
-
-void json_add_preimage(struct json_stream *result, const char *fieldname,
-		     const struct preimage *preimage);
-
 struct command_result *param_bitcoin_address(struct command *cmd,
 					     const char *name,
 					     const char *buffer,
 					     const jsmntok_t *tok,
 					     const u8 **scriptpubkey);
 
-/* Add any json token */
-void json_add_tok(struct json_stream *result, const char *fieldname,
-                  const jsmntok_t *tok, const char *buffer);
-
 #endif /* LIGHTNING_LIGHTNINGD_JSON_H */
diff --git a/lightningd/test/run-find_my_abspath.c b/lightningd/test/run-find_my_abspath.c
index d80f32d57..4425befbc 100644
--- a/lightningd/test/run-find_my_abspath.c
+++ b/lightningd/test/run-find_my_abspath.c
@@ -100,6 +100,28 @@ void htlcs_notify_new_block(struct lightningd *ld UNNEEDED, u32 height UNNEEDED)
 void htlcs_resubmit(struct lightningd *ld UNNEEDED,
 		    struct htlc_in_map *unconnected_htlcs_in UNNEEDED)
 { fprintf(stderr, "htlcs_resubmit called!\n"); abort(); }
+/* Generated stub for json_add_member */
+void json_add_member(struct json_stream *js UNNEEDED,
+		     const char *fieldname UNNEEDED,
+		     bool quote UNNEEDED,
+		     const char *fmt UNNEEDED, ...)
+{ fprintf(stderr, "json_add_member called!\n"); abort(); }
+/* Generated stub for json_array_end */
+void json_array_end(struct json_stream *js UNNEEDED)
+{ fprintf(stderr, "json_array_end called!\n"); abort(); }
+/* 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_member_direct */
+char *json_member_direct(struct json_stream *js UNNEEDED,
+			 const char *fieldname UNNEEDED, size_t extra UNNEEDED)
+{ fprintf(stderr, "json_member_direct called!\n"); abort(); }
+/* Generated stub for json_object_end */
+void json_object_end(struct json_stream *js UNNEEDED)
+{ fprintf(stderr, "json_object_end called!\n"); abort(); }
+/* Generated stub for json_object_start */
+void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED)
+{ fprintf(stderr, "json_object_start called!\n"); abort(); }
 /* Generated stub for jsonrpc_listen */
 void jsonrpc_listen(struct jsonrpc *rpc UNNEEDED, struct lightningd *ld UNNEEDED)
 { fprintf(stderr, "jsonrpc_listen called!\n"); abort(); }
diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c
index b3646647d..0f2702f84 100644
--- a/lightningd/test/run-invoice-select-inchan.c
+++ b/lightningd/test/run-invoice-select-inchan.c
@@ -101,6 +101,9 @@ bool feature_is_set(const u8 *features UNNEEDED, size_t bit UNNEEDED)
 /* Generated stub for fixup_htlcs_out */
 void fixup_htlcs_out(struct lightningd *ld UNNEEDED)
 { fprintf(stderr, "fixup_htlcs_out called!\n"); abort(); }
+/* Generated stub for fmt_wireaddr_without_port */
+char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED)
+{ fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); }
 /* Generated stub for fromwire_channel_dev_memleak_reply */
 bool fromwire_channel_dev_memleak_reply(const void *p UNNEEDED, bool *leak UNNEEDED)
 { fprintf(stderr, "fromwire_channel_dev_memleak_reply called!\n"); abort(); }
@@ -146,92 +149,18 @@ void htlc_set_fail(struct htlc_set *set UNNEEDED, enum onion_type failcode UNNEE
 /* Generated stub for htlc_set_fulfill */
 void htlc_set_fulfill(struct htlc_set *set UNNEEDED, const struct preimage *preimage UNNEEDED)
 { fprintf(stderr, "htlc_set_fulfill called!\n"); abort(); }
-/* Generated stub for json_add_address */
-void json_add_address(struct json_stream *response UNNEEDED, const char *fieldname UNNEEDED,
-		      const struct wireaddr *addr UNNEEDED)
-{ fprintf(stderr, "json_add_address called!\n"); abort(); }
-/* Generated stub for json_add_address_internal */
-void json_add_address_internal(struct json_stream *response UNNEEDED,
-			       const char *fieldname UNNEEDED,
-			       const struct wireaddr_internal *addr UNNEEDED)
-{ fprintf(stderr, "json_add_address_internal called!\n"); abort(); }
-/* Generated stub for json_add_amount_msat_compat */
-void json_add_amount_msat_compat(struct json_stream *result UNNEEDED,
-				 struct amount_msat msat UNNEEDED,
-				 const char *rawfieldname UNNEEDED,
-				 const char *msatfieldname)
-
-{ fprintf(stderr, "json_add_amount_msat_compat called!\n"); abort(); }
-/* Generated stub for json_add_amount_sat_compat */
-void json_add_amount_sat_compat(struct json_stream *result UNNEEDED,
-				struct amount_sat sat UNNEEDED,
-				const char *rawfieldname UNNEEDED,
-				const char *msatfieldname)
-
-{ fprintf(stderr, "json_add_amount_sat_compat called!\n"); abort(); }
-/* Generated stub for json_add_bool */
-void json_add_bool(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED,
-		   bool value UNNEEDED)
-{ fprintf(stderr, "json_add_bool called!\n"); abort(); }
-/* Generated stub for json_add_escaped_string */
-void json_add_escaped_string(struct json_stream *result UNNEEDED,
-			     const char *fieldname UNNEEDED,
-			     const struct json_escape *esc TAKES UNNEEDED)
-{ fprintf(stderr, "json_add_escaped_string called!\n"); abort(); }
-/* Generated stub for json_add_hex_talarr */
-void json_add_hex_talarr(struct json_stream *result UNNEEDED,
-			 const char *fieldname UNNEEDED,
-			 const tal_t *data UNNEEDED)
-{ fprintf(stderr, "json_add_hex_talarr called!\n"); abort(); }
 /* Generated stub for json_add_log */
 void json_add_log(struct json_stream *result UNNEEDED,
 		  const struct log_book *lr UNNEEDED,
 		  const struct node_id *node_id UNNEEDED,
 		  enum log_level minlevel UNNEEDED)
 { fprintf(stderr, "json_add_log called!\n"); abort(); }
-/* Generated stub for json_add_node_id */
-void json_add_node_id(struct json_stream *response UNNEEDED,
-				const char *fieldname UNNEEDED,
-				const struct node_id *id UNNEEDED)
-{ fprintf(stderr, "json_add_node_id called!\n"); abort(); }
-/* Generated stub for json_add_num */
-void json_add_num(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED,
-		  unsigned int value UNNEEDED)
-{ fprintf(stderr, "json_add_num called!\n"); abort(); }
-/* Generated stub for json_add_preimage */
-void json_add_preimage(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED,
-		     const struct preimage *preimage UNNEEDED)
-{ fprintf(stderr, "json_add_preimage called!\n"); abort(); }
-/* Generated stub for json_add_secret */
-void json_add_secret(struct json_stream *response UNNEEDED,
+/* Generated stub for json_add_member */
+void json_add_member(struct json_stream *js UNNEEDED,
 		     const char *fieldname UNNEEDED,
-		     const struct secret *secret UNNEEDED)
-{ fprintf(stderr, "json_add_secret called!\n"); abort(); }
-/* Generated stub for json_add_sha256 */
-void json_add_sha256(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED,
-		     const struct sha256 *hash UNNEEDED)
-{ fprintf(stderr, "json_add_sha256 called!\n"); abort(); }
-/* Generated stub for json_add_short_channel_id */
-void json_add_short_channel_id(struct json_stream *response UNNEEDED,
-			       const char *fieldname UNNEEDED,
-			       const struct short_channel_id *id UNNEEDED)
-{ fprintf(stderr, "json_add_short_channel_id called!\n"); abort(); }
-/* Generated stub for json_add_string */
-void json_add_string(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, const char *value UNNEEDED)
-{ fprintf(stderr, "json_add_string called!\n"); abort(); }
-/* Generated stub for json_add_tx */
-void json_add_tx(struct json_stream *result UNNEEDED,
-		 const char *fieldname UNNEEDED,
-		 const struct bitcoin_tx *tx UNNEEDED)
-{ fprintf(stderr, "json_add_tx called!\n"); abort(); }
-/* Generated stub for json_add_txid */
-void json_add_txid(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED,
-		   const struct bitcoin_txid *txid UNNEEDED)
-{ fprintf(stderr, "json_add_txid called!\n"); abort(); }
-/* Generated stub for json_add_u64 */
-void json_add_u64(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED,
-		  uint64_t value UNNEEDED)
-{ fprintf(stderr, "json_add_u64 called!\n"); abort(); }
+		     bool quote UNNEEDED,
+		     const char *fmt UNNEEDED, ...)
+{ fprintf(stderr, "json_add_member called!\n"); abort(); }
 /* Generated stub for json_add_uncommitted_channel */
 void json_add_uncommitted_channel(struct json_stream *response UNNEEDED,
 				  const struct uncommitted_channel *uc UNNEEDED)
@@ -242,6 +171,10 @@ 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_member_direct */
+char *json_member_direct(struct json_stream *js UNNEEDED,
+			 const char *fieldname UNNEEDED, size_t extra UNNEEDED)
+{ fprintf(stderr, "json_member_direct called!\n"); abort(); }
 /* Generated stub for json_object_end */
 void json_object_end(struct json_stream *js UNNEEDED)
 { fprintf(stderr, "json_object_end called!\n"); abort(); }
diff --git a/lightningd/test/run-log-pruning.c b/lightningd/test/run-log-pruning.c
index 1a99d4f12..c9b4cb3da 100644
--- a/lightningd/test/run-log-pruning.c
+++ b/lightningd/test/run-log-pruning.c
@@ -1,4 +1,5 @@
 #include "../log.c"
+#include <common/wireaddr.h>
 
 /* AUTOGENERATED MOCKS START */
 /* Generated stub for bigsize_get */
@@ -21,39 +22,25 @@ struct command_result *command_success(struct command *cmd UNNEEDED,
 				       struct json_stream *response)
 
 { fprintf(stderr, "command_success called!\n"); abort(); }
-/* Generated stub for json_add_hex_talarr */
-void json_add_hex_talarr(struct json_stream *result UNNEEDED,
-			 const char *fieldname UNNEEDED,
-			 const tal_t *data UNNEEDED)
-{ fprintf(stderr, "json_add_hex_talarr called!\n"); abort(); }
+/* Generated stub for fmt_wireaddr_without_port */
+char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED)
+{ fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); }
 /* Generated stub for json_add_member */
 void json_add_member(struct json_stream *js UNNEEDED,
 		     const char *fieldname UNNEEDED,
 		     bool quote UNNEEDED,
 		     const char *fmt UNNEEDED, ...)
 { fprintf(stderr, "json_add_member called!\n"); abort(); }
-/* Generated stub for json_add_node_id */
-void json_add_node_id(struct json_stream *response UNNEEDED,
-				const char *fieldname UNNEEDED,
-				const struct node_id *id UNNEEDED)
-{ fprintf(stderr, "json_add_node_id called!\n"); abort(); }
-/* Generated stub for json_add_num */
-void json_add_num(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED,
-		  unsigned int value UNNEEDED)
-{ fprintf(stderr, "json_add_num called!\n"); abort(); }
-/* Generated stub for json_add_string */
-void json_add_string(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED, const char *value UNNEEDED)
-{ fprintf(stderr, "json_add_string called!\n"); abort(); }
-/* Generated stub for json_add_time */
-void json_add_time(struct json_stream *result UNNEEDED, const char *fieldname UNNEEDED,
-			  struct timespec ts UNNEEDED)
-{ fprintf(stderr, "json_add_time called!\n"); abort(); }
 /* Generated stub for json_array_end */
 void json_array_end(struct json_stream *js UNNEEDED)
 { fprintf(stderr, "json_array_end called!\n"); abort(); }
 /* 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_member_direct */
+char *json_member_direct(struct json_stream *js UNNEEDED,
+			 const char *fieldname UNNEEDED, size_t extra UNNEEDED)
+{ fprintf(stderr, "json_member_direct called!\n"); abort(); }
 /* Generated stub for json_object_end */
 void json_object_end(struct json_stream *js UNNEEDED)
 { fprintf(stderr, "json_object_end called!\n"); abort(); }
diff --git a/plugins/Makefile b/plugins/Makefile
index cd4b03636..fae47b922 100644
--- a/plugins/Makefile
+++ b/plugins/Makefile
@@ -22,6 +22,7 @@ PLUGIN_COMMON_OBJS :=				\
 	bitcoin/tx.o				\
 	bitcoin/varint.o			\
 	common/amount.o				\
+	common/base32.o				\
 	common/bech32.o				\
 	common/bech32_util.o			\
 	common/bigsize.o			\
@@ -31,6 +32,7 @@ PLUGIN_COMMON_OBJS :=				\
 	common/hash_u5.o			\
 	common/json.o				\
 	common/json_helpers.o			\
+	common/json_stream.o			\
 	common/json_tok.o			\
 	common/memleak.o			\
 	common/node_id.o			\
@@ -39,6 +41,7 @@ PLUGIN_COMMON_OBJS :=				\
 	common/type_to_string.o			\
 	common/utils.o				\
 	common/version.o			\
+	common/wireaddr.o			\
 	wire/fromwire.o				\
 	wire/towire.o