func (b *BlockChain) computeTarget(indexFirst, indexLast *blockNode) *big.Int {
	if indexLast.height <= indexFirst.height {
		panic("indexLast height should be greater than indexFirst height ")
	}
	work := new(big.Int).Sub(indexLast.workSum, indexFirst.workSum)
	work.Mul(work, big.NewInt(int64(b.chainParams.TargetTimePerBlock.Seconds())))
	if indexLast.timestamp <= indexFirst.timestamp {
		panic("indexLast time should greater than indexFirst time ")
	}
	actualTimeSpan := indexLast.timestamp - indexFirst.timestamp
	interval := int64(b.chainParams.TargetTimePerBlock.Seconds())
	if actualTimeSpan > 288*interval {
		actualTimeSpan = 288 * interval
	} else if actualTimeSpan < 72*interval {
		actualTimeSpan = 72 * interval
	}
	work.Div(work, big.NewInt(actualTimeSpan))
	return new(big.Int).Sub(new(big.Int).Div(oneLsh256, work), big.NewInt(1))
}
computeTarget Compute the a target based on the work done between 2 blocks and the time
required to produce that work.
package blockchain
import (
	"math/big"
	"github.com/bcext/gcash/chaincfg"
	"github.com/bcext/gcash/chaincfg/chainhash"
	"github.com/bcext/gcash/wire"
)
var (
	bigOne = big.NewInt(1)
	oneLsh256 = new(big.Int).Lsh(bigOne, 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) GetNextWorkRequired(header *wire.BlockHeader) (uint32, error) {
	prevHash := header.PrevBlock
	prevBlock := b.index.LookupNode(&prevHash)
	if prevBlock == nil {
		return BigToCompact(b.chainParams.PowLimit), nil
	}
	if b.chainParams == &chaincfg.RegressionNetParams ||
		b.chainParams == &chaincfg.SimNetParams {
		return prevBlock.bits, nil
	}
	if prevBlock.height+1 >= b.chainParams.DAAHeight {
		return b.getNextCashWorkRequired(prevBlock, header)
	}
	return b.getNextEDAWorkRequired(prevBlock, header)
}
func (b *BlockChain) getNextCashWorkRequired(prevBlock *blockNode,
	header *wire.BlockHeader) (uint32, error) {
	if b.chainParams.ReduceMinDifficulty &&
		(header.Timestamp.Unix() >
			(prevBlock.timestamp + int64(2*b.chainParams.TargetTimePerBlock.Seconds()))) {
		return b.chainParams.PowLimitBits, nil
	}
	lastNode := b.getSuitableBlock(prevBlock)
	firstHeight := prevBlock.height - 144
	firstNode := b.getSuitableBlock(b.bestChain.NodeByHeight(firstHeight))
	if firstNode == nil {
		panic("the firstNode should not equal nil")
	}
	nextTarget := b.computeTarget(firstNode, lastNode)
	if nextTarget.Cmp(b.chainParams.PowLimit) > 0 {
		return b.chainParams.PowLimitBits, nil
	}
	return BigToCompact(nextTarget), nil
}
func (b *BlockChain) getNextEDAWorkRequired(prevBlock *blockNode,
	header *wire.BlockHeader) (uint32, error) {
	curHeight := prevBlock.height + 1
	if int64(curHeight)%b.chainParams.DifficultyAdjustmentInterval() == 0 &&
		int64(curHeight) >= b.chainParams.DifficultyAdjustmentInterval() {
		firstHeight := curHeight - int32(b.chainParams.DifficultyAdjustmentInterval())
		firstNode := b.bestChain.NodeByHeight(firstHeight)
		return b.calculateNextWorkRequired(prevBlock, firstNode.timestamp)
	}
	proofOfWorkLimit := b.chainParams.PowLimitBits
	if b.chainParams.ReduceMinDifficulty {
		if int64(header.Timestamp.Second()) >
			prevBlock.timestamp+2*int64(b.chainParams.TargetTimePerBlock.Seconds()) {
			return proofOfWorkLimit, nil
		}
		node := prevBlock
		for node.parent != nil &&
			int64(node.height)%b.chainParams.DifficultyAdjustmentInterval() != 0 &&
			node.bits == proofOfWorkLimit {
			node = node.parent
		}
		return node.bits, nil
	}
	bits := prevBlock.bits
	if bits == proofOfWorkLimit {
		return proofOfWorkLimit, nil
	}
	node6 := b.bestChain.NodeByHeight(curHeight - 7)
	if node6 == nil {
		panic("the block Index should not equal nil")
	}
	mtp6Blocks := prevBlock.CalcPastMedianTime().Unix() - node6.CalcPastMedianTime().Unix()
	if mtp6Blocks < 12*3600 {
		return bits, nil
	}
	pow := CompactToBig(bits)
	pow.Add(pow, new(big.Int).Div(pow, big.NewInt(4)))
	powLimit := CompactToBig(b.chainParams.PowLimitBits)
	if pow.Cmp(powLimit) > 0 {
		pow = powLimit
	}
	return BigToCompact(pow), nil
}
func (b *BlockChain) calculateNextWorkRequired(prevNode *blockNode,
	firstBlockTime int64) (uint32, error) {
	if b.chainParams == &chaincfg.RegressionNetParams ||
		b.chainParams == &chaincfg.SimNetParams {
		return prevNode.bits, nil
	}
	actualTimeSpan := prevNode.timestamp - firstBlockTime
	targetTimeSpan := int64(b.chainParams.TargetTimespan.Seconds())
	if actualTimeSpan < targetTimeSpan/4 {
		actualTimeSpan = targetTimeSpan / 4
	}
	if actualTimeSpan > targetTimeSpan*4 {
		actualTimeSpan = targetTimeSpan * 4
	}
	bnNew := CompactToBig(prevNode.bits)
	bnNew.Mul(bnNew, big.NewInt(actualTimeSpan))
	bnNew.Div(bnNew, big.NewInt(targetTimeSpan))
	if bnNew.Cmp(b.chainParams.PowLimit) > 0 {
		bnNew = b.chainParams.PowLimit
	}
	return BigToCompact(bnNew), nil
}