// 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 }
// 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 } } }