#include "config.h" #include "common/json_filter.c" #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 #include #include "plugins/bkpr/db.c" /* AUTOGENERATED MOCKS START */ /* Generated stub for account_entry_tag_str */ const char *account_entry_tag_str(enum account_entry_tag tag UNNEEDED) { fprintf(stderr, "account_entry_tag_str 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 daemon_developer_mode */ bool daemon_developer_mode(char *argv[]) { fprintf(stderr, "daemon_developer_mode 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 deprecated_ok_ */ bool deprecated_ok_(bool deprecated_apis UNNEEDED, const char *feature UNNEEDED, const char *start UNNEEDED, const char *end UNNEEDED, const char **begs UNNEEDED, void (*complain)(const char *feat UNNEEDED, bool allowing UNNEEDED, void *) UNNEEDED, void *cbarg UNNEEDED) { fprintf(stderr, "deprecated_ok_ 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 */ 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 fromwire_wirestring */ char *fromwire_wirestring(const tal_t *ctx UNNEEDED, const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_wirestring 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 is_asterix_notification */ bool is_asterix_notification(const char *notification_name UNNEEDED, const char *subscriptions UNNEEDED) { fprintf(stderr, "is_asterix_notification called!\n"); abort(); } /* Generated stub for json_get_id */ const char *json_get_id(const tal_t *ctx UNNEEDED, const char *buffer UNNEEDED, const jsmntok_t *obj UNNEEDED) { fprintf(stderr, "json_get_id 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_u16 */ bool json_to_u16(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, uint16_t *num UNNEEDED) { fprintf(stderr, "json_to_u16 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_copy */ jsmntok_t *json_tok_copy(const tal_t *ctx UNNEEDED, const jsmntok_t *tok UNNEEDED) { fprintf(stderr, "json_tok_copy 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_remove */ void json_tok_remove(jsmntok_t **tokens UNNEEDED, jsmntok_t *obj_or_array UNNEEDED, const jsmntok_t *tok UNNEEDED, size_t num UNNEEDED) { fprintf(stderr, "json_tok_remove 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 new_channel_event */ struct channel_event *new_channel_event(const tal_t *ctx UNNEEDED, const char *tag UNNEEDED, struct amount_msat credit UNNEEDED, struct amount_msat debit UNNEEDED, struct amount_msat fees UNNEEDED, const char *currency UNNEEDED, struct sha256 *payment_id STEALS UNNEEDED, u32 part_id UNNEEDED, u64 timestamp UNNEEDED) { fprintf(stderr, "new_channel_event called!\n"); abort(); } /* Generated stub for param_check */ bool param_check(struct command *cmd UNNEEDED, const char *buffer UNNEEDED, const jsmntok_t tokens[] UNNEEDED, ...) { fprintf(stderr, "param_check 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(); } /* Generated stub for towire_wirestring */ void towire_wirestring(u8 **pptr UNNEEDED, const char *str UNNEEDED) { fprintf(stderr, "towire_wirestring 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((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(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((e1->rebalance_id != NULL) == (e2->rebalance_id != NULL)); if (e1->rebalance_id) CHECK(*e1->rebalance_id == *e2->rebalance_id); CHECK(streq(e1->currency, e2->currency)); 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); CHECK((e1->desc != NULL) == (e2->desc != NULL)); if (e1->desc) CHECK(streq(e1->desc, e2->desc)); 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((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(streq(e1->currency, e2->currency)); CHECK(e1->timestamp == e2->timestamp); CHECK(e1->blockheight == e2->blockheight); CHECK(e1->stealable == e2->stealable); CHECK(e1->ignored == e2->ignored); 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->desc != NULL) == (e2->desc != NULL)); if (e1->desc) CHECK(streq(e1->desc, e2->desc)); 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->currency = "btc"; ev->timestamp = 1919191; ev->part_id = 19; ev->tag = tag; ev->desc = tal_fmt(ev, "description"); ev->rebalance_id = NULL; 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->currency = "btc"; ev->timestamp = 1919191; ev->blockheight = blockheight; ev->ignored = false; ev->stealable = false; ev->splice_close = false; ev->desc = tal_fmt(ev, "hello hello"); 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 plugin *p) { struct db *db = db_setup(ctx, p, tmp_dsn(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(ctx, tal_fmt(ctx, "wallet"), &peer_id); ext_acct = new_account(ctx, tal_fmt(ctx, "external"), &peer_id); memset(&txid, '1', sizeof(struct bitcoin_txid)); db_begin_transaction(db); account_add(db, wal_acct); account_add(db, ext_acct); 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(db, 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, db, &txid); log_chain_event(db, wal_acct, make_chain_event(ctx, "deposit", AMOUNT_MSAT(200), AMOUNT_MSAT(0), AMOUNT_MSAT(200), blockheight, '1', 1, '*')); maybe_update_onchain_fees(ctx, db, &txid); log_chain_event(db, ext_acct, make_chain_event(ctx, "deposit", AMOUNT_MSAT(700), AMOUNT_MSAT(0), AMOUNT_MSAT(700), blockheight, '1', 0, '*')); maybe_update_onchain_fees(ctx, db, &txid); db_commit_transaction(db); db_begin_transaction(db); ofs = list_chain_fees(ctx, db); db_commit_transaction(db); 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 plugin *p) { struct db *db = db_setup(ctx, p, tmp_dsn(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(ctx, tal_fmt(ctx, "wallet"), &peer_id); ext_acct = new_account(ctx, tal_fmt(ctx, "external"), &peer_id); acct = new_account(ctx, tal_fmt(ctx, "chan-1"), &peer_id); db_begin_transaction(db); account_add(db, wal_acct); account_add(db, ext_acct); account_add(db, acct); 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(db, acct, ev); tags[0] = CHANNEL_OPEN; maybe_update_account(db, 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(db, acct, ev); /* Update the account to have the right info! */ tags[0] = CHANNEL_CLOSE; maybe_update_account(db, acct, ev, tags, close_output_count, NULL); log_chain_event(db, 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, db, &txid); log_chain_event(db, wal_acct, make_chain_event(ctx, "anchor", AMOUNT_MSAT(30), AMOUNT_MSAT(0), AMOUNT_MSAT(30), blockheight, '1', 0, '*')); log_chain_event(db, 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, db, &txid); /* Should be no fees yet */ ofs = list_chain_fees(ctx, db); CHECK(tal_count(ofs) == 0); log_chain_event(db, acct, make_chain_event(ctx, "htlc_timeout", AMOUNT_MSAT(600), AMOUNT_MSAT(0), AMOUNT_MSAT(600), blockheight, '1', 2, '*')); log_chain_event(db, 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, db, &txid); db_commit_transaction(db); /* txid 2222 */ db_begin_transaction(db); log_chain_event(db, acct, make_chain_event(ctx, "to_wallet", AMOUNT_MSAT(0), AMOUNT_MSAT(200), AMOUNT_MSAT(200), blockheight + 1, '1', 1, '2')); log_chain_event(db, 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, db, &txid); CHECK(acct->onchain_resolved_block == 0); maybe_mark_account_onchain(db, acct); CHECK(acct->onchain_resolved_block == 0); db_commit_transaction(db); /* Expect: 1 onchain fee records, all for chan-1 */ db_begin_transaction(db); ofs = list_chain_fees(ctx, db); ofs1 = account_onchain_fees(ctx, db, acct); db_commit_transaction(db); CHECK(tal_count(ofs) == tal_count(ofs1)); CHECK(tal_count(ofs) == 1); /* txid 4444 */ db_begin_transaction(db); log_chain_event(db, 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, db, &txid); /* txid 3333 */ log_chain_event(db, acct, make_chain_event(ctx, "htlc_timeout", AMOUNT_MSAT(0), AMOUNT_MSAT(600), AMOUNT_MSAT(600), blockheight + 2, '1', 2, '3')); maybe_mark_account_onchain(db, acct); CHECK(acct->onchain_resolved_block == 0); log_chain_event(db, 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, db, &txid); log_chain_event(db, 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, db, &txid); db_commit_transaction(db); /* Expect: onchain fee records for tx except channel close */ db_begin_transaction(db); ofs = list_chain_fees(ctx, db); ofs1 = account_onchain_fees(ctx, db, acct); db_commit_transaction(db); 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); maybe_mark_account_onchain(db, acct); CHECK(acct->onchain_resolved_block == blockheight + 2); err = update_channel_onchain_fees(ctx, db, acct); CHECK_MSG(!err, err); ofs = account_onchain_fees(ctx, db, acct); db_commit_transaction(db); /* 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(ofs[i]->acct_db_id == acct->db_id); CHECK(streq(ofs[i]->currency, "btc")); 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 plugin *p) { struct db *db = db_setup(ctx, p, tmp_dsn(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(ctx, tal_fmt(ctx, "wallet"), &peer_id); ext_acct = new_account(ctx, tal_fmt(ctx, "external"), &peer_id); acct = new_account(ctx, tal_fmt(ctx, "chan-1"), &peer_id); acct2 = new_account(ctx, tal_fmt(ctx, "chan-2"), &peer_id); db_begin_transaction(db); account_add(db, wal_acct); account_add(db, ext_acct); account_add(db, acct); account_add(db, acct2); db_commit_transaction(db); /* Assumption that we rely on later */ CHECK(acct->db_id < acct2->db_id); /* 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(db, wal_acct, make_chain_event(ctx, "withdrawal", AMOUNT_MSAT(0), AMOUNT_MSAT(1000), AMOUNT_MSAT(1000), blockheight, 'X', 0, 'A')); log_chain_event(db, wal_acct, make_chain_event(ctx, "withdrawal", AMOUNT_MSAT(0), AMOUNT_MSAT(3001), AMOUNT_MSAT(3001), blockheight, 'Y', 0, 'A')); log_chain_event(db, acct, make_chain_event(ctx, "deposit", AMOUNT_MSAT(500), AMOUNT_MSAT(0), AMOUNT_MSAT(500), blockheight, 'A', 0, '*')); maybe_update_onchain_fees(ctx, db, &txid); log_chain_event(db, acct2, make_chain_event(ctx, "deposit", AMOUNT_MSAT(1000), AMOUNT_MSAT(0), AMOUNT_MSAT(1000), blockheight, 'A', 1, '*')); maybe_update_onchain_fees(ctx, db, &txid); log_chain_event(db, wal_acct, make_chain_event(ctx, "deposit", AMOUNT_MSAT(2200), AMOUNT_MSAT(0), AMOUNT_MSAT(2200), blockheight, 'A', 2, '*')); maybe_update_onchain_fees(ctx, db, &txid); maybe_update_onchain_fees(ctx, db, &txid); db_commit_transaction(db); /* Expect: 5 onchain fee records, totaling to 151/150msat ea, * none for wallet */ db_begin_transaction(db); ofs = list_chain_fees(ctx, db); db_commit_transaction(db); 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(streq(ofs[i]->currency, "btc")); CHECK(bitcoin_txid_eq(&ofs[i]->txid, &txid)); } return true; } static bool test_channel_rebalances(const tal_t *ctx, struct plugin *p) { struct db *db = db_setup(ctx, p, tmp_dsn(ctx)); struct channel_event *ev1, *ev2, *ev3, **chan_evs; struct rebalance **rebals; struct account *acct1, *acct2, *acct3; struct node_id peer_id; memset(&peer_id, 3, sizeof(struct node_id)); acct1 = new_account(ctx, tal_fmt(ctx, "one"), &peer_id); acct2 = new_account(ctx, tal_fmt(ctx, "two"), &peer_id); acct3 = new_account(ctx, tal_fmt(ctx, "three"), &peer_id); db_begin_transaction(db); account_add(db, acct1); account_add(db, acct2); account_add(db, acct3); /* 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, db, acct1); CHECK(tal_count(chan_evs) == 1 && !chan_evs[0]->rebalance_id); chan_evs = account_get_channel_events(ctx, db, acct2); CHECK(tal_count(chan_evs) == 1 && !chan_evs[0]->rebalance_id); chan_evs = account_get_channel_events(ctx, db, acct3); CHECK(tal_count(chan_evs) == 1 && !chan_evs[0]->rebalance_id); maybe_record_rebalance(db, ev2); CHECK(ev2->rebalance_id != NULL); /* Both events should be marked as rebalance */ chan_evs = account_get_channel_events(ctx, db, acct1); CHECK(tal_count(chan_evs) == 1 && chan_evs[0]->rebalance_id); chan_evs = account_get_channel_events(ctx, db, acct2); CHECK(tal_count(chan_evs) == 1 && chan_evs[0]->rebalance_id); /* Third event is not a rebalance though */ chan_evs = account_get_channel_events(ctx, db, acct3); CHECK(tal_count(chan_evs) == 1 && !chan_evs[0]->rebalance_id); /* Did we get an accurate rebalances entry? */ rebals = list_rebalances(ctx, db); CHECK(tal_count(rebals) == 1); CHECK(rebals[0]->in_ev_id == ev1->db_id); CHECK(rebals[0]->out_ev_id == ev2->db_id); CHECK(streq(rebals[0]->in_acct_name, "one")); CHECK(streq(rebals[0]->out_acct_name, "two")); CHECK(amount_msat_eq(rebals[0]->rebal_msat, AMOUNT_MSAT(100))); CHECK(amount_msat_eq(rebals[0]->fee_msat, AMOUNT_MSAT(12))); db_commit_transaction(db); 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; memset(&peer_id, 3, sizeof(struct node_id)); acct = new_account(ctx, tal_fmt(ctx, "example"), &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); 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->currency = "btc"; ev1->timestamp = 11111; ev1->part_id = 19; ev1->desc = tal_strdup(ev1, "hello desc1"); ev1->rebalance_id = NULL; /* Passing unknown tags in should be ok */ ev1->tag = "hello"; ev1->desc = tal_fmt(ev1, "desc"); 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->currency = "brct"; ev2->timestamp = 22222; ev2->part_id = 0; ev2->tag = tal_fmt(ev2, "deposit"); ev2->desc = NULL; ev2->rebalance_id = tal(ev2, u64); *ev2->rebalance_id = 1; 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->currency = "brct"; ev3->timestamp = 33333; ev3->part_id = 5; ev3->tag = tal_fmt(ev3, "routed"); ev3->desc = NULL; ev3->rebalance_id = NULL; 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, db, 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 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); /* 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->currency = "btc"; ev1->timestamp = 1919191; ev1->blockheight = 1919191; ev1->ignored = false; 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; ev1->desc = tal_fmt(ev1, "description"); db_begin_transaction(db); log_chain_event(db, acct, ev1); db_commit_transaction(db); ev2->tag = tal_fmt(ctx, "deposit"); ev2->origin_acct = tal_fmt(ctx, "wallet"); 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; ev2->ignored = false; 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); ev2->desc = NULL; 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->currency = "btc"; ev3->timestamp = 3939393; ev3->blockheight = 3939393; ev3->ignored = false; 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; ev3->desc = 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); /* Try to add an already exiting event */ db_begin_transaction(db); log_chain_event(db, 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, db, 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(db, acct, ev1); log_chain_event(db, acct, ev2); chain_evs = account_get_chain_events(ctx, db, 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; } static bool test_account_balances(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; struct acct_balance **balances; bool exists; char *err; memset(&peer_id, 3, sizeof(struct node_id)); acct = new_account(ctx, tal_fmt(ctx, "example"), &peer_id); acct2 = new_account(ctx, tal_fmt(ctx, "wallet"), &peer_id); db_begin_transaction(db); /* Check that account does not exist yet */ err = account_get_balance(ctx, db, acct->name, true, false, &balances, &exists); CHECK(!err); CHECK_MSG(!exists, "expected account not to exist"); account_add(db, acct); account_add(db, acct2); /* +1000btc */ log_chain_event(db, 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, '*'); /* Make this an ignored event */ ev1->ignored = true; /* -999btc */ log_chain_event(db, 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')); /* +5000chf */ ev1 = make_chain_event(ctx, "two", AMOUNT_MSAT(5000), AMOUNT_MSAT(0), AMOUNT_MSAT(5000), 1999, 'A', 3, '*'); ev1->currency = "chf"; log_chain_event(db, acct, ev1); /* Add same chain event to a different account, shouldn't show */ log_chain_event(db, acct2, ev1); err = account_get_balance(ctx, db, acct->name, true, false, &balances, NULL); CHECK_MSG(!err, err); db_commit_transaction(db); /* Should have 2 balances */ CHECK(tal_count(balances) == 2); CHECK(streq(balances[0]->currency, "btc")); CHECK(amount_msat_eq(balances[0]->balance, AMOUNT_MSAT(500 - 440 + 1))); CHECK(streq(balances[1]->currency, "chf")); CHECK(amount_msat_eq(balances[1]->balance, AMOUNT_MSAT(5000))); /* Should error if account balance is negative */ db_begin_transaction(db); /* -5001chf */ ev1 = make_chain_event(ctx, "two", AMOUNT_MSAT(0), AMOUNT_MSAT(5001), AMOUNT_MSAT(5001), 2020, 'A', 4, '*'); ev1->currency = "chf"; log_chain_event(db, acct, ev1); err = account_get_balance(ctx, db, acct->name, true, false, &balances, &exists); CHECK_MSG(err != NULL, "Expected err message"); CHECK(streq(err, "chf channel balance is negative? 5000msat - 5001msat")); CHECK_MSG(exists, "expected account to exist"); err = account_get_balance(ctx, db, acct->name, false, false, &balances, NULL); CHECK_MSG(!err, err); /* Now with ignored events */ err = account_get_balance(ctx, db, acct->name, true, true, &balances, NULL); CHECK(streq(balances[0]->currency, "btc")); CHECK(amount_msat_eq(balances[0]->balance, AMOUNT_MSAT(500 - 440 + 1000))); db_commit_transaction(db); 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"); peer_id = tal(ctx, struct node_id); memset(peer_id, 3, sizeof(struct node_id)); acct = new_account(ctx, name, NULL); CHECK(!acct->is_wallet); db_begin_transaction(db); account_add(db, acct); db_commit_transaction(db); db_begin_transaction(db); acct_list = list_accounts(ctx, db); db_commit_transaction(db); CHECK(tal_count(acct_list) == 1); accountseq(acct_list[0], acct); acct = new_account(ctx, tal_fmt(ctx, "wallet"), NULL); CHECK(acct->is_wallet); db_begin_transaction(db); account_add(db, acct); db_commit_transaction(db); db_begin_transaction(db); acct_list = list_accounts(ctx, db); db_commit_transaction(db); 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); 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->currency = "btc"; ev1->timestamp = 1919191; ev1->blockheight = 1919191; ev1->ignored = false; 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; ev1->desc = tal_fmt(ev1, "oh hello"); 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, 0, peer_id); 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, 2, peer_id); acct2 = find_account(ctx, db, "wallet"); accountseq(acct, acct2); CHECK(acct->leased); CHECK(acct->open_event_db_id != NULL); CHECK(acct->closed_count == 2); 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, 0, NULL); acct2 = find_account(ctx, db, "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; /* Dummy for migration hooks */ struct plugin *plugin = tal(NULL, struct plugin); list_head_init(&plugin->js_list); plugin->developer = true; common_setup(argv[0]); if (HAVE_SQLITE3) { ok &= test_account_crud(tmpctx, plugin); ok &= test_channel_event_crud(tmpctx, plugin); ok &= test_chain_event_crud(tmpctx, plugin); ok &= test_account_balances(tmpctx, plugin); ok &= test_onchain_fee_chan_close(tmpctx, plugin); ok &= test_onchain_fee_chan_open(tmpctx, plugin); ok &= test_channel_rebalances(tmpctx, plugin); ok &= test_onchain_fee_wallet_spend(tmpctx, plugin); } tal_free(plugin); common_shutdown(); return !ok; }