diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/main_gui.cpp | 50 | ||||
| -rw-r--r-- | src/selftest.cpp | 187 | ||||
| -rw-r--r-- | src/selftest.h | 1 | ||||
| -rw-r--r-- | src/walletcmd.cpp | 5 |
4 files changed, 222 insertions, 21 deletions
diff --git a/src/main_gui.cpp b/src/main_gui.cpp index 8763211..feda428 100644 --- a/src/main_gui.cpp +++ b/src/main_gui.cpp | |||
| @@ -113,8 +113,9 @@ static void PrintUsage() | |||
| 113 | printf(" /debug\n"); | 113 | printf(" /debug\n"); |
| 114 | printf(" /gen\n"); | 114 | printf(" /gen\n"); |
| 115 | printf(" /nogui or /daemon\n"); | 115 | printf(" /nogui or /daemon\n"); |
| 116 | printf(" /selftest=wallet-keypool, wallet-hd, wallet-format, wallet-crypto, wallet-encrypt,\n"); | 116 | printf(" /selftest=wallet-keypool, wallet-hd, wallet-format, wallet-storage-sanity,\n"); |
| 117 | printf(" wallet-portability, net-message, consensus-limits, pool-stratum,\n"); | 117 | printf(" wallet-crypto, wallet-encrypt, wallet-portability, net-message,\n"); |
| 118 | printf(" consensus-limits, pool-stratum,\n"); | ||
| 118 | printf(" parse-money, socks5-proxy, or managed-tor\n"); | 119 | printf(" parse-money, socks5-proxy, or managed-tor\n"); |
| 119 | printf("\n"); | 120 | printf("\n"); |
| 120 | printf("Mining mode:\n"); | 121 | printf("Mining mode:\n"); |
| @@ -495,10 +496,37 @@ int main(int argc, char* argv[]) | |||
| 495 | 496 | ||
| 496 | ParseStartupArguments(argc, argv); | 497 | ParseStartupArguments(argc, argv); |
| 497 | 498 | ||
| 499 | // Hidden self-test helper. It must run before LoadWallet(): several | ||
| 500 | // storage-sanity scenarios deliberately make wallet.dat unsafe to load. | ||
| 501 | string strSelfTestMutateWallet = | ||
| 502 | argval2(argc, argv, "/selftestmutatewallet", "-selftestmutatewallet"); | ||
| 503 | if (!strSelfTestMutateWallet.empty()) | ||
| 504 | return RunSelfTestMutateWallet(strSelfTestMutateWallet); | ||
| 505 | |||
| 498 | string strSelfTest = argval2(argc, argv, "/selftest", "-selftest"); | 506 | string strSelfTest = argval2(argc, argv, "/selftest", "-selftest"); |
| 499 | if (!strSelfTest.empty()) | 507 | if (!strSelfTest.empty()) |
| 500 | return RunSelfTest(strSelfTest); | 508 | return RunSelfTest(strSelfTest); |
| 501 | 509 | ||
| 510 | // Storage diagnostics must run before LoadWallet(). Their job is to | ||
| 511 | // explain wallet.dat states that the normal wallet loader may refuse. | ||
| 512 | string strWalletStorageAuditJson = | ||
| 513 | argval2(argc, argv, "/walletstorageauditjson", "-walletstorageauditjson"); | ||
| 514 | if (arg(argc,argv,"/walletstorageaudit") || | ||
| 515 | arg(argc,argv,"-walletstorageaudit") || | ||
| 516 | !strWalletStorageAuditJson.empty()) | ||
| 517 | { | ||
| 518 | int nRet = CmdWalletStorageAudit(strWalletStorageAuditJson); | ||
| 519 | DBFlush(true); | ||
| 520 | return nRet; | ||
| 521 | } | ||
| 522 | |||
| 523 | if (arg(argc,argv,"/walletstoragecheck") || arg(argc,argv,"-walletstoragecheck")) | ||
| 524 | { | ||
| 525 | int nRet = CmdWalletStorageCheck(); | ||
| 526 | DBFlush(true); | ||
| 527 | return nRet; | ||
| 528 | } | ||
| 529 | |||
| 502 | // Berkeley DB reports failure by throwing, and CDB's constructor lets it | 530 | // Berkeley DB reports failure by throwing, and CDB's constructor lets it |
| 503 | // through. Nothing on this path caught anything, so an unreadable | 531 | // through. Nothing on this path caught anything, so an unreadable |
| 504 | // blkindex.dat or wallet.dat -- one written by another platform's Berkeley | 532 | // blkindex.dat or wallet.dat -- one written by another platform's Berkeley |
| @@ -710,24 +738,6 @@ int main(int argc, char* argv[]) | |||
| 710 | return nRet; | 738 | return nRet; |
| 711 | } | 739 | } |
| 712 | 740 | ||
| 713 | string strWalletStorageAuditJson = | ||
| 714 | argval2(argc, argv, "/walletstorageauditjson", "-walletstorageauditjson"); | ||
| 715 | if (arg(argc,argv,"/walletstorageaudit") || | ||
| 716 | arg(argc,argv,"-walletstorageaudit") || | ||
| 717 | !strWalletStorageAuditJson.empty()) | ||
| 718 | { | ||
| 719 | int nRet = CmdWalletStorageAudit(strWalletStorageAuditJson); | ||
| 720 | DBFlush(true); | ||
| 721 | return nRet; | ||
| 722 | } | ||
| 723 | |||
| 724 | if (arg(argc,argv,"/walletstoragecheck") || arg(argc,argv,"-walletstoragecheck")) | ||
| 725 | { | ||
| 726 | int nRet = CmdWalletStorageCheck(); | ||
| 727 | DBFlush(true); | ||
| 728 | return nRet; | ||
| 729 | } | ||
| 730 | |||
| 731 | if (arg(argc,argv,"/newphrase") || arg(argc,argv,"-newphrase")) | 741 | if (arg(argc,argv,"/newphrase") || arg(argc,argv,"-newphrase")) |
| 732 | { | 742 | { |
| 733 | int nRet = CmdNewPhrase(); | 743 | int nRet = CmdNewPhrase(); |
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 | } |
diff --git a/src/selftest.h b/src/selftest.h index d3699c6..0ffe232 100644 --- a/src/selftest.h +++ b/src/selftest.h | |||
| @@ -7,5 +7,6 @@ | |||
| 7 | #include <string> | 7 | #include <string> |
| 8 | 8 | ||
| 9 | int RunSelfTest(const std::string& name); | 9 | int RunSelfTest(const std::string& name); |
| 10 | int RunSelfTestMutateWallet(const std::string& name); | ||
| 10 | 11 | ||
| 11 | #endif | 12 | #endif |
diff --git a/src/walletcmd.cpp b/src/walletcmd.cpp index 71f1ec3..6fb09a3 100644 --- a/src/walletcmd.cpp +++ b/src/walletcmd.cpp | |||
| @@ -573,6 +573,11 @@ static bool ReadWalletStorageCounts(WalletStorageAuditCounts& counts, | |||
| 573 | CDataStream ssKey = ssKeyIn; | 573 | CDataStream ssKey = ssKeyIn; |
| 574 | std::string strType; | 574 | std::string strType; |
| 575 | ssKey >> strType; | 575 | ssKey >> strType; |
| 576 | if (ssKey.fail()) | ||
| 577 | { | ||
| 578 | counts.nMalformed++; | ||
| 579 | return true; | ||
| 580 | } | ||
| 576 | CountWalletStorageType(strType, counts); | 581 | CountWalletStorageType(strType, counts); |
| 577 | } | 582 | } |
| 578 | catch (...) | 583 | catch (...) |