From b70f4f618402cba43e56b32dd82d6a51485d2733 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Tue, 19 Aug 2025 10:30:52 +0930 Subject: [PATCH] bkpr: convert all the local db sql queries into calls to sql plugin. With some help (and hinderance!) from ChatGPT: the field names differ slightly from our internal db. The particilar wrinkle is that we have to restrict all queries to limit them to entries we've seen already. Our code expects this (we used to only enter it into the db when we processed it), and it would otherwise be confusing if a sql query returned inconsistent results because an event occurred while bookkeeper was processing. Signed-off-by: Rusty Russell --- plugins/bkpr/bookkeeper.c | 36 +- plugins/bkpr/channelsapy.c | 14 +- plugins/bkpr/channelsapy.h | 3 +- plugins/bkpr/incomestmt.c | 44 +- plugins/bkpr/incomestmt.h | 2 + plugins/bkpr/onchain_fee.c | 11 +- plugins/bkpr/recorder.c | 849 +++++++++++-------------------------- plugins/bkpr/recorder.h | 45 +- 8 files changed, 347 insertions(+), 657 deletions(-) diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c index 77f40ac3c..b3c9354bf 100644 --- a/plugins/bkpr/bookkeeper.c +++ b/plugins/bkpr/bookkeeper.c @@ -219,7 +219,7 @@ getblockheight_done(struct command *cmd, /* Get the income events */ db_begin_transaction(bkpr->db); - apys = compute_channel_apys(cmd, bkpr, + apys = compute_channel_apys(cmd, bkpr, cmd, *req->start_time, *req->end_time, blockheight); @@ -314,7 +314,7 @@ static struct command_result *do_dump_income(struct command *cmd, /* Ok, go find me some income events! */ db_begin_transaction(bkpr->db); - evs = list_income_events(cmd, bkpr, *info->start_time, *info->end_time, + evs = list_income_events(cmd, bkpr, cmd, *info->start_time, *info->end_time, *info->consolidate_fees); db_commit_transaction(bkpr->db); @@ -366,7 +366,7 @@ static struct command_result *do_list_income(struct command *cmd, /* Ok, go find me some income events! */ db_begin_transaction(bkpr->db); - evs = list_income_events(cmd, bkpr, *info->start_time, *info->end_time, + evs = list_income_events(cmd, bkpr, cmd, *info->start_time, *info->end_time, *info->consolidate_fees); db_commit_transaction(bkpr->db); @@ -418,7 +418,7 @@ static struct command_result *do_inspect(struct command *cmd, acct_name); db_begin_transaction(bkpr->db); - find_txo_chain(cmd, bkpr, acct, &txos); + find_txo_chain(cmd, bkpr, cmd, acct, &txos); fee_sums = find_account_onchain_fees(cmd, bkpr, acct); db_commit_transaction(bkpr->db); @@ -625,22 +625,22 @@ static struct command_result *do_account_events(struct command *cmd, db_begin_transaction(bkpr->db); if (acct) { - channel_events = account_get_channel_events(cmd, bkpr->db, acct); - chain_events = account_get_chain_events(cmd, bkpr, acct); + channel_events = account_get_channel_events(cmd, bkpr, cmd, acct); + chain_events = account_get_chain_events(cmd, bkpr, cmd, acct); onchain_fees = account_get_chain_fees(tmpctx, bkpr, acct->name); } else if (info->payment_id != NULL) { - channel_events = get_channel_events_by_id(cmd, bkpr->db, info->payment_id); + channel_events = get_channel_events_by_id(cmd, bkpr, cmd, info->payment_id); tx_id = tal(cmd, struct bitcoin_txid); tx_id->shad.sha = *info->payment_id; /* Transaction ids are stored as big-endian in the database */ reverse_bytes(tx_id->shad.sha.u.u8, sizeof(tx_id->shad.sha.u.u8)); - chain_events = find_chain_events_bytxid(cmd, bkpr, tx_id); + chain_events = find_chain_events_bytxid(cmd, bkpr, cmd, tx_id); onchain_fees = get_chain_fees_by_txid(cmd, bkpr, tx_id); } else { - channel_events = list_channel_events(cmd, bkpr->db); - chain_events = list_chain_events(cmd, bkpr); + channel_events = list_channel_events(cmd, bkpr, cmd); + chain_events = list_chain_events(cmd, bkpr, cmd); onchain_fees = list_chain_fees(cmd, bkpr); } db_commit_transaction(bkpr->db); @@ -682,7 +682,7 @@ static struct command_result *do_edit_desc(struct command *cmd, db_begin_transaction(bkpr->db); add_utxo_description(cmd, bkpr, info->outpoint, info->new_desc); - chain_events = get_chain_events_by_outpoint(cmd, bkpr, info->outpoint, true); + chain_events = get_chain_events_by_outpoint(cmd, bkpr, cmd, info->outpoint); db_commit_transaction(bkpr->db); res = jsonrpc_stream_success(cmd); @@ -724,8 +724,8 @@ static struct command_result *do_edit_desc_payment(struct command *cmd, db_begin_transaction(bkpr->db); add_payment_hash_description(cmd, bkpr, info->identifier, info->new_desc); - chain_events = get_chain_events_by_id(cmd, bkpr, info->identifier); - channel_events = get_channel_events_by_id(cmd, bkpr->db, info->identifier); + chain_events = get_chain_events_by_id(cmd, bkpr, cmd, info->identifier); + channel_events = get_channel_events_by_id(cmd, bkpr, cmd, info->identifier); db_commit_transaction(bkpr->db); res = jsonrpc_stream_success(cmd); @@ -768,7 +768,7 @@ static struct command_result *do_list_balances(struct command *cmd, struct amount_msat credit, debit, balance; bool has_events; - has_events = account_get_credit_debit(cmd->plugin, bkpr->db, + has_events = account_get_credit_debit(bkpr, cmd, accts[i]->name, &credit, &debit); if (!amount_msat_sub(&balance, credit, debit)) { @@ -845,7 +845,7 @@ static void try_update_open_fees(struct command *cmd, struct bkpr *bkpr = bkpr_of(cmd->plugin); assert(acct->closed_event_db_id); - ev = find_chain_event_by_id(cmd, bkpr, *acct->closed_event_db_id); + ev = find_chain_event_by_id(cmd, bkpr, cmd, *acct->closed_event_db_id); assert(ev); err = maybe_update_onchain_fees(cmd, cmd, bkpr, ev->spending_txid); @@ -1163,7 +1163,7 @@ static char *do_account_close_checks(struct command *cmd, } else if (!is_channel_account(acct->name) && !e->spending_txid) { const char *acctname; - acctname = find_close_account_name(tmpctx, bkpr->db, &e->outpoint.txid); + acctname = find_close_account_name(tmpctx, bkpr, cmd, &e->outpoint.txid); if (acctname) { closed_acct = find_account(bkpr, acctname); } else { @@ -1175,7 +1175,7 @@ static char *do_account_close_checks(struct command *cmd, if (closed_acct && closed_acct->closed_event_db_id) { - u64 closeheight = account_onchain_closeheight(bkpr, closed_acct); + u64 closeheight = account_onchain_closeheight(bkpr, cmd, closed_acct); if (closeheight != 0) { char *err; account_update_closeheight(cmd, closed_acct, closeheight); @@ -1398,7 +1398,7 @@ listpeerchannels_done(struct command *cmd, info->acct, info->ev->timestamp)) { db_begin_transaction(bkpr->db); - account_get_credit_debit(cmd->plugin, bkpr->db, info->acct->name, + account_get_credit_debit(bkpr, cmd, info->acct->name, &credit, &debit); db_commit_transaction(bkpr->db); diff --git a/plugins/bkpr/channelsapy.c b/plugins/bkpr/channelsapy.c index cc311c48e..ed7f85b90 100644 --- a/plugins/bkpr/channelsapy.c +++ b/plugins/bkpr/channelsapy.c @@ -97,6 +97,7 @@ static struct account *search_account(struct account **accts, const char *acctna } static void fillin_apy_acct_details(const struct bkpr *bkpr, + struct command *cmd, const struct account *acct, u32 current_blockheight, struct channel_apy *apy) @@ -107,7 +108,7 @@ static void fillin_apy_acct_details(const struct bkpr *bkpr, apy->acct_name = tal_strdup(apy, acct->name); assert(acct->open_event_db_id); - ev = find_chain_event_by_id(tmpctx, bkpr, *acct->open_event_db_id); + ev = find_chain_event_by_id(tmpctx, bkpr, cmd, *acct->open_event_db_id); assert(ev); apy->start_blockheight = ev->blockheight; @@ -116,7 +117,7 @@ static void fillin_apy_acct_details(const struct bkpr *bkpr, /* if this account is closed, add closing blockheight */ if (acct->closed_event_db_id) { - ev = find_chain_event_by_id(acct, bkpr, + ev = find_chain_event_by_id(acct, bkpr, cmd, *acct->closed_event_db_id); assert(ev); apy->end_blockheight = ev->blockheight; @@ -140,7 +141,8 @@ static void fillin_apy_acct_details(const struct bkpr *bkpr, } struct channel_apy **compute_channel_apys(const tal_t *ctx, - struct bkpr *bkpr, + const struct bkpr *bkpr, + struct command *cmd, u64 start_time, u64 end_time, u32 current_blockheight) @@ -149,7 +151,7 @@ struct channel_apy **compute_channel_apys(const tal_t *ctx, struct channel_apy *apy, **apys; struct account *acct, **accts; - evs = list_channel_events_timebox(ctx, bkpr->db, start_time, end_time); + evs = list_channel_events_timebox(ctx, bkpr, cmd, start_time, end_time); accts = list_accounts(ctx, bkpr); apys = tal_arr(ctx, struct channel_apy *, 0); @@ -167,7 +169,7 @@ struct channel_apy **compute_channel_apys(const tal_t *ctx, if (!acct || !streq(acct->name, ev->acct_name)) { if (acct && is_channel_account(acct->name)) { - fillin_apy_acct_details(bkpr, acct, + fillin_apy_acct_details(bkpr, cmd, acct, current_blockheight, apy); /* Save current apy, make new */ @@ -225,7 +227,7 @@ struct channel_apy **compute_channel_apys(const tal_t *ctx, } if (acct && is_channel_account(acct->name)) { - fillin_apy_acct_details(bkpr, acct, + fillin_apy_acct_details(bkpr, cmd, acct, current_blockheight, apy); /* Save current apy, make new */ diff --git a/plugins/bkpr/channelsapy.h b/plugins/bkpr/channelsapy.h index 73cacaeeb..6a63d4e07 100644 --- a/plugins/bkpr/channelsapy.h +++ b/plugins/bkpr/channelsapy.h @@ -34,7 +34,8 @@ WARN_UNUSED_RESULT bool channel_apy_sum(struct channel_apy *sum_apy, const struct channel_apy *entry); struct channel_apy **compute_channel_apys(const tal_t *ctx, - struct bkpr *bkpr, + const struct bkpr *bkpr, + struct command *cmd, u64 start_time, u64 end_time, u32 current_blockheight); diff --git a/plugins/bkpr/incomestmt.c b/plugins/bkpr/incomestmt.c index eac90f969..fd3a7e452 100644 --- a/plugins/bkpr/incomestmt.c +++ b/plugins/bkpr/incomestmt.c @@ -1,14 +1,12 @@ #include "config.h" +#include #include #include +#include #include #include #include #include -#include -#include -#include -#include #include #include #include @@ -20,6 +18,7 @@ #include #include #include +#include #include #define ONCHAIN_FEE "onchain_fee" @@ -111,6 +110,7 @@ static char *csv_safe_str(const tal_t *ctx, const char *input TAKES) static struct income_event *maybe_chain_income(const tal_t *ctx, const struct bkpr *bkpr, + struct command *cmd, struct account *acct, struct chain_event *ev) { @@ -141,7 +141,8 @@ static struct income_event *maybe_chain_income(const tal_t *ctx, /* income */ if (streq(ev->tag, "deposit")) { - struct db_stmt *stmt; + const jsmntok_t *toks; + const char *buf; /* deposit to external is cost to us */ if (is_external_account(ev->acct_name)) { @@ -168,16 +169,16 @@ static struct income_event *maybe_chain_income(const tal_t *ctx, * into a tx that included funds from a 3rd party * coming to us... eg. a splice out from the peer * to our onchain wallet */ - stmt = db_prepare_v2(bkpr->db, SQL("SELECT" - " 1" - " FROM chain_events e" - " WHERE " - " e.spending_txid = ?")); - - db_bind_txid(stmt, &ev->outpoint.txid); - db_query_prepared(stmt); - if (!db_step(stmt)) { - tal_free(stmt); + toks = sql_req(tmpctx, cmd, &buf, + "SELECT" + " 1" + " FROM chainmoves" + " WHERE " + " spending_txid = X'%s'" + " AND created_index <= %"PRIu64, + fmt_bitcoin_txid(tmpctx, &ev->outpoint.txid), + bkpr->chainmoves_index); + if (json_get_member(buf, toks, "rows")->size == 0) { /* no matching withdrawal from internal, * so must be new deposit (external) */ return chain_to_income(ctx, bkpr, ev, @@ -185,9 +186,6 @@ static struct income_event *maybe_chain_income(const tal_t *ctx, ev->credit, ev->debit); } - - db_col_ignore(stmt, "1"); - tal_free(stmt); return NULL; } @@ -306,6 +304,7 @@ static struct onchain_fee **find_consolidated_fees(const tal_t *ctx, struct income_event **list_income_events(const tal_t *ctx, const struct bkpr *bkpr, + struct command *cmd, u64 start_time, u64 end_time, bool consolidate_fees) @@ -315,9 +314,9 @@ struct income_event **list_income_events(const tal_t *ctx, struct onchain_fee **onchain_fees; struct income_event **evs; - channel_events = list_channel_events_timebox(ctx, bkpr->db, + channel_events = list_channel_events_timebox(ctx, bkpr, cmd, start_time, end_time); - chain_events = list_chain_events_timebox(ctx, bkpr, start_time, end_time); + chain_events = list_chain_events_timebox(ctx, bkpr, cmd, start_time, end_time); if (consolidate_fees) { onchain_fees = find_consolidated_fees(ctx, bkpr, @@ -369,7 +368,7 @@ struct income_event **list_income_events(const tal_t *ctx, struct account *acct = find_account(bkpr, chain->acct_name); - ev = maybe_chain_income(evs, bkpr, acct, chain); + ev = maybe_chain_income(evs, bkpr, cmd, acct, chain); if (ev) tal_arr_expand(&evs, ev); i++; @@ -407,9 +406,10 @@ struct income_event **list_income_events(const tal_t *ctx, struct income_event **list_income_events_all(const tal_t *ctx, const struct bkpr *bkpr, + struct command *cmd, bool consolidate_fees) { - return list_income_events(ctx, bkpr, 0, SQLITE_MAX_UINT, + return list_income_events(ctx, bkpr, cmd, 0, SQLITE_MAX_UINT, consolidate_fees); } diff --git a/plugins/bkpr/incomestmt.h b/plugins/bkpr/incomestmt.h index a6c813aa0..0de3c00dc 100644 --- a/plugins/bkpr/incomestmt.h +++ b/plugins/bkpr/incomestmt.h @@ -31,12 +31,14 @@ struct csv_fmt { /* List all the events that are income related (gain/loss) */ struct income_event **list_income_events_all(const tal_t *ctx, const struct bkpr *bkpr, + struct command *cmd, bool consolidate_fees); /* List all the events that are income related (gain/loss), * by a start and end date */ struct income_event **list_income_events(const tal_t *ctx, const struct bkpr *bkpr, + struct command *cmd, u64 start_time, u64 end_time, bool consolidate_fees); diff --git a/plugins/bkpr/onchain_fee.c b/plugins/bkpr/onchain_fee.c index 3f4e9dc04..4af85e259 100644 --- a/plugins/bkpr/onchain_fee.c +++ b/plugins/bkpr/onchain_fee.c @@ -397,9 +397,9 @@ char *update_channel_onchain_fees(const tal_t *ctx, struct amount_msat onchain_amt; assert(acct->onchain_resolved_block); - close_ev = find_chain_event_by_id(ctx, bkpr, + close_ev = find_chain_event_by_id(ctx, bkpr, cmd, *acct->closed_event_db_id); - events = find_chain_events_bytxid(ctx, bkpr, + events = find_chain_events_bytxid(ctx, bkpr, cmd, close_ev->spending_txid); /* Starting balance is close-ev's debit amount */ @@ -453,6 +453,7 @@ char *update_channel_onchain_fees(const tal_t *ctx, static char *is_closed_channel_txid(const tal_t *ctx, struct bkpr *bkpr, + struct command *cmd, struct chain_event *ev, struct bitcoin_txid *txid, bool *is_channel_close_tx) @@ -475,7 +476,7 @@ static char *is_closed_channel_txid(const tal_t *ctx, /* is the closed utxo the same as the one * we're trying to find fees for now */ - closed = find_chain_event_by_id(inner_ctx, bkpr, + closed = find_chain_event_by_id(inner_ctx, bkpr, cmd, *acct->closed_event_db_id); if (!closed) { *is_channel_close_tx = false; @@ -516,7 +517,7 @@ char *maybe_update_onchain_fees(const tal_t *ctx, u8 *inner_ctx = tal(NULL, u8); /* Find all the deposits/withdrawals for this txid */ - events = find_chain_events_bytxid(inner_ctx, bkpr, txid); + events = find_chain_events_bytxid(inner_ctx, bkpr, cmd, txid); /* If we don't even have two events, skip */ if (tal_count(events) < 2) @@ -524,7 +525,7 @@ char *maybe_update_onchain_fees(const tal_t *ctx, for (size_t i = 0; i < tal_count(events); i++) { bool is_channel_close_tx; - err = is_closed_channel_txid(ctx, bkpr, + err = is_closed_channel_txid(ctx, bkpr, cmd, events[i], txid, &is_channel_close_tx); diff --git a/plugins/bkpr/recorder.c b/plugins/bkpr/recorder.c index 08fda0f19..2e84bedd2 100644 --- a/plugins/bkpr/recorder.c +++ b/plugins/bkpr/recorder.c @@ -18,220 +18,62 @@ #include #include #include +#include #include - -static struct chain_event *stmt2chain_event(const tal_t *ctx, - const struct bkpr *bkpr, - struct db_stmt *stmt) -{ - struct chain_event *e = tal(ctx, struct chain_event); - e->db_id = db_col_u64(stmt, "e.id"); - e->acct_name = db_col_strdup(e, stmt, "e.account_name"); - - if (!db_col_is_null(stmt, "e.origin")) - e->origin_acct = db_col_strdup(e, stmt, "e.origin"); - else - e->origin_acct = NULL; - - e->tag = db_col_strdup(e, stmt, "e.tag"); - - e->credit = db_col_amount_msat(stmt, "e.credit"); - e->debit = db_col_amount_msat(stmt, "e.debit"); - e->output_value = db_col_amount_msat(stmt, "e.output_value"); - - e->timestamp = db_col_u64(stmt, "e.timestamp"); - e->blockheight = db_col_int(stmt, "e.blockheight"); - - db_col_txid(stmt, "e.utxo_txid", &e->outpoint.txid); - e->outpoint.n = db_col_int(stmt, "e.outnum"); - - if (e->blockheight == 0) - e->blockheight = find_blockheight(bkpr, &e->outpoint.txid); - - if (!db_col_is_null(stmt, "e.payment_id")) { - e->payment_id = tal(e, struct sha256); - db_col_sha256(stmt, "e.payment_id", e->payment_id); - } else - e->payment_id = NULL; - - if (!db_col_is_null(stmt, "e.spending_txid")) { - e->spending_txid = tal(e, struct bitcoin_txid); - db_col_txid(stmt, "e.spending_txid", e->spending_txid); - } else - e->spending_txid = NULL; - - e->stealable = db_col_int(stmt, "e.stealable") == 1; - e->splice_close = db_col_int(stmt, "e.spliced") == 1; - - return e; -} - -static struct chain_event **find_chain_events(const tal_t *ctx, - const struct bkpr *bkpr, - struct db_stmt *stmt TAKES) -{ - struct chain_event **results; - - db_query_prepared(stmt); - if (stmt->error) - db_fatal(stmt->db, "find_chain_events err: %s", stmt->error); - results = tal_arr(ctx, struct chain_event *, 0); - while (db_step(stmt)) { - struct chain_event *e = stmt2chain_event(results, bkpr, 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); - - e->db_id = db_col_u64(stmt, "e.id"); - e->acct_name = db_col_strdup(e, stmt, "e.account_name"); - - e->tag = db_col_strdup(e, stmt, "e.tag"); - - e->credit = db_col_amount_msat(stmt, "e.credit"); - e->debit = db_col_amount_msat(stmt, "e.debit"); - e->fees = db_col_amount_msat(stmt, "e.fees"); - - if (!db_col_is_null(stmt, "e.payment_id")) { - e->payment_id = tal(e, struct sha256); - db_col_sha256(stmt, "e.payment_id", e->payment_id); - } else - e->payment_id = NULL; - e->part_id = db_col_int(stmt, "e.part_id"); - e->timestamp = db_col_u64(stmt, "e.timestamp"); - - return e; -} - -static struct channel_event **find_channel_events(const tal_t *ctx, - struct db_stmt *stmt TAKES) -{ - struct channel_event **results; - - db_query_prepared(stmt); - if (stmt->error) - db_fatal(stmt->db, "find_channel_events err: %s", stmt->error); - 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); - } - - if (taken(stmt)) - tal_free(stmt); - - return results; -} - struct chain_event **list_chain_events_timebox(const tal_t *ctx, const struct bkpr *bkpr, + struct command *cmd, u64 start_time, u64 end_time) { - struct db_stmt *stmt; - - stmt = db_prepare_v2(bkpr->db, SQL("SELECT" - " e.id" - ", e.account_name" - ", e.origin" - ", e.tag" - ", e.credit" - ", e.debit" - ", e.output_value" - ", e.timestamp" - ", e.blockheight" - ", e.utxo_txid" - ", e.outnum" - ", e.spending_txid" - ", e.payment_id" - ", e.stealable" - ", e.spliced" - " FROM chain_events e" - " WHERE e.timestamp > ?" - " AND e.timestamp <= ?" - " ORDER BY e.timestamp, e.id;")); - - db_bind_u64(stmt, start_time); - db_bind_u64(stmt, end_time); - return find_chain_events(ctx, bkpr, take(stmt)); + return chain_events_from_sql(ctx, bkpr, cmd, + SELECT_CHAIN_EVENTS + " WHERE timestamp > %"PRIu64 + " AND timestamp <= %"PRIu64 + " AND created_index <= %"PRIu64 + " ORDER BY timestamp, created_index;", + start_time, end_time, + bkpr->chainmoves_index); } -struct chain_event **list_chain_events(const tal_t *ctx, const struct bkpr *bkpr) +struct chain_event **list_chain_events(const tal_t *ctx, + const struct bkpr *bkpr, + struct command *cmd) { - return list_chain_events_timebox(ctx, bkpr, 0, SQLITE_MAX_UINT); + return list_chain_events_timebox(ctx, bkpr, cmd, 0, SQLITE_MAX_UINT); } struct chain_event **account_get_chain_events(const tal_t *ctx, const struct bkpr *bkpr, + struct command *cmd, struct account *acct) { - struct db_stmt *stmt; - - stmt = db_prepare_v2(bkpr->db, SQL("SELECT" - " e.id" - ", e.account_name" - ", e.origin" - ", e.tag" - ", e.credit" - ", e.debit" - ", e.output_value" - ", e.timestamp" - ", e.blockheight" - ", e.utxo_txid" - ", e.outnum" - ", e.spending_txid" - ", e.payment_id" - ", e.stealable" - ", e.spliced" - " FROM chain_events e" - " WHERE e.account_name = ?" - " ORDER BY e.timestamp, e.id")); - - db_bind_text(stmt, acct->name); - return find_chain_events(ctx, bkpr, take(stmt)); + return chain_events_from_sql(ctx, bkpr, cmd, + SELECT_CHAIN_EVENTS + " WHERE account_id = '%s'" + " AND created_index <= %"PRIu64 + " ORDER BY timestamp, created_index;", + sql_string(tmpctx, acct->name), + bkpr->chainmoves_index); } static struct chain_event **find_txos_for_tx(const tal_t *ctx, const struct bkpr *bkpr, - struct bitcoin_txid *txid) + struct command *cmd, + const struct bitcoin_txid *txid) { - struct db_stmt *stmt; - - stmt = db_prepare_v2(bkpr->db, SQL("SELECT" - " e.id" - ", e.account_name" - ", e.origin" - ", e.tag" - ", e.credit" - ", e.debit" - ", e.output_value" - ", e.timestamp" - ", e.blockheight" - ", e.utxo_txid" - ", e.outnum" - ", e.spending_txid" - ", e.payment_id" - ", e.stealable" - ", e.spliced" - " FROM chain_events e" - " WHERE e.utxo_txid = ?" + return chain_events_from_sql(ctx, bkpr, cmd, + SELECT_CHAIN_EVENTS + /* utxo is txid:outnum */ + " WHERE utxo LIKE '%s:%%'" + " AND created_index <= %"PRIu64 " ORDER BY " - " e.utxo_txid" - ", e.outnum" - ", e.spending_txid NULLS FIRST" - ", e.blockheight")); - - db_bind_txid(stmt, txid); - return find_chain_events(ctx, bkpr, take(stmt)); + " utxo" + ", spending_txid NULLS FIRST" + ", blockheight", + fmt_bitcoin_txid(tmpctx, txid), + bkpr->chainmoves_index); } static struct txo_pair *new_txo_pair(const tal_t *ctx) @@ -244,7 +86,8 @@ static struct txo_pair *new_txo_pair(const tal_t *ctx) static struct txo_set *find_txo_set(const tal_t *ctx, const struct bkpr *bkpr, - struct bitcoin_txid *txid, + struct command *cmd, + const struct bitcoin_txid *txid, const char *acct_name, bool *is_complete) { @@ -255,7 +98,7 @@ static struct txo_set *find_txo_set(const tal_t *ctx, /* In some special cases (the opening tx), we only * want the outputs that pertain to a given account, * most other times we want all utxos, regardless of account */ - evs = find_txos_for_tx(ctx, bkpr, txid); + evs = find_txos_for_tx(ctx, bkpr, cmd, txid); txos->pairs = tal_arr(txos, struct txo_pair *, 0); txos->txid = tal_dup(txos, struct bitcoin_txid, txid); @@ -323,6 +166,7 @@ static bool txid_in_list(struct bitcoin_txid **list, bool find_txo_chain(const tal_t *ctx, const struct bkpr *bkpr, + struct command *cmd, const struct account *acct, struct txo_set ***sets) { @@ -332,7 +176,7 @@ bool find_txo_chain(const tal_t *ctx, const char *start_acct_name; assert(acct->open_event_db_id); - open_ev = find_chain_event_by_id(ctx, bkpr, + open_ev = find_chain_event_by_id(ctx, bkpr, cmd, *acct->open_event_db_id); if (sets) @@ -349,7 +193,7 @@ bool find_txo_chain(const tal_t *ctx, struct txo_set *set; bool set_complete; - set = find_txo_set(ctx, bkpr, txids[i], + set = find_txo_set(ctx, bkpr, cmd, txids[i], start_acct_name, &set_complete); @@ -392,55 +236,69 @@ bool find_txo_chain(const tal_t *ctx, } const char *find_close_account_name(const tal_t *ctx, - struct db *db, + const struct bkpr *bkpr, + struct command *cmd, const struct bitcoin_txid *txid) { - struct db_stmt *stmt; - char *acct_name; + const char *buf; + const jsmntok_t *result, *rows, *row; + size_t i; + const char *acct_name = NULL; - stmt = db_prepare_v2(db, SQL("SELECT" - " e.account_name" - " FROM chain_events e" - " WHERE " - " e.tag = ?" - " AND e.spending_txid = ?" - /* ignore splicing 'close' events */ - " AND e.spliced = 0 ")); + /* We look for a CHANNEL_CLOSE spend, but ignore “spliced” close events. */ + result = sql_req(ctx, cmd, &buf, + "SELECT account_id" + " FROM chainmoves cm" + " WHERE cm.primary_tag = '%s'" + " AND cm.spending_txid = X'%s'" + " AND NOT EXISTS (" + " SELECT 1 FROM chainmoves_extra_tags et" + " WHERE et.row = cm.created_index" + " AND et.extra_tags = 'spliced'" + " )" + " AND" + " cm.created_index <= %"PRIu64 + " LIMIT 1", + sql_string(tmpctx, mvt_tag_str(MVT_CHANNEL_CLOSE)), + fmt_bitcoin_txid(tmpctx, txid), + bkpr->chainmoves_index); - db_bind_text(stmt, mvt_tag_str(MVT_CHANNEL_CLOSE)); - db_bind_txid(stmt, txid); - db_query_prepared(stmt); + rows = json_get_member(buf, result, "rows"); + json_for_each_arr(i, row, rows) { + /* Single column => row->size == 1; first value token is row+1 */ + const jsmntok_t *val = row + 1; + acct_name = json_strdup(ctx, buf, val); + break; /* only need the first row */ + } - if (db_step(stmt)) { - acct_name = db_col_strdup(ctx, stmt, "e.account_name"); - } else - acct_name = NULL; - - tal_free(stmt); - return acct_name; + return acct_name; /* NULL if none found */ } -u64 account_onchain_closeheight(const struct bkpr *bkpr, const struct account *acct) +u64 account_onchain_closeheight(const struct bkpr *bkpr, + struct command *cmd, + const struct account *acct) { const u8 *ctx = tal(NULL, u8); struct txo_set **sets; struct chain_event *close_ev; - struct db_stmt *stmt; u64 height; assert(acct->closed_count > 0); - close_ev = find_chain_event_by_id(ctx, bkpr, + close_ev = find_chain_event_by_id(ctx, bkpr, cmd, *acct->closed_event_db_id); - if (find_txo_chain(ctx, bkpr, acct, &sets)) { + if (find_txo_chain(ctx, bkpr, cmd, acct, &sets)) { /* Ok now we find the max block height of the * spending chain_events for this channel */ bool ok; + const char *buf; + const jsmntok_t *result, *rows, *row; + size_t i; /* Have we accounted for all the outputs */ ok = false; - for (size_t i = 0; i < tal_count(sets); i++) { + for (i = 0; i < tal_count(sets); i++) { if (bitcoin_txid_eq(sets[i]->txid, close_ev->spending_txid)) { @@ -455,21 +313,28 @@ u64 account_onchain_closeheight(const struct bkpr *bkpr, const struct account *a return 0; } - stmt = db_prepare_v2(bkpr->db, SQL("SELECT" - " blockheight" - " FROM chain_events" - " WHERE account_name = ?" - " AND spending_txid IS NOT NULL" - " ORDER BY blockheight DESC" - " LIMIT 1")); + result = sql_req(tmpctx, cmd, &buf, + "SELECT blockheight" + " FROM chainmoves" + " WHERE account_id = '%s'" + " AND spending_txid IS NOT NULL" + " AND created_index <= %"PRIu64 + " ORDER BY blockheight DESC" + " LIMIT 1", + sql_string(tmpctx, acct->name), + bkpr->chainmoves_index); - db_bind_text(stmt, acct->name); - db_query_prepared(stmt); - ok = db_step(stmt); - assert(ok); + rows = json_get_member(buf, result, "rows"); + assert(rows && rows->type == JSMN_ARRAY); - height = db_col_int(stmt, "blockheight"); - tal_free(stmt); + height = 0; + json_for_each_arr(i, row, rows) { + const jsmntok_t *val = row + 1; + assert(row->size == 1); + ok = json_to_u64(buf, val, &height); + assert(ok); + break; + } } else { height = 0; } @@ -480,356 +345,169 @@ u64 account_onchain_closeheight(const struct bkpr *bkpr, const struct account *a struct chain_event *find_chain_event_by_id(const tal_t *ctx, const struct bkpr *bkpr, - u64 event_db_id) + struct command *cmd, + u64 created_index) { - struct db_stmt *stmt; - struct chain_event *e; + struct chain_event **evs = + chain_events_from_sql(tmpctx, bkpr, cmd, + SELECT_CHAIN_EVENTS + " WHERE created_index = %"PRIu64 + " LIMIT 1;", + created_index); - stmt = db_prepare_v2(bkpr->db, SQL("SELECT" - " e.id" - ", e.account_name" - ", e.origin" - ", e.tag" - ", e.credit" - ", e.debit" - ", e.output_value" - ", e.timestamp" - ", e.blockheight" - ", e.utxo_txid" - ", e.outnum" - ", e.spending_txid" - ", e.payment_id" - ", e.stealable" - ", e.spliced" - " FROM chain_events e" - " WHERE " - " e.id = ?")); + if (tal_count(evs) == 0) + return NULL; - db_bind_u64(stmt, event_db_id); - db_query_prepared(stmt); - if (db_step(stmt)) - e = stmt2chain_event(ctx, bkpr, stmt); - else - e = NULL; - - tal_free(stmt); - return e; + return tal_steal(ctx, evs[0]); } struct chain_event **get_chain_events_by_outpoint(const tal_t *ctx, const struct bkpr *bkpr, - const struct bitcoin_outpoint *outpoint, - bool credits_only) + struct command *cmd, + const struct bitcoin_outpoint *outpoint) { - struct db_stmt *stmt; - if (credits_only) - stmt = db_prepare_v2(bkpr->db, SQL("SELECT" - " e.id" - ", e.account_name" - ", e.origin" - ", e.tag" - ", e.credit" - ", e.debit" - ", e.output_value" - ", e.timestamp" - ", e.blockheight" - ", e.utxo_txid" - ", e.outnum" - ", e.spending_txid" - ", e.payment_id" - ", e.stealable" - ", e.spliced" - " FROM chain_events e" - " WHERE " - " e.utxo_txid = ?" - " AND e.outnum = ?" - " AND credit > 0")); - else - stmt = db_prepare_v2(bkpr->db, SQL("SELECT" - " e.id" - ", e.account_name" - ", e.origin" - ", e.tag" - ", e.credit" - ", e.debit" - ", e.output_value" - ", e.timestamp" - ", e.blockheight" - ", e.utxo_txid" - ", e.outnum" - ", e.spending_txid" - ", e.payment_id" - ", e.stealable" - ", e.spliced" - " FROM chain_events e" - " WHERE " - " e.utxo_txid = ?" - " AND e.outnum = ?")); - - db_bind_txid(stmt, &outpoint->txid); - db_bind_int(stmt, outpoint->n); - return find_chain_events(ctx, bkpr, take(stmt)); + return chain_events_from_sql(ctx, bkpr, cmd, + SELECT_CHAIN_EVENTS + " WHERE utxo = '%s'" + " AND credit_msat > 0" + " AND created_index <= %"PRIu64 + " ORDER BY timestamp, created_index", + fmt_bitcoin_outpoint(tmpctx, outpoint), + bkpr->chainmoves_index); } struct chain_event **get_chain_events_by_id(const tal_t *ctx, const struct bkpr *bkpr, + struct command *cmd, const struct sha256 *id) { - struct db_stmt *stmt; - stmt = db_prepare_v2(bkpr->db, SQL("SELECT" - " e.id" - ", e.account_name" - ", e.origin" - ", e.tag" - ", e.credit" - ", e.debit" - ", e.output_value" - ", e.timestamp" - ", e.blockheight" - ", e.utxo_txid" - ", e.outnum" - ", e.spending_txid" - ", e.payment_id" - ", e.stealable" - ", e.spliced" - " FROM chain_events e" - " WHERE " - " e.payment_id = ?")); - - db_bind_sha256(stmt, id); - return find_chain_events(ctx, bkpr, take(stmt)); + return chain_events_from_sql(ctx, bkpr, cmd, + SELECT_CHAIN_EVENTS + " WHERE payment_hash = X'%s'" + " AND created_index <= %"PRIu64 + " ORDER BY timestamp, created_index", + fmt_sha256(tmpctx, id), + bkpr->chainmoves_index); } -static struct chain_event *find_chain_event(const tal_t *ctx, - struct bkpr *bkpr, - const struct account *acct, - const struct bitcoin_outpoint *outpoint, - const struct bitcoin_txid *spending_txid, - const char *tag) - -{ - struct db_stmt *stmt; - struct chain_event *e; - - if (spending_txid) { - stmt = db_prepare_v2(bkpr->db, SQL("SELECT" - " e.id" - ", e.account_name" - ", e.origin" - ", e.tag" - ", e.credit" - ", e.debit" - ", e.output_value" - ", e.timestamp" - ", e.blockheight" - ", e.utxo_txid" - ", e.outnum" - ", e.spending_txid" - ", e.payment_id" - ", e.stealable" - ", e.spliced" - " FROM chain_events e" - " WHERE " - " e.spending_txid = ?" - " AND e.account_name = ?" - " AND e.utxo_txid = ?" - " AND e.outnum = ?")); - db_bind_txid(stmt, spending_txid); - } else { - stmt = db_prepare_v2(bkpr->db, SQL("SELECT" - " e.id" - ", e.account_name" - ", e.origin" - ", e.tag" - ", e.credit" - ", e.debit" - ", e.output_value" - ", e.timestamp" - ", e.blockheight" - ", e.utxo_txid" - ", e.outnum" - ", e.spending_txid" - ", e.payment_id" - ", e.stealable" - ", e.spliced" - " FROM chain_events e" - " WHERE " - " e.tag = ?" - " AND e.account_name = ?" - " AND e.utxo_txid = ?" - " AND e.outnum = ?" - " AND e.spending_txid IS NULL")); - db_bind_text(stmt, tag); - } - - db_bind_text(stmt, acct->name); - db_bind_txid(stmt, &outpoint->txid); - db_bind_int(stmt, outpoint->n); - - db_query_prepared(stmt); - if (db_step(stmt)) - e = stmt2chain_event(ctx, bkpr, stmt); - else - e = NULL; - - tal_free(stmt); - return e; -} - -bool account_get_credit_debit(struct plugin *plugin, - struct db *db, +bool account_get_credit_debit(const struct bkpr *bkpr, + struct command *cmd, const char *acct_name, struct amount_msat *credit, struct amount_msat *debit) { - struct db_stmt *stmt; + const jsmntok_t *result, *rows, *row; + const char *buf; bool exists; /* Get sum from chain_events */ - stmt = db_prepare_v2(db, SQL("SELECT" - " CAST(SUM(ce.credit) AS BIGINT) as credit" - ", CAST(SUM(ce.debit) AS BIGINT) as debit" - " FROM chain_events ce" - " WHERE ce.account_name = ?")); - db_bind_text(stmt, acct_name); - db_query_prepared(stmt); - - db_step(stmt); - if (db_col_is_null(stmt, "credit")) { - db_col_ignore(stmt, "debit"); + result = sql_req(tmpctx, cmd, &buf, + "SELECT" + " CAST(SUM(credit_msat) AS BIGINT)" + ", CAST(SUM(debit_msat) AS BIGINT)" + " FROM chainmoves" + " WHERE account_id = '%s'" + " AND created_index <= %"PRIu64, + sql_string(tmpctx, acct_name), + bkpr->chainmoves_index); + rows = json_get_member(buf, result, "rows"); + assert(rows && rows->type == JSMN_ARRAY && rows->size == 1); + row = rows + 1; + assert(row->size == 2); + if (json_tok_is_null(buf, row + 1)) { *credit = *debit = AMOUNT_MSAT(0); exists = false; } else { - *credit = db_col_amount_msat(stmt, "credit"); - *debit = db_col_amount_msat(stmt, "debit"); + json_to_msat(buf, row + 1, credit); + json_to_msat(buf, row + 2, debit); exists = true; } - tal_free(stmt); /* Get sum from channel_events */ - stmt = db_prepare_v2(db, SQL("SELECT" - " CAST(SUM(ce.credit) AS BIGINT) as credit" - ", CAST(SUM(ce.debit) AS BIGINT) as debit" - " FROM channel_events ce" - " WHERE ce.account_name = ?")); - db_bind_text(stmt, acct_name); - db_query_prepared(stmt); - db_step(stmt); + result = sql_req(tmpctx, cmd, &buf, + "SELECT" + " CAST(SUM(credit_msat) AS BIGINT)" + ", CAST(SUM(debit_msat) AS BIGINT)" + " FROM channelmoves" + " WHERE account_id = '%s'" + " AND created_index <= %"PRIu64, + sql_string(tmpctx, acct_name), + bkpr->channelmoves_index); + rows = json_get_member(buf, result, "rows"); + assert(rows && rows->type == JSMN_ARRAY && rows->size == 1); + row = rows + 1; + assert(row->size == 2); + if (!json_tok_is_null(buf, row + 1)) { + struct amount_msat channel_credit, channel_debit; + json_to_msat(buf, row + 1, &channel_credit); + json_to_msat(buf, row + 2, &channel_debit); - if (db_col_is_null(stmt, "credit")) { - db_col_ignore(stmt, "debit"); - } else { - if (!amount_msat_accumulate(credit, - db_col_amount_msat(stmt, "credit"))) { - plugin_err(plugin, "db overflow: chain credit %s, adding channel credit %s", + if (!amount_msat_accumulate(credit, channel_credit)) { + plugin_err(cmd->plugin, "db overflow: chain credit %s, adding channel credit %s", fmt_amount_msat(tmpctx, *credit), - fmt_amount_msat(tmpctx, - db_col_amount_msat(stmt, "credit"))); + fmt_amount_msat(tmpctx, channel_credit)); } - if (!amount_msat_accumulate(debit, - db_col_amount_msat(stmt, "debit"))) { - plugin_err(plugin, "db overflow: chain debit %s, adding channel debit %s", + if (!amount_msat_accumulate(debit, channel_debit)) { + plugin_err(cmd->plugin, "db overflow: chain debit %s, adding channel debit %s", fmt_amount_msat(tmpctx, *debit), - fmt_amount_msat(tmpctx, - db_col_amount_msat(stmt, "debit"))); + fmt_amount_msat(tmpctx, channel_debit)); } exists = true; } - tal_free(stmt); return exists; } struct channel_event **list_channel_events_timebox(const tal_t *ctx, - struct db *db, + const struct bkpr *bkpr, + struct command *cmd, u64 start_time, u64 end_time) - { - struct db_stmt *stmt; - struct channel_event **results; - - stmt = db_prepare_v2(db, SQL("SELECT" - " e.id" - ", e.account_name" - ", e.tag" - ", e.credit" - ", e.debit" - ", e.fees" - ", e.payment_id" - ", e.part_id" - ", e.timestamp" - " FROM channel_events e" - " WHERE e.timestamp > ?" - " AND e.timestamp <= ?" - " ORDER BY e.timestamp, e.id;")); - - db_bind_u64(stmt, start_time); - db_bind_u64(stmt, end_time); - 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; + return channel_events_from_sql(ctx, cmd, + SELECT_CHANNEL_EVENTS + " WHERE timestamp > %"PRIu64 + " AND timestamp <= %"PRIu64 + " AND created_index <= %"PRIu64 + " ORDER BY timestamp, created_index;", + start_time, end_time, + bkpr->channelmoves_index); } -struct channel_event **list_channel_events(const tal_t *ctx, struct db *db) +struct channel_event **list_channel_events(const tal_t *ctx, + const struct bkpr *bkpr, + struct command *cmd) { - return list_channel_events_timebox(ctx, db, 0, SQLITE_MAX_UINT); + return list_channel_events_timebox(ctx, bkpr, cmd, 0, SQLITE_MAX_UINT); } struct channel_event **account_get_channel_events(const tal_t *ctx, - struct db *db, + const struct bkpr *bkpr, + struct command *cmd, struct account *acct) { - struct db_stmt *stmt; - - stmt = db_prepare_v2(db, SQL("SELECT" - " e.id" - ", e.account_name" - ", e.tag" - ", e.credit" - ", e.debit" - ", e.fees" - ", e.payment_id" - ", e.part_id" - ", e.timestamp" - " FROM channel_events e" - " WHERE e.account_name = ?" - " ORDER BY e.timestamp, e.id")); - - db_bind_text(stmt, acct->name); - return find_channel_events(ctx, take(stmt)); + return channel_events_from_sql(ctx, cmd, + SELECT_CHANNEL_EVENTS + " WHERE account_id = '%s'" + " AND created_index <= %"PRIu64 + " ORDER BY timestamp, created_index", + sql_string(tmpctx, acct->name), + bkpr->channelmoves_index); } struct channel_event **get_channel_events_by_id(const tal_t *ctx, - struct db *db, - struct sha256 *id) + const struct bkpr *bkpr, + struct command *cmd, + const struct sha256 *id) { - struct db_stmt *stmt; - - stmt = db_prepare_v2(db, SQL("SELECT" - " e.id" - ", e.account_name" - ", e.tag" - ", e.credit" - ", e.debit" - ", e.fees" - ", e.payment_id" - ", e.part_id" - ", e.timestamp" - " FROM channel_events e" - " WHERE e.payment_id = ?" - " ORDER BY e.timestamp, e.id")); - - db_bind_sha256(stmt, id); - return find_channel_events(ctx, take(stmt)); + return channel_events_from_sql(ctx, cmd, + SELECT_CHANNEL_EVENTS + " WHERE payment_hash = X'%s'" + " AND created_index <= %"PRIu64 + " ORDER BY timestamp, created_index", + fmt_sha256(tmpctx, id), + bkpr->channelmoves_index); } void log_channel_event(struct db *db, @@ -872,34 +550,18 @@ void log_channel_event(struct db *db, struct chain_event **find_chain_events_bytxid(const tal_t *ctx, const struct bkpr *bkpr, + struct command *cmd, const struct bitcoin_txid *txid) { - struct db_stmt *stmt; - - stmt = db_prepare_v2(bkpr->db, SQL("SELECT " - " e.id" - ", e.account_name" - ", e.origin" - ", e.tag" - ", e.credit" - ", e.debit" - ", e.output_value" - ", e.timestamp" - ", e.blockheight" - ", e.utxo_txid" - ", e.outnum" - ", e.spending_txid" - ", e.payment_id" - ", e.stealable" - ", e.spliced" - " FROM chain_events e" - " WHERE e.spending_txid = ?" - " OR (e.utxo_txid = ? AND e.spending_txid IS NULL)" - " ORDER BY e.account_name")); - - db_bind_txid(stmt, txid); - db_bind_txid(stmt, txid); - return find_chain_events(ctx, bkpr, take(stmt)); + return chain_events_from_sql(ctx, bkpr, cmd, + SELECT_CHAIN_EVENTS + " WHERE created_index <= %"PRIu64 + " AND (spending_txid = X'%s'" + " OR (utxo LIKE '%s%%' AND spending_txid IS NULL))" + " ORDER BY account_id, created_index", + bkpr->chainmoves_index, + fmt_bitcoin_txid(tmpctx, txid), /* spending_txid match */ + fmt_bitcoin_txid(tmpctx, txid)); /* utxo prefix (txid:*) */ } void maybe_record_rebalance(struct command *cmd, @@ -911,7 +573,9 @@ void maybe_record_rebalance(struct command *cmd, * and amt as such. If you repeat a payment_id * with the same amt, they'll be marked as rebalances * also */ - struct db_stmt *stmt; + const char *buf; + const jsmntok_t *res, *rows, *row, *val; + size_t i; struct amount_msat credit; bool ok; @@ -919,54 +583,65 @@ void maybe_record_rebalance(struct command *cmd, ok = amount_msat_sub(&credit, out->debit, out->fees); assert(ok); - stmt = db_prepare_v2(bkpr->db, SQL("SELECT " - " e.id" - " FROM channel_events e" - " WHERE e.payment_id = ?" - " AND e.credit = ?")); + /* Look for a matching credit-side event for the same payment */ + res = sql_req(tmpctx, cmd, &buf, + "SELECT created_index" + " FROM channelmoves" + " WHERE payment_hash = X'%s'" + " AND credit_msat = %"PRIu64 + " AND created_index <= %"PRIu64, + fmt_sha256(tmpctx, out->payment_id), + credit.millisatoshis /* Raw: sql query */, + bkpr->channelmoves_index); - db_bind_sha256(stmt, out->payment_id); - db_bind_amount_msat(stmt, credit); - db_query_prepared(stmt); + rows = json_get_member(buf, res, "rows"); + json_for_each_arr(i, row, rows) { + u64 id; + val = row + 1; /* single column */ + ok = json_to_u64(buf, val, &id); + assert(ok); - while (db_step(stmt)) { - u64 id = db_col_u64(stmt, "e.id"); /* Already has one? */ if (find_rebalance(bkpr, id)) continue; + add_rebalance_pair(cmd, bkpr, out->db_id, id); break; } - tal_free(stmt); } void maybe_closeout_external_deposits(struct command *cmd, struct bkpr *bkpr, - const struct bitcoin_txid *txid, + const struct bitcoin_txid *txid, u32 blockheight) { - struct db_stmt *stmt; + const char *buf; + const jsmntok_t *res, *rows, *row; + size_t i; - assert(txid); - stmt = db_prepare_v2(bkpr->db, SQL("SELECT " - " 1" - " FROM chain_events e" - " WHERE e.blockheight = ?" - " AND e.utxo_txid = ?" - " AND e.account_name = ?")); + /* Find any unconfirmed external deposits for this txid. */ + res = sql_req(tmpctx, cmd, &buf, + "SELECT utxo" + " FROM chainmoves" + " WHERE blockheight = 0" + " AND utxo LIKE '%s:%%'" + " AND account_id = '%s'" + " AND created_index <= %"PRIu64, + /* utxo is ':' so we prefix-match on txid: */ + fmt_bitcoin_txid(tmpctx, txid), + sql_string(tmpctx, ACCOUNT_NAME_EXTERNAL), + bkpr->chainmoves_index); - /* Blockheight for unconfirmeds is zero */ - db_bind_int(stmt, 0); - db_bind_txid(stmt, txid); - db_bind_text(stmt, ACCOUNT_NAME_EXTERNAL); - db_query_prepared(stmt); + rows = json_get_member(buf, res, "rows"); + json_for_each_arr(i, row, rows) { + const jsmntok_t *val = row + 1; /* single column */ + struct bitcoin_outpoint outp; + bool ok; - if (db_step(stmt)) { - db_col_ignore(stmt, "1"); - add_blockheight(cmd, bkpr, txid, blockheight); + ok = json_to_outpoint(buf, val, &outp); + assert(ok); + add_blockheight(cmd, bkpr, &outp.txid, blockheight); } - - tal_free(stmt); } bool log_chain_event(struct bkpr *bkpr, @@ -975,12 +650,6 @@ bool log_chain_event(struct bkpr *bkpr, { struct db_stmt *stmt; - /* We're responsible for de-duping chain events! */ - if (find_chain_event(tmpctx, bkpr, acct, - &e->outpoint, e->spending_txid, - e->tag)) - return false; - stmt = db_prepare_v2(bkpr->db, SQL("INSERT INTO chain_events" " (" " account_name" diff --git a/plugins/bkpr/recorder.h b/plugins/bkpr/recorder.h index 991a2a59c..9ffdd2721 100644 --- a/plugins/bkpr/recorder.h +++ b/plugins/bkpr/recorder.h @@ -33,16 +33,20 @@ struct txo_set { /* Get all channel events for this account */ struct channel_event **account_get_channel_events(const tal_t *ctx, - struct db *db, + const struct bkpr *bkpr, + struct command *cmd, struct account *acct); /* Get all channel events for a payment id, order by timestamp */ struct channel_event **get_channel_events_by_id(const tal_t *ctx, - struct db *db, - struct sha256 *id); + const struct bkpr *bkpr, + struct command *cmd, + const struct sha256 *id); /* Get all channel events, ordered by timestamp */ -struct channel_event **list_channel_events(const tal_t *ctx, struct db *db); +struct channel_event **list_channel_events(const tal_t *ctx, + const struct bkpr *bkpr, + struct command *cmd); /* Get all channel events, order by timestamp. * @@ -52,50 +56,56 @@ struct channel_event **list_channel_events(const tal_t *ctx, struct db *db); * @end_time - UNIX timestamp to query until (inclusive) */ struct channel_event **list_channel_events_timebox(const tal_t *ctx, - struct db *db, + const struct bkpr *bkpr, + struct command *cmd, u64 start_time, u64 end_time); /* Get all chain events for this account */ struct chain_event **account_get_chain_events(const tal_t *ctx, const struct bkpr *bkpr, + struct command *cmd, struct account *acct); /* Get all chain events for a transaction id, order by timestamp */ struct chain_event **find_chain_events_bytxid(const tal_t *ctx, const struct bkpr *bkpr, + struct command *cmd, const struct bitcoin_txid *txid); /* Get all chain events, order by timestamp. */ -struct chain_event **list_chain_events(const tal_t *ctx, const struct bkpr *bkpr); +struct chain_event **list_chain_events(const tal_t *ctx, + const struct bkpr *bkpr, + struct command *cmd); /* Get all chain events, order by timestamp. * * @ctx - context to allocate from - * @db - database to query * @start_time - UNIX timestamp to query after (exclusive) * @end_time - UNIX timestamp to query until (inclusive) */ struct chain_event **list_chain_events_timebox(const tal_t *ctx, const struct bkpr *bkpr, + struct command *cmd, u64 start_time, u64 end_time); /* Get all chain events for a payment hash */ struct chain_event **get_chain_events_by_id(const tal_t *ctx, - const struct bkpr *bkpr, - const struct sha256 *id); + const struct bkpr *bkpr, + struct command *cmd, + const struct sha256 *id); /* Get all chain events for a utxo */ struct chain_event **get_chain_events_by_outpoint(const tal_t *ctx, const struct bkpr *bkpr, - const struct bitcoin_outpoint *outpoint, - bool credits_only); + struct command *cmd, + const struct bitcoin_outpoint *outpoint); /* Get total credits and debits for this account: returns false if no entries at all * (in which case, credit and debit will both be AMOUNT_MSAT(0)). */ -bool account_get_credit_debit(struct plugin *plugin, - struct db *db, +bool account_get_credit_debit(const struct bkpr *bkpr, + struct command *cmd, const char *acct_name, struct amount_msat *credit, struct amount_msat *debit); @@ -104,6 +114,7 @@ bool account_get_credit_debit(struct plugin *plugin, /* Find a chain event by its database id */ struct chain_event *find_chain_event_by_id(const tal_t *ctx, const struct bkpr *bkpr, + struct command *cmd, u64 event_db_id); /* Find the utxos for this account. @@ -113,13 +124,15 @@ struct chain_event *find_chain_event_by_id(const tal_t *ctx, */ bool find_txo_chain(const tal_t *ctx, const struct bkpr *bkpr, + struct command *cmd, const struct account *acct, struct txo_set ***sets); /* Find the account that was closed by this txid. * Returns NULL if none */ const char *find_close_account_name(const tal_t *ctx, - struct db *db, + const struct bkpr *bkpr, + struct command *cmd, const struct bitcoin_txid *txid); /* Have all the outputs for this account's close tx @@ -127,7 +140,9 @@ const char *find_close_account_name(const tal_t *ctx, * highest blockheight that has a resolving tx in it. * * The point of this is to allow us to prune data, eventually */ -u64 account_onchain_closeheight(const struct bkpr *bkpr, const struct account *acct); +u64 account_onchain_closeheight(const struct bkpr *bkpr, + struct command *cmd, + const struct account *acct); /* When we make external deposits from the wallet, we don't * count them until any output that was spent *into* them is