0

BIP9(Version Bits)

 2 years ago
source link: https://imnisen.github.io/bip0009.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

4 相关实现

下面代码来自btcd的实现。

用一个 thresholdConditionChecker 接口将要检查的soft fork相关抽象出来

// 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 associated with the condition is set, but can be more complex as
        // needed.
        Condition(*blockNode) (bool, error)
}

实现上述接口的类型

// 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
}

核心计算state的方法:

// thresholdState returns the current rule change threshold state for the block
// AFTER 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.
        prevNode = prevNode.Ancestor(prevNode.height -
                (prevNode.height+1)%confirmationWindow)

        // 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 := prevNode.CalcPastMedianTime()

                // 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 = prevNode.RelativeAncestor(confirmationWindow)
        }

        // 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 := prevNode.CalcPastMedianTime()
                        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 := prevNode.CalcPastMedianTime()
                        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.
                                countNode = countNode.parent
                        }

                        // 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
}




About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK