blockchain: Don't require chain for db upgrades.

This modifies the database upgrade logic to avoid needing the chain and
moves it earlier in the initialization process just after the initial
database creation (if needed), but prior the final struct instance
population from the database.

It is much simpler logic to have an already upgraded database prior to
populating the initial chain state so all of the initial setup happens
in one place.
This commit is contained in:
Dave Collins 2018-02-17 02:12:51 -06:00
parent eaa59edc88
commit a0fc0806ec
No known key found for this signature in database
GPG Key ID: B8904D9D9C93D1F2
3 changed files with 32 additions and 31 deletions

View File

@ -2008,11 +2008,6 @@ func New(config *Config) (*BlockChain, error) {
}
}
// Apply any upgrades as needed.
if err := b.upgrade(); err != nil {
return nil, err
}
b.subsidyCache = NewSubsidyCache(b.bestNode.height, b.chainParams)
b.pruner = newChainPruner(&b)

View File

@ -1435,6 +1435,11 @@ func (b *BlockChain) initChainState() error {
}
}
// Upgrade the database as needed.
if err := upgradeDB(b.db, b.chainParams, b.dbInfo); err != nil {
return err
}
// Attempt to load the chain state from the database.
err = b.db.View(func(dbTx database.Tx) error {
// Fetch the stored chain state from the database metadata.

View File

@ -11,6 +11,7 @@ import (
"github.com/decred/dcrd/blockchain/internal/progresslog"
"github.com/decred/dcrd/blockchain/stake"
"github.com/decred/dcrd/chaincfg"
"github.com/decred/dcrd/chaincfg/chainhash"
"github.com/decred/dcrd/database"
"github.com/decred/dcrd/dcrutil"
@ -83,17 +84,26 @@ func ticketsRevokedInBlock(bl *dcrutil.Block) []chainhash.Hash {
// upgradeToVersion2 upgrades a version 1 blockchain to version 2, allowing
// use of the new on-disk ticket database.
func (b *BlockChain) upgradeToVersion2() error {
func upgradeToVersion2(db database.DB, chainParams *chaincfg.Params, dbInfo *databaseInfo) error {
// Hardcoded so updates to the global values do not affect old upgrades.
chainStateKeyName := []byte("chainstate")
log.Infof("Initializing upgrade to database version 2")
best := b.BestSnapshot()
progressLogger := progresslog.NewBlockProgressLogger("Upgraded", log)
// The upgrade is atomic, so there is no need to set the flag that
// the database is undergoing an upgrade here. Get the stake node
// for the genesis block, and then begin connecting stake nodes
// incrementally.
err := b.db.Update(func(dbTx database.Tx) error {
bestStakeNode, errLocal := stake.InitDatabaseState(dbTx, b.chainParams)
err := db.Update(func(dbTx database.Tx) error {
// Fetch the stored best chain state from the database metadata.
serializedData := dbTx.Metadata().Get(chainStateKeyName)
best, err := deserializeBestChainState(serializedData)
if err != nil {
return err
}
bestStakeNode, errLocal := stake.InitDatabaseState(dbTx, chainParams)
if errLocal != nil {
return errLocal
}
@ -103,7 +113,7 @@ func (b *BlockChain) upgradeToVersion2() error {
return errLocal
}
for i := int64(1); i <= best.Height; i++ {
for i := int64(1); i <= int64(best.height); i++ {
block, errLocal := dbFetchBlockByHeight(dbTx, i)
if errLocal != nil {
return errLocal
@ -111,8 +121,8 @@ func (b *BlockChain) upgradeToVersion2() error {
// If we need the tickets, fetch them too.
var newTickets []chainhash.Hash
if i >= b.chainParams.StakeEnabledHeight {
matureHeight := i - int64(b.chainParams.TicketMaturity)
if i >= chainParams.StakeEnabledHeight {
matureHeight := i - int64(chainParams.TicketMaturity)
matureBlock, errLocal := dbFetchBlockByHeight(dbTx, matureHeight)
if errLocal != nil {
return errLocal
@ -140,27 +150,18 @@ func (b *BlockChain) upgradeToVersion2() error {
// Write the top block stake node to the database.
errLocal = stake.WriteConnectedBestNode(dbTx, bestStakeNode,
best.Hash)
best.hash)
if errLocal != nil {
return errLocal
}
// Write the best block node when we reach it.
if i == best.Height {
b.bestNode.stakeNode = bestStakeNode
b.bestNode.stakeUndoData = bestStakeNode.UndoData()
b.bestNode.newTickets = newTickets
b.bestNode.ticketsVoted = ticketsVotedInBlock(block)
b.bestNode.ticketsRevoked = ticketsRevokedInBlock(block)
}
progressLogger.LogBlockHeight(block.MsgBlock(), parent.MsgBlock())
parent = block
}
// Write the new database version.
b.dbInfo.version = 2
return dbPutDatabaseInfo(dbTx, b.dbInfo)
dbInfo.version = 2
return dbPutDatabaseInfo(dbTx, dbInfo)
})
if err != nil {
return err
@ -171,15 +172,15 @@ func (b *BlockChain) upgradeToVersion2() error {
return nil
}
// upgrade applies all possible upgrades to the blockchain database iteratively,
// updating old clients to the newest version.
func (b *BlockChain) upgrade() error {
if b.dbInfo.version == 1 {
err := b.upgradeToVersion2()
if err != nil {
// upgradeDB upgrades old database versions to the newest version by applying
// all possible upgrades iteratively.
//
// NOTE: The passed database info will be updated with the latest versions.
func upgradeDB(db database.DB, chainParams *chaincfg.Params, dbInfo *databaseInfo) error {
if dbInfo.version == 1 {
if err := upgradeToVersion2(db, chainParams, dbInfo); err != nil {
return err
}
}
return nil
}