upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src/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/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/gui.cpp')
-rw-r--r--src/gui.cpp239
1 files changed, 232 insertions, 7 deletions
diff --git a/src/gui.cpp b/src/gui.cpp
index 8dcb854..2a7f350 100644
--- a/src/gui.cpp
+++ b/src/gui.cpp
@@ -11,6 +11,11 @@
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>
@@ -172,7 +177,11 @@ static bool PathIsInsideDataDir(const std::string& strPath)
172 177
173static void SetDefaultBackupPath() 178static void SetDefaultBackupPath()
174{ 179{
175 std::string path = DefaultBackupDir() + "/wallet-backup-" + BackupTimeSuffix() + ".dat"; 180 // Match the extension to the live backend: BackupWallet copies wallet.sqlite
181 // under the SQLite backend and wallet.dat under Berkeley DB, so a ".dat"
182 // default name would put SQLite contents in a misleadingly-named file.
183 const char* pszExt = WalletSQLiteRuntimeActive() ? ".sqlite" : ".dat";
184 std::string path = DefaultBackupDir() + "/wallet-backup-" + BackupTimeSuffix() + pszExt;
176 strncpy(g_backupPath, path.c_str(), sizeof(g_backupPath)-1); 185 strncpy(g_backupPath, path.c_str(), sizeof(g_backupPath)-1);
177 g_backupPath[sizeof(g_backupPath)-1] = '\0'; 186 g_backupPath[sizeof(g_backupPath)-1] = '\0';
178} 187}
@@ -1254,11 +1263,11 @@ static void DrawWalletSafetyDialog()
1254 } 1263 }
1255 ImGui::Text("Phrase-backed spendable balance: %s BTF", 1264 ImGui::Text("Phrase-backed spendable balance: %s BTF",
1256 FmtMoney(g_recoveryAudit.nRecoverableCredit).c_str()); 1265 FmtMoney(g_recoveryAudit.nRecoverableCredit).c_str());
1257 ImGui::Text("Wallet.dat-only spendable balance: %s BTF", 1266 ImGui::Text("Not phrase-backed spendable balance: %s BTF",
1258 FmtMoney(g_recoveryAudit.nLegacyCredit).c_str()); 1267 FmtMoney(g_recoveryAudit.nLegacyCredit).c_str());
1259 ImGui::Text("Phrase-backed immature mining rewards: %s BTF", 1268 ImGui::Text("Phrase-backed immature mining rewards: %s BTF",
1260 FmtMoney(g_recoveryAudit.nRecoverableImmatureCredit).c_str()); 1269 FmtMoney(g_recoveryAudit.nRecoverableImmatureCredit).c_str());
1261 ImGui::Text("Wallet.dat-only immature mining rewards: %s BTF", 1270 ImGui::Text("Not phrase-backed immature mining rewards: %s BTF",
1262 FmtMoney(g_recoveryAudit.nLegacyImmatureCredit).c_str()); 1271 FmtMoney(g_recoveryAudit.nLegacyImmatureCredit).c_str());
1263 if (!g_recoveryAudit.fDeriveComplete) 1272 if (!g_recoveryAudit.fDeriveComplete)
1264 { 1273 {
@@ -1272,8 +1281,8 @@ static void DrawWalletSafetyDialog()
1272 ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.75f, 0.25f, 1.0f)); 1281 ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.75f, 0.25f, 1.0f));
1273 ImGui::TextWrapped( 1282 ImGui::TextWrapped(
1274 "Some coins are on keys the phrase does not reproduce. " 1283 "Some coins are on keys the phrase does not reproduce. "
1275 "Keep wallet.dat backups until that balance has been moved to a " 1284 "Keep a file backup of this wallet until that balance has been "
1276 "phrase-backed address."); 1285 "moved to a phrase-backed address.");
1277 ImGui::PopStyleColor(); 1286 ImGui::PopStyleColor();
1278 } 1287 }
1279 else if (HaveHDSeed() && 1288 else if (HaveHDSeed() &&
@@ -1369,16 +1378,38 @@ static void DrawDiagnosticsDialog()
1369 ImGui::End(); 1378 ImGui::End();
1370} 1379}
1371 1380
1381static void OpenURL(const char* pszUrl)
1382{
1383#ifdef _WIN32
1384 ShellExecuteA(NULL, "open", pszUrl, NULL, NULL, SW_SHOWNORMAL);
1385#else
1386 std::string cmd = std::string("xdg-open '") + pszUrl + "' >/dev/null 2>&1 &";
1387 int r = system(cmd.c_str());
1388 (void)r;
1389#endif
1390}
1391
1372static void DrawAboutDialog() 1392static void DrawAboutDialog()
1373{ 1393{
1374 if (!g_showAbout) return; 1394 if (!g_showAbout) return;
1375 ImGui::SetNextWindowSize(ImVec2(380.0f, 170.0f), ImGuiCond_Always); 1395 ImGui::SetNextWindowSize(ImVec2(380.0f, 205.0f), ImGuiCond_Always);
1376 ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(), 1396 ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(),
1377 ImGuiCond_Always, ImVec2(0.5f, 0.5f)); 1397 ImGuiCond_Always, ImVec2(0.5f, 0.5f));
1378 if (ImGui::Begin("About Bitflash", &g_showAbout, 1398 if (ImGui::Begin("About Bitflash", &g_showAbout,
1379 ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse)) 1399 ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse))
1380 { 1400 {
1381 ImGui::TextColored(ImVec4(1.0f, 0.6f, 0.0f, 1.0f), "Bitflash BTF v1.1.0"); 1401 ImGui::TextColored(ImVec4(1.0f, 0.6f, 0.0f, 1.0f),
1402 "Bitflash BTF v" BITFLASH_VERSION_STRING);
1403 ImGui::Spacing();
1404 ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.40f, 0.70f, 1.0f, 1.0f));
1405 ImGui::TextUnformatted("https://bitflash.network");
1406 ImGui::PopStyleColor();
1407 if (ImGui::IsItemHovered())
1408 {
1409 ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
1410 if (ImGui::IsMouseClicked(0))
1411 OpenURL("https://bitflash.network");
1412 }
1382 ImGui::Spacing(); 1413 ImGui::Spacing();
1383 ImGui::TextWrapped( 1414 ImGui::TextWrapped(
1384 "CPU-only cryptocurrency. RandomX proof of work. " 1415 "CPU-only cryptocurrency. RandomX proof of work. "
@@ -1391,6 +1422,184 @@ static void DrawAboutDialog()
1391 ImGui::End(); 1422 ImGui::End();
1392} 1423}
1393 1424
1425// First-run offer to move a Berkeley DB wallet to the SQLite backend. Only the
1426// button actions touch anything: "Convert" exports wallet.dat to wallet.sqlite
1427// and records the backend marker (wallet.dat is left untouched, so a restart
1428// picks up SQLite while the old file stays as a fallback); "Keep Berkeley DB"
1429// just records that choice so the offer does not return; "Decide later" leaves
1430// no marker and asks again next start.
1431static bool g_showBackendWizard = false;
1432static std::string g_wizardStatus;
1433static bool g_wizardConverted = false;
1434static int g_restartArgc = 0;
1435static char** g_restartArgv = NULL;
1436
1437#ifdef _WIN32
1438// Quote one argument so CommandLineToArgvW parses it back to exactly this
1439// string: only backslashes that immediately precede a double quote (including
1440// the closing one) are doubled, so ordinary Windows paths are not mangled.
1441static std::string QuoteArgWin(const std::string& a)
1442{
1443 if (!a.empty() && a.find_first_of(" \t\"") == std::string::npos)
1444 return a;
1445 std::string r = "\"";
1446 size_t bs = 0;
1447 for (size_t i = 0; i < a.size(); i++)
1448 {
1449 if (a[i] == '\\') { bs++; continue; }
1450 if (a[i] == '"') { r.append(bs * 2 + 1, '\\'); r += '"'; bs = 0; continue; }
1451 r.append(bs, '\\'); bs = 0; r += a[i];
1452 }
1453 r.append(bs * 2, '\\');
1454 r += "\"";
1455 return r;
1456}
1457#endif
1458
1459// Relaunch Bitflash with the same arguments and datadir, then ask this instance
1460// to exit -- but only once the relaunch has actually started, so a failure never
1461// leaves the user with no node running. The new instance waits (-restartwait)
1462// before it opens the network port and the block index, so this one is gone
1463// first and they never fight over the same files.
1464static void RestartApplication()
1465{
1466 bool fLaunched = false;
1467#ifdef _WIN32
1468 char exePath[MAX_PATH];
1469 if (GetModuleFileNameA(NULL, exePath, MAX_PATH) == 0)
1470 return;
1471 std::string strCmd = QuoteArgWin(exePath);
1472 bool fHasDataDir = false;
1473 for (int i = 1; i < g_restartArgc; i++)
1474 {
1475 std::string a = g_restartArgv[i] ? g_restartArgv[i] : "";
1476 if (a.find("-datadir=") == 0 || a.find("/datadir=") == 0)
1477 fHasDataDir = true;
1478 strCmd += " " + QuoteArgWin(a);
1479 }
1480 if (!fHasDataDir)
1481 strCmd += " " + QuoteArgWin("-datadir=" + GetAppDir());
1482 strCmd += " -restartwait";
1483
1484 std::vector<char> cmdBuf(strCmd.begin(), strCmd.end());
1485 cmdBuf.push_back('\0');
1486 STARTUPINFOA si; memset(&si, 0, sizeof(si)); si.cb = sizeof(si);
1487 PROCESS_INFORMATION pi; memset(&pi, 0, sizeof(pi));
1488 if (CreateProcessA(NULL, &cmdBuf[0], NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
1489 {
1490 CloseHandle(pi.hProcess);
1491 CloseHandle(pi.hThread);
1492 fLaunched = true;
1493 }
1494#else
1495 pid_t pid = fork();
1496 if (pid == 0)
1497 {
1498 // Child: wait for the parent to release the port and files, then re-exec.
1499 // /proc/self/exe is the reliable absolute path; argv[0] may be relative
1500 // or a bare name that execv() cannot resolve.
1501 sleep(2);
1502 char exePath[4096];
1503 ssize_t len = readlink("/proc/self/exe", exePath, sizeof(exePath) - 1);
1504 if (len > 0)
1505 {
1506 exePath[len] = '\0';
1507 execv(exePath, g_restartArgv);
1508 }
1509 if (g_restartArgv)
1510 execv(g_restartArgv[0], g_restartArgv);
1511 _exit(127);
1512 }
1513 fLaunched = (pid > 0);
1514#endif
1515
1516 if (fLaunched)
1517 {
1518 fShutdown = true;
1519 if (glfwGetCurrentContext())
1520 glfwSetWindowShouldClose(glfwGetCurrentContext(), true);
1521 }
1522 else
1523 g_wizardStatus = "Could not relaunch automatically -- please restart "
1524 "Bitflash yourself to switch to the SQLite wallet.";
1525}
1526
1527static void DrawBackendWizardDialog()
1528{
1529 if (!g_showBackendWizard) return;
1530 ImGui::SetNextWindowSize(ImVec2(560.0f, 0.0f), ImGuiCond_Always);
1531 ImGui::SetNextWindowPos(ImGui::GetMainViewport()->GetCenter(),
1532 ImGuiCond_Always, ImVec2(0.5f, 0.5f));
1533 if (ImGui::Begin("Wallet Storage", &g_showBackendWizard,
1534 ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse))
1535 {
1536 if (g_wizardConverted)
1537 {
1538 ImGui::TextColored(ImVec4(0.35f, 1.0f, 0.45f, 1.0f), "Converted to SQLite.");
1539 ImGui::TextWrapped("%s", g_wizardStatus.c_str());
1540 ImGui::Spacing();
1541 ImGui::TextWrapped(
1542 "The switch takes effect when Bitflash restarts. Until then it is "
1543 "still running on the old Berkeley DB wallet. Your wallet.dat is "
1544 "kept untouched as a fallback.");
1545 ImGui::Spacing();
1546 if (ImGui::Button("Restart Now", ImVec2(140.0f, 0.0f)))
1547 RestartApplication();
1548 ImGui::SameLine();
1549 if (ImGui::Button("Later", ImVec2(120.0f, 0.0f)))
1550 g_showBackendWizard = false;
1551 ImGui::Spacing();
1552 ImGui::TextDisabled(
1553 "If you keep mining before restarting, new coins go to the old "
1554 "wallet until you switch.");
1555 }
1556 else
1557 {
1558 ImGui::TextWrapped(
1559 "Bitflash can store your wallet in a new SQLite format: a single "
1560 "file that is easier to back up and does not depend on the "
1561 "database/ directory beside it.");
1562 ImGui::Spacing();
1563 ImGui::TextWrapped(
1564 "Your current Berkeley DB wallet (wallet.dat) is kept exactly as it "
1565 "is -- nothing is deleted, and you can go back to it at any time. "
1566 "The change takes effect after you restart.");
1567 ImGui::Spacing();
1568 if (ImGui::Button("Convert to SQLite", ImVec2(180.0f, 0.0f)))
1569 {
1570 int nCopied = 0;
1571 std::string strError;
1572 if (DoConvertWalletToSQLite(nCopied, strError))
1573 {
1574 g_wizardStatus =
1575 strprintf("%d record(s) copied to wallet.sqlite.", nCopied);
1576 g_wizardConverted = true;
1577 }
1578 else
1579 g_wizardStatus = "Conversion failed: " + strError;
1580 }
1581 ImGui::SameLine();
1582 if (ImGui::Button("Keep Berkeley DB", ImVec2(160.0f, 0.0f)))
1583 {
1584 WriteWalletBackendMarker("bdb");
1585 g_showBackendWizard = false;
1586 }
1587 ImGui::SameLine();
1588 if (ImGui::Button("Decide later", ImVec2(120.0f, 0.0f)))
1589 g_showBackendWizard = false;
1590
1591 if (!g_wizardStatus.empty())
1592 {
1593 ImGui::Spacing();
1594 ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.45f, 0.35f, 1.0f));
1595 ImGui::TextWrapped("%s", g_wizardStatus.c_str());
1596 ImGui::PopStyleColor();
1597 }
1598 }
1599 }
1600 ImGui::End();
1601}
1602
1394// --------------------------------------------------------------------------- 1603// ---------------------------------------------------------------------------
1395// Entry point 1604// Entry point
1396// --------------------------------------------------------------------------- 1605// ---------------------------------------------------------------------------
@@ -1457,6 +1666,21 @@ int RunGUI(int argc, char* argv[])
1457 g_mineRadio = nMineMode; 1666 g_mineRadio = nMineMode;
1458 strncpy(g_participantPool, strParticipantPool.c_str(), sizeof(g_participantPool)-1); 1667 strncpy(g_participantPool, strParticipantPool.c_str(), sizeof(g_participantPool)-1);
1459 1668
1669 // Kept for the wizard's "Restart Now": relaunch with the same command line.
1670 g_restartArgc = argc;
1671 g_restartArgv = argv;
1672
1673 // First run on a Berkeley DB wallet with no recorded choice yet: offer to
1674 // convert to SQLite. Never when already on SQLite, once wallet.sqlite exists,
1675 // or once a choice has been recorded in the backend marker.
1676 if (!WalletSQLiteRuntimeActive() &&
1677 FileExists((GetAppDir() + "/wallet.dat").c_str()) &&
1678 !FileExists((GetAppDir() + "/wallet.sqlite").c_str()) &&
1679 ReadWalletBackendMarker().empty())
1680 {
1681 g_showBackendWizard = true;
1682 }
1683
1460 int frame = 0; 1684 int frame = 0;
1461 while (!glfwWindowShouldClose(window) && !fShutdown) 1685 while (!glfwWindowShouldClose(window) && !fShutdown)
1462 { 1686 {
@@ -1480,6 +1704,7 @@ int RunGUI(int argc, char* argv[])
1480 DrawRestorePhraseDialog(); 1704 DrawRestorePhraseDialog();
1481 DrawDiagnosticsDialog(); 1705 DrawDiagnosticsDialog();
1482 DrawAboutDialog(); 1706 DrawAboutDialog();
1707 DrawBackendWizardDialog();
1483 DrawStatusBar(); 1708 DrawStatusBar();
1484 1709
1485 ImGui::Render(); 1710 ImGui::Render();