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-03 22:33:20 +0000
committerbitflash-maintainer <noreply@bitflash.invalid>2026-09-03 22:33:20 +0000
commitb810d86752ddb2674118f737582c924bb9eb74c1 (patch)
tree1ceadf421609a2e58da81103165a2fb7085387de /src/main_gui.cpp
parentdb44eac5d347c229539946e8d61f1ff0d1f68001 (diff)
parent5ef59833756da6f47274cb990eb874fb0ed99019 (diff)
merge: upstream 1.2.20 into deploy/all-2026-08-24
# Conflicts: # Makefile
Diffstat (limited to 'src/main_gui.cpp')
-rw-r--r--src/main_gui.cpp335
1 files changed, 326 insertions, 9 deletions
diff --git a/src/main_gui.cpp b/src/main_gui.cpp
index 0e64985..dd001c8 100644
--- a/src/main_gui.cpp
+++ b/src/main_gui.cpp
@@ -115,8 +115,12 @@ static void PrintUsage()
115 printf(" /maxdebugfilesize=N (roll debug.log at N MB, default 1024; 0 = never)\n"); 115 printf(" /maxdebugfilesize=N (roll debug.log at N MB, default 1024; 0 = never)\n");
116 printf(" /gen\n"); 116 printf(" /gen\n");
117 printf(" /nogui or /daemon\n"); 117 printf(" /nogui or /daemon\n");
118 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");
119 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");
120 printf("\n"); 124 printf("\n");
121 printf("Mining mode:\n"); 125 printf("Mining mode:\n");
122 printf(" /operator\n"); 126 printf(" /operator\n");
@@ -177,6 +181,26 @@ static void PrintUsage()
177 printf(" /recoveryaudit (show how much spendable balance is\n"); 181 printf(" /recoveryaudit (show how much spendable balance is\n");
178 printf(" covered by the recovery phrase;\n"); 182 printf(" covered by the recovery phrase;\n");
179 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");
180 printf(" /rescan (walk the chain for coins this wallet owns\n"); 204 printf(" /rescan (walk the chain for coins this wallet owns\n");
181 printf(" but never recorded, then exit)\n"); 205 printf(" but never recorded, then exit)\n");
182 printf("\n"); 206 printf("\n");
@@ -580,10 +604,119 @@ int main(int argc, char* argv[])
580 604
581 ParseStartupArguments(argc, argv); 605 ParseStartupArguments(argc, argv);
582 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
583 string strSelfTest = argval2(argc, argv, "/selftest", "-selftest"); 622 string strSelfTest = argval2(argc, argv, "/selftest", "-selftest");
584 if (!strSelfTest.empty()) 623 if (!strSelfTest.empty())
585 return RunSelfTest(strSelfTest); 624 return RunSelfTest(strSelfTest);
586 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
587 // 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
588 // through. Nothing on this path caught anything, so an unreadable 721 // through. Nothing on this path caught anything, so an unreadable
589 // 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
@@ -628,18 +761,197 @@ int main(int argc, char* argv[])
628 printf("Loading wallet...\n"); 761 printf("Loading wallet...\n");
629 try 762 try
630 { 763 {
631 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")
632 { 774 {
633 // LoadWallet() explains itself into strWalletLoadError when it 775 AttachTerminal();
634 // knows why -- an unsupported wallet format, for one, which is the 776 FatalStartupError(fHeadlessStartup,
635 // case this whole path exists to make visible. 777 strprintf("Unknown wallet backend '%s'.", strWalletBackend.c_str()),
636 FatalStartupError(fHeadlessStartup, "Cannot open wallet.dat.", 778 "Use -walletbackend=sqlite for the SQLite backend, or "
637 strWalletLoadError); 779 "-walletbackend=bdb for the Berkeley DB wallet.dat.");
638 return 1; 780 return 1;
639 } 781 }
640 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
641 // 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
642 // 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.
643 RescanSpentFlags(); 955 RescanSpentFlags();
644 } 956 }
645 catch (const std::exception& e) 957 catch (const std::exception& e)
@@ -908,6 +1220,10 @@ int main(int argc, char* argv[])
908 // 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
909 // open -- issue #40. Not optional. 1221 // open -- issue #40. Not optional.
910 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();
911#ifdef _WIN32 1227#ifdef _WIN32
912 // 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
913 // control handler (CLOSE/LOGOFF/SHUTDOWN) return so the OS can finish. 1229 // control handler (CLOSE/LOGOFF/SHUTDOWN) return so the OS can finish.
@@ -924,6 +1240,7 @@ int main(int argc, char* argv[])
924 StopNode(); 1240 StopNode();
925 BtfStopManagedTor(); 1241 BtfStopManagedTor();
926 DBFlush(true); 1242 DBFlush(true);
1243 WalletSQLiteRuntimeClose();
927 return ret; 1244 return ret;
928#endif 1245#endif
929} 1246}