diff options
| author | Bitflash <admin@bitflash.network> | 2026-08-18 16:35:42 -0300 |
|---|---|---|
| committer | Bitflash <admin@bitflash.network> | 2026-08-18 16:35:42 -0300 |
| commit | b1c6d19fe2855c30d839dbc484165da58a8ee120 (patch) | |
| tree | c320e5c1f6e486affdf255a1a4ac806a01b9da7c /src/selftest.cpp | |
| parent | f3fb7ce6759ac98a7cc39d586fc2824982c74acd (diff) | |
| parent | 477d57b9a01102b6f79ad9b10b2dcfd280faa1cb (diff) | |
Merge pull request 'wallet/gui: SQLite is the default for new wallets + a first-run conversion wizard' (#62) from wallet-sqlite-default into main
Diffstat (limited to 'src/selftest.cpp')
| -rw-r--r-- | src/selftest.cpp | 241 |
1 files changed, 240 insertions, 1 deletions
diff --git a/src/selftest.cpp b/src/selftest.cpp index 90a30d2..a9c73fe 100644 --- a/src/selftest.cpp +++ b/src/selftest.cpp | |||
| @@ -911,6 +911,7 @@ static int RunWalletStorageSanitySelfTest() | |||
| 911 | vCreateArgs.push_back("-datadir=" + strScenarioDir); | 911 | vCreateArgs.push_back("-datadir=" + strScenarioDir); |
| 912 | vCreateArgs.push_back("-nomanagedtor"); | 912 | vCreateArgs.push_back("-nomanagedtor"); |
| 913 | vCreateArgs.push_back("-nogui"); | 913 | vCreateArgs.push_back("-nogui"); |
| 914 | vCreateArgs.push_back("-walletbackend=bdb"); // this suite is about wallet.dat | ||
| 914 | vCreateArgs.push_back("-newphrase"); | 915 | vCreateArgs.push_back("-newphrase"); |
| 915 | int nCreateRet = RunBitflashChild(strExe, vCreateArgs, NULL, &strCreateOut); | 916 | int nCreateRet = RunBitflashChild(strExe, vCreateArgs, NULL, &strCreateOut); |
| 916 | nFail += Check(nCreateRet == 0 && | 917 | nFail += Check(nCreateRet == 0 && |
| @@ -1474,6 +1475,7 @@ static int RunWalletSQLiteMigrationSelfTest() | |||
| 1474 | vCreateArgs.push_back("-datadir=" + strWalletDir); | 1475 | vCreateArgs.push_back("-datadir=" + strWalletDir); |
| 1475 | vCreateArgs.push_back("-nomanagedtor"); | 1476 | vCreateArgs.push_back("-nomanagedtor"); |
| 1476 | vCreateArgs.push_back("-nogui"); | 1477 | vCreateArgs.push_back("-nogui"); |
| 1478 | vCreateArgs.push_back("-walletbackend=bdb"); // migrate FROM a Berkeley DB wallet.dat | ||
| 1477 | vCreateArgs.push_back("-newphrase"); | 1479 | vCreateArgs.push_back("-newphrase"); |
| 1478 | int nCreateRet = RunBitflashChild(strExe, vCreateArgs, NULL, &strCreateOut); | 1480 | int nCreateRet = RunBitflashChild(strExe, vCreateArgs, NULL, &strCreateOut); |
| 1479 | nFail += Check(nCreateRet == 0 && | 1481 | nFail += Check(nCreateRet == 0 && |
| @@ -2534,6 +2536,238 @@ static int RunWalletSQLiteEncryptSelfTest() | |||
| 2534 | return nFail == 0 ? 0 : 1; | 2536 | return nFail == 0 ? 0 : 1; |
| 2535 | } | 2537 | } |
| 2536 | 2538 | ||
| 2539 | // Backend resolution when no -walletbackend flag is given: a fresh install | ||
| 2540 | // starts on SQLite (the new default), an existing wallet.dat stays on Berkeley | ||
| 2541 | // DB, the persistent marker overrides the default, and an explicit flag | ||
| 2542 | // overrides everything. Each scenario runs a real child process. | ||
| 2543 | static int RunWalletBackendDefaultSelfTest() | ||
| 2544 | { | ||
| 2545 | fflush(stdout); | ||
| 2546 | printf("wallet-backend-default self-test\n"); | ||
| 2547 | |||
| 2548 | std::string tmp, cwd; | ||
| 2549 | if (!MakeTempDir(tmp)) | ||
| 2550 | { | ||
| 2551 | printf(" FAIL could not create a temporary data directory\n"); | ||
| 2552 | return 1; | ||
| 2553 | } | ||
| 2554 | if (!GetCurrentDir(cwd) || !SetCurrentDir(tmp)) | ||
| 2555 | { | ||
| 2556 | printf(" FAIL could not move into the temporary data directory\n"); | ||
| 2557 | RemoveTree(tmp); | ||
| 2558 | return 1; | ||
| 2559 | } | ||
| 2560 | |||
| 2561 | int nFail = 0; | ||
| 2562 | strSetDataDir = tmp; | ||
| 2563 | printf(" temp datadir: %s\n", tmp.c_str()); | ||
| 2564 | |||
| 2565 | #ifdef _WIN32 | ||
| 2566 | string strExe = cwd + "\\bitflash.exe"; | ||
| 2567 | #else | ||
| 2568 | string strExe = cwd + "/bitflash-node"; | ||
| 2569 | #endif | ||
| 2570 | |||
| 2571 | try | ||
| 2572 | { | ||
| 2573 | // 1. Fresh datadir, no flag -> new wallet.sqlite, marker "sqlite", no wallet.dat. | ||
| 2574 | string d1 = tmp + "/fresh"; | ||
| 2575 | nFail += Check(MakeDirLocal(d1), "fresh scenario dir can be created") ? 0 : 1; | ||
| 2576 | vector<string> a1; | ||
| 2577 | a1.push_back("-datadir=" + d1); | ||
| 2578 | a1.push_back("-nomanagedtor"); | ||
| 2579 | a1.push_back("-nogui"); | ||
| 2580 | a1.push_back("-newaddress"); | ||
| 2581 | int r1 = RunBitflashChild(strExe, a1); | ||
| 2582 | nFail += Check(r1 == 0 && | ||
| 2583 | FileExists((d1 + "/wallet.sqlite").c_str()) && | ||
| 2584 | !FileExists((d1 + "/wallet.dat").c_str()) && | ||
| 2585 | FileContainsText(d1 + "/wallet-backend", "sqlite"), | ||
| 2586 | "a fresh install starts on SQLite (new default) and records the marker") ? 0 : 1; | ||
| 2587 | |||
| 2588 | // 1b. Second run reuses it, still SQLite, still no wallet.dat. | ||
| 2589 | int r1b = RunBitflashChild(strExe, a1); | ||
| 2590 | nFail += Check(r1b == 0 && | ||
| 2591 | FileExists((d1 + "/wallet.sqlite").c_str()) && | ||
| 2592 | !FileExists((d1 + "/wallet.dat").c_str()), | ||
| 2593 | "the recorded SQLite choice survives a restart") ? 0 : 1; | ||
| 2594 | |||
| 2595 | // 2. Existing wallet.dat, no flag -> stays on Berkeley DB. | ||
| 2596 | string d2 = tmp + "/existing-bdb"; | ||
| 2597 | nFail += Check(MakeDirLocal(d2), "existing-bdb scenario dir can be created") ? 0 : 1; | ||
| 2598 | vector<string> a2c; | ||
| 2599 | a2c.push_back("-datadir=" + d2); | ||
| 2600 | a2c.push_back("-nomanagedtor"); | ||
| 2601 | a2c.push_back("-nogui"); | ||
| 2602 | a2c.push_back("-walletbackend=bdb"); | ||
| 2603 | a2c.push_back("-newphrase"); | ||
| 2604 | RunBitflashChild(strExe, a2c); | ||
| 2605 | vector<string> a2; | ||
| 2606 | a2.push_back("-datadir=" + d2); | ||
| 2607 | a2.push_back("-nomanagedtor"); | ||
| 2608 | a2.push_back("-nogui"); | ||
| 2609 | a2.push_back("-newaddress"); | ||
| 2610 | int r2 = RunBitflashChild(strExe, a2); | ||
| 2611 | nFail += Check(r2 == 0 && | ||
| 2612 | FileExists((d2 + "/wallet.dat").c_str()) && | ||
| 2613 | !FileExists((d2 + "/wallet.sqlite").c_str()), | ||
| 2614 | "an existing wallet.dat keeps a flagless start on Berkeley DB") ? 0 : 1; | ||
| 2615 | |||
| 2616 | // 3. Marker "bdb" forces Berkeley DB even on a fresh datadir. | ||
| 2617 | string d3 = tmp + "/marker-bdb"; | ||
| 2618 | nFail += Check(MakeDirLocal(d3), "marker-bdb scenario dir can be created") ? 0 : 1; | ||
| 2619 | nFail += Check(WriteTextFile(d3 + "/wallet-backend", "bdb\n"), | ||
| 2620 | "a bdb marker can be written") ? 0 : 1; | ||
| 2621 | vector<string> a3; | ||
| 2622 | a3.push_back("-datadir=" + d3); | ||
| 2623 | a3.push_back("-nomanagedtor"); | ||
| 2624 | a3.push_back("-nogui"); | ||
| 2625 | a3.push_back("-newaddress"); | ||
| 2626 | int r3 = RunBitflashChild(strExe, a3); | ||
| 2627 | nFail += Check(r3 == 0 && | ||
| 2628 | FileExists((d3 + "/wallet.dat").c_str()) && | ||
| 2629 | !FileExists((d3 + "/wallet.sqlite").c_str()), | ||
| 2630 | "a bdb marker forces Berkeley DB on a fresh datadir") ? 0 : 1; | ||
| 2631 | |||
| 2632 | // 4. Explicit -walletbackend=bdb on a fresh datadir -> wallet.dat. | ||
| 2633 | string d4 = tmp + "/explicit-bdb"; | ||
| 2634 | nFail += Check(MakeDirLocal(d4), "explicit-bdb scenario dir can be created") ? 0 : 1; | ||
| 2635 | vector<string> a4; | ||
| 2636 | a4.push_back("-datadir=" + d4); | ||
| 2637 | a4.push_back("-nomanagedtor"); | ||
| 2638 | a4.push_back("-nogui"); | ||
| 2639 | a4.push_back("-walletbackend=bdb"); | ||
| 2640 | a4.push_back("-newaddress"); | ||
| 2641 | int r4 = RunBitflashChild(strExe, a4); | ||
| 2642 | nFail += Check(r4 == 0 && | ||
| 2643 | FileExists((d4 + "/wallet.dat").c_str()) && | ||
| 2644 | !FileExists((d4 + "/wallet.sqlite").c_str()), | ||
| 2645 | "an explicit -walletbackend=bdb creates wallet.dat on a fresh datadir") ? 0 : 1; | ||
| 2646 | } | ||
| 2647 | catch (const std::exception& e) | ||
| 2648 | { | ||
| 2649 | printf(" FAIL exception: %s\n", e.what()); | ||
| 2650 | nFail++; | ||
| 2651 | } | ||
| 2652 | catch (...) | ||
| 2653 | { | ||
| 2654 | printf(" FAIL unknown exception\n"); | ||
| 2655 | nFail++; | ||
| 2656 | } | ||
| 2657 | |||
| 2658 | DBFlush(true); | ||
| 2659 | SetCurrentDir(cwd); | ||
| 2660 | RemoveTree(tmp); | ||
| 2661 | printf("%s (%d failure%s)\n", nFail == 0 ? "ALL TESTS PASSED" : "TESTS FAILED", | ||
| 2662 | nFail, nFail == 1 ? "" : "s"); | ||
| 2663 | fflush(stdout); | ||
| 2664 | return nFail == 0 ? 0 : 1; | ||
| 2665 | } | ||
| 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. | ||
| 2671 | static 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 | |||
| 2537 | static int RunWalletPortabilitySelfTest() | 2771 | static int RunWalletPortabilitySelfTest() |
| 2538 | { | 2772 | { |
| 2539 | fflush(stdout); | 2773 | fflush(stdout); |
| @@ -2576,6 +2810,7 @@ static int RunWalletPortabilitySelfTest() | |||
| 2576 | vCreateArgs.push_back("-datadir=" + strOriginal); | 2810 | vCreateArgs.push_back("-datadir=" + strOriginal); |
| 2577 | vCreateArgs.push_back("-nomanagedtor"); | 2811 | vCreateArgs.push_back("-nomanagedtor"); |
| 2578 | vCreateArgs.push_back("-nogui"); | 2812 | vCreateArgs.push_back("-nogui"); |
| 2813 | vCreateArgs.push_back("-walletbackend=bdb"); // this suite is about wallet.dat portability | ||
| 2579 | vCreateArgs.push_back("-newphrase"); | 2814 | vCreateArgs.push_back("-newphrase"); |
| 2580 | int nCreateRet = RunBitflashChild(strExe, vCreateArgs, NULL, &strCreateOut); | 2815 | int nCreateRet = RunBitflashChild(strExe, vCreateArgs, NULL, &strCreateOut); |
| 2581 | nFail += Check(nCreateRet == 0 && | 2816 | nFail += Check(nCreateRet == 0 && |
| @@ -3265,6 +3500,10 @@ int RunSelfTest(const std::string& name) | |||
| 3265 | return RunWalletEncryptSelfTest(); | 3500 | return RunWalletEncryptSelfTest(); |
| 3266 | if (name == "wallet-sqlite-encrypt") | 3501 | if (name == "wallet-sqlite-encrypt") |
| 3267 | return RunWalletSQLiteEncryptSelfTest(); | 3502 | return RunWalletSQLiteEncryptSelfTest(); |
| 3503 | if (name == "wallet-backend-default") | ||
| 3504 | return RunWalletBackendDefaultSelfTest(); | ||
| 3505 | if (name == "wallet-convert") | ||
| 3506 | return RunWalletConvertSelfTest(); | ||
| 3268 | if (name == "wallet-portability") | 3507 | if (name == "wallet-portability") |
| 3269 | return RunWalletPortabilitySelfTest(); | 3508 | return RunWalletPortabilitySelfTest(); |
| 3270 | if (name == "net-message") | 3509 | if (name == "net-message") |
| @@ -3281,6 +3520,6 @@ int RunSelfTest(const std::string& name) | |||
| 3281 | return RunManagedTorSelfTest(); | 3520 | return RunManagedTorSelfTest(); |
| 3282 | 3521 | ||
| 3283 | printf("Unknown self-test '%s'\n", name.c_str()); | 3522 | printf("Unknown self-test '%s'\n", name.c_str()); |
| 3284 | 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-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"); |
| 3285 | return 1; | 3524 | return 1; |
| 3286 | } | 3525 | } |