diff --git a/doc/bips.md b/doc/bips.md index 07e5024864c6..b225e92ad2df 100644 --- a/doc/bips.md +++ b/doc/bips.md @@ -31,6 +31,7 @@ BIPs that are implemented by Bitcoin Core: * [`BIP 86`](https://github.com/bitcoin/bips/blob/master/bip-0086.mediawiki): Descriptor wallets by default use the Hierarchical Deterministic Wallet derivation proposed by BIP 86 since **v23.0** ([PR #22364](https://github.com/bitcoin/bitcoin/pull/22364)). * [`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 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/chainparams.cpp b/src/chainparams.cpp index 32c3bff73482..8286f93cfa70 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -68,8 +68,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{}; const auto start_time{ToIntegral(vDeploymentParams[1])}; @@ -91,13 +91,38 @@ void ReadRegTestArgs(const ArgsManager& args, CChainParams::RegTestOptions& opti } else { vbparams.min_activation_height = 0; } + if (vDeploymentParams.size() >= 5) { + const auto max_activation_height{ToIntegral(vDeploymentParams[4])}; + if (!max_activation_height) { + throw std::runtime_error(strprintf("Invalid max_activation_height (%s)", vDeploymentParams[4])); + } + vbparams.max_activation_height = *max_activation_height; + } + if (vDeploymentParams.size() >= 6) { + const auto active_duration{ToIntegral(vDeploymentParams[5])}; + if (!active_duration) { + throw std::runtime_error(strprintf("Invalid active_duration (%s)", vDeploymentParams[5])); + } + vbparams.active_duration = *active_duration; + } + if (vDeploymentParams.size() >= 7) { + const auto threshold{ToIntegral(vDeploymentParams[6])}; + if (!threshold) { + throw std::runtime_error(strprintf("Invalid threshold (%s)", vDeploymentParams[6])); + } + vbparams.threshold = *threshold; + } + // 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; - LogInfo("Setting version bits activation parameters for %s to start=%ld, timeout=%ld, min_activation_height=%d", - vDeploymentParams[0], vbparams.start_time, vbparams.timeout, vbparams.min_activation_height); + LogInfo("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", + 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 71b5fe2468d9..e6fb0b8ce7b7 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 93ee071fdc16..ee40fe0e79f0 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -34,6 +34,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 }; @@ -54,6 +55,10 @@ 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()}; /** Period of blocks to check signalling in (usually retarget period, ie params.DifficultyAdjustmentInterval()) */ uint32_t period{2016}; /** @@ -62,6 +67,9 @@ struct BIP9Deployment { * Examples: 1916 for 95%, 1512 for testchains. */ uint32_t threshold{1916}; + /** 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()}; /** 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 4efed70fd411..c50c14f26e86 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 ed44d435c1b9..e7e4323fdb38 100644 --- a/src/consensus/tx_verify.h +++ b/src/consensus/tx_verify.h @@ -18,14 +18,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 e1447d4dde23..f24616383e9d 100644 --- a/src/deploymentinfo.cpp +++ b/src/deploymentinfo.cpp @@ -17,6 +17,10 @@ const std::array Versi .name = "taproot", .gbt_optional_rule = true, }, + VBDeploymentInfo{ + .name = "reduced_data", + .gbt_optional_rule = true, + }, }; std::string DeploymentName(Consensus::BuriedDeployment dep) diff --git a/src/deploymentstatus.h b/src/deploymentstatus.h index 921c7c187735..ae93cefa75cf 100644 --- a/src/deploymentstatus.h +++ b/src/deploymentstatus.h @@ -49,4 +49,5 @@ inline bool DeploymentEnabled(const Consensus::Params& params, Consensus::Deploy return params.vDeployments[dep].nStartTime != Consensus::BIP9Deployment::NEVER_ACTIVE; } + #endif // BITCOIN_DEPLOYMENTSTATUS_H diff --git a/src/kernel/chainparams.cpp b/src/kernel/chainparams.cpp index 64809a75911e..fee696e9e198 100644 --- a/src/kernel/chainparams.cpp +++ b/src/kernel/chainparams.cpp @@ -16,6 +16,7 @@ #include #include