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/Makefile | 1 + src/main.cpp | 16 +++++++++--- src/main_gui.cpp | 7 ++++-- src/makefile.mingw | 1 + src/net.cpp | 32 +++++++++++++++++++++-- src/net.h | 20 +++++++++------ src/readme.txt | 3 ++- src/selftest.cpp | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 8 files changed, 138 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/Makefile b/src/Makefile index b83ff58..ab0b307 100644 --- a/src/Makefile +++ b/src/Makefile @@ -193,6 +193,7 @@ tests: test_bip32 bitflash-node ./bitflash-node -selftest=wallet-crypto ./bitflash-node -selftest=wallet-encrypt ./bitflash-node -selftest=net-message + ./bitflash-node -selftest=network-params ./bitflash-node -selftest=consensus-limits ./bitflash-node -selftest=pool-stratum ./bitflash-node -selftest=parse-money diff --git a/src/main.cpp b/src/main.cpp index 5bd4028..6fd8c82 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2497,11 +2497,21 @@ string GetAppDir() { return "."; } - static bool fMkdirDone; - if (!fMkdirDone) + static string strCreatedBaseDir; + if (strCreatedBaseDir != strDir) { - fMkdirDone = true; _mkdir(strDir.c_str()); + strCreatedBaseDir = strDir; + } + if (IsTestNet()) + { + strDir += "/testnet"; + static string strCreatedTestnetDir; + if (strCreatedTestnetDir != strDir) + { + _mkdir(strDir.c_str()); + strCreatedTestnetDir = strDir; + } } return strDir; } diff --git a/src/main_gui.cpp b/src/main_gui.cpp index 943799f..0a961bf 100644 --- a/src/main_gui.cpp +++ b/src/main_gui.cpp @@ -118,7 +118,7 @@ static void PrintUsage() printf(" wallet-crypto, wallet-encrypt, wallet-portability,\n"); printf(" net-message,\n"); printf(" consensus-limits, pool-stratum,\n"); - printf(" parse-money, socks5-proxy, or managed-tor\n"); + printf(" parse-money, network-params, socks5-proxy, or managed-tor\n"); printf("\n"); printf("Mining mode:\n"); printf(" /operator\n"); @@ -143,7 +143,8 @@ static void PrintUsage() printf(" /onionservice=HOST.onion:PORT (advertise this node's Tor hidden service)\n"); printf("\n"); printf("Network:\n"); - printf(" /port=N (P2P listen port, default 8433)\n"); + printf(" /testnet (isolated test network 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"); printf(" /managedtor[=PATH] (start Tor, create a hidden service, advertise its onion)\n"); @@ -205,6 +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")); + if (arg(argc,argv,"/debug") || arg(argc,argv,"-debug")) fDebug = true; diff --git a/src/makefile.mingw b/src/makefile.mingw index 55f186b..910e025 100644 --- a/src/makefile.mingw +++ b/src/makefile.mingw @@ -116,6 +116,7 @@ tests: test_bip32.exe bitflash.exe ./bitflash.exe -selftest=wallet-backend-default ./bitflash.exe -selftest=wallet-convert ./bitflash.exe -selftest=net-message + ./bitflash.exe -selftest=network-params ./bitflash.exe -selftest=consensus-limits ./bitflash.exe -selftest=pool-stratum ./bitflash.exe -selftest=socks5-proxy diff --git a/src/net.cpp b/src/net.cpp index 4603d2d..ecfd565 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -50,10 +50,14 @@ static int BtfPoll(BtfPollFd* pfd, unsigned int nfd, int nTimeoutMs) // // Global state variables // +static const char pchMainnetMessageStart[4] = { 0xbf, 0x20, 0x5c, 0xfd }; +static const char pchTestnetMessageStart[4] = { (char)0xce, (char)0xe2, (char)0xc0, (char)0xff }; +static bool fTestNet = false; +char pchMessageStart[4] = { 0xbf, 0x20, 0x5c, 0xfd }; bool fClient = false; uint64 nLocalServices = (fClient ? 0 : NODE_NETWORK); -CAddress addrLocalHost(0, DEFAULT_PORT, nLocalServices); -unsigned short nListenPort = DEFAULT_PORT; // local P2P port (tunable via /port) +CAddress addrLocalHost(0, GetDefaultPort(), nLocalServices); +unsigned short nListenPort = GetDefaultPort(); // local P2P port (tunable via /port) CNode nodeLocalHost(INVALID_SOCKET, CAddress("127.0.0.1", nLocalServices)); CNode* pnodeLocalHost = &nodeLocalHost; bool fShutdown = false; @@ -90,6 +94,29 @@ static int64 nBtfHandshakeSilent = 0; static string strBtfLastDialFail; static string strBtfLastHandshakeTimeout; +bool IsTestNet() +{ + return fTestNet; +} + +unsigned short GetDefaultPort() +{ + return htons(fTestNet ? TESTNET_PORT : MAINNET_PORT); +} + +void SelectNetworkParams(bool fTestNetIn) +{ + if (fTestNet == fTestNetIn) + return; + + fTestNet = fTestNetIn; + memcpy(pchMessageStart, + fTestNet ? pchTestnetMessageStart : pchMainnetMessageStart, + sizeof(pchMessageStart)); + nListenPort = GetDefaultPort(); + addrLocalHost.port = nListenPort; +} + void BtfChurnNoteResolveAttempt() { CRITICAL_BLOCK(cs_btfChurn) @@ -288,6 +315,7 @@ string GetDiagnosticsText() int nMedian = GetPeerMedianHeight(); str += "Bitflash node diagnostics\n"; + str += strprintf(" network %s\n", IsTestNet() ? "testnet" : "mainnet"); str += strprintf(" uptime %s\n", FormatAge(nNodeStartTime ? nNow - nNodeStartTime : -1).c_str()); if (nMedian < 0) diff --git a/src/net.h b/src/net.h index e3d4a93..56f1341 100644 --- a/src/net.h +++ b/src/net.h @@ -13,7 +13,8 @@ class CBlockIndex; -static const unsigned short DEFAULT_PORT = htons(8433); +static const unsigned short MAINNET_PORT = 8433; +static const unsigned short TESTNET_PORT = 18433; static const unsigned int PUBLISH_HOPS = 5; enum { @@ -26,6 +27,9 @@ enum bool GetMyExternalIP(unsigned int& ipRet); +bool IsTestNet(); +unsigned short GetDefaultPort(); +void SelectNetworkParams(bool fTestNetIn); CNode* ConnectNodeBtf(const string& strBtfAddr); CNode* ConnectNodeBtfResolved(const string& strBtfAddr, const string& strMeeting, const string& strOnion, const unsigned char enc_pub[32], @@ -219,15 +223,17 @@ void CheckForShutdown(int n); // The message start string is designed to be unlikely to occur in normal data. // The characters are rarely used upper ascii, not valid as UTF-8, and produce // a large 4-byte int at any alignment. -// Magic bytes unique to the Bitflash network (0xbf = "BF"). Second byte bumped -// to 0x20 for the 2026 stable relaunch so the old test chain can't interfere. -static const char pchMessageStart[4] = { 0xbf, 0x20, 0x5c, 0xfd }; +// Magic bytes unique to the selected Bitflash network. Mainnet uses 0xbf = +// "BF"; testnet swaps the whole sequence so a test node cannot handshake with +// mainnet before consensus-level testnet activation exists. +extern char pchMessageStart[4]; class CMessageHeader { public: enum { COMMAND_SIZE=12 }; - char pchMessageStart[sizeof(::pchMessageStart)]; + enum { MESSAGE_START_SIZE=4 }; + char pchMessageStart[MESSAGE_START_SIZE]; char pchCommand[COMMAND_SIZE]; unsigned int nMessageSize; @@ -318,7 +324,7 @@ public: nServices = 0; memcpy(pchReserved, pchIPv4, sizeof(pchReserved)); ip = 0; - port = DEFAULT_PORT; + port = GetDefaultPort(); nTime = GetAdjustedTime(); nLastFailed = 0; } @@ -348,7 +354,7 @@ public: nServices = nServicesIn; memcpy(pchReserved, pchIPv4, sizeof(pchReserved)); ip = 0; - port = DEFAULT_PORT; + port = GetDefaultPort(); nTime = GetAdjustedTime(); nLastFailed = 0; diff --git a/src/readme.txt b/src/readme.txt index 752be66..5d62791 100644 --- a/src/readme.txt +++ b/src/readme.txt @@ -41,7 +41,8 @@ The resulting executable is src/bitflash.exe. Command-line options (in addition to the original ones) ------------------------------------------------------- - /port=N listen on P2P port N (default 8433) + /testnet use isolated test network 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 dials through a SOCKS5 proxy, e.g. /socks=127.0.0.1:9050 for local Tor 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