|
| 1 | +# Neutrino Fast Sync via Headers Import |
| 2 | + |
| 3 | +When LND is configured to use the neutrino (light client) backend, the initial |
| 4 | +sync requires downloading all block headers and compact filter headers from the |
| 5 | +network. On mainnet, this can take a significant amount of time as each header |
| 6 | +must be fetched individually via P2P. |
| 7 | + |
| 8 | +The headers import feature allows neutrino to bootstrap from a pre-built header |
| 9 | +file or HTTP endpoint, dramatically reducing initial sync time. After importing |
| 10 | +headers from the external source, neutrino falls back to normal P2P sync for any |
| 11 | +remaining headers not covered by the import. |
| 12 | + |
| 13 | +## How It Works |
| 14 | + |
| 15 | +1. On startup, if header import sources are configured, neutrino downloads (or |
| 16 | + reads from disk) the block headers and filter headers from the specified |
| 17 | + sources. |
| 18 | + |
| 19 | +2. Each import file begins with a 10-byte metadata header: |
| 20 | + - **Network magic** (4 bytes, little-endian): Identifies the target network |
| 21 | + (mainnet, testnet, etc.). |
| 22 | + - **Version** (1 byte): Format version (currently `0`). |
| 23 | + - **Header type** (1 byte): `0` for block headers, `1` for filter headers. |
| 24 | + - **Start height** (4 bytes, little-endian): The block height of the first |
| 25 | + header in the file. |
| 26 | + |
| 27 | +3. Following the metadata, the file contains consecutive raw headers: |
| 28 | + - **Block headers**: 80 bytes each (standard Bitcoin block header). |
| 29 | + - **Filter headers**: 32 bytes each (BIP 158 compact filter header hash). |
| 30 | + |
| 31 | +4. During import, neutrino validates all block headers by checking |
| 32 | + proof-of-work against the target network's consensus rules. Headers that |
| 33 | + fail validation are rejected. |
| 34 | + |
| 35 | +5. After the import completes, neutrino resumes normal P2P sync to fetch any |
| 36 | + headers beyond what the import file contained, ensuring the node is fully |
| 37 | + caught up to the chain tip. |
| 38 | + |
| 39 | +## Configuration |
| 40 | + |
| 41 | +Both `neutrino.blockheaderssource` and `neutrino.filterheaderssource` must be |
| 42 | +specified together. Setting only one will cause LND to fail at startup with a |
| 43 | +configuration error. |
| 44 | + |
| 45 | +Sources are auto-detected as either HTTP URLs or local file paths based on |
| 46 | +whether the value starts with `http`. |
| 47 | + |
| 48 | +### Using block-dn.org (Recommended for Production) |
| 49 | + |
| 50 | +The [block-dn.org](https://github.com/guggero/block-dn) service provides |
| 51 | +pre-built header files for multiple Bitcoin networks via HTTP. This is the |
| 52 | +recommended approach for production deployments. |
| 53 | + |
| 54 | +The `end_block` parameter in the URL must be divisible by 100,000 and should be |
| 55 | +the highest such value below the current chain tip. For example, if the chain |
| 56 | +tip is at block 878,432, use `800000` as the end block. |
| 57 | + |
| 58 | +#### Mainnet |
| 59 | + |
| 60 | +```ini |
| 61 | +[neutrino] |
| 62 | +neutrino.blockheaderssource=https://block-dn.org/headers/import/800000 |
| 63 | +neutrino.filterheaderssource=https://block-dn.org/filter-headers/import/800000 |
| 64 | +``` |
| 65 | + |
| 66 | +#### Testnet3 |
| 67 | + |
| 68 | +```ini |
| 69 | +[neutrino] |
| 70 | +neutrino.blockheaderssource=https://testnet3.block-dn.org/headers/import/2900000 |
| 71 | +neutrino.filterheaderssource=https://testnet3.block-dn.org/filter-headers/import/2900000 |
| 72 | +``` |
| 73 | + |
| 74 | +#### Testnet4 |
| 75 | + |
| 76 | +```ini |
| 77 | +[neutrino] |
| 78 | +neutrino.blockheaderssource=https://testnet4.block-dn.org/headers/import/200000 |
| 79 | +neutrino.filterheaderssource=https://testnet4.block-dn.org/filter-headers/import/200000 |
| 80 | +``` |
| 81 | + |
| 82 | +#### Signet |
| 83 | + |
| 84 | +```ini |
| 85 | +[neutrino] |
| 86 | +neutrino.blockheaderssource=https://signet.block-dn.org/headers/import/200000 |
| 87 | +neutrino.filterheaderssource=https://signet.block-dn.org/filter-headers/import/200000 |
| 88 | +``` |
| 89 | + |
| 90 | +You can check the current status and available block heights for each network at |
| 91 | +the corresponding status page: |
| 92 | +- Mainnet: https://block-dn.org/status |
| 93 | +- Testnet3: https://testnet3.block-dn.org/status |
| 94 | +- Testnet4: https://testnet4.block-dn.org/status |
| 95 | +- Signet: https://signet.block-dn.org/status |
| 96 | + |
| 97 | +### Using Local Files |
| 98 | + |
| 99 | +If you have pre-built header files on disk (for example, copied from an existing |
| 100 | +neutrino data directory), you can point LND at them directly: |
| 101 | + |
| 102 | +```ini |
| 103 | +[neutrino] |
| 104 | +neutrino.blockheaderssource=/path/to/block_headers.bin |
| 105 | +neutrino.filterheaderssource=/path/to/filter_headers.bin |
| 106 | +``` |
| 107 | + |
| 108 | +Local files must include the 10-byte import metadata prefix. Raw header files |
| 109 | +from neutrino's data directory (`block_headers.bin` and |
| 110 | +`reg_filter_headers.bin`) do not include this metadata by default. You can add |
| 111 | +it programmatically using neutrino's `chainimport.AddHeadersImportMetadata()` |
| 112 | +utility. |
| 113 | + |
| 114 | +## Security Considerations |
| 115 | + |
| 116 | +The headers import feature includes several safeguards: |
| 117 | + |
| 118 | +- **Proof-of-work validation**: All imported block headers are validated against |
| 119 | + the target network's consensus rules. An attacker cannot provide invalid |
| 120 | + headers without solving the proof-of-work puzzle. |
| 121 | + |
| 122 | +- **Network magic check**: The import file's network magic must match the |
| 123 | + configured Bitcoin network, preventing accidental cross-network imports. |
| 124 | + |
| 125 | +- **Filter header consistency**: Filter headers are validated against the block |
| 126 | + headers to ensure consistency. |
| 127 | + |
| 128 | +- **P2P fallback**: After import, neutrino continues syncing via P2P. The P2P |
| 129 | + network provides an independent check — if the imported headers diverge from |
| 130 | + the honest chain, the P2P sync will detect and correct this. |
| 131 | + |
| 132 | +For additional assurance, you can combine header import with neutrino's existing |
| 133 | +`assertfilterheader` option to checkpoint a known-good filter header hash at a |
| 134 | +specific height: |
| 135 | + |
| 136 | +```ini |
| 137 | +[neutrino] |
| 138 | +neutrino.assertfilterheader=800000:0123456789abcdef... |
| 139 | +``` |
| 140 | + |
| 141 | +## Troubleshooting |
| 142 | + |
| 143 | +### "both neutrino.blockheaderssource and neutrino.filterheaderssource must be specified together" |
| 144 | + |
| 145 | +Both options must be set together. If you only need block headers, you still |
| 146 | +must provide a filter headers source, and vice versa. |
| 147 | + |
| 148 | +### HTTP download failures |
| 149 | + |
| 150 | +If using HTTP sources, ensure the URL is reachable and the `end_block` value in |
| 151 | +the URL is divisible by 100,000. Check the service status page to confirm |
| 152 | +availability. |
| 153 | + |
| 154 | +### "failed to deserialize import metadata" |
| 155 | + |
| 156 | +The import file is missing or has a corrupt metadata prefix. Ensure the file |
| 157 | +includes the 10-byte metadata header. Files copied directly from neutrino's data |
| 158 | +directory need metadata added via `chainimport.AddHeadersImportMetadata()`. |
| 159 | + |
| 160 | +### "network magic mismatch" |
| 161 | + |
| 162 | +The header file was built for a different Bitcoin network than what LND is |
| 163 | +configured to use. Ensure the header file matches your `bitcoin.network` |
| 164 | +setting (mainnet, testnet, signet, etc.). |
0 commit comments