pull down to refresh

The Problem It SolvesThe Problem It Solves

Running a full Bitcoin node requires downloading and validating the entire blockchain (~600 GB and growing). For mobile apps and lightweight wallets, this is impractical. The older solution (BIP 37/SPV) had serious flaws:

  • Privacy leak: Clients send bloom filters matching their addresses to nodes
  • DoS vulnerability: Malicious clients can overload nodes with expensive queries
  • Trust required: Nodes can omit transactions without detection

Neutrino flips the model: instead of clients sending filters to servers, servers send filters to clients.


How Neutrino WorksHow Neutrino Works

The Three-Layer Sync ProcessThe Three-Layer Sync Process

┌─────────────────────────────────────────────────────────────┐
│ Step 1: Sync Block Headers (~40 MB)                         │
├─────────────────────────────────────────────────────────────┤
│ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐                         │
│ │Block │→│Block │→│Block │→│Block │→ ... (500k+ blocks)     │
│ │  0   │ │  1   │ │  2   │ │  3   │                         │
│ └──────┘ └──────┘ └──────┘ └──────┘                         │
│   80 B     80 B     80 B     80 B                           │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│ Step 2: Sync Filter Headers (~20 MB)                        │
├─────────────────────────────────────────────────────────────┤
│ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐                         │
│ │Filter│→│Filter│→│Filter│→│Filter│→ ...                    │
│ │Hdr 0 │ │Hdr 1 │ │Hdr 2 │ │Hdr 3 │                         │
│ └──────┘ └──────┘ └──────┘ └──────┘                         │
│   32 B     32 B     32 B     32 B                           │
│                                                             │
│ Checkpoints every 1,000 blocks for verification             │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│ Step 3: Download Filters ON DEMAND (lazy loading)           │
├─────────────────────────────────────────────────────────────┤
│ Only fetch filters for blocks you need to check             │
│ Avg ~15 KB per filter (vs ~1-4 MB per block)                │
│ 250x compression!                                           │
└─────────────────────────────────────────────────────────────┘

How Matching WorksHow Matching Works

Your Wallet                          Neutrino Client                    Full Node
─────────────                        ───────────────                    ─────────

Addresses:                           New block arrives
- bc1q...abc                              ↓
- bc1q...xyz                         Download filter (~15 KB)
                                           ↓
                                     Check locally:                     
                                     Does filter match                  
                                     ANY of my addresses?               
                                           ↓                            
                                      ┌────┴────┐                      
                                      │   NO    │   Discard filter     
                                      │  MATCH  │   Move to next block 
                                      └─────────┘                      
                                           ↓                            
                                      ┌────┴────┐                      
                                      │   YES   │   Download full      → Request block
                                      │  MATCH! │   block (~1-4 MB)      
                                      └─────────┘         ↓               ↓
                                                   Verify & extract   ← Send block
                                                   your transactions

Key Privacy Win: Your addresses never leave your device. The node only knows you're interested in some block, not which addresses you're watching.


What's Inside a Filter?What's Inside a Filter?

Filters contain compressed representations of:

  1. All scriptPubKeys (output scripts) in the block
  2. All previous output scripts being spent (inputs)

Excluding:

  • OP_RETURN outputs (to allow future commitments)
  • Coinbase inputs (no previous output)

Filter Technology: Golomb-Coded Sets (GCS)Filter Technology: Golomb-Coded Sets (GCS)

Raw block data → Hash items → Sort → Calculate deltas → Compress
  
  [addr1, addr2, ...] → [hash1, hash2, ...] → [h1, h2, h3...] 
                                                     ↓
                                              [delta1, delta2, ...]
                                                     ↓
                                         Golomb-Rice encode
                                                     ↓
                                            ~15 KB filter
  • Parameters: M=784,931, P=19
  • False positive rate: ~1/784,931 per item
  • Result: Probabilistic, but extremely efficient

Available OperationsAvailable Operations

1. Rescan1. Rescan

Scan the blockchain for relevant transactions.

rescan := neutrino.Rescan{
    StartBlock:    startHeight,     // Where to begin
    EndBlock:      nil,              // nil = scan to tip
    WatchAddrs:    []btcutil.Address{addr1, addr2},
    WatchOutPoints: []wire.OutPoint{utxo1},
    WatchTxIDs:    []chainhash.Hash{txid1},
}

How it works:

  1. Downloads filters for each block in range
  2. Checks if filter matches your watch list
  3. If match: downloads full block and extracts transactions
  4. Notifies you of matches via callback

Use case: Wallet recovery, checking balance for known addresses

2. GetUtxo2. GetUtxo

Check if a specific UTXO exists and is unspent.

report := chainService.GetUtxo(
    outpoint,      // The UTXO to check
    startBlock,    // CRITICAL: where to start searching
)

How it works:

  1. Scans backwards from chain tip
  2. Downloads filters to check if UTXO appears
  3. Stops when found (spent or created)
  4. Returns either:
    • TxOut (if unspent) with scriptPubKey
    • Spending transaction details (if spent)

⚠️ Important: Always specify startBlock! Otherwise, if the UTXO doesn't exist, it will scan all the way back to block 1.

3. GetBlockFromNetwork3. GetBlockFromNetwork

Download a specific block by hash.

block := chainService.GetBlockFromNetwork(blockHash)

Use case: After a filter matches, fetch the full block to extract transactions.


Resource RequirementsResource Requirements

StorageStorage

ComponentSizeRequiredNotes
Block headers~40 MB✅ Always80 bytes × ~500k blocks
Filter headers~20 MB✅ Always32 bytes × ~500k blocks
Filters~7.5 GB total⚠️ OptionalCan discard after checking
BlocksVariable❌ NeverDownloaded on-demand, not saved

Minimal config: ~60 MB (headers only)
With filter cache: ~100-400 MB typical
If storing all filters: ~7.5 GB

Default in-memory caches:

  • Filter cache: 30 MB (~1,450-2,300 filters)
  • Block cache: 40 MB (temporary)

BandwidthBandwidth

Initial sync:

  • Block headers: ~40 MB
  • Filter headers: ~20 MB
  • Total: ~60 MB

Ongoing:

  • New block header: 80 bytes every ~10 min
  • New filter header: 32 bytes every ~10 min
  • New filter: ~15 KB every ~10 min (if checking)
  • Full block: ~1-4 MB (only if filter matches)

Time from Cold StartTime from Cold Start

OperationTime
Initial header sync~5-10 minutes
Initial filter header sync~5-10 minutes
Total cold start~15-20 minutes
Rescan (recent 1000 blocks)~5-20 minutes
Check single UTXOSeconds to minutes

Times vary based on network conditions and peer availability


Key Limitations & ConsiderationsKey Limitations & Considerations

❌ You CANNOT query by address alone❌ You CANNOT query by address alone

You need the scriptPubKey (the actual locking script):

// ❌ Won't work
"bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh"

// ✅ Need this
"0014 1111111254fb1a3bc28b3a3f4c5b1f5e1e1c5f6e" 
(OP_0 + 20-byte hash for P2WPKH)

Why? Filters contain scripts, not addresses. Different address types (P2PKH, P2WPKH, P2SH, etc.) have different scripts.

❌ No instant mempool monitoring❌ No instant mempool monitoring

Neutrino only notifies on confirmed transactions. For 0-conf detection, you need:

  • Direct P2P connection monitoring (not standard Neutrino)
  • Or connect to additional services

❌ No transaction history API❌ No transaction history API

Unlike block explorers, Neutrino doesn't give you:

  • "All transactions for address X"
  • Transaction history
  • Current balance

You must:

  1. Know what addresses/UTXOs to watch
  2. Scan explicitly with Rescan or GetUtxo
  3. Build your own history database

⚠️ Privacy is good, not perfect⚠️ Privacy is good, not perfect

What's hidden:

  • Your exact addresses
  • Which transactions you care about
  • Your wallet's full contents

What's leaked:

  • You're interested in some specific blocks (when downloading)
  • Timing patterns (when you check)
  • The fact you're running a light client

To maximize privacy:

  • Download blocks from random peers
  • Use Tor
  • Don't reuse addresses
  • Consider downloading extra "dummy" blocks

⚠️ Trust model: Need at least ONE honest peer⚠️ Trust model: Need at least ONE honest peer

If ALL your connected peers are malicious and colluding:

  • They could hide transactions from you
  • They could provide invalid filters

Mitigations:

  • Connect to multiple peers (default: 8 outbound)
  • Filter headers have cryptographic commitments
  • Clients detect conflicts and ban lying peers
  • Compare results across peers

Practical ExamplesPractical Examples

Use Case 1: Light Wallet RecoveryUse Case 1: Light Wallet Recovery

Scenario: You have 3 addresses, need to find all transactions

1. Start Neutrino, wait for sync (~20 min)
2. Rescan from wallet creation date
3. For each block:
   - Download filter (~15 KB)
   - Check if any address matches
   - If yes: download block (~2 MB)
   - Extract your transactions
4. Build transaction history locally

Cost: ~60 MB initial + variable for matching blocks

Use Case 2: Lightning Network PaymentUse Case 2: Lightning Network Payment

Scenario: Need to watch for channel force-close transaction

1. Already synced Neutrino client
2. GetUtxo for channel funding output
3. Checks backwards from tip:
   - Is it still unspent? → Channel is open
   - Was it spent? → Get spending tx details
4. React to channel state

Cost: Minimal, just filter downloads until UTXO found

Use Case 3: Mobile WalletUse Case 3: Mobile Wallet

Scenario: iOS/Android app with minimal battery/data use

1. Sync headers at app launch (60 MB one-time)
2. Check filters for new blocks when app opens
3. Only download full blocks with your transactions
4. Discard filters after checking (minimal storage)

Cost:

  • Initial: 60 MB
  • Ongoing: ~15 KB per 10 min + occasional full blocks

Comparison with AlternativesComparison with Alternatives

FeatureFull NodeBIP 37 SPVNeutrinoBlock Explorer API
Storage600+ GB< 1 GB60 MB - 8 GB0 GB
Initial syncDaysMinutes15-20 minInstant
Privacy⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐ (none)
TrustNoneHighLowComplete
Mempool✅ Yes✅ Yes❌ No✅ Yes
Address lookup✅ Any❌ Must watch❌ Must watch✅ Any
DoS resistanceN/A❌ Vulnerable✅ ResistantN/A

Quick ReferenceQuick Reference

What you need to provide:What you need to provide:

  • For Rescan: Addresses (as scriptPubKeys), UTXOs, or TxIDs
  • For GetUtxo: Outpoint (TxID + index) + recommended start block
  • Filter key: First 16 bytes of block hash (automatic)

What you get back:What you get back:

  • Rescan: Notifications of matching transactions in blocks
  • GetUtxo: SpendReport with either:
    • TxOut (if unspent)
    • Spending transaction details (if spent)
    • Nothing (if never existed in scanned range)

When to use Neutrino:When to use Neutrino:

✅ Mobile/embedded devices
✅ Privacy-conscious wallets
✅ Lightning Network clients
✅ Occasional blockchain queries
❌ High-frequency trading
❌ Real-time mempool monitoring
❌ Arbitrary address lookups


ConclusionConclusion

Neutrino represents a significant improvement over BIP 37 SPV:

Advantages:

  • Much better privacy (addresses never leave device)
  • DoS resistant (filters computed once)
  • Efficient (~60 MB for full sync)
  • Verifiable (cryptographic commitments)

Trade-offs:

  • No instant mempool access
  • Must know what to watch for
  • 15-20 minute initial sync
  • Requires scriptPubKey, not just address

For mobile Bitcoin and Lightning wallets that prioritize privacy and efficiency over real-time mempool monitoring, Neutrino is currently the best available light client protocol.