From 39f0d9686095bce469dbfa52333331a5d15c6545 Mon Sep 17 00:00:00 2001 From: Gavin Andresen Date: Thu, 19 Jan 2012 13:30:54 -0500 Subject: [PATCH] Make transactions with extra data in their scriptSig's non-standard. --- src/main.cpp | 25 ++++++++++++++++++++----- src/script.cpp | 19 +++++++++++++++++++ src/script.h | 1 + src/test/script_P2SH_tests.cpp | 9 +++++++++ src/test/transaction_tests.cpp | 24 ++++++++++++++++++------ 5 files changed, 67 insertions(+), 11 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 3c3f06604..db7977dfd 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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 > stack; + if (!EvalScript(stack, vin[i].scriptSig, *this, i, 0)) + return false; + if (whichType == TX_SCRIPTHASH) { - vector > 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 > 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; diff --git a/src/script.cpp b/src/script.cpp index 66962d78b..c0523855b 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -1312,6 +1312,25 @@ bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash return false; } +int ScriptSigArgsExpected(txnouttype t, const std::vector >& 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) { diff --git a/src/script.h b/src/script.h index 63f9f9353..4760b99a9 100644 --- a/src/script.h +++ b/src/script.h @@ -560,6 +560,7 @@ public: bool EvalScript(std::vector >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, int nHashType); bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector >& vSolutionsRet); +int ScriptSigArgsExpected(txnouttype t, const std::vector >& vSolutions); bool IsStandard(const CScript& scriptPubKey); bool IsMine(const CKeyStore& keystore, const CScript& scriptPubKey); bool ExtractAddress(const CScript& scriptPubKey, CBitcoinAddress& addressRet); diff --git a/src/test/script_P2SH_tests.cpp b/src/test/script_P2SH_tests.cpp index aed3e2331..540019c73 100644 --- a/src/test/script_P2SH_tests.cpp +++ b/src/test/script_P2SH_tests.cpp @@ -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()); diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 33765ca96..04a3d29dc 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -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 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(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(65, 0) << std::vector(33, 4); + t1.vin[2].prevout.hash = dummyTransactions[1].GetHash(); t1.vin[2].prevout.n = 1; + t1.vin[2].scriptSig << std::vector(65, 0) << std::vector(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)