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-01 17:14:12 +0000
committerbitflash-maintainer <noreply@bitflash.invalid>2026-09-01 17:14:12 +0000
commitb4f0bc693ef7175702f82bcb2110b627922e8cbd (patch)
tree73435feb1a076dfba94d8143d7d7b7784908c82f /src/main_gui.cpp
parenta1d867ecbcfc9e218408d0a11863a450243a01f9 (diff)
parent6aa3dade77632f13189d2a3ef29897f29a5f9572 (diff)
Merge upstream v1.2.20 into the fork
Merges upstream tag v1.2.20 (6aa3dade) into our 1.2.19+sec tree. The merge base is bc3de30's descendant 9e39a50 -- upstream's own v1.2.19 -- so this is an ordinary three-way merge, not a replay. Five conflict blocks in four files, all resolved below. No consensus change. CheckBlock, ConnectBlock, DisconnectBlock, ConnectInputs, AcceptBlock, AcceptTransaction, GetNextWorkRequired and GetBlockValue hash identical to v1.2.19; script.cpp and script.h are byte-identical on both sides. main.cpp is +3 lines and main.h +2, all wallet-backend plumbing. rpc.cpp is untouched, so the payout ledger arrives unchanged. Makefile, VERSION. OURS, renumbered. 1.2.20+sec, for the same reason 1.2.19 carried +sec: this tree is not the upstream release of that number and the string names every release artifact. Makefile, AppImage recipe. OURS for the AppDir path, THEIRS for the bundled library. Upstream's hunk reverts the icon paths to a hardcoded /tmp/bitflash.AppDir, which is the local-user pre-placement hazard the fork moved out of /tmp in the first place; taking their side wholesale would have reintroduced it silently, inside a conflict about something else. The AppDir stays $(APPDIR), and libsqlite3.so.0 joins the bundled list, which the new backend genuinely needs. src/Makefile, src/makefile.mingw, HEADERS. BOTH. Ours drops market.h, theirs adds wallet_sqlite.h; the resolution does both. NODE_OBJS auto-merged correctly in both files -- obj/wallet_sqlite.o present, obj/market.o still gone -- and was verified rather than assumed, because that line has trapped this fork before. src/selftest.cpp, known-tests list. BOTH, union. Upstream adds eight wallet-storage tests, we keep payout-ledger. The dispatch chain itself auto-merged and still routes payout-ledger to RunPayoutLedgerSelfTest. Hardening verified intact after merge: the TLS 1.2 floor in nostr.cpp, the /rvrelay pin, opt-in managed Tor, strict sig3-on-onion descriptor verification, and the marketplace removal. Upstream shipped no build logs or __pycache__ this time; the three added files are docs/storage.md and src/wallet_sqlite.{cpp,h}. The SQLite backend this release introduces is NOT enabled here. An existing wallet.dat resolves to Berkeley DB on every branch of the new selection logic, and the conversion wizard is GUI-only and not compiled into the headless node. The review findings that gate ever turning it on -- the non-atomic backend marker, the connection-wide WriteHDMaster transaction, and the encrypt-swap crash window -- are recorded on the tracker, not fixed here.
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}