New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Relay OP_RETURN data TxOut as standard transaction type. #2738

Merged
merged 1 commit into from Oct 22, 2013
File filter...
Filter file types
Jump to file or symbol
Failed to load files and symbols.
+56 −10
Diff settings

Always

Just for now

Copy path View file
@@ -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";

This comment has been minimized.

@gavinandresen

gavinandresen Oct 4, 2013

Contributor

"mucho-data" ? You trolling to see if we're paying attention?

"multi-op-return" would be better for non-Spanglish speakers.

This comment has been minimized.

@sipa

sipa Oct 4, 2013

Member

Glad that someone actually reads the code changes...

This comment has been minimized.

@jgarzik

jgarzik Oct 7, 2013

Contributor

Speaking as the person who wrote the "reason=string" code in IsStandardTx(), the strict requirement is that the "reason" is computer-parsable and static vis-a-vis the condition being reported, not necessarily English ;-)

This was added in the most recent rebase, just to have a little fun. If you want to be boring I'll change it :)

This comment has been minimized.

@Diapolo

Diapolo Oct 7, 2013

I want you to be boring and create interesting pulls, I'm the boring-string guy you know ^^.

This comment has been minimized.

@Sjors

Sjors Jan 10, 2019

Member

For historical reference: mucho-data was renamed to multi-op-return: https://github.com/bitcoin/bitcoin/pull/3589/files

return false;
}

return true;
}

Copy path View file
@@ -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)

This comment has been minimized.

@petertodd

petertodd Sep 23, 2013

Contributor

Why 80 bytes? That's significantly larger than a hash, yet still too small for a announce-commit sacrifice.

This comment has been minimized.

@Diapolo

Diapolo Oct 7, 2013

Couldn't that be exactly the maximum size a hash can have?

This comment has been minimized.

@jgarzik

jgarzik Oct 7, 2013

Contributor

It is one SHA512 hash plus some metadata, enabling "BOND $hash" style usage that has been discussed in the past, published on the wiki and implemented in at least one [non-working, inactive] project.

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);
Copy path View file
@@ -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);
Copy path View file
@@ -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)
Copy path View file
@@ -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()
ProTip! Use n and p to navigate between commits in a pull request.