Remove arbitrary limits on OP_Return (datacarrier) outputs

Also removes the code to enforce those limits, including the
`-datacarrier` and `-datacarriersize` config options.

These limits are easily bypassed by both direct submission to miner
mempools (e.g. MARA Slipstream), and forks of Bitcoin Core that do not
enforce them (e.g. Libre Relay). Secondly, protocols are bypassing them
by simply publishing data in other ways, such as unspendable outputs and
scriptsigs.

The *form* of datacarrier outputs remains standardized: a single
OP_Return followed by zero or more data pushes; non-data opcodes remain
non-standard.
This commit is contained in:
Peter Todd 2025-04-27 20:41:59 +00:00
parent d62c2d82e1
commit cd7872ca54
No known key found for this signature in database
GPG key ID: 2E5CB5D535512F37
29 changed files with 42 additions and 200 deletions

View file

@ -635,12 +635,6 @@ void SetupServerArgs(ArgsManager& argsman, bool can_listen_ipc)
argsman.AddArg("-dustrelayfee=<amt>", strprintf("Fee rate (in %s/kvB) used to define dust, the value of an output such that it will cost more than its value in fees at this fee rate to spend it. (default: %s)", CURRENCY_UNIT, FormatMoney(DUST_RELAY_TX_FEE)), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::NODE_RELAY); argsman.AddArg("-dustrelayfee=<amt>", strprintf("Fee rate (in %s/kvB) used to define dust, the value of an output such that it will cost more than its value in fees at this fee rate to spend it. (default: %s)", CURRENCY_UNIT, FormatMoney(DUST_RELAY_TX_FEE)), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::NODE_RELAY);
argsman.AddArg("-acceptstalefeeestimates", strprintf("Read fee estimates even if they are stale (%sdefault: %u) fee estimates are considered stale if they are %s hours old", "regtest only; ", DEFAULT_ACCEPT_STALE_FEE_ESTIMATES, Ticks<std::chrono::hours>(MAX_FILE_AGE)), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); argsman.AddArg("-acceptstalefeeestimates", strprintf("Read fee estimates even if they are stale (%sdefault: %u) fee estimates are considered stale if they are %s hours old", "regtest only; ", DEFAULT_ACCEPT_STALE_FEE_ESTIMATES, Ticks<std::chrono::hours>(MAX_FILE_AGE)), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
argsman.AddArg("-bytespersigop", strprintf("Equivalent bytes per sigop in transactions for relay and mining (default: %u)", DEFAULT_BYTES_PER_SIGOP), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY); argsman.AddArg("-bytespersigop", strprintf("Equivalent bytes per sigop in transactions for relay and mining (default: %u)", DEFAULT_BYTES_PER_SIGOP), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
argsman.AddArg("-datacarrier", strprintf("Relay and mine data carrier transactions (default: %u)", DEFAULT_ACCEPT_DATACARRIER), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
argsman.AddArg("-datacarriersize",
strprintf("Relay and mine transactions whose data-carrying raw scriptPubKey "
"is of this size or less (default: %u)",
MAX_OP_RETURN_RELAY),
ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
argsman.AddArg("-permitbaremultisig", strprintf("Relay transactions creating non-P2SH multisig outputs (default: %u)", DEFAULT_PERMIT_BAREMULTISIG), ArgsManager::ALLOW_ANY, argsman.AddArg("-permitbaremultisig", strprintf("Relay transactions creating non-P2SH multisig outputs (default: %u)", DEFAULT_PERMIT_BAREMULTISIG), ArgsManager::ALLOW_ANY,
OptionsCategory::NODE_RELAY); OptionsCategory::NODE_RELAY);
argsman.AddArg("-minrelaytxfee=<amt>", strprintf("Fees (in %s/kvB) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s)", argsman.AddArg("-minrelaytxfee=<amt>", strprintf("Fees (in %s/kvB) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s)",

View file

@ -43,14 +43,6 @@ struct MemPoolOptions {
/** A fee rate smaller than this is considered zero fee (for relaying, mining and transaction creation) */ /** A fee rate smaller than this is considered zero fee (for relaying, mining and transaction creation) */
CFeeRate min_relay_feerate{DEFAULT_MIN_RELAY_TX_FEE}; CFeeRate min_relay_feerate{DEFAULT_MIN_RELAY_TX_FEE};
CFeeRate dust_relay_feerate{DUST_RELAY_TX_FEE}; CFeeRate dust_relay_feerate{DUST_RELAY_TX_FEE};
/**
* 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 permit_bare_multisig{DEFAULT_PERMIT_BAREMULTISIG};
bool require_standard{true}; bool require_standard{true};
bool persist_v1_dat{DEFAULT_PERSIST_V1_DAT}; bool persist_v1_dat{DEFAULT_PERSIST_V1_DAT};

View file

@ -81,12 +81,6 @@ util::Result<void> ApplyArgsManOptions(const ArgsManager& argsman, const CChainP
mempool_opts.permit_bare_multisig = argsman.GetBoolArg("-permitbaremultisig", DEFAULT_PERMIT_BAREMULTISIG); mempool_opts.permit_bare_multisig = argsman.GetBoolArg("-permitbaremultisig", DEFAULT_PERMIT_BAREMULTISIG);
if (argsman.GetBoolArg("-datacarrier", DEFAULT_ACCEPT_DATACARRIER)) {
mempool_opts.max_datacarrier_bytes = argsman.GetIntArg("-datacarriersize", MAX_OP_RETURN_RELAY);
} else {
mempool_opts.max_datacarrier_bytes = std::nullopt;
}
mempool_opts.require_standard = !argsman.GetBoolArg("-acceptnonstdtxn", DEFAULT_ACCEPT_NON_STD_TXN); mempool_opts.require_standard = !argsman.GetBoolArg("-acceptnonstdtxn", DEFAULT_ACCEPT_NON_STD_TXN);
if (!chainparams.IsTestChain() && !mempool_opts.require_standard) { if (!chainparams.IsTestChain() && !mempool_opts.require_standard) {
return util::Error{Untranslated(strprintf("acceptnonstdtxn is not currently supported for %s chain", chainparams.GetChainTypeString()))}; return util::Error{Untranslated(strprintf("acceptnonstdtxn is not currently supported for %s chain", chainparams.GetChainTypeString()))};

View file

@ -76,7 +76,7 @@ std::vector<uint32_t> GetDust(const CTransaction& tx, CFeeRate dust_relay_rate)
return dust_outputs; return dust_outputs;
} }
bool IsStandard(const CScript& scriptPubKey, const std::optional<unsigned>& max_datacarrier_bytes, TxoutType& whichType) bool IsStandard(const CScript& scriptPubKey, TxoutType& whichType)
{ {
std::vector<std::vector<unsigned char> > vSolutions; std::vector<std::vector<unsigned char> > vSolutions;
whichType = Solver(scriptPubKey, vSolutions); whichType = Solver(scriptPubKey, vSolutions);
@ -91,16 +91,12 @@ bool IsStandard(const CScript& scriptPubKey, const std::optional<unsigned>& max_
return false; return false;
if (m < 1 || m > n) if (m < 1 || m > n)
return false; return false;
} else if (whichType == TxoutType::NULL_DATA) {
if (!max_datacarrier_bytes || scriptPubKey.size() > *max_datacarrier_bytes) {
return false;
}
} }
return true; return true;
} }
bool IsStandardTx(const CTransaction& tx, const std::optional<unsigned>& max_datacarrier_bytes, bool permit_bare_multisig, const CFeeRate& dust_relay_fee, std::string& reason) bool IsStandardTx(const CTransaction& tx, bool permit_bare_multisig, const CFeeRate& dust_relay_fee, std::string& reason)
{ {
if (tx.version > TX_MAX_STANDARD_VERSION || tx.version < 1) { if (tx.version > TX_MAX_STANDARD_VERSION || tx.version < 1) {
reason = "version"; reason = "version";
@ -137,17 +133,14 @@ bool IsStandardTx(const CTransaction& tx, const std::optional<unsigned>& max_dat
} }
} }
unsigned int nDataOut = 0;
TxoutType whichType; TxoutType whichType;
for (const CTxOut& txout : tx.vout) { for (const CTxOut& txout : tx.vout) {
if (!::IsStandard(txout.scriptPubKey, max_datacarrier_bytes, whichType)) { if (!::IsStandard(txout.scriptPubKey, whichType)) {
reason = "scriptpubkey"; reason = "scriptpubkey";
return false; return false;
} }
if (whichType == TxoutType::NULL_DATA) if ((whichType == TxoutType::MULTISIG) && (!permit_bare_multisig)) {
nDataOut++;
else if ((whichType == TxoutType::MULTISIG) && (!permit_bare_multisig)) {
reason = "bare-multisig"; reason = "bare-multisig";
return false; return false;
} }
@ -159,12 +152,6 @@ bool IsStandardTx(const CTransaction& tx, const std::optional<unsigned>& max_dat
return false; return false;
} }
// only one OP_RETURN txout is permitted
if (nDataOut > 1) {
reason = "multi-op-return";
return false;
}
return true; return true;
} }

View file

@ -70,13 +70,6 @@ static constexpr unsigned int DEFAULT_ANCESTOR_SIZE_LIMIT_KVB{101};
static constexpr unsigned int DEFAULT_DESCENDANT_LIMIT{25}; static constexpr unsigned int DEFAULT_DESCENDANT_LIMIT{25};
/** Default for -limitdescendantsize, maximum kilobytes of in-mempool descendants */ /** Default for -limitdescendantsize, maximum kilobytes of in-mempool descendants */
static constexpr unsigned int DEFAULT_DESCENDANT_SIZE_LIMIT_KVB{101}; static constexpr unsigned int DEFAULT_DESCENDANT_SIZE_LIMIT_KVB{101};
/** Default for -datacarrier */
static const bool DEFAULT_ACCEPT_DATACARRIER = true;
/**
* Default setting for -datacarriersize. 80 bytes of data, +1 for OP_RETURN,
* +2 for the pushdata opcodes.
*/
static const unsigned int MAX_OP_RETURN_RELAY = 83;
/** /**
* An extra transaction can be added to a package, as long as it only has one * An extra transaction can be added to a package, as long as it only has one
* ancestor and is no larger than this. Not really any reason to make this * ancestor and is no larger than this. Not really any reason to make this
@ -136,7 +129,7 @@ CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFee);
bool IsDust(const CTxOut& txout, const CFeeRate& dustRelayFee); bool IsDust(const CTxOut& txout, const CFeeRate& dustRelayFee);
bool IsStandard(const CScript& scriptPubKey, const std::optional<unsigned>& max_datacarrier_bytes, TxoutType& whichType); bool IsStandard(const CScript& scriptPubKey, TxoutType& whichType);
/** Get the vout index numbers of all dust outputs */ /** Get the vout index numbers of all dust outputs */
std::vector<uint32_t> GetDust(const CTransaction& tx, CFeeRate dust_relay_rate); std::vector<uint32_t> GetDust(const CTransaction& tx, CFeeRate dust_relay_rate);
@ -150,7 +143,7 @@ static constexpr decltype(CTransaction::version) TX_MAX_STANDARD_VERSION{3};
* Check for standard transaction types * Check for standard transaction types
* @return True if all outputs (scriptPubKeys) use only standard transaction forms * @return True if all outputs (scriptPubKeys) use only standard transaction forms
*/ */
bool IsStandardTx(const CTransaction& tx, const std::optional<unsigned>& max_datacarrier_bytes, bool permit_bare_multisig, const CFeeRate& dust_relay_fee, std::string& reason); bool IsStandardTx(const CTransaction& tx, bool permit_bare_multisig, const CFeeRate& dust_relay_fee, std::string& reason);
/** /**
* Check for standard transaction types * Check for standard transaction types
* @param[in] mapInputs Map of previous transactions that have outputs we're spending * @param[in] mapInputs Map of previous transactions that have outputs we're spending

View file

@ -151,12 +151,12 @@ FUZZ_TARGET(key, .init = initialize_key)
assert(fillable_signing_provider_pub.HaveKey(pubkey.GetID())); assert(fillable_signing_provider_pub.HaveKey(pubkey.GetID()));
TxoutType which_type_tx_pubkey; TxoutType which_type_tx_pubkey;
const bool is_standard_tx_pubkey = IsStandard(tx_pubkey_script, std::nullopt, which_type_tx_pubkey); const bool is_standard_tx_pubkey = IsStandard(tx_pubkey_script, which_type_tx_pubkey);
assert(is_standard_tx_pubkey); assert(is_standard_tx_pubkey);
assert(which_type_tx_pubkey == TxoutType::PUBKEY); assert(which_type_tx_pubkey == TxoutType::PUBKEY);
TxoutType which_type_tx_multisig; TxoutType which_type_tx_multisig;
const bool is_standard_tx_multisig = IsStandard(tx_multisig_script, std::nullopt, which_type_tx_multisig); const bool is_standard_tx_multisig = IsStandard(tx_multisig_script, which_type_tx_multisig);
assert(is_standard_tx_multisig); assert(is_standard_tx_multisig);
assert(which_type_tx_multisig == TxoutType::MULTISIG); assert(which_type_tx_multisig == TxoutType::MULTISIG);

View file

@ -53,7 +53,7 @@ FUZZ_TARGET(script, .init = initialize_script)
} }
TxoutType which_type; TxoutType which_type;
bool is_standard_ret = IsStandard(script, std::nullopt, which_type); bool is_standard_ret = IsStandard(script, which_type);
if (!is_standard_ret) { if (!is_standard_ret) {
assert(which_type == TxoutType::NONSTANDARD || assert(which_type == TxoutType::NONSTANDARD ||
which_type == TxoutType::NULL_DATA || which_type == TxoutType::NULL_DATA ||

View file

@ -61,8 +61,8 @@ FUZZ_TARGET(transaction, .init = initialize_transaction)
const CFeeRate dust_relay_fee{DUST_RELAY_TX_FEE}; const CFeeRate dust_relay_fee{DUST_RELAY_TX_FEE};
std::string reason; std::string 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_with_permit_bare_multisig = IsStandardTx(tx, /* 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); const bool is_standard_without_permit_bare_multisig = IsStandardTx(tx, /* permit_bare_multisig= */ false, dust_relay_fee, reason);
if (is_standard_without_permit_bare_multisig) { if (is_standard_without_permit_bare_multisig) {
assert(is_standard_with_permit_bare_multisig); assert(is_standard_with_permit_bare_multisig);
} }

View file

@ -144,7 +144,7 @@ BOOST_AUTO_TEST_CASE(multisig_IsStandard)
const auto is_standard{[](const CScript& spk) { const auto is_standard{[](const CScript& spk) {
TxoutType type; TxoutType type;
bool res{::IsStandard(spk, std::nullopt, type)}; bool res{::IsStandard(spk, type)};
if (res) { if (res) {
BOOST_CHECK_EQUAL(type, TxoutType::MULTISIG); BOOST_CHECK_EQUAL(type, TxoutType::MULTISIG);
} }

View file

@ -21,13 +21,13 @@
// Helpers: // Helpers:
static bool IsStandardTx(const CTransaction& tx, bool permit_bare_multisig, std::string& reason) static bool IsStandardTx(const CTransaction& tx, bool permit_bare_multisig, std::string& reason)
{ {
return IsStandardTx(tx, std::nullopt, permit_bare_multisig, CFeeRate{DUST_RELAY_TX_FEE}, reason); return IsStandardTx(tx, permit_bare_multisig, CFeeRate{DUST_RELAY_TX_FEE}, reason);
} }
static bool IsStandardTx(const CTransaction& tx, std::string& reason) static bool IsStandardTx(const CTransaction& tx, std::string& reason)
{ {
return IsStandardTx(tx, std::nullopt, /*permit_bare_multisig=*/true, CFeeRate{DUST_RELAY_TX_FEE}, reason) && return IsStandardTx(tx, /*permit_bare_multisig=*/true, CFeeRate{DUST_RELAY_TX_FEE}, reason) &&
IsStandardTx(tx, std::nullopt, /*permit_bare_multisig=*/false, CFeeRate{DUST_RELAY_TX_FEE}, reason); IsStandardTx(tx, /*permit_bare_multisig=*/false, CFeeRate{DUST_RELAY_TX_FEE}, reason);
} }
static std::vector<unsigned char> Serialize(const CScript& s) static std::vector<unsigned char> Serialize(const CScript& s)

View file

@ -799,12 +799,12 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
constexpr auto CheckIsStandard = [](const auto& t) { constexpr auto CheckIsStandard = [](const auto& t) {
std::string reason; std::string reason;
BOOST_CHECK(IsStandardTx(CTransaction{t}, MAX_OP_RETURN_RELAY, g_bare_multi, g_dust, reason)); BOOST_CHECK(IsStandardTx(CTransaction{t}, g_bare_multi, g_dust, reason));
BOOST_CHECK(reason.empty()); BOOST_CHECK(reason.empty());
}; };
constexpr auto CheckIsNotStandard = [](const auto& t, const std::string& reason_in) { constexpr auto CheckIsNotStandard = [](const auto& t, const std::string& reason_in) {
std::string reason; std::string reason;
BOOST_CHECK(!IsStandardTx(CTransaction{t}, MAX_OP_RETURN_RELAY, g_bare_multi, g_dust, reason)); BOOST_CHECK(!IsStandardTx(CTransaction{t}, g_bare_multi, g_dust, reason));
BOOST_CHECK_EQUAL(reason_in, reason); BOOST_CHECK_EQUAL(reason_in, reason);
}; };
@ -858,15 +858,10 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
t.vout[0].scriptPubKey = CScript() << OP_1; t.vout[0].scriptPubKey = CScript() << OP_1;
CheckIsNotStandard(t, "scriptpubkey"); CheckIsNotStandard(t, "scriptpubkey");
// MAX_OP_RETURN_RELAY-byte TxoutType::NULL_DATA (standard) // TxoutType::NULL_DATA
t.vout[0].scriptPubKey = CScript() << OP_RETURN << "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3804678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38"_hex;
BOOST_CHECK_EQUAL(MAX_OP_RETURN_RELAY, t.vout[0].scriptPubKey.size());
CheckIsStandard(t);
// MAX_OP_RETURN_RELAY+1-byte TxoutType::NULL_DATA (non-standard)
t.vout[0].scriptPubKey = CScript() << OP_RETURN << "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3804678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3800"_hex; t.vout[0].scriptPubKey = CScript() << OP_RETURN << "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3804678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef3800"_hex;
BOOST_CHECK_EQUAL(MAX_OP_RETURN_RELAY + 1, t.vout[0].scriptPubKey.size()); BOOST_CHECK_EQUAL(84, t.vout[0].scriptPubKey.size());
CheckIsNotStandard(t, "scriptpubkey"); CheckIsStandard(t);
// Data payload can be encoded in any way... // Data payload can be encoded in any way...
t.vout[0].scriptPubKey = CScript() << OP_RETURN << ""_hex; t.vout[0].scriptPubKey = CScript() << OP_RETURN << ""_hex;
@ -888,21 +883,21 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
t.vout[0].scriptPubKey = CScript() << OP_RETURN; t.vout[0].scriptPubKey = CScript() << OP_RETURN;
CheckIsStandard(t); CheckIsStandard(t);
// Only one TxoutType::NULL_DATA permitted in all cases // Multiple TxoutType::NULL_DATA outputs are permitted
t.vout.resize(2); t.vout.resize(2);
t.vout[0].scriptPubKey = CScript() << OP_RETURN << "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38"_hex; t.vout[0].scriptPubKey = CScript() << OP_RETURN << "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38"_hex;
t.vout[0].nValue = 0; t.vout[0].nValue = 0;
t.vout[1].scriptPubKey = CScript() << OP_RETURN << "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38"_hex; t.vout[1].scriptPubKey = CScript() << OP_RETURN << "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38"_hex;
t.vout[1].nValue = 0; t.vout[1].nValue = 0;
CheckIsNotStandard(t, "multi-op-return"); CheckIsStandard(t);
t.vout[0].scriptPubKey = CScript() << OP_RETURN << "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38"_hex; t.vout[0].scriptPubKey = CScript() << OP_RETURN << "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38"_hex;
t.vout[1].scriptPubKey = CScript() << OP_RETURN; t.vout[1].scriptPubKey = CScript() << OP_RETURN;
CheckIsNotStandard(t, "multi-op-return"); CheckIsStandard(t);
t.vout[0].scriptPubKey = CScript() << OP_RETURN; t.vout[0].scriptPubKey = CScript() << OP_RETURN;
t.vout[1].scriptPubKey = CScript() << OP_RETURN; t.vout[1].scriptPubKey = CScript() << OP_RETURN;
CheckIsNotStandard(t, "multi-op-return"); CheckIsStandard(t);
// Check large scriptSig (non-standard if size is >1650 bytes) // Check large scriptSig (non-standard if size is >1650 bytes)
t.vout.resize(1); t.vout.resize(1);

View file

@ -790,7 +790,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
// Rather not work on nonstandard transactions (unless -testnet/-regtest) // Rather not work on nonstandard transactions (unless -testnet/-regtest)
std::string reason; std::string reason;
if (m_pool.m_opts.require_standard && !IsStandardTx(tx, m_pool.m_opts.max_datacarrier_bytes, m_pool.m_opts.permit_bare_multisig, m_pool.m_opts.dust_relay_feerate, reason)) { if (m_pool.m_opts.require_standard && !IsStandardTx(tx, m_pool.m_opts.permit_bare_multisig, m_pool.m_opts.dust_relay_feerate, reason)) {
return state.Invalid(TxValidationResult::TX_NOT_STANDARD, reason); return state.Invalid(TxValidationResult::TX_NOT_STANDARD, reason);
} }

View file

@ -23,7 +23,6 @@ class BlocksXORTest(BitcoinTestFramework):
self.extra_args = [[ self.extra_args = [[
'-blocksxor=1', '-blocksxor=1',
'-fastprune=1', # use smaller block files '-fastprune=1', # use smaller block files
'-datacarriersize=100000', # needed to pad transaction with MiniWallet
]] ]]
def run_test(self): def run_test(self):

View file

@ -51,7 +51,6 @@ class MaxUploadTest(BitcoinTestFramework):
self.num_nodes = 1 self.num_nodes = 1
self.extra_args = [[ self.extra_args = [[
f"-maxuploadtarget={UPLOAD_TARGET_MB}M", f"-maxuploadtarget={UPLOAD_TARGET_MB}M",
"-datacarriersize=100000",
]] ]]
self.supports_cli = False self.supports_cli = False

View file

@ -326,13 +326,6 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'dust'}], result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'dust'}],
rawtxs=[tx.serialize().hex()], rawtxs=[tx.serialize().hex()],
) )
tx = tx_from_hex(raw_tx_reference)
tx.vout[0].scriptPubKey = CScript([OP_RETURN, b'\xff'])
tx.vout = [tx.vout[0]] * 2
self.check_mempool_result(
result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': 'multi-op-return'}],
rawtxs=[tx.serialize().hex()],
)
self.log.info('A timelocked transaction') self.log.info('A timelocked transaction')
tx = tx_from_hex(raw_tx_reference) tx = tx_from_hex(raw_tx_reference)

View file

@ -1,91 +0,0 @@
#!/usr/bin/env python3
# Copyright (c) 2020-2022 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test datacarrier functionality"""
from test_framework.messages import (
CTxOut,
MAX_OP_RETURN_RELAY,
)
from test_framework.script import (
CScript,
OP_RETURN,
)
from test_framework.test_framework import BitcoinTestFramework
from test_framework.test_node import TestNode
from test_framework.util import assert_raises_rpc_error
from test_framework.wallet import MiniWallet
from random import randbytes
class DataCarrierTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 4
self.extra_args = [
[],
["-datacarrier=0"],
["-datacarrier=1", f"-datacarriersize={MAX_OP_RETURN_RELAY - 1}"],
["-datacarrier=1", "-datacarriersize=2"],
]
def test_null_data_transaction(self, node: TestNode, data, success: bool) -> None:
tx = self.wallet.create_self_transfer(fee_rate=0)["tx"]
data = [] if data is None else [data]
tx.vout.append(CTxOut(nValue=0, scriptPubKey=CScript([OP_RETURN] + data)))
tx.vout[0].nValue -= tx.get_vsize() # simply pay 1sat/vbyte fee
tx_hex = tx.serialize().hex()
if success:
self.wallet.sendrawtransaction(from_node=node, tx_hex=tx_hex)
assert tx.rehash() in node.getrawmempool(True), f'{tx_hex} not in mempool'
else:
assert_raises_rpc_error(-26, "scriptpubkey", self.wallet.sendrawtransaction, from_node=node, tx_hex=tx_hex)
def run_test(self):
self.wallet = MiniWallet(self.nodes[0])
# By default, only 80 bytes are used for data (+1 for OP_RETURN, +2 for the pushdata opcodes).
default_size_data = randbytes(MAX_OP_RETURN_RELAY - 3)
too_long_data = randbytes(MAX_OP_RETURN_RELAY - 2)
small_data = randbytes(MAX_OP_RETURN_RELAY - 4)
one_byte = randbytes(1)
zero_bytes = randbytes(0)
self.log.info("Testing null data transaction with default -datacarrier and -datacarriersize values.")
self.test_null_data_transaction(node=self.nodes[0], data=default_size_data, success=True)
self.log.info("Testing a null data transaction larger than allowed by the default -datacarriersize value.")
self.test_null_data_transaction(node=self.nodes[0], data=too_long_data, success=False)
self.log.info("Testing a null data transaction with -datacarrier=false.")
self.test_null_data_transaction(node=self.nodes[1], data=default_size_data, success=False)
self.log.info("Testing a null data transaction with a size larger than accepted by -datacarriersize.")
self.test_null_data_transaction(node=self.nodes[2], data=default_size_data, success=False)
self.log.info("Testing a null data transaction with a size smaller than accepted by -datacarriersize.")
self.test_null_data_transaction(node=self.nodes[2], data=small_data, success=True)
self.log.info("Testing a null data transaction with no data.")
self.test_null_data_transaction(node=self.nodes[0], data=None, success=True)
self.test_null_data_transaction(node=self.nodes[1], data=None, success=False)
self.test_null_data_transaction(node=self.nodes[2], data=None, success=True)
self.test_null_data_transaction(node=self.nodes[3], data=None, success=True)
self.log.info("Testing a null data transaction with zero bytes of data.")
self.test_null_data_transaction(node=self.nodes[0], data=zero_bytes, success=True)
self.test_null_data_transaction(node=self.nodes[1], data=zero_bytes, success=False)
self.test_null_data_transaction(node=self.nodes[2], data=zero_bytes, success=True)
self.test_null_data_transaction(node=self.nodes[3], data=zero_bytes, success=True)
self.log.info("Testing a null data transaction with one byte of data.")
self.test_null_data_transaction(node=self.nodes[0], data=one_byte, success=True)
self.test_null_data_transaction(node=self.nodes[1], data=one_byte, success=False)
self.test_null_data_transaction(node=self.nodes[2], data=one_byte, success=True)
self.test_null_data_transaction(node=self.nodes[3], data=one_byte, success=False)
if __name__ == '__main__':
DataCarrierTest(__file__).main()

View file

@ -29,7 +29,6 @@ class MempoolLimitTest(BitcoinTestFramework):
self.setup_clean_chain = True self.setup_clean_chain = True
self.num_nodes = 1 self.num_nodes = 1
self.extra_args = [[ self.extra_args = [[
"-datacarriersize=100000",
"-maxmempool=5", "-maxmempool=5",
]] ]]
self.supports_cli = False self.supports_cli = False

View file

@ -52,8 +52,7 @@ class MempoolPackageLimitsTest(BitcoinTestFramework):
self.test_anc_count_limits_2() self.test_anc_count_limits_2()
self.test_anc_count_limits_bushy() self.test_anc_count_limits_bushy()
# The node will accept (nonstandard) extra large OP_RETURN outputs self.restart_node(0)
self.restart_node(0, extra_args=["-datacarriersize=100000"])
self.test_anc_size_limits() self.test_anc_size_limits()
self.test_desc_size_limits() self.test_desc_size_limits()

View file

@ -33,7 +33,6 @@ class PackageRBFTest(BitcoinTestFramework):
self.setup_clean_chain = True self.setup_clean_chain = True
# Required for fill_mempool() # Required for fill_mempool()
self.extra_args = [[ self.extra_args = [[
"-datacarriersize=100000",
"-maxmempool=5", "-maxmempool=5",
]] * self.num_nodes ]] * self.num_nodes

View file

@ -44,8 +44,6 @@ MAX_PUBKEYS_PER_MULTISIG = 20
class BytesPerSigOpTest(BitcoinTestFramework): class BytesPerSigOpTest(BitcoinTestFramework):
def set_test_params(self): def set_test_params(self):
self.num_nodes = 1 self.num_nodes = 1
# allow large datacarrier output to pad transactions
self.extra_args = [['-datacarriersize=100000']]
def create_p2wsh_spending_tx(self, witness_script, output_script): def create_p2wsh_spending_tx(self, witness_script, output_script):
"""Create a 1-input-1-output P2WSH spending transaction with only the """Create a 1-input-1-output P2WSH spending transaction with only the
@ -139,7 +137,7 @@ class BytesPerSigOpTest(BitcoinTestFramework):
self.log.info("Test a overly-large sigops-vbyte hits package limits") self.log.info("Test a overly-large sigops-vbyte hits package limits")
# Make a 2-transaction package which fails vbyte checks even though # Make a 2-transaction package which fails vbyte checks even though
# separately they would work. # separately they would work.
self.restart_node(0, extra_args=["-bytespersigop=5000","-permitbaremultisig=1"] + self.extra_args[0]) self.restart_node(0, extra_args=["-bytespersigop=5000","-permitbaremultisig=1"])
def create_bare_multisig_tx(utxo_to_spend=None): def create_bare_multisig_tx(utxo_to_spend=None):
_, pubkey = generate_keypair() _, pubkey = generate_keypair()
@ -185,7 +183,7 @@ class BytesPerSigOpTest(BitcoinTestFramework):
else: else:
bytespersigop_parameter = f"-bytespersigop={bytes_per_sigop}" bytespersigop_parameter = f"-bytespersigop={bytes_per_sigop}"
self.log.info(f"Test sigops limit setting {bytespersigop_parameter}...") self.log.info(f"Test sigops limit setting {bytespersigop_parameter}...")
self.restart_node(0, extra_args=[bytespersigop_parameter] + self.extra_args[0]) self.restart_node(0, extra_args=[bytespersigop_parameter])
for num_sigops in (69, 101, 142, 183, 222): for num_sigops in (69, 101, 142, 183, 222):
self.test_sigops_limit(bytes_per_sigop, num_sigops) self.test_sigops_limit(bytes_per_sigop, num_sigops)

View file

@ -49,7 +49,7 @@ class MempoolTRUC(BitcoinTestFramework):
assert_equal(len(txids), len(mempool_contents)) assert_equal(len(txids), len(mempool_contents))
assert all([txid in txids for txid in mempool_contents]) assert all([txid in txids for txid in mempool_contents])
@cleanup(extra_args=["-datacarriersize=20000"]) @cleanup()
def test_truc_max_vsize(self): def test_truc_max_vsize(self):
node = self.nodes[0] node = self.nodes[0]
self.log.info("Test TRUC-specific maximum transaction vsize") self.log.info("Test TRUC-specific maximum transaction vsize")
@ -63,7 +63,7 @@ class MempoolTRUC(BitcoinTestFramework):
tx_v2_heavy = self.wallet.send_self_transfer(from_node=node, target_vsize=TRUC_MAX_VSIZE + 1, version=2) tx_v2_heavy = self.wallet.send_self_transfer(from_node=node, target_vsize=TRUC_MAX_VSIZE + 1, version=2)
self.check_mempool([tx_v2_heavy["txid"]]) self.check_mempool([tx_v2_heavy["txid"]])
@cleanup(extra_args=["-datacarriersize=1000"]) @cleanup()
def test_truc_acceptance(self): def test_truc_acceptance(self):
node = self.nodes[0] node = self.nodes[0]
self.log.info("Test a child of a TRUC transaction cannot be more than 1000vB") self.log.info("Test a child of a TRUC transaction cannot be more than 1000vB")
@ -160,7 +160,7 @@ class MempoolTRUC(BitcoinTestFramework):
self.check_mempool([tx_v3_bip125_rbf_v2["txid"], tx_v3_parent["txid"], tx_v3_child["txid"]]) self.check_mempool([tx_v3_bip125_rbf_v2["txid"], tx_v3_parent["txid"], tx_v3_child["txid"]])
@cleanup(extra_args=["-datacarriersize=40000"]) @cleanup()
def test_truc_reorg(self): def test_truc_reorg(self):
node = self.nodes[0] node = self.nodes[0]
self.log.info("Test that, during a reorg, TRUC rules are not enforced") self.log.info("Test that, during a reorg, TRUC rules are not enforced")
@ -182,7 +182,7 @@ class MempoolTRUC(BitcoinTestFramework):
node.reconsiderblock(block[0]) node.reconsiderblock(block[0])
@cleanup(extra_args=["-limitdescendantsize=10", "-datacarriersize=40000"]) @cleanup(extra_args=["-limitdescendantsize=10"])
def test_nondefault_package_limits(self): def test_nondefault_package_limits(self):
""" """
Max standard tx size + TRUC rules imply the ancestor/descendant rules (at their default Max standard tx size + TRUC rules imply the ancestor/descendant rules (at their default
@ -215,7 +215,7 @@ class MempoolTRUC(BitcoinTestFramework):
self.generate(node, 1) self.generate(node, 1)
self.log.info("Test that a decreased limitancestorsize also applies to v3 parent") self.log.info("Test that a decreased limitancestorsize also applies to v3 parent")
self.restart_node(0, extra_args=["-limitancestorsize=10", "-datacarriersize=40000"]) self.restart_node(0, extra_args=["-limitancestorsize=10"])
tx_v3_parent_large2 = self.wallet.send_self_transfer( tx_v3_parent_large2 = self.wallet.send_self_transfer(
from_node=node, from_node=node,
target_vsize=parent_target_vsize, target_vsize=parent_target_vsize,
@ -235,7 +235,7 @@ class MempoolTRUC(BitcoinTestFramework):
assert_raises_rpc_error(-26, "too-long-mempool-chain, exceeds ancestor size limit", node.sendrawtransaction, tx_v3_child_large2["hex"]) assert_raises_rpc_error(-26, "too-long-mempool-chain, exceeds ancestor size limit", node.sendrawtransaction, tx_v3_child_large2["hex"])
self.check_mempool([tx_v3_parent_large2["txid"]]) self.check_mempool([tx_v3_parent_large2["txid"]])
@cleanup(extra_args=["-datacarriersize=1000"]) @cleanup()
def test_truc_ancestors_package(self): def test_truc_ancestors_package(self):
self.log.info("Test that TRUC ancestor limits are checked within the package") self.log.info("Test that TRUC ancestor limits are checked within the package")
node = self.nodes[0] node = self.nodes[0]
@ -384,7 +384,7 @@ class MempoolTRUC(BitcoinTestFramework):
assert_equal(result_package_cpfp["tx-results"][tx_sibling_3['wtxid']]['error'], expected_error_cpfp) assert_equal(result_package_cpfp["tx-results"][tx_sibling_3['wtxid']]['error'], expected_error_cpfp)
@cleanup(extra_args=["-datacarriersize=1000"]) @cleanup()
def test_truc_package_inheritance(self): def test_truc_package_inheritance(self):
self.log.info("Test that TRUC inheritance is checked within package") self.log.info("Test that TRUC inheritance is checked within package")
node = self.nodes[0] node = self.nodes[0]

View file

@ -279,11 +279,11 @@ class MiningTest(BitcoinTestFramework):
def test_block_max_weight(self): def test_block_max_weight(self):
self.log.info("Testing default and custom -blockmaxweight startup options.") self.log.info("Testing default and custom -blockmaxweight startup options.")
# Restart the node to allow large transactions # Restart the node
LARGE_TXS_COUNT = 10 LARGE_TXS_COUNT = 10
LARGE_VSIZE = int(((MAX_BLOCK_WEIGHT - DEFAULT_BLOCK_RESERVED_WEIGHT) / WITNESS_SCALE_FACTOR) / LARGE_TXS_COUNT) LARGE_VSIZE = int(((MAX_BLOCK_WEIGHT - DEFAULT_BLOCK_RESERVED_WEIGHT) / WITNESS_SCALE_FACTOR) / LARGE_TXS_COUNT)
HIGH_FEERATE = Decimal("0.0003") HIGH_FEERATE = Decimal("0.0003")
self.restart_node(0, extra_args=[f"-datacarriersize={LARGE_VSIZE}"]) self.restart_node(0)
# Ensure the mempool is empty # Ensure the mempool is empty
assert_equal(len(self.nodes[0].getrawmempool()), 0) assert_equal(len(self.nodes[0].getrawmempool()), 0)
@ -311,7 +311,7 @@ class MiningTest(BitcoinTestFramework):
# Test block template creation with custom -blockmaxweight # Test block template creation with custom -blockmaxweight
custom_block_weight = MAX_BLOCK_WEIGHT - 2000 custom_block_weight = MAX_BLOCK_WEIGHT - 2000
# Reducing the weight by 2000 units will prevent 1 large transaction from fitting into the block. # Reducing the weight by 2000 units will prevent 1 large transaction from fitting into the block.
self.restart_node(0, extra_args=[f"-datacarriersize={LARGE_VSIZE}", f"-blockmaxweight={custom_block_weight}"]) self.restart_node(0, extra_args=[f"-blockmaxweight={custom_block_weight}"])
self.log.info("Testing the block template with custom -blockmaxweight to include 9 large and 2 normal transactions.") self.log.info("Testing the block template with custom -blockmaxweight to include 9 large and 2 normal transactions.")
self.verify_block_template( self.verify_block_template(
@ -321,7 +321,7 @@ class MiningTest(BitcoinTestFramework):
# Ensure the block weight does not exceed the maximum # Ensure the block weight does not exceed the maximum
self.log.info(f"Testing that the block weight will never exceed {MAX_BLOCK_WEIGHT - DEFAULT_BLOCK_RESERVED_WEIGHT}.") self.log.info(f"Testing that the block weight will never exceed {MAX_BLOCK_WEIGHT - DEFAULT_BLOCK_RESERVED_WEIGHT}.")
self.restart_node(0, extra_args=[f"-datacarriersize={LARGE_VSIZE}", f"-blockmaxweight={MAX_BLOCK_WEIGHT}"]) self.restart_node(0, extra_args=[f"-blockmaxweight={MAX_BLOCK_WEIGHT}"])
self.log.info("Sending 2 additional normal transactions to fill the mempool to the maximum block weight.") self.log.info("Sending 2 additional normal transactions to fill the mempool to the maximum block weight.")
self.send_transactions(utxos[LARGE_TXS_COUNT + 2:], NORMAL_FEERATE, NORMAL_VSIZE) self.send_transactions(utxos[LARGE_TXS_COUNT + 2:], NORMAL_FEERATE, NORMAL_VSIZE)
self.log.info(f"Testing that the mempool's weight matches the maximum block weight: {MAX_BLOCK_WEIGHT}.") self.log.info(f"Testing that the mempool's weight matches the maximum block weight: {MAX_BLOCK_WEIGHT}.")
@ -335,7 +335,7 @@ class MiningTest(BitcoinTestFramework):
self.log.info("Test -blockreservedweight startup option.") self.log.info("Test -blockreservedweight startup option.")
# Lowering the -blockreservedweight by 4000 will allow for two more transactions. # Lowering the -blockreservedweight by 4000 will allow for two more transactions.
self.restart_node(0, extra_args=[f"-datacarriersize={LARGE_VSIZE}", "-blockreservedweight=4000"]) self.restart_node(0, extra_args=["-blockreservedweight=4000"])
self.verify_block_template( self.verify_block_template(
expected_tx_count=12, expected_tx_count=12,
expected_weight=MAX_BLOCK_WEIGHT - 4000, expected_weight=MAX_BLOCK_WEIGHT - 4000,

View file

@ -27,7 +27,6 @@ class PrioritiseTransactionTest(BitcoinTestFramework):
self.num_nodes = 1 self.num_nodes = 1
self.extra_args = [[ self.extra_args = [[
"-printpriority=1", "-printpriority=1",
"-datacarriersize=100000",
]] * self.num_nodes ]] * self.num_nodes
self.supports_cli = False self.supports_cli = False

View file

@ -41,7 +41,6 @@ class PackageRelayTest(BitcoinTestFramework):
# hugely speeds up the test, as it involves multiple hops of tx relay. # hugely speeds up the test, as it involves multiple hops of tx relay.
self.noban_tx_relay = True self.noban_tx_relay = True
self.extra_args = [[ self.extra_args = [[
"-datacarriersize=100000",
"-maxmempool=5", "-maxmempool=5",
]] * self.num_nodes ]] * self.num_nodes
self.supports_cli = False self.supports_cli = False

View file

@ -59,7 +59,6 @@ class PackageRelayTest(BitcoinTestFramework):
self.setup_clean_chain = True self.setup_clean_chain = True
self.num_nodes = 1 self.num_nodes = 1
self.extra_args = [[ self.extra_args = [[
"-datacarriersize=100000",
"-maxmempool=5", "-maxmempool=5",
]] ]]
self.supports_cli = False self.supports_cli = False

View file

@ -68,7 +68,7 @@ class ConnectionType(Enum):
class TxDownloadTest(BitcoinTestFramework): class TxDownloadTest(BitcoinTestFramework):
def set_test_params(self): def set_test_params(self):
self.num_nodes = 2 self.num_nodes = 2
self.extra_args= [['-datacarriersize=100000', '-maxmempool=5', '-persistmempool=0']] * self.num_nodes self.extra_args= [['-maxmempool=5', '-persistmempool=0']] * self.num_nodes
def test_tx_requests(self): def test_tx_requests(self):
self.log.info("Test that we request transactions from all our peers, eventually") self.log.info("Test that we request transactions from all our peers, eventually")

View file

@ -438,7 +438,6 @@ class RPCPackagesTest(BitcoinTestFramework):
# but child is too high fee # but child is too high fee
# Lower mempool limit to make it easier to fill_mempool # Lower mempool limit to make it easier to fill_mempool
self.restart_node(0, extra_args=[ self.restart_node(0, extra_args=[
"-datacarriersize=100000",
"-maxmempool=5", "-maxmempool=5",
"-persistmempool=0", "-persistmempool=0",
]) ])
@ -467,7 +466,7 @@ class RPCPackagesTest(BitcoinTestFramework):
assert parent["txid"] not in node.getrawmempool() assert parent["txid"] not in node.getrawmempool()
assert child["txid"] not in node.getrawmempool() assert child["txid"] not in node.getrawmempool()
# Reset maxmempool, datacarriersize, reset dynamic mempool minimum feerate, and empty mempool. # Reset maxmempool, reset dynamic mempool minimum feerate, and empty mempool.
self.restart_node(0) self.restart_node(0)
self.wallet.rescan_utxos() self.wallet.rescan_utxos()

View file

@ -73,9 +73,6 @@ WITNESS_SCALE_FACTOR = 4
DEFAULT_ANCESTOR_LIMIT = 25 # default max number of in-mempool ancestors DEFAULT_ANCESTOR_LIMIT = 25 # default max number of in-mempool ancestors
DEFAULT_DESCENDANT_LIMIT = 25 # default max number of in-mempool descendants DEFAULT_DESCENDANT_LIMIT = 25 # default max number of in-mempool descendants
# Default setting for -datacarriersize. 80 bytes of data, +1 for OP_RETURN, +2 for the pushdata opcodes.
MAX_OP_RETURN_RELAY = 83
DEFAULT_MEMPOOL_EXPIRY_HOURS = 336 # hours DEFAULT_MEMPOOL_EXPIRY_HOURS = 336 # hours
MAGIC_BYTES = { MAGIC_BYTES = {

View file

@ -338,7 +338,6 @@ BASE_SCRIPTS = [
'feature_unsupported_utxo_db.py', 'feature_unsupported_utxo_db.py',
'feature_logging.py', 'feature_logging.py',
'feature_anchors.py', 'feature_anchors.py',
'mempool_datacarrier.py',
'feature_coinstatsindex.py', 'feature_coinstatsindex.py',
'wallet_orphanedreward.py', 'wallet_orphanedreward.py',
'wallet_timelock.py', 'wallet_timelock.py',