Example #1
0
// checkInputsStandard performs a series of checks on a transaction's inputs
// to ensure they are "standard".  A standard transaction input is one that
// that consumes the expected number of elements from the stack and that number
// is the same as the output script pushes.  This help prevent resource
// exhaustion attacks by "creative" use of scripts that are super expensive to
// process like OP_DUP OP_CHECKSIG OP_DROP repeated a large number of times
// followed by a final OP_TRUE.
func checkInputsStandard(tx *btcutil.Tx, utxoView *blockchain.UtxoViewpoint) error {
	// NOTE: The reference implementation also does a coinbase check here,
	// but coinbases have already been rejected prior to calling this
	// function so no need to recheck.

	for i, txIn := range tx.MsgTx().TxIn {
		// It is safe to elide existence and index checks here since
		// they have already been checked prior to calling this
		// function.
		prevOut := txIn.PreviousOutPoint
		entry := utxoView.LookupEntry(&prevOut.Hash)
		originPkScript := entry.PkScriptByIndex(prevOut.Index)

		// Calculate stats for the script pair.
		scriptInfo, err := txscript.CalcScriptInfo(txIn.SignatureScript,
			originPkScript, true)
		if err != nil {
			str := fmt.Sprintf("transaction input #%d script parse "+
				"failure: %v", i, err)
			return txRuleError(wire.RejectNonstandard, str)
		}

		// A negative value for expected inputs indicates the script is
		// non-standard in some way.
		if scriptInfo.ExpectedInputs < 0 {
			str := fmt.Sprintf("transaction input #%d expects %d "+
				"inputs", i, scriptInfo.ExpectedInputs)
			return txRuleError(wire.RejectNonstandard, str)
		}

		// The script pair is non-standard if the number of available
		// inputs does not match the number of expected inputs.
		if scriptInfo.NumInputs != scriptInfo.ExpectedInputs {
			str := fmt.Sprintf("transaction input #%d expects %d "+
				"inputs, but referenced output script provides "+
				"%d", i, scriptInfo.ExpectedInputs,
				scriptInfo.NumInputs)
			return txRuleError(wire.RejectNonstandard, str)
		}
	}

	return nil
}
Example #2
0
// TestCalcScriptInfo ensures the CalcScriptInfo provides the expected results
// for various valid and invalid script pairs.
func TestCalcScriptInfo(t *testing.T) {
	t.Parallel()

	tests := []struct {
		name          string
		sigScript     string
		pkScript      string
		bip16         bool
		scriptInfo    txscript.ScriptInfo
		scriptInfoErr error
	}{
		{
			// Invented scripts, the hashes do not match
			// Truncated version of test below:
			name: "pkscript doesn't parse",
			sigScript: "1 81 DATA_8 2DUP EQUAL NOT VERIFY ABS " +
				"SWAP ABS EQUAL",
			pkScript: "HASH160 DATA_20 0xfe441065b6532231de2fac56" +
				"3152205ec4f59c",
			bip16:         true,
			scriptInfoErr: txscript.ErrStackShortScript,
		},
		{
			name: "sigScript doesn't parse",
			// Truncated version of p2sh script below.
			sigScript: "1 81 DATA_8 2DUP EQUAL NOT VERIFY ABS " +
				"SWAP ABS",
			pkScript: "HASH160 DATA_20 0xfe441065b6532231de2fac56" +
				"3152205ec4f59c74 EQUAL",
			bip16:         true,
			scriptInfoErr: txscript.ErrStackShortScript,
		},
		{
			// Invented scripts, the hashes do not match
			name: "p2sh standard script",
			sigScript: "1 81 DATA_25 DUP HASH160 DATA_20 0x010203" +
				"0405060708090a0b0c0d0e0f1011121314 EQUALVERIFY " +
				"CHECKSIG",
			pkScript: "HASH160 DATA_20 0xfe441065b6532231de2fac56" +
				"3152205ec4f59c74 EQUAL",
			bip16: true,
			scriptInfo: txscript.ScriptInfo{
				PkScriptClass:  txscript.ScriptHashTy,
				NumInputs:      3,
				ExpectedInputs: 3, // nonstandard p2sh.
				SigOps:         1,
			},
		},
		{
			// from 567a53d1ce19ce3d07711885168484439965501536d0d0294c5d46d46c10e53b
			// from the blockchain.
			name: "p2sh nonstandard script",
			sigScript: "1 81 DATA_8 2DUP EQUAL NOT VERIFY ABS " +
				"SWAP ABS EQUAL",
			pkScript: "HASH160 DATA_20 0xfe441065b6532231de2fac56" +
				"3152205ec4f59c74 EQUAL",
			bip16: true,
			scriptInfo: txscript.ScriptInfo{
				PkScriptClass:  txscript.ScriptHashTy,
				NumInputs:      3,
				ExpectedInputs: -1, // nonstandard p2sh.
				SigOps:         0,
			},
		},
		{
			// Script is invented, numbers all fake.
			name: "multisig script",
			// Extra 0 arg on the end for OP_CHECKMULTISIG bug.
			sigScript: "1 1 1 0",
			pkScript: "3 " +
				"DATA_33 0x0102030405060708090a0b0c0d0e0f1011" +
				"12131415161718191a1b1c1d1e1f2021 DATA_33 " +
				"0x0102030405060708090a0b0c0d0e0f101112131415" +
				"161718191a1b1c1d1e1f2021 DATA_33 0x010203040" +
				"5060708090a0b0c0d0e0f101112131415161718191a1" +
				"b1c1d1e1f2021 3 CHECKMULTISIG",
			bip16: true,
			scriptInfo: txscript.ScriptInfo{
				PkScriptClass:  txscript.MultiSigTy,
				NumInputs:      4,
				ExpectedInputs: 4,
				SigOps:         3,
			},
		},
	}

	for _, test := range tests {
		sigScript := mustParseShortForm(test.sigScript)
		pkScript := mustParseShortForm(test.pkScript)
		si, err := txscript.CalcScriptInfo(sigScript, pkScript,
			test.bip16)
		if err != nil {
			if err != test.scriptInfoErr {
				t.Errorf("scriptinfo test \"%s\": got \"%v\""+
					"expected \"%v\"", test.name, err,
					test.scriptInfoErr)
			}
			continue
		}
		if test.scriptInfoErr != nil {
			t.Errorf("%s: succeeded when expecting \"%v\"",
				test.name, test.scriptInfoErr)
			continue
		}
		if *si != test.scriptInfo {
			t.Errorf("%s: scriptinfo doesn't match expected. "+
				"got: \"%v\" expected \"%v\"", test.name,
				*si, test.scriptInfo)
			continue
		}
	}
}