diff options
Diffstat (limited to 'src/walletcmd.cpp')
| -rw-r--r-- | src/walletcmd.cpp | 119 |
1 files changed, 83 insertions, 36 deletions
diff --git a/src/walletcmd.cpp b/src/walletcmd.cpp index 374b2cc..1f1fd27 100644 --- a/src/walletcmd.cpp +++ b/src/walletcmd.cpp | |||
| @@ -1070,79 +1070,126 @@ public: | |||
| 1070 | 1070 | ||
| 1071 | } // namespace | 1071 | } // namespace |
| 1072 | 1072 | ||
| 1073 | int CmdWalletSQLiteExport(const std::string& strDest) | 1073 | // Export the currently-loaded wallet to a SQLite file. Shared by the |
| 1074 | // -walletsqliteexport command and the GUI conversion wizard: opens the | ||
| 1075 | // destination, copies every record inside one transaction, checkpoints, and | ||
| 1076 | // verifies the stored count matches. Returns false with strErrorRet set on any | ||
| 1077 | // failure and leaves no partial file behind; the source wallet is only read. | ||
| 1078 | bool ExportActiveWalletToSQLite(const std::string& strDest, int& nCopiedRet, | ||
| 1079 | std::string& strErrorRet) | ||
| 1074 | { | 1080 | { |
| 1075 | AttachTerminal(); | 1081 | nCopiedRet = 0; |
| 1076 | |||
| 1077 | if (strDest.empty()) | 1082 | if (strDest.empty()) |
| 1078 | { | 1083 | { |
| 1079 | fprintf(stderr, "Missing SQLite wallet export path.\n"); | 1084 | strErrorRet = "no SQLite export path was given"; |
| 1080 | return 1; | 1085 | return false; |
| 1081 | } | 1086 | } |
| 1082 | if (FileExists(strDest.c_str())) | 1087 | if (FileExists(strDest.c_str())) |
| 1083 | { | 1088 | { |
| 1084 | fprintf(stderr, "Refusing to overwrite existing SQLite wallet export: %s\n", | 1089 | strErrorRet = strprintf("refusing to overwrite existing file: %s", strDest.c_str()); |
| 1085 | strDest.c_str()); | 1090 | return false; |
| 1086 | return 1; | ||
| 1087 | } | 1091 | } |
| 1088 | 1092 | ||
| 1089 | string strError; | ||
| 1090 | CWalletDBSQLite db; | 1093 | CWalletDBSQLite db; |
| 1091 | if (!db.Open(strDest, strError)) | 1094 | if (!db.Open(strDest, strErrorRet)) |
| 1092 | { | 1095 | { |
| 1093 | RemoveSQLiteExportFiles(strDest); | 1096 | RemoveSQLiteExportFiles(strDest); |
| 1094 | fprintf(stderr, "Cannot open SQLite wallet export: %s\n", strError.c_str()); | 1097 | return false; |
| 1095 | return 1; | ||
| 1096 | } | 1098 | } |
| 1097 | if (!db.BeginTransaction(strError)) | 1099 | if (!db.BeginTransaction(strErrorRet)) |
| 1098 | { | 1100 | { |
| 1099 | db.Close(); | 1101 | db.Close(); |
| 1100 | RemoveSQLiteExportFiles(strDest); | 1102 | RemoveSQLiteExportFiles(strDest); |
| 1101 | fprintf(stderr, "Cannot begin SQLite wallet export: %s\n", strError.c_str()); | 1103 | return false; |
| 1102 | return 1; | ||
| 1103 | } | 1104 | } |
| 1104 | 1105 | ||
| 1105 | int nCopied = 0; | 1106 | CWalletSQLiteExportVisitor visitor(db, nCopiedRet); |
| 1106 | CWalletSQLiteExportVisitor visitor(db, nCopied); | 1107 | if (!ScanWalletRecords(visitor, strErrorRet)) |
| 1107 | if (!ScanWalletRecords(visitor, strError)) | ||
| 1108 | { | 1108 | { |
| 1109 | string strRollbackError; | 1109 | string strRollbackError; |
| 1110 | db.RollbackTransaction(strRollbackError); | 1110 | db.RollbackTransaction(strRollbackError); |
| 1111 | db.Close(); | 1111 | db.Close(); |
| 1112 | RemoveSQLiteExportFiles(strDest); | 1112 | RemoveSQLiteExportFiles(strDest); |
| 1113 | fprintf(stderr, "Cannot export wallet.dat to SQLite: %s\n", strError.c_str()); | 1113 | return false; |
| 1114 | return 1; | ||
| 1115 | } | 1114 | } |
| 1116 | if (!db.CommitTransaction(strError)) | 1115 | if (!db.CommitTransaction(strErrorRet) || !db.Checkpoint(strErrorRet)) |
| 1117 | { | 1116 | { |
| 1118 | db.Close(); | 1117 | db.Close(); |
| 1119 | RemoveSQLiteExportFiles(strDest); | 1118 | RemoveSQLiteExportFiles(strDest); |
| 1120 | fprintf(stderr, "Cannot commit SQLite wallet export: %s\n", strError.c_str()); | 1119 | return false; |
| 1121 | return 1; | ||
| 1122 | } | 1120 | } |
| 1123 | if (!db.Checkpoint(strError)) | 1121 | |
| 1122 | int nCount = 0; | ||
| 1123 | if (!db.CountRecords(nCount, strErrorRet)) | ||
| 1124 | { | 1124 | { |
| 1125 | db.Close(); | 1125 | db.Close(); |
| 1126 | RemoveSQLiteExportFiles(strDest); | 1126 | RemoveSQLiteExportFiles(strDest); |
| 1127 | fprintf(stderr, "Cannot checkpoint SQLite wallet export: %s\n", strError.c_str()); | 1127 | return false; |
| 1128 | return 1; | ||
| 1129 | } | 1128 | } |
| 1130 | 1129 | if (nCount != nCopiedRet) | |
| 1131 | int nCount = 0; | ||
| 1132 | if (!db.CountRecords(nCount, strError)) | ||
| 1133 | { | 1130 | { |
| 1131 | strErrorRet = strprintf("export count mismatch: copied %d, stored %d", | ||
| 1132 | nCopiedRet, nCount); | ||
| 1134 | db.Close(); | 1133 | db.Close(); |
| 1135 | RemoveSQLiteExportFiles(strDest); | 1134 | RemoveSQLiteExportFiles(strDest); |
| 1136 | fprintf(stderr, "Cannot verify SQLite wallet export count: %s\n", | 1135 | return false; |
| 1137 | strError.c_str()); | 1136 | } |
| 1137 | db.Close(); | ||
| 1138 | return true; | ||
| 1139 | } | ||
| 1140 | |||
| 1141 | // Convert the running Berkeley DB wallet to SQLite for the GUI wizard: export | ||
| 1142 | // wallet.dat to <datadir>/wallet.sqlite, then record the backend marker so the | ||
| 1143 | // next start opens SQLite. wallet.dat is never modified -- it stays as the | ||
| 1144 | // fallback. Refuses if already on SQLite or if a wallet.sqlite already exists. | ||
| 1145 | bool DoConvertWalletToSQLite(int& nCopiedRet, std::string& strErrorRet) | ||
| 1146 | { | ||
| 1147 | nCopiedRet = 0; | ||
| 1148 | if (WalletSQLiteRuntimeActive()) | ||
| 1149 | { | ||
| 1150 | strErrorRet = "this wallet is already on the SQLite backend"; | ||
| 1151 | return false; | ||
| 1152 | } | ||
| 1153 | string strDest = GetAppDir() + "/wallet.sqlite"; | ||
| 1154 | if (FileExists(strDest.c_str())) | ||
| 1155 | { | ||
| 1156 | strErrorRet = strprintf("a SQLite wallet already exists at %s", strDest.c_str()); | ||
| 1157 | return false; | ||
| 1158 | } | ||
| 1159 | if (!ExportActiveWalletToSQLite(strDest, nCopiedRet, strErrorRet)) | ||
| 1160 | return false; | ||
| 1161 | if (!WriteWalletBackendMarker("sqlite")) | ||
| 1162 | { | ||
| 1163 | strErrorRet = "the wallet was exported but the backend choice could not be saved"; | ||
| 1164 | return false; | ||
| 1165 | } | ||
| 1166 | return true; | ||
| 1167 | } | ||
| 1168 | |||
| 1169 | int CmdWalletSQLiteExport(const std::string& strDest) | ||
| 1170 | { | ||
| 1171 | AttachTerminal(); | ||
| 1172 | |||
| 1173 | // Keep the command's own specific messages for the two conditions the | ||
| 1174 | // shared helper folds into a generic error, so scripts and the tests that | ||
| 1175 | // read this output see the same wording as before. | ||
| 1176 | if (strDest.empty()) | ||
| 1177 | { | ||
| 1178 | fprintf(stderr, "Missing SQLite wallet export path.\n"); | ||
| 1138 | return 1; | 1179 | return 1; |
| 1139 | } | 1180 | } |
| 1140 | if (nCount != nCopied) | 1181 | if (FileExists(strDest.c_str())) |
| 1141 | { | 1182 | { |
| 1142 | db.Close(); | 1183 | fprintf(stderr, "Refusing to overwrite existing SQLite wallet export: %s\n", |
| 1143 | RemoveSQLiteExportFiles(strDest); | 1184 | strDest.c_str()); |
| 1144 | fprintf(stderr, "SQLite wallet export count mismatch: copied %d, stored %d\n", | 1185 | return 1; |
| 1145 | nCopied, nCount); | 1186 | } |
| 1187 | |||
| 1188 | int nCopied = 0; | ||
| 1189 | string strError; | ||
| 1190 | if (!ExportActiveWalletToSQLite(strDest, nCopied, strError)) | ||
| 1191 | { | ||
| 1192 | fprintf(stderr, "Cannot export wallet to SQLite: %s\n", strError.c_str()); | ||
| 1146 | return 1; | 1193 | return 1; |
| 1147 | } | 1194 | } |
| 1148 | 1195 | ||