Research/Bitcoin

Bitcoin Light Clients: SPV, Neutrino, and Compact Block Filters

How light clients verify Bitcoin transactions without downloading the full blockchain. Covers SPV, bloom filters, Neutrino (BIP 157/158), compact block filters, security tradeoffs, and mobile wallet implementations.

bcTanjiFeb 28, 2026

Running a Bitcoin full node means downloading and validating every block since January 2009. That is hundreds of gigabytes of data and days of initial sync time. Most users, especially on mobile devices, cannot dedicate those resources. Light clients solve this by verifying transactions with far less data, but every shortcut introduces a security or privacy tradeoff. Understanding those tradeoffs is essential for anyone building or using a Bitcoin wallet.

SPV: The Original Light Client

Simplified Payment Verification was described in Section 8 of Satoshi Nakamoto's 2008 whitepaper. The core idea: a client can verify that a transaction is included in a block without downloading the entire block. It only needs the block headers and a Merkle proof connecting the transaction to the block's Merkle root.

How SPV verification works

Each Bitcoin block header is 80 bytes and contains the Merkle root of all transactions in that block. An SPV client downloads all block headers (forming a chain it can verify via proof-of-work) and then requests Merkle proofs for specific transactions it cares about. The verification steps are:

  1. Download all block headers and verify the chain of proof-of-work. This confirms the headers belong to the chain with the most cumulative work.
  2. Identify which block contains the transaction of interest.
  3. Request a Merkle branch (proof) from a full node. This is a sequence of hashes that, when combined with the transaction hash, reproduces the Merkle root in the block header.
  4. Verify the Merkle proof locally. If the computed root matches the header, the transaction is confirmed to be in that block.

The entire set of block headers from genesis to present is roughly 70 MB: a fraction of the full blockchain. Merkle proofs for individual transactions are typically a few hundred bytes. This makes SPV verification extremely lightweight in terms of bandwidth and storage.

What SPV does not verify: An SPV client confirms that a transaction was included in a valid-looking block (one with sufficient proof-of-work). It does not verify that the transaction itself is valid according to Bitcoin's consensus rules. It trusts that miners validated the transaction before including it. A full node, by contrast, independently checks every script, UTXO reference, and signature.

The Bloom Filter Era: BIP 37

The whitepaper described SPV conceptually, but it did not specify how a light client should ask a full node for relevant transactions without revealing which addresses it controls. BIP 37, introduced in 2012, proposed bloom filters as the mechanism.

How bloom filters work

A bloom filter is a probabilistic data structure that allows a client to express interest in a set of items (addresses, transaction IDs, output scripts) without listing them explicitly. The client constructs a bloom filter encoding the items it cares about and sends it to a connected full node. The full node then tests each transaction in each new block against the filter and sends back only the transactions that match.

The key property of bloom filters: false positives are possible (the node may send transactions the client does not actually care about), but false negatives are not (a matching transaction will never be missed). Clients can tune the false positive rate to balance privacy against bandwidth.

The privacy problem

In theory, the false positive rate provides plausible deniability: the serving node cannot be certain which transactions the client actually controls. In practice, researchers demonstrated that bloom filters leak substantial information about the client's wallet.

A 2014 paper by Arthur Gervais and others, "On the Privacy Provisions of Bloom Filters in Lightweight Bitcoin Clients", showed that a malicious full node can intersect multiple bloom filter updates from the same client to narrow down the exact set of addresses it owns. Even with high false positive rates, connecting to the same node repeatedly or updating the filter as new addresses are generated (common in HD wallets using a derivation path) erodes privacy quickly.

Additional problems with BIP 37:

  • Full nodes must scan every transaction in every block against the client's filter, creating a denial-of-service vector where lightweight clients impose significant CPU load on full nodes.
  • The serving node can selectively omit transactions that match the filter, and the client has no way to detect the omission.
  • Multiple connections to different nodes with the same bloom filter correlate the client across network identities.

These weaknesses led Bitcoin Core to disable bloom filter serving by default in version 0.19.0 (2019). The approach is now considered deprecated for new wallet implementations.

Neutrino: Compact Block Filters (BIP 157/158)

The successor to bloom filters flips the model. Instead of clients sending filters to servers, BIP 158 defines compact block filters that full nodes generate for each block. BIP 157 specifies the protocol for serving these filters to light clients. The implementation, commonly called Neutrino (after the reference implementation by Lightning Labs), addresses the privacy and DoS problems of BIP 37.

How compact block filters work

For each block, the full node constructs a Golomb-Rice coded set (GCS) containing all the scriptPubKeys (output scripts) and input outpoints in the block. This filter is compact: typically around 20 KB per block, regardless of how many transactions the block contains.

The light client downloads these filters (one per block) and tests them locally against its own set of addresses and scripts. If a filter matches, the client downloads the full block from any peer and extracts the relevant transactions. If no match, the client moves on.

The critical privacy improvement: the full node serving the filter never learns which addresses the client is interested in. The node sends the same filter to every client requesting it. The client performs the matching locally.

Filter header commitments

Compact block filters use a header chain similar to block headers. Each filter header commits to the previous filter header and the current filter hash, forming a chain that clients can verify for consistency. A client can request filter headers from multiple peers and check that they agree. If a peer serves a dishonest filter (one that omits data), the client detects the discrepancy by comparing headers across peers.

This addresses the omission attack that plagued BIP 37: a malicious peer cannot silently hide transactions because the filter contents are deterministic and verifiable.

Bandwidth tradeoff: Compact block filters shift the bandwidth cost. Instead of downloading only matching transactions (as with bloom filters), the client downloads every filter for every block. At roughly 20 KB per filter, syncing from genesis requires downloading the full set of filters in addition to block headers. The total filter data is substantial but still far less than the full blockchain. The privacy gain is significant: the serving node learns nothing about the client's wallet.

Syncing a Light Client: Step by Step

Here is what happens when a Neutrino-based light client starts up for the first time:

  1. Connect to one or more Bitcoin peers using the standard P2P protocol.
  2. Download all block headers from genesis to the chain tip. Verify the proof-of-work chain. This is roughly 70 MB.
  3. Request compact block filter headers from multiple peers. Compare them to detect inconsistencies.
  4. Download the compact block filters themselves. If the wallet has a known creation date or birthday height, skip filters before that point.
  5. For each filter, test the wallet's addresses and scripts against it locally. This is a fast, in-memory operation.
  6. If a filter matches, download the corresponding full block from a peer. Parse the block to extract relevant transactions and update the wallet's UTXO set.
  7. For ongoing operation, subscribe to new blocks and filters as they are mined. Test each new filter against the wallet's current address set.

For wallets with a known birthday (the block height at which the wallet was first created), the initial sync is much faster because filters for blocks before the birthday can be skipped entirely. This is the most common optimization in practice.

Resource Comparison

The resource requirements differ dramatically across client types. The following table compares a full validating node, a BIP 37 SPV client, and a Neutrino (BIP 157/158) light client.

ResourceFull NodeBIP 37 SPVNeutrino (BIP 157/158)
Initial disk storage600+ GB (pruned: ~10 GB)~70 MB (headers only)~70 MB headers + ~4 GB filters
Initial sync bandwidth600+ GB~70 MB + matched transactions~70 MB + all filters + matched blocks
Ongoing bandwidth per blockFull block (~2 MB avg)Matched transactions only~20 KB filter + matched blocks
CPU for validationFull consensus validationMerkle proof onlyFilter matching + Merkle proof
Privacy from serving nodeN/A (self-sovereign)Poor (bloom filter leakage)Strong (server learns nothing)
Transaction validity verificationFullNone (trusts miners)None (trusts miners)
Omission resistanceFullNoneStrong (verifiable filters)

Neutrino uses more bandwidth than BIP 37 SPV because it downloads every filter rather than only matching transactions. However, the privacy and security improvements are substantial enough that Neutrino has become the preferred approach for new wallet implementations.

Security Tradeoffs: What Light Clients Trust

All light clients share a fundamental limitation: they do not fully validate the blockchain. This means they implicitly trust certain properties that a full node verifies independently.

Miner honesty

Both SPV and Neutrino clients verify proof-of-work but not transaction validity. They assume that the chain with the most cumulative work contains only valid transactions. In a scenario where a majority of hash power produces invalid blocks (for example, blocks that create Bitcoin out of thin air or violate consensus rules), a light client would accept those blocks as valid. A full node would reject them.

This is not purely theoretical. During the 2017 SegWit activation debates, competing chain forks highlighted the importance of full validation: nodes running light clients would have followed whichever chain had more hash power, regardless of which rules it enforced.

Eclipse attacks

An eclipse attack occurs when an adversary controls all of a client's network connections. For a light client, this means the adversary can serve a fabricated chain of headers with valid proof-of-work (achievable by mining low-difficulty blocks on a private chain) and trick the client into accepting transactions that do not exist on the real Bitcoin network.

Neutrino mitigates this partially by connecting to multiple peers and cross-checking filter headers. If even one honest peer is connected, the client detects inconsistencies. But if all peers are adversarial, the client is vulnerable.

Confirmation depth

Light clients should wait for multiple confirmations before treating transactions as final. The risk of a double-spend via chain reorganization affects light clients more acutely because they cannot independently verify that the transactions in each block are valid. Deeper confirmations reduce this risk because producing a competing chain with more proof-of-work becomes exponentially harder.

Security PropertyFull NodeLight Client
Validates all consensus rulesYesNo: trusts miners
Detects invalid transactionsYesNo: only verifies inclusion
Resistant to eclipse attacksStronger (validates independently)Weaker (depends on honest peers)
Detects censorship/omissionYes (sees all transactions)Neutrino: yes (verifiable filters). BIP 37: no
Vulnerable to majority hash power attacksNo (rejects invalid blocks)Yes (follows most-work chain)
Suitable for high-value verificationYesAcceptable with sufficient confirmations

Mobile Wallet Implementations

Mobile wallets face the tightest resource constraints: limited storage, metered bandwidth, intermittent connectivity, and battery life concerns. These constraints have driven most mobile wallets toward one of three approaches.

Server-backed wallets

The most common approach for mobile wallets is connecting to a trusted server (often called an Electrum server or a block explorer API) that indexes the blockchain and responds to address queries directly. This is simple to implement but requires trusting the server for both correctness and privacy. The server learns every address the wallet queries.

Popular implementations include Electrum (which supports user-run servers) and wallets built on block explorer APIs. Users who run their own Electrum server regain privacy, but this effectively means running a full node at home: negating the resource savings of a light client.

Neutrino on mobile

Neutrino-based wallets run the filter matching logic on the device itself. The Neutrino library (written in Go) is embedded in wallets like those built on LND. On initial sync, the wallet downloads filter headers and filters from its birthday height, then fetches only the blocks that contain relevant transactions.

The tradeoff on mobile is sync time. A wallet that has been offline for weeks needs to catch up on all filters generated since its last sync. Optimizations include persisting filter state across app sessions and using checkpoint headers to skip known-good portions of the chain.

Hybrid approaches

Some wallets use a hybrid model: initial sync via a trusted server for speed, with Neutrino-style verification running in the background to confirm the server's responses. This gives users fast access to their balance while providing a secondary verification layer that detects dishonest server behavior over time.

Beyond Light Clients: Layer 2 Verification

Light client techniques are relevant beyond on-chain wallets. Layer 2 protocols build on the same principle: users should be able to verify their own state without trusting a third party or running heavyweight infrastructure.

On the Lightning Network, for example, mobile wallets typically rely on Lightning Service Providers for routing information and use route hints embedded in invoices to find payment paths. The wallet does not need to download the full channel graph: a form of light client behavior applied to payment routing rather than transaction verification.

Spark takes a different approach to lightweight verification. Because Spark transfers happen off-chain via statechain-style key rotations secured by FROST threshold signatures, a Spark wallet does not need to scan the blockchain for its transactions at all during normal operation. The wallet holds cryptographic proofs of ownership (its key shares and pre-signed exit transactions) that it can verify locally. Only when exiting to Layer 1 does the wallet interact with the Bitcoin blockchain directly, and even then it only needs to broadcast a single pre-signed transaction rather than validate the full chain.

This represents a meaningful shift in the light client problem. Rather than making blockchain verification lighter, protocols like Spark sidestep the need for continuous chain scanning entirely, while still preserving self-custody through unilateral exit capability.

Future Directions

Utreexo

Utreexo, proposed by Tadge Dryja, is a hash-based accumulator for the UTXO set. It allows nodes to validate transactions with a compact representation of the UTXO set (a few kilobytes) instead of the full set (several gigabytes). While Utreexo targets full validation rather than light client mode, it dramatically reduces the storage requirements for running a validating node, potentially making full validation feasible on mobile devices.

ZK proofs for chain validation

Research into zero-knowledge proofs for Bitcoin chain validation is ongoing. Projects like ZeroSync aim to generate succinct proofs that the entire Bitcoin chain is valid, allowing a client to verify chain validity by checking a single proof rather than replaying every transaction. This would give light clients full-node-level security without full-node-level resources, though the technology remains experimental.

Assumevalid and headers-first sync

Bitcoin Core already uses the assumevalid optimization, which skips signature verification for blocks below a hard-coded checkpoint. This is not a light client technique per se, but it demonstrates the same principle: reducing verification costs for historical data that has been extensively validated by the network. Future improvements to headers-first sync and parallel block validation continue to narrow the gap between full node and light client resource requirements.

Choosing the Right Approach

The right verification strategy depends on the use case. For a merchant processing high-value payments, running a full node is the only option that provides complete security. For a mobile wallet handling everyday transactions, Neutrino-style light clients offer a strong balance of privacy and security. For Layer 2 wallets operating on protocols like Spark or Lightning, the verification requirements are different entirely: the wallet verifies protocol-specific proofs rather than scanning the base chain.

The evolution from BIP 37 bloom filters to BIP 157/158 compact block filters represents a clear improvement in privacy and security. The next generation of verification techniques (Utreexo, ZK proofs, and protocol- level cryptographic proofs) will continue to lower the barrier to trustworthy Bitcoin verification across all device types.

This article is for educational purposes only. It does not constitute financial or investment advice. Bitcoin and Layer 2 protocols involve technical and financial risk. Always do your own research and understand the tradeoffs before using any protocol.