diff --git a/doc/release-notes-dead-branch-data.md b/doc/release-notes-dead-branch-data.md new file mode 100644 index 000000000000..b96b71fa8138 --- /dev/null +++ b/doc/release-notes-dead-branch-data.md @@ -0,0 +1,12 @@ +Policy +------ + +- Data in a conditional part that constants make unreachable now counts as + data carrier bytes, so under the default settings a transaction carrying it + is rejected with `txn-datacarrier-nonstandard`. This already applied to the + `OP_FALSE OP_IF ... OP_ENDIF` inscription envelope; it now also covers + `OP_1 OP_NOTIF ... OP_ENDIF`, guards computed from constants, and the dead + part of an `OP_ELSE`. Scripts that branch on witness data or a signature + check, which is how spendable scripts branch, are unaffected. The new + `-rejectdeadbranches` option (default: 1, reset by `-corepolicy`) turns the + wider rule off and leaves only the envelope counted. diff --git a/src/init.cpp b/src/init.cpp index 1d75c41925b5..048d0cf1ec77 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -721,6 +721,7 @@ void SetupServerArgs(ArgsManager& argsman, bool can_listen_ipc) argsman.AddArg("-bytespersigopstrict", strprintf("Minimum bytes per sigop in transactions we relay and mine (default: %u)", DEFAULT_BYTES_PER_SIGOP_STRICT), 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("-datacarriercost", strprintf("Treat extra data in transactions as at least N vbytes per actual byte (default: %s)", DEFAULT_WEIGHT_PER_DATA_BYTE / 4.0), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY); + argsman.AddArg("-rejectdeadbranches", strprintf("Count data in a conditional branch that a pushed constant makes unreachable as data carrier bytes; the OP_FALSE OP_IF envelope is counted either way (default: %u)", DEFAULT_REJECT_DEAD_BRANCHES), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY); argsman.AddArg("-datacarrierfullcount", strprintf("Apply datacarriersize limit to all known datacarrier methods (default: %u)", DEFAULT_DATACARRIER_FULLCOUNT), ArgsManager::ALLOW_ANY | (DEFAULT_DATACARRIER_FULLCOUNT ? uint32_t{ArgsManager::DEBUG_ONLY} : 0), OptionsCategory::NODE_RELAY); argsman.AddArg("-datacarriersize", strprintf("Maximum size of data in data carrier transactions we relay and mine, in bytes (default: %u)", @@ -873,6 +874,7 @@ void InitParameterInteraction(ArgsManager& args) args.SoftSetArg("-subdustfeepenalty", "0"); args.SoftSetArg("-datacarriercost", "0.25"); args.SoftSetArg("-datacarrierfullcount", "0"); + args.SoftSetArg("-rejectdeadbranches", "0"); args.SoftSetArg("-maxtxlegacysigops", strprintf("%s", std::numeric_limits::max())); args.SoftSetArg("-maxscriptsize", strprintf("%s", std::numeric_limits::max())); args.SoftSetArg("-mempooltruc", "enforce"); @@ -1213,6 +1215,8 @@ bool AppInitParameterInteraction(const ArgsManager& args) g_script_size_policy_limit = args.GetIntArg("-maxscriptsize", g_script_size_policy_limit); + g_reject_dead_branches = args.GetBoolArg("-rejectdeadbranches", g_reject_dead_branches); + nBytesPerSigOp = args.GetIntArg("-bytespersigop", nBytesPerSigOp); nBytesPerSigOpStrict = args.GetIntArg("-bytespersigopstrict", nBytesPerSigOpStrict); diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index 679c0c967a1f..8efcde9bf4ef 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -33,6 +33,7 @@ #include unsigned int g_script_size_policy_limit{DEFAULT_SCRIPT_SIZE_POLICY_LIMIT}; +bool g_reject_dead_branches{DEFAULT_REJECT_DEAD_BRANCHES}; CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFeeIn) { @@ -633,6 +634,16 @@ std::pair GetScriptForTransactionInput(CScript prevScript return std::make_pair(CScript(), 0); } +/** + * Whether to look for dead branches in this input. A witness over the script size + * limit fails IsWitnessStandard right after, so following its constants would be + * work spent on a transaction that is rejected anyway. + */ +static bool FollowDeadBranches(const CTxIn& txin) +{ + return ::g_reject_dead_branches && GetSerializeSize(txin.scriptWitness.stack) <= g_script_size_policy_limit; +} + std::pair DatacarrierBytes(const CTransaction& tx, const CCoinsViewCache& view) { std::pair ret{0, 0}; @@ -640,7 +651,7 @@ std::pair DatacarrierBytes(const CTransaction& tx, const CCoinsV for (const CTxIn& txin : tx.vin) { const CTxOut &utxo = view.AccessCoin(txin.prevout).out; auto[script, consensus_weight_per_byte] = GetScriptForTransactionInput(utxo.scriptPubKey, txin); - const auto dcb = script.DatacarrierBytes(0, &txin.scriptWitness); + const auto dcb = script.DatacarrierBytes(0, &txin.scriptWitness, FollowDeadBranches(txin)); ret.first += dcb.first; ret.second += dcb.second; } @@ -664,7 +675,7 @@ int32_t CalculateExtraTxWeight(const CTransaction& tx, const CCoinsViewCache& vi const CTxOut &utxo = view.AccessCoin(txin.prevout).out; auto[script, consensus_weight_per_byte] = GetScriptForTransactionInput(utxo.scriptPubKey, txin); if (weight_per_data_byte > consensus_weight_per_byte) { - const auto dcb = script.DatacarrierBytes(0, &txin.scriptWitness); + const auto dcb = script.DatacarrierBytes(0, &txin.scriptWitness, FollowDeadBranches(txin)); mod_weight += int64_t(dcb.first + dcb.second) * (weight_per_data_byte - consensus_weight_per_byte); } } diff --git a/src/policy/policy.h b/src/policy/policy.h index 6c52b6aebff2..e5e5be158fae 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -64,6 +64,8 @@ static constexpr unsigned int DEFAULT_BYTES_PER_SIGOP{20}; static constexpr unsigned int DEFAULT_BYTES_PER_SIGOP_STRICT{20}; /** Default for -datacarriercost (multiplied by WITNESS_SCALE_FACTOR) */ static constexpr unsigned int DEFAULT_WEIGHT_PER_DATA_BYTE{4}; +/** Default for -rejectdeadbranches */ +static constexpr bool DEFAULT_REJECT_DEAD_BRANCHES{true}; /** Default for -rejecttokens */ static constexpr bool DEFAULT_REJECT_TOKENS{true}; /** Default for -subdustfeepenalty */ diff --git a/src/policy/settings.h b/src/policy/settings.h index 03cfc3775da1..6c60e3ecef64 100644 --- a/src/policy/settings.h +++ b/src/policy/settings.h @@ -10,5 +10,6 @@ extern unsigned int g_script_size_policy_limit; extern unsigned int nBytesPerSigOp; extern unsigned int nBytesPerSigOpStrict; extern unsigned int g_weight_per_data_byte; +extern bool g_reject_dead_branches; #endif // BITCOIN_POLICY_SETTINGS_H diff --git a/src/script/script.cpp b/src/script/script.cpp index fbb52fee7f9b..0fbb8f4ea7a0 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -8,10 +8,12 @@ #include #include #include +#include #include