diff options
-rw-r--r-- | src/main.cpp | 4 | ||||
-rw-r--r-- | src/script.cpp | 27 | ||||
-rw-r--r-- | src/script.h | 5 | ||||
-rw-r--r-- | src/test/script_tests.cpp | 18 |
4 files changed, 53 insertions, 1 deletions
diff --git a/src/main.cpp b/src/main.cpp index d33ad2fa95..1df9a24d55 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -442,6 +442,10 @@ bool IsStandardTx(const CTransaction& tx, string& reason) reason = "scriptsig-not-pushonly"; return false; } + if (!txin.scriptSig.HasCanonicalPushes()) { + reason = "non-canonical-push"; + return false; + } } unsigned int nDataOut = 0; diff --git a/src/script.cpp b/src/script.cpp index b2d8a67f96..83fc91956c 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -1881,6 +1881,33 @@ bool CScript::IsPushOnly() const return true; } +bool CScript::HasCanonicalPushes() const +{ + const_iterator pc = begin(); + while (pc < end()) + { + opcodetype opcode; + std::vector<unsigned char> 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<bool> { private: diff --git a/src/script.h b/src/script.h index a0a6cd1c44..335ddfb1b2 100644 --- a/src/script.h +++ b/src/script.h @@ -541,9 +541,12 @@ public: bool IsPayToScriptHash() const; - // Called by IsStandardTx + // Called by IsStandardTx and P2SH VerifyScript (which makes it consensus-critical). bool IsPushOnly() const; + // Called by IsStandardTx. + bool HasCanonicalPushes() const; + // Returns whether the script is guaranteed to fail at execution, // regardless of the initial stack. This allows outputs to be pruned // instantly when entering the UTXO set. diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index dee0f110ad..dd1b613047 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -438,4 +438,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<unsigned char> 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() |