-
Notifications
You must be signed in to change notification settings - Fork 2.2k
neutrino: add fast initial sync via header import #10552
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Roasbeef
wants to merge
6
commits into
lightningnetwork:master
Choose a base branch
from
Roasbeef:neutrino-fast-sync
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+491
−104
Open
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
7f5f256
multi: update neutrino dep to include chainimport and header state fix
Roasbeef ed6991f
lncfg: add neutrino header import source config fields
Roasbeef 2c38427
config_builder: wire neutrino header import config
Roasbeef 49b14a6
sample-lnd.conf: document neutrino header import options
Roasbeef e33d93b
itest: add neutrino headers import integration test
Roasbeef 4c6e587
docs: add neutrino headers import documentation
Roasbeef File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,164 @@ | ||
| # Neutrino Fast Sync via Headers Import | ||
|
|
||
| When LND is configured to use the neutrino (light client) backend, the initial | ||
| sync requires downloading all block headers and compact filter headers from the | ||
| network. On mainnet, this can take a significant amount of time as each header | ||
| must be fetched individually via P2P. | ||
|
|
||
| The headers import feature allows neutrino to bootstrap from a pre-built header | ||
| file or HTTP endpoint, dramatically reducing initial sync time. After importing | ||
| headers from the external source, neutrino falls back to normal P2P sync for any | ||
| remaining headers not covered by the import. | ||
|
|
||
| ## How It Works | ||
|
|
||
| 1. On startup, if header import sources are configured, neutrino downloads (or | ||
| reads from disk) the block headers and filter headers from the specified | ||
| sources. | ||
|
|
||
| 2. Each import file begins with a 10-byte metadata header: | ||
| - **Network magic** (4 bytes, little-endian): Identifies the target network | ||
| (mainnet, testnet, etc.). | ||
| - **Version** (1 byte): Format version (currently `0`). | ||
| - **Header type** (1 byte): `0` for block headers, `1` for filter headers. | ||
| - **Start height** (4 bytes, little-endian): The block height of the first | ||
| header in the file. | ||
|
|
||
| 3. Following the metadata, the file contains consecutive raw headers: | ||
| - **Block headers**: 80 bytes each (standard Bitcoin block header). | ||
| - **Filter headers**: 32 bytes each (BIP 158 compact filter header hash). | ||
|
|
||
| 4. During import, neutrino validates all block headers by checking | ||
| proof-of-work against the target network's consensus rules. Headers that | ||
| fail validation are rejected. | ||
|
|
||
| 5. After the import completes, neutrino resumes normal P2P sync to fetch any | ||
| headers beyond what the import file contained, ensuring the node is fully | ||
| caught up to the chain tip. | ||
|
|
||
| ## Configuration | ||
|
|
||
| Both `neutrino.blockheaderssource` and `neutrino.filterheaderssource` must be | ||
| specified together. Setting only one will cause LND to fail at startup with a | ||
| configuration error. | ||
|
|
||
| Sources are auto-detected as either HTTP URLs or local file paths based on | ||
| whether the value starts with `http`. | ||
|
|
||
| ### Using block-dn.org (Recommended for Production) | ||
|
|
||
| The [block-dn.org](https://github.com/guggero/block-dn) service provides | ||
| pre-built header files for multiple Bitcoin networks via HTTP. This is the | ||
| recommended approach for production deployments. | ||
|
|
||
| The `end_block` parameter in the URL must be divisible by 100,000 and should be | ||
| the highest such value below the current chain tip. For example, if the chain | ||
| tip is at block 878,432, use `800000` as the end block. | ||
|
|
||
| #### Mainnet | ||
|
|
||
| ```ini | ||
| [neutrino] | ||
| neutrino.blockheaderssource=https://block-dn.org/headers/import/800000 | ||
| neutrino.filterheaderssource=https://block-dn.org/filter-headers/import/800000 | ||
| ``` | ||
|
|
||
| #### Testnet3 | ||
|
|
||
| ```ini | ||
| [neutrino] | ||
| neutrino.blockheaderssource=https://testnet3.block-dn.org/headers/import/2900000 | ||
| neutrino.filterheaderssource=https://testnet3.block-dn.org/filter-headers/import/2900000 | ||
| ``` | ||
|
|
||
| #### Testnet4 | ||
|
|
||
| ```ini | ||
| [neutrino] | ||
| neutrino.blockheaderssource=https://testnet4.block-dn.org/headers/import/200000 | ||
| neutrino.filterheaderssource=https://testnet4.block-dn.org/filter-headers/import/200000 | ||
| ``` | ||
|
|
||
| #### Signet | ||
|
|
||
| ```ini | ||
| [neutrino] | ||
| neutrino.blockheaderssource=https://signet.block-dn.org/headers/import/200000 | ||
| neutrino.filterheaderssource=https://signet.block-dn.org/filter-headers/import/200000 | ||
| ``` | ||
|
|
||
| You can check the current status and available block heights for each network at | ||
| the corresponding status page: | ||
| - Mainnet: https://block-dn.org/status | ||
| - Testnet3: https://testnet3.block-dn.org/status | ||
| - Testnet4: https://testnet4.block-dn.org/status | ||
| - Signet: https://signet.block-dn.org/status | ||
|
|
||
| ### Using Local Files | ||
|
|
||
| If you have pre-built header files on disk (for example, copied from an existing | ||
| neutrino data directory), you can point LND at them directly: | ||
|
|
||
| ```ini | ||
| [neutrino] | ||
| neutrino.blockheaderssource=/path/to/block_headers.bin | ||
| neutrino.filterheaderssource=/path/to/filter_headers.bin | ||
| ``` | ||
|
|
||
| Local files must include the 10-byte import metadata prefix. Raw header files | ||
| from neutrino's data directory (`block_headers.bin` and | ||
| `reg_filter_headers.bin`) do not include this metadata by default. You can add | ||
| it programmatically using neutrino's `chainimport.AddHeadersImportMetadata()` | ||
| utility. | ||
|
|
||
| ## Security Considerations | ||
|
|
||
| The headers import feature includes several safeguards: | ||
|
|
||
| - **Proof-of-work validation**: All imported block headers are validated against | ||
| the target network's consensus rules. An attacker cannot provide invalid | ||
| headers without solving the proof-of-work puzzle. | ||
|
|
||
| - **Network magic check**: The import file's network magic must match the | ||
| configured Bitcoin network, preventing accidental cross-network imports. | ||
|
|
||
| - **Filter header consistency**: Filter headers are validated against the block | ||
| headers to ensure consistency. | ||
|
|
||
| - **P2P fallback**: After import, neutrino continues syncing via P2P. The P2P | ||
| network provides an independent check — if the imported headers diverge from | ||
| the honest chain, the P2P sync will detect and correct this. | ||
|
|
||
| For additional assurance, you can combine header import with neutrino's existing | ||
| `assertfilterheader` option to checkpoint a known-good filter header hash at a | ||
| specific height: | ||
|
|
||
| ```ini | ||
| [neutrino] | ||
| neutrino.assertfilterheader=800000:0123456789abcdef... | ||
| ``` | ||
|
|
||
| ## Troubleshooting | ||
|
|
||
| ### "both neutrino.blockheaderssource and neutrino.filterheaderssource must be specified together" | ||
|
|
||
| Both options must be set together. If you only need block headers, you still | ||
| must provide a filter headers source, and vice versa. | ||
|
|
||
| ### HTTP download failures | ||
|
|
||
| If using HTTP sources, ensure the URL is reachable and the `end_block` value in | ||
| the URL is divisible by 100,000. Check the service status page to confirm | ||
| availability. | ||
|
|
||
| ### "failed to deserialize import metadata" | ||
|
|
||
| The import file is missing or has a corrupt metadata prefix. Ensure the file | ||
| includes the 10-byte metadata header. Files copied directly from neutrino's data | ||
| directory need metadata added via `chainimport.AddHeadersImportMetadata()`. | ||
|
|
||
| ### "network magic mismatch" | ||
|
|
||
| The header file was built for a different Bitcoin network than what LND is | ||
| configured to use. Ensure the header file matches your `bitcoin.network` | ||
| setting (mainnet, testnet, signet, etc.). |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using
context.TODO()is a pragmatic way to adapt to the updated dependency interface without a major refactoring. However, it introduces some technical debt. For improved robustness, especially during shutdown, it would be beneficial to propagate a proper context with cancellation capabilities from the callers. I recommend creating a follow-up issue to address this throughout the codebase wherecontext.TODO()has been introduced.