diff --git a/src/avalanche/proof.cpp b/src/avalanche/proof.cpp --- a/src/avalanche/proof.cpp +++ b/src/avalanche/proof.cpp @@ -108,6 +108,14 @@ }); } +static bool IsStandardPayoutScript(const CScript &scriptPubKey) { + // Check the script's standardness against the default max OP_RETURN size, + // so that a proof's validity is not affected by a local relay policy + // parameter (see -datacarriersize config option) + TxoutType scriptType; + return IsStandard(scriptPubKey, MAX_OP_RETURN_RELAY, scriptType); +} + bool Proof::verify(const Amount &stakeUtxoDustThreshold, ProofValidationState &state) const { if (stakes.empty()) { @@ -120,8 +128,7 @@ strprintf("%u > %u", stakes.size(), AVALANCHE_MAX_PROOF_STAKES)); } - TxoutType scriptType; - if (!IsStandard(payoutScriptPubKey, scriptType)) { + if (!IsStandardPayoutScript(payoutScriptPubKey)) { return state.Invalid(ProofValidationResult::INVALID_PAYOUT_SCRIPT, "payout-script-non-standard"); } diff --git a/src/init.cpp b/src/init.cpp --- a/src/init.cpp +++ b/src/init.cpp @@ -1970,9 +1970,6 @@ return false; } - fAcceptDatacarrier = - args.GetBoolArg("-datacarrier", DEFAULT_ACCEPT_DATACARRIER); - // Option to startup with mocktime set (used for regression testing): SetMockTime(args.GetIntArg("-mocktime", 0)); // SetMockTime(0) is a no-op diff --git a/src/kernel/mempool_options.h b/src/kernel/mempool_options.h --- a/src/kernel/mempool_options.h +++ b/src/kernel/mempool_options.h @@ -5,6 +5,7 @@ #define BITCOIN_KERNEL_MEMPOOL_OPTIONS_H #include <policy/policy.h> +#include <script/standard.h> #include <chrono> #include <cstdint> @@ -35,6 +36,17 @@ * mining and transaction creation) */ CFeeRate min_relay_feerate{DEFAULT_MIN_RELAY_TX_FEE_PER_KB}; + /** + * A data carrying output is an unspendable output containing data. The + * script type is designated as TxoutType::NULL_DATA. + * + * Maximum size of TxoutType::NULL_DATA scripts that this node considers + * standard. + * If nullopt, any size is nonstandard. + */ + std::optional<unsigned> max_datacarrier_bytes{ + DEFAULT_ACCEPT_DATACARRIER ? std::optional{MAX_OP_RETURN_RELAY} + : std::nullopt}; bool permit_bare_multisig{DEFAULT_PERMIT_BAREMULTISIG}; bool require_standard{true}; }; diff --git a/src/mempool_args.cpp b/src/mempool_args.cpp --- a/src/mempool_args.cpp +++ b/src/mempool_args.cpp @@ -48,6 +48,12 @@ mempool_opts.permit_bare_multisig = argsman.GetBoolArg("-permitbaremultisig", DEFAULT_PERMIT_BAREMULTISIG); + mempool_opts.max_datacarrier_bytes = + argsman.GetBoolArg("-datacarrier", DEFAULT_ACCEPT_DATACARRIER) + ? std::optional<unsigned>{argsman.GetIntArg("-datacarriersize", + MAX_OP_RETURN_RELAY)} + : std::nullopt; + mempool_opts.require_standard = !argsman.GetBoolArg("-acceptnonstdtxn", !chainparams.RequireStandard()); if (!chainparams.IsTestChain() && !mempool_opts.require_standard) { diff --git a/src/policy/policy.h b/src/policy/policy.h --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -10,6 +10,7 @@ #include <feerate.h> #include <script/standard.h> +#include <optional> #include <string> class CCoinsViewCache; @@ -111,15 +112,19 @@ bool IsDust(const CTxOut &txout, const CFeeRate &dustRelayFee); -bool IsStandard(const CScript &scriptPubKey, TxoutType &whichType); +bool IsStandard(const CScript &scriptPubKey, + const std::optional<unsigned> &max_datacarrier_bytes, + TxoutType &whichType); /** * Check for standard transaction types * @return True if all outputs (scriptPubKeys) use only standard transaction * forms */ -bool IsStandardTx(const CTransaction &tx, bool permit_bare_multisig, - const CFeeRate &dust_relay_fee, std::string &reason); +bool IsStandardTx(const CTransaction &tx, + const std::optional<unsigned> &max_datacarrier_bytes, + bool permit_bare_multisig, const CFeeRate &dust_relay_fee, + std::string &reason); /** * Check for standard transaction types diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -35,7 +35,9 @@ return (txout.nValue < GetDustThreshold(txout, dustRelayFeeIn)); } -bool IsStandard(const CScript &scriptPubKey, TxoutType &whichType) { +bool IsStandard(const CScript &scriptPubKey, + const std::optional<unsigned> &max_datacarrier_bytes, + TxoutType &whichType) { std::vector<std::vector<uint8_t>> vSolutions; whichType = Solver(scriptPubKey, vSolutions); @@ -52,13 +54,8 @@ return false; } } else if (whichType == TxoutType::NULL_DATA) { - if (!fAcceptDatacarrier) { - return false; - } - - unsigned nMaxDatacarrierBytes = - gArgs.GetIntArg("-datacarriersize", MAX_OP_RETURN_RELAY); - if (scriptPubKey.size() > nMaxDatacarrierBytes) { + if (!max_datacarrier_bytes || + scriptPubKey.size() > *max_datacarrier_bytes) { return false; } } @@ -66,8 +63,10 @@ return true; } -bool IsStandardTx(const CTransaction &tx, bool permit_bare_multisig, - const CFeeRate &dust_relay_fee, std::string &reason) { +bool IsStandardTx(const CTransaction &tx, + const std::optional<unsigned> &max_datacarrier_bytes, + bool permit_bare_multisig, const CFeeRate &dust_relay_fee, + std::string &reason) { // Only allow these tx versions, there is no point accepting a tx that // violates the consensus rules if (tx.nVersion > CTransaction::MAX_VERSION || @@ -100,7 +99,8 @@ unsigned int nDataOut = 0; TxoutType whichType; for (const CTxOut &txout : tx.vout) { - if (!::IsStandard(txout.scriptPubKey, whichType)) { + if (!::IsStandard(txout.scriptPubKey, max_datacarrier_bytes, + whichType)) { reason = "scriptpubkey"; return false; } diff --git a/src/script/standard.h b/src/script/standard.h --- a/src/script/standard.h +++ b/src/script/standard.h @@ -35,12 +35,6 @@ */ static const unsigned int MAX_OP_RETURN_RELAY = 223; -/** - * A data carrying output is an unspendable output containing data. The script - * type is designated as TxoutType::NULL_DATA. - */ -extern bool fAcceptDatacarrier; - enum class TxoutType { NONSTANDARD, // 'standard' transaction types: diff --git a/src/script/standard.cpp b/src/script/standard.cpp --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -11,8 +11,6 @@ typedef std::vector<uint8_t> valtype; -bool fAcceptDatacarrier = DEFAULT_ACCEPT_DATACARRIER; - CScriptID::CScriptID(const CScript &in) : BaseHash(Hash160(in)) {} CScriptID::CScriptID(const ScriptHash &in) : BaseHash(static_cast<uint160>(in)) {} diff --git a/src/test/fuzz/key.cpp b/src/test/fuzz/key.cpp --- a/src/test/fuzz/key.cpp +++ b/src/test/fuzz/key.cpp @@ -157,13 +157,13 @@ TxoutType which_type_tx_pubkey; const bool is_standard_tx_pubkey = - IsStandard(tx_pubkey_script, which_type_tx_pubkey); + IsStandard(tx_pubkey_script, std::nullopt, which_type_tx_pubkey); assert(is_standard_tx_pubkey); assert(which_type_tx_pubkey == TxoutType::PUBKEY); TxoutType which_type_tx_multisig; - const bool is_standard_tx_multisig = - IsStandard(tx_multisig_script, which_type_tx_multisig); + const bool is_standard_tx_multisig = IsStandard( + tx_multisig_script, std::nullopt, which_type_tx_multisig); assert(is_standard_tx_multisig); assert(which_type_tx_multisig == TxoutType::MULTISIG); diff --git a/src/test/fuzz/script.cpp b/src/test/fuzz/script.cpp --- a/src/test/fuzz/script.cpp +++ b/src/test/fuzz/script.cpp @@ -72,7 +72,9 @@ (void)IsSolvable(signing_provider, script); TxoutType which_type; - (void)IsStandard(script, which_type); + (void)IsStandard(script, std::nullopt, which_type); + (void)IsStandard(script, fuzzed_data_provider.ConsumeIntegral<unsigned>(), + which_type); (void)RecursiveDynamicUsage(script); diff --git a/src/test/fuzz/transaction.cpp b/src/test/fuzz/transaction.cpp --- a/src/test/fuzz/transaction.cpp +++ b/src/test/fuzz/transaction.cpp @@ -64,10 +64,12 @@ const CFeeRate dust_relay_fee{DUST_RELAY_TX_FEE}; std::string reason; - const bool is_standard_with_permit_bare_multisig = IsStandardTx( - tx, /* permit_bare_multisig= */ true, dust_relay_fee, reason); - const bool is_standard_without_permit_bare_multisig = IsStandardTx( - tx, /* permit_bare_multisig= */ false, dust_relay_fee, reason); + const bool is_standard_with_permit_bare_multisig = + IsStandardTx(tx, std::nullopt, /* permit_bare_multisig= */ true, + dust_relay_fee, reason); + const bool is_standard_without_permit_bare_multisig = + IsStandardTx(tx, std::nullopt, /* permit_bare_multisig= */ false, + dust_relay_fee, reason); if (is_standard_without_permit_bare_multisig) { assert(is_standard_with_permit_bare_multisig); } diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp --- a/src/test/multisig_tests.cpp +++ b/src/test/multisig_tests.cpp @@ -174,30 +174,37 @@ key[i].MakeNewKey(true); } - TxoutType whichType; + const auto is_standard{[](const CScript &spk) { + TxoutType type; + bool res{::IsStandard(spk, std::nullopt, type)}; + if (res) { + BOOST_CHECK_EQUAL(type, TxoutType::MULTISIG); + } + return res; + }}; CScript a_and_b; a_and_b << OP_2 << ToByteVector(key[0].GetPubKey()) << ToByteVector(key[1].GetPubKey()) << OP_2 << OP_CHECKMULTISIG; - BOOST_CHECK(::IsStandard(a_and_b, whichType)); + BOOST_CHECK(is_standard(a_and_b)); CScript a_or_b; a_or_b << OP_1 << ToByteVector(key[0].GetPubKey()) << ToByteVector(key[1].GetPubKey()) << OP_2 << OP_CHECKMULTISIG; - BOOST_CHECK(::IsStandard(a_or_b, whichType)); + BOOST_CHECK(is_standard(a_or_b)); CScript escrow; escrow << OP_2 << ToByteVector(key[0].GetPubKey()) << ToByteVector(key[1].GetPubKey()) << ToByteVector(key[2].GetPubKey()) << OP_3 << OP_CHECKMULTISIG; - BOOST_CHECK(::IsStandard(escrow, whichType)); + BOOST_CHECK(is_standard(escrow)); 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; - BOOST_CHECK(!::IsStandard(one_of_four, whichType)); + BOOST_CHECK(!is_standard(one_of_four)); CScript malformed[6]; malformed[0] << OP_3 << ToByteVector(key[0].GetPubKey()) @@ -218,7 +225,7 @@ << ToByteVector(key[1].GetPubKey()); for (int i = 0; i < 6; i++) { - BOOST_CHECK(!::IsStandard(malformed[i], whichType)); + BOOST_CHECK(!is_standard(malformed[i])); } } diff --git a/src/test/script_p2sh_tests.cpp b/src/test/script_p2sh_tests.cpp --- a/src/test/script_p2sh_tests.cpp +++ b/src/test/script_p2sh_tests.cpp @@ -21,7 +21,7 @@ // Helpers: static bool IsStandardTx(const CTransaction &tx, std::string &reason) { - return IsStandardTx(tx, DEFAULT_PERMIT_BAREMULTISIG, + return IsStandardTx(tx, std::nullopt, DEFAULT_PERMIT_BAREMULTISIG, CFeeRate{DUST_RELAY_TX_FEE}, reason); } diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -605,7 +605,8 @@ t.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey())); std::string reason; - BOOST_CHECK(IsStandardTx(CTransaction{t}, g_bare_multi, g_dust, reason)); + BOOST_CHECK(IsStandardTx(CTransaction{t}, MAX_OP_RETURN_RELAY, g_bare_multi, + g_dust, reason)); // Check dust with default relay fee: Amount nDustThreshold = 3 * 182 * g_dust.GetFeePerK() / 1000; @@ -613,34 +614,41 @@ // dust: t.vout[0].nValue = nDustThreshold - SATOSHI; reason.clear(); - BOOST_CHECK(!IsStandardTx(CTransaction{t}, g_bare_multi, g_dust, reason)); + BOOST_CHECK(!IsStandardTx(CTransaction{t}, MAX_OP_RETURN_RELAY, + g_bare_multi, g_dust, reason)); BOOST_CHECK_EQUAL(reason, "dust"); // not dust: t.vout[0].nValue = nDustThreshold; - BOOST_CHECK(IsStandardTx(CTransaction{t}, g_bare_multi, g_dust, reason)); + BOOST_CHECK(IsStandardTx(CTransaction{t}, MAX_OP_RETURN_RELAY, g_bare_multi, + g_dust, reason)); // Disallowed nVersion t.nVersion = -1; reason.clear(); - BOOST_CHECK(!IsStandardTx(CTransaction{t}, g_bare_multi, g_dust, reason)); + BOOST_CHECK(!IsStandardTx(CTransaction{t}, MAX_OP_RETURN_RELAY, + g_bare_multi, g_dust, reason)); BOOST_CHECK_EQUAL(reason, "version"); t.nVersion = 0; reason.clear(); - BOOST_CHECK(!IsStandardTx(CTransaction{t}, g_bare_multi, g_dust, reason)); + BOOST_CHECK(!IsStandardTx(CTransaction{t}, MAX_OP_RETURN_RELAY, + g_bare_multi, g_dust, reason)); BOOST_CHECK_EQUAL(reason, "version"); t.nVersion = 3; reason.clear(); - BOOST_CHECK(!IsStandardTx(CTransaction{t}, g_bare_multi, g_dust, reason)); + BOOST_CHECK(!IsStandardTx(CTransaction{t}, MAX_OP_RETURN_RELAY, + g_bare_multi, g_dust, reason)); BOOST_CHECK_EQUAL(reason, "version"); // Allowed nVersion t.nVersion = 1; - BOOST_CHECK(IsStandardTx(CTransaction{t}, g_bare_multi, g_dust, reason)); + BOOST_CHECK(IsStandardTx(CTransaction{t}, MAX_OP_RETURN_RELAY, g_bare_multi, + g_dust, reason)); t.nVersion = 2; - BOOST_CHECK(IsStandardTx(CTransaction{t}, g_bare_multi, g_dust, reason)); + BOOST_CHECK(IsStandardTx(CTransaction{t}, MAX_OP_RETURN_RELAY, g_bare_multi, + g_dust, reason)); // Check dust with odd relay fee to verify rounding: // nDustThreshold = 182 * 1234 / 1000 * 3 @@ -648,16 +656,19 @@ // dust: t.vout[0].nValue = (672 - 1) * SATOSHI; reason.clear(); - BOOST_CHECK(!IsStandardTx(CTransaction{t}, g_bare_multi, g_dust, reason)); + BOOST_CHECK(!IsStandardTx(CTransaction{t}, MAX_OP_RETURN_RELAY, + g_bare_multi, g_dust, reason)); BOOST_CHECK_EQUAL(reason, "dust"); // not dust: t.vout[0].nValue = 672 * SATOSHI; - BOOST_CHECK(IsStandardTx(CTransaction{t}, g_bare_multi, g_dust, reason)); + BOOST_CHECK(IsStandardTx(CTransaction{t}, MAX_OP_RETURN_RELAY, g_bare_multi, + g_dust, reason)); g_dust = CFeeRate{DUST_RELAY_TX_FEE}; t.vout[0].scriptPubKey = CScript() << OP_1; reason.clear(); - BOOST_CHECK(!IsStandardTx(CTransaction{t}, g_bare_multi, g_dust, reason)); + BOOST_CHECK(!IsStandardTx(CTransaction{t}, MAX_OP_RETURN_RELAY, + g_bare_multi, g_dust, reason)); BOOST_CHECK_EQUAL(reason, "scriptpubkey"); // MAX_OP_RETURN_RELAY-byte TxoutType::NULL_DATA (standard) @@ -674,7 +685,8 @@ "f5d00d4adf73f2dd112ca75cf19754651909becfbe65aed1" "3afb2ab8"); BOOST_CHECK_EQUAL(MAX_OP_RETURN_RELAY, t.vout[0].scriptPubKey.size()); - BOOST_CHECK(IsStandardTx(CTransaction{t}, g_bare_multi, g_dust, reason)); + BOOST_CHECK(IsStandardTx(CTransaction{t}, MAX_OP_RETURN_RELAY, g_bare_multi, + g_dust, reason)); // MAX_OP_RETURN_RELAY+1-byte TxoutType::NULL_DATA (non-standard) t.vout[0].scriptPubKey = @@ -691,14 +703,14 @@ "3afb2ab800"); BOOST_CHECK_EQUAL(MAX_OP_RETURN_RELAY + 1, t.vout[0].scriptPubKey.size()); reason.clear(); - BOOST_CHECK(!IsStandardTx(CTransaction{t}, g_bare_multi, g_dust, reason)); + BOOST_CHECK(!IsStandardTx(CTransaction{t}, MAX_OP_RETURN_RELAY, + g_bare_multi, g_dust, reason)); BOOST_CHECK_EQUAL(reason, "scriptpubkey"); /** - * Check when a custom value is used for -datacarriersize . + * Check when a custom value is used for max_datacarrier_bytes. */ unsigned newMaxSize = 90; - gArgs.ForceSetArg("-datacarriersize", ToString(newMaxSize)); // Max user provided payload size is standard t.vout[0].scriptPubKey = @@ -708,7 +720,8 @@ "271967f1a67130b7105cd6a828e03909a67962e0ea1f61de" "b649f6bc3f4cef3877696e64657878"); BOOST_CHECK_EQUAL(t.vout[0].scriptPubKey.size(), newMaxSize); - BOOST_CHECK(IsStandardTx(CTransaction{t}, g_bare_multi, g_dust, reason)); + BOOST_CHECK(IsStandardTx(CTransaction{t}, newMaxSize, g_bare_multi, g_dust, + reason)); // Max user provided payload size + 1 is non-standard t.vout[0].scriptPubKey = @@ -718,39 +731,43 @@ "271967f1a67130b7105cd6a828e03909a67962e0ea1f61de" "b649f6bc3f4cef3877696e6465787800"); BOOST_CHECK_EQUAL(t.vout[0].scriptPubKey.size(), newMaxSize + 1); - BOOST_CHECK(!IsStandardTx(CTransaction{t}, g_bare_multi, g_dust, reason)); - - // Clear custom confirguration. - gArgs.ClearForcedArg("-datacarriersize"); + BOOST_CHECK(!IsStandardTx(CTransaction{t}, newMaxSize, g_bare_multi, g_dust, + reason)); // Data payload can be encoded in any way... t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex(""); - BOOST_CHECK(IsStandardTx(CTransaction{t}, g_bare_multi, g_dust, reason)); + BOOST_CHECK(IsStandardTx(CTransaction{t}, MAX_OP_RETURN_RELAY, g_bare_multi, + g_dust, reason)); t.vout[0].scriptPubKey = CScript() << OP_RETURN << ParseHex("00") << ParseHex("01"); - BOOST_CHECK(IsStandardTx(CTransaction{t}, g_bare_multi, g_dust, reason)); + BOOST_CHECK(IsStandardTx(CTransaction{t}, MAX_OP_RETURN_RELAY, g_bare_multi, + g_dust, 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(CTransaction{t}, g_bare_multi, g_dust, reason)); + BOOST_CHECK(IsStandardTx(CTransaction{t}, MAX_OP_RETURN_RELAY, g_bare_multi, + g_dust, reason)); t.vout[0].scriptPubKey = CScript() << OP_RETURN << 0 << ParseHex("01") << 2 << ParseHex("fffffffffffffffffffffffffffffffffffff" "fffffffffffffffffffffffffffffffffff"); - BOOST_CHECK(IsStandardTx(CTransaction{t}, g_bare_multi, g_dust, reason)); + BOOST_CHECK(IsStandardTx(CTransaction{t}, MAX_OP_RETURN_RELAY, g_bare_multi, + g_dust, reason)); // ...so long as it only contains PUSHDATA's t.vout[0].scriptPubKey = CScript() << OP_RETURN << OP_RETURN; reason.clear(); - BOOST_CHECK(!IsStandardTx(CTransaction{t}, g_bare_multi, g_dust, reason)); + BOOST_CHECK(!IsStandardTx(CTransaction{t}, MAX_OP_RETURN_RELAY, + g_bare_multi, g_dust, reason)); BOOST_CHECK_EQUAL(reason, "scriptpubkey"); // TxoutType::NULL_DATA w/o PUSHDATA t.vout.resize(1); t.vout[0].scriptPubKey = CScript() << OP_RETURN; - BOOST_CHECK(IsStandardTx(CTransaction{t}, g_bare_multi, g_dust, reason)); + BOOST_CHECK(IsStandardTx(CTransaction{t}, MAX_OP_RETURN_RELAY, g_bare_multi, + g_dust, reason)); // Only one TxoutType::NULL_DATA permitted in all cases t.vout.resize(2); @@ -763,7 +780,8 @@ << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909" "a67962e0ea1f61deb649f6bc3f4cef38"); reason.clear(); - BOOST_CHECK(!IsStandardTx(CTransaction{t}, g_bare_multi, g_dust, reason)); + BOOST_CHECK(!IsStandardTx(CTransaction{t}, MAX_OP_RETURN_RELAY, + g_bare_multi, g_dust, reason)); BOOST_CHECK_EQUAL(reason, "multi-op-return"); t.vout[0].scriptPubKey = @@ -772,13 +790,15 @@ "a67962e0ea1f61deb649f6bc3f4cef38"); t.vout[1].scriptPubKey = CScript() << OP_RETURN; reason.clear(); - BOOST_CHECK(!IsStandardTx(CTransaction{t}, g_bare_multi, g_dust, reason)); + BOOST_CHECK(!IsStandardTx(CTransaction{t}, MAX_OP_RETURN_RELAY, + g_bare_multi, g_dust, reason)); BOOST_CHECK_EQUAL(reason, "multi-op-return"); t.vout[0].scriptPubKey = CScript() << OP_RETURN; t.vout[1].scriptPubKey = CScript() << OP_RETURN; reason.clear(); - BOOST_CHECK(!IsStandardTx(CTransaction{t}, g_bare_multi, g_dust, reason)); + BOOST_CHECK(!IsStandardTx(CTransaction{t}, MAX_OP_RETURN_RELAY, + g_bare_multi, g_dust, reason)); BOOST_CHECK_EQUAL(reason, "multi-op-return"); // Check large scriptSig (non-standard if size is >1650 bytes) @@ -787,11 +807,13 @@ t.vout[0].scriptPubKey = GetScriptForDestination(PKHash(key.GetPubKey())); // OP_PUSHDATA2 with len (3 bytes) + data (1647 bytes) = 1650 bytes t.vin[0].scriptSig = CScript() << std::vector<uint8_t>(1647, 0); // 1650 - BOOST_CHECK(IsStandardTx(CTransaction{t}, g_bare_multi, g_dust, reason)); + BOOST_CHECK(IsStandardTx(CTransaction{t}, MAX_OP_RETURN_RELAY, g_bare_multi, + g_dust, reason)); t.vin[0].scriptSig = CScript() << std::vector<uint8_t>(1648, 0); // 1651 reason.clear(); - BOOST_CHECK(!IsStandardTx(CTransaction{t}, g_bare_multi, g_dust, reason)); + BOOST_CHECK(!IsStandardTx(CTransaction{t}, MAX_OP_RETURN_RELAY, + g_bare_multi, g_dust, reason)); BOOST_CHECK_EQUAL(reason, "scriptsig-size"); // Check scriptSig format (non-standard if there are any other ops than just @@ -806,7 +828,8 @@ << std::vector<uint8_t>(235, 0) // OP_PUSHDATA2 x [...x bytes...] << std::vector<uint8_t>(1234, 0) << OP_9; - BOOST_CHECK(IsStandardTx(CTransaction{t}, g_bare_multi, g_dust, reason)); + BOOST_CHECK(IsStandardTx(CTransaction{t}, MAX_OP_RETURN_RELAY, g_bare_multi, + g_dust, reason)); const std::vector<uint8_t> non_push_ops = { // arbitrary set of non-push operations @@ -842,14 +865,14 @@ // replace current push-op with each non-push-op for (auto op : non_push_ops) { t.vin[0].scriptSig[index] = op; - BOOST_CHECK( - !IsStandardTx(CTransaction{t}, g_bare_multi, g_dust, reason)); + BOOST_CHECK(!IsStandardTx(CTransaction{t}, MAX_OP_RETURN_RELAY, + g_bare_multi, g_dust, reason)); BOOST_CHECK_EQUAL(reason, "scriptsig-not-pushonly"); } // restore op t.vin[0].scriptSig[index] = orig_op; - BOOST_CHECK( - IsStandardTx(CTransaction{t}, g_bare_multi, g_dust, reason)); + BOOST_CHECK(IsStandardTx(CTransaction{t}, MAX_OP_RETURN_RELAY, + g_bare_multi, g_dust, reason)); } // Check bare multisig (standard if policy flag g_bare_multi is set) @@ -857,11 +880,13 @@ // simple 1-of-1 t.vout[0].scriptPubKey = GetScriptForMultisig(1, {key.GetPubKey()}); t.vin[0].scriptSig = CScript() << std::vector<uint8_t>(65, 0); - BOOST_CHECK(IsStandardTx(CTransaction{t}, g_bare_multi, g_dust, reason)); + BOOST_CHECK(IsStandardTx(CTransaction{t}, MAX_OP_RETURN_RELAY, g_bare_multi, + g_dust, reason)); g_bare_multi = false; reason.clear(); - BOOST_CHECK(!IsStandardTx(CTransaction{t}, g_bare_multi, g_dust, reason)); + BOOST_CHECK(!IsStandardTx(CTransaction{t}, MAX_OP_RETURN_RELAY, + g_bare_multi, g_dust, reason)); BOOST_CHECK_EQUAL(reason, "bare-multisig"); g_bare_multi = DEFAULT_PERMIT_BAREMULTISIG; } diff --git a/src/txmempool.h b/src/txmempool.h --- a/src/txmempool.h +++ b/src/txmempool.h @@ -479,6 +479,7 @@ const std::chrono::seconds m_expiry; const CFeeRate m_min_relay_feerate; const bool m_permit_bare_multisig; + const std::optional<unsigned> m_max_datacarrier_bytes; const bool m_require_standard; /** diff --git a/src/txmempool.cpp b/src/txmempool.cpp --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -162,6 +162,7 @@ : m_check_ratio(opts.check_ratio), m_max_size_bytes{opts.max_size_bytes}, m_expiry{opts.expiry}, m_min_relay_feerate{opts.min_relay_feerate}, m_permit_bare_multisig{opts.permit_bare_multisig}, + m_max_datacarrier_bytes{opts.max_datacarrier_bytes}, m_require_standard{opts.require_standard} { // lock free clear _clear(); diff --git a/src/util/system.cpp b/src/util/system.cpp --- a/src/util/system.cpp +++ b/src/util/system.cpp @@ -7,6 +7,7 @@ #include <chainparamsbase.h> #include <fs.h> +#include <script/standard.h> #include <sync.h> #include <util/getuniquepath.h> #include <util/strencodings.h> diff --git a/src/validation.cpp b/src/validation.cpp --- a/src/validation.cpp +++ b/src/validation.cpp @@ -519,8 +519,8 @@ // Rather not work on nonstandard transactions (unless -testnet) std::string reason; if (m_pool.m_require_standard && - !IsStandardTx(tx, m_pool.m_permit_bare_multisig, ::dustRelayFee, - reason)) { + !IsStandardTx(tx, m_pool.m_max_datacarrier_bytes, + m_pool.m_permit_bare_multisig, ::dustRelayFee, reason)) { return state.Invalid(TxValidationResult::TX_NOT_STANDARD, reason); }