diff --git a/doc/bips.md b/doc/bips.md index e6efe68f84f2..f5b70d35e60f 100644 --- a/doc/bips.md +++ b/doc/bips.md @@ -34,6 +34,7 @@ BIPs that are implemented by Bitcoin Knots: * [`BIP 90`](https://github.com/bitcoin/bips/blob/master/bip-0090.mediawiki): Trigger mechanism for activation of BIPs 34, 65, and 66 has been simplified to block height checks since **v0.14.0** ([PR #8391](https://github.com/bitcoin/bitcoin/pull/8391)). * [`BIP 93`](https://github.com/bitcoin/bips/blob/master/bip-0093.mediawiki): Support for importing codex32 seeds via the `importdescriptors` RPC method is available as of **v25.0.knots20230823** ([PR #27351](https://github.com/bitcoin/bitcoin/pull/27351)). * [`BIP 94`](https://github.com/bitcoin/bips/blob/master/bip-0094.mediawiki): Testnet 4 (`-testnet4`) supported as of **v28.0** ([PR #29775](https://github.com/bitcoin/bitcoin/pull/29775)). +* [`BIP 110`](https://github.com/bitcoin/bips/blob/master/bip-0110.mediawiki): ReducedData Temporary Softfork (RDTS) consensus rules are implemented as of **FIXME: update version** ([PR #238](https://github.com/bitcoinknots/bitcoin/pull/238)). * [`BIP 111`](https://github.com/bitcoin/bips/blob/master/bip-0111.mediawiki): `NODE_BLOOM` service bit added, and enforced for all peer versions as of **v0.13.0** ([PR #6579](https://github.com/bitcoin/bitcoin/pull/6579) and [PR #6641](https://github.com/bitcoin/bitcoin/pull/6641)). * [`BIP 112`](https://github.com/bitcoin/bips/blob/master/bip-0112.mediawiki): The CHECKSEQUENCEVERIFY opcode has been implemented since **v0.12.1** ([PR #7524](https://github.com/bitcoin/bitcoin/pull/7524)), and has been *buried* since **v0.19.0** ([PR #16060](https://github.com/bitcoin/bitcoin/pull/16060)). * [`BIP 113`](https://github.com/bitcoin/bips/blob/master/bip-0113.mediawiki): Median time past lock-time calculations have been implemented since **v0.12.1** ([PR #6566](https://github.com/bitcoin/bitcoin/pull/6566)), and has been *buried* since **v0.19.0** ([PR #16060](https://github.com/bitcoin/bitcoin/pull/16060)). diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index 86435410f545..03cb54b9966c 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -483,6 +483,8 @@ class NetinfoRequestHandler : public BaseRequestHandler str += 'T'; } else if (s == "UTREEXO_TMP?") { str += 'y'; + } else if (s == "REDUCED_DATA?") { + str += '4'; } else { str += ToLower(s[0]); } @@ -765,6 +767,7 @@ class NetinfoRequestHandler : public BaseRequestHandler " \"T\" - UTREEXO_ARCHIVE peer can handle Utreexo proof requests for all historical blocks\n" " \"y\" - UTREEXO_TMP? peer can handle Utreexo proof requests\n" " \"r\" - REPLACE_BY_FEE? peer supports replacement of transactions without BIP 125 signalling\n" + " \"4\" - REDUCED_DATA? peer enforces the ReducedData SoftFork\n" " \"m\" - MALICIOUS? peer openly seeks to aid in bypassing network policy/spam filters (OR to sabotage nodes that seek to)\n" " \"u\" - UNKNOWN: unrecognized bit flag\n" " v Version of transport protocol used for the connection\n" diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 7290b31479a1..c123fa5be552 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -77,8 +77,8 @@ void ReadRegTestArgs(const ArgsManager& args, CChainParams::RegTestOptions& opti for (const std::string& strDeployment : args.GetArgs("-vbparams")) { std::vector vDeploymentParams = SplitString(strDeployment, ':'); - if (vDeploymentParams.size() < 3 || 4 < vDeploymentParams.size()) { - throw std::runtime_error("Version bits parameters malformed, expecting deployment:start:end[:min_activation_height]"); + if (vDeploymentParams.size() < 3 || 7 < vDeploymentParams.size()) { + throw std::runtime_error("Version bits parameters malformed, expecting deployment:start:end[:min_activation_height[:max_activation_height[:active_duration[:threshold]]]]"); } CChainParams::VersionBitsParameters vbparams{}; if (!ParseInt64(vDeploymentParams[1], &vbparams.start_time)) { @@ -94,12 +94,31 @@ void ReadRegTestArgs(const ArgsManager& args, CChainParams::RegTestOptions& opti } else { vbparams.min_activation_height = 0; } + if (vDeploymentParams.size() >= 5) { + if (!ParseInt32(vDeploymentParams[4], &vbparams.max_activation_height)) { + throw std::runtime_error(strprintf("Invalid max_activation_height (%s)", vDeploymentParams[4])); + } + } + if (vDeploymentParams.size() >= 6) { + if (!ParseInt32(vDeploymentParams[5], &vbparams.active_duration)) { + throw std::runtime_error(strprintf("Invalid active_duration (%s)", vDeploymentParams[5])); + } + } + if (vDeploymentParams.size() >= 7) { + if (!ParseInt32(vDeploymentParams[6], &vbparams.threshold)) { + throw std::runtime_error(strprintf("Invalid threshold (%s)", vDeploymentParams[6])); + } + } + // Validate that timeout and max_activation_height are mutually exclusive + if (vbparams.timeout != Consensus::BIP9Deployment::NO_TIMEOUT && vbparams.max_activation_height < std::numeric_limits::max()) { + throw std::runtime_error(strprintf("Cannot specify both timeout (%ld) and max_activation_height (%d) for deployment %s. Use timeout for BIP9 or max_activation_height for mandatory activation deadline, not both.", vbparams.timeout, vbparams.max_activation_height, vDeploymentParams[0])); + } bool found = false; for (int j=0; j < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++j) { if (vDeploymentParams[0] == VersionBitsDeploymentInfo[j].name) { options.version_bits_parameters[Consensus::DeploymentPos(j)] = vbparams; found = true; - LogPrintf("Setting version bits activation parameters for %s to start=%ld, timeout=%ld, min_activation_height=%d\n", vDeploymentParams[0], vbparams.start_time, vbparams.timeout, vbparams.min_activation_height); + LogPrintf("Setting version bits activation parameters for %s to start=%ld, timeout=%ld, min_activation_height=%d, max_activation_height=%d, active_duration=%d, threshold=%d\n", vDeploymentParams[0], vbparams.start_time, vbparams.timeout, vbparams.min_activation_height, vbparams.max_activation_height, vbparams.active_duration, vbparams.threshold); break; } } diff --git a/src/consensus/consensus.h b/src/consensus/consensus.h index cffe9cdafd79..b02773f4900b 100644 --- a/src/consensus/consensus.h +++ b/src/consensus/consensus.h @@ -34,4 +34,7 @@ static constexpr unsigned int LOCKTIME_VERIFY_SEQUENCE = (1 << 0); */ static constexpr int64_t MAX_TIMEWARP = 600; +static constexpr unsigned int MAX_OUTPUT_SCRIPT_SIZE{34}; +static constexpr unsigned int MAX_OUTPUT_DATA_SIZE{83}; + #endif // BITCOIN_CONSENSUS_CONSENSUS_H diff --git a/src/consensus/params.h b/src/consensus/params.h index dd29b9408e23..c2ac24bfb5e6 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -32,6 +32,7 @@ constexpr bool ValidDeployment(BuriedDeployment dep) { return dep <= DEPLOYMENT_ enum DeploymentPos : uint16_t { DEPLOYMENT_TESTDUMMY, DEPLOYMENT_TAPROOT, // Deployment of Schnorr/Taproot (BIPs 340-342) + DEPLOYMENT_REDUCED_DATA, // ReducedData Temporary Softfork (RDTS) // NOTE: Also add new deployments to VersionBitsDeploymentInfo in deploymentinfo.cpp MAX_VERSION_BITS_DEPLOYMENTS }; @@ -52,6 +53,16 @@ struct BIP9Deployment { * boundary. */ int min_activation_height{0}; + /** Maximum height for activation. If less than INT_MAX, the deployment will activate + * at this height regardless of signaling (similar to BIP8 flag day). + * std::numeric_limits::max() means no maximum (activation only via signaling). */ + int max_activation_height{std::numeric_limits::max()}; + /** For temporary softforks: number of blocks the deployment remains active after activation. + * std::numeric_limits::max() means permanent (never expires). */ + int active_duration{std::numeric_limits::max()}; + /** Per-deployment activation threshold. If 0, uses the global nRuleChangeActivationThreshold. + * Otherwise, specifies the number of blocks required for this specific deployment. */ + int threshold{0}; /** Constant for nTimeout very far in the future. */ static constexpr int64_t NO_TIMEOUT = std::numeric_limits::max(); diff --git a/src/consensus/tx_verify.cpp b/src/consensus/tx_verify.cpp index 95466b759cbb..84558503c708 100644 --- a/src/consensus/tx_verify.cpp +++ b/src/consensus/tx_verify.cpp @@ -161,7 +161,18 @@ int64_t GetTransactionSigOpCost(const CTransaction& tx, const CCoinsViewCache& i return nSigOps; } -bool Consensus::CheckTxInputs(const CTransaction& tx, TxValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee) +bool Consensus::CheckOutputSizes(const CTransaction& tx, TxValidationState& state) +{ + for (const auto& txout : tx.vout) { + if (txout.scriptPubKey.empty()) continue; + if (txout.scriptPubKey.size() > ((txout.scriptPubKey[0] == OP_RETURN) ? MAX_OUTPUT_DATA_SIZE : MAX_OUTPUT_SCRIPT_SIZE)) { + return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, "bad-txns-vout-script-toolarge"); + } + } + return true; +} + +bool Consensus::CheckTxInputs(const CTransaction& tx, TxValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee, const CheckTxInputsRules rules) { // are the actual inputs available? if (!inputs.HaveInputs(tx)) { @@ -169,6 +180,11 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, TxValidationState& state, strprintf("%s: inputs missing/spent", __func__)); } + // NOTE: CheckTransaction is arguably the more logical place to do this, but it's context-independent, so this is probably the next best place for now + if (rules.test(CheckTxInputsRules::OutputSizeLimit) && !CheckOutputSizes(tx, state)) { + return false; + } + CAmount nValueIn = 0; for (unsigned int i = 0; i < tx.vin.size(); ++i) { const COutPoint &prevout = tx.vin[i].prevout; diff --git a/src/consensus/tx_verify.h b/src/consensus/tx_verify.h index d2cf792cf3f6..8111a5b77a4f 100644 --- a/src/consensus/tx_verify.h +++ b/src/consensus/tx_verify.h @@ -17,14 +17,45 @@ class TxValidationState; /** Transaction validation functions */ +class CheckTxInputsRules { + using underlying_type = unsigned int; + underlying_type m_flags; + constexpr explicit CheckTxInputsRules(underlying_type flags) noexcept : m_flags(flags) {} + + enum class Rule { + None = 0, + OutputSizeLimit = 1 << 0, + }; + +public: + using enum Rule; + + constexpr CheckTxInputsRules(Rule rule) noexcept : m_flags(static_cast(rule)) {} + + [[nodiscard]] constexpr bool test(CheckTxInputsRules rules) const noexcept { + return (m_flags & rules.m_flags) == rules.m_flags; + } + + [[nodiscard]] constexpr CheckTxInputsRules operator|(const CheckTxInputsRules other) const noexcept { + return CheckTxInputsRules{m_flags | other.m_flags}; + } +}; + namespace Consensus { +/** + * Check whether all outputs of this transaction satisfy size limits. + * Regular outputs must be <= MAX_OUTPUT_SCRIPT_SIZE (34 bytes). + * OP_RETURN outputs must be <= MAX_OUTPUT_DATA_SIZE (83 bytes). + */ +bool CheckOutputSizes(const CTransaction& tx, TxValidationState& state); + /** * Check whether all inputs of this transaction are valid (no double spends and amounts) * This does not modify the UTXO set. This does not check scripts and sigs. * @param[out] txfee Set to the transaction fee if successful. * Preconditions: tx.IsCoinBase() is false. */ -[[nodiscard]] bool CheckTxInputs(const CTransaction& tx, TxValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee); +[[nodiscard]] bool CheckTxInputs(const CTransaction& tx, TxValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee, CheckTxInputsRules rules); } // namespace Consensus /** Auxiliary functions for transaction validation (ideally should not be exposed) */ diff --git a/src/deploymentinfo.cpp b/src/deploymentinfo.cpp index 185a7dcb54ce..200f5fd26300 100644 --- a/src/deploymentinfo.cpp +++ b/src/deploymentinfo.cpp @@ -17,6 +17,10 @@ const struct VBDeploymentInfo VersionBitsDeploymentInfo[Consensus::MAX_VERSION_B /*.name =*/ "taproot", /*.gbt_force =*/ true, }, + { + /*.name =*/ "reduced_data", + /*.gbt_force =*/ true, + }, }; std::string DeploymentName(Consensus::BuriedDeployment dep) diff --git a/src/deploymentstatus.h b/src/deploymentstatus.h index 03d3c531ccec..26c66e8be759 100644 --- a/src/deploymentstatus.h +++ b/src/deploymentstatus.h @@ -20,7 +20,15 @@ inline bool DeploymentActiveAfter(const CBlockIndex* pindexPrev, const Consensus inline bool DeploymentActiveAfter(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos dep, VersionBitsCache& versionbitscache) { assert(Consensus::ValidDeployment(dep)); - return ThresholdState::ACTIVE == versionbitscache.State(pindexPrev, params, dep); + if (ThresholdState::ACTIVE != versionbitscache.State(pindexPrev, params, dep)) return false; + + const auto& deployment = params.vDeployments[dep]; + // Permanent deployment (never expires) + if (deployment.active_duration == std::numeric_limits::max()) return true; + + const int activation_height = versionbitscache.StateSinceHeight(pindexPrev, params, dep); + const int height = pindexPrev == nullptr ? 0 : pindexPrev->nHeight + 1; + return height < activation_height + deployment.active_duration; } /** Determine if a deployment is active for this block */ @@ -49,4 +57,17 @@ inline bool DeploymentEnabled(const Consensus::Params& params, Consensus::Deploy return params.vDeployments[dep].nStartTime != Consensus::BIP9Deployment::NEVER_ACTIVE; } +/** Determine if mandatory signaling is required for a deployment at the next block */ +inline bool DeploymentMustSignalAfter(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos dep, ThresholdState state) +{ + assert(Consensus::ValidDeployment(dep)); + const auto& deployment = params.vDeployments[dep]; + if (deployment.max_activation_height >= std::numeric_limits::max()) return false; + if (state != ThresholdState::STARTED) return false; // If must_signal height is reached before start time, abstain from enforcement + const int nPeriod = params.nMinerConfirmationWindow; + const int nHeight = pindexPrev == nullptr ? 0 : pindexPrev->nHeight + 1; + return nHeight >= deployment.max_activation_height - (2 * nPeriod) + && nHeight < deployment.max_activation_height - nPeriod; +} + #endif // BITCOIN_DEPLOYMENTSTATUS_H diff --git a/src/init.cpp b/src/init.cpp index 7b6b188c257d..23ab427b1c2e 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -716,7 +716,8 @@ void SetupServerArgs(ArgsManager& argsman, bool can_listen_ipc) 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("-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)", + strprintf("Maximum size of data in data carrier transactions we relay and mine, in bytes (maximum %s, default: %u)", + MAX_OUTPUT_DATA_SIZE, MAX_OP_RETURN_RELAY), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY); argsman.AddArg("-maxscriptsize", strprintf("Maximum size of scripts (including the entire witness stack) we relay and mine, in bytes (default: %s)", DEFAULT_SCRIPT_SIZE_POLICY_LIMIT), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY); @@ -974,7 +975,7 @@ namespace { // Variables internal to initialization process only int nMaxConnections; int available_fds; -ServiceFlags g_local_services = ServiceFlags(NODE_NETWORK_LIMITED | NODE_WITNESS); +ServiceFlags g_local_services = ServiceFlags(NODE_NETWORK_LIMITED | NODE_WITNESS | NODE_REDUCED_DATA); int64_t peer_connect_timeout; std::set g_enabled_filter_types; diff --git a/src/kernel/chainparams.cpp b/src/kernel/chainparams.cpp index 861850b8e5c7..b418139ef9dc 100644 --- a/src/kernel/chainparams.cpp +++ b/src/kernel/chainparams.cpp @@ -117,6 +117,15 @@ class CMainParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = 1628640000; // August 11th, 2021 consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 709632; // Approximately November 12th, 2021 + // ReducedData Temporary Softfork (RDTS) + consensus.vDeployments[Consensus::DEPLOYMENT_REDUCED_DATA].bit = 4; + consensus.vDeployments[Consensus::DEPLOYMENT_REDUCED_DATA].nStartTime = 1764547200; // December 1st, 2025 + consensus.vDeployments[Consensus::DEPLOYMENT_REDUCED_DATA].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; + consensus.vDeployments[Consensus::DEPLOYMENT_REDUCED_DATA].min_activation_height = 0; + consensus.vDeployments[Consensus::DEPLOYMENT_REDUCED_DATA].max_activation_height = 965664; // ~September 1st, 2026 + consensus.vDeployments[Consensus::DEPLOYMENT_REDUCED_DATA].active_duration = 52416; // ~1 year + consensus.vDeployments[Consensus::DEPLOYMENT_REDUCED_DATA].threshold = 1109; // 55% of 2016 + consensus.nMinimumChainWork = uint256{"0000000000000000000000000000000000000000dee8e2a309ad8a9820433c68"}; consensus.defaultAssumeValid = uint256{"00000000000000000000611fd22f2df7c8fbd0688745c3a6c3bb5109cc2a12cb"}; // 912683 @@ -146,8 +155,8 @@ class CMainParams : public CChainParams { // release ASAP to avoid it where possible. vSeeds.emplace_back("seed.bitcoin.sipa.be."); // Pieter Wuille, only supports x1, x5, x9, and xd vSeeds.emplace_back("dnsseed.bluematt.me."); // Matt Corallo, only supports x9 - vSeeds.emplace_back("dnsseed.bitcoin.dashjr-list-of-p2p-nodes.us."); // Luke Dashjr - vSeeds.emplace_back("seed.bitcoin.haf.ovh."); // Léo Haf + vSeeds.emplace_back("dnsseed.bitcoin.dashjr-list-of-p2p-nodes.us."); // Luke Dashjr, support BIP110 seeding (x8000009) + vSeeds.emplace_back("seed.bitcoin.haf.ovh."); // Léo Haf, support BIP110 seeding (x8000009) vSeeds.emplace_back("seed.bitcoin.jonasschnelli.ch."); // Jonas Schnelli, only supports x1, x5, x9, and xd vSeeds.emplace_back("seed.btc.petertodd.net."); // Peter Todd, only supports x1, x5, x9, and xd vSeeds.emplace_back("seed.bitcoin.sprovoost.nl."); // Sjors Provoost @@ -280,6 +289,14 @@ class CTestNetParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = 1628640000; // August 11th, 2021 consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 0; // No activation delay + // ReducedData Temporary Softfork (RDTS) + consensus.vDeployments[Consensus::DEPLOYMENT_REDUCED_DATA].bit = 4; + consensus.vDeployments[Consensus::DEPLOYMENT_REDUCED_DATA].nStartTime = 1764547200; // December 1st, 2025 + consensus.vDeployments[Consensus::DEPLOYMENT_REDUCED_DATA].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; + consensus.vDeployments[Consensus::DEPLOYMENT_REDUCED_DATA].min_activation_height = 0; + consensus.vDeployments[Consensus::DEPLOYMENT_REDUCED_DATA].active_duration = 52416; // ~1 year + consensus.vDeployments[Consensus::DEPLOYMENT_REDUCED_DATA].threshold = 1109; // 55% of 2016 + consensus.nMinimumChainWork = uint256{"0000000000000000000000000000000000000000000015f5e0c9f13455b0eb17"}; consensus.defaultAssumeValid = uint256{"00000000000003fc7967410ba2d0a8a8d50daedc318d43e8baf1a9782c236a57"}; // 3974606 @@ -379,6 +396,14 @@ class CTestNet4Params : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 0; // No activation delay + // ReducedData Temporary Softfork (RDTS) + consensus.vDeployments[Consensus::DEPLOYMENT_REDUCED_DATA].bit = 4; + consensus.vDeployments[Consensus::DEPLOYMENT_REDUCED_DATA].nStartTime = 1764547200; // December 1st, 2025 + consensus.vDeployments[Consensus::DEPLOYMENT_REDUCED_DATA].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; + consensus.vDeployments[Consensus::DEPLOYMENT_REDUCED_DATA].min_activation_height = 0; + consensus.vDeployments[Consensus::DEPLOYMENT_REDUCED_DATA].active_duration = 52416; // ~1 year + consensus.vDeployments[Consensus::DEPLOYMENT_REDUCED_DATA].threshold = 1109; // 55% of 2016 + consensus.nMinimumChainWork = uint256{"0000000000000000000000000000000000000000000001d6dce8651b6094e4c1"}; consensus.defaultAssumeValid = uint256{"0000000000003ed4f08dbdf6f7d6b271a6bcffce25675cb40aa9fa43179a89f3"}; // 72600 @@ -517,6 +542,11 @@ class SigNetParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 0; // No activation delay + consensus.vDeployments[Consensus::DEPLOYMENT_REDUCED_DATA].bit = 4; + consensus.vDeployments[Consensus::DEPLOYMENT_REDUCED_DATA].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE; + consensus.vDeployments[Consensus::DEPLOYMENT_REDUCED_DATA].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; + consensus.vDeployments[Consensus::DEPLOYMENT_REDUCED_DATA].min_activation_height = 0; + // message start is defined as the first 4 bytes of the sha256d of the block script HashWriter h{}; h << consensus.signet_challenge; @@ -592,6 +622,11 @@ class CRegTestParams : public CChainParams consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 0; // No activation delay + consensus.vDeployments[Consensus::DEPLOYMENT_REDUCED_DATA].bit = 4; + consensus.vDeployments[Consensus::DEPLOYMENT_REDUCED_DATA].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE; + consensus.vDeployments[Consensus::DEPLOYMENT_REDUCED_DATA].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; + consensus.vDeployments[Consensus::DEPLOYMENT_REDUCED_DATA].min_activation_height = 0; + consensus.nMinimumChainWork = uint256{}; consensus.defaultAssumeValid = uint256{}; @@ -628,6 +663,13 @@ class CRegTestParams : public CChainParams consensus.vDeployments[deployment_pos].nStartTime = version_bits_params.start_time; consensus.vDeployments[deployment_pos].nTimeout = version_bits_params.timeout; consensus.vDeployments[deployment_pos].min_activation_height = version_bits_params.min_activation_height; + consensus.vDeployments[deployment_pos].max_activation_height = version_bits_params.max_activation_height; + consensus.vDeployments[deployment_pos].active_duration = version_bits_params.active_duration; + // Validated here rather than in src/chainparams.cpp because nMinerConfirmationWindow is not yet available at parse time + if (version_bits_params.active_duration != std::numeric_limits::max() && version_bits_params.active_duration % consensus.nMinerConfirmationWindow != 0) { + throw std::runtime_error(strprintf("active_duration (%d) must be a multiple of nMinerConfirmationWindow (%d)", version_bits_params.active_duration, consensus.nMinerConfirmationWindow)); + } + consensus.vDeployments[deployment_pos].threshold = version_bits_params.threshold; } genesis = CreateGenesisBlock(1296688602, 2, 0x207fffff, 1, 50 * COIN); diff --git a/src/kernel/chainparams.h b/src/kernel/chainparams.h index a30097ca964c..09d2ddfee51f 100644 --- a/src/kernel/chainparams.h +++ b/src/kernel/chainparams.h @@ -153,6 +153,9 @@ class CChainParams int64_t start_time; int64_t timeout; int min_activation_height; + int max_activation_height{std::numeric_limits::max()}; + int active_duration{std::numeric_limits::max()}; + int threshold{0}; // 0 means use global nRuleChangeActivationThreshold }; /** diff --git a/src/net.cpp b/src/net.cpp index f5413dbfc9c1..6ba9c033ae8b 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -2324,7 +2324,7 @@ void CConnman::ThreadDNSAddressSeed() break; } - outbound_connection_count = GetFullOutboundConnCount(); + outbound_connection_count = GetBIP110FullOutboundConnCount(); if (outbound_connection_count >= SEED_OUTBOUND_CONNECTION_THRESHOLD) { LogPrintf("P2P peers available. Finished fetching data from seed nodes.\n"); break; @@ -2379,7 +2379,7 @@ void CConnman::ThreadDNSAddressSeed() if (!interruptNet.sleep_for(w)) return; to_wait -= w; - if (GetFullOutboundConnCount() >= SEED_OUTBOUND_CONNECTION_THRESHOLD) { + if (GetBIP110FullOutboundConnCount() >= SEED_OUTBOUND_CONNECTION_THRESHOLD) { if (found > 0) { LogPrintf("%d addresses found from DNS seeds\n", found); LogPrintf("P2P peers available. Finished DNS seeding.\n"); @@ -2492,14 +2492,15 @@ void CConnman::StartExtraBlockRelayPeers() m_start_extra_block_relay_peers = true; } -// Return the number of outbound connections that are full relay (not blocks only) -int CConnman::GetFullOutboundConnCount() const +// Return the number of BIP110 outbound connections that are full relay (not blocks only). +// Non-BIP110 outbound peers are excluded as they are "additional" and don't count toward limits. +int CConnman::GetBIP110FullOutboundConnCount() const { int nRelevant = 0; { LOCK(m_nodes_mutex); for (const CNode* pnode : m_nodes) { - if (pnode->fSuccessfullyConnected && pnode->IsFullOutboundConn()) ++nRelevant; + if (pnode->fSuccessfullyConnected && pnode->IsFullOutboundConn() && !pnode->m_is_non_bip110_outbound) ++nRelevant; } } return nRelevant; @@ -2707,7 +2708,8 @@ void CConnman::ThreadOpenConnections(const std::vector connect, Spa { LOCK(m_nodes_mutex); for (const CNode* pnode : m_nodes) { - if (pnode->IsFullOutboundConn()) nOutboundFullRelay++; + // Non-BIP110 outbound peers are "additional" - don't count toward limits + if (pnode->IsFullOutboundConn() && !pnode->m_is_non_bip110_outbound) nOutboundFullRelay++; if (pnode->IsBlockOnlyConn()) nOutboundBlockRelay++; // Make sure our persistent outbound slots to ipv4/ipv6 peers belong to different netgroups. diff --git a/src/net.h b/src/net.h index f913c4e07bb3..7184567d2af8 100644 --- a/src/net.h +++ b/src/net.h @@ -865,6 +865,10 @@ class CNode /** Whether this peer provides all services that we want. Used for eviction decisions */ std::atomic_bool m_has_all_wanted_services{false}; + /** Whether this is a non-BIP110 outbound peer (lacks NODE_REDUCED_DATA). + * Used to exclude from outbound connection counts. Limited to 2 such peers. */ + std::atomic_bool m_is_non_bip110_outbound{false}; + /** Whether we should relay transactions to this peer. This only changes * from false to true. It will never change back to false. */ std::atomic_bool m_relays_txs{false}; @@ -1240,8 +1244,8 @@ class CConnman void StartExtraBlockRelayPeers(); - // Count the number of full-relay peer we have. - int GetFullOutboundConnCount() const; + // Count the number of BIP110 full-relay peers we have (excludes non-BIP110 peers). + int GetBIP110FullOutboundConnCount() const; // Return the number of outbound peers we have in excess of our target (eg, // if we previously called SetTryNewOutboundPeer(true), and have since set // to false, we may have extra peers that we wish to disconnect). This may diff --git a/src/net_processing.cpp b/src/net_processing.cpp index f810e9f12bae..ff15ad22bdd2 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -799,6 +799,9 @@ class PeerManagerImpl final : public PeerManager /** Number of peers with wtxid relay. */ std::atomic m_wtxid_relay_peers{0}; + /** Number of outbound peers without NODE_REDUCED_DATA (BIP-110). Limited to 2. */ + std::atomic m_num_non_bip110_outbound{0}; + /** Number of outbound peers with m_chain_sync.m_protect. */ int m_outbound_peers_with_protect_from_disconnect GUARDED_BY(cs_main) = 0; @@ -1594,6 +1597,11 @@ void PeerManagerImpl::FinalizeNode(const CNode& node) assert(peer != nullptr); m_wtxid_relay_peers -= peer->m_wtxid_relay; assert(m_wtxid_relay_peers >= 0); + // Decrement non-BIP110 counter if this was a non-BIP110 outbound peer + if (node.m_is_non_bip110_outbound) { + --m_num_non_bip110_outbound; + assert(m_num_non_bip110_outbound >= 0); + } } CNodeState *state = State(nodeid); assert(state != nullptr); @@ -3535,6 +3543,19 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type, } pfrom.m_has_all_wanted_services = HasAllDesirableServiceFlags(nServices); + // BIP-110: Allow up to 2 non-BIP110 outbound peers. + if (pfrom.ExpectServicesFromConn() && !(nServices & NODE_REDUCED_DATA)) { + if (m_num_non_bip110_outbound >= 2) { + LogDebug(BCLog::NET, "peer lacks NODE_REDUCED_DATA and already have 2 non-BIP110 outbound peers, %s\n", + pfrom.DisconnectMsg(fLogIPs)); + pfrom.fDisconnect = true; + return; + } + ++m_num_non_bip110_outbound; + pfrom.m_is_non_bip110_outbound = true; + LogDebug(BCLog::NET, "connected to non-BIP110 outbound peer (%d/2), %s\n", + m_num_non_bip110_outbound.load(), pfrom.ConnectionTypeAsString()); + } peer->m_their_services = nServices; pfrom.SetAddrLocal(addrMe); peer->m_starting_height = starting_height; diff --git a/src/node/mempool_args.cpp b/src/node/mempool_args.cpp index 1d82d8f4426f..f2f36931cf11 100644 --- a/src/node/mempool_args.cpp +++ b/src/node/mempool_args.cpp @@ -207,6 +207,10 @@ util::Result ApplyArgsManOptions(const ArgsManager& argsman, const CChainP if (argsman.GetBoolArg("-datacarrier", DEFAULT_ACCEPT_DATACARRIER)) { mempool_opts.max_datacarrier_bytes = argsman.GetIntArg("-datacarriersize", MAX_OP_RETURN_RELAY); + if (mempool_opts.max_datacarrier_bytes.value() > MAX_OUTPUT_DATA_SIZE) { + LogWarning("Limiting datacarriersize to %s", MAX_OUTPUT_DATA_SIZE); + mempool_opts.max_datacarrier_bytes = MAX_OUTPUT_DATA_SIZE; + } } else { mempool_opts.max_datacarrier_bytes = std::nullopt; } diff --git a/src/policy/policy.h b/src/policy/policy.h index 9dbab66a75e0..908c2346e0fc 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -170,7 +170,8 @@ static constexpr unsigned int STANDARD_SCRIPT_VERIFY_FLAGS{MANDATORY_SCRIPT_VERI SCRIPT_VERIFY_CONST_SCRIPTCODE | SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION | SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS | - SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE}; + SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE | + REDUCED_DATA_MANDATORY_VERIFY_FLAGS}; /** For convenience, standard but not mandatory verify flags. */ static constexpr unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS{STANDARD_SCRIPT_VERIFY_FLAGS & ~MANDATORY_SCRIPT_VERIFY_FLAGS}; diff --git a/src/protocol.cpp b/src/protocol.cpp index 589ff53efb33..9a6093678b69 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -103,6 +103,7 @@ static std::string serviceFlagToStr(size_t bit) case NODE_UTREEXO_ARCHIVE: return "UTREEXO_ARCHIVE"; case NODE_UTREEXO_TMP: return "UTREEXO_TMP?"; case NODE_REPLACE_BY_FEE: return "REPLACE_BY_FEE?"; + case NODE_REDUCED_DATA: return "REDUCED_DATA?"; case NODE_MALICIOUS: return "MALICIOUS?"; // Not using default, so we get warned when a case is missing } diff --git a/src/protocol.h b/src/protocol.h index ebfd139c2578..47a6a330b310 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -347,6 +347,9 @@ enum ServiceFlags : uint64_t { NODE_REPLACE_BY_FEE = (1 << 26), + // NODE_REDUCED_DATA means the node enforces ReducedData rules as applicable + NODE_REDUCED_DATA = (1 << 27), + NODE_MALICIOUS = (1 << 29), }; @@ -363,7 +366,7 @@ std::vector serviceFlagsToStr(uint64_t flags); * should be updated appropriately to filter for nodes with * desired service flags (compatible with our new flags). */ -constexpr ServiceFlags SeedsServiceFlags() { return ServiceFlags(NODE_NETWORK | NODE_WITNESS); } +constexpr ServiceFlags SeedsServiceFlags() { return ServiceFlags(NODE_NETWORK | NODE_WITNESS | NODE_REDUCED_DATA); } /** * Checks if a peer with the given service flags may be capable of having a diff --git a/src/psbt.h b/src/psbt.h index 6d49864b3cdb..88a22a84aeb4 100644 --- a/src/psbt.h +++ b/src/psbt.h @@ -872,7 +872,7 @@ struct PSBTOutput s_tree >> depth; s_tree >> leaf_ver; s_tree >> script; - if (depth > TAPROOT_CONTROL_MAX_NODE_COUNT) { + if (depth > TAPROOT_CONTROL_MAX_NODE_COUNT_REDUCED) { throw std::ios_base::failure("Output Taproot tree has as leaf greater than Taproot maximum depth"); } if ((leaf_ver & ~TAPROOT_LEAF_MASK) != 0) { diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 89ab885caa20..05890057d126 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1679,6 +1679,7 @@ static void SoftForkDescPushBack(const CBlockIndex* blockindex, UniValue& softfo case ThresholdState::LOCKED_IN: return "locked_in"; case ThresholdState::ACTIVE: return "active"; case ThresholdState::FAILED: return "failed"; + case ThresholdState::EXPIRED: return "expired"; } return "invalid"; }; @@ -1697,6 +1698,9 @@ static void SoftForkDescPushBack(const CBlockIndex* blockindex, UniValue& softfo bip9.pushKV("start_time", chainman.GetConsensus().vDeployments[id].nStartTime); bip9.pushKV("timeout", chainman.GetConsensus().vDeployments[id].nTimeout); bip9.pushKV("min_activation_height", chainman.GetConsensus().vDeployments[id].min_activation_height); + if (chainman.GetConsensus().vDeployments[id].max_activation_height < std::numeric_limits::max()) { + bip9.pushKV("max_activation_height", chainman.GetConsensus().vDeployments[id].max_activation_height); + } // BIP9 status bip9.pushKV("status", get_state_name(current_state)); @@ -1729,7 +1733,13 @@ static void SoftForkDescPushBack(const CBlockIndex* blockindex, UniValue& softfo UniValue rv(UniValue::VOBJ); rv.pushKV("type", "bip9"); if (ThresholdState::ACTIVE == next_state) { - rv.pushKV("height", chainman.m_versionbitscache.StateSinceHeight(blockindex, chainman.GetConsensus(), id)); + const int activation_height = chainman.m_versionbitscache.StateSinceHeight(blockindex, chainman.GetConsensus(), id); + rv.pushKV("height", activation_height); + // Add height_end for temporary softforks + const auto& deployment = chainman.GetConsensus().vDeployments[id]; + if (deployment.active_duration < std::numeric_limits::max()) { + rv.pushKV("height_end", activation_height + deployment.active_duration - 1); + } } rv.pushKV("active", ThresholdState::ACTIVE == next_state); rv.pushKV("bip9", std::move(bip9)); @@ -1826,7 +1836,8 @@ RPCHelpMan getblockchaininfo() namespace { const std::vector RPCHelpForDeployment{ {RPCResult::Type::STR, "type", "one of \"buried\", \"bip9\""}, - {RPCResult::Type::NUM, "height", /*optional=*/true, "height of the first block which the rules are or will be enforced (only for \"buried\" type, or \"bip9\" type with \"active\" status)"}, + {RPCResult::Type::NUM, "height", /*optional=*/true, "height of the first block which enforces the rules (only for \"buried\" type, or \"bip9\" type with \"active\" status)"}, + {RPCResult::Type::NUM, "height_end", /*optional=*/true, "height of the last block which enforces the rules (only for \"bip9\" type with \"active\" status and temporary deployments)"}, {RPCResult::Type::BOOL, "active", "true if the rules are enforced for the mempool and the next block"}, {RPCResult::Type::OBJ, "bip9", /*optional=*/true, "status of bip9 softforks (only for \"bip9\" type)", { @@ -1834,7 +1845,8 @@ const std::vector RPCHelpForDeployment{ {RPCResult::Type::NUM_TIME, "start_time", "the minimum median time past of a block at which the bit gains its meaning"}, {RPCResult::Type::NUM_TIME, "timeout", "the median time past of a block at which the deployment is considered failed if not yet locked in"}, {RPCResult::Type::NUM, "min_activation_height", "minimum height of blocks for which the rules may be enforced"}, - {RPCResult::Type::STR, "status", "status of deployment at specified block (one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\")"}, + {RPCResult::Type::NUM, "max_activation_height", /*optional=*/true, "height at which the deployment will unconditionally activate (absent for miner-vetoable deployments)"}, + {RPCResult::Type::STR, "status", "status of deployment at specified block (one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\", \"expired\")"}, {RPCResult::Type::NUM, "since", "height of the first block to which the status applies"}, {RPCResult::Type::STR, "status_next", "status of deployment at the next block"}, {RPCResult::Type::OBJ, "statistics", /*optional=*/true, "numeric statistics about signalling for a softfork (only for \"started\" and \"locked_in\" status)", @@ -1860,6 +1872,7 @@ UniValue DeploymentInfo(const CBlockIndex* blockindex, const ChainstateManager& SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_SEGWIT); SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_TESTDUMMY); SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_TAPROOT); + SoftForkDescPushBack(blockindex, softforks, chainman, Consensus::DEPLOYMENT_REDUCED_DATA); return softforks; } } // anon namespace diff --git a/src/rpc/mempool.cpp b/src/rpc/mempool.cpp index e26de95b0863..43a0b01a0e99 100644 --- a/src/rpc/mempool.cpp +++ b/src/rpc/mempool.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -179,6 +180,7 @@ static RPCHelpMan testmempoolaccept() {RPCResult::Type::BOOL, "allowed", /*optional=*/true, "Whether this tx would be accepted to the mempool and pass client-specified maxfeerate. " "If not present, the tx was not fully validated due to a failure in another tx in the list."}, {RPCResult::Type::NUM, "vsize", /*optional=*/true, "Virtual transaction size as defined in BIP 141. This is different from actual serialized size for witness transactions as witness data is discounted (only present when 'allowed' is true)"}, + {RPCResult::Type::NUM, "usage", "Memory usage of transaction for this node"}, {RPCResult::Type::OBJ, "fees", /*optional=*/true, "Transaction fees (only present if 'allowed' is true)", { {RPCResult::Type::STR_AMOUNT, "base", "transaction fee in " + CURRENCY_UNIT}, @@ -254,6 +256,7 @@ static RPCHelpMan testmempoolaccept() UniValue result_inner(UniValue::VOBJ); result_inner.pushKV("txid", tx->GetHash().GetHex()); result_inner.pushKV("wtxid", tx->GetWitnessHash().GetHex()); + result_inner.pushKV("usage", RecursiveDynamicUsage(tx)); if (package_result.m_state.GetResult() == PackageValidationResult::PCKG_POLICY) { result_inner.pushKV("package-error", package_result.m_state.ToString()); } diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index eb885657cbe2..cf9f5cf10fd0 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -1020,12 +1020,14 @@ static UniValue TemplateToJSON(const Consensus::Params& consensusParams, const C } UniValue vbavailable(UniValue::VOBJ); + uint32_t vbrequired = 0; for (int j = 0; j < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++j) { Consensus::DeploymentPos pos = Consensus::DeploymentPos(j); ThresholdState state = chainman.m_versionbitscache.State(pindexPrev, consensusParams, pos); switch (state) { case ThresholdState::DEFINED: case ThresholdState::FAILED: + case ThresholdState::EXPIRED: // Not exposed to GBT at all break; case ThresholdState::LOCKED_IN: @@ -1036,6 +1038,9 @@ static UniValue TemplateToJSON(const Consensus::Params& consensusParams, const C { const struct VBDeploymentInfo& vbinfo = VersionBitsDeploymentInfo[pos]; vbavailable.pushKV(gbt_vb_name(pos), consensusParams.vDeployments[pos].bit); + if (DeploymentMustSignalAfter(pindexPrev, consensusParams, pos, state)) { + vbrequired |= chainman.m_versionbitscache.Mask(consensusParams, pos); + } if (setClientRules.find(vbinfo.name) == setClientRules.end()) { if (!vbinfo.gbt_force) { // If the client doesn't support this, don't indicate it in the [default] version @@ -1062,7 +1067,7 @@ static UniValue TemplateToJSON(const Consensus::Params& consensusParams, const C result.pushKV("version", block_header.nVersion); result.pushKV("rules", std::move(aRules)); result.pushKV("vbavailable", std::move(vbavailable)); - result.pushKV("vbrequired", int(0)); + result.pushKV("vbrequired", vbrequired); result.pushKV("previousblockhash", block.hashPrevBlock.GetHex()); result.pushKV("transactions", std::move(transactions)); diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index 8a903b4452e2..4d2b69b8c4f5 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -1057,7 +1057,7 @@ static RPCHelpMan addpeeraddress() if (net_addr.has_value()) { CService service{net_addr.value(), port}; - CAddress address{MaybeFlipIPv6toCJDNS(service), ServiceFlags{NODE_NETWORK | NODE_WITNESS}}; + CAddress address{MaybeFlipIPv6toCJDNS(service), ServiceFlags{NODE_NETWORK | NODE_WITNESS | NODE_REDUCED_DATA}}; address.nTime = Now(); // The source address is set equal to the address. This is equivalent to the peer // announcing itself. diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp index 88966b1a3c43..391780034ba7 100644 --- a/src/script/descriptor.cpp +++ b/src/script/descriptor.cpp @@ -1967,8 +1967,8 @@ std::vector> ParseScript(uint32_t& key_exp_index // First process all open braces. while (Const("{", expr)) { branches.push_back(false); // new left branch - if (branches.size() > TAPROOT_CONTROL_MAX_NODE_COUNT) { - error = strprintf("tr() supports at most %i nesting levels", TAPROOT_CONTROL_MAX_NODE_COUNT); + if (branches.size() > TAPROOT_CONTROL_MAX_NODE_COUNT_REDUCED) { + error = strprintf("tr() supports at most %i nesting levels", TAPROOT_CONTROL_MAX_NODE_COUNT_REDUCED); return {}; } } diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 7d32fec1f188..eab89bbdbc27 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -433,6 +433,8 @@ bool EvalScript(std::vector >& stack, const CScript& execdata.m_codeseparator_pos = 0xFFFFFFFFUL; execdata.m_codeseparator_pos_init = true; + const unsigned int max_element_size = (flags & SCRIPT_VERIFY_REDUCED_DATA) ? MAX_SCRIPT_ELEMENT_SIZE_REDUCED : MAX_SCRIPT_ELEMENT_SIZE; + try { for (; pc < pend; ++opcode_pos) { @@ -443,7 +445,7 @@ bool EvalScript(std::vector >& stack, const CScript& // if (!script.GetOp(pc, opcode, vchPushValue)) return set_error(serror, SCRIPT_ERR_BAD_OPCODE); - if (vchPushValue.size() > MAX_SCRIPT_ELEMENT_SIZE) + if (vchPushValue.size() > max_element_size) return set_error(serror, SCRIPT_ERR_PUSH_SIZE); if (sigversion == SigVersion::BASE || sigversion == SigVersion::WITNESS_V0) { @@ -616,6 +618,11 @@ bool EvalScript(std::vector >& stack, const CScript& if (vch.size() > 1 || (vch.size() == 1 && vch[0] != 1)) { return set_error(serror, SCRIPT_ERR_TAPSCRIPT_MINIMALIF); } + // REDUCED_DATA bans OP_IF/OP_NOTIF entirely in tapscript; + // reuses MINIMALIF error code as this is a stricter form of the same restriction + if (flags & SCRIPT_VERIFY_REDUCED_DATA) { + return set_error(serror, SCRIPT_ERR_TAPSCRIPT_MINIMALIF); + } } // Under witness v0 rules it is only a policy rule, enabled through SCRIPT_VERIFY_MINIMALIF. if (sigversion == SigVersion::WITNESS_V0 && (flags & SCRIPT_VERIFY_MINIMALIF)) { @@ -1858,8 +1865,9 @@ static bool ExecuteWitnessScript(const Span& stack_span, const CS } // Disallow stack item size > MAX_SCRIPT_ELEMENT_SIZE in witness stack + const unsigned int max_element_size = (flags & SCRIPT_VERIFY_REDUCED_DATA) ? MAX_SCRIPT_ELEMENT_SIZE_REDUCED : MAX_SCRIPT_ELEMENT_SIZE; for (const valtype& elem : stack) { - if (elem.size() > MAX_SCRIPT_ELEMENT_SIZE) return set_error(serror, SCRIPT_ERR_PUSH_SIZE); + if (elem.size() > max_element_size) return set_error(serror, SCRIPT_ERR_PUSH_SIZE); } // Run the script interpreter. @@ -1953,6 +1961,9 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, if (stack.size() >= 2 && !stack.back().empty() && stack.back()[0] == ANNEX_TAG) { // Drop annex (this is non-standard; see IsWitnessStandard) const valtype& annex = SpanPopBack(stack); + if (flags & SCRIPT_VERIFY_REDUCED_DATA) { + return set_error(serror, SCRIPT_ERR_PUSH_SIZE); + } execdata.m_annex_hash = (HashWriter{} << annex).GetSHA256(); execdata.m_annex_present = true; } else { @@ -1969,7 +1980,8 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, // Script path spending (stack size is >1 after removing optional annex) const valtype& control = SpanPopBack(stack); const valtype& script = SpanPopBack(stack); - if (control.size() < TAPROOT_CONTROL_BASE_SIZE || control.size() > TAPROOT_CONTROL_MAX_SIZE || ((control.size() - TAPROOT_CONTROL_BASE_SIZE) % TAPROOT_CONTROL_NODE_SIZE) != 0) { + const unsigned int max_control_size = (flags & SCRIPT_VERIFY_REDUCED_DATA) ? TAPROOT_CONTROL_MAX_SIZE_REDUCED : TAPROOT_CONTROL_MAX_SIZE; + if (control.size() < TAPROOT_CONTROL_BASE_SIZE || control.size() > max_control_size || ((control.size() - TAPROOT_CONTROL_BASE_SIZE) % TAPROOT_CONTROL_NODE_SIZE) != 0) { return set_error(serror, SCRIPT_ERR_TAPROOT_WRONG_CONTROL_SIZE); } execdata.m_tapleaf_hash = ComputeTapleafHash(control[0] & TAPROOT_LEAF_MASK, script); @@ -1989,7 +2001,7 @@ static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, } return set_success(serror); } - } else if (!is_p2sh && CScript::IsPayToAnchor(witversion, program)) { + } else if (stack.empty() && !is_p2sh && CScript::IsPayToAnchor(witversion, program)) { return true; } else { if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM) { @@ -2018,6 +2030,12 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C // scriptSig and scriptPubKey must be evaluated sequentially on the same stack // rather than being simply concatenated (see CVE-2010-5141) std::vector > stack, stackCopy; + if (scriptPubKey.IsPayToScriptHash()) { + // Disable SCRIPT_VERIFY_REDUCED_DATA for pushing the P2SH redeemScript + if (!EvalScript(stack, scriptSig, flags & ~SCRIPT_VERIFY_REDUCED_DATA, checker, SigVersion::BASE, serror)) + // serror is set + return false; + } else if (!EvalScript(stack, scriptSig, flags, checker, SigVersion::BASE, serror)) // serror is set return false; @@ -2069,6 +2087,15 @@ bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const C CScript pubKey2(pubKeySerialized.begin(), pubKeySerialized.end()); popstack(stack); + if (flags & SCRIPT_VERIFY_REDUCED_DATA) { + // We bypassed the reduced data check above to exempt redeemScript + // Now enforce it on the rest of the stack items here + // This is sufficient because P2SH requires scriptSig to be push-only + for (const valtype& elem : stack) { + if (elem.size() > MAX_SCRIPT_ELEMENT_SIZE_REDUCED) return set_error(serror, SCRIPT_ERR_PUSH_SIZE); + } + } + if (!EvalScript(stack, pubKey2, flags, checker, SigVersion::BASE, serror)) // serror is set return false; diff --git a/src/script/interpreter.h b/src/script/interpreter.h index b56933c64456..392914b71f5d 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -143,11 +143,25 @@ enum : uint32_t { // Making unknown public key versions (in BIP 342 scripts) non-standard SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE = (1U << 20), + // Enforce MAX_SCRIPT_ELEMENT_SIZE_REDUCED instead of MAX_SCRIPT_ELEMENT_SIZE + // The P2SH redeemScript push is exempted + // Taproot control blocks are limited to TAPROOT_CONTROL_MAX_SIZE_REDUCED + // Taproot annex is also invalid + // OP_IF is also forbidden inside Tapscript + SCRIPT_VERIFY_REDUCED_DATA = (1U << 21), + // Constants to point to the highest flag in use. Add new flags above this line. // SCRIPT_VERIFY_END_MARKER }; +static constexpr unsigned int REDUCED_DATA_MANDATORY_VERIFY_FLAGS{0 + | SCRIPT_VERIFY_REDUCED_DATA + | SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM + | SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION + | SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS +}; + bool CheckSignatureEncoding(const std::vector &vchSig, unsigned int flags, ScriptError* serror); struct PrecomputedTransactionData @@ -234,6 +248,8 @@ static constexpr size_t TAPROOT_CONTROL_BASE_SIZE = 33; static constexpr size_t TAPROOT_CONTROL_NODE_SIZE = 32; static constexpr size_t TAPROOT_CONTROL_MAX_NODE_COUNT = 128; static constexpr size_t TAPROOT_CONTROL_MAX_SIZE = TAPROOT_CONTROL_BASE_SIZE + TAPROOT_CONTROL_NODE_SIZE * TAPROOT_CONTROL_MAX_NODE_COUNT; +static constexpr size_t TAPROOT_CONTROL_MAX_NODE_COUNT_REDUCED = 7; +static constexpr size_t TAPROOT_CONTROL_MAX_SIZE_REDUCED = TAPROOT_CONTROL_BASE_SIZE + TAPROOT_CONTROL_NODE_SIZE * TAPROOT_CONTROL_MAX_NODE_COUNT_REDUCED; extern const HashWriter HASHER_TAPSIGHASH; //!< Hasher with tag "TapSighash" pre-fed to it. extern const HashWriter HASHER_TAPLEAF; //!< Hasher with tag "TapLeaf" pre-fed to it. diff --git a/src/script/script.h b/src/script/script.h index f38d15811953..2e532f9c5500 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -26,6 +26,7 @@ // Maximum number of bytes pushable to the stack static const unsigned int MAX_SCRIPT_ELEMENT_SIZE = 520; +static const unsigned int MAX_SCRIPT_ELEMENT_SIZE_REDUCED = 256; // Maximum number of non-push operations per script static const int MAX_OPS_PER_SCRIPT = 201; diff --git a/src/script/signingprovider.cpp b/src/script/signingprovider.cpp index d029ee1a96e4..e2f85adfa075 100644 --- a/src/script/signingprovider.cpp +++ b/src/script/signingprovider.cpp @@ -365,7 +365,7 @@ void TaprootBuilder::Insert(TaprootBuilder::NodeInfo&& node, int depth) // as what Insert() performs on the m_branch variable. Instead of // storing a NodeInfo object, just remember whether or not there is one // at that depth. - if (depth < 0 || (size_t)depth > TAPROOT_CONTROL_MAX_NODE_COUNT) return false; + if (depth < 0 || (size_t)depth > TAPROOT_CONTROL_MAX_NODE_COUNT_REDUCED) return false; if ((size_t)depth + 1 < branch.size()) return false; while (branch.size() > (size_t)depth && branch[depth]) { branch.pop_back(); @@ -478,7 +478,7 @@ std::optional, int>>> Inf // Skip script records with nonsensical leaf version. if (leaf_ver < 0 || leaf_ver >= 0x100 || leaf_ver & 1) continue; // Skip script records with invalid control block sizes. - if (control.size() < TAPROOT_CONTROL_BASE_SIZE || control.size() > TAPROOT_CONTROL_MAX_SIZE || + if (control.size() < TAPROOT_CONTROL_BASE_SIZE || control.size() > TAPROOT_CONTROL_MAX_SIZE_REDUCED || ((control.size() - TAPROOT_CONTROL_BASE_SIZE) % TAPROOT_CONTROL_NODE_SIZE) != 0) continue; // Skip script records that don't match the control block. if ((control[0] & TAPROOT_LEAF_MASK) != leaf_ver) continue; diff --git a/src/test/data/tx_valid.json b/src/test/data/tx_valid.json index 70df0d0f697d..547deefe2c20 100644 --- a/src/test/data/tx_valid.json +++ b/src/test/data/tx_valid.json @@ -414,9 +414,9 @@ ["0000000000000000000000000000000000000000000000000000000000000100", 2, "0x51", 3000]], "0100000000010300010000000000000000000000000000000000000000000000000000000000000000000000ffffffff00010000000000000000000000000000000000000000000000000000000000000100000000ffffffff00010000000000000000000000000000000000000000000000000000000000000200000000ffffffff03e8030000000000000151d0070000000000000151b80b00000000000001510002483045022100a3cec69b52cba2d2de623ffffffffff1606184ea55476c0f8189fda231bc9cbb022003181ad597f7c380a7d1c740286b1d022b8b04ded028b833282e055e03b8efef812103596d3451025c19dbbdeb932d6bf8bfb4ad499b95b6f88db8899efac102e5fc710000000000", "DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM"], -["Witness with a push of 520 bytes"], -[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x00 0x20 0x33198a9bfef674ebddb9ffaa52928017b8472791e54c609cb95f278ac6b1e349", 1000]], -"0100000000010100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff010000000000000000015102fd08020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002755100000000", "NONE"], +["Witness with a push of 256 bytes (REDUCED_DATA limit)"], +[[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x00 0x20 0xa57e25ffadd285772f5627ec6fa613bc8fb49b4db475c371dfd4eb76f25c5073", 1000]], +"0100000000010100010000000000000000000000000000000000000000000000000000000000000000000000ffffffff010000000000000000015101fd05014d000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000755100000000", "NONE"], ["Transaction mixing all SigHash, segwit and normal inputs"], [[["0000000000000000000000000000000000000000000000000000000000000100", 0, "0x00 0x14 0x4c9c3dfac4207d5d8cb89df5722cb3d712385e3f", 1000], diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp index 3bb164eac9e8..4ecc27608185 100644 --- a/src/test/denialofservice_tests.cpp +++ b/src/test/denialofservice_tests.cpp @@ -68,8 +68,8 @@ BOOST_AUTO_TEST_CASE(outbound_slow_chain_eviction) connman.Handshake( /*node=*/dummyNode1, /*successfully_connected=*/true, - /*remote_services=*/ServiceFlags(NODE_NETWORK | NODE_WITNESS), - /*local_services=*/ServiceFlags(NODE_NETWORK | NODE_WITNESS), + /*remote_services=*/ServiceFlags(NODE_NETWORK | NODE_WITNESS | NODE_REDUCED_DATA), + /*local_services=*/ServiceFlags(NODE_NETWORK | NODE_WITNESS | NODE_REDUCED_DATA), /*version=*/PROTOCOL_VERSION, /*relay_txs=*/true); diff --git a/src/test/descriptor_tests.cpp b/src/test/descriptor_tests.cpp index 63c53a842c1f..223d2934acf5 100644 --- a/src/test/descriptor_tests.cpp +++ b/src/test/descriptor_tests.cpp @@ -1006,7 +1006,8 @@ BOOST_AUTO_TEST_CASE(descriptor_test) CheckUnparsable("sh(and_v(vc:andor(pk(L4gM1FBdyHNpkzsFh9ipnofLhpZRp2mwobpeULy1a6dBTvw8Ywtd),pk_k(Kx9HCDjGiwFcgVNhTrS5z5NeZdD6veeam61eDxLDCkGWujvL4Gnn),and_v(v:older(1),pk_k(L4o2kDvXXDRH2VS9uBnouScLduWt4dZnM25se7kvEjJeQ285en2A))),after(10)))", "sh(and_v(vc:andor(pk(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204),pk_k(032707170c71d8f75e4ca4e3fce870b9409dcaf12b051d3bcadff74747fa7619c0),and_v(v:older(1),pk_k(02aa27e5eb2c185e87cd1dbc3e0efc9cb1175235e0259df1713424941c3cb40402))),after(10)))", "Miniscript expressions can only be used in wsh or tr."); CheckUnparsable("tr(and_v(vc:andor(pk(L4gM1FBdyHNpkzsFh9ipnofLhpZRp2mwobpeULy1a6dBTvw8Ywtd),pk_k(Kx9HCDjGiwFcgVNhTrS5z5NeZdD6veeam61eDxLDCkGWujvL4Gnn),and_v(v:older(1),pk_k(L4o2kDvXXDRH2VS9uBnouScLduWt4dZnM25se7kvEjJeQ285en2A))),after(10)))", "tr(and_v(vc:andor(pk(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204),pk_k(032707170c71d8f75e4ca4e3fce870b9409dcaf12b051d3bcadff74747fa7619c0),and_v(v:older(1),pk_k(02aa27e5eb2c185e87cd1dbc3e0efc9cb1175235e0259df1713424941c3cb40402))),after(10)))", "tr(): key 'and_v(vc:andor(pk(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204),pk_k(032707170c71d8f75e4ca4e3fce870b9409dcaf12b051d3bcadff74747fa7619c0),and_v(v:older(1),pk_k(02aa27e5eb2c185e87cd1dbc3e0efc9cb1175235e0259df1713424941c3cb40402))),after(10))' is not valid"); CheckUnparsable("raw(and_v(vc:andor(pk(L4gM1FBdyHNpkzsFh9ipnofLhpZRp2mwobpeULy1a6dBTvw8Ywtd),pk_k(Kx9HCDjGiwFcgVNhTrS5z5NeZdD6veeam61eDxLDCkGWujvL4Gnn),and_v(v:older(1),pk_k(L4o2kDvXXDRH2VS9uBnouScLduWt4dZnM25se7kvEjJeQ285en2A))),after(10)))", "sh(and_v(vc:andor(pk(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204),pk_k(032707170c71d8f75e4ca4e3fce870b9409dcaf12b051d3bcadff74747fa7619c0),and_v(v:older(1),pk_k(02aa27e5eb2c185e87cd1dbc3e0efc9cb1175235e0259df1713424941c3cb40402))),after(10)))", "Miniscript expressions can only be used in wsh or tr."); - CheckUnparsable("", "tr(034D2224bbbbbbbbbbcbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb40,{{{{{{{{{{{{{{{{{{{{{{multi(1,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/967808'/9,xprvA1RpRA33e1JQ7ifknakTFNpgXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/968/2/5/8/5/2/5/58/58/2/5/5/5/58/588/2/6/8/5/2/8/2/5/8/2/58/2/5/8/5/2/8/5/8/3/4/5/58/55/2/5/58/58/2/5/5/5/8/5/2/8/5/85/2/8/2/5/8/5/2/5/58/58/2/5/58/58/588/2/58/2/8/5/8/5/4/5/585/2/5/58/58/2/5/5/58/588/2/58/2/5/8/5/2/8/2/5/8/5/5/58/588/2/6/8/5/2/8/2/5/8/5/2/5/58/58/2/5/58/58/2/0/8/5/2/8/5/8/5/4/5/58/588/2/6/8/5/2/8/2/5/8/5/2/5/58/58/2/5/58/58/588/2/58/2/5/8/5/8/24/5/58/52/5/8/5/2/8/24/5/58/588/246/8/5/2/8/2/5/8/5/2/5/58/58/2/5/5/5/58/588/2/6/8/5/2/8/2/5/8/2/58/2/5/8/5/2/8/5/8/5/4/5/58/55/58/2/5/8/55/2/5/8/58/555/58/2/5/8/4//2/5/58/5w/2/5/8/5/2/4/5/58/5558'/2/5/58/58/2/5/5/58/588/2/58/2/5/8/5/2/8/2/5/8/5/5/8/58/2/5/58/58/2/5/8/9/588/2/58/2/5/8/5/2/8/5/8/5/4/5/58/588/2/6/8/5/2/8/2/5/8/5/2/5/58/58/2/5/5/58/588/2/58/2/5/8/5/2/82/5/8/5/5/58/52/6/8/5/2/8/{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{}{{{{{{{{{DDD2/5/8/5/2/5/58/58/2/5/58/58/588/2/58/2/8/5/8/5/4/5/58/588/2/6/8/5/2/8/2/5/8588/246/8/5/2DLDDDDDDDbbD3DDDD/8/2/5/8/5/2/5/58/58/2/5/5/5/58/588/2/6/8/5/2/8/2/5/8/2/58/2/5/8/5/2/8/5/8/3/4/5/58/55/2/5/58/58/2/5/5/5/8/5/2/8/5/85/2/8/2/5/8D)/5/2/5/58/58/2/5/58/58/58/588/2/58/2/5/8/5/25/58/58/2/5/58/58/2/5/8/9/588/2/58/2/6780,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFW/8/5/2/5/58678008')", "'multi(1,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/967808'/9,xprvA1RpRA33e1JQ7ifknakTFNpgXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/968/2/5/8/5/2/5/58/58/2/5/5/5/58/588/2/6/8/5/2/8/2/5/8/2/58/2/5/8/5/2/8/5/8/3/4/5/58/55/2/5/58/58/2/5/5/5/8/5/2/8/5/85/2/8/2/5/8/5/2/5/58/58/2/5/58/58/588/2/58/2/8/5/8/5/4/5/585/2/5/58/58/2/5/5/58/588/2/58/2/5/8/5/2/8/2/5/8/5/5/58/588/2/6/8/5/2/8/2/5/8/5/2/5/58/58/2/5/58/58/2/0/8/5/2/8/5/8/5/4/5/58/588/2/6/8/5/2/8/2/5/8/5/2/5/58/58/2/5/58/58/588/2/58/2/5/8/5/8/24/5/58/52/5/8/5/2/8/24/5/58/588/246/8/5/2/8/2/5/8/5/2/5/58/58/2/5/5/5/58/588/2/6/8/5/2/8/2/5/8/2/58/2/5/8/5/2/8/5/8/5/4/5/58/55/58/2/5/8/55/2/5/8/58/555/58/2/5/8/4//2/5/58/5w/2/5/8/5/2/4/5/58/5558'/2/5/58/58/2/5/5/58/588/2/58/2/5/8/5/2/8/2/5/8/5/5/8/58/2/5/58/58/2/5/8/9/588/2/58/2/5/8/5/2/8/5/8/5/4/5/58/588/2/6/8/5/2/8/2/5/8/5/2/5/58/58/2/5/5/58/588/2/58/2/5/8/5/2/82/5/8/5/5/58/52/6/8/5/2/8/{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{}{{{{{{{{{DDD2/5/8/5/2/5/58/58/2/5/58/58/588/2/58/2/8/5/8/5/4/5/58/588/2/6/8/5/2/8/2/5/8588/246/8/5/2DLDDDDDDDbbD3DDDD/8/2/5/8/5/2/5/58/58/2/5/5/5/58/588/2/6/8/5/2/8/2/5/8/2/58/2/5/8/5/2/8/5/8/3/4/5/58/55/2/5/58/58/2/5/5/5/8/5/2/8/5/85/2/8/2/5/8D)/5/2/5/58/58/2/5/58/58/58/588/2/58/2/5/8/5/25/58/58/2/5/58/58/2/5/8/9/588/2/58/2/6780,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFW/8/5/2/5/58678008'' is not a valid descriptor function"); + // REDUCED_DATA limits Taproot nesting to 7 levels, so this test now hits that limit before the multi() error + CheckUnparsable("", "tr(034D2224bbbbbbbbbbcbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb40,{{{{{{{{{{{{{{{{{{{{{{multi(1,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/967808'/9,xprvA1RpRA33e1JQ7ifknakTFNpgXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc/968/2/5/8/5/2/5/58/58/2/5/5/5/58/588/2/6/8/5/2/8/2/5/8/2/58/2/5/8/5/2/8/5/8/3/4/5/58/55/2/5/58/58/2/5/5/5/8/5/2/8/5/85/2/8/2/5/8/5/2/5/58/58/2/5/58/58/588/2/58/2/8/5/8/5/4/5/585/2/5/58/58/2/5/5/58/588/2/58/2/5/8/5/2/8/2/5/8/5/5/58/588/2/6/8/5/2/8/2/5/8/5/2/5/58/58/2/5/58/58/2/0/8/5/2/8/5/8/5/4/5/58/588/2/6/8/5/2/8/2/5/8/5/2/5/58/58/2/5/58/58/588/2/58/2/5/8/5/8/24/5/58/52/5/8/5/2/8/24/5/58/588/246/8/5/2/8/2/5/8/5/2/5/58/58/2/5/5/5/58/588/2/6/8/5/2/8/2/5/8/2/58/2/5/8/5/2/8/5/8/5/4/5/58/55/58/2/5/8/55/2/5/8/58/555/58/2/5/8/4//2/5/58/5w/2/5/8/5/2/4/5/58/5558'/2/5/58/58/2/5/5/58/588/2/58/2/5/8/5/2/8/2/5/8/5/5/8/58/2/5/58/58/2/5/8/9/588/2/58/2/5/8/5/2/8/5/8/5/4/5/58/588/2/6/8/5/2/8/2/5/8/5/2/5/58/58/2/5/5/58/588/2/58/2/5/8/5/2/82/5/8/5/5/58/52/6/8/5/2/8/{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{}{{{{{{{{{DDD2/5/8/5/2/5/58/58/2/5/58/58/588/2/58/2/8/5/8/5/4/5/58/588/2/6/8/5/2/8/2/5/8588/246/8/5/2DLDDDDDDDbbD3DDDD/8/2/5/8/5/2/5/58/58/2/5/5/5/58/588/2/6/8/5/2/8/2/5/8/2/58/2/5/8/5/2/8/5/8/3/4/5/58/55/2/5/58/58/2/5/5/5/8/5/2/8/5/85/2/8/2/5/8D)/5/2/5/58/58/2/5/58/58/58/588/2/58/2/5/8/5/25/58/58/2/5/58/58/2/5/8/9/588/2/58/2/6780,xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFW/8/5/2/5/58678008')", "tr() supports at most 7 nesting levels"); // No uncompressed keys allowed CheckUnparsable("", "wsh(and_v(vc:andor(pk(03cdabb7f2dce7bfbd8a0b9570c6fd1e712e5d64045e9d6b517b3d5072251dc204),pk_k(032707170c71d8f75e4ca4e3fce870b9409dcaf12b051d3bcadff74747fa7619c0),and_v(v:older(1),pk_k(049228de6902abb4f541791f6d7f925b10e2078ccb1298856e5ea5cc5fd667f930eac37a00cc07f9a91ef3c2d17bf7a17db04552ff90ac312a5b8b4caca6c97aa4))),after(10)))", "Uncompressed keys are not allowed"); // No hybrid keys allowed @@ -1047,7 +1048,8 @@ BOOST_AUTO_TEST_CASE(descriptor_test) Check("wsh(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),pk(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)))", "wsh(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", "wsh(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", SIGNABLE_FAILS, {{"0020cf62bf97baf977aec69cbc290c372899f913337a9093e8f066ab59b8657a365c"}}, OutputType::BECH32, /*op_desc_id=*/uint256{"8412ba3ac20ba3a30f81442d10d32e0468fa52814960d04e959bf84a9b813b88"}, {{}}, /*spender_nlocktime=*/0, /*spender_nsequence=*/CTxIn::SEQUENCE_FINAL, {}); Check("wsh(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),pk(xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc)))", "wsh(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", "wsh(and_v(v:hash256(ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588),pk(xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL)))", SIGNABLE, {{"0020cf62bf97baf977aec69cbc290c372899f913337a9093e8f066ab59b8657a365c"}}, OutputType::BECH32, /*op_desc_id=*/uint256{"8412ba3ac20ba3a30f81442d10d32e0468fa52814960d04e959bf84a9b813b88"}, {{}}, /*spender_nlocktime=*/0, /*spender_nsequence=*/CTxIn::SEQUENCE_FINAL, {{"ae253ca2a54debcac7ecf414f6734f48c56421a08bb59182ff9f39a6fffdb588"_hex_v_u8, "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"_hex_v_u8}}); // Can have a Miniscript expression under tr() if it's alone. - Check("tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,thresh(2,pk(L1NKM8dVA1h52mwDrmk1YreTWkAZZTu2vmKLpmLEbFRqGQYjHeEV),s:pk(Kz3iCBy3HNGP5CZWDsAMmnCMFNwqdDohudVN9fvkrN7tAkzKNtM7),adv:older(42)))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,thresh(2,pk(30a6069f344fb784a2b4c99540a91ee727c91e3a25ef6aae867d9c65b5f23529),s:pk(9918d400c1b8c3c478340a40117ced4054b6b58f48cdb3c89b836bdfee1f5766),adv:older(42)))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,thresh(2,pk(30a6069f344fb784a2b4c99540a91ee727c91e3a25ef6aae867d9c65b5f23529),s:pk(9918d400c1b8c3c478340a40117ced4054b6b58f48cdb3c89b836bdfee1f5766),adv:older(42)))", MISSING_PRIVKEYS | XONLY_KEYS | SIGNABLE, {{"512033982eebe204dc66508e4b19cfc31b5ffc6e1bfcbf6e5597dfc2521a52270795"}}, OutputType::BECH32M); + // Note: thresh() uses OP_IF which is forbidden with REDUCED_DATA, so using and_v() instead + Check("tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,and_v(v:pk(L1NKM8dVA1h52mwDrmk1YreTWkAZZTu2vmKLpmLEbFRqGQYjHeEV),pk(Kz3iCBy3HNGP5CZWDsAMmnCMFNwqdDohudVN9fvkrN7tAkzKNtM7)))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,and_v(v:pk(30a6069f344fb784a2b4c99540a91ee727c91e3a25ef6aae867d9c65b5f23529),pk(9918d400c1b8c3c478340a40117ced4054b6b58f48cdb3c89b836bdfee1f5766)))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,and_v(v:pk(30a6069f344fb784a2b4c99540a91ee727c91e3a25ef6aae867d9c65b5f23529),pk(9918d400c1b8c3c478340a40117ced4054b6b58f48cdb3c89b836bdfee1f5766)))", MISSING_PRIVKEYS | XONLY_KEYS | SIGNABLE, {{"51202aca0fdcbfbc513549e2c9490e60ba54e3c345ff01d667c4f846c802c0e7b8f4"}}, OutputType::BECH32M); // Can have a pkh() expression alone as tr() script path (because pkh() is valid Miniscript). Check("tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,pkh(L1NKM8dVA1h52mwDrmk1YreTWkAZZTu2vmKLpmLEbFRqGQYjHeEV))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,pkh(30a6069f344fb784a2b4c99540a91ee727c91e3a25ef6aae867d9c65b5f23529))", "tr(a34b99f22c790c4e36b2b3c2c35a36db06226e41c692fc82b8b56ac1c540c5bd,pkh(30a6069f344fb784a2b4c99540a91ee727c91e3a25ef6aae867d9c65b5f23529))", MISSING_PRIVKEYS | XONLY_KEYS | SIGNABLE, {{"51201e9875f690f5847404e4c5951e2f029887df0525691ee11a682afd37b608aad4"}}, OutputType::BECH32M); // Can have a Miniscript expression under tr() if it's part of a tree. diff --git a/src/test/fuzz/coins_view.cpp b/src/test/fuzz/coins_view.cpp index 06145e0323b1..6cf72784204c 100644 --- a/src/test/fuzz/coins_view.cpp +++ b/src/test/fuzz/coins_view.cpp @@ -256,7 +256,7 @@ FUZZ_TARGET(coins_view, .init = initialize_coins_view) // It is not allowed to call CheckTxInputs if CheckTransaction failed return; } - if (Consensus::CheckTxInputs(transaction, state, coins_view_cache, fuzzed_data_provider.ConsumeIntegralInRange(0, std::numeric_limits::max()), tx_fee_out)) { + if (Consensus::CheckTxInputs(transaction, state, coins_view_cache, fuzzed_data_provider.ConsumeIntegralInRange(0, std::numeric_limits::max()), tx_fee_out, CheckTxInputsRules::OutputSizeLimit)) { assert(MoneyRange(tx_fee_out)); } }, diff --git a/src/test/fuzz/miniscript.cpp b/src/test/fuzz/miniscript.cpp index 60d096bb5a99..132f9f9d4d28 100644 --- a/src/test/fuzz/miniscript.cpp +++ b/src/test/fuzz/miniscript.cpp @@ -1132,13 +1132,25 @@ void TestNode(const MsCtx script_ctx, const NodeRef& node, FuzzedDataProvider& p SatisfactionToWitness(script_ctx, witness_nonmal, script, builder); ScriptError serror; bool res = VerifyScript(DUMMY_SCRIPTSIG, script_pubkey, &witness_nonmal, STANDARD_SCRIPT_VERIFY_FLAGS, CHECKER_CTX, &serror); - // Non-malleable satisfactions are guaranteed to be valid if ValidSatisfactions(). - if (node->ValidSatisfactions()) assert(res); + // Non-malleable satisfactions are guaranteed to be valid if ValidSatisfactions(), unless REDUCED_DATA rules are violated. + if (node->ValidSatisfactions()) { + assert(res || + serror == ScriptError::SCRIPT_ERR_PUSH_SIZE || + serror == ScriptError::SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM || + serror == ScriptError::SCRIPT_ERR_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION || + serror == ScriptError::SCRIPT_ERR_DISCOURAGE_OP_SUCCESS || + serror == ScriptError::SCRIPT_ERR_TAPSCRIPT_MINIMALIF); + } // More detailed: non-malleable satisfactions must be valid, or could fail with ops count error (if CheckOpsLimit failed), - // or with a stack size error (if CheckStackSize check failed). + // or with a stack size error (if CheckStackSize check failed), or with REDUCED_DATA-related errors. assert(res || (!node->CheckOpsLimit() && serror == ScriptError::SCRIPT_ERR_OP_COUNT) || - (!node->CheckStackSize() && serror == ScriptError::SCRIPT_ERR_STACK_SIZE)); + (!node->CheckStackSize() && serror == ScriptError::SCRIPT_ERR_STACK_SIZE) || + serror == ScriptError::SCRIPT_ERR_PUSH_SIZE || + serror == ScriptError::SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM || + serror == ScriptError::SCRIPT_ERR_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION || + serror == ScriptError::SCRIPT_ERR_DISCOURAGE_OP_SUCCESS || + serror == ScriptError::SCRIPT_ERR_TAPSCRIPT_MINIMALIF); } if (mal_success && (!nonmal_success || witness_mal.stack != witness_nonmal.stack)) { @@ -1148,8 +1160,15 @@ void TestNode(const MsCtx script_ctx, const NodeRef& node, FuzzedDataProvider& p ScriptError serror; bool res = VerifyScript(DUMMY_SCRIPTSIG, script_pubkey, &witness_mal, STANDARD_SCRIPT_VERIFY_FLAGS, CHECKER_CTX, &serror); // Malleable satisfactions are not guaranteed to be valid under any conditions, but they can only - // fail due to stack or ops limits. - assert(res || serror == ScriptError::SCRIPT_ERR_OP_COUNT || serror == ScriptError::SCRIPT_ERR_STACK_SIZE); + // fail due to stack or ops limits, or REDUCED_DATA-related errors. + assert(res || + serror == ScriptError::SCRIPT_ERR_OP_COUNT || + serror == ScriptError::SCRIPT_ERR_STACK_SIZE || + serror == ScriptError::SCRIPT_ERR_PUSH_SIZE || + serror == ScriptError::SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM || + serror == ScriptError::SCRIPT_ERR_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION || + serror == ScriptError::SCRIPT_ERR_DISCOURAGE_OP_SUCCESS || + serror == ScriptError::SCRIPT_ERR_TAPSCRIPT_MINIMALIF); } if (node->IsSane()) { diff --git a/src/test/fuzz/versionbits.cpp b/src/test/fuzz/versionbits.cpp index c1b0f552ea04..4ec954a862db 100644 --- a/src/test/fuzz/versionbits.cpp +++ b/src/test/fuzz/versionbits.cpp @@ -32,15 +32,17 @@ class TestConditionChecker : public AbstractThresholdConditionChecker const int m_period; const int m_threshold; const int m_min_activation_height; + const int m_active_duration; const int m_bit; - TestConditionChecker(int64_t begin, int64_t end, int period, int threshold, int min_activation_height, int bit) - : m_begin{begin}, m_end{end}, m_period{period}, m_threshold{threshold}, m_min_activation_height{min_activation_height}, m_bit{bit} + TestConditionChecker(int64_t begin, int64_t end, int period, int threshold, int min_activation_height, int active_duration, int bit) + : m_begin{begin}, m_end{end}, m_period{period}, m_threshold{threshold}, m_min_activation_height{min_activation_height}, m_active_duration{active_duration}, m_bit{bit} { assert(m_period > 0); assert(0 <= m_threshold && m_threshold <= m_period); assert(0 <= m_bit && m_bit < 32 && m_bit < VERSIONBITS_NUM_BITS); assert(0 <= m_min_activation_height); + assert(m_active_duration > 0); } bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const override { return Condition(pindex->nVersion); } @@ -49,6 +51,7 @@ class TestConditionChecker : public AbstractThresholdConditionChecker int Period(const Consensus::Params& params) const override { return m_period; } int Threshold(const Consensus::Params& params) const override { return m_threshold; } int MinActivationHeight(const Consensus::Params& params) const override { return m_min_activation_height; } + int ActiveDuration(const Consensus::Params& params) const override { return m_active_duration; } ThresholdState GetStateFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateFor(pindexPrev, dummy_params, m_cache); } int GetStateSinceHeightFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateSinceHeightFor(pindexPrev, dummy_params, m_cache); } @@ -168,8 +171,9 @@ FUZZ_TARGET(versionbits, .init = initialize) timeout = fuzzed_data_provider.ConsumeBool() ? Consensus::BIP9Deployment::NO_TIMEOUT : fuzzed_data_provider.ConsumeIntegral(); } int min_activation = fuzzed_data_provider.ConsumeIntegralInRange(0, period * max_periods); + int active_duration = fuzzed_data_provider.ConsumeBool() ? std::numeric_limits::max() : (fuzzed_data_provider.ConsumeIntegralInRange(1, max_periods) * period); - TestConditionChecker checker(start_time, timeout, period, threshold, min_activation, bit); + TestConditionChecker checker(start_time, timeout, period, threshold, min_activation, active_duration, bit); // Early exit if the versions don't signal sensibly for the deployment if (!checker.Condition(ver_signal)) return; @@ -337,13 +341,22 @@ FUZZ_TARGET(versionbits, .init = initialize) assert(exp_state == ThresholdState::FAILED); } break; + case ThresholdState::EXPIRED: + assert(!always_active_test); + assert(active_duration < std::numeric_limits::max()); + assert(min_activation <= current_block->nHeight + 1); + assert(exp_state == ThresholdState::EXPIRED || exp_state == ThresholdState::ACTIVE); + if (exp_state == ThresholdState::ACTIVE) { + assert(since == exp_since + active_duration); // EXPIRED starts exactly active_duration blocks after ACTIVE started + } + break; default: assert(false); } if (blocks.size() >= period * max_periods) { // we chose the timeout (and block times) so that by the time we have this many blocks it's all over - assert(state == ThresholdState::ACTIVE || state == ThresholdState::FAILED); + assert(state == ThresholdState::ACTIVE || state == ThresholdState::FAILED || state == ThresholdState::EXPIRED); } if (always_active_test) { diff --git a/src/test/miniscript_tests.cpp b/src/test/miniscript_tests.cpp index 47fc45df4ac5..e7e32ea2b4a2 100644 --- a/src/test/miniscript_tests.cpp +++ b/src/test/miniscript_tests.cpp @@ -393,13 +393,25 @@ void TestSatisfy(const KeyConverter& converter, const std::string& testcase, con // Test non-malleable satisfaction. ScriptError serror; bool res = VerifyScript(CScript(), script_pubkey, &witness_nonmal, STANDARD_SCRIPT_VERIFY_FLAGS, checker, &serror); - // Non-malleable satisfactions are guaranteed to be valid if ValidSatisfactions(). - if (node->ValidSatisfactions()) BOOST_CHECK(res); + // Non-malleable satisfactions are guaranteed to be valid if ValidSatisfactions(), unless REDUCED_DATA rules are violated. + if (node->ValidSatisfactions()) { + BOOST_CHECK(res || + serror == ScriptError::SCRIPT_ERR_PUSH_SIZE || + serror == ScriptError::SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM || + serror == ScriptError::SCRIPT_ERR_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION || + serror == ScriptError::SCRIPT_ERR_DISCOURAGE_OP_SUCCESS || + serror == ScriptError::SCRIPT_ERR_TAPSCRIPT_MINIMALIF); + } // More detailed: non-malleable satisfactions must be valid, or could fail with ops count error (if CheckOpsLimit failed), - // or with a stack size error (if CheckStackSize check fails). + // or with a stack size error (if CheckStackSize check fails), or with REDUCED_DATA-related errors. BOOST_CHECK(res || (!node->CheckOpsLimit() && serror == ScriptError::SCRIPT_ERR_OP_COUNT) || - (!node->CheckStackSize() && serror == ScriptError::SCRIPT_ERR_STACK_SIZE)); + (!node->CheckStackSize() && serror == ScriptError::SCRIPT_ERR_STACK_SIZE) || + (serror == ScriptError::SCRIPT_ERR_PUSH_SIZE) || + (serror == ScriptError::SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM) || + (serror == ScriptError::SCRIPT_ERR_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION) || + (serror == ScriptError::SCRIPT_ERR_DISCOURAGE_OP_SUCCESS) || + (serror == ScriptError::SCRIPT_ERR_TAPSCRIPT_MINIMALIF)); } if (mal_success && (!nonmal_success || witness_mal.stack != witness_nonmal.stack)) { @@ -407,8 +419,15 @@ void TestSatisfy(const KeyConverter& converter, const std::string& testcase, con ScriptError serror; bool res = VerifyScript(CScript(), script_pubkey, &witness_mal, STANDARD_SCRIPT_VERIFY_FLAGS, checker, &serror); // Malleable satisfactions are not guaranteed to be valid under any conditions, but they can only - // fail due to stack or ops limits. - BOOST_CHECK(res || serror == ScriptError::SCRIPT_ERR_OP_COUNT || serror == ScriptError::SCRIPT_ERR_STACK_SIZE); + // fail due to stack or ops limits, or REDUCED_DATA-related errors. + BOOST_CHECK(res || + serror == ScriptError::SCRIPT_ERR_OP_COUNT || + serror == ScriptError::SCRIPT_ERR_STACK_SIZE || + serror == ScriptError::SCRIPT_ERR_PUSH_SIZE || + serror == ScriptError::SCRIPT_ERR_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM || + serror == ScriptError::SCRIPT_ERR_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION || + serror == ScriptError::SCRIPT_ERR_DISCOURAGE_OP_SUCCESS || + serror == ScriptError::SCRIPT_ERR_TAPSCRIPT_MINIMALIF); } if (node->IsSane()) { diff --git a/src/test/net_tests.cpp b/src/test/net_tests.cpp index 69e9e887e66d..78eeea0aded1 100644 --- a/src/test/net_tests.cpp +++ b/src/test/net_tests.cpp @@ -865,7 +865,7 @@ BOOST_AUTO_TEST_CASE(initial_advertise_from_version_message) /*inbound_onion=*/false, /*network_key=*/2}; - const uint64_t services{NODE_NETWORK | NODE_WITNESS}; + const uint64_t services{NODE_NETWORK | NODE_WITNESS | NODE_REDUCED_DATA}; const int64_t time{0}; // Force ChainstateManager::IsInitialBlockDownload() to return false. @@ -873,7 +873,7 @@ BOOST_AUTO_TEST_CASE(initial_advertise_from_version_message) auto& chainman = static_cast(*m_node.chainman); chainman.JumpOutOfIbd(); - m_node.peerman->InitializeNode(peer, NODE_NETWORK); + m_node.peerman->InitializeNode(peer, ServiceFlags(NODE_NETWORK | NODE_REDUCED_DATA)); std::atomic interrupt_dummy{false}; std::chrono::microseconds time_received_dummy{0}; diff --git a/src/test/peerman_tests.cpp b/src/test/peerman_tests.cpp index e4fa6d20c90f..052e8bf354f6 100644 --- a/src/test/peerman_tests.cpp +++ b/src/test/peerman_tests.cpp @@ -35,6 +35,7 @@ BOOST_AUTO_TEST_CASE(connections_desirable_service_flags) auto consensus = m_node.chainman->GetParams().GetConsensus(); // Check we start connecting to full nodes + // Note: NODE_REDUCED_DATA requirement is enforced separately in VERSION processing ServiceFlags peer_flags{NODE_WITNESS | NODE_NETWORK_LIMITED}; BOOST_CHECK(peerman->GetDesirableServiceFlags(peer_flags) == ServiceFlags(NODE_NETWORK | NODE_WITNESS)); diff --git a/src/test/script_standard_tests.cpp b/src/test/script_standard_tests.cpp index e9ce82ca8a6c..042a6c6275d8 100644 --- a/src/test/script_standard_tests.cpp +++ b/src/test/script_standard_tests.cpp @@ -385,9 +385,10 @@ BOOST_AUTO_TEST_CASE(script_standard_taproot_builder) BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({2,2,0}), false); BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({2,2,1}), true); BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({2,2,2}), false); - BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({2,2,2,3,4,5,6,7,8,9,10,11,12,14,14,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,31,31,31,31,31,31,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,128}), true); - BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({128,128,127,126,125,124,123,122,121,120,119,118,117,116,115,114,113,112,111,110,109,108,107,106,105,104,103,102,101,100,99,98,97,96,95,94,93,92,91,90,89,88,87,86,85,84,83,82,81,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1}), true); - BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({129,129,128,127,126,125,124,123,122,121,120,119,118,117,116,115,114,113,112,111,110,109,108,107,106,105,104,103,102,101,100,99,98,97,96,95,94,93,92,91,90,89,88,87,86,85,84,83,82,81,80,79,78,77,76,75,74,73,72,71,70,69,68,67,66,65,64,63,62,61,60,59,58,57,56,55,54,53,52,51,50,49,48,47,46,45,44,43,42,41,40,39,38,37,36,35,34,33,32,31,30,29,28,27,26,25,24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1}), false); + // REDUCED_DATA limits Taproot tree depth to 7 instead of 128 + BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({2,2,2,3,4,5,6,7,7}), true); + BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({7,7,6,5,4,3,2,1}), true); + BOOST_CHECK_EQUAL(TaprootBuilder::ValidDepths({8,8,7,6,5,4,3,2,1}), false); XOnlyPubKey key_inner{"79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"_hex_u8}; XOnlyPubKey key_1{"c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5"_hex_u8}; diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index f21f1f2ca2c8..58ed2a9c1be3 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -71,6 +71,7 @@ static std::map mapFlagNames = { {std::string("DISCOURAGE_UPGRADABLE_PUBKEYTYPE"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_PUBKEYTYPE}, {std::string("DISCOURAGE_OP_SUCCESS"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_OP_SUCCESS}, {std::string("DISCOURAGE_UPGRADABLE_TAPROOT_VERSION"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_TAPROOT_VERSION}, + {std::string("REDUCED_DATA"), (unsigned int)SCRIPT_VERIFY_REDUCED_DATA}, }; unsigned int ParseScriptFlags(std::string strFlags) diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp index d22b815fcd68..cf93d4702cb1 100644 --- a/src/test/txvalidationcache_tests.cpp +++ b/src/test/txvalidationcache_tests.cpp @@ -2,9 +2,11 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include #include #include #include +#include