upleb.uk

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

summaryrefslogtreecommitdiff
path: root/src/selftest.cpp
diff options
context:
space:
mode:
authorbitflash <dev@bitflash.network>2026-08-18 16:26:34 -0300
committerbitflash <dev@bitflash.network>2026-08-18 16:26:34 -0300
commit477d57b9a01102b6f79ad9b10b2dcfd280faa1cb (patch)
treec320e5c1f6e486affdf255a1a4ac806a01b9da7c /src/selftest.cpp
parent3f94ec0649f935d00de49ad7b55026317dc64a27 (diff)
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.
Diffstat (limited to 'src/selftest.cpp')
-rw-r--r--src/selftest.cpp108
1 files changed, 107 insertions, 1 deletions
diff --git a/src/selftest.cpp b/src/selftest.cpp
index 1960003..a9c73fe 100644
--- a/src/selftest.cpp
+++ b/src/selftest.cpp
@@ -2664,6 +2664,110 @@ static int RunWalletBackendDefaultSelfTest()
2664 return nFail == 0 ? 0 : 1; 2664 return nFail == 0 ? 0 : 1;
2665} 2665}
2666 2666
2667// The wizard's conversion action, headless: build a Berkeley DB wallet, run
2668// DoConvertWalletToSQLite() (what the GUI button calls), and prove it produced a
2669// usable wallet.sqlite + backend marker while leaving wallet.dat untouched, then
2670// that a flagless restart opens the converted SQLite wallet.
2671static int RunWalletConvertSelfTest()
2672{
2673 fflush(stdout);
2674 printf("wallet-convert self-test\n");
2675
2676 std::string tmp, cwd;
2677 if (!MakeTempDir(tmp))
2678 {
2679 printf(" FAIL could not create a temporary data directory\n");
2680 return 1;
2681 }
2682 if (!GetCurrentDir(cwd) || !SetCurrentDir(tmp))
2683 {
2684 printf(" FAIL could not move into the temporary data directory\n");
2685 RemoveTree(tmp);
2686 return 1;
2687 }
2688
2689 int nFail = 0;
2690 strSetDataDir = tmp;
2691 printf(" temp datadir: %s\n", tmp.c_str());
2692
2693#ifdef _WIN32
2694 string strExe = cwd + "\\bitflash.exe";
2695#else
2696 string strExe = cwd + "/bitflash-node";
2697#endif
2698
2699 try
2700 {
2701 if (!LoadWallet())
2702 throw std::runtime_error("LoadWallet failed");
2703
2704 string strError;
2705 nFail += Check(SetHDSeedFromMnemonic(
2706 "abandon abandon abandon abandon abandon abandon abandon "
2707 "abandon abandon abandon abandon about", strError),
2708 "a phrase can be installed before conversion") ? 0 : 1;
2709 TopUpKeyPool();
2710
2711 vector<unsigned char> vchDefaultPubKey = keyUser.GetPubKey();
2712 CPrivKey vchDefaultPrivKey;
2713 nFail += Check(GetWalletPrivKey(vchDefaultPubKey, vchDefaultPrivKey, strError),
2714 "the default private key is readable before conversion") ? 0 : 1;
2715 vector<unsigned char> vchDefaultPrivBytes(vchDefaultPrivKey.begin(),
2716 vchDefaultPrivKey.end());
2717
2718 // The conversion action the GUI button invokes.
2719 int nCopied = 0;
2720 string strConvertError;
2721 bool fConverted = DoConvertWalletToSQLite(nCopied, strConvertError);
2722 nFail += Check(fConverted && nCopied > 0,
2723 "DoConvertWalletToSQLite exports the wallet") ? 0 : 1;
2724 nFail += Check(FileExists((tmp + "/wallet.sqlite").c_str()) &&
2725 FileContainsText(tmp + "/wallet-backend", "sqlite"),
2726 "conversion writes wallet.sqlite and records the sqlite marker") ? 0 : 1;
2727 nFail += Check(FileContainsBytes(tmp + "/wallet.dat", vchDefaultPrivBytes),
2728 "wallet.dat is left untouched by the conversion") ? 0 : 1;
2729
2730 // Idempotence guard: a second conversion refuses rather than overwrite.
2731 int nCopied2 = 0;
2732 string strSecondError;
2733 nFail += Check(!DoConvertWalletToSQLite(nCopied2, strSecondError),
2734 "a second conversion refuses to overwrite wallet.sqlite") ? 0 : 1;
2735
2736 DBFlush(true);
2737
2738 // A flagless restart now opens the converted SQLite wallet (the marker
2739 // routes it) and finds the same recovery phrase.
2740 string strAudit = tmp + "/converted-recovery-audit.txt";
2741 vector<string> vAuditArgs;
2742 vAuditArgs.push_back("-datadir=" + tmp);
2743 vAuditArgs.push_back("-nomanagedtor");
2744 vAuditArgs.push_back("-nogui");
2745 vAuditArgs.push_back("-recoveryaudit");
2746 int nAuditRet = RunBitflashChild(strExe, vAuditArgs, NULL, &strAudit);
2747 nFail += Check(nAuditRet == 0 &&
2748 FileContainsText(strAudit, "recovery phrase: present"),
2749 "a flagless restart opens the converted SQLite wallet") ? 0 : 1;
2750 }
2751 catch (const std::exception& e)
2752 {
2753 printf(" FAIL exception: %s\n", e.what());
2754 nFail++;
2755 }
2756 catch (...)
2757 {
2758 printf(" FAIL unknown exception\n");
2759 nFail++;
2760 }
2761
2762 DBFlush(true);
2763 SetCurrentDir(cwd);
2764 RemoveTree(tmp);
2765 printf("%s (%d failure%s)\n", nFail == 0 ? "ALL TESTS PASSED" : "TESTS FAILED",
2766 nFail, nFail == 1 ? "" : "s");
2767 fflush(stdout);
2768 return nFail == 0 ? 0 : 1;
2769}
2770
2667static int RunWalletPortabilitySelfTest() 2771static int RunWalletPortabilitySelfTest()
2668{ 2772{
2669 fflush(stdout); 2773 fflush(stdout);
@@ -3398,6 +3502,8 @@ int RunSelfTest(const std::string& name)
3398 return RunWalletSQLiteEncryptSelfTest(); 3502 return RunWalletSQLiteEncryptSelfTest();
3399 if (name == "wallet-backend-default") 3503 if (name == "wallet-backend-default")
3400 return RunWalletBackendDefaultSelfTest(); 3504 return RunWalletBackendDefaultSelfTest();
3505 if (name == "wallet-convert")
3506 return RunWalletConvertSelfTest();
3401 if (name == "wallet-portability") 3507 if (name == "wallet-portability")
3402 return RunWalletPortabilitySelfTest(); 3508 return RunWalletPortabilitySelfTest();
3403 if (name == "net-message") 3509 if (name == "net-message")
@@ -3414,6 +3520,6 @@ int RunSelfTest(const std::string& name)
3414 return RunManagedTorSelfTest(); 3520 return RunManagedTorSelfTest();
3415 3521
3416 printf("Unknown self-test '%s'\n", name.c_str()); 3522 printf("Unknown self-test '%s'\n", name.c_str());
3417 printf("Known self-tests: wallet-keypool, wallet-hd, wallet-format, wallet-storage-sanity, db-env-reopen, wallet-sqlite, wallet-sqlite-migration, wallet-crypto, wallet-encrypt, wallet-sqlite-encrypt, wallet-backend-default, wallet-portability, net-message, consensus-limits, pool-stratum, parse-money, socks5-proxy, managed-tor\n"); 3523 printf("Known self-tests: wallet-keypool, wallet-hd, wallet-format, wallet-storage-sanity, db-env-reopen, wallet-sqlite, wallet-sqlite-migration, wallet-crypto, wallet-encrypt, wallet-sqlite-encrypt, wallet-backend-default, wallet-convert, wallet-portability, net-message, consensus-limits, pool-stratum, parse-money, socks5-proxy, managed-tor\n");
3418 return 1; 3524 return 1;
3419} 3525}