aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main.cpp4
-rw-r--r--src/script.cpp45
-rw-r--r--src/script.h23
-rw-r--r--src/test/script_tests.cpp18
4 files changed, 72 insertions, 18 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 2b66bc73d6..83fc91956c 100644
--- a/src/script.cpp
+++ b/src/script.cpp
@@ -1863,6 +1863,51 @@ 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;
+}
+
+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 bd120cc07d..335ddfb1b2 100644
--- a/src/script.h
+++ b/src/script.h
@@ -541,24 +541,11 @@ 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;
- }
+ // 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
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()