From 6a07eb676a020b0035173facb25f92f1ff6380f7 Mon Sep 17 00:00:00 2001 From: Peter Todd Date: Mon, 13 Oct 2014 10:14:58 -0400 Subject: [PATCH 1/3] Make TX_SCRIPTHASH clear vSolutionsRet first Previously unlike other transaction types the TX_SCRIPTHASH would not clear vSolutionsRet, which means that unlike other transaction types if it was called twice in a row you would get the result of the previous invocation as well. --- src/script/standard.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/script/standard.cpp b/src/script/standard.cpp index 1d5aac7b3..59496d795 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -58,6 +58,8 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector Date: Tue, 4 Nov 2014 12:38:56 -0500 Subject: [PATCH 2/3] Add IsPushOnly(const_iterator pc) Allows IsPushOnly() to be applied to just part of the script for OP_RETURN outputs. --- src/script/script.cpp | 8 ++++++-- src/script/script.h | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/script/script.cpp b/src/script/script.cpp index 58dbade0e..0a8ec9fa7 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -210,9 +210,8 @@ bool CScript::IsPayToScriptHash() const this->at(22) == OP_EQUAL); } -bool CScript::IsPushOnly() const +bool CScript::IsPushOnly(const_iterator pc) const { - const_iterator pc = begin(); while (pc < end()) { opcodetype opcode; @@ -227,3 +226,8 @@ bool CScript::IsPushOnly() const } return true; } + +bool CScript::IsPushOnly() const +{ + return this->IsPushOnly(begin()); +} diff --git a/src/script/script.h b/src/script/script.h index f0725bbbf..3923a559b 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -589,6 +589,7 @@ public: bool IsPayToScriptHash() const; /** Called by IsStandardTx and P2SH/BIP62 VerifyScript (which makes it consensus-critical). */ + bool IsPushOnly(const_iterator pc) const; bool IsPushOnly() const; /** From da894ab5da222ad317039eb008ec6443fb9113d9 Mon Sep 17 00:00:00 2001 From: Peter Todd Date: Mon, 13 Oct 2014 10:18:05 -0400 Subject: [PATCH 3/3] Accept any sequence of PUSHDATAs in OP_RETURN outputs Previously only one PUSHDATA was allowed, needlessly limiting applications such as matching OP_RETURN contents with bloom filters that operate on a per-PUSHDATA level. Now any combination that passes IsPushOnly() is allowed, so long as the total size of the scriptPubKey is less than 42 bytes. (unchanged modulo non-minimal PUSHDATA encodings) Also, this fixes the odd bug where previously the PUSHDATA could be replaced by any single opcode, even sigops consuming opcodes such as CHECKMULTISIG. (20 sigops!) --- src/policy/policy.cpp | 4 +++- src/script/script.cpp | 2 +- src/script/script.h | 1 - src/script/standard.cpp | 21 ++++++++++----------- src/script/standard.h | 2 +- src/test/transaction_tests.cpp | 21 +++++++++++++++++++-- 6 files changed, 34 insertions(+), 17 deletions(-) diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index 169fef4af..4c96fbf5a 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -49,7 +49,9 @@ bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType) return false; if (m < 1 || m > n) return false; - } + } else if (whichType == TX_NULL_DATA && + (!GetBoolArg("-datacarrier", true) || scriptPubKey.size() > nMaxDatacarrierBytes)) + return false; return whichType != TX_NONSTANDARD; } diff --git a/src/script/script.cpp b/src/script/script.cpp index 0a8ec9fa7..9a0c067a3 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -144,7 +144,7 @@ const char* GetOpName(opcodetype opcode) case OP_INVALIDOPCODE : return "OP_INVALIDOPCODE"; // Note: - // The template matching params OP_SMALLDATA/etc are defined in opcodetype enum + // The template matching params OP_SMALLINTEGER/etc are defined in opcodetype enum // as kind of implementation hack, they are *NOT* real opcodes. If found in real // Script, just let the default: case deal with them. diff --git a/src/script/script.h b/src/script/script.h index 3923a559b..cdc9a71bb 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -167,7 +167,6 @@ enum opcodetype // template matching params - OP_SMALLDATA = 0xf9, OP_SMALLINTEGER = 0xfa, OP_PUBKEYS = 0xfb, OP_PUBKEYHASH = 0xfd, diff --git a/src/script/standard.cpp b/src/script/standard.cpp index 59496d795..bfef8afa1 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -51,11 +51,6 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector= 1 && scriptPubKey[0] == OP_RETURN && scriptPubKey.IsPushOnly(scriptPubKey.begin()+1)) { + typeRet = TX_NULL_DATA; + return true; + } + // Scan templates const CScript& script1 = scriptPubKey; BOOST_FOREACH(const PAIRTYPE(txnouttype, CScript)& tplate, mTemplates) @@ -142,12 +147,6 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector nMaxDatacarrierBytes) - break; - } else if (opcode1 != opcode2 || vch1 != vch2) { // Others must match exactly diff --git a/src/script/standard.h b/src/script/standard.h index 9e17dac70..ae1bbecca 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -25,7 +25,7 @@ public: CScriptID(const uint160& in) : uint160(in) {} }; -static const unsigned int MAX_OP_RETURN_RELAY = 80; //! bytes +static const unsigned int MAX_OP_RETURN_RELAY = 83; //! bytes (+1 for OP_RETURN, +2 for the pushdata opcodes) extern unsigned nMaxDatacarrierBytes; /** diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index beec39667..9847f6512 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -351,12 +351,29 @@ BOOST_AUTO_TEST_CASE(test_IsStandard) t.vout[0].scriptPubKey = CScript() << OP_1; BOOST_CHECK(!IsStandardTx(t, reason)); - // 80-byte TX_NULL_DATA (standard) + // MAX_OP_RETURN_RELAY-byte TX_NULL_DATA (standard) t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3804678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38"); + BOOST_CHECK_EQUAL(MAX_OP_RETURN_RELAY, t.vout[0].scriptPubKey.size()); BOOST_CHECK(IsStandardTx(t, reason)); - // 81-byte TX_NULL_DATA (non-standard) + // MAX_OP_RETURN_RELAY+1-byte TX_NULL_DATA (non-standard) t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3804678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3800"); + BOOST_CHECK_EQUAL(MAX_OP_RETURN_RELAY + 1, t.vout[0].scriptPubKey.size()); + BOOST_CHECK(!IsStandardTx(t, reason)); + + // Data payload can be encoded in any way... + t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex(""); + BOOST_CHECK(IsStandardTx(t, reason)); + t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("00") << ParseHex("01"); + BOOST_CHECK(IsStandardTx(t, reason)); + // OP_RESERVED *is* considered to be a PUSHDATA type opcode by IsPushOnly()! + t.vout[0].scriptPubKey = CScript() << OP_RETURN << OP_RESERVED << -1 << 0 << ParseHex("01") << 2 << 3 << 4 << 5 << 6 << 7 << 8 << 9 << 10 << 11 << 12 << 13 << 14 << 15 << 16; + BOOST_CHECK(IsStandardTx(t, reason)); + t.vout[0].scriptPubKey = CScript() << OP_RETURN << 0 << ParseHex("01") << 2 << ParseHex("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); + BOOST_CHECK(IsStandardTx(t, reason)); + + // ...so long as it only contains PUSHDATA's + t.vout[0].scriptPubKey = CScript() << OP_RETURN << OP_RETURN; BOOST_CHECK(!IsStandardTx(t, reason)); // TX_NULL_DATA w/o PUSHDATA