Skip to content

Reconcile monitor tips before starting peers, sync Lightning wallets on startup - #1161

Merged
tnull merged 5 commits into
lightningdevkit:mainfrom
tnull:2026-10-fix-monitor-startup-sync
Oct 14, 2026
Merged

tnull merged 5 commits into
lightningdevkit:mainfrom
tnull:2026-10-fix-monitor-startup-sync

Conversation

@tnull

@tnull tnull commented Oct 13, 2026

Copy link
Copy Markdown
Collaborator

Fixes #29.
Fixes #1076.

In this PR we fix a few bugs in our chain syncing logic. In particular we individually sync monitors (disconnecting stale blocks) before giving them all to the chain listener on startup. This now also has the node block until the initial reconciliation is completed.

Relatedly, but somewhat orthogonal we now also have the Esplora/Electrum chain sources block on the initial sync of the lightning wallet, to ensure we never start a node with stale chain information.

Bitcoin Core listener initialization and continuous polling share one
background task, making changes to their startup ordering harder to
review independently.

Extract initialization while keeping it in the same background task.
Preserve retry, cancellation, and polling behavior so startup ordering
can change separately.

Co-Authored-By: HAL 9000
@tnull
tnull requested a review from joostjager October 13, 2026 10:36
@ldk-reviews-bot

ldk-reviews-bot commented Oct 13, 2026 •

Copy link
Copy Markdown

👋 Thanks for assigning @joostjager 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 added this to the 0.8 milestone Oct 13, 2026
@tnull
tnull force-pushed the 2026-10-fix-monitor-startup-sync branch 2 times, most recently from 9c82dc2 to 77bbd1d Compare October 13, 2026 10:53
tnull added 3 commits October 13, 2026 12:54
Bitcoin Core synchronized restored chain listeners in a background
loop after startup. Peers and event processing could run while those
listeners were still catching up, and sync failures only retried.

Wait for initial synchronization before starting peers and event
processing. Return synchronization errors to the caller so a failed
start can shut down cleanly and be retried.

Co-Authored-By: HAL 9000
Bitcoin Core startup synchronized all restored channel monitors from
the lowest saved tip. A monitor saved farther ahead on an orphaned fork
could miss disconnects and retain stale confirmations.

Reconcile each monitor from its own current locator while peers and
event processing remain stopped. Cover divergent persisted tips and
retries after partial synchronization with regression tests.

Fixes lightningdevkit#1076
Reported-by: Project Loupe / instagibbs
Co-Authored-By: HAL 9000
Esplora and Electrum started peers and event processing before their
first Lightning synchronization. With background sync disabled, startup
could leave restored Lightning state stale indefinitely.

Synchronize the Lightning wallet during initialization for both chain
sources. Leave BDK synchronization to background or manual syncing and
cover initial startup and restart against both backends.

Co-Authored-By: HAL 9000
@tnull
tnull force-pushed the 2026-10-fix-monitor-startup-sync branch from 77bbd1d to 6424fb5 Compare October 13, 2026 10:54
Comment thread src/chain/mod.rs Outdated
output_sweeper,
&self.tx_broadcaster,
),
source.update_fee_rate_estimates(),

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.

We have duplicate code for all the chain sources. Can it be simplified?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Not sure what you have in mind, but I find it pretty readable?

Comment thread src/chain/bitcoind.rs
}
}

async fn synchronize_chain_listeners<B: BlockSource>(

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.

Maybe it's better to make a change upstream, in rust-lightning?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

We could, but we want to fix this issue for the upcoming release (based on LDK 0.3) so an upstream change won't make it in time.

@joostjager joostjager 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.

I’d leave the final fee-initialization commit out of this PR. It’s an independent optimization, and concurrent replay may construct claims before fee estimates are loaded.

The sync policy also feels inconsistent: we refuse to start when sync fails, but keep running when we lose sync later. Reconciling restored state is necessary; requiring fresh chain data before starting peers seems like a separate policy decision.

At the risk of sounding like a broken record, I still think independent monitor persistence is doing more harm than good. This PR adds more recovery complexity to accommodate it, with questionable benefit to users.

Comment thread src/lib.rs
Arc::clone(&self.channel_manager),
Arc::clone(&self.chain_monitor),
Arc::clone(&self.output_sweeper),
))?;

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.

Is it intentional that failure to sync here now exits the process whereas previously it would retry? That seems to be a direction different from adding more lifecycle to the node (which we discussed in the context of db leases)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Yes, that's intentional and needed if we want to ensure nodes won't start unless synced to the latest chain state. Yes, if once we have node lifecycle management we can simply retry restarting the node indefinitely, but right now this seems the right choice if we now wan to address this issue (or rather reconsider our prior choice).

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.

Yes, I get that it is a practical choice.

Comment thread src/chain/mod.rs Outdated
ChainSourceKind::Bitcoind(source) => {
source
.initialize_wallets(
tokio::try_join!(

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.

[P2] Load fee estimates before replaying chain data (src/chain/mod.rs:323). The final commit runs synchronization concurrently with fee initialization. Monitor callbacks can construct and queue claim transactions during synchronization, using fallback fees if the fee request finishes later. Waiting for both operations before starting peers cannot correct those already constructed transactions.

@tnull tnull Oct 14, 2026 •

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Hmpf, I intentionally tried to avoid the extra RTT here, but the LLM is right unfortunately, will revert.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Updated.

Startup separately awaited fee estimates and backend capability checks
before initializing wallets. These checks must complete before replay
can construct claim transactions using the fee estimator.

Move the checks into wallet initialization while preserving their
ordering. Run fee refresh and required 0FC validation concurrently,
then synchronize chain state only after both succeed.

Co-Authored-By: HAL 9000
@tnull
tnull force-pushed the 2026-10-fix-monitor-startup-sync branch from 6424fb5 to fc380fd Compare October 14, 2026 11:18
@tnull
tnull requested a review from joostjager October 14, 2026 11:18
@tnull
tnull force-pushed the 2026-10-fix-monitor-startup-sync branch from fc380fd to f552c4b Compare October 14, 2026 11:19
@tnull

tnull commented Oct 14, 2026

Copy link
Copy Markdown
Collaborator Author

I’d leave the final fee-initialization commit out of this PR. It’s an independent optimization, and concurrent replay may construct claims before fee estimates are loaded.

Considered that, but I think doing the fee rate update in the wallet initialization step is preferable, for one it keeps related logic together, and it saves a block_on/block_in_place, i.e., unnecessarily spawning a thread.

The sync policy also feels inconsistent: we refuse to start when sync fails, but keep running when we lose sync later. Reconciling restored state is necessary; requiring fresh chain data before starting peers seems like a separate policy decision.

Yes, though without full lifecycle management there is little we can do beyond refusing on startup, and encountering stale chain state is somewhat expected if a node would have been stopped for a extended period of time.

At the risk of sounding like a broken record, I still think independent monitor persistence is doing more harm than good. This PR adds more recovery complexity to accommodate it, with questionable benefit to users.

Related but orthogonal.

@tnull
tnull merged commit d9d5c49 into lightningdevkit:main Oct 14, 2026
15 of 50 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Startup sync can re-confirm a transaction without first unconfirming it Recover from out-of-sync channelmonitors

4 participants