diff --git a/doc/release-notes-data-outputs.md b/doc/release-notes-data-outputs.md new file mode 100644 index 000000000000..9dae5d7a6282 --- /dev/null +++ b/doc/release-notes-data-outputs.md @@ -0,0 +1,13 @@ +Policy +------ + +- Outputs whose hash or key field is data rather than a hash or key now count + as data carrier bytes, so under the default settings a transaction carrying + them is rejected with `txn-datacarrier-nonstandard`. This covers payloads + spread over three or more distinct P2WSH outputs that share one value of + 546 sat or less (the OLGA layout, whatever magic it uses), taproot outputs + whose key is not on the curve, and any hash with one byte value repeated + eight times or six times in a row. Lightning anchor outputs, ordinals + postage and outputs to the all-zero burn hash are unaffected. The new + `-rejectfakeoutputs` option (default: 1, reset by `-corepolicy`) turns the + detection off without accepting other non-standard data carriers. 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..fced0af4ad1a 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -721,7 +721,9 @@ 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("-rejectfakeoutputs", strprintf("Count outputs whose hash or key is data as data carrier bytes (default: %u)", DEFAULT_REJECT_FAKE_OUTPUTS), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY); argsman.AddArg("-datacarriersize", strprintf("Maximum size of data in data carrier transactions we relay and mine, in bytes (default: %u)", MAX_OP_RETURN_RELAY), @@ -870,9 +872,11 @@ void InitParameterInteraction(ArgsManager& args) args.SoftSetArg("-permitbaremultisig", "1"); args.SoftSetArg("-rejectparasites", "0"); args.SoftSetArg("-rejecttokens", "0"); + args.SoftSetArg("-rejectfakeoutputs", "0"); 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 +1217,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/kernel/mempool_options.h b/src/kernel/mempool_options.h index 0ce9ac96b1e5..4191612b4e0c 100644 --- a/src/kernel/mempool_options.h +++ b/src/kernel/mempool_options.h @@ -80,6 +80,7 @@ struct MemPoolOptions { */ std::optional max_datacarrier_bytes{DEFAULT_ACCEPT_DATACARRIER ? std::optional{MAX_OP_RETURN_RELAY} : std::nullopt}; bool datacarrier_fullcount{DEFAULT_DATACARRIER_FULLCOUNT}; + bool reject_fake_outputs{DEFAULT_REJECT_FAKE_OUTPUTS}; bool permitbaredatacarrier{DEFAULT_PERMITBAREDATACARRIER}; bool permitbareanchor{DEFAULT_PERMITBAREANCHOR}; bool permit_bare_pubkey{DEFAULT_PERMIT_BAREPUBKEY}; diff --git a/src/node/mempool_args.cpp b/src/node/mempool_args.cpp index 76afdd23c32a..b0c6d6408990 100644 --- a/src/node/mempool_args.cpp +++ b/src/node/mempool_args.cpp @@ -205,6 +205,8 @@ util::Result ApplyArgsManOptions(const ArgsManager& argsman, const CChainP mempool_opts.reject_tokens = argsman.GetBoolArg("-rejecttokens", DEFAULT_REJECT_TOKENS); + mempool_opts.reject_fake_outputs = argsman.GetBoolArg("-rejectfakeoutputs", DEFAULT_REJECT_FAKE_OUTPUTS); + mempool_opts.subdustfeepenalty = argsman.GetBoolArg("-subdustfeepenalty", DEFAULT_SUBDUSTFEEPENALTY); if (argsman.GetBoolArg("-datacarrier", DEFAULT_ACCEPT_DATACARRIER)) { diff --git a/src/node/psbt.cpp b/src/node/psbt.cpp index 37b8bd02ccdb..c6c05c0af67e 100644 --- a/src/node/psbt.cpp +++ b/src/node/psbt.cpp @@ -138,7 +138,7 @@ PSBTAnalysis AnalyzePSBT(PartiallySignedTransaction psbtx) if (success) { CTransaction ctx = CTransaction(mtx); - size_t size(GetVirtualTransactionSize(GetTransactionWeight(ctx) + CalculateExtraTxWeight(ctx, view, ::g_weight_per_data_byte), GetTransactionSigOpCost(ctx, view, STANDARD_SCRIPT_VERIFY_FLAGS), ::nBytesPerSigOp)); + size_t size(GetVirtualTransactionSize(GetTransactionWeight(ctx) + CalculateExtraTxWeight(ctx, view, ::g_weight_per_data_byte, DataOutputBytes(ctx, &view)), GetTransactionSigOpCost(ctx, view, STANDARD_SCRIPT_VERIFY_FLAGS), ::nBytesPerSigOp)); result.estimated_vsize = size; // Estimate fee rate CFeeRate feerate(fee, size); diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index 679c0c967a1f..d27ae10cc012 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include