diff options
| author | bitflash-maintainer <noreply@bitflash.invalid> | 2026-09-05 22:46:52 +0000 |
|---|---|---|
| committer | bitflash-maintainer <noreply@bitflash.invalid> | 2026-09-05 22:46:52 +0000 |
| commit | ceae3d076b5ad11ee84729ae43f14e9312896646 (patch) | |
| tree | 7e9132aa871fdc01cb95268d2f5ea3bb11853ddf /src/gui.cpp | |
| parent | be63e83d79e7e00e6b8ca889a70e6a19886aa1d8 (diff) | |
| parent | 66e0b9158d050c46b1ff2509faeee3f4573cac1c (diff) | |
Bring main onto the line this project actually ships
main had drifted to a history with no ancestor in common with the tree we
build and deploy. It sat at be63e83 (2026-08-09) with 28 commits; the shipped
line, deploy/soak-2026-08-21, had 383. The two shared nothing -- git
merge-base returns no result at all -- so anyone cloning this repository
landed on a default branch that was neither current nor related to the
running node.
The 28 commits on main were audited before this merge: every one is already
present in the tree by content, including the two that are hardest to see
because they were rewritten afterwards -- the payout reorg-safety work
(PayoutBlockStillOnChain, since evolved to re-check on every retry rather
than once at maturity) and the nostr relay certificate verification
(SSL_set1_host, now with a Windows root-store path). Nothing on main is lost
by this merge.
Recorded as a merge rather than a force-push: main only ever moves forward,
its old history stays reachable as the first parent, and this is revertible.
The tree is taken wholesale from the merged branch, so main's content is now
exactly what was built and tested.
What this brings onto main:
- compactblock: BIP152-style compact block relay ported from upstream 1.2.23,
primitives and unit test only, with the RAND_bytes failure path closed and
the reconstruction allocation bounded.
- upstream/main 17741d9: isolated testnet parameters and genesis, optional
2 MB large pages for RandomX. Mainnet genesis verified byte-identical
through upstream's BuildGenesisBlock() refactor.
- deploy: every image built on bookworm so binaries run on Debian 12, and the
container build now honours the RandomX and libsecp256k1 pins it had been
silently ignoring since before 1.2.15.
Gates at this tree: 15/15 selftests, compactblock's 17 checks, consensus
constants unchanged against e4f898c apart from the pre-existing
MAX_BLOCK_SIZE_GEN, mainnet genesis intact, money-log check ok.
Diffstat (limited to 'src/gui.cpp')
| -rw-r--r-- | src/gui.cpp | 275 |
1 files changed, 263 insertions, 12 deletions
diff --git a/src/gui.cpp b/src/gui.cpp index 8f1d204..ff6059a 100644 --- a/src/gui.cpp +++ b/src/gui.cpp | |||
| @@ -11,10 +11,16 @@ | |||
| 11 | #include <ctime> | 11 | #include <ctime> |
| 12 | #include <cstdio> | 12 | #include <cstdio> |
| 13 | #include <sstream> | 13 | #include <sstream> |
| 14 | #ifdef _WIN32 | ||
| 15 | #include <shellapi.h> // ShellExecuteA, to open the website in the browser | ||
| 16 | #else | ||
| 17 | #include <unistd.h> | ||
| 18 | #endif | ||
| 14 | #include <iomanip> | 19 | #include <iomanip> |
| 15 | #include <atomic> | 20 | #include <atomic> |
| 16 | #include <mutex> | 21 | #include <mutex> |
| 17 | #include <thread> | 22 | #include <thread> |
| 23 | #include <system_error> | ||
| 18 | #include "bip32.h" | 24 | #include "bip32.h" |
| 19 | #include "walletcmd.h" | 25 | #include "walletcmd.h" |
| 20 | #include "font_roboto.h" | 26 | #include "font_roboto.h" |
| @@ -172,7 +178,11 @@ static bool PathIsInsideDataDir(const std::string& strPath) | |||
| 172 | 178 | ||
| 173 | static void SetDefaultBackupPath() | 179 | static void SetDefaultBackupPath() |
| 174 | { | 180 | { |
| 175 | std::string path = DefaultBackupDir() + "/wallet-backup-" + BackupTimeSuffix() + ".dat"; | 181 | // Match the extension to the live backend: BackupWallet copies wallet.sqlite |
| 182 | // under the SQLite backend and wallet.dat under Berkeley DB, so a ".dat" | ||
| 183 | // default name would put SQLite contents in a misleadingly-named file. | ||
| 184 | const char* pszExt = WalletSQLiteRuntimeActive() ? ".sqlite" : ".dat"; | ||
| 185 | std::string path = DefaultBackupDir() + "/wallet-backup-" + BackupTimeSuffix() + pszExt; | ||
| 176 | strncpy(g_backupPath, path.c_str(), sizeof(g_backupPath)-1); | 186 | strncpy(g_backupPath, path.c_str(), sizeof(g_backupPath)-1); |
| 177 | g_backupPath[sizeof(g_backupPath)-1] = '\0'; | 187 | g_backupPath[sizeof(g_backupPath)-1] = '\0'; |
| 178 | } | 188 | } |
| @@ -910,8 +920,16 @@ static void DrawOptionsDialog() | |||
| 910 | 920 | ||
| 911 | if (nMineMode == MINE_OPERATOR) { | 921 | if (nMineMode == MINE_OPERATOR) { |
| 912 | if (!gPoolServerRunning) { | 922 | if (!gPoolServerRunning) { |
| 913 | gPoolServerRunning = true; | 923 | // Claim the server is up only once it is. The flag used to |
| 914 | _beginthread(ThreadRPCServer, 0, NULL); | 924 | // be set first and the result never looked at, so a refused |
| 925 | // spawn left an operator with a pool that reports itself | ||
| 926 | // running, no RPC server for miners to reach, and nothing | ||
| 927 | // anywhere saying why. The flag also latches: with it set, | ||
| 928 | // this branch is never retried. | ||
| 929 | if (_beginthread(ThreadRPCServer, 0, NULL) == (uintptr_t)-1) | ||
| 930 | printf("Error: _beginthread(ThreadRPCServer) failed -- pool server not started\n"); | ||
| 931 | else | ||
| 932 | gPoolServerRunning = true; | ||
| 915 | } else if (!gPoolRunning) { | 933 | } else if (!gPoolRunning) { |
| 916 | gPoolRunning = true; | 934 | gPoolRunning = true; |
| 917 | } | 935 | } |
| @@ -1124,11 +1142,28 @@ static void DrawRestorePhraseDialog() | |||
| 1124 | } | 1142 | } |
| 1125 | g_restoreDerived.store(0); | 1143 | g_restoreDerived.store(0); |
| 1126 | g_restoreRecovered.store(0); | 1144 | g_restoreRecovered.store(0); |
| 1145 | // Set before the spawn, not after: RestoreThread clears this | ||
| 1146 | // flag itself when it finishes, and a short restore can beat | ||
| 1147 | // us back here and have its `false` overwritten, leaving the | ||
| 1148 | // dialog disabled forever. | ||
| 1127 | g_restoreRunning.store(true); | 1149 | g_restoreRunning.store(true); |
| 1128 | // On its own thread: a restore walks the whole chain once per | 1150 | // On its own thread: a restore walks the whole chain once per |
| 1129 | // batch and would otherwise freeze the window for as long as it | 1151 | // batch and would otherwise freeze the window for as long as it |
| 1130 | // takes. | 1152 | // takes. A refused spawn is one restore that does not happen, |
| 1131 | std::thread(RestoreThread, std::string(g_restoreInput)).detach(); | 1153 | // not a reason to take the window down with an uncaught |
| 1154 | // std::system_error -- and the flag has to come back off, or | ||
| 1155 | // the dialog sits greyed out reporting a search nobody is | ||
| 1156 | // running. | ||
| 1157 | try | ||
| 1158 | { | ||
| 1159 | std::thread(RestoreThread, std::string(g_restoreInput)).detach(); | ||
| 1160 | } | ||
| 1161 | catch (const std::system_error& e) | ||
| 1162 | { | ||
| 1163 | g_restoreRunning.store(false); | ||
| 1164 | std::lock_guard<std::mutex> lock(g_restoreResultMutex); | ||
| 1165 | g_restoreResult = std::string("Could not start the search: ") + e.what(); | ||
| 1166 | } | ||
| 1132 | } | 1167 | } |
| 1133 | } | 1168 | } |
| 1134 | if (fBusy) | 1169 | if (fBusy) |
| @@ -1250,15 +1285,15 @@ static void DrawWalletSafetyDialog() | |||
| 1250 | if (g_recoveryAudit.fHaveSeed) | 1285 | if (g_recoveryAudit.fHaveSeed) |
| 1251 | { | 1286 | { |
| 1252 | ImGui::Text("Derivation schema: %s", HDKeySchemaName(g_recoveryAudit.nSchema).c_str()); | 1287 | ImGui::Text("Derivation schema: %s", HDKeySchemaName(g_recoveryAudit.nSchema).c_str()); |
| 1253 | ImGui::Text("BIP44 coin type: %u (provisional BITFLASH)", g_recoveryAudit.nCoinType); | 1288 | ImGui::Text("BIP44 coin type: %u (SLIP-0044 BITFLASH)", g_recoveryAudit.nCoinType); |
| 1254 | } | 1289 | } |
| 1255 | ImGui::Text("Phrase-backed spendable balance: %s BTF", | 1290 | ImGui::Text("Phrase-backed spendable balance: %s BTF", |
| 1256 | FmtMoney(g_recoveryAudit.nRecoverableCredit).c_str()); | 1291 | FmtMoney(g_recoveryAudit.nRecoverableCredit).c_str()); |
| 1257 | ImGui::Text("Wallet.dat-only spendable balance: %s BTF", | 1292 | ImGui::Text("Not phrase-backed spendable balance: %s BTF", |
| 1258 | FmtMoney(g_recoveryAudit.nLegacyCredit).c_str()); | 1293 | FmtMoney(g_recoveryAudit.nLegacyCredit).c_str()); |
| 1259 | ImGui::Text("Phrase-backed immature mining rewards: %s BTF", | 1294 | ImGui::Text("Phrase-backed immature mining rewards: %s BTF", |
| 1260 | FmtMoney(g_recoveryAudit.nRecoverableImmatureCredit).c_str()); | 1295 | FmtMoney(g_recoveryAudit.nRecoverableImmatureCredit).c_str()); |
| 1261 | ImGui::Text("Wallet.dat-only immature mining rewards: %s BTF", | 1296 | ImGui::Text("Not phrase-backed immature mining rewards: %s BTF", |
| 1262 | FmtMoney(g_recoveryAudit.nLegacyImmatureCredit).c_str()); | 1297 | FmtMoney(g_recoveryAudit.nLegacyImmatureCredit).c_str()); |
| 1263 | if (!g_recoveryAudit.fDeriveComplete) | 1298 | if (!g_recoveryAudit.fDeriveComplete) |
| 1264 | { | 1299 | { |
| @@ -1272,8 +1307,8 @@ static void DrawWalletSafetyDialog() | |||
| 1272 | ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.75f, 0.25f, 1.0f)); | 1307 | ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.75f, 0.25f, 1.0f)); |
| 1273 | ImGui::TextWrapped( | 1308 | ImGui::TextWrapped( |
| 1274 | "Some coins are on keys the phrase does not reproduce. " | 1309 | "Some coins are on keys the phrase does not reproduce. " |
| 1275 | "Keep wallet.dat backups until that balance has been moved to a " | 1310 | "Keep a file backup of this wallet until that balance has been " |
| 1276 | "phrase-backed address."); | 1311 | "moved to a phrase-backed address."); |
| 1277 | ImGui::PopStyleColor(); | 1312 | ImGui::PopStyleColor(); |
| 1278 | } | 1313 | } |
| 1279 | else if (HaveHDSeed() && | 1314 | else if (HaveHDSeed() && |
| @@ -1369,16 +1404,38 @@ static void DrawDiagnosticsDialog() | |||
| 1369 | ImGui::End(); | 1404 | ImGui::End(); |
| 1370 | } | 1405 | } |
| 1371 | 1406 | ||
| 1407 | static void OpenURL(const char* pszUrl) | ||
| 1408 | { | ||
| 1409 | #ifdef _WIN32 | ||
| 1410 | ShellExecuteA(NULL, "open", pszUrl, NULL, NULL, SW_SHOWNORMAL); | ||
| 1411 | #else | ||
| 1412 | std::string cmd = std::string("xdg-open '") + pszUrl + "' >/dev/null 2>&1 &"; | ||
| 1413 | int r = system(cmd.c_str()); | ||
| 1414 | (void)r; | ||
| 1415 | #endif | ||
| 1416 | } | ||
| 1417 | |||
| 1372 | static void DrawAboutDialog() | 1418 | static void DrawAboutDialog() |
| 1373 | { | 1419 | { |
| 1374 | if (!g_showAbout) return; | 1420 | if (!g_showAbout) return; |
| 1375 | ImGui::SetNextWindowSize(ImVec2(380.0f, 170.0f), ImGuiCond_Always); | 1421 | ImGui::SetNextWindowSize(ImVec2(380.0f, 205.0f), ImGuiCond_Always); |
| 1376 | ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), | 1422 | ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), |
| 1377 | ImGuiCond_Always, ImVec2(0.5f, 0.5f)); | 1423 | ImGuiCond_Always, ImVec2(0.5f, 0.5f)); |
| 1378 | if (ImGui::Begin("About Bitflash", &g_showAbout, | 1424 | if (ImGui::Begin("About Bitflash", &g_showAbout, |
| 1379 | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse)) | 1425 | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse)) |
| 1380 | { | 1426 | { |
| 1381 | ImGui::TextColored(ImVec4(1.0f, 0.6f, 0.0f, 1.0f), "Bitflash BTF v1.1.0"); | 1427 | ImGui::TextColored(ImVec4(1.0f, 0.6f, 0.0f, 1.0f), |
| 1428 | "Bitflash BTF v" BITFLASH_VERSION_STRING); | ||
| 1429 | ImGui::Spacing(); | ||
| 1430 | ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.40f, 0.70f, 1.0f, 1.0f)); | ||
| 1431 | ImGui::TextUnformatted("https://bitflash.network"); | ||
| 1432 | ImGui::PopStyleColor(); | ||
| 1433 | if (ImGui::IsItemHovered()) | ||
| 1434 | { | ||
| 1435 | ImGui::SetMouseCursor(ImGuiMouseCursor_Hand); | ||
| 1436 | if (ImGui::IsMouseClicked(0)) | ||
| 1437 | OpenURL("https://bitflash.network"); | ||
| 1438 | } | ||
| 1382 | ImGui::Spacing(); | 1439 | ImGui::Spacing(); |
| 1383 | ImGui::TextWrapped( | 1440 | ImGui::TextWrapped( |
| 1384 | "CPU-only cryptocurrency. RandomX proof of work. " | 1441 | "CPU-only cryptocurrency. RandomX proof of work. " |
| @@ -1391,6 +1448,184 @@ static void DrawAboutDialog() | |||
| 1391 | ImGui::End(); | 1448 | ImGui::End(); |
| 1392 | } | 1449 | } |
| 1393 | 1450 | ||
| 1451 | // First-run offer to move a Berkeley DB wallet to the SQLite backend. Only the | ||
| 1452 | // button actions touch anything: "Convert" exports wallet.dat to wallet.sqlite | ||
| 1453 | // and records the backend marker (wallet.dat is left untouched, so a restart | ||
| 1454 | // picks up SQLite while the old file stays as a fallback); "Keep Berkeley DB" | ||
| 1455 | // just records that choice so the offer does not return; "Decide later" leaves | ||
| 1456 | // no marker and asks again next start. | ||
| 1457 | static bool g_showBackendWizard = false; | ||
| 1458 | static std::string g_wizardStatus; | ||
| 1459 | static bool g_wizardConverted = false; | ||
| 1460 | static int g_restartArgc = 0; | ||
| 1461 | static char** g_restartArgv = NULL; | ||
| 1462 | |||
| 1463 | #ifdef _WIN32 | ||
| 1464 | // Quote one argument so CommandLineToArgvW parses it back to exactly this | ||
| 1465 | // string: only backslashes that immediately precede a double quote (including | ||
| 1466 | // the closing one) are doubled, so ordinary Windows paths are not mangled. | ||
| 1467 | static std::string QuoteArgWin(const std::string& a) | ||
| 1468 | { | ||
| 1469 | if (!a.empty() && a.find_first_of(" \t\"") == std::string::npos) | ||
| 1470 | return a; | ||
| 1471 | std::string r = "\""; | ||
| 1472 | size_t bs = 0; | ||
| 1473 | for (size_t i = 0; i < a.size(); i++) | ||
| 1474 | { | ||
| 1475 | if (a[i] == '\\') { bs++; continue; } | ||
| 1476 | if (a[i] == '"') { r.append(bs * 2 + 1, '\\'); r += '"'; bs = 0; continue; } | ||
| 1477 | r.append(bs, '\\'); bs = 0; r += a[i]; | ||
| 1478 | } | ||
| 1479 | r.append(bs * 2, '\\'); | ||
| 1480 | r += "\""; | ||
| 1481 | return r; | ||
| 1482 | } | ||
| 1483 | #endif | ||
| 1484 | |||
| 1485 | // Relaunch Bitflash with the same arguments and datadir, then ask this instance | ||
| 1486 | // to exit -- but only once the relaunch has actually started, so a failure never | ||
| 1487 | // leaves the user with no node running. The new instance waits (-restartwait) | ||
| 1488 | // before it opens the network port and the block index, so this one is gone | ||
| 1489 | // first and they never fight over the same files. | ||
| 1490 | static void RestartApplication() | ||
| 1491 | { | ||
| 1492 | bool fLaunched = false; | ||
| 1493 | #ifdef _WIN32 | ||
| 1494 | char exePath[MAX_PATH]; | ||
| 1495 | if (GetModuleFileNameA(NULL, exePath, MAX_PATH) == 0) | ||
| 1496 | return; | ||
| 1497 | std::string strCmd = QuoteArgWin(exePath); | ||
| 1498 | bool fHasDataDir = false; | ||
| 1499 | for (int i = 1; i < g_restartArgc; i++) | ||
| 1500 | { | ||
| 1501 | std::string a = g_restartArgv[i] ? g_restartArgv[i] : ""; | ||
| 1502 | if (a.find("-datadir=") == 0 || a.find("/datadir=") == 0) | ||
| 1503 | fHasDataDir = true; | ||
| 1504 | strCmd += " " + QuoteArgWin(a); | ||
| 1505 | } | ||
| 1506 | if (!fHasDataDir) | ||
| 1507 | strCmd += " " + QuoteArgWin("-datadir=" + GetAppDir()); | ||
| 1508 | strCmd += " -restartwait"; | ||
| 1509 | |||
| 1510 | std::vector<char> cmdBuf(strCmd.begin(), strCmd.end()); | ||
| 1511 | cmdBuf.push_back('\0'); | ||
| 1512 | STARTUPINFOA si; memset(&si, 0, sizeof(si)); si.cb = sizeof(si); | ||
| 1513 | PROCESS_INFORMATION pi; memset(&pi, 0, sizeof(pi)); | ||
| 1514 | if (CreateProcessA(NULL, &cmdBuf[0], NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) | ||
| 1515 | { | ||
| 1516 | CloseHandle(pi.hProcess); | ||
| 1517 | CloseHandle(pi.hThread); | ||
| 1518 | fLaunched = true; | ||
| 1519 | } | ||
| 1520 | #else | ||
| 1521 | pid_t pid = fork(); | ||
| 1522 | if (pid == 0) | ||
| 1523 | { | ||
| 1524 | // Child: wait for the parent to release the port and files, then re-exec. | ||
| 1525 | // /proc/self/exe is the reliable absolute path; argv[0] may be relative | ||
| 1526 | // or a bare name that execv() cannot resolve. | ||
| 1527 | sleep(2); | ||
| 1528 | char exePath[4096]; | ||
| 1529 | ssize_t len = readlink("/proc/self/exe", exePath, sizeof(exePath) - 1); | ||
| 1530 | if (len > 0) | ||
| 1531 | { | ||
| 1532 | exePath[len] = '\0'; | ||
| 1533 | execv(exePath, g_restartArgv); | ||
| 1534 | } | ||
| 1535 | if (g_restartArgv) | ||
| 1536 | execv(g_restartArgv[0], g_restartArgv); | ||
| 1537 | _exit(127); | ||
| 1538 | } | ||
| 1539 | fLaunched = (pid > 0); | ||
| 1540 | #endif | ||
| 1541 | |||
| 1542 | if (fLaunched) | ||
| 1543 | { | ||
| 1544 | fShutdown = true; | ||
| 1545 | if (glfwGetCurrentContext()) | ||
| 1546 | glfwSetWindowShouldClose(glfwGetCurrentContext(), true); | ||
| 1547 | } | ||
| 1548 | else | ||
| 1549 | g_wizardStatus = "Could not relaunch automatically -- please restart " | ||
| 1550 | "Bitflash yourself to switch to the SQLite wallet."; | ||
| 1551 | } | ||
| 1552 | |||
| 1553 | static void DrawBackendWizardDialog() | ||
| 1554 | { | ||
| 1555 | if (!g_showBackendWizard) return; | ||
| 1556 | ImGui::SetNextWindowSize(ImVec2(560.0f, 0.0f), ImGuiCond_Always); | ||
| 1557 | ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), | ||
| 1558 | ImGuiCond_Always, ImVec2(0.5f, 0.5f)); | ||
| 1559 | if (ImGui::Begin("Wallet Storage", &g_showBackendWizard, | ||
| 1560 | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse)) | ||
| 1561 | { | ||
| 1562 | if (g_wizardConverted) | ||
| 1563 | { | ||
| 1564 | ImGui::TextColored(ImVec4(0.35f, 1.0f, 0.45f, 1.0f), "Converted to SQLite."); | ||
| 1565 | ImGui::TextWrapped("%s", g_wizardStatus.c_str()); | ||
| 1566 | ImGui::Spacing(); | ||
| 1567 | ImGui::TextWrapped( | ||
| 1568 | "The switch takes effect when Bitflash restarts. Until then it is " | ||
| 1569 | "still running on the old Berkeley DB wallet. Your wallet.dat is " | ||
| 1570 | "kept untouched as a fallback."); | ||
| 1571 | ImGui::Spacing(); | ||
| 1572 | if (ImGui::Button("Restart Now", ImVec2(140.0f, 0.0f))) | ||
| 1573 | RestartApplication(); | ||
| 1574 | ImGui::SameLine(); | ||
| 1575 | if (ImGui::Button("Later", ImVec2(120.0f, 0.0f))) | ||
| 1576 | g_showBackendWizard = false; | ||
| 1577 | ImGui::Spacing(); | ||
| 1578 | ImGui::TextDisabled( | ||
| 1579 | "If you keep mining before restarting, new coins go to the old " | ||
| 1580 | "wallet until you switch."); | ||
| 1581 | } | ||
| 1582 | else | ||
| 1583 | { | ||
| 1584 | ImGui::TextWrapped( | ||
| 1585 | "Bitflash can store your wallet in a new SQLite format: a single " | ||
| 1586 | "file that is easier to back up and does not depend on the " | ||
| 1587 | "database/ directory beside it."); | ||
| 1588 | ImGui::Spacing(); | ||
| 1589 | ImGui::TextWrapped( | ||
| 1590 | "Your current Berkeley DB wallet (wallet.dat) is kept exactly as it " | ||
| 1591 | "is -- nothing is deleted, and you can go back to it at any time. " | ||
| 1592 | "The change takes effect after you restart."); | ||
| 1593 | ImGui::Spacing(); | ||
| 1594 | if (ImGui::Button("Convert to SQLite", ImVec2(180.0f, 0.0f))) | ||
| 1595 | { | ||
| 1596 | int nCopied = 0; | ||
| 1597 | std::string strError; | ||
| 1598 | if (DoConvertWalletToSQLite(nCopied, strError)) | ||
| 1599 | { | ||
| 1600 | g_wizardStatus = | ||
| 1601 | strprintf("%d record(s) copied to wallet.sqlite.", nCopied); | ||
| 1602 | g_wizardConverted = true; | ||
| 1603 | } | ||
| 1604 | else | ||
| 1605 | g_wizardStatus = "Conversion failed: " + strError; | ||
| 1606 | } | ||
| 1607 | ImGui::SameLine(); | ||
| 1608 | if (ImGui::Button("Keep Berkeley DB", ImVec2(160.0f, 0.0f))) | ||
| 1609 | { | ||
| 1610 | WriteWalletBackendMarker("bdb"); | ||
| 1611 | g_showBackendWizard = false; | ||
| 1612 | } | ||
| 1613 | ImGui::SameLine(); | ||
| 1614 | if (ImGui::Button("Decide later", ImVec2(120.0f, 0.0f))) | ||
| 1615 | g_showBackendWizard = false; | ||
| 1616 | |||
| 1617 | if (!g_wizardStatus.empty()) | ||
| 1618 | { | ||
| 1619 | ImGui::Spacing(); | ||
| 1620 | ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.45f, 0.35f, 1.0f)); | ||
| 1621 | ImGui::TextWrapped("%s", g_wizardStatus.c_str()); | ||
| 1622 | ImGui::PopStyleColor(); | ||
| 1623 | } | ||
| 1624 | } | ||
| 1625 | } | ||
| 1626 | ImGui::End(); | ||
| 1627 | } | ||
| 1628 | |||
| 1394 | // --------------------------------------------------------------------------- | 1629 | // --------------------------------------------------------------------------- |
| 1395 | // Entry point | 1630 | // Entry point |
| 1396 | // --------------------------------------------------------------------------- | 1631 | // --------------------------------------------------------------------------- |
| @@ -1457,6 +1692,21 @@ int RunGUI(int argc, char* argv[]) | |||
| 1457 | g_mineRadio = nMineMode; | 1692 | g_mineRadio = nMineMode; |
| 1458 | strncpy(g_participantPool, strParticipantPool.c_str(), sizeof(g_participantPool)-1); | 1693 | strncpy(g_participantPool, strParticipantPool.c_str(), sizeof(g_participantPool)-1); |
| 1459 | 1694 | ||
| 1695 | // Kept for the wizard's "Restart Now": relaunch with the same command line. | ||
| 1696 | g_restartArgc = argc; | ||
| 1697 | g_restartArgv = argv; | ||
| 1698 | |||
| 1699 | // First run on a Berkeley DB wallet with no recorded choice yet: offer to | ||
| 1700 | // convert to SQLite. Never when already on SQLite, once wallet.sqlite exists, | ||
| 1701 | // or once a choice has been recorded in the backend marker. | ||
| 1702 | if (!WalletSQLiteRuntimeActive() && | ||
| 1703 | FileExists((GetAppDir() + "/wallet.dat").c_str()) && | ||
| 1704 | !FileExists((GetAppDir() + "/wallet.sqlite").c_str()) && | ||
| 1705 | ReadWalletBackendMarker().empty()) | ||
| 1706 | { | ||
| 1707 | g_showBackendWizard = true; | ||
| 1708 | } | ||
| 1709 | |||
| 1460 | int frame = 0; | 1710 | int frame = 0; |
| 1461 | while (!glfwWindowShouldClose(window) && !fShutdown) | 1711 | while (!glfwWindowShouldClose(window) && !fShutdown) |
| 1462 | { | 1712 | { |
| @@ -1480,6 +1730,7 @@ int RunGUI(int argc, char* argv[]) | |||
| 1480 | DrawRestorePhraseDialog(); | 1730 | DrawRestorePhraseDialog(); |
| 1481 | DrawDiagnosticsDialog(); | 1731 | DrawDiagnosticsDialog(); |
| 1482 | DrawAboutDialog(); | 1732 | DrawAboutDialog(); |
| 1733 | DrawBackendWizardDialog(); | ||
| 1483 | DrawStatusBar(); | 1734 | DrawStatusBar(); |
| 1484 | 1735 | ||
| 1485 | ImGui::Render(); | 1736 | ImGui::Render(); |