wallet: add accessors to read chain_moves & channel_moves tables.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell
2025-08-19 10:30:45 +09:30
parent 0c77cdd2ef
commit f7e9af23c1
3 changed files with 252 additions and 0 deletions

View File

@@ -36,6 +36,9 @@ void bitcoind_getrawblockbyheight_(const tal_t *ctx UNNEEDED,
void *arg) UNNEEDED,
void *arg UNNEEDED)
{ fprintf(stderr, "bitcoind_getrawblockbyheight_ called!\n"); abort(); }
/* Generated stub for channel_by_dbid */
struct channel *channel_by_dbid(struct lightningd *ld UNNEEDED, const u64 dbid UNNEEDED)
{ fprintf(stderr, "channel_by_dbid called!\n"); abort(); }
/* Generated stub for channel_gossip_get_remote_update */
const struct peer_update *channel_gossip_get_remote_update(const struct channel *channel UNNEEDED)
{ fprintf(stderr, "channel_gossip_get_remote_update called!\n"); abort(); }

View File

@@ -7060,6 +7060,232 @@ out:
tal_free(chain_mvt);
}
static void db_cols_account(struct db_stmt *stmt,
struct lightningd *ld,
const char *channel_colname,
const char *nonnonchannel_colname,
struct mvt_account_id *account)
{
if (db_col_is_null(stmt, channel_colname)) {
account->channel = NULL;
account->alt_account = db_col_strdup(account, stmt, nonnonchannel_colname);
} else {
account->alt_account = NULL;
db_col_ignore(stmt, nonnonchannel_colname);
account->channel = channel_by_dbid(ld, db_col_u64(stmt, channel_colname));
assert(account->channel);
}
}
static void db_col_credit_or_debit(struct db_stmt *stmt,
const char *colname,
struct amount_msat *credit,
struct amount_msat *debit)
{
s64 amount = db_col_s64(stmt, colname);
if (amount < 0) {
*credit = AMOUNT_MSAT(0);
*debit = amount_msat(-amount);
} else {
*credit = amount_msat(amount);
*debit = AMOUNT_MSAT(0);
}
}
static struct mvt_tags db_col_mvt_tags(struct db_stmt *stmt,
const char *colname)
{
struct mvt_tags tags;
tags.bits = db_col_u64(stmt, colname);
assert(mvt_tags_valid(tags));
return tags;
}
struct chain_coin_mvt *wallet_chain_move_extract(const tal_t *ctx,
struct db_stmt *stmt,
struct lightningd *ld,
u64 *id)
{
struct chain_coin_mvt *chain_mvt = tal(ctx, struct chain_coin_mvt);
*id = db_col_u64(stmt, "chain_moves.id");
db_cols_account(stmt, ld,
"account_channel_id", "ma1.name",
&chain_mvt->account);
chain_mvt->tags = db_col_mvt_tags(stmt, "tag_bitmap");
db_col_credit_or_debit(stmt, "credit_or_debit",
&chain_mvt->credit,
&chain_mvt->debit);
chain_mvt->timestamp = db_col_u64(stmt, "timestamp");
db_col_outpoint(stmt, "utxo", &chain_mvt->outpoint);
if (db_col_is_null(stmt, "spending_txid"))
chain_mvt->spending_txid = NULL;
else {
/* Need non-const for db_col_txid */
struct bitcoin_txid *txid;
chain_mvt->spending_txid = txid = tal(chain_mvt, struct bitcoin_txid);
db_col_txid(stmt, "spending_txid", txid);
}
if (db_col_is_null(stmt, "peer_id"))
chain_mvt->peer_id = NULL;
else {
/* Need non-const temporary */
struct node_id *peer_id;
chain_mvt->peer_id = peer_id = tal(chain_mvt, struct node_id);
db_col_node_id(stmt, "peer_id", peer_id);
}
if (db_col_is_null(stmt, "payment_hash"))
chain_mvt->payment_hash = NULL;
else {
/* Need non-const temporary */
struct sha256 *ph;
chain_mvt->payment_hash = ph = tal(chain_mvt, struct sha256);
db_col_sha256(stmt, "payment_hash", ph);
}
chain_mvt->blockheight = db_col_int(stmt, "block_height");
if (db_col_is_null(stmt, "originating_channel_id") && db_col_is_null(stmt, "ma2.name")) {
chain_mvt->originating_acct = NULL;
} else {
/* Need non-const temporary */
struct mvt_account_id *acct;
chain_mvt->originating_acct = acct = tal(chain_mvt, struct mvt_account_id);
db_cols_account(stmt, ld,
"originating_channel_id", "ma2.name",
acct);
}
chain_mvt->output_val = db_col_amount_sat(stmt, "output_sat");
if (db_col_is_null(stmt, "output_count"))
chain_mvt->output_count = 0;
else
chain_mvt->output_count = db_col_int(stmt, "output_count");
return chain_mvt;
}
struct db_stmt *wallet_chain_moves_first(struct wallet *wallet,
u64 liststart,
u32 *listlimit)
{
struct db_stmt *stmt = db_prepare_v2(wallet->db,
SQL("SELECT"
" chain_moves.id"
", account_channel_id"
", ma1.name"
", tag_bitmap"
", credit_or_debit"
", timestamp"
", utxo"
", spending_txid"
", peer_id"
", payment_hash"
", block_height"
", output_sat"
", originating_channel_id"
", ma2.name"
", output_count"
" FROM chain_moves"
" LEFT JOIN"
" move_accounts ma1 ON account_nonchannel_id = ma1.id"
" LEFT JOIN"
" move_accounts ma2 ON originating_nonchannel_id = ma2.id"
" WHERE chain_moves.id >= ?"
" ORDER BY chain_moves.id"
" LIMIT ?;"));
db_bind_u64(stmt, liststart);
if (listlimit)
db_bind_int(stmt, *listlimit);
else
db_bind_int(stmt, INT_MAX);
db_query_prepared(stmt);
return wallet_chain_moves_next(wallet, stmt);
}
struct db_stmt *wallet_chain_moves_next(struct wallet *wallet, struct db_stmt *stmt)
{
if (!db_step(stmt))
return tal_free(stmt);
return stmt;
}
struct channel_coin_mvt *wallet_channel_move_extract(const tal_t *ctx,
struct db_stmt *stmt,
struct lightningd *ld,
u64 *id)
{
struct channel_coin_mvt *chan_mvt = tal(ctx, struct channel_coin_mvt);
*id = db_col_u64(stmt, "channel_moves.id");
db_cols_account(stmt, ld,
"account_channel_id", "ma.name",
&chan_mvt->account);
db_col_credit_or_debit(stmt, "credit_or_debit",
&chan_mvt->credit,
&chan_mvt->debit);
chan_mvt->tags = db_col_mvt_tags(stmt, "tag_bitmap");
chan_mvt->timestamp = db_col_u64(stmt, "timestamp");
if (db_col_is_null(stmt, "payment_hash"))
chan_mvt->payment_hash = NULL;
else {
/* Need non-const temporary */
struct sha256 *ph;
chan_mvt->payment_hash = ph = tal(chan_mvt, struct sha256);
db_col_sha256(stmt, "payment_hash", ph);
}
if (db_col_is_null(stmt, "payment_part_id")) {
db_col_ignore(stmt, "payment_group_id");
chan_mvt->part_and_group = NULL;
} else {
/* Non-const temporary */
struct channel_coin_mvt_id *pandg;
chan_mvt->part_and_group = pandg = tal(chan_mvt, struct channel_coin_mvt_id);
pandg->part_id = db_col_u64(stmt, "payment_part_id");
pandg->group_id = db_col_u64(stmt, "payment_group_id");
}
chan_mvt->fees = db_col_amount_msat(stmt, "fees");
return chan_mvt;
}
struct db_stmt *wallet_channel_moves_first(struct wallet *wallet,
u64 liststart,
u32 *listlimit)
{
struct db_stmt *stmt = db_prepare_v2(wallet->db,
SQL("SELECT"
" channel_moves.id"
", account_channel_id"
", ma.name"
", credit_or_debit"
", tag_bitmap"
", timestamp"
", payment_hash"
", payment_part_id"
", payment_group_id"
", fees"
" FROM channel_moves"
" LEFT JOIN"
" move_accounts ma ON account_nonchannel_id = ma.id"
" WHERE channel_moves.id >= ?"
" ORDER BY channel_moves.id"
" LIMIT ?;"));
db_bind_u64(stmt, liststart);
if (listlimit)
db_bind_int(stmt, *listlimit);
else
db_bind_int(stmt, INT_MAX);
db_query_prepared(stmt);
return wallet_channel_moves_next(wallet, stmt);
}
struct db_stmt *wallet_channel_moves_next(struct wallet *wallet, struct db_stmt *stmt)
{
if (!db_step(stmt))
return tal_free(stmt);
return stmt;
}
struct missing {
size_t num_found;
struct missing_addr *addrs;

View File

@@ -1880,6 +1880,29 @@ void wallet_save_channel_mvt(struct lightningd *ld,
void wallet_save_chain_mvt(struct lightningd *ld,
const struct chain_coin_mvt *chain_mvt);
/* coin movement table iterators */
struct db_stmt *wallet_chain_moves_first(struct wallet *wallet,
u64 liststart,
u32 *listlimit);
struct db_stmt *wallet_chain_moves_next(struct wallet *wallet,
struct db_stmt *stmt);
struct chain_coin_mvt *wallet_chain_move_extract(const tal_t *ctx,
struct db_stmt *stmt,
struct lightningd *ld,
u64 *id);
struct db_stmt *wallet_channel_moves_first(struct wallet *wallet,
u64 liststart,
u32 *listlimit);
struct db_stmt *wallet_channel_moves_next(struct wallet *wallet,
struct db_stmt *stmt);
struct channel_coin_mvt *wallet_channel_move_extract(const tal_t *ctx,
struct db_stmt *stmt,
struct lightningd *ld,
u64 *id);
/**
* wallet_memleak_scan - Check for memleaks in wallet.
*/