/* This is a plugin which allows you to specify
 * your policy for accepting/dual-funding incoming
 * v2 channel-open requests.
 *
 */
#include "config.h"
#include <bitcoin/feerate.h>
#include <bitcoin/psbt.h>
#include <bitcoin/script.h>
#include <ccan/array_size/array_size.h>
#include <ccan/json_out/json_out.h>
#include <ccan/tal/str/str.h>
#include <common/json_param.h>
#include <common/json_stream.h>
#include <common/memleak.h>
#include <common/overflows.h>
#include <common/psbt_open.h>
#include <inttypes.h>
#include <plugins/funder_policy.h>
#include <plugins/libplugin.h>

/* In-progress channel opens */
static struct list_head pending_opens;

/* Current set policy */
static struct funder_policy *current_policy;

struct pending_open {
	struct list_node list;
	struct plugin *p;

	struct node_id peer_id;
	struct channel_id channel_id;

	const struct wally_psbt *psbt;
};

/* How much do we need to keep in reserve for anchor spends? */
static struct amount_sat emergency_reserve;

static struct pending_open *
find_channel_pending_open(const struct channel_id *cid)
{
	struct pending_open *open;
	list_for_each(&pending_opens, open, list) {
		if (channel_id_eq(&open->channel_id, cid))
			return open;
	}
	return NULL;
}

static struct pending_open *
new_channel_open(const tal_t *ctx,
		 struct plugin *p,
		 const struct node_id id,
		 const struct channel_id cid,
		 const struct wally_psbt *psbt STEALS)
{
	struct pending_open *open;

	/* Make sure we haven't gotten this yet */
	assert(!find_channel_pending_open(&cid));

	open = tal(ctx, struct pending_open);
	open->p = p;
	open->peer_id = id;
	open->channel_id = cid;
	open->psbt = tal_steal(open, psbt);

	list_add_tail(&pending_opens, &open->list);

	return open;
}

static struct command_result *
unreserve_done(struct command *aux_cmd,
	       const char *method,
	       const char *buf,
	       const jsmntok_t *result,
	       struct pending_open *open)
{
	plugin_log(open->p, LOG_DBG,
		   "`unreserveinputs` for channel %s completed. %.*s",
		   fmt_channel_id(tmpctx, &open->channel_id),
		   json_tok_full_len(result),
		   json_tok_full(buf, result));

	return aux_command_done(aux_cmd);
}

/* Frees open (eventually, in unreserve_done callback) */
static struct command_result *unreserve_psbt(struct command *cmd,
					     struct pending_open *open)
{
	struct out_req *req;
	struct command *aux;

	plugin_log(open->p, LOG_DBG,
		   "Calling `unreserveinputs` for channel %s",
		   fmt_channel_id(tmpctx,
				  &open->channel_id));

	/* This can outlive the underlying cmd, so use an aux! */
	aux = aux_command(cmd);
	req = jsonrpc_request_start(aux,
				    "unreserveinputs",
				    unreserve_done, unreserve_done,
				    open);
	json_add_psbt(req->js, "psbt", open->psbt);
	send_outreq(req);

	/* We will free this in callback, but remove from list *now*
	 * to avoid calling twice! */
	list_del_from(&pending_opens, &open->list);
	tal_steal(aux, open);

	return command_still_pending(aux);
}

static void cleanup_peer_pending_opens(struct command *cmd,
				       const struct node_id *id)
{
	struct pending_open *i, *next;
	list_for_each_safe(&pending_opens, i, next, list) {
		if (node_id_eq(&i->peer_id, id)) {
			unreserve_psbt(cmd, i);
		}
	}
}

static struct command_result *
command_hook_cont_psbt(struct command *cmd, struct wally_psbt *psbt)
{
	struct json_stream *response;

	response = jsonrpc_stream_success(cmd);
	json_add_string(response, "result", "continue");
	json_add_psbt(response, "psbt", psbt);
	return command_finished(cmd, response);
}

static struct command_result *
datastore_del_fail(struct command *cmd,
		   const char *method,
		   const char *buf,
		   const jsmntok_t *error,
		   void *data UNUSED)
{
	/* Eh, ok fine */
	return notification_handled(cmd);
}

static struct command_result *
datastore_del_success(struct command *cmd,
		      const char *method,
		      const char *buf,
		      const jsmntok_t *result,
		      void *data UNUSED)
{
	/* Cool we deleted some stuff */
	plugin_log(cmd->plugin, LOG_DBG,
		   "`datastore` del succeeded: %.*s",
		   json_tok_full_len(result),
		   json_tok_full(buf, result));

	return notification_handled(cmd);
}

static struct command_result *
datastore_add_fail(struct command *cmd,
		   const char *method,
		   const char *buf,
		   const jsmntok_t *error,
		   struct wally_psbt *signed_psbt)
{
	/* Oops, something's broken */
	plugin_log(cmd->plugin, LOG_BROKEN,
		   "%s failed: %.*s",
		   method, json_tok_full_len(error),
		   json_tok_full(buf, error));

	return command_hook_cont_psbt(cmd, signed_psbt);
}

static struct command_result *
datastore_add_success(struct command *cmd,
		      const char *method,
		      const char *buf,
		      const jsmntok_t *result,
		      struct wally_psbt *signed_psbt)
{
	const char *key, *err;

	err = json_scan(tmpctx, buf, result,
			"{key:%}",
			JSON_SCAN_TAL(cmd, json_strdup, &key));

	if (err)
		plugin_err(cmd->plugin,
			   "`datastore` payload did not scan. %s: %.*s",
			   err, json_tok_full_len(result),
			   json_tok_full(buf, result));

	/* We saved the infos! */
	plugin_log(cmd->plugin, LOG_DBG,
		   "Saved utxos for channel (%s) to datastore",
		   key);

	return command_hook_cont_psbt(cmd, signed_psbt);
}

static struct command_result *
remember_channel_utxos(struct command *cmd,
		       struct pending_open *open,
		       struct wally_psbt *signed_psbt)
{
	struct out_req *req;
	u8 *utxos_bin;
	char *chan_key = tal_fmt(cmd, "funder/%s",
				 fmt_channel_id(cmd,
						&open->channel_id));

	req = jsonrpc_request_start(cmd,
				    "datastore",
				    &datastore_add_success,
				    &datastore_add_fail,
				    signed_psbt);

	utxos_bin = tal_arr(cmd, u8, 0);
	for (size_t i = 0; i < signed_psbt->num_inputs; i++) {
		struct bitcoin_outpoint outpoint;

		/* Don't save peer's UTXOS */
		if (!psbt_input_is_ours(&signed_psbt->inputs[i]))
			continue;

		wally_psbt_input_get_outpoint(&signed_psbt->inputs[i],
					    &outpoint);
		towire_bitcoin_outpoint(&utxos_bin, &outpoint);
	}
	json_add_string(req->js, "key", chan_key);
	/* We either update the existing or add a new one, nbd */
	json_add_string(req->js, "mode", "create-or-replace");
	json_add_hex(req->js, "hex", utxos_bin, tal_bytelen(utxos_bin));
	return send_outreq(req);
}

static struct command_result *
signpsbt_done(struct command *cmd,
	      const char *method,
	      const char *buf,
	      const jsmntok_t *result,
	      struct pending_open *open)
{
	struct wally_psbt *signed_psbt;
	struct command_result *res;
	const char *err;

	plugin_log(cmd->plugin, LOG_DBG,
		   "`signpsbt` done for channel %s",
		   fmt_channel_id(tmpctx,
				  &open->channel_id));
	err = json_scan(tmpctx, buf, result,
			"{signed_psbt:%}",
			JSON_SCAN_TAL(cmd, json_to_psbt, &signed_psbt));

	if (err)
		plugin_err(cmd->plugin,
			   "`signpsbt` payload did not scan %s: %.*s",
			   err, json_tok_full_len(result),
			   json_tok_full(buf, result));

	/* Save the list of utxos to the datastore! We'll need
	 * them again if we rbf */
	res = remember_channel_utxos(cmd, open, signed_psbt);

	/* The in-flight open is done, let's clean it up! */
	list_del_from(&pending_opens, &open->list);
	tal_free(open);

	return res;
}

static struct command_result *
json_openchannel2_sign_call(struct command *cmd,
			    const char *buf,
			    const jsmntok_t *params)
{
	struct channel_id cid;
	struct wally_psbt *psbt;
	const char *err;
	struct out_req *req;
	struct pending_open *open;
	size_t count;

	err = json_scan(tmpctx, buf, params,
			"{openchannel2_sign:"
			"{channel_id:%,psbt:%}}",
			JSON_SCAN(json_to_channel_id, &cid),
			JSON_SCAN_TAL(cmd, json_to_psbt, &psbt));

	if (err)
		plugin_err(cmd->plugin,
			   "`openchannel2_sign` payload did not scan %s: %.*s",
			   err, json_tok_full_len(params),
			   json_tok_full(buf, params));

	/* If we're not tracking this open, just pass through */
	open = find_channel_pending_open(&cid);
	if (!open) {
		plugin_log(cmd->plugin, LOG_DBG,
			   "nothing to sign for channel %s",
			   fmt_channel_id(tmpctx, &cid));
		return command_hook_cont_psbt(cmd, psbt);
	}

	if (!psbt_has_our_input(psbt)) {
		plugin_log(cmd->plugin, LOG_DBG,
			   "no inputs to sign for channel %s",
			   fmt_channel_id(tmpctx, &cid));
		return command_hook_cont_psbt(cmd, psbt);
	}

	plugin_log(cmd->plugin, LOG_DBG,
		   "openchannel_sign PSBT is %s",
		   fmt_wally_psbt(tmpctx, psbt));

	req = jsonrpc_request_start(cmd,
				    "signpsbt",
				    &signpsbt_done,
				    &forward_error,
				    open);
	json_add_psbt(req->js, "psbt", psbt);
	/* Use input markers to identify which inputs
	 * are ours, only sign those */
	json_array_start(req->js, "signonly");
	count = 0;
	for (size_t i = 0; i < psbt->num_inputs; i++) {
		if (psbt_input_is_ours(&psbt->inputs[i])) {
			json_add_num(req->js, NULL, i);
			count++;
		}
	}
	json_array_end(req->js);

	plugin_log(cmd->plugin, LOG_DBG,
		   "calling `signpsbt` for channel %s for %zu input%s",
		   fmt_channel_id(tmpctx,
				  &open->channel_id), count,
		   count == 1 ? "" : "s");
	return send_outreq(req);
}

static struct command_result *
json_openchannel2_changed_call(struct command *cmd,
			       const char *buf,
			       const jsmntok_t *params)
{
	struct channel_id cid;
	struct wally_psbt *psbt;
	const char *err;

	err = json_scan(tmpctx, buf, params,
			"{openchannel2_changed:"
			"{channel_id:%,psbt:%}}",
			JSON_SCAN(json_to_channel_id, &cid),
			JSON_SCAN_TAL(cmd, json_to_psbt, &psbt));

	if (err)
		plugin_err(cmd->plugin,
			   "`openchannel2_changed` payload did not"
			   " scan %s: %.*s",
			   err, json_tok_full_len(params),
			   json_tok_full(buf, params));

	plugin_log(cmd->plugin, LOG_DBG,
		   "openchannel_changed PSBT is %s",
		   fmt_wally_psbt(tmpctx, psbt));

	/* FIXME: do we have any additions or updates to make based
	 * on their changes? */
	/* For now, we assume we're the same as before and continue
	 * on as planned */
	return command_hook_cont_psbt(cmd, psbt);
}

/* Tiny struct to pass info to callback for fundpsbt */
struct open_info {
	struct channel_id cid;
	struct node_id id;
	struct amount_sat our_funding;
	struct amount_sat their_funding;

	/* If this is an RBF, we'll have this */
	struct amount_sat *their_last_funding;
	struct amount_sat *our_last_funding;

	struct amount_sat channel_max;
	u64 funding_feerate_perkw;
	u32 locktime;
	u32 lease_blockheight;
	u32 node_blockheight;

	struct amount_sat requested_lease;

	/* List of previously-used utxos */
	struct bitcoin_outpoint **prev_outs;
};

static struct open_info *new_open_info(const tal_t *ctx)
{
	struct open_info *info = tal(ctx, struct open_info);

	info->their_last_funding = NULL;
	info->our_last_funding = NULL;
	info->requested_lease = AMOUNT_SAT(0);
	info->lease_blockheight = 0;
	info->node_blockheight = 0;
	info->prev_outs = NULL;

	return info;
}

static struct command_result *
psbt_funded(struct command *cmd,
	    const char *method,
	    const char *buf,
	    const jsmntok_t *result,
	    struct open_info *info)
{
	struct wally_psbt *psbt;
	struct json_stream *response;
	struct amount_msat our_funding_msat;

	const char *err;

	err = json_scan(tmpctx, buf, result,
			"{psbt:%}",
			JSON_SCAN_TAL(tmpctx, json_to_psbt, &psbt));
	if (err)
		plugin_err(cmd->plugin,
			   "`fundpsbt` response did not scan %s: %.*s",
			   err, json_tok_full_len(result),
			   json_tok_full(buf, result));

	/* We also mark all of our inputs as *ours*, so we
	 * can easily identify them for `signpsbt` later */
	for (size_t i = 0; i < psbt->num_inputs; i++)
		psbt_input_mark_ours(psbt, &psbt->inputs[i]);

	new_channel_open(cmd->plugin, cmd->plugin,
			 info->id, info->cid, psbt);

	if (!amount_sat_to_msat(&our_funding_msat, info->our_funding))
		abort();

	response = jsonrpc_stream_success(cmd);
	json_add_string(response, "result", "continue");
	json_add_psbt(response, "psbt", psbt);
	json_add_amount_msat(response, "our_funding_msat", our_funding_msat);

	/* If we're accepting an lease request, *and* they've
	 * requested one, fill in our most recent infos */
	if (current_policy->rates && !amount_sat_is_zero(info->requested_lease))
		json_add_lease_rates(response, current_policy->rates);

	return command_finished(cmd, response);
}

static struct command_result *
psbt_fund_failed(struct command *cmd,
		 const char *method,
		 const char *buf,
		 const jsmntok_t *error,
		 struct open_info *info)
{
	/* Attempt to fund a psbt for this open failed.
	 * We probably ran out of funds (race?) */
	plugin_log(cmd->plugin, LOG_INFORM,
		   "%s: unable to secure %s from wallet,"
		   " continuing channel open to %s"
		   " without our participation. err %.*s",
		   method,
		   fmt_amount_sat(tmpctx, info->our_funding),
		   fmt_node_id(tmpctx, &info->id),
		   json_tok_full_len(error),
		   json_tok_full(buf, error));

	return command_hook_success(cmd);
}

/* They give msats, we want sats */
static bool json_to_msat_as_sats(const char *buffer, const jsmntok_t *tok,
				 struct amount_sat *sat)
{
	struct amount_msat msat;
	if (!json_to_msat(buffer, tok, &msat))
		return false;
	return amount_msat_to_sat(sat, msat);
}

static struct command_result *param_msat_as_sat(struct command *cmd,
						const char *name,
						const char *buffer,
						const jsmntok_t *tok,
						struct amount_sat **sat)
{
	struct amount_msat msat;

	*sat = tal(cmd, struct amount_sat);
	if (parse_amount_msat(&msat, buffer + tok->start, tok->end - tok->start)
	    && amount_msat_to_sat(*sat, msat))
		return NULL;

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

static struct bitcoin_outpoint *
previously_reserved(struct bitcoin_outpoint **prev_outs,
		    struct bitcoin_outpoint *out)
{
	for (size_t i = 0; i < tal_count(prev_outs); i++) {
		if (bitcoin_outpoint_eq(prev_outs[i], out))
			return prev_outs[i];
	}

	return NULL;
}

struct funder_utxo {
	struct bitcoin_outpoint out;
	struct amount_sat val;
};

static struct out_req *
build_utxopsbt_request(struct command *cmd,
		       struct open_info *info,
		       struct bitcoin_outpoint **prev_outs,
		       struct amount_sat requested_funds,
		       struct amount_sat committed_funds,
		       struct funder_utxo **avail_utxos)
{
	struct out_req *req;

	req = jsonrpc_request_start(cmd,
				    "utxopsbt",
				    &psbt_funded,
				    &psbt_fund_failed,
				    info);
	/* Add every prev_out */
	json_array_start(req->js, "utxos");
	for (size_t i = 0; i < tal_count(prev_outs); i++)
		json_add_outpoint(req->js, NULL, prev_outs[i]);

	/* Next add available utxos until we surpass the
	 * requested funds goal */
	/* FIXME: Update `utxopsbt` to automatically add more inputs? */
	for (size_t i = 0; i < tal_count(avail_utxos); i++) {
		/* If we've already hit our goal, break */
		if (amount_sat_greater_eq(committed_funds, requested_funds))
			break;

		/* Add this output to the UTXO */
		json_add_outpoint(req->js, NULL, &avail_utxos[i]->out);

		/* Account for it */
		if (!amount_sat_add(&committed_funds, committed_funds,
				    avail_utxos[i]->val))
			/* This should really never happen */
			plugin_err(cmd->plugin, "overflow adding committed");
	}
	json_array_end(req->js);
	return req;
}

static struct command_result *
listfunds_success(struct command *cmd,
		  const char *method,
		  const char *buf,
		  const jsmntok_t *result,
		  struct open_info *info)
{
	struct amount_sat available_funds, committed_funds, total_fee;
	const jsmntok_t *outputs_tok, *tok;
	struct out_req *req;
	struct bitcoin_outpoint **avail_prev_outs;
	size_t i;
	const char *funding_err;

	/* We only use this for RBFs, when there's a prev_outs list */
	struct funder_utxo **avail_utxos = tal_arr(cmd, struct funder_utxo *, 0);

	outputs_tok = json_get_member(buf, result, "outputs");
	if (!outputs_tok)
		plugin_err(cmd->plugin,
			   "`listfunds` payload has no outputs token: %.*s",
			   json_tok_full_len(result),
			   json_tok_full(buf, result));

	available_funds = AMOUNT_SAT(0);
	committed_funds = AMOUNT_SAT(0);
	total_fee = AMOUNT_SAT(0);
	avail_prev_outs = tal_arr(info, struct bitcoin_outpoint *, 0);
	json_for_each_arr(i, tok, outputs_tok) {
		struct funder_utxo *utxo;
		struct amount_sat est_fee;
		bool is_reserved;
		struct bitcoin_outpoint *prev_out;
		char *status;
		const char *err;

		utxo = tal(cmd, struct funder_utxo);
		err = json_scan(tmpctx, buf, tok,
				"{amount_msat:%"
				",status:%"
				",reserved:%"
				",txid:%"
				",output:%}",
				JSON_SCAN(json_to_msat_as_sats, &utxo->val),
				JSON_SCAN_TAL(cmd, json_strdup, &status),
				JSON_SCAN(json_to_bool, &is_reserved),
				JSON_SCAN(json_to_txid, &utxo->out.txid),
				JSON_SCAN(json_to_number, &utxo->out.n));
		if (err)
			plugin_err(cmd->plugin,
				   "`listfunds` payload did not scan. %s: %.*s",
				   err, json_tok_full_len(result),
				   json_tok_full(buf, result));

		/* v2 opens don't support p2sh-wrapped inputs */
		if (json_get_member(buf, tok, "redeemscript"))
			continue;

		/* The estimated fee per utxo. */
		est_fee = amount_tx_fee(info->funding_feerate_perkw,
					bitcoin_tx_input_weight(false, 110));

		/* Did we use this utxo on a previous attempt? */
		prev_out = previously_reserved(info->prev_outs, &utxo->out);

		/* we skip reserved funds that aren't in our previous
		 * inputs list! */
		if (is_reserved && !prev_out)
			continue;

		/* we skip unconfirmed+spent funds */
		if (!streq(status, "confirmed"))
			continue;

		/* Don't include outputs that can't cover their weight;
		 *  subtract the fee for this utxo out of the utxo */
		if (!amount_sat_sub(&utxo->val, utxo->val, est_fee))
			continue;

		if (!amount_sat_add(&available_funds, available_funds,
				    utxo->val))
			plugin_err(cmd->plugin,
				   "`listfunds` overflowed output values");

		if (!amount_sat_add(&total_fee, total_fee, est_fee))
			plugin_err(cmd->plugin,
				   "`listfunds` overflowed fee values");

		/* If this is an RBF, we keep track of available utxos */
		if (info->prev_outs) {
			/* if not previously reserved, it's committed */
			if (!prev_out) {
				tal_arr_expand(&avail_utxos, utxo);
				continue;
			}

			if (!amount_sat_add(&committed_funds,
					    committed_funds, utxo->val))
				plugin_err(cmd->plugin,
					   "`listfunds` overflowed"
					   " committed output values");

			/* We also keep a second list of utxos,
			 * as it's possible some utxos got spent
			 * between last attempt + this one! */
			tal_arr_expand(&avail_prev_outs, prev_out);
		}
	}

	/* Even if we don't have an anchor channel yet, we might soon:
	 * keep reserve, even after fee!  Assume two outputs, one for
	 * change. */
	if (!amount_sat_add(&total_fee, total_fee,
			    amount_tx_fee(info->funding_feerate_perkw,
					  BITCOIN_SCRIPTPUBKEY_P2WSH_LEN
					  + change_weight()))) {
		plugin_err(cmd->plugin,
			   "fee value overflow for estimating total fee");
	}

	if (!amount_sat_sub(&available_funds, available_funds, total_fee)
	    || !amount_sat_sub(&available_funds, available_funds, emergency_reserve))
		available_funds = AMOUNT_SAT(0);

	funding_err = calculate_our_funding(current_policy,
					    info->id,
					    info->their_funding,
					    info->our_last_funding,
					    available_funds,
					    info->channel_max,
					    info->requested_lease,
					    &info->our_funding);
	plugin_log(cmd->plugin, LOG_DBG,
		   "Policy %s returned funding amount of %s. %s",
		   funder_policy_desc(tmpctx, current_policy),
		   fmt_amount_sat(tmpctx, info->our_funding),
		   funding_err ? funding_err : "");

	if (amount_sat_is_zero(info->our_funding))
		return command_hook_success(cmd);

	plugin_log(cmd->plugin, LOG_DBG,
		   "Funding channel %s with %s (their input %s)",
		   fmt_channel_id(tmpctx, &info->cid),
		   fmt_amount_sat(tmpctx, info->our_funding),
		   fmt_amount_sat(tmpctx, info->their_funding));

	/* If there's prevouts, we compose a psbt with those first,
	 * then add more funds for anything missing */
	if (info->prev_outs) {
		req = build_utxopsbt_request(cmd, info,
					     avail_prev_outs,
					     info->our_funding,
					     committed_funds,
					     avail_utxos);
		json_add_bool(req->js, "reservedok", true);
		/* We don't re-reserve any UTXOS :) */
		json_add_num(req->js, "reserve", 0);
	} else {
		req = jsonrpc_request_start(cmd,
					    "fundpsbt",
					    &psbt_funded,
					    &psbt_fund_failed,
					    info);

		json_add_bool(req->js, "nonwrapped", true);
	}
	json_add_string(req->js, "satoshi",
			fmt_amount_sat(tmpctx, info->our_funding));
	json_add_string(req->js, "feerate",
			tal_fmt(tmpctx, "%"PRIu64"%s",
				info->funding_feerate_perkw,
				feerate_style_name(FEERATE_PER_KSIPA)));

	/* Our startweight is zero because we're freeriding on their open
	 * transaction ! */
	json_add_num(req->js, "startweight", 0);
	json_add_num(req->js, "min_witness_weight", 110);
	json_add_bool(req->js, "excess_as_change", true);
	json_add_num(req->js, "locktime", info->locktime);

	return send_outreq(req);
}

static struct command_result *
listfunds_failed(struct command *cmd,
		 const char *method,
		 const char *buf,
		 const jsmntok_t *error,
		 struct open_info *info)
{

	/* Something went wrong fetching the funds info
	 * for our wallet. Just keep going */
	plugin_log(cmd->plugin, LOG_INFORM,
		   "Unable to fetch wallet funds info."
		   " Continuing channel open to %s"
		   " without our participation. err %.*s",
		   fmt_node_id(tmpctx,
				  &info->id),
		   json_tok_full_len(error),
		   json_tok_full(buf, error));

	return command_hook_success(cmd);
}

static struct command_result *
json_openchannel2_call(struct command *cmd,
		       const char *buf,
		       const jsmntok_t *params)
{
	struct open_info *info = new_open_info(cmd);
	struct amount_msat max_htlc_inflight, htlc_minimum;
	u64 commitment_feerate_perkw,
	    feerate_our_max, feerate_our_min;
	u32 to_self_delay, max_accepted_htlcs;
	u16 channel_flags;
	const char *err;
	struct out_req *req;

	err = json_scan(tmpctx, buf, params,
			"{openchannel2:"
			"{id:%"
			",channel_id:%"
			",their_funding_msat:%"
			",max_htlc_value_in_flight_msat:%"
			",htlc_minimum_msat:%"
			",funding_feerate_per_kw:%"
			",commitment_feerate_per_kw:%"
			",feerate_our_max:%"
			",feerate_our_min:%"
			",to_self_delay:%"
			",max_accepted_htlcs:%"
			",channel_flags:%"
			",locktime:%"
			",channel_max_msat:%}}",
			JSON_SCAN(json_to_node_id, &info->id),
			JSON_SCAN(json_to_channel_id, &info->cid),
			JSON_SCAN(json_to_msat_as_sats, &info->their_funding),
			JSON_SCAN(json_to_msat, &max_htlc_inflight),
			JSON_SCAN(json_to_msat, &htlc_minimum),
			JSON_SCAN(json_to_u64, &info->funding_feerate_perkw),
			JSON_SCAN(json_to_u64, &commitment_feerate_perkw),
			JSON_SCAN(json_to_u64, &feerate_our_max),
			JSON_SCAN(json_to_u64, &feerate_our_min),
			JSON_SCAN(json_to_u32, &to_self_delay),
			JSON_SCAN(json_to_u32, &max_accepted_htlcs),
			JSON_SCAN(json_to_u16, &channel_flags),
			JSON_SCAN(json_to_u32, &info->locktime),
			JSON_SCAN(json_to_msat_as_sats, &info->channel_max));

	if (err)
		plugin_err(cmd->plugin,
			   "`openchannel2` payload did not scan %s: %.*s",
			   err, json_tok_full_len(params),
			   json_tok_full(buf, params));

	/* Channel lease info isn't necessarily included, ignore any err */
	json_scan(tmpctx, buf, params,
		  "{openchannel2:{"
		  "requested_lease_msat:%"
		  ",lease_blockheight_start:%"
		  ",node_blockheight:%}}",
		  JSON_SCAN(json_to_msat_as_sats, &info->requested_lease),
		  JSON_SCAN(json_to_u32, &info->lease_blockheight),
		  JSON_SCAN(json_to_u32, &info->node_blockheight));

	/* We don't fund anything that's above or below our feerate */
	if (info->funding_feerate_perkw < feerate_our_min
	    || info->funding_feerate_perkw > feerate_our_max) {

		plugin_log(cmd->plugin, LOG_DBG,
			   "their feerate %"PRIu64" is out of"
			   " our bounds (%"PRIu64"-%"PRIu64")",
			   info->funding_feerate_perkw,
			   feerate_our_min,
			   feerate_our_max);

		return command_hook_success(cmd);
	}

	/* If they've requested funds, but we're not actually
	 * supporting requested funds...*/
	if (!current_policy->rates &&
	    !amount_sat_is_zero(info->requested_lease)) {
		struct json_stream *res = jsonrpc_stream_success(cmd);
		json_add_string(res, "result", "reject");
		json_add_string(res, "error_message",
				"Peer requested funds but we're not advertising"
				" liquidity right now");
		return command_finished(cmd, res);
	}


	/* Check that their block height isn't too far behind */
	if (!amount_sat_is_zero(info->requested_lease)) {
		u32 upper_bound, lower_bound;

		/* BOLT- #2:
		 * The receiving node:
		 * - MAY fail the negotiation if:  ...
		 *   - if the `option_will_fund` tlv is present and:
		 *    - the `blockheight` is considered too far in the
		 *      past or future
		 */
		/* We consider 24 hrs too far out */
		upper_bound = info->node_blockheight + 24 * 6;
		lower_bound = info->node_blockheight - 24 * 6;

		/* Check overflow */
		if (upper_bound < info->node_blockheight)
			upper_bound = -1;
		if (lower_bound > info->node_blockheight)
			lower_bound = 0;

		if (upper_bound < info->lease_blockheight
		    || lower_bound > info->lease_blockheight) {

			plugin_log(cmd->plugin, LOG_DBG,
				   "their blockheight %d is out of"
				   " our bounds (ours is %d)",
				   info->lease_blockheight,
				   info->node_blockheight);

			return command_hook_success(cmd);
		}
	}

	/* Figure out what our funds are */
	req = jsonrpc_request_start(cmd,
				    "listfunds",
				    &listfunds_success,
				    &listfunds_failed,
				    info);

	return send_outreq(req);
}

static struct command_result *
datastore_list_fail(struct command *cmd,
		    const char *method,
		    const char *buf,
		    const jsmntok_t *error,
		    struct open_info *info)
{
	struct out_req *req;

	/* Oops, something's broken */
	plugin_log(cmd->plugin, LOG_BROKEN,
		   "`datastore` list failed: %.*s",
		   json_tok_full_len(error),
		   json_tok_full(buf, error));

	/* Figure out what our funds are... same flow
	 * as with openchannel2 callback.  */
	req = jsonrpc_request_start(cmd,
				    "listfunds",
				    &listfunds_success,
				    &listfunds_failed,
				    info);
	return send_outreq(req);
}

static struct command_result *
datastore_list_success(struct command *cmd,
		       const char *method,
		       const char *buf,
		       const jsmntok_t *result,
		       struct open_info *info)
{
	struct out_req *req;
	const char *key, *err;
	const u8 *utxos_bin;
	size_t len, i;
	const jsmntok_t *ds_arr_tok, *ds_result;

	ds_arr_tok = json_get_member(buf, result, "datastore");
	assert(ds_arr_tok->type == JSMN_ARRAY);

	/* There should only be one result */
	utxos_bin = NULL;
	json_for_each_arr(i, ds_result, ds_arr_tok) {
		err = json_scan(tmpctx, buf, ds_result,
				"{key:%,hex:%}",
				JSON_SCAN_TAL(cmd, json_strdup, &key),
				JSON_SCAN_TAL(cmd, json_tok_bin_from_hex,
					      &utxos_bin));

		if (err)
			plugin_err(cmd->plugin,
				   "`listdatastore` payload did"
				   " not scan. %s: %.*s",
				   err, json_tok_full_len(result),
				   json_tok_full(buf, result));

		/* We found the prev utxo list */
		plugin_log(cmd->plugin, LOG_DBG,
			   "Saved utxos for channel (%s)"
			   " pulled from datastore", key);

		/* There should only be one result */
		break;
	}

	/* Resurrect outpoints from stashed binary */
	len = tal_bytelen(utxos_bin);
	while (len > 0) {
		struct bitcoin_outpoint *outpoint =
			tal(info, struct bitcoin_outpoint);
		fromwire_bitcoin_outpoint(&utxos_bin,
					  &len, outpoint);
		/* Cursor gets set to null if above fails */
		if (!utxos_bin)
			plugin_err(cmd->plugin,
				   "Unable to parse saved utxos: %.*s",
				   json_tok_full_len(result),
				   json_tok_full(buf, result));

		if (!info->prev_outs)
			info->prev_outs =
				tal_arr(info, struct bitcoin_outpoint *, 0);

		tal_arr_expand(&info->prev_outs, outpoint);
	}

	req = jsonrpc_request_start(cmd,
				    "listfunds",
				    &listfunds_success,
				    &listfunds_failed,
				    info);
	return send_outreq(req);
}

/* Peer has asked us to RBF */
static struct command_result *
json_rbf_channel_call(struct command *cmd,
		      const char *buf,
		      const jsmntok_t *params)
{
	struct open_info *info = new_open_info(cmd);
	u64 feerate_our_max, feerate_our_min;
	const char *err, *chan_key;
	struct out_req *req;

	info->their_last_funding = tal(info, struct amount_sat);
	info->our_last_funding = tal(info, struct amount_sat);
	err = json_scan(tmpctx, buf, params,
			"{rbf_channel:"
			"{id:%"
			",channel_id:%"
			",their_last_funding_msat:%"
			",their_funding_msat:%"
			",our_last_funding_msat:%"
			",funding_feerate_per_kw:%"
			",feerate_our_max:%"
			",feerate_our_min:%"
			",locktime:%"
			",channel_max_msat:%}}",
			JSON_SCAN(json_to_node_id, &info->id),
			JSON_SCAN(json_to_channel_id, &info->cid),
			JSON_SCAN(json_to_msat_as_sats,
				  info->their_last_funding),
			JSON_SCAN(json_to_msat_as_sats,
				  &info->their_funding),
			JSON_SCAN(json_to_msat_as_sats,
				  info->our_last_funding),
			JSON_SCAN(json_to_u64, &info->funding_feerate_perkw),
			JSON_SCAN(json_to_u64, &feerate_our_max),
			JSON_SCAN(json_to_u64, &feerate_our_min),
			JSON_SCAN(json_to_u32, &info->locktime),
			JSON_SCAN(json_to_msat_as_sats, &info->channel_max));

	if (err)
		plugin_err(cmd->plugin,
			   "`rbf_channel` payload did not scan %s: %.*s",
			   err, json_tok_full_len(params),
			   json_tok_full(buf, params));

	/* Lease info isn't necessarily included, ignore any err */
	/* FIXME: blockheights?? */
	json_scan(tmpctx, buf, params,
		  "{rbf_channel:{"
		  "requested_lease_msat:%}}",
		  JSON_SCAN(json_to_msat_as_sats, &info->requested_lease));

	/* We don't fund anything that's above or below our feerate */
	if (info->funding_feerate_perkw < feerate_our_min
	    || info->funding_feerate_perkw > feerate_our_max) {

		plugin_log(cmd->plugin, LOG_DBG,
			   "their feerate %"PRIu64" is out of"
			   " our bounds (%"PRIu64"-%"PRIu64")",
			   info->funding_feerate_perkw,
			   feerate_our_min,
			   feerate_our_max);

		return command_hook_success(cmd);
	}

	/* Fetch out previous utxos from the datastore */
	req = jsonrpc_request_start(cmd,
				    "listdatastore",
				    &datastore_list_success,
				    &datastore_list_fail,
				    info);
	chan_key = tal_fmt(cmd, "funder/%s",
			   fmt_channel_id(cmd,
					  &info->cid));
	json_add_string(req->js, "key", chan_key);
	return send_outreq(req);
}

static struct command_result *json_disconnect(struct command *cmd,
					      const char *buf,
					      const jsmntok_t *params)
{
	struct node_id id;
	const char *err;

	err = json_scan(tmpctx, buf, params,
			"{disconnect:{id:%}}",
			JSON_SCAN(json_to_node_id, &id));
	if (err)
		plugin_err(cmd->plugin,
			   "`disconnect` notification payload did not"
			   " scan %s: %.*s",
			   err, json_tok_full_len(params),
			   json_tok_full(buf, params));

	plugin_log(cmd->plugin, LOG_DBG,
		   "Cleaning up inflights for peer id %s",
		   fmt_node_id(tmpctx, &id));

	cleanup_peer_pending_opens(cmd, &id);

	return notification_handled(cmd);
}

static struct command_result *
delete_channel_from_datastore(struct command *cmd,
			      struct channel_id *cid)
{
	const struct out_req *req;

	/* Fetch out previous utxos from the datastore.
	 * If we were clever, we'd have some way of tracking
	 * channels that we actually might have data for
	 * but this is much easier */
	req = jsonrpc_request_start(cmd,
				    "deldatastore",
				    &datastore_del_success,
				    &datastore_del_fail,
				    NULL);
	json_add_string(req->js, "key",
			tal_fmt(cmd, "funder/%s",
				fmt_channel_id(cmd, cid)));
	return send_outreq(req);
}

static struct command_result *json_channel_state_changed(struct command *cmd,
							 const char *buf,
							 const jsmntok_t *params)
{
	struct channel_id cid;
	const char *err, *old_state, *new_state;

	old_state = NULL;
	err = json_scan(tmpctx, buf, params,
			"{channel_state_changed:"
			"{channel_id:%"
			",old_state?:%"
			",new_state:%}}",
			JSON_SCAN(json_to_channel_id, &cid),
			JSON_SCAN_TAL(cmd, json_strdup, &old_state),
			JSON_SCAN_TAL(cmd, json_strdup, &new_state));

	if (err)
		plugin_err(cmd->plugin,
			   "`channel_state_changed` notification payload did"
			   " not scan %s: %.*s",
			   err, json_tok_full_len(params),
			   json_tok_full(buf, params));

	/* Moving out of "awaiting lockin",
	 * means we clean up the datastore */
	/* FIXME: splicing state? */
	if (!old_state)
		return notification_handled(cmd);

	if (!streq(old_state, "DUALOPEND_AWAITING_LOCKIN")
	    && !streq(old_state, "CHANNELD_AWAITING_LOCKIN"))
		return notification_handled(cmd);

	plugin_log(cmd->plugin, LOG_DBG,
		   "Cleaning up datastore for channel_id %s",
		   fmt_channel_id(tmpctx, &cid));

	return delete_channel_from_datastore(cmd, &cid);
}

static struct command_result *json_channel_open_failed(struct command *cmd,
						       const char *buf,
						       const jsmntok_t *params)
{
	struct channel_id cid;
	struct pending_open *open;
	const char *err;

	err = json_scan(tmpctx, buf, params,
			"{channel_open_failed:"
			"{channel_id:%}}",
			JSON_SCAN(json_to_channel_id, &cid));
	if (err)
		plugin_err(cmd->plugin,
			   "`channel_open_failed` notification payload did"
			   " not scan %s: %.*s",
			   err, json_tok_full_len(params),
			   json_tok_full(buf, params));

	plugin_log(cmd->plugin, LOG_DBG,
		   "Cleaning up inflight for channel_id %s",
		   fmt_channel_id(tmpctx, &cid));

	open = find_channel_pending_open(&cid);
	if (open)
		unreserve_psbt(cmd, open);

	/* Also clean up datastore for this channel */
	return delete_channel_from_datastore(cmd, &cid);
}

static void json_add_policy(struct json_stream *stream,
			    struct funder_policy *policy)
{
	json_add_string(stream, "summary",
			funder_policy_desc(stream, current_policy));
	json_add_string(stream, "policy",
			funder_opt_name(policy->opt));
	json_add_num(stream, "policy_mod", policy->mod);
	json_add_bool(stream, "leases_only", policy->leases_only);
	json_add_amount_sat_msat(stream, "min_their_funding_msat",
				 policy->min_their_funding);
	json_add_amount_sat_msat(stream, "max_their_funding_msat",
				 policy->max_their_funding);
	json_add_amount_sat_msat(stream, "per_channel_min_msat",
				 policy->per_channel_min);
	json_add_amount_sat_msat(stream, "per_channel_max_msat",
				 policy->per_channel_max);
	json_add_amount_sat_msat(stream, "reserve_tank_msat",
				 policy->reserve_tank);
	json_add_num(stream, "fuzz_percent", policy->fuzz_factor);
	json_add_num(stream, "fund_probability", policy->fund_probability);

	if (policy->rates) {
		json_add_lease_rates(stream, policy->rates);
		json_add_string(stream, "compact_lease",
				lease_rates_tohex(tmpctx, policy->rates));
	}
}

static struct command_result *
param_funder_opt(struct command *cmd, const char *name,
		 const char *buffer, const jsmntok_t *tok,
		 enum funder_opt **opt)
{
	char *opt_str, *err;

	*opt = tal(cmd, enum funder_opt);
	opt_str = tal_strndup(cmd, buffer + tok->start,
			      tok->end - tok->start);

	err = funding_option(cmd, opt_str, false, *opt);
	if (err)
		return command_fail_badparam(cmd, name, buffer, tok, err);

	return NULL;
}


static struct command_result *
param_policy_mod(struct command *cmd, const char *name,
		 const char *buffer, const jsmntok_t *tok,
		 u64 **mod)
{
	struct amount_sat sats;
	char *arg_str, *err;

	*mod = tal(cmd, u64);
	arg_str = tal_strndup(cmd, buffer + tok->start,
			      tok->end - tok->start);

	err = u64_option(cmd, arg_str, false, *mod);
	if (err) {
		tal_free(err);
		if (!parse_amount_sat(&sats, arg_str, strlen(arg_str)))
			return command_fail_badparam(cmd, name,
						     buffer, tok, err);

		**mod = sats.satoshis; /* Raw: convert to u64 */
	}

	return NULL;
}

static struct command_result *
parse_lease_rates(struct command *cmd, const char *buffer,
		  const jsmntok_t *tok,
		  struct funder_policy *policy,
		  struct funder_policy *current_policy,
		  u32 *lease_fee_basis,
		  struct amount_sat *lease_fee_sats,
		  u32 *funding_weight,
		  u32 *channel_fee_max_proportional_thousandths,
		  struct amount_msat *chan_fee_msats)

{
	/* If there's already rates set, we start with those */
	if (!lease_rates_empty(current_policy->rates))
		policy->rates = tal_dup(policy, struct lease_rates,
					current_policy->rates);
	else if (lease_fee_basis
		 || lease_fee_sats
		 || funding_weight
		 || channel_fee_max_proportional_thousandths
		 || chan_fee_msats)
		policy->rates = default_lease_rates(policy);
	else
		policy->rates = NULL;

	/* Sometimes a local macro is neater than the alternative */
#define ASSIGN_OR_RETURN_FAIL(type, member)				\
	do {								\
		if (member &&						\
		    !assign_overflow_##type(&policy->rates->member, *member)) \
			return command_fail_badparam(cmd, #member,	\
						     buffer, tok, "overflow"); \
} while(0)

	ASSIGN_OR_RETURN_FAIL(u16, lease_fee_basis);
	ASSIGN_OR_RETURN_FAIL(u16, funding_weight);
	ASSIGN_OR_RETURN_FAIL(u16, channel_fee_max_proportional_thousandths);
#undef ASSIGN_OR_RETURN_FAIL

	if (chan_fee_msats
	    && !assign_overflow_u32(&policy->rates->channel_fee_max_base_msat,
				    chan_fee_msats->millisatoshis /* Raw: conversion */)) {
		return command_fail_badparam(cmd, "channel_fee_max_base_msat",
					     buffer, tok, "overflow");
	}
	if (lease_fee_sats
	    && !assign_overflow_u32(&policy->rates->lease_fee_base_sat,
				    lease_fee_sats->satoshis /* Raw: conversion */)) {
		return command_fail_badparam(cmd, "lease_fee_base_sat",
					     buffer, tok, "overflow");
	}

	return NULL;
}

static struct command_result *
leaserates_set(struct command *cmd,
	       const char *method,
	       const char *buf,
	       const jsmntok_t *result,
	       struct funder_policy *policy)
{
	struct json_stream *res;

	/* Ok, we updated lightningd with latest info */
	res = jsonrpc_stream_success(cmd);
	json_add_policy(res, policy);
	return command_finished(cmd, res);
}

static struct command_result *
json_funderupdate(struct command *cmd,
		  const char *buf,
		  const jsmntok_t *params)
{
	struct amount_sat *min_their_funding, *max_their_funding,
			  *per_channel_min, *per_channel_max,
			  *reserve_tank, *lease_fee_sats;
	struct amount_msat *channel_fee_msats;
	u32 *fuzz_factor, *fund_probability, *chan_fee_ppt,
	    *lease_fee_basis, *funding_weight;
	u64 *mod;
	bool *leases_only;
	enum funder_opt *opt;
	const struct out_req *req;
	const char *err;
	struct command_result *res;
	struct funder_policy *policy = tal(cmd, struct funder_policy);

	if (!param(cmd, buf, params,
		   p_opt_def("policy", param_funder_opt, &opt,
			     current_policy->opt),
		   p_opt_def("policy_mod", param_policy_mod, &mod,
			     current_policy->mod),
		   p_opt_def("leases_only", param_bool, &leases_only,
			     current_policy->leases_only),
		   p_opt_def("min_their_funding_msat", param_msat_as_sat,
			     &min_their_funding,
			     current_policy->min_their_funding),
		   p_opt_def("max_their_funding_msat", param_msat_as_sat,
			     &max_their_funding,
			     current_policy->max_their_funding),
		   p_opt_def("per_channel_min_msat", param_msat_as_sat,
			     &per_channel_min,
			     current_policy->per_channel_min),
		   p_opt_def("per_channel_max_msat", param_msat_as_sat,
			     &per_channel_max,
			     current_policy->per_channel_max),
		   p_opt_def("reserve_tank_msat", param_msat_as_sat, &reserve_tank,
			     current_policy->reserve_tank),
		   p_opt_def("fuzz_percent", param_number,
			     &fuzz_factor,
			     current_policy->fuzz_factor),
		   p_opt_def("fund_probability", param_number,
			     &fund_probability,
			     current_policy->fund_probability),
		   p_opt("lease_fee_base_msat", param_msat_as_sat, &lease_fee_sats),
		   p_opt("lease_fee_basis", param_number, &lease_fee_basis),
		   p_opt("funding_weight", param_number, &funding_weight),
		   p_opt("channel_fee_max_base_msat", param_msat,
			 &channel_fee_msats),
		   p_opt("channel_fee_max_proportional_thousandths",
			 param_number, &chan_fee_ppt),
		   NULL))
		return command_param_failed();

	policy->opt = *opt;
	policy->mod = *mod;
	policy->min_their_funding = *min_their_funding;
	policy->max_their_funding = *max_their_funding;
	policy->per_channel_min = *per_channel_min;
	policy->per_channel_max = *per_channel_max;
	policy->reserve_tank = *reserve_tank;
	policy->fuzz_factor = *fuzz_factor;
	policy->fund_probability = *fund_probability;
	policy->leases_only = *leases_only;

	res = parse_lease_rates(cmd, buf, params,
				policy, current_policy,
				lease_fee_basis,
				lease_fee_sats,
				funding_weight,
				chan_fee_ppt,
				channel_fee_msats);
	if (res)
		return res;

	err = funder_check_policy(policy);
	if (err) {
		tal_free(policy);
		return command_done_err(cmd, JSONRPC2_INVALID_PARAMS,
					err, NULL);
	}

	tal_free(current_policy);
	current_policy = tal_steal(NULL, policy);

	/* Update lightningd, also */
	req = jsonrpc_request_start(cmd,
				    "setleaserates",
				    &leaserates_set,
				    &forward_error,
				    current_policy);

	if (current_policy->rates)
		json_add_lease_rates(req->js, current_policy->rates);
	else {
		/* Add empty rates to turn off */
		struct lease_rates rates;
		memset(&rates, 0, sizeof(rates));
		json_add_lease_rates(req->js, &rates);
	}

	return send_outreq(req);
}

static const struct plugin_command commands[] = {
	{
		"funderupdate",
		json_funderupdate
	},
};

static void tell_lightningd_lease_rates(struct command *init_cmd,
					struct lease_rates *rates)
{
	struct json_out *jout;
	struct amount_msat mval;

	/* Tell lightningd with our lease rates*/
	jout = json_out_new(NULL);
	json_out_start(jout, NULL, '{');

	mval = amount_msat(rates->lease_fee_base_sat * 1000);
	json_out_addstr(jout, "lease_fee_base_msat",
			fmt_amount_msat(tmpctx, mval));
	json_out_add(jout, "lease_fee_basis", false,
		     "%d", rates->lease_fee_basis);

	json_out_add(jout, "funding_weight", false,
		     "%d", rates->funding_weight);

	mval = amount_msat(rates->channel_fee_max_base_msat);
	json_out_addstr(jout, "channel_fee_max_base_msat",
			fmt_amount_msat(tmpctx, mval));
	json_out_add(jout, "channel_fee_max_proportional_thousandths", false,
		     "%d", rates->channel_fee_max_proportional_thousandths);

	json_out_end(jout, '}');
	json_out_finished(jout);

	rpc_scan(init_cmd, "setleaserates", take(jout),
		 /* Unused */
		 "{lease_fee_base_msat:%}",
		 JSON_SCAN(json_to_msat, &mval));

}

static void memleak_mark(struct plugin *p, struct htable *memtable)
{
	memleak_scan_list_head(memtable, &pending_opens);
	memleak_scan_obj(memtable, current_policy);
}

static const char *init(struct command *init_cmd, const char *b, const jsmntok_t *t)
{
	const char *err;
	struct amount_msat msat;

	list_head_init(&pending_opens);

	err = funder_check_policy(current_policy);
	if (err)
		plugin_err(init_cmd->plugin, "Invalid parameter combination: %s", err);

	if (current_policy->rates)
		tell_lightningd_lease_rates(init_cmd, current_policy->rates);

	rpc_scan(init_cmd, "listconfigs",
		 take(json_out_obj(NULL, NULL, NULL)),
		 "{configs:"
		 "{min-emergency-msat:{value_msat:%}}}",
		 JSON_SCAN(json_to_msat, &msat));

	emergency_reserve = amount_msat_to_sat_round_down(msat);
	plugin_set_memleak_handler(init_cmd->plugin, memleak_mark);

	return NULL;
}

const struct plugin_hook hooks[] = {
	{
		"openchannel2",
		json_openchannel2_call,
	},
	{
		"openchannel2_changed",
		json_openchannel2_changed_call,
	},
	{
		"openchannel2_sign",
		json_openchannel2_sign_call,
	},
	{
		"rbf_channel",
		json_rbf_channel_call,
	},
};

const struct plugin_notification notifs[] = {
	{
		"channel_open_failed",
		json_channel_open_failed,
	},
	{
		"disconnect",
		json_disconnect,
	},
	{
		"channel_state_changed",
		json_channel_state_changed,
	},
};

static char *option_channel_base(struct command *cmd, const char *arg,
				 bool check_only, struct funder_policy *policy)
{
	struct amount_msat amt;
	u32 cfmbm;

	if (!parse_amount_msat(&amt, arg, strlen(arg)))
		return tal_fmt(tmpctx, "Unable to parse amount '%s'", arg);

	if (!assign_overflow_u32(&cfmbm, amt.millisatoshis)) /* Raw: conversion */
		return tal_fmt(tmpctx, "channel_fee_max_base_msat overflowed");

	if (!check_only) {
		if (!policy->rates)
			policy->rates = default_lease_rates(policy);
		policy->rates->channel_fee_max_base_msat = cfmbm;
	}

	return NULL;
}

static char *
option_channel_fee_proportional_thousandths_max(struct command *cmd,
						const char *arg,
						bool check_only,
						struct funder_policy *policy)
{
	u16 fptm;
	char *problem = u16_option(cmd, arg, false, &fptm);

	if (problem || check_only)
		return problem;

	if (!policy->rates)
		policy->rates = default_lease_rates(policy);
	policy->rates->channel_fee_max_proportional_thousandths = fptm;
	return NULL;
}

static char *amount_option(struct command *cmd, const char *arg, bool check_only,
			   struct amount_sat *amt)
{
	struct amount_sat v;
	if (!parse_amount_sat(&v, arg, strlen(arg)))
		return tal_fmt(tmpctx, "Unable to parse amount '%s'", arg);

	if (!check_only)
		*amt = v;
	return NULL;
}

static bool jsonfmt_amount_sat(struct command *cmd,
			       struct json_stream *js,
			       const char *fieldname,
			       struct amount_sat *sats)
{
	/* We do not expose raw numbers for sats fields: raw numbers
	 * in our interface means MSAT! */
	json_add_str_fmt(js, fieldname, "%"PRIu64"sat", sats->satoshis /* Raw: fmt */);
	return true;
}

static char *option_lease_fee_base(struct command *cmd, const char *arg,
				   bool check_only,
				   struct funder_policy *policy)
{
	struct amount_sat amt;
	u32 lfbs;
	char *err;

	err = amount_option(cmd, arg, false, &amt);
	if (err)
		return err;

	if (!assign_overflow_u32(&lfbs, amt.satoshis)) /* Raw: conversion */
		return tal_fmt(tmpctx, "lease_fee_base_sat overflowed");

	if (!check_only) {
		if (!policy->rates)
			policy->rates = default_lease_rates(policy);

		policy->rates->lease_fee_base_sat = lfbs;
	}

	return NULL;
}

static char *option_lease_fee_basis(struct command *cmd, const char *arg,
				    bool check_only,
				    struct funder_policy *policy)
{
	u16 lfb;
	char *problem = u16_option(cmd, arg, false, &lfb);

	if (problem || check_only)
		return problem;

	if (!policy->rates)
		policy->rates = default_lease_rates(policy);
	policy->rates->lease_fee_basis = lfb;
	return NULL;
}

static char *option_lease_weight_max(struct command *cmd, const char *arg,
				     bool check_only,
				     struct funder_policy *policy)
{
	u16 fw;
	char *problem = u16_option(cmd, arg, false, &fw);

	if (problem || check_only)
		return problem;

	if (!policy->rates)
		policy->rates = default_lease_rates(policy);
	policy->rates->funding_weight = fw;
	return NULL;
}

static char *amount_sat_or_u64_option(struct command *cmd,
				      const char *arg,
				      bool check_only,
				      u64 *amt)
{
	struct amount_sat sats;
	char *err;

	err = u64_option(cmd, arg, false, &sats.satoshis); /* Raw: want sats below */
	if (err) {
		tal_free(err);
		if (!parse_amount_sat(&sats, arg, strlen(arg)))
			return tal_fmt(tmpctx,
				       "Unable to parse option '%s'",
				       arg);
	}

	if (!check_only)
		*amt = sats.satoshis; /* Raw: convert to u64 */

	return NULL;
}

static bool jsonfmt_policy_mod(struct command *cmd,
			       struct json_stream *js,
			       const char *fieldname,
			       u64 *amt)
{
	json_add_u64(js, fieldname, *amt);
	return true;
}

int main(int argc, char **argv)
{
	setup_locale();

	/* Our default funding policy is fixed (0msat) */
	current_policy = default_funder_policy(NULL, FIXED, 0);

	plugin_main(argv, init, NULL, PLUGIN_RESTARTABLE, true,
		    NULL,
		    commands, ARRAY_SIZE(commands),
		    notifs, ARRAY_SIZE(notifs),
		    hooks, ARRAY_SIZE(hooks),
		    NULL, 0,
		    plugin_option("funder-policy",
				  "string",
				  "Policy to use for dual-funding requests."
				  " [match, available, fixed]",
				  funding_option,
				  jsonfmt_funding_option,
				  &current_policy->opt),
		    plugin_option("funder-policy-mod",
				  "string",
				  "Percent to apply policy at"
				  " (match/available); or amount to fund"
				  " (fixed)",
				  amount_sat_or_u64_option,
				  jsonfmt_policy_mod,
				  &current_policy->mod),
		    plugin_option("funder-min-their-funding",
				  "string",
				  "Minimum funding peer must open with"
				  " to activate our policy",
				  amount_option,
				  jsonfmt_amount_sat,
				  &current_policy->min_their_funding),
		    plugin_option("funder-max-their-funding",
				  "string",
				  "Maximum funding peer may open with"
				  " to activate our policy",
				  amount_option,
				  jsonfmt_amount_sat,
				  &current_policy->max_their_funding),
		    plugin_option("funder-per-channel-min",
				  "string",
				  "Minimum funding we'll add to a channel."
				  " If we can't meet this, we don't fund",
				  amount_option,
				  jsonfmt_amount_sat,
				  &current_policy->per_channel_min),
		    plugin_option("funder-per-channel-max",
				  "string",
				  "Maximum funding we'll add to a channel."
				  " We cap all contributions to this",
				  amount_option,
				  jsonfmt_amount_sat,
				  &current_policy->per_channel_max),
		    plugin_option("funder-reserve-tank",
				  "string",
				  "Amount of funds we'll always leave"
				  " available.",
				  amount_option,
				  jsonfmt_amount_sat,
				  &current_policy->reserve_tank),
		    plugin_option("funder-fuzz-percent",
				  "int",
				  "Percent to fuzz the policy contribution by."
				  " Defaults to 0%. Max is 100%",
				  u32_option, u32_jsonfmt,
				  &current_policy->fuzz_factor),
		    plugin_option("funder-fund-probability",
				  "int",
				  "Percent of requests to consider."
				  " Defaults to 100%. Setting to 0% will"
				  " disable dual-funding",
				  u32_option, u32_jsonfmt,
				  &current_policy->fund_probability),
		    plugin_option("funder-lease-requests-only",
				  "bool",
				  "Only fund lease requests. Defaults to"
				  " true if channel lease rates are"
				  " being advertised",
				  bool_option, bool_jsonfmt,
				  &current_policy->leases_only),
		    plugin_option("lease-fee-base-sat",
				  "string",
				  "Channel lease rates, base fee for leased"
				  " funds, in satoshi.",
				  option_lease_fee_base,
				  NULL,
				  current_policy),
		    plugin_option("lease-fee-basis",
				  "int",
				  "Channel lease rates, basis charged"
				  " for leased funds (per 10,000 satoshi.)",
				  option_lease_fee_basis,
				  NULL,
				  current_policy),
		    plugin_option("lease-funding-weight",
				  "int",
				  "Channel lease rates, weight"
				  " we'll ask opening peer to pay for in"
				  " funding transaction",
				  option_lease_weight_max,
				  NULL,
				  current_policy),
		    plugin_option("channel-fee-max-base-msat",
				  "string",
				  "Channel lease rates, maximum channel"
				  " fee base we'll charge for funds"
				  " routed through a leased channel.",
				  option_channel_base,
				  NULL,
				  current_policy),
		    plugin_option("channel-fee-max-proportional-thousandths",
				  "int",
				  "Channel lease rates, maximum"
				  " proportional fee (in thousandths, or ppt)"
				  " we'll charge for funds routed through a"
				  " leased channel. Note: 1ppt = 1,000ppm",
				  option_channel_fee_proportional_thousandths_max,
				  NULL,
				  current_policy),
		    NULL);

	tal_free(current_policy);
	return 0;
}
