mirror of
https://github.com/btcsuite/btcd.git
synced 2024-11-19 09:50:08 +01:00
c6b66ee79c
In this commit, we extend the existing version bits state machine to add support for the new minimum activation height and custom block threshold for activation. We then extend the existing BIP 9 tests (tho this isn't really BIP 9 anymore...) to exercise the new min activation height logic.
327 lines
12 KiB
Go
327 lines
12 KiB
Go
// 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 (
|
|
"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
|
|
)
|
|
|
|
// 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{}
|
|
|
|
// HasStarted returns true if based on the passed block blockNode the consensus
|
|
// is eligible for deployment.
|
|
//
|
|
// Since this implementation checks for unknown rules, it returns true so
|
|
// is always treated as active.
|
|
//
|
|
// This is part of the thresholdConditionChecker interface implementation.
|
|
func (c bitConditionChecker) HasStarted(_ *blockNode) bool {
|
|
return true
|
|
}
|
|
|
|
// HasStarted returns true if based on the passed block blockNode the consensus
|
|
// is eligible for deployment.
|
|
//
|
|
// Since this implementation checks for unknown rules, it returns false so the
|
|
// rule is always treated as active.
|
|
//
|
|
// This is part of the thresholdConditionChecker interface implementation.
|
|
func (c bitConditionChecker) HasEnded(_ *blockNode) bool {
|
|
return false
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// EligibleToActivate returns true if a custom deployment can transition from
|
|
// the LockedIn to the Active state. For normal deployments, this always
|
|
// returns true. However, some deployments add extra rules like a minimum
|
|
// activation height, which can be abstracted into a generic arbitrary check at
|
|
// the final state via this method.
|
|
//
|
|
// This implementation always returns true, as it's used to warn about other
|
|
// unknown deployments.
|
|
//
|
|
// This is part of the thresholdConditionChecker interface implementation.
|
|
func (c bitConditionChecker) EligibleToActivate(blkNode *blockNode) bool {
|
|
return true
|
|
}
|
|
|
|
// IsSpeedy returns true if this is to be a "speedy" deployment. A speedy
|
|
// deployment differs from a regular one in that only after a miner block
|
|
// confirmation window can the deployment expire.
|
|
//
|
|
// This implementation returns false, as we want to always be warned if
|
|
// something is about to activate.
|
|
//
|
|
// This is part of the thresholdConditionChecker interface implementation.
|
|
func (c bitConditionChecker) IsSpeedy() bool {
|
|
return false
|
|
}
|
|
|
|
// 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{}
|
|
|
|
// HasEnded returns true if the target consensus rule change has expired
|
|
// or timed out (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) HasStarted(blkNode *blockNode) bool {
|
|
// Can't fail as we make sure to set the clock above when we
|
|
// instantiate *BlockChain.
|
|
header := blkNode.Header()
|
|
started, _ := c.deployment.DeploymentStarter.HasStarted(&header)
|
|
|
|
return started
|
|
}
|
|
|
|
// HasEnded returns true if the target consensus rule change has expired
|
|
// or timed out.
|
|
//
|
|
// 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) HasEnded(blkNode *blockNode) bool {
|
|
// Can't fail as we make sure to set the clock above when we
|
|
// instantiate *BlockChain.
|
|
header := blkNode.Header()
|
|
ended, _ := c.deployment.DeploymentEnder.HasEnded(&header)
|
|
|
|
return ended
|
|
}
|
|
|
|
// 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 {
|
|
// Some deployments like taproot used a custom activation threshold
|
|
// that ovverides the network level threshold.
|
|
if c.deployment.CustomActivationThreshold != 0 {
|
|
return c.deployment.CustomActivationThreshold
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
// EligibleToActivate returns true if a custom deployment can transition from
|
|
// the LockedIn to the Active state. For normal deployments, this always
|
|
// returns true. However, some deployments add extra rules like a minimum
|
|
// activation height, which can be abstracted into a generic arbitrary check at
|
|
// the final state via this method.
|
|
//
|
|
// This implementation always returns true, unless a minimum activation height
|
|
// is specified.
|
|
//
|
|
// This is part of the thresholdConditionChecker interface implementation.
|
|
func (c deploymentChecker) EligibleToActivate(blkNode *blockNode) bool {
|
|
// No activation height, so it's always ready to go.
|
|
if c.deployment.MinActivationHeight == 0 {
|
|
return true
|
|
}
|
|
|
|
// If the _next_ block (as this is the prior block to the one being
|
|
// connected is the min height or beyond, then this can activate.
|
|
return uint32(blkNode.height)+1 >= c.deployment.MinActivationHeight
|
|
}
|
|
|
|
// IsSpeedy returns true if this is to be a "speedy" deployment. A speedy
|
|
// deployment differs from a regular one in that only after a miner block
|
|
// confirmation window can the deployment expire. This implementation returns
|
|
// true if a min activation height is set.
|
|
//
|
|
// This is part of the thresholdConditionChecker interface implementation.
|
|
func (c deploymentChecker) IsSpeedy() bool {
|
|
return c.deployment.MinActivationHeight != 0
|
|
}
|
|
|
|
// 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
|
|
}
|