From f7e9af23c1ce20880caf5c0bfe1cad02b005ea3f Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 19 Aug 2025 10:30:45 +0930 Subject: [PATCH] wallet: add accessors to read chain_moves & channel_moves tables. Signed-off-by: Rusty Russell --- wallet/test/run-db.c | 3 + wallet/wallet.c | 226 +++++++++++++++++++++++++++++++++++++++++++ wallet/wallet.h | 23 +++++ 3 files changed, 252 insertions(+) diff --git a/wallet/test/run-db.c b/wallet/test/run-db.c index fbf4f3923..79629beba 100644 --- a/wallet/test/run-db.c +++ b/wallet/test/run-db.c @@ -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(); } diff --git a/wallet/wallet.c b/wallet/wallet.c index bbe078f4f..93a5a4b8c 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -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; diff --git a/wallet/wallet.h b/wallet/wallet.h index 0ee7914bb..c7108a1aa 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -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. */