mirror of
https://github.com/aljazceru/lightning.git
synced 2025-12-24 01:24:26 +01:00
bookkeeper: initial crud (no tests)
This commit is contained in:
@@ -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
23
plugins/bkpr/account.c
Normal 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
43
plugins/bkpr/account.h
Normal 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 */
|
||||
@@ -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, ×tamp));
|
||||
|
||||
@@ -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);
|
||||
|
||||
50
plugins/bkpr/chain_event.h
Normal file
50
plugins/bkpr/chain_event.h
Normal 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 */
|
||||
43
plugins/bkpr/channel_event.h
Normal file
43
plugins/bkpr/channel_event.h
Normal 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 */
|
||||
@@ -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},
|
||||
|
||||
25
plugins/bkpr/onchain_fee.h
Normal file
25
plugins/bkpr/onchain_fee.h
Normal 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
545
plugins/bkpr/recorder.c
Normal 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
56
plugins/bkpr/recorder.h
Normal 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 */
|
||||
Reference in New Issue
Block a user