diff --git a/src/init.cpp b/src/init.cpp index a151c9d0a9..2235a8149e 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -641,6 +641,7 @@ void SetupServerArgs(ArgsManager& argsman, bool can_listen_ipc) "is of this size or less (default: %u)", MAX_OP_RETURN_RELAY), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY); + argsman.AddArg("-limitdummyscriptdatasize", strprintf("Maximum size of dummy script data we relay and mine (default: %u)", MAX_DUMMY_SCRIPT_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, OptionsCategory::NODE_RELAY); argsman.AddArg("-minrelaytxfee=", strprintf("Fees (in %s/kvB) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s)", diff --git a/src/kernel/mempool_options.h b/src/kernel/mempool_options.h index d57dbb393f..ff0109a093 100644 --- a/src/kernel/mempool_options.h +++ b/src/kernel/mempool_options.h @@ -51,6 +51,7 @@ struct MemPoolOptions { * If nullopt, any size is nonstandard. */ std::optional max_datacarrier_bytes{DEFAULT_ACCEPT_DATACARRIER ? std::optional{MAX_OP_RETURN_RELAY} : std::nullopt}; + unsigned int max_dummy_script_bytes{MAX_DUMMY_SCRIPT_RELAY}; bool permit_bare_multisig{DEFAULT_PERMIT_BAREMULTISIG}; bool require_standard{true}; bool persist_v1_dat{DEFAULT_PERSIST_V1_DAT}; diff --git a/src/node/mempool_args.cpp b/src/node/mempool_args.cpp index 6dbba78381..7d2b85621d 100644 --- a/src/node/mempool_args.cpp +++ b/src/node/mempool_args.cpp @@ -87,6 +87,8 @@ util::Result ApplyArgsManOptions(const ArgsManager& argsman, const CChainP mempool_opts.max_datacarrier_bytes = std::nullopt; } + mempool_opts.max_dummy_script_bytes = argsman.GetIntArg("-limitdummyscriptdatasize", MAX_DUMMY_SCRIPT_RELAY); + mempool_opts.require_standard = !argsman.GetBoolArg("-acceptnonstdtxn", DEFAULT_ACCEPT_NON_STD_TXN); if (!chainparams.IsTestChain() && !mempool_opts.require_standard) { return util::Error{Untranslated(strprintf("acceptnonstdtxn is not currently supported for %s chain", chainparams.GetChainTypeString()))}; diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index 07147694c3..e553241adf 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -368,3 +368,19 @@ std::pair GetScriptForTransactionInput(CScript prev_scrip return std::make_pair(CScript(), 0); } + +size_t DummyScriptBytes(const CTransaction& tx, const CCoinsViewCache& view) +{ + size_t ret{0}; + + for (const CTxIn& txin : tx.vin) { + const CTxOut &utxo = view.AccessCoin(txin.prevout).out; + auto[script, consensus_weight_per_byte] = GetScriptForTransactionInput(utxo.scriptPubKey, txin); + ret += script.DummyScriptBytes(); + } + for (const CTxOut& txout : tx.vout) { + ret += txout.scriptPubKey.DummyScriptBytes(); + } + + return ret; +} diff --git a/src/policy/policy.h b/src/policy/policy.h index 411cf6ca09..82b3897faa 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -73,6 +73,8 @@ static constexpr unsigned int DEFAULT_DESCENDANT_LIMIT{25}; static constexpr unsigned int DEFAULT_DESCENDANT_SIZE_LIMIT_KVB{101}; /** Default for -datacarrier */ static const bool DEFAULT_ACCEPT_DATACARRIER = true; +/** Default setting for -limitdummyscriptdatasize */ +static const unsigned int MAX_DUMMY_SCRIPT_RELAY{0}; /** * Default setting for -datacarriersize. 80 bytes of data, +1 for OP_RETURN, * +2 for the pushdata opcodes. @@ -184,4 +186,6 @@ static inline int64_t GetVirtualTransactionInputSize(const CTxIn& tx) std::pair GetScriptForTransactionInput(CScript prev_script, const CTxIn&); +size_t DummyScriptBytes(const CTransaction& tx, const CCoinsViewCache& view); + #endif // BITCOIN_POLICY_POLICY_H diff --git a/src/validation.cpp b/src/validation.cpp index 1213d8be9f..0c6bcd7353 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -882,6 +882,12 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws) return state.Invalid(TxValidationResult::TX_INPUTS_NOT_STANDARD, "bad-txns-nonstandard-inputs"); } + if (tx.GetTotalSize() > m_pool.m_opts.max_dummy_script_bytes) { + if (DummyScriptBytes(tx, m_view) > m_pool.m_opts.max_dummy_script_bytes) { + return state.Invalid(TxValidationResult::TX_INPUTS_NOT_STANDARD, "txn-dummyscript-exceeded"); + } + } + // Check for non-standard witnesses. if (tx.HasWitness() && m_pool.m_opts.require_standard && !IsWitnessStandard(tx, m_view)) { return state.Invalid(TxValidationResult::TX_WITNESS_MUTATED, "bad-witness-nonstandard");