diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/main_gui.cpp | 11 | ||||
| -rw-r--r-- | src/selftest.cpp | 54 | ||||
| -rw-r--r-- | src/walletcmd.cpp | 209 | ||||
| -rw-r--r-- | src/walletcmd.h | 4 |
4 files changed, 278 insertions, 0 deletions
diff --git a/src/main_gui.cpp b/src/main_gui.cpp index cf96822..c9aac7d 100644 --- a/src/main_gui.cpp +++ b/src/main_gui.cpp | |||
| @@ -187,6 +187,8 @@ static void PrintUsage() | |||
| 187 | printf(" printing counts only, then exit)\n"); | 187 | printf(" printing counts only, then exit)\n"); |
| 188 | printf(" /walletsqliterestore=FILE (rebuild wallet.dat from a SQLite export in\n"); | 188 | printf(" /walletsqliterestore=FILE (rebuild wallet.dat from a SQLite export in\n"); |
| 189 | printf(" an empty data directory, then exit)\n"); | 189 | printf(" an empty data directory, then exit)\n"); |
| 190 | printf(" /walletsqliteloadcheck=FILE\n"); | ||
| 191 | printf(" (parse a SQLite export like the wallet loader)\n"); | ||
| 190 | printf(" /rescan (walk the chain for coins this wallet owns\n"); | 192 | printf(" /rescan (walk the chain for coins this wallet owns\n"); |
| 191 | printf(" but never recorded, then exit)\n"); | 193 | printf(" but never recorded, then exit)\n"); |
| 192 | printf("\n"); | 194 | printf("\n"); |
| @@ -562,6 +564,15 @@ int main(int argc, char* argv[]) | |||
| 562 | return nRet; | 564 | return nRet; |
| 563 | } | 565 | } |
| 564 | 566 | ||
| 567 | string strWalletSQLiteLoadCheck = | ||
| 568 | argval2(argc, argv, "/walletsqliteloadcheck", "-walletsqliteloadcheck"); | ||
| 569 | if (!strWalletSQLiteLoadCheck.empty()) | ||
| 570 | { | ||
| 571 | int nRet = CmdWalletSQLiteLoadCheck(strWalletSQLiteLoadCheck); | ||
| 572 | DBFlush(true); | ||
| 573 | return nRet; | ||
| 574 | } | ||
| 575 | |||
| 565 | // Berkeley DB reports failure by throwing, and CDB's constructor lets it | 576 | // Berkeley DB reports failure by throwing, and CDB's constructor lets it |
| 566 | // through. Nothing on this path caught anything, so an unreadable | 577 | // through. Nothing on this path caught anything, so an unreadable |
| 567 | // blkindex.dat or wallet.dat -- one written by another platform's Berkeley | 578 | // blkindex.dat or wallet.dat -- one written by another platform's Berkeley |
diff --git a/src/selftest.cpp b/src/selftest.cpp index 343d9c5..feb4076 100644 --- a/src/selftest.cpp +++ b/src/selftest.cpp | |||
| @@ -1195,6 +1195,28 @@ static bool CreateSQLiteWalletWithEmptyKey(const string& strPath) | |||
| 1195 | return fOk; | 1195 | return fOk; |
| 1196 | } | 1196 | } |
| 1197 | 1197 | ||
| 1198 | static bool CreateSQLiteWalletWithFutureMinVersion(const string& strPath) | ||
| 1199 | { | ||
| 1200 | string strError; | ||
| 1201 | CWalletDBSQLite db; | ||
| 1202 | CDataStream ssKey(SER_DISK); | ||
| 1203 | ssKey << string("walletminversion"); | ||
| 1204 | CDataStream ssValue(SER_DISK); | ||
| 1205 | ssValue << (WALLET_FORMAT_SUPPORTED + 1); | ||
| 1206 | |||
| 1207 | if (!db.Open(strPath, strError)) | ||
| 1208 | return false; | ||
| 1209 | if (!db.BeginTransaction(strError)) | ||
| 1210 | return false; | ||
| 1211 | if (!db.WriteRecord(DataStreamBytes(ssKey), DataStreamBytes(ssValue), strError)) | ||
| 1212 | return false; | ||
| 1213 | if (!db.CommitTransaction(strError)) | ||
| 1214 | return false; | ||
| 1215 | if (!db.Checkpoint(strError)) | ||
| 1216 | return false; | ||
| 1217 | return true; | ||
| 1218 | } | ||
| 1219 | |||
| 1198 | static int RunWalletSQLiteMigrationSelfTest() | 1220 | static int RunWalletSQLiteMigrationSelfTest() |
| 1199 | { | 1221 | { |
| 1200 | fflush(stdout); | 1222 | fflush(stdout); |
| @@ -1263,6 +1285,19 @@ static int RunWalletSQLiteMigrationSelfTest() | |||
| 1263 | FileContainsText(strVerifyOut, "verification: ok"), | 1285 | FileContainsText(strVerifyOut, "verification: ok"), |
| 1264 | "SQLite wallet export verifies against wallet.dat") ? 0 : 1; | 1286 | "SQLite wallet export verifies against wallet.dat") ? 0 : 1; |
| 1265 | 1287 | ||
| 1288 | string strLoadCheckOut = tmp + "/sqlite-loadcheck.txt"; | ||
| 1289 | vector<string> vLoadCheckArgs; | ||
| 1290 | vLoadCheckArgs.push_back("-datadir=" + strWalletDir); | ||
| 1291 | vLoadCheckArgs.push_back("-nomanagedtor"); | ||
| 1292 | vLoadCheckArgs.push_back("-nogui"); | ||
| 1293 | vLoadCheckArgs.push_back("-walletsqliteloadcheck=" + strSQLite); | ||
| 1294 | int nLoadCheckRet = | ||
| 1295 | RunBitflashChild(strExe, vLoadCheckArgs, NULL, &strLoadCheckOut); | ||
| 1296 | nFail += Check(nLoadCheckRet == 0 && | ||
| 1297 | FileContainsText(strLoadCheckOut, | ||
| 1298 | "load check: ok"), | ||
| 1299 | "SQLite wallet export passes the loader compatibility check") ? 0 : 1; | ||
| 1300 | |||
| 1266 | string strRestoredDir = tmp + "/restored-wallet"; | 1301 | string strRestoredDir = tmp + "/restored-wallet"; |
| 1267 | nFail += Check(MakeDirLocal(strRestoredDir), | 1302 | nFail += Check(MakeDirLocal(strRestoredDir), |
| 1268 | "restored wallet directory can be created") ? 0 : 1; | 1303 | "restored wallet directory can be created") ? 0 : 1; |
| @@ -1318,6 +1353,25 @@ static int RunWalletSQLiteMigrationSelfTest() | |||
| 1318 | !FileExists((strBadRestoreDir + "/wallet.dat").c_str()), | 1353 | !FileExists((strBadRestoreDir + "/wallet.dat").c_str()), |
| 1319 | "failed SQLite wallet restore removes incomplete wallet.dat") ? 0 : 1; | 1354 | "failed SQLite wallet restore removes incomplete wallet.dat") ? 0 : 1; |
| 1320 | 1355 | ||
| 1356 | string strFutureSQLite = tmp + "/future-wallet.sqlite"; | ||
| 1357 | nFail += Check(CreateSQLiteWalletWithFutureMinVersion(strFutureSQLite), | ||
| 1358 | "SQLite export with a future wallet format can be created") ? 0 : 1; | ||
| 1359 | string strFutureLoadCheckOut = tmp + "/sqlite-loadcheck-future.txt"; | ||
| 1360 | vector<string> vFutureLoadCheckArgs; | ||
| 1361 | vFutureLoadCheckArgs.push_back("-datadir=" + strWalletDir); | ||
| 1362 | vFutureLoadCheckArgs.push_back("-nomanagedtor"); | ||
| 1363 | vFutureLoadCheckArgs.push_back("-nogui"); | ||
| 1364 | vFutureLoadCheckArgs.push_back("-walletsqliteloadcheck=" + strFutureSQLite); | ||
| 1365 | int nFutureLoadCheckRet = | ||
| 1366 | RunBitflashChild(strExe, vFutureLoadCheckArgs, NULL, | ||
| 1367 | &strFutureLoadCheckOut); | ||
| 1368 | nFail += Check(nFutureLoadCheckRet == 2 && | ||
| 1369 | FileContainsText(strFutureLoadCheckOut, | ||
| 1370 | "unsupported future wallet format") && | ||
| 1371 | FileContainsText(strFutureLoadCheckOut, | ||
| 1372 | "load check: failed"), | ||
| 1373 | "SQLite wallet load check rejects future wallet formats") ? 0 : 1; | ||
| 1374 | |||
| 1321 | string strOverwriteOut = tmp + "/sqlite-export-overwrite.txt"; | 1375 | string strOverwriteOut = tmp + "/sqlite-export-overwrite.txt"; |
| 1322 | int nOverwriteRet = | 1376 | int nOverwriteRet = |
| 1323 | RunBitflashChild(strExe, vExportArgs, NULL, &strOverwriteOut); | 1377 | RunBitflashChild(strExe, vExportArgs, NULL, &strOverwriteOut); |
diff --git a/src/walletcmd.cpp b/src/walletcmd.cpp index 9e4a981..4dda8dd 100644 --- a/src/walletcmd.cpp +++ b/src/walletcmd.cpp | |||
| @@ -903,6 +903,171 @@ public: | |||
| 903 | } | 903 | } |
| 904 | }; | 904 | }; |
| 905 | 905 | ||
| 906 | class CWalletSQLiteLoadCheckVisitor : public CWalletRecordVisitor | ||
| 907 | { | ||
| 908 | public: | ||
| 909 | int nRecords; | ||
| 910 | int nUnknown; | ||
| 911 | string strFailure; | ||
| 912 | |||
| 913 | CWalletSQLiteLoadCheckVisitor() : nRecords(0), nUnknown(0) { } | ||
| 914 | |||
| 915 | bool Fail(const string& strMessage, string& strErrorRet) | ||
| 916 | { | ||
| 917 | strFailure = strMessage; | ||
| 918 | strErrorRet = strMessage; | ||
| 919 | return false; | ||
| 920 | } | ||
| 921 | |||
| 922 | bool VisitWalletRecord(const CDataStream& ssKeyIn, | ||
| 923 | const CDataStream& ssValueIn, | ||
| 924 | string& strErrorRet) | ||
| 925 | { | ||
| 926 | nRecords++; | ||
| 927 | try | ||
| 928 | { | ||
| 929 | CDataStream ssKey = ssKeyIn; | ||
| 930 | CDataStream ssValue = ssValueIn; | ||
| 931 | string strType; | ||
| 932 | ssKey >> strType; | ||
| 933 | if (ssKey.fail()) | ||
| 934 | return Fail("malformed wallet record key", strErrorRet); | ||
| 935 | |||
| 936 | if (strType == "name") | ||
| 937 | { | ||
| 938 | string strAddress; | ||
| 939 | string strName; | ||
| 940 | ssKey >> strAddress; | ||
| 941 | ssValue >> strName; | ||
| 942 | } | ||
| 943 | else if (strType == "tx") | ||
| 944 | { | ||
| 945 | uint256 hash; | ||
| 946 | CWalletTx wtx; | ||
| 947 | ssKey >> hash; | ||
| 948 | ssValue >> wtx; | ||
| 949 | } | ||
| 950 | else if (strType == "key") | ||
| 951 | { | ||
| 952 | vector<unsigned char> vchPubKey; | ||
| 953 | CPrivKey vchPrivKey; | ||
| 954 | ssKey >> vchPubKey; | ||
| 955 | ssValue >> vchPrivKey; | ||
| 956 | } | ||
| 957 | else if (strType == "mkey") | ||
| 958 | { | ||
| 959 | unsigned int nID = 0; | ||
| 960 | CWalletMasterKey kMasterKey; | ||
| 961 | ssKey >> nID; | ||
| 962 | ssValue >> kMasterKey; | ||
| 963 | } | ||
| 964 | else if (strType == "ckey") | ||
| 965 | { | ||
| 966 | vector<unsigned char> vchPubKey; | ||
| 967 | vector<unsigned char> vchCryptedSecret; | ||
| 968 | ssKey >> vchPubKey; | ||
| 969 | ssValue >> vchCryptedSecret; | ||
| 970 | } | ||
| 971 | else if (strType == "defaultkey" || | ||
| 972 | strType == "hdmaster" || | ||
| 973 | strType == "hdchaincode" || | ||
| 974 | strType == "cryptedhdmaster" || | ||
| 975 | strType == "cryptedhdchaincode") | ||
| 976 | { | ||
| 977 | vector<unsigned char> vch; | ||
| 978 | ssValue >> vch; | ||
| 979 | } | ||
| 980 | else if (strType == "hdnext" || | ||
| 981 | strType == "hdreceivenext" || | ||
| 982 | strType == "hdchangenext" || | ||
| 983 | strType == "hdcointype") | ||
| 984 | { | ||
| 985 | unsigned int nValue = 0; | ||
| 986 | ssValue >> nValue; | ||
| 987 | } | ||
| 988 | else if (strType == "hdschema") | ||
| 989 | { | ||
| 990 | int nSchema = 0; | ||
| 991 | ssValue >> nSchema; | ||
| 992 | if (!ssValue.fail() && | ||
| 993 | nSchema != HD_SCHEMA_NONE && | ||
| 994 | nSchema != HD_SCHEMA_LEGACY && | ||
| 995 | nSchema != HD_SCHEMA_BIP44) | ||
| 996 | return Fail("unsupported deterministic wallet schema", | ||
| 997 | strErrorRet); | ||
| 998 | } | ||
| 999 | else if (strType == "walletminversion") | ||
| 1000 | { | ||
| 1001 | int nMinVersion = 0; | ||
| 1002 | ssValue >> nMinVersion; | ||
| 1003 | if (!ssValue.fail() && nMinVersion > WALLET_FORMAT_SUPPORTED) | ||
| 1004 | return Fail("unsupported future wallet format", strErrorRet); | ||
| 1005 | } | ||
| 1006 | else if (strType == "pool") | ||
| 1007 | { | ||
| 1008 | int64 nIndex = 0; | ||
| 1009 | vector<unsigned char> vchPubKey; | ||
| 1010 | ssKey >> nIndex; | ||
| 1011 | ssValue >> vchPubKey; | ||
| 1012 | } | ||
| 1013 | else if (strType == "setting") | ||
| 1014 | { | ||
| 1015 | string strKey; | ||
| 1016 | ssKey >> strKey; | ||
| 1017 | if (strKey == "nTransactionFee") | ||
| 1018 | { | ||
| 1019 | int64 nValue = 0; | ||
| 1020 | ssValue >> nValue; | ||
| 1021 | } | ||
| 1022 | else if (strKey == "addrIncoming") | ||
| 1023 | { | ||
| 1024 | CAddress addr; | ||
| 1025 | ssValue >> addr; | ||
| 1026 | } | ||
| 1027 | else if (strKey == "nMineMode") | ||
| 1028 | { | ||
| 1029 | int nValue = 0; | ||
| 1030 | ssValue >> nValue; | ||
| 1031 | } | ||
| 1032 | else if (strKey == "fGenerateBitcoins") | ||
| 1033 | { | ||
| 1034 | int fValue = 0; | ||
| 1035 | ssValue >> fValue; | ||
| 1036 | } | ||
| 1037 | else if (strKey == "strParticipantPool") | ||
| 1038 | { | ||
| 1039 | string strValue; | ||
| 1040 | ssValue >> strValue; | ||
| 1041 | } | ||
| 1042 | } | ||
| 1043 | else if (strType == "version") | ||
| 1044 | { | ||
| 1045 | int nVersion = 0; | ||
| 1046 | ssValue >> nVersion; | ||
| 1047 | } | ||
| 1048 | else | ||
| 1049 | { | ||
| 1050 | nUnknown++; | ||
| 1051 | } | ||
| 1052 | |||
| 1053 | if (ssKey.fail()) | ||
| 1054 | return Fail("malformed wallet record key", strErrorRet); | ||
| 1055 | if (ssValue.fail()) | ||
| 1056 | return Fail("malformed wallet record value", strErrorRet); | ||
| 1057 | } | ||
| 1058 | catch (const std::exception& e) | ||
| 1059 | { | ||
| 1060 | return Fail(strprintf("wallet record parse exception: %s", e.what()), | ||
| 1061 | strErrorRet); | ||
| 1062 | } | ||
| 1063 | catch (...) | ||
| 1064 | { | ||
| 1065 | return Fail("wallet record parse exception", strErrorRet); | ||
| 1066 | } | ||
| 1067 | return true; | ||
| 1068 | } | ||
| 1069 | }; | ||
| 1070 | |||
| 906 | } // namespace | 1071 | } // namespace |
| 907 | 1072 | ||
| 908 | int CmdWalletSQLiteExport(const std::string& strDest) | 1073 | int CmdWalletSQLiteExport(const std::string& strDest) |
| @@ -1152,6 +1317,50 @@ int CmdWalletSQLiteRestore(const std::string& strPath) | |||
| 1152 | return 0; | 1317 | return 0; |
| 1153 | } | 1318 | } |
| 1154 | 1319 | ||
| 1320 | int CmdWalletSQLiteLoadCheck(const std::string& strPath) | ||
| 1321 | { | ||
| 1322 | AttachTerminal(); | ||
| 1323 | |||
| 1324 | if (strPath.empty()) | ||
| 1325 | { | ||
| 1326 | fprintf(stderr, "Missing SQLite wallet export path.\n"); | ||
| 1327 | return 1; | ||
| 1328 | } | ||
| 1329 | if (!FileExists(strPath.c_str())) | ||
| 1330 | { | ||
| 1331 | fprintf(stderr, "SQLite wallet export does not exist: %s\n", | ||
| 1332 | strPath.c_str()); | ||
| 1333 | return 1; | ||
| 1334 | } | ||
| 1335 | |||
| 1336 | string strError; | ||
| 1337 | CWalletDBSQLite sqlite; | ||
| 1338 | if (!sqlite.Open(strPath, strError)) | ||
| 1339 | { | ||
| 1340 | fprintf(stderr, "Cannot open SQLite wallet export: %s\n", strError.c_str()); | ||
| 1341 | return 1; | ||
| 1342 | } | ||
| 1343 | |||
| 1344 | CWalletSQLiteLoadCheckVisitor visitor; | ||
| 1345 | if (!sqlite.ScanRecords(visitor, strError)) | ||
| 1346 | { | ||
| 1347 | printf("SQLite wallet load check\n"); | ||
| 1348 | printf(" records checked: %d\n", visitor.nRecords); | ||
| 1349 | printf(" unknown records: %d\n", visitor.nUnknown); | ||
| 1350 | printf(" load check: failed\n"); | ||
| 1351 | printf(" failure: %s\n", strError.c_str()); | ||
| 1352 | fflush(stdout); | ||
| 1353 | return 2; | ||
| 1354 | } | ||
| 1355 | |||
| 1356 | printf("SQLite wallet load check\n"); | ||
| 1357 | printf(" records checked: %d\n", visitor.nRecords); | ||
| 1358 | printf(" unknown records: %d\n", visitor.nUnknown); | ||
| 1359 | printf(" load check: ok\n"); | ||
| 1360 | fflush(stdout); | ||
| 1361 | return 0; | ||
| 1362 | } | ||
| 1363 | |||
| 1155 | int CmdEncryptWallet(const std::string& strPassphrase) | 1364 | int CmdEncryptWallet(const std::string& strPassphrase) |
| 1156 | { | 1365 | { |
| 1157 | AttachTerminal(); | 1366 | AttachTerminal(); |
diff --git a/src/walletcmd.h b/src/walletcmd.h index 50dc69e..d993856 100644 --- a/src/walletcmd.h +++ b/src/walletcmd.h | |||
| @@ -83,6 +83,10 @@ int CmdWalletSQLiteVerify(const std::string& strPath); | |||
| 83 | // empty data directory. Refuses to overwrite an existing wallet.dat. | 83 | // empty data directory. Refuses to overwrite an existing wallet.dat. |
| 84 | int CmdWalletSQLiteRestore(const std::string& strPath); | 84 | int CmdWalletSQLiteRestore(const std::string& strPath); |
| 85 | 85 | ||
| 86 | // Diagnostic/migration staging: parse a SQLite wallet export as the runtime | ||
| 87 | // wallet loader would. Prints counts only, never record keys or values. | ||
| 88 | int CmdWalletSQLiteLoadCheck(const std::string& strPath); | ||
| 89 | |||
| 86 | // Rewrite wallet.dat so private keys and the HD seed are encrypted with the | 90 | // Rewrite wallet.dat so private keys and the HD seed are encrypted with the |
| 87 | // given passphrase. Returns 0 on success and exits without starting the node. | 91 | // given passphrase. Returns 0 on success and exits without starting the node. |
| 88 | int CmdEncryptWallet(const std::string& strPassphrase); | 92 | int CmdEncryptWallet(const std::string& strPassphrase); |