Commit a9d3c2ee authored by Tom Zander's avatar Tom Zander
Browse files

Support change to allow multiple op_returns

parent aa57e4bb
Pipeline #269958228 passed with stages
in 20 minutes and 33 seconds
......@@ -697,7 +697,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler)
fIsBareMultisigStd = GetBoolArg("-permitbaremultisig", Settings::DefaultPermitBareMultisig);
fAcceptDatacarrier = GetBoolArg("-datacarrier", Settings::DefaultAcceptDataCarrier);
nMaxDatacarrierBytes = GetArg("-datacarriersize", nMaxDatacarrierBytes);
nMaxDatacarrierBytes = GetArg("-datacarriersize", Settings::MaxOpReturnRelay);
// Option to startup with mocktime set (used for regression testing):
SetMockTime(GetArg("-mocktime", 0)); // SetMockTime(0) is a no-op
......
......@@ -2,7 +2,7 @@
* This file is part of the Flowee project
* Copyright (C) 2009-2010 Satoshi Nakamoto
* Copyright (C) 2009-2015 The Bitcoin developers
* Copyright (C) 2019-2020 Tom Zander <tomz@freedommail.ch>
* Copyright (C) 2019-2021 Tom Zander <tom@flowee.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -53,7 +53,7 @@
* DUP CHECKSIG DROP ... repeated 100 times... OP_1
*/
bool IsStandard(const CScript& scriptPubKey, Script::TxnOutType& whichType)
bool IsStandard(const CScript &scriptPubKey, Script::TxnOutType &whichType, int &dataUsed)
{
std::vector<std::vector<unsigned char> > vSolutions;
if (!Script::solver(scriptPubKey, whichType, vSolutions))
......@@ -67,9 +67,11 @@ bool IsStandard(const CScript& scriptPubKey, Script::TxnOutType& whichType)
return false;
if (m < 1 || m > n)
return false;
} else if (whichType == Script::TX_NULL_DATA &&
(!fAcceptDatacarrier || scriptPubKey.size() > nMaxDatacarrierBytes))
return false;
} else if (whichType == Script::TX_NULL_DATA) {
if (!fAcceptDatacarrier)
return false;
dataUsed += scriptPubKey.size() - 3; // (-1 for OP_RETURN, -2 for the pushdata opcodes)
  • In my reading of the CHIP, the - 3 should be removed (thus including OP_RETURN and push opcodes in the count), otherwise the new counting logic is incompatible with the old logic.

    Note that there can be multiple data pushes, so the assumption that the pushdata opcodes is always 2 bytes per output is not correct. It can even be zero, so the - 3 is also exploitable (every OP_RETURN without any data increases the data limit with 2 bytes).

    Edited by BigBlockIfTrue
  • right, so the result would be that the end-user loses 2 or 3 bytes of total op-return usage every time they start a new output.

    I'm fine either way, will suggest a clarification on the CHIP using your suggestion.

Please register or sign in to reply
}
return whichType != Script::TX_NONSTANDARD;
}
......@@ -109,16 +111,15 @@ bool IsStandardTx(const CTransaction& tx, std::string& reason)
}
}
unsigned int nDataOut = 0;
Script::TxnOutType whichType;
int dataUsed = 0; // TX_NULL_DATA type number of bytes used
for (const CTxOut& txout : tx.vout) {
if (!::IsStandard(txout.scriptPubKey, whichType)) {
if (!::IsStandard(txout.scriptPubKey, whichType, dataUsed)) {
reason = "scriptpubkey";
return false;
}
if (whichType == Script::TX_NULL_DATA) {
nDataOut++;
} else if (whichType == Script::TX_NULL_DATA && dataUsed > nMaxDatacarrierBytes) {
reason = "oversize-op-return";
return false;
} else if ((whichType == Script::TX_MULTISIG) && (!fIsBareMultisigStd)) {
reason = "bare-multisig";
return false;
......@@ -128,12 +129,6 @@ bool IsStandardTx(const CTransaction& tx, std::string& reason)
}
}
// only one OP_RETURN txout is permitted
if (nDataOut > 1) {
reason = "multi-op-return";
return false;
}
return true;
}
......
......@@ -2,7 +2,7 @@
* This file is part of the Flowee project
* Copyright (C) 2009-2010 Satoshi Nakamoto
* Copyright (C) 2009-2015 The Bitcoin developers
* Copyright (C) 2019-2020 Tom Zander <tomz@freedommail.ch>
* Copyright (C) 2019-2021 Tom Zander <tom@flowee.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
......@@ -57,7 +57,8 @@ static const unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS = STANDARD_SCRIPT_
static const unsigned int STANDARD_LOCKTIME_VERIFY_FLAGS = LOCKTIME_VERIFY_SEQUENCE |
LOCKTIME_MEDIAN_TIME_PAST;
bool IsStandard(const CScript &scriptPubKey, Script::TxnOutType &whichType);
/// \internal. Only here for unit tests.
bool IsStandard(const CScript &scriptPubKey, Script::TxnOutType &whichType, int &dataUsed);
/**
* Check for standard transaction types
* @return True if all outputs (scriptPubKeys) use only standard transaction forms
......
......@@ -31,7 +31,7 @@
typedef std::vector<unsigned char> valtype;
bool fAcceptDatacarrier = Settings::DefaultAcceptDataCarrier;
unsigned nMaxDatacarrierBytes = Settings::MaxOpReturnRelay;
int nMaxDatacarrierBytes = Settings::MaxOpReturnRelay;
CScriptID::CScriptID(const CScript& in) : uint160(Hash160(in.begin(), in.end())) {}
......
......@@ -38,7 +38,7 @@ public:
};
extern bool fAcceptDatacarrier;
extern unsigned nMaxDatacarrierBytes;
extern int nMaxDatacarrierBytes;
/**
* Mandatory script verification flags that all new blocks must comply with for
......
......@@ -34,7 +34,7 @@ constexpr uint32_t DefaultCheckLevel = 3;
constexpr int32_t DefaultBlockAcceptSize = 128000000;
constexpr bool DefaultAcceptDataCarrier = true;
constexpr uint32_t MaxOpReturnRelay = 223; //! bytes (+1 for OP_RETURN, +2 for the pushdata opcodes)
constexpr int MaxOpReturnRelay = 220; //! bytes
constexpr bool DefaultRelayPriority = true;
/** Default for setting that we download and accept blocks only (no transactions, no mempool) */
......
......@@ -156,19 +156,20 @@ void MultiSigTests::multisig_IsStandard()
CScript a_and_b;
a_and_b << OP_2 << ToByteVector(key[0].GetPubKey()) << ToByteVector(key[1].GetPubKey()) << OP_2 << OP_CHECKMULTISIG;
QVERIFY(::IsStandard(a_and_b, whichType));
int dataSize = 0;
QVERIFY(::IsStandard(a_and_b, whichType, dataSize));
CScript a_or_b;
a_or_b << OP_1 << ToByteVector(key[0].GetPubKey()) << ToByteVector(key[1].GetPubKey()) << OP_2 << OP_CHECKMULTISIG;
QVERIFY(::IsStandard(a_or_b, whichType));
QVERIFY(::IsStandard(a_or_b, whichType, dataSize));
CScript escrow;
escrow << OP_2 << ToByteVector(key[0].GetPubKey()) << ToByteVector(key[1].GetPubKey()) << ToByteVector(key[2].GetPubKey()) << OP_3 << OP_CHECKMULTISIG;
QVERIFY(::IsStandard(escrow, whichType));
QVERIFY(::IsStandard(escrow, whichType, dataSize));
CScript one_of_four;
one_of_four << OP_1 << ToByteVector(key[0].GetPubKey()) << ToByteVector(key[1].GetPubKey()) << ToByteVector(key[2].GetPubKey()) << ToByteVector(key[3].GetPubKey()) << OP_4 << OP_CHECKMULTISIG;
QVERIFY(!::IsStandard(one_of_four, whichType));
QVERIFY(!::IsStandard(one_of_four, whichType, dataSize));
CScript malformed[6];
malformed[0] << OP_3 << ToByteVector(key[0].GetPubKey()) << ToByteVector(key[1].GetPubKey()) << OP_2 << OP_CHECKMULTISIG;
......@@ -179,7 +180,7 @@ void MultiSigTests::multisig_IsStandard()
malformed[5] << OP_1 << ToByteVector(key[0].GetPubKey()) << ToByteVector(key[1].GetPubKey());
for (int i = 0; i < 6; i++)
QVERIFY(!::IsStandard(malformed[i], whichType));
QVERIFY(!::IsStandard(malformed[i], whichType, dataSize));
}
void MultiSigTests::multisig_Solver1()
......
......@@ -334,14 +334,14 @@ void TransactionTests::test_IsStandard()
t.vout[0].scriptPubKey = CScript() << OP_RETURN
<< ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3804678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38"
"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
QCOMPARE(Settings::MaxOpReturnRelay, t.vout[0].scriptPubKey.size());
QCOMPARE(Settings::MaxOpReturnRelay + 3, t.vout[0].scriptPubKey.size());
QVERIFY(IsStandardTx(t, reason));
// MAX_OP_RETURN_RELAY+1-byte TX_NULL_DATA (non-standard)
t.vout[0].scriptPubKey = CScript() << OP_RETURN
<< ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3804678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3800"
"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000");
QCOMPARE(Settings::MaxOpReturnRelay + 1, t.vout[0].scriptPubKey.size());
QCOMPARE(Settings::MaxOpReturnRelay + 4, t.vout[0].scriptPubKey.size());
QVERIFY(!IsStandardTx(t, reason));
// Data payload can be encoded in any way...
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment