What changed, and why it matters
This commit removes an unused internal transaction filter called txfilter. The code that added wallet addresses and public keys to this filter is deleted because nothing was actually using the filter anymore. There is no indication in the commit that this fixes a security bug; it appears to be ordinary code cleanup.
No immediate security action is required. Treat as routine refactoring. If evaluating for a security advisory, verify whether the removed txfilter had any remaining consumer that relied on it for detecting deposits or spends; the commit message asserts there was none.
Security signals we found
Large deletion of code paths that tracked wallet-owned scriptPubKeys
Removal of a filter that matched transactions against known wallet scripts
No explicit security claim, CVE, or bug description in commit message
Evidence from the diff
The patch deletes the txfilter module’s public API and all call sites that populated it. Previously, txfilter was initialized with BIP32/BIP86-derived keys, channel shutdown scripts, change outputs, new addresses, and unconfirmed UTXO scripts. The commit message states ‘chaintopology was the only user, so we don’t need the txfilter at all.’ The remaining outpointfilter is left intact. No replacement filtering logic is introduced, and no vulnerability is described.
Changed components
wallet/txfilter.cwallet/txfilter.hlightningd/lightningd.clightningd/channel.cwallet/reservation.cwallet/wallet.cwallet/walletrpc.cInspect captured patch +0 / −219
diff --git a/lightningd/channel.c b/lightningd/channel.c
index e92ff8d7..2d4167d7 100644
--- a/lightningd/channel.c
+++ b/lightningd/channel.c
@@ -718,18 +718,6 @@ struct channel *new_channel(struct peer *peer, u64 dbid,
/* Populate channel->channel_gossip */
channel_gossip_init(channel, take(peer_update));
- /* Make sure we see any spends using this key */
- if (!local_shutdown_scriptpubkey) {
- if (anysegwit) {
- txfilter_add_scriptpubkey(peer->ld->owned_txfilter,
- take(p2tr_for_keyidx(NULL, peer->ld,
- channel->final_key_idx)));
- } else {
- txfilter_add_scriptpubkey(peer->ld->owned_txfilter,
- take(p2wpkh_for_keyidx(NULL, peer->ld,
- channel->final_key_idx)));
- }
- }
/* scid is NULL when opening a new channel so we don't
* need to set error in that case as well */
if (channel->scid && is_stub_scid(*channel->scid))
diff --git a/lightningd/lightningd.c b/lightningd/lightningd.c
index 97c5461a..fd5a978e 100644
--- a/lightningd/lightningd.c
+++ b/lightningd/lightningd.c
@@ -77,7 +77,6 @@
#include <lightningd/subd.h>
#include <sys/resource.h>
#include <wallet/invoices.h>
-#include <wallet/txfilter.h>
#include <wally_bip32.h>
static void destroy_alt_subdaemons(struct lightningd *ld);
@@ -665,44 +664,6 @@ static void shutdown_global_subdaemons(struct lightningd *ld)
ld->hsm = subd_shutdown(ld->hsm, 10);
}
-/*~ Our wallet logic needs to know what outputs we might be interested in. We
- * use BIP32 (a.k.a. "HD wallet") to generate keys from a single seed, so we
- * keep the maximum-ever-used key index in the db, and add them all to the
- * filter here. */
-static void init_txfilter(struct wallet *w,
- const struct ext_key *bip32_base,
- struct txfilter *filter)
-{
- /*~ This is defined in libwally, so we didn't have to reimplement */
- struct ext_key ext;
- /*~ Note the use of ccan/short_types u64 rather than uint64_t.
- * Thank me later. */
- u64 bip32_max_index, bip86_max_index;
-
- bip32_max_index = db_get_intvar(w->db, "bip32_max_index", 0);
- /*~ One of the C99 things I unequivocally approve: for-loop scope. */
- for (u64 i = 0; i <= bip32_max_index + w->keyscan_gap; i++) {
- if (bip32_key_from_parent(bip32_base, i, BIP32_FLAG_KEY_PUBLIC, &ext) != WALLY_OK) {
- abort();
- }
- txfilter_add_derkey(filter, ext.pub_key);
- }
-
- /* If BIP86 is enabled, also add BIP86-derived keys to the filter */
- if (w->ld->bip86_base) {
- bip86_max_index = db_get_intvar(w->db, "bip86_max_index", 0);
- for (u64 i = 0; i <= bip86_max_index + w->keyscan_gap; i++) {
- struct pubkey pubkey;
- bip86_pubkey(w->ld, &pubkey, i);
- /* Add both P2TR and P2WPKH scripts since BIP86 keys can be used for both */
- u8 *p2tr_script = scriptpubkey_p2tr(tmpctx, &pubkey);
- txfilter_add_scriptpubkey(filter, take(p2tr_script));
- u8 *p2wpkh_script = scriptpubkey_p2wpkh(tmpctx, &pubkey);
- txfilter_add_scriptpubkey(filter, take(p2wpkh_script));
- }
- }
-}
-
/*~ The normal advice for daemons is to move into the root directory, so you
* don't prevent unmounting whatever filesystem you happen to start in.
*
@@ -1317,9 +1278,6 @@ int main(int argc, char *argv[])
ld->wallet = wallet_new(ld, ld->timers);
trace_span_end(ld);
- /*~ We keep a filter of scriptpubkeys we're interested in. */
- ld->owned_txfilter = txfilter_new(ld);
-
/*~ This is the ccan/io central poll override from above. */
io_poll_override(io_poll_lightningd);
@@ -1351,11 +1309,6 @@ int main(int argc, char *argv[])
if (!wallet_sanity_check(ld->wallet))
errx(EXITCODE_WALLET_DB_MISMATCH, "Wallet sanity check failed.");
- /*~ Initialize the transaction filter with our pubkeys. */
- trace_span_start("init_txfilter", ld->wallet);
- init_txfilter(ld->wallet, ld->bip32_base, ld->owned_txfilter);
- trace_span_end(ld->wallet);
-
/*~ Finish our runes initialization (includes reading from db) */
runes_finish_init(ld->runes);
diff --git a/lightningd/lightningd.h b/lightningd/lightningd.h
index 79613faa..9edba38d 100644
--- a/lightningd/lightningd.h
+++ b/lightningd/lightningd.h
@@ -257,9 +257,6 @@ struct lightningd {
/* Maintained by invoices.c */
struct invoices *invoices;
- /* Transaction filter matching what we're interested in */
- struct txfilter *owned_txfilter;
-
/* PID file */
char *pidfile;
diff --git a/lightningd/test/run-find_my_abspath.c b/lightningd/test/run-find_my_abspath.c
index 37ff3c1a..3d227bd9 100644
--- a/lightningd/test/run-find_my_abspath.c
+++ b/lightningd/test/run-find_my_abspath.c
@@ -12,9 +12,6 @@ int unused_main(int argc, char *argv[]);
/* Generated stub for begin_topology */
void begin_topology(struct chain_topology *topo UNNEEDED)
{ fprintf(stderr, "begin_topology called!\n"); abort(); }
-/* Generated stub for bip86_pubkey */
-void bip86_pubkey(struct lightningd *ld UNNEEDED, struct pubkey *pubkey UNNEEDED, u32 index UNNEEDED)
-{ fprintf(stderr, "bip86_pubkey called!\n"); abort(); }
/* Generated stub for channel_gossip_notify_new_block */
void channel_gossip_notify_new_block(struct lightningd *ld UNNEEDED)
{ fprintf(stderr, "channel_gossip_notify_new_block called!\n"); abort(); }
@@ -55,9 +52,6 @@ void db_begin_transaction_(struct db *db UNNEEDED, const char *location UNNEEDED
/* Generated stub for db_commit_transaction */
void db_commit_transaction(struct db *db UNNEEDED)
{ fprintf(stderr, "db_commit_transaction called!\n"); abort(); }
-/* Generated stub for db_get_intvar */
-s64 db_get_intvar(struct db *db UNNEEDED, const char *varname UNNEEDED, s64 defval UNNEEDED)
-{ fprintf(stderr, "db_get_intvar called!\n"); abort(); }
/* Generated stub for db_in_transaction */
bool db_in_transaction(struct db *db UNNEEDED)
{ fprintf(stderr, "db_in_transaction called!\n"); abort(); }
@@ -213,16 +207,6 @@ void stop_topology(struct chain_topology *topo UNNEEDED)
/* Generated stub for towire_hsmd_ecdh_req */
u8 *towire_hsmd_ecdh_req(const tal_t *ctx UNNEEDED, const struct pubkey *point UNNEEDED)
{ fprintf(stderr, "towire_hsmd_ecdh_req called!\n"); abort(); }
-/* Generated stub for txfilter_add_derkey */
-void txfilter_add_derkey(struct txfilter *filter UNNEEDED,
- const u8 derkey[PUBKEY_CMPR_LEN])
-{ fprintf(stderr, "txfilter_add_derkey called!\n"); abort(); }
-/* Generated stub for txfilter_add_scriptpubkey */
-void txfilter_add_scriptpubkey(struct txfilter *filter UNNEEDED, const u8 *script TAKES UNNEEDED)
-{ fprintf(stderr, "txfilter_add_scriptpubkey called!\n"); abort(); }
-/* Generated stub for txfilter_new */
-struct txfilter *txfilter_new(const tal_t *ctx UNNEEDED)
-{ fprintf(stderr, "txfilter_new called!\n"); abort(); }
/* Generated stub for waitblockheight_notify_new_block */
void waitblockheight_notify_new_block(struct lightningd *ld UNNEEDED)
{ fprintf(stderr, "waitblockheight_notify_new_block called!\n"); abort(); }
diff --git a/wallet/reservation.c b/wallet/reservation.c
index b31870a6..a340cd88 100644
--- a/wallet/reservation.c
+++ b/wallet/reservation.c
@@ -386,7 +386,6 @@ static struct command_result *finish_psbt(struct command *cmd,
"Failed to generate change address."
" Keys generation failure");
}
- txfilter_add_scriptpubkey(cmd->ld->owned_txfilter, b32script);
change_outnum = psbt->num_outputs;
psbt_append_output(psbt, b32script, change);
@@ -725,7 +724,6 @@ static struct command_result *json_addpsbtoutput(struct command *cmd,
"Failed to generate change address."
" Keys generation failure");
}
- txfilter_add_scriptpubkey(cmd->ld->owned_txfilter, b32script);
}
outnum = psbt->num_outputs;
diff --git a/wallet/test/run-chain_moves_duplicate-detect.c b/wallet/test/run-chain_moves_duplicate-detect.c
index 428768d2..491ccc1d 100644
--- a/wallet/test/run-chain_moves_duplicate-detect.c
+++ b/wallet/test/run-chain_moves_duplicate-detect.c
@@ -355,9 +355,6 @@ u8 *towire_hsmd_get_channel_basepoints(const tal_t *ctx UNNEEDED, const struct n
/* Generated stub for towire_hsmd_get_output_scriptpubkey */
u8 *towire_hsmd_get_output_scriptpubkey(const tal_t *ctx UNNEEDED, u64 channel_id UNNEEDED, const struct node_id *peer_id UNNEEDED, const struct pubkey *commitment_point UNNEEDED)
{ fprintf(stderr, "towire_hsmd_get_output_scriptpubkey called!\n"); abort(); }
-/* Generated stub for txfilter_add_scriptpubkey */
-void txfilter_add_scriptpubkey(struct txfilter *filter UNNEEDED, const u8 *script TAKES UNNEEDED)
-{ fprintf(stderr, "txfilter_add_scriptpubkey called!\n"); abort(); }
/* Generated stub for wait_index_increment */
u64 wait_index_increment(struct lightningd *ld UNNEEDED,
struct db *db UNNEEDED,
diff --git a/wallet/test/run-db.c b/wallet/test/run-db.c
index c1b4cb1b..3ac23b20 100644
--- a/wallet/test/run-db.c
+++ b/wallet/test/run-db.c
@@ -368,9 +368,6 @@ u8 *towire_hsmd_get_channel_basepoints(const tal_t *ctx UNNEEDED, const struct n
/* Generated stub for towire_hsmd_get_output_scriptpubkey */
u8 *towire_hsmd_get_output_scriptpubkey(const tal_t *ctx UNNEEDED, u64 channel_id UNNEEDED, const struct node_id *peer_id UNNEEDED, const struct pubkey *commitment_point UNNEEDED)
{ fprintf(stderr, "towire_hsmd_get_output_scriptpubkey called!\n"); abort(); }
-/* Generated stub for txfilter_add_scriptpubkey */
-void txfilter_add_scriptpubkey(struct txfilter *filter UNNEEDED, const u8 *script TAKES UNNEEDED)
-{ fprintf(stderr, "txfilter_add_scriptpubkey called!\n"); abort(); }
/* Generated stub for wait_index_increment */
u64 wait_index_increment(struct lightningd *ld UNNEEDED,
struct db *db UNNEEDED,
diff --git a/wallet/test/run-migrate_remove_chain_moves_duplicates.c b/wallet/test/run-migrate_remove_chain_moves_duplicates.c
index 2bb792e2..d2f25f8f 100644
--- a/wallet/test/run-migrate_remove_chain_moves_duplicates.c
+++ b/wallet/test/run-migrate_remove_chain_moves_duplicates.c
@@ -404,9 +404,6 @@ u8 *towire_hsmd_get_channel_basepoints(const tal_t *ctx UNNEEDED, const struct n
/* Generated stub for towire_hsmd_get_output_scriptpubkey */
u8 *towire_hsmd_get_output_scriptpubkey(const tal_t *ctx UNNEEDED, u64 channel_id UNNEEDED, const struct node_id *peer_id UNNEEDED, const struct pubkey *commitment_point UNNEEDED)
{ fprintf(stderr, "towire_hsmd_get_output_scriptpubkey called!\n"); abort(); }
-/* Generated stub for txfilter_add_scriptpubkey */
-void txfilter_add_scriptpubkey(struct txfilter *filter UNNEEDED, const u8 *script TAKES UNNEEDED)
-{ fprintf(stderr, "txfilter_add_scriptpubkey called!\n"); abort(); }
/* Generated stub for wait_index_increment */
u64 wait_index_increment(struct lightningd *ld UNNEEDED,
struct db *db UNNEEDED,
diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c
index 6913299c..4f698c34 100644
--- a/wallet/test/run-wallet.c
+++ b/wallet/test/run-wallet.c
@@ -858,12 +858,6 @@ struct log_book *new_log_book(struct lightningd *ld UNNEEDED)
return NULL;
}
-void txfilter_add_scriptpubkey(struct txfilter *filter UNNEEDED, const u8 *script TAKES)
-{
- if (taken(script))
- tal_free(script);
-}
-
/* Can actually be called by new_channel */
u32 get_block_height(const struct chain_topology *topo UNNEEDED)
{
diff --git a/wallet/txfilter.c b/wallet/txfilter.c
index e3249acc..f5255b47 100644
--- a/wallet/txfilter.c
+++ b/wallet/txfilter.c
@@ -5,31 +5,6 @@
#include <wallet/txfilter.h>
#include <wallet/wallet.h>
-size_t scriptpubkey_hash(const u8 *out)
-{
- struct siphash24_ctx ctx;
- siphash24_init(&ctx, siphash_seed());
- siphash24_update(&ctx, out, tal_bytelen(out));
- return siphash24_done(&ctx);
-}
-
-static const u8 *scriptpubkey_keyof(const u8 *out)
-{
- return out;
-}
-
-static bool scriptpubkey_eq(const u8 *a, const u8 *b)
-{
- return tal_arr_eq(a, b);
-}
-
-/* FIXME: Should we disallow dups here? */
-HTABLE_DEFINE_DUPS_TYPE(u8, scriptpubkey_keyof, scriptpubkey_hash, scriptpubkey_eq, scriptpubkeyset);
-
-struct txfilter {
- struct scriptpubkeyset scriptpubkeyset;
-};
-
static size_t outpoint_hash(const struct bitcoin_outpoint *out)
{
struct siphash24_ctx ctx;
@@ -51,52 +26,6 @@ struct outpointfilter {
struct outpointset *set;
};
-struct txfilter *txfilter_new(const tal_t *ctx)
-{
- struct txfilter *filter = tal(ctx, struct txfilter);
- scriptpubkeyset_init(&filter->scriptpubkeyset);
- return filter;
-}
-
-void txfilter_add_scriptpubkey(struct txfilter *filter, const u8 *script TAKES)
-{
- scriptpubkeyset_add(
- &filter->scriptpubkeyset,
- notleak(tal_dup_talarr(filter, u8, script)));
-}
-
-void txfilter_add_derkey(struct txfilter *filter,
- const u8 derkey[PUBKEY_CMPR_LEN])
-{
- u8 *skp, *p2sh, *p2tr;
-
- skp = scriptpubkey_p2wpkh_derkey(tmpctx, derkey);
- p2sh = scriptpubkey_p2sh(tmpctx, skp);
- p2tr = scriptpubkey_p2tr_derkey(tmpctx, derkey);
-
- txfilter_add_scriptpubkey(filter, take(skp));
- txfilter_add_scriptpubkey(filter, take(p2sh));
- txfilter_add_scriptpubkey(filter, take(p2tr));
-}
-
-
-bool txfilter_match(const struct txfilter *filter, const struct bitcoin_tx *tx)
-{
- for (size_t i = 0; i < tx->wtx->num_outputs; i++) {
- const struct wally_tx_output *txout = &tx->wtx->outputs[i];
- if (txfilter_scriptpubkey_matches(filter, txout->script))
- return true;
- }
- return false;
-}
-
-bool txfilter_scriptpubkey_matches(const struct txfilter *filter, const u8 *scriptPubKey)
-{
- if (!scriptPubKey)
- return false;
- return scriptpubkeyset_exists(&filter->scriptpubkeyset, scriptPubKey);
-}
-
void outpointfilter_add(struct outpointfilter *of,
const struct bitcoin_outpoint *outpoint)
{
diff --git a/wallet/txfilter.h b/wallet/txfilter.h
index 2e72b68e..768ca157 100644
--- a/wallet/txfilter.h
+++ b/wallet/txfilter.h
@@ -4,44 +4,11 @@
#include <bitcoin/pubkey.h>
#include <bitcoin/tx.h>
-struct txfilter;
-
/**
* outpointfilter -- Simple filter that keeps track of outpoints
*/
struct outpointfilter;
-/**
- * txfilter_new -- Construct and initialize a new txfilter
- */
-struct txfilter *txfilter_new(const tal_t *ctx);
-
-/**
- * txfilter_add_derkey -- Add a scriptpubkeys matching the der key to the filter
- *
- * This ensures that we recognize the scriptpubkeys to our keys when
- * filtering transactions. If any of the outputs matches the
- * scriptpubkey then the transaction is marked as a match. Adds
- * scriptpubkey for taproot, raw p2wpkh and p2wpkh wrapped in p2sh.
- */
-void txfilter_add_derkey(struct txfilter *filter,
- const u8 derkey[PUBKEY_CMPR_LEN]);
-
-/**
- * txfilter_match -- Check whether the tx matches the filter
- */
-bool txfilter_match(const struct txfilter *filter, const struct bitcoin_tx *tx);
-
-/**
- * txfilter_matches -- Check whether the scriptpubkey matches the filter
- */
-bool txfilter_scriptpubkey_matches(const struct txfilter *filter, const u8 *scriptPubKey);
-
-/**
- * txfilter_add_scriptpubkey -- Add a serialized scriptpubkey to the filter
- */
-void txfilter_add_scriptpubkey(struct txfilter *filter, const u8 *script TAKES);
-
/**
* outpointfilter_new -- Create a new outpointfilter
*/
@@ -64,7 +31,4 @@ bool outpointfilter_matches(struct outpointfilter *of,
void outpointfilter_remove(struct outpointfilter *of,
const struct bitcoin_outpoint *outpoint);
-/* Useful for other callers */
-size_t scriptpubkey_hash(const u8 *out);
-
#endif /* LIGHTNING_WALLET_TXFILTER_H */
diff --git a/wallet/wallet.c b/wallet/wallet.c
index fdfa48ed..b1dea4c6 100644
--- a/wallet/wallet.c
+++ b/wallet/wallet.c
@@ -3354,10 +3354,6 @@ type_ok:
return;
}
- /* This is an unconfirmed change output, we should track it */
- if (utxo->utxotype != UTXO_P2SH_P2WPKH && !blockheight)
- txfilter_add_scriptpubkey(w->ld->owned_txfilter, txout->script);
-
outpointfilter_add(w->owned_outpoints, &utxo->outpoint);
wallet_annotate_txout(w, &utxo->outpoint, TX_WALLET_DEPOSIT, 0);
diff --git a/wallet/walletrpc.c b/wallet/walletrpc.c
index 3c022f96..68b0d80c 100644
--- a/wallet/walletrpc.c
+++ b/wallet/walletrpc.c
@@ -108,8 +108,6 @@ static struct command_result *param_newaddr(struct command *cmd,
bool WARN_UNUSED_RESULT newaddr_inner(struct command *cmd, struct pubkey *pubkey, enum addrtype addrtype)
{
s64 keyidx;
- u8 *b32script;
- u8 *p2tr_script;
bool use_bip86_base = (cmd->ld->bip86_base != NULL);
/* Get new index - wallet_get_newindex now handles both BIP32 and BIP86 */
@@ -124,17 +122,6 @@ bool WARN_UNUSED_RESULT newaddr_inner(struct command *cmd, struct pubkey *pubkey
/* Legacy wallet - use BIP32 derivation */
bip32_pubkey(cmd->ld, pubkey, keyidx);
}
-
- /* Generate scripts from pubkey (same logic for both wallet types) */
- b32script = scriptpubkey_p2wpkh(tmpctx, pubkey);
- p2tr_script = scriptpubkey_p2tr(tmpctx, pubkey);
-
- /* Add scripts to filter based on requested address type */
- if (addrtype & ADDR_BECH32)
- txfilter_add_scriptpubkey(cmd->ld->owned_txfilter, b32script);
- if (addrtype & ADDR_P2TR)
- txfilter_add_scriptpubkey(cmd->ld->owned_txfilter, p2tr_script);
-
return true;
}
Why this scored 12/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.