func sdiffAlgoDeploymentVersion(network wire.CurrencyNet) uint32 {
	if network != wire.MainNet {
		return 5
	}
	return 4
}
sdiffAlgoDeploymentVersion returns the deployment vesion for the stake
difficulty algorithm change as defined in DCP0001 for the provided network.

This function is safe for concurrent access.
package blockchain
import (
	"fmt"
	"math/big"
	"time"
	"github.com/HcashOrg/hcashd/chaincfg"
	"github.com/HcashOrg/hcashd/chaincfg/chainhash"
	"github.com/HcashOrg/hcashd/wire"
)
var (
	bigZero = big.NewInt(0)
	bigOne = big.NewInt(1)
	oneLsh256 = new(big.Int).Lsh(bigOne, 256)
)
const maxShift = uint(256)
func HashToBig(hash *chainhash.Hash) *big.Int {
	buf := *hash
	blen := len(buf)
	for i := 0; i < blen/2; i++ {
		buf[i], buf[blen-1-i] = buf[blen-1-i], buf[i]
	}
	return new(big.Int).SetBytes(buf[:])
}
func CompactToBig(compact uint32) *big.Int {
	mantissa := compact & 0x007fffff
	isNegative := compact&0x00800000 != 0
	exponent := uint(compact >> 24)
	var bn *big.Int
	if exponent <= 3 {
		mantissa >>= 8 * (3 - exponent)
		bn = big.NewInt(int64(mantissa))
	} else {
		bn = big.NewInt(int64(mantissa))
		bn.Lsh(bn, 8*(exponent-3))
	}
	if isNegative {
		bn = bn.Neg(bn)
	}
	return bn
}
func BigToCompact(n *big.Int) uint32 {
	if n.Sign() == 0 {
		return 0
	}
	var mantissa uint32
	exponent := uint(len(n.Bytes()))
	if exponent <= 3 {
		mantissa = uint32(n.Bits()[0])
		mantissa <<= 8 * (3 - exponent)
	} else {
		tn := new(big.Int).Set(n)
		mantissa = uint32(tn.Rsh(tn, 8*(exponent-3)).Bits()[0])
	}
	if mantissa&0x00800000 != 0 {
		mantissa >>= 8
		exponent++
	}
	compact := uint32(exponent<<24) | mantissa
	if n.Sign() < 0 {
		compact |= 0x00800000
	}
	return compact
}
func CalcWork(bits uint32) *big.Int {
	difficultyNum := CompactToBig(bits)
	if difficultyNum.Sign() <= 0 {
		return big.NewInt(0)
	}
	denominator := new(big.Int).Add(difficultyNum, bigOne)
	return new(big.Int).Div(oneLsh256, denominator)
}
func (b *BlockChain) calcEasiestDifficulty(bits uint32,
	duration time.Duration) uint32 {
	durationVal := int64(duration)
	adjustmentFactor := big.NewInt(b.chainParams.RetargetAdjustmentFactor)
	maxRetargetTimespan := int64(b.chainParams.TargetTimespan) *
		b.chainParams.RetargetAdjustmentFactor
	if b.chainParams.ReduceMinDifficulty {
		if durationVal > int64(b.chainParams.MinDiffReductionTime) {
			return b.chainParams.PowLimitBits
		}
	}
	newTarget := CompactToBig(bits)
	for durationVal > 0 && newTarget.Cmp(b.chainParams.PowLimit) < 0 {
		newTarget.Mul(newTarget, adjustmentFactor)
		durationVal -= maxRetargetTimespan
	}
	if newTarget.Cmp(b.chainParams.PowLimit) > 0 {
		newTarget.Set(b.chainParams.PowLimit)
	}
	return BigToCompact(newTarget)
}
func (b *BlockChain) findPrevTestNetDifficulty(startNode *blockNode) (uint32, error) {
	blocksPerRetarget := b.chainParams.WorkDiffWindowSize *
		b.chainParams.WorkDiffWindows
	iterNode := startNode
	for iterNode != nil && iterNode.height%blocksPerRetarget != 0 &&
		iterNode.header.Bits == b.chainParams.PowLimitBits {
		var err error
		iterNode, err = b.getPrevNodeFromNode(iterNode)
		if err != nil {
			log.Errorf("getPrevNodeFromNode: %v", err)
			return 0, err
		}
	}
	lastBits := b.chainParams.PowLimitBits
	if iterNode != nil {
		lastBits = iterNode.header.Bits
	}
	return lastBits, nil
}
func (b *BlockChain) calcNextRequiredDifficulty(curNode *blockNode,					 
	newBlockTime time.Time) (uint32, error) {
	if curNode == nil {
		return b.chainParams.PowLimitBits, nil
	}
	if curNode.height == 0{
		return curNode.header.Bits, nil
	}
	newCurNode := curNode
	if !curNode.isKeyBlock{
		newCurNode, _ = b.getPrevKeyNodeFromNode(curNode)
	}
	if newCurNode == nil {
		return b.chainParams.PowLimitBits, nil
	}
	oldDiff := newCurNode.header.Bits
	oldDiffBig := CompactToBig(newCurNode.header.Bits)
	if (newCurNode.keyHeight+1)%b.chainParams.WorkDiffWindowSize != 0 {
		if b.chainParams.ReduceMinDifficulty {
			reductionTime := b.chainParams.MinDiffReductionTime
			allowMinTime := newCurNode.header.Timestamp.Add(reductionTime)
			if newBlockTime.After(allowMinTime) {
				timePassed := newBlockTime.Sub(newCurNode.header.Timestamp)
				timePassed -= b.chainParams.MinDiffReductionTime
				shifts := uint((timePassed / b.chainParams.TargetTimePerBlock) + 1)
				oldTarget := CompactToBig(newCurNode.header.Bits)
				newTarget := new(big.Int)
				if shifts < maxShift {
					newTarget.Lsh(oldTarget, shifts)
				} else {
					newTarget.Set(oneLsh256)
				}
				if newTarget.Cmp(b.chainParams.PowLimit) > 0 {
					newTarget.Set(b.chainParams.PowLimit)
				}
				return BigToCompact(newTarget), nil
			}
			prevBits, err := b.findPrevTestNetDifficulty(newCurNode)
			if err != nil {
				return 0, err
			}
			return prevBits, nil
		}
		return oldDiff, nil
	}
	RAFBig := big.NewInt(b.chainParams.RetargetAdjustmentFactor)
	nextDiffBigMin := CompactToBig(newCurNode.header.Bits)
	nextDiffBigMin.Div(nextDiffBigMin, RAFBig)
	nextDiffBigMax := CompactToBig(newCurNode.header.Bits)
	nextDiffBigMax.Mul(nextDiffBigMax, RAFBig)
	alpha := b.chainParams.WorkDiffAlpha
	nodesToTraverse := (b.chainParams.WorkDiffWindowSize *
		b.chainParams.WorkDiffWindows)
	windowChanges := make([]*big.Int, b.chainParams.WorkDiffWindows)
	oldNode := newCurNode
	windowPeriod := int64(0)
	weights := uint64(0)
	recentTime := newCurNode.header.Timestamp.UnixNano()
	olderTime := int64(0)
	for i := int64(0); ; i++ {
		if i%b.chainParams.WorkDiffWindowSize == 0 && i != 0 {
			olderTime = oldNode.header.Timestamp.UnixNano()
			timeDifference := recentTime - olderTime
			if oldNode.height == 0 {
				timeDifference = int64(b.chainParams.TargetTimespan)
			}
			timeDifBig := big.NewInt(timeDifference)
			timeDifBig.Lsh(timeDifBig, 32)  
			targetTemp := big.NewInt(int64(b.chainParams.TargetTimespan))
			windowAdjusted := targetTemp.Div(timeDifBig, targetTemp)
			windowAdjusted = windowAdjusted.Lsh(windowAdjusted,
				uint((b.chainParams.WorkDiffWindows-windowPeriod)*alpha))
			weights += 1 << uint64((b.chainParams.WorkDiffWindows-windowPeriod)*
				alpha)
			windowChanges[windowPeriod] = windowAdjusted
			windowPeriod++
			recentTime = olderTime
		}
		if i == nodesToTraverse {
			break  
		}
		var err error
		tempNode := oldNode
		oldNode, err = b.getPrevKeyNodeFromNode(oldNode)
		if err != nil {
			return 0, err
		}
		if oldNode == nil {
			oldNode = tempNode
		}
	}
	weightedSum := big.NewInt(0)
	for i := int64(0); i < b.chainParams.WorkDiffWindows; i++ {
		weightedSum.Add(weightedSum, windowChanges[i])
	}
	weightsBig := big.NewInt(int64(weights))
	weightedSumDiv := weightedSum.Div(weightedSum, weightsBig)
	nextDiffBig := weightedSumDiv.Mul(weightedSumDiv, oldDiffBig)
	nextDiffBig = nextDiffBig.Rsh(nextDiffBig, 32)
	if oldDiffBig.Cmp(bigZero) == 0 {  
		nextDiffBig.Set(nextDiffBig)  
	} else if nextDiffBig.Cmp(bigZero) == 0 {
		nextDiffBig.Set(b.chainParams.PowLimit)
	} else if nextDiffBig.Cmp(nextDiffBigMax) == 1 {
		nextDiffBig.Set(nextDiffBigMax)
	} else if nextDiffBig.Cmp(nextDiffBigMin) == -1 {
		nextDiffBig.Set(nextDiffBigMin)
	}
	if nextDiffBig.Cmp(b.chainParams.PowLimit) > 0 {
		nextDiffBig.Set(b.chainParams.PowLimit)
	}
	nextDiffBits := BigToCompact(nextDiffBig)
	log.Debugf("Difficulty retarget at block height %d", curNode.height+1)
	log.Debugf("Old target %08x (%064x)", curNode.header.Bits, oldDiffBig)
	log.Debugf("New target %08x (%064x)", nextDiffBits, CompactToBig(nextDiffBits))
	return nextDiffBits, nil
}
func (b *BlockChain) CalcNextRequiredDiffFromNode(hash *chainhash.Hash,
	timestamp time.Time) (uint32, error) {
	node, err := b.findNode(hash, maxSearchDepth)
	if err != nil {
		return 0, err
	}
	return b.calcNextRequiredDifficulty(node, timestamp)
}
func (b *BlockChain) CalcNextRequiredDifficulty(timestamp time.Time) (uint32,
	error) {
	b.chainLock.Lock()
	difficulty, err := b.calcNextRequiredDifficulty(b.bestNode, timestamp)
	b.chainLock.Unlock()
	return difficulty, err
}
func mergeDifficulty(oldDiff int64, newDiff1 int64, newDiff2 int64) int64 {
	newDiff1Big := big.NewInt(newDiff1)
	newDiff2Big := big.NewInt(newDiff2)
	newDiff2Big.Lsh(newDiff2Big, 32)
	oldDiffBig := big.NewInt(oldDiff)
	oldDiffBigLSH := big.NewInt(oldDiff)
	oldDiffBigLSH.Lsh(oldDiffBig, 32)
	newDiff1Big.Div(oldDiffBigLSH, newDiff1Big)
	newDiff2Big.Div(newDiff2Big, oldDiffBig)
	summedChange := big.NewInt(0)
	summedChange.Set(newDiff2Big)
	summedChange.Lsh(summedChange, 32)
	summedChange.Div(summedChange, newDiff1Big)
	summedChange.Mul(summedChange, oldDiffBig)
	summedChange.Rsh(summedChange, 32)
	return summedChange.Int64()
}
func (b *BlockChain) calcNextRequiredStakeDifficultyV1(curNode *blockNode) (int64, error) {
	alpha := b.chainParams.StakeDiffAlpha
	stakeDiffStartHeight := int64(b.chainParams.CoinbaseMaturity) +
		1
	maxRetarget := b.chainParams.RetargetAdjustmentFactor
	TicketPoolWeight := int64(b.chainParams.TicketPoolSizeWeight)
	nodesToTraverse := (b.chainParams.StakeDiffWindowSize *
		b.chainParams.StakeDiffWindows)
	if curNode == nil ||
		curNode.height < stakeDiffStartHeight {
		return b.chainParams.MinimumStakeDiff, nil
	}
	oldDiff := curNode.header.SBits
	if (curNode.height+1)%b.chainParams.StakeDiffWindowSize != 0 {
		return oldDiff, nil
	}
	targetForTicketPool := int64(b.chainParams.TicketsPerBlock) *
		int64(b.chainParams.TicketPoolSize)
	windowChanges := make([]*big.Int, b.chainParams.StakeDiffWindows)
	oldNode := curNode
	windowPeriod := int64(0)
	weights := uint64(0)
	for i := int64(0); ; i++ {
		if (i+1)%b.chainParams.StakeDiffWindowSize == 0 {
			poolSizeSkew := (int64(oldNode.header.PoolSize)-
				targetForTicketPool)*TicketPoolWeight + targetForTicketPool
			if poolSizeSkew <= 0 {
				poolSizeSkew = 1
			}
			curPoolSizeTemp := big.NewInt(poolSizeSkew)
			curPoolSizeTemp.Lsh(curPoolSizeTemp, 32)  
			targetTemp := big.NewInt(targetForTicketPool)
			windowAdjusted := curPoolSizeTemp.Div(curPoolSizeTemp, targetTemp)
			windowAdjusted = windowAdjusted.Lsh(windowAdjusted,
				uint((b.chainParams.StakeDiffWindows-windowPeriod)*alpha))
			weights += 1 << uint64((b.chainParams.StakeDiffWindows-windowPeriod)*
				alpha)
			windowChanges[windowPeriod] = windowAdjusted
			windowPeriod++
		}
		if (i + 1) == nodesToTraverse {
			break  
		}
		var err error
		tempNode := oldNode
		oldNode, err = b.getPrevNodeFromNode(oldNode)
		if err != nil {
			return 0, err
		}
		if oldNode == nil {
			oldNode = tempNode
		}
	}
	weightedSum := big.NewInt(0)
	for i := int64(0); i < b.chainParams.StakeDiffWindows; i++ {
		weightedSum.Add(weightedSum, windowChanges[i])
	}
	weightsBig := big.NewInt(int64(weights))
	weightedSumDiv := weightedSum.Div(weightedSum, weightsBig)
	oldDiffBig := big.NewInt(oldDiff)
	nextDiffBig := weightedSumDiv.Mul(weightedSumDiv, oldDiffBig)
	nextDiffBig = nextDiffBig.Rsh(nextDiffBig, 32)
	nextDiffTicketPool := nextDiffBig.Int64()
	if oldDiff == 0 {  
		return nextDiffTicketPool, nil
	} else if nextDiffTicketPool == 0 {
		nextDiffTicketPool = oldDiff / maxRetarget
	} else if (nextDiffTicketPool / oldDiff) > (maxRetarget - 1) {
		nextDiffTicketPool = oldDiff * maxRetarget
	} else if (oldDiff / nextDiffTicketPool) > (maxRetarget - 1) {
		nextDiffTicketPool = oldDiff / maxRetarget
	}
	targetForWindow := b.chainParams.StakeDiffWindowSize *
		int64(b.chainParams.TicketsPerBlock)
	oldNode = curNode
	windowFreshStake := int64(0)
	windowPeriod = int64(0)
	weights = uint64(0)
	for i := int64(0); ; i++ {
		windowFreshStake += int64(oldNode.header.FreshStake)
		if (i+1)%b.chainParams.StakeDiffWindowSize == 0 {
			if windowFreshStake <= 0 {
				windowFreshStake = 1
			}
			freshTemp := big.NewInt(windowFreshStake)
			freshTemp.Lsh(freshTemp, 32)  
			targetTemp := big.NewInt(targetForWindow)
			windowAdjusted := freshTemp.Div(freshTemp, targetTemp)
			windowAdjusted = windowAdjusted.Lsh(windowAdjusted,
				uint((b.chainParams.StakeDiffWindows-windowPeriod)*alpha))
			weights += 1 <<
				uint64((b.chainParams.StakeDiffWindows-windowPeriod)*alpha)
			windowChanges[windowPeriod] = windowAdjusted
			windowFreshStake = 0
			windowPeriod++
		}
		if (i + 1) == nodesToTraverse {
			break  
		}
		var err error
		tempNode := oldNode
		oldNode, err = b.getPrevNodeFromNode(oldNode)
		if err != nil {
			return 0, err
		}
		if oldNode == nil {
			oldNode = tempNode
		}
	}
	weightedSum = big.NewInt(0)
	for i := int64(0); i < b.chainParams.StakeDiffWindows; i++ {
		weightedSum.Add(weightedSum, windowChanges[i])
	}
	weightsBig = big.NewInt(int64(weights))
	weightedSumDiv = weightedSum.Div(weightedSum, weightsBig)
	oldDiffBig = big.NewInt(oldDiff)
	nextDiffBig = weightedSumDiv.Mul(weightedSumDiv, oldDiffBig)
	nextDiffBig = nextDiffBig.Rsh(nextDiffBig, 32)
	nextDiffFreshStake := nextDiffBig.Int64()
	if oldDiff == 0 {  
		return nextDiffFreshStake, nil
	} else if nextDiffFreshStake == 0 {
		nextDiffFreshStake = oldDiff / maxRetarget
	} else if (nextDiffFreshStake / oldDiff) > (maxRetarget - 1) {
		nextDiffFreshStake = oldDiff * maxRetarget
	} else if (oldDiff / nextDiffFreshStake) > (maxRetarget - 1) {
		nextDiffFreshStake = oldDiff / maxRetarget
	}
	nextDiff := mergeDifficulty(oldDiff, nextDiffTicketPool, nextDiffFreshStake)
	if oldDiff == 0 {  
		return oldDiff, nil
	} else if nextDiff == 0 {
		nextDiff = oldDiff / maxRetarget
	} else if (nextDiff / oldDiff) > (maxRetarget - 1) {
		nextDiff = oldDiff * maxRetarget
	} else if (oldDiff / nextDiff) > (maxRetarget - 1) {
		nextDiff = oldDiff / maxRetarget
	}
	if nextDiff < b.chainParams.MinimumStakeDiff {
		return b.chainParams.MinimumStakeDiff, nil
	}
	return nextDiff, nil
}
func estimateSupply(params *chaincfg.Params, height int64) int64 {
	if height <= 0 {
		return 0
	}
	supply := params.BlockOneSubsidy()
	reductions := int64(height) / params.SubsidyReductionInterval
	subsidy := params.BaseSubsidy
	for i := int64(0); i < reductions; i++ {
		supply += params.SubsidyReductionInterval * subsidy
		subsidy *= params.MulSubsidy
		subsidy /= params.DivSubsidy
	}
	supply += (1 + int64(height)%params.SubsidyReductionInterval) * subsidy
	supply -= params.BaseSubsidy * 2
	return supply
}
func (b *BlockChain) sumPurchasedTickets(startNode *blockNode, numToSum int64) (int64, error) {
	var numPurchased int64
	for node, numTraversed := startNode, int64(0); node != nil &&
		numTraversed < numToSum; numTraversed++ {
		numPurchased += int64(node.header.FreshStake)
		var err error
		node, err = b.getPrevKeyNodeFromNode(node)
		if err != nil {
			return 0, err
		}
	}
	return numPurchased, nil
}
func calcNextStakeDiffV2(params *chaincfg.Params, nextHeight, curDiff, prevPoolSizeAll, curPoolSizeAll int64) int64 {
	votesPerBlock := int64(params.TicketsPerBlock)
	ticketPoolSize := int64(params.TicketPoolSize)
	ticketMaturity := int64(params.TicketMaturity)
	targetPoolSizeAll := votesPerBlock * (ticketPoolSize + ticketMaturity)
	curPoolSizeAllBig := big.NewInt(curPoolSizeAll)
	nextDiffBig := big.NewInt(curDiff)
	nextDiffBig.Mul(nextDiffBig, curPoolSizeAllBig)
	nextDiffBig.Mul(nextDiffBig, curPoolSizeAllBig)
	nextDiffBig.Div(nextDiffBig, big.NewInt(prevPoolSizeAll))
	nextDiffBig.Div(nextDiffBig, big.NewInt(targetPoolSizeAll))
	nextDiff := nextDiffBig.Int64()
	estimatedSupply := estimateSupply(params, nextHeight)
	maximumStakeDiff := estimatedSupply / ticketPoolSize
	if nextDiff > maximumStakeDiff {
		nextDiff = maximumStakeDiff
	}
	if nextDiff < params.MinimumStakeDiff {
		nextDiff = params.MinimumStakeDiff
	}
	return nextDiff
}
func (b *BlockChain) calcNextRequiredStakeDifficultyV2(curNode *blockNode) (int64, error) {
	nextHeight := int64(0)
	if curNode != nil {
		nextHeight = curNode.keyHeight + 2
	}
	stakeDiffStartHeight := int64(b.chainParams.CoinbaseMaturity) + 1
	if nextHeight < stakeDiffStartHeight {
		return b.chainParams.MinimumStakeDiff, nil
	}
	intervalSize := b.chainParams.StakeDiffWindowSize
	curDiff := curNode.header.SBits
	if nextHeight%intervalSize != 0 || !curNode.isKeyBlock{
		return curDiff, nil
	}
	var prevPoolSize int64
	prevRetargetHeight := nextHeight - intervalSize - 1
	prevRetargetNode, err := b.ancestorKeyNode(curNode, prevRetargetHeight)
	if err != nil {
		return 0, err
	}
	if prevRetargetNode != nil {
		prevPoolSize = int64(prevRetargetNode.header.PoolSize)
	}
	ticketMaturity := int64(b.chainParams.TicketMaturity)
	prevImmatureTickets, err := b.sumPurchasedTickets(prevRetargetNode,
		ticketMaturity)
	if err != nil {
		return 0, err
	}
	prevPoolSizeAll := prevPoolSize + prevImmatureTickets
	if prevPoolSizeAll == 0 {
		return curDiff, nil
	}
	immatureTickets, err := b.sumPurchasedTickets(curNode, ticketMaturity)
	if err != nil {
		return 0, err
	}
	curPoolSizeAll := int64(curNode.header.PoolSize) + immatureTickets
	return calcNextStakeDiffV2(b.chainParams, nextHeight, curDiff,
		prevPoolSizeAll, curPoolSizeAll), nil
}