diff options
| author | bitflash-maintainer <noreply@bitflash.invalid> | 2026-09-05 05:08:30 +0000 |
|---|---|---|
| committer | bitflash-maintainer <noreply@bitflash.invalid> | 2026-09-05 05:08:30 +0000 |
| commit | 66e0b9158d050c46b1ff2509faeee3f4573cac1c (patch) | |
| tree | 7e9132aa871fdc01cb95268d2f5ea3bb11853ddf | |
| parent | 36201d2795b7b49beb1a837b0252134096beb64d (diff) | |
| parent | 17741d9cf4c762ea03dc83e8aae6a176e5f338b0 (diff) | |
Merge upstream/main into the fork
Brings the nine commits that are actually the latest on our lineage:
isolated testnet parameters and genesis, optional 2 MB large pages for
RandomX, and five README changes. This is what "up to date" means here --
not the 1.2.2x tags, which carry higher version numbers but were cut
2026-07-30, three weeks before the line we ship, and which upstream/main
does not contain.
Three conflicts, all resolved toward upstream's structure while keeping
what this fork knows:
main.cpp -- upstream moved genesis construction into BuildGenesisBlock() so
mainnet and the new testnet can share it. Verified byte-for-byte before
taking it: MAINNET_GENESIS_TIMESTAMP, _TIME (1753315200), _NONCE (3141),
_HASH, _MERKLE, the scriptPubKey, nBits and nVersion all equal what this
tree built inline. The mainnet genesis hash is unchanged and the asserts
still guard it. Kept our warning about never editing those inputs, and kept
our note that 1753315200 is 2025-07-24, not 2026 -- upstream's comment on
that constant states the wrong year.
net.h -- DEFAULT_PORT = htons(8433) becomes MAINNET_PORT/TESTNET_PORT in
host order. Nothing is lost: net.cpp's GetDefaultPort() applies htons()
itself and is what every caller uses, so the value reaching a sockaddr is
unchanged and mainnet stays on 8433.
selftest.cpp -- the known-tests string had diverged in both directions.
Took the union: upstream's network-params alongside our payout-ledger,
rather than dropping either.
Gates: 15/15 selftests (our 14 plus upstream's network-params), consensus
constants unchanged against e4f898c except the pre-existing
MAX_BLOCK_SIZE_GEN, mainnet genesis intact, compactblock's 17 checks still
pass, money-log check ok, GNU_STACK RW.
| -rw-r--r-- | README.md | 50 | ||||
| -rw-r--r-- | src/Makefile | 1 | ||||
| -rw-r--r-- | src/main.cpp | 163 | ||||
| -rw-r--r-- | src/main.h | 2 | ||||
| -rw-r--r-- | src/main_gui.cpp | 15 | ||||
| -rw-r--r-- | src/makefile.mingw | 1 | ||||
| -rw-r--r-- | src/net.cpp | 33 | ||||
| -rw-r--r-- | src/net.h | 25 | ||||
| -rw-r--r-- | src/randomx_pow.cpp | 117 | ||||
| -rw-r--r-- | src/randomx_pow.h | 7 | ||||
| -rw-r--r-- | src/readme.txt | 3 | ||||
| -rw-r--r-- | src/selftest.cpp | 85 |
12 files changed, 424 insertions, 78 deletions
| @@ -8,9 +8,11 @@ CPU-only cryptocurrency. A revival of Bitcoin 0.1.0 with RandomX proof of work a | |||
| 8 | 8 | ||
| 9 | -2ea44f?style=for-the-badge) | 9 | -2ea44f?style=for-the-badge) |
| 10 |  | 10 |  |
| 11 |  | ||
| 11 |  | 12 |  |
| 13 | [](https://discord.gg/n7B2Eamn3) | ||
| 12 | 14 | ||
| 13 | **[Download](../../releases/latest)** | 15 | **[Download](https://releases.bitflash.network/)** · **[Discord](https://discord.gg/n7B2Eamn3)** |
| 14 | 16 | ||
| 15 | </div> | 17 | </div> |
| 16 | 18 | ||
| @@ -46,14 +48,14 @@ For a plain SOCKS5 proxy without Tor-specific defaults, use | |||
| 46 | 48 | ||
| 47 | ## Quick start | 49 | ## Quick start |
| 48 | 50 | ||
| 49 | Download the [latest release](../../releases/latest) and run. No install, no | 51 | Download the [latest release](https://releases.bitflash.network/) and run. No |
| 50 | configuration — it connects automatically and starts syncing. | 52 | install, no configuration — it connects automatically and starts syncing. |
| 51 | 53 | ||
| 52 | **Linux:** make the `.AppImage` executable and run it. | 54 | **Linux:** make the `.AppImage` executable and run it. |
| 53 | 55 | ||
| 54 | **Windows:** extract the `-windows.zip` and run `Bitflash.exe`. | 56 | **Windows:** extract the `-windows-with-tor.zip` and run `Bitflash.exe`. Tor is |
| 55 | If you want managed Tor without a separate Tor install, use the | 57 | bundled and a managed Tor instance starts automatically, so onion transport |
| 56 | `-windows-with-tor.zip` package and start Bitflash with `-managedtor`. | 58 | works with no separate install. Pass `-nomanagedtor` to turn it off. |
| 57 | 59 | ||
| 58 | Every release ships a `SHA256SUMS` covering the assets. Verifying takes a second | 60 | Every release ships a `SHA256SUMS` covering the assets. Verifying takes a second |
| 59 | and is worth doing: | 61 | and is worth doing: |
| @@ -83,7 +85,7 @@ Public mirrors should make the latest checksum files available both under the | |||
| 83 | versioned release directory and at the release root: | 85 | versioned release directory and at the release root: |
| 84 | 86 | ||
| 85 | ```text | 87 | ```text |
| 86 | https://releases.bitflash.network/v1.2.19/SHA256SUMS | 88 | https://releases.bitflash.network/v1.2.20/SHA256SUMS |
| 87 | https://releases.bitflash.network/SHA256SUMS # alias to latest | 89 | https://releases.bitflash.network/SHA256SUMS # alias to latest |
| 88 | ``` | 90 | ``` |
| 89 | 91 | ||
| @@ -166,6 +168,14 @@ words also cover the address the window shows you. **Both still matter** — the | |||
| 166 | phrase and the file back up different things, and the difference is where people | 168 | phrase and the file back up different things, and the difference is where people |
| 167 | lose money. | 169 | lose money. |
| 168 | 170 | ||
| 171 | Since 1.2.20 the file is a single self-contained `wallet.sqlite` by default. A | ||
| 172 | fresh data directory starts on it; an existing Berkeley DB `wallet.dat` keeps | ||
| 173 | working and the desktop app converts it in one click on first run, never | ||
| 174 | touching the original, which stays as a fallback. Choose a backend explicitly | ||
| 175 | with `-walletbackend=sqlite` or `-walletbackend=bdb`, recorded in a | ||
| 176 | `wallet-backend` marker in the data directory. See | ||
| 177 | [wallet storage](docs/storage.md). | ||
| 178 | |||
| 169 | The derivation is written down in [docs/derivation.md](docs/derivation.md), with | 179 | The derivation is written down in [docs/derivation.md](docs/derivation.md), with |
| 170 | test vectors and a script that reproduces them from scratch. It is there so the | 180 | test vectors and a script that reproduces them from scratch. It is there so the |
| 171 | twelve words keep working even if this software does not: paths, address format | 181 | twelve words keep working even if this software does not: paths, address format |
| @@ -179,8 +189,11 @@ bitflash -newphrase # create it, show it once, exit | |||
| 179 | bitflash -restorephrase="twelve words here" # rebuild a wallet from it | 189 | bitflash -restorephrase="twelve words here" # rebuild a wallet from it |
| 180 | ``` | 190 | ``` |
| 181 | 191 | ||
| 182 | `-newphrase` installs a BIP32 seed, derives the wallet's key pool and default | 192 | `-newphrase` installs a BIP39/BIP32 seed and derives the wallet as a BIP44 tree |
| 183 | receiving address from it, and prints the words once. It refuses if a phrase | 193 | along `m/44'/4346950'/0'/…` — coin type **4346950** is `0x425446`, the ASCII |
| 194 | bytes `BTF`, registered in [SLIP-0044](https://github.com/satoshilabs/slips/blob/master/slip-0044.md). | ||
| 195 | It builds the key pool and default receiving address from the seed, and prints | ||
| 196 | the words once. It refuses if a phrase | ||
| 184 | already exists: replacing one silently would strand every coin on addresses the | 197 | already exists: replacing one silently would strand every coin on addresses the |
| 185 | written-down words no longer describe. | 198 | written-down words no longer describe. |
| 186 | 199 | ||
| @@ -207,6 +220,10 @@ matter. | |||
| 207 | 220 | ||
| 208 | ### The file | 221 | ### The file |
| 209 | 222 | ||
| 223 | Under the SQLite default the wallet is one self-contained `wallet.sqlite`, and | ||
| 224 | `-backupwallet` writes a matching `.sqlite` copy that opens on its own — no | ||
| 225 | sidecar directory. The Berkeley DB notes below apply to a legacy `wallet.dat`. | ||
| 226 | |||
| 210 | **Copying `wallet.dat` on its own is not a backup.** Berkeley DB ties the file | 227 | **Copying `wallet.dat` on its own is not a backup.** Berkeley DB ties the file |
| 211 | to the environment in the `database/` subdirectory beside it, so a lone | 228 | to the environment in the `database/` subdirectory beside it, so a lone |
| 212 | `wallet.dat` will not open elsewhere — the keys are all still in there, and the | 229 | `wallet.dat` will not open elsewhere — the keys are all still in there, and the |
| @@ -244,7 +261,7 @@ as a verdict to somebody who has just lost a wallet. | |||
| 244 | 261 | ||
| 245 | ### When wallet.dat itself will not open | 262 | ### When wallet.dat itself will not open |
| 246 | 263 | ||
| 247 | Everything above still runs through Berkeley DB. If the database is the thing | 264 | A legacy `wallet.dat` runs on Berkeley DB. If that database is the thing |
| 248 | that is broken — a build that will not read it, a file truncated by a bad copy, | 265 | that is broken — a build that will not read it, a file truncated by a bad copy, |
| 249 | an environment beyond recovery — the keys are usually still fine, and you can | 266 | an environment beyond recovery — the keys are usually still fine, and you can |
| 250 | take them out as text: | 267 | take them out as text: |
| @@ -348,10 +365,12 @@ Other options worth knowing: | |||
| 348 | 365 | ||
| 349 | ```bash | 366 | ```bash |
| 350 | -datadir=PATH # wallet and chain data elsewhere | 367 | -datadir=PATH # wallet and chain data elsewhere |
| 351 | -port=N # P2P listen port, default 8433 | 368 | -testnet # isolated test network genesis, datadir, port 18433, and magic |
| 369 | -port=N # P2P listen port, default 8433; testnet default 18433 | ||
| 352 | -socks=HOST:PORT # SOCKS5 for outbound Nostr, .btf relay, and onion peer dials | 370 | -socks=HOST:PORT # SOCKS5 for outbound Nostr, .btf relay, and onion peer dials |
| 353 | -tor[=HOST:PORT] # Tor mode; default local Tor SOCKS5 proxy is 127.0.0.1:9050 | 371 | -tor[=HOST:PORT] # Tor mode; default local Tor SOCKS5 proxy is 127.0.0.1:9050 |
| 354 | -managedtor[=PATH] # start Tor, create a hidden service, advertise its onion | 372 | -managedtor[=PATH] # start Tor, create a hidden service, advertise its onion |
| 373 | -nomanagedtor # disable the automatic bundled-Tor startup | ||
| 355 | -onionservice=HOST.onion:PORT # advertise this node's Tor hidden service | 374 | -onionservice=HOST.onion:PORT # advertise this node's Tor hidden service |
| 356 | -debug # verbose log; without it debug.log is nearly silent | 375 | -debug # verbose log; without it debug.log is nearly silent |
| 357 | -help # full list | 376 | -help # full list |
| @@ -372,6 +391,10 @@ proxy. IPv6 proxy endpoints use brackets, for example `-socks=[::1]:9050`. | |||
| 372 | and signed direct `.onion` peer dials through Tor, and keeps external-IP probes | 391 | and signed direct `.onion` peer dials through Tor, and keeps external-IP probes |
| 373 | disabled. Use `-tor=HOST:PORT` when Tor listens somewhere else. | 392 | disabled. Use `-tor=HOST:PORT` when Tor listens somewhere else. |
| 374 | 393 | ||
| 394 | The desktop app starts a managed Tor automatically when no other Tor option is | ||
| 395 | set, so onion transport works out of the box; `-nomanagedtor` turns that off. | ||
| 396 | For a headless node, ask for it explicitly: | ||
| 397 | |||
| 375 | `-managedtor[=PATH]` starts a Tor process for this node, writes a local `torrc`, | 398 | `-managedtor[=PATH]` starts a Tor process for this node, writes a local `torrc`, |
| 376 | creates a v3 hidden service for port `8433`, routes outbound discovery through | 399 | creates a v3 hidden service for port `8433`, routes outbound discovery through |
| 377 | that Tor instance, and signs the generated `.onion:8433` endpoint into the | 400 | that Tor instance, and signs the generated `.onion:8433` endpoint into the |
| @@ -496,7 +519,8 @@ windows-tor` to require the extra signature check. | |||
| 496 | | Addressing | `.btf` rendezvous — see the caveats above | | 519 | | Addressing | `.btf` rendezvous — see the caveats above | |
| 497 | | Premine | None | | 520 | | Premine | None | |
| 498 | | Pool server | Built-in — `.btf` rendezvous only | | 521 | | Pool server | Built-in — `.btf` rendezvous only | |
| 499 | | Wallet recovery | Twelve-word phrase (BIP39 + BIP32), plus file backup | | 522 | | Wallet recovery | Twelve-word phrase (BIP39 + BIP32 + BIP44, coin type 4346950), plus file backup | |
| 523 | | Wallet storage | SQLite `wallet.sqlite` by default, or Berkeley DB `wallet.dat` | | ||
| 500 | 524 | ||
| 501 | The halving interval is the number most people get wrong coming from Bitcoin. | 525 | The halving interval is the number most people get wrong coming from Bitcoin. |
| 502 | Same 210,000 blocks, but at two minutes instead of ten, so it arrives in about | 526 | Same 210,000 blocks, but at two minutes instead of ten, so it arrives in about |
| @@ -504,6 +528,6 @@ ten months rather than four years. | |||
| 504 | 528 | ||
| 505 | --- | 529 | --- |
| 506 | 530 | ||
| 507 | Experimental software. Young network. Don't put in more than you are willing to lose. | 531 | Young network. Keep your node current and don't put in more than you are willing to lose. |
| 508 | 532 | ||
| 509 | MIT. Built on Bitcoin 0.1.0 (Satoshi Nakamoto, 2009). | 533 | MIT. Built on Bitcoin 0.1.0 (Satoshi Nakamoto, 2009). |
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; | |||
| 42 | map<COutPoint, CInPoint> mapNextTx; | 42 | map<COutPoint, CInPoint> mapNextTx; |
| 43 | 43 | ||
| 44 | map<uint256, CBlockIndex*> mapBlockIndex; | 44 | map<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. |
| 48 | unsigned int GENESIS_NONCE = 3141; | 48 | static const unsigned int MAINNET_GENESIS_TIME = 1753315200; // 2026-07-24 00:00:00 UTC |
| 49 | uint256 hashGenesisBlock("0x5bd7cb255d814e48cebcdfb72da4dc87b34bd774227f8ceb546c5640f4bdc169"); | 49 | static const unsigned int MAINNET_GENESIS_NONCE = 3141; |
| 50 | uint256 hashGenesisMerkleRoot("0x1a45b4482532abb29b10e234d3f13132230525a339ecea91658ffa675a5b1325"); | 50 | static const char* MAINNET_GENESIS_TIMESTAMP = |
| 51 | "Bitflash 24/Jul/2026 Fair launch: one CPU one vote, no premine"; | ||
| 52 | static const char* MAINNET_GENESIS_HASH = | ||
| 53 | "0x5bd7cb255d814e48cebcdfb72da4dc87b34bd774227f8ceb546c5640f4bdc169"; | ||
| 54 | static const char* MAINNET_GENESIS_MERKLE = | ||
| 55 | "0x1a45b4482532abb29b10e234d3f13132230525a339ecea91658ffa675a5b1325"; | ||
| 56 | |||
| 57 | static const unsigned int TESTNET_GENESIS_TIME = 1787184000; // 2026-08-20 00:00:00 UTC | ||
| 58 | static const unsigned int TESTNET_GENESIS_NONCE = 3199; | ||
| 59 | static const char* TESTNET_GENESIS_TIMESTAMP = | ||
| 60 | "Bitflash testnet 20/Aug/2026: resettable public test chain"; | ||
| 61 | static const char* TESTNET_GENESIS_HASH = | ||
| 62 | "0xa9d11c6d697bcb7aea0653bc088b12dcf9f44e732df205162cf085fd9d9963eb"; | ||
| 63 | static const char* TESTNET_GENESIS_MERKLE = | ||
| 64 | "0x510b293105e3ff85b8d4c3920e357f85d1a86bf199eb4e7d11f04f380030ec5c"; | ||
| 65 | |||
| 66 | unsigned int GENESIS_NONCE = MAINNET_GENESIS_NONCE; | ||
| 67 | uint256 hashGenesisBlock(MAINNET_GENESIS_HASH); | ||
| 68 | uint256 hashGenesisMerkleRoot(MAINNET_GENESIS_MERKLE); | ||
| 51 | CBlockIndex* pindexGenesisBlock = NULL; | 69 | CBlockIndex* pindexGenesisBlock = NULL; |
| 52 | int nBestHeight = -1; | 70 | int nBestHeight = -1; |
| 53 | uint256 hashBestChain = 0; | 71 | uint256 hashBestChain = 0; |
| @@ -254,6 +272,63 @@ static std::atomic<uint64> gParticipantHashRateX1000{0}; | |||
| 254 | static std::mutex gParticipantStatusMutex; | 272 | static std::mutex gParticipantStatusMutex; |
| 255 | static std::string gParticipantStatus; | 273 | static std::string gParticipantStatus; |
| 256 | 274 | ||
| 275 | void 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 | |||
| 299 | void SelectChainParams(bool fTestNetIn) | ||
| 300 | { | ||
| 301 | SelectNetworkParams(fTestNetIn); | ||
| 302 | SelectGenesisParams(fTestNetIn); | ||
| 303 | } | ||
| 304 | |||
| 305 | static 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 | |||
| 257 | void SetParticipantMiningStatus(const std::string& status) | 332 | void 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. |
| @@ -94,6 +94,8 @@ extern map<uint256, CBlockIndex*> mapBlockIndex; | |||
| 94 | extern uint256 hashGenesisBlock; | 94 | extern uint256 hashGenesisBlock; |
| 95 | extern uint256 hashGenesisMerkleRoot; | 95 | extern uint256 hashGenesisMerkleRoot; |
| 96 | extern unsigned int GENESIS_NONCE; | 96 | extern unsigned int GENESIS_NONCE; |
| 97 | void SelectGenesisParams(bool fTestNetIn); | ||
| 98 | void SelectChainParams(bool fTestNetIn); | ||
| 97 | extern CBlockIndex* pindexGenesisBlock; | 99 | extern CBlockIndex* pindexGenesisBlock; |
| 98 | extern int nBestHeight; | 100 | extern int nBestHeight; |
| 99 | extern uint256 hashBestChain; | 101 | extern 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 | // |
| 53 | static const char pchMainnetMessageStart[4] = { 0xbf, 0x20, 0x5c, 0xfd }; | ||
| 54 | static const char pchTestnetMessageStart[4] = { (char)0xce, (char)0xe2, (char)0xc0, (char)0xff }; | ||
| 55 | static bool fTestNet = false; | ||
| 56 | char pchMessageStart[4] = { 0xbf, 0x20, 0x5c, 0xfd }; | ||
| 53 | bool fClient = false; | 57 | bool fClient = false; |
| 54 | uint64 nLocalServices = (fClient ? 0 : NODE_NETWORK); | 58 | uint64 nLocalServices = (fClient ? 0 : NODE_NETWORK); |
| 55 | CAddress addrLocalHost(0, DEFAULT_PORT, nLocalServices); | 59 | CAddress addrLocalHost(0, GetDefaultPort(), nLocalServices); |
| 56 | unsigned short nListenPort = DEFAULT_PORT; // local P2P port (tunable via /port) | 60 | unsigned short nListenPort = GetDefaultPort(); // local P2P port (tunable via /port) |
| 57 | CNode nodeLocalHost(INVALID_SOCKET, CAddress("127.0.0.1", nLocalServices)); | 61 | CNode nodeLocalHost(INVALID_SOCKET, CAddress("127.0.0.1", nLocalServices)); |
| 58 | CNode* pnodeLocalHost = &nodeLocalHost; | 62 | CNode* pnodeLocalHost = &nodeLocalHost; |
| 59 | bool fShutdown = false; | 63 | bool fShutdown = false; |
| @@ -90,6 +94,29 @@ static int64 nBtfHandshakeSilent = 0; | |||
| 90 | static string strBtfLastDialFail; | 94 | static string strBtfLastDialFail; |
| 91 | static string strBtfLastHandshakeTimeout; | 95 | static string strBtfLastHandshakeTimeout; |
| 92 | 96 | ||
| 97 | bool IsTestNet() | ||
| 98 | { | ||
| 99 | return fTestNet; | ||
| 100 | } | ||
| 101 | |||
| 102 | unsigned short GetDefaultPort() | ||
| 103 | { | ||
| 104 | return htons(fTestNet ? TESTNET_PORT : MAINNET_PORT); | ||
| 105 | } | ||
| 106 | |||
| 107 | void 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 | |||
| 93 | void BtfChurnNoteResolveAttempt() | 120 | void 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 | ||
| @@ -12,7 +12,13 @@ class CBlockIndex; | |||
| 12 | 12 | ||
| 13 | 13 | ||
| 14 | 14 | ||
| 15 | static 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. | ||
| 19 | static const unsigned short MAINNET_PORT = 8433; | ||
| 20 | static const unsigned short TESTNET_PORT = 18433; | ||
| 21 | static const unsigned int PUBLISH_HOPS = 5; | ||
| 16 | enum | 22 | enum |
| 17 | { | 23 | { |
| 18 | NODE_NETWORK = (1 << 0), | 24 | NODE_NETWORK = (1 << 0), |
| @@ -30,6 +36,9 @@ enum | |||
| 30 | bool IsBlockFetchNearTip(); | 36 | bool IsBlockFetchNearTip(); |
| 31 | 37 | ||
| 32 | bool GetMyExternalIP(unsigned int& ipRet); | 38 | bool GetMyExternalIP(unsigned int& ipRet); |
| 39 | bool IsTestNet(); | ||
| 40 | unsigned short GetDefaultPort(); | ||
| 41 | void SelectNetworkParams(bool fTestNetIn); | ||
| 33 | CNode* ConnectNodeBtf(const string& strBtfAddr); | 42 | CNode* ConnectNodeBtf(const string& strBtfAddr); |
| 34 | CNode* ConnectNodeBtfResolved(const string& strBtfAddr, const string& strMeeting, | 43 | CNode* 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 |
| 271 | static const char pchMessageStart[4] = { 0xbf, 0x20, 0x5c, 0xfd }; | 280 | // mainnet before consensus-level testnet activation exists. |
| 281 | extern char pchMessageStart[4]; | ||
| 272 | 282 | ||
| 273 | class CMessageHeader | 283 | class CMessageHeader |
| 274 | { | 284 | { |
| 275 | public: | 285 | public: |
| 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; | |||
| 24 | static bool g_fInit = false; | 24 | static bool g_fInit = false; |
| 25 | static bool g_fFast = false; | 25 | static 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. | ||
| 33 | bool fRandomXLargePages = true; // cleared by -nolargepages | ||
| 34 | static bool g_fLargeCache = false; | ||
| 35 | static bool g_fLargeDataset = false; | ||
| 36 | static 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. | ||
| 40 | static 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 | |||
| 49 | static 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. | ||
| 56 | static 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 | ||
| 28 | bool RandomXInit() | 72 | bool 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 | ||
| 120 | const 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). |
| 42 | bool RandomXFastReady(); | 42 | bool 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(). | ||
| 46 | extern bool fRandomXLargePages; | ||
| 47 | |||
| 48 | // Which allocations actually got large pages, for the status display. | ||
| 49 | const 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 | ||
| 42 | Command-line options (in addition to the original ones) | 42 | Command-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 | ||
| 3121 | static 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 | |||
| 3121 | static CBlock MakeSizedConsensusBlock(size_t nScriptBytes) | 3202 | static 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 | } |