func IsSSGen(tx *wire.MsgTx) (bool, error) {
	if len(tx.TxIn) != NumInputsPerSSGen {
		return false, stakeRuleError(ErrSSGenWrongNumInputs, "SSgen tx has an "+
			"invalid number of inputs")
	}
	if len(tx.TxOut) > MaxOutputsPerSSGen {
		return false, stakeRuleError(ErrSSGenTooManyOutputs, "SSgen tx has too "+
			"many outputs")
	}
	if len(tx.TxOut) == 0 {
		return false, stakeRuleError(ErrSSGenNoOutputs, "SSgen tx no "+
			"many outputs")
	}
	if !IsStakeBase(tx) {
		return false, stakeRuleError(ErrSSGenNoStakebase, "SSGen tx did not "+
			"include a stakebase in the zeroeth input position")
	}
	for i, txin := range tx.TxIn {
		if i == 0 {
			continue
		}
		if txin.PreviousOutPoint.Index != 0 {
			errStr := fmt.Sprintf("SSGen used an invalid input idx (got %v, "+
				"want 0)", txin.PreviousOutPoint.Index)
			return false, stakeRuleError(ErrSSGenWrongIndex, errStr)
		}
		if txin.PreviousOutPoint.Tree != wire.TxTreeStake {
			return false, stakeRuleError(ErrSSGenWrongTxTree, "SSGen used "+
				"a non-stake input")
		}
	}
	for _, txOut := range tx.TxOut {
		if txOut.Version != txscript.DefaultScriptVersion {
			return false, stakeRuleError(ErrSSGenBadGenOuts, "invalid "+
				"script version found in txOut")
		}
	}
	zeroethOutputVersion := tx.TxOut[0].Version
	zeroethOutputScript := tx.TxOut[0].PkScript
	if txscript.GetScriptClass(zeroethOutputVersion, zeroethOutputScript) !=
		txscript.NullDataTy {
		return false, stakeRuleError(ErrSSGenNoReference, "First SSGen output "+
			"should have been an OP_RETURN data push, but was not")
	}
	if len(zeroethOutputScript) != SSGenBlockReferenceOutSize {
		return false, stakeRuleError(ErrSSGenBadReference, "First SSGen output "+
			"should have been 47 bytes long, but was not")
	}
	zeroethOutputScriptBuffer := bytes.NewBuffer(zeroethOutputScript)
	zeroethOutputScriptPrefix := zeroethOutputScriptBuffer.Next(2)
	if !bytes.Equal(zeroethOutputScriptPrefix,
		validSSGenReferenceOutPrefix) {
		return false, stakeRuleError(ErrSSGenBadReference, "First SSGen output "+
			"had an invalid prefix")
	}
	firstOutputVersion := tx.TxOut[1].Version
	firstOutputScript := tx.TxOut[1].PkScript
	if txscript.GetScriptClass(firstOutputVersion, firstOutputScript) !=
		txscript.NullDataTy {
		return false, stakeRuleError(ErrSSGenNoVotePush, "Second SSGen output "+
			"should have been an OP_RETURN data push, but was not")
	}
	if len(firstOutputScript) < SSGenVoteBitsOutputMinSize ||
		len(firstOutputScript) > SSGenVoteBitsOutputMaxSize {
		str := fmt.Sprintf("SSGen votebits output at output index 1 was a " +
			"NullData (OP_RETURN) push of the wrong size")
		return false, stakeRuleError(ErrSSGenBadVotePush, str)
	}
	firstOutputScriptBuffer := bytes.NewBuffer(firstOutputScript)
	firstOutputScriptPrefix := firstOutputScriptBuffer.Next(2)
	minPush := validSSGenVoteOutMinPrefix[1]
	maxPush := validSSGenVoteOutMinPrefix[1] +
		(MaxSingleBytePushLength - minPush)
	pushLen := firstOutputScriptPrefix[1]
	pushLengthValid := (pushLen >= minPush) && (pushLen <= maxPush)
	if !(firstOutputScriptPrefix[0] == validSSGenVoteOutMinPrefix[0]) ||
		!pushLengthValid {
		return false, stakeRuleError(ErrSSGenBadVotePush, "Second SSGen output "+
			"had an invalid prefix")
	}
	for outTxIndex := 2; outTxIndex < len(tx.TxOut); outTxIndex++ {
		scrVersion := tx.TxOut[outTxIndex].Version
		rawScript := tx.TxOut[outTxIndex].PkScript
		if txscript.GetScriptClass(scrVersion, rawScript) !=
			txscript.StakeGenTy {
			str := fmt.Sprintf("SSGen tx output at output index %d was not "+
				"an OP_SSGEN tagged output", outTxIndex)
			return false, stakeRuleError(ErrSSGenBadGenOuts, str)
		}
	}
	return true, nil
}
IsSSGen returns whether or not a transaction is an SSGen tx.  It does some
simple validation steps to make sure the number of inputs, number of
outputs, and the input/output scripts are valid.

This does NOT check to see if the subsidy is valid or whether or not the
value of input[0] + subsidy = value of the outputs.

SSGen transactions are specified as below.
Inputs:
Stakebase null input [index 0]
SStx-tagged output [index 1]

Outputs:
OP_RETURN push of 40 bytes containing: [index 0]
i. 32-byte block header of block being voted on.
ii. 8-byte int of this block's height.
OP_RETURN push of 2 bytes containing votebits [index 1]
SSGen-tagged output to address from SStx-tagged output's tx index output 1
[index 2]
SSGen-tagged output to address from SStx-tagged output's tx index output 2
[index 3]
...
SSGen-tagged output to address from SStx-tagged output's tx index output
MaxInputsPerSStx [index MaxOutputsPerSSgen - 1]

The errors in this function can be ignored if you want to use it in to
identify SSGen from a list of stake tx.
package stake
import (
	"bytes"
	"encoding/binary"
	"errors"
	"fmt"
	"math"
	"math/big"
	"github.com/HcashOrg/hcashd/chaincfg"
	"github.com/HcashOrg/hcashd/chaincfg/chainec"
	"github.com/HcashOrg/hcashd/chaincfg/chainhash"
	"github.com/HcashOrg/hcashd/txscript"
	"github.com/HcashOrg/hcashd/wire"
	"github.com/HcashOrg/hcashutil"
)
type TxType int
const (
	TxTypeRegular TxType = iota
	TxTypeSStx
	TxTypeSSGen
	TxTypeSSRtx
)
const (
	MaxInputsPerSStx = 64
	MaxOutputsPerSStx = MaxInputsPerSStx*2 + 1
	NumInputsPerSSGen = 2  
	MaxOutputsPerSSGen = MaxInputsPerSStx + 2
	NumInputsPerSSRtx = 1
	MaxOutputsPerSSRtx = MaxInputsPerSStx
	SStxPKHMinOutSize = 32
	SStxPKHMaxOutSize = 77
	SSGenBlockReferenceOutSize = 38 + 4
	SSGenVoteBitsOutputMinSize = 4
	SSGenVoteBitsOutputMaxSize = 77
	MaxSingleBytePushLength = 75
	SSGenVoteBitsExtendedMaxSize = MaxSingleBytePushLength - 2
	SStxVoteReturnFractionMask = 0x003f
	SStxRevReturnFractionMask = 0x3f00
	SStxVoteFractionFlag = 0x0040
	SStxRevFractionFlag = 0x4000
	VoteConsensusVersionAbsent = 0
)
var (
	validSStxAddressOutMinPrefix = []byte{0x6a, 0x1e}
	validSSGenReferenceOutPrefix = []byte{0x6a, 0x28}
	validSSGenVoteOutMinPrefix = []byte{0x6a, 0x02}
	zeroHash = &chainhash.Hash{}
	rangeLimitMax = uint16(63)
)
type VoteBits struct {
	Bits         uint16
	ExtendedBits []byte
}
func isNullOutpoint(tx *wire.MsgTx) bool {
	nullInOP := tx.TxIn[0].PreviousOutPoint
	if nullInOP.Index == math.MaxUint32 && nullInOP.Hash.IsEqual(zeroHash) &&
		nullInOP.Tree == wire.TxTreeRegular {
		return true
	}
	return false
}
func isNullFraudProof(tx *wire.MsgTx) bool {
	txIn := tx.TxIn[0]
	switch {
	case txIn.BlockHeight != wire.NullBlockHeight:
		return false
	case txIn.BlockIndex != wire.NullBlockIndex:
		return false
	}
	return true
}
func IsStakeBase(tx *wire.MsgTx) bool {
	if len(tx.TxIn) != 2 {
		return false
	}
	if !isNullOutpoint(tx) {
		return false
	}
	if !isNullFraudProof(tx) {
		return false
	}
	return true
}
type MinimalOutput struct {
	PkScript []byte
	Value    int64
	Version  uint16
}
func ConvertToMinimalOutputs(tx *wire.MsgTx) []*MinimalOutput {
	minOuts := make([]*MinimalOutput, len(tx.TxOut))
	for i, txOut := range tx.TxOut {
		minOuts[i] = &MinimalOutput{
			PkScript: txOut.PkScript,
			Value:    txOut.Value,
			Version:  txOut.Version,
		}
	}
	return minOuts
}
func SStxStakeOutputInfo(outs []*MinimalOutput) ([]bool, [][]byte, []int64,
	[]int64, [][]bool, [][]uint16) {
	expectedInLen := len(outs) / 2
	isP2SH := make([]bool, expectedInLen)
	addresses := make([][]byte, expectedInLen)
	amounts := make([]int64, expectedInLen)
	changeAmounts := make([]int64, expectedInLen)
	allSpendRules := make([][]bool, expectedInLen)
	allSpendLimits := make([][]uint16, expectedInLen)
	for idx, out := range outs {
		if (idx > 0) && (idx%2 != 0) {
			amtEncoded := make([]byte, 8, 8)
			copy(amtEncoded, out.PkScript[22:30])
			isP2SH[idx/2] = !(amtEncoded[7]&(1<<7) == 0)  
			amtEncoded[7] &= ^uint8(1 << 7)               
			addresses[idx/2] = out.PkScript[2:22]
			amounts[idx/2] = int64(binary.LittleEndian.Uint64(amtEncoded))
			spendRules := make([]bool, 2, 2)
			spendLimits := make([]uint16, 2, 2)
			feeLimitUint16 := binary.LittleEndian.Uint16(out.PkScript[30:32])
			spendRules[0] = (feeLimitUint16 & SStxVoteFractionFlag) ==
				SStxVoteFractionFlag
			spendRules[1] = (feeLimitUint16 & SStxRevFractionFlag) ==
				SStxRevFractionFlag
			allSpendRules[idx/2] = spendRules
			spendLimits[0] = feeLimitUint16 & SStxVoteReturnFractionMask
			spendLimits[1] = feeLimitUint16 & SStxRevReturnFractionMask
			spendLimits[1] >>= 8
			allSpendLimits[idx/2] = spendLimits
		}
		if (idx > 0) && (idx%2 == 0) {
			changeAmounts[(idx/2)-1] = out.Value
		}
	}
	return isP2SH, addresses, amounts, changeAmounts, allSpendRules,
		allSpendLimits
}
func TxSStxStakeOutputInfo(tx *wire.MsgTx) ([]bool, [][]byte, []int64, []int64,
	[][]bool, [][]uint16) {
	return SStxStakeOutputInfo(ConvertToMinimalOutputs(tx))
}
func AddrFromSStxPkScrCommitment(pkScript []byte,
	params *chaincfg.Params) (hcashutil.Address, error) {
	if len(pkScript) < SStxPKHMinOutSize {
		return nil, stakeRuleError(ErrSStxBadCommitAmount, "short read "+
			"of sstx commit pkscript")
	}
	amtEncoded := make([]byte, 8, 8)
	copy(amtEncoded, pkScript[22:30])
	isP2SH := !(amtEncoded[7]&(1<<7) == 0)  
	hashBytes := pkScript[2:22]
	var err error
	var addr hcashutil.Address
	if isP2SH {
		addr, err = hcashutil.NewAddressScriptHashFromHash(hashBytes, params)
	} else {
		addr, err = hcashutil.NewAddressPubKeyHash(hashBytes, params,
			chainec.ECTypeSecp256k1)
	}
	return addr, err
}
func AmountFromSStxPkScrCommitment(pkScript []byte) (hcashutil.Amount, error) {
	if len(pkScript) < SStxPKHMinOutSize {
		return 0, stakeRuleError(ErrSStxBadCommitAmount, "short read "+
			"of sstx commit pkscript")
	}
	amtEncoded := make([]byte, 8, 8)
	copy(amtEncoded, pkScript[22:30])
	amtEncoded[7] &= ^uint8(1 << 7)  
	return hcashutil.Amount(binary.LittleEndian.Uint64(amtEncoded)), nil
}
func TxSSGenStakeOutputInfo(tx *wire.MsgTx, params *chaincfg.Params) ([]bool,
	[][]byte, []int64, error) {
	numOutputsInSSGen := len(tx.TxOut)
	isP2SH := make([]bool, numOutputsInSSGen-2)
	addresses := make([][]byte, numOutputsInSSGen-2)
	amounts := make([]int64, numOutputsInSSGen-2)
	for idx, out := range tx.TxOut {
		if (idx > 1) && (idx < numOutputsInSSGen) {
			class, addr, _, err :=
				txscript.ExtractPkScriptAddrs(out.Version, out.PkScript, params)
			if err != nil {
				return nil, nil, nil, err
			}
			if class != txscript.StakeGenTy {
				return nil, nil, nil, fmt.Errorf("ssgen output included non "+
					"ssgen tagged output in idx %v", idx)
			}
			subClass, err := txscript.GetStakeOutSubclass(out.PkScript)
			if err != nil {
				return nil, nil, nil, err
			}
			if !(subClass == txscript.PubKeyHashTy ||
				subClass == txscript.ScriptHashTy) {
				return nil, nil, nil, fmt.Errorf("bad script type")
			}
			isP2SH[idx-2] = false
			if subClass == txscript.ScriptHashTy {
				isP2SH[idx-2] = true
			}
			amt := out.Value
			addresses[idx-2] = addr[0].ScriptAddress()
			amounts[idx-2] = amt
		}
	}
	return isP2SH, addresses, amounts, nil
}
func SSGenBlockVotedOn(tx *wire.MsgTx) (chainhash.Hash, uint32, uint32, error) {
	blockHash, err := chainhash.NewHash(tx.TxOut[0].PkScript[2:34])
	if err != nil {
		return chainhash.Hash{}, 0, 0, err
	}
	height := binary.LittleEndian.Uint32(tx.TxOut[0].PkScript[34:38])
	keyHeight := binary.LittleEndian.Uint32(tx.TxOut[0].PkScript[38:42])
	return *blockHash, height, keyHeight, nil
}
func SSGenVoteBits(tx *wire.MsgTx) uint16 {
	return binary.LittleEndian.Uint16(tx.TxOut[1].PkScript[2:4])
}
func SSGenVersion(tx *wire.MsgTx) uint32 {
	if len(tx.TxOut[1].PkScript) < 8 {
		return VoteConsensusVersionAbsent
	}
	return binary.LittleEndian.Uint32(tx.TxOut[1].PkScript[4:8])
}
func TxSSRtxStakeOutputInfo(tx *wire.MsgTx, params *chaincfg.Params) ([]bool,
	[][]byte, []int64, error) {
	numOutputsInSSRtx := len(tx.TxOut)
	isP2SH := make([]bool, numOutputsInSSRtx)
	addresses := make([][]byte, numOutputsInSSRtx)
	amounts := make([]int64, numOutputsInSSRtx)
	for idx, out := range tx.TxOut {
		class, addr, _, err :=
			txscript.ExtractPkScriptAddrs(out.Version, out.PkScript, params)
		if err != nil {
			return nil, nil, nil, err
		}
		if class != txscript.StakeRevocationTy {
			return nil, nil, nil, fmt.Errorf("ssrtx output included non "+
				"ssrtx tagged output in idx %v", idx)
		}
		subClass, err := txscript.GetStakeOutSubclass(out.PkScript)
		if err != nil {
			return nil, nil, nil, err
		}
		if !(subClass == txscript.PubKeyHashTy ||
			subClass == txscript.ScriptHashTy) {
			return nil, nil, nil, fmt.Errorf("bad script type")
		}
		isP2SH[idx] = false
		if subClass == txscript.ScriptHashTy {
			isP2SH[idx] = true
		}
		amt := out.Value
		addresses[idx] = addr[0].ScriptAddress()
		amounts[idx] = amt
	}
	return isP2SH, addresses, amounts, nil
}
func SStxNullOutputAmounts(amounts []int64,
	changeAmounts []int64,
	amountTicket int64) (int64, []int64, error) {
	lengthAmounts := len(amounts)
	if lengthAmounts != len(changeAmounts) {
		errStr := fmt.Sprintf("amounts was not equal in length " +
			"to change amounts!")
		return 0, nil, errors.New(errStr)
	}
	if amountTicket <= 0 {
		errStr := fmt.Sprintf("committed amount was too small!")
		return 0, nil, stakeRuleError(ErrSStxBadCommitAmount, errStr)
	}
	contribAmounts := make([]int64, lengthAmounts)
	sum := int64(0)
	for i := 0; i < lengthAmounts; i++ {
		contribAmounts[i] = amounts[i] - changeAmounts[i]
		if contribAmounts[i] < 0 {
			errStr := fmt.Sprintf("change at idx %v spent more coins than "+
				"allowed (have: %v, spent: %v)", i, amounts[i], changeAmounts[i])
			return 0, nil, stakeRuleError(ErrSStxBadChangeAmts, errStr)
		}
		sum += contribAmounts[i]
	}
	fees := sum - amountTicket
	return fees, contribAmounts, nil
}
func CalculateRewards(amounts []int64, amountTicket int64,
	subsidy int64) []int64 {
	outputsAmounts := make([]int64, len(amounts))
	amountWithStakebase := amountTicket + subsidy
	totalContrib := int64(0)
	for _, amount := range amounts {
		totalContrib += amount
	}
	amountWithStakebaseBig := big.NewInt(amountWithStakebase)
	totalContribBig := big.NewInt(totalContrib)
	for idx, amount := range amounts {
		amountBig := big.NewInt(amount)  
		amountBig.Mul(amountBig, amountWithStakebaseBig)
		amountBig.Lsh(amountBig, 32)
		amountBig.Div(amountBig, totalContribBig)
		amountBig.Rsh(amountBig, 32)
		amountFinal := int64(amountBig.Uint64())
		outputsAmounts[idx] = amountFinal
	}
	return outputsAmounts
}
func VerifySStxAmounts(sstxAmts []int64, sstxCalcAmts []int64) error {
	if len(sstxCalcAmts) != len(sstxAmts) {
		errStr := fmt.Sprintf("SStx verify error: number of calculated " +
			"sstx output values was not equivalent to the number of sstx " +
			"commitment outputs")
		return stakeRuleError(ErrVerSStxAmts, errStr)
	}
	for idx, amt := range sstxCalcAmts {
		if !(amt == sstxAmts[idx]) {
			errStr := fmt.Sprintf("SStx verify error: at index %v incongruent "+
				"amt %v in SStx calculated reward and amt %v in "+
				"SStx", idx, amt, sstxAmts[idx])
			return stakeRuleError(ErrVerSStxAmts, errStr)
		}
	}
	return nil
}
func VerifyStakingPkhsAndAmounts(
	sstxTypes []bool,
	sstxPkhs [][]byte,
	ssSpendAmts []int64,
	ssSpendTypes []bool,
	ssSpendPkhs [][]byte,
	ssSpendCalcAmts []int64,
	isVote bool,
	spendRules [][]bool,
	spendLimits [][]uint16) error {
	if len(sstxTypes) != len(ssSpendTypes) {
		errStr := fmt.Sprintf("Staking verify error: number of " +
			"sstx type values was not equivalent to the number " +
			"of ss*** type values")
		return stakeRuleError(ErrVerifyInput, errStr)
	}
	if len(ssSpendAmts) != len(ssSpendCalcAmts) {
		errStr := fmt.Sprintf("Staking verify error: number of " +
			"sstx output values was not equivalent to the number " +
			"of ss*** output values")
		return stakeRuleError(ErrVerifyInput, errStr)
	}
	if len(sstxPkhs) != len(ssSpendPkhs) {
		errStr := fmt.Sprintf("Staking verify error: number of " +
			"sstx output pks was not equivalent to the number " +
			"of ss*** output pkhs")
		return stakeRuleError(ErrVerifyInput, errStr)
	}
	for idx, typ := range sstxTypes {
		if typ != ssSpendTypes[idx] {
			errStr := fmt.Sprintf("SStx in/SS*** out verify error: at index %v "+
				"non-equivalent type %v in SStx and type %v in SS***", idx, typ,
				ssSpendTypes[idx])
			return stakeRuleError(ErrVerifyOutType, errStr)
		}
	}
	for idx, amt := range ssSpendAmts {
		rule := false
		limit := uint16(0)
		if isVote {
			rule = spendRules[idx][0]
			limit = spendLimits[idx][0]
		} else {
			rule = spendRules[idx][1]
			limit = spendLimits[idx][1]
		}
		if rule {
			feeAllowance := ssSpendCalcAmts[idx]
			if limit < rangeLimitMax {
				if int64(1<<uint64(limit)) < ssSpendCalcAmts[idx] {
					feeAllowance = int64(1 << uint64(limit))
				}
			}
			amtLimitLow := ssSpendCalcAmts[idx] - feeAllowance
			amtLimitHigh := ssSpendCalcAmts[idx]
			if amt < amtLimitLow {
				errStr := fmt.Sprintf("SStx in/SS*** out verify error: "+
					"at index %v amt min limit was calculated to be %v yet "+
					"the actual amount output was %v", idx, amtLimitLow,
					amt)
				return stakeRuleError(ErrVerifyTooMuchFees, errStr)
			}
			if amt > amtLimitHigh {
				errStr := fmt.Sprintf("at index %v amt max limit was "+
					"calculated to be %v yet the actual amount output "+
					"was %v", idx, amtLimitHigh, amt)
				return stakeRuleError(ErrVerifySpendTooMuch, errStr)
			}
		} else {
			if amt != ssSpendCalcAmts[idx] {
				errStr := fmt.Sprintf("SStx in/SS*** out verify error: "+
					"at index %v non-equivalent amt %v in SStx and amt "+
					"%v in SS***", idx, amt, ssSpendCalcAmts[idx])
				return stakeRuleError(ErrVerifyOutputAmt, errStr)
			}
		}
	}
	for idx, pkh := range sstxPkhs {
		if !bytes.Equal(pkh, ssSpendPkhs[idx]) {
			errStr := fmt.Sprintf("SStx in/SS*** out verify error: at index %v "+
				"non-equivalent pkh %x in SStx and pkh %x in SS***", idx, pkh,
				ssSpendPkhs[idx])
			return stakeRuleError(ErrVerifyOutPkhs, errStr)
		}
	}
	return nil
}
func IsSStx(tx *wire.MsgTx) (bool, error) {
	if len(tx.TxIn) > MaxInputsPerSStx {
		return false, stakeRuleError(ErrSStxTooManyInputs, "SStx has too many "+
			"inputs")
	}
	if len(tx.TxOut) > MaxOutputsPerSStx {
		return false, stakeRuleError(ErrSStxTooManyOutputs, "SStx has too many "+
			"outputs")
	}
	if len(tx.TxOut) == 0 {
		return false, stakeRuleError(ErrSStxNoOutputs, "SStx has no "+
			"outputs")
	}
	for idx, txOut := range tx.TxOut {
		if txOut.Version != txscript.DefaultScriptVersion {
			errStr := fmt.Sprintf("invalid script version found in "+
				"txOut idx %v", idx)
			return false, stakeRuleError(ErrSStxInvalidOutputs, errStr)
		}
	}
	if txscript.GetScriptClass(tx.TxOut[0].Version, tx.TxOut[0].PkScript) !=
		txscript.StakeSubmissionTy {
		return false, stakeRuleError(ErrSStxInvalidOutputs, "First SStx output "+
			"should have been OP_SSTX tagged, but it was not")
	}
	if (len(tx.TxIn)*2 + 1) != len(tx.TxOut) {
		return false, stakeRuleError(ErrSStxInOutProportions, "The number of "+
			"inputs in the SStx tx was not the number of outputs/2 - 1")
	}
	for outTxIndex := 1; outTxIndex < len(tx.TxOut); outTxIndex++ {
		scrVersion := tx.TxOut[outTxIndex].Version
		rawScript := tx.TxOut[outTxIndex].PkScript
		if outTxIndex%2 == 0 {
			if txscript.GetScriptClass(scrVersion, rawScript) !=
				txscript.StakeSubChangeTy {
				str := fmt.Sprintf("SStx output at output index %d was not "+
					"an sstx change output", outTxIndex)
				return false, stakeRuleError(ErrSStxInvalidOutputs, str)
			}
			continue
		}
		if txscript.GetScriptClass(scrVersion, rawScript) !=
			txscript.NullDataTy {
			str := fmt.Sprintf("SStx output at output index %d was not "+
				"a NullData (OP_RETURN) push", outTxIndex)
			return false, stakeRuleError(ErrSStxInvalidOutputs, str)
		}
		if len(rawScript) < SStxPKHMinOutSize ||
			len(rawScript) > SStxPKHMaxOutSize {
			str := fmt.Sprintf("SStx output at output index %d was a "+
				"NullData (OP_RETURN) push of the wrong size", outTxIndex)
			return false, stakeRuleError(ErrSStxInvalidOutputs, str)
		}
		outputScriptBuffer := bytes.NewBuffer(rawScript)
		outputScriptPrefix := outputScriptBuffer.Next(2)
		minPush := validSStxAddressOutMinPrefix[1]
		maxPush := validSStxAddressOutMinPrefix[1] +
			(MaxSingleBytePushLength - minPush)
		pushLen := outputScriptPrefix[1]
		pushLengthValid := (pushLen >= minPush) && (pushLen <= maxPush)
		if !(outputScriptPrefix[0] == validSStxAddressOutMinPrefix[0]) ||
			!pushLengthValid {
			errStr := fmt.Sprintf("sstx commitment at output idx %v had "+
				"an invalid prefix", outTxIndex)
			return false, stakeRuleError(ErrSStxInvalidOutputs,
				errStr)
		}
	}
	return true, nil
}