blockchain: Load all block headers into block index on init.

Currently only the blocks in the active chain are loaded into the
block index on initialization. This instead iterates over the entire
block index bucket in LevelDB and loads all nodes.
This commit is contained in:
Jim Posen 2017-09-05 16:58:49 -07:00 committed by Dave Collins
parent 175fd940bb
commit 6315cea70c

View file

@ -1171,41 +1171,68 @@ func (b *BlockChain) initChainState() error {
// for them versus a whole bunch of little ones to reduce // for them versus a whole bunch of little ones to reduce
// pressure on the GC. // pressure on the GC.
log.Infof("Loading block index. This might take a while...") log.Infof("Loading block index. This might take a while...")
bestHeight := int32(state.height)
blockNodes := make([]blockNode, bestHeight+1) blockIndexBucket := dbTx.Metadata().Bucket(blockIndexBucketName)
var tip *blockNode
for height := int32(0); height <= bestHeight; height++ { // Determine how many blocks will be loaded into the index so we can
header, err := dbFetchHeaderByHeight(dbTx, height) // allocate the right amount.
var blockCount int32
cursor := blockIndexBucket.Cursor()
for ok := cursor.First(); ok; ok = cursor.Next() {
blockCount++
}
blockNodes := make([]blockNode, blockCount)
var i int32
var lastNode *blockNode
cursor = blockIndexBucket.Cursor()
for ok := cursor.First(); ok; ok = cursor.Next() {
var header wire.BlockHeader
headerBytes := cursor.Value()
err := header.Deserialize(bytes.NewReader(headerBytes))
if err != nil { if err != nil {
return err return err
} }
// Initialize the block node for the block, connect it, // Initialize the block node for the block, connect it,
// and add it to the block index. // and add it to the block index.
node := &blockNodes[height] node := &blockNodes[i]
initBlockNode(node, header, height) initBlockNode(node, &header, 0)
node.status = statusDataStored | statusValid node.status = statusDataStored | statusValid
if tip != nil { if lastNode == nil {
node.parent = tip if node.hash != *b.chainParams.GenesisHash {
node.workSum = node.workSum.Add(tip.workSum, return AssertError(fmt.Sprintf("initChainState: Expected "+
node.workSum) "first entry in block index to be genesis block, "+
} "found %s", header.BlockHash()))
b.index.AddNode(node) }
} else {
// Since we iterate block headers in order of height, if the
// blocks are mostly linear there is a very good chance the
// previous header processed is the parent.
parent := lastNode
if header.PrevBlock != parent.hash {
parent = b.index.LookupNode(&header.PrevBlock)
}
if parent == nil {
return AssertError(fmt.Sprintf("initChainState: Could "+
"not find parent for block %s", header.BlockHash()))
}
// This node is now the end of the best chain. node.parent = parent
tip = node node.height = parent.height + 1
node.workSum = node.workSum.Add(parent.workSum, node.workSum)
}
b.index.AddNode(node)
lastNode = node
i++
} }
// Ensure the resulting best chain matches the stored best state // Set the best chain view to the stored best state.
// hash and set the best chain view accordingly. tip := b.index.LookupNode(&state.hash)
if tip == nil || tip.hash != state.hash { if tip == nil {
var tipHash chainhash.Hash return AssertError(fmt.Sprintf("initChainState: cannot find "+
if tip != nil { "chain tip %s in block index", state.hash))
tipHash = tip.hash
}
return AssertError(fmt.Sprintf("initChainState: block "+
"index chain tip %s does not match stored "+
"best state %s", tipHash, state.hash))
} }
b.bestChain.SetTip(tip) b.bestChain.SetTip(tip)