From 9aea601b05b3541fcc7e0c45cba8fd2178224809 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 24 Sep 2013 00:45:18 +0200 Subject: [PATCH 1/2] Move IsPushOnly() to script.cpp --- src/script.cpp | 18 ++++++++++++++++++ src/script.h | 18 +----------------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/script.cpp b/src/script.cpp index 2b66bc73d..b2d8a67f9 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -1863,6 +1863,24 @@ bool CScript::IsPayToScriptHash() const this->at(22) == OP_EQUAL); } +bool CScript::IsPushOnly() const +{ + const_iterator pc = begin(); + while (pc < end()) + { + opcodetype opcode; + if (!GetOp(pc, opcode)) + return false; + // Note that IsPushOnly() *does* consider OP_RESERVED to be a + // push-type opcode, however execution of OP_RESERVED fails, so + // it's not relevant to P2SH as the scriptSig would fail prior to + // the P2SH special validation code being executed. + if (opcode > OP_16) + return false; + } + return true; +} + class CScriptVisitor : public boost::static_visitor { private: diff --git a/src/script.h b/src/script.h index bd120cc07..a0a6cd1c4 100644 --- a/src/script.h +++ b/src/script.h @@ -542,23 +542,7 @@ public: bool IsPayToScriptHash() const; // Called by IsStandardTx - bool IsPushOnly() const - { - const_iterator pc = begin(); - while (pc < end()) - { - opcodetype opcode; - if (!GetOp(pc, opcode)) - return false; - // Note that IsPushOnly() *does* consider OP_RESERVED to be a - // push-type opcode, however execution of OP_RESERVED fails, so - // it's not relevant to P2SH as the scriptSig would fail prior to - // the P2SH special validation code being executed. - if (opcode > OP_16) - return false; - } - return true; - } + bool IsPushOnly() const; // Returns whether the script is guaranteed to fail at execution, // regardless of the initial stack. This allows outputs to be pruned From 87fe71e1fc810ee120a10063fdd26c3245686d54 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Tue, 24 Sep 2013 00:48:00 +0200 Subject: [PATCH 2/2] Add HasCanonicalPushes(), and use it in IsStandardTx --- src/main.cpp | 4 ++++ src/script.cpp | 27 +++++++++++++++++++++++++++ src/script.h | 5 ++++- src/test/script_tests.cpp | 18 ++++++++++++++++++ 4 files changed, 53 insertions(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index d33ad2fa9..1df9a24d5 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 b2d8a67f9..83fc91956 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 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 a0a6cd1c4..335ddfb1b 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 dee0f110a..dd1b61304 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 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()