8000 Track best-possible-headers (TheBlueMatt) by Sjors · Pull Request #13937 · bitcoin/bitcoin · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Track best-possible-headers (TheBlueMatt) #13937

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

Closed
wants to merge 7 commits into from

Conversation

Sjors
Copy link
Member
@Sjors Sjors commented Aug 10, 2018

Rebase of #12138:

This adds a setBlockIndexHeaderCandidates which mimics setBlockIndexCandidates and is
The set of all leaf CBlockIndex entries with BLOCK_VALID_TREE (for itself and all ancestors) and
as good as our current tip or better. Entries here are potential future candidates for insertion
into setBlockIndexCandidates, once we get all the required block data. Thus, entries here
represent chains on which we should be actively downloading block data.

Note that we define "as good as our current tip or better" slightly differently here than in
setBlockIndexCandidates - we include things which will have a higher nSequence (but have the
same chain work) here, but do not include such entries in setBlockIndexCandidates. This is
because we prefer to also download towards chains which have the same total work as our current
chain (as an optimization since a reorg is very possible in such cases).

Note that, unlike setBlockIndexCandidates, we only store "leaf" entries here, as we are not as
aggressively prune-able (setBlockIndexCandidates are things which we can, and usually do, try to connect immediately, and thus entries dont stick around for long). Thus, it may be the case that
chainActive.Tip() is NOT in setBlockIndexHeaderCandidates.

Additionally, unlike setBlockIndexCandidates, we are happy to store entries which are not
connectable due to pruning here.

This is useful as it (finally) disconnects net_processing logic from the "store on disk" logic in validation.cpp. More importantly, it represents what you'd need from the consensus logic to implement a spv-first sync mode, as this provides a best-header which will follow invalidity - always pointing to the best-possible header even after block(s) are found to be invalid.

Not included:

Noteble rebase changes:

  • pindex_was_in_chain is still needed due to Only call NotifyBlockTip when chainActive changes #12431, in order to notify the gui about a new block tip, only if the active chain was modified
  • qt/clientmodel.cpp changed significantly, because things were moved to interfaces/node.cpp
  • updated ProcessNewBlock bench

I added an additional commit, because the new !fHasMoreOrSameWork && !parent_of_header_candidate check in AcceptBlo 8000 ck can cause submitblock (modified in #13439) to mistakingly believe the block is a duplicate.

Added debug log statements to rpc_preciousblock.py that helped me figure out the above issue.

@Sjors
Copy link
Member Author
Sjors commented Aug 10, 2018

cc @TheBlueMatt, @MarcoFalke, @jamesob as this touches your work

cc @jonasschnelli since you reviewed the original PR (@skeees did too, but only the commit that I dropped)

@Sjors Sjors force-pushed the 2018/08/best-header-tracking branch from 97eb1f2 to 7b2a938 Compare August 10, 2018 14:55
@DrahtBot
Copy link
Contributor
DrahtBot commented Aug 10, 2018

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

Conflicts

Reviewers, this pull request conflicts with the following ones:

  • #14624 (Some simple improvements to the RNG code by sipa)
  • #12151 (rpc: Remove cs_main lock from blockToJSON and blockheaderToJSON by promag)

If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

LOCK(cs_main);
auto it = setBlockIndexHeaderCandidates.rbegin();
if (it == setBlockIndexHeaderCandidates.rend())
return nullptr;
Copy link
Contributor

Choose a reason for hiding this comment

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

Numerous places call this function but no nullptr check is performed and dereferences are executed on the ptr.
Can this return null in any case where this function is called?

Copy link
Member Author
@Sjors Sjors Aug 26, 2018

Choose a reason for hiding this comment

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

That seems problematic indeed. There's an assert for this in https://github.com/bitcoin/bitcoin/blame/7b1ab22384d944f638c514f838e25b391204ef67/src/net_processing.cpp#L3308, but not in the calls from validation.cpp:

  • in NotifyHeaderTip(): here it's compared with pindexHeaderOld which is a nullptr initially, but would non-null the next time. It's not dereferenced in that function, but it is passed on to uiInterface.NotifyHeaderTip. I could add an assert insideif (fNotify)
  • in IsCurrentForFeeEstimation(): I could add an assert directly above the if statement that deferences it

The comment in InvalidBlockFound suggests that sometimes setBlockIndexHeaderCandidates can be empty, so the above wouldn't be enough. Rather than assert I could explicitly check for nullptr in both functions and deal with them, as well as put a warning where this set is defined.

height = ::pindexBestHeader->nHeight;
block_time = ::pindexBestHeader->GetBlockTime();
const CBlockIndex* best_header = ::GetBestHeader();
if (best_header) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Possible race condition here since we don't lock cs_main? Can the best_head data be deleted between time we call GetBestHeader() and when we use the ptr?

I'm not familiar with the code enough to know the answer to this so just putting it here in case.

Copy link
Member Author
@Sjors Sjors Aug 26, 2018

Choose a reason for hiding this comment

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

It sounds like it would be safer to also make a PR for a9db3da that I left out? (nvm that was already merged in #13023)

I'll see if I can understand the potential race condition you mention.

@Sjors Sjors force-pushed the 2018/08/best-header-tracking branch 2 times, most recently from 54e17d2 to 7b1ab22 Compare August 26, 2018 12:20
@Sjors
Copy link
Member Author
Sjors commented Aug 26, 2018

Rebased.

I added commits to explicitly handle cases where GetBestHeader() returns nullptr in validation.cpp. Both cases are UI related and don't seem very important, so they're handled in a fairly lazy way.

I added a commit to make header count fall back to block height in getblockchaininfo (rpc/blockchain.cpp).

That leaves net_processing.cpp, where I'm not sure what to do:

Original commit where pindexBestHeader was replaced by GetBestHeader() for reference: 8525f50

I'm not sure if those are safe.

Perhaps there should be a guarantee that GetBestHeader() returns the last known valid block (worst case genesis), by inserting that whenever the last entry is deleted?

TheBlueMatt and others added 7 commits October 26, 2018 13:52
When we find an invalid block, instead of adding BLOCK_FAILED_CHILD
to its descendants in FindMostWorkChain, iterate
setBlockIndexCandidates to find candidate descendants and mark them
as BLOCK_FAILED_CHILD immediately, removing them from
setBlockIndexCandidates as we go. This keeps BLOCK_FAILED_MASK
entries out of setBlockIndexCandidates.

This also adds a few checks to CheckBlockIndex, including one which
checks that blocks with BLOCK_FAILED_CHILD are not, themselves,
marked invalid, but have an invalid parent. This should be fine for
most block indexes, however InvalidateBlock previously violated
this. Luckily most users shouldn't be running with -checkblockindex

Note that this introduces a bug where a block who's header was
received but data was not when a ancestor was found to be invalid
will not be marked BLOCK_FAILED_CHILD. Thus, when that block is
received, it will be added to setBlockIndexCandidates, violating
the new invariant. This is fixed in the next commit.
This mirrors setBlockIndexCandidates but for BLOCK_VALID_TREE
instead of BLOCK_VALID_TRANSACTIONS && nTx. There are a few
differences between the two to keep the new set practical, see
code comments for more.
pindexBestHeader never considers the invalidity of a chain, only
the validity of the header chain. Using it for sync estimation and
GETHEADERS etc requests in net makes no sense. Instead, use the
new setBlockIndexHeaderCandidates to find the best candidate tip.
Instead of allowing net_processing to inform AcceptBlock as to
whether a block is interesting enough to commit to disk, use the
new setBlockIndexHeadersCandidates to determine if they lead
towards a chain with at least the same (or more) work as our
current tip. This further decouples the maze in net_processing from
our consensus anti-DoS logic.
@Sjors Sjors force-pushed the 2018/08/best-header-tracking branch from 99adad5 to bdaf9f9 Compare October 30, 2018 08:48
@Sjors
Copy link
Member Author
Sjors commented Oct 30, 2018

Rebased. I'll hold back on addressing specific feedback until there's some high level agreement on whether this is useful at all.

// to do here.
setBlockIndexHeaderCandidates.erase(it);
break;
parent_present = true;
Copy link
Contributor

Choose a reason for hiding this comment

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

This is dead code. The parent_present = true; is not meant to be after break; in this scope? :-)

if ((*it)->GetAncestor(pindex->nHeight) == pindex) {
// pindex is useless - even if there are other tips based on it
// which we want in setBlockIndexHeaderCandidates, we're not
// gonna find them here.
Copy link
Contributor

Choose a reason for hiding this comment

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

"going to find them here." :-)

*
* Works even if chainActive is empty!
*
* If chain_ordered_inertion, we assume that if pindex->pprev was previously a
Copy link
Contributor

Choose a reason for hiding this comment

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

Should be insertion?

*
* Note that, unlike setBlockIndexCandidates, we only store "leaf" entries here, as we are not as
* aggressively prune-able (setBlockIndexCandidates are things which we can, and usually do, try to
* connect immediately, and thus entries dont stick around for long). Thus, it may be the case that
Copy link
Contributor

Choose a reason for hiding this comment

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

"Don't" :-)

}
}
}
if (!fHasMoreOrSameWork && !parent_of_header_candidate) return true; // We dont think this block leads somewhere interesting
Copy link
Contributor

Choose a reason for hiding this comment

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

"Don't" :-)

@Sjors
Copy link
Member Author
Sjors commented Nov 30, 2018

Abandoning for now. Consider it up for grabs, but I suggest getting concept ACKs before trying to rebase.

@ryanofsky
Copy link
Contributor

Since this is marked up for grabs, it might be useful to say what motivations for this change are.

It looks like it was brought up in IRC on 12/1 in the context of "SPV sync" but the relationship seems unclear. (I believe "SPV sync" refers to downloading blocks out of order during initial sync so wallet can show unvalidated transactions and be usable before the sync finishes.)

@Sjors
Copy link
Member Author
Sjors commented Dec 4, 2018

@ryanofsky it started out here: #9483 (comment), so just one step to make it easier to sync headers and fresh blocks first, and then catch up on historical blocks later.

@bitcoin bitcoin locked as resolved and limited conversation to collaborators Sep 8, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants
0