blockchain: in initChainState mark ancestors of best tip as valid if not marked

In this commit, we add an additional consistency check within the
`initChainState` method.  It has been observed that at times, a block
wil lbe written to disk (as it's valid), but then the block index isn't
updated to reflect this. This can cause btcd to fail to do things like
serve cfheaders for valid blocks.

To partially remedy this, when we're loading in the index, we assume
that all ancestors of the current chain tip are valid, and mark them as
such. At the very end, we'll flush the index to ensure the state is
fully consistent. Typically this will be a noop, as only dirty elements
are flushed.
This commit is contained in:
Olaoluwa Osuntokun 2018-08-08 17:49:26 -07:00
parent cf05f92c3f
commit 4b13e79691
No known key found for this signature in database
GPG key ID: 964EA263DD637C21

View file

@ -1128,7 +1128,7 @@ func (b *BlockChain) initChainState() error {
}
// Attempt to load the chain state from the database.
return b.db.View(func(dbTx database.Tx) error {
err = b.db.View(func(dbTx database.Tx) error {
// Fetch the stored chain state from the database metadata.
// When it doesn't exist, it means the database hasn't been
// initialized for use with chain yet, so break out now to allow
@ -1221,6 +1221,25 @@ func (b *BlockChain) initChainState() error {
return err
}
// As a final consistency check, we'll run through all the
// nodes which are ancestors of the current chain tip, and mark
// them as valid if they aren't already marked as such. This
// is a safe assumption as all the block before the current tip
// are valid by definition.
for iterNode := tip; iterNode != nil; iterNode = iterNode.parent {
// If this isn't already marked as valid in the index, then
// we'll mark it as valid now to ensure consistency once
// we're up and running.
if !iterNode.status.KnownValid() {
log.Infof("Block %v (height=%v) ancestor of "+
"chain tip not marked as valid, "+
"upgrading to valid for consistency",
iterNode.hash, iterNode.height)
b.index.SetStatusFlags(iterNode, statusValid)
}
}
// Initialize the state related to the best block.
blockSize := uint64(len(blockBytes))
blockWeight := uint64(GetBlockWeight(btcutil.NewBlock(&block)))
@ -1230,6 +1249,14 @@ func (b *BlockChain) initChainState() error {
return nil
})
if err != nil {
return err
}
// As we might have updated the index after it was loaded, we'll
// attempt to flush the index to the DB. This will only result in a
// write if the elements are dirty, so it'll usually be a noop.
return b.index.flushToDB()
}
// deserializeBlockRow parses a value in the block index bucket into a block