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:
Diffstat (limited to 'src/main_gui.cpp')
-rw-r--r--src/main_gui.cpp385
1 files changed, 376 insertions, 9 deletions
diff --git a/src/main_gui.cpp b/src/main_gui.cpp
index 211e1e2..dd001c8 100644
--- a/src/main_gui.cpp
+++ b/src/main_gui.cpp
@@ -111,10 +111,16 @@ static void PrintUsage()
111 printf(" /help, -help, --help, /?\n"); 111 printf(" /help, -help, --help, /?\n");
112 printf(" /datadir=PATH\n"); 112 printf(" /datadir=PATH\n");
113 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");
114 printf(" /gen\n"); 116 printf(" /gen\n");
115 printf(" /nogui or /daemon\n"); 117 printf(" /nogui or /daemon\n");
116 printf(" /selftest=wallet-keypool, wallet-hd, wallet-format, wallet-crypto, wallet-encrypt,\n"); 118 printf(" /selftest=wallet-keypool, wallet-hd, wallet-format, wallet-storage-sanity,\n");
117 printf(" net-message, consensus-limits, pool-stratum, parse-money, socks5-proxy, or managed-tor\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, socks5-proxy, or managed-tor\n");
118 printf("\n"); 124 printf("\n");
119 printf("Mining mode:\n"); 125 printf("Mining mode:\n");
120 printf(" /operator\n"); 126 printf(" /operator\n");
@@ -175,6 +181,26 @@ static void PrintUsage()
175 printf(" /recoveryaudit (show how much spendable balance is\n"); 181 printf(" /recoveryaudit (show how much spendable balance is\n");
176 printf(" covered by the recovery phrase;\n"); 182 printf(" covered by the recovery phrase;\n");
177 printf(" exits 2 when wallet.dat is still needed)\n"); 183 printf(" exits 2 when wallet.dat is still needed)\n");
184 printf(" /walletstorageaudit (count wallet.dat record types without\n");
185 printf(" printing wallet values, then exit)\n");
186 printf(" /walletstorageauditjson=FILE\n");
187 printf(" (write the same audit as deterministic JSON)\n");
188 printf(" /walletstoragecheck (fail closed on inconsistent or unsafe\n");
189 printf(" wallet.dat storage state)\n");
190 printf(" /walletsqliteexport=FILE (copy raw wallet.dat records into an\n");
191 printf(" SQLite store, then exit)\n");
192 printf(" /walletsqliteverify=FILE (compare wallet.dat with a SQLite export,\n");
193 printf(" printing counts only, then exit)\n");
194 printf(" /walletsqliterestore=FILE (rebuild wallet.dat from a SQLite export in\n");
195 printf(" an empty data directory, then exit)\n");
196 printf(" /walletsqliteloadcheck=FILE\n");
197 printf(" (parse a SQLite export like the wallet loader)\n");
198 printf(" /walletsqlite=FILE (stage-load a SQLite wallet export, then exit)\n");
199 printf(" /walletbackend=sqlite (opt-in: run the node on <datadir>/wallet.sqlite\n");
200 printf(" instead of wallet.dat; export one first)\n");
201 printf(" /createwallet (headless only: permit creating a new wallet in a\n");
202 printf(" datadir that has none -- otherwise refused, since\n");
203 printf(" a missing wallet is usually a mount/backup fault)\n");
178 printf(" /rescan (walk the chain for coins this wallet owns\n"); 204 printf(" /rescan (walk the chain for coins this wallet owns\n");
179 printf(" but never recorded, then exit)\n"); 205 printf(" but never recorded, then exit)\n");
180 printf("\n"); 206 printf("\n");
@@ -191,6 +217,54 @@ static void ParseStartupArguments(int argc, char* argv[])
191 if (arg(argc,argv,"/debug") || arg(argc,argv,"-debug")) 217 if (arg(argc,argv,"/debug") || arg(argc,argv,"-debug"))
192 fDebug = true; 218 fDebug = true;
193 219
220 // /debugrelay -- everything /debug shows, plus the per-message firehose
221 // ("relay" and "nostrdiscovery" categories: sending: tx, Relaying wtx,
222 // raw CBlock dumps, per-peer discovery lines). That traffic was ~90% of
223 // a 13-hour production debug.log (issue #35), so plain /debug leaves it
224 // off; the message text itself is unchanged for anyone grepping it.
225 if (arg(argc,argv,"/debugrelay") || arg(argc,argv,"-debugrelay"))
226 {
227 fDebug = true;
228 fDebugRelay = true;
229 }
230
231 // /maxdebugfilesize=N -- roll debug.log to debug.log.old once it passes
232 // N MB, checked on each write. Default is 1024 (see util.cpp); 0 turns
233 // the check off and leaves rotation entirely to logrotate.
234 string strMaxDebugSize = argval2(argc, argv, "/maxdebugfilesize", "-maxdebugfilesize");
235 if (!strMaxDebugSize.empty())
236 {
237 // Parsed with atoi64() before, which is strtoll() with the endptr
238 // thrown away: it returns 0 for anything it cannot read, so
239 // /maxdebugfilesize=1G and =1024MB both came out as 0 -- "never
240 // roll", identical to asking for that on purpose, with nothing said
241 // to the operator. The old code also multiplied without a range
242 // check, so a big enough N wrapped to a negative cap, which the roll
243 // reads as "off" too. Every rejection is now reported and leaves the
244 // default in place; the value is a plain count of MB, no suffix.
245 const char* psz = strMaxDebugSize.c_str();
246 char* pszEnd = NULL;
247 errno = 0;
248 int64 nMB = strtoll(psz, &pszEnd, 10);
249 // INT64_MAX, without depending on which limits header this tree pulls in.
250 const int64 nMaxMB = (int64)((~(uint64)0) >> 1) / (1024 * 1024);
251 const char* pszWhy = NULL;
252 if (pszEnd == psz)
253 pszWhy = "not a number";
254 else if (*pszEnd != '\0')
255 pszWhy = "trailing characters -- the value is a count of MB, with no unit suffix";
256 else if (errno == ERANGE || nMB < 0)
257 pszWhy = "not a size in MB";
258 else if (nMB > nMaxMB)
259 pszWhy = "larger than a file size can be";
260 if (pszWhy)
261 fprintf(stderr, "Ignoring /maxdebugfilesize=%s: %s (keeping %lld MB)\n",
262 strMaxDebugSize.c_str(), pszWhy,
263 (long long)(nMaxDebugFileSize / 1024 / 1024));
264 else
265 nMaxDebugFileSize = nMB * 1024 * 1024;
266 }
267
194 if (arg(argc,argv,"/gen") || arg(argc,argv,"-gen")) 268 if (arg(argc,argv,"/gen") || arg(argc,argv,"-gen"))
195 { 269 {
196 fGenerateBitcoins = 1; 270 fGenerateBitcoins = 1;
@@ -530,10 +604,119 @@ int main(int argc, char* argv[])
530 604
531 ParseStartupArguments(argc, argv); 605 ParseStartupArguments(argc, argv);
532 606
607#ifdef _WIN32
608 // Set by the GUI "Restart Now" button: give the previous instance a moment
609 // to release the network port and the block-index files before this one
610 // opens them, so a relaunch does not race the process it is replacing.
611 if (arg(argc, argv, "/restartwait") || arg(argc, argv, "-restartwait"))
612 Sleep(2500);
613#endif
614
615 // Hidden self-test helper. It must run before LoadWallet(): several
616 // storage-sanity scenarios deliberately make wallet.dat unsafe to load.
617 string strSelfTestMutateWallet =
618 argval2(argc, argv, "/selftestmutatewallet", "-selftestmutatewallet");
619 if (!strSelfTestMutateWallet.empty())
620 return RunSelfTestMutateWallet(strSelfTestMutateWallet);
621
533 string strSelfTest = argval2(argc, argv, "/selftest", "-selftest"); 622 string strSelfTest = argval2(argc, argv, "/selftest", "-selftest");
534 if (!strSelfTest.empty()) 623 if (!strSelfTest.empty())
535 return RunSelfTest(strSelfTest); 624 return RunSelfTest(strSelfTest);
536 625
626 // Storage diagnostics must run before LoadWallet(). Their job is to
627 // explain wallet.dat states that the normal wallet loader may refuse.
628 string strWalletStorageAuditJson =
629 argval2(argc, argv, "/walletstorageauditjson", "-walletstorageauditjson");
630 bool fDiagWantsAudit = arg(argc,argv,"/walletstorageaudit") ||
631 arg(argc,argv,"-walletstorageaudit") ||
632 !strWalletStorageAuditJson.empty();
633 bool fDiagWantsCheck = arg(argc,argv,"/walletstoragecheck") ||
634 arg(argc,argv,"-walletstoragecheck");
635
636 // These diagnostics normally read wallet.dat. Under -walletbackend=sqlite
637 // the live wallet is wallet.sqlite, so open it read-only as the active
638 // store and let the same scan walk the right file. This runs before the
639 // normal backend resolution further down, so validate the value here too.
640 string strDiagBackend = argval2(argc, argv, "/walletbackend", "-walletbackend");
641 if ((fDiagWantsAudit || fDiagWantsCheck) && !strDiagBackend.empty() &&
642 strDiagBackend != "sqlite" && strDiagBackend != "bdb")
643 {
644 AttachTerminal();
645 fprintf(stderr, "Unknown wallet backend '%s'. Use -walletbackend=sqlite "
646 "or -walletbackend=bdb.\n", strDiagBackend.c_str());
647 return 1;
648 }
649 bool fDiagSQLite = (strDiagBackend == "sqlite") &&
650 (fDiagWantsAudit || fDiagWantsCheck);
651 if (fDiagSQLite)
652 {
653 AttachTerminal();
654 string strSQLitePath = GetAppDir() + "/wallet.sqlite";
655 string strOpenErr;
656 if (!WalletSQLiteRuntimeOpenReadOnly(strSQLitePath, strOpenErr))
657 {
658 fprintf(stderr, "Cannot open the SQLite wallet for diagnostics: %s\n",
659 strOpenErr.c_str());
660 return 1;
661 }
662 }
663
664 if (fDiagWantsAudit)
665 {
666 int nRet = CmdWalletStorageAudit(strWalletStorageAuditJson);
667 if (fDiagSQLite)
668 WalletSQLiteRuntimeClose();
669 else
670 DBFlush(true);
671 return nRet;
672 }
673
674 if (fDiagWantsCheck)
675 {
676 int nRet = CmdWalletStorageCheck();
677 if (fDiagSQLite)
678 WalletSQLiteRuntimeClose();
679 else
680 DBFlush(true);
681 return nRet;
682 }
683
684 string strWalletSQLiteExport =
685 argval2(argc, argv, "/walletsqliteexport", "-walletsqliteexport");
686 if (!strWalletSQLiteExport.empty())
687 {
688 int nRet = CmdWalletSQLiteExport(strWalletSQLiteExport);
689 DBFlush(true);
690 return nRet;
691 }
692
693 string strWalletSQLiteVerify =
694 argval2(argc, argv, "/walletsqliteverify", "-walletsqliteverify");
695 if (!strWalletSQLiteVerify.empty())
696 {
697 int nRet = CmdWalletSQLiteVerify(strWalletSQLiteVerify);
698 DBFlush(true);
699 return nRet;
700 }
701
702 string strWalletSQLiteRestore =
703 argval2(argc, argv, "/walletsqliterestore", "-walletsqliterestore");
704 if (!strWalletSQLiteRestore.empty())
705 {
706 int nRet = CmdWalletSQLiteRestore(strWalletSQLiteRestore);
707 DBFlush(true);
708 return nRet;
709 }
710
711 string strWalletSQLiteLoadCheck =
712 argval2(argc, argv, "/walletsqliteloadcheck", "-walletsqliteloadcheck");
713 if (!strWalletSQLiteLoadCheck.empty())
714 {
715 int nRet = CmdWalletSQLiteLoadCheck(strWalletSQLiteLoadCheck);
716 DBFlush(true);
717 return nRet;
718 }
719
537 // Berkeley DB reports failure by throwing, and CDB's constructor lets it 720 // Berkeley DB reports failure by throwing, and CDB's constructor lets it
538 // through. Nothing on this path caught anything, so an unreadable 721 // through. Nothing on this path caught anything, so an unreadable
539 // blkindex.dat or wallet.dat -- one written by another platform's Berkeley 722 // blkindex.dat or wallet.dat -- one written by another platform's Berkeley
@@ -578,18 +761,197 @@ int main(int argc, char* argv[])
578 printf("Loading wallet...\n"); 761 printf("Loading wallet...\n");
579 try 762 try
580 { 763 {
581 if (!LoadWallet()) 764 string strWalletBackend =
765 argval2(argc, argv, "/walletbackend", "-walletbackend");
766 string strWalletSQLite =
767 argval2(argc, argv, "/walletsqlite", "-walletsqlite");
768
769 // A misspelled backend must not fall through to Berkeley DB silently --
770 // someone who typed -walletbackend=sqlit meant to run on SQLite, and
771 // opening wallet.dat instead of telling them is the wrong surprise.
772 if (!strWalletBackend.empty() &&
773 strWalletBackend != "sqlite" && strWalletBackend != "bdb")
582 { 774 {
583 // LoadWallet() explains itself into strWalletLoadError when it 775 AttachTerminal();
584 // knows why -- an unsupported wallet format, for one, which is the 776 FatalStartupError(fHeadlessStartup,
585 // case this whole path exists to make visible. 777 strprintf("Unknown wallet backend '%s'.", strWalletBackend.c_str()),
586 FatalStartupError(fHeadlessStartup, "Cannot open wallet.dat.", 778 "Use -walletbackend=sqlite for the SQLite backend, or "
587 strWalletLoadError); 779 "-walletbackend=bdb for the Berkeley DB wallet.dat.");
588 return 1; 780 return 1;
589 } 781 }
590 782
783 if (strWalletBackend == "sqlite")
784 {
785 // Explicit opt-in via the flag. The node runs on
786 // <datadir>/wallet.sqlite and never opens wallet.dat, but the user
787 // has to have exported one first -- there is no silent migration,
788 // and wallet.dat is left
789 // exactly where it is so a plain restart goes back to it.
790 AttachTerminal();
791 string strSQLitePath = GetAppDir() + "/wallet.sqlite";
792 if (!FileExists(strSQLitePath.c_str()))
793 {
794 string strGuide = strprintf(
795 "No SQLite wallet was found at\n"
796 " %s\n\n"
797 "The SQLite wallet backend is opt-in and needs an exported\n"
798 "wallet first. Your Berkeley DB wallet.dat is not touched by\n"
799 "any of this.\n\n"
800 " Step 1 Export your existing wallet to SQLite:\n"
801 " bitflash -walletsqliteexport=\"%s\"\n"
802 " Step 2 Start again with the SQLite backend:\n"
803 " bitflash -walletbackend=sqlite\n\n"
804 "To go back to Berkeley DB at any time, just start without\n"
805 "-walletbackend=sqlite. wallet.dat is still your wallet.",
806 strSQLitePath.c_str(), strSQLitePath.c_str());
807 FatalStartupError(fHeadlessStartup,
808 "The SQLite wallet backend needs an exported wallet first.",
809 strGuide);
810 return 1;
811 }
812
813 if (!LoadWalletFromSQLiteRuntime(strSQLitePath))
814 {
815 FatalStartupError(fHeadlessStartup,
816 "Cannot open the SQLite wallet backend.",
817 strWalletLoadError);
818 return 1;
819 }
820
821 fprintf(stderr,
822 "Wallet backend: SQLite\n"
823 " %s\n"
824 "Your Berkeley DB wallet.dat is left untouched. Restart without\n"
825 "-walletbackend=sqlite to go back to it.\n",
826 strSQLitePath.c_str());
827 fflush(stderr);
828 // Fall through and run the node normally: every CWalletDB read,
829 // write, and erase now routes to this SQLite file.
830 }
831 else if (!strWalletSQLite.empty())
832 {
833 AttachTerminal();
834 if (!LoadWalletFromSQLite(strWalletSQLite))
835 {
836 FatalStartupError(fHeadlessStartup,
837 "Cannot open SQLite wallet export.",
838 strWalletLoadError);
839 return 1;
840 }
841
842 fprintf(stderr, "SQLite wallet loaded: read-only staging backend\n");
843 fprintf(stderr, "SQLite runtime writes are not enabled yet; exiting before "
844 "network, mining, GUI, or wallet mutation starts.\n");
845 fflush(stderr);
846 DBFlush(true);
847 return 0;
848 }
849 else if (strWalletBackend == "bdb")
850 {
851 // Explicit -walletbackend=bdb overrides the marker and the default:
852 // force Berkeley DB (LoadWallet creates wallet.dat if none exists).
853 if (!LoadWallet())
854 {
855 FatalStartupError(fHeadlessStartup, "Cannot open wallet.dat.",
856 strWalletLoadError);
857 return 1;
858 }
859 }
860 else
861 {
862 // No explicit -walletbackend on the command line. Resolve it from
863 // the persistent marker, then defaults: an existing wallet.sqlite
864 // stays on SQLite, an existing wallet.dat stays on Berkeley DB (the
865 // GUI offers to convert), and a fresh install starts on SQLite --
866 // the new default. wallet.dat is never touched by any of this.
867 string strSQLitePath = GetAppDir() + "/wallet.sqlite";
868 bool fSQLiteExists = FileExists(strSQLitePath.c_str());
869 bool fWalletDatExists = FileExists((GetAppDir() + "/wallet.dat").c_str());
870 string strMarker = ReadWalletBackendMarker();
871
872 bool fUseSQLite = false;
873 bool fCreateSQLite = false;
874 if (strMarker == "sqlite")
875 {
876 if (fSQLiteExists) fUseSQLite = true;
877 else if (fWalletDatExists) fUseSQLite = false; // marker stale; real wallet is BDB
878 else { fUseSQLite = true; fCreateSQLite = true; }
879 }
880 else if (strMarker == "bdb")
881 {
882 fUseSQLite = false;
883 }
884 else // no marker recorded yet
885 {
886 if (fSQLiteExists && !fWalletDatExists) fUseSQLite = true;
887 else if (fWalletDatExists) fUseSQLite = false;
888 else { fUseSQLite = true; fCreateSQLite = true; }
889 }
890
891 // A headless node whose datadir holds no wallet of either kind is
892 // far more often an operations fault -- datadir not mounted, or
893 // wallet.dat caught mid-rename by a backup -- than a first run.
894 // Creating one here is not the harmless default it looks like:
895 // success also writes the backend marker, and from then on
896 // restoring the real wallet.dat no longer takes effect, because the
897 // marker keeps the node on the empty wallet it just made. Under
898 // 1.2.19 the same fault created a wallet.dat that a restore
899 // overwrote, so operators could recover by putting the file back.
900 // Refuse instead, and name the flag that makes it deliberate.
901 if (fCreateSQLite && fHeadlessStartup &&
902 !arg(argc, argv, "/createwallet") && !arg(argc, argv, "-createwallet"))
903 {
904 AttachTerminal();
905 fprintf(stderr,
906 "No wallet found in %s\n"
907 "Refusing to create one. On a headless node an absent wallet is\n"
908 "usually a mount or backup fault, and creating one would record a\n"
909 "backend marker that makes restoring the real wallet a no-op.\n"
910 "Pass -createwallet to start a new one deliberately, or\n"
911 "-walletbackend=bdb to run on a Berkeley DB wallet.dat.\n",
912 GetAppDir().c_str());
913 fflush(stderr);
914 return 1;
915 }
916
917 if (fUseSQLite)
918 {
919 AttachTerminal();
920 bool fOk = fCreateSQLite
921 ? CreateNewSQLiteWallet(strSQLitePath)
922 : LoadWalletFromSQLiteRuntime(strSQLitePath);
923 if (!fOk)
924 {
925 FatalStartupError(fHeadlessStartup,
926 fCreateSQLite ? "Cannot create the SQLite wallet."
927 : "Cannot open the SQLite wallet.",
928 strWalletLoadError);
929 return 1;
930 }
931 // Record the choice so it is stable from here on.
932 if (ReadWalletBackendMarker() != "sqlite")
933 WriteWalletBackendMarker("sqlite");
934 fprintf(stderr, "Wallet backend: SQLite\n %s\n%s",
935 strSQLitePath.c_str(),
936 fCreateSQLite ? "A new wallet was created here.\n" : "");
937 fflush(stderr);
938 // Fall through and run the node; CWalletDB routes to this file.
939 }
940 else if (!LoadWallet())
941 {
942 // LoadWallet() explains itself into strWalletLoadError when it
943 // knows why -- an unsupported wallet format, for one, which is
944 // the case this whole path exists to make visible.
945 FatalStartupError(fHeadlessStartup, "Cannot open wallet.dat.",
946 strWalletLoadError);
947 return 1;
948 }
949 }
950
591 // After the block index, so there is a chain to compare the wallet 951 // After the block index, so there is a chain to compare the wallet
592 // against, and before anything reports a balance. 952 // against, and before anything reports a balance. Works on either
953 // backend: RescanSpentFlags writes corrected flags through CWalletDB,
954 // which routes to SQLite when that backend is active.
593 RescanSpentFlags(); 955 RescanSpentFlags();
594 } 956 }
595 catch (const std::exception& e) 957 catch (const std::exception& e)
@@ -858,6 +1220,10 @@ int main(int argc, char* argv[])
858 // called here, which is why a wallet.dat copied elsewhere would not 1220 // called here, which is why a wallet.dat copied elsewhere would not
859 // open -- issue #40. Not optional. 1221 // open -- issue #40. Not optional.
860 DBFlush(true); 1222 DBFlush(true);
1223 // Checkpoint and close the SQLite wallet backend if one is active. A
1224 // no-op on the Berkeley DB path. Safe to kill before this runs: WAL +
1225 // synchronous=FULL already made every committed write durable.
1226 WalletSQLiteRuntimeClose();
861#ifdef _WIN32 1227#ifdef _WIN32
862 // wallet.dat is safely flushed and closed now; let a pending console 1228 // wallet.dat is safely flushed and closed now; let a pending console
863 // control handler (CLOSE/LOGOFF/SHUTDOWN) return so the OS can finish. 1229 // control handler (CLOSE/LOGOFF/SHUTDOWN) return so the OS can finish.
@@ -874,6 +1240,7 @@ int main(int argc, char* argv[])
874 StopNode(); 1240 StopNode();
875 BtfStopManagedTor(); 1241 BtfStopManagedTor();
876 DBFlush(true); 1242 DBFlush(true);
1243 WalletSQLiteRuntimeClose();
877 return ret; 1244 return ret;
878#endif 1245#endif
879} 1246}