func CheckProofOfWork(block *provautil.Block, powLimit *big.Int) error {
	return checkProofOfWork(&block.MsgBlock().Header, powLimit, 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 (
	"fmt"
	"math"
	"math/big"
	"time"
	"github.com/bitgo/prova/btcec"
	"github.com/bitgo/prova/chaincfg"
	"github.com/bitgo/prova/chaincfg/chainhash"
	"github.com/bitgo/prova/provautil"
	"github.com/bitgo/prova/txscript"
	"github.com/bitgo/prova/wire"
)
const (
	MaxSigOpsPerBlock = wire.MaxBlockPayload / 50
	MaxTimeOffsetSeconds = 2 * 60 * 60
	MinCoinbaseScriptLen = 2
	MaxCoinbaseScriptLen = 100
	medianTimeBlocks = 11
	serializedHeightVersion = 2
	baseSubsidy = 0 * provautil.AtomsPerGram
	MaxAdminKeySetSize = 42
)
var (
	zeroHash = &chainhash.Hash{}
)
func isNullOutpoint(outpoint *wire.OutPoint) bool {
	if outpoint.Index == math.MaxUint32 && outpoint.Hash.IsEqual(zeroHash) {
		return true
	}
	return false
}
func IsCoinBaseTx(msgTx *wire.MsgTx) bool {
	if len(msgTx.TxIn) != 1 {
		return false
	}
	prevOut := &msgTx.TxIn[0].PreviousOutPoint
	if !isNullOutpoint(prevOut) {
		return false
	}
	return true
}
func IsCoinBase(tx *provautil.Tx) bool {
	return IsCoinBaseTx(tx.MsgTx())
}
func SequenceLockActive(sequenceLock *SequenceLock, blockHeight int32,
	medianTimePast time.Time) bool {
	if sequenceLock.Seconds >= medianTimePast.Unix() ||
		sequenceLock.BlockHeight >= blockHeight {
		return false
	}
	return true
}
func IsFinalizedTransaction(tx *provautil.Tx, blockHeight uint32, blockTime time.Time) bool {
	msgTx := tx.MsgTx()
	lockTime := msgTx.LockTime
	if lockTime == 0 {
		return true
	}
	blockTimeOrHeight := int64(0)
	if lockTime < txscript.LockTimeThreshold {
		blockTimeOrHeight = int64(blockHeight)
	} else {
		blockTimeOrHeight = blockTime.Unix()
	}
	if int64(lockTime) < blockTimeOrHeight {
		return true
	}
	for _, txIn := range msgTx.TxIn {
		if txIn.Sequence != math.MaxUint32 {
			return false
		}
	}
	return true
}
func CalcBlockSubsidy(height uint32, chainParams *chaincfg.Params) int64 {
	if chainParams.SubsidyReductionInterval == 0 {
		return baseSubsidy
	}
	return baseSubsidy >> uint(height/chainParams.SubsidyReductionInterval)
}
func CheckTransactionSanity(tx *provautil.Tx) error {
	msgTx := tx.MsgTx()
	if len(msgTx.TxIn) == 0 {
		return ruleError(ErrNoTxInputs, "transaction has no inputs")
	}
	if len(msgTx.TxOut) == 0 {
		return ruleError(ErrNoTxOutputs, "transaction has no outputs")
	}
	serializedTxSize := tx.MsgTx().SerializeSize()
	if serializedTxSize > wire.MaxBlockPayload {
		str := fmt.Sprintf("serialized transaction is too big - got "+
			"%d, max %d", serializedTxSize, wire.MaxBlockPayload)
		return ruleError(ErrTxTooBig, str)
	}
	var totalAtoms int64
	threadInt, adminOutputs := txscript.GetAdminDetails(tx)
	hasAdminOut := (threadInt >= 0)
	for txOutIndex, txOut := range msgTx.TxOut {
		atoms := txOut.Value
		if atoms < 0 {
			str := fmt.Sprintf("transaction output has negative "+
				"value of %v", atoms)
			return ruleError(ErrBadTxOutValue, str)
		}
		if atoms > provautil.MaxAtoms {
			str := fmt.Sprintf("transaction output value of %v is "+
				"higher than max allowed value of %v", atoms,
				provautil.MaxAtoms)
			return ruleError(ErrBadTxOutValue, str)
		}
		totalAtoms += atoms
		if totalAtoms < 0 {
			str := fmt.Sprintf("total value of all transaction "+
				"outputs exceeds max allowed value of %v",
				provautil.MaxAtoms)
			return ruleError(ErrBadTxOutValue, str)
		}
		if totalAtoms > provautil.MaxAtoms {
			str := fmt.Sprintf("total value of all transaction "+
				"outputs is %v which is higher than max "+
				"allowed value of %v", totalAtoms,
				provautil.MaxAtoms)
			return ruleError(ErrBadTxOutValue, str)
		}
		scriptClass := txscript.GetScriptClass(txOut.PkScript)
		if scriptClass == txscript.ProvaAdminTy {
			if txOutIndex != 0 {
				str := fmt.Sprintf("transaction output %d: admin output "+
					"only allowed at position 0.", txOutIndex)
				return ruleError(ErrInvalidAdminTx, str)
			}
		}
		if hasAdminOut {
			if provautil.ThreadID(threadInt) != provautil.IssueThread {
				if txOut.Value != 0 {
					str := fmt.Sprintf("admin transaction with non-zero value "+
						"output #%d.", txOutIndex)
					return ruleError(ErrInvalidAdminTx, str)
				}
			} else {
				if txOutIndex > 0 {
					isDestruction := len(msgTx.TxIn) > 1
					if scriptClass == txscript.NullDataTy {
						if !isDestruction {
							str := fmt.Sprintf("issue transaction %v tries to destroy funds", tx.Hash)
							return ruleError(ErrInvalidAdminTx, str)
						}
						if atoms == 0 {
							str := fmt.Sprintf("admin issue transaction %v "+
								"trying to destroy 0 at output "+
								"#%d.", tx.Hash, txOutIndex)
							return ruleError(ErrInvalidAdminTx, str)
						}
					} else {
						if scriptClass != txscript.ProvaTy &&
							scriptClass != txscript.GeneralProvaTy {
							str := fmt.Sprintf("admin issue transaction %v "+
								"expected to have prova output at %d"+
								"but found %x.", tx.Hash, txOutIndex)
							return ruleError(ErrInvalidAdminTx, str)
						}
						if atoms == 0 {
							str := fmt.Sprintf("admin issue transaction %v "+
								"trying to issue 0 at output "+
								"#%d.", tx.Hash, txOutIndex)
							return ruleError(ErrInvalidAdminTx, str)
						}
					}
				}
			}
		}
	}
	existingTxOut := make(map[wire.OutPoint]struct{})
	for _, txIn := range msgTx.TxIn {
		if _, exists := existingTxOut[txIn.PreviousOutPoint]; exists {
			return ruleError(ErrDuplicateTxInputs, "transaction "+
				"contains duplicate inputs")
		}
		existingTxOut[txIn.PreviousOutPoint] = struct{}{}
	}
	if IsCoinBase(tx) {
		if !txscript.IsProvaTx(tx) {
			return ruleError(ErrInvalidCoinbase, "coinbase transaction is not of an allowed form")
		}
		slen := len(msgTx.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)
		}
		return nil
	}
	if hasAdminOut {
		threadId := provautil.ThreadID(threadInt)
		if threadId == provautil.RootThread || threadId == provautil.ProvisionThread {
			if len(msgTx.TxIn) > 1 {
				str := fmt.Sprintf("admin transaction with more than 1 input.")
				return ruleError(ErrInvalidAdminTx, str)
			}
			if len(msgTx.TxOut) < 2 {
				str := fmt.Sprintf("admin transaction with no admin operations.")
				return ruleError(ErrInvalidAdminTx, str)
			}
			for _, adminOpOut := range adminOutputs {
				if !txscript.IsValidAdminOp(adminOpOut, threadId) {
					str := fmt.Sprintf("admin transaction with invalid admin " +
						"operation found.")
					return ruleError(ErrInvalidAdminTx, str)
				}
			}
		}
	}
	if !(threadInt >= 0) && !txscript.IsProvaTx(tx) {
		return ruleError(ErrInvalidTx, "transaction is not of an allowed form")
	}
	for _, txIn := range msgTx.TxIn {
		prevOut := &txIn.PreviousOutPoint
		if isNullOutpoint(prevOut) {
			return ruleError(ErrBadTxInput, "transaction "+
				"input refers to previous output that "+
				"is null")
		}
	}
	return nil
}
func checkProofOfWork(header *wire.BlockHeader, powLimit *big.Int, 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)
	}
	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 && header.Height >= 1 {
		hash := header.BlockHash()
		hashNum := HashToBig(&hash)
		if hashNum.Cmp(target) > 0 {
			str := fmt.Sprintf("block hash of %064x is higher than "+
				"expected max of %064x", hashNum, target)
			return ruleError(ErrHighHash, str)
		}
	}
	return nil
}