github.com/lbryio/lbcd@v0.22.119/blockchain/thresholdstate.go (about)

     1  // Copyright (c) 2016-2017 The btcsuite developers
     2  // Use of this source code is governed by an ISC
     3  // license that can be found in the LICENSE file.
     4  
     5  package blockchain
     6  
     7  import (
     8  	"fmt"
     9  
    10  	"github.com/lbryio/lbcd/chaincfg/chainhash"
    11  )
    12  
    13  // ThresholdState define the various threshold states used when voting on
    14  // consensus changes.
    15  type ThresholdState byte
    16  
    17  // These constants are used to identify specific threshold states.
    18  const (
    19  	// ThresholdDefined is the first state for each deployment and is the
    20  	// state for the genesis block has by definition for all deployments.
    21  	ThresholdDefined ThresholdState = iota
    22  
    23  	// ThresholdStarted is the state for a deployment once its start time
    24  	// has been reached.
    25  	ThresholdStarted
    26  
    27  	// ThresholdLockedIn is the state for a deployment during the retarget
    28  	// period which is after the ThresholdStarted state period and the
    29  	// number of blocks that have voted for the deployment equal or exceed
    30  	// the required number of votes for the deployment.
    31  	ThresholdLockedIn
    32  
    33  	// ThresholdActive is the state for a deployment for all blocks after a
    34  	// retarget period in which the deployment was in the ThresholdLockedIn
    35  	// state.
    36  	ThresholdActive
    37  
    38  	// ThresholdFailed is the state for a deployment once its expiration
    39  	// time has been reached and it did not reach the ThresholdLockedIn
    40  	// state.
    41  	ThresholdFailed
    42  
    43  	// numThresholdsStates is the maximum number of threshold states used in
    44  	// tests.
    45  	numThresholdsStates
    46  )
    47  
    48  // thresholdStateStrings is a map of ThresholdState values back to their
    49  // constant names for pretty printing.
    50  var thresholdStateStrings = map[ThresholdState]string{
    51  	ThresholdDefined:  "ThresholdDefined",
    52  	ThresholdStarted:  "ThresholdStarted",
    53  	ThresholdLockedIn: "ThresholdLockedIn",
    54  	ThresholdActive:   "ThresholdActive",
    55  	ThresholdFailed:   "ThresholdFailed",
    56  }
    57  
    58  // String returns the ThresholdState as a human-readable name.
    59  func (t ThresholdState) String() string {
    60  	if s := thresholdStateStrings[t]; s != "" {
    61  		return s
    62  	}
    63  	return fmt.Sprintf("Unknown ThresholdState (%d)", int(t))
    64  }
    65  
    66  // thresholdConditionChecker provides a generic interface that is invoked to
    67  // determine when a consensus rule change threshold should be changed.
    68  type thresholdConditionChecker interface {
    69  	// BeginTime returns the unix timestamp for the median block time after
    70  	// which voting on a rule change starts (at the next window).
    71  	BeginTime() uint64
    72  
    73  	// EndTime returns the unix timestamp for the median block time after
    74  	// which an attempted rule change fails if it has not already been
    75  	// locked in or activated.
    76  	EndTime() uint64
    77  
    78  	// RuleChangeActivationThreshold is the number of blocks for which the
    79  	// condition must be true in order to lock in a rule change.
    80  	RuleChangeActivationThreshold() uint32
    81  
    82  	// MinerConfirmationWindow is the number of blocks in each threshold
    83  	// state retarget window.
    84  	MinerConfirmationWindow() uint32
    85  
    86  	// Condition returns whether or not the rule change activation condition
    87  	// has been met.  This typically involves checking whether or not the
    88  	// bit associated with the condition is set, but can be more complex as
    89  	// needed.
    90  	Condition(*blockNode) (bool, error)
    91  }
    92  
    93  // thresholdStateCache provides a type to cache the threshold states of each
    94  // threshold window for a set of IDs.
    95  type thresholdStateCache struct {
    96  	entries map[chainhash.Hash]ThresholdState
    97  }
    98  
    99  // Lookup returns the threshold state associated with the given hash along with
   100  // a boolean that indicates whether or not it is valid.
   101  func (c *thresholdStateCache) Lookup(hash *chainhash.Hash) (ThresholdState, bool) {
   102  	state, ok := c.entries[*hash]
   103  	return state, ok
   104  }
   105  
   106  // Update updates the cache to contain the provided hash to threshold state
   107  // mapping.
   108  func (c *thresholdStateCache) Update(hash *chainhash.Hash, state ThresholdState) {
   109  	c.entries[*hash] = state
   110  }
   111  
   112  // newThresholdCaches returns a new array of caches to be used when calculating
   113  // threshold states.
   114  func newThresholdCaches(numCaches uint32) []thresholdStateCache {
   115  	caches := make([]thresholdStateCache, numCaches)
   116  	for i := 0; i < len(caches); i++ {
   117  		caches[i] = thresholdStateCache{
   118  			entries: make(map[chainhash.Hash]ThresholdState),
   119  		}
   120  	}
   121  	return caches
   122  }
   123  
   124  // thresholdState returns the current rule change threshold state for the block
   125  // AFTER the given node and deployment ID.  The cache is used to ensure the
   126  // threshold states for previous windows are only calculated once.
   127  //
   128  // This function MUST be called with the chain state lock held (for writes).
   129  func (b *BlockChain) thresholdState(prevNode *blockNode, checker thresholdConditionChecker, cache *thresholdStateCache) (ThresholdState, error) {
   130  	// The threshold state for the window that contains the genesis block is
   131  	// defined by definition.
   132  	confirmationWindow := int32(checker.MinerConfirmationWindow())
   133  	if prevNode == nil || (prevNode.height+1) < confirmationWindow {
   134  		return ThresholdDefined, nil
   135  	}
   136  
   137  	// Get the ancestor that is the last block of the previous confirmation
   138  	// window in order to get its threshold state.  This can be done because
   139  	// the state is the same for all blocks within a given window.
   140  	prevNode = prevNode.Ancestor(prevNode.height -
   141  		(prevNode.height+1)%confirmationWindow)
   142  
   143  	// Iterate backwards through each of the previous confirmation windows
   144  	// to find the most recently cached threshold state.
   145  	var neededStates []*blockNode
   146  	for prevNode != nil {
   147  		// Nothing more to do if the state of the block is already
   148  		// cached.
   149  		if _, ok := cache.Lookup(&prevNode.hash); ok {
   150  			break
   151  		}
   152  
   153  		// The start and expiration times are based on the median block
   154  		// time, so calculate it now.
   155  		medianTime := prevNode.CalcPastMedianTime()
   156  
   157  		// The state is simply defined if the start time hasn't been
   158  		// been reached yet.
   159  		if uint64(medianTime.Unix()) < checker.BeginTime() {
   160  			cache.Update(&prevNode.hash, ThresholdDefined)
   161  			break
   162  		}
   163  
   164  		// Add this node to the list of nodes that need the state
   165  		// calculated and cached.
   166  		neededStates = append(neededStates, prevNode)
   167  
   168  		// Get the ancestor that is the last block of the previous
   169  		// confirmation window.
   170  		prevNode = prevNode.RelativeAncestor(confirmationWindow)
   171  	}
   172  
   173  	// Start with the threshold state for the most recent confirmation
   174  	// window that has a cached state.
   175  	state := ThresholdDefined
   176  	if prevNode != nil {
   177  		var ok bool
   178  		state, ok = cache.Lookup(&prevNode.hash)
   179  		if !ok {
   180  			return ThresholdFailed, AssertError(fmt.Sprintf(
   181  				"thresholdState: cache lookup failed for %v",
   182  				prevNode.hash))
   183  		}
   184  	}
   185  
   186  	// Since each threshold state depends on the state of the previous
   187  	// window, iterate starting from the oldest unknown window.
   188  	for neededNum := len(neededStates) - 1; neededNum >= 0; neededNum-- {
   189  		prevNode := neededStates[neededNum]
   190  
   191  		switch state {
   192  		case ThresholdDefined:
   193  			// The deployment of the rule change fails if it expires
   194  			// before it is accepted and locked in.
   195  			medianTime := prevNode.CalcPastMedianTime()
   196  			medianTimeUnix := uint64(medianTime.Unix())
   197  			if medianTimeUnix >= checker.EndTime() {
   198  				state = ThresholdFailed
   199  				break
   200  			}
   201  
   202  			// The state for the rule moves to the started state
   203  			// once its start time has been reached (and it hasn't
   204  			// already expired per the above).
   205  			if medianTimeUnix >= checker.BeginTime() {
   206  				state = ThresholdStarted
   207  			}
   208  
   209  		case ThresholdStarted:
   210  			// The deployment of the rule change fails if it expires
   211  			// before it is accepted and locked in.
   212  			medianTime := prevNode.CalcPastMedianTime()
   213  			if uint64(medianTime.Unix()) >= checker.EndTime() {
   214  				state = ThresholdFailed
   215  				break
   216  			}
   217  
   218  			// At this point, the rule change is still being voted
   219  			// on by the miners, so iterate backwards through the
   220  			// confirmation window to count all of the votes in it.
   221  			var count uint32
   222  			countNode := prevNode
   223  			for i := int32(0); i < confirmationWindow; i++ {
   224  				condition, err := checker.Condition(countNode)
   225  				if err != nil {
   226  					return ThresholdFailed, err
   227  				}
   228  				if condition {
   229  					count++
   230  				}
   231  
   232  				// Get the previous block node.
   233  				countNode = countNode.parent
   234  			}
   235  
   236  			// The state is locked in if the number of blocks in the
   237  			// period that voted for the rule change meets the
   238  			// activation threshold.
   239  			if count >= checker.RuleChangeActivationThreshold() {
   240  				state = ThresholdLockedIn
   241  			}
   242  
   243  		case ThresholdLockedIn:
   244  			// The new rule becomes active when its previous state
   245  			// was locked in.
   246  			state = ThresholdActive
   247  
   248  		// Nothing to do if the previous state is active or failed since
   249  		// they are both terminal states.
   250  		case ThresholdActive:
   251  		case ThresholdFailed:
   252  		}
   253  
   254  		// Update the cache to avoid recalculating the state in the
   255  		// future.
   256  		cache.Update(&prevNode.hash, state)
   257  	}
   258  
   259  	return state, nil
   260  }
   261  
   262  // ThresholdState returns the current rule change threshold state of the given
   263  // deployment ID for the block AFTER the end of the current best chain.
   264  //
   265  // This function is safe for concurrent access.
   266  func (b *BlockChain) ThresholdState(deploymentID uint32) (ThresholdState, error) {
   267  	b.chainLock.Lock()
   268  	state, err := b.deploymentState(b.bestChain.Tip(), deploymentID)
   269  	b.chainLock.Unlock()
   270  
   271  	return state, err
   272  }
   273  
   274  // IsDeploymentActive returns true if the target deploymentID is active, and
   275  // false otherwise.
   276  //
   277  // This function is safe for concurrent access.
   278  func (b *BlockChain) IsDeploymentActive(deploymentID uint32) (bool, error) {
   279  	b.chainLock.Lock()
   280  	state, err := b.deploymentState(b.bestChain.Tip(), deploymentID)
   281  	b.chainLock.Unlock()
   282  	if err != nil {
   283  		return false, err
   284  	}
   285  
   286  	return state == ThresholdActive, nil
   287  }
   288  
   289  // deploymentState returns the current rule change threshold for a given
   290  // deploymentID. The threshold is evaluated from the point of view of the block
   291  // node passed in as the first argument to this method.
   292  //
   293  // It is important to note that, as the variable name indicates, this function
   294  // expects the block node prior to the block for which the deployment state is
   295  // desired.  In other words, the returned deployment state is for the block
   296  // AFTER the passed node.
   297  //
   298  // This function MUST be called with the chain state lock held (for writes).
   299  func (b *BlockChain) deploymentState(prevNode *blockNode, deploymentID uint32) (ThresholdState, error) {
   300  	if deploymentID > uint32(len(b.chainParams.Deployments)) {
   301  		return ThresholdFailed, DeploymentError(deploymentID)
   302  	}
   303  
   304  	deployment := &b.chainParams.Deployments[deploymentID]
   305  
   306  	// added to mimic LBRYcrd:
   307  	if deployment.ForceActiveAt > 0 && prevNode != nil && prevNode.height+1 >= deployment.ForceActiveAt {
   308  		return ThresholdActive, nil
   309  	}
   310  
   311  	checker := deploymentChecker{deployment: deployment, chain: b}
   312  	cache := &b.deploymentCaches[deploymentID]
   313  
   314  	return b.thresholdState(prevNode, checker, cache)
   315  }
   316  
   317  // initThresholdCaches initializes the threshold state caches for each warning
   318  // bit and defined deployment and provides warnings if the chain is current per
   319  // the warnUnknownRuleActivations function.
   320  func (b *BlockChain) initThresholdCaches() error {
   321  	// Initialize the warning and deployment caches by calculating the
   322  	// threshold state for each of them.  This will ensure the caches are
   323  	// populated and any states that needed to be recalculated due to
   324  	// definition changes is done now.
   325  	prevNode := b.bestChain.Tip().parent
   326  	for bit := uint32(0); bit < vbNumBits; bit++ {
   327  		checker := bitConditionChecker{bit: bit, chain: b}
   328  		cache := &b.warningCaches[bit]
   329  		_, err := b.thresholdState(prevNode, checker, cache)
   330  		if err != nil {
   331  			return err
   332  		}
   333  	}
   334  	for id := 0; id < len(b.chainParams.Deployments); id++ {
   335  		deployment := &b.chainParams.Deployments[id]
   336  		cache := &b.deploymentCaches[id]
   337  		checker := deploymentChecker{deployment: deployment, chain: b}
   338  		_, err := b.thresholdState(prevNode, checker, cache)
   339  		if err != nil {
   340  			return err
   341  		}
   342  	}
   343  
   344  	// No warnings about unknown rules until the chain is current.
   345  	if b.isCurrent() {
   346  		bestNode := b.bestChain.Tip()
   347  
   348  		// Warn if any unknown new rules are either about to activate or
   349  		// have already been activated.
   350  		if err := b.warnUnknownRuleActivations(bestNode); err != nil {
   351  			return err
   352  		}
   353  	}
   354  
   355  	return nil
   356  }