Skip to content

Drop the need for fork headers when calling Listen's disconnect #3876

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
wants to merge 5 commits into
base: main
Choose a base branch
from

Conversation

TheBlueMatt
Copy link
Collaborator

The Listen::block_disconnected method is nice in that listeners
learn about each block disconnected in series. Further, it included
the header of the block that is being disconnected to allow the
listeners to do some checking that the interface is being used
correctly (namely, asserting that the header's block hash matches
their current understanding of the best chain).

However, this interface has some substantial drawbacks. Namely, the
requirement that fork headers be passed in means that restarting
with a new node that has no idea about a previous fork leaves us
unable to replay the chain at all. Further, while when various
listeners were initially written learning about each block
disconnected in series seemed useful, but now we no longer rely on
that anyway because the Confirm interface does not allow for it.

Thus, here, we replace Listen::block_disconnected with a new
Listen::blocks_disconnected, taking only information about the
fork point/new best chain tip (in the form of its block hash and
height) rather than information about previous fork blocks and only
requiring a single call to complete multiple block disconnections
during a reorg.

This requires removing some assertions on block disconnection
ordering, but because we now provide lightning-block-sync and
expect users to use it when using the Listen interface, these
assertions are much less critical.

@ldk-reviews-bot
Copy link

ldk-reviews-bot commented Jun 18, 2025

👋 Thanks for assigning @tnull 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.

@ldk-reviews-bot ldk-reviews-bot requested a review from jkczyz June 18, 2025 21:12
@TheBlueMatt TheBlueMatt force-pushed the 2025-06-disconnect-less-info branch from b045453 to 5942c8b Compare June 18, 2025 21:25
The `Listen::block_disconnected` method is nice in that listeners
learn about each block disconnected in series. Further, it included
the header of the block that is being disconnected to allow the
listeners to do some checking that the interface is being used
correctly (namely, asserting that the header's block hash matches
their current understanding of the best chain).

However, this interface has some substantial drawbacks. Namely, the
requirement that fork headers be passed in means that restarting
with a new node that has no idea about a previous fork leaves us
unable to replay the chain at all. Further, while when various
listeners were initially written learning about each block
disconnected in series seemed useful, but now we no longer rely on
that anyway because the `Confirm` interface does not allow for it.

Thus, here, we replace `Listen::block_disconnected` with a new
`Listen::blocks_disconnected`, taking only information about the
fork point/new best chain tip (in the form of its block hash and
height) rather than information about previous fork blocks and only
requiring a single call to complete multiple block disconnections
during a reorg.

We also swap to using a single `BestBlock` to describe the new
chain tip, in anticipation of future extensions to `BestBlock`.

This requires removing some assertions on block disconnection
ordering, but because we now provide `lightning-block-sync` and
expect users to use it when using the `Listen` interface, these
assertions are much less critical.
@ldk-reviews-bot
Copy link

👋 The first review has been submitted!

Do you think this PR is ready for a second reviewer? If so, click here to assign a second reviewer.

@ldk-reviews-bot
Copy link

✅ Added second reviewer: @joostjager

@tnull tnull requested review from tnull and removed request for joostjager June 19, 2025 16:11
Copy link

codecov bot commented Jun 19, 2025

Codecov Report

Attention: Patch coverage is 80.55556% with 21 lines in your changes missing coverage. Please review.

Project coverage is 90.46%. Comparing base (9db0fb2) to head (a54159c).
Report is 10 commits behind head on main.

Files with missing lines Patch % Lines
lightning-block-sync/src/test_utils.rs 44.44% 5 Missing ⚠️
lightning/src/chain/mod.rs 0.00% 5 Missing ⚠️
lightning/src/chain/channelmonitor.rs 71.42% 4 Missing ⚠️
lightning/src/util/sweep.rs 0.00% 3 Missing ⚠️
lightning-liquidity/src/manager.rs 0.00% 2 Missing ⚠️
lightning-block-sync/src/init.rs 90.00% 1 Missing ⚠️
lightning/src/ln/functional_tests.rs 66.66% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3876      +/-   ##
==========================================
+ Coverage   89.76%   90.46%   +0.70%     
==========================================
  Files         164      164              
  Lines      133048   139516    +6468     
  Branches   133048   139516    +6468     
==========================================
+ Hits       119434   126218    +6784     
+ Misses      10937    10589     -348     
- Partials     2677     2709      +32     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Now that the `Listen` interface allows blocks to be disconnected in
batches rather than one at a time, we should test this. Here we add
a new `ConnectStyle` for the functional test framework which tests
doing so.
When calling `Channel::best_block_updated` we pass it the timestamp
of the block we're connecting so that it can track the highest
timestamp it has seen.

However, in some cases, we don't actually have a timestamp to pass,
which `Channel::best_block_updated` will happily ignore as it
always takes the `max` of its existing value. Thus, we really
should pass a `None` to ensure the API is understandable, which we
do here.
@TheBlueMatt TheBlueMatt force-pushed the 2025-06-disconnect-less-info branch from b06cbd4 to a54159c Compare June 19, 2025 18:47
jkczyz
jkczyz previously approved these changes Jun 20, 2025
@ldk-reviews-bot
Copy link

🔔 1st Reminder

Hey @tnull! This PR has been waiting for your review.
Please take a look when you have a chance. If you're unable to review, please let us know so we can find another reviewer.

Copy link
Contributor

@tnull tnull left a comment

Choose a reason for hiding this comment

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

Changes LGTM, but while we're here it would be good to add some details regarding the implicit assumptions on the Listen docs, and adding (back) some asserts/debug_asserts would be appreciated.


for output_info in state_lock.outputs.iter_mut() {
if output_info.status.confirmation_hash() == Some(block_hash) {
debug_assert_eq!(output_info.status.confirmation_height(), Some(height));
if output_info.status.confirmation_height() > Some(new_best_block.height) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm, not super happy we have to resort to height over hash in a few places like here now, as it would easily would become a footgun if users implemented their own syncing logic.

Can we at least sprinkle in a few more assert/debug_asserts to ascertain that the height difference is exactly 1 everywhere we lean on it?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Hmm, not super happy we have to resort to height over hash in a few places like here now, as it would easily would become a footgun if users implemented their own syncing logic.

Indeed, it does worry me a bit, though luckily I imagine most folks these days use our lightning-block-sync or lightning-transaction-sync crates, which should be fairly robust at this point.

Can we at least sprinkle in a few more assert/debug_asserts to ascertain that the height difference is exactly 1 everywhere we lean on it?

Sadly we don't lean on this anywhere anymore in the disconnect pipeline. We still rely on it in the connection logic, but those asserts are still there.

Copy link
Contributor

Choose a reason for hiding this comment

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

Sadly we don't lean on this anywhere anymore in the disconnect pipeline. We still rely on it in the connection logic, but those asserts are still there.

Hmm, but we could still check that the old best block height is new_best_block.height + 1 in blocks_disconnected above though, and also in a few other places, no?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Hmm, but we could still check that the old best block height is new_best_block.height + 1 in blocks_disconnected above though, and also in a few other places, no?

No, this is a very specific API change to not require that anymore. ie you can call blocks_disconnected once for many blocks disconnected. This is important for allowing a node to recover from being on a fork as you no longer need to know anything about any blocks that were on the chain you were on, only blocks that are on the new main chain (you just need to be able to figure out a fork point, but its even okay if that fork point is behind the actual fork point).

`Listen` is somewhat quiet on high-level use and even requirements,
which we document further here.
@TheBlueMatt TheBlueMatt requested review from jkczyz and tnull June 27, 2025 21:01
Sign up for free to 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.

4 participants