#include "config.h"
#include <bitcoin/address.h>
#include <bitcoin/base58.h>
#include <bitcoin/psbt.h>
#include <bitcoin/script.h>
#include <ccan/asort/asort.h>
#include <ccan/json_escape/json_escape.h>
#include <ccan/mem/mem.h>
#include <ccan/str/hex/hex.h>
#include <ccan/tal/str/str.h>
#include <common/bech32.h>
#include <common/json_command.h>
#include <common/json_param.h>
#include <common/route.h>
#include <common/utils.h>

/* Overridden by run-param.c */
#ifndef paramcheck_assert
#define paramcheck_assert assert
#endif

/* Overridden by run-param.c */
#ifndef paramcheck_assert
#define paramcheck_assert assert
#endif

struct param {
	const char *name;
	const char *depr_start, *depr_end;
	bool is_set;
	enum param_style style;
	param_cbx cbx;
	void *arg;
};

static void param_add(struct param **params,
		      const char *name,
		      enum param_style style,
		      const char *depr_start,
		      const char *depr_end,
		      param_cbx cbx, void *arg)
{
	struct param last;

	paramcheck_assert(name);
	paramcheck_assert(cbx);
	paramcheck_assert(arg);

	last.is_set = false;
	last.name = name;
	last.depr_start = depr_start;
	last.depr_end = depr_end;
	last.style = style;
	last.cbx = cbx;
	last.arg = arg;

	tal_arr_expand(params, last);
}

static bool is_required(enum param_style style)
{
	return style == PARAM_REQUIRED;
}

static struct command_result *make_callback(struct command *cmd,
					     struct param *def,
					     const char *buffer,
					     const jsmntok_t *tok)
{
	/* If it had a default, free that now to avoid leak */
	if ((def->style == PARAM_OPTIONAL_WITH_DEFAULT
	     || def->style == PARAM_OPTIONAL_DEV_WITH_DEFAULT)
	    && !def->is_set)
		tal_free(*(void **)def->arg);

	def->is_set = true;

	return def->cbx(cmd, def->name, buffer, tok, def->arg);
}

static struct command_result *post_check(struct command *cmd,
					 struct param *params)
{
	struct param *first = params;
	struct param *last = first + tal_count(params);

	/* Make sure required params were provided. */
	while (first != last && is_required(first->style)) {
		if (!first->is_set) {
			return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
					    "missing required parameter: %s",
					    first->name);
		}
		first++;
	}
	return NULL;
}

static struct command_result *parse_by_position(struct command *cmd,
						struct param *params,
						const char *buffer,
						const jsmntok_t tokens[],
						bool allow_extra)
{
	struct command_result *res;
	const jsmntok_t *tok;
	size_t i;

	json_for_each_arr(i, tok, tokens) {
		/* check for unexpected trailing params */
		if (i == tal_count(params)) {
			if (!allow_extra) {
				return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
						    "too many parameters:"
						    " got %u, expected %zu",
						    tokens->size,
						    tal_count(params));
			}
			break;
		}

		if (!json_tok_is_null(buffer, tok)) {
			if (params[i].style == PARAM_OPTIONAL_DEV_WITH_DEFAULT
			    && !command_dev_apis(cmd)) {
				return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
						    "Parameter %zu is developer-only", i);
			}
			res = make_callback(cmd, params+i, buffer, tok);
			if (res)
				return res;
		}
	}

	return post_check(cmd, params);
}

static struct param *find_param(struct command *cmd,
				struct param *params, const char *start,
				size_t n)
{
	struct param *first = params;
	struct param *last = first + tal_count(params);

	while (first != last) {
		if (memeqstr(start, n, first->name)) {
			if (!command_deprecated_in_ok(cmd, first->name,
						      first->depr_start,
						      first->depr_end)) {
				return NULL;
			}
			return first;
		}
		first++;
	}
	return NULL;
}

static struct command_result *parse_by_name(struct command *cmd,
					    struct param *params,
					    const char *buffer,
					    const jsmntok_t tokens[],
					    bool allow_extra)
{
	size_t i;
	const jsmntok_t *t;

	json_for_each_obj(i, t, tokens) {
		struct param *p = find_param(cmd, params, buffer + t->start,
					     t->end - t->start);
		if (!p) {
			if (!allow_extra) {
				return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
						    "unknown parameter: %.*s, this may be caused by a failure to autodetect key=value-style parameters. Please try using the -k flag and explicit key=value pairs of parameters.",
						    t->end - t->start,
						    buffer + t->start);
			}
		} else {
			struct command_result *res;

			if (p->is_set) {
				return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
						    "duplicate json names: %s",
						    p->name);
			}

			if (p->style == PARAM_OPTIONAL_DEV_WITH_DEFAULT
			    && !command_dev_apis(cmd)) {
				return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
						    "Parameter '%s' is developer-only",
						    p->name);
			}
			res = make_callback(cmd, p, buffer, t + 1);
			if (res)
				return res;
		}
	}
	return post_check(cmd, params);
}

static int comp_by_name(const struct param *a, const struct param *b,
			void *unused)
{
	return strcmp(a->name, b->name);
}

/* This comparator is a bit different, but works well.
 * Return 0 if @a is optional and @b is required. Otherwise return 1.
 */
static int comp_req_order(const struct param *a, const struct param *b,
			  void *unused)
{
	if (!is_required(a->style) && is_required(b->style))
		return 0;
	return 1;
}

/*
 * Make sure 2 sequential items in @params are not equal (based on
 * provided comparator).
 */
static void check_distinct(const struct param *params,
			   int (*compar)(const struct param *a,
					 const struct param *b, void *unused))
{
	const struct param *first = params;
	const struct param *last = first + tal_count(params);
	first++;
	while (first != last) {
		paramcheck_assert(compar(first - 1, first, NULL) != 0);
		first++;
	}
}

static void check_unique(struct param *copy,
			 int (*compar) (const struct param *a,
					const struct param *b, void *unused))
{
	asort(copy, tal_count(copy), compar, NULL);
	check_distinct(copy, compar);
}

/*
 * Verify consistent internal state.
 */
static void check_params(const struct param *params)
{
	if (tal_count(params) < 2)
		return;

	/* make sure there are no required params following optional */
	check_distinct(params, comp_req_order);

	/* duplicate so we can sort */
	struct param *copy = tal_dup_talarr(params, struct param, params);

	/* check for repeated names and args */
	check_unique(copy, comp_by_name);

	tal_free(copy);
}

static char *param_usage(const tal_t *ctx,
			 const struct param *params)
{
	char *usage = tal_strdup(ctx, "");
	for (size_t i = 0; i < tal_count(params); i++) {
		/* Don't print |deprecated part! */
		int len = strcspn(params[i].name, "|");
		if (i != 0)
			tal_append_fmt(&usage, " ");
		if (is_required(params[i].style))
			tal_append_fmt(&usage, "%.*s", len, params[i].name);
		else
			tal_append_fmt(&usage, "[%.*s]", len, params[i].name);
	}
	return usage;
}

static struct command_result *param_arr(struct command *cmd, const char *buffer,
					const jsmntok_t tokens[],
					struct param *params,
					bool allow_extra)
{
	if (tokens->type == JSMN_ARRAY)
		return parse_by_position(cmd, params, buffer, tokens, allow_extra);
	else if (tokens->type == JSMN_OBJECT)
		return parse_by_name(cmd, params, buffer, tokens, allow_extra);

	return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
			    "Expected array or object for params");
}

const char *param_subcommand(struct command *cmd, const char *buffer,
			     const jsmntok_t tokens[],
			     const char *name, ...)
{
	va_list ap;
	struct param *params = tal_arr(cmd, struct param, 0);
	const char *arg, **names = tal_arr(tmpctx, const char *, 1);
	const char *subcmd;

	param_add(&params, "subcommand", PARAM_REQUIRED, NULL, NULL,
		  (void *)param_string, &subcmd);
	names[0] = name;
	va_start(ap, name);
	while ((arg = va_arg(ap, const char *)) != NULL)
		tal_arr_expand(&names, arg);
	va_end(ap);

	if (command_usage_only(cmd)) {
		char *usage = tal_strdup(cmd, "subcommand");
		for (size_t i = 0; i < tal_count(names); i++)
			tal_append_fmt(&usage, "%c%s",
				       i == 0 ? '=' : '|', names[i]);
		check_params(params);
		command_set_usage(cmd, usage);
		return NULL;
	}

	/* Check it's valid */
	if (param_arr(cmd, buffer, tokens, params, true) != NULL) {
		return NULL;
	}

	/* Check it's one of the known ones. */
	for (size_t i = 0; i < tal_count(names); i++)
		if (streq(subcmd, names[i]))
			return subcmd;

	/* We really do ignore this. */
	struct command_result *ignore;
	ignore = command_fail(cmd, JSONRPC2_INVALID_PARAMS,
			      "Unknown subcommand '%s'", subcmd);
	assert(ignore);
	return NULL;
}

static bool param_core(struct command *cmd,
		       const char *buffer,
		       const jsmntok_t tokens[],
		       va_list ap)
{
	struct param *params = tal_arr(tmpctx, struct param, 0);
	const char *name;
	bool allow_extra = false;

	while ((name = va_arg(ap, const char *)) != NULL) {
		enum param_style style = va_arg(ap, enum param_style);
		const char *depr_start = va_arg(ap, const char *);
		const char *depr_end = va_arg(ap, const char *);
		param_cbx cbx = va_arg(ap, param_cbx);
		void *arg = va_arg(ap, void *);
		if (streq(name, "")) {
			allow_extra = true;
			continue;
		}
		param_add(&params, name, style, depr_start, depr_end, cbx, arg);
	}

	if (command_usage_only(cmd)) {
		check_params(params);
		command_set_usage(cmd, param_usage(cmd, params));
		return false;
	}

	return param_arr(cmd, buffer, tokens, params, allow_extra) == NULL;
}

bool param(struct command *cmd,
	   const char *buffer,
	   const jsmntok_t tokens[], ...)
{
	bool ret;
	va_list ap;

	va_start(ap, tokens);
	ret = param_core(cmd, buffer, tokens, ap);
	va_end(ap);

	/* Always "fail" if we're just checking! */
	if (ret && command_check_only(cmd)) {
		/* We really do ignore result here! */
		if (command_check_done(cmd))
			;
		ret = false;
	}
	return ret;
}

bool param_check(struct command *cmd,
		 const char *buffer,
		 const jsmntok_t tokens[], ...)
{
	bool ret;
	va_list ap;

	va_start(ap, tokens);
	ret = param_core(cmd, buffer, tokens, ap);
	va_end(ap);

	return ret;
}

struct command_result *param_array(struct command *cmd, const char *name,
				   const char *buffer, const jsmntok_t *tok,
				   const jsmntok_t **arr)
{
	if (tok->type == JSMN_ARRAY) {
		*arr = tok;
		return NULL;
	}

	return command_fail_badparam(cmd, name, buffer, tok, "should be an array");
}

struct command_result *param_bool(struct command *cmd, const char *name,
				  const char *buffer, const jsmntok_t *tok,
				  bool **b)
{
	*b = tal(cmd, bool);
	if (json_to_bool(buffer, tok, *b))
		return NULL;
	return command_fail_badparam(cmd, name, buffer, tok,
				     "should be 'true' or 'false'");
}

struct command_result *param_millionths(struct command *cmd, const char *name,
					const char *buffer,
					const jsmntok_t *tok, uint64_t **num)
{
	*num = tal(cmd, uint64_t);
	if (json_to_millionths(buffer, tok, *num))
		return NULL;

	return command_fail_badparam(cmd, name, buffer, tok,
				     "should be a non-negative floating-point number");
}

struct command_result *param_escaped_string(struct command *cmd,
					    const char *name,
					    const char * buffer,
					    const jsmntok_t *tok,
					    const char **str)
{
	if (tok->type == JSMN_STRING) {
		/* jsmn always gives us ~ well-formed strings. */
		*str = json_escape_unescape_len(cmd, buffer + tok->start,
						tok->end - tok->start);
		if (*str)
			return NULL;
	}
	return command_fail_badparam(cmd, name, buffer, tok,
				     "should be a string (without \\u)");
}

struct command_result *param_string(struct command *cmd, const char *name,
				    const char * buffer, const jsmntok_t *tok,
				    const char **str)
{
	*str = tal_strndup(cmd, buffer + tok->start,
			   tok->end - tok->start);
	return NULL;
}

/* Extract a string or a json array */
struct command_result *param_string_or_array(struct command *cmd, const char *name,
					     const char * buffer, const jsmntok_t *tok,
					     struct str_or_arr **result)
{
	*result = tal(cmd, struct str_or_arr);
	(*result)->arr = NULL;
	(*result)->str = NULL;
	if (tok->type == JSMN_ARRAY) {
		(*result)->arr = tok;
		return NULL;
	}

	return param_string(cmd, name, buffer, tok, &(*result)->str);
}

struct command_result *param_string_array(struct command *cmd, const char *name,
					  const char *buffer, const jsmntok_t *tok,
					  const char ***arr)
{
	size_t i;
	const jsmntok_t *s;

	if (tok->type != JSMN_ARRAY)
		return command_fail_badparam(cmd, name, buffer, tok,
					     "should be an array");
	*arr = tal_arr(cmd, const char *, tok->size);
	json_for_each_arr(i, s, tok)
		(*arr)[i] = json_strdup(*arr, buffer, s);
	return NULL;
}

struct command_result *param_invstring(struct command *cmd, const char *name,
				       const char * buffer, const jsmntok_t *tok,
				       const char **str)
{
	const char *strtmp = json_strdup(cmd, buffer, tok);
	*str = to_canonical_invstr(cmd, strtmp);
	return NULL;
}

struct command_result *param_ignore(struct command *cmd, const char *name,
				    const char *buffer, const jsmntok_t *tok,
				    const void *unused)
{
	return NULL;
}

struct command_result *param_label(struct command *cmd, const char *name,
				   const char * buffer, const jsmntok_t *tok,
				   struct json_escape **label)
{
	/* We accept both strings and number literals here. */
	*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;

	return command_fail_badparam(cmd, name, buffer, tok,
				     "should be a string or number");
}

struct command_result *param_number(struct command *cmd, const char *name,
				    const char *buffer, const jsmntok_t *tok,
				    unsigned int **num)
{
	*num = tal(cmd, unsigned int);
	if (json_to_number(buffer, tok, *num))
		return NULL;

	return command_fail_badparam(cmd, name, buffer, tok,
				     "should be an integer");
}

struct command_result *param_sha256(struct command *cmd, const char *name,
				    const char *buffer, const jsmntok_t *tok,
				    struct sha256 **hash)
{
	*hash = tal(cmd, struct sha256);
	if (hex_decode(buffer + tok->start,
		       tok->end - tok->start,
		       *hash, sizeof(**hash)))
		return NULL;

	return command_fail_badparam(cmd, name, buffer, tok,
				     "should be a 32 byte hex value");
}

struct command_result *param_u16(struct command *cmd, const char *name,
				 const char *buffer, const jsmntok_t *tok,
				 uint16_t **num)
{
	*num = tal(cmd, uint16_t);
	if (json_to_u16(buffer, tok, *num))
		return NULL;

	return command_fail_badparam(cmd, name, buffer, tok,
				     "should be an unsigned 16 bit integer");
}

struct command_result *param_u32(struct command *cmd, const char *name,
				 const char *buffer, const jsmntok_t *tok,
				 uint32_t **num)
{
	*num = tal(cmd, uint32_t);
	if (json_to_u32(buffer, tok, *num))
		return NULL;

	return command_fail_badparam(cmd, name, buffer, tok,
				     "should be an unsigned 32 bit integer");
}

struct command_result *param_u64(struct command *cmd, const char *name,
				 const char *buffer, const jsmntok_t *tok,
				 uint64_t **num)
{
	*num = tal(cmd, uint64_t);
	if (json_to_u64(buffer, tok, *num))
		return NULL;

	return command_fail_badparam(cmd, name, buffer, tok,
				     "should be an unsigned 64 bit integer");
}

struct command_result *param_s64(struct command *cmd, const char *name,
				 const char *buffer, const jsmntok_t *tok,
				 int64_t **num)
{
	*num = tal(cmd, int64_t);
	if (json_to_s64(buffer, tok, *num))
		return NULL;

	return command_fail_badparam(cmd, name, buffer, tok,
				     "should be an sign 64 bit integer");
}

struct command_result *param_msat(struct command *cmd, const char *name,
				  const char *buffer, const jsmntok_t *tok,
				  struct amount_msat **msat)
{
	*msat = tal(cmd, struct amount_msat);
	if (parse_amount_msat(*msat, buffer + tok->start, tok->end - tok->start))
		return NULL;

	return command_fail_badparam(cmd, name, buffer, tok,
				     "should be a millisatoshi amount");
}

struct command_result *param_sat(struct command *cmd, const char *name,
				 const char *buffer, const jsmntok_t *tok,
				 struct amount_sat **sat)
{
	*sat = tal(cmd, struct amount_sat);
	if (parse_amount_sat(*sat, buffer + tok->start, tok->end - tok->start))
		return NULL;

	return command_fail_badparam(cmd, name, buffer, tok,
				     "should be a satoshi amount");
}

struct command_result *param_sat_or_all(struct command *cmd, const char *name,
					const char *buffer, const jsmntok_t *tok,
					struct amount_sat **sat)
{
	if (json_tok_streq(buffer, tok, "all")) {
		*sat = tal(cmd, struct amount_sat);
		**sat = AMOUNT_SAT(-1ULL);
		return NULL;
	}
	return param_sat(cmd, name, buffer, tok, sat);
}

struct command_result *param_node_id(struct command *cmd, const char *name,
		  		     const char *buffer, const jsmntok_t *tok,
				     struct node_id **id)
{
	*id = tal(cmd, struct node_id);
	if (json_to_node_id(buffer, tok, *id))
		return NULL;

	return command_fail_badparam(cmd, name, buffer, tok,
				     "should be a node id");
}

struct command_result *param_channel_id(struct command *cmd, const char *name,
					const char *buffer, const jsmntok_t *tok,
					struct channel_id **cid)
{
	*cid = tal(cmd, struct channel_id);
	if (json_to_channel_id(buffer, tok, *cid))
		return NULL;

	return command_fail_badparam(cmd, name, buffer, tok,
				     "should be a channel id");
}

struct command_result *param_short_channel_id(struct command *cmd,
					      const char *name,
					      const char *buffer,
					      const jsmntok_t *tok,
					      struct short_channel_id **scid)
{
	*scid = tal(cmd, struct short_channel_id);
	if (json_to_short_channel_id(buffer, tok, *scid))
		return NULL;

	return command_fail_badparam(cmd, name, buffer, tok,
				     "should be a short_channel_id of form NxNxN");
}

struct command_result *param_short_channel_id_dir(struct command *cmd,
						  const char *name,
						  const char *buffer,
						  const jsmntok_t *tok,
						  struct short_channel_id_dir **scidd)
{
	*scidd = tal(cmd, struct short_channel_id_dir);
	if (!json_to_short_channel_id_dir(buffer, tok, *scidd))
		return command_fail_badparam(cmd, name, buffer, tok,
					     "should be a short_channel_id_dir of form NxNxN/N");
	return NULL;
}

struct command_result *param_secret(struct command *cmd, const char *name,
				    const char *buffer, const jsmntok_t *tok,
				    struct secret **secret)
{
	*secret = tal(cmd, struct secret);
	if (hex_decode(buffer + tok->start,
		       tok->end - tok->start,
		       *secret, sizeof(**secret)))
		return NULL;

	return command_fail_badparam(cmd, name, buffer, tok,
				     "should be a 32 byte hex value");
}

struct command_result *param_bin_from_hex(struct command *cmd, const char *name,
				    const char *buffer, const jsmntok_t *tok,
				    u8 **bin)
{
	*bin = json_tok_bin_from_hex(cmd, buffer, tok);
	if (bin != NULL)
		return NULL;

	return command_fail_badparam(cmd, name, buffer, tok,
				     "should be a hex value");
}

struct command_result *param_hops_array(struct command *cmd, const char *name,
					const char *buffer, const jsmntok_t *tok,
					struct sphinx_hop **hops)
{
	const jsmntok_t *hop, *payloadtok, *pubkeytok;
	struct sphinx_hop h;
	size_t i;
	if (tok->type != JSMN_ARRAY) {
		return command_fail_badparam(cmd, name, buffer, tok,
					     "should be an array of hops");
	}

	*hops = tal_arr(cmd, struct sphinx_hop, 0);

	json_for_each_arr(i, hop, tok) {
		payloadtok = json_get_member(buffer, hop, "payload");
		pubkeytok = json_get_member(buffer, hop, "pubkey");

		if (!pubkeytok)
			return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
					    "Hop %zu does not have a pubkey", i);

		if (!payloadtok)
			return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
					    "Hop %zu does not have a payload", i);

		h.raw_payload = json_tok_bin_from_hex(*hops, buffer, payloadtok);
		if (!json_to_pubkey(buffer, pubkeytok, &h.pubkey))
			return command_fail_badparam(cmd, name, buffer, pubkeytok,
						     "should be a pubkey");

		if (!h.raw_payload)
			return command_fail_badparam(cmd, name, buffer,
						     payloadtok,
						     "should be hex");

		tal_arr_expand(hops, h);
	}

	if (tal_count(*hops) == 0) {
		return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
				    "At least one hop must be specified.");
	}

	return NULL;
}

struct command_result *param_secrets_array(struct command *cmd,
					   const char *name, const char *buffer,
					   const jsmntok_t *tok,
					   struct secret **secrets)
{
	size_t i;
	const jsmntok_t *s;
	struct secret secret;

	if (tok->type != JSMN_ARRAY) {
		return command_fail_badparam(cmd, name, buffer, tok,
					     "should be an array of secrets");
	}

	*secrets = tal_arr(cmd, struct secret, 0);
	json_for_each_arr(i, s, tok) {
		if (!hex_decode(buffer + s->start, s->end - s->start, &secret,
				sizeof(secret)))
			return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
					    "'%s[%zu]' should be a 32 byte hex "
					    "value, not '%.*s'",
					    name, i, s->end - s->start,
					    buffer + s->start);

		tal_arr_expand(secrets, secret);
	}
	return NULL;
}

/**
 * segwit_addr_net_decode - Try to decode a Bech32(m) address and detect
 * testnet/mainnet/regtest/signet/testnet4
 *
 * This processes the address and returns a string if it is a Bech32
 * address specified by BIP173 or Bech32m as by BIP350. The string is
 * set whether it is testnet/testnet4/signet ("tb"),  mainnet ("bc"),
 * regtest ("bcrt"). It does not check witness version and program size
 * restrictions.
 *
 *  Out: witness_version: Pointer to an int that will be updated to contain
 *                 the witness program version (between 0 and 16 inclusive).
 *       witness_program: Pointer to a buffer of size 40 that will be updated
 *                 to contain the witness program bytes.
 *       witness_program_len: Pointer to a size_t that will be updated to
 *                 contain the length of bytes in witness_program.
 *  In:  addrz:    Pointer to the null-terminated address.
 *  Returns string containing the human readable segment of bech32 address
 */
static const char *segwit_addr_net_decode(int *witness_version,
					  uint8_t *witness_program,
					  size_t *witness_program_len,
					  const char *addrz,
					  const struct chainparams *chainparams)
{
	if (segwit_addr_decode(witness_version, witness_program,
			       witness_program_len, chainparams->onchain_hrp,
			       addrz))
		return chainparams->onchain_hrp;
	else
		return NULL;
}

enum address_parse_result
json_to_address_scriptpubkey(const tal_t *ctx,
			      const struct chainparams *chainparams,
			      const char *buffer,
			      const jsmntok_t *tok, const u8 **scriptpubkey)
{
	struct bitcoin_address destination;
	int witness_version;
	/* segwit_addr_net_decode requires a buffer of size 40, and will
	 * not write to the buffer if the address is too long, so a buffer
	 * of fixed size 40 will not overflow. */
	uint8_t witness_program[40];
	size_t witness_program_len;

	char *addrz;
	const char *bech32;

	u8 addr_version;

	if (ripemd160_from_base58(&addr_version, &destination.addr,
				  buffer + tok->start, tok->end - tok->start)) {
		if (addr_version == chainparams->p2pkh_version) {
			*scriptpubkey = scriptpubkey_p2pkh(ctx, &destination);
			return ADDRESS_PARSE_SUCCESS;
		} else if (addr_version == chainparams->p2sh_version) {
			*scriptpubkey =
			    scriptpubkey_p2sh_hash(ctx, &destination.addr);
			return ADDRESS_PARSE_SUCCESS;
		} else {
			return ADDRESS_PARSE_WRONG_NETWORK;
		}
		/* Insert other parsers that accept pointer+len here. */
		return ADDRESS_PARSE_UNRECOGNIZED;
	}

	/* Generate null-terminated address. */
	addrz = tal_dup_arr(tmpctx, char, buffer + tok->start, tok->end - tok->start, 1);
	addrz[tok->end - tok->start] = '\0';

	bech32 = segwit_addr_net_decode(&witness_version, witness_program,
					&witness_program_len, addrz, chainparams);
	if (bech32) {
		bool witness_ok;

		/* Only V0 has restricted lengths of witness programs */
		if (witness_version == 0) {
			witness_ok = (witness_program_len == 20 ||
				       witness_program_len == 32);
		} else
			witness_ok = true;

		if (!witness_ok)
			return ADDRESS_PARSE_UNRECOGNIZED;

		if (!streq(bech32, chainparams->onchain_hrp))
			return ADDRESS_PARSE_WRONG_NETWORK;

		*scriptpubkey = scriptpubkey_witness_raw(ctx, witness_version,
							 witness_program, witness_program_len);
		return ADDRESS_PARSE_SUCCESS;
	}

	/* Insert other parsers that accept null-terminated string here. */
	return ADDRESS_PARSE_UNRECOGNIZED;
}

struct command_result *param_txid(struct command *cmd,
				  const char *name,
				  const char *buffer,
				  const jsmntok_t *tok,
				  struct bitcoin_txid **txid)
{
	*txid = tal(cmd, struct bitcoin_txid);
	if (json_to_txid(buffer, tok, *txid))
		return NULL;
	return command_fail_badparam(cmd, name, buffer, tok,
				     "should be a txid");
}

struct command_result *param_outpoint(struct command *cmd,
				      const char *name,
				      const char *buffer,
				      const jsmntok_t *tok,
				      struct bitcoin_outpoint **outp)
{
	*outp = tal(cmd, struct bitcoin_outpoint);
	if (json_to_outpoint(buffer, tok, *outp))
		return NULL;
	return command_fail_badparam(cmd, name, buffer, tok,
				     "should be a txid:outnum");
}

struct command_result *param_bitcoin_address(struct command *cmd,
					     const char *name,
					     const char *buffer,
					     const jsmntok_t *tok,
					     const u8 **scriptpubkey)
{
	/* Parse address. */
	switch (json_to_address_scriptpubkey(cmd,
					     chainparams,
					     buffer, tok,
					     scriptpubkey)) {
	case ADDRESS_PARSE_UNRECOGNIZED:
		return command_fail(cmd, LIGHTNINGD,
				    "Could not parse destination address, "
				    "%s should be a valid address",
				    name ? name : "address field");
	case ADDRESS_PARSE_WRONG_NETWORK:
		return command_fail(cmd, LIGHTNINGD,
				    "Destination address is not on network %s",
				    chainparams->network_name);
	case ADDRESS_PARSE_SUCCESS:
		return NULL;
	}
	abort();
}

struct command_result *param_psbt(struct command *cmd,
				  const char *name,
				  const char *buffer,
				  const jsmntok_t *tok,
				  struct wally_psbt **psbt)
{
	*psbt = psbt_from_b64(cmd, buffer + tok->start, tok->end - tok->start);
	if (*psbt)
		return NULL;

	return command_fail_badparam(cmd, name, buffer, tok,
				     "Expected a PSBT");
}

struct command_result *param_outpoint_arr(struct command *cmd,
					  const char *name,
					  const char *buffer,
					  const jsmntok_t *tok,
					  struct bitcoin_outpoint **outpoints)
{
	size_t i;
	const jsmntok_t *curr;
	if (tok->type != JSMN_ARRAY) {
		return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
				    "Could not decode the outpoint array for %s: "
				    "\"%s\" is not a valid outpoint array.",
				    name, json_strdup(tmpctx, buffer, tok));
	}

	*outpoints = tal_arr(cmd, struct bitcoin_outpoint, tok->size);

	json_for_each_arr(i, curr, tok) {
		if (!json_to_outpoint(buffer, curr, &(*outpoints)[i]))
			return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
					    "Could not decode outpoint \"%.*s\", "
					    "expected format: txid:output",
					    json_tok_full_len(curr), json_tok_full(buffer, curr));
	}
	return NULL;
}

struct command_result *param_extra_tlvs(struct command *cmd, const char *name,
					const char *buffer,
					const jsmntok_t *tok,
					struct tlv_field **fields)
{
	size_t i;
	const jsmntok_t *curr;
	struct tlv_field *f, *temp;

	if (tok->type != JSMN_OBJECT) {
		return command_fail(
		    cmd, JSONRPC2_INVALID_PARAMS,
		    "Could not decode the TLV object from %s: "
		    "\"%s\" is not a valid JSON object.",
		    name, json_strdup(tmpctx, buffer, tok));
	}

	temp = tal_arr(cmd, struct tlv_field, tok->size);
	json_for_each_obj(i, curr, tok) {
		f = &temp[i];
		/* Accept either bare ints as keys (not spec
		 * compliant, but simpler), or ints in strings, which
		 * are JSON spec compliant. */
		if (!json_to_u64(buffer, curr, &f->numtype)) {
			return command_fail(
			    cmd, JSONRPC2_INVALID_PARAMS,
			    "\"%s\" is not a valid numeric TLV type.",
			    json_strdup(tmpctx, buffer, curr));
		}
		f->value = json_tok_bin_from_hex(temp, buffer, curr + 1);

		if (f->value == NULL) {
			return command_fail(
			    cmd, JSONRPC2_INVALID_PARAMS,
			    "\"%s\" is not a valid hex encoded TLV value.",
			    json_strdup(tmpctx, buffer, curr));
		}
		f->length = tal_bytelen(f->value);
		f->meta = NULL;
	}
	*fields = temp;
	return NULL;
}

static struct command_result *param_routehint(struct command *cmd,
					      const char *name,
					      const char *buffer,
					      const jsmntok_t *tok,
					      struct route_info **ri)
{
	size_t i;
	const jsmntok_t *curr;
	const char *err;

	if (tok->type != JSMN_ARRAY) {
		return command_fail(
		    cmd, JSONRPC2_INVALID_PARAMS,
		    "Routehint %s (\"%s\") is not an array of hop objects",
		    name, json_strdup(tmpctx, buffer, tok));
	}

	*ri = tal_arr(cmd, struct route_info, tok->size);
	json_for_each_arr(i, curr, tok) {
		struct route_info *e = &(*ri)[i];
		struct amount_msat temp;

		err = json_scan(tmpctx, buffer, curr,
				"{id:%,scid:%,feebase:%,feeprop:%,expirydelta:%}",
				JSON_SCAN(json_to_node_id, &e->pubkey),
				JSON_SCAN(json_to_short_channel_id, &e->short_channel_id),
				JSON_SCAN(json_to_msat, &temp),
				JSON_SCAN(json_to_u32, &e->fee_proportional_millionths),
				JSON_SCAN(json_to_u16, &e->cltv_expiry_delta)
			);
		e->fee_base_msat =
		    temp.millisatoshis; /* Raw: internal conversion. */
		if (err != NULL) {
			return command_fail(
			    cmd, JSONRPC2_INVALID_PARAMS,
			    "Error parsing routehint %s[%zu]: %s", name, i,
			    err);
		}
	}
	return NULL;
}

struct command_result *
param_routehint_array(struct command *cmd, const char *name, const char *buffer,
		      const jsmntok_t *tok, struct route_info ***ris)
{
	size_t i;
	const jsmntok_t *curr;
	char *element_name;
	struct command_result *err;
	if (tok->type != JSMN_ARRAY) {
		return command_fail(
		    cmd, JSONRPC2_INVALID_PARAMS,
		    "Routehint array %s (\"%s\") is not an array",
		    name, json_strdup(tmpctx, buffer, tok));
	}

	*ris = tal_arr(cmd, struct route_info *, 0);
	json_for_each_arr(i, curr, tok) {
		struct route_info *element;
		element_name = tal_fmt(cmd, "%s[%zu]", name, i);
		err = param_routehint(cmd, element_name, buffer, curr, &element);
		if (err != NULL) {
			return err;
		}
		tal_arr_expand(ris, element);

		tal_free(element_name);
	}
	return NULL;
}

struct command_result *param_route_exclusion(struct command *cmd,
					const char *name, const char *buffer, const jsmntok_t *tok,
					struct route_exclusion **re)
{
	*re = tal(cmd, struct route_exclusion);
	struct short_channel_id_dir *chan_id =
					tal(tmpctx, struct short_channel_id_dir);
	if (!short_channel_id_dir_from_str(buffer + tok->start,
						tok->end - tok->start,
						chan_id)) {
		struct node_id *node_id = tal(tmpctx, struct node_id);

		if (!json_to_node_id(buffer, tok, node_id))
			return command_fail_badparam(cmd, "exclude",
								buffer, tok,
								"should be short_channel_id_dir or node_id");

		(*re)->type = EXCLUDE_NODE;
		(*re)->u.node_id = *node_id;
	} else {
		(*re)->type = EXCLUDE_CHANNEL;
		(*re)->u.chan_id = *chan_id;
	}

	return NULL;
}

struct command_result *
param_route_exclusion_array(struct command *cmd, const char *name,
					const char *buffer, const jsmntok_t *tok,
					struct route_exclusion ***res)
{
	size_t i;
	const jsmntok_t *curr;
	char *element_name;
	struct command_result *err;
	if (tok->type != JSMN_ARRAY) {
		return command_fail(
		    cmd, JSONRPC2_INVALID_PARAMS,
		    "Exclude array %s (\"%s\") is not an array",
		    name, json_strdup(tmpctx, buffer, tok));
	}

	*res = tal_arr(cmd, struct route_exclusion *, 0);
	json_for_each_arr(i, curr, tok) {
		struct route_exclusion *element;
		element_name = tal_fmt(cmd, "%s[%zu]", name, i);
		err = param_route_exclusion(cmd, element_name, buffer, curr, &element);
		if (err != NULL) {
			return err;
		}
		tal_arr_expand(res, element);

		tal_free(element_name);
	}
	return NULL;
}

struct command_result *param_lease_hex(struct command *cmd,
				       const char *name,
				       const char *buffer,
				       const jsmntok_t *tok,
				       struct lease_rates **rates)
{
	*rates = lease_rates_fromhex(cmd, buffer + tok->start,
				     tok->end - tok->start);
	if (!*rates)
		return command_fail(cmd, JSONRPC2_INVALID_PARAMS,
				    "Could not decode '%s' %.*s",
				    name, json_tok_full_len(tok),
				    json_tok_full(buffer, tok));
	return NULL;
}

struct command_result *param_pubkey(struct command *cmd, const char *name,
				    const char *buffer, const jsmntok_t *tok,
				    struct pubkey **pubkey)
{
	*pubkey = tal(cmd, struct pubkey);
	if (json_to_pubkey(buffer, tok, *pubkey))
		return NULL;

	return command_fail_badparam(cmd, name, buffer, tok,
				     "should be a compressed pubkey");
}

