Skip to content

Commit

Permalink
blockchain: Remove main chain index from db.
Browse files Browse the repository at this point in the history
This removes the main chain index from the database as well as all code
related to reading and writing it (other than those in the upgrade
paths) since it is no longer used and bumps the database version since
it is not possible to downgrade after the main chain index has been
removed.
  • Loading branch information
davecgh committed Jul 9, 2018
1 parent 6c9bc1d commit 3ca9def
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 133 deletions.
12 changes: 0 additions & 12 deletions blockchain/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -750,12 +750,6 @@ func (b *BlockChain) connectBlock(node *blockNode, block, parent *dcrutil.Block,
return err
}

// Add the block hash and height to the main chain index.
err = dbPutMainChainIndex(dbTx, block.Hash(), node.height)
if err != nil {
return err
}

// Update the utxo set using the state of the utxo view. This
// entails removing all of the utxos spent and adding the new
// ones created by the block.
Expand Down Expand Up @@ -928,12 +922,6 @@ func (b *BlockChain) disconnectBlock(node *blockNode, block, parent *dcrutil.Blo
return err
}

// Remove the block hash and height from the main chain index.
err = dbRemoveMainChainIndex(dbTx, block.Hash(), node.height)
if err != nil {
return err
}

// Update the utxo set using the state of the utxo view. This
// entails restoring all of the utxos spent and removing the new
// ones created by the block.
Expand Down
112 changes: 1 addition & 111 deletions blockchain/chainio.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import (
const (
// currentDatabaseVersion indicates what the current database
// version is.
currentDatabaseVersion = 3
currentDatabaseVersion = 4

// currentBlockIndexVersion indicates what the current block index
// database version.
Expand Down Expand Up @@ -1215,95 +1215,6 @@ func dbPutUtxoView(dbTx database.Tx, view *UtxoViewpoint) error {
return nil
}

// -----------------------------------------------------------------------------
// The main chain index consists of two buckets with an entry for every block in
// the main chain. One bucket is for the hash to height mapping and the other
// is for the height to hash mapping.
//
// The serialized format for values in the hash to height bucket is:
// <height>
//
// Field Type Size
// height uint32 4 bytes
//
// The serialized format for values in the height to hash bucket is:
// <hash>
//
// Field Type Size
// hash chainhash.Hash chainhash.HashSize
// -----------------------------------------------------------------------------

// dbPutMainChainIndex uses an existing database transaction to update or add
// index entries for the hash to height and height to hash mappings for the
// provided values.
func dbPutMainChainIndex(dbTx database.Tx, hash *chainhash.Hash, height int64) error {
// Serialize the height for use in the index entries.
var serializedHeight [4]byte
dbnamespace.ByteOrder.PutUint32(serializedHeight[:], uint32(height))

// Add the block hash to height mapping to the index.
meta := dbTx.Metadata()
hashIndex := meta.Bucket(dbnamespace.HashIndexBucketName)
if err := hashIndex.Put(hash[:], serializedHeight[:]); err != nil {
return err
}

// Add the block height to hash mapping to the index.
heightIndex := meta.Bucket(dbnamespace.HeightIndexBucketName)
return heightIndex.Put(serializedHeight[:], hash[:])
}

// dbRemoveMainChainIndex uses an existing database transaction remove main
// chain index entries from the hash to height and height to hash mappings for
// the provided values.
func dbRemoveMainChainIndex(dbTx database.Tx, hash *chainhash.Hash, height int64) error {
// Remove the block hash to height mapping.
meta := dbTx.Metadata()
hashIndex := meta.Bucket(dbnamespace.HashIndexBucketName)
if err := hashIndex.Delete(hash[:]); err != nil {
return err
}

// Remove the block height to hash mapping.
var serializedHeight [4]byte
dbnamespace.ByteOrder.PutUint32(serializedHeight[:], uint32(height))
heightIndex := meta.Bucket(dbnamespace.HeightIndexBucketName)
return heightIndex.Delete(serializedHeight[:])
}

// dbFetchHeightByHash uses an existing database transaction to retrieve the
// height for the provided hash from the index.
func dbFetchHeightByHash(dbTx database.Tx, hash *chainhash.Hash) (int64, error) {
meta := dbTx.Metadata()
hashIndex := meta.Bucket(dbnamespace.HashIndexBucketName)
serializedHeight := hashIndex.Get(hash[:])
if serializedHeight == nil {
str := fmt.Sprintf("block %s is not in the main chain", hash)
return 0, errNotInMainChain(str)
}

return int64(dbnamespace.ByteOrder.Uint32(serializedHeight)), nil
}

// dbFetchHashByHeight uses an existing database transaction to retrieve the
// hash for the provided height from the index.
func dbFetchHashByHeight(dbTx database.Tx, height int64) (*chainhash.Hash, error) {
var serializedHeight [4]byte
dbnamespace.ByteOrder.PutUint32(serializedHeight[:], uint32(height))

meta := dbTx.Metadata()
heightIndex := meta.Bucket(dbnamespace.HeightIndexBucketName)
hashBytes := heightIndex.Get(serializedHeight[:])
if hashBytes == nil {
str := fmt.Sprintf("no block at height %d exists", height)
return nil, errNotInMainChain(str)
}

var hash chainhash.Hash
copy(hash[:], hashBytes)
return &hash, nil
}

// -----------------------------------------------------------------------------
// The database information contains information about the version and date
// of the blockchain database.
Expand Down Expand Up @@ -1585,20 +1496,6 @@ func (b *BlockChain) createChainState() error {
return err
}

// Create the bucket that houses the chain block hash to height
// index.
_, err = meta.CreateBucket(dbnamespace.HashIndexBucketName)
if err != nil {
return err
}

// Create the bucket that houses the chain block height to hash
// index.
_, err = meta.CreateBucket(dbnamespace.HeightIndexBucketName)
if err != nil {
return err
}

// Create the bucket that houses the spend journal data.
_, err = meta.CreateBucket(dbnamespace.SpendJournalBucketName)
if err != nil {
Expand All @@ -1619,13 +1516,6 @@ func (b *BlockChain) createChainState() error {
return err
}

// Add the genesis block hash to height and height to hash
// mappings to the index.
err = dbPutMainChainIndex(dbTx, &node.hash, node.height)
if err != nil {
return err
}

// Store the current best chain state into the database.
err = dbPutBestState(dbTx, stateSnapshot, node.workSum)
if err != nil {
Expand Down
8 changes: 0 additions & 8 deletions blockchain/internal/dbnamespace/dbnamespace.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,6 @@ var (
// BCDBInfoBucketName bucket.
BCDBInfoCreatedKeyName = []byte("created")

// HashIndexBucketName is the name of the db bucket used to house to the
// block hash -> block height index.
HashIndexBucketName = []byte("hashidx")

// HeightIndexBucketName is the name of the db bucket used to house to
// the block height -> block hash index.
HeightIndexBucketName = []byte("heightidx")

// ChainStateKeyName is the name of the db key used to store the best
// chain state.
ChainStateKeyName = []byte("chainstate")
Expand Down
101 changes: 99 additions & 2 deletions blockchain/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,28 @@ func ticketsRevokedInBlock(bl *dcrutil.Block) []chainhash.Hash {
// use of the new on-disk ticket database.
func upgradeToVersion2(db database.DB, chainParams *chaincfg.Params, dbInfo *databaseInfo) error {
// Hardcoded so updates to the global values do not affect old upgrades.
byteOrder := binary.LittleEndian
chainStateKeyName := []byte("chainstate")
heightIdxBucketName := []byte("heightidx")

// These are legacy functions that relied on information in the database
// that is no longer available in more recent code.
dbFetchHashByHeight := func(dbTx database.Tx, height int64) (*chainhash.Hash, error) {
var serializedHeight [4]byte
byteOrder.PutUint32(serializedHeight[:], uint32(height))

// This is a legacy function that relied on information in the database that
// is no longer available in more recent code.
meta := dbTx.Metadata()
heightIndex := meta.Bucket(heightIdxBucketName)
hashBytes := heightIndex.Get(serializedHeight[:])
if hashBytes == nil {
str := fmt.Sprintf("no block at height %d exists", height)
return nil, errNotInMainChain(str)
}

var hash chainhash.Hash
copy(hash[:], hashBytes)
return &hash, nil
}
dbFetchBlockByHeight := func(dbTx database.Tx, height int64) (*dcrutil.Block, error) {
// First find the hash associated with the provided height in the index.
hash, err := dbFetchHashByHeight(dbTx, height)
Expand Down Expand Up @@ -419,6 +437,78 @@ func upgradeToVersion3(db database.DB, dbInfo *databaseInfo, interrupt <-chan st
})
}

// removeMainChainIndex removes the main chain hash index and height index
// buckets. These are no longer needed due to using the full block index in
// memory.
//
// The database is guaranteed to be fully updated if this returns without
// failure.
func removeMainChainIndex(db database.DB, interrupt <-chan struct{}) error {
// Hardcoded bucket names so updates to the global values do not affect old
// upgrades.
hashIdxBucketName := []byte("hashidx")
heightIdxBucketName := []byte("heightidx")

log.Info("Removing unneeded indexes in the database...")
start := time.Now()

// Delete the main chain index buckets.
err := db.Update(func(dbTx database.Tx) error {
// Delete the main chain hash to height index.
meta := dbTx.Metadata()
hashIdxBucket := meta.Bucket(hashIdxBucketName)
if hashIdxBucket != nil {
if err := meta.DeleteBucket(hashIdxBucketName); err != nil {
return err
}
log.Infof("Removed hash index.")
}

if interruptRequested(interrupt) {
// No error here so the database transaction is not cancelled
// and therefore outstanding work is written to disk. The
// outer function will exit with an interrupted error below due
// to another interrupted check.
return nil
}

// Delete the main chain hash to height index.
heightIdxBucket := meta.Bucket(heightIdxBucketName)
if heightIdxBucket != nil {
if err := meta.DeleteBucket(heightIdxBucketName); err != nil {
return err
}
log.Infof("Removed height index.")
}

return nil
})
if err != nil {
return err
}

if interruptRequested(interrupt) {
return errInterruptRequested
}

elapsed := time.Since(start).Round(time.Millisecond)
log.Infof("Done upgrading database in %v.", elapsed)
return nil
}

// upgradeToVersion4 upgrades a version 3 blockchain database to version 4.
func upgradeToVersion4(db database.DB, dbInfo *databaseInfo, interrupt <-chan struct{}) error {
if err := removeMainChainIndex(db, interrupt); err != nil {
return err
}

// Update and persist the updated database versions.
dbInfo.version = 4
return db.Update(func(dbTx database.Tx) error {
return dbPutDatabaseInfo(dbTx, dbInfo)
})
}

// upgradeDB upgrades old database versions to the newest version by applying
// all possible upgrades iteratively.
//
Expand All @@ -439,5 +529,12 @@ func upgradeDB(db database.DB, chainParams *chaincfg.Params, dbInfo *databaseInf
}
}

// Remove the main chain index from the database if needed.
if dbInfo.version == 3 {
if err := upgradeToVersion4(db, dbInfo, interrupt); err != nil {
return err
}
}

return nil
}

0 comments on commit 3ca9def

Please sign in to comment.