func (b *BlockChain) checkBlockHeaderContext(header *wire.BlockHeader, prevNode *blockNode, flags BehaviorFlags) error {
	if prevNode == nil {
		return nil
	}
	fastAdd := flags&BFFastAdd == BFFastAdd
	if !fastAdd {
		expDiff, err := b.calcNextRequiredDifficulty(prevNode,
			header.Timestamp)
		if err != nil {
			return err
		}
		blockDifficulty := header.Bits
		if blockDifficulty != expDiff {
			str := fmt.Sprintf("block difficulty of %d is not the"+
				" expected "+"value of %d", blockDifficulty,
				expDiff)
			return ruleError(ErrUnexpectedDifficulty, str)
		}
		medianTime, err := b.calcPastMedianTime(prevNode)
		if err != nil {
			log.Errorf("calcPastMedianTime: %v", err)
			return err
		}
		if !header.Timestamp.After(medianTime) {
			str := "block timestamp of %v is not after expected %v"
			str = fmt.Sprintf(str, header.Timestamp, medianTime)
			return ruleError(ErrTimeTooOld, str)
		}
	}
	blockHeight := prevNode.height + 1
	blockHash := header.BlockHash()
	if !b.verifyCheckpoint(blockHeight, &blockHash) {
		str := fmt.Sprintf("block at height %d does not match "+
			"checkpoint hash", blockHeight)
		return ruleError(ErrBadCheckpoint, str)
	}
	if header.Height > 1 && (header.VoteBits & hcashutil.BlockValid) != (prevNode.header.VoteBits & hcashutil.BlockValid) {
		if HashToBig(&(prevNode.hash)).Cmp(CompactToBig(prevNode.header.Bits)) > 0 {
			str := fmt.Sprintf("VoteBits of block %v is not same as the parent micro block %v", header.BlockHash(), prevNode.hash)
			return ruleError(ErrVoteBitsNotCompatible, str)
		}
	}
	checkpointBlock, err := b.findPreviousCheckpoint()
	if err != nil {
		return err
	}
	if checkpointBlock != nil && blockHeight < checkpointBlock.Height() {
		str := fmt.Sprintf("block at height %d forks the main chain "+
			"before the previous checkpoint at height %d",
			blockHeight, checkpointBlock.Height())
		return ruleError(ErrForkTooOld, str)
	}
	if !fastAdd {
	}
	return nil
}
checkBlockHeaderContext peforms several validation checks on the block
header which depend on its position within the block chain.

The flags modify the behavior of this function as follows:
- BFFastAdd: All checks except those involving comparing the header against
the checkpoints are not performed.

This function MUST be called with the chain state lock held (for writes).
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
}
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)
}
func checkBlockHeaderSanity(chain *BlockChain, block *hcashutil.Block, timeSource MedianTimeSource, flags BehaviorFlags, chainParams *chaincfg.Params) error {
	powLimit := chainParams.PowLimit
	posLimit := chainParams.MinimumStakeDiff
	difficultyRate := chainParams.DifficultyRate
	min := chainParams.MicroBlockValidationHeight
	header := &block.MsgBlock().Header
	err := checkProofOfWork(chain, header, powLimit, difficultyRate, uint32(min), flags)
	if err != nil {
		return err
	}
	err = checkProofOfStake(block, posLimit)
	if err != nil {
		return err
	}
	if !header.Timestamp.Equal(time.Unix(header.Timestamp.Unix(), 0)) {
		str := fmt.Sprintf("block timestamp of %v has a higher "+
			"precision than one second", header.Timestamp)
		return ruleError(ErrInvalidTime, str)
	}
	maxTimestamp := timeSource.AdjustedTime().Add(time.Second *
		MaxTimeOffsetSeconds)
	if header.Timestamp.After(maxTimestamp) {
		str := fmt.Sprintf("block timestamp of %v is too far in the "+
			"future", header.Timestamp)
		return ruleError(ErrTimeTooNew, str)
	}
	return nil
}
func checkBlockSanity(chain *BlockChain, block *hcashutil.Block, timeSource MedianTimeSource, flags BehaviorFlags, chainParams *chaincfg.Params) error {
	msgBlock := block.MsgBlock()
	header := &msgBlock.Header
	err := checkBlockHeaderSanity(chain, block, timeSource, flags, chainParams)
	isKeyBlock := false
	noPowCheck := flags&BFNoPoWCheck == BFNoPoWCheck
	if err != nil {
		return err
	}
	numTx := len(msgBlock.Transactions)
	if numTx == 0 {
		return ruleError(ErrNoTransactions, "block does not contain "+
			"any transactions")
	}
	serializedSize := msgBlock.SerializeSize()
	if serializedSize > wire.MaxBlockPayload {
		str := fmt.Sprintf("serialized block is too big - got %d, "+
			"max %d", serializedSize, wire.MaxBlockPayload)
		return ruleError(ErrBlockTooBig, str)
	}
	if msgBlock.Header.Size < uint32(serializedSize) {
		str := fmt.Sprintf("serialized block is not size indicated in "+
			"header - got %d, expected %d", msgBlock.Header.Size,
			serializedSize)
		return ruleError(ErrWrongBlockSize, str)
	}
	transactions := block.Transactions()
	if !IsCoinBaseTx(transactions[0].MsgTx()) {
		return ruleError(ErrFirstTxNotCoinbase, "first transaction in "+
			"block is not a coinbase")
	}
	txStart := 1
	if noPowCheck {
		if !IsCoinBaseTx(transactions[1].MsgTx()) {
			return ruleError(ErrFirstTxNotCoinbase, "first transaction in "+
				"block is not a coinbase")
		}
		txStart = 2
	}else {
		targetDifficulty := CompactToBig(header.Bits)
		if HashToBig(block.Hash()).Cmp(targetDifficulty) <= 0 {
			if !IsCoinBaseTx(transactions[1].MsgTx()) {
				return ruleError(ErrFirstTxNotCoinbase, "first transaction in "+
					"block is not a coinbase")
			}
			isKeyBlock = true
			txStart = 2
		}
	}
	for i, tx := range transactions[txStart:] {
		if IsCoinBaseTx(tx.MsgTx()) {
			str := fmt.Sprintf("block contains second coinbase at "+
				"index %d", i+1)
			return ruleError(ErrMultipleCoinbases, str)
		}
	}
	for _, tx := range transactions {
		msgTx := tx.MsgTx()
		txType := stake.DetermineTxType(msgTx)
		if txType != stake.TxTypeRegular {
			errStr := fmt.Sprintf("found stake tx in regular tx " +
				"tree")
			return ruleError(ErrStakeTxInRegularTree, errStr)
		}
		err := CheckTransactionSanity(msgTx, chainParams)
		if err != nil {
			return err
		}
	}
	totalTickets := 0
	totalVotes := 0
	totalRevocations := 0
	if isKeyBlock {
		for _, stx := range block.MsgBlock().STransactions {
			err := CheckTransactionSanity(stx, chainParams)
			if err != nil {
				return err
			}
			txType := stake.DetermineTxType(stx)
			if txType == stake.TxTypeRegular {
				errStr := fmt.Sprintf("found regular tx in stake tx " +
					"tree")
				return ruleError(ErrRegTxInStakeTree, errStr)
			}
			switch txType {
			case stake.TxTypeSStx:
				totalTickets++
			case stake.TxTypeSSGen:
				totalVotes++
			case stake.TxTypeSSRtx:
				totalRevocations++
			}
		}
		if totalTickets != int(block.MsgBlock().Header.FreshStake) {
			errStr := fmt.Sprintf("%v tickets found in block, while "+
				"header reports %v", totalTickets,
				block.MsgBlock().Header.FreshStake)
			return ruleError(ErrFreshStakeMismatch, errStr)
		}
		if int64(block.MsgBlock().Header.KeyHeight + 1) >= chainParams.StakeValidationHeight &&
			totalVotes <= int(chainParams.TicketsPerBlock)/2 {
			errStr := fmt.Sprintf("block contained too few votes! %v "+
				"votes but %v or more required", totalVotes,
				(int(chainParams.TicketsPerBlock)/2)+1)
			return ruleError(ErrNotEnoughVotes, errStr)
		}
		if totalVotes > int(chainParams.TicketsPerBlock) {
			errStr := fmt.Sprintf("the number of SSGen tx in block %v "+
				"was %v, overflowing the maximum allowed (%v)",
				block.Hash(), totalVotes,
				int(chainParams.TicketsPerBlock))
			return ruleError(ErrTooManyVotes, errStr)
		}
		if totalVotes != int(block.MsgBlock().Header.Voters) {
			errStr := fmt.Sprintf("%v votes found in block, while header "+
				"reports %v", totalVotes,
				block.MsgBlock().Header.Voters)
			return ruleError(ErrVotesMismatch, errStr)
		}
		if totalRevocations != int(block.MsgBlock().Header.Revocations) {
			errStr := fmt.Sprintf("%v revocations found in block, while "+
				"header reports %v", totalRevocations,
				block.MsgBlock().Header.Revocations)
			return ruleError(ErrRevocationsMismatch, errStr)
		}
	}
	merkles := BuildMerkleTreeStore(block.Transactions(), !(isKeyBlock || noPowCheck))
	calculatedMerkleRoot := merkles[len(merkles)-1]
	if !header.MerkleRoot.IsEqual(calculatedMerkleRoot) {
		str := fmt.Sprintf("block merkle root is invalid - block "+
			"header indicates %v, but calculated value is %v",
			header.MerkleRoot, calculatedMerkleRoot)
		return ruleError(ErrBadMerkleRoot, str)
	}
	if isKeyBlock {
		merkleStake := BuildMerkleTreeStore(block.STransactions(), false)
		calculatedStakeMerkleRoot := merkleStake[len(merkleStake)-1]
		if !header.StakeRoot.IsEqual(calculatedStakeMerkleRoot) {
			str := fmt.Sprintf("block stake merkle root is invalid - block"+
				" header indicates %v, but calculated value is %v",
				header.StakeRoot, calculatedStakeMerkleRoot)
			return ruleError(ErrBadMerkleRoot, str)
		}
	}
	existingTxHashes := make(map[chainhash.Hash]struct{})
	stakeTransactions := block.STransactions()
	allTransactions := append(transactions, stakeTransactions...)
	for _, tx := range allTransactions {
		hash := tx.Hash()
		if _, exists := existingTxHashes[*hash]; exists {
			str := fmt.Sprintf("block contains duplicate "+
				"transaction %v", hash)
			return ruleError(ErrDuplicateTx, str)
		}
		existingTxHashes[*hash] = struct{}{}
	}
	totalSigOps := 0
	for _, tx := range allTransactions {
		msgTx := tx.MsgTx()
		lastSigOps := totalSigOps
		isSSGen, _ := stake.IsSSGen(msgTx)
		isCoinBase := IsCoinBaseTx(msgTx)
		totalSigOps += CountSigOps(tx, isCoinBase, isSSGen)
		if totalSigOps < lastSigOps || totalSigOps > MaxSigOpsPerBlock {
			str := fmt.Sprintf("block contains too many signature "+
				"operations - got %v, max %v", totalSigOps,
				MaxSigOpsPerBlock)
			return ruleError(ErrTooManySigOps, str)
		}
	}
	if int64(header.KeyHeight + 1) < chainParams.StakeValidationHeight {
		if header.VoteBits != earlyVoteBitsValue {
			str := fmt.Sprintf("pre stake validation height "+
				"block %v contained an invalid votebits value"+
				" (expected %v, got %v)", block.Hash(),
				earlyVoteBitsValue, header.VoteBits)
			return ruleError(ErrInvalidEarlyVoteBits, str)
		}
	}
	return nil
}
func CheckBlockSanity(chain *BlockChain, block *hcashutil.Block, timeSource MedianTimeSource, chainParams *chaincfg.Params) error {
	return checkBlockSanity(chain, block, timeSource, BFNone, chainParams)
}
func CheckWorklessBlockSanity(chain *BlockChain, block *hcashutil.Block, timeSource MedianTimeSource, chainParams *chaincfg.Params) error {
	return checkBlockSanity(chain , block, timeSource, BFNoPoWCheck, chainParams)
}