Make transactions with extra data in their scriptSig's non-standard.
authorGavin Andresen <gavinandresen@gmail.com>
Thu, 19 Jan 2012 18:30:54 +0000 (13:30 -0500)
committerGavin Andresen <gavinandresen@gmail.com>
Thu, 19 Jan 2012 18:30:54 +0000 (13:30 -0500)
src/main.cpp
src/script.cpp
src/script.h
src/test/script_P2SH_tests.cpp
src/test/transaction_tests.cpp

index 3c3f066..db7977d 100644 (file)
@@ -293,18 +293,33 @@ bool CTransaction::AreInputsStandard(const MapPrevTx& mapInputs) const
         const CScript& prevScript = prev.scriptPubKey;
         if (!Solver(prevScript, whichType, vSolutions))
             return false;
+        int nArgsExpected = ScriptSigArgsExpected(whichType, vSolutions);
+
+        // Transactions with extra stuff in their scriptSigs are
+        // non-standard. Note that this EvalScript() call will
+        // be quick, because if there are any operations
+        // beside "push data" in the scriptSig the
+        // IsStandard() call returns false
+        vector<vector<unsigned char> > stack;
+        if (!EvalScript(stack, vin[i].scriptSig, *this, i, 0))
+            return false;
+
         if (whichType == TX_SCRIPTHASH)
         {
-            vector<vector<unsigned char> > stack;
-
-            if (!EvalScript(stack, vin[i].scriptSig, *this, i, 0))
-                return false;
             if (stack.empty())
                 return false;
             CScript subscript(stack.back().begin(), stack.back().end());
-            if (!::IsStandard(subscript))
+            vector<vector<unsigned char> > vSolutions2;
+            txnouttype whichType2;
+            if (!Solver(subscript, whichType2, vSolutions2))
                 return false;
+            if (whichType2 == TX_SCRIPTHASH)
+                return false;
+            nArgsExpected += ScriptSigArgsExpected(whichType2, vSolutions2);
         }
+
+        if (stack.size() != nArgsExpected)
+            return false;
     }
 
     return true;
index 66962d7..c052385 100644 (file)
@@ -1312,6 +1312,25 @@ bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash
     return false;
 }
 
+int ScriptSigArgsExpected(txnouttype t, const std::vector<std::vector<unsigned char> >& vSolutions)
+{
+    switch (t)
+    {
+    case TX_NONSTANDARD:
+        return -1;
+    case TX_PUBKEY:
+        return 1;
+    case TX_PUBKEYHASH:
+        return 2;
+    case TX_MULTISIG:
+        if (vSolutions.size() < 1 || vSolutions[0].size() < 1)
+            return -1;
+        return vSolutions[0][0] + 1;
+    case TX_SCRIPTHASH:
+        return 1; // doesn't include args needed by the script
+    }
+    return -1;
+}
 
 bool IsStandard(const CScript& scriptPubKey)
 {
index 63f9f93..4760b99 100644 (file)
@@ -560,6 +560,7 @@ public:
 
 bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType);
 bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::vector<unsigned char> >& vSolutionsRet);
+int ScriptSigArgsExpected(txnouttype t, const std::vector<std::vector<unsigned char> >& vSolutions);
 bool IsStandard(const CScript& scriptPubKey);
 bool IsMine(const CKeyStore& keystore, const CScript& scriptPubKey);
 bool ExtractAddress(const CScript& scriptPubKey, CBitcoinAddress& addressRet);
index aed3e23..540019c 100644 (file)
@@ -293,6 +293,15 @@ BOOST_AUTO_TEST_CASE(AreInputsStandard)
 
     BOOST_CHECK(txTo.AreInputsStandard(mapInputs));
 
+    // Make sure adding crap to the scriptSigs makes them non-standard:
+    for (int i = 0; i < 3; i++)
+    {
+        CScript t = txTo.vin[i].scriptSig;
+        txTo.vin[i].scriptSig = (CScript() << 11) + t;
+        BOOST_CHECK(!txTo.AreInputsStandard(mapInputs));
+        txTo.vin[i].scriptSig = t;
+    }
+
     CTransaction txToNonStd;
     txToNonStd.vout.resize(1);
     txToNonStd.vout[0].scriptPubKey.SetBitcoinAddress(key[1].GetPubKey());
index 33765ca..04a3d29 100644 (file)
@@ -24,8 +24,9 @@ BOOST_AUTO_TEST_CASE(basic_transaction_tests)
 
 //
 // Helper: create two dummy transactions, each with
-// two outputs.  The first has 11 and 50 CENT outputs,
-// the second 21 and 22 CENT outputs.
+// two outputs.  The first has 11 and 50 CENT outputs
+// paid to a TX_PUBKEY, the second 21 and 22 CENT outputs
+// paid to a TX_PUBKEYHASH.
 //
 static std::vector<CTransaction>
 SetupDummyInputs(CBasicKeyStore& keystoreRet, MapPrevTx& inputsRet)
@@ -44,9 +45,9 @@ SetupDummyInputs(CBasicKeyStore& keystoreRet, MapPrevTx& inputsRet)
     // Create some dummy input transactions
     dummyTransactions[0].vout.resize(2);
     dummyTransactions[0].vout[0].nValue = 11*CENT;
-    dummyTransactions[0].vout[0].scriptPubKey.SetBitcoinAddress(key[0].GetPubKey());
+    dummyTransactions[0].vout[0].scriptPubKey << key[0].GetPubKey() << OP_CHECKSIG;
     dummyTransactions[0].vout[1].nValue = 50*CENT;
-    dummyTransactions[0].vout[1].scriptPubKey.SetBitcoinAddress(key[1].GetPubKey());
+    dummyTransactions[0].vout[1].scriptPubKey << key[1].GetPubKey() << OP_CHECKSIG;
     inputsRet[dummyTransactions[0].GetHash()] = make_pair(CTxIndex(), dummyTransactions[0]);
 
     dummyTransactions[1].vout.resize(2);
@@ -69,10 +70,13 @@ BOOST_AUTO_TEST_CASE(test_Get)
     t1.vin.resize(3);
     t1.vin[0].prevout.hash = dummyTransactions[0].GetHash();
     t1.vin[0].prevout.n = 1;
-    t1.vin[1].prevout.hash = dummyTransactions[1].GetHash();;
+    t1.vin[0].scriptSig << std::vector<unsigned char>(65, 0);
+    t1.vin[1].prevout.hash = dummyTransactions[1].GetHash();
     t1.vin[1].prevout.n = 0;
-    t1.vin[2].prevout.hash = dummyTransactions[1].GetHash();;
+    t1.vin[1].scriptSig << std::vector<unsigned char>(65, 0) << std::vector<unsigned char>(33, 4);
+    t1.vin[2].prevout.hash = dummyTransactions[1].GetHash();
     t1.vin[2].prevout.n = 1;
+    t1.vin[2].scriptSig << std::vector<unsigned char>(65, 0) << std::vector<unsigned char>(33, 4);
     t1.vout.resize(2);
     t1.vout[0].nValue = 90*CENT;
     t1.vout[0].scriptPubKey << OP_1;
@@ -80,6 +84,14 @@ BOOST_AUTO_TEST_CASE(test_Get)
     BOOST_CHECK(t1.AreInputsStandard(dummyInputs));
     BOOST_CHECK_EQUAL(t1.GetSigOpCount(dummyInputs), 3);
     BOOST_CHECK_EQUAL(t1.GetValueIn(dummyInputs), (50+21+22)*CENT);
+
+    // Adding extra junk to the scriptSig should make it non-standard:
+    t1.vin[0].scriptSig << OP_11;
+    BOOST_CHECK(!t1.AreInputsStandard(dummyInputs));
+
+    // ... as should not having enough:
+    t1.vin[0].scriptSig = CScript();
+    BOOST_CHECK(!t1.AreInputsStandard(dummyInputs));
 }
 
 BOOST_AUTO_TEST_CASE(test_GetThrow)