upleb.uk

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

summaryrefslogtreecommitdiff
path: root/src/main_gui.cpp
diff options
context:
space:
mode:
authorbitflash-maintainer <noreply@bitflash.invalid>2026-09-06 01:53:03 +0000
committerbitflash-maintainer <noreply@bitflash.invalid>2026-09-06 01:53:03 +0000
commitef9f06d43fc6577c15df2256abbb25b20d4227b8 (patch)
tree7e9132aa871fdc01cb95268d2f5ea3bb11853ddf /src/main_gui.cpp
parentbe63e83d79e7e00e6b8ca889a70e6a19886aa1d8 (diff)
parentceae3d076b5ad11ee84729ae43f14e9312896646 (diff)
Merge branch 'chore/main-catchup-2026-08-26' into 'main'HEADmain
Bring main onto the line this project actually ships (incl. the 1.2.23 compactblock port) Closes #6, #7, #8, #11, #35, #34, #29, #28, #27, and #16 See merge request !16
Diffstat (limited to 'src/main_gui.cpp')
-rw-r--r--src/main_gui.cpp596
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.)
582static volatile bool g_fHeadlessShutdownDone = false;
583static 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
371int main(int argc, char* argv[]) 602int 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}