Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix tiebreak when loading blocks from disk (and add tests for comparing chain ties) #29640

Open
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

sr-gi
Copy link
Member

@sr-gi sr-gi commented Mar 12, 2024

This PR grabs some interesting bits from #29284 and fixes some edge cases in how block tiebreaks are dealt with.

Regarding #29284

The main functionality from the PR was dropped given it was not an issue anymore, however, reviewers pointed out some comments were outdated #29284 (comment) (which to my understanding may have led to thinking that there was still an issue) it also added test coverage for the aforementioned case which was already passing on master and is useful to keep.

New functionality

While reviewing the superseded PR, it was noticed that blocks that are loaded from disk may face a similar issue (check #29284 (comment) for more context).

The issue comes from how tiebreaks for equal work blocks are handled: if two blocks have the same amount of work, the one that is activatable first wins, that is, the one for which we have all its data (and all of its ancestors'). The variable that keeps track of this, within CBlockIndex is nSequenceId, which is not persisted over restarts. This means that when a node is restarted, all blocks loaded from disk are defaulted the same nSequenceId: 0.
Now, when trying to decide what chain is best on loading blocks from disk, the previous tiebreaker rule is not decisive anymore, so the CBlockIndexWorkComparator has to default to its last rule: whatever block is loaded first (has a smaller memory address).

This means that if multiple same work tip candidates were available before restarting the node, it could be the case that the selected chain tip after restarting does not match the one before.

Therefore, the way nSequenceId is initialized is changed to:

  • 0 for blocks that belong to the previously known best chain
  • 1 to all other blocks loaded from disk

@DrahtBot
Copy link
Contributor

DrahtBot commented Mar 12, 2024

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Code Coverage

For detailed information about the code coverage, see the test coverage report.

Reviews

See the guideline for information on the review process.
A summary of reviews will appear here.

Conflicts

No conflicts as of last run.

src/validation.cpp Outdated Show resolved Hide resolved
@sr-gi sr-gi changed the title Adds missing test to chain ties (CBlockIndexWorkComparator) Fixes tiebreak when loading blocks from disk and adds missing test to chain ties (CBlockIndexWorkComparator) Mar 14, 2024
@sr-gi sr-gi changed the title Fixes tiebreak when loading blocks from disk and adds missing test to chain ties (CBlockIndexWorkComparator) Fix tiebreak when loading blocks from disk (and add tests for comparing chain ties) Mar 14, 2024
@DrahtBot
Copy link
Contributor

🚧 At least one of the CI tasks failed. Make sure to run all tests locally, according to the
documentation.

Possibly this is due to a silent merge conflict (the changes in this pull request being
incompatible with the current code in the target branch). If so, make sure to rebase on the latest
commit of the target branch.

Leave a comment here, if you need help tracking down a confusing failure.

Debug: https://github.com/bitcoin/bitcoin/runs/22675498262

@sr-gi sr-gi force-pushed the 202403-block-tiebreak branch 3 times, most recently from a705e00 to 77e1c45 Compare March 15, 2024 21:09
@sr-gi sr-gi force-pushed the 202403-block-tiebreak branch 2 times, most recently from 8225bd6 to f95e896 Compare March 22, 2024 15:23
sr-gi and others added 4 commits March 25, 2024 08:43
Before this, if we had two (or more) same work tip candidates and restarted our node,
it could be the case that the block set as tip after bootstrap didn't match the one
before stopping. That's because the work and `nSequenceId` of both block will be the same
(the latter is only kept in memory), so the active chain after restart would have depended
on what tip candidate was loaded first.

This makes sure that we are consistent over reboots.
Adds tests to make sure we are consistent on activating the same chain over
a node restart if two or more candidates have the same work when the node is shutdown
@sr-gi
Copy link
Member Author

sr-gi commented Mar 25, 2024

Rebased to drop the custom log fix in favor of a more generic solution (#29640 (comment))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants