func (b *BlockChain) CalcStakeVersionByHash(hash *chainhash.Hash) (uint32, error) {
	b.chainLock.Lock()
	defer b.chainLock.Unlock()
	return b.calcStakeVersionByHash(hash)
}
CalcStakeVersionByHash calculates the expected stake version for the
provided block hash.

This function is safe for concurrent access.
package blockchain
import (
	"encoding/binary"
	"errors"
	"fmt"
	"github.com/HcashOrg/hcashd/chaincfg/chainhash"
)
var (
	errVoterVersionMajorityNotFound = errors.New("voter version majority " +
		"not found")
	errStakeVersionMajorityNotFound = errors.New("stake version majority " +
		"not found")
)
func stakeMajorityCacheVersionKey(version uint32, hash *chainhash.Hash) [stakeMajorityCacheKeySize]byte {
	key := [stakeMajorityCacheKeySize]byte{}
	binary.LittleEndian.PutUint32(key[0:], version)
	copy(key[4:], hash[:])
	return key
}
func calcWantHeight(stakeValidationHeight, interval, height int64) int64 {
	intervalOffset := stakeValidationHeight % interval
	adjustedHeight := height - intervalOffset - 1
	return (adjustedHeight - ((adjustedHeight + 1) % interval)) +
		intervalOffset
}
func (b *BlockChain) CalcWantHeight(interval, height int64) int64 {
	return calcWantHeight(b.chainParams.StakeValidationHeight, interval,
		height)
}
func (b *BlockChain) findStakeVersionPriorNode(prevNode *blockNode) (*blockNode, error) {
	if !prevNode.isKeyBlock {
		prevNode, _ = b.getPrevKeyNodeFromNode(prevNode)
	}
	nextHeight := prevNode.keyHeight + 2
	if nextHeight < b.chainParams.StakeValidationHeight+
		b.chainParams.StakeVersionInterval {
		return nil, nil
	}
	wantHeight := calcWantHeight(b.chainParams.StakeValidationHeight,
		b.chainParams.StakeVersionInterval, nextHeight)
	iterNode := prevNode
	for iterNode.keyHeight > wantHeight-1 {
		var err error
		iterNode, err = b.getPrevKeyNodeFromNode(iterNode)
		if err != nil {
			return nil, err
		}
	}
	return iterNode, nil
}
func (b *BlockChain) isVoterMajorityVersion(minVer uint32, prevNode *blockNode) bool {
	node, err := b.findStakeVersionPriorNode(prevNode)
	if err != nil {
		return false
	}
	if node == nil {
		return 0 >= minVer
	}
	key := stakeMajorityCacheVersionKey(minVer, &node.hash)
	if result, ok := b.isVoterMajorityVersionCache[key]; ok {
		return result
	}
	totalVotesFound := int32(0)
	versionCount := int32(0)
	iterNode := node
	for i := int64(0); i < b.chainParams.StakeVersionInterval && iterNode != nil; i++ {
		totalVotesFound += int32(len(iterNode.votes))
		for _, v := range iterNode.votes {
			if v.Version >= minVer {
				versionCount += 1
			}
		}
		var err error
		iterNode, err = b.getPrevKeyNodeFromNode(iterNode)
		if err != nil {
			return false
		}
	}
	numRequired := totalVotesFound * b.chainParams.StakeMajorityMultiplier /
		b.chainParams.StakeMajorityDivisor
	result := versionCount >= numRequired
	b.isVoterMajorityVersionCache[key] = result
	return result
}
func (b *BlockChain) isStakeMajorityVersion(minVer uint32, prevNode *blockNode) bool {
	node, err := b.findStakeVersionPriorNode(prevNode)
	if err != nil {
		return false
	}
	if node == nil {
		return 0 >= minVer
	}
	key := stakeMajorityCacheVersionKey(minVer, &node.hash)
	if result, ok := b.isStakeMajorityVersionCache[key]; ok {
		return result
	}
	versionCount := int32(0)
	iterNode := node
	for i := int64(0); i < b.chainParams.StakeVersionInterval && iterNode != nil; i++ {
		if iterNode.header.StakeVersion >= minVer {
			versionCount += 1
		}
		var err error
		iterNode, err = b.getPrevKeyNodeFromNode(iterNode)
		if err != nil {
			b.isStakeMajorityVersionCache[key] = false
			return false
		}
	}
	numRequired := int32(b.chainParams.StakeVersionInterval) *
		b.chainParams.StakeMajorityMultiplier /
		b.chainParams.StakeMajorityDivisor
	result := versionCount >= numRequired
	b.isStakeMajorityVersionCache[key] = result
	return result
}
func (b *BlockChain) calcPriorStakeVersion(prevNode *blockNode) (uint32, error) {
	node, err := b.findStakeVersionPriorNode(prevNode)
	if err != nil {
		return 0, err
	}
	if node == nil {
		return 0, nil
	}
	if result, ok := b.calcPriorStakeVersionCache[node.hash]; ok {
		return result, nil
	}
	versions := make(map[uint32]int32)  
	iterNode := node
	for i := int64(0); i < b.chainParams.StakeVersionInterval && iterNode != nil; i++ {
		versions[iterNode.header.StakeVersion]++
		var err error
		iterNode, err = b.getPrevKeyNodeFromNode(iterNode)
		if err != nil {
			return 0, err
		}
	}
	numRequired := int32(b.chainParams.StakeVersionInterval) *
		b.chainParams.StakeMajorityMultiplier /
		b.chainParams.StakeMajorityDivisor
	for version, count := range versions {
		if count >= numRequired {
			b.calcPriorStakeVersionCache[node.hash] = version
			return version, nil
		}
	}
	return 0, errStakeVersionMajorityNotFound
}
func (b *BlockChain) calcVoterVersionInterval(prevNode *blockNode) (uint32, error) {
	var err error
	if result, ok := b.calcVoterVersionIntervalCache[prevNode.hash]; ok {
		return result, nil
	}
	versions := make(map[uint32]int32)  
	totalVotesFound := int32(0)
	iterNode := prevNode
	if !iterNode.isKeyBlock {
		iterNode, _ = b.getPrevKeyNodeFromNode(iterNode)
	}
	for i := int64(0); i < b.chainParams.StakeVersionInterval && iterNode != nil; i++ {
		totalVotesFound += int32(len(iterNode.votes))
		for _, v := range iterNode.votes {
			versions[v.Version]++
		}
		iterNode, err = b.getPrevKeyNodeFromNode(iterNode)
		if err != nil {
			return 0, err
		}
	}
	if int64(totalVotesFound) < b.chainParams.StakeVersionInterval*
		(int64(b.chainParams.TicketsPerBlock/2)+1) {
		return 0, AssertError(fmt.Sprintf("Not enough "+
			"votes: %v expected: %v ", totalVotesFound,
			b.chainParams.StakeVersionInterval*
				(int64(b.chainParams.TicketsPerBlock/2)+1)))
	}
	numRequired := totalVotesFound * b.chainParams.StakeMajorityMultiplier /
		b.chainParams.StakeMajorityDivisor
	for version, count := range versions {
		if count >= numRequired {
			b.calcVoterVersionIntervalCache[prevNode.hash] = version
			return version, nil
		}
	}
	return 0, errVoterVersionMajorityNotFound
}
func (b *BlockChain) calcVoterVersion(prevNode *blockNode) (uint32, *blockNode) {
	if !prevNode.isKeyBlock {
		prevNode, _ = b.getPrevKeyNodeFromNode(prevNode)
	}
	node, err := b.findStakeVersionPriorNode(prevNode)
	if err != nil {
		return 0, nil
	}
	iterNode := node
	for iterNode != nil {
		version, err := b.calcVoterVersionInterval(iterNode)
		if err == nil {
			return version, iterNode
		}
		if err != errVoterVersionMajorityNotFound {
			break
		}
		iterNode, err = b.getPrevKeyNodeFromNode(iterNode)
		if err != nil {
			break
		}
		iterNode, err = b.findStakeVersionPriorNode(iterNode)
		if err != nil {
			break
		}
	}
	return 0, nil
}
func (b *BlockChain) calcStakeVersion(prevNode *blockNode) uint32 {
	if prevNode.height <= 1 {
		return 0
	}
	if !prevNode.isKeyBlock {
		prevNode, _ = b.getPrevKeyNodeFromNode(prevNode)
	}
	version, node := b.calcVoterVersion(prevNode)
	if version == 0 || node == nil {
		return 0
	}
	if result, ok := b.calcStakeVersionCache[node.hash]; ok {
		return result
	}
	startIntervalHeight := calcWantHeight(b.chainParams.StakeValidationHeight,
		b.chainParams.StakeVersionInterval, node.keyHeight+1) + 1
	iterNode := node
	for iterNode.keyHeight > startIntervalHeight-1 {
		var err error
		iterNode, err = b.getPrevKeyNodeFromNode(iterNode)
		if err != nil || iterNode == nil {
			b.calcStakeVersionCache[node.hash] = 0
			return 0
		}
	}
	if !b.isMajorityVersion(3, iterNode,
		b.chainParams.BlockRejectNumRequired) {
		b.calcStakeVersionCache[node.hash] = 0
		return 0
	}
	ourVersion := version
	if b.isStakeMajorityVersion(version, node) {
		priorVersion, _ := b.calcPriorStakeVersion(node)
		if version <= priorVersion {
			ourVersion = priorVersion
		}
	}
	b.calcStakeVersionCache[node.hash] = ourVersion
	return ourVersion
}
func (b *BlockChain) calcStakeVersionByHash(hash *chainhash.Hash) (uint32, error) {
	prevNode, err := b.findNode(hash, 0)
	if err != nil {
		return 0, err
	}
	return b.calcStakeVersionByNode(prevNode)
}
func (b *BlockChain) calcStakeVersionByNode(prevNode *blockNode) (uint32, error) {
	return b.calcStakeVersion(prevNode), nil
}