func (g *Generator) calcNextRequiredStakeDifficulty() int64 {
	nextHeight := g.tip.Header.Height + 1
	stakeDiffStartHeight := uint32(g.params.CoinbaseMaturity) + 1
	if nextHeight < stakeDiffStartHeight {
		return g.params.MinimumStakeDiff
	}
	curDiff := g.tip.Header.SBits
	if curDiff == 0 {
		return 0
	}
	windowSize := g.params.StakeDiffWindowSize
	if int64(nextHeight)%windowSize != 0 {
		return curDiff
	}
	var weightedPoolSizeSum, weightSum uint64
	ticketsPerBlock := int64(g.params.TicketsPerBlock)
	targetPoolSize := ticketsPerBlock * int64(g.params.TicketPoolSize)
	numWindows := g.params.StakeDiffWindows
	weightAlpha := g.params.StakeDiffAlpha
	block := g.tip
	for i := int64(0); i < numWindows; i++ {
		prevRetargetHeight := nextHeight - uint32(windowSize*(i+1))
		windowPoolSize := int64(0)
		block = g.ancestorBlock(block, prevRetargetHeight, nil)
		if block != nil {
			windowPoolSize = int64(block.Header.PoolSize)
		}
		skewedPoolSize := targetPoolSize + (windowPoolSize-
			targetPoolSize)*int64(g.params.TicketPoolSizeWeight)
		if skewedPoolSize <= 0 {
			skewedPoolSize = 1
		}
		adjusted := (skewedPoolSize << 32) / targetPoolSize
		adjusted = adjusted << uint64((numWindows-i)*weightAlpha)
		weightedPoolSizeSum += uint64(adjusted)
		weightSum += 1 << uint64((numWindows-i)*weightAlpha)
	}
	nextPoolSizeDiff := (int64(weightedPoolSizeSum/weightSum) * curDiff) >> 32
	nextPoolSizeDiff = g.limitRetarget(curDiff, nextPoolSizeDiff)
	var weightedTicketsSum uint64
	targetTicketsPerWindow := ticketsPerBlock * windowSize
	block = g.tip
	for i := int64(0); i < numWindows; i++ {
		var windowNewTickets int64
		if i == 0 {
			windowNewTickets = int64(block.Header.FreshStake)
		}
		prevRetargetHeight := nextHeight - uint32(windowSize*(i+1))
		block = g.ancestorBlock(block, prevRetargetHeight, func(blk *wire.MsgBlock) {
			windowNewTickets += int64(blk.Header.FreshStake)
		})
		if windowNewTickets <= 0 {
			windowNewTickets = 1
		}
		adjusted := (windowNewTickets << 32) / targetTicketsPerWindow
		adjusted = adjusted << uint64((numWindows-i)*weightAlpha)
		weightedTicketsSum += uint64(adjusted)
	}
	nextNewTixDiff := (int64(weightedTicketsSum/weightSum) * curDiff) >> 32
	nextNewTixDiff = g.limitRetarget(curDiff, nextNewTixDiff)
	nextDiff := mergeDifficulty(curDiff, nextPoolSizeDiff, nextNewTixDiff)
	nextDiff = g.limitRetarget(curDiff, nextDiff)
	if nextDiff < g.params.MinimumStakeDiff {
		return g.params.MinimumStakeDiff
	}
	return nextDiff
}
calcNextRequiredStakeDifficulty returns the required stake difficulty (aka
ticket price) for the block after the current tip block the generator is
associated with.

An overview of the algorithm is as follows:
1) Use the minimum value for any blocks before any tickets could have
possibly been purchased due to coinbase maturity requirements
2) Return 0 if the current tip block stake difficulty is 0.  This is a
safety check against a condition that should never actually happen.
3) Use the previous block's difficulty if the next block is not at a retarget
interval
4) Calculate the ideal retarget difficulty for each window based on the
actual pool size in the window versus the target pool size skewed by a
constant factor to weight the ticket pool size instead of the tickets per
block and exponentially weight each difficulty such that the most recent
window has the highest weight
5) Calculate the pool size retarget difficulty based on the exponential
weighted average and ensure it is limited to the max retarget adjustment
factor -- This is the first metric used to calculate the final difficulty
6) Calculate the ideal retarget difficulty for each window based on the
actual new tickets in the window versus the target new tickets per window
and exponentially weight each difficulty such that the most recent window
has the highest weight
7) Calculate the tickets per window retarget difficulty based on the
exponential weighted average and ensure it is limited to the max retarget
adjustment factor
8) Calculate the final difficulty by averaging the pool size retarget
difficulty from #5 and the tickets per window retarget difficulty from #7
using scaled multiplication and ensure it is limited to the max retarget
adjustment factor

NOTE: In order to simplify the test code, this implementation does not use
big integers so it will NOT match the actual consensus code for really big
numbers.  However, the parameters on simnet and the pool sizes used in these
tests are low enough that this is not an issue for the tests.  Anyone looking
at this code should NOT use it for mainnet calculations as is since it will
not always yield the correct results.
package chaingen
import (
	"bytes"
	"encoding/binary"
	"fmt"
	"math"
	"math/big"
	"runtime"
	"sort"
	"time"
	"github.com/HcashOrg/hcashd/blockchain"
	"github.com/HcashOrg/hcashd/chaincfg"
	"github.com/HcashOrg/hcashd/chaincfg/chainhash"
	"github.com/HcashOrg/hcashd/txscript"
	"github.com/HcashOrg/hcashd/wire"
	"github.com/HcashOrg/hcashutil"
)
var (
	hash256prngSeedConst = []byte{0x24, 0x3f, 0x6a, 0x88, 0x85, 0xa3, 0x08,
		0xd3}
	opTrueScript = []byte{txscript.OP_TRUE}
	opTrueRedeemScript = []byte{txscript.OP_DATA_1, txscript.OP_TRUE}
	coinbaseSigScript = []byte{txscript.OP_0, txscript.OP_0}
)
const (
	voteBitYes = 0x01
)
type SpendableOut struct {
	prevOut     wire.OutPoint
	blockHeight uint32
	blockIndex  uint32
	amount      hcashutil.Amount
}
func (s *SpendableOut) PrevOut() wire.OutPoint {
	return s.prevOut
}
func (s *SpendableOut) BlockHeight() uint32 {
	return s.blockHeight
}
func (s *SpendableOut) BlockIndex() uint32 {
	return s.blockIndex
}
func (s *SpendableOut) Amount() hcashutil.Amount {
	return s.amount
}
func MakeSpendableOutForTx(tx *wire.MsgTx, blockHeight, txIndex, txOutIndex uint32) SpendableOut {
	return SpendableOut{
		prevOut: wire.OutPoint{
			Hash:  *tx.CachedTxHash(),
			Index: txOutIndex,
			Tree:  wire.TxTreeRegular,
		},
		blockHeight: blockHeight,
		blockIndex:  txIndex,
		amount:      hcashutil.Amount(tx.TxOut[txOutIndex].Value),
	}
}
func MakeSpendableOut(block *wire.MsgBlock, txIndex, txOutIndex uint32) SpendableOut {
	tx := block.Transactions[txIndex]
	return MakeSpendableOutForTx(tx, block.Header.Height, txIndex, txOutIndex)
}
type stakeTicket struct {
	tx          *wire.MsgTx
	blockHeight uint32
	blockIndex  uint32
}
type stakeTicketSorter []*stakeTicket
func (t stakeTicketSorter) Len() int { return len(t) }
func (t stakeTicketSorter) Swap(i, j int) { t[i], t[j] = t[j], t[i] }
func (t stakeTicketSorter) Less(i, j int) bool {
	iHash := t[i].tx.CachedTxHash()[:]
	jHash := t[j].tx.CachedTxHash()[:]
	return bytes.Compare(iHash, jHash) < 0
}
type Generator struct {
	params           *chaincfg.Params
	tip              *wire.MsgBlock
	tipName          string
	blocks           map[chainhash.Hash]*wire.MsgBlock
	blocksByName     map[string]*wire.MsgBlock
	p2shOpTrueAddr   hcashutil.Address
	p2shOpTrueScript []byte
	spendableOuts     [][]SpendableOut
	prevCollectedHash chainhash.Hash
	originalParents map[chainhash.Hash]chainhash.Hash
	immatureTickets []*stakeTicket
	liveTickets     []*stakeTicket
	wonTickets      map[chainhash.Hash][]*stakeTicket
	expiredTickets  []*stakeTicket
}
func MakeGenerator(params *chaincfg.Params) (Generator, error) {
	p2shOpTrueAddr, err := hcashutil.NewAddressScriptHash(opTrueScript, params)
	if err != nil {
		return Generator{}, err
	}
	p2shOpTrueScript, err := txscript.PayToAddrScript(p2shOpTrueAddr)
	if err != nil {
		return Generator{}, err
	}
	genesis := params.GenesisBlock
	genesisHash := genesis.BlockHash()
	return Generator{
		params:           params,
		tip:              genesis,
		tipName:          "genesis",
		blocks:           map[chainhash.Hash]*wire.MsgBlock{genesisHash: genesis},
		blocksByName:     map[string]*wire.MsgBlock{"genesis": genesis},
		p2shOpTrueAddr:   p2shOpTrueAddr,
		p2shOpTrueScript: p2shOpTrueScript,
		originalParents:  make(map[chainhash.Hash]chainhash.Hash),
		wonTickets:       make(map[chainhash.Hash][]*stakeTicket),
	}, nil
}
func (g *Generator) Params() *chaincfg.Params {
	return g.params
}
func (g *Generator) Tip() *wire.MsgBlock {
	return g.tip
}
func (g *Generator) TipName() string {
	return g.tipName
}
func (g *Generator) BlockByName(blockName string) *wire.MsgBlock {
	block, ok := g.blocksByName[blockName]
	if !ok {
		panic(fmt.Sprintf("block name %s does not exist", blockName))
	}
	return block
}
func (g *Generator) BlockByHash(hash *chainhash.Hash) *wire.MsgBlock {
	block, ok := g.blocks[*hash]
	if !ok {
		panic(fmt.Sprintf("block with hash %s does not exist", hash))
	}
	return block
}
func opReturnScript(data []byte) []byte {
	builder := txscript.NewScriptBuilder()
	script, err := builder.AddOp(txscript.OP_RETURN).AddData(data).Script()
	if err != nil {
		panic(err)
	}
	return script
}
func UniqueOpReturnScript() []byte {
	rand, err := wire.RandomUint64()
	if err != nil {
		panic(err)
	}
	data := make([]byte, 8, 8)
	binary.LittleEndian.PutUint64(data[0:8], rand)
	return opReturnScript(data)
}
func (g *Generator) calcFullSubsidy(blockHeight uint32) hcashutil.Amount {
	iterations := int64(blockHeight) / g.params.SubsidyReductionInterval
	subsidy := g.params.BaseSubsidy
	for i := int64(0); i < iterations; i++ {
		subsidy *= g.params.MulSubsidy
		subsidy /= g.params.DivSubsidy
	}
	return hcashutil.Amount(subsidy)
}
func (g *Generator) calcPoWSubsidy(fullSubsidy hcashutil.Amount, blockHeight uint32, numVotes uint16) hcashutil.Amount {
	powProportion := hcashutil.Amount(g.params.WorkRewardProportion)
	totalProportions := hcashutil.Amount(g.params.TotalSubsidyProportions())
	powSubsidy := (fullSubsidy * powProportion) / totalProportions
	if int64(blockHeight) < g.params.StakeValidationHeight {
		return powSubsidy
	}
	ticketsPerBlock := hcashutil.Amount(g.params.TicketsPerBlock)
	return (powSubsidy * hcashutil.Amount(numVotes)) / ticketsPerBlock
}
func (g *Generator) calcPoSSubsidy(heightVotedOn uint32) hcashutil.Amount {
	if int64(heightVotedOn+2) < g.params.StakeValidationHeight {
		return 0
	}
	fullSubsidy := g.calcFullSubsidy(heightVotedOn)
	posProportion := hcashutil.Amount(g.params.StakeRewardProportion)
	totalProportions := hcashutil.Amount(g.params.TotalSubsidyProportions())
	return (fullSubsidy * posProportion) / totalProportions
}
func (g *Generator) calcDevSubsidy(fullSubsidy hcashutil.Amount, blockHeight uint32, numVotes uint16) hcashutil.Amount {
	devProportion := hcashutil.Amount(g.params.BlockTaxProportion)
	totalProportions := hcashutil.Amount(g.params.TotalSubsidyProportions())
	devSubsidy := (fullSubsidy * devProportion) / totalProportions
	if int64(blockHeight) < g.params.StakeValidationHeight {
		return devSubsidy
	}
	ticketsPerBlock := hcashutil.Amount(g.params.TicketsPerBlock)
	return (devSubsidy * hcashutil.Amount(numVotes)) / ticketsPerBlock
}
func standardCoinbaseOpReturnScript(blockHeight uint32) []byte {
	rand, err := wire.RandomUint64()
	if err != nil {
		panic(err)
	}
	data := make([]byte, 36, 36)
	binary.LittleEndian.PutUint32(data[0:4], blockHeight)
	binary.LittleEndian.PutUint64(data[28:36], rand)
	return opReturnScript(data)
}
func (g *Generator) addExtraCoinbaseTxOutputs(tx *wire.MsgTx, blockHeight uint32, devSubsidy, powSubsidy hcashutil.Amount) {
	tx.AddTxOut(&wire.TxOut{
		Value:    int64(devSubsidy),
		Version:  g.params.OrganizationPkScriptVersion,
		PkScript: g.params.OrganizationPkScript,
	})
	tx.AddTxOut(wire.NewTxOut(0, standardCoinbaseOpReturnScript(blockHeight)))
	const numPoWOutputs = 6
	amount := powSubsidy / numPoWOutputs
	for i := 0; i < numPoWOutputs; i++ {
		if i == numPoWOutputs-1 {
			amount = powSubsidy - amount*(numPoWOutputs-1)
		}
		tx.AddTxOut(wire.NewTxOut(int64(amount), g.p2shOpTrueScript))
	}
}
func (g *Generator) addCoinbaseTxOutputs(tx *wire.MsgTx, opReturnPkScript []byte) {
	tx.AddTxOut(&wire.TxOut{
		Value:    int64(0),
		Version:  g.params.OrganizationPkScriptVersion,
		PkScript: opReturnPkScript,
	})
	tx.AddTxOut(wire.NewTxOut(int64(0), g.p2shOpTrueScript))
}
func (g *Generator) CreateCoinbaseTx(opReturnPkScript []byte) *wire.MsgTx {
	tx := wire.NewMsgTx()
	tx.AddTxIn(&wire.TxIn{
		PreviousOutPoint: *wire.NewOutPoint(&chainhash.Hash{},
			wire.MaxPrevOutIndex, wire.TxTreeRegular),
		Sequence:        wire.MaxTxInSequenceNum,
		ValueIn:         int64(0),
		BlockHeight:     wire.NullBlockHeight,
		BlockIndex:      wire.NullBlockIndex,
		SignatureScript: coinbaseSigScript,
	})
	g.addCoinbaseTxOutputs(tx, opReturnPkScript)
	return tx
}
func (g *Generator) CreateExtraCoinbaseTx(blockHeight uint32, numVotes uint16) *wire.MsgTx {
	height := blockHeight
	if blockHeight >= 0 {
		height = height - 1
	}
	fullSubsidy := g.calcFullSubsidy(height)
	devSubsidy := g.calcDevSubsidy(fullSubsidy, height, numVotes)
	powSubsidy := g.calcPoWSubsidy(fullSubsidy, height, numVotes)
	tx := wire.NewMsgTx()
	tx.AddTxIn(&wire.TxIn{
		PreviousOutPoint: *wire.NewOutPoint(&chainhash.Hash{},
			wire.MaxPrevOutIndex, wire.TxTreeRegular),
		Sequence:        wire.MaxTxInSequenceNum,
		ValueIn:         int64(devSubsidy + powSubsidy),
		BlockHeight:     wire.NullBlockHeight,
		BlockIndex:      wire.NullBlockIndex,
		SignatureScript: coinbaseSigScript,
	})
	g.addExtraCoinbaseTxOutputs(tx, blockHeight, devSubsidy, powSubsidy)
	return tx
}
func purchaseCommitmentScript(addr hcashutil.Address, amount, voteFeeLimit, revocationFeeLimit hcashutil.Amount) []byte {
	limits := uint16(0)
	if voteFeeLimit != 0 {
		exp := uint16(math.Ceil(math.Log2(float64(voteFeeLimit))))
		limits |= (exp | 0x40)
	}
	if revocationFeeLimit != 0 {
		exp := uint16(math.Ceil(math.Log2(float64(revocationFeeLimit))))
		limits |= ((exp | 0x40) << 8)
	}
	var data [30]byte
	copy(data[:], addr.ScriptAddress())
	binary.LittleEndian.PutUint64(data[20:], uint64(amount))
	data[27] |= 1 << 7
	binary.LittleEndian.PutUint16(data[28:], limits)
	script, err := txscript.NewScriptBuilder().AddOp(txscript.OP_RETURN).
		AddData(data[:]).Script()
	if err != nil {
		panic(err)
	}
	return script
}
func (g *Generator) createTicketPurchaseTx(spend *SpendableOut, ticketPrice, fee hcashutil.Amount) *wire.MsgTx {
	pkScript, err := txscript.PayToSStx(g.p2shOpTrueAddr)
	if err != nil {
		panic(err)
	}
	commitScript := purchaseCommitmentScript(g.p2shOpTrueAddr,
		ticketPrice+fee, 0, ticketPrice)
	change := spend.amount - ticketPrice - fee
	changeScript, err := txscript.PayToSStxChange(g.p2shOpTrueAddr)
	if err != nil {
		panic(err)
	}
	tx := wire.NewMsgTx()
	tx.AddTxIn(&wire.TxIn{
		PreviousOutPoint: spend.prevOut,
		Sequence:         wire.MaxTxInSequenceNum,
		ValueIn:          int64(spend.amount),
		BlockHeight:      spend.blockHeight,
		BlockIndex:       spend.blockIndex,
		SignatureScript:  opTrueRedeemScript,
	})
	tx.AddTxOut(wire.NewTxOut(int64(ticketPrice), pkScript))
	tx.AddTxOut(wire.NewTxOut(0, commitScript))
	tx.AddTxOut(wire.NewTxOut(int64(change), changeScript))
	return tx
}
func isTicketPurchaseTx(tx *wire.MsgTx) bool {
	if len(tx.TxOut) == 0 {
		return false
	}
	txOut := tx.TxOut[0]
	scriptClass := txscript.GetScriptClass(txOut.Version, txOut.PkScript)
	return scriptClass == txscript.StakeSubmissionTy
}
func isVoteTx(tx *wire.MsgTx) bool {
	if len(tx.TxOut) < 3 {
		return false
	}
	txOut := tx.TxOut[2]
	scriptClass := txscript.GetScriptClass(txOut.Version, txOut.PkScript)
	return scriptClass == txscript.StakeGenTy
}
func isRevocationTx(tx *wire.MsgTx) bool {
	if len(tx.TxOut) == 0 {
		return false
	}
	txOut := tx.TxOut[0]
	scriptClass := txscript.GetScriptClass(txOut.Version, txOut.PkScript)
	return scriptClass == txscript.StakeRevocationTy
}
func voteBlockScript(parentBlock *wire.MsgBlock) []byte {
	var data [40]byte
	parentHash := parentBlock.BlockHash()
	copy(data[:], parentHash[:])
	binary.LittleEndian.PutUint32(data[32:], parentBlock.Header.Height)
	binary.LittleEndian.PutUint32(data[36:], parentBlock.Header.KeyHeight)
	script, err := txscript.NewScriptBuilder().AddOp(txscript.OP_RETURN).
		AddData(data[:]).Script()
	if err != nil {
		panic(err)
	}
	return script
}
func voteBitsScript(bits uint16, voteVersion uint32) []byte {
	data := make([]byte, 6)
	binary.LittleEndian.PutUint16(data[:], bits)
	binary.LittleEndian.PutUint32(data[2:], voteVersion)
	if voteVersion == 0 {
		data = data[:2]
	}
	script, err := txscript.NewScriptBuilder().AddOp(txscript.OP_RETURN).
		AddData(data[:]).Script()
	if err != nil {
		panic(err)
	}
	return script
}
func (g *Generator) createVoteTx(parentBlock *wire.MsgBlock, ticket *stakeTicket) *wire.MsgTx {
	posSubsidy := g.calcPoSSubsidy(parentBlock.Header.KeyHeight)
	voteSubsidy := posSubsidy / hcashutil.Amount(g.params.TicketsPerBlock)
	ticketPrice := hcashutil.Amount(ticket.tx.TxOut[0].Value)
	blockScript := voteBlockScript(parentBlock)
	voteScript := voteBitsScript(voteBitYes, 0)
	stakeGenScript, err := txscript.PayToSSGen(g.p2shOpTrueAddr)
	if err != nil {
		panic(err)
	}
	ticketHash := ticket.tx.CachedTxHash()
	tx := wire.NewMsgTx()
	tx.AddTxIn(&wire.TxIn{
		PreviousOutPoint: *wire.NewOutPoint(&chainhash.Hash{},
			wire.MaxPrevOutIndex, wire.TxTreeRegular),
		Sequence:        wire.MaxTxInSequenceNum,
		ValueIn:         int64(voteSubsidy),
		BlockHeight:     wire.NullBlockHeight,
		BlockIndex:      wire.NullBlockIndex,
		SignatureScript: g.params.StakeBaseSigScript,
	})
	tx.AddTxIn(&wire.TxIn{
		PreviousOutPoint: *wire.NewOutPoint(ticketHash, 0,
			wire.TxTreeStake),
		Sequence:        wire.MaxTxInSequenceNum,
		ValueIn:         int64(ticketPrice),
		BlockHeight:     ticket.blockHeight,
		BlockIndex:      ticket.blockIndex,
		SignatureScript: opTrueRedeemScript,
	})
	tx.AddTxOut(wire.NewTxOut(0, blockScript))
	tx.AddTxOut(wire.NewTxOut(0, voteScript))
	tx.AddTxOut(wire.NewTxOut(int64(voteSubsidy+ticketPrice), stakeGenScript))
	return tx
}
func (g *Generator) ancestorBlock(block *wire.MsgBlock, height uint32, f func(*wire.MsgBlock)) *wire.MsgBlock {
	if block == nil || height > block.Header.Height {
		return nil
	}
	for block != nil && block.Header.Height > height {
		block = g.blocks[block.Header.PrevBlock]
		if f != nil && block != nil {
			f(block)
		}
	}
	return block
}
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 (g *Generator) limitRetarget(oldDiff, newDiff int64) int64 {
	maxRetarget := g.params.RetargetAdjustmentFactor
	switch {
	case newDiff == 0:
		fallthrough
	case (oldDiff / newDiff) > (maxRetarget - 1):
		return oldDiff / maxRetarget
	case (newDiff / oldDiff) > (maxRetarget - 1):
		return oldDiff * maxRetarget
	}
	return newDiff
}
func (g *Generator) calcNextRequiredDifficulty() uint32 {
	nextHeight := g.tip.Header.Height + 1
	windowSize := g.params.WorkDiffWindowSize
	if int64(nextHeight) < windowSize {
		return g.params.PowLimitBits
	}
	curDiff := int64(g.tip.Header.Bits)
	if int64(nextHeight)%windowSize != 0 {
		return uint32(curDiff)
	}
	adjustedTimespan := big.NewInt(0)
	tempBig := big.NewInt(0)
	weightedTimespanSum, weightSum := big.NewInt(0), big.NewInt(0)
	targetTimespan := int64(g.params.TargetTimespan)
	targetTimespanBig := big.NewInt(targetTimespan)
	numWindows := g.params.WorkDiffWindows
	weightAlpha := g.params.WorkDiffAlpha
	block := g.tip
	finalWindowTime := block.Header.Timestamp.UnixNano()
	for i := int64(0); i < numWindows; i++ {
		actualTimespan := targetTimespan
		if int64(block.Header.Height) > windowSize {
			for j := int64(0); j < windowSize; j++ {
				block = g.blocks[block.Header.PrevBlock]
			}
			startWindowTime := block.Header.Timestamp.UnixNano()
			actualTimespan = finalWindowTime - startWindowTime
			finalWindowTime = startWindowTime
		}
		windowWeightShift := uint((numWindows - i) * weightAlpha)
		adjustedTimespan.SetInt64(actualTimespan)
		adjustedTimespan.Lsh(adjustedTimespan, 32)
		adjustedTimespan.Div(adjustedTimespan, targetTimespanBig)
		adjustedTimespan.Lsh(adjustedTimespan, windowWeightShift)
		weightedTimespanSum.Add(weightedTimespanSum, adjustedTimespan)
		weight := tempBig.SetInt64(1)
		weight.Lsh(weight, windowWeightShift)
		weightSum.Add(weightSum, weight)
	}
	curDiffBig := tempBig.SetInt64(curDiff)
	weightedTimespanSum.Div(weightedTimespanSum, weightSum)
	weightedTimespanSum.Mul(weightedTimespanSum, curDiffBig)
	weightedTimespanSum.Rsh(weightedTimespanSum, 32)
	nextDiff := weightedTimespanSum.Int64()
	nextDiff = g.limitRetarget(curDiff, nextDiff)
	if nextDiff > int64(g.params.PowLimitBits) {
		return g.params.PowLimitBits
	}
	return uint32(nextDiff)
}