+++ /dev/null
-// Copyright (c) 2016-2017 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
- }
-
- expectedVersion, err := c.chain.calcNextBlockVersion(node.parent)
- 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.bestChain.Tip())
- 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.parent, 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)", activationHeight, bit)
- }
- }
-
- 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++
- }
-
- node = node.parent
- }
- 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
-}