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.cpp306
1 files changed, 297 insertions, 9 deletions
diff --git a/src/main_gui.cpp b/src/main_gui.cpp
index 211e1e2..2743429 100644
--- a/src/main_gui.cpp
+++ b/src/main_gui.cpp
@@ -113,8 +113,12 @@ static void PrintUsage()
113 printf(" /debug\n"); 113 printf(" /debug\n");
114 printf(" /gen\n"); 114 printf(" /gen\n");
115 printf(" /nogui or /daemon\n"); 115 printf(" /nogui or /daemon\n");
116 printf(" /selftest=wallet-keypool, wallet-hd, wallet-format, wallet-crypto, wallet-encrypt,\n"); 116 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"); 117 printf(" db-env-reopen, wallet-sqlite, wallet-sqlite-migration,\n");
118 printf(" wallet-crypto, wallet-encrypt, wallet-portability,\n");
119 printf(" net-message,\n");
120 printf(" consensus-limits, pool-stratum,\n");
121 printf(" parse-money, socks5-proxy, or managed-tor\n");
118 printf("\n"); 122 printf("\n");
119 printf("Mining mode:\n"); 123 printf("Mining mode:\n");
120 printf(" /operator\n"); 124 printf(" /operator\n");
@@ -175,6 +179,23 @@ static void PrintUsage()
175 printf(" /recoveryaudit (show how much spendable balance is\n"); 179 printf(" /recoveryaudit (show how much spendable balance is\n");
176 printf(" covered by the recovery phrase;\n"); 180 printf(" covered by the recovery phrase;\n");
177 printf(" exits 2 when wallet.dat is still needed)\n"); 181 printf(" exits 2 when wallet.dat is still needed)\n");
182 printf(" /walletstorageaudit (count wallet.dat record types without\n");
183 printf(" printing wallet values, then exit)\n");
184 printf(" /walletstorageauditjson=FILE\n");
185 printf(" (write the same audit as deterministic JSON)\n");
186 printf(" /walletstoragecheck (fail closed on inconsistent or unsafe\n");
187 printf(" wallet.dat storage state)\n");
188 printf(" /walletsqliteexport=FILE (copy raw wallet.dat records into an\n");
189 printf(" SQLite store, then exit)\n");
190 printf(" /walletsqliteverify=FILE (compare wallet.dat with a SQLite export,\n");
191 printf(" printing counts only, then exit)\n");
192 printf(" /walletsqliterestore=FILE (rebuild wallet.dat from a SQLite export in\n");
193 printf(" an empty data directory, then exit)\n");
194 printf(" /walletsqliteloadcheck=FILE\n");
195 printf(" (parse a SQLite export like the wallet loader)\n");
196 printf(" /walletsqlite=FILE (stage-load a SQLite wallet export, then exit)\n");
197 printf(" /walletbackend=sqlite (opt-in: run the node on <datadir>/wallet.sqlite\n");
198 printf(" instead of wallet.dat; export one first)\n");
178 printf(" /rescan (walk the chain for coins this wallet owns\n"); 199 printf(" /rescan (walk the chain for coins this wallet owns\n");
179 printf(" but never recorded, then exit)\n"); 200 printf(" but never recorded, then exit)\n");
180 printf("\n"); 201 printf("\n");
@@ -530,10 +551,119 @@ int main(int argc, char* argv[])
530 551
531 ParseStartupArguments(argc, argv); 552 ParseStartupArguments(argc, argv);
532 553
554#ifdef _WIN32
555 // Set by the GUI "Restart Now" button: give the previous instance a moment
556 // to release the network port and the block-index files before this one
557 // opens them, so a relaunch does not race the process it is replacing.
558 if (arg(argc, argv, "/restartwait") || arg(argc, argv, "-restartwait"))
559 Sleep(2500);
560#endif
561
562 // Hidden self-test helper. It must run before LoadWallet(): several
563 // storage-sanity scenarios deliberately make wallet.dat unsafe to load.
564 string strSelfTestMutateWallet =
565 argval2(argc, argv, "/selftestmutatewallet", "-selftestmutatewallet");
566 if (!strSelfTestMutateWallet.empty())
567 return RunSelfTestMutateWallet(strSelfTestMutateWallet);
568
533 string strSelfTest = argval2(argc, argv, "/selftest", "-selftest"); 569 string strSelfTest = argval2(argc, argv, "/selftest", "-selftest");
534 if (!strSelfTest.empty()) 570 if (!strSelfTest.empty())
535 return RunSelfTest(strSelfTest); 571 return RunSelfTest(strSelfTest);
536 572
573 // Storage diagnostics must run before LoadWallet(). Their job is to
574 // explain wallet.dat states that the normal wallet loader may refuse.
575 string strWalletStorageAuditJson =
576 argval2(argc, argv, "/walletstorageauditjson", "-walletstorageauditjson");
577 bool fDiagWantsAudit = arg(argc,argv,"/walletstorageaudit") ||
578 arg(argc,argv,"-walletstorageaudit") ||
579 !strWalletStorageAuditJson.empty();
580 bool fDiagWantsCheck = arg(argc,argv,"/walletstoragecheck") ||
581 arg(argc,argv,"-walletstoragecheck");
582
583 // These diagnostics normally read wallet.dat. Under -walletbackend=sqlite
584 // the live wallet is wallet.sqlite, so open it read-only as the active
585 // store and let the same scan walk the right file. This runs before the
586 // normal backend resolution further down, so validate the value here too.
587 string strDiagBackend = argval2(argc, argv, "/walletbackend", "-walletbackend");
588 if ((fDiagWantsAudit || fDiagWantsCheck) && !strDiagBackend.empty() &&
589 strDiagBackend != "sqlite" && strDiagBackend != "bdb")
590 {
591 AttachTerminal();
592 fprintf(stderr, "Unknown wallet backend '%s'. Use -walletbackend=sqlite "
593 "or -walletbackend=bdb.\n", strDiagBackend.c_str());
594 return 1;
595 }
596 bool fDiagSQLite = (strDiagBackend == "sqlite") &&
597 (fDiagWantsAudit || fDiagWantsCheck);
598 if (fDiagSQLite)
599 {
600 AttachTerminal();
601 string strSQLitePath = GetAppDir() + "/wallet.sqlite";
602 string strOpenErr;
603 if (!WalletSQLiteRuntimeOpenReadOnly(strSQLitePath, strOpenErr))
604 {
605 fprintf(stderr, "Cannot open the SQLite wallet for diagnostics: %s\n",
606 strOpenErr.c_str());
607 return 1;
608 }
609 }
610
611 if (fDiagWantsAudit)
612 {
613 int nRet = CmdWalletStorageAudit(strWalletStorageAuditJson);
614 if (fDiagSQLite)
615 WalletSQLiteRuntimeClose();
616 else
617 DBFlush(true);
618 return nRet;
619 }
620
621 if (fDiagWantsCheck)
622 {
623 int nRet = CmdWalletStorageCheck();
624 if (fDiagSQLite)
625 WalletSQLiteRuntimeClose();
626 else
627 DBFlush(true);
628 return nRet;
629 }
630
631 string strWalletSQLiteExport =
632 argval2(argc, argv, "/walletsqliteexport", "-walletsqliteexport");
633 if (!strWalletSQLiteExport.empty())
634 {
635 int nRet = CmdWalletSQLiteExport(strWalletSQLiteExport);
636 DBFlush(true);
637 return nRet;
638 }
639
640 string strWalletSQLiteVerify =
641 argval2(argc, argv, "/walletsqliteverify", "-walletsqliteverify");
642 if (!strWalletSQLiteVerify.empty())
643 {
644 int nRet = CmdWalletSQLiteVerify(strWalletSQLiteVerify);
645 DBFlush(true);
646 return nRet;
647 }
648
649 string strWalletSQLiteRestore =
650 argval2(argc, argv, "/walletsqliterestore", "-walletsqliterestore");
651 if (!strWalletSQLiteRestore.empty())
652 {
653 int nRet = CmdWalletSQLiteRestore(strWalletSQLiteRestore);
654 DBFlush(true);
655 return nRet;
656 }
657
658 string strWalletSQLiteLoadCheck =
659 argval2(argc, argv, "/walletsqliteloadcheck", "-walletsqliteloadcheck");
660 if (!strWalletSQLiteLoadCheck.empty())
661 {
662 int nRet = CmdWalletSQLiteLoadCheck(strWalletSQLiteLoadCheck);
663 DBFlush(true);
664 return nRet;
665 }
666
537 // Berkeley DB reports failure by throwing, and CDB's constructor lets it 667 // Berkeley DB reports failure by throwing, and CDB's constructor lets it
538 // through. Nothing on this path caught anything, so an unreadable 668 // through. Nothing on this path caught anything, so an unreadable
539 // blkindex.dat or wallet.dat -- one written by another platform's Berkeley 669 // blkindex.dat or wallet.dat -- one written by another platform's Berkeley
@@ -578,18 +708,171 @@ int main(int argc, char* argv[])
578 printf("Loading wallet...\n"); 708 printf("Loading wallet...\n");
579 try 709 try
580 { 710 {
581 if (!LoadWallet()) 711 string strWalletBackend =
712 argval2(argc, argv, "/walletbackend", "-walletbackend");
713 string strWalletSQLite =
714 argval2(argc, argv, "/walletsqlite", "-walletsqlite");
715
716 // A misspelled backend must not fall through to Berkeley DB silently --
717 // someone who typed -walletbackend=sqlit meant to run on SQLite, and
718 // opening wallet.dat instead of telling them is the wrong surprise.
719 if (!strWalletBackend.empty() &&
720 strWalletBackend != "sqlite" && strWalletBackend != "bdb")
582 { 721 {
583 // LoadWallet() explains itself into strWalletLoadError when it 722 AttachTerminal();
584 // knows why -- an unsupported wallet format, for one, which is the 723 FatalStartupError(fHeadlessStartup,
585 // case this whole path exists to make visible. 724 strprintf("Unknown wallet backend '%s'.", strWalletBackend.c_str()),
586 FatalStartupError(fHeadlessStartup, "Cannot open wallet.dat.", 725 "Use -walletbackend=sqlite for the SQLite backend, or "
587 strWalletLoadError); 726 "-walletbackend=bdb for the Berkeley DB wallet.dat.");
588 return 1; 727 return 1;
589 } 728 }
590 729
730 if (strWalletBackend == "sqlite")
731 {
732 // Explicit opt-in via the flag. The node runs on
733 // <datadir>/wallet.sqlite and never opens wallet.dat, but the user
734 // has to have exported one first -- there is no silent migration,
735 // and wallet.dat is left
736 // exactly where it is so a plain restart goes back to it.
737 AttachTerminal();
738 string strSQLitePath = GetAppDir() + "/wallet.sqlite";
739 if (!FileExists(strSQLitePath.c_str()))
740 {
741 string strGuide = strprintf(
742 "No SQLite wallet was found at\n"
743 " %s\n\n"
744 "The SQLite wallet backend is opt-in and needs an exported\n"
745 "wallet first. Your Berkeley DB wallet.dat is not touched by\n"
746 "any of this.\n\n"
747 " Step 1 Export your existing wallet to SQLite:\n"
748 " bitflash -walletsqliteexport=\"%s\"\n"
749 " Step 2 Start again with the SQLite backend:\n"
750 " bitflash -walletbackend=sqlite\n\n"
751 "To go back to Berkeley DB at any time, just start without\n"
752 "-walletbackend=sqlite. wallet.dat is still your wallet.",
753 strSQLitePath.c_str(), strSQLitePath.c_str());
754 FatalStartupError(fHeadlessStartup,
755 "The SQLite wallet backend needs an exported wallet first.",
756 strGuide);
757 return 1;
758 }
759
760 if (!LoadWalletFromSQLiteRuntime(strSQLitePath))
761 {
762 FatalStartupError(fHeadlessStartup,
763 "Cannot open the SQLite wallet backend.",
764 strWalletLoadError);
765 return 1;
766 }
767
768 fprintf(stderr,
769 "Wallet backend: SQLite\n"
770 " %s\n"
771 "Your Berkeley DB wallet.dat is left untouched. Restart without\n"
772 "-walletbackend=sqlite to go back to it.\n",
773 strSQLitePath.c_str());
774 fflush(stderr);
775 // Fall through and run the node normally: every CWalletDB read,
776 // write, and erase now routes to this SQLite file.
777 }
778 else if (!strWalletSQLite.empty())
779 {
780 AttachTerminal();
781 if (!LoadWalletFromSQLite(strWalletSQLite))
782 {
783 FatalStartupError(fHeadlessStartup,
784 "Cannot open SQLite wallet export.",
785 strWalletLoadError);
786 return 1;
787 }
788
789 fprintf(stderr, "SQLite wallet loaded: read-only staging backend\n");
790 fprintf(stderr, "SQLite runtime writes are not enabled yet; exiting before "
791 "network, mining, GUI, or wallet mutation starts.\n");
792 fflush(stderr);
793 DBFlush(true);
794 return 0;
795 }
796 else if (strWalletBackend == "bdb")
797 {
798 // Explicit -walletbackend=bdb overrides the marker and the default:
799 // force Berkeley DB (LoadWallet creates wallet.dat if none exists).
800 if (!LoadWallet())
801 {
802 FatalStartupError(fHeadlessStartup, "Cannot open wallet.dat.",
803 strWalletLoadError);
804 return 1;
805 }
806 }
807 else
808 {
809 // No explicit -walletbackend on the command line. Resolve it from
810 // the persistent marker, then defaults: an existing wallet.sqlite
811 // stays on SQLite, an existing wallet.dat stays on Berkeley DB (the
812 // GUI offers to convert), and a fresh install starts on SQLite --
813 // the new default. wallet.dat is never touched by any of this.
814 string strSQLitePath = GetAppDir() + "/wallet.sqlite";
815 bool fSQLiteExists = FileExists(strSQLitePath.c_str());
816 bool fWalletDatExists = FileExists((GetAppDir() + "/wallet.dat").c_str());
817 string strMarker = ReadWalletBackendMarker();
818
819 bool fUseSQLite = false;
820 bool fCreateSQLite = false;
821 if (strMarker == "sqlite")
822 {
823 if (fSQLiteExists) fUseSQLite = true;
824 else if (fWalletDatExists) fUseSQLite = false; // marker stale; real wallet is BDB
825 else { fUseSQLite = true; fCreateSQLite = true; }
826 }
827 else if (strMarker == "bdb")
828 {
829 fUseSQLite = false;
830 }
831 else // no marker recorded yet
832 {
833 if (fSQLiteExists && !fWalletDatExists) fUseSQLite = true;
834 else if (fWalletDatExists) fUseSQLite = false;
835 else { fUseSQLite = true; fCreateSQLite = true; }
836 }
837
838 if (fUseSQLite)
839 {
840 AttachTerminal();
841 bool fOk = fCreateSQLite
842 ? CreateNewSQLiteWallet(strSQLitePath)
843 : LoadWalletFromSQLiteRuntime(strSQLitePath);
844 if (!fOk)
845 {
846 FatalStartupError(fHeadlessStartup,
847 fCreateSQLite ? "Cannot create the SQLite wallet."
848 : "Cannot open the SQLite wallet.",
849 strWalletLoadError);
850 return 1;
851 }
852 // Record the choice so it is stable from here on.
853 if (ReadWalletBackendMarker() != "sqlite")
854 WriteWalletBackendMarker("sqlite");
855 fprintf(stderr, "Wallet backend: SQLite\n %s\n%s",
856 strSQLitePath.c_str(),
857 fCreateSQLite ? "A new wallet was created here.\n" : "");
858 fflush(stderr);
859 // Fall through and run the node; CWalletDB routes to this file.
860 }
861 else if (!LoadWallet())
862 {
863 // LoadWallet() explains itself into strWalletLoadError when it
864 // knows why -- an unsupported wallet format, for one, which is
865 // the case this whole path exists to make visible.
866 FatalStartupError(fHeadlessStartup, "Cannot open wallet.dat.",
867 strWalletLoadError);
868 return 1;
869 }
870 }
871
591 // After the block index, so there is a chain to compare the wallet 872 // After the block index, so there is a chain to compare the wallet
592 // against, and before anything reports a balance. 873 // against, and before anything reports a balance. Works on either
874 // backend: RescanSpentFlags writes corrected flags through CWalletDB,
875 // which routes to SQLite when that backend is active.
593 RescanSpentFlags(); 876 RescanSpentFlags();
594 } 877 }
595 catch (const std::exception& e) 878 catch (const std::exception& e)
@@ -858,6 +1141,10 @@ int main(int argc, char* argv[])
858 // called here, which is why a wallet.dat copied elsewhere would not 1141 // called here, which is why a wallet.dat copied elsewhere would not
859 // open -- issue #40. Not optional. 1142 // open -- issue #40. Not optional.
860 DBFlush(true); 1143 DBFlush(true);
1144 // Checkpoint and close the SQLite wallet backend if one is active. A
1145 // no-op on the Berkeley DB path. Safe to kill before this runs: WAL +
1146 // synchronous=FULL already made every committed write durable.
1147 WalletSQLiteRuntimeClose();
861#ifdef _WIN32 1148#ifdef _WIN32
862 // wallet.dat is safely flushed and closed now; let a pending console 1149 // wallet.dat is safely flushed and closed now; let a pending console
863 // control handler (CLOSE/LOGOFF/SHUTDOWN) return so the OS can finish. 1150 // control handler (CLOSE/LOGOFF/SHUTDOWN) return so the OS can finish.
@@ -874,6 +1161,7 @@ int main(int argc, char* argv[])
874 StopNode(); 1161 StopNode();
875 BtfStopManagedTor(); 1162 BtfStopManagedTor();
876 DBFlush(true); 1163 DBFlush(true);
1164 WalletSQLiteRuntimeClose();
877 return ret; 1165 return ret;
878#endif 1166#endif
879} 1167}