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:
commit
0f95fff561
2 changed files with 59 additions and 9 deletions
|
@ -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) {
|
func (b *BlockChain) connectBestChain(node *blockNode, block *btcutil.Block, flags BehaviorFlags) (bool, error) {
|
||||||
fastAdd := flags&BFFastAdd == BFFastAdd
|
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
|
// We are extending the main (best) chain with a new block. This is the
|
||||||
// most common case.
|
// most common case.
|
||||||
parentHash := &block.MsgBlock().Header.PrevBlock
|
parentHash := &block.MsgBlock().Header.PrevBlock
|
||||||
|
@ -1108,14 +1119,7 @@ func (b *BlockChain) connectBestChain(node *blockNode, block *btcutil.Block, fla
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Intentionally ignore errors writing updated node status to DB. If
|
flushIndexState()
|
||||||
// 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
|
@ -1140,9 +1144,28 @@ func (b *BlockChain) connectBestChain(node *blockNode, block *btcutil.Block, fla
|
||||||
// Connect the block to the main chain.
|
// Connect the block to the main chain.
|
||||||
err := b.connectBlock(node, block, view, stxos)
|
err := b.connectBlock(node, block, view, stxos)
|
||||||
if err != nil {
|
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
|
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
|
return true, nil
|
||||||
}
|
}
|
||||||
if fastAdd {
|
if fastAdd {
|
||||||
|
|
|
@ -1128,7 +1128,7 @@ func (b *BlockChain) initChainState() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempt to load the chain state from the database.
|
// 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.
|
// Fetch the stored chain state from the database metadata.
|
||||||
// When it doesn't exist, it means the database hasn't been
|
// When it doesn't exist, it means the database hasn't been
|
||||||
// initialized for use with chain yet, so break out now to allow
|
// initialized for use with chain yet, so break out now to allow
|
||||||
|
@ -1221,6 +1221,25 @@ func (b *BlockChain) initChainState() error {
|
||||||
return err
|
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.
|
// Initialize the state related to the best block.
|
||||||
blockSize := uint64(len(blockBytes))
|
blockSize := uint64(len(blockBytes))
|
||||||
blockWeight := uint64(GetBlockWeight(btcutil.NewBlock(&block)))
|
blockWeight := uint64(GetBlockWeight(btcutil.NewBlock(&block)))
|
||||||
|
@ -1230,6 +1249,14 @@ func (b *BlockChain) initChainState() error {
|
||||||
|
|
||||||
return nil
|
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
|
// deserializeBlockRow parses a value in the block index bucket into a block
|
||||||
|
|
Loading…
Reference in a new issue