Merge pull request #1243 from Roasbeef/init-block-index-consistency

blockchain: in initChainState mark ancestors of best tip as valid if …
This commit is contained in:
Olaoluwa Osuntokun 2018-08-09 17:02:37 -07:00 committed by GitHub
commit 0f95fff561
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 59 additions and 9 deletions

View file

@ -1085,6 +1085,17 @@ func (b *BlockChain) reorganizeChain(detachNodes, attachNodes *list.List) error
func (b *BlockChain) connectBestChain(node *blockNode, block *btcutil.Block, flags BehaviorFlags) (bool, error) {
fastAdd := flags&BFFastAdd == BFFastAdd
flushIndexState := func() {
// Intentionally ignore errors writing updated node status to DB. If
// it fails to write, it's not the end of the world. If the block is
// valid, we flush in connectBlock and if the block is invalid, the
// worst that can happen is we revalidate the block after a restart.
if writeErr := b.index.flushToDB(); writeErr != nil {
log.Warnf("Error flushing block index changes to disk: %v",
writeErr)
}
}
// We are extending the main (best) chain with a new block. This is the
// most common case.
parentHash := &block.MsgBlock().Header.PrevBlock
@ -1108,14 +1119,7 @@ func (b *BlockChain) connectBestChain(node *blockNode, block *btcutil.Block, fla
return false, err
}
// Intentionally ignore errors writing updated node status to DB. If
// it fails to write, it's not the end of the world. If the block is
// valid, we flush in connectBlock and if the block is invalid, the
// worst that can happen is we revalidate the block after a restart.
if writeErr := b.index.flushToDB(); writeErr != nil {
log.Warnf("Error flushing block index changes to disk: %v",
writeErr)
}
flushIndexState()
if err != nil {
return false, err
@ -1140,9 +1144,28 @@ func (b *BlockChain) connectBestChain(node *blockNode, block *btcutil.Block, fla
// Connect the block to the main chain.
err := b.connectBlock(node, block, view, stxos)
if err != nil {
// If we got hit with a rule error, then we'll mark
// that status of the block as invalid and flush the
// index state to disk before returning with the error.
if _, ok := err.(RuleError); ok {
b.index.SetStatusFlags(
node, statusValidateFailed,
)
}
flushIndexState()
return false, err
}
// If this is fast add, or this block node isn't yet marked as
// valid, then we'll update its status and flush the state to
// disk again.
if fastAdd || !b.index.NodeStatus(node).KnownValid() {
b.index.SetStatusFlags(node, statusValid)
flushIndexState()
}
return true, nil
}
if fastAdd {

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