From c12cd99039174336f3c3f2da7d9e0bad45ef8f1c Mon Sep 17 00:00:00 2001 From: niftynei Date: Tue, 19 Jul 2022 14:52:01 +0930 Subject: [PATCH] bkpr: tests for db crud --- plugins/bkpr/recorder.c | 2 +- plugins/bkpr/test/Makefile | 4 +- plugins/bkpr/test/run-recorder.c | 609 +++++++++++++++++++++++++++++++ 3 files changed, 613 insertions(+), 2 deletions(-) create mode 100644 plugins/bkpr/test/run-recorder.c diff --git a/plugins/bkpr/recorder.c b/plugins/bkpr/recorder.c index 6a1f01605..c1b7826dc 100644 --- a/plugins/bkpr/recorder.c +++ b/plugins/bkpr/recorder.c @@ -519,7 +519,7 @@ void log_chain_event(struct db *db, ", spending_txid" ")" " VALUES" - " (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")); + " (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")); db_bind_u64(stmt, 0, acct->db_id); db_bind_text(stmt, 1, e->tag); diff --git a/plugins/bkpr/test/Makefile b/plugins/bkpr/test/Makefile index 5eb076ee3..642e35214 100644 --- a/plugins/bkpr/test/Makefile +++ b/plugins/bkpr/test/Makefile @@ -25,7 +25,9 @@ BOOKKEEPER_TEST_COMMON_OBJS := \ db/db_sqlite3.o \ db/exec.o \ db/utils.o \ - plugins/bkpr/db_sqlite3_sqlgen.o + plugins/bkpr/account.o \ + plugins/bkpr/db_sqlite3_sqlgen.o \ + plugins/bkpr/recorder.o $(BOOKKEEPER_TEST_PROGRAMS): $(BITCOIN_OBJS) $(BOOKKEEPER_TEST_COMMON_OBJS) $(BOOKKEEPER_TEST_OBJS): $(BOOKKEEPER_HEADER) $(BOOKKEEPER_SRC) $(BOOKKEEPER_TEST_COMMON_OBJS) diff --git a/plugins/bkpr/test/run-recorder.c b/plugins/bkpr/test/run-recorder.c new file mode 100644 index 000000000..bff4a17a3 --- /dev/null +++ b/plugins/bkpr/test/run-recorder.c @@ -0,0 +1,609 @@ +#include "config.h" +#include "test_utils.h" + +#include "plugins/libplugin.c" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static char *db_err; +#ifndef DB_FATAL +#define DB_FATAL +void db_fatal(const char *fmt, ...) +{ + va_list ap; + + /* Fail hard if we're complaining about not being in transaction */ + assert(!strstarts(fmt, "No longer in transaction")); + + va_start(ap, fmt); + db_err = tal_vfmt(NULL, fmt, ap); + va_end(ap); +} +#endif /* DB_FATAL */ + +#include "plugins/bkpr/db.c" + + +/* AUTOGENERATED MOCKS START */ +/* Generated stub for daemon_maybe_debug */ +void daemon_maybe_debug(char *argv[]) +{ fprintf(stderr, "daemon_maybe_debug called!\n"); abort(); } +/* Generated stub for daemon_setup */ +void daemon_setup(const char *argv0 UNNEEDED, + void (*backtrace_print)(const char *fmt UNNEEDED, ...) UNNEEDED, + void (*backtrace_exit)(void)) +{ fprintf(stderr, "daemon_setup 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_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 */ +const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED) +{ fprintf(stderr, "fromwire called!\n"); abort(); } +/* Generated stub for fromwire_bool */ +bool fromwire_bool(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_bool called!\n"); abort(); } +/* Generated stub for fromwire_fail */ +void *fromwire_fail(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_fail called!\n"); abort(); } +/* Generated stub for fromwire_secp256k1_ecdsa_signature */ +void fromwire_secp256k1_ecdsa_signature(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, + secp256k1_ecdsa_signature *signature UNNEEDED) +{ fprintf(stderr, "fromwire_secp256k1_ecdsa_signature called!\n"); abort(); } +/* Generated stub for fromwire_sha256 */ +void fromwire_sha256(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct sha256 *sha256 UNNEEDED) +{ fprintf(stderr, "fromwire_sha256 called!\n"); abort(); } +/* Generated stub for fromwire_tal_arrn */ +u8 *fromwire_tal_arrn(const tal_t *ctx UNNEEDED, + const u8 **cursor UNNEEDED, size_t *max UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "fromwire_tal_arrn called!\n"); abort(); } +/* Generated stub for fromwire_u16 */ +u16 fromwire_u16(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u16 called!\n"); abort(); } +/* Generated stub for fromwire_u32 */ +u32 fromwire_u32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u32 called!\n"); abort(); } +/* Generated stub for fromwire_u64 */ +u64 fromwire_u64(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u64 called!\n"); abort(); } +/* Generated stub for fromwire_u8 */ +u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u8 called!\n"); abort(); } +/* Generated stub for fromwire_u8_array */ +void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "fromwire_u8_array 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 json_get_member */ +const jsmntok_t *json_get_member(const char *buffer UNNEEDED, const jsmntok_t tok[] UNNEEDED, + const char *label UNNEEDED) +{ fprintf(stderr, "json_get_member called!\n"); abort(); } +/* Generated stub for json_next */ +const jsmntok_t *json_next(const jsmntok_t *tok UNNEEDED) +{ fprintf(stderr, "json_next called!\n"); abort(); } +/* Generated stub for json_parse_input */ +bool json_parse_input(jsmn_parser *parser UNNEEDED, + jsmntok_t **toks UNNEEDED, + const char *input UNNEEDED, int len UNNEEDED, + bool *complete UNNEEDED) +{ fprintf(stderr, "json_parse_input called!\n"); abort(); } +/* Generated stub for json_parse_simple */ +jsmntok_t *json_parse_simple(const tal_t *ctx UNNEEDED, const char *input UNNEEDED, int len UNNEEDED) +{ fprintf(stderr, "json_parse_simple called!\n"); abort(); } +/* Generated stub for json_scan */ +const char *json_scan(const tal_t *ctx UNNEEDED, + const char *buffer UNNEEDED, + const jsmntok_t *tok UNNEEDED, + const char *guide UNNEEDED, + ...) +{ fprintf(stderr, "json_scan called!\n"); abort(); } +/* Generated stub for json_scanv */ +const char *json_scanv(const tal_t *ctx UNNEEDED, + const char *buffer UNNEEDED, + const jsmntok_t *tok UNNEEDED, + const char *guide UNNEEDED, + va_list ap UNNEEDED) +{ fprintf(stderr, "json_scanv called!\n"); abort(); } +/* Generated stub for json_strdup */ +char *json_strdup(const tal_t *ctx UNNEEDED, const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED) +{ fprintf(stderr, "json_strdup called!\n"); abort(); } +/* Generated stub for json_to_bool */ +bool json_to_bool(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, bool *b UNNEEDED) +{ fprintf(stderr, "json_to_bool called!\n"); abort(); } +/* Generated stub for json_to_int */ +bool json_to_int(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, int *num UNNEEDED) +{ fprintf(stderr, "json_to_int called!\n"); abort(); } +/* Generated stub for json_to_msat */ +bool json_to_msat(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + struct amount_msat *msat UNNEEDED) +{ fprintf(stderr, "json_to_msat called!\n"); abort(); } +/* Generated stub for json_to_node_id */ +bool json_to_node_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + struct node_id *id UNNEEDED) +{ fprintf(stderr, "json_to_node_id called!\n"); abort(); } +/* Generated stub for json_to_number */ +bool json_to_number(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + unsigned int *num UNNEEDED) +{ fprintf(stderr, "json_to_number called!\n"); abort(); } +/* Generated stub for json_to_secret */ +bool json_to_secret(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, struct secret *dest UNNEEDED) +{ fprintf(stderr, "json_to_secret called!\n"); abort(); } +/* Generated stub for json_to_short_channel_id */ +bool json_to_short_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + struct short_channel_id *scid UNNEEDED) +{ fprintf(stderr, "json_to_short_channel_id called!\n"); abort(); } +/* Generated stub for json_to_txid */ +bool json_to_txid(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + struct bitcoin_txid *txid UNNEEDED) +{ fprintf(stderr, "json_to_txid called!\n"); abort(); } +/* Generated stub for json_to_u64 */ +bool json_to_u64(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, u64 *num UNNEEDED) +{ fprintf(stderr, "json_to_u64 called!\n"); abort(); } +/* Generated stub for json_tok_bin_from_hex */ +u8 *json_tok_bin_from_hex(const tal_t *ctx UNNEEDED, const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED) +{ fprintf(stderr, "json_tok_bin_from_hex called!\n"); abort(); } +/* Generated stub for json_tok_full */ +const char *json_tok_full(const char *buffer UNNEEDED, const jsmntok_t *t UNNEEDED) +{ fprintf(stderr, "json_tok_full called!\n"); abort(); } +/* Generated stub for json_tok_full_len */ +int json_tok_full_len(const jsmntok_t *t UNNEEDED) +{ fprintf(stderr, "json_tok_full_len called!\n"); abort(); } +/* Generated stub for json_tok_streq */ +bool json_tok_streq(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, const char *str UNNEEDED) +{ fprintf(stderr, "json_tok_streq 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 log_level_name */ +const char *log_level_name(enum log_level level UNNEEDED) +{ fprintf(stderr, "log_level_name called!\n"); abort(); } +/* Generated stub for toks_alloc */ +jsmntok_t *toks_alloc(const tal_t *ctx UNNEEDED) +{ fprintf(stderr, "toks_alloc called!\n"); abort(); } +/* Generated stub for toks_reset */ +void toks_reset(jsmntok_t *toks UNNEEDED) +{ fprintf(stderr, "toks_reset called!\n"); abort(); } +/* Generated stub for towire */ +void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) +{ fprintf(stderr, "towire called!\n"); abort(); } +/* Generated stub for towire_bool */ +void towire_bool(u8 **pptr UNNEEDED, bool v UNNEEDED) +{ fprintf(stderr, "towire_bool called!\n"); abort(); } +/* Generated stub for towire_secp256k1_ecdsa_signature */ +void towire_secp256k1_ecdsa_signature(u8 **pptr UNNEEDED, + const secp256k1_ecdsa_signature *signature UNNEEDED) +{ fprintf(stderr, "towire_secp256k1_ecdsa_signature called!\n"); abort(); } +/* Generated stub for towire_sha256 */ +void towire_sha256(u8 **pptr UNNEEDED, const struct sha256 *sha256 UNNEEDED) +{ fprintf(stderr, "towire_sha256 called!\n"); abort(); } +/* Generated stub for towire_u16 */ +void towire_u16(u8 **pptr UNNEEDED, u16 v UNNEEDED) +{ fprintf(stderr, "towire_u16 called!\n"); abort(); } +/* Generated stub for towire_u32 */ +void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED) +{ fprintf(stderr, "towire_u32 called!\n"); abort(); } +/* Generated stub for towire_u64 */ +void towire_u64(u8 **pptr UNNEEDED, u64 v UNNEEDED) +{ fprintf(stderr, "towire_u64 called!\n"); abort(); } +/* Generated stub for towire_u8 */ +void towire_u8(u8 **pptr UNNEEDED, u8 v UNNEEDED) +{ fprintf(stderr, "towire_u8 called!\n"); abort(); } +/* Generated stub for towire_u8_array */ +void towire_u8_array(u8 **pptr UNNEEDED, const u8 *arr UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "towire_u8_array called!\n"); abort(); } +/* AUTOGENERATED MOCKS END */ + +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, "sqlite3://%s", filename); + tal_free(filename); + + return dsn; +} + +static bool accountseq(struct account *a1, struct account *a2) +{ + CHECK(a1->db_id == a2->db_id); + CHECK(streq(a1->name, a2->name)); + 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); + + return true; +} + +static bool channel_events_eq(struct channel_event *e1, struct channel_event *e2) +{ + CHECK(e1->db_id == e2->db_id); + CHECK(e1->acct_db_id == e2->acct_db_id); + 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(streq(e1->currency, e2->currency)); + 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(e1->acct_db_id == e2->acct_db_id); + 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(streq(e1->currency, e2->currency)); + CHECK(e1->timestamp == e2->timestamp); + CHECK(e1->blockheight == e2->blockheight); + 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)); + + return true; +} + +/* +static bool onchain_fees_eq(struct onchain_fee *of1, struct onchain_fee *of2) +{ + CHECK(of1->acct_db_id == of2->acct_db_id); + CHECK(bitcoin_txid_eq(&of1->txid, &of2->txid)); + CHECK(amount_msat_eq(of1->amount, of2->amount)); + CHECK(streq(of1->currency, of2->currency)); + + return true; +} +*/ + +static bool test_channel_event_crud(const tal_t *ctx, struct plugin *p) +{ + struct db *db = db_setup(ctx, p, tmp_dsn(ctx)); + struct node_id peer_id; + struct account *acct, *acct2; + struct channel_event ev1, ev2, ev3, **chan_evs; + char *name = tal_fmt(ctx, "example"); + + memset(&peer_id, 3, sizeof(struct node_id)); + + acct = new_account(ctx, name, &peer_id); + acct2 = new_account(ctx, tal_fmt(ctx, "wallet"), &peer_id); + db_begin_transaction(db); + account_add(db, acct); + account_add(db, acct2); + db_commit_transaction(db); + CHECK_MSG(!db_err, db_err); + + memset(&ev1.payment_id, 'B', sizeof(struct sha256)); + ev1.credit = AMOUNT_MSAT(100); + ev1.debit = AMOUNT_MSAT(102); + ev1.fees = AMOUNT_MSAT(104); + ev1.currency = "btc"; + ev1.timestamp = 1919191; + ev1.part_id = 19; + + /* Passing unknown tags in should be ok */ + ev1.tag = "hello"; + + memset(&ev2.payment_id, 'C', sizeof(struct sha256)); + ev2.credit = AMOUNT_MSAT(200); + ev2.debit = AMOUNT_MSAT(202); + ev2.fees = AMOUNT_MSAT(204); + ev2.currency = "brct"; + ev2.timestamp = 1818181; + ev2.part_id = 0; + ev2.tag = tal_fmt(ctx, "deposit"); + + memset(&ev3.payment_id, 'D', sizeof(struct sha256)); + ev3.credit = AMOUNT_MSAT(300); + ev3.debit = AMOUNT_MSAT(302); + ev3.fees = AMOUNT_MSAT(304); + ev3.currency = "brct"; + ev3.timestamp = 1717171; + ev3.part_id = 5; + ev3.tag = tal_fmt(ctx, "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); + db_commit_transaction(db); + CHECK_MSG(!db_err, db_err); + + db_begin_transaction(db); + chan_evs = account_get_channel_events(ctx, db, acct); + db_commit_transaction(db); + CHECK_MSG(!db_err, db_err); + CHECK_MSG(!db_err, db_err); + + 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 plugin *p) +{ + struct db *db = db_setup(ctx, p, tmp_dsn(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(ctx, name, &peer_id); + acct2 = new_account(ctx, tal_fmt(ctx, "wallet"), &peer_id); + db_begin_transaction(db); + account_add(db, acct); + account_add(db, acct2); + db_commit_transaction(db); + CHECK_MSG(!db_err, db_err); + + /* This event spends the second inserted event */ + ev1.tag = tal_fmt(ctx, "withdrawal"); + ev1.credit = AMOUNT_MSAT(100); + ev1.debit = AMOUNT_MSAT(102); + ev1.output_value = AMOUNT_MSAT(104); + ev1.currency = "btc"; + ev1.timestamp = 1919191; + ev1.blockheight = 1919191; + 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(db, acct, &ev1); + db_commit_transaction(db); + CHECK_MSG(!db_err, db_err); + + ev2->tag = tal_fmt(ctx, "deposit"); + ev2->credit = AMOUNT_MSAT(200); + ev2->debit = AMOUNT_MSAT(202); + ev2->output_value = AMOUNT_MSAT(104); + ev2->currency = "btc"; + ev2->timestamp = 1919191; + ev2->blockheight = 1919191; + 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.tag = tal_fmt(ctx, "deposit"); + ev3.credit = AMOUNT_MSAT(300); + ev3.debit = AMOUNT_MSAT(302); + ev3.output_value = AMOUNT_MSAT(304); + ev3.currency = "btc"; + ev3.timestamp = 3939393; + ev3.blockheight = 3939393; + 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(db, acct, ev2); + + /* log new event to a different account.. */ + log_chain_event(db, acct2, &ev3); + db_commit_transaction(db); + CHECK_MSG(!db_err, db_err); + + /* Try to add an already exiting event */ + db_begin_transaction(db); + log_chain_event(db, acct, ev2); + db_commit_transaction(db); + CHECK_MSG(!db_err, db_err); + + /* Ok now we ge the list, there should only be two */ + db_begin_transaction(db); + chain_evs = account_get_chain_events(ctx, db, acct); + db_commit_transaction(db); + CHECK_MSG(!db_err, db_err); + CHECK(tal_count(chain_evs) == 2); + + 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(db, acct, &ev1); + log_chain_event(db, acct, ev2); + chain_evs = account_get_chain_events(ctx, db, acct); + db_commit_transaction(db); + CHECK_MSG(!db_err, db_err); + + /* 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; +} + +static bool test_account_crud(const tal_t *ctx, struct plugin *p) +{ + struct db *db = db_setup(ctx, p, tmp_dsn(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"); + + memset(&peer_id, 3, sizeof(struct node_id)); + + acct = new_account(ctx, name, &peer_id); + CHECK(!acct->is_wallet); + + db_begin_transaction(db); + account_add(db, acct); + db_commit_transaction(db); + CHECK_MSG(!db_err, db_err); + + db_begin_transaction(db); + acct_list = list_accounts(ctx, db); + db_commit_transaction(db); + CHECK_MSG(!db_err, db_err); + CHECK(tal_count(acct_list) == 1); + accountseq(acct_list[0], acct); + + acct = new_account(ctx, tal_fmt(ctx, "wallet"), &peer_id); + CHECK(acct->is_wallet); + + db_begin_transaction(db); + account_add(db, acct); + db_commit_transaction(db); + CHECK_MSG(!db_err, db_err); + + db_begin_transaction(db); + acct_list = list_accounts(ctx, db); + db_commit_transaction(db); + CHECK_MSG(!db_err, db_err); + CHECK(tal_count(acct_list) == 2); + + /* Can we find an account ok? */ + db_begin_transaction(db); + acct2 = find_account(ctx, db, "wallet"); + db_commit_transaction(db); + CHECK_MSG(!db_err, db_err); + accountseq(acct, acct2); + + /* Will we update an account's properties + * correctly, given an event and tag list? */ + ev1.tag = tal_fmt(ctx, "withdrawal"); + ev1.credit = AMOUNT_MSAT(100); + ev1.debit = AMOUNT_MSAT(102); + ev1.output_value = AMOUNT_MSAT(104); + ev1.currency = "btc"; + ev1.timestamp = 1919191; + ev1.blockheight = 1919191; + 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(db, acct, &ev1); + + tags = tal_arr(ctx, enum mvt_tag, 2); + + /* should not update the account info */ + tags[0] = PUSHED; + tags[1] = PENALTY; + maybe_update_account(db, acct, &ev1, tags); + acct2 = find_account(ctx, db, "wallet"); + accountseq(acct, acct2); + + /* channel_open -> open event db updated */ + CHECK(!acct->leased); + CHECK(acct->open_event_db_id == NULL); + tags[0] = CHANNEL_OPEN; + tags[1] = LEASED; + maybe_update_account(db, acct, &ev1, tags); + acct2 = find_account(ctx, db, "wallet"); + accountseq(acct, acct2); + CHECK(acct->leased); + CHECK(acct->open_event_db_id != NULL); + + tags[0] = CHANNEL_CLOSE; + tags[1] = OPENER; + CHECK(acct->closed_event_db_id == NULL); + CHECK(!acct->we_opened); + maybe_update_account(db, acct, &ev1, tags); + acct2 = find_account(ctx, db, "wallet"); + accountseq(acct, acct2); + CHECK(acct->closed_event_db_id != NULL); + CHECK(acct->we_opened); + + db_commit_transaction(db); + CHECK_MSG(!db_err, db_err); + + return true; +} + +int main(int argc, char *argv[]) +{ + bool ok = true; + /* Dummy for migration hooks */ + struct plugin *plugin = tal(NULL, struct plugin); + plugin->js_arr = tal_arr(plugin, struct json_stream *, 0); + + common_setup(argv[0]); + + ok &= test_account_crud(tmpctx, plugin); + ok &= test_channel_event_crud(tmpctx, plugin); + ok &= test_chain_event_crud(tmpctx, plugin); + + tal_free(plugin); + common_shutdown(); + return !ok; +}