bkpr: remove missing event handling.
What changed, and why it matters
This commit removes a large chunk of fallback code in the bookkeeper plugin that tried to reconstruct missing channel-opening events by calling `listpeerchannels`. The author says missing events will no longer happen because they will be handled as a database migration at initialization. It is a cleanup/refactoring change, not a fix for an exploitable vulnerability.
Treat as a normal maintenance commit. Verify that the promised migration for missing events exists and correctly backfills data, since the fallback path is gone. No immediate security response is indicated by the diff itself.
Security signals we found
Large code deletion in a financial-accounting plugin
Removal of fallback path that reconstructed channel open events from live node state
Removal of JSON parsing of peer channel funding details for backfill
Removal of journal-entry balancing logic for missed events
Evidence from the diff
The diff deletes ~390 lines from plugins/bkpr/bookkeeper.c, including try_update_open_fees, find_push_amts, new_missed_channel_account, msat_net, msat_find_diff, log_journal_entry, listpeerchannels_done, and the call site that triggered listpeerchannels when a channel event arrived without a recorded open event. The remaining code simply logs chain moves and does close checks. The commit message frames this as removing obsolete missing-event handling because such events will be backfilled via migration.
Changed components
plugins/bkpr/bookkeeper.cInspect captured patch +3 / −397
diff --git a/plugins/bkpr/bookkeeper.c b/plugins/bkpr/bookkeeper.c
index b3c9354b..ae7d89d9 100644
--- a/plugins/bkpr/bookkeeper.c
+++ b/plugins/bkpr/bookkeeper.c
@@ -837,303 +837,6 @@ struct new_account_info {
u32 timestamp;
};
-static void try_update_open_fees(struct command *cmd,
- struct account *acct)
-{
- struct chain_event *ev;
- char *err;
- struct bkpr *bkpr = bkpr_of(cmd->plugin);
-
- assert(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);
- if (err)
- plugin_err(cmd->plugin,
- "failure updating chain fees:"
- " %s", err);
-
-}
-
-static void find_push_amts(const char *buf,
- const jsmntok_t *curr_chan,
- bool is_opener,
- struct amount_msat *push_credit,
- struct amount_msat *push_debit,
- bool *is_leased)
-{
- const char *err;
- struct amount_msat push_amt;
-
- /* Try to pull out fee_rcvd_msat */
- err = json_scan(tmpctx, buf, curr_chan,
- "{funding:{fee_rcvd_msat:%}}",
- JSON_SCAN(json_to_msat,
- push_credit));
-
- if (!err) {
- *is_leased = true;
- *push_debit = AMOUNT_MSAT(0);
- return;
- }
-
- /* Try to pull out fee_paid_msat */
- err = json_scan(tmpctx, buf, curr_chan,
- "{funding:{fee_paid_msat:%}}",
- JSON_SCAN(json_to_msat,
- push_debit));
- if (!err) {
- *is_leased = true;
- *push_credit = AMOUNT_MSAT(0);
- return;
- }
-
- /* Try to pull out pushed amt? */
- err = json_scan(tmpctx, buf, curr_chan,
- "{funding:{pushed_msat:%}}",
- JSON_SCAN(json_to_msat, &push_amt));
-
- if (!err) {
- *is_leased = false;
- if (is_opener) {
- *push_credit = AMOUNT_MSAT(0);
- *push_debit = push_amt;
- } else {
- *push_credit = push_amt;
- *push_debit = AMOUNT_MSAT(0);
- }
- return;
- }
-
- /* Nothing pushed nor fees paid */
- *is_leased = false;
- *push_credit = AMOUNT_MSAT(0);
- *push_debit = AMOUNT_MSAT(0);
-}
-
-static bool new_missed_channel_account(struct command *cmd,
- const char *buf,
- const jsmntok_t *result,
- struct account *acct,
- u64 timestamp)
-{
- struct chain_event *chain_ev;
- const char *err;
- size_t i;
- const jsmntok_t *curr_chan, *chan_arr_tok;
- struct bkpr *bkpr = bkpr_of(cmd->plugin);
-
- chan_arr_tok = json_get_member(buf, result, "channels");
- assert(chan_arr_tok && chan_arr_tok->type == JSMN_ARRAY);
-
- json_for_each_arr(i, curr_chan, chan_arr_tok) {
- struct bitcoin_outpoint opt;
- struct amount_msat amt, remote_amt,
- push_credit, push_debit;
- struct node_id peer_id;
- char *opener, *chan_id;
- enum mvt_tag *tags;
- bool ok, is_opener, is_leased;
-
- err = json_scan(tmpctx, buf, curr_chan,
- "{peer_id:%,"
- "channel_id:%,"
- "funding_txid:%,"
- "funding_outnum:%,"
- "funding:{local_funds_msat:%,"
- "remote_funds_msat:%},"
- "opener:%}",
- JSON_SCAN(json_to_node_id, &peer_id),
- JSON_SCAN_TAL(tmpctx, json_strdup, &chan_id),
- JSON_SCAN(json_to_txid, &opt.txid),
- JSON_SCAN(json_to_number, &opt.n),
- JSON_SCAN(json_to_msat, &amt),
- JSON_SCAN(json_to_msat, &remote_amt),
- JSON_SCAN_TAL(tmpctx, json_strdup, &opener));
- if (err)
- plugin_err(cmd->plugin,
- "failure scanning listpeerchannels"
- " result: %s", err);
-
- if (!streq(chan_id, acct->name))
- continue;
-
- plugin_log(cmd->plugin, LOG_DBG,
- "Logging channel account from list %s",
- acct->name);
-
- chain_ev = tal(cmd, struct chain_event);
- chain_ev->tag = mvt_tag_str(MVT_CHANNEL_OPEN);
- chain_ev->debit = AMOUNT_MSAT(0);
- ok = amount_msat_add(&chain_ev->output_value,
- amt, remote_amt);
- assert(ok);
- chain_ev->origin_acct = NULL;
- /* 2s before the channel opened, minimum */
- chain_ev->timestamp = timestamp - 2;
- chain_ev->blockheight = 0;
- chain_ev->outpoint = opt;
- chain_ev->spending_txid = NULL;
- chain_ev->payment_id = NULL;
- chain_ev->stealable = false;
- chain_ev->splice_close = false;
-
- /* Update the account info too */
- tags = tal_arr(chain_ev, enum mvt_tag, 1);
- tags[0] = MVT_CHANNEL_OPEN;
-
- is_opener = streq(opener, "local");
-
- /* Leased/pushed channels have some extra work */
- find_push_amts(buf, curr_chan, is_opener,
- &push_credit, &push_debit,
- &is_leased);
-
- if (is_leased)
- tal_arr_expand(&tags, MVT_LEASED);
- if (is_opener)
- tal_arr_expand(&tags, MVT_OPENER);
-
- chain_ev->credit = amt;
- db_begin_transaction(bkpr->db);
- if (!log_chain_event(bkpr, acct, chain_ev))
- goto done;
-
- maybe_update_account(cmd, acct, chain_ev,
- tags, 0, &peer_id);
- maybe_update_onchain_fees(cmd, cmd, bkpr, &opt.txid);
-
- /* We won't count the close's fees if we're
- * *not* the opener, which we didn't know
- * until now, so now try to update the
- * fees for the close tx's spending_txid..*/
- if (acct->closed_event_db_id)
- try_update_open_fees(cmd, acct);
-
- /* We log a channel event for the push amt */
- if (!amount_msat_is_zero(push_credit)
- || !amount_msat_is_zero(push_debit)) {
- struct channel_event *chan_ev;
- char *chan_tag;
-
- chan_tag = tal_fmt(tmpctx, "%s",
- mvt_tag_str(
- is_leased ?
- MVT_LEASE_FEE : MVT_PUSHED));
- chan_ev = new_channel_event(tmpctx,
- chan_tag,
- push_credit,
- push_debit,
- AMOUNT_MSAT(0),
- NULL, 0,
- timestamp - 1);
- log_channel_event(bkpr->db, acct, chan_ev);
- }
-
-done:
- db_commit_transaction(bkpr->db);
- return true;
- }
-
- return false;
-}
-
-/* Net out credit/debit --> basically find the diff */
-static char *msat_net(const tal_t *ctx,
- struct amount_msat credit,
- struct amount_msat debit,
- struct amount_msat *credit_net,
- struct amount_msat *debit_net)
-{
- if (amount_msat_eq(credit, debit)) {
- *credit_net = AMOUNT_MSAT(0);
- *debit_net = AMOUNT_MSAT(0);
- } else if (amount_msat_greater(credit, debit)) {
- if (!amount_msat_sub(credit_net, credit, debit))
- return tal_fmt(ctx, "unexpected fail, can't sub."
- " %s - %s",
- fmt_amount_msat(ctx, credit),
- fmt_amount_msat(ctx, debit));
- *debit_net = AMOUNT_MSAT(0);
- } else {
- if (!amount_msat_sub(debit_net, debit, credit)) {
- return tal_fmt(ctx, "unexpected fail, can't sub."
- " %s - %s",
- fmt_amount_msat(ctx, debit),
- fmt_amount_msat(ctx, credit));
- }
- *credit_net = AMOUNT_MSAT(0);
- }
-
- return NULL;
-}
-
-static char *msat_find_diff(struct amount_msat balance,
- struct amount_msat credits,
- struct amount_msat debits,
- struct amount_msat *credit_diff,
- struct amount_msat *debit_diff)
-{
- struct amount_msat net_credit, net_debit;
- char *err;
-
- err = msat_net(tmpctx, credits, debits,
- &net_credit, &net_debit);
- if (err)
- return err;
-
- /* If we're not missing events, debits == 0 */
- if (!amount_msat_is_zero(net_debit)) {
- assert(amount_msat_is_zero(net_credit));
- if (!amount_msat_add(credit_diff, net_debit, balance))
- return "Overflow finding credit_diff";
- *debit_diff = AMOUNT_MSAT(0);
- } else {
- assert(amount_msat_is_zero(net_debit));
- if (amount_msat_greater(net_credit, balance)) {
- if (!amount_msat_sub(debit_diff, net_credit,
- balance))
- return "Err net_credit - amt";
- *credit_diff = AMOUNT_MSAT(0);
- } else {
- if (!amount_msat_sub(credit_diff, balance,
- net_credit))
- return "Err amt - net_credit";
-
- *debit_diff = AMOUNT_MSAT(0);
- }
- }
-
- return NULL;
-}
-
-static void log_journal_entry(struct db *db,
- struct account *acct,
- u64 timestamp,
- struct amount_msat credit_diff,
- struct amount_msat debit_diff)
-{
- struct channel_event *chan_ev;
-
- /* No diffs to register, no journal needed */
- if (amount_msat_is_zero(credit_diff)
- && amount_msat_is_zero(debit_diff))
- return;
-
- chan_ev = new_channel_event(tmpctx,
- tal_fmt(tmpctx, "%s",
- account_entry_tag_str(JOURNAL_ENTRY)),
- credit_diff,
- debit_diff,
- AMOUNT_MSAT(0),
- NULL, 0,
- timestamp);
- db_begin_transaction(db);
- log_channel_event(db, acct, chan_ev);
- db_commit_transaction(db);
-}
-
static struct command_result *log_error(struct command *cmd,
const char *method,
const char *buf,
@@ -1377,64 +1080,6 @@ static struct command_result *lookup_invoice_desc(struct command *cmd,
return send_outreq(req);
}
-struct event_info {
- struct refresh_info *rinfo;
- struct chain_event *ev;
- struct account *acct;
-};
-
-static struct command_result *
-listpeerchannels_done(struct command *cmd,
- const char *method,
- const char *buf,
- const jsmntok_t *result,
- struct event_info *info)
-{
- struct amount_msat credit, debit, credit_diff, debit_diff;
- const char *err;
- struct bkpr *bkpr = bkpr_of(cmd->plugin);
-
- if (new_missed_channel_account(cmd, buf, result,
- info->acct,
- info->ev->timestamp)) {
- db_begin_transaction(bkpr->db);
- account_get_credit_debit(bkpr, cmd, info->acct->name,
- &credit, &debit);
- db_commit_transaction(bkpr->db);
-
- /* The expected current balance is zero, since
- * we just got the channel close event */
- err = msat_find_diff(AMOUNT_MSAT(0),
- credit,
- debit,
- &credit_diff, &debit_diff);
- if (err)
- plugin_err(cmd->plugin, "%s", err);
-
- log_journal_entry(bkpr->db,
- info->acct,
- info->ev->timestamp - 1,
- credit_diff, debit_diff);
- } else
- plugin_log(cmd->plugin, LOG_BROKEN,
- "Unable to find account %s in listpeers",
- info->acct->name);
-
- /* Maybe mark acct as onchain resolved */
- err = do_account_close_checks(cmd, bkpr, info->ev, info->acct);
- if (err)
- plugin_err(cmd->plugin, "%s", err);
-
- if (info->ev->payment_id &&
- streq(info->ev->tag, mvt_tag_str(MVT_INVOICE))) {
- lookup_invoice_desc(cmd, info->ev->credit,
- info->ev->payment_id,
- info->rinfo);
- }
-
- return rinfo_one_done(cmd, info->rinfo);
-}
-
static enum mvt_tag *json_to_tags(const tal_t *ctx, const char *buffer, const jsmntok_t *tok)
{
size_t i;
@@ -1459,7 +1104,7 @@ parse_and_log_chain_move(struct command *cmd,
struct sha256 *payment_hash = tal(cmd, struct sha256);
struct bitcoin_txid *spending_txid = tal(cmd, struct bitcoin_txid);
struct node_id *peer_id;
- struct account *acct, *orig_acct;
+ struct account *acct;
u32 closed_count;
char *acct_name;
const char *err;
@@ -1577,13 +1222,8 @@ parse_and_log_chain_move(struct command *cmd,
/* FIXME: lookup the peer id for this channel! */
acct = find_or_create_account(cmd, bkpr, acct_name);
- if (e->origin_acct) {
- /* Go fetch the originating account
- * (we might not have it) */
- orig_acct = find_or_create_account(cmd, bkpr, e->origin_acct);
- } else
- orig_acct = NULL;
-
+ if (e->origin_acct)
+ find_or_create_account(cmd, bkpr, e->origin_acct);
/* Make this visible for queries (we expect increasing!) */
assert(e->db_id > bkpr->chainmoves_index);
@@ -1627,40 +1267,6 @@ parse_and_log_chain_move(struct command *cmd,
db_commit_transaction(bkpr->db);
}
- /* If this is a channel account event, it's possible
- * that we *never* got the open event. (This happens
- * if you add the plugin *after* you've closed the channel) */
- if (!e->foreign
- && ((!acct->open_event_db_id && is_channel_account(acct->name))
- || (orig_acct && is_channel_account(orig_acct->name)
- && !orig_acct->open_event_db_id))) {
- /* Find the channel open info for this peer */
- struct out_req *req;
- struct event_info *info;
-
- plugin_log(cmd->plugin, LOG_DBG,
- "channel event received but no open for channel %s."
- " Calling `listpeerchannls` to fetch missing info",
- acct->name);
-
- info = tal(cmd, struct event_info);
- info->rinfo = use_rinfo(rinfo);
- info->ev = tal_steal(info, e);
- info->acct = tal_steal(info,
- is_channel_account(acct->name) ?
- acct : orig_acct);
- info->rinfo = use_rinfo(rinfo);
-
- req = jsonrpc_request_start(cmd,
- "listpeerchannels",
- listpeerchannels_done,
- log_error,
- info);
- /* FIXME: use the peer_id to reduce work here */
- send_outreq(req);
- return;
- }
-
/* Maybe mark acct as onchain resolved */
err = do_account_close_checks(cmd, bkpr, e, acct);
if (err)
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.