From 477d57b9a01102b6f79ad9b10b2dcfd280faa1cb Mon Sep 17 00:00:00 2001 From: bitflash Date: Tue, 18 Aug 2026 16:26:34 -0300 Subject: gui: offer to convert a Berkeley DB wallet to SQLite on first run A first-run dialog for an existing wallet.dat with no backend choice yet: "Convert to SQLite" exports the wallet and records the sqlite marker so the next start opens it; "Keep Berkeley DB" records that choice so the offer does not return; "Decide later" leaves no marker and asks again. wallet.dat is never touched, and the change takes effect on restart -- matching the restart-to-apply model. The conversion itself is DoConvertWalletToSQLite(), a headless-testable function the button calls: it reuses the export path (extracted as ExportActiveWalletToSQLite, shared with -walletsqliteexport, which keeps its own specific messages), refuses if already on SQLite or if a wallet.sqlite exists, and writes the marker only after a verified export. New wallet-convert self-test proves the action end to end: build a Berkeley DB wallet, convert, and confirm wallet.sqlite + the marker are written, wallet.dat is left untouched, a second convert refuses, and a flagless restart opens the converted SQLite wallet with its phrase intact. All wallet suites pass. The modal itself is thin UI over this tested action. --- src/walletcmd.cpp | 119 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 83 insertions(+), 36 deletions(-) (limited to 'src/walletcmd.cpp') diff --git a/src/walletcmd.cpp b/src/walletcmd.cpp index 374b2cc..1f1fd27 100644 --- a/src/walletcmd.cpp +++ b/src/walletcmd.cpp @@ -1070,79 +1070,126 @@ public: } // namespace -int CmdWalletSQLiteExport(const std::string& strDest) +// Export the currently-loaded wallet to a SQLite file. Shared by the +// -walletsqliteexport command and the GUI conversion wizard: opens the +// destination, copies every record inside one transaction, checkpoints, and +// verifies the stored count matches. Returns false with strErrorRet set on any +// failure and leaves no partial file behind; the source wallet is only read. +bool ExportActiveWalletToSQLite(const std::string& strDest, int& nCopiedRet, + std::string& strErrorRet) { - AttachTerminal(); - + nCopiedRet = 0; if (strDest.empty()) { - fprintf(stderr, "Missing SQLite wallet export path.\n"); - return 1; + strErrorRet = "no SQLite export path was given"; + return false; } if (FileExists(strDest.c_str())) { - fprintf(stderr, "Refusing to overwrite existing SQLite wallet export: %s\n", - strDest.c_str()); - return 1; + strErrorRet = strprintf("refusing to overwrite existing file: %s", strDest.c_str()); + return false; } - string strError; CWalletDBSQLite db; - if (!db.Open(strDest, strError)) + if (!db.Open(strDest, strErrorRet)) { RemoveSQLiteExportFiles(strDest); - fprintf(stderr, "Cannot open SQLite wallet export: %s\n", strError.c_str()); - return 1; + return false; } - if (!db.BeginTransaction(strError)) + if (!db.BeginTransaction(strErrorRet)) { db.Close(); RemoveSQLiteExportFiles(strDest); - fprintf(stderr, "Cannot begin SQLite wallet export: %s\n", strError.c_str()); - return 1; + return false; } - int nCopied = 0; - CWalletSQLiteExportVisitor visitor(db, nCopied); - if (!ScanWalletRecords(visitor, strError)) + CWalletSQLiteExportVisitor visitor(db, nCopiedRet); + if (!ScanWalletRecords(visitor, strErrorRet)) { string strRollbackError; db.RollbackTransaction(strRollbackError); db.Close(); RemoveSQLiteExportFiles(strDest); - fprintf(stderr, "Cannot export wallet.dat to SQLite: %s\n", strError.c_str()); - return 1; + return false; } - if (!db.CommitTransaction(strError)) + if (!db.CommitTransaction(strErrorRet) || !db.Checkpoint(strErrorRet)) { db.Close(); RemoveSQLiteExportFiles(strDest); - fprintf(stderr, "Cannot commit SQLite wallet export: %s\n", strError.c_str()); - return 1; + return false; } - if (!db.Checkpoint(strError)) + + int nCount = 0; + if (!db.CountRecords(nCount, strErrorRet)) { db.Close(); RemoveSQLiteExportFiles(strDest); - fprintf(stderr, "Cannot checkpoint SQLite wallet export: %s\n", strError.c_str()); - return 1; + return false; } - - int nCount = 0; - if (!db.CountRecords(nCount, strError)) + if (nCount != nCopiedRet) { + strErrorRet = strprintf("export count mismatch: copied %d, stored %d", + nCopiedRet, nCount); db.Close(); RemoveSQLiteExportFiles(strDest); - fprintf(stderr, "Cannot verify SQLite wallet export count: %s\n", - strError.c_str()); + return false; + } + db.Close(); + return true; +} + +// Convert the running Berkeley DB wallet to SQLite for the GUI wizard: export +// wallet.dat to /wallet.sqlite, then record the backend marker so the +// next start opens SQLite. wallet.dat is never modified -- it stays as the +// fallback. Refuses if already on SQLite or if a wallet.sqlite already exists. +bool DoConvertWalletToSQLite(int& nCopiedRet, std::string& strErrorRet) +{ + nCopiedRet = 0; + if (WalletSQLiteRuntimeActive()) + { + strErrorRet = "this wallet is already on the SQLite backend"; + return false; + } + string strDest = GetAppDir() + "/wallet.sqlite"; + if (FileExists(strDest.c_str())) + { + strErrorRet = strprintf("a SQLite wallet already exists at %s", strDest.c_str()); + return false; + } + if (!ExportActiveWalletToSQLite(strDest, nCopiedRet, strErrorRet)) + return false; + if (!WriteWalletBackendMarker("sqlite")) + { + strErrorRet = "the wallet was exported but the backend choice could not be saved"; + return false; + } + return true; +} + +int CmdWalletSQLiteExport(const std::string& strDest) +{ + AttachTerminal(); + + // Keep the command's own specific messages for the two conditions the + // shared helper folds into a generic error, so scripts and the tests that + // read this output see the same wording as before. + if (strDest.empty()) + { + fprintf(stderr, "Missing SQLite wallet export path.\n"); return 1; } - if (nCount != nCopied) + if (FileExists(strDest.c_str())) { - db.Close(); - RemoveSQLiteExportFiles(strDest); - fprintf(stderr, "SQLite wallet export count mismatch: copied %d, stored %d\n", - nCopied, nCount); + fprintf(stderr, "Refusing to overwrite existing SQLite wallet export: %s\n", + strDest.c_str()); + return 1; + } + + int nCopied = 0; + string strError; + if (!ExportActiveWalletToSQLite(strDest, nCopied, strError)) + { + fprintf(stderr, "Cannot export wallet to SQLite: %s\n", strError.c_str()); return 1; } -- cgit v1.2.3