core-lightning/plugins/bkpr/test/run-recorder.c
niftynei a4f92eac81 bkpr: now that we're not doing empty acct logging, we dont need this bool
We stopped doing empty journal logs, so we no longer need to switch
our log severity based on whether or not an account exists.

Should make bookkeeper less chatty and remove noisy logs

Changelog-None
2024-01-29 10:05:03 +10:30

1398 lines
44 KiB
C

#include "config.h"
#include "common/json_filter.c"
#include "test_utils.h"
#include "plugins/libplugin.c"
#include <bitcoin/tx.h>
#include <ccan/tal/str/str.h>
#include <common/coin_mvt.h>
#include <common/fee_states.h>
#include <common/htlc.h>
#include <common/json_stream.h>
#include <common/setup.h>
#include <common/utils.h>
#include <db/common.h>
#include <plugins/bkpr/account.h>
#include <plugins/bkpr/account_entry.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 <stdio.h>
#include <unistd.h>
#include <wire/wire.h>
#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 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_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 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_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 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 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));
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->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_zero(ofs[0]->debit));
CHECK(ofs[0]->update_count == 1);
CHECK(amount_msat_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_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_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_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_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;
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;
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;
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;
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;
}