diff --git a/contrib/completions/bash/bitcoin-cli.bash b/contrib/completions/bash/bitcoin-cli.bash index 5f817f7218..9e0695838f 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/contrib/seeds/generate-seeds.py b/contrib/seeds/generate-seeds.py index f67e7b0f4c..72dd5d28cc 100755 --- a/contrib/seeds/generate-seeds.py +++ b/contrib/seeds/generate-seeds.py @@ -5,11 +5,12 @@ ''' Script to generate list of seed nodes for kernel/chainparams.cpp. -This script expects two text files in the directory that is passed as an +This script expects three text files in the directory that is passed as an argument: nodes_main.txt nodes_test.txt + nodes_testnet4.txt These files must consist of lines in the format @@ -171,6 +172,9 @@ def main(): g.write('\n') with open(os.path.join(indir,'nodes_test.txt'), 'r', encoding="utf8") as f: process_nodes(g, f, 'chainparams_seed_test') + g.write('\n') + with open(os.path.join(indir,'nodes_testnet4.txt'), 'r', encoding="utf8") as f: + process_nodes(g, f, 'chainparams_seed_testnet4') g.write('#endif // BITCOIN_CHAINPARAMSSEEDS_H\n') if __name__ == '__main__': diff --git a/doc/REST-interface.md b/doc/REST-interface.md index 2d7d0e3769..6664bc2a3a 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 5c5a61bd56..b738d6055a 100644 --- a/doc/files.md +++ b/doc/files.md @@ -34,12 +34,13 @@ Windows | `%LOCALAPPDATA%\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*`/testnet3/` +`-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 1e6d49100e..fa2c53eb0c 100644 --- a/doc/release-process.md +++ b/doc/release-process.md @@ -311,13 +311,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 `mainnet` -> the data directory, excluding the `/testnet3`, `/testnet4`, `/signet`, and `/regtest` directories and any overly large files, e.g. a huge `debug.log` - For `testnet` -> `/testnet3` +- 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 `testnet4` -> `/testnet4/chainstate` - For `signet` -> `/signet/chainstate` Notes: diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index 4ffbae0167..86995d6cf8 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -75,6 +75,7 @@ static void SetupCliArgs(ArgsManager& argsman) const auto defaultBaseParams = CreateBaseChainParams(ChainType::LIQUID1); // ELEMENTS const auto testnetBaseParams = CreateBaseChainParams(ChainType::TESTNET); + const auto testnet4BaseParams = CreateBaseChainParams(ChainType::TESTNET4); const auto signetBaseParams = CreateBaseChainParams(ChainType::SIGNET); const auto regtestBaseParams = CreateBaseChainParams(ChainType::REGTEST); @@ -98,7 +99,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 @@ private: { const ChainTypeMeta chain = gArgs.GetChainTypeMeta(); // ELEMENTS switch (chain.chain_type) { + 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 ae13bdc5c5..25c60b4948 100644 --- a/src/bitcoin-wallet.cpp +++ b/src/bitcoin-wallet.cpp @@ -69,7 +69,7 @@ static std::optional WalletAppInit(ArgsManager& args, int argc, char* argv[ strUsage += "\n" "elements-wallet is an offline tool for creating and interacting with " PACKAGE_NAME " wallet files.\n" "By default elements-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" " elements-wallet [options] \n"; strUsage += "\n" + args.GetHelpMessage(); diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 07d41b4a30..0b4b52517e 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -124,6 +124,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 a329de003a..fdc6224cc7 100644 --- a/src/chainparamsbase.cpp +++ b/src/chainparamsbase.cpp @@ -21,7 +21,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 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 or custom only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS); argsman.AddArg("-seednode=", "Use specified node as seed node. This option can be specified multiple times to connect to multiple nodes. (custom only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS); @@ -70,7 +71,7 @@ const CBaseChainParams& BaseParams() } /** - * Port numbers for incoming Tor connections (8334, 18334, 38334, 18445) have + * Port numbers for incoming Tor connections (8334, 18334, 38334, 48334, 18445) have * been chosen arbitrarily to keep ranges of used ports tight. */ std::unique_ptr CreateBaseChainParams(const ChainTypeMeta chain) @@ -80,6 +81,8 @@ std::unique_ptr CreateBaseChainParams(const ChainTypeMeta chai return std::make_unique("", 8332, 18332, 8334); case ChainType::TESTNET: return std::make_unique("testnet3", 18332, 8332, 18334); + case ChainType::TESTNET4: + return std::make_unique("testnet4", 48332, 8332, 48334); case ChainType::SIGNET: return std::make_unique("signet", 38332, 18332, 38334); case ChainType::REGTEST: diff --git a/src/chainparamsseeds.h b/src/chainparamsseeds.h index dcfb55bb20..4a4b366310 100644 --- a/src/chainparamsseeds.h +++ b/src/chainparamsseeds.h @@ -1787,6 +1787,18 @@ static const uint8_t chainparams_seed_test[] = { 0x04,0x20,0xc8,0x88,0xfe,0x71,0x5f,0xa3,0x6c,0x96,0x6a,0xd7,0x9e,0x38,0x84,0x9f,0x44,0xe1,0x6b,0xdc,0x98,0x31,0xad,0x96,0x29,0xe7,0x00,0x83,0x63,0x03,0xae,0x69,0x2e,0x63,0x47,0x9d, }; +static const uint8_t chainparams_seed_testnet4[] = { + 0x01,0x04,0x39,0x80,0xb0,0xa3,0xbc,0xcd, + 0x01,0x04,0x33,0x9e,0xf8,0x08,0xbc,0xcd, + 0x01,0x04,0x5f,0xd9,0x49,0xa2,0xbc,0xcd, + 0x01,0x04,0x12,0xbd,0x9c,0x66,0xbc,0xcd, + 0x01,0x04,0x67,0x63,0xab,0xd4,0xbc,0xcd, + 0x01,0x04,0x52,0x43,0x66,0x0f,0xbc,0xcd, + 0x01,0x04,0x58,0x63,0xf8,0x32,0xbc,0xcd, + 0x01,0x04,0x67,0xa5,0xc0,0xd2,0xbc,0xcd, + 0x01,0x04,0x12,0xc9,0xcf,0x37,0xbc,0xcd, +}; + static uint8_t pnSeed6_liquidv1[] = { 0x01,0x04,0x23,0xc4,0x10,0xfe,0x1b,0x82, 0x01,0x04,0x23,0xed,0xb0,0x3f,0x1b,0x82, diff --git a/src/common/args.cpp b/src/common/args.cpp index 1a9f3fc34e..1fa6e11165 100644 --- a/src/common/args.cpp +++ b/src/common/args.cpp @@ -829,10 +829,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; @@ -842,6 +843,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::LIQUID1; } diff --git a/src/common/args.h b/src/common/args.h index 76f2f6ee8f..381d0ba71b 100644 --- a/src/common/args.h +++ b/src/common/args.h @@ -425,7 +425,7 @@ private: 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/consensus/params.h b/src/consensus/params.h index b7a45ebc56..ea385c13ad 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -122,6 +122,7 @@ struct Params { /** Proof of work parameters */ uint256 powLimit; bool fPowAllowMinDifficultyBlocks; + bool enforce_BIP94; bool fPowNoRetargeting; int64_t nPowTargetSpacing; int64_t nPowTargetTimespan; diff --git a/src/init.cpp b/src/init.cpp index bd218457da..f84a3ced39 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -464,11 +464,13 @@ void SetupServerArgs(ArgsManager& argsman) const auto defaultBaseParams = CreateBaseChainParams(ChainType::LIQUID1); const auto mainnetBaseParams = 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::LIQUID1); const auto mainnetChainParams = 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); @@ -482,7 +484,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("-blocksxor", strprintf("Whether an XOR-key applies to blocksdir *.dat files. " @@ -508,7 +510,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); @@ -538,7 +540,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); @@ -565,7 +567,7 @@ void SetupServerArgs(ArgsManager& argsman) argsman.AddArg("-peerbloomfilters", strprintf("Support filtering of blocks and transaction with bloom filters (default: %u)", DEFAULT_PEERBLOOMFILTERS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); argsman.AddArg("-peerblockfilters", strprintf("Serve compact block filters to peers per BIP 157 (default: %u)", DEFAULT_PEERBLOCKFILTERS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); argsman.AddArg("-txreconciliation", strprintf("Enable transaction reconciliations per BIP 330 (default: %d)", DEFAULT_TXRECONCILIATION_ENABLE), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CONNECTION); - argsman.AddArg("-port=", strprintf("Listen for connections on (default: %u, testnet: %u, signet: %u, regtest: %u). 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 (default: %u, testnet3: %u, testnet4: %u, signet: %u, regtest: %u). 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); #ifdef 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 @@ -683,7 +685,7 @@ void SetupServerArgs(ArgsManager& argsman) 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("-rpccookieperms=", strprintf("Set permissions on the RPC auth cookie file so that it is readable by [owner|group|all] (default: owner [via umask 0077])"), 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); @@ -976,6 +978,11 @@ bool AppInitParameterInteraction(const ArgsManager& args) return InitError(errors); } + // Testnet3 deprecation warning + if (chain.chain_type == ChainType::TESTNET) { + LogInfo("Warning: Support for testnet3 is deprecated and will be removed in an upcoming release. Consider switching to testnet4.\n"); + } + if (!fs::is_directory(args.GetBlocksDirPath())) { return InitError(strprintf(_("Specified blocks directory \"%s\" does not exist."), args.GetArg("-blocksdir", ""))); } diff --git a/src/kernel/chainparams.cpp b/src/kernel/chainparams.cpp index 18e08051b8..842d2273bc 100644 --- a/src/kernel/chainparams.cpp +++ b/src/kernel/chainparams.cpp @@ -226,6 +226,7 @@ public: consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks consensus.nPowTargetSpacing = 10 * 60; consensus.fPowAllowMinDifficultyBlocks = false; + consensus.enforce_BIP94 = false; consensus.fPowNoRetargeting = false; consensus.nRuleChangeActivationThreshold = 1815; // 90% of 2016 consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing @@ -374,6 +375,7 @@ public: consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks consensus.nPowTargetSpacing = 10 * 60; consensus.fPowAllowMinDifficultyBlocks = true; + consensus.enforce_BIP94 = false; consensus.fPowNoRetargeting = false; consensus.nRuleChangeActivationThreshold = 1512; // 75% for testchains consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing @@ -480,6 +482,114 @@ public: } }; +/** + * Testnet (v4): public test network which is reset from time to time. + */ +class CTestNet4Params : public CChainParams { +public: + CTestNet4Params() { + m_chain_type = ChainTypeMetaFrom(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.enforce_BIP94 = 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.vDeployments[Consensus::DEPLOYMENT_DYNA_FED].bit = 25; + consensus.vDeployments[Consensus::DEPLOYMENT_DYNA_FED].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE; + consensus.vDeployments[Consensus::DEPLOYMENT_DYNA_FED].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; + consensus.vDeployments[Consensus::DEPLOYMENT_DYNA_FED].min_activation_height = 0; // No activation delay + + consensus.vDeployments[Consensus::DEPLOYMENT_SIMPLICITY].bit = 21; + consensus.vDeployments[Consensus::DEPLOYMENT_SIMPLICITY].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE; + consensus.vDeployments[Consensus::DEPLOYMENT_SIMPLICITY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; + consensus.vDeployments[Consensus::DEPLOYMENT_SIMPLICITY].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; + + const char* testnet4_genesis_msg = "03/May/2024 000000000000000000001ebd58c244970b3aa9d783bb001011fbe8ea8e98e00e"; + const CScript testnet4_genesis_script = CScript() << ParseHex("000000000000000000000000000000000000000000000000000000000000000000") << OP_CHECKSIG; + genesis = CreateGenesisBlock(consensus, CScript() << 486604799 << CScriptNum(4) << std::vector((const unsigned char*)testnet4_genesis_msg, (const unsigned char*)testnet4_genesis_msg + strlen(testnet4_genesis_msg)), + testnet4_genesis_script, + 1714777860, + 393743547, + 0x1d00ffff, + 1, + 50 * COIN); + consensus.hashGenesisBlock = genesis.GetHash(); + assert(consensus.hashGenesisBlock == uint256S("0x00000000da84f2bafbbc53dee25a72ae507ff4914b867c565be350b0da8bf043")); + assert(genesis.hashMerkleRoot == uint256S("0x7aa0a7ae1e223414cb807e40cd57e667b718e42aaf9306db9102fe28912b7b4e")); + + vFixedSeeds.clear(); + vSeeds.clear(); + // nodes with support for servicebits filtering should be at the top + vSeeds.emplace_back("seed.testnet4.bitcoin.sprovoost.nl."); // Sjors Provoost + vSeeds.emplace_back("seed.testnet4.wiz.biz."); // Jason Maurice + + 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_testnet4), std::end(chainparams_seed_testnet4)); + + fDefaultConsistencyChecks = false; + m_is_mockable_chain = false; + + checkpointData = { + { + {}, + } + }; + + m_assumeutxo_data = { + {} + }; + + chainTxData = ChainTxData{ + .nTime = 0, + .tx_count = 0, + .dTxRate = 0, + }; + } +}; + /** * Signet: test network with an additional consensus parameter (see BIP325). */ @@ -540,6 +650,7 @@ public: consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks consensus.nPowTargetSpacing = 10 * 60; consensus.fPowAllowMinDifficultyBlocks = false; + consensus.enforce_BIP94 = false; consensus.fPowNoRetargeting = false; consensus.nRuleChangeActivationThreshold = 1815; // 90% of 2016 consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing @@ -649,6 +760,7 @@ public: consensus.nPowTargetTimespan = 14 * 24 * 60 * 60; // two weeks consensus.nPowTargetSpacing = 10 * 60; consensus.fPowAllowMinDifficultyBlocks = true; + consensus.enforce_BIP94 = false; consensus.fPowNoRetargeting = true; consensus.nRuleChangeActivationThreshold = 108; // 75% for testchains consensus.nMinerConfirmationWindow = 144; // Faster than normal for regtest (144 instead of 2016) @@ -1763,6 +1875,11 @@ std::unique_ptr CChainParams::TestNet() return std::make_unique(); } +std::unique_ptr CChainParams::TestNet4() +{ + return std::make_unique(); +} + std::vector CChainParams::GetAvailableSnapshotHeights() const { std::vector heights; diff --git a/src/kernel/chainparams.h b/src/kernel/chainparams.h index 08013bacfb..8f94e6163f 100644 --- a/src/kernel/chainparams.h +++ b/src/kernel/chainparams.h @@ -209,6 +209,7 @@ public: 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() = default; diff --git a/src/pow.cpp b/src/pow.cpp index 1e8d53de8b..50de8946be 100644 --- a/src/pow.cpp +++ b/src/pow.cpp @@ -61,7 +61,19 @@ unsigned int CalculateNextWorkRequired(const CBlockIndex* pindexLast, int64_t nF // Retarget const arith_uint256 bnPowLimit = UintToArith256(params.powLimit); arith_uint256 bnNew; - bnNew.SetCompact(pindexLast->nBits); + + // Special difficulty rule for Testnet4 + if (params.enforce_BIP94) { + // Here we use the first block of the difficulty period. This way + // the real difficulty is always preserved in the first block as + // it is not allowed to use the min-difficulty exception. + int nHeightFirst = pindexLast->nHeight - (params.DifficultyAdjustmentInterval()-1); + const CBlockIndex* pindexFirst = pindexLast->GetAncestor(nHeightFirst); + bnNew.SetCompact(pindexFirst->nBits); + } else { + bnNew.SetCompact(pindexLast->nBits); + } + bnNew *= nActualTimespan; bnNew /= params.nPowTargetTimespan; diff --git a/src/qt/guiconstants.h b/src/qt/guiconstants.h index 55c8646275..35fd7e7078 100644 --- a/src/qt/guiconstants.h +++ b/src/qt/guiconstants.h @@ -55,6 +55,7 @@ static const int TOOLTIP_WRAP_THRESHOLD = 80; #define QAPP_APP_NAME_DEFAULT "Elements-Qt" #define QAPP_APP_NAME_TESTNET "Elements-Qt-testnet" +#define QAPP_APP_NAME_TESTNET4 "Elements-Qt-testnet4" #define QAPP_APP_NAME_SIGNET "Elements-Qt-signet" #define QAPP_APP_NAME_REGTEST "Elements-Qt-regtest" #define QAPP_APP_NAME_LIQUID "Elements-Qt (Liquid)" diff --git a/src/qt/networkstyle.cpp b/src/qt/networkstyle.cpp index 733c1708f4..78ab4c1efa 100644 --- a/src/qt/networkstyle.cpp +++ b/src/qt/networkstyle.cpp @@ -23,6 +23,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}, {ChainType::LIQUID1, QAPP_APP_NAME_LIQUID, 0, 0}, diff --git a/src/test/argsman_tests.cpp b/src/test/argsman_tests.cpp index 8b2fb0b456..16b5d255f3 100644 --- a/src/test/argsman_tests.cpp +++ b/src/test/argsman_tests.cpp @@ -648,10 +648,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"}; @@ -667,6 +669,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(), "liquidv1"); + + 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"); @@ -762,8 +770,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); } @@ -917,7 +925,7 @@ BOOST_FIXTURE_TEST_CASE(util_ArgsMerge, ArgsMergeTestingSetup) // Results file is formatted like: // // || | | - BOOST_CHECK_EQUAL(out_sha_hex, "f0a5a84ec85569ae13bf8c94f0dd5d5c6989c84041fb56bd4fb41e1a09818e40"); + BOOST_CHECK_EQUAL(out_sha_hex, "f4cb247cc5589eceb8071a1d1d7a3148b84701af7075c547f8223faea84206ac"); } // Similar test as above, but for ArgsManager::GetChainTypeString function. @@ -1020,7 +1028,7 @@ BOOST_FIXTURE_TEST_CASE(util_ChainMerge, ChainMergeTestingSetup) // Results file is formatted like: // // || - BOOST_CHECK_EQUAL(out_sha_hex, "4b5aebc617224a00e19eeda5e11986506256c859b0e4068ddc338edb9ba341be"); + BOOST_CHECK_EQUAL(out_sha_hex, "2c539d739e40181fd1c0c8346a420cf61d849c548845623212213b2c248382fa"); } BOOST_AUTO_TEST_CASE(util_ReadWriteSettings) diff --git a/src/test/pow_tests.cpp b/src/test/pow_tests.cpp index 9aadebbe63..4af66af283 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 f462895edb..896840b0f3 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 bddda85b36..ac6eef690b 100644 --- a/src/util/chaintype.cpp +++ b/src/util/chaintype.cpp @@ -19,6 +19,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: @@ -41,6 +43,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 16c93d8a5b..aa54e96678 100644 --- a/src/util/chaintype.h +++ b/src/util/chaintype.h @@ -15,6 +15,7 @@ enum class ChainType { MAIN, TESTNET, + TESTNET4, SIGNET, REGTEST, // ELEMENTS diff --git a/src/validation.cpp b/src/validation.cpp index 0cb037d3e6..7c2f89e883 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -4767,6 +4767,18 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, BlockValidatio LogPrintf("ERROR: %s: block height in header is incorrect (got %d, expected %d)\n", __func__, block.block_height, nHeight); return state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER, "bad-header-height"); } + + // Testnet4 only: Check timestamp against prev for difficulty-adjustment + // blocks to prevent timewarp attacks (see https://github.com/bitcoin/bitcoin/pull/15482). + if (consensusParams.enforce_BIP94) { + // Check timestamp for the first block of each difficulty adjustment + // interval, except the genesis block. + if (nHeight % consensusParams.DifficultyAdjustmentInterval() == 0) { + if (block.GetBlockTime() < pindexPrev->GetBlockTime() - 60 * 60 * 2) { + return state.Invalid(BlockValidationResult::BLOCK_INVALID_HEADER, "time-timewarp-attack", "block's timestamp is too early on diff adjustment block"); + } + } + } // Check timestamp if (block.Time() > NodeClock::now() + std::chrono::seconds{MAX_FUTURE_BLOCK_TIME}) { diff --git a/test/functional/feature_config_args.py b/test/functional/feature_config_args.py index 32055bf86d..fbfa985ac5 100755 --- a/test/functional/feature_config_args.py +++ b/test/functional/feature_config_args.py @@ -373,11 +373,44 @@ class ConfArgsTest(BitcoinTestFramework): def test_acceptstalefeeestimates_arg_support(self): self.log.info("Test -acceptstalefeeestimates option support") conf_file = self.nodes[0].datadir_path / "elements.conf" - for chain, chain_name in {("main", "main"), ("test", "test"), ("signet", "signet")}: + for chain, chain_name in {("main", "main"), ("test", "test"), ("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="elementsregtest") # 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([('chain=elementsregtest', 'chain=test'), ('[elementsregtest]', '[test]')]) + with self.nodes[0].assert_debug_log([t3_warning_log]): + self.start_node(0, extra_args=["-validatepegin=0"]) + # Some CI environments will have limited space and some others won't + # so we need to handle both cases as a valid result. + self.nodes[0].stderr.seek(0) + err = self.nodes[0].stdout.read() + self.nodes[0].stderr.seek(0) + self.nodes[0].stderr.truncate() + if err != b'' and err != warning_msg(self.nodes[0], 42): + raise AssertionError("Unexpected stderr after shutdown of Testnet3 node") + self.stop_node(0) + + # Testnet4 node will not log the warning + self.nodes[0].chain = 'testnet4' + self.nodes[0].replace_in_config([('chain=test\n', 'chain=testnet4\n'), ('[test]', '[testnet4]')]) + with self.nodes[0].assert_debug_log([], unexpected_msgs=[t3_warning_log]): + self.start_node(0, extra_args=["-validatepegin=0"]) + self.stop_node(0) + + # Reset to elementsregtest + self.nodes[0].chain = 'elementsregtest' + self.nodes[0].replace_in_config([('chain=testnet4', 'chain=elementsregtest'), ('[testnet4]', '[elementsregtest]')]) + def run_test(self): self.test_log_buffer() self.test_args_log() @@ -391,6 +424,7 @@ class ConfArgsTest(BitcoinTestFramework): 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/wallet_crosschain.py b/test/functional/wallet_crosschain.py index f7ec735a9d..984661d027 100755 --- a/test/functional/wallet_crosschain.py +++ b/test/functional/wallet_crosschain.py @@ -11,7 +11,7 @@ class WalletCrossChain(BitcoinTestFramework): 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 @@ class WalletCrossChain(BitcoinTestFramework): self.nodes[1].chain = 'liquidtestnet' self.nodes[1].extra_args = ['-maxconnections=0', '-prune=550'] # disable testnet sync self.nodes[1].replace_in_config([('regtest=', 'testnet='), ('elementsregtest', 'liquidtestnet')]) + + # Switch node 2 to testnet4 before starting it. + self.nodes[2].chain = 'testnet4' + self.nodes[2].extra_args = ['-maxconnections=0', '-prune=550', '-validatepegin=0'] # disable testnet4 sync + self.nodes[2].replace_in_config([('chain=elementsregtest', 'chain=testnet4'), ('[elementsregtest]', '[testnet4]')]) + self.start_nodes() def run_test(self): @@ -39,19 +45,40 @@ class WalletCrossChain(BitcoinTestFramework): 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 / 'node2_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[1].loadwallet, node2_wallet) + assert_raises_rpc_error(-18, 'Wallet file verification failed.', self.nodes[2].loadwallet, node1_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) + assert_raises_rpc_error(-18, 'Wallet file verification failed.', self.nodes[1].restorewallet, 'w', node2_wallet_backup) + assert_raises_rpc_error(-18, 'Wallet file verification failed.', self.nodes[2].restorewallet, 'w', node1_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[1].loadwallet, node2_wallet) + assert_raises_rpc_error(-4, 'Wallet files should not be reused across chains.', self.nodes[2].loadwallet, node1_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) + assert_raises_rpc_error(-4, 'Wallet files should not be reused across chains.', self.nodes[1].restorewallet, 'w', node2_wallet_backup) + assert_raises_rpc_error(-4, 'Wallet files should not be reused across chains.', self.nodes[2].restorewallet, 'w', node1_wallet_backup) if not self.options.descriptors: self.log.info("Override cross-chain wallet load protection")