bookkeeper: initial crud (no tests)

This commit is contained in:
niftynei
2022-07-19 14:39:26 +09:30
committed by Rusty Russell
parent cd95d91ed5
commit b08ccfec1e
10 changed files with 871 additions and 43 deletions

View File

@@ -1,15 +1,24 @@
#! /usr/bin/make
BOOKKEEPER_PLUGIN_SRC := \
plugins/bkpr/account.c \
plugins/bkpr/bookkeeper.c \
plugins/bkpr/db.c
plugins/bkpr/db.c \
plugins/bkpr/recorder.c
BOOKKEEPER_DB_QUERIES := \
plugins/bkpr/db_sqlite3_sqlgen.c \
plugins/bkpr/db_postgres_sqlgen.c
BOOKKEEPER_SRC := $(BOOKKEEPER_PLUGIN_SRC) $(BOOKKEEPER_DB_QUERIES)
BOOKKEEPER_HEADER := plugins/bkpr/db.h
BOOKKEEPER_HEADER := \
plugins/bkpr/account.h \
plugins/bkpr/chain_event.h \
plugins/bkpr/channel_event.h \
plugins/bkpr/db.h \
plugins/bkpr/onchain_fee.h \
plugins/bkpr/recorder.h
BOOKKEEPER_OBJS := $(BOOKKEEPER_SRC:.c=.o)
$(BOOKKEEPER_OBJS): $(PLUGIN_LIB_HEADER) $(BOOKKEEPER_HEADER)
@@ -23,7 +32,8 @@ plugins/bookkeeper: bitcoin/chainparams.o common/coin_mvt.o $(BOOKKEEPER_OBJS) $
# The following files contain SQL-annotated statements that we need to extact
BOOKKEEPER_SQL_FILES := \
$(DB_SQL_FILES) \
plugins/bkpr/db.c
plugins/bkpr/db.c \
plugins/bkpr/recorder.c
plugins/bkpr/statements_gettextgen.po: $(BOOKKEEPER_SQL_FILES) $(FORCE)
@if $(call SHA256STAMP_CHANGED_ALL); then \

23
plugins/bkpr/account.c Normal file
View File

@@ -0,0 +1,23 @@
#include "config.h"
#include <common/coin_mvt.h>
#include <common/node_id.h>
#include <plugins/bkpr/account.h>
struct account *new_account(const tal_t *ctx,
const char *name STEALS,
struct node_id *peer_id)
{
struct account *a = tal(ctx, struct account);
a->name = tal_steal(a, name);
a->peer_id = peer_id;
a->is_wallet = streq(a->name, WALLET);
a->we_opened = false;
a->leased = false;
a->onchain_resolved_block = 0;
a->open_event_db_id = NULL;
a->closed_event_db_id = NULL;
return a;
}

43
plugins/bkpr/account.h Normal file
View File

@@ -0,0 +1,43 @@
#ifndef LIGHTNING_PLUGINS_BKPR_ACCOUNT_H
#define LIGHTNING_PLUGINS_BKPR_ACCOUNT_H
#include "config.h"
#include <ccan/short_types/short_types.h>
struct node_id;
struct account {
/* Id of this account in the database */
u64 db_id;
/* Name of account, typically channel id */
const char *name;
/* Peer we have this account with (NULL if not a channel) */
struct node_id *peer_id;
/* Is this our internal wallet account? */
bool is_wallet;
/* Is this an account we initiated open for? */
bool we_opened;
/* Was any portion of this account's funds leased? */
bool leased;
/* Block account was totally resolved at */
u64 onchain_resolved_block;
/* db_id of chain event that opened this account */
u64 *open_event_db_id;
/* db_id of chain event that closed this account */
u64 *closed_event_db_id;
};
/* Get a new account */
struct account *new_account(const tal_t *ctx,
const char *name STEALS,
struct node_id *peer_id);
#endif /* LIGHTNING_PLUGINS_BKPR_ACCOUNT_H */

View File

@@ -3,9 +3,13 @@
#include <common/coin_mvt.h>
#include <common/json_param.h>
#include <common/memleak.h>
#include <common/node_id.h>
#include <common/type_to_string.h>
#include <db/exec.h>
#include <plugins/bkpr/db.h>
#include <plugins/bkpr/account.h>
#include <plugins/bkpr/chain_event.h>
#include <plugins/bkpr/channel_event.h>
#include <plugins/bkpr/recorder.h>
#include <plugins/libplugin.h>
#define CHAIN_MOVE "chain_mvt"
@@ -42,7 +46,6 @@ static struct command_result *json_balance_snapshot(struct command *cmd,
{
const char *err;
size_t i;
struct node_id node_id;
u32 blockheight;
u64 timestamp;
struct account_snap *snaps;
@@ -56,10 +59,8 @@ static struct command_result *json_balance_snapshot(struct command *cmd,
json_tok_full(buf, params));
err = json_scan(cmd, buf, snap_tok,
"{node_id:%"
",blockheight:%"
"{blockheight:%"
",timestamp:%}",
JSON_SCAN(json_to_node_id, &node_id),
JSON_SCAN(json_to_number, &blockheight),
JSON_SCAN(json_to_u64, &timestamp));
@@ -69,11 +70,6 @@ static struct command_result *json_balance_snapshot(struct command *cmd,
err, json_tok_full_len(params),
json_tok_full(buf, params));
plugin_log(cmd->plugin, LOG_DBG, "balances for node %s at %d"
" (%"PRIu64")",
type_to_string(tmpctx, struct node_id, &node_id),
blockheight, timestamp);
accounts_tok = json_get_member(buf, snap_tok, "accounts");
if (accounts_tok == NULL || accounts_tok->type != JSMN_ARRAY)
plugin_err(cmd->plugin,
@@ -113,19 +109,17 @@ static struct command_result *json_balance_snapshot(struct command *cmd,
static const char *parse_and_log_chain_move(struct command *cmd,
const char *buf,
const jsmntok_t *params,
const struct node_id *node_id,
const char *acct_name STEALS,
const struct amount_msat credit,
const struct amount_msat debit,
const char *coin_type STEALS,
const u32 timestamp,
const u64 timestamp,
const enum mvt_tag *tags)
{
struct bitcoin_outpoint outpt;
static struct amount_msat output_value;
struct sha256 *payment_hash = tal(tmpctx, struct sha256);
struct bitcoin_txid *spending_txid = tal(tmpctx, struct bitcoin_txid);
u32 blockheight;
struct chain_event *e = tal(cmd, struct chain_event);
struct sha256 *payment_hash = tal(cmd, struct sha256);
struct bitcoin_txid *spending_txid = tal(cmd, struct bitcoin_txid);
struct account *acct;
const char *err;
/* Fields we expect on *every* chain movement */
@@ -136,10 +130,10 @@ static const char *parse_and_log_chain_move(struct command *cmd,
",output_msat:%"
",blockheight:%"
"}}",
JSON_SCAN(json_to_txid, &outpt.txid),
JSON_SCAN(json_to_number, &outpt.n),
JSON_SCAN(json_to_msat, &output_value),
JSON_SCAN(json_to_number, &blockheight));
JSON_SCAN(json_to_txid, &e->outpoint.txid),
JSON_SCAN(json_to_number, &e->outpoint.n),
JSON_SCAN(json_to_msat, &e->output_value),
JSON_SCAN(json_to_number, &e->blockheight));
if (err)
return err;
@@ -154,6 +148,8 @@ static const char *parse_and_log_chain_move(struct command *cmd,
if (err)
spending_txid = tal_free(spending_txid);
e->spending_txid = tal_steal(e, spending_txid);
/* Now try to get out the optional parts */
err = json_scan(tmpctx, buf, params,
"{coin_movement:"
@@ -164,25 +160,49 @@ static const char *parse_and_log_chain_move(struct command *cmd,
if (err)
payment_hash = tal_free(payment_hash);
// FIXME: enter into database
e->payment_id = tal_steal(e, payment_hash);
e->credit = credit;
e->debit = debit;
e->currency = tal_steal(e, coin_type);
e->timestamp = timestamp;
e->tag = mvt_tag_str(tags[0]);
db_begin_transaction(db);
acct = find_account(cmd, db, acct_name);
if (!acct) {
/* FIXME: lookup the peer id for this channel! */
acct = new_account(cmd, acct_name, NULL);
account_add(db, acct);
}
log_chain_event(db, acct, e);
/* This event *might* have implications for account;
* update as necessary */
maybe_update_account(db, acct, e, tags);
/* Can we calculate any onchain fees now? */
/* FIXME: maybe mark channel as 'onchain_resolved' */
db_commit_transaction(db);
return NULL;
}
static const char *parse_and_log_channel_move(struct command *cmd,
const char *buf,
const jsmntok_t *params,
const struct node_id *node_id,
const char *acct_name STEALS,
const struct amount_msat credit,
const struct amount_msat debit,
const char *coin_type STEALS,
const u32 timestamp,
const u64 timestamp,
const enum mvt_tag *tags)
{
struct sha256 payment_hash;
u64 part_id;
struct amount_msat fees;
struct channel_event *e = tal(cmd, struct channel_event);
struct account *acct;
const char *err;
err = json_scan(tmpctx, buf, params,
@@ -190,8 +210,8 @@ static const char *parse_and_log_channel_move(struct command *cmd,
"{payment_hash:%"
",fees:%"
"}}",
JSON_SCAN(json_to_sha256, &payment_hash),
JSON_SCAN(json_to_msat, &fees));
JSON_SCAN(json_to_sha256, &e->payment_id),
JSON_SCAN(json_to_msat, &e->fees));
if (err)
return err;
@@ -199,11 +219,27 @@ static const char *parse_and_log_channel_move(struct command *cmd,
err = json_scan(tmpctx, buf, params,
"{coin_movement:"
"{part_id:%}}",
JSON_SCAN(json_to_u64, &part_id));
JSON_SCAN(json_to_number, &e->part_id));
if (err)
part_id = 0;
e->part_id = 0;
// FIXME: enter into database?
e->credit = credit;
e->debit = debit;
e->currency = tal_steal(e, coin_type);
e->timestamp = timestamp;
e->tag = mvt_tag_str(tags[0]);
/* Go find the account for this event */
db_begin_transaction(db);
acct = find_account(cmd, db, acct_name);
if (!acct)
plugin_err(cmd->plugin,
"Received channel event,"
" but no account exists %s",
acct_name);
log_channel_event(db, acct, e);
db_commit_transaction(db);
return NULL;
}
@@ -234,7 +270,6 @@ static struct command_result * json_coin_moved(struct command *cmd,
const jsmntok_t *params)
{
const char *err, *mvt_type, *acct_name, *coin_type;
struct node_id node_id;
u32 version;
u64 timestamp;
struct amount_msat credit, debit;
@@ -243,7 +278,6 @@ static struct command_result * json_coin_moved(struct command *cmd,
err = json_scan(tmpctx, buf, params,
"{coin_movement:"
"{version:%"
",node_id:%"
",type:%"
",account_id:%"
",credit_msat:%"
@@ -252,7 +286,6 @@ static struct command_result * json_coin_moved(struct command *cmd,
",timestamp:%"
"}}",
JSON_SCAN(json_to_number, &version),
JSON_SCAN(json_to_node_id, &node_id),
JSON_SCAN_TAL(tmpctx, json_strdup, &mvt_type),
JSON_SCAN_TAL(tmpctx, json_strdup, &acct_name),
JSON_SCAN(json_to_msat, &credit),
@@ -285,13 +318,13 @@ static struct command_result * json_coin_moved(struct command *cmd,
mvt_type, timestamp);
if (streq(mvt_type, CHAIN_MOVE))
err = parse_and_log_chain_move(cmd, buf, params, &node_id,
err = parse_and_log_chain_move(cmd, buf, params,
acct_name, credit, debit,
coin_type, timestamp,
tags);
else {
assert(streq(mvt_type, CHANNEL_MOVE));
err = parse_and_log_channel_move(cmd, buf, params, &node_id,
err = parse_and_log_channel_move(cmd, buf, params,
acct_name, credit, debit,
coin_type, timestamp,
tags);

View File

@@ -0,0 +1,50 @@
#ifndef LIGHTNING_PLUGINS_BKPR_CHAIN_EVENT_H
#define LIGHTNING_PLUGINS_BKPR_CHAIN_EVENT_H
#include "config.h"
#include <ccan/short_types/short_types.h>
struct amount_msat;
struct bitcoin_outpoint;
struct bitcoin_txid;
struct chain_event {
/* Id of this chain event in the database */
u64 db_id;
/* db_id of account this event belongs to */
u64 acct_db_id;
/* Tag describing the event */
const char *tag;
/* Amount we received in this event */
struct amount_msat credit;
/* Amount we paid in this event */
struct amount_msat debit;
/* Total 'amount' of output on this chain event */
struct amount_msat output_value;
/* What token are the credit/debits? */
const char *currency;
/* What time did the event happen */
u64 timestamp;
/* What block did the event happen */
u32 blockheight;
/* What txo did this event concern */
struct bitcoin_outpoint outpoint;
/* What tx was the outpoint spent in (if spent) */
struct bitcoin_txid *spending_txid;
/* Sometimes chain events resolve payments */
struct sha256 *payment_id;
};
#endif /* LIGHTNING_PLUGINS_BKPR_CHAIN_EVENT_H */

View File

@@ -0,0 +1,43 @@
#ifndef LIGHTNING_PLUGINS_BKPR_CHANNEL_EVENT_H
#define LIGHTNING_PLUGINS_BKPR_CHANNEL_EVENT_H
#include "config.h"
#include <ccan/short_types/short_types.h>
struct amount_msat;
struct sha256;
struct channel_event {
/* Id of this chain event in the database */
u64 db_id;
/* db_id of account this event belongs to */
u64 acct_db_id;
/* Tag describing the event */
const char *tag;
/* Amount we received in this event */
struct amount_msat credit;
/* Amount we paid in this event */
struct amount_msat debit;
/* Total 'fees' related to this channel event */
struct amount_msat fees;
/* What token are the credit/debits? */
const char *currency;
/* Payment identifier (typically the preimage hash) */
struct sha256 payment_id;
/* Some payments share a payment_id, and are differentiable via id */
u32 part_id;
/* What time did the event happen */
u64 timestamp;
};
#endif /* LIGHTNING_PLUGINS_BKPR_CHANNEL_EVENT_H */

View File

@@ -45,8 +45,6 @@ static struct migration db_migrations[] = {
", is_wallet INTEGER"
", we_opened INTEGER"
", leased INTEGER"
", last_updated_ts BIGINT"
", last_updated_block INTEGER"
", PRIMARY KEY (id)"
");"),
NULL},
@@ -62,6 +60,7 @@ static struct migration db_migrations[] = {
", blockheight INTEGER"
", utxo_txid BLOB"
", outnum INTEGER"
", payment_id BLOB"
", spending_txid BLOB"
", PRIMARY KEY (id)"
");"),
@@ -84,6 +83,7 @@ static struct migration db_migrations[] = {
"account_id BIGINT REFERENCES accounts(id)"
", txid BLOB"
", amount BIGINT"
", currency TEXT"
", PRIMARY KEY (account_id, txid)"
");"),
NULL},

View File

@@ -0,0 +1,25 @@
#ifndef LIGHTNING_PLUGINS_BKPR_ONCHAIN_FEE_H
#define LIGHTNING_PLUGINS_BKPR_ONCHAIN_FEE_H
#include "config.h"
#include <ccan/short_types/short_types.h>
struct amount_msat;
struct bitcoin_txid;
struct onchain_fee {
/* db_id of account this event belongs to */
u64 acct_db_id;
/* Transaction that we're recording fees for */
struct bitcoin_txid txid;
/* Total amount of onchain fees we paid for this txid */
struct amount_msat amount;
/* What token are fees? */
char *currency;
};
#endif /* LIGHTNING_PLUGINS_BKPR_ONCHAIN_FEE_H */

545
plugins/bkpr/recorder.c Normal file
View File

@@ -0,0 +1,545 @@
#include "config.h"
#include <ccan/array_size/array_size.h>
#include <common/coin_mvt.h>
#include <common/node_id.h>
#include <db/bindings.h>
#include <db/common.h>
#include <db/exec.h>
#include <db/utils.h>
#include <plugins/bkpr/account.h>
#include <plugins/bkpr/chain_event.h>
#include <plugins/bkpr/channel_event.h>
#include <plugins/bkpr/onchain_fee.h>
#include <plugins/bkpr/recorder.h>
static struct chain_event *stmt2chain_event(const tal_t *ctx, struct db_stmt *stmt)
{
struct chain_event *e = tal(ctx, struct chain_event);
e->db_id = db_col_u64(stmt, "id");
e->acct_db_id = db_col_u64(stmt, "account_id");
e->tag = db_col_strdup(e, stmt, "tag");
db_col_amount_msat(stmt, "credit", &e->credit);
db_col_amount_msat(stmt, "debit", &e->debit);
db_col_amount_msat(stmt, "output_value", &e->output_value);
e->currency = db_col_strdup(e, stmt, "currency");
e->timestamp = db_col_u64(stmt, "timestamp");
e->blockheight = db_col_int(stmt, "blockheight");
db_col_txid(stmt, "utxo_txid", &e->outpoint.txid);
e->outpoint.n = db_col_int(stmt, "outnum");
if (!db_col_is_null(stmt, "payment_id")) {
e->payment_id = tal(e, struct sha256);
db_col_sha256(stmt, "payment_id", e->payment_id);
} else
e->payment_id = NULL;
if (!db_col_is_null(stmt, "spending_txid")) {
e->spending_txid = tal(e, struct bitcoin_txid);
db_col_txid(stmt, "spending_txid", e->spending_txid);
} else
e->spending_txid = NULL;
return e;
}
static struct channel_event *stmt2channel_event(const tal_t *ctx, struct db_stmt *stmt)
{
struct channel_event *e = tal(ctx, struct channel_event);
e->db_id = db_col_u64(stmt, "id");
e->acct_db_id = db_col_u64(stmt, "account_id");
e->tag = db_col_strdup(e, stmt, "tag");
db_col_amount_msat(stmt, "credit", &e->credit);
db_col_amount_msat(stmt, "debit", &e->debit);
db_col_amount_msat(stmt, "fees", &e->fees);
e->currency = db_col_strdup(e, stmt, "currency");
db_col_sha256(stmt, "payment_id", &e->payment_id);
e->part_id = db_col_int(stmt, "part_id");
e->timestamp = db_col_u64(stmt, "timestamp");
return e;
}
struct chain_event **account_get_chain_events(const tal_t *ctx,
struct db *db,
struct account *acct)
{
struct db_stmt *stmt;
struct chain_event **results;
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 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;
}
static struct chain_event *find_chain_event(const tal_t *ctx,
struct db *db,
const struct account *acct,
const struct bitcoin_outpoint *outpoint,
const struct bitcoin_txid *spending_txid)
{
struct db_stmt *stmt;
struct chain_event *e;
if (spending_txid) {
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 "
" account_id = ?"
" AND utxo_txid = ?"
" AND outnum = ?"
" AND spending_txid = ?"));
db_bind_txid(stmt, 3, spending_txid);
} else {
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 "
" account_id = ?"
" AND utxo_txid = ?"
" AND outnum = ?"
" AND spending_txid IS NULL"));
}
db_bind_u64(stmt, 0, acct->db_id);
db_bind_txid(stmt, 1, &outpoint->txid);
db_bind_int(stmt, 2, outpoint->n);
db_query_prepared(stmt);
if (db_step(stmt))
e = stmt2chain_event(ctx, stmt);
else
e = NULL;
tal_free(stmt);
return e;
}
struct channel_event **account_get_channel_events(const tal_t *ctx,
struct db *db,
struct account *acct)
{
struct db_stmt *stmt;
struct channel_event **results;
stmt = db_prepare_v2(db, SQL("SELECT"
" id"
", account_id"
", tag"
", credit"
", debit"
", fees"
", currency"
", payment_id"
", part_id"
", timestamp"
" FROM channel_events"
" WHERE account_id = ?;"));
db_bind_u64(stmt, 0, acct->db_id);
db_query_prepared(stmt);
results = tal_arr(ctx, struct channel_event *, 0);
while (db_step(stmt)) {
struct channel_event *e = stmt2channel_event(results, stmt);
tal_arr_expand(&results, e);
}
tal_free(stmt);
return results;
}
static struct account *stmt2account(const tal_t *ctx, struct db_stmt *stmt)
{
struct account *a = tal(ctx, struct account);
a->db_id = db_col_u64(stmt, "id");
a->name = db_col_strdup(a, stmt, "name");
if (!db_col_is_null(stmt, "peer_id")) {
a->peer_id = tal(a, struct node_id);
db_col_node_id(stmt, "peer_id", a->peer_id);
} else
a->peer_id = NULL;
a->is_wallet = db_col_int(stmt, "is_wallet") != 0;
a->we_opened = db_col_int(stmt, "we_opened") != 0;
a->leased = db_col_int(stmt, "leased") != 0;
if (!db_col_is_null(stmt, "onchain_resolved_block")) {
a->onchain_resolved_block = db_col_int(stmt, "onchain_resolved_block");
} else
a->onchain_resolved_block = 0;
if (!db_col_is_null(stmt, "opened_event_id")) {
a->open_event_db_id = tal(a, u64);
*a->open_event_db_id = db_col_u64(stmt, "opened_event_id");
} else
a->open_event_db_id = NULL;
if (!db_col_is_null(stmt, "closed_event_id")) {
a->closed_event_db_id = tal(a, u64);
*a->closed_event_db_id = db_col_u64(stmt, "closed_event_id");
} else
a->closed_event_db_id = NULL;
return a;
}
struct account *find_account(const tal_t *ctx,
struct db *db,
const char *name)
{
struct db_stmt *stmt;
struct account *a;
stmt = db_prepare_v2(db, SQL("SELECT"
" id"
", name"
", peer_id"
", opened_event_id"
", closed_event_id"
", onchain_resolved_block"
", is_wallet"
", we_opened"
", leased"
" FROM accounts"
" WHERE name = ?"));
db_bind_text(stmt, 0, name);
db_query_prepared(stmt);
if (db_step(stmt))
a = stmt2account(ctx, stmt);
else
a = NULL;
tal_free(stmt);
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)
{
struct db_stmt *stmt;
struct onchain_fee **results;
stmt = db_prepare_v2(db, SQL("SELECT"
" account_id"
", txid"
", amount"
", currency"
" FROM onchain_fees"
" WHERE account_id = ?;"));
db_bind_int(stmt, 0, acct->db_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;
}
struct account **list_accounts(const tal_t *ctx, struct db *db)
{
struct db_stmt *stmt;
struct account **results;
stmt = db_prepare_v2(db, SQL("SELECT"
" id"
", name"
", peer_id"
", opened_event_id"
", closed_event_id"
", onchain_resolved_block"
", is_wallet"
", we_opened"
", leased"
" FROM accounts;"));
db_query_prepared(stmt);
results = tal_arr(ctx, struct account *, 0);
while (db_step(stmt)) {
struct account *a = stmt2account(results, stmt);
tal_arr_expand(&results, a);
}
tal_free(stmt);
return results;
}
void account_add(struct db *db, struct account *acct)
{
struct db_stmt *stmt;
stmt = db_prepare_v2(db, SQL("INSERT INTO accounts"
" ("
" name"
", peer_id"
", is_wallet"
", we_opened"
", leased"
")"
" VALUES"
" (?, ?, ?, ?, ?);"));
db_bind_text(stmt, 0, acct->name);
if (acct->peer_id)
db_bind_node_id(stmt, 1, acct->peer_id);
else
db_bind_null(stmt, 1);
db_bind_int(stmt, 2, acct->is_wallet ? 1 : 0);
db_bind_int(stmt, 3, acct->we_opened ? 1 : 0);
db_bind_int(stmt, 4, acct->leased ? 1 : 0);
db_exec_prepared_v2(stmt);
acct->db_id = db_last_insert_id_v2(stmt);
tal_free(stmt);
}
void maybe_update_account(struct db *db,
struct account *acct,
struct chain_event *e,
const enum mvt_tag *tags)
{
struct db_stmt *stmt;
bool updated = false;
for (size_t i = 0; i < tal_count(tags); i++) {
switch (tags[i]) {
case CHANNEL_OPEN:
updated = true;
acct->open_event_db_id = tal(acct, u64);
*acct->open_event_db_id = e->db_id;
break;
case CHANNEL_CLOSE:
updated = true;
acct->closed_event_db_id = tal(acct, u64);
*acct->closed_event_db_id = e->db_id;
break;
case LEASED:
updated = true;
acct->leased = true;
break;
case OPENER:
updated = true;
acct->we_opened = true;
break;
case DEPOSIT:
case WITHDRAWAL:
case PENALTY:
case INVOICE:
case ROUTED:
case PUSHED:
case CHANNEL_TO_US:
case HTLC_TIMEOUT:
case HTLC_FULFILL:
case HTLC_TX:
case TO_WALLET:
case IGNORED:
case ANCHOR:
case TO_THEM:
case PENALIZED:
case STOLEN:
case TO_MINER:
case LEASE_FEE:
/* Ignored */
break;
}
}
/* Nothing new here */
if (!updated)
return;
/* Otherwise, we update the account ! */
stmt = db_prepare_v2(db, SQL("UPDATE accounts SET"
" opened_event_id = ?"
", closed_event_id = ?"
", we_opened = ?"
", leased = ?"
" WHERE"
" name = ?"));
if (acct->open_event_db_id)
db_bind_u64(stmt, 0, *acct->open_event_db_id);
else
db_bind_null(stmt, 0);
if (acct->closed_event_db_id)
db_bind_u64(stmt, 1, *acct->closed_event_db_id);
else
db_bind_null(stmt, 1);
db_bind_int(stmt, 2, acct->we_opened ? 1 : 0);
db_bind_int(stmt, 3, acct->leased ? 1 : 0);
db_bind_text(stmt, 4, acct->name);
db_exec_prepared_v2(take(stmt));
}
void log_channel_event(struct db *db,
const struct account *acct,
struct channel_event *e)
{
struct db_stmt *stmt;
stmt = db_prepare_v2(db, SQL("INSERT INTO channel_events"
" ("
" account_id"
", tag"
", credit"
", debit"
", fees"
", currency"
", payment_id"
", part_id"
", timestamp"
")"
" VALUES"
" (?, ?, ?, ?, ?, ?, ?, ?, ?);"));
db_bind_u64(stmt, 0, acct->db_id);
db_bind_text(stmt, 1, e->tag);
db_bind_amount_msat(stmt, 2, &e->credit);
db_bind_amount_msat(stmt, 3, &e->debit);
db_bind_amount_msat(stmt, 4, &e->fees);
db_bind_text(stmt, 5, e->currency);
db_bind_sha256(stmt, 6, &e->payment_id);
db_bind_int(stmt, 7, e->part_id);
db_bind_u64(stmt, 8, e->timestamp);
db_exec_prepared_v2(stmt);
e->db_id = db_last_insert_id_v2(stmt);
e->acct_db_id = acct->db_id;
tal_free(stmt);
}
void log_chain_event(struct db *db,
const struct account *acct,
struct chain_event *e)
{
struct db_stmt *stmt;
/* We're responsible for de-duping chain events! */
if (find_chain_event(e, db, acct,
&e->outpoint, e->spending_txid))
return;
stmt = db_prepare_v2(db, SQL("INSERT INTO chain_events"
" ("
" account_id"
", tag"
", credit"
", debit"
", output_value"
", currency"
", timestamp"
", blockheight"
", utxo_txid"
", outnum"
", payment_id"
", spending_txid"
")"
" VALUES"
" (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"));
db_bind_u64(stmt, 0, acct->db_id);
db_bind_text(stmt, 1, e->tag);
db_bind_amount_msat(stmt, 2, &e->credit);
db_bind_amount_msat(stmt, 3, &e->debit);
db_bind_amount_msat(stmt, 4, &e->output_value);
db_bind_text(stmt, 5, e->currency);
db_bind_u64(stmt, 6, e->timestamp);
db_bind_int(stmt, 7, e->blockheight);
db_bind_txid(stmt, 8, &e->outpoint.txid);
db_bind_int(stmt, 9, e->outpoint.n);
db_bind_sha256(stmt, 10, e->payment_id);
if (e->spending_txid)
db_bind_txid(stmt, 11, e->spending_txid);
else
db_bind_null(stmt, 11);
db_exec_prepared_v2(stmt);
e->db_id = db_last_insert_id_v2(stmt);
e->acct_db_id = acct->db_id;
tal_free(stmt);
}

56
plugins/bkpr/recorder.h Normal file
View File

@@ -0,0 +1,56 @@
#ifndef LIGHTNING_PLUGINS_BKPR_RECORDER_H
#define LIGHTNING_PLUGINS_BKPR_RECORDER_H
#include "config.h"
#include <ccan/tal/tal.h>
struct account;
struct chain_event;
struct channel_event;
struct db;
enum mvt_tag;
struct onchain_fee;
/* Get all accounts */
struct account **list_accounts(const tal_t *ctx, struct db *db);
/* Get all onchain fee records for this account */
struct onchain_fee **account_onchain_fees(const tal_t *ctx,
struct db *db,
struct account *acct);
/* Get all channel events for this account */
struct channel_event **account_get_channel_events(const tal_t *ctx,
struct db *db,
struct account *acct);
/* Get all chain events for this account */
struct chain_event **account_get_chain_events(const tal_t *ctx,
struct db *db,
struct account *acct);
/* Add the given account to the database */
void account_add(struct db *db, struct account *acct);
/* Given an account name, find that account record */
struct account *find_account(const tal_t *ctx,
struct db *db,
const char *name);
/* Some events update account information */
void maybe_update_account(struct db *db,
struct account *acct,
struct chain_event *e,
const enum mvt_tag *tags);
/* Log a channel event */
void log_channel_event(struct db *db,
const struct account *acct,
struct channel_event *e);
/* Log a chain event. */
void log_chain_event(struct db *db,
const struct account *acct,
struct chain_event *e);
#endif /* LIGHTNING_PLUGINS_BKPR_RECORDER_H */