From bf267fda81c073ce1b01b6e6298ef3a3e373efdb Mon Sep 17 00:00:00 2001 From: Fabian Jahr Date: Sun, 31 Mar 2024 23:50:42 +0200 Subject: [PATCH] testnet: Introduce Testnet4 --- contrib/completions/bash/bitcoin-cli.bash | 2 +- doc/REST-interface.md | 2 +- doc/files.md | 13 ++-- doc/release-process.md | 8 +- src/bitcoin-cli.cpp | 5 +- src/bitcoin-wallet.cpp | 2 +- src/chainparams.cpp | 2 + src/chainparamsbase.cpp | 5 +- src/common/args.cpp | 7 +- src/common/args.h | 2 +- src/init.cpp | 17 +++-- src/kernel/chainparams.cpp | 93 +++++++++++++++++++++++ src/kernel/chainparams.h | 1 + src/qt/guiconstants.h | 1 + src/qt/networkstyle.cpp | 1 + src/test/argsman_tests.cpp | 18 +++-- src/test/pow_tests.cpp | 5 ++ src/test/versionbits_tests.cpp | 2 +- src/util/chaintype.cpp | 4 + src/util/chaintype.h | 1 + test/functional/feature_config_args.py | 28 ++++++- test/functional/p2p_dos_header_tree.py | 1 + test/functional/wallet_crosschain.py | 21 ++++- 23 files changed, 211 insertions(+), 30 deletions(-) diff --git a/contrib/completions/bash/bitcoin-cli.bash b/contrib/completions/bash/bitcoin-cli.bash index 89e01bc09ae691..b04fdbcb0e8705 100644 --- a/contrib/completions/bash/bitcoin-cli.bash +++ b/contrib/completions/bash/bitcoin-cli.bash @@ -9,7 +9,7 @@ _bitcoin_rpc() { local rpcargs=() for i in ${COMP_LINE}; do case "$i" in - -conf=*|-datadir=*|-regtest|-rpc*|-testnet) + -conf=*|-datadir=*|-regtest|-rpc*|-testnet|-testnet4) rpcargs=( "${rpcargs[@]}" "$i" ) ;; esac diff --git a/doc/REST-interface.md b/doc/REST-interface.md index 2d7d0e3769367a..6664bc2a3ae1d1 100644 --- a/doc/REST-interface.md +++ b/doc/REST-interface.md @@ -4,7 +4,7 @@ Unauthenticated REST Interface The REST API can be enabled with the `-rest` option. The interface runs on the same port as the JSON-RPC interface, by default port 8332 for mainnet, port 18332 for testnet, -port 38332 for signet, and port 18443 for regtest. +port 48332 for testnet4, port 38332 for signet, and port 18443 for regtest. REST Interface consistency guarantees ------------------------------------- diff --git a/doc/files.md b/doc/files.md index f88d3f91a1c166..3888f9beb588ba 100644 --- a/doc/files.md +++ b/doc/files.md @@ -34,12 +34,13 @@ Windows | `%APPDATA%\Bitcoin\` [\[1\]](#note1) 3. All content of the data directory, except for `bitcoin.conf` file, is chain-specific. This means the actual data directory paths for non-mainnet cases differ: -Chain option | Data directory path --------------------------------|------------------------------ -`-chain=main` (default) | *path_to_datadir*`/` -`-chain=test` or `-testnet` | *path_to_datadir*`/testnet3/` -`-chain=signet` or `-signet` | *path_to_datadir*`/signet/` -`-chain=regtest` or `-regtest` | *path_to_datadir*`/regtest/` +Chain option | Data directory path +---------------------------------|------------------------------ +`-chain=main` (default) | *path_to_datadir*`/` +`-chain=test` or `-testnet` | *path_to_datadir*`/testnet/` +`-chain=testnet4` or `-testnet4` | *path_to_datadir*`/testnet4/` +`-chain=signet` or `-signet` | *path_to_datadir*`/signet/` +`-chain=regtest` or `-regtest` | *path_to_datadir*`/regtest/` ## Data directory layout diff --git a/doc/release-process.md b/doc/release-process.md index 95ef08a26f3df5..87b20462e0e14f 100644 --- a/doc/release-process.md +++ b/doc/release-process.md @@ -310,13 +310,15 @@ Both variables are used as a guideline for how much space the user needs on thei Note that all values should be taken from a **fully synced** node and have an overhead of 5-10% added on top of its base value. To calculate `m_assumed_blockchain_size`, take the size in GiB of these directories: -- For `mainnet` -> the data directory, excluding the `/testnet3`, `/signet`, and `/regtest` directories and any overly large files, e.g. a huge `debug.log` -- For `testnet` -> `/testnet3` +- For `mainnet` -> the data directory, excluding the `/testnet`, `/testnet4`, `/signet`, and `/regtest` directories and any overly large files, e.g. a huge `debug.log` +- For `testnet` -> `/testnet` +- For `testnet4` -> `/testnet4` - For `signet` -> `/signet` To calculate `m_assumed_chain_state_size`, take the size in GiB of these directories: - For `mainnet` -> `/chainstate` -- For `testnet` -> `/testnet3/chainstate` +- For `testnet` -> `/testnet/chainstate` +- For `testnet4` -> `/testnet4/chainstate` - For `signet` -> `/signet/chainstate` Notes: diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index f0e27cb6750dac..3f1583a6af2a7f 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -76,6 +76,7 @@ static void SetupCliArgs(ArgsManager& argsman) const auto defaultBaseParams = CreateBaseChainParams(ChainType::MAIN); const auto testnetBaseParams = CreateBaseChainParams(ChainType::TESTNET); + const auto testnet4BaseParams = CreateBaseChainParams(ChainType::TESTNET4); const auto signetBaseParams = CreateBaseChainParams(ChainType::SIGNET); const auto regtestBaseParams = CreateBaseChainParams(ChainType::REGTEST); @@ -99,7 +100,7 @@ static void SetupCliArgs(ArgsManager& argsman) argsman.AddArg("-rpcconnect=", strprintf("Send commands to node running on (default: %s)", DEFAULT_RPCCONNECT), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-rpccookiefile=", "Location of the auth cookie. Relative paths will be prefixed by a net-specific datadir location. (default: data dir)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-rpcpassword=", "Password for JSON-RPC connections", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); - argsman.AddArg("-rpcport=", strprintf("Connect to JSON-RPC on (default: %u, testnet: %u, signet: %u, regtest: %u)", defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort(), signetBaseParams->RPCPort(), regtestBaseParams->RPCPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::OPTIONS); + argsman.AddArg("-rpcport=", strprintf("Connect to JSON-RPC on (default: %u, testnet: %u, testnet4: %u, signet: %u, regtest: %u)", defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort(), testnet4BaseParams->RPCPort(), signetBaseParams->RPCPort(), regtestBaseParams->RPCPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::OPTIONS); argsman.AddArg("-rpcuser=", "Username for JSON-RPC connections", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-rpcwait", "Wait for RPC server to start", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-rpcwaittimeout=", strprintf("Timeout in seconds to wait for the RPC server to start, or 0 for no timeout. (default: %d)", DEFAULT_WAIT_CLIENT_TIMEOUT), ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::OPTIONS); @@ -429,6 +430,8 @@ class NetinfoRequestHandler : public BaseRequestHandler std::string ChainToString() const { switch (gArgs.GetChainType()) { + case ChainType::TESTNET4: + return " testnet4"; case ChainType::TESTNET: return " testnet"; case ChainType::SIGNET: diff --git a/src/bitcoin-wallet.cpp b/src/bitcoin-wallet.cpp index d5dfbbec2713a7..ba3c60ac5ccce7 100644 --- a/src/bitcoin-wallet.cpp +++ b/src/bitcoin-wallet.cpp @@ -70,7 +70,7 @@ static std::optional WalletAppInit(ArgsManager& args, int argc, char* argv[ strUsage += "\n" "bitcoin-wallet is an offline tool for creating and interacting with " PACKAGE_NAME " wallet files.\n" "By default bitcoin-wallet will act on wallets in the default mainnet wallet directory in the datadir.\n" - "To change the target wallet, use the -datadir, -wallet and -regtest/-signet/-testnet arguments.\n\n" + "To change the target wallet, use the -datadir, -wallet and -regtest/-signet/-testnet/-testnet4 arguments.\n\n" "Usage:\n" " bitcoin-wallet [options] \n"; strUsage += "\n" + args.GetHelpMessage(); diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 539578085b5add..290a70a0222efb 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -113,6 +113,8 @@ std::unique_ptr CreateChainParams(const ArgsManager& args, c return CChainParams::Main(); case ChainType::TESTNET: return CChainParams::TestNet(); + case ChainType::TESTNET4: + return CChainParams::TestNet4(); case ChainType::SIGNET: { auto opts = CChainParams::SigNetOptions{}; ReadSigNetArgs(args, opts); diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp index 8cbf9e85e0cad2..8aa5a8c1bae559 100644 --- a/src/chainparamsbase.cpp +++ b/src/chainparamsbase.cpp @@ -17,7 +17,8 @@ void SetupChainParamsBaseOptions(ArgsManager& argsman) 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("-testnet", "Use the test chain. Equivalent to -chain=test.", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS); + argsman.AddArg("-testnet", "Use the testnet3 chain. Equivalent to -chain=test. Support for testnet3 is deprecated and will be removed with the next release. Consider moving to testnet4 now by using -testnet4.", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS); + argsman.AddArg("-testnet4", "Use the testnet3 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("-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); @@ -43,6 +44,8 @@ std::unique_ptr CreateBaseChainParams(const ChainType chain) return std::make_unique("", 8332, 8334); case ChainType::TESTNET: return std::make_unique("testnet3", 18332, 18334); + case ChainType::TESTNET4: + return std::make_unique("testnet4", 48332, 48334); case ChainType::SIGNET: return std::make_unique("signet", 38332, 38334); case ChainType::REGTEST: diff --git a/src/common/args.cpp b/src/common/args.cpp index c90eb0c6856d89..9b41399f5781d7 100644 --- a/src/common/args.cpp +++ b/src/common/args.cpp @@ -159,6 +159,7 @@ std::list ArgsManager::GetUnrecognizedSections() const ChainTypeToString(ChainType::REGTEST), ChainTypeToString(ChainType::SIGNET), ChainTypeToString(ChainType::TESTNET), + ChainTypeToString(ChainType::TESTNET4), ChainTypeToString(ChainType::MAIN), }; @@ -766,10 +767,11 @@ std::variant ArgsManager::GetChainArg() const const bool fRegTest = get_net("-regtest"); const bool fSigNet = get_net("-signet"); const bool fTestNet = get_net("-testnet"); + const bool fTestNet4 = get_net("-testnet4"); const auto chain_arg = GetArg("-chain"); - if ((int)chain_arg.has_value() + (int)fRegTest + (int)fSigNet + (int)fTestNet > 1) { - throw std::runtime_error("Invalid combination of -regtest, -signet, -testnet and -chain. Can use at most one."); + if ((int)chain_arg.has_value() + (int)fRegTest + (int)fSigNet + (int)fTestNet + (int)fTestNet4 > 1) { + throw std::runtime_error("Invalid combination of -regtest, -signet, -testnet, -testnet4 and -chain. Can use at most one."); } if (chain_arg) { if (auto parsed = ChainTypeFromString(*chain_arg)) return *parsed; @@ -779,6 +781,7 @@ std::variant ArgsManager::GetChainArg() const if (fRegTest) return ChainType::REGTEST; if (fSigNet) return ChainType::SIGNET; if (fTestNet) return ChainType::TESTNET; + if (fTestNet4) return ChainType::TESTNET4; return ChainType::MAIN; } diff --git a/src/common/args.h b/src/common/args.h index 78a61313b91dfb..323a86d8dca3be 100644 --- a/src/common/args.h +++ b/src/common/args.h @@ -423,7 +423,7 @@ class ArgsManager fs::path GetDataDir(bool net_specific) const; /** - * Return -regtest/-signet/-testnet/-chain= setting as a ChainType enum if a + * Return -regtest/-signet/-testnet/-testnet4/-chain= setting as a ChainType enum if a * recognized chain type was set, or as a string if an unrecognized chain * name was set. Raise an exception if an invalid combination of flags was * provided. diff --git a/src/init.cpp b/src/init.cpp index 885c0673ddb7a0..58b4649f26c650 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -444,10 +444,12 @@ void SetupServerArgs(ArgsManager& argsman) const auto defaultBaseParams = CreateBaseChainParams(ChainType::MAIN); const auto testnetBaseParams = CreateBaseChainParams(ChainType::TESTNET); + const auto testnet4BaseParams = CreateBaseChainParams(ChainType::TESTNET4); const auto signetBaseParams = CreateBaseChainParams(ChainType::SIGNET); const auto regtestBaseParams = CreateBaseChainParams(ChainType::REGTEST); const auto defaultChainParams = CreateChainParams(argsman, ChainType::MAIN); const auto testnetChainParams = CreateChainParams(argsman, ChainType::TESTNET); + const auto testnet4ChainParams = CreateChainParams(argsman, ChainType::TESTNET4); const auto signetChainParams = CreateChainParams(argsman, ChainType::SIGNET); const auto regtestChainParams = CreateChainParams(argsman, ChainType::REGTEST); @@ -461,7 +463,7 @@ void SetupServerArgs(ArgsManager& argsman) #if HAVE_SYSTEM argsman.AddArg("-alertnotify=", "Execute command when an alert is raised (%s in cmd is replaced by message)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); #endif - argsman.AddArg("-assumevalid=", strprintf("If this block is in the chain assume that it and its ancestors are valid and potentially skip their script verification (0 to verify all, default: %s, testnet: %s, signet: %s)", defaultChainParams->GetConsensus().defaultAssumeValid.GetHex(), testnetChainParams->GetConsensus().defaultAssumeValid.GetHex(), signetChainParams->GetConsensus().defaultAssumeValid.GetHex()), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-assumevalid=", strprintf("If this block is in the chain assume that it and its ancestors are valid and potentially skip their script verification (0 to verify all, default: %s, testnet3: %s, testnet4: %s, signet: %s)", defaultChainParams->GetConsensus().defaultAssumeValid.GetHex(), testnetChainParams->GetConsensus().defaultAssumeValid.GetHex(), testnet4ChainParams->GetConsensus().defaultAssumeValid.GetHex(), signetChainParams->GetConsensus().defaultAssumeValid.GetHex()), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-blocksdir=", "Specify directory to hold blocks subdirectory for *.dat files (default: )", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-fastprune", "Use smaller block files and lower minimum prune height for testing purposes", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST); #if HAVE_SYSTEM @@ -480,7 +482,7 @@ void SetupServerArgs(ArgsManager& argsman) argsman.AddArg("-maxmempool=", strprintf("Keep the transaction memory pool below megabytes (default: %u)", DEFAULT_MAX_MEMPOOL_SIZE_MB), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-maxorphantx=", strprintf("Keep at most unconnectable transactions in memory (default: %u)", DEFAULT_MAX_ORPHAN_TRANSACTIONS), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-mempoolexpiry=", strprintf("Do not keep transactions in the mempool longer than hours (default: %u)", DEFAULT_MEMPOOL_EXPIRY_HOURS), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); - argsman.AddArg("-minimumchainwork=", strprintf("Minimum work assumed to exist on a valid chain in hex (default: %s, testnet: %s, signet: %s)", defaultChainParams->GetConsensus().nMinimumChainWork.GetHex(), testnetChainParams->GetConsensus().nMinimumChainWork.GetHex(), signetChainParams->GetConsensus().nMinimumChainWork.GetHex()), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::OPTIONS); + argsman.AddArg("-minimumchainwork=", strprintf("Minimum work assumed to exist on a valid chain in hex (default: %s, testnet3: %s, testnet4: %s, signet: %s)", defaultChainParams->GetConsensus().nMinimumChainWork.GetHex(), testnetChainParams->GetConsensus().nMinimumChainWork.GetHex(), testnet4ChainParams->GetConsensus().nMinimumChainWork.GetHex(), signetChainParams->GetConsensus().nMinimumChainWork.GetHex()), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::OPTIONS); argsman.AddArg("-par=", strprintf("Set the number of script verification threads (0 = auto, up to %d, <0 = leave that many cores free, default: %d)", MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-persistmempool", strprintf("Whether to save the mempool on shutdown and load on restart (default: %u)", DEFAULT_PERSIST_MEMPOOL), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); @@ -509,7 +511,7 @@ void SetupServerArgs(ArgsManager& argsman) argsman.AddArg("-addnode=", strprintf("Add a node to connect to and attempt to keep the connection open (see the addnode RPC help for more info). This option can be specified multiple times to add multiple nodes; connections are limited to %u at a time and are counted separately from the -maxconnections limit.", MAX_ADDNODE_CONNECTIONS), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION); argsman.AddArg("-asmap=", strprintf("Specify asn mapping used for bucketing of the peers (default: %s). Relative paths will be prefixed by the net-specific datadir location.", DEFAULT_ASMAP_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); argsman.AddArg("-bantime=", strprintf("Default duration (in seconds) of manually configured bans (default: %u)", DEFAULT_MISBEHAVING_BANTIME), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); - argsman.AddArg("-bind=[:][=onion]", strprintf("Bind to given address and always listen on it (default: 0.0.0.0). Use [host]:port notation for IPv6. Append =onion to tag any incoming connections to that address and port as incoming Tor connections (default: 127.0.0.1:%u=onion, testnet: 127.0.0.1:%u=onion, signet: 127.0.0.1:%u=onion, regtest: 127.0.0.1:%u=onion)", defaultBaseParams->OnionServiceTargetPort(), testnetBaseParams->OnionServiceTargetPort(), signetBaseParams->OnionServiceTargetPort(), regtestBaseParams->OnionServiceTargetPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION); + argsman.AddArg("-bind=[:][=onion]", strprintf("Bind to given address and always listen on it (default: 0.0.0.0). Use [host]:port notation for IPv6. Append =onion to tag any incoming connections to that address and port as incoming Tor connections (default: 127.0.0.1:%u=onion, testnet3: 127.0.0.1:%u=onion, testnet4: 127.0.0.1:%u=onion, signet: 127.0.0.1:%u=onion, regtest: 127.0.0.1:%u=onion)", defaultBaseParams->OnionServiceTargetPort(), testnetBaseParams->OnionServiceTargetPort(), testnet4BaseParams->OnionServiceTargetPort(), signetBaseParams->OnionServiceTargetPort(), regtestBaseParams->OnionServiceTargetPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION); argsman.AddArg("-cjdnsreachable", "If set, then this host is configured for CJDNS (connecting to fc00::/8 addresses would lead us to the CJDNS network, see doc/cjdns.md) (default: 0)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); argsman.AddArg("-connect=", "Connect only to the specified node; -noconnect disables automatic connections (the rules for this peer are the same as for -addnode). This option can be specified multiple times to connect to multiple nodes.", ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION); argsman.AddArg("-discover", "Discover own IP addresses (default: 1 when listening and no -externalip or -proxy)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); @@ -539,7 +541,7 @@ void SetupServerArgs(ArgsManager& argsman) argsman.AddArg("-txreconciliation", strprintf("Enable transaction reconciliations per BIP 330 (default: %d)", DEFAULT_TXRECONCILIATION_ENABLE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CONNECTION); // TODO: remove the sentence "Nodes not using ... incoming connections." once the changes from // https://github.com/bitcoin/bitcoin/pull/23542 have become widespread. - argsman.AddArg("-port=", strprintf("Listen for connections on . Nodes not using the default ports (default: %u, testnet: %u, signet: %u, regtest: %u) are unlikely to get incoming connections. Not relevant for I2P (see doc/i2p.md).", defaultChainParams->GetDefaultPort(), testnetChainParams->GetDefaultPort(), signetChainParams->GetDefaultPort(), regtestChainParams->GetDefaultPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION); + argsman.AddArg("-port=", strprintf("Listen for connections on . Nodes not using the default ports (default: %u, testnet3: %u, testnet4: %u, signet: %u, regtest: %u) are unlikely to get incoming connections. Not relevant for I2P (see doc/i2p.md).", defaultChainParams->GetDefaultPort(), testnetChainParams->GetDefaultPort(), testnet4ChainParams->GetDefaultPort(), signetChainParams->GetDefaultPort(), regtestChainParams->GetDefaultPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION); #if HAVE_SOCKADDR_UN argsman.AddArg("-proxy=", "Connect through SOCKS5 proxy, set -noproxy to disable (default: disabled). May be a local file path prefixed with 'unix:' if the proxy supports it.", ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_ELISION, OptionsCategory::CONNECTION); #else @@ -659,7 +661,7 @@ void SetupServerArgs(ArgsManager& argsman) argsman.AddArg("-rpcdoccheck", strprintf("Throw a non-fatal error at runtime if the documentation for an RPC is incorrect (default: %u)", DEFAULT_RPC_DOC_CHECK), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::RPC); argsman.AddArg("-rpccookiefile=", "Location of the auth cookie. Relative paths will be prefixed by a net-specific datadir location. (default: data dir)", ArgsManager::ALLOW_ANY, OptionsCategory::RPC); argsman.AddArg("-rpcpassword=", "Password for JSON-RPC connections", ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE, OptionsCategory::RPC); - argsman.AddArg("-rpcport=", strprintf("Listen for JSON-RPC connections on (default: %u, testnet: %u, signet: %u, regtest: %u)", defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort(), signetBaseParams->RPCPort(), regtestBaseParams->RPCPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::RPC); + argsman.AddArg("-rpcport=", strprintf("Listen for JSON-RPC connections on (default: %u, testnet3: %u, testnet4: %u, signet: %u, regtest: %u)", defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort(), testnet4BaseParams->RPCPort(), signetBaseParams->RPCPort(), regtestBaseParams->RPCPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::RPC); argsman.AddArg("-rpcservertimeout=", strprintf("Timeout during HTTP requests (default: %d)", DEFAULT_HTTP_SERVER_TIMEOUT), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::RPC); argsman.AddArg("-rpcthreads=", strprintf("Set the number of threads to service RPC calls (default: %d)", DEFAULT_HTTP_THREADS), ArgsManager::ALLOW_ANY, OptionsCategory::RPC); argsman.AddArg("-rpcuser=", "Username for JSON-RPC connections", ArgsManager::ALLOW_ANY | ArgsManager::SENSITIVE, OptionsCategory::RPC); @@ -906,6 +908,11 @@ bool AppInitParameterInteraction(const ArgsManager& args) return InitError(errors); } + // Testnet3 deprecation warning + if (chain == ChainType::TESTNET) { + LogPrintf("Warning: Support for testnet3 is deprecated and will be removed in an upcoming release. Consider switching to testnet4.\n"); + } + // Warn if unrecognized section name are present in the config file. bilingual_str warnings; for (const auto& section : args.GetUnrecognizedSections()) { diff --git a/src/kernel/chainparams.cpp b/src/kernel/chainparams.cpp index 264a2fd68130cd..7e3612cec1a5f7 100644 --- a/src/kernel/chainparams.cpp +++ b/src/kernel/chainparams.cpp @@ -284,6 +284,94 @@ class CTestNetParams : public CChainParams { } }; +/** + * Testnet (v4): public test network which is reset from time to time. + */ +class CTestNet4Params : public CChainParams { +public: + CTestNet4Params() { + m_chain_type = ChainType::TESTNET4; + consensus.signet_blocks = false; + consensus.signet_challenge.clear(); + consensus.nSubsidyHalvingInterval = 210000; + consensus.BIP34Height = 1; + consensus.BIP34Hash = uint256{}; + consensus.BIP65Height = 1; + consensus.BIP66Height = 1; + consensus.CSVHeight = 1; + consensus.SegwitHeight = 1; + consensus.MinBIP9WarningHeight = 0; + consensus.powLimit = uint256S("00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); + consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks + consensus.nPowTargetSpacing = 10 * 60; + consensus.fPowAllowMinDifficultyBlocks = true; + consensus.fPowNoRetargeting = false; + consensus.nRuleChangeActivationThreshold = 1512; // 75% for testchains + consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28; + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE; + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].min_activation_height = 0; // No activation delay + + // Deployment of Taproot (BIPs 340-342) + consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].bit = 2; + consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = Consensus::BIP9Deployment::ALWAYS_ACTIVE; + consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; + consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 0; // No activation delay + + consensus.nMinimumChainWork = uint256{}; + consensus.defaultAssumeValid = uint256{}; + + pchMessageStart[0] = 0x1c; + pchMessageStart[1] = 0x16; + pchMessageStart[2] = 0x3f; + pchMessageStart[3] = 0x28; + nDefaultPort = 48333; + nPruneAfterHeight = 1000; + m_assumed_blockchain_size = 0; + m_assumed_chain_state_size = 0; + + genesis = CreateGenesisBlock(1711910298, 2215745201, 0x1d00ffff, 1, 50 * COIN); + consensus.hashGenesisBlock = genesis.GetHash(); + assert(consensus.hashGenesisBlock == uint256S("0x000000008d6faa98083fa55742aa82d4ed249bd1bfc3239c706e0a61ef9e3931")); + assert(genesis.hashMerkleRoot == uint256S("0x4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b")); + + vFixedSeeds.clear(); + vSeeds.clear(); + // nodes with support for servicebits filtering should be at the top + // TODO: Add Testnet4 seeds + + base58Prefixes[PUBKEY_ADDRESS] = std::vector(1,111); + base58Prefixes[SCRIPT_ADDRESS] = std::vector(1,196); + base58Prefixes[SECRET_KEY] = std::vector(1,239); + base58Prefixes[EXT_PUBLIC_KEY] = {0x04, 0x35, 0x87, 0xCF}; + base58Prefixes[EXT_SECRET_KEY] = {0x04, 0x35, 0x83, 0x94}; + + bech32_hrp = "tb"; + + vFixedSeeds = std::vector(std::begin(chainparams_seed_test), std::end(chainparams_seed_test)); + + fDefaultConsistencyChecks = false; + m_is_mockable_chain = false; + + checkpointData = { + { + {}, + } + }; + + m_assumeutxo_data = { + {} + }; + + chainTxData = ChainTxData{ + .nTime = 0, + .nTxCount = 0, + .dTxRate = 0, + }; + } +}; + /** * Signet: test network with an additional consensus parameter (see BIP325). */ @@ -542,3 +630,8 @@ std::unique_ptr CChainParams::TestNet() { return std::make_unique(); } + +std::unique_ptr CChainParams::TestNet4() +{ + return std::make_unique(); +} diff --git a/src/kernel/chainparams.h b/src/kernel/chainparams.h index 7a5539bc71c548..38f9147dbd165d 100644 --- a/src/kernel/chainparams.h +++ b/src/kernel/chainparams.h @@ -160,6 +160,7 @@ class CChainParams static std::unique_ptr SigNet(const SigNetOptions& options); static std::unique_ptr Main(); static std::unique_ptr TestNet(); + static std::unique_ptr TestNet4(); protected: CChainParams() {} diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h index 0386689bafe767..30ffa302a4b3bb 100644 --- a/src/qt/guiconstants.h +++ b/src/qt/guiconstants.h @@ -50,6 +50,7 @@ static const int TOOLTIP_WRAP_THRESHOLD = 80; #define QAPP_ORG_DOMAIN "bitcoin.org" #define QAPP_APP_NAME_DEFAULT "Bitcoin-Qt" #define QAPP_APP_NAME_TESTNET "Bitcoin-Qt-testnet" +#define QAPP_APP_NAME_TESTNET4 "Bitcoin-Qt-testnet4" #define QAPP_APP_NAME_SIGNET "Bitcoin-Qt-signet" #define QAPP_APP_NAME_REGTEST "Bitcoin-Qt-regtest" diff --git a/src/qt/networkstyle.cpp b/src/qt/networkstyle.cpp index b6314f553310b8..d3f7c02d05d954 100644 --- a/src/qt/networkstyle.cpp +++ b/src/qt/networkstyle.cpp @@ -19,6 +19,7 @@ static const struct { } network_styles[] = { {ChainType::MAIN, QAPP_APP_NAME_DEFAULT, 0, 0}, {ChainType::TESTNET, QAPP_APP_NAME_TESTNET, 70, 30}, + {ChainType::TESTNET4, QAPP_APP_NAME_TESTNET4, 70, 30}, {ChainType::SIGNET, QAPP_APP_NAME_SIGNET, 35, 15}, {ChainType::REGTEST, QAPP_APP_NAME_REGTEST, 160, 30}, }; diff --git a/src/test/argsman_tests.cpp b/src/test/argsman_tests.cpp index 1f46efe464027c..ebc756424a6181 100644 --- a/src/test/argsman_tests.cpp +++ b/src/test/argsman_tests.cpp @@ -642,10 +642,12 @@ BOOST_AUTO_TEST_CASE(util_GetChainTypeString) { TestArgsManager test_args; const auto testnet = std::make_pair("-testnet", ArgsManager::ALLOW_ANY); + const auto testnet4 = std::make_pair("-testnet4", ArgsManager::ALLOW_ANY); const auto regtest = std::make_pair("-regtest", ArgsManager::ALLOW_ANY); - test_args.SetupArgs({testnet, regtest}); + test_args.SetupArgs({testnet, testnet4, regtest}); const char* argv_testnet[] = {"cmd", "-testnet"}; + const char* argv_testnet4[] = {"cmd", "-testnet4"}; const char* argv_regtest[] = {"cmd", "-regtest"}; const char* argv_test_no_reg[] = {"cmd", "-testnet", "-noregtest"}; const char* argv_both[] = {"cmd", "-testnet", "-regtest"}; @@ -661,6 +663,12 @@ BOOST_AUTO_TEST_CASE(util_GetChainTypeString) BOOST_CHECK(test_args.ParseParameters(2, argv_testnet, error)); BOOST_CHECK_EQUAL(test_args.GetChainTypeString(), "test"); + BOOST_CHECK(test_args.ParseParameters(0, argv_testnet4, error)); + BOOST_CHECK_EQUAL(test_args.GetChainTypeString(), "main"); + + BOOST_CHECK(test_args.ParseParameters(2, argv_testnet4, error)); + BOOST_CHECK_EQUAL(test_args.GetChainTypeString(), "testnet4"); + BOOST_CHECK(test_args.ParseParameters(2, argv_regtest, error)); BOOST_CHECK_EQUAL(test_args.GetChainTypeString(), "regtest"); @@ -756,8 +764,8 @@ struct ArgsMergeTestingSetup : public BasicTestingSetup { ForEachNoDup(conf_actions, SET, SECTION_NEGATE, [&] { for (bool soft_set : {false, true}) { for (bool force_set : {false, true}) { - for (const std::string& section : {ChainTypeToString(ChainType::MAIN), ChainTypeToString(ChainType::TESTNET), ChainTypeToString(ChainType::SIGNET)}) { - for (const std::string& network : {ChainTypeToString(ChainType::MAIN), ChainTypeToString(ChainType::TESTNET), ChainTypeToString(ChainType::SIGNET)}) { + for (const std::string& section : {ChainTypeToString(ChainType::MAIN), ChainTypeToString(ChainType::TESTNET), ChainTypeToString(ChainType::TESTNET4), ChainTypeToString(ChainType::SIGNET)}) { + for (const std::string& network : {ChainTypeToString(ChainType::MAIN), ChainTypeToString(ChainType::TESTNET), ChainTypeToString(ChainType::TESTNET4), ChainTypeToString(ChainType::SIGNET)}) { for (bool net_specific : {false, true}) { fn(arg_actions, conf_actions, soft_set, force_set, section, network, net_specific); } @@ -911,7 +919,7 @@ BOOST_FIXTURE_TEST_CASE(util_ArgsMerge, ArgsMergeTestingSetup) // Results file is formatted like: // // || | | - BOOST_CHECK_EQUAL(out_sha_hex, "d1e436c1cd510d0ec44d5205d4b4e3bee6387d316e0075c58206cb16603f3d82"); + BOOST_CHECK_EQUAL(out_sha_hex, "f1ee5ab094cc43d16a6086fa7f2c10389e0f99902616b31bbf29189972ad1473"); } // Similar test as above, but for ArgsManager::GetChainTypeString function. @@ -1014,7 +1022,7 @@ BOOST_FIXTURE_TEST_CASE(util_ChainMerge, ChainMergeTestingSetup) // Results file is formatted like: // // || - BOOST_CHECK_EQUAL(out_sha_hex, "f263493e300023b6509963887444c41386f44b63bc30047eb8402e8c1144854c"); + BOOST_CHECK_EQUAL(out_sha_hex, "9e60306e1363528bbc19a47f22bcede88e5d6815212f18ec8e6cdc4638dddab4"); } BOOST_AUTO_TEST_CASE(util_ReadWriteSettings) diff --git a/src/test/pow_tests.cpp b/src/test/pow_tests.cpp index 3a44d1da499852..037232dbe2ae0a 100644 --- a/src/test/pow_tests.cpp +++ b/src/test/pow_tests.cpp @@ -198,6 +198,11 @@ BOOST_AUTO_TEST_CASE(ChainParams_TESTNET_sanity) sanity_check_chainparams(*m_node.args, ChainType::TESTNET); } +BOOST_AUTO_TEST_CASE(ChainParams_TESTNET4_sanity) +{ + sanity_check_chainparams(*m_node.args, ChainType::TESTNET4); +} + BOOST_AUTO_TEST_CASE(ChainParams_SIGNET_sanity) { sanity_check_chainparams(*m_node.args, ChainType::SIGNET); diff --git a/src/test/versionbits_tests.cpp b/src/test/versionbits_tests.cpp index f462895edb6bb6..896840b0f3b95d 100644 --- a/src/test/versionbits_tests.cpp +++ b/src/test/versionbits_tests.cpp @@ -419,7 +419,7 @@ BOOST_AUTO_TEST_CASE(versionbits_computeblockversion) // check that any deployment on any chain can conceivably reach both // ACTIVE and FAILED states in roughly the way we expect - for (const auto& chain_type: {ChainType::MAIN, ChainType::TESTNET, ChainType::SIGNET, ChainType::REGTEST}) { + for (const auto& chain_type: {ChainType::MAIN, ChainType::TESTNET, ChainType::TESTNET4, ChainType::SIGNET, ChainType::REGTEST}) { const auto chainParams = CreateChainParams(*m_node.args, chain_type); uint32_t chain_all_vbits{0}; for (int i = 0; i < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++i) { diff --git a/src/util/chaintype.cpp b/src/util/chaintype.cpp index 8a199e352a41d6..272466e7afccef 100644 --- a/src/util/chaintype.cpp +++ b/src/util/chaintype.cpp @@ -15,6 +15,8 @@ std::string ChainTypeToString(ChainType chain) return "main"; case ChainType::TESTNET: return "test"; + case ChainType::TESTNET4: + return "testnet4"; case ChainType::SIGNET: return "signet"; case ChainType::REGTEST: @@ -29,6 +31,8 @@ std::optional ChainTypeFromString(std::string_view chain) return ChainType::MAIN; } else if (chain == "test") { return ChainType::TESTNET; + } else if (chain == "testnet4") { + return ChainType::TESTNET4; } else if (chain == "signet") { return ChainType::SIGNET; } else if (chain == "regtest") { diff --git a/src/util/chaintype.h b/src/util/chaintype.h index c73985df578ace..2fe734b64a5b84 100644 --- a/src/util/chaintype.h +++ b/src/util/chaintype.h @@ -13,6 +13,7 @@ enum class ChainType { TESTNET, SIGNET, REGTEST, + TESTNET4, }; std::string ChainTypeToString(ChainType chain); diff --git a/test/functional/feature_config_args.py b/test/functional/feature_config_args.py index 9e13a3deefa19d..10ba4baca1aa5a 100755 --- a/test/functional/feature_config_args.py +++ b/test/functional/feature_config_args.py @@ -371,11 +371,36 @@ def test_ignored_default_conf(self): def test_acceptstalefeeestimates_arg_support(self): self.log.info("Test -acceptstalefeeestimates option support") conf_file = self.nodes[0].datadir_path / "bitcoin.conf" - for chain, chain_name in {("main", ""), ("test", "testnet3"), ("signet", "signet")}: + for chain, chain_name in {("main", ""), ("test", "testnet3"), ("signet", "signet"), ("testnet4", "testnet4")}: util.write_config(conf_file, n=0, chain=chain_name, extra_config='acceptstalefeeestimates=1\n') self.nodes[0].assert_start_raises_init_error(expected_msg=f'Error: acceptstalefeeestimates is not supported on {chain} chain.') util.write_config(conf_file, n=0, chain="regtest") # Reset to regtest + def test_testnet3_deprecation_msg(self): + self.log.info("Test testnet3 deprecation warning") + t3_warning_log = "Warning: Support for testnet3 is deprecated and will be removed in an upcoming release. Consider switching to testnet4." + + def warning_msg(node, approx_size): + return f"Warning: Disk space for \"{node.datadir_path / node.chain / "blocks" }\" may not accommodate the block files. Approximately {approx_size} GB of data will be stored in this directory." + + # Testnet3 node will log the warning + self.nodes[0].chain = 'testnet3' + self.nodes[0].replace_in_config([('regtest=', 'testnet='), ('[regtest]', '[test]')]) + with self.nodes[0].assert_debug_log([t3_warning_log]): + self.start_node(0) + self.stop_node(0, warning_msg(self.nodes[0], 42)) + + # Testnet4 node will not log the warning + self.nodes[0].chain = 'testnet4' + self.nodes[0].replace_in_config([('testnet=', 'testnet4='), ('[test]', '[testnet4]')]) + with self.nodes[0].assert_debug_log([], unexpected_msgs=[t3_warning_log]): + self.start_node(0) + self.stop_node(0) + + # Reset to regtest + self.nodes[0].chain = 'regtest' + self.nodes[0].replace_in_config([('testnet4=', 'regtest='), ('[testnet4]', '[regtest]')]) + def run_test(self): self.test_log_buffer() self.test_args_log() @@ -389,6 +414,7 @@ def run_test(self): self.test_ignored_conf() self.test_ignored_default_conf() self.test_acceptstalefeeestimates_arg_support() + self.test_testnet3_deprecation_msg() # Remove the -datadir argument so it doesn't override the config file self.nodes[0].args = [arg for arg in self.nodes[0].args if not arg.startswith("-datadir")] diff --git a/test/functional/p2p_dos_header_tree.py b/test/functional/p2p_dos_header_tree.py index 4b4346af4911d3..9a41c255669f40 100755 --- a/test/functional/p2p_dos_header_tree.py +++ b/test/functional/p2p_dos_header_tree.py @@ -20,6 +20,7 @@ class RejectLowDifficultyHeadersTest(BitcoinTestFramework): def set_test_params(self): self.setup_clean_chain = True + # TODO: Update to testnet4 when a checkpoint is available self.chain = 'testnet3' # Use testnet chain because it has an early checkpoint self.num_nodes = 2 self.extra_args = [["-minimumchainwork=0x0", '-prune=550']] * self.num_nodes diff --git a/test/functional/wallet_crosschain.py b/test/functional/wallet_crosschain.py index 4c5d7192ae0763..d613c590eaf62a 100755 --- a/test/functional/wallet_crosschain.py +++ b/test/functional/wallet_crosschain.py @@ -11,7 +11,7 @@ def add_options(self, parser): self.add_wallet_options(parser) def set_test_params(self): - self.num_nodes = 2 + self.num_nodes = 3 self.setup_clean_chain = True def skip_test_if_missing_module(self): @@ -24,6 +24,12 @@ def setup_network(self): self.nodes[1].chain = 'testnet3' self.nodes[1].extra_args = ['-maxconnections=0', '-prune=550'] # disable testnet sync self.nodes[1].replace_in_config([('regtest=', 'testnet='), ('[regtest]', '[test]')]) + + # Switch node 1 to testnet4 before starting it. + self.nodes[2].chain = 'testnet4' + self.nodes[2].extra_args = ['-maxconnections=0', '-prune=550'] # disable testnet4 sync + self.nodes[2].replace_in_config([('regtest=', 'testnet4='), ('[regtest]', '[testnet4]')]) + self.start_nodes() def run_test(self): @@ -39,19 +45,32 @@ def run_test(self): self.nodes[1].createwallet(node1_wallet) self.nodes[1].backupwallet(node1_wallet_backup) self.nodes[1].unloadwallet(node1_wallet) + node2_wallet = self.nodes[2].datadir_path / 'node2_wallet' + node2_wallet_backup = self.nodes[0].datadir_path / 'node1_wallet.bak' + self.nodes[2].createwallet(node2_wallet) + self.nodes[2].backupwallet(node2_wallet_backup) + self.nodes[2].unloadwallet(node2_wallet) self.log.info("Loading/restoring wallets into nodes with a different genesis block") if self.options.descriptors: assert_raises_rpc_error(-18, 'Wallet file verification failed.', self.nodes[0].loadwallet, node1_wallet) + assert_raises_rpc_error(-18, 'Wallet file verification failed.', self.nodes[0].loadwallet, node2_wallet) assert_raises_rpc_error(-18, 'Wallet file verification failed.', self.nodes[1].loadwallet, node0_wallet) + assert_raises_rpc_error(-18, 'Wallet file verification failed.', self.nodes[2].loadwallet, node0_wallet) assert_raises_rpc_error(-18, 'Wallet file verification failed.', self.nodes[0].restorewallet, 'w', node1_wallet_backup) + assert_raises_rpc_error(-18, 'Wallet file verification failed.', self.nodes[0].restorewallet, 'w', node2_wallet_backup) assert_raises_rpc_error(-18, 'Wallet file verification failed.', self.nodes[1].restorewallet, 'w', node0_wallet_backup) + assert_raises_rpc_error(-18, 'Wallet file verification failed.', self.nodes[2].restorewallet, 'w', node0_wallet_backup) else: assert_raises_rpc_error(-4, 'Wallet files should not be reused across chains.', self.nodes[0].loadwallet, node1_wallet) + assert_raises_rpc_error(-4, 'Wallet files should not be reused across chains.', self.nodes[0].loadwallet, node2_wallet) assert_raises_rpc_error(-4, 'Wallet files should not be reused across chains.', self.nodes[1].loadwallet, node0_wallet) + assert_raises_rpc_error(-4, 'Wallet files should not be reused across chains.', self.nodes[2].loadwallet, node0_wallet) assert_raises_rpc_error(-4, 'Wallet files should not be reused across chains.', self.nodes[0].restorewallet, 'w', node1_wallet_backup) + assert_raises_rpc_error(-4, 'Wallet files should not be reused across chains.', self.nodes[0].restorewallet, 'w', node2_wallet_backup) assert_raises_rpc_error(-4, 'Wallet files should not be reused across chains.', self.nodes[1].restorewallet, 'w', node0_wallet_backup) + assert_raises_rpc_error(-4, 'Wallet files should not be reused across chains.', self.nodes[2].restorewallet, 'w', node0_wallet_backup) if not self.options.descriptors: self.log.info("Override cross-chain wallet load protection")