rgb-cln/plugins/bkpr/test/run-recorder.c

1399 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;
}