diff --git a/src/main.cpp b/src/main.cpp index 0ae33275d..9180331fd 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -399,6 +399,10 @@ bool CTransaction::IsStandard(string& strReason) const strReason = "scriptsig-not-pushonly"; return false; } + if (!txin.scriptSig.HasCanonicalPushes()) { + strReason = "non-canonical-push"; + return false; + } } BOOST_FOREACH(const CTxOut& txout, vout) { if (!::IsStandard(txout.scriptPubKey)) { diff --git a/src/script.cpp b/src/script.cpp index b41166635..3daa26203 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -1740,6 +1740,33 @@ bool CScript::IsPayToScriptHash() const this->at(22) == OP_EQUAL); } +bool CScript::HasCanonicalPushes() const +{ + const_iterator pc = begin(); + while (pc < end()) + { + opcodetype opcode; + std::vector data; + if (!GetOp(pc, opcode, data)) + return false; + if (opcode > OP_16) + continue; + if (opcode < OP_PUSHDATA1 && opcode > OP_0 && (data.size() == 1 && data[0] <= 16)) + // Could have used an OP_n code, rather than a 1-byte push. + return false; + if (opcode == OP_PUSHDATA1 && data.size() < OP_PUSHDATA1) + // Could have used a normal n-byte push, rather than OP_PUSHDATA1. + return false; + if (opcode == OP_PUSHDATA2 && data.size() <= 0xFF) + // Could have used an OP_PUSHDATA1. + return false; + if (opcode == OP_PUSHDATA4 && data.size() <= 0xFFFF) + // Could have used an OP_PUSHDATA2. + return false; + } + return true; +} + class CScriptVisitor : public boost::static_visitor { private: diff --git a/src/script.h b/src/script.h index 3cbb2cf32..9113024b3 100644 --- a/src/script.h +++ b/src/script.h @@ -533,7 +533,7 @@ public: bool IsPayToScriptHash() const; - // Called by CTransaction::IsStandard + // Called by CTransaction::IsStandard and P2SH VerifyScript (which makes it consensus-critical). bool IsPushOnly() const { const_iterator pc = begin(); @@ -548,6 +548,8 @@ public: return true; } + // Called by IsStandardTx. + bool HasCanonicalPushes() const; void SetDestination(const CTxDestination& address); void SetMultisig(int nRequired, const std::vector& keys); diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index e7ad52627..183e952c3 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -444,4 +444,22 @@ BOOST_AUTO_TEST_CASE(script_combineSigs) BOOST_CHECK(combined == partial3c); } +BOOST_AUTO_TEST_CASE(script_standard_push) +{ + for (int i=0; i<1000; i++) { + CScript script; + script << i; + BOOST_CHECK_MESSAGE(script.IsPushOnly(), "Number " << i << " is not pure push."); + BOOST_CHECK_MESSAGE(script.HasCanonicalPushes(), "Number " << i << " push is not canonical."); + } + + for (int i=0; i<1000; i++) { + std::vector data(i, '\111'); + CScript script; + script << data; + BOOST_CHECK_MESSAGE(script.IsPushOnly(), "Length " << i << " is not pure push."); + BOOST_CHECK_MESSAGE(script.HasCanonicalPushes(), "Length " << i << " push is not canonical."); + } +} + BOOST_AUTO_TEST_SUITE_END()