chaintopology: don't use txfilter, rely on wallet_extract_owned_outputs.
What changed, and why it matters
This commit removes an old shortcut (a Bloom-like 'txfilter') that Core Lightning used to guess whether a new Bitcoin block contained transactions relevant to the wallet. Instead, it now asks the wallet directly to identify which transaction outputs belong to the node. The change is a code simplification and likely a defensive fix: the old filter could in theory miss transactions or produce false positives, while the wallet lookup is authoritative. There is no direct evidence in the commit that this was exploited or that it caused a concrete loss of funds, but it touches the code that decides whether the node notices its own on-chain payments.
Treat as a hardening/simplification change rather than an active vulnerability. Reviewers should verify that wallet_extract_owned_outputs() reliably populates the wallet's our_addresses cache for every scriptPubKey the old txfilter would have matched, including change outputs, final addresses, and migration/fixup paths, so no UTXOs are missed during block scanning. Regression tests around block filtering and invoice_onchain_payment should be run.
Security signals we found
Removal of redundant address-matching filter in favor of authoritative wallet lookup
Change to on-chain output discovery path that affects UTXO detection and invoice payment recognition
Potential for missed or delayed UTXO detection if wallet_extract_owned_outputs population behavior differs from txfilter
No explicit bug, CVE, or exploit described in commit or references
Evidence from the diff
The patch changes filter_block_txs() in lightningd/chaintopology.c to stop using the txfilter (a separate hash of watched scriptPubKeys/derived keys) and instead rely on wallet_extract_owned_outputs() to detect owned outputs when scanning blocks. The previous flow first ran txfilter_match() and txfilter_scriptpubkey_matches() to decide whether to call wallet_extract_owned_outputs() and invoice_check_onchain_payment(). The new flow calls wallet_extract_owned_outputs() directly, which populates an our_outnums array of output indices, then iterates only those outputs for invoice checking. The commit message argues all prior txfilter insertion points are already covered by wallet_get_newindex() or by wallet_extract_owned_outputs itself, making the filter redundant. This reduces duplicated state and removes a potential source of inconsistency between the filter and the wallet’s authoritative address/key index.
Changed components
lightningd/chaintopology.cwallet output extraction and UTXO detectionon-chain invoice payment detectionInspect captured patch +13 / −15
diff --git a/lightningd/chaintopology.c b/lightningd/chaintopology.c
index 1f460e8..1788081 100644
--- a/lightningd/chaintopology.c
+++ b/lightningd/chaintopology.c
@@ -40,7 +40,6 @@ static bool we_broadcast(const struct chain_topology *topo,
static void filter_block_txs(struct chain_topology *topo, struct block *b)
{
- struct txfilter *filter = topo->bitcoind->ld->owned_txfilter;
size_t i;
/* Now we see if any of those txs are interesting. */
@@ -50,6 +49,7 @@ static void filter_block_txs(struct chain_topology *topo, struct block *b)
struct bitcoin_txid txid;
size_t j;
bool is_coinbase = i == 0;
+ size_t *our_outnums;
/* Tell them if it spends a txo we care about. */
for (j = 0; j < tx->wtx->num_inputs; j++) {
@@ -69,23 +69,21 @@ static void filter_block_txs(struct chain_topology *topo, struct block *b)
}
txid = b->txids[i];
- if (txfilter_match(filter, tx)) {
- wallet_extract_owned_outputs(topo->bitcoind->ld->wallet,
- tx->wtx, is_coinbase, &b->height, NULL);
+ our_outnums = tal_arr(tmpctx, size_t, 0);
+ if (wallet_extract_owned_outputs(topo->bitcoind->ld->wallet,
+ tx->wtx, is_coinbase, &b->height, &our_outnums)) {
wallet_transaction_add(topo->ld->wallet, tx->wtx,
b->height, i);
- // invoice_check_onchain_payment(tx);
- for (size_t k = 0; k < tx->wtx->num_outputs; k++) {
+ for (size_t k = 0; k < tal_count(our_outnums); k++) {
const struct wally_tx_output *txout;
- txout = &tx->wtx->outputs[k];
- if (txfilter_scriptpubkey_matches(filter, txout->script)) {
- struct amount_sat amount;
- struct bitcoin_outpoint outpoint;
- outpoint.txid = txid;
- outpoint.n = k;
- amount = bitcoin_tx_output_get_amount_sat(tx, k);
- invoice_check_onchain_payment(topo->ld, txout->script, amount, &outpoint);
- }
+ struct amount_sat amount;
+ struct bitcoin_outpoint outpoint;
+
+ txout = &tx->wtx->outputs[our_outnums[k]];
+ outpoint.txid = txid;
+ outpoint.n = our_outnums[k];
+ amount = bitcoin_tx_output_get_amount_sat(tx, our_outnums[k]);
+ invoice_check_onchain_payment(topo->ld, txout->script, amount, &outpoint);
}
}
Why this scored 24/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.