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:
Diffstat (limited to 'src/selftest.cpp')
-rw-r--r--src/selftest.cpp504
1 files changed, 503 insertions, 1 deletions
diff --git a/src/selftest.cpp b/src/selftest.cpp
index aee2951..ff0289b 100644
--- a/src/selftest.cpp
+++ b/src/selftest.cpp
@@ -33,6 +33,8 @@
33 33
34#ifdef _WIN32 34#ifdef _WIN32
35#include <direct.h> 35#include <direct.h>
36#include <fcntl.h>
37#include <io.h>
36#else 38#else
37#include <dirent.h> 39#include <dirent.h>
38#include <sys/stat.h> 40#include <sys/stat.h>
@@ -601,6 +603,500 @@ static int RunWalletHDSelfTest()
601 return nFail == 0 ? 0 : 1; 603 return nFail == 0 ? 0 : 1;
602} 604}
603 605
606static int RunWalletFormatSelfTest()
607{
608 fflush(stdout);
609 printf("wallet-format self-test\n");
610
611 std::string tmp;
612 std::string cwd;
613 if (!MakeTempDir(tmp))
614 {
615 printf(" FAIL could not create a temporary data directory\n");
616 return 1;
617 }
618 if (!GetCurrentDir(cwd) || !SetCurrentDir(tmp))
619 {
620 printf(" FAIL could not move into the temporary data directory\n");
621 RemoveTree(tmp);
622 return 1;
623 }
624
625 int nFail = 0;
626 strSetDataDir = tmp;
627 printf(" temp datadir: %s\n", tmp.c_str());
628
629 try
630 {
631 class CWalletDBRaw : public CWalletDB
632 {
633 public:
634 CWalletDBRaw(const char* pszMode="r+") : CWalletDB(pszMode) { }
635 bool WriteStringRecord(const string& strType, int nValue)
636 {
637 return Write(strType, nValue);
638 }
639 bool EraseStringRecord(const string& strType)
640 {
641 return Erase(strType);
642 }
643 };
644
645 if (!LoadWallet())
646 throw std::runtime_error("LoadWallet failed");
647
648 {
649 CWalletDB walletdb;
650 nFail += Check(walletdb.WriteWalletMinVersion(WALLET_FORMAT_SUPPORTED),
651 "the current wallet format marker can be written") ? 0 : 1;
652 }
653
654 std::vector<unsigned char> vchDefaultKey;
655 nFail += Check(CWalletDB("r").LoadWallet(vchDefaultKey),
656 "the current wallet format marker loads") ? 0 : 1;
657
658 {
659 CWalletDBRaw walletdb;
660 nFail += Check(walletdb.WriteStringRecord("mkey", 1),
661 "a malformed encrypted master-key record can be written") ? 0 : 1;
662 }
663
664 vchDefaultKey.clear();
665 nFail += Check(!CWalletDB("r").LoadWallet(vchDefaultKey),
666 "a malformed encrypted master-key record is refused") ? 0 : 1;
667
668 {
669 CWalletDBRaw walletdb;
670 nFail += Check(walletdb.EraseStringRecord("mkey"),
671 "the malformed encrypted master-key record can be removed") ? 0 : 1;
672 }
673
674 {
675 CWalletDB walletdb;
676 nFail += Check(walletdb.WriteWalletMinVersion(WALLET_FORMAT_SUPPORTED + 1),
677 "a future wallet format marker can be written") ? 0 : 1;
678 }
679
680 vchDefaultKey.clear();
681 nFail += Check(!CWalletDB("r").LoadWallet(vchDefaultKey),
682 "a future wallet format marker is refused") ? 0 : 1;
683 }
684 catch (const std::exception& e)
685 {
686 printf(" FAIL exception: %s\n", e.what());
687 nFail++;
688 }
689 catch (...)
690 {
691 printf(" FAIL unknown exception\n");
692 nFail++;
693 }
694
695 DBFlush(true);
696 SetCurrentDir(cwd);
697 RemoveTree(tmp);
698 printf("%s (%d failure%s)\n", nFail == 0 ? "ALL TESTS PASSED" : "TESTS FAILED",
699 nFail, nFail == 1 ? "" : "s");
700 fflush(stdout);
701 return nFail == 0 ? 0 : 1;
702}
703
704static void ClearWalletRuntimeForTest()
705{
706 CRITICAL_BLOCK(cs_mapKeys)
707 {
708 mapKeys.clear();
709 mapCryptedKeys.clear();
710 mapPubKeys.clear();
711 mapMasterKeys.clear();
712 vWalletMasterKey.clear();
713 vchCryptedHDMaster.clear();
714 vchCryptedHDChainCode.clear();
715 fWalletEncrypted = false;
716 fWalletLocked = true;
717 }
718 CRITICAL_BLOCK(cs_keyPool)
719 mapKeyPool.clear();
720 vchHDMaster.clear();
721 vchHDChainCode.clear();
722 nHDNext = 0;
723}
724
725static int RunWalletCryptoSelfTest()
726{
727 fflush(stdout);
728 printf("wallet-crypto self-test\n");
729
730 std::string tmp;
731 std::string cwd;
732 if (!MakeTempDir(tmp))
733 {
734 printf(" FAIL could not create a temporary data directory\n");
735 return 1;
736 }
737 if (!GetCurrentDir(cwd) || !SetCurrentDir(tmp))
738 {
739 printf(" FAIL could not move into the temporary data directory\n");
740 RemoveTree(tmp);
741 return 1;
742 }
743
744 int nFail = 0;
745 strSetDataDir = tmp;
746 printf(" temp datadir: %s\n", tmp.c_str());
747
748 try
749 {
750 const string strPassphrase = "btf-test-passphrase";
751 const string strWrongPassphrase = "btf-test-passphraser";
752
753 CKey key;
754 key.MakeNewKey();
755 vector<unsigned char> vchPubKey = key.GetPubKey();
756 CPrivKey vchPrivKey = key.GetPrivKey();
757
758 CKeyingMaterial vchMasterKey;
759 for (int i = 0; i < 32; i++)
760 vchMasterKey.push_back((unsigned char)(i + 1));
761
762 CWalletMasterKey kMasterKey;
763 kMasterKey.vchSalt.clear();
764 for (int i = 0; i < 8; i++)
765 kMasterKey.vchSalt.push_back((unsigned char)(0xa0 + i));
766 kMasterKey.nDeriveIterations = 2500;
767
768 CKeyingMaterial vchPassKey;
769 vector<unsigned char> vchPassIV;
770 nFail += Check(DeriveWalletPassphraseKey(strPassphrase, kMasterKey.vchSalt,
771 kMasterKey.nDeriveIterations,
772 vchPassKey, vchPassIV),
773 "a passphrase key can be derived") ? 0 : 1;
774 nFail += Check(EncryptSecret(vchPassKey,
775 vector<unsigned char>(vchMasterKey.begin(), vchMasterKey.end()),
776 vchPassIV, kMasterKey.vchCryptedKey),
777 "the wallet master key can be encrypted") ? 0 : 1;
778
779 vector<unsigned char> vchCryptedKey;
780 nFail += Check(EncryptSecret(vchMasterKey,
781 vector<unsigned char>(vchPrivKey.begin(), vchPrivKey.end()),
782 WalletKeyIV(vchPubKey), vchCryptedKey),
783 "a private key can be encrypted under the wallet master key") ? 0 : 1;
784
785 {
786 CWalletDB walletdb("cr");
787 nFail += Check(walletdb.WriteWalletMinVersion(WALLET_FORMAT_ENCRYPTED),
788 "an encrypted wallet format marker can be written") ? 0 : 1;
789 nFail += Check(walletdb.WriteMasterKey(0, kMasterKey),
790 "the encrypted master key can be written") ? 0 : 1;
791 nFail += Check(walletdb.WriteCryptedKey(vchPubKey, vchCryptedKey),
792 "the encrypted private key can be written") ? 0 : 1;
793 nFail += Check(walletdb.WriteDefaultKey(vchPubKey),
794 "the encrypted wallet default key can be written") ? 0 : 1;
795 }
796
797 ClearWalletRuntimeForTest();
798
799 vector<unsigned char> vchDefaultKey;
800 nFail += Check(CWalletDB("r").LoadWallet(vchDefaultKey),
801 "an encrypted wallet can be loaded") ? 0 : 1;
802 nFail += Check(vchDefaultKey == vchPubKey,
803 "the encrypted wallet default public key loads") ? 0 : 1;
804 nFail += Check(IsWalletEncrypted() && IsWalletLocked(),
805 "an encrypted wallet loads locked") ? 0 : 1;
806 nFail += Check(WalletCanSpendKey(vchPubKey),
807 "an encrypted key is recognized as wallet-owned while locked") ? 0 : 1;
808
809 CPrivKey vchDecrypted;
810 string strError;
811 nFail += Check(!GetWalletPrivKey(vchPubKey, vchDecrypted, strError),
812 "a locked encrypted key cannot be read") ? 0 : 1;
813 nFail += Check(!UnlockWallet(strWrongPassphrase, strError),
814 "the wrong-passphrase does not unlock") ? 0 : 1;
815 nFail += Check(UnlockWallet(strPassphrase, strError),
816 "the right passphrase unlocks") ? 0 : 1;
817 nFail += Check(!IsWalletLocked(),
818 "the wallet reports unlocked") ? 0 : 1;
819 nFail += Check(GetWalletPrivKey(vchPubKey, vchDecrypted, strError) &&
820 vchDecrypted == vchPrivKey,
821 "an unlocked encrypted key decrypts to the original private key") ? 0 : 1;
822 LockWallet();
823 nFail += Check(IsWalletLocked(),
824 "locking clears the unlocked state") ? 0 : 1;
825 }
826 catch (const std::exception& e)
827 {
828 printf(" FAIL exception: %s\n", e.what());
829 nFail++;
830 }
831 catch (...)
832 {
833 printf(" FAIL unknown exception\n");
834 nFail++;
835 }
836
837 DBFlush(true);
838 SetCurrentDir(cwd);
839 RemoveTree(tmp);
840 printf("%s (%d failure%s)\n", nFail == 0 ? "ALL TESTS PASSED" : "TESTS FAILED",
841 nFail, nFail == 1 ? "" : "s");
842 fflush(stdout);
843 return nFail == 0 ? 0 : 1;
844}
845
846static bool FileContainsBytes(const string& strPath, const vector<unsigned char>& vchNeedle)
847{
848 if (vchNeedle.empty())
849 return false;
850 FILE* pf = fopen(strPath.c_str(), "rb");
851 if (!pf)
852 return false;
853
854 vector<unsigned char> vchHaystack;
855 unsigned char buf[4096];
856 size_t n = 0;
857 while ((n = fread(buf, 1, sizeof(buf), pf)) > 0)
858 vchHaystack.insert(vchHaystack.end(), buf, buf + n);
859 fclose(pf);
860
861 if (vchHaystack.size() < vchNeedle.size())
862 return false;
863 return search(vchHaystack.begin(), vchHaystack.end(),
864 vchNeedle.begin(), vchNeedle.end()) != vchHaystack.end();
865}
866
867static bool FileContainsText(const string& strPath, const string& strNeedle)
868{
869 FILE* pf = fopen(strPath.c_str(), "rb");
870 if (!pf)
871 return false;
872
873 string strHaystack;
874 char buf[4096];
875 size_t n = 0;
876 while ((n = fread(buf, 1, sizeof(buf), pf)) > 0)
877 strHaystack.append(buf, buf + n);
878 fclose(pf);
879
880 return strHaystack.find(strNeedle) != string::npos;
881}
882
883static bool WriteTextFile(const string& strPath, const string& strText)
884{
885 FILE* pf = fopen(strPath.c_str(), "wb");
886 if (!pf)
887 return false;
888 bool fOk = fwrite(strText.data(), 1, strText.size(), pf) == strText.size();
889 if (fclose(pf) != 0)
890 fOk = false;
891 return fOk;
892}
893
894static string QuoteCommandArg(const string& str)
895{
896 string out = "\"";
897 for (size_t i = 0; i < str.size(); i++)
898 {
899 if (str[i] == '"')
900 out += "\\\"";
901 else
902 out += str[i];
903 }
904 out += "\"";
905 return out;
906}
907
908static int RunBitflashChild(const string& strExe,
909 const vector<string>& vArgs,
910 const string* pStdinFile = NULL)
911{
912#ifdef _WIN32
913 vector<char*> argv;
914 argv.push_back((char*)strExe.c_str());
915 for (size_t i = 0; i < vArgs.size(); i++)
916 argv.push_back((char*)vArgs[i].c_str());
917 argv.push_back(NULL);
918
919 int nOldOut = _dup(1);
920 int nOldErr = _dup(2);
921 int nOldIn = _dup(0);
922 int nNull = _open("NUL", _O_WRONLY);
923 int nIn = -1;
924 if (nNull >= 0)
925 {
926 _dup2(nNull, 1);
927 _dup2(nNull, 2);
928 }
929 if (pStdinFile)
930 {
931 nIn = _open(pStdinFile->c_str(), _O_RDONLY);
932 if (nIn >= 0)
933 _dup2(nIn, 0);
934 }
935 int nRet = _spawnv(_P_WAIT, strExe.c_str(), &argv[0]);
936 if (nIn >= 0)
937 _close(nIn);
938 if (nNull >= 0)
939 _close(nNull);
940 if (nOldIn >= 0)
941 {
942 _dup2(nOldIn, 0);
943 _close(nOldIn);
944 }
945 if (nOldOut >= 0)
946 {
947 _dup2(nOldOut, 1);
948 _close(nOldOut);
949 }
950 if (nOldErr >= 0)
951 {
952 _dup2(nOldErr, 2);
953 _close(nOldErr);
954 }
955 return nRet;
956#else
957 string strCmd = QuoteCommandArg(strExe);
958 for (size_t i = 0; i < vArgs.size(); i++)
959 strCmd += " " + QuoteCommandArg(vArgs[i]);
960 if (pStdinFile)
961 strCmd += " < " + QuoteCommandArg(*pStdinFile);
962 strCmd += " > /dev/null 2>&1";
963 return system(strCmd.c_str());
964#endif
965}
966
967static int RunWalletEncryptSelfTest()
968{
969 fflush(stdout);
970 printf("wallet-encrypt self-test\n");
971
972 std::string tmp;
973 std::string cwd;
974 if (!MakeTempDir(tmp))
975 {
976 printf(" FAIL could not create a temporary data directory\n");
977 return 1;
978 }
979 if (!GetCurrentDir(cwd) || !SetCurrentDir(tmp))
980 {
981 printf(" FAIL could not move into the temporary data directory\n");
982 RemoveTree(tmp);
983 return 1;
984 }
985
986 int nFail = 0;
987 bool fWalletEnvClosed = false;
988 strSetDataDir = tmp;
989 printf(" temp datadir: %s\n", tmp.c_str());
990
991 try
992 {
993 if (!LoadWallet())
994 throw std::runtime_error("LoadWallet failed");
995
996 string strError;
997 const string strMnemonic =
998 "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
999 nFail += Check(SetHDSeedFromMnemonic(strMnemonic, strError),
1000 "a phrase can be installed before encryption") ? 0 : 1;
1001 TopUpKeyPool();
1002
1003 vector<unsigned char> vchDefaultPubKey = keyUser.GetPubKey();
1004 CPrivKey vchDefaultPrivKey;
1005 nFail += Check(GetWalletPrivKey(vchDefaultPubKey, vchDefaultPrivKey, strError),
1006 "the default private key is readable before encryption") ? 0 : 1;
1007 vector<unsigned char> vchDefaultPrivBytes(vchDefaultPrivKey.begin(), vchDefaultPrivKey.end());
1008 vector<unsigned char> vchPlainHDMaster = vchHDMaster;
1009 vector<unsigned char> vchPlainHDChainCode = vchHDChainCode;
1010
1011 string strBackup;
1012 nFail += Check(EncryptWallet("btf-test-passphrase", strBackup, strError),
1013 "wallet.dat can be rewritten encrypted") ? 0 : 1;
1014 fWalletEnvClosed = true;
1015 nFail += Check(FileExists(strBackup.c_str()),
1016 "the original wallet is preserved as an explicit backup") ? 0 : 1;
1017
1018 string strWalletPath = GetAppDir() + "/wallet.dat";
1019 nFail += Check(!FileContainsBytes(strWalletPath, vchDefaultPrivBytes),
1020 "the active wallet no longer contains the default private key bytes") ? 0 : 1;
1021 nFail += Check(!FileContainsBytes(strWalletPath, vchPlainHDMaster),
1022 "the active wallet no longer contains the HD master key bytes") ? 0 : 1;
1023 nFail += Check(!FileContainsBytes(strWalletPath, vchPlainHDChainCode),
1024 "the active wallet no longer contains the HD chain-code bytes") ? 0 : 1;
1025
1026#ifdef _WIN32
1027 string strExe = cwd + "\\bitflash.exe";
1028#else
1029 string strExe = cwd + "/bitflash-node";
1030#endif
1031 string strWrongDump = tmp + "/wrong-pass-dump.txt";
1032 string strRightDump = tmp + "/right-pass-dump.txt";
1033 string strLiteralDump = tmp + "/literal-pass-dump.txt";
1034 string strStdinDump = tmp + "/stdin-pass-dump.txt";
1035 string strWrongPassFile = tmp + "/wrong-pass.txt";
1036 string strRightPassFile = tmp + "/right-pass.txt";
1037 nFail += Check(WriteTextFile(strWrongPassFile, "wrong-passphrase\n") &&
1038 WriteTextFile(strRightPassFile, "btf-test-passphrase\n"),
1039 "passphrase files can be written for restarted commands") ? 0 : 1;
1040
1041 vector<string> vWrongArgs;
1042 vWrongArgs.push_back("-datadir=" + tmp);
1043 vWrongArgs.push_back("-walletpassphrase=@" + strWrongPassFile);
1044 vWrongArgs.push_back("-dumpwallet=" + strWrongDump);
1045 vWrongArgs.push_back("-nogui");
1046 int nWrongRet = RunBitflashChild(strExe, vWrongArgs);
1047 nFail += Check(nWrongRet != 0 && !FileExists(strWrongDump.c_str()),
1048 "a restarted wallet rejects the wrong-passphrase") ? 0 : 1;
1049
1050 vector<string> vLiteralArgs;
1051 vLiteralArgs.push_back("-datadir=" + tmp);
1052 vLiteralArgs.push_back("-walletpassphrase=btf-test-passphrase");
1053 vLiteralArgs.push_back("-dumpwallet=" + strLiteralDump);
1054 vLiteralArgs.push_back("-nogui");
1055 int nLiteralRet = RunBitflashChild(strExe, vLiteralArgs);
1056 nFail += Check(nLiteralRet != 0 && !FileExists(strLiteralDump.c_str()),
1057 "literal passphrases on the command line are refused") ? 0 : 1;
1058
1059 vector<string> vRightArgs;
1060 vRightArgs.push_back("-datadir=" + tmp);
1061 vRightArgs.push_back("-walletpassphrase=@" + strRightPassFile);
1062 vRightArgs.push_back("-dumpwallet=" + strRightDump);
1063 vRightArgs.push_back("-nogui");
1064 int nRightRet = RunBitflashChild(strExe, vRightArgs);
1065 nFail += Check(nRightRet == 0 && FileExists(strRightDump.c_str()),
1066 "a restarted wallet unlocks with the right passphrase") ? 0 : 1;
1067 nFail += Check(FileContainsText(strRightDump, HexStrLocal(vchDefaultPrivBytes)),
1068 "the restarted wallet can decrypt and dump the original key") ? 0 : 1;
1069
1070 vector<string> vStdinArgs;
1071 vStdinArgs.push_back("-datadir=" + tmp);
1072 vStdinArgs.push_back("-walletpassphrase");
1073 vStdinArgs.push_back("-dumpwallet=" + strStdinDump);
1074 vStdinArgs.push_back("-nogui");
1075 int nStdinRet = RunBitflashChild(strExe, vStdinArgs, &strRightPassFile);
1076 nFail += Check(nStdinRet == 0 && FileExists(strStdinDump.c_str()),
1077 "a restarted wallet can read the passphrase from stdin") ? 0 : 1;
1078 }
1079 catch (const std::exception& e)
1080 {
1081 printf(" FAIL exception: %s\n", e.what());
1082 nFail++;
1083 }
1084 catch (...)
1085 {
1086 printf(" FAIL unknown exception\n");
1087 nFail++;
1088 }
1089
1090 if (!fWalletEnvClosed)
1091 DBFlush(true);
1092 SetCurrentDir(cwd);
1093 RemoveTree(tmp);
1094 printf("%s (%d failure%s)\n", nFail == 0 ? "ALL TESTS PASSED" : "TESTS FAILED",
1095 nFail, nFail == 1 ? "" : "s");
1096 fflush(stdout);
1097 return nFail == 0 ? 0 : 1;
1098}
1099
604static int RunNetMessageSelfTest() 1100static int RunNetMessageSelfTest()
605{ 1101{
606 fflush(stdout); 1102 fflush(stdout);
@@ -728,12 +1224,18 @@ int RunSelfTest(const std::string& name)
728 return RunWalletKeyPoolSelfTest(); 1224 return RunWalletKeyPoolSelfTest();
729 if (name == "wallet-hd") 1225 if (name == "wallet-hd")
730 return RunWalletHDSelfTest(); 1226 return RunWalletHDSelfTest();
1227 if (name == "wallet-format")
1228 return RunWalletFormatSelfTest();
1229 if (name == "wallet-crypto")
1230 return RunWalletCryptoSelfTest();
1231 if (name == "wallet-encrypt")
1232 return RunWalletEncryptSelfTest();
731 if (name == "net-message") 1233 if (name == "net-message")
732 return RunNetMessageSelfTest(); 1234 return RunNetMessageSelfTest();
733 if (name == "consensus-limits") 1235 if (name == "consensus-limits")
734 return RunConsensusLimitsSelfTest(); 1236 return RunConsensusLimitsSelfTest();
735 1237
736 printf("Unknown self-test '%s'\n", name.c_str()); 1238 printf("Unknown self-test '%s'\n", name.c_str());
737 printf("Known self-tests: wallet-keypool, wallet-hd, net-message, consensus-limits\n"); 1239 printf("Known self-tests: wallet-keypool, wallet-hd, wallet-format, wallet-crypto, wallet-encrypt, net-message, consensus-limits\n");
738 return 1; 1240 return 1;
739} 1241}