diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/Makefile | 1 | ||||
| -rw-r--r-- | src/main_gui.cpp | 15 | ||||
| -rw-r--r-- | src/makefile.mingw | 1 | ||||
| -rw-r--r-- | src/selftest.cpp | 148 | ||||
| -rw-r--r-- | src/wallet_sqlite.cpp | 10 | ||||
| -rw-r--r-- | src/wallet_sqlite.h | 1 | ||||
| -rw-r--r-- | src/walletcmd.cpp | 97 | ||||
| -rw-r--r-- | src/walletcmd.h | 5 |
8 files changed, 275 insertions, 3 deletions
diff --git a/src/Makefile b/src/Makefile index cfa40f6..b83ff58 100644 --- a/src/Makefile +++ b/src/Makefile | |||
| @@ -189,6 +189,7 @@ tests: test_bip32 bitflash-node | |||
| 189 | ./bitflash-node -selftest=wallet-hd | 189 | ./bitflash-node -selftest=wallet-hd |
| 190 | ./bitflash-node -selftest=wallet-format | 190 | ./bitflash-node -selftest=wallet-format |
| 191 | ./bitflash-node -selftest=wallet-sqlite | 191 | ./bitflash-node -selftest=wallet-sqlite |
| 192 | ./bitflash-node -selftest=wallet-sqlite-migration | ||
| 192 | ./bitflash-node -selftest=wallet-crypto | 193 | ./bitflash-node -selftest=wallet-crypto |
| 193 | ./bitflash-node -selftest=wallet-encrypt | 194 | ./bitflash-node -selftest=wallet-encrypt |
| 194 | ./bitflash-node -selftest=net-message | 195 | ./bitflash-node -selftest=net-message |
diff --git a/src/main_gui.cpp b/src/main_gui.cpp index 965c818..6e89270 100644 --- a/src/main_gui.cpp +++ b/src/main_gui.cpp | |||
| @@ -114,8 +114,8 @@ static void PrintUsage() | |||
| 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-storage-sanity,\n"); | 116 | printf(" /selftest=wallet-keypool, wallet-hd, wallet-format, wallet-storage-sanity,\n"); |
| 117 | printf(" db-env-reopen, wallet-sqlite, wallet-crypto, wallet-encrypt,\n"); | 117 | printf(" db-env-reopen, wallet-sqlite, wallet-sqlite-migration,\n"); |
| 118 | printf(" wallet-portability,\n"); | 118 | printf(" wallet-crypto, wallet-encrypt, wallet-portability,\n"); |
| 119 | printf(" net-message,\n"); | 119 | printf(" net-message,\n"); |
| 120 | printf(" consensus-limits, pool-stratum,\n"); | 120 | printf(" consensus-limits, pool-stratum,\n"); |
| 121 | printf(" parse-money, socks5-proxy, or managed-tor\n"); | 121 | printf(" parse-money, socks5-proxy, or managed-tor\n"); |
| @@ -181,6 +181,8 @@ static void PrintUsage() | |||
| 181 | printf(" (write the same audit as deterministic JSON)\n"); | 181 | printf(" (write the same audit as deterministic JSON)\n"); |
| 182 | printf(" /walletstoragecheck (fail closed on inconsistent or unsafe\n"); | 182 | printf(" /walletstoragecheck (fail closed on inconsistent or unsafe\n"); |
| 183 | printf(" wallet.dat storage state)\n"); | 183 | printf(" wallet.dat storage state)\n"); |
| 184 | printf(" /walletsqliteexport=FILE (copy raw wallet.dat records into an\n"); | ||
| 185 | printf(" experimental SQLite store, then exit)\n"); | ||
| 184 | printf(" /rescan (walk the chain for coins this wallet owns\n"); | 186 | printf(" /rescan (walk the chain for coins this wallet owns\n"); |
| 185 | printf(" but never recorded, then exit)\n"); | 187 | printf(" but never recorded, then exit)\n"); |
| 186 | printf("\n"); | 188 | printf("\n"); |
| @@ -529,6 +531,15 @@ int main(int argc, char* argv[]) | |||
| 529 | return nRet; | 531 | return nRet; |
| 530 | } | 532 | } |
| 531 | 533 | ||
| 534 | string strWalletSQLiteExport = | ||
| 535 | argval2(argc, argv, "/walletsqliteexport", "-walletsqliteexport"); | ||
| 536 | if (!strWalletSQLiteExport.empty()) | ||
| 537 | { | ||
| 538 | int nRet = CmdWalletSQLiteExport(strWalletSQLiteExport); | ||
| 539 | DBFlush(true); | ||
| 540 | return nRet; | ||
| 541 | } | ||
| 542 | |||
| 532 | // Berkeley DB reports failure by throwing, and CDB's constructor lets it | 543 | // Berkeley DB reports failure by throwing, and CDB's constructor lets it |
| 533 | // through. Nothing on this path caught anything, so an unreadable | 544 | // through. Nothing on this path caught anything, so an unreadable |
| 534 | // blkindex.dat or wallet.dat -- one written by another platform's Berkeley | 545 | // blkindex.dat or wallet.dat -- one written by another platform's Berkeley |
diff --git a/src/makefile.mingw b/src/makefile.mingw index 2ff4e11..14e6b32 100644 --- a/src/makefile.mingw +++ b/src/makefile.mingw | |||
| @@ -109,6 +109,7 @@ tests: test_bip32.exe bitflash.exe | |||
| 109 | ./bitflash.exe -selftest=wallet-hd | 109 | ./bitflash.exe -selftest=wallet-hd |
| 110 | ./bitflash.exe -selftest=wallet-format | 110 | ./bitflash.exe -selftest=wallet-format |
| 111 | ./bitflash.exe -selftest=wallet-sqlite | 111 | ./bitflash.exe -selftest=wallet-sqlite |
| 112 | ./bitflash.exe -selftest=wallet-sqlite-migration | ||
| 112 | ./bitflash.exe -selftest=wallet-crypto | 113 | ./bitflash.exe -selftest=wallet-crypto |
| 113 | ./bitflash.exe -selftest=wallet-encrypt | 114 | ./bitflash.exe -selftest=wallet-encrypt |
| 114 | ./bitflash.exe -selftest=net-message | 115 | ./bitflash.exe -selftest=net-message |
diff --git a/src/selftest.cpp b/src/selftest.cpp index f9c4a8e..7a54271 100644 --- a/src/selftest.cpp +++ b/src/selftest.cpp | |||
| @@ -1134,6 +1134,150 @@ static int RunWalletSQLiteSelfTest() | |||
| 1134 | return nFail == 0 ? 0 : 1; | 1134 | return nFail == 0 ? 0 : 1; |
| 1135 | } | 1135 | } |
| 1136 | 1136 | ||
| 1137 | class CWalletRecordMapVisitor : public CWalletRecordVisitor | ||
| 1138 | { | ||
| 1139 | public: | ||
| 1140 | std::map<vector<unsigned char>, vector<unsigned char> >& mapRecords; | ||
| 1141 | |||
| 1142 | explicit CWalletRecordMapVisitor( | ||
| 1143 | std::map<vector<unsigned char>, vector<unsigned char> >& mapRecordsIn) | ||
| 1144 | : mapRecords(mapRecordsIn) { } | ||
| 1145 | |||
| 1146 | bool VisitWalletRecord(const CDataStream& ssKey, | ||
| 1147 | const CDataStream& ssValue, | ||
| 1148 | string& strErrorRet) | ||
| 1149 | { | ||
| 1150 | vector<unsigned char> vchKey = DataStreamBytes(ssKey); | ||
| 1151 | if (mapRecords.count(vchKey)) | ||
| 1152 | { | ||
| 1153 | strErrorRet = "duplicate serialized wallet key"; | ||
| 1154 | return false; | ||
| 1155 | } | ||
| 1156 | mapRecords[vchKey] = DataStreamBytes(ssValue); | ||
| 1157 | return true; | ||
| 1158 | } | ||
| 1159 | }; | ||
| 1160 | |||
| 1161 | static int RunWalletSQLiteMigrationSelfTest() | ||
| 1162 | { | ||
| 1163 | fflush(stdout); | ||
| 1164 | printf("wallet-sqlite-migration self-test\n"); | ||
| 1165 | |||
| 1166 | std::string tmp; | ||
| 1167 | std::string cwd; | ||
| 1168 | if (!MakeTempDir(tmp)) | ||
| 1169 | { | ||
| 1170 | printf(" FAIL could not create a temporary data directory\n"); | ||
| 1171 | return 1; | ||
| 1172 | } | ||
| 1173 | if (!GetCurrentDir(cwd)) | ||
| 1174 | { | ||
| 1175 | printf(" FAIL could not read current directory\n"); | ||
| 1176 | RemoveTree(tmp); | ||
| 1177 | return 1; | ||
| 1178 | } | ||
| 1179 | |||
| 1180 | int nFail = 0; | ||
| 1181 | printf(" temp root: %s\n", tmp.c_str()); | ||
| 1182 | |||
| 1183 | try | ||
| 1184 | { | ||
| 1185 | #ifdef _WIN32 | ||
| 1186 | string strExe = cwd + "\\bitflash.exe"; | ||
| 1187 | #else | ||
| 1188 | string strExe = cwd + "/bitflash-node"; | ||
| 1189 | #endif | ||
| 1190 | string strWalletDir = tmp + "/wallet"; | ||
| 1191 | nFail += Check(MakeDirLocal(strWalletDir), | ||
| 1192 | "temporary wallet directory can be created") ? 0 : 1; | ||
| 1193 | |||
| 1194 | string strCreateOut = tmp + "/create.txt"; | ||
| 1195 | vector<string> vCreateArgs; | ||
| 1196 | vCreateArgs.push_back("-datadir=" + strWalletDir); | ||
| 1197 | vCreateArgs.push_back("-nomanagedtor"); | ||
| 1198 | vCreateArgs.push_back("-nogui"); | ||
| 1199 | vCreateArgs.push_back("-newphrase"); | ||
| 1200 | int nCreateRet = RunBitflashChild(strExe, vCreateArgs, NULL, &strCreateOut); | ||
| 1201 | nFail += Check(nCreateRet == 0 && | ||
| 1202 | FileContainsText(strCreateOut, "Write these twelve words down"), | ||
| 1203 | "a phrase wallet can be created for SQLite export") ? 0 : 1; | ||
| 1204 | |||
| 1205 | string strSQLite = tmp + "/wallet.sqlite"; | ||
| 1206 | string strExportOut = tmp + "/sqlite-export.txt"; | ||
| 1207 | vector<string> vExportArgs; | ||
| 1208 | vExportArgs.push_back("-datadir=" + strWalletDir); | ||
| 1209 | vExportArgs.push_back("-nomanagedtor"); | ||
| 1210 | vExportArgs.push_back("-nogui"); | ||
| 1211 | vExportArgs.push_back("-walletsqliteexport=" + strSQLite); | ||
| 1212 | int nExportRet = RunBitflashChild(strExe, vExportArgs, NULL, &strExportOut); | ||
| 1213 | nFail += Check(nExportRet == 0 && | ||
| 1214 | FileExists(strSQLite.c_str()) && | ||
| 1215 | FileContainsText(strExportOut, "SQLite wallet export written"), | ||
| 1216 | "wallet.dat can be exported to SQLite") ? 0 : 1; | ||
| 1217 | |||
| 1218 | string strOverwriteOut = tmp + "/sqlite-export-overwrite.txt"; | ||
| 1219 | int nOverwriteRet = | ||
| 1220 | RunBitflashChild(strExe, vExportArgs, NULL, &strOverwriteOut); | ||
| 1221 | nFail += Check(nOverwriteRet != 0 && | ||
| 1222 | FileContainsText(strOverwriteOut, | ||
| 1223 | "Refusing to overwrite existing SQLite wallet export"), | ||
| 1224 | "SQLite wallet export refuses to overwrite") ? 0 : 1; | ||
| 1225 | remove((strSQLite + "-wal").c_str()); | ||
| 1226 | remove((strSQLite + "-shm").c_str()); | ||
| 1227 | |||
| 1228 | std::map<vector<unsigned char>, vector<unsigned char> > mapBDB; | ||
| 1229 | string strOldDataDir = strSetDataDir; | ||
| 1230 | string strScanError; | ||
| 1231 | strSetDataDir = strWalletDir; | ||
| 1232 | CWalletRecordMapVisitor visitor(mapBDB); | ||
| 1233 | bool fScanOk = ScanWalletRecords(visitor, strScanError); | ||
| 1234 | DBFlush(true); | ||
| 1235 | strSetDataDir = strOldDataDir; | ||
| 1236 | nFail += Check(fScanOk && !mapBDB.empty(), | ||
| 1237 | "wallet.dat records can be scanned for comparison") ? 0 : 1; | ||
| 1238 | |||
| 1239 | CWalletDBSQLite db; | ||
| 1240 | string strError; | ||
| 1241 | nFail += Check(db.Open(strSQLite, strError), | ||
| 1242 | "exported SQLite wallet opens") ? 0 : 1; | ||
| 1243 | int nSQLiteRecords = 0; | ||
| 1244 | nFail += Check(db.CountRecords(nSQLiteRecords, strError) && | ||
| 1245 | nSQLiteRecords == (int)mapBDB.size(), | ||
| 1246 | "SQLite export has the same record count as wallet.dat") ? 0 : 1; | ||
| 1247 | |||
| 1248 | bool fAllMatch = true; | ||
| 1249 | for (std::map<vector<unsigned char>, vector<unsigned char> >::const_iterator it = | ||
| 1250 | mapBDB.begin(); it != mapBDB.end(); ++it) | ||
| 1251 | { | ||
| 1252 | vector<unsigned char> vchSQLiteValue; | ||
| 1253 | if (!db.ReadRecord(it->first, vchSQLiteValue, strError) || | ||
| 1254 | vchSQLiteValue != it->second) | ||
| 1255 | { | ||
| 1256 | fAllMatch = false; | ||
| 1257 | break; | ||
| 1258 | } | ||
| 1259 | } | ||
| 1260 | nFail += Check(fAllMatch, | ||
| 1261 | "SQLite export preserves every wallet.dat key/value byte-for-byte") ? 0 : 1; | ||
| 1262 | } | ||
| 1263 | catch (const std::exception& e) | ||
| 1264 | { | ||
| 1265 | printf(" FAIL exception: %s\n", e.what()); | ||
| 1266 | nFail++; | ||
| 1267 | } | ||
| 1268 | catch (...) | ||
| 1269 | { | ||
| 1270 | printf(" FAIL unknown exception\n"); | ||
| 1271 | nFail++; | ||
| 1272 | } | ||
| 1273 | |||
| 1274 | RemoveTree(tmp); | ||
| 1275 | printf("%s (%d failure%s)\n", nFail == 0 ? "ALL TESTS PASSED" : "TESTS FAILED", | ||
| 1276 | nFail, nFail == 1 ? "" : "s"); | ||
| 1277 | fflush(stdout); | ||
| 1278 | return nFail == 0 ? 0 : 1; | ||
| 1279 | } | ||
| 1280 | |||
| 1137 | static void ClearWalletRuntimeForTest() | 1281 | static void ClearWalletRuntimeForTest() |
| 1138 | { | 1282 | { |
| 1139 | CRITICAL_BLOCK(cs_mapKeys) | 1283 | CRITICAL_BLOCK(cs_mapKeys) |
| @@ -2395,6 +2539,8 @@ int RunSelfTest(const std::string& name) | |||
| 2395 | return RunDbEnvReopenSelfTest(); | 2539 | return RunDbEnvReopenSelfTest(); |
| 2396 | if (name == "wallet-sqlite") | 2540 | if (name == "wallet-sqlite") |
| 2397 | return RunWalletSQLiteSelfTest(); | 2541 | return RunWalletSQLiteSelfTest(); |
| 2542 | if (name == "wallet-sqlite-migration") | ||
| 2543 | return RunWalletSQLiteMigrationSelfTest(); | ||
| 2398 | if (name == "wallet-crypto") | 2544 | if (name == "wallet-crypto") |
| 2399 | return RunWalletCryptoSelfTest(); | 2545 | return RunWalletCryptoSelfTest(); |
| 2400 | if (name == "wallet-encrypt") | 2546 | if (name == "wallet-encrypt") |
| @@ -2415,6 +2561,6 @@ int RunSelfTest(const std::string& name) | |||
| 2415 | return RunManagedTorSelfTest(); | 2561 | return RunManagedTorSelfTest(); |
| 2416 | 2562 | ||
| 2417 | printf("Unknown self-test '%s'\n", name.c_str()); | 2563 | printf("Unknown self-test '%s'\n", name.c_str()); |
| 2418 | printf("Known self-tests: wallet-keypool, wallet-hd, wallet-format, wallet-storage-sanity, db-env-reopen, wallet-sqlite, wallet-crypto, wallet-encrypt, wallet-portability, net-message, consensus-limits, pool-stratum, parse-money, socks5-proxy, managed-tor\n"); | 2564 | printf("Known self-tests: wallet-keypool, wallet-hd, wallet-format, wallet-storage-sanity, db-env-reopen, wallet-sqlite, wallet-sqlite-migration, wallet-crypto, wallet-encrypt, wallet-portability, net-message, consensus-limits, pool-stratum, parse-money, socks5-proxy, managed-tor\n"); |
| 2419 | return 1; | 2565 | return 1; |
| 2420 | } | 2566 | } |
diff --git a/src/wallet_sqlite.cpp b/src/wallet_sqlite.cpp index 4104ac6..4369af6 100644 --- a/src/wallet_sqlite.cpp +++ b/src/wallet_sqlite.cpp | |||
| @@ -97,6 +97,16 @@ bool CWalletDBSQLite::RollbackTransaction(string& strError) | |||
| 97 | return Exec("ROLLBACK;", strError); | 97 | return Exec("ROLLBACK;", strError); |
| 98 | } | 98 | } |
| 99 | 99 | ||
| 100 | bool CWalletDBSQLite::Checkpoint(string& strError) | ||
| 101 | { | ||
| 102 | if (!pdb) | ||
| 103 | { | ||
| 104 | strError = "SQLite wallet is not open"; | ||
| 105 | return false; | ||
| 106 | } | ||
| 107 | return Exec("PRAGMA wal_checkpoint(TRUNCATE);", strError); | ||
| 108 | } | ||
| 109 | |||
| 100 | bool CWalletDBSQLite::Exec(const char* pszSql, string& strError) | 110 | bool CWalletDBSQLite::Exec(const char* pszSql, string& strError) |
| 101 | { | 111 | { |
| 102 | char* pszErr = NULL; | 112 | char* pszErr = NULL; |
diff --git a/src/wallet_sqlite.h b/src/wallet_sqlite.h index 9812d3a..5ef862d 100644 --- a/src/wallet_sqlite.h +++ b/src/wallet_sqlite.h | |||
| @@ -18,6 +18,7 @@ public: | |||
| 18 | bool BeginTransaction(string& strError); | 18 | bool BeginTransaction(string& strError); |
| 19 | bool CommitTransaction(string& strError); | 19 | bool CommitTransaction(string& strError); |
| 20 | bool RollbackTransaction(string& strError); | 20 | bool RollbackTransaction(string& strError); |
| 21 | bool Checkpoint(string& strError); | ||
| 21 | 22 | ||
| 22 | bool WriteRecord(const vector<unsigned char>& vchKey, | 23 | bool WriteRecord(const vector<unsigned char>& vchKey, |
| 23 | const vector<unsigned char>& vchValue, | 24 | const vector<unsigned char>& vchValue, |
diff --git a/src/walletcmd.cpp b/src/walletcmd.cpp index 6fb09a3..20e7e33 100644 --- a/src/walletcmd.cpp +++ b/src/walletcmd.cpp | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | #include "headers_core.h" | 12 | #include "headers_core.h" |
| 13 | #include "bip32.h" | 13 | #include "bip32.h" |
| 14 | #include "walletcmd.h" | 14 | #include "walletcmd.h" |
| 15 | #include "wallet_sqlite.h" | ||
| 15 | 16 | ||
| 16 | #undef printf | 17 | #undef printf |
| 17 | 18 | ||
| @@ -790,6 +791,102 @@ int CmdWalletStorageAudit(const std::string& strJsonOut) | |||
| 790 | return 0; | 791 | return 0; |
| 791 | } | 792 | } |
| 792 | 793 | ||
| 794 | static vector<unsigned char> DataStreamBytesLocal(const CDataStream& ss) | ||
| 795 | { | ||
| 796 | return vector<unsigned char>(ss.begin(), ss.end()); | ||
| 797 | } | ||
| 798 | |||
| 799 | class CWalletSQLiteExportVisitor : public CWalletRecordVisitor | ||
| 800 | { | ||
| 801 | public: | ||
| 802 | CWalletDBSQLite& db; | ||
| 803 | int& nCopied; | ||
| 804 | |||
| 805 | CWalletSQLiteExportVisitor(CWalletDBSQLite& dbIn, int& nCopiedIn) | ||
| 806 | : db(dbIn), nCopied(nCopiedIn) { } | ||
| 807 | |||
| 808 | bool VisitWalletRecord(const CDataStream& ssKey, | ||
| 809 | const CDataStream& ssValue, | ||
| 810 | string& strErrorRet) | ||
| 811 | { | ||
| 812 | if (!db.WriteRecord(DataStreamBytesLocal(ssKey), | ||
| 813 | DataStreamBytesLocal(ssValue), | ||
| 814 | strErrorRet)) | ||
| 815 | return false; | ||
| 816 | nCopied++; | ||
| 817 | return true; | ||
| 818 | } | ||
| 819 | }; | ||
| 820 | |||
| 821 | int CmdWalletSQLiteExport(const std::string& strDest) | ||
| 822 | { | ||
| 823 | AttachTerminal(); | ||
| 824 | |||
| 825 | if (strDest.empty()) | ||
| 826 | { | ||
| 827 | fprintf(stderr, "Missing SQLite wallet export path.\n"); | ||
| 828 | return 1; | ||
| 829 | } | ||
| 830 | if (FileExists(strDest.c_str())) | ||
| 831 | { | ||
| 832 | fprintf(stderr, "Refusing to overwrite existing SQLite wallet export: %s\n", | ||
| 833 | strDest.c_str()); | ||
| 834 | return 1; | ||
| 835 | } | ||
| 836 | |||
| 837 | string strError; | ||
| 838 | CWalletDBSQLite db; | ||
| 839 | if (!db.Open(strDest, strError)) | ||
| 840 | { | ||
| 841 | fprintf(stderr, "Cannot open SQLite wallet export: %s\n", strError.c_str()); | ||
| 842 | return 1; | ||
| 843 | } | ||
| 844 | if (!db.BeginTransaction(strError)) | ||
| 845 | { | ||
| 846 | fprintf(stderr, "Cannot begin SQLite wallet export: %s\n", strError.c_str()); | ||
| 847 | return 1; | ||
| 848 | } | ||
| 849 | |||
| 850 | int nCopied = 0; | ||
| 851 | CWalletSQLiteExportVisitor visitor(db, nCopied); | ||
| 852 | if (!ScanWalletRecords(visitor, strError)) | ||
| 853 | { | ||
| 854 | string strRollbackError; | ||
| 855 | db.RollbackTransaction(strRollbackError); | ||
| 856 | fprintf(stderr, "Cannot export wallet.dat to SQLite: %s\n", strError.c_str()); | ||
| 857 | return 1; | ||
| 858 | } | ||
| 859 | if (!db.CommitTransaction(strError)) | ||
| 860 | { | ||
| 861 | fprintf(stderr, "Cannot commit SQLite wallet export: %s\n", strError.c_str()); | ||
| 862 | return 1; | ||
| 863 | } | ||
| 864 | if (!db.Checkpoint(strError)) | ||
| 865 | { | ||
| 866 | fprintf(stderr, "Cannot checkpoint SQLite wallet export: %s\n", strError.c_str()); | ||
| 867 | return 1; | ||
| 868 | } | ||
| 869 | |||
| 870 | int nCount = 0; | ||
| 871 | if (!db.CountRecords(nCount, strError)) | ||
| 872 | { | ||
| 873 | fprintf(stderr, "Cannot verify SQLite wallet export count: %s\n", | ||
| 874 | strError.c_str()); | ||
| 875 | return 1; | ||
| 876 | } | ||
| 877 | if (nCount != nCopied) | ||
| 878 | { | ||
| 879 | fprintf(stderr, "SQLite wallet export count mismatch: copied %d, stored %d\n", | ||
| 880 | nCopied, nCount); | ||
| 881 | return 1; | ||
| 882 | } | ||
| 883 | |||
| 884 | printf("SQLite wallet export written to %s\n", strDest.c_str()); | ||
| 885 | printf(" records copied: %d\n", nCopied); | ||
| 886 | fflush(stdout); | ||
| 887 | return 0; | ||
| 888 | } | ||
| 889 | |||
| 793 | int CmdEncryptWallet(const std::string& strPassphrase) | 890 | int CmdEncryptWallet(const std::string& strPassphrase) |
| 794 | { | 891 | { |
| 795 | AttachTerminal(); | 892 | AttachTerminal(); |
diff --git a/src/walletcmd.h b/src/walletcmd.h index 6077669..b1a29d2 100644 --- a/src/walletcmd.h +++ b/src/walletcmd.h | |||
| @@ -70,6 +70,11 @@ int CmdWalletStorageAudit(const std::string& strJsonOut = ""); | |||
| 70 | // or migration-unsafe mix of storage records. | 70 | // or migration-unsafe mix of storage records. |
| 71 | int CmdWalletStorageCheck(); | 71 | int CmdWalletStorageCheck(); |
| 72 | 72 | ||
| 73 | // Diagnostic/migration staging: copy every raw wallet.dat record into an | ||
| 74 | // experimental SQLite key/value store, preserving serialized bytes. This does | ||
| 75 | // not change which backend the runtime wallet opens. | ||
| 76 | int CmdWalletSQLiteExport(const std::string& strDest); | ||
| 77 | |||
| 73 | // Rewrite wallet.dat so private keys and the HD seed are encrypted with the | 78 | // Rewrite wallet.dat so private keys and the HD seed are encrypted with the |
| 74 | // given passphrase. Returns 0 on success and exits without starting the node. | 79 | // given passphrase. Returns 0 on success and exits without starting the node. |
| 75 | int CmdEncryptWallet(const std::string& strPassphrase); | 80 | int CmdEncryptWallet(const std::string& strPassphrase); |