bkpr: create onchain fee records for events

clightning doesn't give us any info about onchain fees (how could it?
it only knows about utxo object levels, and doesn't keep track of
how/when those are all related)

Instead, we keep running totals of the onchain fees for utxos. This
implements the master method for accounting for them, plus includes
tests to account for channel opens (across two accounts) as well as a
htlc-tx channel close.

Missing: we don't currently emit an event from cln for `withdraw`
initiated removal of funds, so the accounting for wallet -> external
funds is a bit janky. We don't account for the fees on these
transactions since we don't have the resulting 'external' event to
register them against!
This commit is contained in:
niftynei
2022-07-19 15:04:40 +09:30
committed by Rusty Russell
parent c12cd99039
commit dc113d0a3f
3 changed files with 678 additions and 34 deletions

View File

@@ -1,7 +1,10 @@
#include "config.h"
#include <bitcoin/tx.h>
#include <ccan/array_size/array_size.h>
#include <ccan/tal/str/str.h>
#include <common/coin_mvt.h>
#include <common/node_id.h>
#include <common/type_to_string.h>
#include <db/bindings.h>
#include <db/common.h>
#include <db/exec.h>
@@ -12,6 +15,9 @@
#include <plugins/bkpr/onchain_fee.h>
#include <plugins/bkpr/recorder.h>
#define EXTERNAL_ACCT "external"
#define WALLET_ACCT WALLET
static struct chain_event *stmt2chain_event(const tal_t *ctx, struct db_stmt *stmt)
{
struct chain_event *e = tal(ctx, struct chain_event);
@@ -46,6 +52,24 @@ static struct chain_event *stmt2chain_event(const tal_t *ctx, struct db_stmt *st
return e;
}
static struct chain_event **find_chain_events(const tal_t *ctx,
struct db_stmt *stmt TAKES)
{
struct chain_event **results;
db_query_prepared(stmt);
results = tal_arr(ctx, struct chain_event *, 0);
while (db_step(stmt)) {
struct chain_event *e = stmt2chain_event(results, stmt);
tal_arr_expand(&results, e);
}
if (taken(stmt))
tal_free(stmt);
return results;
}
static struct channel_event *stmt2channel_event(const tal_t *ctx, struct db_stmt *stmt)
{
struct channel_event *e = tal(ctx, struct channel_event);
@@ -72,7 +96,6 @@ struct chain_event **account_get_chain_events(const tal_t *ctx,
struct account *acct)
{
struct db_stmt *stmt;
struct chain_event **results;
stmt = db_prepare_v2(db, SQL("SELECT"
" id"
@@ -92,16 +115,7 @@ struct chain_event **account_get_chain_events(const tal_t *ctx,
" WHERE account_id = ?;"));
db_bind_int(stmt, 0, acct->db_id);
db_query_prepared(stmt);
results = tal_arr(ctx, struct chain_event *, 0);
while (db_step(stmt)) {
struct chain_event *e = stmt2chain_event(results, stmt);
tal_arr_expand(&results, e);
}
tal_free(stmt);
return results;
return find_chain_events(ctx, take(stmt));
}
static struct chain_event *find_chain_event(const tal_t *ctx,
@@ -207,6 +221,43 @@ struct channel_event **account_get_channel_events(const tal_t *ctx,
return results;
}
static struct onchain_fee *stmt2onchain_fee(const tal_t *ctx,
struct db_stmt *stmt)
{
struct onchain_fee *of = tal(ctx, struct onchain_fee);
of->acct_db_id = db_col_u64(stmt, "account_id");
db_col_txid(stmt, "txid", &of->txid);
db_col_amount_msat(stmt, "amount", &of->amount);
of->currency = db_col_strdup(of, stmt, "currency");
return of;
}
struct onchain_fee **list_chain_fees(const tal_t *ctx, struct db *db)
{
struct db_stmt *stmt;
struct onchain_fee **results;
stmt = db_prepare_v2(db, SQL("SELECT"
" account_id"
", txid"
", amount"
", currency"
" FROM onchain_fees"
" ORDER BY account_id"));
db_query_prepared(stmt);
results = tal_arr(ctx, struct onchain_fee *, 0);
while (db_step(stmt)) {
struct onchain_fee *of = stmt2onchain_fee(results, stmt);
tal_arr_expand(&results, of);
}
tal_free(stmt);
return results;
}
static struct account *stmt2account(const tal_t *ctx, struct db_stmt *stmt)
{
struct account *a = tal(ctx, struct account);
@@ -276,18 +327,6 @@ struct account *find_account(const tal_t *ctx,
return a;
}
static struct onchain_fee *stmt2onchain_fee(const tal_t *ctx, struct db_stmt *stmt)
{
struct onchain_fee *of = tal(ctx, struct onchain_fee);
of->acct_db_id = db_col_u64(stmt, "account_id");
db_col_txid(stmt, "txid", &of->txid);
db_col_amount_msat(stmt, "amount", &of->amount);
of->currency = db_col_strdup(of, stmt, "currency");
return of;
}
struct onchain_fee **account_onchain_fees(const tal_t *ctx,
struct db *db,
struct account *acct)
@@ -303,10 +342,10 @@ struct onchain_fee **account_onchain_fees(const tal_t *ctx,
" FROM onchain_fees"
" WHERE account_id = ?;"));
db_bind_int(stmt, 0, acct->db_id);
db_bind_u64(stmt, 0, acct->db_id);
db_query_prepared(stmt);
results = tal_arr(ctx, struct onchain_fee*, 0);
results = tal_arr(ctx, struct onchain_fee *, 0);
while (db_step(stmt)) {
struct onchain_fee *of = stmt2onchain_fee(results, stmt);
tal_arr_expand(&results, of);
@@ -492,6 +531,261 @@ void log_channel_event(struct db *db,
tal_free(stmt);
}
static struct chain_event **find_chain_events_bytxid(const tal_t *ctx, struct db *db,
struct bitcoin_txid *txid)
{
struct db_stmt *stmt;
stmt = db_prepare_v2(db, SQL("SELECT "
" id"
", account_id"
", tag"
", credit"
", debit"
", output_value"
", currency"
", timestamp"
", blockheight"
", utxo_txid"
", outnum"
", spending_txid"
", payment_id"
" FROM chain_events"
" WHERE spending_txid = ?"
" OR (utxo_txid = ? AND spending_txid IS NULL)"
" ORDER BY account_id"));
db_bind_txid(stmt, 0, txid);
db_bind_txid(stmt, 1, txid);
return find_chain_events(ctx, take(stmt));
}
static u64 find_acct_id(struct db *db, const char *name)
{
u64 acct_id;
struct db_stmt *stmt;
stmt = db_prepare_v2(db, SQL("SELECT"
" id"
" FROM accounts"
" WHERE name = ?"));
db_bind_text(stmt, 0, name);
db_query_prepared(stmt);
if (db_step(stmt))
acct_id = db_col_u64(stmt, "id");
else
acct_id = 0;
tal_free(stmt);
return acct_id;
}
static void update_or_insert_chain_fees(struct db *db,
u64 acct_id,
struct bitcoin_txid *txid,
struct amount_msat *amount,
const char *currency)
{
struct db_stmt *stmt;
/* First, look to see if there's an already existing
* record to update */
stmt = db_prepare_v2(db, SQL("SELECT"
" 1"
" FROM onchain_fees"
" WHERE txid = ?"
" AND account_id = ?"));
db_bind_txid(stmt, 0, txid);
db_bind_u64(stmt, 1, acct_id);
db_query_prepared(stmt);
/* If there's no current record, add it */
if (!db_step(stmt)) {
tal_free(stmt);
stmt = db_prepare_v2(db, SQL("INSERT INTO onchain_fees"
" ("
" account_id"
", txid"
", amount"
", currency"
") VALUES"
" (?, ?, ?, ?);"));
db_bind_u64(stmt, 0, acct_id);
db_bind_txid(stmt, 1, txid);
db_bind_amount_msat(stmt, 2, amount);
db_bind_text(stmt, 3, currency);
db_exec_prepared_v2(take(stmt));
return;
}
/* Otherwise, we update the existing record */
db_col_ignore(stmt, "1");
tal_free(stmt);
stmt = db_prepare_v2(db, SQL("UPDATE onchain_fees SET"
" amount = ?"
" WHERE txid = ?"
" AND account_id = ?"));
db_bind_amount_msat(stmt, 0, amount);
db_bind_txid(stmt, 1, txid);
db_bind_u64(stmt, 2, acct_id);
db_exec_prepared_v2(take(stmt));
}
char *maybe_update_onchain_fees(const tal_t *ctx, struct db *db,
struct bitcoin_txid *txid)
{
size_t no_accts = 0, plus_ones;
u64 last_id = 0, wallet_id, extern_id;
bool contains_wallet = false, skip_wallet = true;
struct chain_event **events;
struct amount_msat deposit_msat = AMOUNT_MSAT(0),
withdraw_msat = AMOUNT_MSAT(0),
fees_msat, fee_part_msat;
char *err = NULL;
u8 *inner_ctx = tal(NULL, u8);
/* Find all the deposits/withdrawals for this txid */
events = find_chain_events_bytxid(inner_ctx, db, txid);
wallet_id = find_acct_id(db, WALLET_ACCT);
extern_id = find_acct_id(db, EXTERNAL_ACCT);
/* If we don't even have two events, skip */
if (tal_count(events) < 2)
goto finished;
for (size_t i = 0; i < tal_count(events); i++) {
if (events[i]->spending_txid) {
if (!amount_msat_add(&withdraw_msat, withdraw_msat,
events[i]->debit)) {
err = tal_fmt(ctx, "Overflow adding withdrawal debits for"
" txid: %s",
type_to_string(ctx, struct bitcoin_txid,
txid));
goto finished;
}
} else {
if (!amount_msat_add(&deposit_msat, deposit_msat,
events[i]->credit)) {
err = tal_fmt(ctx, "Overflow adding deposit credits for"
" txid: %s",
type_to_string(ctx, struct bitcoin_txid,
txid));
goto finished;
}
}
/* While we're here, also count number of accts
* that were involved! Two little tricks here.
*
* One) we sorted the output
* by acct id, so we can cheat how we count: if
* it's a different acct_id than the last seen, we inc
* the counter.
*
* Two) who "gets" fee attribution is complicated
* and requires knowing if the wallet/external accts
* were involved (everything else is channel accts)
* */
if (last_id != events[i]->acct_db_id) {
last_id = events[i]->acct_db_id;
/* Don't count external accts */
if (last_id != extern_id)
no_accts++;
contains_wallet |= (last_id == wallet_id);
}
}
/* If either is zero, keep waiting */
if (amount_msat_zero(withdraw_msat)
|| amount_msat_zero(deposit_msat))
goto finished;
/* If our withdraws < deposits, wait for more data */
if (amount_msat_less(withdraw_msat, deposit_msat))
goto finished;
/* At this point, we have no way to know we've gotten all the data.
* But that's what the 'onchain_resolved_block' marker on
* accounts is for */
if (!amount_msat_sub(&fees_msat, withdraw_msat, deposit_msat)) {
err = tal_fmt(ctx, "Err subtracting withdraw %s from deposit %s"
" for txid %s",
type_to_string(ctx, struct amount_msat, &withdraw_msat),
type_to_string(ctx, struct amount_msat, &deposit_msat),
type_to_string(ctx, struct bitcoin_txid, txid));
goto finished;
}
/* Now we need to figure out how to allocate fees to each account
* that was involved in the tx. This is a lil complex, buckle up*/
/* If the wallet's involved + there were any other accounts, decr by one */
if (no_accts > 1 && contains_wallet) {
skip_wallet = true;
no_accts--;
}
/* Now we divide by the number of accts involved, to figure out the
* value to log for each account! */
fee_part_msat = amount_msat_div(fees_msat, no_accts);
/* So we don't lose any msats b/c of rounding, find the number of
* accts to add an extra msat onto */
plus_ones = fees_msat.millisatoshis % no_accts; /* Raw: mod calc */
/* Now we log (or update the existing record) for each acct */
last_id = 0;
for (size_t i = 0; i < tal_count(events); i++) {
struct amount_msat fees;
if (last_id == events[i]->acct_db_id)
continue;
last_id = events[i]->acct_db_id;
/* We *never assign fees to external accounts;
* if external funds were contributed to a tx
* we wouldn't record it -- fees are solely ours */
if (last_id == extern_id)
continue;
/* We only attribute fees to the wallet
* if the wallet is the only game in town */
if (skip_wallet && last_id == wallet_id)
continue;
/* Add an extra msat onto plus_ones accts
* so we don't lose any precision in
* our accounting */
if (plus_ones > 0) {
plus_ones--;
if (!amount_msat_add(&fees, fee_part_msat,
AMOUNT_MSAT(1))) {
err = "Overflow adding 1 ... yeah right";
/* We're gonna keep going, yolo */
fees = fee_part_msat;
}
} else
fees = fee_part_msat;
/* FIXME: fee_currency property of acct? */
update_or_insert_chain_fees(db, last_id,
txid, &fees,
events[i]->currency);
}
finished:
tal_free(inner_ctx);
return err;
}
void log_chain_event(struct db *db,
const struct account *acct,
struct chain_event *e)

View File

@@ -5,6 +5,7 @@
#include <ccan/tal/tal.h>
struct account;
struct bitcoin_txid;
struct chain_event;
struct channel_event;
struct db;
@@ -29,6 +30,9 @@ struct chain_event **account_get_chain_events(const tal_t *ctx,
struct db *db,
struct account *acct);
/* List all chain fees, for all accounts */
struct onchain_fee **list_chain_fees(const tal_t *ctx, struct db *db);
/* Add the given account to the database */
void account_add(struct db *db, struct account *acct);
@@ -43,6 +47,11 @@ void maybe_update_account(struct db *db,
struct chain_event *e,
const enum mvt_tag *tags);
/* Update our onchain fees now? */
char *maybe_update_onchain_fees(const tal_t *ctx,
struct db *db,
struct bitcoin_txid *txid);
/* Log a channel event */
void log_channel_event(struct db *db,
const struct account *acct,

View File

@@ -290,17 +290,356 @@ static bool chain_events_eq(struct chain_event *e1, struct chain_event *e2)
return true;
}
/*
static bool onchain_fees_eq(struct onchain_fee *of1, struct onchain_fee *of2)
static struct chain_event *make_chain_event(const tal_t *ctx,
char *tag,
struct amount_msat credit,
struct amount_msat debit,
char outpoint_char,
u32 outnum,
/* Note that '*' is magic */
char spend_char)
{
CHECK(of1->acct_db_id == of2->acct_db_id);
CHECK(bitcoin_txid_eq(&of1->txid, &of2->txid));
CHECK(amount_msat_eq(of1->amount, of2->amount));
CHECK(streq(of1->currency, of2->currency));
struct chain_event *ev = tal(ctx, struct chain_event);
/* This event spends the second inserted event */
ev->tag = tal_fmt(ctx, "%s", tag);
ev->credit = credit;
ev->debit = debit;
ev->output_value = AMOUNT_MSAT(1000);
ev->currency = "btc";
ev->timestamp = 1919191;
ev->blockheight = 1919191;
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;
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);
db_begin_transaction(db);
account_add(db, wal_acct);
account_add(db, ext_acct);
db_commit_transaction(db);
CHECK_MSG(!db_err, db_err);
/* 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
* *screms*
*/
db_begin_transaction(db);
log_chain_event(db, wal_acct,
make_chain_event(ctx, "withdrawal",
AMOUNT_MSAT(0),
AMOUNT_MSAT(1000),
'X', 0, '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, "deposit",
AMOUNT_MSAT(200),
AMOUNT_MSAT(0),
'1', 1, '*'));
memset(&txid, '1', sizeof(struct bitcoin_txid));
maybe_update_onchain_fees(ctx, db, &txid);
db_commit_transaction(db);
CHECK_MSG(!db_err, db_err);
/* Send some funds to an external acct? */
db_begin_transaction(db);
ofs = list_chain_fees(ctx, db);
db_commit_transaction(db);
CHECK_MSG(!db_err, db_err);
/* FIXME: track onchain wallet withdrawals?? */
CHECK(tal_count(ofs) == 0);
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;
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);
db_begin_transaction(db);
account_add(db, wal_acct);
account_add(db, ext_acct);
account_add(db, acct);
db_commit_transaction(db);
CHECK_MSG(!db_err, db_err);
/* Close a channel */
/* tag utxo_id vout txid debits credits acct_id
* close XXXX 0 1111 1000 wallet
* delay 1111 1 2222 200 chan-1
* htlc_tx 1111 2 3333 600 chan-1
* to_them 1111 3 external
* to_wall 3333 0 500 wallet
* to_wall 2222 0 150 wallet
*/
db_begin_transaction(db);
log_chain_event(db, wal_acct,
make_chain_event(ctx, "close",
AMOUNT_MSAT(0),
AMOUNT_MSAT(1000),
'X', 0, '1'));
log_chain_event(db, acct,
make_chain_event(ctx, "delayed_to_us",
AMOUNT_MSAT(200),
AMOUNT_MSAT(0),
'1', 1, '*'));
memset(&txid, '1', sizeof(struct bitcoin_txid));
maybe_update_onchain_fees(ctx, db, &txid);
/* Should be one fees now */
ofs = list_chain_fees(ctx, db);
CHECK_MSG(!db_err, db_err);
CHECK(tal_count(ofs) == 1);
CHECK(ofs[0]->acct_db_id == acct->db_id);
CHECK(amount_msat_eq(ofs[0]->amount, AMOUNT_MSAT(800)));
log_chain_event(db, acct,
make_chain_event(ctx, "htlc_tx",
AMOUNT_MSAT(600),
AMOUNT_MSAT(0),
'1', 2, '*'));
log_chain_event(db, ext_acct,
make_chain_event(ctx, "to_them",
AMOUNT_MSAT(0),
AMOUNT_MSAT(0),
'1', 3, '*'));
memset(&txid, '1', sizeof(struct bitcoin_txid));
maybe_update_onchain_fees(ctx, db, &txid);
db_commit_transaction(db);
CHECK_MSG(!db_err, db_err);
/* txid 2222 */
db_begin_transaction(db);
log_chain_event(db, acct,
make_chain_event(ctx, "withdraw",
AMOUNT_MSAT(0),
AMOUNT_MSAT(200),
'1', 1, '2'));
log_chain_event(db, wal_acct,
make_chain_event(ctx, "to_wallet",
AMOUNT_MSAT(150),
AMOUNT_MSAT(0),
'2', 0, '*'));
memset(&txid, '2', sizeof(struct bitcoin_txid));
maybe_update_onchain_fees(ctx, db, &txid);
db_commit_transaction(db);
CHECK_MSG(!db_err, db_err);
/* Expect: 2 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_MSG(!db_err, db_err);
CHECK(tal_count(ofs) == tal_count(ofs1));
CHECK(tal_count(ofs) == 2);
/* txid 3333 */
db_begin_transaction(db);
log_chain_event(db, acct,
make_chain_event(ctx, "htlc_tx",
AMOUNT_MSAT(0),
AMOUNT_MSAT(600),
'1', 2, '3'));
log_chain_event(db, wal_acct,
make_chain_event(ctx, "to_wallet",
AMOUNT_MSAT(500),
AMOUNT_MSAT(0),
'3', 0, '*'));
memset(&txid, '3', sizeof(struct bitcoin_txid));
maybe_update_onchain_fees(ctx, db, &txid);
db_commit_transaction(db);
CHECK_MSG(!db_err, db_err);
/* Expect: 3 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_MSG(!db_err, db_err);
CHECK(tal_count(ofs) == tal_count(ofs1));
CHECK(tal_count(ofs) == 3);
/* Expect: fees as follows
*
* chan-1, 1111, 200
* chan-1, 3333, 100
* chan-1, 2222, 50
*/
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(200 == ofs[i]->amount.millisatoshis); /* Raw: test eq */
continue;
}
memset(&txid, '2', sizeof(struct bitcoin_txid));
if (bitcoin_txid_eq(&txid, &ofs[i]->txid)) {
CHECK(50 == ofs[i]->amount.millisatoshis); /* Raw: test eq */
continue;
}
memset(&txid, '3', sizeof(struct bitcoin_txid));
if (bitcoin_txid_eq(&txid, &ofs[i]->txid)) {
CHECK(100 == ofs[i]->amount.millisatoshis); /* Raw: test eq */
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;
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);
CHECK_MSG(!db_err, db_err);
/* 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 3000 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, acct,
make_chain_event(ctx, "deposit",
AMOUNT_MSAT(500),
AMOUNT_MSAT(0),
'A', 0, '*'));
log_chain_event(db, acct2,
make_chain_event(ctx, "deposit",
AMOUNT_MSAT(1000),
AMOUNT_MSAT(0),
'A', 1, '*'));
log_chain_event(db, wal_acct,
make_chain_event(ctx, "deposit",
AMOUNT_MSAT(2200),
AMOUNT_MSAT(0),
'A', 2, '*'));
maybe_update_onchain_fees(ctx, db, &txid);
/* Should be no fee records yet! */
ofs = list_chain_fees(ctx, db);
CHECK_MSG(tal_count(ofs) == 0,
"no fees counted yet");
log_chain_event(db, wal_acct,
make_chain_event(ctx, "withdrawal",
AMOUNT_MSAT(0),
AMOUNT_MSAT(1000),
'X', 0, 'A'));
log_chain_event(db, wal_acct,
make_chain_event(ctx, "withdrawal",
AMOUNT_MSAT(0),
AMOUNT_MSAT(3001),
'Y', 0, 'A'));
maybe_update_onchain_fees(ctx, db, &txid);
db_commit_transaction(db);
CHECK_MSG(!db_err, db_err);
/* Expect: 2 onchain fee records of 151/150msat ea,
* none for wallet */
db_begin_transaction(db);
ofs = list_chain_fees(ctx, db);
db_commit_transaction(db);
CHECK_MSG(!db_err, db_err);
CHECK(tal_count(ofs) == 2);
/* Since these are sorted by acct_id on fetch,
* this *should* be stable */
CHECK(ofs[0]->acct_db_id == acct->db_id);
CHECK(amount_msat_eq(ofs[0]->amount, AMOUNT_MSAT(151)));
CHECK(streq(ofs[0]->currency, "btc"));
CHECK(bitcoin_txid_eq(&ofs[0]->txid, &txid));
CHECK(ofs[1]->acct_db_id == acct2->db_id);
CHECK(amount_msat_eq(ofs[1]->amount, AMOUNT_MSAT(150)));
CHECK(streq(ofs[1]->currency, "btc"));
CHECK(bitcoin_txid_eq(&ofs[1]->txid, &txid));
return true;
}
*/
static bool test_channel_event_crud(const tal_t *ctx, struct plugin *p)
{
@@ -308,11 +647,10 @@ static bool test_channel_event_crud(const tal_t *ctx, struct plugin *p)
struct node_id peer_id;
struct account *acct, *acct2;
struct channel_event ev1, ev2, ev3, **chan_evs;
char *name = tal_fmt(ctx, "example");
memset(&peer_id, 3, sizeof(struct node_id));
acct = new_account(ctx, name, &peer_id);
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);
@@ -602,6 +940,9 @@ int main(int argc, char *argv[])
ok &= test_account_crud(tmpctx, plugin);
ok &= test_channel_event_crud(tmpctx, plugin);
ok &= test_chain_event_crud(tmpctx, plugin);
ok &= test_onchain_fee_chan_close(tmpctx, plugin);
ok &= test_onchain_fee_chan_open(tmpctx, plugin);
ok &= test_onchain_fee_wallet_spend(tmpctx, plugin);
tal_free(plugin);
common_shutdown();