thresholdstate.go raw

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