func (b *BlockChain) checkBIP0030(node *blockNode, block *provautil.Block, view *UtxoViewpoint) error {
	fetchSet := make(map[chainhash.Hash]struct{})
	for _, tx := range block.Transactions() {
		fetchSet[*tx.Hash()] = struct{}{}
	}
	err := view.fetchUtxos(b.db, fetchSet)
	if err != nil {
		return err
	}
	for _, tx := range block.Transactions() {
		txEntry := view.LookupEntry(tx.Hash())
		if txEntry != nil && !txEntry.IsFullySpent() {
			str := fmt.Sprintf("tried to overwrite transaction %v "+
				"at block height %d that is not fully spent",
				tx.Hash(), txEntry.blockHeight)
			return ruleError(ErrOverwriteTx, str)
		}
	}
	return nil
}
checkBIP0030 ensures blocks do not contain duplicate transactions which
'overwrite' older transactions that are not fully spent.  This prevents an
attack where a coinbase and all of its dependent transactions could be
duplicated to effectively revert the overwritten transactions to a single
confirmation thereby making them vulnerable to a double spend.

For more details, see https://en.bitcoin.it/wiki/BIP_0030 and
http://r6.ca/blog/20120206T005236Z.html.

This function MUST be called with the chain state lock held (for reads).
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
}
func CheckProofOfWork(block *provautil.Block, powLimit *big.Int) error {
	return checkProofOfWork(&block.MsgBlock().Header, powLimit, BFNone)
}
func CountSigOps(tx *provautil.Tx) int {
	msgTx := tx.MsgTx()
	totalSigOps := 0
	for _, txIn := range msgTx.TxIn {
		numSigOps := txscript.GetSigOpCount(txIn.SignatureScript)
		totalSigOps += numSigOps
	}
	for _, txOut := range msgTx.TxOut {
		numSigOps := txscript.GetSigOpCount(txOut.PkScript)
		totalSigOps += numSigOps
	}
	return totalSigOps
}
func CountP2SHSigOps(tx *provautil.Tx, isCoinBaseTx bool, utxoView *UtxoViewpoint) (int, error) {
	if isCoinBaseTx {
		return 0, nil
	}
	msgTx := tx.MsgTx()
	totalSigOps := 0
	for txInIndex, txIn := range msgTx.TxIn {
		originTxHash := &txIn.PreviousOutPoint.Hash
		originTxIndex := txIn.PreviousOutPoint.Index
		txEntry := utxoView.LookupEntry(originTxHash)
		if txEntry == nil || txEntry.IsOutputSpent(originTxIndex) {
			str := fmt.Sprintf("unable to find unspent output "+
				"%v referenced from transaction %s:%d",
				txIn.PreviousOutPoint, tx.Hash(), txInIndex)
			return 0, ruleError(ErrMissingTx, str)
		}
		pkScript := txEntry.PkScriptByIndex(originTxIndex)
		if !txscript.IsPayToScriptHash(pkScript) {
			continue
		}
		sigScript := txIn.SignatureScript
		numSigOps := txscript.GetPreciseSigOpCount(sigScript, pkScript,
			true)
		lastSigOps := totalSigOps
		totalSigOps += numSigOps
		if totalSigOps < lastSigOps {
			str := fmt.Sprintf("the public key script from output "+
				"%v contains too many signature operations - "+
				"overflow", txIn.PreviousOutPoint)
			return 0, ruleError(ErrTooManySigOps, str)
		}
	}
	return totalSigOps, nil
}
func checkBlockHeaderSanity(header *wire.BlockHeader, powLimit *big.Int, timeSource MedianTimeSource, flags BehaviorFlags) error {
	err := checkProofOfWork(header, powLimit, flags)
	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)
	}
	return nil
}
func checkBlockSanity(block *provautil.Block, powLimit *big.Int, timeSource MedianTimeSource, flags BehaviorFlags) error {
	msgBlock := block.MsgBlock()
	header := &msgBlock.Header
	err := checkBlockHeaderSanity(header, powLimit, timeSource, flags)
	if err != nil {
		return err
	}
	numTx := len(msgBlock.Transactions)
	if numTx == 0 {
		return ruleError(ErrNoTransactions, "block does not contain "+
			"any transactions")
	}
	if numTx > wire.MaxBlockPayload {
		str := fmt.Sprintf("block contains too many transactions - "+
			"got %d, max %d", numTx, wire.MaxBlockPayload)
		return ruleError(ErrTooManyTransactions, str)
	}
	serializedSize := msgBlock.SerializeSize()
	if serializedSize != int(header.Size) {
		str := fmt.Sprintf("serialized block size %d, does not match "+
			"header size %d", serializedSize, header.Size)
		return ruleError(ErrInconsistentBlkSize, str)
	}
	if serializedSize > wire.MaxBlockPayload {
		str := fmt.Sprintf("serialized block is too big - got %d, "+
			"max %d", serializedSize, wire.MaxBlockPayload)
		return ruleError(ErrBlockTooBig, str)
	}
	transactions := block.Transactions()
	if !IsCoinBase(transactions[0]) {
		return ruleError(ErrFirstTxNotCoinbase, "first transaction in "+
			"block is not a coinbase")
	}
	for i, tx := range transactions[1:] {
		if IsCoinBase(tx) {
			str := fmt.Sprintf("block contains second coinbase at "+
				"index %d", i+1)
			return ruleError(ErrMultipleCoinbases, str)
		}
	}
	for _, tx := range transactions {
		err := CheckTransactionSanity(tx)
		if err != nil {
			return err
		}
	}
	merkles := BuildMerkleTreeStore(block.Transactions())
	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)
	}
	existingTxHashes := make(map[chainhash.Hash]struct{})
	for _, tx := range transactions {
		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 transactions {
		lastSigOps := totalSigOps
		totalSigOps += CountSigOps(tx)
		if totalSigOps < lastSigOps || totalSigOps > MaxSigOpsPerBlock {
			str := fmt.Sprintf("block contains too many signature "+
				"operations - got %v, max %v", totalSigOps,
				MaxSigOpsPerBlock)
			return ruleError(ErrTooManySigOps, str)
		}
	}
	return nil
}
func CheckBlockSanity(block *provautil.Block, powLimit *big.Int, timeSource MedianTimeSource) error {
	return checkBlockSanity(block, powLimit, timeSource, BFNone)
}
func (b *BlockChain) checkBlockHeaderContext(header *wire.BlockHeader, prevNode *blockNode, flags BehaviorFlags) error {
	if prevNode == nil {
		return nil
	}
	fastAdd := flags&BFFastAdd == BFFastAdd
	if !fastAdd {
		expectedDifficulty, err := b.calcNextRequiredDifficulty(prevNode)
		if err != nil {
			return err
		}
		blockDifficulty := header.Bits
		if blockDifficulty != expectedDifficulty {
			str := "block difficulty of %d is not the expected value of %d"
			str = fmt.Sprintf(str, blockDifficulty, expectedDifficulty)
			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)
		}
		pubKey, err := btcec.ParsePubKey(header.ValidatingPubKey[:], btcec.S256())
		if err != nil {
			return err
		}
		if !header.Verify(pubKey) {
			return ruleError(ErrBadBlockSignature, "unable to validate block signature")
		}
	}
	blockHeight := prevNode.height + 1
	if header.Height != blockHeight {
		str := "block height of %d is not the expected value of %d"
		str = fmt.Sprintf(str, header.Height, blockHeight)
		return ruleError(ErrBadHeight, str)
	}
	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)
	}
	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 {
		if header.Version < 4 && b.isMajorityVersion(4, prevNode,
			b.chainParams.BlockRejectNumRequired) {
			str := "new blocks with version %d are no longer valid"
			str = fmt.Sprintf(str, header.Version)
			return ruleError(ErrBlockVersionTooOld, str)
		}
		if header.Version < 3 && b.isMajorityVersion(3, prevNode,
			b.chainParams.BlockRejectNumRequired) {
			str := "new blocks with version %d are no longer valid"
			str = fmt.Sprintf(str, header.Version)
			return ruleError(ErrBlockVersionTooOld, str)
		}
		if header.Version < 2 && b.isMajorityVersion(2, prevNode,
			b.chainParams.BlockRejectNumRequired) {
			str := "new blocks with version %d are no longer valid"
			str = fmt.Sprintf(str, header.Version)
			return ruleError(ErrBlockVersionTooOld, str)
		}
	}
	return nil
}
func (b *BlockChain) checkBlockContext(block *provautil.Block, prevNode *blockNode, flags BehaviorFlags) error {
	if prevNode == nil {
		return nil
	}
	header := &block.MsgBlock().Header
	err := b.checkBlockHeaderContext(header, prevNode, flags)
	if err != nil {
		return err
	}
	fastAdd := flags&BFFastAdd == BFFastAdd
	if !fastAdd {
		blockHeight := prevNode.height + 1
		for _, tx := range block.Transactions() {
			if !IsFinalizedTransaction(tx, blockHeight,
				header.Timestamp) {
				str := fmt.Sprintf("block contains unfinalized "+
					"transaction %v", tx.Hash())
				return ruleError(ErrUnfinalizedTx, str)
			}
		}
	}
	return nil
}