diff options
| author | bitflash-maintainer <noreply@bitflash.invalid> | 2026-09-05 22:46:52 +0000 |
|---|---|---|
| committer | bitflash-maintainer <noreply@bitflash.invalid> | 2026-09-05 22:46:52 +0000 |
| commit | ceae3d076b5ad11ee84729ae43f14e9312896646 (patch) | |
| tree | 7e9132aa871fdc01cb95268d2f5ea3bb11853ddf /src/main_gui.cpp | |
| parent | be63e83d79e7e00e6b8ca889a70e6a19886aa1d8 (diff) | |
| parent | 66e0b9158d050c46b1ff2509faeee3f4573cac1c (diff) | |
Bring main onto the line this project actually ships
main had drifted to a history with no ancestor in common with the tree we
build and deploy. It sat at be63e83 (2026-08-09) with 28 commits; the shipped
line, deploy/soak-2026-08-21, had 383. The two shared nothing -- git
merge-base returns no result at all -- so anyone cloning this repository
landed on a default branch that was neither current nor related to the
running node.
The 28 commits on main were audited before this merge: every one is already
present in the tree by content, including the two that are hardest to see
because they were rewritten afterwards -- the payout reorg-safety work
(PayoutBlockStillOnChain, since evolved to re-check on every retry rather
than once at maturity) and the nostr relay certificate verification
(SSL_set1_host, now with a Windows root-store path). Nothing on main is lost
by this merge.
Recorded as a merge rather than a force-push: main only ever moves forward,
its old history stays reachable as the first parent, and this is revertible.
The tree is taken wholesale from the merged branch, so main's content is now
exactly what was built and tested.
What this brings onto main:
- compactblock: BIP152-style compact block relay ported from upstream 1.2.23,
primitives and unit test only, with the RAND_bytes failure path closed and
the reconstruction allocation bounded.
- upstream/main 17741d9: isolated testnet parameters and genesis, optional
2 MB large pages for RandomX. Mainnet genesis verified byte-identical
through upstream's BuildGenesisBlock() refactor.
- deploy: every image built on bookworm so binaries run on Debian 12, and the
container build now honours the RandomX and libsecp256k1 pins it had been
silently ignoring since before 1.2.15.
Gates at this tree: 15/15 selftests, compactblock's 17 checks, consensus
constants unchanged against e4f898c apart from the pre-existing
MAX_BLOCK_SIZE_GEN, mainnet genesis intact, money-log check ok.
Diffstat (limited to 'src/main_gui.cpp')
| -rw-r--r-- | src/main_gui.cpp | 596 |
1 files changed, 569 insertions, 27 deletions
diff --git a/src/main_gui.cpp b/src/main_gui.cpp index bee4b95..2e4eb91 100644 --- a/src/main_gui.cpp +++ b/src/main_gui.cpp | |||
| @@ -1,7 +1,10 @@ | |||
| 1 | // Bitflash entry point -- starts node threads then runs GUI (or headless). | 1 | // Bitflash entry point -- starts node threads then runs GUI (or headless). |
| 2 | 2 | ||
| 3 | #include "headers_core.h" | 3 | #include "headers_core.h" |
| 4 | #include "nostr.h" | ||
| 5 | #include "proxy.h" | ||
| 4 | #include "selftest.h" | 6 | #include "selftest.h" |
| 7 | #include "tor.h" | ||
| 5 | #include "walletcmd.h" | 8 | #include "walletcmd.h" |
| 6 | #include <thread> // hardware_concurrency, to sanity-check /genproclimit | 9 | #include <thread> // hardware_concurrency, to sanity-check /genproclimit |
| 7 | #ifndef _WIN32 | 10 | #ifndef _WIN32 |
| @@ -108,9 +111,16 @@ static void PrintUsage() | |||
| 108 | printf(" /help, -help, --help, /?\n"); | 111 | printf(" /help, -help, --help, /?\n"); |
| 109 | printf(" /datadir=PATH\n"); | 112 | printf(" /datadir=PATH\n"); |
| 110 | printf(" /debug\n"); | 113 | printf(" /debug\n"); |
| 114 | printf(" /debugrelay (/debug plus per-message relay chatter and block dumps)\n"); | ||
| 115 | printf(" /maxdebugfilesize=N (roll debug.log at N MB, default 1024; 0 = never)\n"); | ||
| 111 | printf(" /gen\n"); | 116 | printf(" /gen\n"); |
| 112 | printf(" /nogui or /daemon\n"); | 117 | printf(" /nogui or /daemon\n"); |
| 113 | printf(" /selftest=wallet-keypool, wallet-hd, wallet-format, wallet-crypto, or wallet-encrypt\n"); | 118 | printf(" /selftest=wallet-keypool, wallet-hd, wallet-format, wallet-storage-sanity,\n"); |
| 119 | printf(" db-env-reopen, wallet-sqlite, wallet-sqlite-migration,\n"); | ||
| 120 | printf(" wallet-crypto, wallet-encrypt, wallet-portability,\n"); | ||
| 121 | printf(" net-message,\n"); | ||
| 122 | printf(" consensus-limits, pool-stratum,\n"); | ||
| 123 | printf(" parse-money, network-params, socks5-proxy, or managed-tor\n"); | ||
| 114 | printf("\n"); | 124 | printf("\n"); |
| 115 | printf("Mining mode:\n"); | 125 | printf("Mining mode:\n"); |
| 116 | printf(" /operator\n"); | 126 | printf(" /operator\n"); |
| @@ -118,22 +128,36 @@ static void PrintUsage() | |||
| 118 | printf(" /payoutaddr=BTF_ADDRESS (participant: pay shares to this address, no wallet needed)\n"); | 128 | printf(" /payoutaddr=BTF_ADDRESS (participant: pay shares to this address, no wallet needed)\n"); |
| 119 | printf(" /payoutfile=FILE (participant: rotate payout addresses from file)\n"); | 129 | printf(" /payoutfile=FILE (participant: rotate payout addresses from file)\n"); |
| 120 | printf(" /hashthreads=N (participant: mining threads, default cores-1)\n"); | 130 | printf(" /hashthreads=N (participant: mining threads, default cores-1)\n"); |
| 131 | printf(" /stratumbridge=POOL_BTF_ADDRESS\n"); | ||
| 132 | printf(" /stratumbridgeport=N (default 3333; listen on 127.0.0.1)\n"); | ||
| 121 | printf(" /solomine\n"); | 133 | printf(" /solomine\n"); |
| 122 | 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"); | ||
| 123 | 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"); |
| 124 | printf("\n"); | 138 | printf("\n"); |
| 125 | printf("Pool operator announcement:\n"); | 139 | printf("Pool operator announcement:\n"); |
| 126 | printf(" /poolname=NAME\n"); | 140 | printf(" /poolname=NAME\n"); |
| 127 | printf(" /poolfee=PCT\n"); | 141 | printf(" /poolfee=PCT\n"); |
| 128 | printf(" /pooldashboard=URL (alias: /pooldash=URL)\n"); | 142 | printf(" /pooldashboard=URL (alias: /pooldash=URL)\n"); |
| 143 | printf(" /poolstatusfile=PATH (write pool status JSON for dashboards)\n"); | ||
| 144 | printf(" /poolroundsfile=PATH (write public pool round proofs JSON)\n"); | ||
| 129 | printf("\n"); | 145 | printf("\n"); |
| 130 | printf(".btf and rendezvous:\n"); | 146 | printf(".btf and rendezvous:\n"); |
| 131 | printf(" /connectbtf=PEER_BTF_ADDRESS\n"); | 147 | printf(" /connectbtf=PEER_BTF_ADDRESS\n"); |
| 132 | printf(" /rvrelay=HOST:PORT\n"); | 148 | printf(" /rvrelay=HOST:PORT\n"); |
| 133 | printf(" /announcerelay=HOST:PORT\n"); | 149 | printf(" /announcerelay=HOST:PORT\n"); |
| 150 | printf(" /onionservice=HOST.onion:PORT (advertise this node's Tor hidden service)\n"); | ||
| 134 | printf("\n"); | 151 | printf("\n"); |
| 135 | printf("Network:\n"); | 152 | printf("Network:\n"); |
| 136 | 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"); | ||
| 155 | printf(" /socks=HOST:PORT (SOCKS5 proxy for Nostr, .btf relay, and onion dials)\n"); | ||
| 156 | printf(" /tor[=HOST:PORT] (Tor mode; default SOCKS5 proxy is 127.0.0.1:9050)\n"); | ||
| 157 | printf(" /managedtor[=PATH] (start Tor, create a hidden service, advertise its onion)\n"); | ||
| 158 | printf(" /automanagedtor (auto-start a bundled Tor if one sits next to the executable)\n"); | ||
| 159 | printf(" /nomanagedtor (disable automatic bundled Tor startup)\n"); | ||
| 160 | printf(" /oniononly (do not fall back to rendezvous when a .btf onion dial fails)\n"); | ||
| 137 | printf(" /btfseed=ADDRESS:ENCHEX (extra bootstrap peer, repeatable)\n"); | 161 | printf(" /btfseed=ADDRESS:ENCHEX (extra bootstrap peer, repeatable)\n"); |
| 138 | printf("\n"); | 162 | printf("\n"); |
| 139 | printf("Wallet:\n"); | 163 | printf("Wallet:\n"); |
| @@ -160,6 +184,26 @@ static void PrintUsage() | |||
| 160 | printf(" /recoveryaudit (show how much spendable balance is\n"); | 184 | printf(" /recoveryaudit (show how much spendable balance is\n"); |
| 161 | printf(" covered by the recovery phrase;\n"); | 185 | printf(" covered by the recovery phrase;\n"); |
| 162 | printf(" exits 2 when wallet.dat is still needed)\n"); | 186 | printf(" exits 2 when wallet.dat is still needed)\n"); |
| 187 | printf(" /walletstorageaudit (count wallet.dat record types without\n"); | ||
| 188 | printf(" printing wallet values, then exit)\n"); | ||
| 189 | printf(" /walletstorageauditjson=FILE\n"); | ||
| 190 | printf(" (write the same audit as deterministic JSON)\n"); | ||
| 191 | printf(" /walletstoragecheck (fail closed on inconsistent or unsafe\n"); | ||
| 192 | printf(" wallet.dat storage state)\n"); | ||
| 193 | printf(" /walletsqliteexport=FILE (copy raw wallet.dat records into an\n"); | ||
| 194 | printf(" SQLite store, then exit)\n"); | ||
| 195 | printf(" /walletsqliteverify=FILE (compare wallet.dat with a SQLite export,\n"); | ||
| 196 | printf(" printing counts only, then exit)\n"); | ||
| 197 | printf(" /walletsqliterestore=FILE (rebuild wallet.dat from a SQLite export in\n"); | ||
| 198 | printf(" an empty data directory, then exit)\n"); | ||
| 199 | printf(" /walletsqliteloadcheck=FILE\n"); | ||
| 200 | printf(" (parse a SQLite export like the wallet loader)\n"); | ||
| 201 | printf(" /walletsqlite=FILE (stage-load a SQLite wallet export, then exit)\n"); | ||
| 202 | printf(" /walletbackend=sqlite (opt-in: run the node on <datadir>/wallet.sqlite\n"); | ||
| 203 | printf(" instead of wallet.dat; export one first)\n"); | ||
| 204 | printf(" /createwallet (headless only: permit creating a new wallet in a\n"); | ||
| 205 | printf(" datadir that has none -- otherwise refused, since\n"); | ||
| 206 | printf(" a missing wallet is usually a mount/backup fault)\n"); | ||
| 163 | printf(" /rescan (walk the chain for coins this wallet owns\n"); | 207 | printf(" /rescan (walk the chain for coins this wallet owns\n"); |
| 164 | printf(" but never recorded, then exit)\n"); | 208 | printf(" but never recorded, then exit)\n"); |
| 165 | printf("\n"); | 209 | printf("\n"); |
| @@ -173,9 +217,60 @@ static void ParseStartupArguments(int argc, char* argv[]) | |||
| 173 | if (arg(argc,argv,"/datadir") || arg(argc,argv,"-datadir")) | 217 | if (arg(argc,argv,"/datadir") || arg(argc,argv,"-datadir")) |
| 174 | strSetDataDir = argval2(argc, argv, "/datadir", "-datadir"); | 218 | strSetDataDir = argval2(argc, argv, "/datadir", "-datadir"); |
| 175 | 219 | ||
| 220 | bool fUseTestNet = arg(argc, argv, "/testnet") || arg(argc, argv, "-testnet"); | ||
| 221 | SelectChainParams(fUseTestNet); | ||
| 222 | |||
| 176 | if (arg(argc,argv,"/debug") || arg(argc,argv,"-debug")) | 223 | if (arg(argc,argv,"/debug") || arg(argc,argv,"-debug")) |
| 177 | fDebug = true; | 224 | fDebug = true; |
| 178 | 225 | ||
| 226 | // /debugrelay -- everything /debug shows, plus the per-message firehose | ||
| 227 | // ("relay" and "nostrdiscovery" categories: sending: tx, Relaying wtx, | ||
| 228 | // raw CBlock dumps, per-peer discovery lines). That traffic was ~90% of | ||
| 229 | // a 13-hour production debug.log (issue #35), so plain /debug leaves it | ||
| 230 | // off; the message text itself is unchanged for anyone grepping it. | ||
| 231 | if (arg(argc,argv,"/debugrelay") || arg(argc,argv,"-debugrelay")) | ||
| 232 | { | ||
| 233 | fDebug = true; | ||
| 234 | fDebugRelay = true; | ||
| 235 | } | ||
| 236 | |||
| 237 | // /maxdebugfilesize=N -- roll debug.log to debug.log.old once it passes | ||
| 238 | // N MB, checked on each write. Default is 1024 (see util.cpp); 0 turns | ||
| 239 | // the check off and leaves rotation entirely to logrotate. | ||
| 240 | string strMaxDebugSize = argval2(argc, argv, "/maxdebugfilesize", "-maxdebugfilesize"); | ||
| 241 | if (!strMaxDebugSize.empty()) | ||
| 242 | { | ||
| 243 | // Parsed with atoi64() before, which is strtoll() with the endptr | ||
| 244 | // thrown away: it returns 0 for anything it cannot read, so | ||
| 245 | // /maxdebugfilesize=1G and =1024MB both came out as 0 -- "never | ||
| 246 | // roll", identical to asking for that on purpose, with nothing said | ||
| 247 | // to the operator. The old code also multiplied without a range | ||
| 248 | // check, so a big enough N wrapped to a negative cap, which the roll | ||
| 249 | // reads as "off" too. Every rejection is now reported and leaves the | ||
| 250 | // default in place; the value is a plain count of MB, no suffix. | ||
| 251 | const char* psz = strMaxDebugSize.c_str(); | ||
| 252 | char* pszEnd = NULL; | ||
| 253 | errno = 0; | ||
| 254 | int64 nMB = strtoll(psz, &pszEnd, 10); | ||
| 255 | // INT64_MAX, without depending on which limits header this tree pulls in. | ||
| 256 | const int64 nMaxMB = (int64)((~(uint64)0) >> 1) / (1024 * 1024); | ||
| 257 | const char* pszWhy = NULL; | ||
| 258 | if (pszEnd == psz) | ||
| 259 | pszWhy = "not a number"; | ||
| 260 | else if (*pszEnd != '\0') | ||
| 261 | pszWhy = "trailing characters -- the value is a count of MB, with no unit suffix"; | ||
| 262 | else if (errno == ERANGE || nMB < 0) | ||
| 263 | pszWhy = "not a size in MB"; | ||
| 264 | else if (nMB > nMaxMB) | ||
| 265 | pszWhy = "larger than a file size can be"; | ||
| 266 | if (pszWhy) | ||
| 267 | fprintf(stderr, "Ignoring /maxdebugfilesize=%s: %s (keeping %lld MB)\n", | ||
| 268 | strMaxDebugSize.c_str(), pszWhy, | ||
| 269 | (long long)(nMaxDebugFileSize / 1024 / 1024)); | ||
| 270 | else | ||
| 271 | nMaxDebugFileSize = nMB * 1024 * 1024; | ||
| 272 | } | ||
| 273 | |||
| 179 | if (arg(argc,argv,"/gen") || arg(argc,argv,"-gen")) | 274 | if (arg(argc,argv,"/gen") || arg(argc,argv,"-gen")) |
| 180 | { | 275 | { |
| 181 | fGenerateBitcoins = 1; | 276 | fGenerateBitcoins = 1; |
| @@ -195,6 +290,11 @@ static void ParseStartupArguments(int argc, char* argv[]) | |||
| 195 | if (!strCheckBlocks.empty()) | 290 | if (!strCheckBlocks.empty()) |
| 196 | nCheckBlocksOnLoad = atoi(strCheckBlocks.c_str()); | 291 | nCheckBlocksOnLoad = atoi(strCheckBlocks.c_str()); |
| 197 | 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 | |||
| 198 | // /genproclimit=N -- threads to hash with. 0 or absent means automatic, | 298 | // /genproclimit=N -- threads to hash with. 0 or absent means automatic, |
| 199 | // 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 |
| 200 | // familiarly to anyone who has run one of these before. | 300 | // familiarly to anyone who has run one of these before. |
| @@ -240,6 +340,22 @@ static void ParseStartupArguments(int argc, char* argv[]) | |||
| 240 | if (!hashThreads.empty()) | 340 | if (!hashThreads.empty()) |
| 241 | nHashThreads = atoi(hashThreads.c_str()); | 341 | nHashThreads = atoi(hashThreads.c_str()); |
| 242 | 342 | ||
| 343 | if (arg(argc,argv,"/stratumbridge") || arg(argc,argv,"-stratumbridge")) | ||
| 344 | { | ||
| 345 | fStratumBridge = true; | ||
| 346 | string bridgePool = argval2(argc, argv, "/stratumbridge", "-stratumbridge"); | ||
| 347 | if (!bridgePool.empty()) | ||
| 348 | strParticipantPool = bridgePool; | ||
| 349 | } | ||
| 350 | |||
| 351 | string bridgePort = argval2(argc, argv, "/stratumbridgeport", "-stratumbridgeport"); | ||
| 352 | if (!bridgePort.empty()) | ||
| 353 | { | ||
| 354 | int nPort = atoi(bridgePort.c_str()); | ||
| 355 | if (nPort > 0 && nPort <= 65535) | ||
| 356 | nStratumBridgePort = nPort; | ||
| 357 | } | ||
| 358 | |||
| 243 | string poolName = argval2(argc, argv, "/poolname", "-poolname"); | 359 | string poolName = argval2(argc, argv, "/poolname", "-poolname"); |
| 244 | if (!poolName.empty()) | 360 | if (!poolName.empty()) |
| 245 | strPoolName = poolName; | 361 | strPoolName = poolName; |
| @@ -260,9 +376,18 @@ static void ParseStartupArguments(int argc, char* argv[]) | |||
| 260 | if (!poolFee.empty()) | 376 | if (!poolFee.empty()) |
| 261 | dPoolFeePercent = atof(poolFee.c_str()); | 377 | dPoolFeePercent = atof(poolFee.c_str()); |
| 262 | 378 | ||
| 379 | string poolStatusFile = argval2(argc, argv, "/poolstatusfile", "-poolstatusfile"); | ||
| 380 | if (!poolStatusFile.empty()) | ||
| 381 | strPoolStatusFile = poolStatusFile; | ||
| 382 | |||
| 383 | string poolRoundsFile = argval2(argc, argv, "/poolroundsfile", "-poolroundsfile"); | ||
| 384 | if (!poolRoundsFile.empty()) | ||
| 385 | strPoolRoundsFile = poolRoundsFile; | ||
| 386 | |||
| 263 | string btfConnect = argval2(argc, argv, "/connectbtf", "-connectbtf"); | 387 | string btfConnect = argval2(argc, argv, "/connectbtf", "-connectbtf"); |
| 264 | if (!btfConnect.empty()) | 388 | if (!btfConnect.empty()) |
| 265 | strBtfConnect = btfConnect; | 389 | strBtfConnect = btfConnect; |
| 390 | fBtfOnionOnly = arg(argc, argv, "/oniononly") || arg(argc, argv, "-oniononly"); | ||
| 266 | 391 | ||
| 267 | string rvRelay = argval2(argc, argv, "/rvrelay", "-rvrelay"); | 392 | string rvRelay = argval2(argc, argv, "/rvrelay", "-rvrelay"); |
| 268 | if (!rvRelay.empty()) | 393 | if (!rvRelay.empty()) |
| @@ -283,10 +408,102 @@ static void ParseStartupArguments(int argc, char* argv[]) | |||
| 283 | } | 408 | } |
| 284 | } | 409 | } |
| 285 | 410 | ||
| 411 | // net.cpp has described nListenPort as "tunable via /port" since it was | ||
| 412 | // written, but nothing ever read the option, so the port was fixed at 8433 | ||
| 413 | // and a second node could not start on a machine already running one. Parse | ||
| 414 | // it before Tor setup so a managed hidden service maps the real listener. | ||
| 415 | string strPort = argval2(argc, argv, "/port", "-port"); | ||
| 416 | if (!strPort.empty()) | ||
| 417 | { | ||
| 418 | int nPort = atoi(strPort.c_str()); | ||
| 419 | if (nPort <= 0 || nPort > 65535) | ||
| 420 | fprintf(stderr, "Ignoring /port=%s: not a port number\n", strPort.c_str()); | ||
| 421 | else | ||
| 422 | { | ||
| 423 | nListenPort = htons((unsigned short)nPort); | ||
| 424 | // Or we would announce a port we never bound. | ||
| 425 | addrLocalHost.port = nListenPort; | ||
| 426 | } | ||
| 427 | } | ||
| 428 | |||
| 286 | string announceRelay = argval2(argc, argv, "/announcerelay", "-announcerelay"); | 429 | string announceRelay = argval2(argc, argv, "/announcerelay", "-announcerelay"); |
| 287 | if (!announceRelay.empty()) | 430 | if (!announceRelay.empty()) |
| 288 | strBtfAnnounceRelay = announceRelay; | 431 | strBtfAnnounceRelay = announceRelay; |
| 289 | 432 | ||
| 433 | string onionService = argval2(argc, argv, "/onionservice", "-onionservice"); | ||
| 434 | if (!onionService.empty()) | ||
| 435 | { | ||
| 436 | string err; | ||
| 437 | if (!BtfSetLocalOnionEndpoint(onionService, err)) | ||
| 438 | fprintf(stderr, "Ignoring /onionservice=%s: %s\n", onionService.c_str(), err.c_str()); | ||
| 439 | else | ||
| 440 | fprintf(stderr, "Direct onion peer endpoint advertised: %s\n", | ||
| 441 | BtfLocalOnionEndpoint().c_str()); | ||
| 442 | } | ||
| 443 | |||
| 444 | bool fManagedTor = arg(argc, argv, "/managedtor") || arg(argc, argv, "-managedtor"); | ||
| 445 | bool fNoManagedTor = arg(argc, argv, "/nomanagedtor") || arg(argc, argv, "-nomanagedtor"); | ||
| 446 | bool fTorMode = arg(argc, argv, "/tor") || arg(argc, argv, "-tor"); | ||
| 447 | string socksProxy = argval2(argc, argv, "/socks", "-socks"); | ||
| 448 | string bundledTorPath; | ||
| 449 | // Managed Tor is opt-in on this fork. Upstream 1.2.19 auto-starts a | ||
| 450 | // bundled tor whenever one sits next to the executable, which silently | ||
| 451 | // moves Nostr/.btf traffic onto Tor, creates a hidden service, and starts | ||
| 452 | // publishing descriptors with meeting_node="rendezvous-pending" on nodes | ||
| 453 | // that never asked for it -- against our pinned-relay posture. The | ||
| 454 | // upstream behavior remains available under the explicit /automanagedtor. | ||
| 455 | bool fAutoManagedTor = !fManagedTor && !fNoManagedTor && !fTorMode && | ||
| 456 | socksProxy.empty() && | ||
| 457 | (arg(argc, argv, "/automanagedtor") || arg(argc, argv, "-automanagedtor")) && | ||
| 458 | BtfBundledTorPath(bundledTorPath); | ||
| 459 | if (fManagedTor || fAutoManagedTor) | ||
| 460 | { | ||
| 461 | if (fManagedTor && (fTorMode || !socksProxy.empty())) | ||
| 462 | fprintf(stderr, "Warning: /managedtor takes precedence over /tor and /socks\n"); | ||
| 463 | |||
| 464 | string torPath = fManagedTor ? argval2(argc, argv, "/managedtor", "-managedtor") : bundledTorPath; | ||
| 465 | string err; | ||
| 466 | if (!BtfStartManagedTor(torPath, err)) | ||
| 467 | { | ||
| 468 | if (fAutoManagedTor) | ||
| 469 | fprintf(stderr, "Bundled Tor not enabled automatically: %s\n", err.c_str()); | ||
| 470 | else | ||
| 471 | fprintf(stderr, "Ignoring /managedtor=%s: %s\n", torPath.c_str(), err.c_str()); | ||
| 472 | } | ||
| 473 | else | ||
| 474 | fprintf(stderr, "Managed Tor enabled%s: %s\n", | ||
| 475 | fAutoManagedTor ? " automatically" : "", | ||
| 476 | BtfManagedTorStatus().c_str()); | ||
| 477 | } | ||
| 478 | else if (fTorMode) | ||
| 479 | { | ||
| 480 | if (!socksProxy.empty()) | ||
| 481 | fprintf(stderr, "Warning: /tor takes precedence over /socks=%s\n", | ||
| 482 | socksProxy.c_str()); | ||
| 483 | |||
| 484 | string torProxy = argval2(argc, argv, "/tor", "-tor"); | ||
| 485 | string err; | ||
| 486 | if (!BtfEnableTorProxy(torProxy, err)) | ||
| 487 | fprintf(stderr, "Ignoring /tor=%s: %s\n", torProxy.c_str(), err.c_str()); | ||
| 488 | else | ||
| 489 | fprintf(stderr, "Tor mode enabled through SOCKS5 proxy %s\n", | ||
| 490 | BtfSocks5ProxyName().c_str()); | ||
| 491 | } | ||
| 492 | else | ||
| 493 | { | ||
| 494 | if (!socksProxy.empty()) | ||
| 495 | { | ||
| 496 | string err; | ||
| 497 | if (!BtfSetSocks5Proxy(socksProxy, err)) | ||
| 498 | fprintf(stderr, "Ignoring /socks=%s: %s\n", socksProxy.c_str(), err.c_str()); | ||
| 499 | else | ||
| 500 | fprintf(stderr, "SOCKS5 proxy enabled for outbound discovery/relay dials: %s\n", | ||
| 501 | BtfSocks5ProxyName().c_str()); | ||
| 502 | } | ||
| 503 | } | ||
| 504 | if (fBtfOnionOnly && !BtfSocks5ProxyEnabled()) | ||
| 505 | fprintf(stderr, "Warning: /oniononly without /tor, /managedtor, or /socks cannot dial .onion peers\n"); | ||
| 506 | |||
| 290 | // /btfseed=ADDRESS:ENCHEX -- extra bootstrap peers, repeatable. Useful for | 507 | // /btfseed=ADDRESS:ENCHEX -- extra bootstrap peers, repeatable. Useful for |
| 291 | // testing the seed path and for private networks that ship no compiled list. | 508 | // testing the seed path and for private networks that ship no compiled list. |
| 292 | for (int i = 1; i < argc; i++) | 509 | for (int i = 1; i < argc; i++) |
| @@ -306,22 +523,6 @@ static void ParseStartupArguments(int argc, char* argv[]) | |||
| 306 | vBtfExtraSeeds.push_back(make_pair(val.substr(0, colon), val.substr(colon + 1))); | 523 | vBtfExtraSeeds.push_back(make_pair(val.substr(0, colon), val.substr(colon + 1))); |
| 307 | } | 524 | } |
| 308 | 525 | ||
| 309 | // net.cpp has described nListenPort as "tunable via /port" since it was | ||
| 310 | // written, but nothing ever read the option, so the port was fixed at 8433 | ||
| 311 | // and a second node could not start on a machine already running one. | ||
| 312 | string strPort = argval2(argc, argv, "/port", "-port"); | ||
| 313 | if (!strPort.empty()) | ||
| 314 | { | ||
| 315 | int nPort = atoi(strPort.c_str()); | ||
| 316 | if (nPort <= 0 || nPort > 65535) | ||
| 317 | fprintf(stderr, "Ignoring /port=%s: not a port number\n", strPort.c_str()); | ||
| 318 | else | ||
| 319 | { | ||
| 320 | nListenPort = htons((unsigned short)nPort); | ||
| 321 | // Or we would announce a port we never bound. | ||
| 322 | addrLocalHost.port = nListenPort; | ||
| 323 | } | ||
| 324 | } | ||
| 325 | } | 526 | } |
| 326 | 527 | ||
| 327 | // Report a startup failure somewhere the user will actually see it. | 528 | // Report a startup failure somewhere the user will actually see it. |
| @@ -368,6 +569,36 @@ static void FatalStartupError(bool fHeadless, const string& strWhat, const strin | |||
| 368 | #endif | 569 | #endif |
| 369 | } | 570 | } |
| 370 | 571 | ||
| 572 | #ifdef _WIN32 | ||
| 573 | // Windows equivalent of the SIGINT/SIGTERM handler the headless loop installs on | ||
| 574 | // POSIX. Without it a headless node on Windows had no way to reach the clean | ||
| 575 | // StopNode()+DBFlush() path: Ctrl-C, closing the console window, a service stop | ||
| 576 | // and OS shutdown all killed it mid-write, and a wallet.dat left unflushed by | ||
| 577 | // Berkeley DB will not reopen. For CLOSE/LOGOFF/SHUTDOWN the OS terminates us | ||
| 578 | // shortly after the handler returns, so we set fShutdown and then block until the | ||
| 579 | // main loop signals the flush is done -- returning any sooner throws the flush | ||
| 580 | // away, which is the whole bug. (A `taskkill /F` is still an uncatchable hard | ||
| 581 | // kill; nothing can help there, and nothing should pretend to.) | ||
| 582 | static volatile bool g_fHeadlessShutdownDone = false; | ||
| 583 | static BOOL WINAPI HeadlessConsoleCtrlHandler(DWORD dwCtrlType) | ||
| 584 | { | ||
| 585 | switch (dwCtrlType) | ||
| 586 | { | ||
| 587 | case CTRL_C_EVENT: | ||
| 588 | case CTRL_BREAK_EVENT: | ||
| 589 | case CTRL_CLOSE_EVENT: | ||
| 590 | case CTRL_LOGOFF_EVENT: | ||
| 591 | case CTRL_SHUTDOWN_EVENT: | ||
| 592 | fShutdown = true; | ||
| 593 | while (!g_fHeadlessShutdownDone) | ||
| 594 | Sleep(50); | ||
| 595 | return TRUE; | ||
| 596 | default: | ||
| 597 | return FALSE; | ||
| 598 | } | ||
| 599 | } | ||
| 600 | #endif | ||
| 601 | |||
| 371 | int main(int argc, char* argv[]) | 602 | int main(int argc, char* argv[]) |
| 372 | { | 603 | { |
| 373 | if (arg(argc,argv,"/help") || arg(argc,argv,"-help") || | 604 | if (arg(argc,argv,"/help") || arg(argc,argv,"-help") || |
| @@ -384,10 +615,119 @@ int main(int argc, char* argv[]) | |||
| 384 | 615 | ||
| 385 | ParseStartupArguments(argc, argv); | 616 | ParseStartupArguments(argc, argv); |
| 386 | 617 | ||
| 618 | #ifdef _WIN32 | ||
| 619 | // Set by the GUI "Restart Now" button: give the previous instance a moment | ||
| 620 | // to release the network port and the block-index files before this one | ||
| 621 | // opens them, so a relaunch does not race the process it is replacing. | ||
| 622 | if (arg(argc, argv, "/restartwait") || arg(argc, argv, "-restartwait")) | ||
| 623 | Sleep(2500); | ||
| 624 | #endif | ||
| 625 | |||
| 626 | // Hidden self-test helper. It must run before LoadWallet(): several | ||
| 627 | // storage-sanity scenarios deliberately make wallet.dat unsafe to load. | ||
| 628 | string strSelfTestMutateWallet = | ||
| 629 | argval2(argc, argv, "/selftestmutatewallet", "-selftestmutatewallet"); | ||
| 630 | if (!strSelfTestMutateWallet.empty()) | ||
| 631 | return RunSelfTestMutateWallet(strSelfTestMutateWallet); | ||
| 632 | |||
| 387 | string strSelfTest = argval2(argc, argv, "/selftest", "-selftest"); | 633 | string strSelfTest = argval2(argc, argv, "/selftest", "-selftest"); |
| 388 | if (!strSelfTest.empty()) | 634 | if (!strSelfTest.empty()) |
| 389 | return RunSelfTest(strSelfTest); | 635 | return RunSelfTest(strSelfTest); |
| 390 | 636 | ||
| 637 | // Storage diagnostics must run before LoadWallet(). Their job is to | ||
| 638 | // explain wallet.dat states that the normal wallet loader may refuse. | ||
| 639 | string strWalletStorageAuditJson = | ||
| 640 | argval2(argc, argv, "/walletstorageauditjson", "-walletstorageauditjson"); | ||
| 641 | bool fDiagWantsAudit = arg(argc,argv,"/walletstorageaudit") || | ||
| 642 | arg(argc,argv,"-walletstorageaudit") || | ||
| 643 | !strWalletStorageAuditJson.empty(); | ||
| 644 | bool fDiagWantsCheck = arg(argc,argv,"/walletstoragecheck") || | ||
| 645 | arg(argc,argv,"-walletstoragecheck"); | ||
| 646 | |||
| 647 | // These diagnostics normally read wallet.dat. Under -walletbackend=sqlite | ||
| 648 | // the live wallet is wallet.sqlite, so open it read-only as the active | ||
| 649 | // store and let the same scan walk the right file. This runs before the | ||
| 650 | // normal backend resolution further down, so validate the value here too. | ||
| 651 | string strDiagBackend = argval2(argc, argv, "/walletbackend", "-walletbackend"); | ||
| 652 | if ((fDiagWantsAudit || fDiagWantsCheck) && !strDiagBackend.empty() && | ||
| 653 | strDiagBackend != "sqlite" && strDiagBackend != "bdb") | ||
| 654 | { | ||
| 655 | AttachTerminal(); | ||
| 656 | fprintf(stderr, "Unknown wallet backend '%s'. Use -walletbackend=sqlite " | ||
| 657 | "or -walletbackend=bdb.\n", strDiagBackend.c_str()); | ||
| 658 | return 1; | ||
| 659 | } | ||
| 660 | bool fDiagSQLite = (strDiagBackend == "sqlite") && | ||
| 661 | (fDiagWantsAudit || fDiagWantsCheck); | ||
| 662 | if (fDiagSQLite) | ||
| 663 | { | ||
| 664 | AttachTerminal(); | ||
| 665 | string strSQLitePath = GetAppDir() + "/wallet.sqlite"; | ||
| 666 | string strOpenErr; | ||
| 667 | if (!WalletSQLiteRuntimeOpenReadOnly(strSQLitePath, strOpenErr)) | ||
| 668 | { | ||
| 669 | fprintf(stderr, "Cannot open the SQLite wallet for diagnostics: %s\n", | ||
| 670 | strOpenErr.c_str()); | ||
| 671 | return 1; | ||
| 672 | } | ||
| 673 | } | ||
| 674 | |||
| 675 | if (fDiagWantsAudit) | ||
| 676 | { | ||
| 677 | int nRet = CmdWalletStorageAudit(strWalletStorageAuditJson); | ||
| 678 | if (fDiagSQLite) | ||
| 679 | WalletSQLiteRuntimeClose(); | ||
| 680 | else | ||
| 681 | DBFlush(true); | ||
| 682 | return nRet; | ||
| 683 | } | ||
| 684 | |||
| 685 | if (fDiagWantsCheck) | ||
| 686 | { | ||
| 687 | int nRet = CmdWalletStorageCheck(); | ||
| 688 | if (fDiagSQLite) | ||
| 689 | WalletSQLiteRuntimeClose(); | ||
| 690 | else | ||
| 691 | DBFlush(true); | ||
| 692 | return nRet; | ||
| 693 | } | ||
| 694 | |||
| 695 | string strWalletSQLiteExport = | ||
| 696 | argval2(argc, argv, "/walletsqliteexport", "-walletsqliteexport"); | ||
| 697 | if (!strWalletSQLiteExport.empty()) | ||
| 698 | { | ||
| 699 | int nRet = CmdWalletSQLiteExport(strWalletSQLiteExport); | ||
| 700 | DBFlush(true); | ||
| 701 | return nRet; | ||
| 702 | } | ||
| 703 | |||
| 704 | string strWalletSQLiteVerify = | ||
| 705 | argval2(argc, argv, "/walletsqliteverify", "-walletsqliteverify"); | ||
| 706 | if (!strWalletSQLiteVerify.empty()) | ||
| 707 | { | ||
| 708 | int nRet = CmdWalletSQLiteVerify(strWalletSQLiteVerify); | ||
| 709 | DBFlush(true); | ||
| 710 | return nRet; | ||
| 711 | } | ||
| 712 | |||
| 713 | string strWalletSQLiteRestore = | ||
| 714 | argval2(argc, argv, "/walletsqliterestore", "-walletsqliterestore"); | ||
| 715 | if (!strWalletSQLiteRestore.empty()) | ||
| 716 | { | ||
| 717 | int nRet = CmdWalletSQLiteRestore(strWalletSQLiteRestore); | ||
| 718 | DBFlush(true); | ||
| 719 | return nRet; | ||
| 720 | } | ||
| 721 | |||
| 722 | string strWalletSQLiteLoadCheck = | ||
| 723 | argval2(argc, argv, "/walletsqliteloadcheck", "-walletsqliteloadcheck"); | ||
| 724 | if (!strWalletSQLiteLoadCheck.empty()) | ||
| 725 | { | ||
| 726 | int nRet = CmdWalletSQLiteLoadCheck(strWalletSQLiteLoadCheck); | ||
| 727 | DBFlush(true); | ||
| 728 | return nRet; | ||
| 729 | } | ||
| 730 | |||
| 391 | // Berkeley DB reports failure by throwing, and CDB's constructor lets it | 731 | // Berkeley DB reports failure by throwing, and CDB's constructor lets it |
| 392 | // through. Nothing on this path caught anything, so an unreadable | 732 | // through. Nothing on this path caught anything, so an unreadable |
| 393 | // blkindex.dat or wallet.dat -- one written by another platform's Berkeley | 733 | // blkindex.dat or wallet.dat -- one written by another platform's Berkeley |
| @@ -432,18 +772,197 @@ int main(int argc, char* argv[]) | |||
| 432 | printf("Loading wallet...\n"); | 772 | printf("Loading wallet...\n"); |
| 433 | try | 773 | try |
| 434 | { | 774 | { |
| 435 | if (!LoadWallet()) | 775 | string strWalletBackend = |
| 776 | argval2(argc, argv, "/walletbackend", "-walletbackend"); | ||
| 777 | string strWalletSQLite = | ||
| 778 | argval2(argc, argv, "/walletsqlite", "-walletsqlite"); | ||
| 779 | |||
| 780 | // A misspelled backend must not fall through to Berkeley DB silently -- | ||
| 781 | // someone who typed -walletbackend=sqlit meant to run on SQLite, and | ||
| 782 | // opening wallet.dat instead of telling them is the wrong surprise. | ||
| 783 | if (!strWalletBackend.empty() && | ||
| 784 | strWalletBackend != "sqlite" && strWalletBackend != "bdb") | ||
| 436 | { | 785 | { |
| 437 | // LoadWallet() explains itself into strWalletLoadError when it | 786 | AttachTerminal(); |
| 438 | // knows why -- an unsupported wallet format, for one, which is the | 787 | FatalStartupError(fHeadlessStartup, |
| 439 | // case this whole path exists to make visible. | 788 | strprintf("Unknown wallet backend '%s'.", strWalletBackend.c_str()), |
| 440 | FatalStartupError(fHeadlessStartup, "Cannot open wallet.dat.", | 789 | "Use -walletbackend=sqlite for the SQLite backend, or " |
| 441 | strWalletLoadError); | 790 | "-walletbackend=bdb for the Berkeley DB wallet.dat."); |
| 442 | return 1; | 791 | return 1; |
| 443 | } | 792 | } |
| 444 | 793 | ||
| 794 | if (strWalletBackend == "sqlite") | ||
| 795 | { | ||
| 796 | // Explicit opt-in via the flag. The node runs on | ||
| 797 | // <datadir>/wallet.sqlite and never opens wallet.dat, but the user | ||
| 798 | // has to have exported one first -- there is no silent migration, | ||
| 799 | // and wallet.dat is left | ||
| 800 | // exactly where it is so a plain restart goes back to it. | ||
| 801 | AttachTerminal(); | ||
| 802 | string strSQLitePath = GetAppDir() + "/wallet.sqlite"; | ||
| 803 | if (!FileExists(strSQLitePath.c_str())) | ||
| 804 | { | ||
| 805 | string strGuide = strprintf( | ||
| 806 | "No SQLite wallet was found at\n" | ||
| 807 | " %s\n\n" | ||
| 808 | "The SQLite wallet backend is opt-in and needs an exported\n" | ||
| 809 | "wallet first. Your Berkeley DB wallet.dat is not touched by\n" | ||
| 810 | "any of this.\n\n" | ||
| 811 | " Step 1 Export your existing wallet to SQLite:\n" | ||
| 812 | " bitflash -walletsqliteexport=\"%s\"\n" | ||
| 813 | " Step 2 Start again with the SQLite backend:\n" | ||
| 814 | " bitflash -walletbackend=sqlite\n\n" | ||
| 815 | "To go back to Berkeley DB at any time, just start without\n" | ||
| 816 | "-walletbackend=sqlite. wallet.dat is still your wallet.", | ||
| 817 | strSQLitePath.c_str(), strSQLitePath.c_str()); | ||
| 818 | FatalStartupError(fHeadlessStartup, | ||
| 819 | "The SQLite wallet backend needs an exported wallet first.", | ||
| 820 | strGuide); | ||
| 821 | return 1; | ||
| 822 | } | ||
| 823 | |||
| 824 | if (!LoadWalletFromSQLiteRuntime(strSQLitePath)) | ||
| 825 | { | ||
| 826 | FatalStartupError(fHeadlessStartup, | ||
| 827 | "Cannot open the SQLite wallet backend.", | ||
| 828 | strWalletLoadError); | ||
| 829 | return 1; | ||
| 830 | } | ||
| 831 | |||
| 832 | fprintf(stderr, | ||
| 833 | "Wallet backend: SQLite\n" | ||
| 834 | " %s\n" | ||
| 835 | "Your Berkeley DB wallet.dat is left untouched. Restart without\n" | ||
| 836 | "-walletbackend=sqlite to go back to it.\n", | ||
| 837 | strSQLitePath.c_str()); | ||
| 838 | fflush(stderr); | ||
| 839 | // Fall through and run the node normally: every CWalletDB read, | ||
| 840 | // write, and erase now routes to this SQLite file. | ||
| 841 | } | ||
| 842 | else if (!strWalletSQLite.empty()) | ||
| 843 | { | ||
| 844 | AttachTerminal(); | ||
| 845 | if (!LoadWalletFromSQLite(strWalletSQLite)) | ||
| 846 | { | ||
| 847 | FatalStartupError(fHeadlessStartup, | ||
| 848 | "Cannot open SQLite wallet export.", | ||
| 849 | strWalletLoadError); | ||
| 850 | return 1; | ||
| 851 | } | ||
| 852 | |||
| 853 | fprintf(stderr, "SQLite wallet loaded: read-only staging backend\n"); | ||
| 854 | fprintf(stderr, "SQLite runtime writes are not enabled yet; exiting before " | ||
| 855 | "network, mining, GUI, or wallet mutation starts.\n"); | ||
| 856 | fflush(stderr); | ||
| 857 | DBFlush(true); | ||
| 858 | return 0; | ||
| 859 | } | ||
| 860 | else if (strWalletBackend == "bdb") | ||
| 861 | { | ||
| 862 | // Explicit -walletbackend=bdb overrides the marker and the default: | ||
| 863 | // force Berkeley DB (LoadWallet creates wallet.dat if none exists). | ||
| 864 | if (!LoadWallet()) | ||
| 865 | { | ||
| 866 | FatalStartupError(fHeadlessStartup, "Cannot open wallet.dat.", | ||
| 867 | strWalletLoadError); | ||
| 868 | return 1; | ||
| 869 | } | ||
| 870 | } | ||
| 871 | else | ||
| 872 | { | ||
| 873 | // No explicit -walletbackend on the command line. Resolve it from | ||
| 874 | // the persistent marker, then defaults: an existing wallet.sqlite | ||
| 875 | // stays on SQLite, an existing wallet.dat stays on Berkeley DB (the | ||
| 876 | // GUI offers to convert), and a fresh install starts on SQLite -- | ||
| 877 | // the new default. wallet.dat is never touched by any of this. | ||
| 878 | string strSQLitePath = GetAppDir() + "/wallet.sqlite"; | ||
| 879 | bool fSQLiteExists = FileExists(strSQLitePath.c_str()); | ||
| 880 | bool fWalletDatExists = FileExists((GetAppDir() + "/wallet.dat").c_str()); | ||
| 881 | string strMarker = ReadWalletBackendMarker(); | ||
| 882 | |||
| 883 | bool fUseSQLite = false; | ||
| 884 | bool fCreateSQLite = false; | ||
| 885 | if (strMarker == "sqlite") | ||
| 886 | { | ||
| 887 | if (fSQLiteExists) fUseSQLite = true; | ||
| 888 | else if (fWalletDatExists) fUseSQLite = false; // marker stale; real wallet is BDB | ||
| 889 | else { fUseSQLite = true; fCreateSQLite = true; } | ||
| 890 | } | ||
| 891 | else if (strMarker == "bdb") | ||
| 892 | { | ||
| 893 | fUseSQLite = false; | ||
| 894 | } | ||
| 895 | else // no marker recorded yet | ||
| 896 | { | ||
| 897 | if (fSQLiteExists && !fWalletDatExists) fUseSQLite = true; | ||
| 898 | else if (fWalletDatExists) fUseSQLite = false; | ||
| 899 | else { fUseSQLite = true; fCreateSQLite = true; } | ||
| 900 | } | ||
| 901 | |||
| 902 | // A headless node whose datadir holds no wallet of either kind is | ||
| 903 | // far more often an operations fault -- datadir not mounted, or | ||
| 904 | // wallet.dat caught mid-rename by a backup -- than a first run. | ||
| 905 | // Creating one here is not the harmless default it looks like: | ||
| 906 | // success also writes the backend marker, and from then on | ||
| 907 | // restoring the real wallet.dat no longer takes effect, because the | ||
| 908 | // marker keeps the node on the empty wallet it just made. Under | ||
| 909 | // 1.2.19 the same fault created a wallet.dat that a restore | ||
| 910 | // overwrote, so operators could recover by putting the file back. | ||
| 911 | // Refuse instead, and name the flag that makes it deliberate. | ||
| 912 | if (fCreateSQLite && fHeadlessStartup && | ||
| 913 | !arg(argc, argv, "/createwallet") && !arg(argc, argv, "-createwallet")) | ||
| 914 | { | ||
| 915 | AttachTerminal(); | ||
| 916 | fprintf(stderr, | ||
| 917 | "No wallet found in %s\n" | ||
| 918 | "Refusing to create one. On a headless node an absent wallet is\n" | ||
| 919 | "usually a mount or backup fault, and creating one would record a\n" | ||
| 920 | "backend marker that makes restoring the real wallet a no-op.\n" | ||
| 921 | "Pass -createwallet to start a new one deliberately, or\n" | ||
| 922 | "-walletbackend=bdb to run on a Berkeley DB wallet.dat.\n", | ||
| 923 | GetAppDir().c_str()); | ||
| 924 | fflush(stderr); | ||
| 925 | return 1; | ||
| 926 | } | ||
| 927 | |||
| 928 | if (fUseSQLite) | ||
| 929 | { | ||
| 930 | AttachTerminal(); | ||
| 931 | bool fOk = fCreateSQLite | ||
| 932 | ? CreateNewSQLiteWallet(strSQLitePath) | ||
| 933 | : LoadWalletFromSQLiteRuntime(strSQLitePath); | ||
| 934 | if (!fOk) | ||
| 935 | { | ||
| 936 | FatalStartupError(fHeadlessStartup, | ||
| 937 | fCreateSQLite ? "Cannot create the SQLite wallet." | ||
| 938 | : "Cannot open the SQLite wallet.", | ||
| 939 | strWalletLoadError); | ||
| 940 | return 1; | ||
| 941 | } | ||
| 942 | // Record the choice so it is stable from here on. | ||
| 943 | if (ReadWalletBackendMarker() != "sqlite") | ||
| 944 | WriteWalletBackendMarker("sqlite"); | ||
| 945 | fprintf(stderr, "Wallet backend: SQLite\n %s\n%s", | ||
| 946 | strSQLitePath.c_str(), | ||
| 947 | fCreateSQLite ? "A new wallet was created here.\n" : ""); | ||
| 948 | fflush(stderr); | ||
| 949 | // Fall through and run the node; CWalletDB routes to this file. | ||
| 950 | } | ||
| 951 | else if (!LoadWallet()) | ||
| 952 | { | ||
| 953 | // LoadWallet() explains itself into strWalletLoadError when it | ||
| 954 | // knows why -- an unsupported wallet format, for one, which is | ||
| 955 | // the case this whole path exists to make visible. | ||
| 956 | FatalStartupError(fHeadlessStartup, "Cannot open wallet.dat.", | ||
| 957 | strWalletLoadError); | ||
| 958 | return 1; | ||
| 959 | } | ||
| 960 | } | ||
| 961 | |||
| 445 | // After the block index, so there is a chain to compare the wallet | 962 | // After the block index, so there is a chain to compare the wallet |
| 446 | // against, and before anything reports a balance. | 963 | // against, and before anything reports a balance. Works on either |
| 964 | // backend: RescanSpentFlags writes corrected flags through CWalletDB, | ||
| 965 | // which routes to SQLite when that backend is active. | ||
| 447 | RescanSpentFlags(); | 966 | RescanSpentFlags(); |
| 448 | } | 967 | } |
| 449 | catch (const std::exception& e) | 968 | catch (const std::exception& e) |
| @@ -668,7 +1187,12 @@ int main(int argc, char* argv[]) | |||
| 668 | return 0; | 1187 | return 0; |
| 669 | } | 1188 | } |
| 670 | 1189 | ||
| 671 | if (!StartNode(strErrors)) { fprintf(stderr,"StartNode: %s\n",strErrors.c_str()); return 1; } | 1190 | if (!StartNode(strErrors)) |
| 1191 | { | ||
| 1192 | fprintf(stderr,"StartNode: %s\n",strErrors.c_str()); | ||
| 1193 | BtfStopManagedTor(); | ||
| 1194 | return 1; | ||
| 1195 | } | ||
| 672 | 1196 | ||
| 673 | if (nMineMode == MINE_OPERATOR) { | 1197 | if (nMineMode == MINE_OPERATOR) { |
| 674 | gPoolServerRunning = true; | 1198 | gPoolServerRunning = true; |
| @@ -676,6 +1200,10 @@ int main(int argc, char* argv[]) | |||
| 676 | if (_beginthread(ThreadRPCServer, 0, NULL) == (uintptr_t)-1) | 1200 | if (_beginthread(ThreadRPCServer, 0, NULL) == (uintptr_t)-1) |
| 677 | printf("Error: _beginthread(ThreadRPCServer) failed\n"); | 1201 | printf("Error: _beginthread(ThreadRPCServer) failed\n"); |
| 678 | } | 1202 | } |
| 1203 | if (fStratumBridge) { | ||
| 1204 | if (_beginthread(ThreadStratumBridge, 0, NULL) == (uintptr_t)-1) | ||
| 1205 | printf("Error: _beginthread(ThreadStratumBridge) failed\n"); | ||
| 1206 | } | ||
| 679 | if (fGenerateBitcoins) | 1207 | if (fGenerateBitcoins) |
| 680 | StartMinerThreads(); | 1208 | StartMinerThreads(); |
| 681 | 1209 | ||
| @@ -688,18 +1216,30 @@ int main(int argc, char* argv[]) | |||
| 688 | #endif | 1216 | #endif |
| 689 | 1217 | ||
| 690 | if (fHeadless) { | 1218 | if (fHeadless) { |
| 691 | #ifndef _WIN32 | 1219 | #ifdef _WIN32 |
| 1220 | SetConsoleCtrlHandler(HeadlessConsoleCtrlHandler, TRUE); | ||
| 1221 | #else | ||
| 692 | auto sig=[](int){fShutdown=true;}; | 1222 | auto sig=[](int){fShutdown=true;}; |
| 693 | signal(SIGINT,sig); signal(SIGTERM,sig); | 1223 | signal(SIGINT,sig); signal(SIGTERM,sig); |
| 694 | #endif | 1224 | #endif |
| 695 | printf("Running headless. Ctrl-C to stop.\n"); | 1225 | printf("Running headless. Ctrl-C to stop.\n"); |
| 696 | while (!fShutdown) Sleep(500); | 1226 | while (!fShutdown) Sleep(500); |
| 697 | StopNode(); | 1227 | StopNode(); |
| 1228 | BtfStopManagedTor(); | ||
| 698 | // Checkpoints, releases wallet.dat from this directory's Berkeley DB | 1229 | // Checkpoints, releases wallet.dat from this directory's Berkeley DB |
| 699 | // environment, and closes it. Declared since 2009 and never once | 1230 | // environment, and closes it. Declared since 2009 and never once |
| 700 | // called here, which is why a wallet.dat copied elsewhere would not | 1231 | // called here, which is why a wallet.dat copied elsewhere would not |
| 701 | // open -- issue #40. Not optional. | 1232 | // open -- issue #40. Not optional. |
| 702 | DBFlush(true); | 1233 | DBFlush(true); |
| 1234 | // Checkpoint and close the SQLite wallet backend if one is active. A | ||
| 1235 | // no-op on the Berkeley DB path. Safe to kill before this runs: WAL + | ||
| 1236 | // synchronous=FULL already made every committed write durable. | ||
| 1237 | WalletSQLiteRuntimeClose(); | ||
| 1238 | #ifdef _WIN32 | ||
| 1239 | // wallet.dat is safely flushed and closed now; let a pending console | ||
| 1240 | // control handler (CLOSE/LOGOFF/SHUTDOWN) return so the OS can finish. | ||
| 1241 | g_fHeadlessShutdownDone = true; | ||
| 1242 | #endif | ||
| 703 | return 0; | 1243 | return 0; |
| 704 | } | 1244 | } |
| 705 | 1245 | ||
| @@ -709,7 +1249,9 @@ int main(int argc, char* argv[]) | |||
| 709 | int ret = RunGUI(argc, argv); | 1249 | int ret = RunGUI(argc, argv); |
| 710 | fShutdown = true; | 1250 | fShutdown = true; |
| 711 | StopNode(); | 1251 | StopNode(); |
| 1252 | BtfStopManagedTor(); | ||
| 712 | DBFlush(true); | 1253 | DBFlush(true); |
| 1254 | WalletSQLiteRuntimeClose(); | ||
| 713 | return ret; | 1255 | return ret; |
| 714 | #endif | 1256 | #endif |
| 715 | } | 1257 | } |