Relay OP_RETURN data TxOut as standard transaction type

This commit is contained in:
Jeff Garzik 2013-06-24 15:09:50 -04:00
parent 28f6b8dbad
commit a79342479f
5 changed files with 56 additions and 10 deletions

View file

@ -497,17 +497,28 @@ bool IsStandardTx(const CTransaction& tx, string& reason)
return false; return false;
} }
} }
unsigned int nDataOut = 0;
txnouttype whichType;
BOOST_FOREACH(const CTxOut& txout, tx.vout) { BOOST_FOREACH(const CTxOut& txout, tx.vout) {
if (!::IsStandard(txout.scriptPubKey)) { if (!::IsStandard(txout.scriptPubKey, whichType)) {
reason = "scriptpubkey"; reason = "scriptpubkey";
return false; return false;
} }
if (txout.IsDust(CTransaction::nMinRelayTxFee)) { if (whichType == TX_NULL_DATA)
nDataOut++;
else if (txout.IsDust(CTransaction::nMinRelayTxFee)) {
reason = "dust"; reason = "dust";
return false; return false;
} }
} }
// only one OP_RETURN txout is permitted
if (nDataOut > 1) {
reason = "mucho-data";
return false;
}
return true; return true;
} }

View file

@ -79,6 +79,7 @@ const char* GetTxnOutputType(txnouttype t)
case TX_PUBKEYHASH: return "pubkeyhash"; case TX_PUBKEYHASH: return "pubkeyhash";
case TX_SCRIPTHASH: return "scripthash"; case TX_SCRIPTHASH: return "scripthash";
case TX_MULTISIG: return "multisig"; case TX_MULTISIG: return "multisig";
case TX_NULL_DATA: return "nulldata";
} }
return NULL; return NULL;
} }
@ -220,6 +221,7 @@ const char* GetOpName(opcodetype opcode)
// template matching params // template matching params
case OP_PUBKEYHASH : return "OP_PUBKEYHASH"; case OP_PUBKEYHASH : return "OP_PUBKEYHASH";
case OP_PUBKEY : return "OP_PUBKEY"; case OP_PUBKEY : return "OP_PUBKEY";
case OP_SMALLDATA : return "OP_SMALLDATA";
case OP_INVALIDOPCODE : return "OP_INVALIDOPCODE"; case OP_INVALIDOPCODE : return "OP_INVALIDOPCODE";
default: default:
@ -1148,6 +1150,9 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector<vector<unsi
// Sender provides N pubkeys, receivers provides M signatures // Sender provides N pubkeys, receivers provides M signatures
mTemplates.insert(make_pair(TX_MULTISIG, CScript() << OP_SMALLINTEGER << OP_PUBKEYS << OP_SMALLINTEGER << OP_CHECKMULTISIG)); 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: // 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 else
break; break;
} }
else if (opcode2 == OP_SMALLDATA)
{
// small pushdata, <= 80 bytes
if (vch1.size() > 80)
break;
}
else if (opcode1 != opcode2 || vch1 != vch2) else if (opcode1 != opcode2 || vch1 != vch2)
{ {
// Others must match exactly // Others must match exactly
@ -1294,6 +1305,7 @@ bool Solver(const CKeyStore& keystore, const CScript& scriptPubKey, uint256 hash
switch (whichTypeRet) switch (whichTypeRet)
{ {
case TX_NONSTANDARD: case TX_NONSTANDARD:
case TX_NULL_DATA:
return false; return false;
case TX_PUBKEY: case TX_PUBKEY:
keyID = CPubKey(vSolutions[0]).GetID(); keyID = CPubKey(vSolutions[0]).GetID();
@ -1325,6 +1337,8 @@ int ScriptSigArgsExpected(txnouttype t, const std::vector<std::vector<unsigned c
{ {
case TX_NONSTANDARD: case TX_NONSTANDARD:
return -1; return -1;
case TX_NULL_DATA:
return 1;
case TX_PUBKEY: case TX_PUBKEY:
return 1; return 1;
case TX_PUBKEYHASH: case TX_PUBKEYHASH:
@ -1339,10 +1353,9 @@ int ScriptSigArgsExpected(txnouttype t, const std::vector<std::vector<unsigned c
return -1; return -1;
} }
bool IsStandard(const CScript& scriptPubKey) bool IsStandard(const CScript& scriptPubKey, txnouttype& whichType)
{ {
vector<valtype> vSolutions; vector<valtype> vSolutions;
txnouttype whichType;
if (!Solver(scriptPubKey, whichType, vSolutions)) if (!Solver(scriptPubKey, whichType, vSolutions))
return false; return false;
@ -1401,6 +1414,7 @@ bool IsMine(const CKeyStore &keystore, const CScript& scriptPubKey)
switch (whichType) switch (whichType)
{ {
case TX_NONSTANDARD: case TX_NONSTANDARD:
case TX_NULL_DATA:
return false; return false;
case TX_PUBKEY: case TX_PUBKEY:
keyID = CPubKey(vSolutions[0]).GetID(); keyID = CPubKey(vSolutions[0]).GetID();
@ -1462,6 +1476,8 @@ bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, vecto
vector<valtype> vSolutions; vector<valtype> vSolutions;
if (!Solver(scriptPubKey, typeRet, vSolutions)) if (!Solver(scriptPubKey, typeRet, vSolutions))
return false; return false;
if (typeRet == TX_NULL_DATA)
return true;
if (typeRet == TX_MULTISIG) if (typeRet == TX_MULTISIG)
{ {
@ -1677,6 +1693,7 @@ static CScript CombineSignatures(CScript scriptPubKey, const CTransaction& txTo,
switch (txType) switch (txType)
{ {
case TX_NONSTANDARD: case TX_NONSTANDARD:
case TX_NULL_DATA:
// Don't know anything about this, assume bigger one is correct: // Don't know anything about this, assume bigger one is correct:
if (sigs1.size() >= sigs2.size()) if (sigs1.size() >= sigs2.size())
return PushAll(sigs1); return PushAll(sigs1);

View file

@ -46,6 +46,7 @@ enum txnouttype
TX_PUBKEYHASH, TX_PUBKEYHASH,
TX_SCRIPTHASH, TX_SCRIPTHASH,
TX_MULTISIG, TX_MULTISIG,
TX_NULL_DATA,
}; };
class CNoDestination { class CNoDestination {
@ -202,6 +203,7 @@ enum opcodetype
// template matching params // template matching params
OP_SMALLDATA = 0xf9,
OP_SMALLINTEGER = 0xfa, OP_SMALLINTEGER = 0xfa,
OP_PUBKEYS = 0xfb, OP_PUBKEYS = 0xfb,
OP_PUBKEYHASH = 0xfd, 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 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); 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); 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 CScript& scriptPubKey);
bool IsMine(const CKeyStore& keystore, const CTxDestination &dest); bool IsMine(const CKeyStore& keystore, const CTxDestination &dest);
void ExtractAffectedKeys(const CKeyStore &keystore, const CScript& scriptPubKey, std::vector<CKeyID> &vKeys); void ExtractAffectedKeys(const CKeyStore &keystore, const CScript& scriptPubKey, std::vector<CKeyID> &vKeys);

View file

@ -133,21 +133,23 @@ BOOST_AUTO_TEST_CASE(multisig_IsStandard)
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
key[i].MakeNewKey(true); key[i].MakeNewKey(true);
txnouttype whichType;
CScript a_and_b; CScript a_and_b;
a_and_b << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG; 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; CScript a_or_b;
a_or_b << OP_1 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG; 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; CScript escrow;
escrow << OP_2 << key[0].GetPubKey() << key[1].GetPubKey() << key[2].GetPubKey() << OP_3 << OP_CHECKMULTISIG; 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; 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; 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]; CScript malformed[6];
malformed[0] << OP_3 << key[0].GetPubKey() << key[1].GetPubKey() << OP_2 << OP_CHECKMULTISIG; 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(); malformed[5] << OP_1 << key[0].GetPubKey() << key[1].GetPubKey();
for (int i = 0; i < 6; i++) for (int i = 0; i < 6; i++)
BOOST_CHECK(!::IsStandard(malformed[i])); BOOST_CHECK(!::IsStandard(malformed[i], whichType));
} }
BOOST_AUTO_TEST_CASE(multisig_Solver1) BOOST_AUTO_TEST_CASE(multisig_Solver1)

View file

@ -273,6 +273,20 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
t.vout[0].scriptPubKey = CScript() << OP_1; t.vout[0].scriptPubKey = CScript() << OP_1;
BOOST_CHECK(!IsStandardTx(t, reason)); 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() BOOST_AUTO_TEST_SUITE_END()