diff options
| author | Bitflash <admin@bitflash.network> | 2026-08-14 12:20:01 -0300 |
|---|---|---|
| committer | Bitflash <admin@bitflash.network> | 2026-08-14 12:20:01 -0300 |
| commit | 94cda9794f302ca7b5e8b6f192c98ddcb3d4a12e (patch) | |
| tree | 1181df60aaefb3d67c85dd9f5aa816f787835fc1 /src/selftest.cpp | |
| parent | 225a89b643a79b73351abe530d6eb7af8906be63 (diff) | |
| parent | 83b9c4feddeac14bbc5edf63b44d3a2013384ecc (diff) | |
Merge pull request 'tests: prove wallet storage sanity failures' (#44) from tests-wallet-storage-sanity-negative into main
Diffstat (limited to 'src/selftest.cpp')
| -rw-r--r-- | src/selftest.cpp | 187 |
1 files changed, 186 insertions, 1 deletions
diff --git a/src/selftest.cpp b/src/selftest.cpp index 81c4439..81aa480 100644 --- a/src/selftest.cpp +++ b/src/selftest.cpp | |||
| @@ -736,6 +736,189 @@ static int RunWalletFormatSelfTest() | |||
| 736 | return nFail == 0 ? 0 : 1; | 736 | return nFail == 0 ? 0 : 1; |
| 737 | } | 737 | } |
| 738 | 738 | ||
| 739 | static bool FileContainsText(const string& strPath, const string& strNeedle); | ||
| 740 | static bool MakeDirLocal(const string& strPath); | ||
| 741 | static int RunBitflashChild(const string& strExe, | ||
| 742 | const vector<string>& vArgs, | ||
| 743 | const string* pStdinFile, | ||
| 744 | const string* pOutputFile); | ||
| 745 | |||
| 746 | class CWalletStorageSanityRawDB : public CWalletDB | ||
| 747 | { | ||
| 748 | public: | ||
| 749 | CWalletStorageSanityRawDB(const char* pszMode="r+") | ||
| 750 | : CWalletDB(pszMode) { } | ||
| 751 | |||
| 752 | bool WriteUnknownRecord() | ||
| 753 | { | ||
| 754 | return Write(string("storage-sanity-unknown"), 1); | ||
| 755 | } | ||
| 756 | |||
| 757 | bool WriteMalformedKeyRecord() | ||
| 758 | { | ||
| 759 | if (!pdb) | ||
| 760 | return false; | ||
| 761 | unsigned char chKey = 0xff; | ||
| 762 | unsigned char chValue = 0x01; | ||
| 763 | Dbt datKey(&chKey, 1); | ||
| 764 | Dbt datValue(&chValue, 1); | ||
| 765 | return pdb->put(GetTxn(), &datKey, &datValue, 0) == 0; | ||
| 766 | } | ||
| 767 | |||
| 768 | bool MakePlainHDSeedIncomplete() | ||
| 769 | { | ||
| 770 | return Erase(string("hdchaincode")); | ||
| 771 | } | ||
| 772 | |||
| 773 | bool AddEncryptedMarkerToPlainWallet() | ||
| 774 | { | ||
| 775 | CWalletMasterKey kMasterKey; | ||
| 776 | kMasterKey.vchCryptedKey.resize(48); | ||
| 777 | for (size_t i = 0; i < kMasterKey.vchCryptedKey.size(); i++) | ||
| 778 | kMasterKey.vchCryptedKey[i] = (unsigned char)(0x80 + (i & 0x3f)); | ||
| 779 | for (size_t i = 0; i < kMasterKey.vchSalt.size(); i++) | ||
| 780 | kMasterKey.vchSalt[i] = (unsigned char)(0x40 + i); | ||
| 781 | return WriteWalletMinVersion(WALLET_FORMAT_ENCRYPTED) && | ||
| 782 | WriteMasterKey(1, kMasterKey); | ||
| 783 | } | ||
| 784 | }; | ||
| 785 | |||
| 786 | int RunSelfTestMutateWallet(const std::string& name) | ||
| 787 | { | ||
| 788 | bool fOk = false; | ||
| 789 | try | ||
| 790 | { | ||
| 791 | CWalletStorageSanityRawDB walletdb; | ||
| 792 | if (name == "unknown-record") | ||
| 793 | fOk = walletdb.WriteUnknownRecord(); | ||
| 794 | else if (name == "malformed-key") | ||
| 795 | fOk = walletdb.WriteMalformedKeyRecord(); | ||
| 796 | else if (name == "incomplete-plain-hd") | ||
| 797 | fOk = walletdb.MakePlainHDSeedIncomplete(); | ||
| 798 | else if (name == "encrypted-marker-with-plain-keys") | ||
| 799 | fOk = walletdb.AddEncryptedMarkerToPlainWallet(); | ||
| 800 | else | ||
| 801 | { | ||
| 802 | fprintf(stderr, "Unknown wallet storage mutation '%s'\n", name.c_str()); | ||
| 803 | return 1; | ||
| 804 | } | ||
| 805 | } | ||
| 806 | catch (const std::exception& e) | ||
| 807 | { | ||
| 808 | fprintf(stderr, "Cannot mutate wallet storage: %s\n", e.what()); | ||
| 809 | return 1; | ||
| 810 | } | ||
| 811 | catch (...) | ||
| 812 | { | ||
| 813 | fprintf(stderr, "Cannot mutate wallet storage: unknown exception\n"); | ||
| 814 | return 1; | ||
| 815 | } | ||
| 816 | |||
| 817 | DBFlush(true); | ||
| 818 | return fOk ? 0 : 1; | ||
| 819 | } | ||
| 820 | |||
| 821 | static int RunWalletStorageSanitySelfTest() | ||
| 822 | { | ||
| 823 | fflush(stdout); | ||
| 824 | printf("wallet-storage-sanity self-test\n"); | ||
| 825 | |||
| 826 | std::string tmp; | ||
| 827 | std::string cwd; | ||
| 828 | if (!MakeTempDir(tmp)) | ||
| 829 | { | ||
| 830 | printf(" FAIL could not create a temporary data directory\n"); | ||
| 831 | return 1; | ||
| 832 | } | ||
| 833 | if (!GetCurrentDir(cwd)) | ||
| 834 | { | ||
| 835 | printf(" FAIL could not read current directory\n"); | ||
| 836 | RemoveTree(tmp); | ||
| 837 | return 1; | ||
| 838 | } | ||
| 839 | |||
| 840 | int nFail = 0; | ||
| 841 | printf(" temp root: %s\n", tmp.c_str()); | ||
| 842 | |||
| 843 | try | ||
| 844 | { | ||
| 845 | #ifdef _WIN32 | ||
| 846 | string strExe = cwd + "\\bitflash.exe"; | ||
| 847 | #else | ||
| 848 | string strExe = cwd + "/bitflash-node"; | ||
| 849 | #endif | ||
| 850 | |||
| 851 | struct StorageScenario | ||
| 852 | { | ||
| 853 | const char* pszName; | ||
| 854 | const char* pszExpectedFailure; | ||
| 855 | }; | ||
| 856 | |||
| 857 | StorageScenario scenarios[] = { | ||
| 858 | { "unknown-record", "wallet.dat contains unknown records" }, | ||
| 859 | { "malformed-key", "wallet.dat contains malformed records" }, | ||
| 860 | { "incomplete-plain-hd", "plain HD seed is incomplete" }, | ||
| 861 | { "encrypted-marker-with-plain-keys", | ||
| 862 | "encrypted wallet still contains plain private key records" }, | ||
| 863 | }; | ||
| 864 | |||
| 865 | for (size_t i = 0; i < sizeof(scenarios)/sizeof(scenarios[0]); i++) | ||
| 866 | { | ||
| 867 | string strScenarioDir = tmp + "/" + scenarios[i].pszName; | ||
| 868 | nFail += Check(MakeDirLocal(strScenarioDir), | ||
| 869 | "scenario datadir can be created") ? 0 : 1; | ||
| 870 | |||
| 871 | string strCreateOut = tmp + "/" + scenarios[i].pszName + "-create.txt"; | ||
| 872 | vector<string> vCreateArgs; | ||
| 873 | vCreateArgs.push_back("-datadir=" + strScenarioDir); | ||
| 874 | vCreateArgs.push_back("-nomanagedtor"); | ||
| 875 | vCreateArgs.push_back("-nogui"); | ||
| 876 | vCreateArgs.push_back("-newphrase"); | ||
| 877 | int nCreateRet = RunBitflashChild(strExe, vCreateArgs, NULL, &strCreateOut); | ||
| 878 | nFail += Check(nCreateRet == 0 && | ||
| 879 | FileContainsText(strCreateOut, "Write these twelve words down"), | ||
| 880 | "scenario phrase wallet can be created") ? 0 : 1; | ||
| 881 | |||
| 882 | string strMutateOut = tmp + "/" + scenarios[i].pszName + "-mutate.txt"; | ||
| 883 | vector<string> vMutateArgs; | ||
| 884 | vMutateArgs.push_back("-datadir=" + strScenarioDir); | ||
| 885 | vMutateArgs.push_back("-nomanagedtor"); | ||
| 886 | vMutateArgs.push_back("-nogui"); | ||
| 887 | vMutateArgs.push_back("-selftestmutatewallet=" + string(scenarios[i].pszName)); | ||
| 888 | int nMutateRet = RunBitflashChild(strExe, vMutateArgs, NULL, &strMutateOut); | ||
| 889 | nFail += Check(nMutateRet == 0, scenarios[i].pszName) ? 0 : 1; | ||
| 890 | |||
| 891 | string strCheckOut = tmp + "/" + scenarios[i].pszName + "-check.txt"; | ||
| 892 | vector<string> vCheckArgs; | ||
| 893 | vCheckArgs.push_back("-datadir=" + strScenarioDir); | ||
| 894 | vCheckArgs.push_back("-nomanagedtor"); | ||
| 895 | vCheckArgs.push_back("-nogui"); | ||
| 896 | vCheckArgs.push_back("-walletstoragecheck"); | ||
| 897 | int nCheckRet = RunBitflashChild(strExe, vCheckArgs, NULL, &strCheckOut); | ||
| 898 | nFail += Check(nCheckRet == 2 && | ||
| 899 | FileContainsText(strCheckOut, "storage sanity: failed") && | ||
| 900 | FileContainsText(strCheckOut, scenarios[i].pszExpectedFailure), | ||
| 901 | scenarios[i].pszExpectedFailure) ? 0 : 1; | ||
| 902 | } | ||
| 903 | } | ||
| 904 | catch (const std::exception& e) | ||
| 905 | { | ||
| 906 | printf(" FAIL exception: %s\n", e.what()); | ||
| 907 | nFail++; | ||
| 908 | } | ||
| 909 | catch (...) | ||
| 910 | { | ||
| 911 | printf(" FAIL unknown exception\n"); | ||
| 912 | nFail++; | ||
| 913 | } | ||
| 914 | |||
| 915 | RemoveTree(tmp); | ||
| 916 | printf("%s (%d failure%s)\n", nFail == 0 ? "ALL TESTS PASSED" : "TESTS FAILED", | ||
| 917 | nFail, nFail == 1 ? "" : "s"); | ||
| 918 | fflush(stdout); | ||
| 919 | return nFail == 0 ? 0 : 1; | ||
| 920 | } | ||
| 921 | |||
| 739 | static void ClearWalletRuntimeForTest() | 922 | static void ClearWalletRuntimeForTest() |
| 740 | { | 923 | { |
| 741 | CRITICAL_BLOCK(cs_mapKeys) | 924 | CRITICAL_BLOCK(cs_mapKeys) |
| @@ -1991,6 +2174,8 @@ int RunSelfTest(const std::string& name) | |||
| 1991 | return RunWalletHDSelfTest(); | 2174 | return RunWalletHDSelfTest(); |
| 1992 | if (name == "wallet-format") | 2175 | if (name == "wallet-format") |
| 1993 | return RunWalletFormatSelfTest(); | 2176 | return RunWalletFormatSelfTest(); |
| 2177 | if (name == "wallet-storage-sanity") | ||
| 2178 | return RunWalletStorageSanitySelfTest(); | ||
| 1994 | if (name == "wallet-crypto") | 2179 | if (name == "wallet-crypto") |
| 1995 | return RunWalletCryptoSelfTest(); | 2180 | return RunWalletCryptoSelfTest(); |
| 1996 | if (name == "wallet-encrypt") | 2181 | if (name == "wallet-encrypt") |
| @@ -2011,6 +2196,6 @@ int RunSelfTest(const std::string& name) | |||
| 2011 | return RunManagedTorSelfTest(); | 2196 | return RunManagedTorSelfTest(); |
| 2012 | 2197 | ||
| 2013 | printf("Unknown self-test '%s'\n", name.c_str()); | 2198 | printf("Unknown self-test '%s'\n", name.c_str()); |
| 2014 | printf("Known self-tests: wallet-keypool, wallet-hd, wallet-format, wallet-crypto, wallet-encrypt, wallet-portability, net-message, consensus-limits, pool-stratum, parse-money, socks5-proxy, managed-tor\n"); | 2199 | printf("Known self-tests: wallet-keypool, wallet-hd, wallet-format, wallet-storage-sanity, wallet-crypto, wallet-encrypt, wallet-portability, net-message, consensus-limits, pool-stratum, parse-money, socks5-proxy, managed-tor\n"); |
| 2015 | return 1; | 2200 | return 1; |
| 2016 | } | 2201 | } |