Skip to content

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

Merged
jkczyz merged 5 commits into
lightningdevkit:mainfrom
TheBlueMatt:2025-06-disconnect-less-info
Sep 5, 2025
Merged

Drop the need for fork headers when calling Listen's disconnect#3876
jkczyz merged 5 commits into
lightningdevkit:mainfrom
TheBlueMatt:2025-06-disconnect-less-info

Conversation

@TheBlueMatt

Copy link
Copy Markdown
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

ldk-reviews-bot commented Jun 18, 2025

Copy link
Copy Markdown

👋 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.

Comment threadlightning/src/ln/channelmanager.rs Outdated
@ldk-reviews-bot

Copy link
Copy Markdown

👋 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
Copy Markdown

✅ Added second reviewer: @joostjager

@tnull
tnull requested review from tnull and removed request for joostjagerJune 19, 2025 16:11
@codecov

codecovBot commented Jun 19, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 79.36508% with 26 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.74%. Comparing base (86c6960) to head (a5b745a).
⚠️ Report is 38 commits behind head on main.

Files with missing linesPatch %Lines
lightning/src/chain/channelmonitor.rs70.00%6 Missing ⚠️
lightning/src/chain/mod.rs0.00%5 Missing ⚠️
lightning/src/util/sweep.rs0.00%5 Missing ⚠️
lightning-liquidity/src/manager.rs0.00%4 Missing ⚠️
lightning-block-sync/src/test_utils.rs66.66%3 Missing ⚠️
lightning-block-sync/src/init.rs90.00%1 Missing ⚠️
lightning/src/ln/channelmanager.rs95.83%1 Missing ⚠️
lightning/src/ln/functional_tests.rs66.66%1 Missing ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #3876 +/- ##
=======================================
Coverage 88.73% 88.74% =======================================
Files 176 176 Lines 129106 129102 -4 Branches 129106 129102 -4 =======================================
+ Hits 114567 114575 +8 + Misses 11937 11931 -6 + Partials 2602 2596 -6 
FlagCoverage Δ
fuzzing22.32% <13.18%> (+0.01%)⬆️
tests88.58% <79.36%> (+<0.01%)⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ 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.

@TheBlueMatt
TheBlueMattforce-pushed the 2025-06-disconnect-less-info branch from b06cbd4 to a54159cCompareJune 19, 2025 18:47
jkczyz
jkczyz previously approved these changes Jun 20, 2025
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 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.

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

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.

Comment threadlightning/src/chain/mod.rs
Comment threadlightning/src/util/sweep.rs Outdated
Comment threadlightning/src/chain/mod.rs Outdated
Comment threadlightning/src/chain/mod.rs Outdated
Comment threadlightning/src/chain/mod.rs Outdated
Comment threadlightning/src/chain/mod.rs Outdated
@TheBlueMatt
TheBlueMatt requested review from jkczyz and tnullJune 27, 2025 21:01
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 1st Reminder

Hey @tnull@jkczyz! 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.

1 similar comment
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 1st Reminder

Hey @tnull@jkczyz! 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.

Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs Outdated
///
/// Each block must be connected in chain order with one call to either
/// [`Listen::block_connected`] or [`Listen::filtered_block_connected`]. If a call to the
/// [`Filter`] interface was made during block processing and further transaction(s) from the same

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.

Hmm, is this a new requirement? I imagine it's very hard to reliably implement this: keep track of all registered Txids/outputs, then match them on a block, apply the block, then check again if something new was registered, filter the block again, then potentially call once more. Also, why do we require it only here, but not on Confirm?

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.

This has always been the case, just poorly documented. It is, indeed, not the case on Confirm (which doesn't require "blocks" to be connected in-order at all, though it does require all transactions to be topologically sorted).

}
self.chain_listener.block_disconnected(&header.header, header.height);
}
if let Some(block) = disconnected_blocks.last() {

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.

If we're now happy to use the fork point, why are we keeping a Vec of all disconnected blocks around at all? Shouldn't we now be able to convert this to the new behavior entirely? Seems ChainDifference::common_ancestor would already provide what we need, no?

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.

Its needed by the cache, which I didn't want to change in this PR - #3876 (comment)

Comment threadlightning/src/util/sweep.rs Outdated
for header in disconnected_blocks.drain(..) {
fn disconnect_blocks(&mut self, disconnected_blocks: Vec<ValidatedBlockHeader>) {
for header in disconnected_blocks.iter() {
if let Some(cached_header) = self.header_cache.block_disconnected(&header.block_hash) {

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 change the Cache API accordingly?

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.

We could, but it seems somewhat disjoint from this PR, given there's further work to do in lightning-block-sync anyway, so I kinda wanted to keep it as small as can be.

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.

Mhh, might be good to document somewhere what steps you deem left before you'd consider the transition to this new approach complete. I guess all of them should land before the next release then, to not end up with a half-migrated codebase in the release?

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.

I mean the API/stuff in lightning in in a perfectly fine state after this PR, and lightning-block-sync just deserves a bit of efficiency in dropping a vec and improving the cache afterwards. This PR obviously doesn't accomplish the goals that #3600 set out to, but is just a step towards it (the "complicated" step that requires making sure all the lightning-internal stuff isn't broken by changing the API).

AFAIU, to get to the point that bitcoind (and all) syncs support swapping to a new chain during a reorg and don't fetch a pile of redundant blocks we need to:

  • make BestBlock contain a list of 6(ish?) block hashes, not one
  • use that list in lightning-block-sync (and eventually lightning-transaction-sync) to do the sync without leaning on the cache
  • expand the runtime caching to cache partial chains across clients in the init sync logic
  • (eventually) ensure lightning-block-sync doesn't have any spare vecs or whatever lying around.

@tnulltnullJul 3, 2025

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 still think it would make sense to open an issue for this larger refactoring you're planning, as there are connected aspects to discuss that don't really belong in this PR.

make BestBlock contain a list of 6(ish?) block hashes, not one

For example, how do we imagine this to work exactly, especially for the Confirm/lightning-transaction-sync case?

For Esplora, we currently retrieve the tip hash, retrieve the header and the block status (including the hight), before we call best_block_updated, which is already 3 costly RTTs. If we now would require to track the 6 most-recent hashes, we'd need to make at least 5 more subsequent calls (bringing this it at least 8 RTTs per sync round) to retrieve the hashes at [(height-5)..(height-1)]. But, given that these are individual calls, there is no way to tell if any reorg happened during some of these calls, so they are inherently race-y.

For Electrum, the results would be very similar for the polling client in its current form. Note we eventually want to switch the client to a streaming version making use of Electum's subscription model though, and requiring 'last 6 blocks' would probably require us to resort to costly polling again.

If we'd otherwise extend the client to start tracking more of the chain state (i.e., actually tracking a local chain 6-suffix) across rounds this would complicate the client logic quite a bit and make it even more susceptible to race conditions.

TLDR: I still maintain all of this would be/is resulting in a major refactor across our chain syncing crates and logic, and it would be great to discuss what we imagine to be involved and the corresponding trade-offs before we just jump into it heads first.

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.

We can definitely make it optional (and only used for full-node-sync) if we think its a major issue. Of course the issue becomes that you cannot (even today) safely transition from an esplora/electrum-synced node to a full-chain-sync one, but addressing that really requires a substantial rework of how we think about chain sync, so maybe its not worth worrying about that.

Of course, storing the last 6 blocks is pretty trivial for a full-node-sync client, and the point is really just to expose the thing that the sync driver gave us, so its not like we'd use it in Listen/Confirm implementations.

My understanding is that this would address the restart-with-different-node case that #3600 wanted to address, even if its optional and just used by the full-node-syncing logic?

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.

but addressing that really requires a substantial rework of how we think about chain sync, so maybe its not worth worrying about that.

Hmm, given that we even recently had users inquire whether they could switch chain sources at runtime, I do expect that we will need to tackle this eventually, as there will be users wanting to switch chain sources, and it's very unintuitive to either prohibit that, have it one-way, or even document the risks that might come from it.

Of course, storing the last 6 blocks is pretty trivial for a full-node-sync client, and the point is really just to expose the thing that the sync driver gave us, so its not like we'd use it in Listen/Confirm implementations.

So we would only do it when it's convenient, which means we could switch bitcoind nodes, but still not between chain sources?

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.

Hmm, given that we even recently had users inquire whether they could switch chain sources at runtime, I do expect that we will need to tackle this eventually, as there will be users wanting to switch chain sources, and it's very unintuitive to either prohibit that, have it one-way, or even document the risks that might come from it.

Sure, I agree, hence why I suggested moving to storing several recent blocks rather than only one. However, as you note it might not be super practical, so we're kinda stuck.

I think, basically, if we did that plus required transactions_confirmed calls come before best_block_updated calls (which may we're doing anyway, cc #3867 (comment)) then moving from one chain source to another is totally fine, even at runtime (as long as you don't miss transactions).

So we would only do it when it's convenient, which means we could switch bitcoind nodes, but still not between chain sources?

I mean I'm not sure what else we can do? If its too impractical to do it on some chain sources we can't really make it required, and if its not available switching chain sources isn't going to be 100% reliable.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 2nd Reminder

Hey @jkczyz! 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.

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

rustfmt is unhappy right now

@TheBlueMatt
TheBlueMattforce-pushed the 2025-06-disconnect-less-info branch from ef73b0a to a6985ebCompareJuly 2, 2025 22:37
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 3rd Reminder

Hey @jkczyz! 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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 4th Reminder

Hey @jkczyz! 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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 5th Reminder

Hey @jkczyz! 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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 6th Reminder

Hey @jkczyz! 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.

@jkczyzjkczyz 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. Please squash.

@TheBlueMatt
TheBlueMattforce-pushed the 2025-06-disconnect-less-info branch from bf41b15 to c78b08bCompareAugust 7, 2025 20:29
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed without further changes.

jkczyz
jkczyz previously approved these changes Aug 7, 2025

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

Linting CI failures look unrelated.

@TheBlueMatt
TheBlueMattforce-pushed the 2025-06-disconnect-less-info branch from c78b08b to 7b590eeCompareAugust 21, 2025 14:57
@TheBlueMatt
TheBlueMatt requested review from tnull and removed request for tnullAugust 21, 2025 15:01
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 2nd 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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 3rd 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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 4th 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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 5th 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.

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

This unfortunately needs a small rebase by now.

tnull
tnull previously approved these changes Sep 2, 2025

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

ACK, after rebase. Please excuse the considerable delay here.

I still think it would be good to document in an issue what's 'left' post this PR, at least where we want to take things directionally. Maybe we can just transfer #3876 (comment) to an issue that can serve as a base for discussions?

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.
`OnchainTxHandler` is an internal struct and doesn't implement
`Listen`, but its still nice to have its API mirror the `Listen`
API so that internal code all looks similar.
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.
`Listen` is somewhat quiet on high-level use and even requirements,
which we document further here.
@TheBlueMatt

TheBlueMatt commented Sep 2, 2025

Copy link
Copy Markdown
CollaboratorAuthor

Rebased with no further changes. Also opened #4042

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 1st Reminder

Hey @jkczyz! 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.

@jkczyz
jkczyz merged commit eb300b6 into lightningdevkit:mainSep 5, 2025
25 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.

4 participants

@TheBlueMatt@ldk-reviews-bot@tnull@jkczyz
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
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;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Drop the need for fork headers when calling Listen's disconnect by TheBlueMatt · Pull Request #3876 · lightningdevkit/rust-lightning · GitHub
Skip to content

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

Merged
jkczyz merged 5 commits into
lightningdevkit:mainfrom
TheBlueMatt:2025-06-disconnect-less-info
Sep 5, 2025
Merged

Drop the need for fork headers when calling Listen's disconnect#3876
jkczyz merged 5 commits into
lightningdevkit:mainfrom
TheBlueMatt:2025-06-disconnect-less-info

Conversation

@TheBlueMatt

Copy link
Copy Markdown
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

ldk-reviews-bot commented Jun 18, 2025

Copy link
Copy Markdown

👋 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.

Comment threadlightning/src/ln/channelmanager.rs Outdated
@ldk-reviews-bot

Copy link
Copy Markdown

👋 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
Copy Markdown

✅ Added second reviewer: @joostjager

@tnull
tnull requested review from tnull and removed request for joostjagerJune 19, 2025 16:11
@codecov

codecovBot commented Jun 19, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 79.36508% with 26 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.74%. Comparing base (86c6960) to head (a5b745a).
⚠️ Report is 38 commits behind head on main.

Files with missing linesPatch %Lines
lightning/src/chain/channelmonitor.rs70.00%6 Missing ⚠️
lightning/src/chain/mod.rs0.00%5 Missing ⚠️
lightning/src/util/sweep.rs0.00%5 Missing ⚠️
lightning-liquidity/src/manager.rs0.00%4 Missing ⚠️
lightning-block-sync/src/test_utils.rs66.66%3 Missing ⚠️
lightning-block-sync/src/init.rs90.00%1 Missing ⚠️
lightning/src/ln/channelmanager.rs95.83%1 Missing ⚠️
lightning/src/ln/functional_tests.rs66.66%1 Missing ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #3876 +/- ##
=======================================
Coverage 88.73% 88.74% =======================================
Files 176 176 Lines 129106 129102 -4 Branches 129106 129102 -4 =======================================
+ Hits 114567 114575 +8 + Misses 11937 11931 -6 + Partials 2602 2596 -6 
FlagCoverage Δ
fuzzing22.32% <13.18%> (+0.01%)⬆️
tests88.58% <79.36%> (+<0.01%)⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ 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.

@TheBlueMatt
TheBlueMattforce-pushed the 2025-06-disconnect-less-info branch from b06cbd4 to a54159cCompareJune 19, 2025 18:47
jkczyz
jkczyz previously approved these changes Jun 20, 2025
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 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.

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

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.

Comment threadlightning/src/chain/mod.rs
Comment threadlightning/src/util/sweep.rs Outdated
Comment threadlightning/src/chain/mod.rs Outdated
Comment threadlightning/src/chain/mod.rs Outdated
Comment threadlightning/src/chain/mod.rs Outdated
Comment threadlightning/src/chain/mod.rs Outdated
@TheBlueMatt
TheBlueMatt requested review from jkczyz and tnullJune 27, 2025 21:01
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 1st Reminder

Hey @tnull@jkczyz! 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.

1 similar comment
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 1st Reminder

Hey @tnull@jkczyz! 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.

Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs Outdated
///
/// Each block must be connected in chain order with one call to either
/// [`Listen::block_connected`] or [`Listen::filtered_block_connected`]. If a call to the
/// [`Filter`] interface was made during block processing and further transaction(s) from the same

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.

Hmm, is this a new requirement? I imagine it's very hard to reliably implement this: keep track of all registered Txids/outputs, then match them on a block, apply the block, then check again if something new was registered, filter the block again, then potentially call once more. Also, why do we require it only here, but not on Confirm?

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.

This has always been the case, just poorly documented. It is, indeed, not the case on Confirm (which doesn't require "blocks" to be connected in-order at all, though it does require all transactions to be topologically sorted).

}
self.chain_listener.block_disconnected(&header.header, header.height);
}
if let Some(block) = disconnected_blocks.last() {

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.

If we're now happy to use the fork point, why are we keeping a Vec of all disconnected blocks around at all? Shouldn't we now be able to convert this to the new behavior entirely? Seems ChainDifference::common_ancestor would already provide what we need, no?

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.

Its needed by the cache, which I didn't want to change in this PR - #3876 (comment)

Comment threadlightning/src/util/sweep.rs Outdated
for header in disconnected_blocks.drain(..) {
fn disconnect_blocks(&mut self, disconnected_blocks: Vec<ValidatedBlockHeader>) {
for header in disconnected_blocks.iter() {
if let Some(cached_header) = self.header_cache.block_disconnected(&header.block_hash) {

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 change the Cache API accordingly?

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.

We could, but it seems somewhat disjoint from this PR, given there's further work to do in lightning-block-sync anyway, so I kinda wanted to keep it as small as can be.

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.

Mhh, might be good to document somewhere what steps you deem left before you'd consider the transition to this new approach complete. I guess all of them should land before the next release then, to not end up with a half-migrated codebase in the release?

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.

I mean the API/stuff in lightning in in a perfectly fine state after this PR, and lightning-block-sync just deserves a bit of efficiency in dropping a vec and improving the cache afterwards. This PR obviously doesn't accomplish the goals that #3600 set out to, but is just a step towards it (the "complicated" step that requires making sure all the lightning-internal stuff isn't broken by changing the API).

AFAIU, to get to the point that bitcoind (and all) syncs support swapping to a new chain during a reorg and don't fetch a pile of redundant blocks we need to:

  • make BestBlock contain a list of 6(ish?) block hashes, not one
  • use that list in lightning-block-sync (and eventually lightning-transaction-sync) to do the sync without leaning on the cache
  • expand the runtime caching to cache partial chains across clients in the init sync logic
  • (eventually) ensure lightning-block-sync doesn't have any spare vecs or whatever lying around.

@tnulltnullJul 3, 2025

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 still think it would make sense to open an issue for this larger refactoring you're planning, as there are connected aspects to discuss that don't really belong in this PR.

make BestBlock contain a list of 6(ish?) block hashes, not one

For example, how do we imagine this to work exactly, especially for the Confirm/lightning-transaction-sync case?

For Esplora, we currently retrieve the tip hash, retrieve the header and the block status (including the hight), before we call best_block_updated, which is already 3 costly RTTs. If we now would require to track the 6 most-recent hashes, we'd need to make at least 5 more subsequent calls (bringing this it at least 8 RTTs per sync round) to retrieve the hashes at [(height-5)..(height-1)]. But, given that these are individual calls, there is no way to tell if any reorg happened during some of these calls, so they are inherently race-y.

For Electrum, the results would be very similar for the polling client in its current form. Note we eventually want to switch the client to a streaming version making use of Electum's subscription model though, and requiring 'last 6 blocks' would probably require us to resort to costly polling again.

If we'd otherwise extend the client to start tracking more of the chain state (i.e., actually tracking a local chain 6-suffix) across rounds this would complicate the client logic quite a bit and make it even more susceptible to race conditions.

TLDR: I still maintain all of this would be/is resulting in a major refactor across our chain syncing crates and logic, and it would be great to discuss what we imagine to be involved and the corresponding trade-offs before we just jump into it heads first.

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.

We can definitely make it optional (and only used for full-node-sync) if we think its a major issue. Of course the issue becomes that you cannot (even today) safely transition from an esplora/electrum-synced node to a full-chain-sync one, but addressing that really requires a substantial rework of how we think about chain sync, so maybe its not worth worrying about that.

Of course, storing the last 6 blocks is pretty trivial for a full-node-sync client, and the point is really just to expose the thing that the sync driver gave us, so its not like we'd use it in Listen/Confirm implementations.

My understanding is that this would address the restart-with-different-node case that #3600 wanted to address, even if its optional and just used by the full-node-syncing logic?

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.

but addressing that really requires a substantial rework of how we think about chain sync, so maybe its not worth worrying about that.

Hmm, given that we even recently had users inquire whether they could switch chain sources at runtime, I do expect that we will need to tackle this eventually, as there will be users wanting to switch chain sources, and it's very unintuitive to either prohibit that, have it one-way, or even document the risks that might come from it.

Of course, storing the last 6 blocks is pretty trivial for a full-node-sync client, and the point is really just to expose the thing that the sync driver gave us, so its not like we'd use it in Listen/Confirm implementations.

So we would only do it when it's convenient, which means we could switch bitcoind nodes, but still not between chain sources?

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.

Hmm, given that we even recently had users inquire whether they could switch chain sources at runtime, I do expect that we will need to tackle this eventually, as there will be users wanting to switch chain sources, and it's very unintuitive to either prohibit that, have it one-way, or even document the risks that might come from it.

Sure, I agree, hence why I suggested moving to storing several recent blocks rather than only one. However, as you note it might not be super practical, so we're kinda stuck.

I think, basically, if we did that plus required transactions_confirmed calls come before best_block_updated calls (which may we're doing anyway, cc #3867 (comment)) then moving from one chain source to another is totally fine, even at runtime (as long as you don't miss transactions).

So we would only do it when it's convenient, which means we could switch bitcoind nodes, but still not between chain sources?

I mean I'm not sure what else we can do? If its too impractical to do it on some chain sources we can't really make it required, and if its not available switching chain sources isn't going to be 100% reliable.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 2nd Reminder

Hey @jkczyz! 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.

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

rustfmt is unhappy right now

@TheBlueMatt
TheBlueMattforce-pushed the 2025-06-disconnect-less-info branch from ef73b0a to a6985ebCompareJuly 2, 2025 22:37
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 3rd Reminder

Hey @jkczyz! 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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 4th Reminder

Hey @jkczyz! 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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 5th Reminder

Hey @jkczyz! 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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 6th Reminder

Hey @jkczyz! 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.

@jkczyzjkczyz 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. Please squash.

@TheBlueMatt
TheBlueMattforce-pushed the 2025-06-disconnect-less-info branch from bf41b15 to c78b08bCompareAugust 7, 2025 20:29
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed without further changes.

jkczyz
jkczyz previously approved these changes Aug 7, 2025

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

Linting CI failures look unrelated.

@TheBlueMatt
TheBlueMattforce-pushed the 2025-06-disconnect-less-info branch from c78b08b to 7b590eeCompareAugust 21, 2025 14:57
@TheBlueMatt
TheBlueMatt requested review from tnull and removed request for tnullAugust 21, 2025 15:01
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 2nd 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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 3rd 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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 4th 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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 5th 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.

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

This unfortunately needs a small rebase by now.

tnull
tnull previously approved these changes Sep 2, 2025

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

ACK, after rebase. Please excuse the considerable delay here.

I still think it would be good to document in an issue what's 'left' post this PR, at least where we want to take things directionally. Maybe we can just transfer #3876 (comment) to an issue that can serve as a base for discussions?

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.
`OnchainTxHandler` is an internal struct and doesn't implement
`Listen`, but its still nice to have its API mirror the `Listen`
API so that internal code all looks similar.
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.
`Listen` is somewhat quiet on high-level use and even requirements,
which we document further here.
@TheBlueMatt

TheBlueMatt commented Sep 2, 2025

Copy link
Copy Markdown
CollaboratorAuthor

Rebased with no further changes. Also opened #4042

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 1st Reminder

Hey @jkczyz! 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.

@jkczyz
jkczyz merged commit eb300b6 into lightningdevkit:mainSep 5, 2025
25 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.

4 participants

@TheBlueMatt@ldk-reviews-bot@tnull@jkczyz
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Drop the need for fork headers when calling Listen's disconnect by TheBlueMatt · Pull Request #3876 · lightningdevkit/rust-lightning · GitHub
Skip to content

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

Merged
jkczyz merged 5 commits into
lightningdevkit:mainfrom
TheBlueMatt:2025-06-disconnect-less-info
Sep 5, 2025
Merged

Drop the need for fork headers when calling Listen's disconnect#3876
jkczyz merged 5 commits into
lightningdevkit:mainfrom
TheBlueMatt:2025-06-disconnect-less-info

Conversation

@TheBlueMatt

Copy link
Copy Markdown
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

ldk-reviews-bot commented Jun 18, 2025

Copy link
Copy Markdown

👋 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.

Comment threadlightning/src/ln/channelmanager.rs Outdated
@ldk-reviews-bot

Copy link
Copy Markdown

👋 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
Copy Markdown

✅ Added second reviewer: @joostjager

@tnull
tnull requested review from tnull and removed request for joostjagerJune 19, 2025 16:11
@codecov

codecovBot commented Jun 19, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 79.36508% with 26 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.74%. Comparing base (86c6960) to head (a5b745a).
⚠️ Report is 38 commits behind head on main.

Files with missing linesPatch %Lines
lightning/src/chain/channelmonitor.rs70.00%6 Missing ⚠️
lightning/src/chain/mod.rs0.00%5 Missing ⚠️
lightning/src/util/sweep.rs0.00%5 Missing ⚠️
lightning-liquidity/src/manager.rs0.00%4 Missing ⚠️
lightning-block-sync/src/test_utils.rs66.66%3 Missing ⚠️
lightning-block-sync/src/init.rs90.00%1 Missing ⚠️
lightning/src/ln/channelmanager.rs95.83%1 Missing ⚠️
lightning/src/ln/functional_tests.rs66.66%1 Missing ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #3876 +/- ##
=======================================
Coverage 88.73% 88.74% =======================================
Files 176 176 Lines 129106 129102 -4 Branches 129106 129102 -4 =======================================
+ Hits 114567 114575 +8 + Misses 11937 11931 -6 + Partials 2602 2596 -6 
FlagCoverage Δ
fuzzing22.32% <13.18%> (+0.01%)⬆️
tests88.58% <79.36%> (+<0.01%)⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ 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.

@TheBlueMatt
TheBlueMattforce-pushed the 2025-06-disconnect-less-info branch from b06cbd4 to a54159cCompareJune 19, 2025 18:47
jkczyz
jkczyz previously approved these changes Jun 20, 2025
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 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.

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

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.

Comment threadlightning/src/chain/mod.rs
Comment threadlightning/src/util/sweep.rs Outdated
Comment threadlightning/src/chain/mod.rs Outdated
Comment threadlightning/src/chain/mod.rs Outdated
Comment threadlightning/src/chain/mod.rs Outdated
Comment threadlightning/src/chain/mod.rs Outdated
@TheBlueMatt
TheBlueMatt requested review from jkczyz and tnullJune 27, 2025 21:01
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 1st Reminder

Hey @tnull@jkczyz! 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.

1 similar comment
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 1st Reminder

Hey @tnull@jkczyz! 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.

Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs Outdated
///
/// Each block must be connected in chain order with one call to either
/// [`Listen::block_connected`] or [`Listen::filtered_block_connected`]. If a call to the
/// [`Filter`] interface was made during block processing and further transaction(s) from the same

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.

Hmm, is this a new requirement? I imagine it's very hard to reliably implement this: keep track of all registered Txids/outputs, then match them on a block, apply the block, then check again if something new was registered, filter the block again, then potentially call once more. Also, why do we require it only here, but not on Confirm?

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.

This has always been the case, just poorly documented. It is, indeed, not the case on Confirm (which doesn't require "blocks" to be connected in-order at all, though it does require all transactions to be topologically sorted).

}
self.chain_listener.block_disconnected(&header.header, header.height);
}
if let Some(block) = disconnected_blocks.last() {

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.

If we're now happy to use the fork point, why are we keeping a Vec of all disconnected blocks around at all? Shouldn't we now be able to convert this to the new behavior entirely? Seems ChainDifference::common_ancestor would already provide what we need, no?

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.

Its needed by the cache, which I didn't want to change in this PR - #3876 (comment)

Comment threadlightning/src/util/sweep.rs Outdated
for header in disconnected_blocks.drain(..) {
fn disconnect_blocks(&mut self, disconnected_blocks: Vec<ValidatedBlockHeader>) {
for header in disconnected_blocks.iter() {
if let Some(cached_header) = self.header_cache.block_disconnected(&header.block_hash) {

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 change the Cache API accordingly?

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.

We could, but it seems somewhat disjoint from this PR, given there's further work to do in lightning-block-sync anyway, so I kinda wanted to keep it as small as can be.

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.

Mhh, might be good to document somewhere what steps you deem left before you'd consider the transition to this new approach complete. I guess all of them should land before the next release then, to not end up with a half-migrated codebase in the release?

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.

I mean the API/stuff in lightning in in a perfectly fine state after this PR, and lightning-block-sync just deserves a bit of efficiency in dropping a vec and improving the cache afterwards. This PR obviously doesn't accomplish the goals that #3600 set out to, but is just a step towards it (the "complicated" step that requires making sure all the lightning-internal stuff isn't broken by changing the API).

AFAIU, to get to the point that bitcoind (and all) syncs support swapping to a new chain during a reorg and don't fetch a pile of redundant blocks we need to:

  • make BestBlock contain a list of 6(ish?) block hashes, not one
  • use that list in lightning-block-sync (and eventually lightning-transaction-sync) to do the sync without leaning on the cache
  • expand the runtime caching to cache partial chains across clients in the init sync logic
  • (eventually) ensure lightning-block-sync doesn't have any spare vecs or whatever lying around.

@tnulltnullJul 3, 2025

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 still think it would make sense to open an issue for this larger refactoring you're planning, as there are connected aspects to discuss that don't really belong in this PR.

make BestBlock contain a list of 6(ish?) block hashes, not one

For example, how do we imagine this to work exactly, especially for the Confirm/lightning-transaction-sync case?

For Esplora, we currently retrieve the tip hash, retrieve the header and the block status (including the hight), before we call best_block_updated, which is already 3 costly RTTs. If we now would require to track the 6 most-recent hashes, we'd need to make at least 5 more subsequent calls (bringing this it at least 8 RTTs per sync round) to retrieve the hashes at [(height-5)..(height-1)]. But, given that these are individual calls, there is no way to tell if any reorg happened during some of these calls, so they are inherently race-y.

For Electrum, the results would be very similar for the polling client in its current form. Note we eventually want to switch the client to a streaming version making use of Electum's subscription model though, and requiring 'last 6 blocks' would probably require us to resort to costly polling again.

If we'd otherwise extend the client to start tracking more of the chain state (i.e., actually tracking a local chain 6-suffix) across rounds this would complicate the client logic quite a bit and make it even more susceptible to race conditions.

TLDR: I still maintain all of this would be/is resulting in a major refactor across our chain syncing crates and logic, and it would be great to discuss what we imagine to be involved and the corresponding trade-offs before we just jump into it heads first.

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.

We can definitely make it optional (and only used for full-node-sync) if we think its a major issue. Of course the issue becomes that you cannot (even today) safely transition from an esplora/electrum-synced node to a full-chain-sync one, but addressing that really requires a substantial rework of how we think about chain sync, so maybe its not worth worrying about that.

Of course, storing the last 6 blocks is pretty trivial for a full-node-sync client, and the point is really just to expose the thing that the sync driver gave us, so its not like we'd use it in Listen/Confirm implementations.

My understanding is that this would address the restart-with-different-node case that #3600 wanted to address, even if its optional and just used by the full-node-syncing logic?

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.

but addressing that really requires a substantial rework of how we think about chain sync, so maybe its not worth worrying about that.

Hmm, given that we even recently had users inquire whether they could switch chain sources at runtime, I do expect that we will need to tackle this eventually, as there will be users wanting to switch chain sources, and it's very unintuitive to either prohibit that, have it one-way, or even document the risks that might come from it.

Of course, storing the last 6 blocks is pretty trivial for a full-node-sync client, and the point is really just to expose the thing that the sync driver gave us, so its not like we'd use it in Listen/Confirm implementations.

So we would only do it when it's convenient, which means we could switch bitcoind nodes, but still not between chain sources?

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.

Hmm, given that we even recently had users inquire whether they could switch chain sources at runtime, I do expect that we will need to tackle this eventually, as there will be users wanting to switch chain sources, and it's very unintuitive to either prohibit that, have it one-way, or even document the risks that might come from it.

Sure, I agree, hence why I suggested moving to storing several recent blocks rather than only one. However, as you note it might not be super practical, so we're kinda stuck.

I think, basically, if we did that plus required transactions_confirmed calls come before best_block_updated calls (which may we're doing anyway, cc #3867 (comment)) then moving from one chain source to another is totally fine, even at runtime (as long as you don't miss transactions).

So we would only do it when it's convenient, which means we could switch bitcoind nodes, but still not between chain sources?

I mean I'm not sure what else we can do? If its too impractical to do it on some chain sources we can't really make it required, and if its not available switching chain sources isn't going to be 100% reliable.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 2nd Reminder

Hey @jkczyz! 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.

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

rustfmt is unhappy right now

@TheBlueMatt
TheBlueMattforce-pushed the 2025-06-disconnect-less-info branch from ef73b0a to a6985ebCompareJuly 2, 2025 22:37
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 3rd Reminder

Hey @jkczyz! 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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 4th Reminder

Hey @jkczyz! 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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 5th Reminder

Hey @jkczyz! 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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 6th Reminder

Hey @jkczyz! 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.

@jkczyzjkczyz 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. Please squash.

@TheBlueMatt
TheBlueMattforce-pushed the 2025-06-disconnect-less-info branch from bf41b15 to c78b08bCompareAugust 7, 2025 20:29
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed without further changes.

jkczyz
jkczyz previously approved these changes Aug 7, 2025

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

Linting CI failures look unrelated.

@TheBlueMatt
TheBlueMattforce-pushed the 2025-06-disconnect-less-info branch from c78b08b to 7b590eeCompareAugust 21, 2025 14:57
@TheBlueMatt
TheBlueMatt requested review from tnull and removed request for tnullAugust 21, 2025 15:01
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 2nd 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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 3rd 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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 4th 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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 5th 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.

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

This unfortunately needs a small rebase by now.

tnull
tnull previously approved these changes Sep 2, 2025

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

ACK, after rebase. Please excuse the considerable delay here.

I still think it would be good to document in an issue what's 'left' post this PR, at least where we want to take things directionally. Maybe we can just transfer #3876 (comment) to an issue that can serve as a base for discussions?

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.
`OnchainTxHandler` is an internal struct and doesn't implement
`Listen`, but its still nice to have its API mirror the `Listen`
API so that internal code all looks similar.
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.
`Listen` is somewhat quiet on high-level use and even requirements,
which we document further here.
@TheBlueMatt

TheBlueMatt commented Sep 2, 2025

Copy link
Copy Markdown
CollaboratorAuthor

Rebased with no further changes. Also opened #4042

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 1st Reminder

Hey @jkczyz! 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.

@jkczyz
jkczyz merged commit eb300b6 into lightningdevkit:mainSep 5, 2025
25 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.

4 participants

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

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

Merged
jkczyz merged 5 commits into
lightningdevkit:mainfrom
TheBlueMatt:2025-06-disconnect-less-info
Sep 5, 2025
Merged

Drop the need for fork headers when calling Listen's disconnect#3876
jkczyz merged 5 commits into
lightningdevkit:mainfrom
TheBlueMatt:2025-06-disconnect-less-info

Conversation

@TheBlueMatt

Copy link
Copy Markdown
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

ldk-reviews-bot commented Jun 18, 2025

Copy link
Copy Markdown

👋 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.

Comment threadlightning/src/ln/channelmanager.rs Outdated
@ldk-reviews-bot

Copy link
Copy Markdown

👋 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
Copy Markdown

✅ Added second reviewer: @joostjager

@tnull
tnull requested review from tnull and removed request for joostjagerJune 19, 2025 16:11
@codecov

codecovBot commented Jun 19, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 79.36508% with 26 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.74%. Comparing base (86c6960) to head (a5b745a).
⚠️ Report is 38 commits behind head on main.

Files with missing linesPatch %Lines
lightning/src/chain/channelmonitor.rs70.00%6 Missing ⚠️
lightning/src/chain/mod.rs0.00%5 Missing ⚠️
lightning/src/util/sweep.rs0.00%5 Missing ⚠️
lightning-liquidity/src/manager.rs0.00%4 Missing ⚠️
lightning-block-sync/src/test_utils.rs66.66%3 Missing ⚠️
lightning-block-sync/src/init.rs90.00%1 Missing ⚠️
lightning/src/ln/channelmanager.rs95.83%1 Missing ⚠️
lightning/src/ln/functional_tests.rs66.66%1 Missing ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #3876 +/- ##
=======================================
Coverage 88.73% 88.74% =======================================
Files 176 176 Lines 129106 129102 -4 Branches 129106 129102 -4 =======================================
+ Hits 114567 114575 +8 + Misses 11937 11931 -6 + Partials 2602 2596 -6 
FlagCoverage Δ
fuzzing22.32% <13.18%> (+0.01%)⬆️
tests88.58% <79.36%> (+<0.01%)⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ 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.

@TheBlueMatt
TheBlueMattforce-pushed the 2025-06-disconnect-less-info branch from b06cbd4 to a54159cCompareJune 19, 2025 18:47
jkczyz
jkczyz previously approved these changes Jun 20, 2025
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 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.

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

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.

Comment threadlightning/src/chain/mod.rs
Comment threadlightning/src/util/sweep.rs Outdated
Comment threadlightning/src/chain/mod.rs Outdated
Comment threadlightning/src/chain/mod.rs Outdated
Comment threadlightning/src/chain/mod.rs Outdated
Comment threadlightning/src/chain/mod.rs Outdated
@TheBlueMatt
TheBlueMatt requested review from jkczyz and tnullJune 27, 2025 21:01
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 1st Reminder

Hey @tnull@jkczyz! 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.

1 similar comment
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 1st Reminder

Hey @tnull@jkczyz! 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.

Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs Outdated
///
/// Each block must be connected in chain order with one call to either
/// [`Listen::block_connected`] or [`Listen::filtered_block_connected`]. If a call to the
/// [`Filter`] interface was made during block processing and further transaction(s) from the same

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.

Hmm, is this a new requirement? I imagine it's very hard to reliably implement this: keep track of all registered Txids/outputs, then match them on a block, apply the block, then check again if something new was registered, filter the block again, then potentially call once more. Also, why do we require it only here, but not on Confirm?

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.

This has always been the case, just poorly documented. It is, indeed, not the case on Confirm (which doesn't require "blocks" to be connected in-order at all, though it does require all transactions to be topologically sorted).

}
self.chain_listener.block_disconnected(&header.header, header.height);
}
if let Some(block) = disconnected_blocks.last() {

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.

If we're now happy to use the fork point, why are we keeping a Vec of all disconnected blocks around at all? Shouldn't we now be able to convert this to the new behavior entirely? Seems ChainDifference::common_ancestor would already provide what we need, no?

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.

Its needed by the cache, which I didn't want to change in this PR - #3876 (comment)

Comment threadlightning/src/util/sweep.rs Outdated
for header in disconnected_blocks.drain(..) {
fn disconnect_blocks(&mut self, disconnected_blocks: Vec<ValidatedBlockHeader>) {
for header in disconnected_blocks.iter() {
if let Some(cached_header) = self.header_cache.block_disconnected(&header.block_hash) {

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 change the Cache API accordingly?

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.

We could, but it seems somewhat disjoint from this PR, given there's further work to do in lightning-block-sync anyway, so I kinda wanted to keep it as small as can be.

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.

Mhh, might be good to document somewhere what steps you deem left before you'd consider the transition to this new approach complete. I guess all of them should land before the next release then, to not end up with a half-migrated codebase in the release?

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.

I mean the API/stuff in lightning in in a perfectly fine state after this PR, and lightning-block-sync just deserves a bit of efficiency in dropping a vec and improving the cache afterwards. This PR obviously doesn't accomplish the goals that #3600 set out to, but is just a step towards it (the "complicated" step that requires making sure all the lightning-internal stuff isn't broken by changing the API).

AFAIU, to get to the point that bitcoind (and all) syncs support swapping to a new chain during a reorg and don't fetch a pile of redundant blocks we need to:

  • make BestBlock contain a list of 6(ish?) block hashes, not one
  • use that list in lightning-block-sync (and eventually lightning-transaction-sync) to do the sync without leaning on the cache
  • expand the runtime caching to cache partial chains across clients in the init sync logic
  • (eventually) ensure lightning-block-sync doesn't have any spare vecs or whatever lying around.

@tnulltnullJul 3, 2025

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 still think it would make sense to open an issue for this larger refactoring you're planning, as there are connected aspects to discuss that don't really belong in this PR.

make BestBlock contain a list of 6(ish?) block hashes, not one

For example, how do we imagine this to work exactly, especially for the Confirm/lightning-transaction-sync case?

For Esplora, we currently retrieve the tip hash, retrieve the header and the block status (including the hight), before we call best_block_updated, which is already 3 costly RTTs. If we now would require to track the 6 most-recent hashes, we'd need to make at least 5 more subsequent calls (bringing this it at least 8 RTTs per sync round) to retrieve the hashes at [(height-5)..(height-1)]. But, given that these are individual calls, there is no way to tell if any reorg happened during some of these calls, so they are inherently race-y.

For Electrum, the results would be very similar for the polling client in its current form. Note we eventually want to switch the client to a streaming version making use of Electum's subscription model though, and requiring 'last 6 blocks' would probably require us to resort to costly polling again.

If we'd otherwise extend the client to start tracking more of the chain state (i.e., actually tracking a local chain 6-suffix) across rounds this would complicate the client logic quite a bit and make it even more susceptible to race conditions.

TLDR: I still maintain all of this would be/is resulting in a major refactor across our chain syncing crates and logic, and it would be great to discuss what we imagine to be involved and the corresponding trade-offs before we just jump into it heads first.

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.

We can definitely make it optional (and only used for full-node-sync) if we think its a major issue. Of course the issue becomes that you cannot (even today) safely transition from an esplora/electrum-synced node to a full-chain-sync one, but addressing that really requires a substantial rework of how we think about chain sync, so maybe its not worth worrying about that.

Of course, storing the last 6 blocks is pretty trivial for a full-node-sync client, and the point is really just to expose the thing that the sync driver gave us, so its not like we'd use it in Listen/Confirm implementations.

My understanding is that this would address the restart-with-different-node case that #3600 wanted to address, even if its optional and just used by the full-node-syncing logic?

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.

but addressing that really requires a substantial rework of how we think about chain sync, so maybe its not worth worrying about that.

Hmm, given that we even recently had users inquire whether they could switch chain sources at runtime, I do expect that we will need to tackle this eventually, as there will be users wanting to switch chain sources, and it's very unintuitive to either prohibit that, have it one-way, or even document the risks that might come from it.

Of course, storing the last 6 blocks is pretty trivial for a full-node-sync client, and the point is really just to expose the thing that the sync driver gave us, so its not like we'd use it in Listen/Confirm implementations.

So we would only do it when it's convenient, which means we could switch bitcoind nodes, but still not between chain sources?

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.

Hmm, given that we even recently had users inquire whether they could switch chain sources at runtime, I do expect that we will need to tackle this eventually, as there will be users wanting to switch chain sources, and it's very unintuitive to either prohibit that, have it one-way, or even document the risks that might come from it.

Sure, I agree, hence why I suggested moving to storing several recent blocks rather than only one. However, as you note it might not be super practical, so we're kinda stuck.

I think, basically, if we did that plus required transactions_confirmed calls come before best_block_updated calls (which may we're doing anyway, cc #3867 (comment)) then moving from one chain source to another is totally fine, even at runtime (as long as you don't miss transactions).

So we would only do it when it's convenient, which means we could switch bitcoind nodes, but still not between chain sources?

I mean I'm not sure what else we can do? If its too impractical to do it on some chain sources we can't really make it required, and if its not available switching chain sources isn't going to be 100% reliable.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 2nd Reminder

Hey @jkczyz! 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.

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

rustfmt is unhappy right now

@TheBlueMatt
TheBlueMattforce-pushed the 2025-06-disconnect-less-info branch from ef73b0a to a6985ebCompareJuly 2, 2025 22:37
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 3rd Reminder

Hey @jkczyz! 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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 4th Reminder

Hey @jkczyz! 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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 5th Reminder

Hey @jkczyz! 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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 6th Reminder

Hey @jkczyz! 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.

@jkczyzjkczyz 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. Please squash.

@TheBlueMatt
TheBlueMattforce-pushed the 2025-06-disconnect-less-info branch from bf41b15 to c78b08bCompareAugust 7, 2025 20:29
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed without further changes.

jkczyz
jkczyz previously approved these changes Aug 7, 2025

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

Linting CI failures look unrelated.

@TheBlueMatt
TheBlueMattforce-pushed the 2025-06-disconnect-less-info branch from c78b08b to 7b590eeCompareAugust 21, 2025 14:57
@TheBlueMatt
TheBlueMatt requested review from tnull and removed request for tnullAugust 21, 2025 15:01
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 2nd 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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 3rd 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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 4th 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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 5th 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.

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

This unfortunately needs a small rebase by now.

tnull
tnull previously approved these changes Sep 2, 2025

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

ACK, after rebase. Please excuse the considerable delay here.

I still think it would be good to document in an issue what's 'left' post this PR, at least where we want to take things directionally. Maybe we can just transfer #3876 (comment) to an issue that can serve as a base for discussions?

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.
`OnchainTxHandler` is an internal struct and doesn't implement
`Listen`, but its still nice to have its API mirror the `Listen`
API so that internal code all looks similar.
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.
`Listen` is somewhat quiet on high-level use and even requirements,
which we document further here.
@TheBlueMatt

TheBlueMatt commented Sep 2, 2025

Copy link
Copy Markdown
CollaboratorAuthor

Rebased with no further changes. Also opened #4042

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 1st Reminder

Hey @jkczyz! 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.

@jkczyz
jkczyz merged commit eb300b6 into lightningdevkit:mainSep 5, 2025
25 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.

4 participants

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

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

Merged
jkczyz merged 5 commits into
lightningdevkit:mainfrom
TheBlueMatt:2025-06-disconnect-less-info
Sep 5, 2025
Merged

Drop the need for fork headers when calling Listen's disconnect#3876
jkczyz merged 5 commits into
lightningdevkit:mainfrom
TheBlueMatt:2025-06-disconnect-less-info

Conversation

@TheBlueMatt

Copy link
Copy Markdown
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

ldk-reviews-bot commented Jun 18, 2025

Copy link
Copy Markdown

👋 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.

Comment threadlightning/src/ln/channelmanager.rs Outdated
@ldk-reviews-bot

Copy link
Copy Markdown

👋 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
Copy Markdown

✅ Added second reviewer: @joostjager

@tnull
tnull requested review from tnull and removed request for joostjagerJune 19, 2025 16:11
@codecov

codecovBot commented Jun 19, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 79.36508% with 26 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.74%. Comparing base (86c6960) to head (a5b745a).
⚠️ Report is 38 commits behind head on main.

Files with missing linesPatch %Lines
lightning/src/chain/channelmonitor.rs70.00%6 Missing ⚠️
lightning/src/chain/mod.rs0.00%5 Missing ⚠️
lightning/src/util/sweep.rs0.00%5 Missing ⚠️
lightning-liquidity/src/manager.rs0.00%4 Missing ⚠️
lightning-block-sync/src/test_utils.rs66.66%3 Missing ⚠️
lightning-block-sync/src/init.rs90.00%1 Missing ⚠️
lightning/src/ln/channelmanager.rs95.83%1 Missing ⚠️
lightning/src/ln/functional_tests.rs66.66%1 Missing ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #3876 +/- ##
=======================================
Coverage 88.73% 88.74% =======================================
Files 176 176 Lines 129106 129102 -4 Branches 129106 129102 -4 =======================================
+ Hits 114567 114575 +8 + Misses 11937 11931 -6 + Partials 2602 2596 -6 
FlagCoverage Δ
fuzzing22.32% <13.18%> (+0.01%)⬆️
tests88.58% <79.36%> (+<0.01%)⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ 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.

@TheBlueMatt
TheBlueMattforce-pushed the 2025-06-disconnect-less-info branch from b06cbd4 to a54159cCompareJune 19, 2025 18:47
jkczyz
jkczyz previously approved these changes Jun 20, 2025
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 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.

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

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.

Comment threadlightning/src/chain/mod.rs
Comment threadlightning/src/util/sweep.rs Outdated
Comment threadlightning/src/chain/mod.rs Outdated
Comment threadlightning/src/chain/mod.rs Outdated
Comment threadlightning/src/chain/mod.rs Outdated
Comment threadlightning/src/chain/mod.rs Outdated
@TheBlueMatt
TheBlueMatt requested review from jkczyz and tnullJune 27, 2025 21:01
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 1st Reminder

Hey @tnull@jkczyz! 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.

1 similar comment
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 1st Reminder

Hey @tnull@jkczyz! 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.

Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs Outdated
///
/// Each block must be connected in chain order with one call to either
/// [`Listen::block_connected`] or [`Listen::filtered_block_connected`]. If a call to the
/// [`Filter`] interface was made during block processing and further transaction(s) from the same

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.

Hmm, is this a new requirement? I imagine it's very hard to reliably implement this: keep track of all registered Txids/outputs, then match them on a block, apply the block, then check again if something new was registered, filter the block again, then potentially call once more. Also, why do we require it only here, but not on Confirm?

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.

This has always been the case, just poorly documented. It is, indeed, not the case on Confirm (which doesn't require "blocks" to be connected in-order at all, though it does require all transactions to be topologically sorted).

}
self.chain_listener.block_disconnected(&header.header, header.height);
}
if let Some(block) = disconnected_blocks.last() {

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.

If we're now happy to use the fork point, why are we keeping a Vec of all disconnected blocks around at all? Shouldn't we now be able to convert this to the new behavior entirely? Seems ChainDifference::common_ancestor would already provide what we need, no?

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.

Its needed by the cache, which I didn't want to change in this PR - #3876 (comment)

Comment threadlightning/src/util/sweep.rs Outdated
for header in disconnected_blocks.drain(..) {
fn disconnect_blocks(&mut self, disconnected_blocks: Vec<ValidatedBlockHeader>) {
for header in disconnected_blocks.iter() {
if let Some(cached_header) = self.header_cache.block_disconnected(&header.block_hash) {

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 change the Cache API accordingly?

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.

We could, but it seems somewhat disjoint from this PR, given there's further work to do in lightning-block-sync anyway, so I kinda wanted to keep it as small as can be.

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.

Mhh, might be good to document somewhere what steps you deem left before you'd consider the transition to this new approach complete. I guess all of them should land before the next release then, to not end up with a half-migrated codebase in the release?

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.

I mean the API/stuff in lightning in in a perfectly fine state after this PR, and lightning-block-sync just deserves a bit of efficiency in dropping a vec and improving the cache afterwards. This PR obviously doesn't accomplish the goals that #3600 set out to, but is just a step towards it (the "complicated" step that requires making sure all the lightning-internal stuff isn't broken by changing the API).

AFAIU, to get to the point that bitcoind (and all) syncs support swapping to a new chain during a reorg and don't fetch a pile of redundant blocks we need to:

  • make BestBlock contain a list of 6(ish?) block hashes, not one
  • use that list in lightning-block-sync (and eventually lightning-transaction-sync) to do the sync without leaning on the cache
  • expand the runtime caching to cache partial chains across clients in the init sync logic
  • (eventually) ensure lightning-block-sync doesn't have any spare vecs or whatever lying around.

@tnulltnullJul 3, 2025

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 still think it would make sense to open an issue for this larger refactoring you're planning, as there are connected aspects to discuss that don't really belong in this PR.

make BestBlock contain a list of 6(ish?) block hashes, not one

For example, how do we imagine this to work exactly, especially for the Confirm/lightning-transaction-sync case?

For Esplora, we currently retrieve the tip hash, retrieve the header and the block status (including the hight), before we call best_block_updated, which is already 3 costly RTTs. If we now would require to track the 6 most-recent hashes, we'd need to make at least 5 more subsequent calls (bringing this it at least 8 RTTs per sync round) to retrieve the hashes at [(height-5)..(height-1)]. But, given that these are individual calls, there is no way to tell if any reorg happened during some of these calls, so they are inherently race-y.

For Electrum, the results would be very similar for the polling client in its current form. Note we eventually want to switch the client to a streaming version making use of Electum's subscription model though, and requiring 'last 6 blocks' would probably require us to resort to costly polling again.

If we'd otherwise extend the client to start tracking more of the chain state (i.e., actually tracking a local chain 6-suffix) across rounds this would complicate the client logic quite a bit and make it even more susceptible to race conditions.

TLDR: I still maintain all of this would be/is resulting in a major refactor across our chain syncing crates and logic, and it would be great to discuss what we imagine to be involved and the corresponding trade-offs before we just jump into it heads first.

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.

We can definitely make it optional (and only used for full-node-sync) if we think its a major issue. Of course the issue becomes that you cannot (even today) safely transition from an esplora/electrum-synced node to a full-chain-sync one, but addressing that really requires a substantial rework of how we think about chain sync, so maybe its not worth worrying about that.

Of course, storing the last 6 blocks is pretty trivial for a full-node-sync client, and the point is really just to expose the thing that the sync driver gave us, so its not like we'd use it in Listen/Confirm implementations.

My understanding is that this would address the restart-with-different-node case that #3600 wanted to address, even if its optional and just used by the full-node-syncing logic?

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.

but addressing that really requires a substantial rework of how we think about chain sync, so maybe its not worth worrying about that.

Hmm, given that we even recently had users inquire whether they could switch chain sources at runtime, I do expect that we will need to tackle this eventually, as there will be users wanting to switch chain sources, and it's very unintuitive to either prohibit that, have it one-way, or even document the risks that might come from it.

Of course, storing the last 6 blocks is pretty trivial for a full-node-sync client, and the point is really just to expose the thing that the sync driver gave us, so its not like we'd use it in Listen/Confirm implementations.

So we would only do it when it's convenient, which means we could switch bitcoind nodes, but still not between chain sources?

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.

Hmm, given that we even recently had users inquire whether they could switch chain sources at runtime, I do expect that we will need to tackle this eventually, as there will be users wanting to switch chain sources, and it's very unintuitive to either prohibit that, have it one-way, or even document the risks that might come from it.

Sure, I agree, hence why I suggested moving to storing several recent blocks rather than only one. However, as you note it might not be super practical, so we're kinda stuck.

I think, basically, if we did that plus required transactions_confirmed calls come before best_block_updated calls (which may we're doing anyway, cc #3867 (comment)) then moving from one chain source to another is totally fine, even at runtime (as long as you don't miss transactions).

So we would only do it when it's convenient, which means we could switch bitcoind nodes, but still not between chain sources?

I mean I'm not sure what else we can do? If its too impractical to do it on some chain sources we can't really make it required, and if its not available switching chain sources isn't going to be 100% reliable.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 2nd Reminder

Hey @jkczyz! 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.

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

rustfmt is unhappy right now

@TheBlueMatt
TheBlueMattforce-pushed the 2025-06-disconnect-less-info branch from ef73b0a to a6985ebCompareJuly 2, 2025 22:37
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 3rd Reminder

Hey @jkczyz! 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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 4th Reminder

Hey @jkczyz! 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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 5th Reminder

Hey @jkczyz! 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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 6th Reminder

Hey @jkczyz! 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.

@jkczyzjkczyz 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. Please squash.

@TheBlueMatt
TheBlueMattforce-pushed the 2025-06-disconnect-less-info branch from bf41b15 to c78b08bCompareAugust 7, 2025 20:29
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed without further changes.

jkczyz
jkczyz previously approved these changes Aug 7, 2025

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

Linting CI failures look unrelated.

@TheBlueMatt
TheBlueMattforce-pushed the 2025-06-disconnect-less-info branch from c78b08b to 7b590eeCompareAugust 21, 2025 14:57
@TheBlueMatt
TheBlueMatt requested review from tnull and removed request for tnullAugust 21, 2025 15:01
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 2nd 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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 3rd 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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 4th 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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 5th 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.

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

This unfortunately needs a small rebase by now.

tnull
tnull previously approved these changes Sep 2, 2025

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

ACK, after rebase. Please excuse the considerable delay here.

I still think it would be good to document in an issue what's 'left' post this PR, at least where we want to take things directionally. Maybe we can just transfer #3876 (comment) to an issue that can serve as a base for discussions?

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.
`OnchainTxHandler` is an internal struct and doesn't implement
`Listen`, but its still nice to have its API mirror the `Listen`
API so that internal code all looks similar.
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.
`Listen` is somewhat quiet on high-level use and even requirements,
which we document further here.
@TheBlueMatt

TheBlueMatt commented Sep 2, 2025

Copy link
Copy Markdown
CollaboratorAuthor

Rebased with no further changes. Also opened #4042

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 1st Reminder

Hey @jkczyz! 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.

@jkczyz
jkczyz merged commit eb300b6 into lightningdevkit:mainSep 5, 2025
25 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.

4 participants

@TheBlueMatt@ldk-reviews-bot@tnull@jkczyz
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' Drop the need for fork headers when calling Listen's disconnect by TheBlueMatt · Pull Request #3876 · lightningdevkit/rust-lightning · GitHub
Skip to content

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

Merged
jkczyz merged 5 commits into
lightningdevkit:mainfrom
TheBlueMatt:2025-06-disconnect-less-info
Sep 5, 2025
Merged

Drop the need for fork headers when calling Listen's disconnect#3876
jkczyz merged 5 commits into
lightningdevkit:mainfrom
TheBlueMatt:2025-06-disconnect-less-info

Conversation

@TheBlueMatt

Copy link
Copy Markdown
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

ldk-reviews-bot commented Jun 18, 2025

Copy link
Copy Markdown

👋 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.

Comment threadlightning/src/ln/channelmanager.rs Outdated
@ldk-reviews-bot

Copy link
Copy Markdown

👋 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
Copy Markdown

✅ Added second reviewer: @joostjager

@tnull
tnull requested review from tnull and removed request for joostjagerJune 19, 2025 16:11
@codecov

codecovBot commented Jun 19, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 79.36508% with 26 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.74%. Comparing base (86c6960) to head (a5b745a).
⚠️ Report is 38 commits behind head on main.

Files with missing linesPatch %Lines
lightning/src/chain/channelmonitor.rs70.00%6 Missing ⚠️
lightning/src/chain/mod.rs0.00%5 Missing ⚠️
lightning/src/util/sweep.rs0.00%5 Missing ⚠️
lightning-liquidity/src/manager.rs0.00%4 Missing ⚠️
lightning-block-sync/src/test_utils.rs66.66%3 Missing ⚠️
lightning-block-sync/src/init.rs90.00%1 Missing ⚠️
lightning/src/ln/channelmanager.rs95.83%1 Missing ⚠️
lightning/src/ln/functional_tests.rs66.66%1 Missing ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #3876 +/- ##
=======================================
Coverage 88.73% 88.74% =======================================
Files 176 176 Lines 129106 129102 -4 Branches 129106 129102 -4 =======================================
+ Hits 114567 114575 +8 + Misses 11937 11931 -6 + Partials 2602 2596 -6 
FlagCoverage Δ
fuzzing22.32% <13.18%> (+0.01%)⬆️
tests88.58% <79.36%> (+<0.01%)⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ 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.

@TheBlueMatt
TheBlueMattforce-pushed the 2025-06-disconnect-less-info branch from b06cbd4 to a54159cCompareJune 19, 2025 18:47
jkczyz
jkczyz previously approved these changes Jun 20, 2025
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 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.

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

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.

Comment threadlightning/src/chain/mod.rs
Comment threadlightning/src/util/sweep.rs Outdated
Comment threadlightning/src/chain/mod.rs Outdated
Comment threadlightning/src/chain/mod.rs Outdated
Comment threadlightning/src/chain/mod.rs Outdated
Comment threadlightning/src/chain/mod.rs Outdated
@TheBlueMatt
TheBlueMatt requested review from jkczyz and tnullJune 27, 2025 21:01
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 1st Reminder

Hey @tnull@jkczyz! 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.

1 similar comment
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 1st Reminder

Hey @tnull@jkczyz! 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.

Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs Outdated
///
/// Each block must be connected in chain order with one call to either
/// [`Listen::block_connected`] or [`Listen::filtered_block_connected`]. If a call to the
/// [`Filter`] interface was made during block processing and further transaction(s) from the same

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.

Hmm, is this a new requirement? I imagine it's very hard to reliably implement this: keep track of all registered Txids/outputs, then match them on a block, apply the block, then check again if something new was registered, filter the block again, then potentially call once more. Also, why do we require it only here, but not on Confirm?

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.

This has always been the case, just poorly documented. It is, indeed, not the case on Confirm (which doesn't require "blocks" to be connected in-order at all, though it does require all transactions to be topologically sorted).

}
self.chain_listener.block_disconnected(&header.header, header.height);
}
if let Some(block) = disconnected_blocks.last() {

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.

If we're now happy to use the fork point, why are we keeping a Vec of all disconnected blocks around at all? Shouldn't we now be able to convert this to the new behavior entirely? Seems ChainDifference::common_ancestor would already provide what we need, no?

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.

Its needed by the cache, which I didn't want to change in this PR - #3876 (comment)

Comment threadlightning/src/util/sweep.rs Outdated
for header in disconnected_blocks.drain(..) {
fn disconnect_blocks(&mut self, disconnected_blocks: Vec<ValidatedBlockHeader>) {
for header in disconnected_blocks.iter() {
if let Some(cached_header) = self.header_cache.block_disconnected(&header.block_hash) {

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 change the Cache API accordingly?

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.

We could, but it seems somewhat disjoint from this PR, given there's further work to do in lightning-block-sync anyway, so I kinda wanted to keep it as small as can be.

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.

Mhh, might be good to document somewhere what steps you deem left before you'd consider the transition to this new approach complete. I guess all of them should land before the next release then, to not end up with a half-migrated codebase in the release?

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.

I mean the API/stuff in lightning in in a perfectly fine state after this PR, and lightning-block-sync just deserves a bit of efficiency in dropping a vec and improving the cache afterwards. This PR obviously doesn't accomplish the goals that #3600 set out to, but is just a step towards it (the "complicated" step that requires making sure all the lightning-internal stuff isn't broken by changing the API).

AFAIU, to get to the point that bitcoind (and all) syncs support swapping to a new chain during a reorg and don't fetch a pile of redundant blocks we need to:

  • make BestBlock contain a list of 6(ish?) block hashes, not one
  • use that list in lightning-block-sync (and eventually lightning-transaction-sync) to do the sync without leaning on the cache
  • expand the runtime caching to cache partial chains across clients in the init sync logic
  • (eventually) ensure lightning-block-sync doesn't have any spare vecs or whatever lying around.

@tnulltnullJul 3, 2025

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 still think it would make sense to open an issue for this larger refactoring you're planning, as there are connected aspects to discuss that don't really belong in this PR.

make BestBlock contain a list of 6(ish?) block hashes, not one

For example, how do we imagine this to work exactly, especially for the Confirm/lightning-transaction-sync case?

For Esplora, we currently retrieve the tip hash, retrieve the header and the block status (including the hight), before we call best_block_updated, which is already 3 costly RTTs. If we now would require to track the 6 most-recent hashes, we'd need to make at least 5 more subsequent calls (bringing this it at least 8 RTTs per sync round) to retrieve the hashes at [(height-5)..(height-1)]. But, given that these are individual calls, there is no way to tell if any reorg happened during some of these calls, so they are inherently race-y.

For Electrum, the results would be very similar for the polling client in its current form. Note we eventually want to switch the client to a streaming version making use of Electum's subscription model though, and requiring 'last 6 blocks' would probably require us to resort to costly polling again.

If we'd otherwise extend the client to start tracking more of the chain state (i.e., actually tracking a local chain 6-suffix) across rounds this would complicate the client logic quite a bit and make it even more susceptible to race conditions.

TLDR: I still maintain all of this would be/is resulting in a major refactor across our chain syncing crates and logic, and it would be great to discuss what we imagine to be involved and the corresponding trade-offs before we just jump into it heads first.

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.

We can definitely make it optional (and only used for full-node-sync) if we think its a major issue. Of course the issue becomes that you cannot (even today) safely transition from an esplora/electrum-synced node to a full-chain-sync one, but addressing that really requires a substantial rework of how we think about chain sync, so maybe its not worth worrying about that.

Of course, storing the last 6 blocks is pretty trivial for a full-node-sync client, and the point is really just to expose the thing that the sync driver gave us, so its not like we'd use it in Listen/Confirm implementations.

My understanding is that this would address the restart-with-different-node case that #3600 wanted to address, even if its optional and just used by the full-node-syncing logic?

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.

but addressing that really requires a substantial rework of how we think about chain sync, so maybe its not worth worrying about that.

Hmm, given that we even recently had users inquire whether they could switch chain sources at runtime, I do expect that we will need to tackle this eventually, as there will be users wanting to switch chain sources, and it's very unintuitive to either prohibit that, have it one-way, or even document the risks that might come from it.

Of course, storing the last 6 blocks is pretty trivial for a full-node-sync client, and the point is really just to expose the thing that the sync driver gave us, so its not like we'd use it in Listen/Confirm implementations.

So we would only do it when it's convenient, which means we could switch bitcoind nodes, but still not between chain sources?

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.

Hmm, given that we even recently had users inquire whether they could switch chain sources at runtime, I do expect that we will need to tackle this eventually, as there will be users wanting to switch chain sources, and it's very unintuitive to either prohibit that, have it one-way, or even document the risks that might come from it.

Sure, I agree, hence why I suggested moving to storing several recent blocks rather than only one. However, as you note it might not be super practical, so we're kinda stuck.

I think, basically, if we did that plus required transactions_confirmed calls come before best_block_updated calls (which may we're doing anyway, cc #3867 (comment)) then moving from one chain source to another is totally fine, even at runtime (as long as you don't miss transactions).

So we would only do it when it's convenient, which means we could switch bitcoind nodes, but still not between chain sources?

I mean I'm not sure what else we can do? If its too impractical to do it on some chain sources we can't really make it required, and if its not available switching chain sources isn't going to be 100% reliable.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 2nd Reminder

Hey @jkczyz! 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.

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

rustfmt is unhappy right now

@TheBlueMatt
TheBlueMattforce-pushed the 2025-06-disconnect-less-info branch from ef73b0a to a6985ebCompareJuly 2, 2025 22:37
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 3rd Reminder

Hey @jkczyz! 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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 4th Reminder

Hey @jkczyz! 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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 5th Reminder

Hey @jkczyz! 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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 6th Reminder

Hey @jkczyz! 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.

@jkczyzjkczyz 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. Please squash.

@TheBlueMatt
TheBlueMattforce-pushed the 2025-06-disconnect-less-info branch from bf41b15 to c78b08bCompareAugust 7, 2025 20:29
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed without further changes.

jkczyz
jkczyz previously approved these changes Aug 7, 2025

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

Linting CI failures look unrelated.

@TheBlueMatt
TheBlueMattforce-pushed the 2025-06-disconnect-less-info branch from c78b08b to 7b590eeCompareAugust 21, 2025 14:57
@TheBlueMatt
TheBlueMatt requested review from tnull and removed request for tnullAugust 21, 2025 15:01
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 2nd 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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 3rd 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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 4th 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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 5th 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.

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

This unfortunately needs a small rebase by now.

tnull
tnull previously approved these changes Sep 2, 2025

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

ACK, after rebase. Please excuse the considerable delay here.

I still think it would be good to document in an issue what's 'left' post this PR, at least where we want to take things directionally. Maybe we can just transfer #3876 (comment) to an issue that can serve as a base for discussions?

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.
`OnchainTxHandler` is an internal struct and doesn't implement
`Listen`, but its still nice to have its API mirror the `Listen`
API so that internal code all looks similar.
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.
`Listen` is somewhat quiet on high-level use and even requirements,
which we document further here.
@TheBlueMatt

TheBlueMatt commented Sep 2, 2025

Copy link
Copy Markdown
CollaboratorAuthor

Rebased with no further changes. Also opened #4042

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 1st Reminder

Hey @jkczyz! 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.

@jkczyz
jkczyz merged commit eb300b6 into lightningdevkit:mainSep 5, 2025
25 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.

4 participants

@TheBlueMatt@ldk-reviews-bot@tnull@jkczyz
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); Drop the need for fork headers when calling Listen's disconnect by TheBlueMatt · Pull Request #3876 · lightningdevkit/rust-lightning · GitHub
Skip to content

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

Merged
jkczyz merged 5 commits into
lightningdevkit:mainfrom
TheBlueMatt:2025-06-disconnect-less-info
Sep 5, 2025
Merged

Drop the need for fork headers when calling Listen's disconnect#3876
jkczyz merged 5 commits into
lightningdevkit:mainfrom
TheBlueMatt:2025-06-disconnect-less-info

Conversation

@TheBlueMatt

Copy link
Copy Markdown
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

ldk-reviews-bot commented Jun 18, 2025

Copy link
Copy Markdown

👋 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.

Comment threadlightning/src/ln/channelmanager.rs Outdated
@ldk-reviews-bot

Copy link
Copy Markdown

👋 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
Copy Markdown

✅ Added second reviewer: @joostjager

@tnull
tnull requested review from tnull and removed request for joostjagerJune 19, 2025 16:11
@codecov

codecovBot commented Jun 19, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 79.36508% with 26 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.74%. Comparing base (86c6960) to head (a5b745a).
⚠️ Report is 38 commits behind head on main.

Files with missing linesPatch %Lines
lightning/src/chain/channelmonitor.rs70.00%6 Missing ⚠️
lightning/src/chain/mod.rs0.00%5 Missing ⚠️
lightning/src/util/sweep.rs0.00%5 Missing ⚠️
lightning-liquidity/src/manager.rs0.00%4 Missing ⚠️
lightning-block-sync/src/test_utils.rs66.66%3 Missing ⚠️
lightning-block-sync/src/init.rs90.00%1 Missing ⚠️
lightning/src/ln/channelmanager.rs95.83%1 Missing ⚠️
lightning/src/ln/functional_tests.rs66.66%1 Missing ⚠️
Additional details and impacted files
@@ Coverage Diff @@## main #3876 +/- ##
=======================================
Coverage 88.73% 88.74% =======================================
Files 176 176 Lines 129106 129102 -4 Branches 129106 129102 -4 =======================================
+ Hits 114567 114575 +8 + Misses 11937 11931 -6 + Partials 2602 2596 -6 
FlagCoverage Δ
fuzzing22.32% <13.18%> (+0.01%)⬆️
tests88.58% <79.36%> (+<0.01%)⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ 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.

@TheBlueMatt
TheBlueMattforce-pushed the 2025-06-disconnect-less-info branch from b06cbd4 to a54159cCompareJune 19, 2025 18:47
jkczyz
jkczyz previously approved these changes Jun 20, 2025
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 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.

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

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.

Comment threadlightning/src/chain/mod.rs
Comment threadlightning/src/util/sweep.rs Outdated
Comment threadlightning/src/chain/mod.rs Outdated
Comment threadlightning/src/chain/mod.rs Outdated
Comment threadlightning/src/chain/mod.rs Outdated
Comment threadlightning/src/chain/mod.rs Outdated
@TheBlueMatt
TheBlueMatt requested review from jkczyz and tnullJune 27, 2025 21:01
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 1st Reminder

Hey @tnull@jkczyz! 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.

1 similar comment
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 1st Reminder

Hey @tnull@jkczyz! 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.

Comment threadlightning/src/chain/channelmonitor.rs Outdated
Comment threadlightning/src/chain/channelmonitor.rs Outdated
///
/// Each block must be connected in chain order with one call to either
/// [`Listen::block_connected`] or [`Listen::filtered_block_connected`]. If a call to the
/// [`Filter`] interface was made during block processing and further transaction(s) from the same

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.

Hmm, is this a new requirement? I imagine it's very hard to reliably implement this: keep track of all registered Txids/outputs, then match them on a block, apply the block, then check again if something new was registered, filter the block again, then potentially call once more. Also, why do we require it only here, but not on Confirm?

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.

This has always been the case, just poorly documented. It is, indeed, not the case on Confirm (which doesn't require "blocks" to be connected in-order at all, though it does require all transactions to be topologically sorted).

}
self.chain_listener.block_disconnected(&header.header, header.height);
}
if let Some(block) = disconnected_blocks.last() {

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.

If we're now happy to use the fork point, why are we keeping a Vec of all disconnected blocks around at all? Shouldn't we now be able to convert this to the new behavior entirely? Seems ChainDifference::common_ancestor would already provide what we need, no?

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.

Its needed by the cache, which I didn't want to change in this PR - #3876 (comment)

Comment threadlightning/src/util/sweep.rs Outdated
for header in disconnected_blocks.drain(..) {
fn disconnect_blocks(&mut self, disconnected_blocks: Vec<ValidatedBlockHeader>) {
for header in disconnected_blocks.iter() {
if let Some(cached_header) = self.header_cache.block_disconnected(&header.block_hash) {

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 change the Cache API accordingly?

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.

We could, but it seems somewhat disjoint from this PR, given there's further work to do in lightning-block-sync anyway, so I kinda wanted to keep it as small as can be.

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.

Mhh, might be good to document somewhere what steps you deem left before you'd consider the transition to this new approach complete. I guess all of them should land before the next release then, to not end up with a half-migrated codebase in the release?

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.

I mean the API/stuff in lightning in in a perfectly fine state after this PR, and lightning-block-sync just deserves a bit of efficiency in dropping a vec and improving the cache afterwards. This PR obviously doesn't accomplish the goals that #3600 set out to, but is just a step towards it (the "complicated" step that requires making sure all the lightning-internal stuff isn't broken by changing the API).

AFAIU, to get to the point that bitcoind (and all) syncs support swapping to a new chain during a reorg and don't fetch a pile of redundant blocks we need to:

  • make BestBlock contain a list of 6(ish?) block hashes, not one
  • use that list in lightning-block-sync (and eventually lightning-transaction-sync) to do the sync without leaning on the cache
  • expand the runtime caching to cache partial chains across clients in the init sync logic
  • (eventually) ensure lightning-block-sync doesn't have any spare vecs or whatever lying around.

@tnulltnullJul 3, 2025

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 still think it would make sense to open an issue for this larger refactoring you're planning, as there are connected aspects to discuss that don't really belong in this PR.

make BestBlock contain a list of 6(ish?) block hashes, not one

For example, how do we imagine this to work exactly, especially for the Confirm/lightning-transaction-sync case?

For Esplora, we currently retrieve the tip hash, retrieve the header and the block status (including the hight), before we call best_block_updated, which is already 3 costly RTTs. If we now would require to track the 6 most-recent hashes, we'd need to make at least 5 more subsequent calls (bringing this it at least 8 RTTs per sync round) to retrieve the hashes at [(height-5)..(height-1)]. But, given that these are individual calls, there is no way to tell if any reorg happened during some of these calls, so they are inherently race-y.

For Electrum, the results would be very similar for the polling client in its current form. Note we eventually want to switch the client to a streaming version making use of Electum's subscription model though, and requiring 'last 6 blocks' would probably require us to resort to costly polling again.

If we'd otherwise extend the client to start tracking more of the chain state (i.e., actually tracking a local chain 6-suffix) across rounds this would complicate the client logic quite a bit and make it even more susceptible to race conditions.

TLDR: I still maintain all of this would be/is resulting in a major refactor across our chain syncing crates and logic, and it would be great to discuss what we imagine to be involved and the corresponding trade-offs before we just jump into it heads first.

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.

We can definitely make it optional (and only used for full-node-sync) if we think its a major issue. Of course the issue becomes that you cannot (even today) safely transition from an esplora/electrum-synced node to a full-chain-sync one, but addressing that really requires a substantial rework of how we think about chain sync, so maybe its not worth worrying about that.

Of course, storing the last 6 blocks is pretty trivial for a full-node-sync client, and the point is really just to expose the thing that the sync driver gave us, so its not like we'd use it in Listen/Confirm implementations.

My understanding is that this would address the restart-with-different-node case that #3600 wanted to address, even if its optional and just used by the full-node-syncing logic?

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.

but addressing that really requires a substantial rework of how we think about chain sync, so maybe its not worth worrying about that.

Hmm, given that we even recently had users inquire whether they could switch chain sources at runtime, I do expect that we will need to tackle this eventually, as there will be users wanting to switch chain sources, and it's very unintuitive to either prohibit that, have it one-way, or even document the risks that might come from it.

Of course, storing the last 6 blocks is pretty trivial for a full-node-sync client, and the point is really just to expose the thing that the sync driver gave us, so its not like we'd use it in Listen/Confirm implementations.

So we would only do it when it's convenient, which means we could switch bitcoind nodes, but still not between chain sources?

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.

Hmm, given that we even recently had users inquire whether they could switch chain sources at runtime, I do expect that we will need to tackle this eventually, as there will be users wanting to switch chain sources, and it's very unintuitive to either prohibit that, have it one-way, or even document the risks that might come from it.

Sure, I agree, hence why I suggested moving to storing several recent blocks rather than only one. However, as you note it might not be super practical, so we're kinda stuck.

I think, basically, if we did that plus required transactions_confirmed calls come before best_block_updated calls (which may we're doing anyway, cc #3867 (comment)) then moving from one chain source to another is totally fine, even at runtime (as long as you don't miss transactions).

So we would only do it when it's convenient, which means we could switch bitcoind nodes, but still not between chain sources?

I mean I'm not sure what else we can do? If its too impractical to do it on some chain sources we can't really make it required, and if its not available switching chain sources isn't going to be 100% reliable.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 2nd Reminder

Hey @jkczyz! 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.

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

rustfmt is unhappy right now

@TheBlueMatt
TheBlueMattforce-pushed the 2025-06-disconnect-less-info branch from ef73b0a to a6985ebCompareJuly 2, 2025 22:37
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 3rd Reminder

Hey @jkczyz! 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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 4th Reminder

Hey @jkczyz! 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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 5th Reminder

Hey @jkczyz! 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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 6th Reminder

Hey @jkczyz! 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.

@jkczyzjkczyz 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. Please squash.

@TheBlueMatt
TheBlueMattforce-pushed the 2025-06-disconnect-less-info branch from bf41b15 to c78b08bCompareAugust 7, 2025 20:29
@TheBlueMatt

Copy link
Copy Markdown
CollaboratorAuthor

Squashed without further changes.

jkczyz
jkczyz previously approved these changes Aug 7, 2025

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

Linting CI failures look unrelated.

@TheBlueMatt
TheBlueMattforce-pushed the 2025-06-disconnect-less-info branch from c78b08b to 7b590eeCompareAugust 21, 2025 14:57
@TheBlueMatt
TheBlueMatt requested review from tnull and removed request for tnullAugust 21, 2025 15:01
@ldk-reviews-bot

Copy link
Copy Markdown

🔔 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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 2nd 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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 3rd 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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 4th 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.

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 5th 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.

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

This unfortunately needs a small rebase by now.

tnull
tnull previously approved these changes Sep 2, 2025

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

ACK, after rebase. Please excuse the considerable delay here.

I still think it would be good to document in an issue what's 'left' post this PR, at least where we want to take things directionally. Maybe we can just transfer #3876 (comment) to an issue that can serve as a base for discussions?

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.
`OnchainTxHandler` is an internal struct and doesn't implement
`Listen`, but its still nice to have its API mirror the `Listen`
API so that internal code all looks similar.
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.
`Listen` is somewhat quiet on high-level use and even requirements,
which we document further here.
@TheBlueMatt

TheBlueMatt commented Sep 2, 2025

Copy link
Copy Markdown
CollaboratorAuthor

Rebased with no further changes. Also opened #4042

@ldk-reviews-bot

Copy link
Copy Markdown

🔔 1st Reminder

Hey @jkczyz! 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.

@jkczyz
jkczyz merged commit eb300b6 into lightningdevkit:mainSep 5, 2025
25 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.

4 participants

@TheBlueMatt@ldk-reviews-bot@tnull@jkczyz