blockchain: Enhance migration to v2 block index bucket.
This updates the block index migration to also store entries with valid status bytes.
This commit is contained in:
parent
52cddc19cd
commit
9aa9e79ebf
1 changed files with 85 additions and 26 deletions
|
@ -23,6 +23,16 @@ const (
|
||||||
blockHdrOffset = 12
|
blockHdrOffset = 12
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// blockChainContext represents a particular block's placement in the block
|
||||||
|
// chain. This is used by the block index migration to track block metadata that
|
||||||
|
// will be written to disk.
|
||||||
|
type blockChainContext struct {
|
||||||
|
parent *chainhash.Hash
|
||||||
|
children []*chainhash.Hash
|
||||||
|
height int32
|
||||||
|
mainChain bool
|
||||||
|
}
|
||||||
|
|
||||||
// migrateBlockIndex migrates all block entries from the v1 block index bucket
|
// migrateBlockIndex migrates all block entries from the v1 block index bucket
|
||||||
// to the v2 bucket. The v1 bucket stores all block entries keyed by block hash,
|
// to the v2 bucket. The v1 bucket stores all block entries keyed by block hash,
|
||||||
// whereas the v2 bucket stores the exact same values, but keyed instead by
|
// whereas the v2 bucket stores the exact same values, but keyed instead by
|
||||||
|
@ -47,15 +57,29 @@ func migrateBlockIndex(db database.DB) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get tip of the main chain.
|
||||||
|
serializedData := dbTx.Metadata().Get(chainStateKeyName)
|
||||||
|
state, err := deserializeBestChainState(serializedData)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
tip := &state.hash
|
||||||
|
|
||||||
// Scan the old block index bucket and construct a mapping of each block
|
// Scan the old block index bucket and construct a mapping of each block
|
||||||
// to all child blocks.
|
// to parent block and all child blocks.
|
||||||
childBlocksMap, err := readBlockTree(v1BlockIdxBucket)
|
blocksMap, err := readBlockTree(v1BlockIdxBucket)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use the block graph to calculate the height of each block.
|
// Use the block graph to calculate the height of each block.
|
||||||
blockHeights := determineBlockHeights(childBlocksMap)
|
err = determineBlockHeights(blocksMap)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find blocks on the main chain with the block graph and current tip.
|
||||||
|
determineMainChainBlocks(blocksMap, tip)
|
||||||
|
|
||||||
// Now that we have heights for all blocks, scan the old block index
|
// Now that we have heights for all blocks, scan the old block index
|
||||||
// bucket and insert all rows into the new one.
|
// bucket and insert all rows into the new one.
|
||||||
|
@ -65,16 +89,26 @@ func migrateBlockIndex(db database.DB) error {
|
||||||
|
|
||||||
var hash chainhash.Hash
|
var hash chainhash.Hash
|
||||||
copy(hash[:], hashBytes[0:chainhash.HashSize])
|
copy(hash[:], hashBytes[0:chainhash.HashSize])
|
||||||
|
chainContext := blocksMap[hash]
|
||||||
|
|
||||||
height, exists := blockHeights[hash]
|
if chainContext.height == -1 {
|
||||||
if !exists {
|
|
||||||
return fmt.Errorf("Unable to calculate chain height for "+
|
return fmt.Errorf("Unable to calculate chain height for "+
|
||||||
"stored block %s", hash)
|
"stored block %s", hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mark blocks as valid if they are part of the main chain.
|
||||||
|
status := statusDataStored
|
||||||
|
if chainContext.mainChain {
|
||||||
|
status |= statusValid
|
||||||
|
}
|
||||||
|
|
||||||
// Write header to v2 bucket
|
// Write header to v2 bucket
|
||||||
key := blockIndexKey(&hash, height)
|
value := make([]byte, blockHdrSize+1)
|
||||||
err := v2BlockIdxBucket.Put(key, headerBytes)
|
copy(value[0:blockHdrSize], headerBytes)
|
||||||
|
value[blockHdrSize] = byte(status)
|
||||||
|
|
||||||
|
key := blockIndexKey(&hash, uint32(chainContext.height))
|
||||||
|
err := v2BlockIdxBucket.Put(key, value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -93,10 +127,11 @@ func migrateBlockIndex(db database.DB) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// readBlockTree reads the old block index bucket and constructs a mapping of
|
// readBlockTree reads the old block index bucket and constructs a mapping of
|
||||||
// each block to all child blocks. This mapping represents the full tree of
|
// each block to its parent block and all child blocks. This mapping represents
|
||||||
// blocks.
|
// the full tree of blocks. This function does not populate the height or
|
||||||
func readBlockTree(v1BlockIdxBucket database.Bucket) (map[chainhash.Hash][]*chainhash.Hash, error) {
|
// mainChain fields of the returned blockChainContext values.
|
||||||
childBlocksMap := make(map[chainhash.Hash][]*chainhash.Hash)
|
func readBlockTree(v1BlockIdxBucket database.Bucket) (map[chainhash.Hash]*blockChainContext, error) {
|
||||||
|
blocksMap := make(map[chainhash.Hash]*blockChainContext)
|
||||||
err := v1BlockIdxBucket.ForEach(func(_, blockRow []byte) error {
|
err := v1BlockIdxBucket.ForEach(func(_, blockRow []byte) error {
|
||||||
var header wire.BlockHeader
|
var header wire.BlockHeader
|
||||||
endOffset := blockHdrOffset + blockHdrSize
|
endOffset := blockHdrOffset + blockHdrSize
|
||||||
|
@ -107,41 +142,65 @@ func readBlockTree(v1BlockIdxBucket database.Bucket) (map[chainhash.Hash][]*chai
|
||||||
}
|
}
|
||||||
|
|
||||||
blockHash := header.BlockHash()
|
blockHash := header.BlockHash()
|
||||||
childBlocksMap[header.PrevBlock] =
|
prevHash := header.PrevBlock
|
||||||
append(childBlocksMap[header.PrevBlock], &blockHash)
|
|
||||||
|
if blocksMap[blockHash] == nil {
|
||||||
|
blocksMap[blockHash] = &blockChainContext{height: -1}
|
||||||
|
}
|
||||||
|
if blocksMap[prevHash] == nil {
|
||||||
|
blocksMap[prevHash] = &blockChainContext{height: -1}
|
||||||
|
}
|
||||||
|
|
||||||
|
blocksMap[blockHash].parent = &prevHash
|
||||||
|
blocksMap[prevHash].children =
|
||||||
|
append(blocksMap[prevHash].children, &blockHash)
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
return childBlocksMap, err
|
return blocksMap, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// determineBlockHeights takes a map of block hashes to a slice of child hashes
|
// determineBlockHeights takes a map of block hashes to a slice of child hashes
|
||||||
// and uses it to compute the height for each block. The function assigns a
|
// and uses it to compute the height for each block. The function assigns a
|
||||||
// height of 0 to the genesis hash and explores the tree of blocks
|
// height of 0 to the genesis hash and explores the tree of blocks
|
||||||
// breadth-first, assigning a height to every block with a path back to the
|
// breadth-first, assigning a height to every block with a path back to the
|
||||||
// genesis block.
|
// genesis block. This function modifies the height field on the blocksMap
|
||||||
func determineBlockHeights(childBlocksMap map[chainhash.Hash][]*chainhash.Hash) map[chainhash.Hash]uint32 {
|
// entries.
|
||||||
blockHeights := make(map[chainhash.Hash]uint32)
|
func determineBlockHeights(blocksMap map[chainhash.Hash]*blockChainContext) error {
|
||||||
queue := list.New()
|
queue := list.New()
|
||||||
|
|
||||||
// The genesis block is included in childBlocksMap as a child of the zero
|
// The genesis block is included in blocksMap as a child of the zero hash
|
||||||
// hash because that is the value of the PrevBlock field in the genesis
|
// because that is the value of the PrevBlock field in the genesis header.
|
||||||
// header.
|
preGenesisContext, exists := blocksMap[zeroHash]
|
||||||
for _, genesisHash := range childBlocksMap[zeroHash] {
|
if !exists || len(preGenesisContext.children) == 0 {
|
||||||
blockHeights[*genesisHash] = 0
|
return fmt.Errorf("Unable to find genesis block")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, genesisHash := range preGenesisContext.children {
|
||||||
|
blocksMap[*genesisHash].height = 0
|
||||||
queue.PushBack(genesisHash)
|
queue.PushBack(genesisHash)
|
||||||
}
|
}
|
||||||
|
|
||||||
for e := queue.Front(); e != nil; e = queue.Front() {
|
for e := queue.Front(); e != nil; e = queue.Front() {
|
||||||
queue.Remove(e)
|
queue.Remove(e)
|
||||||
hash := e.Value.(*chainhash.Hash)
|
hash := e.Value.(*chainhash.Hash)
|
||||||
height := blockHeights[*hash]
|
height := blocksMap[*hash].height
|
||||||
|
|
||||||
// For each block with this one as a parent, assign it a height and
|
// For each block with this one as a parent, assign it a height and
|
||||||
// push to queue for future processing.
|
// push to queue for future processing.
|
||||||
for _, childHash := range childBlocksMap[*hash] {
|
for _, childHash := range blocksMap[*hash].children {
|
||||||
blockHeights[*childHash] = height + 1
|
blocksMap[*childHash].height = height + 1
|
||||||
queue.PushBack(childHash)
|
queue.PushBack(childHash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return blockHeights
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// determineMainChainBlocks traverses the block graph down from the tip to
|
||||||
|
// determine which block hashes that are part of the main chain. This function
|
||||||
|
// modifies the mainChain field on the blocksMap entries.
|
||||||
|
func determineMainChainBlocks(blocksMap map[chainhash.Hash]*blockChainContext, tip *chainhash.Hash) {
|
||||||
|
for nextHash := tip; *nextHash != zeroHash; nextHash = blocksMap[*nextHash].parent {
|
||||||
|
blocksMap[*nextHash].mainChain = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue