diff --git a/blockchain/chain.go b/blockchain/chain.go
index be4d006d3..7e76c390d 100644
--- a/blockchain/chain.go
+++ b/blockchain/chain.go
@@ -223,6 +223,34 @@ type BlockChain struct {
 	// chain state can be quickly reconstructed on load.
 	stateLock     sync.RWMutex
 	stateSnapshot *BestState
+
+	// The following caches are used to efficiently keep track of the
+	// current deployment threshold state of each rule change deployment.
+	//
+	// This information is stored in the database so it can be quickly
+	// reconstructed on load.
+	//
+	// warningCaches caches the current deployment threshold state for blocks
+	// in each of the **possible** deployments.  This is used in order to
+	// detect when new unrecognized rule changes are being voted on and/or
+	// have been activated such as will be the case when older versions of
+	// the software are being used
+	//
+	// deploymentCaches caches the current deployment threshold state for
+	// blocks in each of the actively defined deployments.
+	warningCaches    []thresholdStateCache
+	deploymentCaches []thresholdStateCache
+
+	// The following fields are used to determine if certain warnings have
+	// already been shown.
+	//
+	// unknownRulesWarned refers to warnings due to unknown rules being
+	// activated.
+	//
+	// unknownVersionsWarned refers to warnings due to unknown versions
+	// being mined.
+	unknownRulesWarned    bool
+	unknownVersionsWarned bool
 }
 
 // DisableVerify provides a mechanism to disable transaction script validation
@@ -556,6 +584,38 @@ func (b *BlockChain) relativeNode(anchor *blockNode, distance uint32) (*blockNod
 	return iterNode, nil
 }
 
+// ancestorNode returns the ancestor block node at the provided height by
+// following the chain backwards from the given node while dynamically loading
+// any pruned nodes from the database and updating the memory block chain as
+// needed.  The returned block will be nil when a height is requested that is
+// after the height of the passed node or is less than zero.
+//
+// This function MUST be called with the chain state lock held (for writes).
+func (b *BlockChain) ancestorNode(node *blockNode, height int32) (*blockNode, error) {
+	// Nothing to do if the requested height is outside of the valid range.
+	if height > node.height || height < 0 {
+		return nil, nil
+	}
+
+	// Iterate backwards until the requested height is reached.
+	iterNode := node
+	for iterNode != nil && iterNode.height > height {
+		// Get the previous block node.  This function is used over
+		// simply accessing iterNode.parent directly as it will
+		// dynamically create previous block nodes as needed.  This
+		// helps allow only the pieces of the chain that are needed
+		// to remain in memory.
+		var err error
+		iterNode, err = b.getPrevNodeFromNode(iterNode)
+		if err != nil {
+			log.Errorf("getPrevNodeFromNode: %v", err)
+			return nil, err
+		}
+	}
+
+	return iterNode, nil
+}
+
 // removeBlockNode removes the passed block node from the memory chain by
 // unlinking all of its children and removing it from the the node and
 // dependency indices.
@@ -934,6 +994,22 @@ func (b *BlockChain) connectBlock(node *blockNode, block *btcutil.Block, view *U
 			"spent transaction out information")
 	}
 
+	// No warnings about unknown rules or versions until the chain is
+	// current.
+	if b.isCurrent() {
+		// Warn if any unknown new rules are either about to activate or
+		// have already been activated.
+		if err := b.warnUnknownRuleActivations(node); err != nil {
+			return err
+		}
+
+		// Warn if a high enough percentage of the last blocks have
+		// unexpected versions.
+		if err := b.warnUnknownVersions(node); err != nil {
+			return err
+		}
+	}
+
 	// Calculate the median time for the block.
 	medianTime, err := b.calcPastMedianTime(node)
 	if err != nil {
@@ -996,12 +1072,17 @@ func (b *BlockChain) connectBlock(node *blockNode, block *btcutil.Block, view *U
 			}
 		}
 
-		return nil
+		// Update the cached threshold states in the database as needed.
+		return b.putThresholdCaches(dbTx)
 	})
 	if err != nil {
 		return err
 	}
 
+	// Mark all modified entries in the threshold caches as flushed now that
+	// they have been committed to the database.
+	b.markThresholdCachesFlushed()
+
 	// Prune fully spent entries and mark all entries in the view unmodified
 	// now that the modifications have been committed to the database.
 	view.commit()
@@ -1507,17 +1588,14 @@ func (b *BlockChain) connectBestChain(node *blockNode, block *btcutil.Block, fla
 	return true, nil
 }
 
-// IsCurrent returns whether or not the chain believes it is current.  Several
+// isCurrent returns whether or not the chain believes it is current.  Several
 // factors are used to guess, but the key factors that allow the chain to
 // believe it is current are:
 //  - Latest block height is after the latest checkpoint (if enabled)
 //  - Latest block has a timestamp newer than 24 hours ago
 //
-// This function is safe for concurrent access.
-func (b *BlockChain) IsCurrent() bool {
-	b.chainLock.RLock()
-	defer b.chainLock.RUnlock()
-
+// This function MUST be called with the chain state lock held (for reads).
+func (b *BlockChain) isCurrent() bool {
 	// Not current if the latest main (best) chain height is before the
 	// latest known good checkpoint (when checkpoints are enabled).
 	checkpoint := b.latestCheckpoint()
@@ -1534,6 +1612,20 @@ func (b *BlockChain) IsCurrent() bool {
 	return !b.bestNode.timestamp.Before(minus24Hours)
 }
 
+// IsCurrent returns whether or not the chain believes it is current.  Several
+// factors are used to guess, but the key factors that allow the chain to
+// believe it is current are:
+//  - Latest block height is after the latest checkpoint (if enabled)
+//  - Latest block has a timestamp newer than 24 hours ago
+//
+// This function is safe for concurrent access.
+func (b *BlockChain) IsCurrent() bool {
+	b.chainLock.RLock()
+	defer b.chainLock.RUnlock()
+
+	return b.isCurrent()
+}
+
 // BestSnapshot returns information about the current best chain block and
 // related state as of the current point in time.  The returned instance must be
 // treated as immutable since it is shared by all callers.
@@ -1653,6 +1745,8 @@ func New(config *Config) (*BlockChain, error) {
 		orphans:             make(map[chainhash.Hash]*orphanBlock),
 		prevOrphans:         make(map[chainhash.Hash][]*orphanBlock),
 		blockCache:          make(map[chainhash.Hash]*btcutil.Block),
+		warningCaches:       newThresholdCaches(vbNumBits),
+		deploymentCaches:    newThresholdCaches(chaincfg.DefinedDeployments),
 	}
 
 	// Initialize the chain state from the passed database.  When the db
@@ -1670,6 +1764,14 @@ func New(config *Config) (*BlockChain, error) {
 		}
 	}
 
+	// Initialize rule change threshold state caches from the passed
+	// database.  When the db does not yet contains any cached information
+	// for a given threshold cache, the threshold states will be calculated
+	// using the chain state.
+	if err := b.initThresholdCaches(); err != nil {
+		return nil, err
+	}
+
 	log.Infof("Chain state (height %d, hash %v, totaltx %d, work %v)",
 		b.bestNode.height, b.bestNode.hash, b.stateSnapshot.TotalTxns,
 		b.bestNode.workSum)
diff --git a/blockchain/chainio.go b/blockchain/chainio.go
index cd318dd60..216241599 100644
--- a/blockchain/chainio.go
+++ b/blockchain/chainio.go
@@ -11,6 +11,7 @@ import (
 	"math/big"
 	"sort"
 
+	"github.com/btcsuite/btcd/chaincfg"
 	"github.com/btcsuite/btcd/chaincfg/chainhash"
 	"github.com/btcsuite/btcd/database"
 	"github.com/btcsuite/btcd/wire"
@@ -38,6 +39,27 @@ var (
 	// unspent transaction output set.
 	utxoSetBucketName = []byte("utxoset")
 
+	// thresholdBucketName is the name of the db bucket used to house cached
+	// threshold states.
+	thresholdBucketName = []byte("thresholdstate")
+
+	// numDeploymentsKeyName is the name of the db key used to store the
+	// number of saved deployment caches.
+	numDeploymentsKeyName = []byte("numdeployments")
+
+	// deploymentBucketName is the name of the db bucket used to house the
+	// cached threshold states for the actively defined rule deployments.
+	deploymentBucketName = []byte("deploymentcache")
+
+	// deploymentStateKeyName is the name of the db key used to store the
+	// deployment state associated with the threshold cache for a given rule
+	// deployment.
+	deploymentStateKeyName = []byte("deploymentstate")
+
+	// warningBucketName is the name of the db bucket used to house the
+	// cached threshold states for unknown rule deployments.
+	warningBucketName = []byte("warningcache")
+
 	// byteOrder is the preferred byte order used for serializing numeric
 	// fields for storage in the database.
 	byteOrder = binary.LittleEndian
@@ -75,6 +97,13 @@ func isDeserializeErr(err error) bool {
 	return ok
 }
 
+// isDbBucketNotFoundErr returns whether or not the passed error is a
+// database.Error with an error code of database.ErrBucketNotFound.
+func isDbBucketNotFoundErr(err error) bool {
+	dbErr, ok := err.(database.Error)
+	return ok && dbErr.ErrorCode == database.ErrBucketNotFound
+}
+
 // -----------------------------------------------------------------------------
 // The transaction spend journal consists of an entry for each block connected
 // to the main chain which contains the transaction outputs the block spends
@@ -1403,3 +1432,528 @@ func (b *BlockChain) HeightRange(startHeight, endHeight int32) ([]chainhash.Hash
 	})
 	return hashList, err
 }
+
+// -----------------------------------------------------------------------------
+// The threshold state consists of individual threshold cache buckets for each
+// cache id under one main threshold state bucket.  Each threshold cache bucket
+// contains entries keyed by the block hash for the final block in each window
+// and their associated threshold states as well as the associated deployment
+// parameters.
+//
+// The serialized value format is for each cache entry keyed by hash is:
+//
+//   <thresholdstate>
+//
+//   Field             Type      Size
+//   threshold state   uint8     1 byte
+//
+//
+// In addition, the threshold cache buckets for deployments contain the specific
+// deployment parameters they were created with.  This allows the cache
+// invalidation when there any changes to their definitions.
+//
+// The serialized value format for the deployment parameters is:
+//
+//   <bit number><start time><expire time>
+//
+//   Field            Type      Size
+//   bit number       uint8     1 byte
+//   start time       uint64    8 bytes
+//   expire time      uint64    8 bytes
+//
+//
+// Finally, the main threshold bucket also contains the number of stored
+// deployment buckets as described above.
+//
+// The serialized value format for the number of stored deployment buckets is:
+//
+//   <num deployments>
+//
+//   Field             Type      Size
+//   num deployments   uint32    4 bytes
+// -----------------------------------------------------------------------------
+
+// serializeDeploymentCacheParams serializes the parameters for the passed
+// deployment into a single byte slice according to the format described in
+// detail above.
+func serializeDeploymentCacheParams(deployment *chaincfg.ConsensusDeployment) []byte {
+	serialized := make([]byte, 1+8+8)
+	serialized[0] = deployment.BitNumber
+	byteOrder.PutUint64(serialized[1:], deployment.StartTime)
+	byteOrder.PutUint64(serialized[9:], deployment.ExpireTime)
+	return serialized
+}
+
+// deserializeDeploymentCacheParams deserializes the passed serialized
+// deployment cache parameters into a deployment struct.
+func deserializeDeploymentCacheParams(serialized []byte) (chaincfg.ConsensusDeployment, error) {
+	// Ensure the serialized data has enough bytes to properly deserialize
+	// the bit number, start time, and expire time.
+	if len(serialized) != 1+8+8 {
+		return chaincfg.ConsensusDeployment{}, database.Error{
+			ErrorCode:   database.ErrCorruption,
+			Description: "corrupt deployment cache state",
+		}
+	}
+
+	var deployment chaincfg.ConsensusDeployment
+	deployment.BitNumber = serialized[0]
+	deployment.StartTime = byteOrder.Uint64(serialized[1:])
+	deployment.ExpireTime = byteOrder.Uint64(serialized[9:])
+	return deployment, nil
+}
+
+// dbPutDeploymentCacheParams uses an existing database transaction to update
+// the deployment cache params with the given values.
+func dbPutDeploymentCacheParams(bucket database.Bucket, deployment *chaincfg.ConsensusDeployment) error {
+	serialized := serializeDeploymentCacheParams(deployment)
+	return bucket.Put(deploymentStateKeyName, serialized)
+}
+
+// dbFetchDeploymentCacheParams uses an existing database transaction to
+// retrieve the deployment parameters from the given bucket, deserialize them,
+// and returns the resulting deployment struct.
+func dbFetchDeploymentCacheParams(bucket database.Bucket) (chaincfg.ConsensusDeployment, error) {
+	serialized := bucket.Get(deploymentStateKeyName)
+	return deserializeDeploymentCacheParams(serialized)
+}
+
+// serializeNumDeployments serializes the parameters for the passed number of
+// deployments into a single byte slice according to the format described in
+// detail above.
+func serializeNumDeployments(numDeployments uint32) []byte {
+	serialized := make([]byte, 4)
+	byteOrder.PutUint32(serialized, numDeployments)
+	return serialized
+}
+
+// deserializeDeploymentCacheParams deserializes the passed serialized
+// number of deployments.
+func deserializeNumDeployments(serialized []byte) (uint32, error) {
+	if len(serialized) != 4 {
+		return 0, database.Error{
+			ErrorCode:   database.ErrCorruption,
+			Description: "corrupt stored number of deployments",
+		}
+	}
+	return byteOrder.Uint32(serialized), nil
+}
+
+// dbPutNumDeployments uses an existing database transaction to update the
+// number of deployments to the given value.
+func dbPutNumDeployments(bucket database.Bucket, numDeployments uint32) error {
+	serialized := serializeNumDeployments(numDeployments)
+	return bucket.Put(numDeploymentsKeyName, serialized)
+}
+
+// dbFetchNumDeployments uses an existing database transaction to retrieve the
+// number of deployments, deserialize it, and returns the result.
+func dbFetchNumDeployments(bucket database.Bucket) (uint32, error) {
+	// Ensure the serialized data has enough bytes to properly deserialize
+	// the number of stored deployments.
+	serialized := bucket.Get(numDeploymentsKeyName)
+	return deserializeNumDeployments(serialized)
+}
+
+// thresholdCacheBucket returns the serialized bucket name to use for a
+// threshold cache given a prefix and an ID.
+func thresholdCacheBucket(prefix []byte, id uint32) []byte {
+	bucketName := make([]byte, len(prefix)+4)
+	copy(bucketName, prefix)
+	byteOrder.PutUint32(bucketName[len(bucketName)-4:], id)
+	return bucketName
+}
+
+// dbPutThresholdState uses an existing database transaction to update or add
+// the rule change threshold state for the provided block hash.
+func dbPutThresholdState(bucket database.Bucket, hash chainhash.Hash, state ThresholdState) error {
+	// Add the block hash to threshold state mapping.
+	var serializedState [1]byte
+	serializedState[0] = byte(state)
+	return bucket.Put(hash[:], serializedState[:])
+}
+
+// dbPutThresholdCaches uses an existing database transaction to update the
+// provided threshold state caches using the given bucket prefix.
+func dbPutThresholdCaches(dbTx database.Tx, caches []thresholdStateCache, bucketPrefix []byte) error {
+	// Loop through each of the defined cache IDs in the provided cache and
+	// populate the associated bucket with all of the block hash to
+	// threshold state mappings for it.
+	cachesBucket := dbTx.Metadata().Bucket(thresholdBucketName)
+	for i := uint32(0); i < uint32(len(caches)); i++ {
+		cache := &caches[i]
+		if len(cache.dbUpdates) == 0 {
+			continue
+		}
+
+		cacheIDBucketName := thresholdCacheBucket(bucketPrefix, i)
+		bucket := cachesBucket.Bucket(cacheIDBucketName)
+		for blockHash, state := range cache.dbUpdates {
+			err := dbPutThresholdState(bucket, blockHash, state)
+			if err != nil {
+				return err
+			}
+		}
+	}
+
+	return nil
+}
+
+// putThresholdCaches uses an existing database transaction to update the
+// threshold state caches.
+func (b *BlockChain) putThresholdCaches(dbTx database.Tx) error {
+	err := dbPutThresholdCaches(dbTx, b.deploymentCaches,
+		deploymentBucketName)
+	if err != nil {
+		return err
+	}
+
+	return dbPutThresholdCaches(dbTx, b.warningCaches, warningBucketName)
+}
+
+// markThresholdCachesFlushed clears any pending updates to be written from
+// threshold state caches.  Callers are intended to call this after the pending
+// updates have been successfully written to the database via the
+// putThresholdCaches function and its associated database transation is closed.
+// This approach is taken to ensure the memory state is not updated until after
+// the atomic database update was successful.
+func (b *BlockChain) markThresholdCachesFlushed() {
+	for i := 0; i < len(b.deploymentCaches); i++ {
+		b.deploymentCaches[i].MarkFlushed()
+	}
+	for i := 0; i < len(b.warningCaches); i++ {
+		b.warningCaches[i].MarkFlushed()
+	}
+}
+
+// dbFetchThresholdCaches uses an existing database transaction to retrieve
+// the threshold state caches from the provided bucket prefix into the given
+// cache parameter.  When the db does not contain any information for a specific
+// id within that cache, that entry will simply be empty.
+func dbFetchThresholdCaches(dbTx database.Tx, caches []thresholdStateCache, bucketPrefix []byte) error {
+	// Nothing to load if the main threshold state caches bucket
+	// doesn't exist.
+	cachesBucket := dbTx.Metadata().Bucket(thresholdBucketName)
+	if cachesBucket == nil {
+		return nil
+	}
+
+	// Loop through each of the cache IDs and load any saved threshold
+	// states.
+	for i := 0; i < len(caches); i++ {
+		// Nothing to do for this cache ID if there is no bucket for it.
+		cacheIDBucketName := thresholdCacheBucket(bucketPrefix, uint32(i))
+		cacheIDBucket := cachesBucket.Bucket(cacheIDBucketName[:])
+		if cacheIDBucket == nil {
+			continue
+		}
+
+		// Load all of the cached block hash to threshold state mappings
+		// from the bucket.
+		err := cacheIDBucket.ForEach(func(k, v []byte) error {
+			// Skip non-hash entries.
+			if len(k) != chainhash.HashSize {
+				return nil
+			}
+
+			var hash chainhash.Hash
+			copy(hash[:], k)
+			caches[i].entries[hash] = ThresholdState(v[0])
+			return nil
+		})
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+// invalidateThresholdCaches removes any threshold state caches that are no
+// longer valid.  This can happen if a deployment ID is changed such as when it
+// is reused, or if it is reordered in the parameter definitions.  It is also
+// necessary for specific bits in the warning cache when deployment definitions
+// are added and removed since it could change the expected block versions and
+// hence potentially change the result of the warning states for that bit.
+func (b *BlockChain) invalidateThresholdCaches(cachesBucket database.Bucket) error {
+	deployments := b.chainParams.Deployments[:]
+
+	// Remove any stored deployments that are no longer defined along with
+	// the warning cache associated with their bits.
+	numStoredDeployments, err := dbFetchNumDeployments(cachesBucket)
+	if err != nil {
+		return err
+	}
+	definedDeployments := uint32(len(deployments))
+	for i := definedDeployments; i < numStoredDeployments; i++ {
+		// Nothing to do when nothing is stored for the deployment.
+		deployBucketKey := thresholdCacheBucket(deploymentBucketName, i)
+		deployBucket := cachesBucket.Bucket(deployBucketKey)
+		if deployBucket == nil {
+			continue
+		}
+
+		// Load the deployment details the cache was created for from
+		// the database.
+		stored, err := dbFetchDeploymentCacheParams(deployBucket)
+		if err != nil {
+			return err
+		}
+
+		// Remove the warning cache for the bit associated with the old
+		// deployment definition.
+		oldBit := uint32(stored.BitNumber)
+		bn := thresholdCacheBucket(warningBucketName, oldBit)
+		err = cachesBucket.DeleteBucket(bn)
+		if err != nil && !isDbBucketNotFoundErr(err) {
+			return err
+		}
+
+		// Remove deployment state and cache.
+		err = cachesBucket.DeleteBucket(deployBucketKey)
+		if err != nil && !isDbBucketNotFoundErr(err) {
+			return err
+		}
+		log.Debugf("Removed threshold state caches for deployment %d "+
+			"and warning bit %d", i, oldBit)
+	}
+
+	// Remove any deployment caches that no longer match the associated
+	// deployment definition.
+	for i := uint32(0); i < uint32(len(deployments)); i++ {
+		// Remove the warning cache for the bit associated with the new
+		// deployment definition if nothing is already stored for the
+		// deployment.
+		deployBucketKey := thresholdCacheBucket(deploymentBucketName, i)
+		deployBucket := cachesBucket.Bucket(deployBucketKey)
+		if deployBucket == nil {
+			// Remove the warning cache for the bit associated with
+			// the new deployment definition.
+			newBit := uint32(deployments[i].BitNumber)
+			bn := thresholdCacheBucket(warningBucketName, newBit)
+			err = cachesBucket.DeleteBucket(bn)
+			if err != nil && !isDbBucketNotFoundErr(err) {
+				return err
+			}
+			log.Debugf("Removed threshold state cache for warning "+
+				"bit %d ", newBit)
+			continue
+		}
+
+		// Load the deployment details the cache was created for from
+		// the database, compare them against the currently defined
+		// deployment, and invalidate the relevant caches if they don't
+		// match.
+		stored, err := dbFetchDeploymentCacheParams(deployBucket)
+		if err != nil {
+			return err
+		}
+		if stored != deployments[i] {
+			// Remove deployment state and cache.
+			err := cachesBucket.DeleteBucket(deployBucketKey)
+			if err != nil && !isDbBucketNotFoundErr(err) {
+				return err
+			}
+
+			// Remove the warning cache for the bit associated with
+			// the new deployment definition.
+			newBit := uint32(deployments[i].BitNumber)
+			bn := thresholdCacheBucket(warningBucketName, newBit)
+			err = cachesBucket.DeleteBucket(bn)
+			if err != nil && !isDbBucketNotFoundErr(err) {
+				return err
+			}
+
+			// Remove the warning cache for the bit associated with
+			// the old deployment definition if it is different than
+			// the new one.
+			oldBit := uint32(stored.BitNumber)
+			if oldBit == newBit {
+				log.Debugf("Removed threshold state caches for "+
+					"deployment %d and warning bit %d", i,
+					newBit)
+				continue
+			}
+			bn = thresholdCacheBucket(warningBucketName, oldBit)
+			err = cachesBucket.DeleteBucket(bn)
+			if err != nil && !isDbBucketNotFoundErr(err) {
+				return err
+			}
+			log.Debugf("Removed threshold state caches for "+
+				"deployment %d and warning bits %d and %d", i,
+				oldBit, newBit)
+		}
+	}
+
+	return nil
+}
+
+// initThresholdCacheBuckets creates any missing buckets needed for the defined
+// threshold caches and populates them with state-related details so they can
+// be invalidated as needed.
+func (b *BlockChain) initThresholdCacheBuckets(meta database.Bucket) error {
+	// Create overall bucket that houses all of the threshold caches and
+	// their related state as needed.
+	cachesBucket, err := meta.CreateBucketIfNotExists(thresholdBucketName)
+	if err != nil {
+		return err
+	}
+
+	// Update the number of stored deployment as needed.
+	definedDeployments := uint32(len(b.deploymentCaches))
+	storedDeployments, err := dbFetchNumDeployments(cachesBucket)
+	if err != nil || storedDeployments != definedDeployments {
+		err := dbPutNumDeployments(cachesBucket, definedDeployments)
+		if err != nil {
+			return err
+		}
+	}
+
+	// Create buckets for each of the deployment caches as needed, and
+	// populate the created buckets with the specific deployment details so
+	// that the cache(s) can be invalidated properly with future updates.
+	for i := uint32(0); i < definedDeployments; i++ {
+		name := thresholdCacheBucket(deploymentBucketName, i)
+		if bucket := cachesBucket.Bucket(name); bucket != nil {
+			continue
+		}
+
+		deployBucket, err := cachesBucket.CreateBucket(name)
+		if err != nil {
+			return err
+		}
+
+		deployment := &b.chainParams.Deployments[i]
+		err = dbPutDeploymentCacheParams(deployBucket, deployment)
+		if err != nil {
+			return err
+		}
+	}
+
+	// Create buckets for each of the warning caches as needed.
+	for i := uint32(0); i < uint32(len(b.warningCaches)); i++ {
+		name := thresholdCacheBucket(warningBucketName, i)
+		_, err := cachesBucket.CreateBucketIfNotExists(name)
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+// initThresholdCaches initializes the threshold state caches from the database.
+// When the db does not yet contain any information for a specific threshold
+// cache or a given id within that cache, it will simply be empty which will
+// lead to it being calculated as needed.
+func (b *BlockChain) initThresholdCaches() error {
+	// Create and initialize missing threshold state cache buckets and
+	// remove any that are no longer valid.
+	err := b.db.Update(func(dbTx database.Tx) error {
+		meta := dbTx.Metadata()
+		cachesBucket := meta.Bucket(thresholdBucketName)
+		if cachesBucket != nil {
+			err := b.invalidateThresholdCaches(cachesBucket)
+			if err != nil {
+				return err
+			}
+		}
+
+		// Create all cache buckets as needed.
+		return b.initThresholdCacheBuckets(meta)
+	})
+	if err != nil {
+		return err
+	}
+
+	// Load the deployment caches.
+	err = b.db.View(func(dbTx database.Tx) error {
+		// Load the deployment threshold states.
+		err := dbFetchThresholdCaches(dbTx, b.deploymentCaches,
+			deploymentBucketName)
+		if err != nil {
+			return err
+		}
+
+		// Load the warning threshold states.
+		return dbFetchThresholdCaches(dbTx, b.warningCaches,
+			warningBucketName)
+	})
+	if err != nil {
+		return err
+	}
+
+	// Inform the user the states might take a while to recalculate if any
+	// of the threshold state caches aren't populated.
+	var showMsg bool
+	for i := 0; i < len(b.warningCaches); i++ {
+		if len(b.warningCaches[i].entries) == 0 {
+			showMsg = true
+			break
+		}
+	}
+	if !showMsg {
+		for i := 0; i < len(b.deploymentCaches); i++ {
+			if len(b.deploymentCaches[i].entries) == 0 {
+				showMsg = true
+				break
+			}
+		}
+	}
+	if showMsg {
+		log.Info("Recalculating threshold states due to definition " +
+			"change.  This might take a while...")
+	}
+
+	// Initialize the warning and deployment caches by calculating the
+	// threshold state for each of them.  This will ensure the caches are
+	// populated and any states that needed to be recalculated due to
+	// definition changes is done now.
+	for bit := uint32(0); bit < vbNumBits; bit++ {
+		checker := bitConditionChecker{bit: bit, chain: b}
+		cache := &b.warningCaches[bit]
+		_, err := b.thresholdState(b.bestNode, checker, cache)
+		if err != nil {
+			return err
+		}
+	}
+	for id := 0; id < len(b.chainParams.Deployments); id++ {
+		deployment := &b.chainParams.Deployments[id]
+		cache := &b.deploymentCaches[id]
+		checker := deploymentChecker{deployment: deployment, chain: b}
+		_, err := b.thresholdState(b.bestNode, checker, cache)
+		if err != nil {
+			return err
+		}
+	}
+
+	// No warnings about unknown rules or versions until the chain is
+	// current.
+	if b.isCurrent() {
+		// Warn if a high enough percentage of the last blocks have
+		// unexpected versions.
+		if err := b.warnUnknownVersions(b.bestNode); err != nil {
+			return err
+		}
+
+		// Warn if any unknown new rules are either about to activate or
+		// have already been activated.
+		if err := b.warnUnknownRuleActivations(b.bestNode); err != nil {
+			return err
+		}
+	}
+
+	// Update the cached threshold states in the database as needed.
+	err = b.db.Update(func(dbTx database.Tx) error {
+		return b.putThresholdCaches(dbTx)
+	})
+	if err != nil {
+		return err
+	}
+
+	// Mark all modified entries in the threshold caches as flushed now that
+	// they have been committed to the database.
+	b.markThresholdCachesFlushed()
+
+	return nil
+}
diff --git a/blockchain/error.go b/blockchain/error.go
index a31123a02..db71a5668 100644
--- a/blockchain/error.go
+++ b/blockchain/error.go
@@ -8,11 +8,21 @@ import (
 	"fmt"
 )
 
+// DeploymentError identifies an error that indicates a deployment ID was
+// specified that does not exist.
+type DeploymentError uint32
+
+// Error returns the assertion error as a human-readable string and satisfies
+// the error interface.
+func (e DeploymentError) Error() string {
+	return fmt.Sprintf("deployment ID %d does not exist", uint32(e))
+}
+
 // AssertError identifies an error that indicates an internal code consistency
 // issue and should be treated as a critical and unrecoverable error.
 type AssertError string
 
-// Error returns the assertion error as a huma-readable string and satisfies
+// Error returns the assertion error as a human-readable string and satisfies
 // the error interface.
 func (e AssertError) Error() string {
 	return "assertion failed: " + string(e)
diff --git a/blockchain/error_test.go b/blockchain/error_test.go
index 640f504c9..c18abf0f6 100644
--- a/blockchain/error_test.go
+++ b/blockchain/error_test.go
@@ -95,3 +95,37 @@ func TestRuleError(t *testing.T) {
 		}
 	}
 }
+
+// TestDeploymentError tests the stringized output for the DeploymentError type.
+func TestDeploymentError(t *testing.T) {
+	t.Parallel()
+
+	tests := []struct {
+		in   blockchain.DeploymentError
+		want string
+	}{
+		{
+			blockchain.DeploymentError(0),
+			"deployment ID 0 does not exist",
+		},
+		{
+			blockchain.DeploymentError(10),
+			"deployment ID 10 does not exist",
+		},
+		{
+			blockchain.DeploymentError(123),
+			"deployment ID 123 does not exist",
+		},
+	}
+
+	t.Logf("Running %d tests", len(tests))
+	for i, test := range tests {
+		result := test.in.Error()
+		if result != test.want {
+			t.Errorf("Error #%d\n got: %s want: %s", i, result,
+				test.want)
+			continue
+		}
+	}
+
+}
diff --git a/blockchain/thresholdstate.go b/blockchain/thresholdstate.go
new file mode 100644
index 000000000..e7620be97
--- /dev/null
+++ b/blockchain/thresholdstate.go
@@ -0,0 +1,322 @@
+// Copyright (c) 2016 The btcsuite developers
+// Use of this source code is governed by an ISC
+// license that can be found in the LICENSE file.
+
+package blockchain
+
+import (
+	"fmt"
+
+	"github.com/btcsuite/btcd/chaincfg/chainhash"
+)
+
+// ThresholdState define the various threshold states used when voting on
+// consensus changes.
+type ThresholdState byte
+
+// These constants are used to identify specific threshold states.
+//
+// NOTE: This section specifically does not use iota for the individual states
+// since these values are serialized and must be stable for long-term storage.
+const (
+	// ThresholdDefined is the first state for each deployment and is the
+	// state for the genesis block has by defintion for all deployments.
+	ThresholdDefined ThresholdState = 0
+
+	// ThresholdStarted is the state for a deployment once its start time
+	// has been reached.
+	ThresholdStarted ThresholdState = 1
+
+	// ThresholdLockedIn is the state for a deployment during the retarget
+	// period which is after the ThresholdStarted state period and the
+	// number of blocks that have voted for the deployment equal or exceed
+	// the required number of votes for the deployment.
+	ThresholdLockedIn ThresholdState = 2
+
+	// ThresholdActive is the state for a deployment for all blocks after a
+	// retarget period in which the deployment was in the ThresholdLockedIn
+	// state.
+	ThresholdActive ThresholdState = 3
+
+	// ThresholdFailed is the state for a deployment once its expiration
+	// time has been reached and it did not reach the ThresholdLockedIn
+	// state.
+	ThresholdFailed ThresholdState = 4
+
+	// numThresholdsStates is the maximum number of threshold states used in
+	// tests.
+	numThresholdsStates = iota
+)
+
+// thresholdStateStrings is a map of ThresholdState values back to their
+// constant names for pretty printing.
+var thresholdStateStrings = map[ThresholdState]string{
+	ThresholdDefined:  "ThresholdDefined",
+	ThresholdStarted:  "ThresholdStarted",
+	ThresholdLockedIn: "ThresholdLockedIn",
+	ThresholdActive:   "ThresholdActive",
+	ThresholdFailed:   "ThresholdFailed",
+}
+
+// String returns the ThresholdState as a human-readable name.
+func (t ThresholdState) String() string {
+	if s := thresholdStateStrings[t]; s != "" {
+		return s
+	}
+	return fmt.Sprintf("Unknown ThresholdState (%d)", int(t))
+}
+
+// thresholdConditionChecker provides a generic interface that is invoked to
+// determine when a consensus rule change threshold should be changed.
+type thresholdConditionChecker interface {
+	// BeginTime returns the unix timestamp for the median block time after
+	// which voting on a rule change starts (at the next window).
+	BeginTime() uint64
+
+	// EndTime returns the unix timestamp for the median block time after
+	// which an attempted rule change fails if it has not already been
+	// locked in or activated.
+	EndTime() uint64
+
+	// RuleChangeActivationThreshold is the number of blocks for which the
+	// condition must be true in order to lock in a rule change.
+	RuleChangeActivationThreshold() uint32
+
+	// MinerConfirmationWindow is the number of blocks in each threshold
+	// state retarget window.
+	MinerConfirmationWindow() uint32
+
+	// Condition returns whether or not the rule change activation condition
+	// has been met.  This typically involves checking whether or not the
+	// bit assocaited with the condition is set, but can be more complex as
+	// needed.
+	Condition(*blockNode) (bool, error)
+}
+
+// thresholdStateCache provides a type to cache the threshold states of each
+// threshold window for a set of IDs.  It also keeps track of which entries have
+// been modified and therefore need to be written to the database.
+type thresholdStateCache struct {
+	dbUpdates map[chainhash.Hash]ThresholdState
+	entries   map[chainhash.Hash]ThresholdState
+}
+
+// Lookup returns the threshold state associated with the given hash along with
+// a boolean that indicates whether or not it is valid.
+func (c *thresholdStateCache) Lookup(hash chainhash.Hash) (ThresholdState, bool) {
+	state, ok := c.entries[hash]
+	return state, ok
+}
+
+// Update updates the cache to contain the provided hash to threshold state
+// mapping while properly tracking needed updates flush changes to the database.
+func (c *thresholdStateCache) Update(hash chainhash.Hash, state ThresholdState) {
+	if existing, ok := c.entries[hash]; ok && existing == state {
+		return
+	}
+
+	c.dbUpdates[hash] = state
+	c.entries[hash] = state
+}
+
+// MarkFlushed marks all of the current udpates as flushed to the database.
+// This is useful so the caller can ensure the needed database updates are not
+// lost until they have successfully been written to the database.
+func (c *thresholdStateCache) MarkFlushed() {
+	for hash := range c.dbUpdates {
+		delete(c.dbUpdates, hash)
+	}
+}
+
+// newThresholdCaches returns a new array of caches to be used when calculating
+// threshold states.
+func newThresholdCaches(numCaches uint32) []thresholdStateCache {
+	caches := make([]thresholdStateCache, numCaches)
+	for i := 0; i < len(caches); i++ {
+		caches[i] = thresholdStateCache{
+			entries:   make(map[chainhash.Hash]ThresholdState),
+			dbUpdates: make(map[chainhash.Hash]ThresholdState),
+		}
+	}
+	return caches
+}
+
+// thresholdState returns the current rule change threshold state for the given
+// node and deployment ID.  The cache is used to ensure the threshold states for
+// previous windows are only calculated once.
+//
+// This function MUST be called with the chain state lock held (for writes).
+func (b *BlockChain) thresholdState(prevNode *blockNode, checker thresholdConditionChecker, cache *thresholdStateCache) (ThresholdState, error) {
+	// The threshold state for the window that contains the genesis block is
+	// defined by definition.
+	confirmationWindow := int32(checker.MinerConfirmationWindow())
+	if prevNode == nil || (prevNode.height+1) < confirmationWindow {
+		return ThresholdDefined, nil
+	}
+
+	// Get the ancestor that is the last block of the previous confirmation
+	// window in order to get its threshold state.  This can be done because
+	// the state is the same for all blocks within a given window.
+	var err error
+	prevNode, err = b.ancestorNode(prevNode, prevNode.height-
+		(prevNode.height+1)%confirmationWindow)
+	if err != nil {
+		return ThresholdFailed, err
+	}
+
+	// Iterate backwards through each of the previous confirmation windows
+	// to find the most recently cached threshold state.
+	var neededStates []*blockNode
+	for prevNode != nil {
+		// Nothing more to do if the state of the block is already
+		// cached.
+		if _, ok := cache.Lookup(*prevNode.hash); ok {
+			break
+		}
+
+		// The start and expiration times are based on the median block
+		// time, so calculate it now.
+		medianTime, err := b.calcPastMedianTime(prevNode)
+		if err != nil {
+			return ThresholdFailed, err
+		}
+
+		// The state is simply defined if the start time hasn't been
+		// been reached yet.
+		if uint64(medianTime.Unix()) < checker.BeginTime() {
+			cache.Update(*prevNode.hash, ThresholdDefined)
+			break
+		}
+
+		// Add this node to the list of nodes that need the state
+		// calculated and cached.
+		neededStates = append(neededStates, prevNode)
+
+		// Get the ancestor that is the last block of the previous
+		// confirmation window.
+		prevNode, err = b.ancestorNode(prevNode, prevNode.height-
+			confirmationWindow)
+		if err != nil {
+			return ThresholdFailed, err
+		}
+	}
+
+	// Start with the threshold state for the most recent confirmation
+	// window that has a cached state.
+	state := ThresholdDefined
+	if prevNode != nil {
+		var ok bool
+		state, ok = cache.Lookup(*prevNode.hash)
+		if !ok {
+			return ThresholdFailed, AssertError(fmt.Sprintf(
+				"thresholdState: cache lookup failed for %v",
+				prevNode.hash))
+		}
+	}
+
+	// Since each threshold state depends on the state of the previous
+	// window, iterate starting from the oldest unknown window.
+	for neededNum := len(neededStates) - 1; neededNum >= 0; neededNum-- {
+		prevNode := neededStates[neededNum]
+
+		switch state {
+		case ThresholdDefined:
+			// The deployment of the rule change fails if it expires
+			// before it is accepted and locked in.
+			medianTime, err := b.calcPastMedianTime(prevNode)
+			if err != nil {
+				return ThresholdFailed, err
+			}
+			medianTimeUnix := uint64(medianTime.Unix())
+			if medianTimeUnix >= checker.EndTime() {
+				state = ThresholdFailed
+				break
+			}
+
+			// The state for the rule moves to the started state
+			// once its start time has been reached (and it hasn't
+			// already expired per the above).
+			if medianTimeUnix >= checker.BeginTime() {
+				state = ThresholdStarted
+			}
+
+		case ThresholdStarted:
+			// The deployment of the rule change fails if it expires
+			// before it is accepted and locked in.
+			medianTime, err := b.calcPastMedianTime(prevNode)
+			if err != nil {
+				return ThresholdFailed, err
+			}
+			if uint64(medianTime.Unix()) >= checker.EndTime() {
+				state = ThresholdFailed
+				break
+			}
+
+			// At this point, the rule change is still being voted
+			// on by the miners, so iterate backwards through the
+			// confirmation window to count all of the votes in it.
+			var count uint32
+			countNode := prevNode
+			for i := int32(0); i < confirmationWindow; i++ {
+				condition, err := checker.Condition(countNode)
+				if err != nil {
+					return ThresholdFailed, err
+				}
+				if condition {
+					count++
+				}
+
+				// Get the previous block node.  This function
+				// is used over simply accessing countNode.parent
+				// directly as it will dynamically create
+				// previous block nodes as needed.  This helps
+				// allow only the pieces of the chain that are
+				// needed to remain in memory.
+				countNode, err = b.getPrevNodeFromNode(countNode)
+				if err != nil {
+					return ThresholdFailed, err
+				}
+			}
+
+			// The state is locked in if the number of blocks in the
+			// period that voted for the rule change meets the
+			// activation threshold.
+			if count >= checker.RuleChangeActivationThreshold() {
+				state = ThresholdLockedIn
+			}
+
+		case ThresholdLockedIn:
+			// The new rule becomes active when its previous state
+			// was locked in.
+			state = ThresholdActive
+
+		// Nothing to do if the previous state is active or failed since
+		// they are both terminal states.
+		case ThresholdActive:
+		case ThresholdFailed:
+		}
+
+		// Update the cache to avoid recalculating the state in the
+		// future.
+		cache.Update(*prevNode.hash, state)
+	}
+
+	return state, nil
+}
+
+// ThresholdState returns the current rule change threshold state of the given
+// deployment ID for the end of the current best chain.
+//
+// This function is safe for concurrent access.
+func (b *BlockChain) ThresholdState(deploymentID uint32) (ThresholdState, error) {
+	if deploymentID > uint32(len(b.chainParams.Deployments)) {
+		return ThresholdFailed, DeploymentError(deploymentID)
+	}
+	deployment := &b.chainParams.Deployments[deploymentID]
+	checker := deploymentChecker{deployment: deployment, chain: b}
+	cache := &b.deploymentCaches[deploymentID]
+	b.chainLock.Lock()
+	state, err := b.thresholdState(b.bestNode, checker, cache)
+	b.chainLock.Unlock()
+	return state, err
+}
diff --git a/blockchain/thresholdstate_test.go b/blockchain/thresholdstate_test.go
new file mode 100644
index 000000000..e2bf4f347
--- /dev/null
+++ b/blockchain/thresholdstate_test.go
@@ -0,0 +1,195 @@
+// Copyright (c) 2016 The btcsuite developers
+// Use of this source code is governed by an ISC
+// license that can be found in the LICENSE file.
+
+package blockchain
+
+import (
+	"testing"
+
+	"github.com/btcsuite/btcd/chaincfg/chainhash"
+)
+
+// TestThresholdStateStringer tests the stringized output for the
+// ThresholdState type.
+func TestThresholdStateStringer(t *testing.T) {
+	t.Parallel()
+
+	tests := []struct {
+		in   ThresholdState
+		want string
+	}{
+		{ThresholdDefined, "ThresholdDefined"},
+		{ThresholdStarted, "ThresholdStarted"},
+		{ThresholdLockedIn, "ThresholdLockedIn"},
+		{ThresholdActive, "ThresholdActive"},
+		{ThresholdFailed, "ThresholdFailed"},
+		{0xff, "Unknown ThresholdState (255)"},
+	}
+
+	// Detect additional threshold states that don't have the stringer added.
+	if len(tests)-1 != int(numThresholdsStates) {
+		t.Errorf("It appears a threshold statewas added without " +
+			"adding an associated stringer test")
+	}
+
+	t.Logf("Running %d tests", len(tests))
+	for i, test := range tests {
+		result := test.in.String()
+		if result != test.want {
+			t.Errorf("String #%d\n got: %s want: %s", i, result,
+				test.want)
+			continue
+		}
+	}
+}
+
+// TestThresholdStateCache ensure the threshold state cache works as intended
+// including adding entries, updating existing entries, and flushing.
+func TestThresholdStateCache(t *testing.T) {
+	t.Parallel()
+
+	tests := []struct {
+		name       string
+		numEntries int
+		state      ThresholdState
+	}{
+		{name: "2 entries defined", numEntries: 2, state: ThresholdDefined},
+		{name: "7 entries started", numEntries: 7, state: ThresholdStarted},
+		{name: "10 entries active", numEntries: 10, state: ThresholdActive},
+		{name: "5 entries locked in", numEntries: 5, state: ThresholdLockedIn},
+		{name: "3 entries failed", numEntries: 3, state: ThresholdFailed},
+	}
+
+nextTest:
+	for _, test := range tests {
+		cache := &newThresholdCaches(1)[0]
+		for i := 0; i < test.numEntries; i++ {
+			var hash chainhash.Hash
+			hash[0] = uint8(i + 1)
+
+			// Ensure the hash isn't available in the cache already.
+			_, ok := cache.Lookup(hash)
+			if ok {
+				t.Errorf("Lookup (%s): has entry for hash %v",
+					test.name, hash)
+				continue nextTest
+			}
+
+			// Ensure hash that was added to the cache reports it's
+			// available and the state is the expected value.
+			cache.Update(hash, test.state)
+			state, ok := cache.Lookup(hash)
+			if !ok {
+				t.Errorf("Lookup (%s): missing entry for hash "+
+					"%v", test.name, hash)
+				continue nextTest
+			}
+			if state != test.state {
+				t.Errorf("Lookup (%s): state mismatch - got "+
+					"%v, want %v", test.name, state,
+					test.state)
+				continue nextTest
+			}
+
+			// Ensure the update is also added to the internal
+			// database updates map and its state matches.
+			state, ok = cache.dbUpdates[hash]
+			if !ok {
+				t.Errorf("dbUpdates (%s): missing entry for "+
+					"hash %v", test.name, hash)
+				continue nextTest
+			}
+			if state != test.state {
+				t.Errorf("dbUpdates (%s): state mismatch - "+
+					"got %v, want %v", test.name, state,
+					test.state)
+				continue nextTest
+			}
+
+			// Ensure flushing the cache removes all entries from
+			// the internal database updates map.
+			cache.MarkFlushed()
+			if len(cache.dbUpdates) != 0 {
+				t.Errorf("dbUpdates (%s): unflushed entries",
+					test.name)
+				continue nextTest
+			}
+
+			// Ensure hash is still available in the cache and the
+			// state is the expected value.
+			state, ok = cache.Lookup(hash)
+			if !ok {
+				t.Errorf("Lookup (%s): missing entry after "+
+					"flush for hash %v", test.name, hash)
+				continue nextTest
+			}
+			if state != test.state {
+				t.Errorf("Lookup (%s): state mismatch after "+
+					"flush - got %v, want %v", test.name,
+					state, test.state)
+				continue nextTest
+			}
+
+			// Ensure adding an existing hash with the same state
+			// doesn't break the existing entry and it is NOT added
+			// to the database updates map.
+			cache.Update(hash, test.state)
+			state, ok = cache.Lookup(hash)
+			if !ok {
+				t.Errorf("Lookup (%s): missing entry after "+
+					"second add for hash %v", test.name,
+					hash)
+				continue nextTest
+			}
+			if state != test.state {
+				t.Errorf("Lookup (%s): state mismatch after "+
+					"second add - got %v, want %v",
+					test.name, state, test.state)
+				continue nextTest
+			}
+			if len(cache.dbUpdates) != 0 {
+				t.Errorf("dbUpdates (%s): unflushed entries "+
+					"after duplicate add", test.name)
+				continue nextTest
+			}
+
+			// Ensure adding an existing hash with a different state
+			// updates the existing entry.
+			newState := ThresholdFailed
+			if newState == test.state {
+				newState = ThresholdStarted
+			}
+			cache.Update(hash, newState)
+			state, ok = cache.Lookup(hash)
+			if !ok {
+				t.Errorf("Lookup (%s): missing entry after "+
+					"state change for hash %v", test.name,
+					hash)
+				continue nextTest
+			}
+			if state != newState {
+				t.Errorf("Lookup (%s): state mismatch after "+
+					"state change - got %v, want %v",
+					test.name, state, newState)
+				continue nextTest
+			}
+
+			// Ensure the update is also added to the internal
+			// database updates map and its state matches.
+			state, ok = cache.dbUpdates[hash]
+			if !ok {
+				t.Errorf("dbUpdates (%s): missing entry after "+
+					"state change for hash %v", test.name,
+					hash)
+				continue nextTest
+			}
+			if state != newState {
+				t.Errorf("dbUpdates (%s): state mismatch "+
+					"after state change - got %v, want %v",
+					test.name, state, newState)
+				continue nextTest
+			}
+		}
+	}
+}
diff --git a/blockchain/versionbits.go b/blockchain/versionbits.go
new file mode 100644
index 000000000..c2ad2cde2
--- /dev/null
+++ b/blockchain/versionbits.go
@@ -0,0 +1,316 @@
+// Copyright (c) 2016 The btcsuite developers
+// Use of this source code is governed by an ISC
+// license that can be found in the LICENSE file.
+
+package blockchain
+
+import (
+	"math"
+
+	"github.com/btcsuite/btcd/chaincfg"
+)
+
+const (
+	// vbLegacyBlockVersion is the highest legacy block version before the
+	// version bits scheme became active.
+	vbLegacyBlockVersion = 4
+
+	// vbTopBits defines the bits to set in the version to signal that the
+	// version bits scheme is being used.
+	vbTopBits = 0x20000000
+
+	// vbTopMask is the bitmask to use to determine whether or not the
+	// version bits scheme is in use.
+	vbTopMask = 0xe0000000
+
+	// vbNumBits is the total number of bits available for use with the
+	// version bits scheme.
+	vbNumBits = 29
+
+	// unknownVerNumToCheck is the number of previous blocks to consider
+	// when checking for a threshold of unknown block versions for the
+	// purposes of warning the user.
+	unknownVerNumToCheck = 100
+
+	// unknownVerWarnNum is the threshold of previous blocks that have an
+	// unknown version to use for the purposes of warning the user.
+	unknownVerWarnNum = unknownVerNumToCheck / 2
+)
+
+// bitConditionChecker provides a thresholdConditionChecker which can be used to
+// test whether or not a specific bit is set when it's not supposed to be
+// according to the expected version based on the known deployments and the
+// current state of the chain.  This is useful for detecting and warning about
+// unknown rule activations.
+type bitConditionChecker struct {
+	bit   uint32
+	chain *BlockChain
+}
+
+// Ensure the bitConditionChecker type implements the thresholdConditionChecker
+// interface.
+var _ thresholdConditionChecker = bitConditionChecker{}
+
+// BeginTime returns the unix timestamp for the median block time after which
+// voting on a rule change starts (at the next window).
+//
+// Since this implementation checks for unknown rules, it returns 0 so the rule
+// is always treated as active.
+//
+// This is part of the thresholdConditionChecker interface implementation.
+func (c bitConditionChecker) BeginTime() uint64 {
+	return 0
+}
+
+// EndTime returns the unix timestamp for the median block time after which an
+// attempted rule change fails if it has not already been locked in or
+// activated.
+//
+// Since this implementation checks for unknown rules, it returns the maximum
+// possible timestamp so the rule is always treated as active.
+//
+// This is part of the thresholdConditionChecker interface implementation.
+func (c bitConditionChecker) EndTime() uint64 {
+	return math.MaxUint64
+}
+
+// RuleChangeActivationThreshold is the number of blocks for which the condition
+// must be true in order to lock in a rule change.
+//
+// This implementation returns the value defined by the chain params the checker
+// is associated with.
+//
+// This is part of the thresholdConditionChecker interface implementation.
+func (c bitConditionChecker) RuleChangeActivationThreshold() uint32 {
+	return c.chain.chainParams.RuleChangeActivationThreshold
+}
+
+// MinerConfirmationWindow is the number of blocks in each threshold state
+// retarget window.
+//
+// This implementation returns the value defined by the chain params the checker
+// is associated with.
+//
+// This is part of the thresholdConditionChecker interface implementation.
+func (c bitConditionChecker) MinerConfirmationWindow() uint32 {
+	return c.chain.chainParams.MinerConfirmationWindow
+}
+
+// Condition returns true when the specific bit associated with the checker is
+// set and it's not supposed to be according to the expected version based on
+// the known deployments and the current state of the chain.
+//
+// This function MUST be called with the chain state lock held (for writes).
+//
+// This is part of the thresholdConditionChecker interface implementation.
+func (c bitConditionChecker) Condition(node *blockNode) (bool, error) {
+	conditionMask := uint32(1) << c.bit
+	version := uint32(node.version)
+	if version&vbTopMask != vbTopBits {
+		return false, nil
+	}
+	if version&conditionMask == 0 {
+		return false, nil
+	}
+
+	// Get the previous block node.  This function is used over simply
+	// accessing node.parent directly as it will dynamically create previous
+	// block nodes as needed.  This helps allow only the pieces of the chain
+	// that are needed to remain in memory.
+	prevNode, err := c.chain.getPrevNodeFromNode(node)
+	if err != nil {
+		return false, err
+	}
+	expectedVersion, err := c.chain.calcNextBlockVersion(prevNode)
+	if err != nil {
+		return false, err
+	}
+	return uint32(expectedVersion)&conditionMask == 0, nil
+}
+
+// deploymentChecker provides a thresholdConditionChecker which can be used to
+// test a specific deployment rule.  This is required for properly detecting
+// and activating consensus rule changes.
+type deploymentChecker struct {
+	deployment *chaincfg.ConsensusDeployment
+	chain      *BlockChain
+}
+
+// Ensure the deploymentChecker type implements the thresholdConditionChecker
+// interface.
+var _ thresholdConditionChecker = deploymentChecker{}
+
+// BeginTime returns the unix timestamp for the median block time after which
+// voting on a rule change starts (at the next window).
+//
+// This implementation returns the value defined by the specific deployment the
+// checker is associated with.
+//
+// This is part of the thresholdConditionChecker interface implementation.
+func (c deploymentChecker) BeginTime() uint64 {
+	return c.deployment.StartTime
+}
+
+// EndTime returns the unix timestamp for the median block time after which an
+// attempted rule change fails if it has not already been locked in or
+// activated.
+//
+// This implementation returns the value defined by the specific deployment the
+// checker is associated with.
+//
+// This is part of the thresholdConditionChecker interface implementation.
+func (c deploymentChecker) EndTime() uint64 {
+	return c.deployment.ExpireTime
+}
+
+// RuleChangeActivationThreshold is the number of blocks for which the condition
+// must be true in order to lock in a rule change.
+//
+// This implementation returns the value defined by the chain params the checker
+// is associated with.
+//
+// This is part of the thresholdConditionChecker interface implementation.
+func (c deploymentChecker) RuleChangeActivationThreshold() uint32 {
+	return c.chain.chainParams.RuleChangeActivationThreshold
+}
+
+// MinerConfirmationWindow is the number of blocks in each threshold state
+// retarget window.
+//
+// This implementation returns the value defined by the chain params the checker
+// is associated with.
+//
+// This is part of the thresholdConditionChecker interface implementation.
+func (c deploymentChecker) MinerConfirmationWindow() uint32 {
+	return c.chain.chainParams.MinerConfirmationWindow
+}
+
+// Condition returns true when the specific bit defined by the deployment
+// associated with the checker is set.
+//
+// This is part of the thresholdConditionChecker interface implementation.
+func (c deploymentChecker) Condition(node *blockNode) (bool, error) {
+	conditionMask := uint32(1) << c.deployment.BitNumber
+	version := uint32(node.version)
+	return (version&vbTopMask == vbTopBits) && (version&conditionMask != 0),
+		nil
+}
+
+// calcNextBlockVersion calculates the expected version of the block after the
+// passed previous block node based on the state of started and locked in
+// rule change deployments.
+//
+// This function differs from the exported CalcNextBlockVersion in that the
+// exported version uses the current best chain as the previous block node
+// while this function accepts any block node.
+//
+// This function MUST be called with the chain state lock held (for writes).
+func (b *BlockChain) calcNextBlockVersion(prevNode *blockNode) (int32, error) {
+	// Set the appropriate bits for each actively defined rule deployment
+	// that is either in the process of being voted on, or locked in for the
+	// activation at the next threshold window change.
+	expectedVersion := uint32(vbTopBits)
+	for id := 0; id < len(b.chainParams.Deployments); id++ {
+		deployment := &b.chainParams.Deployments[id]
+		cache := &b.deploymentCaches[id]
+		checker := deploymentChecker{deployment: deployment, chain: b}
+		state, err := b.thresholdState(prevNode, checker, cache)
+		if err != nil {
+			return 0, err
+		}
+		if state == ThresholdStarted || state == ThresholdLockedIn {
+			expectedVersion |= uint32(1) << deployment.BitNumber
+		}
+	}
+	return int32(expectedVersion), nil
+}
+
+// CalcNextBlockVersion calculates the expected version of the block after the
+// end of the current best chain based on the state of started and locked in
+// rule change deployments.
+//
+// This function is safe for concurrent access.
+func (b *BlockChain) CalcNextBlockVersion() (int32, error) {
+	b.chainLock.Lock()
+	version, err := b.calcNextBlockVersion(b.bestNode)
+	b.chainLock.Unlock()
+	return version, err
+}
+
+// warnUnknownRuleActivations displays a warning when any unknown new rules are
+// either about to activate or have been activated.  This will only happen once
+// when new rules have been activated and every block for those about to be
+// activated.
+//
+// This function MUST be called with the chain state lock held (for writes)
+func (b *BlockChain) warnUnknownRuleActivations(node *blockNode) error {
+	// Warn if any unknown new rules are either about to activate or have
+	// already been activated.
+	for bit := uint32(0); bit < vbNumBits; bit++ {
+		checker := bitConditionChecker{bit: bit, chain: b}
+		cache := &b.warningCaches[bit]
+		state, err := b.thresholdState(node, checker, cache)
+		if err != nil {
+			return err
+		}
+
+		switch state {
+		case ThresholdActive:
+			if !b.unknownRulesWarned {
+				log.Warnf("Unknown new rules activated (bit %d)",
+					bit)
+				b.unknownRulesWarned = true
+			}
+
+		case ThresholdLockedIn:
+			window := int32(checker.MinerConfirmationWindow())
+			activationHeight := window - (node.height % window)
+			log.Warnf("Unknown new rules are about to activate in "+
+				"%d blocks (bit %d)", bit, activationHeight)
+		}
+	}
+
+	return nil
+}
+
+// warnUnknownVersions logs a warning if a high enough percentage of the last
+// blocks have unexpected versions.
+//
+// This function MUST be called with the chain state lock held (for writes)
+func (b *BlockChain) warnUnknownVersions(node *blockNode) error {
+	// Nothing to do if already warned.
+	if b.unknownVersionsWarned {
+		return nil
+	}
+
+	// Warn if enough previous blocks have unexpected versions.
+	numUpgraded := uint32(0)
+	for i := uint32(0); i < unknownVerNumToCheck && node != nil; i++ {
+		expectedVersion, err := b.calcNextBlockVersion(node.parent)
+		if err != nil {
+			return err
+		}
+		if expectedVersion > vbLegacyBlockVersion &&
+			(node.version & ^expectedVersion) != 0 {
+
+			numUpgraded++
+		}
+
+		// Get the previous block node.  This function is used over
+		// simply accessing node.parent directly as it will dynamically
+		// create previous block nodes as needed.  This helps allow only
+		// the pieces of the chain that are needed to remain in memory.
+		node, err = b.getPrevNodeFromNode(node)
+		if err != nil {
+			return err
+		}
+	}
+	if numUpgraded > unknownVerWarnNum {
+		log.Warn("Unknown block versions are being mined, so new " +
+			"rules might be in effect.  Are you running the " +
+			"latest version of the software?")
+		b.unknownVersionsWarned = true
+	}
+
+	return nil
+}
diff --git a/chaincfg/params.go b/chaincfg/params.go
index 7165f4a7d..da2959bf9 100644
--- a/chaincfg/params.go
+++ b/chaincfg/params.go
@@ -6,6 +6,7 @@ package chaincfg
 
 import (
 	"errors"
+	"math"
 	"math/big"
 	"time"
 
@@ -60,6 +61,37 @@ type DNSSeed struct {
 	HasFiltering bool
 }
 
+// ConsensusDeployment defines details related to a specific consensus rule
+// change that is voted in.  This is part of BIP0009.
+type ConsensusDeployment struct {
+	// BitNumber defines the specific bit number within the block version
+	// this particular soft-fork deployment refers to.
+	BitNumber uint8
+
+	// StartTime is the median block time after which voting on the
+	// deployment starts.
+	StartTime uint64
+
+	// ExpireTime is the median block time after which the attempted
+	// deployment expires.
+	ExpireTime uint64
+}
+
+// Constants that define the deployment offset in the deployments field of the
+// parameters for each deployment.  This is useful to be able to get the details
+// of a specific deployment by name.
+const (
+	// DeploymentTestDummy defines the rule change deployment ID for testing
+	// purposes.
+	DeploymentTestDummy = iota
+
+	// NOTE: DefinedDeployments must always come last since it is used to
+	// determine how many defined deployments there currently are.
+
+	// DefinedDeployments is the number of currently defined deployments.
+	DefinedDeployments
+)
+
 // Params defines a Bitcoin network by its parameters.  These parameters may be
 // used by Bitcoin applications to differentiate networks as well as addresses
 // and keys for one network from those intended for use on another network.
@@ -143,6 +175,23 @@ type Params struct {
 	// The number of nodes to check.  This is part of BIP0034.
 	BlockUpgradeNumToCheck uint64
 
+	// These fields are related to voting on consensus rule changes as
+	// defined by BIP0009.
+	//
+	// RuleChangeActivationThreshold is the number of blocks in a threshold
+	// state retarget window for which a positive vote for a rule change
+	// must be cast in order to lock in a rule change. It should typically
+	// be 95% for the main network and 75% for test networks.
+	//
+	// MinerConfirmationWindow is the number of blocks in each threshold
+	// state retarget window.
+	//
+	// Deployments define the specific consensus rule changes to be voted
+	// on.
+	RuleChangeActivationThreshold uint32
+	MinerConfirmationWindow       uint32
+	Deployments                   [DefinedDeployments]ConsensusDeployment
+
 	// Mempool parameters
 	RelayNonStdTxs bool
 
@@ -221,6 +270,20 @@ var MainNetParams = Params{
 	BlockRejectNumRequired:  950,
 	BlockUpgradeNumToCheck:  1000,
 
+	// Consensus rule change deployments.
+	//
+	// The miner confirmation window is defined as:
+	//   target proof of work timespan / target proof of work spacing
+	RuleChangeActivationThreshold: 1916, // 95% of MinerConfirmationWindow
+	MinerConfirmationWindow:       2016, //
+	Deployments: [DefinedDeployments]ConsensusDeployment{
+		DeploymentTestDummy: {
+			BitNumber:  28,
+			StartTime:  1199145601, // January 1, 2008 UTC
+			ExpireTime: 1230767999, // December 31, 2008 UTC
+		},
+	},
+
 	// Mempool parameters
 	RelayNonStdTxs: false,
 
@@ -274,6 +337,20 @@ var RegressionNetParams = Params{
 	BlockRejectNumRequired:  950,
 	BlockUpgradeNumToCheck:  1000,
 
+	// Consensus rule change deployments.
+	//
+	// The miner confirmation window is defined as:
+	//   target proof of work timespan / target proof of work spacing
+	RuleChangeActivationThreshold: 108, // 75%  of MinerConfirmationWindow
+	MinerConfirmationWindow:       144,
+	Deployments: [DefinedDeployments]ConsensusDeployment{
+		DeploymentTestDummy: {
+			BitNumber:  28,
+			StartTime:  0,             // Always available for vote
+			ExpireTime: math.MaxInt64, // Never expires
+		},
+	},
+
 	// Mempool parameters
 	RelayNonStdTxs: true,
 
@@ -334,6 +411,20 @@ var TestNet3Params = Params{
 	BlockRejectNumRequired:  75,
 	BlockUpgradeNumToCheck:  100,
 
+	// Consensus rule change deployments.
+	//
+	// The miner confirmation window is defined as:
+	//   target proof of work timespan / target proof of work spacing
+	RuleChangeActivationThreshold: 1512, // 75% of MinerConfirmationWindow
+	MinerConfirmationWindow:       2016,
+	Deployments: [DefinedDeployments]ConsensusDeployment{
+		DeploymentTestDummy: {
+			BitNumber:  28,
+			StartTime:  1199145601, // January 1, 2008 UTC
+			ExpireTime: 1230767999, // December 31, 2008 UTC
+		},
+	},
+
 	// Mempool parameters
 	RelayNonStdTxs: true,
 
@@ -391,6 +482,20 @@ var SimNetParams = Params{
 	BlockRejectNumRequired:  75,
 	BlockUpgradeNumToCheck:  100,
 
+	// Consensus rule change deployments.
+	//
+	// The miner confirmation window is defined as:
+	//   target proof of work timespan / target proof of work spacing
+	RuleChangeActivationThreshold: 75, // 75% of MinerConfirmationWindow
+	MinerConfirmationWindow:       100,
+	Deployments: [DefinedDeployments]ConsensusDeployment{
+		DeploymentTestDummy: {
+			BitNumber:  28,
+			StartTime:  0,             // Always available for vote
+			ExpireTime: math.MaxInt64, // Never expires
+		},
+	},
+
 	// Mempool parameters
 	RelayNonStdTxs: true,
 
diff --git a/mining/mining.go b/mining/mining.go
index 4e8d99fa8..e3843ab07 100644
--- a/mining/mining.go
+++ b/mining/mining.go
@@ -22,14 +22,6 @@ const (
 	// transaction to be considered high priority.
 	MinHighPriority = btcutil.SatoshiPerBitcoin * 144.0 / 250
 
-	// generatedBlockVersion is the version of the block being generated.
-	// It is defined as a constant here rather than using the
-	// wire.BlockVersion constant since a change in the block version
-	// will require changes to the generated block.  Using the wire constant
-	// for generated block version could allow creation of invalid blocks
-	// for the updated version.
-	generatedBlockVersion = 4
-
 	// blockHeaderOverhead is the max number of bytes it takes to serialize
 	// a block header and max possible transaction count.
 	blockHeaderOverhead = wire.MaxBlockHeaderPayload + wire.MaxVarIntPayload
@@ -766,11 +758,18 @@ mempoolLoop:
 		return nil, err
 	}
 
+	// Calculate the next expected block version based on the state of the
+	// rule change deployments.
+	nextBlockVersion, err := g.chain.CalcNextBlockVersion()
+	if err != nil {
+		return nil, err
+	}
+
 	// Create a new block ready to be solved.
 	merkles := blockchain.BuildMerkleTreeStore(blockTxns)
 	var msgBlock wire.MsgBlock
 	msgBlock.Header = wire.BlockHeader{
-		Version:    generatedBlockVersion,
+		Version:    nextBlockVersion,
 		PrevBlock:  *prevHash,
 		MerkleRoot: *merkles[len(merkles)-1],
 		Timestamp:  ts,
diff --git a/peer/peer_test.go b/peer/peer_test.go
index 955613c98..71fa71e42 100644
--- a/peer/peer_test.go
+++ b/peer/peer_test.go
@@ -454,7 +454,8 @@ func TestPeerListeners(t *testing.T) {
 		},
 		{
 			"OnBlock",
-			wire.NewMsgBlock(wire.NewBlockHeader(&chainhash.Hash{}, &chainhash.Hash{}, 1, 1)),
+			wire.NewMsgBlock(wire.NewBlockHeader(1,
+				&chainhash.Hash{}, &chainhash.Hash{}, 1, 1)),
 		},
 		{
 			"OnInv",
@@ -498,7 +499,8 @@ func TestPeerListeners(t *testing.T) {
 		},
 		{
 			"OnMerkleBlock",
-			wire.NewMsgMerkleBlock(wire.NewBlockHeader(&chainhash.Hash{}, &chainhash.Hash{}, 1, 1)),
+			wire.NewMsgMerkleBlock(wire.NewBlockHeader(1,
+				&chainhash.Hash{}, &chainhash.Hash{}, 1, 1)),
 		},
 		// only one version message is allowed
 		// only one verack message is allowed
diff --git a/wire/bench_test.go b/wire/bench_test.go
index cec8560ee..517fafa47 100644
--- a/wire/bench_test.go
+++ b/wire/bench_test.go
@@ -419,7 +419,7 @@ func BenchmarkDecodeHeaders(b *testing.B) {
 		if err != nil {
 			b.Fatalf("NewHashFromStr: unexpected error: %v", err)
 		}
-		m.AddBlockHeader(NewBlockHeader(hash, hash, 0, uint32(i)))
+		m.AddBlockHeader(NewBlockHeader(1, hash, hash, 0, uint32(i)))
 	}
 
 	// Serialize it so the bytes are available to test the decode below.
@@ -565,7 +565,7 @@ func BenchmarkDecodeMerkleBlock(b *testing.B) {
 	if err != nil {
 		b.Fatalf("NewHashFromStr: unexpected error: %v", err)
 	}
-	m.Header = *NewBlockHeader(hash, hash, 0, uint32(10000))
+	m.Header = *NewBlockHeader(1, hash, hash, 0, uint32(10000))
 	for i := 0; i < 105; i++ {
 		hash, err := chainhash.NewHashFromStr(fmt.Sprintf("%x", i))
 		if err != nil {
diff --git a/wire/blockheader.go b/wire/blockheader.go
index ba67ee48c..c2c0ae033 100644
--- a/wire/blockheader.go
+++ b/wire/blockheader.go
@@ -12,9 +12,6 @@ import (
 	"github.com/btcsuite/btcd/chaincfg/chainhash"
 )
 
-// BlockVersion is the current latest supported block version.
-const BlockVersion = 4
-
 // MaxBlockHeaderPayload is the maximum number of bytes a block header can be.
 // Version 4 bytes + Timestamp 4 bytes + Bits 4 bytes + Nonce 4 bytes +
 // PrevBlock and MerkleRoot hashes.
@@ -95,16 +92,16 @@ func (h *BlockHeader) Serialize(w io.Writer) error {
 	return writeBlockHeader(w, 0, h)
 }
 
-// NewBlockHeader returns a new BlockHeader using the provided previous block
-// hash, merkle root hash, difficulty bits, and nonce used to generate the
+// NewBlockHeader returns a new BlockHeader using the provided version, previous
+// block hash, merkle root hash, difficulty bits, and nonce used to generate the
 // block with defaults for the remaining fields.
-func NewBlockHeader(prevHash *chainhash.Hash, merkleRootHash *chainhash.Hash,
+func NewBlockHeader(version int32, prevHash, merkleRootHash *chainhash.Hash,
 	bits uint32, nonce uint32) *BlockHeader {
 
 	// Limit the timestamp to one second precision since the protocol
 	// doesn't support better.
 	return &BlockHeader{
-		Version:    BlockVersion,
+		Version:    version,
 		PrevBlock:  *prevHash,
 		MerkleRoot: *merkleRootHash,
 		Timestamp:  time.Unix(time.Now().Unix(), 0),
diff --git a/wire/blockheader_test.go b/wire/blockheader_test.go
index a37082332..2035b1aa7 100644
--- a/wire/blockheader_test.go
+++ b/wire/blockheader_test.go
@@ -24,7 +24,7 @@ func TestBlockHeader(t *testing.T) {
 	hash := mainNetGenesisHash
 	merkleHash := mainNetGenesisMerkleRoot
 	bits := uint32(0x1d00ffff)
-	bh := NewBlockHeader(&hash, &merkleHash, bits, nonce)
+	bh := NewBlockHeader(1, &hash, &merkleHash, bits, nonce)
 
 	// Ensure we get the same data back out.
 	if !bh.PrevBlock.IsEqual(&hash) {
diff --git a/wire/message_test.go b/wire/message_test.go
index e5f338b08..83005f4d1 100644
--- a/wire/message_test.go
+++ b/wire/message_test.go
@@ -66,7 +66,7 @@ func TestMessage(t *testing.T) {
 	msgFilterAdd := NewMsgFilterAdd([]byte{0x01})
 	msgFilterClear := NewMsgFilterClear()
 	msgFilterLoad := NewMsgFilterLoad([]byte{0x01}, 10, 0, BloomUpdateNone)
-	bh := NewBlockHeader(&chainhash.Hash{}, &chainhash.Hash{}, 0, 0)
+	bh := NewBlockHeader(1, &chainhash.Hash{}, &chainhash.Hash{}, 0, 0)
 	msgMerkleBlock := NewMsgMerkleBlock(bh)
 	msgReject := NewMsgReject("block", RejectDuplicate, "duplicate block")
 
diff --git a/wire/msgblock_test.go b/wire/msgblock_test.go
index bef0deeda..9c07e0c39 100644
--- a/wire/msgblock_test.go
+++ b/wire/msgblock_test.go
@@ -24,7 +24,7 @@ func TestBlock(t *testing.T) {
 	merkleHash := &blockOne.Header.MerkleRoot
 	bits := blockOne.Header.Bits
 	nonce := blockOne.Header.Nonce
-	bh := NewBlockHeader(prevHash, merkleHash, bits, nonce)
+	bh := NewBlockHeader(1, prevHash, merkleHash, bits, nonce)
 
 	// Ensure the command is expected value.
 	wantCmd := "block"
diff --git a/wire/msgheaders_test.go b/wire/msgheaders_test.go
index 481d89a57..99e26c941 100644
--- a/wire/msgheaders_test.go
+++ b/wire/msgheaders_test.go
@@ -66,7 +66,7 @@ func TestHeadersWire(t *testing.T) {
 	merkleHash := blockOne.Header.MerkleRoot
 	bits := uint32(0x1d00ffff)
 	nonce := uint32(0x9962e301)
-	bh := NewBlockHeader(&hash, &merkleHash, bits, nonce)
+	bh := NewBlockHeader(1, &hash, &merkleHash, bits, nonce)
 	bh.Version = blockOne.Header.Version
 	bh.Timestamp = blockOne.Header.Timestamp
 
@@ -223,7 +223,7 @@ func TestHeadersWireErrors(t *testing.T) {
 	merkleHash := blockOne.Header.MerkleRoot
 	bits := uint32(0x1d00ffff)
 	nonce := uint32(0x9962e301)
-	bh := NewBlockHeader(&hash, &merkleHash, bits, nonce)
+	bh := NewBlockHeader(1, &hash, &merkleHash, bits, nonce)
 	bh.Version = blockOne.Header.Version
 	bh.Timestamp = blockOne.Header.Timestamp
 
@@ -260,7 +260,7 @@ func TestHeadersWireErrors(t *testing.T) {
 
 	// Intentionally invalid block header that has a transaction count used
 	// to force errors.
-	bhTrans := NewBlockHeader(&hash, &merkleHash, bits, nonce)
+	bhTrans := NewBlockHeader(1, &hash, &merkleHash, bits, nonce)
 	bhTrans.Version = blockOne.Header.Version
 	bhTrans.Timestamp = blockOne.Header.Timestamp
 
diff --git a/wire/msgmerkleblock_test.go b/wire/msgmerkleblock_test.go
index e04ad1ca7..417d2ee1a 100644
--- a/wire/msgmerkleblock_test.go
+++ b/wire/msgmerkleblock_test.go
@@ -25,7 +25,7 @@ func TestMerkleBlock(t *testing.T) {
 	merkleHash := &blockOne.Header.MerkleRoot
 	bits := blockOne.Header.Bits
 	nonce := blockOne.Header.Nonce
-	bh := NewBlockHeader(prevHash, merkleHash, bits, nonce)
+	bh := NewBlockHeader(1, prevHash, merkleHash, bits, nonce)
 
 	// Ensure the command is expected value.
 	wantCmd := "merkleblock"
@@ -117,7 +117,7 @@ func TestMerkleBlockCrossProtocol(t *testing.T) {
 	merkleHash := &blockOne.Header.MerkleRoot
 	bits := blockOne.Header.Bits
 	nonce := blockOne.Header.Nonce
-	bh := NewBlockHeader(prevHash, merkleHash, bits, nonce)
+	bh := NewBlockHeader(1, prevHash, merkleHash, bits, nonce)
 
 	msg := NewMsgMerkleBlock(bh)