diff options
author | Jeff Garzik <jgarzik@bitpay.com> | 2013-06-24 15:09:50 -0400 |
---|---|---|
committer | Jeff Garzik <jgarzik@bitpay.com> | 2013-10-02 11:49:43 -0400 |
commit | a79342479f577013f2fd2573fb32585d6f4981b3 (patch) | |
tree | dccd44003e725234363f2e95a3fb61e1ae24e486 | |
parent | 28f6b8dbad27f0dac72daca6f1bfe41d7e701908 (diff) |
Relay OP_RETURN data TxOut as standard transaction type
-rw-r--r-- | src/main.cpp | 15 | ||||
-rw-r--r-- | src/script.cpp | 21 | ||||
-rw-r--r-- | src/script.h | 4 | ||||
-rw-r--r-- | src/test/multisig_tests.cpp | 12 | ||||
-rw-r--r-- | src/test/transaction_tests.cpp | 14 |
5 files changed, 56 insertions, 10 deletions
diff --git a/src/main.cpp b/src/main.cpp index dc690111e6..ed48fd5760 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -497,17 +497,28 @@ bool IsStandardTx(const CTransaction& tx, string& reason) return false; } } + + unsigned int nDataOut = 0; + txnouttype whichType; BOOST_FOREACH(const CTxOut& txout, tx.vout) { - if (!::IsStandard(txout.scriptPubKey)) { + if (!::IsStandard(txout.scriptPubKey, whichType)) { reason = "scriptpubkey"; return false; } - if (txout.IsDust(CTransaction::nMinRelayTxFee)) { + if (whichType == TX_NULL_DATA) + nDataOut++; + else if (txout.IsDust(CTransaction::nMinRelayTxFee)) { reason = "dust"; return false; } } + // only one OP_RETURN txout is permitted + if (nDataOut > 1) { + reason = "mucho-data"; + return false; + } + return true; } diff --git a/src/script.cpp b/src/script.cpp index 0fe2953548..858743f887 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -79,6 +79,7 @@ const char* GetTxnOutputType(txnouttype t) case TX_PUBKEYHASH: return "pubkeyhash"; case TX_SCRIPTHASH: return "scripthash"; case TX_MULTISIG: return "multisig"; + case TX_NULL_DATA: return "nulldata"; } return NULL; } @@ -220,6 +221,7 @@ const char* GetOpName(opcodetype opcode) // template matching params case OP_PUBKEYHASH : return "OP_PUBKEYHASH"; case OP_PUBKEY : return "OP_PUBKEY"; + case OP_SMALLDATA : return "OP_SMALLDATA"; case OP_INVALIDOPCODE : return "OP_INVALIDOPCODE"; default: @@ -1148,6 +1150,9 @@ 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 + mTemplates.insert(make_pair(TX_NULL_DATA, CScript() << OP_RETURN << OP_SMALLDATA)); } // Shortcut for pay-to-script-hash, which are more constrained than the other types: @@ -1232,6 +1237,12 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector<vector<unsi else break; } + else if (opcode2 == OP_SMALLDATA) + { + // small pushdata, <= 80 bytes + if (vch1.size() > 80) + break; + } else if (opcode1 != opcode2 || vch1 != vch2) { // Others must match exactly @@ -1294,6 +1305,7 @@ bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash switch (whichTypeRet) { case TX_NONSTANDARD: + case TX_NULL_DATA: return false; case TX_PUBKEY: keyID = CPubKey(vSolutions[0]).GetID(); @@ -1325,6 +1337,8 @@ int ScriptSigArgsExpected(txnouttype t, const std::vector<std::vector<unsigned c { case TX_NONSTANDARD: return -1; + case TX_NULL_DATA: + return 1; case TX_PUBKEY: return 1; case TX_PUBKEYHASH: @@ -1339,10 +1353,9 @@ int ScriptSigArgsExpected(txnouttype t, const std::vector<std::vector<unsigned c return -1; } -bool IsStandard(const CScript& scriptPubKey) +bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType) { vector<valtype> vSolutions; - txnouttype whichType; if (!Solver(scriptPubKey, whichType, vSolutions)) return false; @@ -1401,6 +1414,7 @@ bool IsMine(const CKeyStore &keystore, const CScript& scriptPubKey) switch (whichType) { case TX_NONSTANDARD: + case TX_NULL_DATA: return false; case TX_PUBKEY: keyID = CPubKey(vSolutions[0]).GetID(); @@ -1462,6 +1476,8 @@ bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, vecto vector<valtype> vSolutions; if (!Solver(scriptPubKey, typeRet, vSolutions)) return false; + if (typeRet == TX_NULL_DATA) + return true; if (typeRet == TX_MULTISIG) { @@ -1677,6 +1693,7 @@ static CScript CombineSignatures(CScript scriptPubKey, const CTransaction& txTo, switch (txType) { case TX_NONSTANDARD: + case TX_NULL_DATA: // Don't know anything about this, assume bigger one is correct: if (sigs1.size() >= sigs2.size()) return PushAll(sigs1); diff --git a/src/script.h b/src/script.h index 842b8512eb..30a7c07692 100644 --- a/src/script.h +++ b/src/script.h @@ -46,6 +46,7 @@ enum txnouttype TX_PUBKEYHASH, TX_SCRIPTHASH, TX_MULTISIG, + TX_NULL_DATA, }; class CNoDestination { @@ -202,6 +203,7 @@ enum opcodetype // template matching params + OP_SMALLDATA = 0xf9, OP_SMALLINTEGER = 0xfa, OP_PUBKEYS = 0xfb, OP_PUBKEYHASH = 0xfd, @@ -683,7 +685,7 @@ bool IsCanonicalSignature(const std::vector<unsigned char> &vchSig, unsigned int bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType); bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::vector<unsigned char> >& vSolutionsRet); int ScriptSigArgsExpected(txnouttype t, const std::vector<std::vector<unsigned char> >& vSolutions); -bool IsStandard(const CScript& scriptPubKey); +bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType); bool IsMine(const CKeyStore& keystore, const CScript& scriptPubKey); bool IsMine(const CKeyStore& keystore, const CTxDestination &dest); void ExtractAffectedKeys(const CKeyStore &keystore, const CScript& scriptPubKey, std::vector<CKeyID> &vKeys); diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp index 9ef932b5b4..29b190692f 100644 --- a/src/test/multisig_tests.cpp +++ b/src/test/multisig_tests.cpp @@ -133,21 +133,23 @@ BOOST_AUTO_TEST_CASE(multisig_IsStandard) for (int i = 0; i < 4; i++) key[i].MakeNewKey(true); + txnouttype whichType; + CScript a_and_b; a_and_b << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG; - BOOST_CHECK(::IsStandard(a_and_b)); + BOOST_CHECK(::IsStandard(a_and_b, whichType)); CScript a_or_b; a_or_b << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG; - BOOST_CHECK(::IsStandard(a_or_b)); + BOOST_CHECK(::IsStandard(a_or_b, whichType)); CScript escrow; escrow << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << key[2].GetPubKey() << OP_3 << OP_CHECKMULTISIG; - BOOST_CHECK(::IsStandard(escrow)); + BOOST_CHECK(::IsStandard(escrow, whichType)); CScript one_of_four; one_of_four << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << key[2].GetPubKey() << key[3].GetPubKey() << OP_4 << OP_CHECKMULTISIG; - BOOST_CHECK(!::IsStandard(one_of_four)); + BOOST_CHECK(!::IsStandard(one_of_four, whichType)); CScript malformed[6]; malformed[0] << OP_3 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG; @@ -158,7 +160,7 @@ BOOST_AUTO_TEST_CASE(multisig_IsStandard) malformed[5] << OP_1 << key[0].GetPubKey() << key[1].GetPubKey(); for (int i = 0; i < 6; i++) - BOOST_CHECK(!::IsStandard(malformed[i])); + BOOST_CHECK(!::IsStandard(malformed[i], whichType)); } BOOST_AUTO_TEST_CASE(multisig_Solver1) diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index 416b93ab33..5dfb67cbe4 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -273,6 +273,20 @@ BOOST_AUTO_TEST_CASE(test_IsStandard) t.vout[0].scriptPubKey = CScript() << OP_1; BOOST_CHECK(!IsStandardTx(t, reason)); + + // 80-byte TX_NULL_DATA (standard) + t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3804678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38"); + BOOST_CHECK(IsStandardTx(t, reason)); + + // 81-byte TX_NULL_DATA (non-standard) + t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3804678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3800"); + BOOST_CHECK(!IsStandardTx(t, reason)); + + // Only one TX_NULL_DATA permitted + t.vout.resize(2); + t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3804678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38"); + t.vout[1].scriptPubKey = CScript() << OP_RETURN << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3804678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38"); + BOOST_CHECK(!IsStandardTx(t, reason)); } BOOST_AUTO_TEST_SUITE_END() |