Allow wallet imports from arbitrary heights, forcing full scans - #884

Merged
tnull merged 3 commits into
lightningdevkit:mainfrom
tnull:2026-04-abort-on-first-startup-tip-fetch-failure
Jun 30, 2026
Merged

Allow wallet imports from arbitrary heights, forcing full scans#884
tnull merged 3 commits into
lightningdevkit:mainfrom
tnull:2026-04-abort-on-first-startup-tip-fetch-failure

Conversation

@tnull

@tnulltnull commented Apr 21, 2026

Copy link
Copy Markdown
Collaborator

Fixes#818.

Previously, we would always start syncing from the latest block height or were able to force a full-resync from genesis via recovery_mode = true. Here we add more fine-grained control and allow an initial wallet import from a known birthday height for bitcoind RPC/REST chain sources. Relatedly, we also fix a minor bug where, if we failed to retrieve the tip hash on first startup, we'd always resync from genesis, which can be very costly.

Moreover, we also add a flag that allows the user to force a full scan for Esplora/Electrum wallets.

@tnulltnull added this to the 0.8 milestone Apr 21, 2026
@ldk-reviews-bot

ldk-reviews-bot commented Apr 21, 2026

Copy link
Copy Markdown

I've assigned @wpaulino as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@tnull
tnull marked this pull request as draft April 21, 2026 13:06
@tnull
tnullforce-pushed the 2026-04-abort-on-first-startup-tip-fetch-failure branch from c692081 to 608108aCompareApril 21, 2026 13:07
@tnull
tnullforce-pushed the 2026-04-abort-on-first-startup-tip-fetch-failure branch from 608108a to 494a448CompareJune 26, 2026 12:19
@tnulltnull changed the title 2026 04 abort on first startup tip fetch failureAllow wallet imports from arbitrary heights, forcing full scansJun 26, 2026
@tnull
tnull marked this pull request as ready for review June 26, 2026 12:25
@tnull

Copy link
Copy Markdown
CollaboratorAuthor

Rebased & refactored a bit, ready for review.

tnull added 3 commits June 29, 2026 16:18
When a fresh node's bitcoind RPC/REST chain source fails to return the
current chain tip, we previously silently fell back to the genesis
block as the wallet birthday. The next successful startup would then
force a full-history rescan of the whole chain. Instead, return a new
BuildError::ChainTipFetchFailed on the first build so the
misconfiguration surfaces immediately and no stale fresh state is
persisted.
Restarts with a previously-persisted wallet are unaffected: a
transient chain source outage on an existing node still allows startup
to proceed. Esplora/Electrum backends currently never expose a tip at
build time so the guard only fires for bitcoind sources; the latent
wallet-birthday-at-genesis issue on those backends is left for a
follow-up.
Co-Authored-By: HAL 9000
Allow Bitcoin Core RPC and REST chain-source configuration to specify
the wallet birthday height used when creating a fresh wallet. This lets
restored wallets rescan from a known height, including genesis, without
overloading a global recovery toggle.
Reject requested heights above the current chain tip with an explicit
build error before wallet state is created. Existing wallets are not
rewound by this option because a safe rewind must invalidate persisted
wallet and LDK state before replaying blocks.
Co-Authored-By: HAL 9000
Let Esplora and Electrum sync configs request BDK full scans
until one succeeds. This keeps recovery scans retryable after
transient sync failures while preserving normal incremental syncs
once recovery has completed.
Co-Authored-By: HAL 9000
@tnull
tnullforce-pushed the 2026-04-abort-on-first-startup-tip-fetch-failure branch from 494a448 to 101c827CompareJune 29, 2026 14:19
@tnull

Copy link
Copy Markdown
CollaboratorAuthor

Rebased to resolve minor conflict.

@wpaulinowpaulino left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, just one comment

Comment threadsrc/chain/electrum.rs
config: Arc<Config>,
logger: Arc<Logger>,
node_metrics: Arc<PersistedNodeMetrics>,
force_wallet_full_scan: AtomicBool,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we expose a block height as well to let the user optionally decide whether they want to start from a specific point?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean the current block height the node is synced to? That's already exposed via NodeStatus::current_best_block (cf. https://docs.rs/ldk-node/latest/ldk_node/struct.NodeStatus.html).

@tnull
tnull merged commit f2e44fd into lightningdevkit:mainJun 30, 2026
23 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Wallet birthday height for seed recovery on pruned nodes

3 participants

@tnull@ldk-reviews-bot@wpaulino
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Allow wallet imports from arbitrary heights, forcing full scans - #884

Merged
tnull merged 3 commits into
lightningdevkit:mainfrom
tnull:2026-04-abort-on-first-startup-tip-fetch-failure
Jun 30, 2026
Merged

Allow wallet imports from arbitrary heights, forcing full scans#884
tnull merged 3 commits into
lightningdevkit:mainfrom
tnull:2026-04-abort-on-first-startup-tip-fetch-failure

Conversation

@tnull

@tnulltnull commented Apr 21, 2026

Copy link
Copy Markdown
Collaborator

Fixes#818.

Previously, we would always start syncing from the latest block height or were able to force a full-resync from genesis via recovery_mode = true. Here we add more fine-grained control and allow an initial wallet import from a known birthday height for bitcoind RPC/REST chain sources. Relatedly, we also fix a minor bug where, if we failed to retrieve the tip hash on first startup, we'd always resync from genesis, which can be very costly.

Moreover, we also add a flag that allows the user to force a full scan for Esplora/Electrum wallets.

@tnulltnull added this to the 0.8 milestone Apr 21, 2026
@ldk-reviews-bot

ldk-reviews-bot commented Apr 21, 2026

Copy link
Copy Markdown

I've assigned @wpaulino as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@tnull
tnull marked this pull request as draft April 21, 2026 13:06
@tnull
tnullforce-pushed the 2026-04-abort-on-first-startup-tip-fetch-failure branch from c692081 to 608108aCompareApril 21, 2026 13:07
@tnull
tnullforce-pushed the 2026-04-abort-on-first-startup-tip-fetch-failure branch from 608108a to 494a448CompareJune 26, 2026 12:19
@tnulltnull changed the title 2026 04 abort on first startup tip fetch failureAllow wallet imports from arbitrary heights, forcing full scansJun 26, 2026
@tnull
tnull marked this pull request as ready for review June 26, 2026 12:25
@tnull

Copy link
Copy Markdown
CollaboratorAuthor

Rebased & refactored a bit, ready for review.

tnull added 3 commits June 29, 2026 16:18
When a fresh node's bitcoind RPC/REST chain source fails to return the
current chain tip, we previously silently fell back to the genesis
block as the wallet birthday. The next successful startup would then
force a full-history rescan of the whole chain. Instead, return a new
BuildError::ChainTipFetchFailed on the first build so the
misconfiguration surfaces immediately and no stale fresh state is
persisted.
Restarts with a previously-persisted wallet are unaffected: a
transient chain source outage on an existing node still allows startup
to proceed. Esplora/Electrum backends currently never expose a tip at
build time so the guard only fires for bitcoind sources; the latent
wallet-birthday-at-genesis issue on those backends is left for a
follow-up.
Co-Authored-By: HAL 9000
Allow Bitcoin Core RPC and REST chain-source configuration to specify
the wallet birthday height used when creating a fresh wallet. This lets
restored wallets rescan from a known height, including genesis, without
overloading a global recovery toggle.
Reject requested heights above the current chain tip with an explicit
build error before wallet state is created. Existing wallets are not
rewound by this option because a safe rewind must invalidate persisted
wallet and LDK state before replaying blocks.
Co-Authored-By: HAL 9000
Let Esplora and Electrum sync configs request BDK full scans
until one succeeds. This keeps recovery scans retryable after
transient sync failures while preserving normal incremental syncs
once recovery has completed.
Co-Authored-By: HAL 9000
@tnull
tnullforce-pushed the 2026-04-abort-on-first-startup-tip-fetch-failure branch from 494a448 to 101c827CompareJune 29, 2026 14:19
@tnull

Copy link
Copy Markdown
CollaboratorAuthor

Rebased to resolve minor conflict.

@wpaulinowpaulino left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, just one comment

Comment threadsrc/chain/electrum.rs
config: Arc<Config>,
logger: Arc<Logger>,
node_metrics: Arc<PersistedNodeMetrics>,
force_wallet_full_scan: AtomicBool,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we expose a block height as well to let the user optionally decide whether they want to start from a specific point?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean the current block height the node is synced to? That's already exposed via NodeStatus::current_best_block (cf. https://docs.rs/ldk-node/latest/ldk_node/struct.NodeStatus.html).

@tnull
tnull merged commit f2e44fd into lightningdevkit:mainJun 30, 2026
23 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Wallet birthday height for seed recovery on pruned nodes

3 participants

@tnull@ldk-reviews-bot@wpaulino
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Allow wallet imports from arbitrary heights, forcing full scans - #884

Merged
tnull merged 3 commits into
lightningdevkit:mainfrom
tnull:2026-04-abort-on-first-startup-tip-fetch-failure
Jun 30, 2026
Merged

Allow wallet imports from arbitrary heights, forcing full scans#884
tnull merged 3 commits into
lightningdevkit:mainfrom
tnull:2026-04-abort-on-first-startup-tip-fetch-failure

Conversation

@tnull

@tnulltnull commented Apr 21, 2026

Copy link
Copy Markdown
Collaborator

Fixes#818.

Previously, we would always start syncing from the latest block height or were able to force a full-resync from genesis via recovery_mode = true. Here we add more fine-grained control and allow an initial wallet import from a known birthday height for bitcoind RPC/REST chain sources. Relatedly, we also fix a minor bug where, if we failed to retrieve the tip hash on first startup, we'd always resync from genesis, which can be very costly.

Moreover, we also add a flag that allows the user to force a full scan for Esplora/Electrum wallets.

@tnulltnull added this to the 0.8 milestone Apr 21, 2026
@ldk-reviews-bot

ldk-reviews-bot commented Apr 21, 2026

Copy link
Copy Markdown

I've assigned @wpaulino as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@tnull
tnull marked this pull request as draft April 21, 2026 13:06
@tnull
tnullforce-pushed the 2026-04-abort-on-first-startup-tip-fetch-failure branch from c692081 to 608108aCompareApril 21, 2026 13:07
@tnull
tnullforce-pushed the 2026-04-abort-on-first-startup-tip-fetch-failure branch from 608108a to 494a448CompareJune 26, 2026 12:19
@tnulltnull changed the title 2026 04 abort on first startup tip fetch failureAllow wallet imports from arbitrary heights, forcing full scansJun 26, 2026
@tnull
tnull marked this pull request as ready for review June 26, 2026 12:25
@tnull

Copy link
Copy Markdown
CollaboratorAuthor

Rebased & refactored a bit, ready for review.

tnull added 3 commits June 29, 2026 16:18
When a fresh node's bitcoind RPC/REST chain source fails to return the
current chain tip, we previously silently fell back to the genesis
block as the wallet birthday. The next successful startup would then
force a full-history rescan of the whole chain. Instead, return a new
BuildError::ChainTipFetchFailed on the first build so the
misconfiguration surfaces immediately and no stale fresh state is
persisted.
Restarts with a previously-persisted wallet are unaffected: a
transient chain source outage on an existing node still allows startup
to proceed. Esplora/Electrum backends currently never expose a tip at
build time so the guard only fires for bitcoind sources; the latent
wallet-birthday-at-genesis issue on those backends is left for a
follow-up.
Co-Authored-By: HAL 9000
Allow Bitcoin Core RPC and REST chain-source configuration to specify
the wallet birthday height used when creating a fresh wallet. This lets
restored wallets rescan from a known height, including genesis, without
overloading a global recovery toggle.
Reject requested heights above the current chain tip with an explicit
build error before wallet state is created. Existing wallets are not
rewound by this option because a safe rewind must invalidate persisted
wallet and LDK state before replaying blocks.
Co-Authored-By: HAL 9000
Let Esplora and Electrum sync configs request BDK full scans
until one succeeds. This keeps recovery scans retryable after
transient sync failures while preserving normal incremental syncs
once recovery has completed.
Co-Authored-By: HAL 9000
@tnull
tnullforce-pushed the 2026-04-abort-on-first-startup-tip-fetch-failure branch from 494a448 to 101c827CompareJune 29, 2026 14:19
@tnull

Copy link
Copy Markdown
CollaboratorAuthor

Rebased to resolve minor conflict.

@wpaulinowpaulino left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, just one comment

Comment threadsrc/chain/electrum.rs
config: Arc<Config>,
logger: Arc<Logger>,
node_metrics: Arc<PersistedNodeMetrics>,
force_wallet_full_scan: AtomicBool,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we expose a block height as well to let the user optionally decide whether they want to start from a specific point?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean the current block height the node is synced to? That's already exposed via NodeStatus::current_best_block (cf. https://docs.rs/ldk-node/latest/ldk_node/struct.NodeStatus.html).

@tnull
tnull merged commit f2e44fd into lightningdevkit:mainJun 30, 2026
23 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Wallet birthday height for seed recovery on pruned nodes

3 participants

@tnull@ldk-reviews-bot@wpaulino
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Allow wallet imports from arbitrary heights, forcing full scans - #884

Merged
tnull merged 3 commits into
lightningdevkit:mainfrom
tnull:2026-04-abort-on-first-startup-tip-fetch-failure
Jun 30, 2026
Merged

Allow wallet imports from arbitrary heights, forcing full scans#884
tnull merged 3 commits into
lightningdevkit:mainfrom
tnull:2026-04-abort-on-first-startup-tip-fetch-failure

Conversation

@tnull

@tnulltnull commented Apr 21, 2026

Copy link
Copy Markdown
Collaborator

Fixes#818.

Previously, we would always start syncing from the latest block height or were able to force a full-resync from genesis via recovery_mode = true. Here we add more fine-grained control and allow an initial wallet import from a known birthday height for bitcoind RPC/REST chain sources. Relatedly, we also fix a minor bug where, if we failed to retrieve the tip hash on first startup, we'd always resync from genesis, which can be very costly.

Moreover, we also add a flag that allows the user to force a full scan for Esplora/Electrum wallets.

@tnulltnull added this to the 0.8 milestone Apr 21, 2026
@ldk-reviews-bot

ldk-reviews-bot commented Apr 21, 2026

Copy link
Copy Markdown

I've assigned @wpaulino as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@tnull
tnull marked this pull request as draft April 21, 2026 13:06
@tnull
tnullforce-pushed the 2026-04-abort-on-first-startup-tip-fetch-failure branch from c692081 to 608108aCompareApril 21, 2026 13:07
@tnull
tnullforce-pushed the 2026-04-abort-on-first-startup-tip-fetch-failure branch from 608108a to 494a448CompareJune 26, 2026 12:19
@tnulltnull changed the title 2026 04 abort on first startup tip fetch failureAllow wallet imports from arbitrary heights, forcing full scansJun 26, 2026
@tnull
tnull marked this pull request as ready for review June 26, 2026 12:25
@tnull

Copy link
Copy Markdown
CollaboratorAuthor

Rebased & refactored a bit, ready for review.

tnull added 3 commits June 29, 2026 16:18
When a fresh node's bitcoind RPC/REST chain source fails to return the
current chain tip, we previously silently fell back to the genesis
block as the wallet birthday. The next successful startup would then
force a full-history rescan of the whole chain. Instead, return a new
BuildError::ChainTipFetchFailed on the first build so the
misconfiguration surfaces immediately and no stale fresh state is
persisted.
Restarts with a previously-persisted wallet are unaffected: a
transient chain source outage on an existing node still allows startup
to proceed. Esplora/Electrum backends currently never expose a tip at
build time so the guard only fires for bitcoind sources; the latent
wallet-birthday-at-genesis issue on those backends is left for a
follow-up.
Co-Authored-By: HAL 9000
Allow Bitcoin Core RPC and REST chain-source configuration to specify
the wallet birthday height used when creating a fresh wallet. This lets
restored wallets rescan from a known height, including genesis, without
overloading a global recovery toggle.
Reject requested heights above the current chain tip with an explicit
build error before wallet state is created. Existing wallets are not
rewound by this option because a safe rewind must invalidate persisted
wallet and LDK state before replaying blocks.
Co-Authored-By: HAL 9000
Let Esplora and Electrum sync configs request BDK full scans
until one succeeds. This keeps recovery scans retryable after
transient sync failures while preserving normal incremental syncs
once recovery has completed.
Co-Authored-By: HAL 9000
@tnull
tnullforce-pushed the 2026-04-abort-on-first-startup-tip-fetch-failure branch from 494a448 to 101c827CompareJune 29, 2026 14:19
@tnull

Copy link
Copy Markdown
CollaboratorAuthor

Rebased to resolve minor conflict.

@wpaulinowpaulino left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, just one comment

Comment threadsrc/chain/electrum.rs
config: Arc<Config>,
logger: Arc<Logger>,
node_metrics: Arc<PersistedNodeMetrics>,
force_wallet_full_scan: AtomicBool,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we expose a block height as well to let the user optionally decide whether they want to start from a specific point?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean the current block height the node is synced to? That's already exposed via NodeStatus::current_best_block (cf. https://docs.rs/ldk-node/latest/ldk_node/struct.NodeStatus.html).

@tnull
tnull merged commit f2e44fd into lightningdevkit:mainJun 30, 2026
23 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Wallet birthday height for seed recovery on pruned nodes

3 participants

@tnull@ldk-reviews-bot@wpaulino
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Allow wallet imports from arbitrary heights, forcing full scans - #884

Merged
tnull merged 3 commits into
lightningdevkit:mainfrom
tnull:2026-04-abort-on-first-startup-tip-fetch-failure
Jun 30, 2026
Merged

Allow wallet imports from arbitrary heights, forcing full scans#884
tnull merged 3 commits into
lightningdevkit:mainfrom
tnull:2026-04-abort-on-first-startup-tip-fetch-failure

Conversation

@tnull

@tnulltnull commented Apr 21, 2026

Copy link
Copy Markdown
Collaborator

Fixes#818.

Previously, we would always start syncing from the latest block height or were able to force a full-resync from genesis via recovery_mode = true. Here we add more fine-grained control and allow an initial wallet import from a known birthday height for bitcoind RPC/REST chain sources. Relatedly, we also fix a minor bug where, if we failed to retrieve the tip hash on first startup, we'd always resync from genesis, which can be very costly.

Moreover, we also add a flag that allows the user to force a full scan for Esplora/Electrum wallets.

@tnulltnull added this to the 0.8 milestone Apr 21, 2026
@ldk-reviews-bot

ldk-reviews-bot commented Apr 21, 2026

Copy link
Copy Markdown

I've assigned @wpaulino as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@tnull
tnull marked this pull request as draft April 21, 2026 13:06
@tnull
tnullforce-pushed the 2026-04-abort-on-first-startup-tip-fetch-failure branch from c692081 to 608108aCompareApril 21, 2026 13:07
@tnull
tnullforce-pushed the 2026-04-abort-on-first-startup-tip-fetch-failure branch from 608108a to 494a448CompareJune 26, 2026 12:19
@tnulltnull changed the title 2026 04 abort on first startup tip fetch failureAllow wallet imports from arbitrary heights, forcing full scansJun 26, 2026
@tnull
tnull marked this pull request as ready for review June 26, 2026 12:25
@tnull

Copy link
Copy Markdown
CollaboratorAuthor

Rebased & refactored a bit, ready for review.

tnull added 3 commits June 29, 2026 16:18
When a fresh node's bitcoind RPC/REST chain source fails to return the
current chain tip, we previously silently fell back to the genesis
block as the wallet birthday. The next successful startup would then
force a full-history rescan of the whole chain. Instead, return a new
BuildError::ChainTipFetchFailed on the first build so the
misconfiguration surfaces immediately and no stale fresh state is
persisted.
Restarts with a previously-persisted wallet are unaffected: a
transient chain source outage on an existing node still allows startup
to proceed. Esplora/Electrum backends currently never expose a tip at
build time so the guard only fires for bitcoind sources; the latent
wallet-birthday-at-genesis issue on those backends is left for a
follow-up.
Co-Authored-By: HAL 9000
Allow Bitcoin Core RPC and REST chain-source configuration to specify
the wallet birthday height used when creating a fresh wallet. This lets
restored wallets rescan from a known height, including genesis, without
overloading a global recovery toggle.
Reject requested heights above the current chain tip with an explicit
build error before wallet state is created. Existing wallets are not
rewound by this option because a safe rewind must invalidate persisted
wallet and LDK state before replaying blocks.
Co-Authored-By: HAL 9000
Let Esplora and Electrum sync configs request BDK full scans
until one succeeds. This keeps recovery scans retryable after
transient sync failures while preserving normal incremental syncs
once recovery has completed.
Co-Authored-By: HAL 9000
@tnull
tnullforce-pushed the 2026-04-abort-on-first-startup-tip-fetch-failure branch from 494a448 to 101c827CompareJune 29, 2026 14:19
@tnull

Copy link
Copy Markdown
CollaboratorAuthor

Rebased to resolve minor conflict.

@wpaulinowpaulino left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, just one comment

Comment threadsrc/chain/electrum.rs
config: Arc<Config>,
logger: Arc<Logger>,
node_metrics: Arc<PersistedNodeMetrics>,
force_wallet_full_scan: AtomicBool,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we expose a block height as well to let the user optionally decide whether they want to start from a specific point?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean the current block height the node is synced to? That's already exposed via NodeStatus::current_best_block (cf. https://docs.rs/ldk-node/latest/ldk_node/struct.NodeStatus.html).

@tnull
tnull merged commit f2e44fd into lightningdevkit:mainJun 30, 2026
23 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Wallet birthday height for seed recovery on pruned nodes

3 participants

@tnull@ldk-reviews-bot@wpaulino
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Allow wallet imports from arbitrary heights, forcing full scans - #884

Merged
tnull merged 3 commits into
lightningdevkit:mainfrom
tnull:2026-04-abort-on-first-startup-tip-fetch-failure
Jun 30, 2026
Merged

Allow wallet imports from arbitrary heights, forcing full scans#884
tnull merged 3 commits into
lightningdevkit:mainfrom
tnull:2026-04-abort-on-first-startup-tip-fetch-failure

Conversation

@tnull

@tnulltnull commented Apr 21, 2026

Copy link
Copy Markdown
Collaborator

Fixes#818.

Previously, we would always start syncing from the latest block height or were able to force a full-resync from genesis via recovery_mode = true. Here we add more fine-grained control and allow an initial wallet import from a known birthday height for bitcoind RPC/REST chain sources. Relatedly, we also fix a minor bug where, if we failed to retrieve the tip hash on first startup, we'd always resync from genesis, which can be very costly.

Moreover, we also add a flag that allows the user to force a full scan for Esplora/Electrum wallets.

@tnulltnull added this to the 0.8 milestone Apr 21, 2026
@ldk-reviews-bot

ldk-reviews-bot commented Apr 21, 2026

Copy link
Copy Markdown

I've assigned @wpaulino as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@tnull
tnull marked this pull request as draft April 21, 2026 13:06
@tnull
tnullforce-pushed the 2026-04-abort-on-first-startup-tip-fetch-failure branch from c692081 to 608108aCompareApril 21, 2026 13:07
@tnull
tnullforce-pushed the 2026-04-abort-on-first-startup-tip-fetch-failure branch from 608108a to 494a448CompareJune 26, 2026 12:19
@tnulltnull changed the title 2026 04 abort on first startup tip fetch failureAllow wallet imports from arbitrary heights, forcing full scansJun 26, 2026
@tnull
tnull marked this pull request as ready for review June 26, 2026 12:25
@tnull

Copy link
Copy Markdown
CollaboratorAuthor

Rebased & refactored a bit, ready for review.

tnull added 3 commits June 29, 2026 16:18
When a fresh node's bitcoind RPC/REST chain source fails to return the
current chain tip, we previously silently fell back to the genesis
block as the wallet birthday. The next successful startup would then
force a full-history rescan of the whole chain. Instead, return a new
BuildError::ChainTipFetchFailed on the first build so the
misconfiguration surfaces immediately and no stale fresh state is
persisted.
Restarts with a previously-persisted wallet are unaffected: a
transient chain source outage on an existing node still allows startup
to proceed. Esplora/Electrum backends currently never expose a tip at
build time so the guard only fires for bitcoind sources; the latent
wallet-birthday-at-genesis issue on those backends is left for a
follow-up.
Co-Authored-By: HAL 9000
Allow Bitcoin Core RPC and REST chain-source configuration to specify
the wallet birthday height used when creating a fresh wallet. This lets
restored wallets rescan from a known height, including genesis, without
overloading a global recovery toggle.
Reject requested heights above the current chain tip with an explicit
build error before wallet state is created. Existing wallets are not
rewound by this option because a safe rewind must invalidate persisted
wallet and LDK state before replaying blocks.
Co-Authored-By: HAL 9000
Let Esplora and Electrum sync configs request BDK full scans
until one succeeds. This keeps recovery scans retryable after
transient sync failures while preserving normal incremental syncs
once recovery has completed.
Co-Authored-By: HAL 9000
@tnull
tnullforce-pushed the 2026-04-abort-on-first-startup-tip-fetch-failure branch from 494a448 to 101c827CompareJune 29, 2026 14:19
@tnull

Copy link
Copy Markdown
CollaboratorAuthor

Rebased to resolve minor conflict.

@wpaulinowpaulino left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, just one comment

Comment threadsrc/chain/electrum.rs
config: Arc<Config>,
logger: Arc<Logger>,
node_metrics: Arc<PersistedNodeMetrics>,
force_wallet_full_scan: AtomicBool,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we expose a block height as well to let the user optionally decide whether they want to start from a specific point?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean the current block height the node is synced to? That's already exposed via NodeStatus::current_best_block (cf. https://docs.rs/ldk-node/latest/ldk_node/struct.NodeStatus.html).

@tnull
tnull merged commit f2e44fd into lightningdevkit:mainJun 30, 2026
23 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Wallet birthday height for seed recovery on pruned nodes

3 participants

@tnull@ldk-reviews-bot@wpaulino
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Allow wallet imports from arbitrary heights, forcing full scans - #884

Merged
tnull merged 3 commits into
lightningdevkit:mainfrom
tnull:2026-04-abort-on-first-startup-tip-fetch-failure
Jun 30, 2026
Merged

Allow wallet imports from arbitrary heights, forcing full scans#884
tnull merged 3 commits into
lightningdevkit:mainfrom
tnull:2026-04-abort-on-first-startup-tip-fetch-failure

Conversation

@tnull

@tnulltnull commented Apr 21, 2026

Copy link
Copy Markdown
Collaborator

Fixes#818.

Previously, we would always start syncing from the latest block height or were able to force a full-resync from genesis via recovery_mode = true. Here we add more fine-grained control and allow an initial wallet import from a known birthday height for bitcoind RPC/REST chain sources. Relatedly, we also fix a minor bug where, if we failed to retrieve the tip hash on first startup, we'd always resync from genesis, which can be very costly.

Moreover, we also add a flag that allows the user to force a full scan for Esplora/Electrum wallets.

@tnulltnull added this to the 0.8 milestone Apr 21, 2026
@ldk-reviews-bot

ldk-reviews-bot commented Apr 21, 2026

Copy link
Copy Markdown

I've assigned @wpaulino as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@tnull
tnull marked this pull request as draft April 21, 2026 13:06
@tnull
tnullforce-pushed the 2026-04-abort-on-first-startup-tip-fetch-failure branch from c692081 to 608108aCompareApril 21, 2026 13:07
@tnull
tnullforce-pushed the 2026-04-abort-on-first-startup-tip-fetch-failure branch from 608108a to 494a448CompareJune 26, 2026 12:19
@tnulltnull changed the title 2026 04 abort on first startup tip fetch failureAllow wallet imports from arbitrary heights, forcing full scansJun 26, 2026
@tnull
tnull marked this pull request as ready for review June 26, 2026 12:25
@tnull

Copy link
Copy Markdown
CollaboratorAuthor

Rebased & refactored a bit, ready for review.

tnull added 3 commits June 29, 2026 16:18
When a fresh node's bitcoind RPC/REST chain source fails to return the
current chain tip, we previously silently fell back to the genesis
block as the wallet birthday. The next successful startup would then
force a full-history rescan of the whole chain. Instead, return a new
BuildError::ChainTipFetchFailed on the first build so the
misconfiguration surfaces immediately and no stale fresh state is
persisted.
Restarts with a previously-persisted wallet are unaffected: a
transient chain source outage on an existing node still allows startup
to proceed. Esplora/Electrum backends currently never expose a tip at
build time so the guard only fires for bitcoind sources; the latent
wallet-birthday-at-genesis issue on those backends is left for a
follow-up.
Co-Authored-By: HAL 9000
Allow Bitcoin Core RPC and REST chain-source configuration to specify
the wallet birthday height used when creating a fresh wallet. This lets
restored wallets rescan from a known height, including genesis, without
overloading a global recovery toggle.
Reject requested heights above the current chain tip with an explicit
build error before wallet state is created. Existing wallets are not
rewound by this option because a safe rewind must invalidate persisted
wallet and LDK state before replaying blocks.
Co-Authored-By: HAL 9000
Let Esplora and Electrum sync configs request BDK full scans
until one succeeds. This keeps recovery scans retryable after
transient sync failures while preserving normal incremental syncs
once recovery has completed.
Co-Authored-By: HAL 9000
@tnull
tnullforce-pushed the 2026-04-abort-on-first-startup-tip-fetch-failure branch from 494a448 to 101c827CompareJune 29, 2026 14:19
@tnull

Copy link
Copy Markdown
CollaboratorAuthor

Rebased to resolve minor conflict.

@wpaulinowpaulino left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, just one comment

Comment threadsrc/chain/electrum.rs
config: Arc<Config>,
logger: Arc<Logger>,
node_metrics: Arc<PersistedNodeMetrics>,
force_wallet_full_scan: AtomicBool,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we expose a block height as well to let the user optionally decide whether they want to start from a specific point?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean the current block height the node is synced to? That's already exposed via NodeStatus::current_best_block (cf. https://docs.rs/ldk-node/latest/ldk_node/struct.NodeStatus.html).

@tnull
tnull merged commit f2e44fd into lightningdevkit:mainJun 30, 2026
23 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Wallet birthday height for seed recovery on pruned nodes

3 participants

@tnull@ldk-reviews-bot@wpaulino
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Allow wallet imports from arbitrary heights, forcing full scans - #884

Merged
tnull merged 3 commits into
lightningdevkit:mainfrom
tnull:2026-04-abort-on-first-startup-tip-fetch-failure
Jun 30, 2026
Merged

Allow wallet imports from arbitrary heights, forcing full scans#884
tnull merged 3 commits into
lightningdevkit:mainfrom
tnull:2026-04-abort-on-first-startup-tip-fetch-failure

Conversation

@tnull

@tnulltnull commented Apr 21, 2026

Copy link
Copy Markdown
Collaborator

Fixes#818.

Previously, we would always start syncing from the latest block height or were able to force a full-resync from genesis via recovery_mode = true. Here we add more fine-grained control and allow an initial wallet import from a known birthday height for bitcoind RPC/REST chain sources. Relatedly, we also fix a minor bug where, if we failed to retrieve the tip hash on first startup, we'd always resync from genesis, which can be very costly.

Moreover, we also add a flag that allows the user to force a full scan for Esplora/Electrum wallets.

@tnulltnull added this to the 0.8 milestone Apr 21, 2026
@ldk-reviews-bot

ldk-reviews-bot commented Apr 21, 2026

Copy link
Copy Markdown

I've assigned @wpaulino as a reviewer!
I'll wait for their review and will help manage the review process.
Once they submit their review, I'll check if a second reviewer would be helpful.

@tnull
tnull marked this pull request as draft April 21, 2026 13:06
@tnull
tnullforce-pushed the 2026-04-abort-on-first-startup-tip-fetch-failure branch from c692081 to 608108aCompareApril 21, 2026 13:07
@tnull
tnullforce-pushed the 2026-04-abort-on-first-startup-tip-fetch-failure branch from 608108a to 494a448CompareJune 26, 2026 12:19
@tnulltnull changed the title 2026 04 abort on first startup tip fetch failureAllow wallet imports from arbitrary heights, forcing full scansJun 26, 2026
@tnull
tnull marked this pull request as ready for review June 26, 2026 12:25
@tnull

Copy link
Copy Markdown
CollaboratorAuthor

Rebased & refactored a bit, ready for review.

tnull added 3 commits June 29, 2026 16:18
When a fresh node's bitcoind RPC/REST chain source fails to return the
current chain tip, we previously silently fell back to the genesis
block as the wallet birthday. The next successful startup would then
force a full-history rescan of the whole chain. Instead, return a new
BuildError::ChainTipFetchFailed on the first build so the
misconfiguration surfaces immediately and no stale fresh state is
persisted.
Restarts with a previously-persisted wallet are unaffected: a
transient chain source outage on an existing node still allows startup
to proceed. Esplora/Electrum backends currently never expose a tip at
build time so the guard only fires for bitcoind sources; the latent
wallet-birthday-at-genesis issue on those backends is left for a
follow-up.
Co-Authored-By: HAL 9000
Allow Bitcoin Core RPC and REST chain-source configuration to specify
the wallet birthday height used when creating a fresh wallet. This lets
restored wallets rescan from a known height, including genesis, without
overloading a global recovery toggle.
Reject requested heights above the current chain tip with an explicit
build error before wallet state is created. Existing wallets are not
rewound by this option because a safe rewind must invalidate persisted
wallet and LDK state before replaying blocks.
Co-Authored-By: HAL 9000
Let Esplora and Electrum sync configs request BDK full scans
until one succeeds. This keeps recovery scans retryable after
transient sync failures while preserving normal incremental syncs
once recovery has completed.
Co-Authored-By: HAL 9000
@tnull
tnullforce-pushed the 2026-04-abort-on-first-startup-tip-fetch-failure branch from 494a448 to 101c827CompareJune 29, 2026 14:19
@tnull

Copy link
Copy Markdown
CollaboratorAuthor

Rebased to resolve minor conflict.

@wpaulinowpaulino left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, just one comment

Comment threadsrc/chain/electrum.rs
config: Arc<Config>,
logger: Arc<Logger>,
node_metrics: Arc<PersistedNodeMetrics>,
force_wallet_full_scan: AtomicBool,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we expose a block height as well to let the user optionally decide whether they want to start from a specific point?

Copy link
Copy Markdown
CollaboratorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean the current block height the node is synced to? That's already exposed via NodeStatus::current_best_block (cf. https://docs.rs/ldk-node/latest/ldk_node/struct.NodeStatus.html).

@tnull
tnull merged commit f2e44fd into lightningdevkit:mainJun 30, 2026
23 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Wallet birthday height for seed recovery on pruned nodes

3 participants

@tnull@ldk-reviews-bot@wpaulino