diff --git a/contrib/signet/miner b/contrib/signet/miner index 3c90fe96a1d8..cd1d56f66b08 100755 --- a/contrib/signet/miner +++ b/contrib/signet/miner @@ -19,7 +19,7 @@ PATH_BASE_TEST_FUNCTIONAL = os.path.abspath(os.path.join(PATH_BASE_CONTRIB_SIGNE sys.path.insert(0, PATH_BASE_TEST_FUNCTIONAL) from test_framework.blocktools import get_witness_script, script_BIP34_coinbase_height # noqa: E402 -from test_framework.messages import CBlock, CBlockHeader, COutPoint, CTransaction, CTxIn, CTxInWitness, CTxOut, from_binary, from_hex, ser_string, ser_uint256, tx_from_hex # noqa: E402 +from test_framework.messages import CBlock, CBlockHeader, COutPoint, CTransaction, CTxIn, CTxInWitness, CTxOut, from_binary, from_hex, ser_string, ser_uint256, tx_from_hex, MAX_SEQUENCE_NONFINAL # noqa: E402 from test_framework.psbt import PSBT, PSBTMap, PSBT_GLOBAL_UNSIGNED_TX, PSBT_IN_FINAL_SCRIPTSIG, PSBT_IN_FINAL_SCRIPTWITNESS, PSBT_IN_NON_WITNESS_UTXO, PSBT_IN_SIGHASH_TYPE # noqa: E402 from test_framework.script import CScript, CScriptOp # noqa: E402 @@ -102,7 +102,8 @@ def generate_psbt(tmpl, reward_spk, *, blocktime=None, poolid=None): scriptSig = CScript(b"" + scriptSig + CScriptOp.encode_op_pushdata(poolid)) cbtx = CTransaction() - cbtx.vin = [CTxIn(COutPoint(0, 0xffffffff), scriptSig, 0xffffffff)] + cbtx.nLockTime = tmpl["height"] - 1 + cbtx.vin = [CTxIn(COutPoint(0, 0xffffffff), scriptSig, MAX_SEQUENCE_NONFINAL)] cbtx.vout = [CTxOut(tmpl["coinbasevalue"], reward_spk)] cbtx.vin[0].nSequence = 2**32-2 cbtx.rehash() diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 41577b2ad6dd..c86802c22a7d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -148,6 +148,7 @@ add_library(bitcoin_common STATIC EXCLUDE_FROM_ALL common/system.cpp common/url.cpp compressor.cpp + consensus/tx_verify.cpp core_read.cpp core_write.cpp deploymentinfo.cpp @@ -222,7 +223,6 @@ add_library(bitcoin_node STATIC EXCLUDE_FROM_ALL bip324.cpp blockencodings.cpp blockfilter.cpp - consensus/tx_verify.cpp dbwrapper.cpp deploymentstatus.cpp flatfile.cpp diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 7cf28d69b420..f83992a16440 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -23,29 +23,8 @@ using util::SplitString; -void ReadSigNetArgs(const ArgsManager& args, CChainParams::SigNetOptions& options) -{ - if (!args.GetArgs("-signetseednode").empty()) { - options.seeds.emplace(args.GetArgs("-signetseednode")); - } - if (!args.GetArgs("-signetchallenge").empty()) { - const auto signet_challenge = args.GetArgs("-signetchallenge"); - if (signet_challenge.size() != 1) { - throw std::runtime_error("-signetchallenge cannot be multiple values."); - } - const auto val{TryParseHex(signet_challenge[0])}; - if (!val) { - throw std::runtime_error(strprintf("-signetchallenge must be hex, not '%s'.", signet_challenge[0])); - } - options.challenge.emplace(*val); - } -} - -void ReadRegTestArgs(const ArgsManager& args, CChainParams::RegTestOptions& options) +static void HandleDeploymentArgs(const ArgsManager& args, CChainParams::DeploymentOptions& options) { - if (auto value = args.GetBoolArg("-fastprune")) options.fastprune = *value; - if (HasTestOption(args, "bip94")) options.enforce_bip94 = true; - for (const std::string& arg : args.GetArgs("-testactivationheight")) { const auto found{arg.find('@')}; if (found == std::string::npos) { @@ -100,6 +79,43 @@ void ReadRegTestArgs(const ArgsManager& args, CChainParams::RegTestOptions& opti } } +void ReadMainNetArgs(const ArgsManager& args, CChainParams::MainNetOptions& options) +{ + HandleDeploymentArgs(args, options.dep_opts); +} + +void ReadTestNetArgs(const ArgsManager& args, CChainParams::TestNetOptions& options) +{ + HandleDeploymentArgs(args, options.dep_opts); +} + +void ReadSigNetArgs(const ArgsManager& args, CChainParams::SigNetOptions& options) +{ + if (!args.GetArgs("-signetseednode").empty()) { + options.seeds.emplace(args.GetArgs("-signetseednode")); + } + if (!args.GetArgs("-signetchallenge").empty()) { + const auto signet_challenge = args.GetArgs("-signetchallenge"); + if (signet_challenge.size() != 1) { + throw std::runtime_error("-signetchallenge cannot be multiple values."); + } + const auto val{TryParseHex(signet_challenge[0])}; + if (!val) { + throw std::runtime_error(strprintf("-signetchallenge must be hex, not '%s'.", signet_challenge[0])); + } + options.challenge.emplace(*val); + } + HandleDeploymentArgs(args, options.dep_opts); +} + +void ReadRegTestArgs(const ArgsManager& args, CChainParams::RegTestOptions& options) +{ + if (auto value = args.GetBoolArg("-fastprune")) options.fastprune = *value; + if (HasTestOption(args, "bip94")) options.enforce_bip94 = true; + + HandleDeploymentArgs(args, options.dep_opts); +} + static std::unique_ptr globalChainParams; const CChainParams &Params() { @@ -110,12 +126,21 @@ const CChainParams &Params() { std::unique_ptr CreateChainParams(const ArgsManager& args, const ChainType chain) { switch (chain) { - case ChainType::MAIN: - return CChainParams::Main(); - case ChainType::TESTNET: - return CChainParams::TestNet(); - case ChainType::TESTNET4: - return CChainParams::TestNet4(); + case ChainType::MAIN: { + auto opts = CChainParams::MainNetOptions{}; + ReadMainNetArgs(args, opts); + return CChainParams::Main(opts); + } + case ChainType::TESTNET: { + auto opts = CChainParams::TestNetOptions{}; + ReadTestNetArgs(args, opts); + return CChainParams::TestNet(opts); + } + case ChainType::TESTNET4: { + auto opts = CChainParams::TestNetOptions{}; + ReadTestNetArgs(args, opts); + return CChainParams::TestNet4(opts); + } case ChainType::SIGNET: { auto opts = CChainParams::SigNetOptions{}; ReadSigNetArgs(args, opts); diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp index 060d519d9207..72cd825a0231 100644 --- a/src/chainparamsbase.cpp +++ b/src/chainparamsbase.cpp @@ -16,10 +16,10 @@ void SetupChainParamsBaseOptions(ArgsManager& argsman) argsman.AddArg("-chain=", "Use the chain (default: main). Allowed values: " LIST_CHAIN_NAMES, ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS); argsman.AddArg("-regtest", "Enter regression test mode, which uses a special chain in which blocks can be solved instantly. " "This is intended for regression testing tools and app development. Equivalent to -chain=regtest.", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS); - argsman.AddArg("-testactivationheight=name@height.", "Set the activation height of 'name' (segwit, bip34, dersig, cltv, csv). (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); + argsman.AddArg("-testactivationheight=name@height.", "Set the activation height of 'name' (segwit, bip34, dersig, cltv, csv). (test-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); argsman.AddArg("-testnet", "Use the testnet3 chain. Equivalent to -chain=test. Support for testnet3 is deprecated and will be removed in an upcoming release. Consider moving to testnet4 now by using -testnet4.", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS); argsman.AddArg("-testnet4", "Use the testnet4 chain. Equivalent to -chain=testnet4.", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS); - argsman.AddArg("-vbparams=deployment:start:end[:min_activation_height]", "Use given start/end times and min_activation_height for specified version bits deployment (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS); + argsman.AddArg("-vbparams=deployment:start:end[:min_activation_height]", "Use given start/end times and min_activation_height for specified version bits deployment (test-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS); argsman.AddArg("-signet", "Use the signet chain. Equivalent to -chain=signet. Note that the network is defined by the -signetchallenge parameter", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS); argsman.AddArg("-signetchallenge", "Blocks must satisfy the given script to be considered valid (only for signet networks; defaults to the global default signet test network challenge)", ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::CHAINPARAMS); argsman.AddArg("-signetseednode", "Specify a seed node for the signet network, in the hostname[:port] format, e.g. sig.net:1234 (may be used multiple times to specify multiple seed nodes; defaults to the global default signet test network seed node(s))", ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::CHAINPARAMS); diff --git a/src/consensus/consensus.h b/src/consensus/consensus.h index cffe9cdafd79..f09bcf4849e4 100644 --- a/src/consensus/consensus.h +++ b/src/consensus/consensus.h @@ -27,11 +27,26 @@ static const size_t MIN_SERIALIZABLE_TRANSACTION_WEIGHT = WITNESS_SCALE_FACTOR * /** Interpret sequence numbers as relative lock-time constraints. */ static constexpr unsigned int LOCKTIME_VERIFY_SEQUENCE = (1 << 0); +/** + * Under BIP54, the first block in a difficulty adjustment period must not be more than 2 + * hours (7200 seconds) earlier than the last block of the previous period. + */ +static constexpr int64_t MAX_TIMEWARP_BIP54{2 * 60 * 60}; + /** * Maximum number of seconds that the timestamp of the first * block of a difficulty adjustment period is allowed to * be earlier than the last block of the previous period (BIP94). */ -static constexpr int64_t MAX_TIMEWARP = 600; +static constexpr int64_t MAX_TIMEWARP_TESTNET4 = 600; + +/** The maximum number of potentially executed legacy signature operations in a single tx */ +static constexpr unsigned int MAX_TX_BIP54_SIGOPS{2'500}; + +/** + * 64-byte transactions are invalid (BIP 54) due to serious flaws in the Merkle tree algorithm + * that make it so that such transactions may be re-interpreted as inner tree nodes. + */ +static constexpr unsigned int INVALID_TX_NONWITNESS_SIZE{64}; #endif // BITCOIN_CONSENSUS_CONSENSUS_H diff --git a/src/consensus/params.h b/src/consensus/params.h index dd29b9408e23..bde29fd09790 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_CONSENSUSCLEANUP, // Deployment of BIP 54 // NOTE: Also add new deployments to VersionBitsDeploymentInfo in deploymentinfo.cpp MAX_VERSION_BITS_DEPLOYMENTS }; diff --git a/src/consensus/tx_verify.cpp b/src/consensus/tx_verify.cpp index 95466b759cbb..398faef5804e 100644 --- a/src/consensus/tx_verify.cpp +++ b/src/consensus/tx_verify.cpp @@ -161,7 +161,33 @@ 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::CheckSigopsBIP54(const CTransaction& tx, const CCoinsViewCache& inputs) +{ + Assert(!tx.IsCoinBase()); + + unsigned int sigops{0}; + for (const auto& txin: tx.vin) { + const auto& prev_txo{inputs.AccessCoin(txin.prevout).out}; + + // Unlike the existing block wide sigop limit which counts sigops present in the block + // itself (including the scriptPubKey which is not executed until spending later), BIP54 + // counts sigops in the block where they are potentially executed (only). + // This means sigops in the spent scriptPubKey count toward the limit. + // `fAccurate` means correctly accounting sigops for CHECKMULTISIGs(VERIFY) with 16 pubkeys + // or fewer. This method of accounting was introduced by BIP16, and BIP54 reuses it. + // The GetSigOpCount call on the previous scriptPubKey counts both bare and P2SH sigops. + sigops += txin.scriptSig.GetSigOpCount(/*fAccurate=*/true); + sigops += prev_txo.scriptPubKey.GetSigOpCount(txin.scriptSig); + + if (sigops > MAX_TX_BIP54_SIGOPS) { + return false; + } + } + + return true; +} + +bool Consensus::CheckTxInputs(const CTransaction& tx, TxValidationState& state, const CCoinsViewCache& inputs, int nSpendHeight, CAmount& txfee, bool enforce_bip54) { // are the actual inputs available? if (!inputs.HaveInputs(tx)) { @@ -169,6 +195,10 @@ bool Consensus::CheckTxInputs(const CTransaction& tx, TxValidationState& state, strprintf("%s: inputs missing/spent", __func__)); } + if (enforce_bip54 && !Consensus::CheckSigopsBIP54(tx, inputs)) { + return state.Invalid(TxValidationResult::TX_CONSENSUS, "bad-txns-legacy-sigops", "too many legacy sigops (BIP54)"); + } + 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..1e3d0c71644d 100644 --- a/src/consensus/tx_verify.h +++ b/src/consensus/tx_verify.h @@ -18,13 +18,19 @@ class TxValidationState; /** Transaction validation functions */ namespace Consensus { +/** + * Check the total number of non-witness sigops across the whole transaction, as per BIP54. + */ +bool CheckSigopsBIP54(const CTransaction& tx, const CCoinsViewCache& inputs); + /** * 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. + * @param[in] enforce_bip54 Whether to perform the BIP54 sigops check. * 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, bool enforce_bip54); } // namespace Consensus /** Auxiliary functions for transaction validation (ideally should not be exposed) */ diff --git a/src/deploymentinfo.cpp b/src/deploymentinfo.cpp index 185a7dcb54ce..57a1fbdc083b 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 =*/ "consensuscleanup", + /*.gbt_force =*/ false, + }, }; std::string DeploymentName(Consensus::BuriedDeployment dep) diff --git a/src/init.cpp b/src/init.cpp index 70615a191be1..65d6b65620fa 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1081,6 +1081,16 @@ bool AppInitParameterInteraction(const ArgsManager& args) } } + // Prevent setting deployment parameters on mainnet. + if (chainparams.GetChainType() == ChainType::MAIN) { + if (args.IsArgSet("-testactivationheight")) { + return InitError(_("The -testactivationheight option may not be used on mainnet.")); + } + if (args.IsArgSet("-vbparams")) { + return InitError(_("The -vbparams option may not be used on mainnet.")); + } + } + // Also report errors from parsing before daemonization { kernel::Notifications notifications{}; diff --git a/src/kernel/chainparams.cpp b/src/kernel/chainparams.cpp index 0f193eff74d9..ef13e5c589f8 100644 --- a/src/kernel/chainparams.cpp +++ b/src/kernel/chainparams.cpp @@ -77,12 +77,41 @@ static CBlock CreateGenesisBlock(uint32_t nTime, uint32_t nNonce, uint32_t nBits return CreateGenesisBlock(pszTimestamp, genesisOutputScript, nTime, nNonce, nBits, nVersion, genesisReward); } +void CChainParams::ApplyDeploymentOptions(const DeploymentOptions& opts) +{ + for (const auto& [dep, height] : opts.activation_heights) { + switch (dep) { + case Consensus::BuriedDeployment::DEPLOYMENT_SEGWIT: + consensus.SegwitHeight = int{height}; + break; + case Consensus::BuriedDeployment::DEPLOYMENT_HEIGHTINCB: + consensus.BIP34Height = int{height}; + break; + case Consensus::BuriedDeployment::DEPLOYMENT_DERSIG: + consensus.BIP66Height = int{height}; + break; + case Consensus::BuriedDeployment::DEPLOYMENT_CLTV: + consensus.BIP65Height = int{height}; + break; + case Consensus::BuriedDeployment::DEPLOYMENT_CSV: + consensus.CSVHeight = int{height}; + break; + } + } + + for (const auto& [deployment_pos, version_bits_params] : opts.version_bits_parameters) { + 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; + } +} + /** * Main network on which people trade goods and services. */ class CMainParams : public CChainParams { public: - CMainParams() { + CMainParams(const MainNetOptions& opts) { m_chain_type = ChainType::MAIN; consensus.signet_blocks = false; consensus.signet_challenge.clear(); @@ -117,6 +146,13 @@ 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 + // Deployment of the Consensus Cleanup (BIP 54) + consensus.vDeployments[Consensus::DEPLOYMENT_CONSENSUSCLEANUP].bit = 3; + consensus.vDeployments[Consensus::DEPLOYMENT_CONSENSUSCLEANUP].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE; + consensus.vDeployments[Consensus::DEPLOYMENT_CONSENSUSCLEANUP].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; + + ApplyDeploymentOptions(opts.dep_opts); + consensus.nMinimumChainWork = uint256{"0000000000000000000000000000000000000000b1f3b93b65b16d035a82be84"}; consensus.defaultAssumeValid = uint256{"00000000000000000001b658dd1120e82e66d2790811f89ede9742ada3ed6d77"}; // 886157 @@ -213,7 +249,7 @@ class CMainParams : public CChainParams { */ class CTestNetParams : public CChainParams { public: - CTestNetParams() { + CTestNetParams(const TestNetOptions& opts) { m_chain_type = ChainType::TESTNET; consensus.signet_blocks = false; consensus.signet_challenge.clear(); @@ -246,6 +282,13 @@ 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 + // Deployment of the Consensus Cleanup (BIP 54) + consensus.vDeployments[Consensus::DEPLOYMENT_CONSENSUSCLEANUP].bit = 3; + consensus.vDeployments[Consensus::DEPLOYMENT_CONSENSUSCLEANUP].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE; + consensus.vDeployments[Consensus::DEPLOYMENT_CONSENSUSCLEANUP].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; + + ApplyDeploymentOptions(opts.dep_opts); + consensus.nMinimumChainWork = uint256{"0000000000000000000000000000000000000000000015f5e0c9f13455b0eb17"}; consensus.defaultAssumeValid = uint256{"00000000000003fc7967410ba2d0a8a8d50daedc318d43e8baf1a9782c236a57"}; // 3974606 @@ -314,7 +357,7 @@ class CTestNetParams : public CChainParams { */ class CTestNet4Params : public CChainParams { public: - CTestNet4Params() { + CTestNet4Params(const TestNetOptions& opts) { m_chain_type = ChainType::TESTNET4; consensus.signet_blocks = false; consensus.signet_challenge.clear(); @@ -345,6 +388,13 @@ 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 + // Deployment of the Consensus Cleanup (BIP 54) + consensus.vDeployments[Consensus::DEPLOYMENT_CONSENSUSCLEANUP].bit = 3; + consensus.vDeployments[Consensus::DEPLOYMENT_CONSENSUSCLEANUP].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE; + consensus.vDeployments[Consensus::DEPLOYMENT_CONSENSUSCLEANUP].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; + + ApplyDeploymentOptions(opts.dep_opts); + consensus.nMinimumChainWork = uint256{"0000000000000000000000000000000000000000000001d6dce8651b6094e4c1"}; consensus.defaultAssumeValid = uint256{"0000000000003ed4f08dbdf6f7d6b271a6bcffce25675cb40aa9fa43179a89f3"}; // 72600 @@ -483,6 +533,13 @@ 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 + // Deployment of the Consensus Cleanup (BIP 54) + consensus.vDeployments[Consensus::DEPLOYMENT_CONSENSUSCLEANUP].bit = 3; + consensus.vDeployments[Consensus::DEPLOYMENT_CONSENSUSCLEANUP].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE; + consensus.vDeployments[Consensus::DEPLOYMENT_CONSENSUSCLEANUP].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; + + ApplyDeploymentOptions(options.dep_opts); + // message start is defined as the first 4 bytes of the sha256d of the block script HashWriter h{}; h << consensus.signet_challenge; @@ -558,6 +615,12 @@ 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 + // Deployment of the Consensus Cleanup (BIP 54) + consensus.vDeployments[Consensus::DEPLOYMENT_CONSENSUSCLEANUP].bit = 3; + consensus.vDeployments[Consensus::DEPLOYMENT_CONSENSUSCLEANUP].nStartTime = Consensus::BIP9Deployment::ALWAYS_ACTIVE; + consensus.vDeployments[Consensus::DEPLOYMENT_CONSENSUSCLEANUP].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; + consensus.vDeployments[Consensus::DEPLOYMENT_CONSENSUSCLEANUP].min_activation_height = 0; // No activation delay + consensus.nMinimumChainWork = uint256{}; consensus.defaultAssumeValid = uint256{}; @@ -570,31 +633,7 @@ class CRegTestParams : public CChainParams m_assumed_blockchain_size = 0; m_assumed_chain_state_size = 0; - for (const auto& [dep, height] : opts.activation_heights) { - switch (dep) { - case Consensus::BuriedDeployment::DEPLOYMENT_SEGWIT: - consensus.SegwitHeight = int{height}; - break; - case Consensus::BuriedDeployment::DEPLOYMENT_HEIGHTINCB: - consensus.BIP34Height = int{height}; - break; - case Consensus::BuriedDeployment::DEPLOYMENT_DERSIG: - consensus.BIP66Height = int{height}; - break; - case Consensus::BuriedDeployment::DEPLOYMENT_CLTV: - consensus.BIP65Height = int{height}; - break; - case Consensus::BuriedDeployment::DEPLOYMENT_CSV: - consensus.CSVHeight = int{height}; - break; - } - } - - for (const auto& [deployment_pos, version_bits_params] : opts.version_bits_parameters) { - 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; - } + ApplyDeploymentOptions(opts.dep_opts); genesis = CreateGenesisBlock(1296688602, 2, 0x207fffff, 1, 50 * COIN); consensus.hashGenesisBlock = genesis.GetHash(); @@ -617,23 +656,23 @@ class CRegTestParams : public CChainParams m_assumeutxo_data = { { // For use by unit tests .height = 110, - .hash_serialized = AssumeutxoHash{uint256{"6657b736d4fe4db0cbc796789e812d5dba7f5c143764b1b6905612f1830609d1"}}, + .hash_serialized = AssumeutxoHash{uint256{"b952555c8ab81fec46f3d4253b7af256d766ceb39fb7752b9d18cdf4a0141327"}}, .m_chain_tx_count = 111, - .blockhash = consteval_ctor(uint256{"696e92821f65549c7ee134edceeeeaaa4105647a3c4fd9f298c0aec0ab50425c"}), + .blockhash = consteval_ctor(uint256{"6affe030b7965ab538f820a56ef56c8149b7dc1d1c144af57113be080db7c397"}), }, { // For use by fuzz target src/test/fuzz/utxo_snapshot.cpp .height = 200, - .hash_serialized = AssumeutxoHash{uint256{"4f34d431c3e482f6b0d67b64609ece3964dc8d7976d02ac68dd7c9c1421738f2"}}, + .hash_serialized = AssumeutxoHash{uint256{"17dcc016d188d16068907cdeb38b75691a118d43053b8cd6a25969419381d13a"}}, .m_chain_tx_count = 201, - .blockhash = consteval_ctor(uint256{"5e93653318f294fb5aa339d00bbf8cf1c3515488ad99412c37608b139ea63b27"}), + .blockhash = consteval_ctor(uint256{"385901ccbd69dff6bbd00065d01fb8a9e464dede7cfe0372443884f9b1dcf6b9"}), }, { // For use by test/functional/feature_assumeutxo.py .height = 299, - .hash_serialized = AssumeutxoHash{uint256{"a4bf3407ccb2cc0145c49ebba8fa91199f8a3903daf0883875941497d2493c27"}}, + .hash_serialized = AssumeutxoHash{uint256{"d2b051ff5e8eef46520350776f4100dd710a63447a8e01d917e92e79751a63e2"}}, .m_chain_tx_count = 334, - .blockhash = consteval_ctor(uint256{"3bb7ce5eba0be48939b7a521ac1ba9316afee2c7bada3a0cca24188e6d7d96c0"}), + .blockhash = consteval_ctor(uint256{"7cc695046fec709f8c9394b6f928f81e81fd3ac20977bb68760fa1faa7916ea2"}), }, }; @@ -663,19 +702,19 @@ std::unique_ptr CChainParams::RegTest(const RegTestOptions& return std::make_unique(options); } -std::unique_ptr CChainParams::Main() +std::unique_ptr CChainParams::Main(const MainNetOptions& options) { - return std::make_unique(); + return std::make_unique(options); } -std::unique_ptr CChainParams::TestNet() +std::unique_ptr CChainParams::TestNet(const TestNetOptions& options) { - return std::make_unique(); + return std::make_unique(options); } -std::unique_ptr CChainParams::TestNet4() +std::unique_ptr CChainParams::TestNet4(const TestNetOptions& options) { - return std::make_unique(); + return std::make_unique(options); } std::vector CChainParams::GetAvailableSnapshotHeights() const diff --git a/src/kernel/chainparams.h b/src/kernel/chainparams.h index f7ea141c37c3..4528cb843dec 100644 --- a/src/kernel/chainparams.h +++ b/src/kernel/chainparams.h @@ -131,14 +131,6 @@ class CChainParams const ChainTxData& TxData() const { return chainTxData; } - /** - * SigNetOptions holds configurations for creating a signet CChainParams. - */ - struct SigNetOptions { - std::optional> challenge{}; - std::optional> seeds{}; - }; - /** * VersionBitsParameters holds activation parameters */ @@ -148,21 +140,45 @@ class CChainParams int min_activation_height; }; + struct DeploymentOptions { + std::unordered_map version_bits_parameters{}; + std::unordered_map activation_heights{}; + }; + + /** + * SigNetOptions holds configurations for creating a signet CChainParams. + */ + struct SigNetOptions { + DeploymentOptions dep_opts{}; + std::optional> challenge{}; + std::optional> seeds{}; + }; + /** * RegTestOptions holds configurations for creating a regtest CChainParams. */ struct RegTestOptions { - std::unordered_map version_bits_parameters{}; - std::unordered_map activation_heights{}; + DeploymentOptions dep_opts{}; bool fastprune{false}; bool enforce_bip94{false}; }; + struct MainNetOptions { + DeploymentOptions dep_opts{}; + }; + + struct TestNetOptions { + DeploymentOptions dep_opts{}; + }; + static std::unique_ptr RegTest(const RegTestOptions& options); static std::unique_ptr SigNet(const SigNetOptions& options); - static std::unique_ptr Main(); - static std::unique_ptr TestNet(); - static std::unique_ptr TestNet4(); + static std::unique_ptr Main(const MainNetOptions& options); + static std::unique_ptr Main() { const MainNetOptions opts{}; return Main(opts); } + static std::unique_ptr TestNet(const TestNetOptions& options); + static std::unique_ptr TestNet() { const TestNetOptions opts{}; return TestNet(opts); } + static std::unique_ptr TestNet4(const TestNetOptions& options); + static std::unique_ptr TestNet4() { const TestNetOptions opts{}; return TestNet4(opts); } protected: CChainParams() = default; @@ -184,6 +200,8 @@ class CChainParams CCheckpointData checkpointData; std::vector m_assumeutxo_data; ChainTxData chainTxData; + + void ApplyDeploymentOptions(const DeploymentOptions& opts); }; std::optional GetNetworkForMagic(const MessageStartChars& pchMessageStart); diff --git a/src/node/miner.cpp b/src/node/miner.cpp index b9ecd855f0a6..c679020b5d0f 100644 --- a/src/node/miner.cpp +++ b/src/node/miner.cpp @@ -34,10 +34,10 @@ int64_t GetMinimumTime(const CBlockIndex* pindexPrev, const int64_t difficulty_a int64_t min_time{pindexPrev->GetMedianTimePast() + 1}; // Height of block to be mined. const int height{pindexPrev->nHeight + 1}; - // Account for BIP94 timewarp rule on all networks. This makes future - // activation safer. + // Account for BIP94 timewarp rule on all networks. This makes BIP54 + // activation safer since BIP94 sets a tighter bound. if (height % difficulty_adjustment_interval == 0) { - min_time = std::max(min_time, pindexPrev->GetBlockTime() - MAX_TIMEWARP); + min_time = std::max(min_time, pindexPrev->GetBlockTime() - MAX_TIMEWARP_TESTNET4); } return min_time; } @@ -159,10 +159,13 @@ std::unique_ptr BlockAssembler::CreateNewBlock() CMutableTransaction coinbaseTx; coinbaseTx.vin.resize(1); coinbaseTx.vin[0].prevout.SetNull(); + coinbaseTx.vin[0].nSequence = CTxIn::MAX_SEQUENCE_NONFINAL; // Make sure timelock is enforced. coinbaseTx.vout.resize(1); coinbaseTx.vout[0].scriptPubKey = m_options.coinbase_output_script; coinbaseTx.vout[0].nValue = nFees + GetBlockSubsidy(nHeight, chainparams.GetConsensus()); coinbaseTx.vin[0].scriptSig = CScript() << nHeight << OP_0; + Assert(nHeight > 0); + coinbaseTx.nLockTime = static_cast(nHeight - 1); pblock->vtx[0] = MakeTransactionRef(std::move(coinbaseTx)); pblocktemplate->vchCoinbaseCommitment = m_chainstate.m_chainman.GenerateCoinbaseCommitment(*pblock, pindexPrev); pblocktemplate->vTxFees[0] = -nFees; diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp index 5942747d6099..1d670dfa82df 100644 --- a/src/policy/policy.cpp +++ b/src/policy/policy.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -168,35 +169,6 @@ bool IsStandardTx(const CTransaction& tx, const std::optional& max_dat return true; } -/** - * Check the total number of non-witness sigops across the whole transaction, as per BIP54. - */ -static bool CheckSigopsBIP54(const CTransaction& tx, const CCoinsViewCache& inputs) -{ - Assert(!tx.IsCoinBase()); - - unsigned int sigops{0}; - for (const auto& txin: tx.vin) { - const auto& prev_txo{inputs.AccessCoin(txin.prevout).out}; - - // Unlike the existing block wide sigop limit which counts sigops present in the block - // itself (including the scriptPubKey which is not executed until spending later), BIP54 - // counts sigops in the block where they are potentially executed (only). - // This means sigops in the spent scriptPubKey count toward the limit. - // `fAccurate` means correctly accounting sigops for CHECKMULTISIGs(VERIFY) with 16 pubkeys - // or fewer. This method of accounting was introduced by BIP16, and BIP54 reuses it. - // The GetSigOpCount call on the previous scriptPubKey counts both bare and P2SH sigops. - sigops += txin.scriptSig.GetSigOpCount(/*fAccurate=*/true); - sigops += prev_txo.scriptPubKey.GetSigOpCount(txin.scriptSig); - - if (sigops > MAX_TX_LEGACY_SIGOPS) { - return false; - } - } - - return true; -} - /** * Check transaction inputs to mitigate two * potential denial-of-service attacks: @@ -214,8 +186,6 @@ static bool CheckSigopsBIP54(const CTransaction& tx, const CCoinsViewCache& inpu * DUP CHECKSIG DROP ... repeated 100 times... OP_1 * * Note that only the non-witness portion of the transaction is checked here. - * - * We also check the total number of non-witness sigops across the whole transaction, as per BIP54. */ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs) { @@ -223,10 +193,6 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs) return true; // Coinbases don't use vin normally } - if (!CheckSigopsBIP54(tx, mapInputs)) { - return false; - } - for (unsigned int i = 0; i < tx.vin.size(); i++) { const CTxOut& prev = mapInputs.AccessCoin(tx.vin[i].prevout).out; diff --git a/src/policy/policy.h b/src/policy/policy.h index a6ce608bcfad..535ff3a45f27 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -32,14 +32,13 @@ static constexpr unsigned int MINIMUM_BLOCK_RESERVED_WEIGHT{2000}; static constexpr unsigned int DEFAULT_BLOCK_MIN_TX_FEE{1}; /** The maximum weight for transactions we're willing to relay/mine */ static constexpr int32_t MAX_STANDARD_TX_WEIGHT{400000}; -/** The minimum non-witness size for transactions we're willing to relay/mine: one larger than 64 */ +/** The minimum non-witness size for transactions we're willing to relay/mine: one larger than 64. + * BIP54 makes 64-byte transactions invalid, but smaller than that are still non-standard. */ static constexpr unsigned int MIN_STANDARD_TX_NONWITNESS_SIZE{65}; /** Maximum number of signature check operations in an IsStandard() P2SH script */ static constexpr unsigned int MAX_P2SH_SIGOPS{15}; /** The maximum number of sigops we're willing to relay/mine in a single tx */ static constexpr unsigned int MAX_STANDARD_TX_SIGOPS_COST{MAX_BLOCK_SIGOPS_COST/5}; -/** The maximum number of potentially executed legacy signature operations in a single standard tx */ -static constexpr unsigned int MAX_TX_LEGACY_SIGOPS{2'500}; /** Default for -incrementalrelayfee, which sets the minimum feerate increase for mempool limiting or replacement **/ static constexpr unsigned int DEFAULT_INCREMENTAL_RELAY_FEE{100}; /** Default for -bytespersigop */ diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index edda17d3697a..c283564a9929 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1410,6 +1410,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_CONSENSUSCLEANUP); return softforks; } } // anon namespace diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 6d75344194fe..8b5793481974 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -18,6 +18,7 @@ add_executable(test_bitcoin bech32_tests.cpp bip32_tests.cpp bip324_tests.cpp + bip54_tests.cpp blockchain_tests.cpp blockencodings_tests.cpp blockfilter_index_tests.cpp @@ -128,6 +129,8 @@ include(TargetDataSources) target_json_data_sources(test_bitcoin data/base58_encode_decode.json data/bip341_wallet_vectors.json + data/bip54_coinbases.json + data/bip54_timestamps.json data/blockfilters.json data/key_io_invalid.json data/key_io_valid.json diff --git a/src/test/bip54_tests.cpp b/src/test/bip54_tests.cpp new file mode 100644 index 000000000000..4038437a1727 --- /dev/null +++ b/src/test/bip54_tests.cpp @@ -0,0 +1,1622 @@ +// Copyright (c) The Bitcoin Core developers +// 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include