diff options
author | Peter Todd <pete@petertodd.org> | 2014-10-13 10:18:05 -0400 |
---|---|---|
committer | Jorge Timón <jtimon@jtimon.cc> | 2015-10-01 18:28:13 +0200 |
commit | da894ab5da222ad317039eb008ec6443fb9113d9 (patch) | |
tree | 391ecd1cf9ea432a1558ae2e70d9a1fbb91d5dd3 /src | |
parent | 5d8709c3b7fcc66fbf537ec0b6c1a98d69214e25 (diff) |
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!)
Diffstat (limited to 'src')
-rw-r--r-- | src/policy/policy.cpp | 4 | ||||
-rw-r--r-- | src/script/script.cpp | 2 | ||||
-rw-r--r-- | src/script/script.h | 1 | ||||
-rw-r--r-- | src/script/standard.cpp | 21 | ||||
-rw-r--r-- | src/script/standard.h | 2 | ||||
-rw-r--r-- | 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 169fef4af4..4c96fbf5a5 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 0a8ec9fa7b..9a0c067a33 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 3923a559b1..cdc9a71bb2 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 59496d795d..bfef8afa17 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -51,11 +51,6 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector<vector<unsi // Sender provides N pubkeys, receivers provides M signatures mTemplates.insert(make_pair(TX_MULTISIG, CScript() << OP_SMALLINTEGER << OP_PUBKEYS << OP_SMALLINTEGER << OP_CHECKMULTISIG)); - - // Empty, provably prunable, data-carrying output - if (GetBoolArg("-datacarrier", true)) - mTemplates.insert(make_pair(TX_NULL_DATA, CScript() << OP_RETURN << OP_SMALLDATA)); - mTemplates.insert(make_pair(TX_NULL_DATA, CScript() << OP_RETURN)); } vSolutionsRet.clear(); @@ -70,6 +65,16 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector<vector<unsi return true; } + // Provably prunable, data-carrying output + // + // So long as script passes the IsUnspendable() test and all but the first + // byte passes the IsPushOnly() test we don't care what exactly is in the + // script. + if (scriptPubKey.size() >= 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<vector<unsi else break; } - else if (opcode2 == OP_SMALLDATA) - { - // small pushdata, <= nMaxDatacarrierBytes - if (vch1.size() > 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 9e17dac700..ae1bbecca0 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 beec396675..9847f6512e 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 |