From 28a2656d22a193fcd92d225964da5a01e8183623 Mon Sep 17 00:00:00 2001 From: Nsable-sh Date: Thu, 20 Aug 2026 15:37:44 -0300 Subject: net: add isolated testnet parameters --- src/selftest.cpp | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 73 insertions(+), 1 deletion(-) (limited to 'src/selftest.cpp') diff --git a/src/selftest.cpp b/src/selftest.cpp index dc9936e..30bd28e 100644 --- a/src/selftest.cpp +++ b/src/selftest.cpp @@ -3076,6 +3076,76 @@ static int RunNetMessageSelfTest() return nFail == 0 ? 0 : 1; } +static int RunNetworkParamsSelfTest() +{ + fflush(stdout); + printf("network-params self-test\n"); + + int nFail = 0; + std::string strOldDataDir = strSetDataDir; + try + { + SelectNetworkParams(false); + nFail += Check(!IsTestNet(), "starts on mainnet after explicit selection") ? 0 : 1; + nFail += Check(ntohs(GetDefaultPort()) == MAINNET_PORT, + "mainnet default P2P port is 8433") ? 0 : 1; + CAddress mainAddr("127.0.0.1"); + nFail += Check(mainAddr.port == htons(MAINNET_PORT), + "mainnet address parsing uses the mainnet default port") ? 0 : 1; + CMessageHeader mainHeader("ping", 0); + char mainMagic[4]; + memcpy(mainMagic, mainHeader.pchMessageStart, sizeof(mainMagic)); + + SelectNetworkParams(true); + nFail += Check(IsTestNet(), "testnet selection is sticky") ? 0 : 1; + nFail += Check(ntohs(GetDefaultPort()) == TESTNET_PORT, + "testnet default P2P port is 18433") ? 0 : 1; + CAddress testAddr("127.0.0.1"); + nFail += Check(testAddr.port == htons(TESTNET_PORT), + "testnet address parsing uses the testnet default port") ? 0 : 1; + CMessageHeader testHeader("ping", 0); + nFail += Check(memcmp(mainMagic, testHeader.pchMessageStart, sizeof(mainMagic)) != 0, + "testnet message magic differs from mainnet") ? 0 : 1; + + std::string tmp; + if (!MakeTempDir(tmp)) + { + nFail += Check(false, "creates a temporary data directory") ? 0 : 1; + } + else + { + strSetDataDir = tmp; + SelectNetworkParams(false); + std::string mainDir = GetAppDir(); + SelectNetworkParams(true); + std::string testDir = GetAppDir(); + nFail += Check(mainDir == tmp, + "mainnet uses the selected data directory") ? 0 : 1; + nFail += Check(testDir == tmp + "/testnet", + "testnet stores data below a testnet subdirectory") ? 0 : 1; + RemoveTree(tmp); + } + } + catch (const std::exception& e) + { + printf(" FAIL exception: %s\n", e.what()); + nFail++; + } + catch (...) + { + printf(" FAIL unknown exception\n"); + nFail++; + } + + strSetDataDir = strOldDataDir; + SelectNetworkParams(false); + + printf("%s (%d failure%s)\n", nFail == 0 ? "ALL TESTS PASSED" : "TESTS FAILED", + nFail, nFail == 1 ? "" : "s"); + fflush(stdout); + return nFail == 0 ? 0 : 1; +} + static CBlock MakeSizedConsensusBlock(size_t nScriptBytes) { CTransaction tx; @@ -3517,6 +3587,8 @@ int RunSelfTest(const std::string& name) return RunWalletPortabilitySelfTest(); if (name == "net-message") return RunNetMessageSelfTest(); + if (name == "network-params") + return RunNetworkParamsSelfTest(); if (name == "consensus-limits") return RunConsensusLimitsSelfTest(); if (name == "pool-stratum") @@ -3529,6 +3601,6 @@ int RunSelfTest(const std::string& name) return RunManagedTorSelfTest(); printf("Unknown self-test '%s'\n", name.c_str()); - printf("Known self-tests: wallet-keypool, wallet-hd, wallet-format, wallet-storage-sanity, db-env-reopen, wallet-sqlite, wallet-sqlite-migration, wallet-crypto, wallet-encrypt, wallet-sqlite-encrypt, wallet-backend-default, wallet-convert, wallet-portability, net-message, consensus-limits, pool-stratum, parse-money, socks5-proxy, managed-tor\n"); + printf("Known self-tests: wallet-keypool, wallet-hd, wallet-format, wallet-storage-sanity, db-env-reopen, wallet-sqlite, wallet-sqlite-migration, wallet-crypto, wallet-encrypt, wallet-sqlite-encrypt, wallet-backend-default, wallet-convert, wallet-portability, net-message, network-params, consensus-limits, pool-stratum, parse-money, socks5-proxy, managed-tor\n"); return 1; } -- cgit v1.2.3 From 56be66dd66e64e6d21eadd80a808b4627921059f Mon Sep 17 00:00:00 2001 From: Nsable-sh Date: Thu, 20 Aug 2026 17:06:08 -0300 Subject: chain: add isolated testnet genesis --- README.md | 2 +- src/main.cpp | 103 +++++++++++++++++++++++++++++++++++++++++++------------ src/main.h | 2 ++ src/main_gui.cpp | 5 +-- src/readme.txt | 2 +- src/selftest.cpp | 21 +++++++++--- 6 files changed, 105 insertions(+), 30 deletions(-) (limited to 'src/selftest.cpp') diff --git a/README.md b/README.md index 4843f5c..77227fa 100644 --- a/README.md +++ b/README.md @@ -355,7 +355,7 @@ Other options worth knowing: ```bash -datadir=PATH # wallet and chain data elsewhere --testnet # isolated test network datadir, port 18433, and message magic +-testnet # isolated test network genesis, datadir, port 18433, and magic -port=N # P2P listen port, default 8433; testnet default 18433 -socks=HOST:PORT # SOCKS5 for outbound Nostr, .btf relay, and onion peer dials -tor[=HOST:PORT] # Tor mode; default local Tor SOCKS5 proxy is 127.0.0.1:9050 diff --git a/src/main.cpp b/src/main.cpp index 6fd8c82..5d266ff 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -41,12 +41,30 @@ unsigned int nTransactionsUpdated = 0; map mapNextTx; map mapBlockIndex; -// Bitflash genesis -- mined under RandomX PoW on 2026-07-22 -// (nTime 1753142400, nBits 0x1f0fffff, RandomX key "Bitflash/RandomX/v1/...") -// Bitflash 2026 stable relaunch genesis (2-min blocks, RandomX). Mined values. -unsigned int GENESIS_NONCE = 3141; -uint256 hashGenesisBlock("0x5bd7cb255d814e48cebcdfb72da4dc87b34bd774227f8ceb546c5640f4bdc169"); -uint256 hashGenesisMerkleRoot("0x1a45b4482532abb29b10e234d3f13132230525a339ecea91658ffa675a5b1325"); +// Bitflash mainnet genesis -- mined under RandomX PoW for the 2026 stable +// relaunch. Testnet gets its own resettable genesis below so block indexes, +// headers, and wallet scans cannot be accidentally mixed across networks. +static const unsigned int MAINNET_GENESIS_TIME = 1753315200; // 2026-07-24 00:00:00 UTC +static const unsigned int MAINNET_GENESIS_NONCE = 3141; +static const char* MAINNET_GENESIS_TIMESTAMP = + "Bitflash 24/Jul/2026 Fair launch: one CPU one vote, no premine"; +static const char* MAINNET_GENESIS_HASH = + "0x5bd7cb255d814e48cebcdfb72da4dc87b34bd774227f8ceb546c5640f4bdc169"; +static const char* MAINNET_GENESIS_MERKLE = + "0x1a45b4482532abb29b10e234d3f13132230525a339ecea91658ffa675a5b1325"; + +static const unsigned int TESTNET_GENESIS_TIME = 1787184000; // 2026-08-20 00:00:00 UTC +static const unsigned int TESTNET_GENESIS_NONCE = 3199; +static const char* TESTNET_GENESIS_TIMESTAMP = + "Bitflash testnet 20/Aug/2026: resettable public test chain"; +static const char* TESTNET_GENESIS_HASH = + "0xa9d11c6d697bcb7aea0653bc088b12dcf9f44e732df205162cf085fd9d9963eb"; +static const char* TESTNET_GENESIS_MERKLE = + "0x510b293105e3ff85b8d4c3920e357f85d1a86bf199eb4e7d11f04f380030ec5c"; + +unsigned int GENESIS_NONCE = MAINNET_GENESIS_NONCE; +uint256 hashGenesisBlock(MAINNET_GENESIS_HASH); +uint256 hashGenesisMerkleRoot(MAINNET_GENESIS_MERKLE); CBlockIndex* pindexGenesisBlock = NULL; int nBestHeight = -1; uint256 hashBestChain = 0; @@ -117,6 +135,63 @@ static std::atomic gParticipantHashRateX1000{0}; static std::mutex gParticipantStatusMutex; static std::string gParticipantStatus; +void SelectGenesisParams(bool fTestNetIn) +{ + static bool fGenesisParamsSelected = false; + static bool fGenesisParamsTestNet = false; + if (fGenesisParamsSelected && fGenesisParamsTestNet == fTestNetIn) + return; + + fGenesisParamsSelected = true; + fGenesisParamsTestNet = fTestNetIn; + + if (fTestNetIn) + { + GENESIS_NONCE = TESTNET_GENESIS_NONCE; + hashGenesisBlock = uint256(TESTNET_GENESIS_HASH); + hashGenesisMerkleRoot = uint256(TESTNET_GENESIS_MERKLE); + } + else + { + GENESIS_NONCE = MAINNET_GENESIS_NONCE; + hashGenesisBlock = uint256(MAINNET_GENESIS_HASH); + hashGenesisMerkleRoot = uint256(MAINNET_GENESIS_MERKLE); + } +} + +void SelectChainParams(bool fTestNetIn) +{ + SelectNetworkParams(fTestNetIn); + SelectGenesisParams(fTestNetIn); +} + +static CBlock BuildGenesisBlock() +{ + const char* pszTimestamp = IsTestNet() ? + TESTNET_GENESIS_TIMESTAMP : MAINNET_GENESIS_TIMESTAMP; + + CTransaction txNew; + txNew.vin.resize(1); + txNew.vout.resize(1); + txNew.vin[0].scriptSig = CScript() << 486604799 << CBigNum(4) << + vector((unsigned char*)pszTimestamp, + (unsigned char*)pszTimestamp + strlen(pszTimestamp)); + txNew.vout[0].nValue = 50 * COIN; + txNew.vout[0].scriptPubKey = CScript() << + CBigNum("0x5F1DF16B2B704C8A578D0BBAF74D385CDE12C11EE50455F3C438EF4C3FBCF649B6DE611FEAE06279A60939E028A8D65C10B73071A6F16719274855FEB0FD8A6704") << + OP_CHECKSIG; + + CBlock block; + block.vtx.push_back(txNew); + block.hashPrevBlock = 0; + block.hashMerkleRoot = block.BuildMerkleTree(); + block.nVersion = 1; + block.nTime = IsTestNet() ? TESTNET_GENESIS_TIME : MAINNET_GENESIS_TIME; + block.nBits = 0x1f0fffff; // = bnProofOfWorkLimit (Bitflash RandomX floor) + block.nNonce = GENESIS_NONCE; + return block; +} + void SetParticipantMiningStatus(const std::string& status) { // Updates the GUI status string only. Logging is done at each call site @@ -2664,21 +2739,7 @@ bool LoadBlockIndex(bool fAllowNew) // vMerkleTree: 4a5e1e // Bitflash genesis block -- own header (fair launch, 2026 stable relaunch) - char* pszTimestamp = "Bitflash 24/Jul/2026 Fair launch: one CPU one vote, no premine"; - CTransaction txNew; - txNew.vin.resize(1); - txNew.vout.resize(1); - txNew.vin[0].scriptSig = CScript() << 486604799 << CBigNum(4) << vector((unsigned char*)pszTimestamp, (unsigned char*)pszTimestamp + strlen(pszTimestamp)); - txNew.vout[0].nValue = 50 * COIN; - txNew.vout[0].scriptPubKey = CScript() << CBigNum("0x5F1DF16B2B704C8A578D0BBAF74D385CDE12C11EE50455F3C438EF4C3FBCF649B6DE611FEAE06279A60939E028A8D65C10B73071A6F16719274855FEB0FD8A6704") << OP_CHECKSIG; - CBlock block; - block.vtx.push_back(txNew); - block.hashPrevBlock = 0; - block.hashMerkleRoot = block.BuildMerkleTree(); - block.nVersion = 1; - block.nTime = 1753315200; // 2026-07-24 00:00:00 UTC (stable relaunch) - block.nBits = 0x1f0fffff; // = bnProofOfWorkLimit (Bitflash RandomX floor) - block.nNonce = GENESIS_NONCE; + CBlock block = BuildGenesisBlock(); // If the nonce has not been found yet, mine the genesis once (RandomX) // and print the values to be hardcoded in the constants above. diff --git a/src/main.h b/src/main.h index dc5527e..793c4e8 100644 --- a/src/main.h +++ b/src/main.h @@ -59,6 +59,8 @@ extern map mapBlockIndex; extern uint256 hashGenesisBlock; extern uint256 hashGenesisMerkleRoot; extern unsigned int GENESIS_NONCE; +void SelectGenesisParams(bool fTestNetIn); +void SelectChainParams(bool fTestNetIn); extern CBlockIndex* pindexGenesisBlock; extern int nBestHeight; extern uint256 hashBestChain; diff --git a/src/main_gui.cpp b/src/main_gui.cpp index 0a961bf..9f0fec0 100644 --- a/src/main_gui.cpp +++ b/src/main_gui.cpp @@ -143,7 +143,7 @@ static void PrintUsage() printf(" /onionservice=HOST.onion:PORT (advertise this node's Tor hidden service)\n"); printf("\n"); printf("Network:\n"); - printf(" /testnet (isolated test network datadir, port, and magic)\n"); + printf(" /testnet (isolated test network genesis, datadir, port, and magic)\n"); printf(" /port=N (P2P listen port, default 8433; testnet 18433)\n"); printf(" /socks=HOST:PORT (SOCKS5 proxy for Nostr, .btf relay, and onion dials)\n"); printf(" /tor[=HOST:PORT] (Tor mode; default SOCKS5 proxy is 127.0.0.1:9050)\n"); @@ -206,7 +206,8 @@ static void ParseStartupArguments(int argc, char* argv[]) if (arg(argc,argv,"/datadir") || arg(argc,argv,"-datadir")) strSetDataDir = argval2(argc, argv, "/datadir", "-datadir"); - SelectNetworkParams(arg(argc, argv, "/testnet") || arg(argc, argv, "-testnet")); + bool fUseTestNet = arg(argc, argv, "/testnet") || arg(argc, argv, "-testnet"); + SelectChainParams(fUseTestNet); if (arg(argc,argv,"/debug") || arg(argc,argv,"-debug")) fDebug = true; diff --git a/src/readme.txt b/src/readme.txt index 5d62791..810e8b7 100644 --- a/src/readme.txt +++ b/src/readme.txt @@ -41,7 +41,7 @@ The resulting executable is src/bitflash.exe. Command-line options (in addition to the original ones) ------------------------------------------------------- - /testnet use isolated test network datadir, port 18433, and message magic + /testnet use isolated test network genesis, datadir, port 18433, and message magic /port=N listen on P2P port N (default 8433; testnet default 18433) /socks=HOST:PORT route outbound Nostr, .btf rendezvous, and direct onion peer diff --git a/src/selftest.cpp b/src/selftest.cpp index 30bd28e..1a72e3a 100644 --- a/src/selftest.cpp +++ b/src/selftest.cpp @@ -3085,7 +3085,7 @@ static int RunNetworkParamsSelfTest() std::string strOldDataDir = strSetDataDir; try { - SelectNetworkParams(false); + SelectChainParams(false); nFail += Check(!IsTestNet(), "starts on mainnet after explicit selection") ? 0 : 1; nFail += Check(ntohs(GetDefaultPort()) == MAINNET_PORT, "mainnet default P2P port is 8433") ? 0 : 1; @@ -3095,8 +3095,11 @@ static int RunNetworkParamsSelfTest() CMessageHeader mainHeader("ping", 0); char mainMagic[4]; memcpy(mainMagic, mainHeader.pchMessageStart, sizeof(mainMagic)); + uint256 mainGenesis = hashGenesisBlock; + uint256 mainMerkle = hashGenesisMerkleRoot; + unsigned int mainNonce = GENESIS_NONCE; - SelectNetworkParams(true); + SelectChainParams(true); nFail += Check(IsTestNet(), "testnet selection is sticky") ? 0 : 1; nFail += Check(ntohs(GetDefaultPort()) == TESTNET_PORT, "testnet default P2P port is 18433") ? 0 : 1; @@ -3106,6 +3109,14 @@ static int RunNetworkParamsSelfTest() CMessageHeader testHeader("ping", 0); nFail += Check(memcmp(mainMagic, testHeader.pchMessageStart, sizeof(mainMagic)) != 0, "testnet message magic differs from mainnet") ? 0 : 1; + nFail += Check(hashGenesisBlock != mainGenesis && + hashGenesisMerkleRoot != mainMerkle && + GENESIS_NONCE != mainNonce, + "testnet genesis constants differ from mainnet") ? 0 : 1; + nFail += Check(hashGenesisBlock == uint256("0xa9d11c6d697bcb7aea0653bc088b12dcf9f44e732df205162cf085fd9d9963eb") && + hashGenesisMerkleRoot == uint256("0x510b293105e3ff85b8d4c3920e357f85d1a86bf199eb4e7d11f04f380030ec5c") && + GENESIS_NONCE == 3199, + "testnet genesis constants are pinned") ? 0 : 1; std::string tmp; if (!MakeTempDir(tmp)) @@ -3115,9 +3126,9 @@ static int RunNetworkParamsSelfTest() else { strSetDataDir = tmp; - SelectNetworkParams(false); + SelectChainParams(false); std::string mainDir = GetAppDir(); - SelectNetworkParams(true); + SelectChainParams(true); std::string testDir = GetAppDir(); nFail += Check(mainDir == tmp, "mainnet uses the selected data directory") ? 0 : 1; @@ -3138,7 +3149,7 @@ static int RunNetworkParamsSelfTest() } strSetDataDir = strOldDataDir; - SelectNetworkParams(false); + SelectChainParams(false); printf("%s (%d failure%s)\n", nFail == 0 ? "ALL TESTS PASSED" : "TESTS FAILED", nFail, nFail == 1 ? "" : "s"); -- cgit v1.2.3