This requires us to turn "sql" calls into calls to a local db, which means pulling in a lot of infrastructure. But it's possible. Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
1678 lines
49 KiB
C
1678 lines
49 KiB
C
#include "config.h"
|
|
#include "common/json_filter.c"
|
|
#include "test_utils.h"
|
|
|
|
#include <bitcoin/tx.h>
|
|
#include <ccan/tal/str/str.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"
|
|
|
|
#if HAVE_SQLITE3
|
|
#include <sqlite3.h>
|
|
#endif
|
|
|
|
/* 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_fail_badparam */
|
|
struct command_result *command_fail_badparam(struct command *cmd UNNEEDED,
|
|
const char *paramname UNNEEDED,
|
|
const char *buffer UNNEEDED,
|
|
const jsmntok_t *tok UNNEEDED,
|
|
const char *msg UNNEEDED)
|
|
{ fprintf(stderr, "command_fail_badparam 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 first_fee_state */
|
|
enum htlc_state first_fee_state(enum side opener UNNEEDED)
|
|
{ fprintf(stderr, "first_fee_state called!\n"); abort(); }
|
|
/* Generated stub for fmt_channel_id */
|
|
char *fmt_channel_id(const tal_t *ctx UNNEEDED, const struct channel_id *channel_id UNNEEDED)
|
|
{ fprintf(stderr, "fmt_channel_id called!\n"); abort(); }
|
|
/* Generated stub for fmt_wireaddr_without_port */
|
|
char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED)
|
|
{ fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); }
|
|
/* Generated stub for fromwire_wireaddr */
|
|
bool fromwire_wireaddr(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct wireaddr *addr UNNEEDED)
|
|
{ fprintf(stderr, "fromwire_wireaddr called!\n"); abort(); }
|
|
/* Generated stub for htlc_state_flags */
|
|
int htlc_state_flags(enum htlc_state state UNNEEDED)
|
|
{ fprintf(stderr, "htlc_state_flags called!\n"); abort(); }
|
|
/* Generated stub for htlc_state_name */
|
|
const char *htlc_state_name(enum htlc_state s UNNEEDED)
|
|
{ fprintf(stderr, "htlc_state_name called!\n"); abort(); }
|
|
/* Generated stub for last_fee_state */
|
|
enum htlc_state last_fee_state(enum side opener UNNEEDED)
|
|
{ fprintf(stderr, "last_fee_state 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 */
|
|
|
|
static sqlite3 *bkpr_db;
|
|
|
|
/* 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);
|
|
}
|
|
|
|
struct command_result *jsonrpc_set_datastore_(struct command *cmd UNNEEDED,
|
|
const char *path 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);
|
|
if (taken(str))
|
|
tal_free(str);
|
|
json_out_end(jout, '}');
|
|
json_out_finished(jout);
|
|
|
|
return jout;
|
|
}
|
|
|
|
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)
|
|
{
|
|
#if HAVE_SQLITE3
|
|
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);
|
|
|
|
if (taken(params))
|
|
tal_free(params);
|
|
*resp = buf;
|
|
return toks;
|
|
#else
|
|
return NULL;
|
|
#endif
|
|
}
|
|
|
|
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(NULL, wal_acct, "must-create");
|
|
account_datastore_set(NULL, 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, NULL, 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, NULL, 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, NULL, 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(NULL, wal_acct, "must-create");
|
|
account_datastore_set(NULL, ext_acct, "must-create");
|
|
account_datastore_set(NULL, 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(NULL, 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(NULL, 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, NULL, 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, NULL, 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, NULL, 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, NULL, bkpr, &txid);
|
|
CHECK(acct->onchain_resolved_block == 0);
|
|
|
|
assert(account_onchain_closeheight(bkpr, NULL, 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, NULL, 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, NULL, 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, NULL, 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, NULL, 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(NULL, acct, account_onchain_closeheight(bkpr, NULL, acct));
|
|
CHECK(acct->onchain_resolved_block == blockheight + 2);
|
|
err = update_channel_onchain_fees(ctx, NULL, 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(NULL, wal_acct, "must-create");
|
|
account_datastore_set(NULL, ext_acct, "must-create");
|
|
account_datastore_set(NULL, acct, "must-create");
|
|
account_datastore_set(NULL, 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, NULL, 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, NULL, 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, NULL, bkpr, &txid);
|
|
|
|
maybe_update_onchain_fees(ctx, NULL, 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(NULL, acct1, "must-create");
|
|
account_datastore_set(NULL, acct2, "must-create");
|
|
account_datastore_set(NULL, 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, NULL, acct1);
|
|
CHECK(tal_count(chan_evs) == 1 && !find_rebalance(bkpr, chan_evs[0]->db_id));
|
|
|
|
chan_evs = account_get_channel_events(ctx, bkpr, NULL, acct2);
|
|
CHECK(tal_count(chan_evs) == 1 && !find_rebalance(bkpr, chan_evs[0]->db_id));
|
|
|
|
chan_evs = account_get_channel_events(ctx, bkpr, NULL, acct3);
|
|
CHECK(tal_count(chan_evs) == 1 && !find_rebalance(bkpr, chan_evs[0]->db_id));
|
|
|
|
maybe_record_rebalance(NULL, 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, NULL, 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, NULL, 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, NULL, 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(NULL, acct, "must-create");
|
|
account_datastore_set(NULL, 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, NULL, 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(NULL, acct, "must-create");
|
|
account_datastore_set(NULL, 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(NULL, acct, "must-create");
|
|
account_datastore_set(NULL, 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(NULL, 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(NULL, 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(NULL, 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(NULL, 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(NULL, 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]);
|
|
|
|
if (HAVE_SQLITE3) {
|
|
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;
|
|
}
|