func (b *BlockChain) thresholdState(prevNode *blockNode, checker thresholdConditionChecker, cache *thresholdStateCache) (ThresholdState, error) {
	confirmationWindow := int32(checker.MinerConfirmationWindow())
	if prevNode == nil || (prevNode.height+1) < confirmationWindow {
		return ThresholdDefined, nil
	}
	prevNode = prevNode.Ancestor(prevNode.height -
		(prevNode.height+1)%confirmationWindow)
	var neededStates []*blockNode
	for prevNode != nil {
		if _, ok := cache.Lookup(&prevNode.hash); ok {
			break
		}
		medianTime := prevNode.CalcPastMedianTime()
		if uint64(medianTime.Unix()) < checker.BeginTime() {
			cache.Update(&prevNode.hash, ThresholdDefined)
			break
		}
		neededStates = append(neededStates, prevNode)
		prevNode = prevNode.RelativeAncestor(confirmationWindow)
	}
	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))
		}
	}
	for neededNum := len(neededStates) - 1; neededNum >= 0; neededNum-- {
		prevNode := neededStates[neededNum]
		switch state {
		case ThresholdDefined:
			medianTime := prevNode.CalcPastMedianTime()
			medianTimeUnix := uint64(medianTime.Unix())
			if medianTimeUnix >= checker.EndTime() {
				state = ThresholdFailed
				break
			}
			if medianTimeUnix >= checker.BeginTime() {
				state = ThresholdStarted
			}
		case ThresholdStarted:
			medianTime := prevNode.CalcPastMedianTime()
			if uint64(medianTime.Unix()) >= checker.EndTime() {
				state = ThresholdFailed
				break
			}
			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++
				}
				countNode = countNode.parent
			}
			if count >= checker.RuleChangeActivationThreshold() {
				state = ThresholdLockedIn
			}
		case ThresholdLockedIn:
			state = ThresholdActive
		case ThresholdActive:
		case ThresholdFailed:
		}
		cache.Update(&prevNode.hash, state)
	}
	return state, nil
}
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).
package blockchain
import (
	"fmt"
	"github.com/soteria-dag/soterd/chaincfg/chainhash"
)
type ThresholdState byte
const (
	ThresholdDefined ThresholdState = iota
	ThresholdStarted
	ThresholdLockedIn
	ThresholdActive
	ThresholdFailed
	numThresholdsStates
)
var thresholdStateStrings = map[ThresholdState]string{
	ThresholdDefined:  "ThresholdDefined",
	ThresholdStarted:  "ThresholdStarted",
	ThresholdLockedIn: "ThresholdLockedIn",
	ThresholdActive:   "ThresholdActive",
	ThresholdFailed:   "ThresholdFailed",
}
func (t ThresholdState) String() string {
	if s := thresholdStateStrings[t]; s != "" {
		return s
	}
	return fmt.Sprintf("Unknown ThresholdState (%d)", int(t))
}
type thresholdConditionChecker interface {
	BeginTime() uint64
	EndTime() uint64
	RuleChangeActivationThreshold() uint32
	MinerConfirmationWindow() uint32
	Condition(*blockNode) (bool, error)
}
type thresholdStateCache struct {
	entries map[chainhash.Hash]ThresholdState
}
func (c *thresholdStateCache) Lookup(hash *chainhash.Hash) (ThresholdState, bool) {
	state, ok := c.entries[*hash]
	return state, ok
}
func (c *thresholdStateCache) Update(hash *chainhash.Hash, state ThresholdState) {
	c.entries[*hash] = state
}
func newThresholdCaches(numCaches uint32) []thresholdStateCache {
	caches := make([]thresholdStateCache, numCaches)
	for i := 0; i < len(caches); i++ {
		caches[i] = thresholdStateCache{
			entries: make(map[chainhash.Hash]ThresholdState),
		}
	}
	return caches
}