chainparams: make deployment configuration available on all test networks
What changed, and why it matters
This commit lets Bitcoin Core accept two testing-only command-line options on test networks (testnet, testnet4, signet) instead of only on regtest. It also adds an explicit guard so those options cannot be used on the real mainnet. The change is aimed at making unit tests more flexible, not at fixing a live security bug.
No immediate action required. Treat as a routine refactor/testability improvement. Reviewers may want to confirm the mainnet guard is reached before any deployment options are applied and that the default no-argument overloads cannot accidentally pass user-supplied args.
Security signals we found
Deployment parameter options expanded from regtest-only to all test networks
New mainnet guard explicitly prevents -testactivationheight and -vbparams on mainnet
Help text changed from 'regtest-only' to 'test-only'
No validation logic changes for production consensus rules
Evidence from the diff
The patch refactors chain parameter creation so that MainNet, TestNet, TestNet4, and SigNet all accept DeploymentOptions. It wires -testactivationheight and -vbparams into testnet/testnet4/signet, updates help text from ‘regtest-only’ to ‘test-only’, and adds an AppInitParameterInteraction check that rejects both options when the selected chain is mainnet. A default no-argument overload is preserved for callers that do not supply options.
Changed components
src/chainparams.cppsrc/chainparamsbase.cppsrc/init.cppsrc/kernel/chainparams.cppsrc/kernel/chainparams.hInspect captured patch +70 / −20
diff --git a/src/chainparams.cpp b/src/chainparams.cpp
index 4cfd9589..cba8fd27 100644
--- a/src/chainparams.cpp
+++ b/src/chainparams.cpp
@@ -86,6 +86,16 @@ static void HandleDeploymentArgs(const ArgsManager& args, CChainParams::Deployme
}
}
+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()) {
@@ -102,6 +112,7 @@ void ReadSigNetArgs(const ArgsManager& args, CChainParams::SigNetOptions& option
}
options.challenge.emplace(*val);
}
+ HandleDeploymentArgs(args, options.dep_opts);
}
void ReadRegTestArgs(const ArgsManager& args, CChainParams::RegTestOptions& options)
@@ -122,12 +133,21 @@ const CChainParams &Params() {
std::unique_ptr<const CChainParams> 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 d816d1af..f62e455f 100644
--- a/src/chainparamsbase.cpp
+++ b/src/chainparamsbase.cpp
@@ -16,10 +16,10 @@ void SetupChainParamsBaseOptions(ArgsManager& argsman)
argsman.AddArg("-chain=<chain>", "Use the chain <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/init.cpp b/src/init.cpp
index c53e5ed6..f61235ee 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -1125,6 +1125,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 27e846fe..93bfe335 100644
--- a/src/kernel/chainparams.cpp
+++ b/src/kernel/chainparams.cpp
@@ -106,7 +106,7 @@ void CChainParams::ApplyDeploymentOptions(const DeploymentOptions& opts)
*/
class CMainParams : public CChainParams {
public:
- CMainParams() {
+ CMainParams(const MainNetOptions& opts) {
m_chain_type = ChainType::MAIN;
consensus.signet_blocks = false;
consensus.signet_challenge.clear();
@@ -135,6 +135,8 @@ public:
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].threshold = 1815; // 90%
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].period = 2016;
+ ApplyDeploymentOptions(opts.dep_opts);
+
consensus.nMinimumChainWork = uint256{"0000000000000000000000000000000000000001128750f82f4c366153a3a030"};
consensus.defaultAssumeValid = uint256{"00000000000000000000ccebd6d74d9194d8dcdc1d177c478e094bfad51ba5ac"}; // 938343
@@ -231,7 +233,7 @@ public:
*/
class CTestNetParams : public CChainParams {
public:
- CTestNetParams() {
+ CTestNetParams(const TestNetOptions& opts) {
m_chain_type = ChainType::TESTNET;
consensus.signet_blocks = false;
consensus.signet_challenge.clear();
@@ -258,6 +260,8 @@ public:
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].threshold = 1512; // 75%
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].period = 2016;
+ ApplyDeploymentOptions(opts.dep_opts);
+
consensus.nMinimumChainWork = uint256{"0000000000000000000000000000000000000000000017dde1c649f3708d14b6"};
consensus.defaultAssumeValid = uint256{"000000007a61e4230b28ac5cb6b5e5a0130de37ac1faf2f8987d2fa6505b67f4"}; // 4842348
@@ -332,7 +336,7 @@ public:
*/
class CTestNet4Params : public CChainParams {
public:
- CTestNet4Params() {
+ CTestNet4Params(const TestNetOptions& opts) {
m_chain_type = ChainType::TESTNET4;
consensus.signet_blocks = false;
consensus.signet_challenge.clear();
@@ -358,6 +362,8 @@ public:
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].threshold = 1512; // 75%
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].period = 2016;
+ ApplyDeploymentOptions(opts.dep_opts);
+
consensus.nMinimumChainWork = uint256{"0000000000000000000000000000000000000000000009a0fe15d0177d086304"};
consensus.defaultAssumeValid = uint256{"0000000002368b1e4ee27e2e85676ae6f9f9e69579b29093e9a82c170bf7cf8a"}; // 123613
@@ -501,6 +507,8 @@ public:
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].threshold = 1815; // 90%
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].period = 2016;
+ 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;
@@ -664,19 +672,19 @@ std::unique_ptr<const CChainParams> CChainParams::RegTest(const RegTestOptions&
return std::make_unique<const CRegTestParams>(options);
}
-std::unique_ptr<const CChainParams> CChainParams::Main()
+std::unique_ptr<const CChainParams> CChainParams::Main(const MainNetOptions& options)
{
- return std::make_unique<const CMainParams>();
+ return std::make_unique<const CMainParams>(options);
}
-std::unique_ptr<const CChainParams> CChainParams::TestNet()
+std::unique_ptr<const CChainParams> CChainParams::TestNet(const TestNetOptions& options)
{
- return std::make_unique<const CTestNetParams>();
+ return std::make_unique<const CTestNetParams>(options);
}
-std::unique_ptr<const CChainParams> CChainParams::TestNet4()
+std::unique_ptr<const CChainParams> CChainParams::TestNet4(const TestNetOptions& options)
{
- return std::make_unique<const CTestNet4Params>();
+ return std::make_unique<const CTestNet4Params>(options);
}
std::vector<int> CChainParams::GetAvailableSnapshotHeights() const
diff --git a/src/kernel/chainparams.h b/src/kernel/chainparams.h
index ae57fd82..b78c5635 100644
--- a/src/kernel/chainparams.h
+++ b/src/kernel/chainparams.h
@@ -145,6 +145,7 @@ public:
* SigNetOptions holds configurations for creating a signet CChainParams.
*/
struct SigNetOptions {
+ DeploymentOptions dep_opts{};
std::optional<std::vector<uint8_t>> challenge{};
std::optional<std::vector<std::string>> seeds{};
};
@@ -158,11 +159,22 @@ public:
bool enforce_bip94{false};
};
+ struct MainNetOptions {
+ DeploymentOptions dep_opts{};
+ };
+
+ struct TestNetOptions {
+ DeploymentOptions dep_opts{};
+ };
+
static std::unique_ptr<const CChainParams> RegTest(const RegTestOptions& options);
static std::unique_ptr<const CChainParams> SigNet(const SigNetOptions& options);
- static std::unique_ptr<const CChainParams> Main();
- static std::unique_ptr<const CChainParams> TestNet();
- static std::unique_ptr<const CChainParams> TestNet4();
+ static std::unique_ptr<const CChainParams> Main(const MainNetOptions& options);
+ static std::unique_ptr<const CChainParams> Main() { const MainNetOptions opts{}; return Main(opts); }
+ static std::unique_ptr<const CChainParams> TestNet(const TestNetOptions& options);
+ static std::unique_ptr<const CChainParams> TestNet() { const TestNetOptions opts{}; return TestNet(opts); }
+ static std::unique_ptr<const CChainParams> TestNet4(const TestNetOptions& options);
+ static std::unique_ptr<const CChainParams> TestNet4() { const TestNetOptions opts{}; return TestNet4(opts); }
protected:
CChainParams() = default;
Why this scored 18/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.