#include "config.h"
#include "common/json_filter.c"
#include "test_utils.h"

#include <bitcoin/tx.h>
#include <ccan/tal/str/str.h>
#include <common/clock_time.h>
#include <common/coin_mvt.h>
#include <common/daemon.h>
#include <common/deprecation.h>
#include <common/fee_states.h>
#include <common/htlc.h>
#include <common/json_param.h>
#include <common/json_parse_simple.h>
#include <common/json_stream.h>
#include <common/plugin.h>
#include <common/setup.h>
#include <common/trace.h>
#include <common/utils.h>
#include <plugins/bkpr/account.h>
#include <plugins/bkpr/account_entry.h>
#include <plugins/bkpr/bookkeeper.h>
#include <plugins/bkpr/chain_event.h>
#include <plugins/bkpr/channel_event.h>
#include <plugins/bkpr/onchain_fee.h>
#include <plugins/bkpr/recorder.h>
#include <plugins/libplugin.h>
#include <stdio.h>
#include <unistd.h>
#include <wire/wire.h>

#include "plugins/bkpr/account.c"
#include "plugins/bkpr/blockheights.c"
#include "plugins/bkpr/channel_event.c"
#include "plugins/bkpr/chain_event.c"
#include "plugins/bkpr/recorder.c"
#include "plugins/bkpr/onchain_fee.c"
#include "plugins/bkpr/rebalances.c"
#include "plugins/bkpr/sql.c"

/* AUTOGENERATED MOCKS START */
/* Generated stub for chain_event_description */
const char *chain_event_description(const struct bkpr *bkpr UNNEEDED,
				    const struct chain_event *ce UNNEEDED)
{ fprintf(stderr, "chain_event_description called!\n"); abort(); }
/* Generated stub for channel_event_description */
const char *channel_event_description(const struct bkpr *bkpr UNNEEDED,
				      const struct channel_event *ce UNNEEDED)
{ fprintf(stderr, "channel_event_description called!\n"); abort(); }
/* Generated stub for command_check_only */
bool command_check_only(const struct command *cmd UNNEEDED)
{ fprintf(stderr, "command_check_only called!\n"); abort(); }
/* Generated stub for command_dev_apis */
bool command_dev_apis(const struct command *cmd UNNEEDED)
{ fprintf(stderr, "command_dev_apis called!\n"); abort(); }
/* Generated stub for command_fail */
struct command_result *command_fail(struct command *cmd UNNEEDED, enum jsonrpc_errcode code UNNEEDED,
				    const char *fmt UNNEEDED, ...)

{ fprintf(stderr, "command_fail called!\n"); abort(); }
/* Generated stub for command_filter_ptr */
struct json_filter **command_filter_ptr(struct command *cmd UNNEEDED)
{ fprintf(stderr, "command_filter_ptr called!\n"); abort(); }
/* Generated stub for command_log */
void command_log(struct command *cmd UNNEEDED, enum log_level level UNNEEDED,
		 const char *fmt UNNEEDED, ...)

{ fprintf(stderr, "command_log called!\n"); abort(); }
/* Generated stub for json_add_currencyrate */
void json_add_currencyrate(struct json_stream *result UNNEEDED,
			   const char *fieldname UNNEEDED,
			   const struct bkpr *bkpr UNNEEDED,
			   u64 timestamp UNNEEDED)
{ fprintf(stderr, "json_add_currencyrate called!\n"); abort(); }
/* Generated stub for plugin_err */
void   plugin_err(struct plugin *p UNNEEDED, const char *fmt UNNEEDED, ...)
{ fprintf(stderr, "plugin_err called!\n"); abort(); }
/* Generated stub for plugin_log */
void plugin_log(struct plugin *p UNNEEDED, enum log_level l UNNEEDED, const char *fmt UNNEEDED, ...)
{ fprintf(stderr, "plugin_log called!\n"); abort(); }
/* AUTOGENERATED MOCKS END */

struct command_result *jsonrpc_set_datastore_(struct command *cmd UNNEEDED,
					      const char **keys UNNEEDED,
					      const void *value UNNEEDED,
					      int len_or_str UNNEEDED,
					      const char *mode UNNEEDED,
					      struct command_result *(*cb)(struct command *command,
									   const char *method,
									   const char *buf,
									   const jsmntok_t *result,
									   void *arg),
					      struct command_result *(*errcb)(struct command *command UNNEEDED,
									      const char *method UNNEEDED,
									      const char *buf UNNEEDED,
									      const jsmntok_t *result UNNEEDED,
									      void *arg) UNNEEDED,
					      void *arg)

{
	return cb(cmd, NULL, NULL, NULL, arg);
}

struct command_result *ignore_datastore_reply(struct command *cmd,
					      const char *method UNNEEDED,
					      const char *buf UNNEEDED,
					      const jsmntok_t *result UNNEEDED,
					      void *arg UNNEEDED)
{
	return NULL;
}

struct json_out *json_out_obj(const tal_t *ctx,
			      const char *fieldname,
			      const char *str)
{
	struct json_out *jout = json_out_new(ctx);
	json_out_start(jout, NULL, '{');
	if (str)
		json_out_addstr(jout, fieldname, str);
	tal_free_if_taken(str);
	json_out_end(jout, '}');
	json_out_finished(jout);

	return jout;
}

#if HAVE_SQLITE3
#include <sqlite3.h>

static sqlite3 *bkpr_db;
static struct command *cmd;

/* Stolen from old plugins/bkpr/db.c */
struct migration {
	const char *sql;
	void (*func)(struct plugin *p, struct db *db);
};

static struct migration db_migrations[] = {
	{SQL("CREATE TABLE version (version INTEGER);"), NULL},
	{SQL("INSERT INTO version VALUES (1);"), NULL},
	{SQL("CREATE TABLE vars ("
		"  name TEXT"
		", val TEXT"
		", intval INTEGER"
		", blobval BLOB"
		", PRIMARY KEY (name)"
		");"),
	NULL},
	{SQL("INSERT INTO vars ("
		"  name"
		", intval"
		") VALUES ("
		" 'data_version'"
		", 0"
		");"),
	NULL},
	{SQL("CREATE TABLE accounts ("
		"  id INTEGER"
		", name TEXT"
		", peer_id BLOB"
		", opened_event_id INTEGER"
		", closed_event_id INTEGER"
		", onchain_resolved_block INTEGER"
		", is_wallet INTEGER"
		", we_opened INTEGER"
		", leased INTEGER"
		", PRIMARY KEY (id)"
		");"),
	NULL},
	{SQL("CREATE TABLE chainmoves ("
		"  created_index INTEGER"
		", account_id TEXT"
		", primary_tag TEXT"
		", credit_msat INTEGER"
		", debit_msat INTEGER"
		", output_msat INTEGER"
		", timestamp INTEGER"
		", blockheight INTEGER"
		", utxo TEXT"
		", payment_hash BLOB"
		", spending_txid BLOB"
		", PRIMARY KEY (created_index)"
		");"),
	NULL},
	{SQL("CREATE TABLE chainmoves_extra_tags ("
	     " row INTEGER"
	     ", arrindex INTEGER"
	     ", extra_tags TEXT)"), NULL},
	{SQL("CREATE TABLE channelmoves ("
		"  created_index INTEGER"
		", account_id TEXT"
		", primary_tag TEXT"
		", credit_msat INTEGER"
		", debit_msat INTEGER"
		", fees_msat INTEGER"
		", payment_hash BLOB"
		", part_id INTEGER"
		", timestamp INTEGER"
		", PRIMARY KEY (created_index)"
		");"),
	NULL},
	{SQL("CREATE TABLE onchain_fees ("
		"account_id TEXT"
		", txid BLOB"
		", credit INTEGER"
		", debit INTEGER"
		", currency TEXT"
		", timestamp INTEGER"
		", update_count INT"
		", PRIMARY KEY (account_id, txid, update_count)"
		");"),
	NULL},
	{SQL("ALTER TABLE chainmoves ADD originating_account TEXT;"), NULL},
	{SQL("ALTER TABLE accounts ADD closed_count INTEGER DEFAULT 0;"), NULL},
	{SQL("ALTER TABLE chainmoves ADD ignored INTEGER;"), NULL},
	{SQL("ALTER TABLE chainmoves ADD stealable INTEGER;"), NULL},
	{SQL("ALTER TABLE chainmoves ADD ev_desc TEXT DEFAULT NULL;"), NULL},
	{SQL("ALTER TABLE channelmoves ADD ev_desc TEXT DEFAULT NULL;"), NULL},
	{SQL("ALTER TABLE channelmoves ADD rebalance_id INTEGER DEFAULT NULL;"), NULL},
	{SQL("ALTER TABLE chainmoves ADD spliced INTEGER DEFAULT 0;"), NULL},
};

#undef db_begin_transaction
#define db_begin_transaction(db)
#define db_commit_transaction(db)

static bool db_migrate(struct plugin *p, sqlite3 *db)
{
	sqlite3_stmt *s;
	for (size_t i = 0; i < ARRAY_SIZE(db_migrations); i++) {
		int rc;
		sqlite3_prepare_v2(db, db_migrations[i].sql, -1, &s, NULL);
		rc = sqlite3_step(s);
		assert(rc == SQLITE_DONE);
		sqlite3_finalize(s);
	}
	return true;
}

static sqlite3 *db_setup(struct plugin *p, const char *db_dsn)
{
	sqlite3_stmt *stmt;
	sqlite3 *sql;
	int err;

	err = sqlite3_open_v2(db_dsn, &sql, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, NULL);
	assert(err == SQLITE_OK);

	err = sqlite3_extended_result_codes(sql, 1);
	assert(err == SQLITE_OK);

	sqlite3_busy_timeout(sql, 60000);
	sqlite3_prepare_v2(sql,
			   "PRAGMA foreign_keys = ON;", -1, &stmt, NULL);
	err = sqlite3_step(stmt);
	assert(err == SQLITE_DONE);
	sqlite3_finalize(stmt);

	db_migrate(p, sql);
	return sql;
}

static int sqlite3_bind_outpoint(sqlite3_stmt *stmt, int pos, const struct bitcoin_outpoint *o)
{
	return sqlite3_bind_text(stmt, pos, fmt_bitcoin_outpoint(tmpctx, o), -1, SQLITE_TRANSIENT);
}

static int sqlite3_bind_txid(sqlite3_stmt *stmt, int pos, const struct bitcoin_txid *txid)
{
	return sqlite3_bind_blob(stmt, pos, txid, sizeof(*txid), SQLITE_TRANSIENT);
}

static int sqlite3_bind_hash(sqlite3_stmt *stmt, int pos, const struct sha256 *hash)
{
	return sqlite3_bind_blob(stmt, pos, hash, sizeof(*hash), SQLITE_TRANSIENT);
}

/* Stolen from old plugins/bkpr/recorder.c */
static bool find_chain_event(sqlite3 *db,
			     const struct account *acct,
			     const struct bitcoin_outpoint *outpoint,
			     const struct bitcoin_txid *spending_txid,
			     const char *tag)

{
	sqlite3_stmt *stmt;
	bool ret;
	int rc;

	if (spending_txid) {
		sqlite3_prepare_v2(db,
				   "SELECT 1"
				   " FROM chainmoves e"
				   " LEFT OUTER JOIN accounts a"
				   " ON e.account_id = a.name"
				   " WHERE "
				   " e.spending_txid = ?"
				   " AND e.account_id = ?"
				   " AND e.utxo = ?",
				   -1, &stmt, NULL);
		rc = sqlite3_bind_txid(stmt, 1, spending_txid);
		assert(rc == SQLITE_OK);
	} else {
		sqlite3_prepare_v2(db,
				   "SELECT 1"
				   " FROM chainmoves e"
				   " LEFT OUTER JOIN accounts a"
				   " ON e.account_id = a.name"
				   " WHERE "
				   " e.primary_tag = ?"
				   " AND e.account_id = ?"
				   " AND e.utxo = ?"
				   " AND e.spending_txid IS NULL",
				   -1, &stmt, NULL);
		rc = sqlite3_bind_text(stmt, 1, tag, -1, SQLITE_TRANSIENT);
		assert(rc == SQLITE_OK);
	}

	rc = sqlite3_bind_text(stmt, 2, acct->name, -1, SQLITE_TRANSIENT);
	assert(rc == SQLITE_OK);
	rc = sqlite3_bind_outpoint(stmt, 3, outpoint);
	assert(rc == SQLITE_OK);

	ret = (sqlite3_step(stmt) == SQLITE_ROW);
	sqlite3_finalize(stmt);
	return ret;
}

static bool log_chain_event(struct bkpr *bkpr,
			    const struct account *acct,
			    struct chain_event *e)
{
	int rc;
	sqlite3_stmt *stmt;

	/* We're responsible for de-duping chain events! */
	if (find_chain_event(bkpr_db, acct,
			     &e->outpoint, e->spending_txid,
			     e->tag))
		return false;
	rc = sqlite3_prepare_v2(bkpr_db,
				"INSERT INTO chainmoves"
				     " ("
				     "  account_id"
				     ", originating_account"
				     ", primary_tag"
				     ", credit_msat"
				     ", debit_msat"
				     ", output_msat"
				     ", timestamp"
				     ", blockheight"
				     ", utxo"
				     ", payment_hash"
				     ", spending_txid"
				     ")"
				     " VALUES "
				"(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);",
				-1, &stmt, NULL);
	assert(rc == SQLITE_OK);

	rc = sqlite3_bind_text(stmt, 1, acct->name, -1, SQLITE_TRANSIENT);
	assert(rc == SQLITE_OK);
	if (e->origin_acct)
		sqlite3_bind_text(stmt, 2, e->origin_acct, -1, SQLITE_TRANSIENT);
	else
		sqlite3_bind_null(stmt, 2);
	rc = sqlite3_bind_text(stmt, 3, e->tag, -1, SQLITE_TRANSIENT);
	assert(rc == SQLITE_OK);
	rc = sqlite3_bind_int64(stmt, 4, (sqlite3_int64)e->credit.millisatoshis /* Raw: db */);
	assert(rc == SQLITE_OK);
	rc = sqlite3_bind_int64(stmt, 5, (sqlite3_int64)e->debit.millisatoshis /* Raw: db */);
	assert(rc == SQLITE_OK);
	rc = sqlite3_bind_int64(stmt, 6, (sqlite3_int64)e->output_value.millisatoshis /* Raw: db */);
	assert(rc == SQLITE_OK);
	rc = sqlite3_bind_int64(stmt, 7, (sqlite3_int64)e->timestamp);
	assert(rc == SQLITE_OK);
	rc = sqlite3_bind_int64(stmt, 8, (sqlite3_int64)e->blockheight);
	assert(rc == SQLITE_OK);
	rc = sqlite3_bind_outpoint(stmt, 9, &e->outpoint);
	assert(rc == SQLITE_OK);
	if (e->payment_id) {
		rc = sqlite3_bind_hash(stmt, 10, e->payment_id);
	} else {
		rc = sqlite3_bind_null(stmt,10);
	}
	assert(rc == SQLITE_OK);
	if (e->spending_txid) {
		rc = sqlite3_bind_txid(stmt, 11, e->spending_txid);
	} else {
		rc = sqlite3_bind_null(stmt, 11);
	}
	assert(rc == SQLITE_OK);

	/* FIXME: Put stealable and spliced into extra_tags! */
	/* Execute */
	rc = sqlite3_step(stmt);
	assert(rc == SQLITE_DONE);

	e->db_id = (u64)sqlite3_last_insert_rowid(bkpr_db);
	e->acct_name = tal_strdup(e, acct->name);
	sqlite3_finalize(stmt);
	return true;
}

/* All chatgpt */
static void log_channel_event(sqlite3 *db,
                              const struct account *acct,
                              struct channel_event *e)
{
    static const char *SQL =
        "INSERT INTO channelmoves ("
        "  account_id, primary_tag, credit_msat, debit_msat, fees_msat, payment_hash, part_id, timestamp"
        ") VALUES (?, ?, ?, ?, ?, ?, ?, ?);";

    sqlite3_stmt *stmt;
    int rc = sqlite3_prepare_v2(db, SQL, -1, &stmt, NULL);
    assert(rc == SQLITE_OK);

    /* 1 account_id (TEXT) */
    rc = sqlite3_bind_text(stmt, 1, acct->name, -1, SQLITE_TRANSIENT);
    assert(rc == SQLITE_OK);

    /* 2 tag (TEXT) */
    rc = sqlite3_bind_text(stmt, 2, e->tag, -1, SQLITE_TRANSIENT);
    assert(rc == SQLITE_OK);

    /* 3 credit (INTEGER) */
    rc = sqlite3_bind_int64(stmt, 3, (sqlite3_int64)e->credit.millisatoshis /* Raw: db */);
    assert(rc == SQLITE_OK);

    /* 4 debit (INTEGER) */
    rc = sqlite3_bind_int64(stmt, 4, (sqlite3_int64)e->debit.millisatoshis /* Raw: db */);
    assert(rc == SQLITE_OK);

    /* 5 fees (INTEGER) */
    rc = sqlite3_bind_int64(stmt, 5, (sqlite3_int64)e->fees.millisatoshis /* Raw: db */);
    assert(rc == SQLITE_OK);

    /* 6 payment_hash (BLOB 32) or NULL */
    if (e->payment_id) {
        rc = sqlite3_bind_blob(stmt, 6, (const void *)e->payment_id,
                               (int)sizeof(*e->payment_id), SQLITE_TRANSIENT);
    } else {
        rc = sqlite3_bind_null(stmt, 6);
    }
    assert(rc == SQLITE_OK);

    /* 7 part_id (INTEGER) */
    rc = sqlite3_bind_int64(stmt, 7, (sqlite3_int64)e->part_id);
    assert(rc == SQLITE_OK);

    /* 8 timestamp (INTEGER) */
    rc = sqlite3_bind_int64(stmt, 8, (sqlite3_int64)e->timestamp);
    assert(rc == SQLITE_OK);

    rc = sqlite3_step(stmt);
    assert(rc == SQLITE_DONE);

    e->db_id = (u64)sqlite3_last_insert_rowid(db);
    e->acct_name = tal_strdup(e, acct->name);

    sqlite3_finalize(stmt);
}

const jsmntok_t *jsonrpc_request_sync(const tal_t *ctx,
				      struct command *cmd,
				      const char *method,
				      const struct json_out *params TAKES,
				      const char **resp)
{
	sqlite3_stmt *s;
	jsmntok_t *toks;
	jsmn_parser parser;
	size_t len;
	char *buf;
	const char *p, *querystr;
	bool complete;
	int rc;

	if (streq(method, "listdatastore")) {
		buf = tal_fmt(ctx, "{\"datastore\": []}");
		goto done;
	}

	assert(streq(method, "sql"));

	/* Grab select query */
	jsmn_init(&parser);
	p = json_out_contents(params, &len);
	toks = toks_alloc(tmpctx);
	assert(json_parse_input(&parser, &toks, p, len, &complete));
	assert(complete);
	/* { "query": <querystr> } */
	querystr = json_strdup(tmpctx, p, toks + 2);

	sqlite3_prepare_v2(bkpr_db, querystr,
			   -1, &s, NULL);

	/* Make JSON-style result */
	buf = tal_fmt(ctx, "{\"rows\": [");
	while ((rc = sqlite3_step(s)) == SQLITE_ROW) {
		size_t num_cols = sqlite3_column_count(s);

		tal_append_fmt(&buf, "[");
		for (size_t i = 0; i < num_cols; i++) {
			switch (sqlite3_column_type(s, i)) {
			case SQLITE_NULL:
				tal_append_fmt(&buf, "null");
				break;
			case SQLITE_INTEGER:
				tal_append_fmt(&buf, "%"PRIu64, (u64)sqlite3_column_int64(s, i));
				break;
			case SQLITE_TEXT:
				tal_append_fmt(&buf, "\"%s\"", sqlite3_column_text(s, i));
				break;
			case SQLITE_BLOB: {
				const void *blob = sqlite3_column_blob(s, i);
				int bloblen = sqlite3_column_bytes(s, i);
				tal_append_fmt(&buf, "\"%s\"", tal_hexstr(tmpctx, blob, bloblen));
				break;
			}
			default:
				abort();
			}
			if (i != num_cols - 1)
				tal_append_fmt(&buf, ",");
		}
		tal_append_fmt(&buf, "]");
	}
	assert(rc == SQLITE_DONE);
	tal_append_fmt(&buf, "]}");
	sqlite3_finalize(s);

done:
	jsmn_init(&parser);
	toks = toks_alloc(ctx);
	assert(json_parse_input(&parser, &toks, buf, strlen(buf), &complete));
	assert(complete);

	tal_free_if_taken(params);
	*resp = buf;
	return toks;
}

static char *tmp_dsn(const tal_t *ctx)
{
	char *dsn, *filename;
	int fd = tmpdir_mkstemp(ctx, "lacct-db-XXXXXX", &filename);
	if (fd == -1)
		return NULL;
	close(fd);

	dsn = tal_fmt(ctx, "%s", filename);
	tal_free(filename);

	return dsn;
}

static struct bkpr *bkpr_setup(const tal_t *ctx)
{
	struct bkpr *bkpr = tal(ctx, struct bkpr);

	if (bkpr_db)
		assert(sqlite3_close(bkpr_db) == SQLITE_OK);

	bkpr_db = db_setup(NULL, tmp_dsn(ctx));
	bkpr->accounts = init_accounts(ctx, NULL);
	bkpr->onchain_fees = init_onchain_fees(bkpr, NULL);
	bkpr->rebalances = init_rebalances(bkpr, NULL);

	/* We need to see everything */
	bkpr->chainmoves_index = bkpr->channelmoves_index = UINT64_MAX;
	return bkpr;
}

static bool accountseq(struct account *a1, struct account *a2)
{
	CHECK(streq(a1->name, a2->name));
	CHECK((a1->peer_id != NULL) == (a2->peer_id != NULL));
	if (a1->peer_id)
		CHECK(node_id_eq(a1->peer_id, a2->peer_id));
	CHECK(a1->is_wallet == a2->is_wallet);
	CHECK(a1->we_opened == a2->we_opened);
	CHECK(a1->leased == a2->leased);
	CHECK(a1->onchain_resolved_block == a2->onchain_resolved_block);

	CHECK((a1->open_event_db_id != NULL) == (a2->open_event_db_id != NULL));
	if (a1->open_event_db_id)
		CHECK(*a1->open_event_db_id == *a2->open_event_db_id);

	CHECK((a1->closed_event_db_id != NULL) == (a2->closed_event_db_id != NULL));
	if (a1->closed_event_db_id)
		CHECK(*a1->closed_event_db_id == *a2->closed_event_db_id);

	CHECK(a1->closed_count == a2->closed_count);

	return true;
}

static bool channel_events_eq(struct channel_event *e1, struct channel_event *e2)
{
	CHECK(e1->db_id == e2->db_id);
	CHECK(streq(e1->acct_name, e2->acct_name));
	CHECK(streq(e1->tag, e2->tag));
	CHECK(amount_msat_eq(e1->credit, e2->credit));
	CHECK(amount_msat_eq(e1->debit, e2->debit));
	CHECK(amount_msat_eq(e1->fees, e2->fees));
	CHECK((e1->payment_id != NULL) == (e2->payment_id != NULL));
	if (e1->payment_id)
		CHECK(sha256_eq(e1->payment_id, e2->payment_id));
	CHECK(e1->part_id == e2->part_id);
	CHECK(e1->timestamp == e2->timestamp);

	return true;
}

static bool chain_events_eq(struct chain_event *e1, struct chain_event *e2)
{
	CHECK(e1->db_id == e2->db_id);
	CHECK(streq(e1->acct_name, e2->acct_name));
	CHECK((e1->origin_acct != NULL) == (e2->origin_acct != NULL));
	if (e1->origin_acct)
		CHECK(streq(e1->origin_acct, e2->origin_acct));
	CHECK(streq(e1->tag, e2->tag));
	CHECK(amount_msat_eq(e1->credit, e2->credit));
	CHECK(amount_msat_eq(e1->debit, e2->debit));
	CHECK(amount_msat_eq(e1->output_value, e2->output_value));
	CHECK(e1->timestamp == e2->timestamp);
	CHECK(e1->blockheight == e2->blockheight);
	CHECK(e1->stealable == e2->stealable);
	CHECK(bitcoin_outpoint_eq(&e1->outpoint, &e2->outpoint));

	CHECK((e1->spending_txid != NULL) == (e2->spending_txid != NULL));
	if (e1->spending_txid)
		CHECK(bitcoin_txid_eq(e1->spending_txid, e2->spending_txid));

	CHECK((e1->payment_id != NULL) == (e2->payment_id != NULL));
	if (e1->payment_id)
		CHECK(sha256_eq(e1->payment_id, e2->payment_id));

	CHECK(e1->splice_close == e2->splice_close);

	return true;
}

static struct channel_event *make_channel_event(const tal_t *ctx,
						char *tag,
						struct amount_msat credit,
						struct amount_msat debit,
						char payment_char)
{
	struct channel_event *ev = tal(ctx, struct channel_event);

	ev->payment_id = tal(ev, struct sha256);
	memset(ev->payment_id, payment_char, sizeof(struct sha256));
	ev->credit = credit;
	ev->debit = debit;
	ev->fees = AMOUNT_MSAT(104);
	ev->timestamp = 1919191;
	ev->part_id = 19;
	ev->tag = tag;
	return ev;
}

static struct chain_event *make_chain_event(const tal_t *ctx,
					    char *tag,
					    struct amount_msat credit,
					    struct amount_msat debit,
					    struct amount_msat output_val,
					    u32 blockheight,
					    char outpoint_char,
					    u32 outnum,
					    /* Note that '*' is magic */
					    char spend_char)


{
	struct chain_event *ev = tal(ctx, struct chain_event);

	/* This event spends the second inserted event */
	ev->tag = tal_fmt(ctx, "%s", tag);
	ev->origin_acct = NULL;
	ev->credit = credit;
	ev->debit = debit;
	ev->output_value = output_val;
	ev->timestamp = 1919191;
	ev->blockheight = blockheight;
	ev->stealable = false;
	ev->splice_close = false;
	memset(&ev->outpoint.txid, outpoint_char, sizeof(struct bitcoin_txid));
	ev->outpoint.n = outnum;

	if (spend_char != '*') {
		ev->spending_txid = tal(ctx, struct bitcoin_txid);
		memset(ev->spending_txid, spend_char,
		       sizeof(struct bitcoin_txid));
	} else
		ev->spending_txid = NULL;

	ev->payment_id = NULL;

	return ev;
}

static bool test_onchain_fee_wallet_spend(const tal_t *ctx)
{
	struct bkpr *bkpr = bkpr_setup(ctx);
	struct node_id node_id, peer_id;
	struct account *wal_acct, *ext_acct;
	struct bitcoin_txid txid;
	struct onchain_fee **ofs;
	u32 blockheight = 100000;

	memset(&node_id, 2, sizeof(struct node_id));
	memset(&peer_id, 3, sizeof(struct node_id));

	wal_acct = new_account(bkpr->accounts, tal_fmt(ctx, ACCOUNT_NAME_WALLET));
	wal_acct->peer_id = &node_id;
	ext_acct = new_account(bkpr->accounts, tal_fmt(ctx, ACCOUNT_NAME_EXTERNAL));
	ext_acct->peer_id = &peer_id;
	memset(&txid, '1', sizeof(struct bitcoin_txid));

	db_begin_transaction(db);
	account_datastore_set(cmd, wal_acct, "must-create");
	account_datastore_set(cmd, ext_acct, "must-create");
	db_commit_transaction(db);


	/* Send funds to an external address
	 * tag     utxo_id vout    txid    debits  credits acct_id
	 * withdr  XXXX    0       1111    1000            wallet
	 * deposit 1111    1                        200    wallet
	 * deposit 1111    0                        700    external
	 */
	db_begin_transaction(db);
	log_chain_event(bkpr, wal_acct,
		make_chain_event(ctx, "withdrawal",
				 AMOUNT_MSAT(0),
				 AMOUNT_MSAT(1000),
				 AMOUNT_MSAT(1000),
				 blockheight,
				 'X', 0, '1'));
	maybe_update_onchain_fees(ctx, cmd, bkpr, &txid);

	log_chain_event(bkpr, wal_acct,
		make_chain_event(ctx, "deposit",
				 AMOUNT_MSAT(200),
				 AMOUNT_MSAT(0),
				 AMOUNT_MSAT(200),
				 blockheight,
				 '1', 1, '*'));
	maybe_update_onchain_fees(ctx, cmd, bkpr, &txid);

	log_chain_event(bkpr, ext_acct,
		make_chain_event(ctx, "deposit",
				 AMOUNT_MSAT(700),
				 AMOUNT_MSAT(0),
				 AMOUNT_MSAT(700),
				 blockheight,
				 '1', 0, '*'));
	maybe_update_onchain_fees(ctx, cmd, bkpr, &txid);
	db_commit_transaction(db);

	ofs = list_chain_fees(ctx, bkpr);
	CHECK(tal_count(ofs) == 2);

	/* we expect 800, then -700 */
	CHECK(amount_msat_eq(ofs[0]->credit, AMOUNT_MSAT(800)));
	CHECK(amount_msat_is_zero(ofs[0]->debit));
	CHECK(ofs[0]->update_count == 1);

	CHECK(amount_msat_is_zero(ofs[1]->credit));
	CHECK(amount_msat_eq(ofs[1]->debit, AMOUNT_MSAT(700)));
	CHECK(ofs[1]->update_count == 2);

	return true;
}

static bool test_onchain_fee_chan_close(const tal_t *ctx)
{
	struct bkpr *bkpr = bkpr_setup(ctx);
	struct node_id node_id, peer_id;
	struct account *acct, *wal_acct, *ext_acct;
	struct onchain_fee **ofs, **ofs1;
	struct bitcoin_txid txid;
	struct chain_event *ev;
	enum mvt_tag *tags;
	u32 close_output_count;
	u32 blockheight = 100000;
	char *err;

	memset(&node_id, 2, sizeof(struct node_id));
	memset(&peer_id, 3, sizeof(struct node_id));
	/* to_us, to_them, 1 htlc, 2 anchors */
	close_output_count = 5;

	wal_acct = new_account(bkpr->accounts, tal_fmt(ctx, ACCOUNT_NAME_WALLET));
	wal_acct->peer_id = &node_id;
	ext_acct = new_account(bkpr->accounts, tal_fmt(ctx, ACCOUNT_NAME_EXTERNAL));
	ext_acct->peer_id = &peer_id;
	acct = new_account(bkpr->accounts, tal_fmt(ctx, "chan-1"));
	acct->peer_id = &peer_id;

	db_begin_transaction(db);
	account_datastore_set(cmd, wal_acct, "must-create");
	account_datastore_set(cmd, ext_acct, "must-create");
	account_datastore_set(cmd, acct, "must-create");
	db_commit_transaction(db);

	/* Close a channel */
	/* tag     utxo_id vout    txid    debits  credits acct_id
	 * close   XXXX    0       1111    1000            wallet
	 * delay   1111    1                        200    chan-1
	 * anchor  1111    0                         30    chan-1
	 * anchor  1111    4                         30    external
	 * to_wall 1111    1       2222     200            chan-1
	 * htlc_tim1111    2                        600    chan-1
	 * htlc_tim1111    2       3333     600            chan-1
	 * to_them 1111    3                        300    external
	 * deposit 2222    0                        150    chan-1
	 * htlc_tx 3333    0                        450    chan-1
	 * to_wall 3333    0       4444     450            chan-1
	 * deposit 4444    0                        350    wallet
	 */
	tags = tal_arr(ctx, enum mvt_tag, 1);
	db_begin_transaction(db);
	ev = make_chain_event(ctx, "channel_open",
				    AMOUNT_MSAT(1000),
				    AMOUNT_MSAT(0),
				    AMOUNT_MSAT(1660),
				    blockheight,
				    'X', 0, '*');
	log_chain_event(bkpr, acct, ev);
	tags[0] = MVT_CHANNEL_OPEN;
	maybe_update_account(cmd, acct, ev, tags, 0, NULL);

	ev = make_chain_event(ctx, "channel_close",
				    AMOUNT_MSAT(0),
				    AMOUNT_MSAT(1000),
				    AMOUNT_MSAT(1660),
				    blockheight,
				    'X', 0, '1');
	log_chain_event(bkpr, acct, ev);

	/* Update the account to have the right info! */
	tags[0] = MVT_CHANNEL_CLOSE;
	maybe_update_account(cmd, acct, ev, tags, close_output_count, NULL);

	log_chain_event(bkpr, acct,
		make_chain_event(ctx, "delayed_to_us",
				 AMOUNT_MSAT(200),
				 AMOUNT_MSAT(0),
				 AMOUNT_MSAT(200),
				 blockheight,
				 '1', 1, '*'));
	memset(&txid, '1', sizeof(struct bitcoin_txid));
	maybe_update_onchain_fees(ctx, cmd, bkpr, &txid);

	log_chain_event(bkpr, wal_acct,
		make_chain_event(ctx, "anchor",
				 AMOUNT_MSAT(30),
				 AMOUNT_MSAT(0),
				 AMOUNT_MSAT(30),
				 blockheight,
				 '1', 0, '*'));
	log_chain_event(bkpr, ext_acct,
		make_chain_event(ctx, "anchor",
				 AMOUNT_MSAT(30),
				 AMOUNT_MSAT(0),
				 AMOUNT_MSAT(30),
				 blockheight,
				 '1', 4, '*'));
	memset(&txid, '1', sizeof(struct bitcoin_txid));
	maybe_update_onchain_fees(ctx, cmd, bkpr, &txid);

	/* Should be no fees yet */
	ofs = list_chain_fees(ctx, bkpr);
	CHECK(tal_count(ofs) == 0);

	log_chain_event(bkpr, acct,
		make_chain_event(ctx, "htlc_timeout",
				 AMOUNT_MSAT(600),
				 AMOUNT_MSAT(0),
				 AMOUNT_MSAT(600),
				 blockheight,
				 '1', 2, '*'));

	log_chain_event(bkpr, ext_acct,
		make_chain_event(ctx, "to_them",
				 AMOUNT_MSAT(300),
				 AMOUNT_MSAT(0),
				 AMOUNT_MSAT(300),
				 blockheight,
				 '1', 3, '*'));

	memset(&txid, '1', sizeof(struct bitcoin_txid));
	maybe_update_onchain_fees(ctx, cmd, bkpr, &txid);
	db_commit_transaction(db);

	/* txid 2222 */
	db_begin_transaction(db);
	log_chain_event(bkpr, acct,
		make_chain_event(ctx, "to_wallet",
				 AMOUNT_MSAT(0),
				 AMOUNT_MSAT(200),
				 AMOUNT_MSAT(200),
				 blockheight + 1,
				 '1', 1, '2'));

	log_chain_event(bkpr, wal_acct,
		make_chain_event(ctx, "deposit",
				 AMOUNT_MSAT(150),
				 AMOUNT_MSAT(0),
				 AMOUNT_MSAT(150),
				 blockheight + 1,
				 '2', 0, '*'));
	memset(&txid, '2', sizeof(struct bitcoin_txid));
	maybe_update_onchain_fees(ctx, cmd, bkpr, &txid);
	CHECK(acct->onchain_resolved_block == 0);

	assert(account_onchain_closeheight(bkpr, cmd, acct) == 0);
	CHECK(acct->onchain_resolved_block == 0);
	db_commit_transaction(db);

	/* Expect: 1 onchain fee records, all for chan-1 */
	ofs = list_chain_fees(ctx, bkpr);
	ofs1 = account_get_chain_fees(tmpctx, bkpr, acct->name);

	CHECK(tal_count(ofs) == tal_count(ofs1));
	CHECK(tal_count(ofs) == 1);

	/* txid 4444 */
	db_begin_transaction(db);
	log_chain_event(bkpr, wal_acct,
		make_chain_event(ctx, "deposit",
				 AMOUNT_MSAT(350),
				 AMOUNT_MSAT(0),
				 AMOUNT_MSAT(350),
				 blockheight + 2,
				 '4', 0, '*'));

	memset(&txid, '4', sizeof(struct bitcoin_txid));
	maybe_update_onchain_fees(ctx, cmd, bkpr, &txid);

	/* txid 3333 */
	log_chain_event(bkpr, acct,
		make_chain_event(ctx, "htlc_timeout",
				 AMOUNT_MSAT(0),
				 AMOUNT_MSAT(600),
				 AMOUNT_MSAT(600),
				 blockheight + 2,
				 '1', 2, '3'));

	assert(account_onchain_closeheight(bkpr, cmd, acct) == 0);
	CHECK(acct->onchain_resolved_block == 0);

	log_chain_event(bkpr, acct,
		make_chain_event(ctx, "htlc_tx",
				 AMOUNT_MSAT(450),
				 AMOUNT_MSAT(0),
				 AMOUNT_MSAT(450),
				 blockheight + 2,
				 '3', 0, '*'));

	memset(&txid, '3', sizeof(struct bitcoin_txid));
	maybe_update_onchain_fees(ctx, cmd, bkpr, &txid);

	log_chain_event(bkpr, acct,
		make_chain_event(ctx, "to_wallet",
				 AMOUNT_MSAT(0),
				 AMOUNT_MSAT(450),
				 AMOUNT_MSAT(450),
				 blockheight + 2,
				 '3', 0, '4'));

	memset(&txid, '4', sizeof(struct bitcoin_txid));
	maybe_update_onchain_fees(ctx, cmd, bkpr, &txid);

	db_commit_transaction(db);

	/* Expect: onchain fee records for tx except channel close */
	ofs = list_chain_fees(ctx, bkpr);
	ofs1 = account_get_chain_fees(tmpctx, bkpr, acct->name);

	CHECK(tal_count(ofs) == tal_count(ofs1));
	CHECK(tal_count(ofs) == 3);

	/* Now we update the channel's onchain fees */
	CHECK(acct->onchain_resolved_block == 0);
	db_begin_transaction(db);
	account_update_closeheight(cmd, acct, account_onchain_closeheight(bkpr, cmd, acct));
	CHECK(acct->onchain_resolved_block == blockheight + 2);
	err = update_channel_onchain_fees(ctx, cmd, bkpr, acct);
	CHECK_MSG(!err, err);
	db_commit_transaction(db);
	ofs = account_get_chain_fees(tmpctx, bkpr, acct->name);

	/* Expect: fees as follows
	 *
	 * chan-1, 1111, 200 (anchor outs ignored)
	 * chan-1, 2222,  50
	 * chan-1, 3333, 150
	 * chan-1, 4444, 100
	 */
	CHECK(tal_count(ofs) == 4);
	for (size_t i = 0; i < tal_count(ofs); i++) {
		CHECK(streq(ofs[i]->acct_name, acct->name));

		memset(&txid, '1', sizeof(struct bitcoin_txid));
		if (bitcoin_txid_eq(&txid, &ofs[i]->txid)) {
			CHECK(ofs[i]->update_count == 1);
			CHECK(amount_msat_eq(ofs[i]->credit, AMOUNT_MSAT(200)));
			CHECK(amount_msat_is_zero(ofs[i]->debit));
			continue;
		}
		memset(&txid, '2', sizeof(struct bitcoin_txid));
		if (bitcoin_txid_eq(&txid, &ofs[i]->txid)) {
			CHECK(ofs[i]->update_count == 1);
			CHECK(amount_msat_eq(ofs[i]->credit, AMOUNT_MSAT(50)));
			CHECK(amount_msat_is_zero(ofs[i]->debit));
			continue;
		}
		memset(&txid, '3', sizeof(struct bitcoin_txid));
		if (bitcoin_txid_eq(&txid, &ofs[i]->txid)) {
			CHECK(ofs[i]->update_count == 1);
			CHECK(amount_msat_eq(ofs[i]->credit, AMOUNT_MSAT(150)));
			CHECK(amount_msat_is_zero(ofs[i]->debit));
			continue;
		}
		memset(&txid, '4', sizeof(struct bitcoin_txid));
		if (bitcoin_txid_eq(&txid, &ofs[i]->txid)) {
			CHECK(ofs[i]->update_count == 1);
			CHECK(amount_msat_eq(ofs[i]->credit, AMOUNT_MSAT(100)));
			CHECK(amount_msat_is_zero(ofs[i]->debit));
			continue;
		}

		CHECK_MSG(false, "txid didn't match");
	}

	return true;
}

static bool test_onchain_fee_chan_open(const tal_t *ctx)
{
	struct bkpr *bkpr = bkpr_setup(ctx);
	struct node_id node_id, peer_id;
	struct account *acct, *acct2, *wal_acct, *ext_acct;
	struct bitcoin_txid txid;
	struct onchain_fee **ofs;
	u32 blockheight = 100000;

	memset(&node_id, 2, sizeof(struct node_id));
	memset(&peer_id, 3, sizeof(struct node_id));

	wal_acct = new_account(bkpr->accounts, tal_fmt(ctx, ACCOUNT_NAME_WALLET));
	wal_acct->peer_id = &peer_id;
	ext_acct = new_account(bkpr->accounts, tal_fmt(ctx, ACCOUNT_NAME_EXTERNAL));
	ext_acct->peer_id = &peer_id;
	acct = new_account(bkpr->accounts, tal_fmt(ctx, "chan-1"));
	acct->peer_id = &peer_id;
	acct2 = new_account(bkpr->accounts, tal_fmt(ctx, "chan-2"));
	acct2->peer_id = &peer_id;

	db_begin_transaction(db);
	account_datastore_set(cmd, wal_acct, "must-create");
	account_datastore_set(cmd, ext_acct, "must-create");
	account_datastore_set(cmd, acct, "must-create");
	account_datastore_set(cmd, acct2, "must-create");
	db_commit_transaction(db);

	/* Assumption that we rely on later */
	CHECK(strcmp(acct->name, acct2->name) < 0);

	/* Open two channels from wallet */
	/* tag     utxo_id vout    txid    debits  credits acct_id
	 * withd   XXXX    0       AAAA    1000            wallet
	 * withd   YYYY    0       AAAA    3001            wallet
	 * open    AAAA    0                        500    chan-1
	 * open    AAAA    1                       1000    chan-2
	 * depo    AAAA    2                       2200    wallet
	 */
	memset(&txid, 'A', sizeof(struct bitcoin_txid));
	db_begin_transaction(db);
	log_chain_event(bkpr, wal_acct,
		make_chain_event(ctx, "withdrawal",
				 AMOUNT_MSAT(0),
				 AMOUNT_MSAT(1000),
				 AMOUNT_MSAT(1000),
				 blockheight,
				 'X', 0, 'A'));
	log_chain_event(bkpr, wal_acct,
		make_chain_event(ctx, "withdrawal",
				 AMOUNT_MSAT(0),
				 AMOUNT_MSAT(3001),
				 AMOUNT_MSAT(3001),
				 blockheight,
				 'Y', 0, 'A'));

	log_chain_event(bkpr, acct,
		make_chain_event(ctx, "deposit",
				 AMOUNT_MSAT(500),
				 AMOUNT_MSAT(0),
				 AMOUNT_MSAT(500),
				 blockheight,
				 'A', 0, '*'));
	maybe_update_onchain_fees(ctx, cmd, bkpr, &txid);

	log_chain_event(bkpr, acct2,
		make_chain_event(ctx, "deposit",
				 AMOUNT_MSAT(1000),
				 AMOUNT_MSAT(0),
				 AMOUNT_MSAT(1000),
				 blockheight,
				 'A', 1, '*'));
	maybe_update_onchain_fees(ctx, cmd, bkpr, &txid);

	log_chain_event(bkpr, wal_acct,
		make_chain_event(ctx, "deposit",
				 AMOUNT_MSAT(2200),
				 AMOUNT_MSAT(0),
				 AMOUNT_MSAT(2200),
				 blockheight,
				 'A', 2, '*'));
	maybe_update_onchain_fees(ctx, cmd, bkpr, &txid);

	maybe_update_onchain_fees(ctx, cmd, bkpr, &txid);
	db_commit_transaction(db);

	/* Expect: 5 onchain fee records, totaling to 151/150msat ea,
	 * none for wallet */
	ofs = list_chain_fees(ctx, bkpr);

	CHECK(tal_count(ofs) == 5);

	struct exp_result {
		u32 credit;
		u32 debit;
		u32 update_count;
	};

	struct exp_result exp_results[] = {
		{ .credit = 3501, .debit = 0, .update_count = 1 },
		{ .credit = 0, .debit = 2250, .update_count = 2 },
		{ .credit = 0, .debit = 1100, .update_count = 3 },
		{ .credit = 1250, .debit = 0, .update_count = 1 },
		{ .credit = 0, .debit = 1100, .update_count = 2 },
	};

	/* Since these are sorted on fetch,
	 * this *should* be stable */
	for (size_t i = 0; i < tal_count(ofs); i++) {
		CHECK(i < ARRAY_SIZE(exp_results));
		CHECK(amount_msat_eq(ofs[i]->credit,
				     amount_msat(exp_results[i].credit)));
		CHECK(amount_msat_eq(ofs[i]->debit,
				     amount_msat(exp_results[i].debit)));
		CHECK(ofs[i]->update_count == exp_results[i].update_count);

		CHECK(bitcoin_txid_eq(&ofs[i]->txid, &txid));
	}

	return true;
}

static bool test_channel_rebalances(const tal_t *ctx)
{
	struct bkpr *bkpr = bkpr_setup(ctx);
	sqlite3 *db = bkpr_db;
	struct channel_event *ev1, *ev2, *ev3, **chan_evs;
	struct rebalance_pair *rebals;
	struct rebalance_htable_iter it;
	struct account *acct1, *acct2, *acct3;
	struct node_id peer_id;

	memset(&peer_id, 3, sizeof(struct node_id));
	acct1 = new_account(bkpr->accounts, tal_fmt(ctx, "one"));
	acct1->peer_id = &peer_id;
	acct2 = new_account(bkpr->accounts, tal_fmt(ctx, "two"));
	acct2->peer_id = &peer_id;
	acct3 = new_account(bkpr->accounts, tal_fmt(ctx, "three"));
	acct3->peer_id = &peer_id;

	db_begin_transaction(db);

	account_datastore_set(cmd, acct1, "must-create");
	account_datastore_set(cmd, acct2, "must-create");
	account_datastore_set(cmd, acct3, "must-create");

	/* Simulate a rebalance of 100msats, w/ a 12msat fee */
	ev1 = make_channel_event(ctx, "invoice",
		           AMOUNT_MSAT(100),
			   AMOUNT_MSAT(0),
			   'A');
	ev1->fees = AMOUNT_MSAT(0);
	log_channel_event(db, acct1, ev1);

	ev2 = make_channel_event(ctx, "invoice",
				 AMOUNT_MSAT(0),
				 AMOUNT_MSAT(112),
				 'A');
	ev2->fees = AMOUNT_MSAT(12);
	log_channel_event(db, acct2, ev2);

	/* Third event w/ same preimage but diff amounts */
	ev3 = make_channel_event(ctx, "invoice",
				 AMOUNT_MSAT(105),
				 AMOUNT_MSAT(0),
				 'A');
	log_channel_event(db, acct3, ev3);
	db_commit_transaction(db);

	db_begin_transaction(db);
	chan_evs = account_get_channel_events(ctx, bkpr, cmd, acct1);
	CHECK(tal_count(chan_evs) == 1 && !find_rebalance(bkpr, chan_evs[0]->db_id));

	chan_evs = account_get_channel_events(ctx, bkpr, cmd, acct2);
	CHECK(tal_count(chan_evs) == 1 && !find_rebalance(bkpr, chan_evs[0]->db_id));

	chan_evs = account_get_channel_events(ctx, bkpr, cmd, acct3);
	CHECK(tal_count(chan_evs) == 1 && !find_rebalance(bkpr, chan_evs[0]->db_id));

	maybe_record_rebalance(cmd, bkpr, ev2);
	CHECK(find_rebalance(bkpr, ev2->db_id) != NULL);

	/* Both events should be marked as rebalance */
	chan_evs = account_get_channel_events(ctx, bkpr, cmd, acct1);
	CHECK(tal_count(chan_evs) == 1 && find_rebalance(bkpr, chan_evs[0]->db_id));
	ev1 = chan_evs[0];
	chan_evs = account_get_channel_events(ctx, bkpr, cmd, acct2);
	CHECK(tal_count(chan_evs) == 1 && find_rebalance(bkpr, chan_evs[0]->db_id));
	CHECK(*find_rebalance(bkpr, chan_evs[0]->db_id) == ev1->db_id);
	CHECK(*find_rebalance(bkpr, ev1->db_id) == chan_evs[0]->db_id);

	/* Third event is not a rebalance though */
	chan_evs = account_get_channel_events(ctx, bkpr, cmd, acct3);
	CHECK(tal_count(chan_evs) == 1 && !find_rebalance(bkpr, chan_evs[0]->db_id));

	/* Did we get an accurate rebalances entry? */
	assert(rebalance_htable_count(bkpr->rebalances->pairs) == 2);
	rebals = rebalance_htable_first(bkpr->rebalances->pairs, &it);

	if (rebals->pair[0] == ev1->db_id) {
		CHECK(rebals->pair[1] == ev2->db_id);
	} else {
		CHECK(rebals->pair[1] == ev1->db_id);
		CHECK(rebals->pair[0] == ev2->db_id);
	}
	db_commit_transaction(db);

	return true;
}

static bool test_channel_event_crud(const tal_t *ctx)
{
	struct bkpr *bkpr = bkpr_setup(ctx);
	sqlite3 *db = bkpr_db;
	struct node_id peer_id;
	struct account *acct, *acct2;
	struct channel_event *ev1, *ev2, *ev3, **chan_evs;

	memset(&peer_id, 3, sizeof(struct node_id));

	acct = new_account(bkpr->accounts, tal_fmt(ctx, "example"));
	acct->peer_id = &peer_id;
	acct2 = new_account(bkpr->accounts, tal_fmt(ctx, ACCOUNT_NAME_WALLET));
	acct2->peer_id = &peer_id;
	db_begin_transaction(db);
	account_datastore_set(cmd, acct, "must-create");
	account_datastore_set(cmd, acct2, "must-create");
	db_commit_transaction(db);

	ev1 = tal(ctx, struct channel_event);
	ev1->payment_id = tal(ev1, struct sha256);
	memset(ev1->payment_id, 'B', sizeof(struct sha256));
	ev1->credit = AMOUNT_MSAT(100);
	ev1->debit = AMOUNT_MSAT(102);
	ev1->fees = AMOUNT_MSAT(104);
	ev1->timestamp = 11111;
	ev1->part_id = 19;

	/* Passing unknown tags in should be ok */
	ev1->tag = "hello";

	ev2 = tal(ctx, struct channel_event);
	ev2->payment_id = tal(ev2, struct sha256);
	memset(ev2->payment_id, 'C', sizeof(struct sha256));
	ev2->credit = AMOUNT_MSAT(200);
	ev2->debit = AMOUNT_MSAT(202);
	ev2->fees = AMOUNT_MSAT(204);
	ev2->timestamp = 22222;
	ev2->part_id = 0;
	ev2->tag = tal_fmt(ev2, "deposit");

	ev3 = tal(ctx, struct channel_event);
	ev3->payment_id = tal(ev3, struct sha256);
	memset(ev3->payment_id, 'D', sizeof(struct sha256));
	ev3->credit = AMOUNT_MSAT(300);
	ev3->debit = AMOUNT_MSAT(302);
	ev3->fees = AMOUNT_MSAT(304);
	ev3->timestamp = 33333;
	ev3->part_id = 5;
	ev3->tag = tal_fmt(ev3, "routed");

	db_begin_transaction(db);
	log_channel_event(db, acct, ev1);
	log_channel_event(db, acct, ev2);

	/* log a channel event to a different acct */
	log_channel_event(db, acct2, ev3);

	/* log a channel event without a payment id */
	ev3->payment_id = NULL;
	log_channel_event(db, acct2, ev3);

	db_commit_transaction(db);

	db_begin_transaction(db);
	chan_evs = account_get_channel_events(ctx, bkpr, cmd, acct);
	db_commit_transaction(db);

	CHECK(streq(acct->name, chan_evs[0]->acct_name));
	CHECK(streq(acct->name, chan_evs[1]->acct_name));

	CHECK(tal_count(chan_evs) == 2);
	channel_events_eq(ev1, chan_evs[0]);
	channel_events_eq(ev2, chan_evs[1]);

	return true;
}

static bool test_chain_event_crud(const tal_t *ctx)
{
	struct bkpr *bkpr = bkpr_setup(ctx);
	struct node_id peer_id;
	struct account *acct, *acct2;
	struct chain_event *ev1, *ev2, *ev3, **chain_evs;
	char *name = tal_fmt(ctx, "example");

	ev2 = tal(ctx, struct chain_event);
	memset(&peer_id, 3, sizeof(struct node_id));

	acct = new_account(bkpr->accounts, name);
	acct->peer_id = &peer_id;
	acct2 = new_account(bkpr->accounts, tal_fmt(ctx, ACCOUNT_NAME_WALLET));
	acct2->peer_id = &peer_id;
	db_begin_transaction(db);
	account_datastore_set(cmd, acct, "must-create");
	account_datastore_set(cmd, acct2, "must-create");
	db_commit_transaction(db);

	/* This event spends the second inserted event */
	ev1 = tal(ctx, struct chain_event);
	ev1->tag = tal_fmt(ev1, "withdrawal");
	ev1->origin_acct = NULL;
	ev1->credit = AMOUNT_MSAT(100);
	ev1->debit = AMOUNT_MSAT(102);
	ev1->output_value = AMOUNT_MSAT(104);
	ev1->timestamp = 1919191;
	ev1->blockheight = 1919191;
	ev1->stealable  = false;
	ev1->splice_close = false;
	memset(&ev1->outpoint.txid, 'D', sizeof(struct bitcoin_txid));
	ev1->outpoint.n = 1;
	ev1->spending_txid = tal(ctx, struct bitcoin_txid);
	memset(ev1->spending_txid, 'C', sizeof(struct bitcoin_txid));
	ev1->payment_id = NULL;

	db_begin_transaction(db);
	log_chain_event(bkpr, acct, ev1);
	db_commit_transaction(db);

	ev2->tag = tal_fmt(ctx, "deposit");
	ev2->origin_acct = tal_fmt(ctx, ACCOUNT_NAME_WALLET);
	ev2->credit = AMOUNT_MSAT(200);
	ev2->debit = AMOUNT_MSAT(202);
	ev2->output_value = AMOUNT_MSAT(104);
	ev2->timestamp = 1919191;
	ev2->blockheight = 1919191;
	ev2->stealable = false;
	ev2->splice_close = false;
	memset(&ev2->outpoint.txid, 'D', sizeof(struct bitcoin_txid));
	ev2->outpoint.n = 1;
	ev2->spending_txid = NULL;
	ev2->payment_id = tal(ctx, struct sha256);
	memset(ev2->payment_id, 'B', sizeof(struct sha256));

	/* Dummy event, logged to separate account */
	ev3 = tal(ctx, struct chain_event);
	ev3->tag = tal_fmt(ev3, "deposit");
	ev3->origin_acct = NULL;
	ev3->credit = AMOUNT_MSAT(300);
	ev3->debit = AMOUNT_MSAT(302);
	ev3->output_value = AMOUNT_MSAT(304);
	ev3->timestamp = 3939393;
	ev3->blockheight = 3939393;
	ev3->stealable = false;
	ev3->splice_close = false;
	memset(&ev3->outpoint.txid, 'E', sizeof(struct bitcoin_txid));
	ev3->outpoint.n = 1;
	ev3->spending_txid = tal(ctx, struct bitcoin_txid);
	memset(ev3->spending_txid, 'D', sizeof(struct bitcoin_txid));
	ev3->payment_id = NULL;

	db_begin_transaction(db);
	log_chain_event(bkpr, acct, ev2);

	/* log new event to a different account.. */
	log_chain_event(bkpr, acct2, ev3);
	db_commit_transaction(db);

	/* Try to add an already exiting event */
	db_begin_transaction(db);
	log_chain_event(bkpr, acct, ev2);
	db_commit_transaction(db);

	/* Ok now we ge the list, there should only be two */
	db_begin_transaction(db);
	chain_evs = account_get_chain_events(ctx, bkpr, NULL, acct);
	db_commit_transaction(db);
	CHECK(tal_count(chain_evs) == 2);

	CHECK(streq(acct->name, chain_evs[0]->acct_name));
	CHECK(streq(acct->name, chain_evs[1]->acct_name));
	chain_events_eq(ev1, chain_evs[0]);
	chain_events_eq(ev2, chain_evs[1]);

	/* Now insert a utxo create and spend, in that order */
	ev1->db_id = 0;
	memset(&ev1->outpoint.txid, 'A', sizeof(struct bitcoin_txid));
	ev1->outpoint.n = 10;
	ev1->spending_txid = tal_free(ev1->spending_txid);

	ev2->db_id = 0;
	memset(&ev2->outpoint.txid, 'A', sizeof(struct bitcoin_txid));
	ev2->outpoint.n = 10;
	ev2->spending_txid = tal(ctx, struct bitcoin_txid);
	memset(ev2->spending_txid, 'B', sizeof(struct bitcoin_txid));


	db_begin_transaction(db);
	log_chain_event(bkpr, acct, ev1);
	log_chain_event(bkpr, acct, ev2);
	chain_evs = account_get_chain_events(ctx, bkpr, NULL, acct);
	db_commit_transaction(db);

	/* There should be four now */
	CHECK(tal_count(chain_evs) == 4);

	chain_events_eq(ev1, chain_evs[2]);
	chain_events_eq(ev2, chain_evs[3]);

	return true;
}

struct acct_balance {
	struct amount_msat credit;
	struct amount_msat debit;
	struct amount_msat balance;
};

static bool account_get_balance(struct bkpr *bkpr,
				const char *acct_name,
				struct acct_balance *bal)
{
	account_get_credit_debit(bkpr, NULL, acct_name,
				 &bal->credit, &bal->debit);

	return amount_msat_sub(&bal->balance, bal->credit, bal->debit);
}

static bool test_account_balances(const tal_t *ctx)
{
	struct bkpr *bkpr = bkpr_setup(ctx);
	sqlite3 *db = bkpr_db;
	struct node_id peer_id;
	struct account *acct, *acct2;
	struct chain_event *ev1;
	struct acct_balance balance;
	bool ok;

	memset(&peer_id, 3, sizeof(struct node_id));

	acct = new_account(bkpr->accounts, tal_fmt(ctx, "example"));
	acct->peer_id = &peer_id;
	acct2 = new_account(bkpr->accounts, tal_fmt(ctx, ACCOUNT_NAME_WALLET));
	acct2->peer_id = &peer_id;

	db_begin_transaction(db);
	/* Check that account does not exist yet */
	ok = account_get_balance(bkpr, acct->name, &balance);
	CHECK(ok);

	account_datastore_set(cmd, acct, "must-create");
	account_datastore_set(cmd, acct2, "must-create");

	/* +1000btc */
	log_chain_event(bkpr, acct,
			make_chain_event(ctx, "one",
					 AMOUNT_MSAT(1000),
					 AMOUNT_MSAT(0),
					 AMOUNT_MSAT(1000),
					 1019,
					 'A', 1, '*'));
	ev1 = make_chain_event(ctx, "two",
			       AMOUNT_MSAT(0), AMOUNT_MSAT(999),
			       AMOUNT_MSAT(999),
			       1020, 'A', 2, '*');

	/* -999btc */
	log_chain_event(bkpr, acct, ev1);

	/* -440btc */
	log_channel_event(db, acct,
			  make_channel_event(ctx, "chan",
					     AMOUNT_MSAT(0),
					     AMOUNT_MSAT(440),
					     'C'));

	/* 500btc */
	log_channel_event(db, acct,
			  make_channel_event(ctx, "chan",
					     AMOUNT_MSAT(500),
					     AMOUNT_MSAT(0),
					     'D'));

	ok = account_get_balance(bkpr, acct->name, &balance);
	CHECK(ok);
	db_commit_transaction(db);

	CHECK(amount_msat_eq(balance.balance, AMOUNT_MSAT(500 - 440 + 1)));

	/* Should error if account balance is negative */
	db_begin_transaction(db);
	/* -5001btc */
	ev1 = make_chain_event(ctx, "two",
			       AMOUNT_MSAT(0), AMOUNT_MSAT(5001),
			       AMOUNT_MSAT(5001), 2020,
			       'A', 4, '*');
	log_chain_event(bkpr, acct, ev1);

	ok = account_get_balance(bkpr, acct->name, &balance);
	CHECK(!ok);
	db_commit_transaction(db);

	return true;
}

static bool test_account_crud(const tal_t *ctx)
{
	struct bkpr *bkpr = bkpr_setup(ctx);
	struct node_id *peer_id;
	struct account *acct, *acct2, **acct_list;
	struct chain_event *ev1;
	enum mvt_tag *tags;
	char *name = tal_fmt(ctx, "example");

	peer_id = tal(ctx, struct node_id);
	memset(peer_id, 3, sizeof(struct node_id));

	acct = new_account(bkpr->accounts, name);
	CHECK(!acct->is_wallet);

	db_begin_transaction(db);
	account_datastore_set(cmd, acct, "must-create");
	db_commit_transaction(db);

	acct_list = list_accounts(ctx, bkpr);
	CHECK(tal_count(acct_list) == 1);
	accountseq(acct_list[0], acct);

	acct = new_account(bkpr->accounts, tal_fmt(ctx, ACCOUNT_NAME_WALLET));
	CHECK(acct->is_wallet);

	db_begin_transaction(db);
	account_datastore_set(cmd, acct, "must-create");
	db_commit_transaction(db);

	acct_list = list_accounts(ctx, bkpr);
	CHECK(tal_count(acct_list) == 2);

	/* Can we find an account ok? */
	acct2 = find_account(bkpr, ACCOUNT_NAME_WALLET);
	accountseq(acct, acct2);

	/* Will we update an account's properties
	 * correctly, given an event and tag list? */
	ev1 = tal(ctx, struct chain_event);
	ev1->tag = tal_fmt(ctx, "withdrawal");
	ev1->origin_acct = NULL;
	ev1->credit = AMOUNT_MSAT(100);
	ev1->debit = AMOUNT_MSAT(102);
	ev1->output_value = AMOUNT_MSAT(104);
	ev1->timestamp = 1919191;
	ev1->blockheight = 1919191;
	ev1->stealable = false;
	ev1->splice_close = false;
	memset(&ev1->outpoint.txid, 'D', sizeof(struct bitcoin_txid));
	ev1->outpoint.n = 1;
	ev1->spending_txid = tal(ctx, struct bitcoin_txid);
	memset(ev1->spending_txid, 'C', sizeof(struct bitcoin_txid));
	ev1->payment_id = NULL;

	db_begin_transaction(db);
	log_chain_event(bkpr, acct, ev1);

	tags = tal_arr(ctx, enum mvt_tag, 2);

	/* should not update the account info */
	tags[0] = MVT_PUSHED;
	tags[1] = MVT_PENALTY;
	maybe_update_account(cmd, acct, ev1, tags, 0, peer_id);
	acct2 = find_account(bkpr, ACCOUNT_NAME_WALLET);
	accountseq(acct, acct2);

	/* channel_open -> open event db updated */
	CHECK(!acct->leased);
	CHECK(acct->open_event_db_id == NULL);
	tags[0] = MVT_CHANNEL_OPEN;
	tags[1] = MVT_LEASED;
	maybe_update_account(cmd, acct, ev1, tags, 2, peer_id);
	acct2 = find_account(bkpr, ACCOUNT_NAME_WALLET);
	accountseq(acct, acct2);
	CHECK(acct->leased);
	CHECK(acct->open_event_db_id != NULL);
	CHECK(acct->closed_count == 2);

	tags[0] = MVT_CHANNEL_CLOSE;
	tags[1] = MVT_OPENER;
	CHECK(acct->closed_event_db_id == NULL);
	CHECK(!acct->we_opened);
	maybe_update_account(cmd, acct, ev1, tags, 0, NULL);
	acct2 = find_account(bkpr, ACCOUNT_NAME_WALLET);
	accountseq(acct, acct2);
	CHECK(acct->closed_event_db_id != NULL);
	CHECK(acct->we_opened);

	db_commit_transaction(db);

	return true;
}

int main(int argc, char *argv[])
{
	bool ok = true;

	common_setup(argv[0]);

	/* UBSan insists cmd isn't NULL */
	cmd = tal(tmpctx, struct command);
	ok &= test_account_crud(tmpctx);
	ok &= test_channel_event_crud(tmpctx);
	ok &= test_chain_event_crud(tmpctx);
	ok &= test_account_balances(tmpctx);
	ok &= test_onchain_fee_chan_close(tmpctx);
	ok &= test_onchain_fee_chan_open(tmpctx);
	ok &= test_channel_rebalances(tmpctx);
	ok &= test_onchain_fee_wallet_spend(tmpctx);
	sqlite3_close(bkpr_db);

	common_shutdown();
	trace_cleanup();
	return !ok;
}
#else /* No sqlite, no test! */
const jsmntok_t *jsonrpc_request_sync(const tal_t *ctx,
				      struct command *cmd,
				      const char *method,
				      const struct json_out *params TAKES,
				      const char **resp)
{
	abort();
}

int main(int argc, char *argv[])
{
	common_setup(argv[0]);
	common_shutdown();
}
#endif /* !HAVE_SQLITE3 */
