func CheckProofOfWork(chain *BlockChain, block *hcashutil.Block, powLimit *big.Int, difficultyRate uint32, min uint32) error {
	return checkProofOfWork(chain, &block.MsgBlock().Header, powLimit, difficultyRate, min, BFNone)
}
CheckProofOfWork ensures the block header bits which indicate the target
difficulty is in min/max range and that the block hash is less than the
target difficulty as claimed.
package blockchain
import (
	"bytes"
	"fmt"
	"math"
	"math/big"
	"time"
	"github.com/HcashOrg/hcashd/blockchain/stake"
	"github.com/HcashOrg/hcashd/chaincfg"
	"github.com/HcashOrg/hcashd/chaincfg/chainhash"
	"github.com/HcashOrg/hcashd/database"
	"github.com/HcashOrg/hcashd/txscript"
	"github.com/HcashOrg/hcashd/wire"
	"github.com/HcashOrg/hcashutil"
)
const (
	MaxSigOpsPerBlock = 3000000
	MaxTimeOffsetSeconds = 2 * 60 * 60
	MinCoinbaseScriptLen = 2
	MaxCoinbaseScriptLen = 100
	medianTimeBlocks = 11
	earlyVoteBitsValue = 0x0001
	FraudProofNoCheck = 0
	FraudProofKeyBlockCheck = 1
	FraudProofMicroBlockCheck = 2
)
var (
	zeroHash = &chainhash.Hash{}
)
func isNullOutpoint(outpoint *wire.OutPoint) bool {
	if outpoint.Index == math.MaxUint32 &&
		outpoint.Hash.IsEqual(zeroHash) &&
		outpoint.Tree == wire.TxTreeRegular {
		return true
	}
	return false
}
func isNullFraudProof(txIn *wire.TxIn) bool {
	switch {
	case txIn.BlockHeight != wire.NullBlockHeight:
		return false
	case txIn.BlockIndex != wire.NullBlockIndex:
		return false
	}
	return true
}
func IsCoinBaseTx(msgTx *wire.MsgTx) bool {
	if len(msgTx.TxIn) != 1 {
		return false
	}
	prevOut := &msgTx.TxIn[0].PreviousOutPoint
	if prevOut.Index != math.MaxUint32 || !prevOut.Hash.IsEqual(zeroHash) {
		return false
	}
	return true
}
func IsCoinBase(tx *hcashutil.Tx) bool {
	return IsCoinBaseTx(tx.MsgTx())
}
func CheckTransactionSanity(tx *wire.MsgTx, params *chaincfg.Params) error {
	if len(tx.TxIn) == 0 {
		return ruleError(ErrNoTxInputs, "transaction has no inputs")
	}
	if len(tx.TxOut) == 0 {
		return ruleError(ErrNoTxOutputs, "transaction has no outputs")
	}
	serializedTxSize := tx.SerializeSize()
	if serializedTxSize > params.MaxTxSize {
		str := fmt.Sprintf("serialized transaction is too big - got "+
			"%d, max %d", serializedTxSize, params.MaxTxSize)
		return ruleError(ErrTxTooBig, str)
	}
	var totalAtom int64
	for _, txOut := range tx.TxOut {
		atom := txOut.Value
		if atom < 0 {
			str := fmt.Sprintf("transaction output has negative "+
				"value of %v", atom)
			return ruleError(ErrBadTxOutValue, str)
		}
		if atom > hcashutil.MaxAmount {
			str := fmt.Sprintf("transaction output value of %v is "+
				"higher than max allowed value of %v", atom,
				hcashutil.MaxAmount)
			return ruleError(ErrBadTxOutValue, str)
		}
		totalAtom += atom
		if totalAtom < 0 {
			str := fmt.Sprintf("total value of all transaction "+
				"outputs exceeds max allowed value of %v",
				hcashutil.MaxAmount)
			return ruleError(ErrBadTxOutValue, str)
		}
		if totalAtom > hcashutil.MaxAmount {
			str := fmt.Sprintf("total value of all transaction "+
				"outputs is %v which is higher than max "+
				"allowed value of %v", totalAtom,
				hcashutil.MaxAmount)
			return ruleError(ErrBadTxOutValue, str)
		}
	}
	isSSGen, _ := stake.IsSSGen(tx)
	if IsCoinBaseTx(tx) {
		if !isNullOutpoint(&tx.TxIn[0].PreviousOutPoint) {
			str := fmt.Sprintf("coinbase transaction did not use " +
				"a null outpoint")
			return ruleError(ErrBadCoinbaseOutpoint, str)
		}
		if !isNullFraudProof(tx.TxIn[0]) {
			str := fmt.Sprintf("coinbase transaction fraud proof " +
				"was non-null")
			return ruleError(ErrBadCoinbaseFraudProof, str)
		}
		slen := len(tx.TxIn[0].SignatureScript)
		if slen < MinCoinbaseScriptLen || slen > MaxCoinbaseScriptLen {
			str := fmt.Sprintf("coinbase transaction script "+
				"length of %d is out of range (min: %d, max: "+
				"%d)", slen, MinCoinbaseScriptLen,
				MaxCoinbaseScriptLen)
			return ruleError(ErrBadCoinbaseScriptLen, str)
		}
	} else if isSSGen {
		slen := len(tx.TxIn[0].SignatureScript)
		if slen < MinCoinbaseScriptLen || slen > MaxCoinbaseScriptLen {
			str := fmt.Sprintf("stakebase transaction script "+
				"length of %d is out of range (min: %d, max: "+
				"%d)", slen, MinCoinbaseScriptLen,
				MaxCoinbaseScriptLen)
			return ruleError(ErrBadStakebaseScriptLen, str)
		}
		if !bytes.Equal(tx.TxIn[0].SignatureScript,
			params.StakeBaseSigScript) {
			str := fmt.Sprintf("stakebase transaction signature "+
				"script was set to disallowed value (got %x, "+
				"want %x)", tx.TxIn[0].SignatureScript,
				params.StakeBaseSigScript)
			return ruleError(ErrBadStakebaseScrVal, str)
		}
		ticketHash := &tx.TxIn[1].PreviousOutPoint
		if isNullOutpoint(ticketHash) {
			return ruleError(ErrBadTxInput, "ssgen tx ticket input"+
				" refers to previous output that is null")
		}
	} else {
		for _, txIn := range tx.TxIn {
			prevOut := &txIn.PreviousOutPoint
			if isNullOutpoint(prevOut) {
				return ruleError(ErrBadTxInput, "transaction "+
					"input refers to previous output that "+
					"is null")
			}
		}
	}
	existingTxOut := make(map[wire.OutPoint]struct{})
	for _, txIn := range tx.TxIn {
		if _, exists := existingTxOut[txIn.PreviousOutPoint]; exists {
			return ruleError(ErrDuplicateTxInputs, "transaction "+
				"contains duplicate inputs")
		}
		existingTxOut[txIn.PreviousOutPoint] = struct{}{}
	}
	return nil
}
func checkProofOfStake(block *hcashutil.Block, posLimit int64) error {
	msgBlock := block.MsgBlock()
	for _, staketx := range block.STransactions() {
		msgTx := staketx.MsgTx()
		if is, _ := stake.IsSStx(msgTx); is {
			commitValue := msgTx.TxOut[0].Value
			if commitValue < msgBlock.Header.SBits {
				errStr := fmt.Sprintf("Stake tx %v has a "+
					"commitment value less than the "+
					"minimum stake difficulty specified in"+
					" the block (%v)", staketx.Hash(),
					msgBlock.Header.SBits)
				return ruleError(ErrNotEnoughStake, errStr)
			}
			if commitValue < posLimit {
				errStr := fmt.Sprintf("Stake tx %v has a "+
					"commitment value less than the "+
					"minimum stake difficulty for the "+
					"network (%v)", staketx.Hash(),
					posLimit)
				return ruleError(ErrStakeBelowMinimum, errStr)
			}
		}
	}
	return nil
}
func CheckProofOfStake(block *hcashutil.Block, posLimit int64) error {
	return checkProofOfStake(block, posLimit)
}
func checkProofOfWork(chain *BlockChain, header *wire.BlockHeader, powLimit *big.Int, difficultyRate uint32,
	microBlockValidationHeight uint32, flags BehaviorFlags) error {
	target := CompactToBig(header.Bits)
	if target.Sign() <= 0 {
		str := fmt.Sprintf("block target difficulty of %064x is too "+
			"low", target)
		return ruleError(ErrUnexpectedDifficulty, str)
	}
	microTarget := big.NewInt(0)
	microTarget.Mul(target, big.NewInt(int64(difficultyRate)))
	if target.Cmp(powLimit) > 0 {
		str := fmt.Sprintf("block target difficulty of %064x is "+
			"higher than max of %064x", target, powLimit)
		return ruleError(ErrUnexpectedDifficulty, str)
	}
	if flags&BFNoPoWCheck != BFNoPoWCheck {
		hash := header.BlockHash()
		hashNum := HashToBig(&hash)
		if header.Height <= microBlockValidationHeight{
			if hashNum.Cmp(target) > 0 {
				str := fmt.Sprintf("the block height  %v, is not larger than " +
					"than microBlockMinHeight %v: ", header.Height, microBlockValidationHeight)
				return ruleError(ErrHighHash, str)
			}
		} else {
			if hashNum.Cmp(microTarget) > 0 {
				str := fmt.Sprintf("block hash of %064x is higher than"+
					" expected max of %064x", hashNum, microTarget)
			return ruleError(ErrHighHash, str)
			}
		}
	}
	return nil
}