Example #1
0
// checkTransactionStandard performs a series of checks on a transaction to
// ensure it is a "standard" transaction.  A standard transaction is one that
// conforms to several additional limiting cases over what is considered a
// "sane" transaction such as having a version in the supported range, being
// finalized, conforming to more stringent size constraints, having scripts
// of recognized forms, and not containing "dust" outputs (those that are
// so small it costs more to process them than they are worth).
func checkTransactionStandard(tx *btcutil.Tx, height int64) error {
	msgTx := tx.MsgTx()

	// The transaction must be a currently supported version.
	if msgTx.Version > btcwire.TxVersion || msgTx.Version < 1 {
		str := fmt.Sprintf("transaction version %d is not in the "+
			"valid range of %d-%d", msgTx.Version, 1,
			btcwire.TxVersion)
		return TxRuleError(str)
	}

	// The transaction must be finalized to be standard and therefore
	// considered for inclusion in a block.
	if !btcchain.IsFinalizedTransaction(tx, height, time.Now()) {
		return TxRuleError("transaction is not finalized")
	}

	// Since extremely large transactions with a lot of inputs can cost
	// almost as much to process as the sender fees, limit the maximum
	// size of a transaction.  This also helps mitigate CPU exhaustion
	// attacks.
	serializedLen := msgTx.SerializeSize()
	if serializedLen > maxStandardTxSize {
		str := fmt.Sprintf("transaction size of %v is larger than max "+
			"allowed size of %v", serializedLen, maxStandardTxSize)
		return TxRuleError(str)
	}

	for i, txIn := range msgTx.TxIn {
		// Each transaction input signature script must not exceed the
		// maximum size allowed for a standard transaction.  See
		// the comment on maxStandardSigScriptSize for more details.
		sigScriptLen := len(txIn.SignatureScript)
		if sigScriptLen > maxStandardSigScriptSize {
			str := fmt.Sprintf("transaction input %d: signature "+
				"script size of %d bytes is large than max "+
				"allowed size of %d bytes", i, sigScriptLen,
				maxStandardSigScriptSize)
			return TxRuleError(str)
		}

		// Each transaction input signature script must only contain
		// opcodes which push data onto the stack.
		if !btcscript.IsPushOnlyScript(txIn.SignatureScript) {
			str := fmt.Sprintf("transaction input %d: signature "+
				"script is not push only", i)
			return TxRuleError(str)
		}

		// Each transaction input signature script must only contain
		// canonical data pushes.  A canonical data push is one where
		// the minimum possible number of bytes is used to represent
		// the data push as possible.
		if !btcscript.HasCanonicalPushes(txIn.SignatureScript) {
			str := fmt.Sprintf("transaction input %d: signature "+
				"script has a non-canonical data push", i)
			return TxRuleError(str)
		}
	}

	// None of the output public key scripts can be a non-standard script or
	// be "dust".
	numNullDataOutputs := 0
	for i, txOut := range msgTx.TxOut {
		scriptClass := btcscript.GetScriptClass(txOut.PkScript)
		err := checkPkScriptStandard(txOut.PkScript, scriptClass)
		if err != nil {
			str := fmt.Sprintf("transaction output %d: %v", i, err)
			return TxRuleError(str)
		}

		// Accumulate the number of outputs which only carry data.
		if scriptClass == btcscript.NullDataTy {
			numNullDataOutputs++
		}

		if isDust(txOut) {
			str := fmt.Sprintf("transaction output %d: payment "+
				"of %d is dust", i, txOut.Value)
			return TxRuleError(str)
		}
	}

	// A standard transaction must not have more than one output script that
	// only carries data.
	if numNullDataOutputs > 1 {
		return TxRuleError("more than one transaction output is a " +
			"nulldata script")
	}

	return nil
}
Example #2
0
// checkTransactionStandard performs a series of checks on a transaction to
// ensure it is a "standard" transaction.  A standard transaction is one that
// conforms to several additional limiting cases over what is considered a
// "sane" transaction such as having a version in the supported range, being
// finalized, conforming to more stringent size constraints, having scripts
// of recognized forms, and not containing "dust" outputs (those that are
// so small it costs more to process them than they are worth).
func checkTransactionStandard(tx *btcwire.MsgTx, height int64) error {
	// The transaction must be a currently supported version.
	if tx.Version > btcwire.TxVersion || tx.Version < 1 {
		str := fmt.Sprintf("transaction version %d is not in the "+
			"valid range of %d-%d", tx.Version, 1,
			btcwire.TxVersion)
		return TxRuleError(str)
	}

	// The transaction must be finalized to be standard and therefore
	// considered for inclusion in a block.
	if !btcchain.IsFinalizedTransaction(tx, height, time.Now()) {
		str := fmt.Sprintf("transaction is not finalized")
		return TxRuleError(str)
	}

	// Since extremely large transactions with a lot of inputs can cost
	// almost as much to process as the sender fees, limit the maximum
	// size of a transaction.  This also helps mitigate CPU exhaustion
	// attacks.
	var serializedTxBuf bytes.Buffer
	err := tx.Serialize(&serializedTxBuf)
	if err != nil {
		return err
	}
	serializedLen := serializedTxBuf.Len()
	if serializedLen > maxStandardTxSize {
		str := fmt.Sprintf("transaction size of %v is larger than max "+
			"allowed size of %v", serializedLen, maxStandardTxSize)
		return TxRuleError(str)
	}

	for i, txIn := range tx.TxIn {
		// Each transaction input signature script must not exceed the
		// maximum size allowed for a standard transaction.  See
		// the comment on maxStandardSigScriptSize for more details.
		sigScriptLen := len(txIn.SignatureScript)
		if sigScriptLen > maxStandardSigScriptSize {
			str := fmt.Sprintf("transaction input %d: signature "+
				"script size of %d bytes is large than max "+
				"allowed size of %d bytes", i, sigScriptLen,
				maxStandardSigScriptSize)
			return TxRuleError(str)
		}

		// Each transaction input signature script must only contain
		// opcodes which push data onto the stack.
		if !btcscript.IsPushOnlyScript(txIn.SignatureScript) {
			str := fmt.Sprintf("transaction input %d: signature "+
				"script is not push only", i)
			return TxRuleError(str)
		}
	}

	// None of the output public key scripts can be a non-standard script or
	// be "dust".
	for i, txOut := range tx.TxOut {
		err := checkPkScriptStandard(txOut.PkScript)
		if err != nil {
			str := fmt.Sprintf("transaction output %d: %v", i, err)
			return TxRuleError(str)
		}

		if isDust(txOut) {
			str := fmt.Sprintf("transaction output %d: payment "+
				"of %d is dust", i, txOut.Value)
			return TxRuleError(str)
		}
	}

	return nil
}