upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile1
-rw-r--r--src/main.cpp163
-rw-r--r--src/main.h2
-rw-r--r--src/main_gui.cpp15
-rw-r--r--src/makefile.mingw1
-rw-r--r--src/net.cpp33
-rw-r--r--src/net.h25
-rw-r--r--src/randomx_pow.cpp117
-rw-r--r--src/randomx_pow.h7
-rw-r--r--src/readme.txt3
-rw-r--r--src/selftest.cpp85
11 files changed, 387 insertions, 65 deletions
diff --git a/src/Makefile b/src/Makefile
index 888110f..c997ae3 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -225,6 +225,7 @@ tests: test_bip32 test_compactblock bitflash-node
225 ./bitflash-node -selftest=wallet-crypto 225 ./bitflash-node -selftest=wallet-crypto
226 ./bitflash-node -selftest=wallet-encrypt 226 ./bitflash-node -selftest=wallet-encrypt
227 ./bitflash-node -selftest=net-message 227 ./bitflash-node -selftest=net-message
228 ./bitflash-node -selftest=network-params
228 ./bitflash-node -selftest=consensus-limits 229 ./bitflash-node -selftest=consensus-limits
229 ./bitflash-node -selftest=pool-stratum 230 ./bitflash-node -selftest=pool-stratum
230 ./bitflash-node -selftest=payout-ledger 231 ./bitflash-node -selftest=payout-ledger
diff --git a/src/main.cpp b/src/main.cpp
index 7398064..0fa6b89 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -42,12 +42,30 @@ unsigned int nTransactionsUpdated = 0;
42map<COutPoint, CInPoint> mapNextTx; 42map<COutPoint, CInPoint> mapNextTx;
43 43
44map<uint256, CBlockIndex*> mapBlockIndex; 44map<uint256, CBlockIndex*> mapBlockIndex;
45// Bitflash genesis -- mined under RandomX PoW on 2026-07-22 45// Bitflash mainnet genesis -- mined under RandomX PoW for the 2026 stable
46// (nTime 1753142400, nBits 0x1f0fffff, RandomX key "Bitflash/RandomX/v1/...") 46// relaunch. Testnet gets its own resettable genesis below so block indexes,
47// Bitflash 2026 stable relaunch genesis (2-min blocks, RandomX). Mined values. 47// headers, and wallet scans cannot be accidentally mixed across networks.
48unsigned int GENESIS_NONCE = 3141; 48static const unsigned int MAINNET_GENESIS_TIME = 1753315200; // 2026-07-24 00:00:00 UTC
49uint256 hashGenesisBlock("0x5bd7cb255d814e48cebcdfb72da4dc87b34bd774227f8ceb546c5640f4bdc169"); 49static const unsigned int MAINNET_GENESIS_NONCE = 3141;
50uint256 hashGenesisMerkleRoot("0x1a45b4482532abb29b10e234d3f13132230525a339ecea91658ffa675a5b1325"); 50static const char* MAINNET_GENESIS_TIMESTAMP =
51 "Bitflash 24/Jul/2026 Fair launch: one CPU one vote, no premine";
52static const char* MAINNET_GENESIS_HASH =
53 "0x5bd7cb255d814e48cebcdfb72da4dc87b34bd774227f8ceb546c5640f4bdc169";
54static const char* MAINNET_GENESIS_MERKLE =
55 "0x1a45b4482532abb29b10e234d3f13132230525a339ecea91658ffa675a5b1325";
56
57static const unsigned int TESTNET_GENESIS_TIME = 1787184000; // 2026-08-20 00:00:00 UTC
58static const unsigned int TESTNET_GENESIS_NONCE = 3199;
59static const char* TESTNET_GENESIS_TIMESTAMP =
60 "Bitflash testnet 20/Aug/2026: resettable public test chain";
61static const char* TESTNET_GENESIS_HASH =
62 "0xa9d11c6d697bcb7aea0653bc088b12dcf9f44e732df205162cf085fd9d9963eb";
63static const char* TESTNET_GENESIS_MERKLE =
64 "0x510b293105e3ff85b8d4c3920e357f85d1a86bf199eb4e7d11f04f380030ec5c";
65
66unsigned int GENESIS_NONCE = MAINNET_GENESIS_NONCE;
67uint256 hashGenesisBlock(MAINNET_GENESIS_HASH);
68uint256 hashGenesisMerkleRoot(MAINNET_GENESIS_MERKLE);
51CBlockIndex* pindexGenesisBlock = NULL; 69CBlockIndex* pindexGenesisBlock = NULL;
52int nBestHeight = -1; 70int nBestHeight = -1;
53uint256 hashBestChain = 0; 71uint256 hashBestChain = 0;
@@ -254,6 +272,63 @@ static std::atomic<uint64> gParticipantHashRateX1000{0};
254static std::mutex gParticipantStatusMutex; 272static std::mutex gParticipantStatusMutex;
255static std::string gParticipantStatus; 273static std::string gParticipantStatus;
256 274
275void SelectGenesisParams(bool fTestNetIn)
276{
277 static bool fGenesisParamsSelected = false;
278 static bool fGenesisParamsTestNet = false;
279 if (fGenesisParamsSelected && fGenesisParamsTestNet == fTestNetIn)
280 return;
281
282 fGenesisParamsSelected = true;
283 fGenesisParamsTestNet = fTestNetIn;
284
285 if (fTestNetIn)
286 {
287 GENESIS_NONCE = TESTNET_GENESIS_NONCE;
288 hashGenesisBlock = uint256(TESTNET_GENESIS_HASH);
289 hashGenesisMerkleRoot = uint256(TESTNET_GENESIS_MERKLE);
290 }
291 else
292 {
293 GENESIS_NONCE = MAINNET_GENESIS_NONCE;
294 hashGenesisBlock = uint256(MAINNET_GENESIS_HASH);
295 hashGenesisMerkleRoot = uint256(MAINNET_GENESIS_MERKLE);
296 }
297}
298
299void SelectChainParams(bool fTestNetIn)
300{
301 SelectNetworkParams(fTestNetIn);
302 SelectGenesisParams(fTestNetIn);
303}
304
305static CBlock BuildGenesisBlock()
306{
307 const char* pszTimestamp = IsTestNet() ?
308 TESTNET_GENESIS_TIMESTAMP : MAINNET_GENESIS_TIMESTAMP;
309
310 CTransaction txNew;
311 txNew.vin.resize(1);
312 txNew.vout.resize(1);
313 txNew.vin[0].scriptSig = CScript() << 486604799 << CBigNum(4) <<
314 vector<unsigned char>((unsigned char*)pszTimestamp,
315 (unsigned char*)pszTimestamp + strlen(pszTimestamp));
316 txNew.vout[0].nValue = 50 * COIN;
317 txNew.vout[0].scriptPubKey = CScript() <<
318 CBigNum("0x5F1DF16B2B704C8A578D0BBAF74D385CDE12C11EE50455F3C438EF4C3FBCF649B6DE611FEAE06279A60939E028A8D65C10B73071A6F16719274855FEB0FD8A6704") <<
319 OP_CHECKSIG;
320
321 CBlock block;
322 block.vtx.push_back(txNew);
323 block.hashPrevBlock = 0;
324 block.hashMerkleRoot = block.BuildMerkleTree();
325 block.nVersion = 1;
326 block.nTime = IsTestNet() ? TESTNET_GENESIS_TIME : MAINNET_GENESIS_TIME;
327 block.nBits = 0x1f0fffff; // = bnProofOfWorkLimit (Bitflash RandomX floor)
328 block.nNonce = GENESIS_NONCE;
329 return block;
330}
331
257void SetParticipantMiningStatus(const std::string& status) 332void SetParticipantMiningStatus(const std::string& status)
258{ 333{
259 // Updates the GUI status string only. Logging is done at each call site 334 // Updates the GUI status string only. Logging is done at each call site
@@ -2654,11 +2729,21 @@ string GetAppDir()
2654 { 2729 {
2655 return "."; 2730 return ".";
2656 } 2731 }
2657 static bool fMkdirDone; 2732 static string strCreatedBaseDir;
2658 if (!fMkdirDone) 2733 if (strCreatedBaseDir != strDir)
2659 { 2734 {
2660 fMkdirDone = true;
2661 _mkdir(strDir.c_str()); 2735 _mkdir(strDir.c_str());
2736 strCreatedBaseDir = strDir;
2737 }
2738 if (IsTestNet())
2739 {
2740 strDir += "/testnet";
2741 static string strCreatedTestnetDir;
2742 if (strCreatedTestnetDir != strDir)
2743 {
2744 _mkdir(strDir.c_str());
2745 strCreatedTestnetDir = strDir;
2746 }
2662 } 2747 }
2663 return strDir; 2748 return strDir;
2664} 2749}
@@ -2812,38 +2897,34 @@ bool LoadBlockIndex(bool fAllowNew)
2812 2897
2813 // Bitflash genesis block -- own header (fair launch) 2898 // Bitflash genesis block -- own header (fair launch)
2814 // 2899 //
2815 // Do not change pszTimestamp, the scriptPubKey, nTime or nNonce. They 2900 // Upstream moved the construction into BuildGenesisBlock() so mainnet
2816 // are the inputs to the genesis hash, and the genesis hash is 2901 // and the isolated testnet can share it. The mainnet inputs it uses are
2817 // consensus. Changing one on its own trips the assert below, which is 2902 // byte-identical to what this block built inline before the merge --
2818 // loud and reverts cleanly; changing one and regenerating 2903 // timestamp string, nTime, nNonce, scriptPubKey, nBits and nVersion all
2819 // GENESIS_NONCE and hashGenesisBlock to match -- which is exactly what 2904 // verified equal against MAINNET_GENESIS_* -- so the mainnet genesis
2820 // the re-mining branch below prints out and invites -- gives a node 2905 // hash is unchanged. The warning below is why that was checked rather
2821 // that starts without complaint, builds its own chain from height 0, 2906 // than assumed, and it still applies to those constants:
2822 // and rejects every real peer as being on a different chain. That one 2907 //
2823 // fails silently, and the damage scales with how many people run the 2908 // Do not change MAINNET_GENESIS_TIMESTAMP, the scriptPubKey,
2824 // "fixed" build. 2909 // MAINNET_GENESIS_TIME or MAINNET_GENESIS_NONCE. They are the inputs to
2825 char* pszTimestamp = "Bitflash 24/Jul/2026 Fair launch: one CPU one vote, no premine"; 2910 // the genesis hash, and the genesis hash is consensus. Changing one on
2826 CTransaction txNew; 2911 // its own trips the assert below, which is loud and reverts cleanly;
2827 txNew.vin.resize(1); 2912 // changing one and regenerating GENESIS_NONCE and hashGenesisBlock to
2828 txNew.vout.resize(1); 2913 // match -- which is exactly what the re-mining branch below prints out
2829 txNew.vin[0].scriptSig = CScript() << 486604799 << CBigNum(4) << vector<unsigned char>((unsigned char*)pszTimestamp, (unsigned char*)pszTimestamp + strlen(pszTimestamp)); 2914 // and invites -- gives a node that starts without complaint, builds its
2830 txNew.vout[0].nValue = 50 * COIN; 2915 // own chain from height 0, and rejects every real peer as being on a
2831 txNew.vout[0].scriptPubKey = CScript() << CBigNum("0x5F1DF16B2B704C8A578D0BBAF74D385CDE12C11EE50455F3C438EF4C3FBCF649B6DE611FEAE06279A60939E028A8D65C10B73071A6F16719274855FEB0FD8A6704") << OP_CHECKSIG; 2916 // different chain. That one fails silently, and the damage scales with
2832 CBlock block; 2917 // how many people run the "fixed" build.
2833 block.vtx.push_back(txNew); 2918 //
2834 block.hashPrevBlock = 0; 2919 // MAINNET_GENESIS_TIME is 1753315200, which is 2025-07-24T00:00:00Z,
2835 block.hashMerkleRoot = block.BuildMerkleTree(); 2920 // while the headline in MAINNET_GENESIS_TIMESTAMP reads 2026 -- as does
2836 block.nVersion = 1; 2921 // the comment upstream put on that constant, which is wrong about the
2837 // 1753315200 is 2025-07-24T00:00:00Z. The headline in pszTimestamp 2922 // year. This is a known discrepancy and not a typo in the value: it is
2838 // above reads 2026 and does not match it. This is a known discrepancy 2923 // only consensus-critical as an input to the genesis hash, the block
2839 // and not a typo in the constant: the value is only consensus-critical 2924 // hashes to hashGenesisBlock with it, and every node on the network
2840 // as an input to the genesis hash, the block hashes to 2925 // agrees. The mismatch is in the launched chain and cannot be corrected
2841 // hashGenesisBlock with it, and every node on the network agrees. The 2926 // -- see the warning above before deciding otherwise.
2842 // mismatch is in the launched chain and cannot be corrected -- see the 2927 CBlock block = BuildGenesisBlock();
2843 // warning above before deciding otherwise.
2844 block.nTime = 1753315200;
2845 block.nBits = 0x1f0fffff; // = bnProofOfWorkLimit (Bitflash RandomX floor)
2846 block.nNonce = GENESIS_NONCE;
2847 2928
2848 // If the nonce has not been found yet, mine the genesis once (RandomX) 2929 // If the nonce has not been found yet, mine the genesis once (RandomX)
2849 // and print the values to be hardcoded in the constants above. 2930 // and print the values to be hardcoded in the constants above.
diff --git a/src/main.h b/src/main.h
index e05b467..26b3b69 100644
--- a/src/main.h
+++ b/src/main.h
@@ -94,6 +94,8 @@ extern map<uint256, CBlockIndex*> mapBlockIndex;
94extern uint256 hashGenesisBlock; 94extern uint256 hashGenesisBlock;
95extern uint256 hashGenesisMerkleRoot; 95extern uint256 hashGenesisMerkleRoot;
96extern unsigned int GENESIS_NONCE; 96extern unsigned int GENESIS_NONCE;
97void SelectGenesisParams(bool fTestNetIn);
98void SelectChainParams(bool fTestNetIn);
97extern CBlockIndex* pindexGenesisBlock; 99extern CBlockIndex* pindexGenesisBlock;
98extern int nBestHeight; 100extern int nBestHeight;
99extern uint256 hashBestChain; 101extern uint256 hashBestChain;
diff --git a/src/main_gui.cpp b/src/main_gui.cpp
index dd001c8..2e4eb91 100644
--- a/src/main_gui.cpp
+++ b/src/main_gui.cpp
@@ -120,7 +120,7 @@ static void PrintUsage()
120 printf(" wallet-crypto, wallet-encrypt, wallet-portability,\n"); 120 printf(" wallet-crypto, wallet-encrypt, wallet-portability,\n");
121 printf(" net-message,\n"); 121 printf(" net-message,\n");
122 printf(" consensus-limits, pool-stratum,\n"); 122 printf(" consensus-limits, pool-stratum,\n");
123 printf(" parse-money, socks5-proxy, or managed-tor\n"); 123 printf(" parse-money, network-params, socks5-proxy, or managed-tor\n");
124 printf("\n"); 124 printf("\n");
125 printf("Mining mode:\n"); 125 printf("Mining mode:\n");
126 printf(" /operator\n"); 126 printf(" /operator\n");
@@ -132,6 +132,8 @@ static void PrintUsage()
132 printf(" /stratumbridgeport=N (default 3333; listen on 127.0.0.1)\n"); 132 printf(" /stratumbridgeport=N (default 3333; listen on 127.0.0.1)\n");
133 printf(" /solomine\n"); 133 printf(" /solomine\n");
134 printf(" /genproclimit=N (mining threads; 0 or absent = every core but one)\n"); 134 printf(" /genproclimit=N (mining threads; 0 or absent = every core but one)\n");
135 printf(" /nolargepages (do not ask for 2 MB pages for the RandomX\n");
136 printf(" cache, dataset and scratchpads)\n");
135 printf(" /checkblocks=N (blocks re-verified at startup, default 288, 0 = all)\n"); 137 printf(" /checkblocks=N (blocks re-verified at startup, default 288, 0 = all)\n");
136 printf("\n"); 138 printf("\n");
137 printf("Pool operator announcement:\n"); 139 printf("Pool operator announcement:\n");
@@ -148,7 +150,8 @@ static void PrintUsage()
148 printf(" /onionservice=HOST.onion:PORT (advertise this node's Tor hidden service)\n"); 150 printf(" /onionservice=HOST.onion:PORT (advertise this node's Tor hidden service)\n");
149 printf("\n"); 151 printf("\n");
150 printf("Network:\n"); 152 printf("Network:\n");
151 printf(" /port=N (P2P listen port, default 8433)\n"); 153 printf(" /testnet (isolated test network genesis, datadir, port, and magic)\n");
154 printf(" /port=N (P2P listen port, default 8433; testnet 18433)\n");
152 printf(" /socks=HOST:PORT (SOCKS5 proxy for Nostr, .btf relay, and onion dials)\n"); 155 printf(" /socks=HOST:PORT (SOCKS5 proxy for Nostr, .btf relay, and onion dials)\n");
153 printf(" /tor[=HOST:PORT] (Tor mode; default SOCKS5 proxy is 127.0.0.1:9050)\n"); 156 printf(" /tor[=HOST:PORT] (Tor mode; default SOCKS5 proxy is 127.0.0.1:9050)\n");
154 printf(" /managedtor[=PATH] (start Tor, create a hidden service, advertise its onion)\n"); 157 printf(" /managedtor[=PATH] (start Tor, create a hidden service, advertise its onion)\n");
@@ -214,6 +217,9 @@ static void ParseStartupArguments(int argc, char* argv[])
214 if (arg(argc,argv,"/datadir") || arg(argc,argv,"-datadir")) 217 if (arg(argc,argv,"/datadir") || arg(argc,argv,"-datadir"))
215 strSetDataDir = argval2(argc, argv, "/datadir", "-datadir"); 218 strSetDataDir = argval2(argc, argv, "/datadir", "-datadir");
216 219
220 bool fUseTestNet = arg(argc, argv, "/testnet") || arg(argc, argv, "-testnet");
221 SelectChainParams(fUseTestNet);
222
217 if (arg(argc,argv,"/debug") || arg(argc,argv,"-debug")) 223 if (arg(argc,argv,"/debug") || arg(argc,argv,"-debug"))
218 fDebug = true; 224 fDebug = true;
219 225
@@ -284,6 +290,11 @@ static void ParseStartupArguments(int argc, char* argv[])
284 if (!strCheckBlocks.empty()) 290 if (!strCheckBlocks.empty())
285 nCheckBlocksOnLoad = atoi(strCheckBlocks.c_str()); 291 nCheckBlocksOnLoad = atoi(strCheckBlocks.c_str());
286 292
293 // Must be read before LoadBlockIndex(), which is what first calls
294 // RandomXInit() and fixes how the cache is allocated.
295 if (arg(argc,argv,"/nolargepages") || arg(argc,argv,"-nolargepages"))
296 fRandomXLargePages = false;
297
287 // /genproclimit=N -- threads to hash with. 0 or absent means automatic, 298 // /genproclimit=N -- threads to hash with. 0 or absent means automatic,
288 // which is every core but one. Named after Bitcoin's own option so it reads 299 // which is every core but one. Named after Bitcoin's own option so it reads
289 // familiarly to anyone who has run one of these before. 300 // familiarly to anyone who has run one of these before.
diff --git a/src/makefile.mingw b/src/makefile.mingw
index 740bba6..362794a 100644
--- a/src/makefile.mingw
+++ b/src/makefile.mingw
@@ -116,6 +116,7 @@ tests: test_bip32.exe bitflash.exe
116 ./bitflash.exe -selftest=wallet-backend-default 116 ./bitflash.exe -selftest=wallet-backend-default
117 ./bitflash.exe -selftest=wallet-convert 117 ./bitflash.exe -selftest=wallet-convert
118 ./bitflash.exe -selftest=net-message 118 ./bitflash.exe -selftest=net-message
119 ./bitflash.exe -selftest=network-params
119 ./bitflash.exe -selftest=consensus-limits 120 ./bitflash.exe -selftest=consensus-limits
120 ./bitflash.exe -selftest=pool-stratum 121 ./bitflash.exe -selftest=pool-stratum
121 ./bitflash.exe -selftest=socks5-proxy 122 ./bitflash.exe -selftest=socks5-proxy
diff --git a/src/net.cpp b/src/net.cpp
index bb05259..0576402 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -50,10 +50,14 @@ static int BtfPoll(BtfPollFd* pfd, unsigned int nfd, int nTimeoutMs)
50// 50//
51// Global state variables 51// Global state variables
52// 52//
53static const char pchMainnetMessageStart[4] = { 0xbf, 0x20, 0x5c, 0xfd };
54static const char pchTestnetMessageStart[4] = { (char)0xce, (char)0xe2, (char)0xc0, (char)0xff };
55static bool fTestNet = false;
56char pchMessageStart[4] = { 0xbf, 0x20, 0x5c, 0xfd };
53bool fClient = false; 57bool fClient = false;
54uint64 nLocalServices = (fClient ? 0 : NODE_NETWORK); 58uint64 nLocalServices = (fClient ? 0 : NODE_NETWORK);
55CAddress addrLocalHost(0, DEFAULT_PORT, nLocalServices); 59CAddress addrLocalHost(0, GetDefaultPort(), nLocalServices);
56unsigned short nListenPort = DEFAULT_PORT; // local P2P port (tunable via /port) 60unsigned short nListenPort = GetDefaultPort(); // local P2P port (tunable via /port)
57CNode nodeLocalHost(INVALID_SOCKET, CAddress("127.0.0.1", nLocalServices)); 61CNode nodeLocalHost(INVALID_SOCKET, CAddress("127.0.0.1", nLocalServices));
58CNode* pnodeLocalHost = &nodeLocalHost; 62CNode* pnodeLocalHost = &nodeLocalHost;
59bool fShutdown = false; 63bool fShutdown = false;
@@ -90,6 +94,29 @@ static int64 nBtfHandshakeSilent = 0;
90static string strBtfLastDialFail; 94static string strBtfLastDialFail;
91static string strBtfLastHandshakeTimeout; 95static string strBtfLastHandshakeTimeout;
92 96
97bool IsTestNet()
98{
99 return fTestNet;
100}
101
102unsigned short GetDefaultPort()
103{
104 return htons(fTestNet ? TESTNET_PORT : MAINNET_PORT);
105}
106
107void SelectNetworkParams(bool fTestNetIn)
108{
109 if (fTestNet == fTestNetIn)
110 return;
111
112 fTestNet = fTestNetIn;
113 memcpy(pchMessageStart,
114 fTestNet ? pchTestnetMessageStart : pchMainnetMessageStart,
115 sizeof(pchMessageStart));
116 nListenPort = GetDefaultPort();
117 addrLocalHost.port = nListenPort;
118}
119
93void BtfChurnNoteResolveAttempt() 120void BtfChurnNoteResolveAttempt()
94{ 121{
95 CRITICAL_BLOCK(cs_btfChurn) 122 CRITICAL_BLOCK(cs_btfChurn)
@@ -288,6 +315,7 @@ string GetDiagnosticsText()
288 int nMedian = GetPeerMedianHeight(); 315 int nMedian = GetPeerMedianHeight();
289 316
290 str += "Bitflash node diagnostics\n"; 317 str += "Bitflash node diagnostics\n";
318 str += strprintf(" network %s\n", IsTestNet() ? "testnet" : "mainnet");
291 str += strprintf(" uptime %s\n", 319 str += strprintf(" uptime %s\n",
292 FormatAge(nNodeStartTime ? nNow - nNodeStartTime : -1).c_str()); 320 FormatAge(nNodeStartTime ? nNow - nNodeStartTime : -1).c_str());
293 if (nMedian < 0) 321 if (nMedian < 0)
@@ -338,6 +366,7 @@ string GetDiagnosticsText()
338 nMining, 366 nMining,
339 (RandomXFastReady() ? 2080 : 256) + 2 * nMining).c_str() 367 (RandomXFastReady() ? 2080 : 256) + 2 * nMining).c_str()
340 : ", not mining"); 368 : ", not mining");
369 str += strprintf(" large pages %s\n", RandomXLargePagesStatus());
341 370
342 str += SockAccountingText(); 371 str += SockAccountingText();
343 372
diff --git a/src/net.h b/src/net.h
index 752b1fe..0988542 100644
--- a/src/net.h
+++ b/src/net.h
@@ -12,7 +12,13 @@ class CBlockIndex;
12 12
13 13
14 14
15static const unsigned short DEFAULT_PORT = htons(8433); 15// Held in host byte order, unlike the DEFAULT_PORT constant these replace,
16// which was htons(8433). Nothing is lost: net.cpp's GetDefaultPort() applies
17// htons() itself and is what every caller now uses, so the value that reaches
18// a sockaddr is byte-for-byte what it was. Mainnet stays on 8433.
19static const unsigned short MAINNET_PORT = 8433;
20static const unsigned short TESTNET_PORT = 18433;
21static const unsigned int PUBLISH_HOPS = 5;
16enum 22enum
17{ 23{
18 NODE_NETWORK = (1 << 0), 24 NODE_NETWORK = (1 << 0),
@@ -30,6 +36,9 @@ enum
30bool IsBlockFetchNearTip(); 36bool IsBlockFetchNearTip();
31 37
32bool GetMyExternalIP(unsigned int& ipRet); 38bool GetMyExternalIP(unsigned int& ipRet);
39bool IsTestNet();
40unsigned short GetDefaultPort();
41void SelectNetworkParams(bool fTestNetIn);
33CNode* ConnectNodeBtf(const string& strBtfAddr); 42CNode* ConnectNodeBtf(const string& strBtfAddr);
34CNode* ConnectNodeBtfResolved(const string& strBtfAddr, const string& strMeeting, 43CNode* ConnectNodeBtfResolved(const string& strBtfAddr, const string& strMeeting,
35 const string& strOnion, const unsigned char enc_pub[32], 44 const string& strOnion, const unsigned char enc_pub[32],
@@ -266,15 +275,17 @@ void CheckForShutdown(int n);
266// The message start string is designed to be unlikely to occur in normal data. 275// The message start string is designed to be unlikely to occur in normal data.
267// The characters are rarely used upper ascii, not valid as UTF-8, and produce 276// The characters are rarely used upper ascii, not valid as UTF-8, and produce
268// a large 4-byte int at any alignment. 277// a large 4-byte int at any alignment.
269// Magic bytes unique to the Bitflash network (0xbf = "BF"). Second byte bumped 278// Magic bytes unique to the selected Bitflash network. Mainnet uses 0xbf =
270// to 0x20 for the 2026 stable relaunch so the old test chain can't interfere. 279// "BF"; testnet swaps the whole sequence so a test node cannot handshake with
271static const char pchMessageStart[4] = { 0xbf, 0x20, 0x5c, 0xfd }; 280// mainnet before consensus-level testnet activation exists.
281extern char pchMessageStart[4];
272 282
273class CMessageHeader 283class CMessageHeader
274{ 284{
275public: 285public:
276 enum { COMMAND_SIZE=12 }; 286 enum { COMMAND_SIZE=12 };
277 char pchMessageStart[sizeof(::pchMessageStart)]; 287 enum { MESSAGE_START_SIZE=4 };
288 char pchMessageStart[MESSAGE_START_SIZE];
278 char pchCommand[COMMAND_SIZE]; 289 char pchCommand[COMMAND_SIZE];
279 unsigned int nMessageSize; 290 unsigned int nMessageSize;
280 291
@@ -365,7 +376,7 @@ public:
365 nServices = 0; 376 nServices = 0;
366 memcpy(pchReserved, pchIPv4, sizeof(pchReserved)); 377 memcpy(pchReserved, pchIPv4, sizeof(pchReserved));
367 ip = 0; 378 ip = 0;
368 port = DEFAULT_PORT; 379 port = GetDefaultPort();
369 nTime = GetAdjustedTime(); 380 nTime = GetAdjustedTime();
370 nLastFailed = 0; 381 nLastFailed = 0;
371 } 382 }
@@ -395,7 +406,7 @@ public:
395 nServices = nServicesIn; 406 nServices = nServicesIn;
396 memcpy(pchReserved, pchIPv4, sizeof(pchReserved)); 407 memcpy(pchReserved, pchIPv4, sizeof(pchReserved));
397 ip = 0; 408 ip = 0;
398 port = DEFAULT_PORT; 409 port = GetDefaultPort();
399 nTime = GetAdjustedTime(); 410 nTime = GetAdjustedTime();
400 nLastFailed = 0; 411 nLastFailed = 0;
401 412
diff --git a/src/randomx_pow.cpp b/src/randomx_pow.cpp
index 9c59364..a509543 100644
--- a/src/randomx_pow.cpp
+++ b/src/randomx_pow.cpp
@@ -24,6 +24,50 @@ static std::mutex g_csInit;
24static bool g_fInit = false; 24static bool g_fInit = false;
25static bool g_fFast = false; 25static bool g_fFast = false;
26 26
27// RandomX reads a 256 MB cache, a 2 GB dataset and a 2 MB scratchpad per VM
28// at random, so most reads miss the TLB on 4 KB pages. 2 MB pages recover
29// roughly a tenth of the hash rate.
30//
31// The pool must be reserved first (vm.nr_hugepages), and RandomX returns NULL
32// instead of falling back, so each allocation below retries without the flag.
33bool fRandomXLargePages = true; // cleared by -nolargepages
34static bool g_fLargeCache = false;
35static bool g_fLargeDataset = false;
36static bool g_fLargeAny = false;
37
38// How to reserve the large-page pool, for the hint printed when a large-page
39// allocation falls back to normal pages. The mechanism is OS-specific.
40static const char* LargePagesHint()
41{
42#ifdef _WIN32
43 return "enable the \"Lock pages in memory\" privilege and run elevated";
44#else
45 return "reserve them with: sysctl vm.nr_hugepages=1280";
46#endif
47}
48
49static randomx_flags WithLargePages(randomx_flags f)
50{
51 return (randomx_flags)(f | RANDOMX_FLAG_LARGE_PAGES);
52}
53
54// Ask for a cache in large pages, then plain pages. Sets fLargeOut to say
55// which one answered.
56static randomx_cache* AllocCache(randomx_flags flags, bool& fLargeOut)
57{
58 fLargeOut = false;
59 if (fRandomXLargePages)
60 {
61 randomx_cache* c = randomx_alloc_cache(WithLargePages(flags));
62 if (c)
63 {
64 fLargeOut = true;
65 return c;
66 }
67 }
68 return randomx_alloc_cache(flags);
69}
70
27 71
28bool RandomXInit() 72bool RandomXInit()
29{ 73{
@@ -37,28 +81,56 @@ bool RandomXInit()
37 // Detect the best flags for this CPU (JIT, hardware AES, Argon2). 81 // Detect the best flags for this CPU (JIT, hardware AES, Argon2).
38 g_flags = randomx_get_flags(); 82 g_flags = randomx_get_flags();
39 83
40 g_cache = randomx_alloc_cache(g_flags); 84 g_cache = AllocCache(g_flags, g_fLargeCache);
41 if (!g_cache) 85 if (!g_cache)
42 { 86 {
43 // Try without JIT as a fallback 87 // Try without JIT as a fallback
44 g_flags = RANDOMX_FLAG_DEFAULT; 88 g_flags = RANDOMX_FLAG_DEFAULT;
45 g_cache = randomx_alloc_cache(g_flags); 89 g_cache = AllocCache(g_flags, g_fLargeCache);
46 if (!g_cache) 90 if (!g_cache)
47 return error("RandomX: failed to allocate cache (256 MB)"); 91 return error("RandomX: failed to allocate cache (256 MB)");
48 } 92 }
49 randomx_init_cache(g_cache, RANDOMX_KEY, strlen(RANDOMX_KEY)); 93 randomx_init_cache(g_cache, RANDOMX_KEY, strlen(RANDOMX_KEY));
50 94
51 // Verification VM in light mode (cache only) 95 // Verification VM in light mode (cache only). The scratchpad is only 2 MB
52 g_vmVerify = randomx_create_vm(g_flags, g_cache, NULL); 96 // and this VM is created once, but it hashes every block the node ever
97 // validates, so it gets the same treatment.
98 bool fLargeVerify = false;
99 if (fRandomXLargePages)
100 {
101 g_vmVerify = randomx_create_vm(WithLargePages(g_flags), g_cache, NULL);
102 fLargeVerify = (g_vmVerify != NULL);
103 }
104 if (!g_vmVerify)
105 g_vmVerify = randomx_create_vm(g_flags, g_cache, NULL);
53 if (!g_vmVerify) 106 if (!g_vmVerify)
54 return error("RandomX: failed to create verification VM"); 107 return error("RandomX: failed to create verification VM");
55 108
109 g_fLargeAny = g_fLargeCache || fLargeVerify;
56 g_fInit = true; 110 g_fInit = true;
57 printf("RandomX: initialized (cache 256 MB, flags=%d)\n", (int)g_flags); 111 printf("RandomX: initialized (cache 256 MB, flags=%d, large pages: %s)\n",
112 (int)g_flags, RandomXLargePagesStatus());
113 if (fRandomXLargePages && !g_fLargeCache)
114 printf("RandomX: large pages unavailable for the cache; %s "
115 "for about 10%% more hash rate\n", LargePagesHint());
58 return true; 116 return true;
59} 117}
60 118
61 119
120const char* RandomXLargePagesStatus()
121{
122 if (!fRandomXLargePages)
123 return "off (-nolargepages)";
124 if (g_fLargeCache && g_fLargeDataset)
125 return "cache + dataset";
126 if (g_fLargeCache)
127 return "cache only";
128 if (g_fLargeDataset)
129 return "dataset only";
130 return g_fLargeAny ? "scratchpad only" : "unavailable";
131}
132
133
62// One dataset, once, no matter how many miners ask for it. 134// One dataset, once, no matter how many miners ask for it.
63// 135//
64// Every miner thread calls this at startup. The g_fFast guard below is only 136// Every miner thread calls this at startup. The g_fFast guard below is only
@@ -88,8 +160,18 @@ bool RandomXInitDataset(int nThreads)
88 if (g_fFast) 160 if (g_fFast)
89 return true; 161 return true;
90 162
91 randomx_flags fastFlags = (randomx_flags)(g_flags | RANDOMX_FLAG_FULL_MEM); 163 // Only RANDOMX_FLAG_LARGE_PAGES means anything to randomx_alloc_dataset;
92 g_dataset = randomx_alloc_dataset(fastFlags); 164 // the rest of g_flags belongs to the VMs that read it.
165 if (fRandomXLargePages)
166 {
167 g_dataset = randomx_alloc_dataset(WithLargePages(RANDOMX_FLAG_DEFAULT));
168 g_fLargeDataset = (g_dataset != NULL);
169 if (!g_dataset)
170 printf("RandomX: large pages unavailable for the 2 GB dataset; %s "
171 "for about 10%% more hash rate\n", LargePagesHint());
172 }
173 if (!g_dataset)
174 g_dataset = randomx_alloc_dataset(RANDOMX_FLAG_DEFAULT);
93 if (!g_dataset) 175 if (!g_dataset)
94 { 176 {
95 printf("RandomX: not enough memory for the 2 GB dataset, staying in light mode\n"); 177 printf("RandomX: not enough memory for the 2 GB dataset, staying in light mode\n");
@@ -158,13 +240,26 @@ void* RandomXCreateMinerVM()
158{ 240{
159 if (!g_fInit && !RandomXInit()) 241 if (!g_fInit && !RandomXInit())
160 return NULL; 242 return NULL;
243
244 randomx_flags flags = g_flags;
245 randomx_cache* cache = g_cache;
246 randomx_dataset* dataset = NULL;
161 if (g_fFast && g_dataset) 247 if (g_fFast && g_dataset)
162 { 248 {
163 randomx_flags fastFlags = (randomx_flags)(g_flags | RANDOMX_FLAG_FULL_MEM); 249 flags = (randomx_flags)(g_flags | RANDOMX_FLAG_FULL_MEM);
164 return randomx_create_vm(fastFlags, NULL, g_dataset); 250 cache = NULL;
251 dataset = g_dataset;
252 }
253
254 // Each thread gets its own 2 MB scratchpad, so the large-page pool can run
255 // dry partway through a fleet of miners. Threads that miss out still run.
256 if (fRandomXLargePages)
257 {
258 randomx_vm* vm = randomx_create_vm(WithLargePages(flags), cache, dataset);
259 if (vm)
260 return vm;
165 } 261 }
166 // Light mode: own VM sharing the cache 262 return randomx_create_vm(flags, cache, dataset);
167 return randomx_create_vm(g_flags, g_cache, NULL);
168} 263}
169 264
170 265
diff --git a/src/randomx_pow.h b/src/randomx_pow.h
index 0027571..355b708 100644
--- a/src/randomx_pow.h
+++ b/src/randomx_pow.h
@@ -41,4 +41,11 @@ uint256 RandomXHashWithVM(void* vm, const void* pHeader, size_t nSize);
41// True when the 2 GB dataset is ready (fast mode active). 41// True when the 2 GB dataset is ready (fast mode active).
42bool RandomXFastReady(); 42bool RandomXFastReady();
43 43
44// Ask for large (2 MB) pages for the cache, dataset and scratchpads. Set false
45// by -nolargepages. Must be set before the first RandomXInit().
46extern bool fRandomXLargePages;
47
48// Which allocations actually got large pages, for the status display.
49const char* RandomXLargePagesStatus();
50
44#endif 51#endif
diff --git a/src/readme.txt b/src/readme.txt
index 7490a9a..455884c 100644
--- a/src/readme.txt
+++ b/src/readme.txt
@@ -41,7 +41,8 @@ The resulting executable is src/bitflash.exe.
41 41
42Command-line options (in addition to the original ones) 42Command-line options (in addition to the original ones)
43------------------------------------------------------- 43-------------------------------------------------------
44 /port=N listen on P2P port N (default 8433) 44 /testnet use isolated test network genesis, datadir, port 18433, and message magic
45 /port=N listen on P2P port N (default 8433; testnet default 18433)
45 /socks=HOST:PORT 46 /socks=HOST:PORT
46 route outbound Nostr, .btf rendezvous, and direct onion peer 47 route outbound Nostr, .btf rendezvous, and direct onion peer
47 dials through a SOCKS5 proxy, e.g. /socks=127.0.0.1:9050 for local Tor 48 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 65f215b..357d532 100644
--- a/src/selftest.cpp
+++ b/src/selftest.cpp
@@ -3118,6 +3118,87 @@ static int RunNetMessageSelfTest()
3118 return nFail == 0 ? 0 : 1; 3118 return nFail == 0 ? 0 : 1;
3119} 3119}
3120 3120
3121static int RunNetworkParamsSelfTest()
3122{
3123 fflush(stdout);
3124 printf("network-params self-test\n");
3125
3126 int nFail = 0;
3127 std::string strOldDataDir = strSetDataDir;
3128 try
3129 {
3130 SelectChainParams(false);
3131 nFail += Check(!IsTestNet(), "starts on mainnet after explicit selection") ? 0 : 1;
3132 nFail += Check(ntohs(GetDefaultPort()) == MAINNET_PORT,
3133 "mainnet default P2P port is 8433") ? 0 : 1;
3134 CAddress mainAddr("127.0.0.1");
3135 nFail += Check(mainAddr.port == htons(MAINNET_PORT),
3136 "mainnet address parsing uses the mainnet default port") ? 0 : 1;
3137 CMessageHeader mainHeader("ping", 0);
3138 char mainMagic[4];
3139 memcpy(mainMagic, mainHeader.pchMessageStart, sizeof(mainMagic));
3140 uint256 mainGenesis = hashGenesisBlock;
3141 uint256 mainMerkle = hashGenesisMerkleRoot;
3142 unsigned int mainNonce = GENESIS_NONCE;
3143
3144 SelectChainParams(true);
3145 nFail += Check(IsTestNet(), "testnet selection is sticky") ? 0 : 1;
3146 nFail += Check(ntohs(GetDefaultPort()) == TESTNET_PORT,
3147 "testnet default P2P port is 18433") ? 0 : 1;
3148 CAddress testAddr("127.0.0.1");
3149 nFail += Check(testAddr.port == htons(TESTNET_PORT),
3150 "testnet address parsing uses the testnet default port") ? 0 : 1;
3151 CMessageHeader testHeader("ping", 0);
3152 nFail += Check(memcmp(mainMagic, testHeader.pchMessageStart, sizeof(mainMagic)) != 0,
3153 "testnet message magic differs from mainnet") ? 0 : 1;
3154 nFail += Check(hashGenesisBlock != mainGenesis &&
3155 hashGenesisMerkleRoot != mainMerkle &&
3156 GENESIS_NONCE != mainNonce,
3157 "testnet genesis constants differ from mainnet") ? 0 : 1;
3158 nFail += Check(hashGenesisBlock == uint256("0xa9d11c6d697bcb7aea0653bc088b12dcf9f44e732df205162cf085fd9d9963eb") &&
3159 hashGenesisMerkleRoot == uint256("0x510b293105e3ff85b8d4c3920e357f85d1a86bf199eb4e7d11f04f380030ec5c") &&
3160 GENESIS_NONCE == 3199,
3161 "testnet genesis constants are pinned") ? 0 : 1;
3162
3163 std::string tmp;
3164 if (!MakeTempDir(tmp))
3165 {
3166 nFail += Check(false, "creates a temporary data directory") ? 0 : 1;
3167 }
3168 else
3169 {
3170 strSetDataDir = tmp;
3171 SelectChainParams(false);
3172 std::string mainDir = GetAppDir();
3173 SelectChainParams(true);
3174 std::string testDir = GetAppDir();
3175 nFail += Check(mainDir == tmp,
3176 "mainnet uses the selected data directory") ? 0 : 1;
3177 nFail += Check(testDir == tmp + "/testnet",
3178 "testnet stores data below a testnet subdirectory") ? 0 : 1;
3179 RemoveTree(tmp);
3180 }
3181 }
3182 catch (const std::exception& e)
3183 {
3184 printf(" FAIL exception: %s\n", e.what());
3185 nFail++;
3186 }
3187 catch (...)
3188 {
3189 printf(" FAIL unknown exception\n");
3190 nFail++;
3191 }
3192
3193 strSetDataDir = strOldDataDir;
3194 SelectChainParams(false);
3195
3196 printf("%s (%d failure%s)\n", nFail == 0 ? "ALL TESTS PASSED" : "TESTS FAILED",
3197 nFail, nFail == 1 ? "" : "s");
3198 fflush(stdout);
3199 return nFail == 0 ? 0 : 1;
3200}
3201
3121static CBlock MakeSizedConsensusBlock(size_t nScriptBytes) 3202static CBlock MakeSizedConsensusBlock(size_t nScriptBytes)
3122{ 3203{
3123 CTransaction tx; 3204 CTransaction tx;
@@ -3716,6 +3797,8 @@ int RunSelfTest(const std::string& name)
3716 return RunWalletPortabilitySelfTest(); 3797 return RunWalletPortabilitySelfTest();
3717 if (name == "net-message") 3798 if (name == "net-message")
3718 return RunNetMessageSelfTest(); 3799 return RunNetMessageSelfTest();
3800 if (name == "network-params")
3801 return RunNetworkParamsSelfTest();
3719 if (name == "consensus-limits") 3802 if (name == "consensus-limits")
3720 return RunConsensusLimitsSelfTest(); 3803 return RunConsensusLimitsSelfTest();
3721 if (name == "pool-stratum") 3804 if (name == "pool-stratum")
@@ -3730,6 +3813,6 @@ int RunSelfTest(const std::string& name)
3730 return RunManagedTorSelfTest(); 3813 return RunManagedTorSelfTest();
3731 3814
3732 printf("Unknown self-test '%s'\n", name.c_str()); 3815 printf("Unknown self-test '%s'\n", name.c_str());
3733 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, payout-ledger, parse-money, socks5-proxy, managed-tor\n"); 3816 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, payout-ledger, parse-money, socks5-proxy, managed-tor\n");
3734 return 1; 3817 return 1;
3735} 3818}