diff options
| -rw-r--r-- | src/main.cpp | 3 | ||||
| -rw-r--r-- | src/main.h | 2 | ||||
| -rw-r--r-- | src/selftest.cpp | 44 | ||||
| -rw-r--r-- | src/walletcmd.cpp | 16 |
4 files changed, 61 insertions, 4 deletions
diff --git a/src/main.cpp b/src/main.cpp index f83164f..5bd4028 100644 --- a/src/main.cpp +++ b/src/main.cpp | |||
| @@ -4355,6 +4355,9 @@ WalletRecoveryAudit GetWalletRecoveryAudit() | |||
| 4355 | CRITICAL_BLOCK(cs_keyPool) | 4355 | CRITICAL_BLOCK(cs_keyPool) |
| 4356 | { | 4356 | { |
| 4357 | audit.fHaveSeed = HaveHDSeed(); | 4357 | audit.fHaveSeed = HaveHDSeed(); |
| 4358 | audit.fSeedEncryptedLocked = !audit.fHaveSeed && | ||
| 4359 | (!vchCryptedHDMaster.empty() || | ||
| 4360 | !vchCryptedHDChainCode.empty()); | ||
| 4358 | audit.nSchema = nHDKeySchema; | 4361 | audit.nSchema = nHDKeySchema; |
| 4359 | audit.nDerivedKnown = nHDNext; | 4362 | audit.nDerivedKnown = nHDNext; |
| 4360 | audit.nReceiveNext = nHDReceiveNext; | 4363 | audit.nReceiveNext = nHDReceiveNext; |
| @@ -292,6 +292,7 @@ int64 GetBalance(); | |||
| 292 | struct WalletRecoveryAudit | 292 | struct WalletRecoveryAudit |
| 293 | { | 293 | { |
| 294 | bool fHaveSeed; | 294 | bool fHaveSeed; |
| 295 | bool fSeedEncryptedLocked; | ||
| 295 | bool fDeriveComplete; | 296 | bool fDeriveComplete; |
| 296 | int nSchema; | 297 | int nSchema; |
| 297 | unsigned int nDerivedKnown; | 298 | unsigned int nDerivedKnown; |
| @@ -311,6 +312,7 @@ struct WalletRecoveryAudit | |||
| 311 | WalletRecoveryAudit() | 312 | WalletRecoveryAudit() |
| 312 | { | 313 | { |
| 313 | fHaveSeed = false; | 314 | fHaveSeed = false; |
| 315 | fSeedEncryptedLocked = false; | ||
| 314 | fDeriveComplete = true; | 316 | fDeriveComplete = true; |
| 315 | nSchema = HD_SCHEMA_NONE; | 317 | nSchema = HD_SCHEMA_NONE; |
| 316 | nDerivedKnown = 0; | 318 | nDerivedKnown = 0; |
diff --git a/src/selftest.cpp b/src/selftest.cpp index 3c93927..79feddf 100644 --- a/src/selftest.cpp +++ b/src/selftest.cpp | |||
| @@ -941,7 +941,8 @@ static string QuoteCommandArg(const string& str) | |||
| 941 | 941 | ||
| 942 | static int RunBitflashChild(const string& strExe, | 942 | static int RunBitflashChild(const string& strExe, |
| 943 | const vector<string>& vArgs, | 943 | const vector<string>& vArgs, |
| 944 | const string* pStdinFile = NULL) | 944 | const string* pStdinFile = NULL, |
| 945 | const string* pOutputFile = NULL) | ||
| 945 | { | 946 | { |
| 946 | #ifdef _WIN32 | 947 | #ifdef _WIN32 |
| 947 | vector<char*> argv; | 948 | vector<char*> argv; |
| @@ -954,8 +955,16 @@ static int RunBitflashChild(const string& strExe, | |||
| 954 | int nOldErr = _dup(2); | 955 | int nOldErr = _dup(2); |
| 955 | int nOldIn = _dup(0); | 956 | int nOldIn = _dup(0); |
| 956 | int nNull = _open("NUL", _O_WRONLY); | 957 | int nNull = _open("NUL", _O_WRONLY); |
| 958 | int nOut = -1; | ||
| 957 | int nIn = -1; | 959 | int nIn = -1; |
| 958 | if (nNull >= 0) | 960 | if (pOutputFile) |
| 961 | nOut = _open(pOutputFile->c_str(), _O_WRONLY|_O_CREAT|_O_TRUNC|_O_BINARY, _S_IREAD|_S_IWRITE); | ||
| 962 | if (nOut >= 0) | ||
| 963 | { | ||
| 964 | _dup2(nOut, 1); | ||
| 965 | _dup2(nOut, 2); | ||
| 966 | } | ||
| 967 | else if (nNull >= 0) | ||
| 959 | { | 968 | { |
| 960 | _dup2(nNull, 1); | 969 | _dup2(nNull, 1); |
| 961 | _dup2(nNull, 2); | 970 | _dup2(nNull, 2); |
| @@ -969,6 +978,8 @@ static int RunBitflashChild(const string& strExe, | |||
| 969 | int nRet = _spawnv(_P_WAIT, strExe.c_str(), &argv[0]); | 978 | int nRet = _spawnv(_P_WAIT, strExe.c_str(), &argv[0]); |
| 970 | if (nIn >= 0) | 979 | if (nIn >= 0) |
| 971 | _close(nIn); | 980 | _close(nIn); |
| 981 | if (nOut >= 0) | ||
| 982 | _close(nOut); | ||
| 972 | if (nNull >= 0) | 983 | if (nNull >= 0) |
| 973 | _close(nNull); | 984 | _close(nNull); |
| 974 | if (nOldIn >= 0) | 985 | if (nOldIn >= 0) |
| @@ -993,7 +1004,10 @@ static int RunBitflashChild(const string& strExe, | |||
| 993 | strCmd += " " + QuoteCommandArg(vArgs[i]); | 1004 | strCmd += " " + QuoteCommandArg(vArgs[i]); |
| 994 | if (pStdinFile) | 1005 | if (pStdinFile) |
| 995 | strCmd += " < " + QuoteCommandArg(*pStdinFile); | 1006 | strCmd += " < " + QuoteCommandArg(*pStdinFile); |
| 996 | strCmd += " > /dev/null 2>&1"; | 1007 | if (pOutputFile) |
| 1008 | strCmd += " > " + QuoteCommandArg(*pOutputFile) + " 2>&1"; | ||
| 1009 | else | ||
| 1010 | strCmd += " > /dev/null 2>&1"; | ||
| 997 | return system(strCmd.c_str()); | 1011 | return system(strCmd.c_str()); |
| 998 | #endif | 1012 | #endif |
| 999 | } | 1013 | } |
| @@ -1066,6 +1080,8 @@ static int RunWalletEncryptSelfTest() | |||
| 1066 | string strRightDump = tmp + "/right-pass-dump.txt"; | 1080 | string strRightDump = tmp + "/right-pass-dump.txt"; |
| 1067 | string strLiteralDump = tmp + "/literal-pass-dump.txt"; | 1081 | string strLiteralDump = tmp + "/literal-pass-dump.txt"; |
| 1068 | string strStdinDump = tmp + "/stdin-pass-dump.txt"; | 1082 | string strStdinDump = tmp + "/stdin-pass-dump.txt"; |
| 1083 | string strLockedAudit = tmp + "/locked-recovery-audit.txt"; | ||
| 1084 | string strUnlockedAudit = tmp + "/unlocked-recovery-audit.txt"; | ||
| 1069 | string strWrongPassFile = tmp + "/wrong-pass.txt"; | 1085 | string strWrongPassFile = tmp + "/wrong-pass.txt"; |
| 1070 | string strRightPassFile = tmp + "/right-pass.txt"; | 1086 | string strRightPassFile = tmp + "/right-pass.txt"; |
| 1071 | nFail += Check(WriteTextFile(strWrongPassFile, "wrong-passphrase\n") && | 1087 | nFail += Check(WriteTextFile(strWrongPassFile, "wrong-passphrase\n") && |
| @@ -1081,6 +1097,17 @@ static int RunWalletEncryptSelfTest() | |||
| 1081 | nFail += Check(nWrongRet != 0 && !FileExists(strWrongDump.c_str()), | 1097 | nFail += Check(nWrongRet != 0 && !FileExists(strWrongDump.c_str()), |
| 1082 | "a restarted wallet rejects the wrong-passphrase") ? 0 : 1; | 1098 | "a restarted wallet rejects the wrong-passphrase") ? 0 : 1; |
| 1083 | 1099 | ||
| 1100 | vector<string> vLockedAuditArgs; | ||
| 1101 | vLockedAuditArgs.push_back("-datadir=" + tmp); | ||
| 1102 | vLockedAuditArgs.push_back("-recoveryaudit"); | ||
| 1103 | vLockedAuditArgs.push_back("-nogui"); | ||
| 1104 | int nLockedAuditRet = RunBitflashChild(strExe, vLockedAuditArgs, NULL, &strLockedAudit); | ||
| 1105 | nFail += Check(nLockedAuditRet == 2 && | ||
| 1106 | FileContainsText(strLockedAudit, "recovery phrase: encrypted, unlock wallet to audit") && | ||
| 1107 | FileContainsText(strLockedAudit, "recovery coverage: unavailable while wallet is locked") && | ||
| 1108 | FileContainsText(strLockedAudit, "Unlock the wallet with /walletpassphrase"), | ||
| 1109 | "a locked encrypted wallet reports that phrase coverage needs unlock") ? 0 : 1; | ||
| 1110 | |||
| 1084 | vector<string> vLiteralArgs; | 1111 | vector<string> vLiteralArgs; |
| 1085 | vLiteralArgs.push_back("-datadir=" + tmp); | 1112 | vLiteralArgs.push_back("-datadir=" + tmp); |
| 1086 | vLiteralArgs.push_back("-walletpassphrase=btf-test-passphrase"); | 1113 | vLiteralArgs.push_back("-walletpassphrase=btf-test-passphrase"); |
| @@ -1101,6 +1128,17 @@ static int RunWalletEncryptSelfTest() | |||
| 1101 | nFail += Check(FileContainsText(strRightDump, HexStrLocal(vchDefaultPrivBytes)), | 1128 | nFail += Check(FileContainsText(strRightDump, HexStrLocal(vchDefaultPrivBytes)), |
| 1102 | "the restarted wallet can decrypt and dump the original key") ? 0 : 1; | 1129 | "the restarted wallet can decrypt and dump the original key") ? 0 : 1; |
| 1103 | 1130 | ||
| 1131 | vector<string> vUnlockedAuditArgs; | ||
| 1132 | vUnlockedAuditArgs.push_back("-datadir=" + tmp); | ||
| 1133 | vUnlockedAuditArgs.push_back("-walletpassphrase=@" + strRightPassFile); | ||
| 1134 | vUnlockedAuditArgs.push_back("-recoveryaudit"); | ||
| 1135 | vUnlockedAuditArgs.push_back("-nogui"); | ||
| 1136 | int nUnlockedAuditRet = RunBitflashChild(strExe, vUnlockedAuditArgs, NULL, &strUnlockedAudit); | ||
| 1137 | nFail += Check(nUnlockedAuditRet == 0 && | ||
| 1138 | FileContainsText(strUnlockedAudit, "recovery phrase: present") && | ||
| 1139 | !FileContainsText(strUnlockedAudit, "recovery phrase: not installed"), | ||
| 1140 | "an unlocked encrypted wallet audits the installed recovery phrase") ? 0 : 1; | ||
| 1141 | |||
| 1104 | vector<string> vStdinArgs; | 1142 | vector<string> vStdinArgs; |
| 1105 | vStdinArgs.push_back("-datadir=" + tmp); | 1143 | vStdinArgs.push_back("-datadir=" + tmp); |
| 1106 | vStdinArgs.push_back("-walletpassphrase"); | 1144 | vStdinArgs.push_back("-walletpassphrase"); |
diff --git a/src/walletcmd.cpp b/src/walletcmd.cpp index e8ac4bf..eccb9b3 100644 --- a/src/walletcmd.cpp +++ b/src/walletcmd.cpp | |||
| @@ -400,7 +400,12 @@ int CmdRecoveryAudit() | |||
| 400 | int64 nImmatureTotal = audit.nRecoverableImmatureCredit + audit.nLegacyImmatureCredit; | 400 | int64 nImmatureTotal = audit.nRecoverableImmatureCredit + audit.nLegacyImmatureCredit; |
| 401 | 401 | ||
| 402 | printf("Wallet recovery audit\n"); | 402 | printf("Wallet recovery audit\n"); |
| 403 | printf(" recovery phrase: %s\n", audit.fHaveSeed ? "present" : "not installed"); | 403 | if (audit.fHaveSeed) |
| 404 | printf(" recovery phrase: present\n"); | ||
| 405 | else if (audit.fSeedEncryptedLocked) | ||
| 406 | printf(" recovery phrase: encrypted, unlock wallet to audit\n"); | ||
| 407 | else | ||
| 408 | printf(" recovery phrase: not installed\n"); | ||
| 404 | if (audit.fHaveSeed) | 409 | if (audit.fHaveSeed) |
| 405 | { | 410 | { |
| 406 | printf(" derivation schema: %s\n", HDKeySchemaName(audit.nSchema).c_str()); | 411 | printf(" derivation schema: %s\n", HDKeySchemaName(audit.nSchema).c_str()); |
| @@ -412,6 +417,15 @@ int CmdRecoveryAudit() | |||
| 412 | if (!audit.fDeriveComplete) | 417 | if (!audit.fDeriveComplete) |
| 413 | printf(" derivation warning: %s\n", audit.strDeriveError.c_str()); | 418 | printf(" derivation warning: %s\n", audit.strDeriveError.c_str()); |
| 414 | printf(" total spendable balance: %s BTF\n", FormatMoney(nTotal).c_str()); | 419 | printf(" total spendable balance: %s BTF\n", FormatMoney(nTotal).c_str()); |
| 420 | if (audit.fSeedEncryptedLocked) | ||
| 421 | { | ||
| 422 | printf(" recovery coverage: unavailable while wallet is locked\n"); | ||
| 423 | printf(" immature mining rewards: %s BTF\n", FormatMoney(nImmatureTotal).c_str()); | ||
| 424 | printf("\n"); | ||
| 425 | printf("Unlock the wallet with /walletpassphrase or /walletpassphrase=@FILE to audit phrase coverage.\n"); | ||
| 426 | fflush(stdout); | ||
| 427 | return 2; | ||
| 428 | } | ||
| 415 | printf(" covered by recovery phrase: %s BTF (%d transaction(s))\n", | 429 | printf(" covered by recovery phrase: %s BTF (%d transaction(s))\n", |
| 416 | FormatMoney(audit.nRecoverableCredit).c_str(), audit.nRecoverableTx); | 430 | FormatMoney(audit.nRecoverableCredit).c_str(), audit.nRecoverableTx); |
| 417 | printf(" wallet.dat-only balance: %s BTF (%d transaction(s))\n", | 431 | printf(" wallet.dat-only balance: %s BTF (%d transaction(s))\n", |