func CheckTransactionSanity(tx *soterutil.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().SerializeSizeStripped()
	if serializedTxSize > MaxBlockBaseSize {
		str := fmt.Sprintf("serialized transaction is too big - got "+
			"%d, max %d", serializedTxSize, MaxBlockBaseSize)
		return ruleError(ErrTxTooBig, str)
	}
	var totalNanoSoter int64
	for _, txOut := range msgTx.TxOut {
		nanoSoter := txOut.Value
		if nanoSoter < 0 {
			str := fmt.Sprintf("transaction output has negative "+
				"value of %v", nanoSoter)
			return ruleError(ErrBadTxOutValue, str)
		}
		if nanoSoter > soterutil.MaxNanoSoter {
			str := fmt.Sprintf("transaction output value of %v is "+
				"higher than max allowed value of %v", nanoSoter,
				soterutil.MaxNanoSoter)
			return ruleError(ErrBadTxOutValue, str)
		}
		totalNanoSoter += nanoSoter
		if totalNanoSoter < 0 {
			str := fmt.Sprintf("total value of all transaction "+
				"outputs exceeds max allowed value of %v",
				soterutil.MaxNanoSoter)
			return ruleError(ErrBadTxOutValue, str)
		}
		if totalNanoSoter > soterutil.MaxNanoSoter {
			str := fmt.Sprintf("total value of all transaction "+
				"outputs is %v which is higher than max "+
				"allowed value of %v", totalNanoSoter,
				soterutil.MaxNanoSoter)
			return ruleError(ErrBadTxOutValue, 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) {
		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)
		}
	} else {
		for _, txIn := range msgTx.TxIn {
			if isNullOutpoint(&txIn.PreviousOutPoint) {
				return ruleError(ErrBadTxInput, "transaction "+
					"input refers to previous output that "+
					"is null")
			}
		}
	}
	return nil
}
CheckTransactionSanity performs some preliminary checks on a transaction to
ensure it is sane.  These checks are context free.
package blockdag
import (
	"encoding/binary"
	"fmt"
	"math"
	"math/big"
	"time"
	"github.com/soteria-dag/soterd/chaincfg"
	"github.com/soteria-dag/soterd/chaincfg/chainhash"
	"github.com/soteria-dag/soterd/mining/cuckoo"
	"github.com/soteria-dag/soterd/soterutil"
	"github.com/soteria-dag/soterd/txscript"
	"github.com/soteria-dag/soterd/wire"
)
const (
	MaxTimeOffsetSeconds = 2 * 60 * 60
	MinCoinbaseScriptLen = 2
	MaxCoinbaseScriptLen = 100
	medianTimeBlocks = 11
	serializedHeightVersion = 2
	baseSubsidy = 50 * soterutil.NanoSoterPerSoter
)
var (
	zeroHash chainhash.Hash
)
func isNullOutpoint(outpoint *wire.OutPoint) bool {
	if outpoint.Index == math.MaxUint32 && outpoint.Hash == zeroHash {
		return true
	}
	return false
}
func ShouldHaveSerializedBlockHeight(header *wire.BlockHeader) bool {
	return header.Version >= serializedHeightVersion
}
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 != zeroHash {
		return false
	}
	return true
}
func IsCoinBase(tx *soterutil.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 *soterutil.Tx, blockHeight int32, 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 int32, chainParams *chaincfg.Params) int64 {
	if chainParams.SubsidyReductionInterval == 0 {
		return baseSubsidy
	}
	return baseSubsidy >> uint(height/chainParams.SubsidyReductionInterval)
}