aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/main.cpp4
-rw-r--r--src/script.cpp27
-rw-r--r--src/script.h5
-rw-r--r--src/test/script_tests.cpp18
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()