diff options
| author | bitflash-maintainer <noreply@bitflash.invalid> | 2026-09-04 05:59:40 +0000 |
|---|---|---|
| committer | bitflash-maintainer <noreply@bitflash.invalid> | 2026-09-04 05:59:40 +0000 |
| commit | 152c859b18b222a7c11a82b5485110e902267d40 (patch) | |
| tree | 1ceadf421609a2e58da81103165a2fb7085387de /src/main_gui.cpp | |
| parent | e4f898c4a6b9900553c64b9804889b5fdae3e55f (diff) | |
| parent | b810d86752ddb2674118f737582c924bb9eb74c1 (diff) | |
Merge branch 'deploy/all-2026-08-24' into 'deploy/soak-2026-08-21'
deploy: land MRs 1-12 and upstream 1.2.20, as shipped in relay component
See merge request !13
Diffstat (limited to 'src/main_gui.cpp')
| -rw-r--r-- | src/main_gui.cpp | 385 |
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 | } |