upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main_gui.cpp11
-rw-r--r--src/selftest.cpp35
-rw-r--r--src/walletcmd.cpp126
-rw-r--r--src/walletcmd.h4
4 files changed, 176 insertions, 0 deletions
diff --git a/src/main_gui.cpp b/src/main_gui.cpp
index caacf13..cf96822 100644
--- a/src/main_gui.cpp
+++ b/src/main_gui.cpp
@@ -185,6 +185,8 @@ static void PrintUsage()
185 printf(" experimental SQLite store, then exit)\n"); 185 printf(" experimental SQLite store, then exit)\n");
186 printf(" /walletsqliteverify=FILE (compare wallet.dat with a SQLite export,\n"); 186 printf(" /walletsqliteverify=FILE (compare wallet.dat with a SQLite export,\n");
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");
189 printf(" an empty data directory, then exit)\n");
188 printf(" /rescan (walk the chain for coins this wallet owns\n"); 190 printf(" /rescan (walk the chain for coins this wallet owns\n");
189 printf(" but never recorded, then exit)\n"); 191 printf(" but never recorded, then exit)\n");
190 printf("\n"); 192 printf("\n");
@@ -551,6 +553,15 @@ int main(int argc, char* argv[])
551 return nRet; 553 return nRet;
552 } 554 }
553 555
556 string strWalletSQLiteRestore =
557 argval2(argc, argv, "/walletsqliterestore", "-walletsqliterestore");
558 if (!strWalletSQLiteRestore.empty())
559 {
560 int nRet = CmdWalletSQLiteRestore(strWalletSQLiteRestore);
561 DBFlush(true);
562 return nRet;
563 }
564
554 // Berkeley DB reports failure by throwing, and CDB's constructor lets it 565 // Berkeley DB reports failure by throwing, and CDB's constructor lets it
555 // through. Nothing on this path caught anything, so an unreadable 566 // through. Nothing on this path caught anything, so an unreadable
556 // blkindex.dat or wallet.dat -- one written by another platform's Berkeley 567 // blkindex.dat or wallet.dat -- one written by another platform's Berkeley
diff --git a/src/selftest.cpp b/src/selftest.cpp
index 4ba90c3..7d3a96e 100644
--- a/src/selftest.cpp
+++ b/src/selftest.cpp
@@ -1230,6 +1230,41 @@ static int RunWalletSQLiteMigrationSelfTest()
1230 FileContainsText(strVerifyOut, "verification: ok"), 1230 FileContainsText(strVerifyOut, "verification: ok"),
1231 "SQLite wallet export verifies against wallet.dat") ? 0 : 1; 1231 "SQLite wallet export verifies against wallet.dat") ? 0 : 1;
1232 1232
1233 string strRestoredDir = tmp + "/restored-wallet";
1234 nFail += Check(MakeDirLocal(strRestoredDir),
1235 "restored wallet directory can be created") ? 0 : 1;
1236 string strRestoreOut = tmp + "/sqlite-restore.txt";
1237 vector<string> vRestoreArgs;
1238 vRestoreArgs.push_back("-datadir=" + strRestoredDir);
1239 vRestoreArgs.push_back("-nomanagedtor");
1240 vRestoreArgs.push_back("-nogui");
1241 vRestoreArgs.push_back("-walletsqliterestore=" + strSQLite);
1242 int nRestoreRet = RunBitflashChild(strExe, vRestoreArgs, NULL, &strRestoreOut);
1243 nFail += Check(nRestoreRet == 0 &&
1244 FileContainsText(strRestoreOut, "records restored:"),
1245 "SQLite wallet export restores into a fresh wallet.dat") ? 0 : 1;
1246
1247 string strVerifyRestoredOut = tmp + "/sqlite-verify-restored.txt";
1248 vector<string> vVerifyRestoredArgs;
1249 vVerifyRestoredArgs.push_back("-datadir=" + strRestoredDir);
1250 vVerifyRestoredArgs.push_back("-nomanagedtor");
1251 vVerifyRestoredArgs.push_back("-nogui");
1252 vVerifyRestoredArgs.push_back("-walletsqliteverify=" + strSQLite);
1253 int nVerifyRestoredRet =
1254 RunBitflashChild(strExe, vVerifyRestoredArgs, NULL, &strVerifyRestoredOut);
1255 nFail += Check(nVerifyRestoredRet == 0 &&
1256 FileContainsText(strVerifyRestoredOut,
1257 "verification: ok"),
1258 "restored wallet.dat verifies against the SQLite export") ? 0 : 1;
1259
1260 string strRestoreOverwriteOut = tmp + "/sqlite-restore-overwrite.txt";
1261 int nRestoreOverwriteRet =
1262 RunBitflashChild(strExe, vRestoreArgs, NULL, &strRestoreOverwriteOut);
1263 nFail += Check(nRestoreOverwriteRet == 1 &&
1264 FileContainsText(strRestoreOverwriteOut,
1265 "Refusing to overwrite existing wallet.dat"),
1266 "SQLite wallet restore refuses to overwrite wallet.dat") ? 0 : 1;
1267
1233 string strOverwriteOut = tmp + "/sqlite-export-overwrite.txt"; 1268 string strOverwriteOut = tmp + "/sqlite-export-overwrite.txt";
1234 int nOverwriteRet = 1269 int nOverwriteRet =
1235 RunBitflashChild(strExe, vExportArgs, NULL, &strOverwriteOut); 1270 RunBitflashChild(strExe, vExportArgs, NULL, &strOverwriteOut);
diff --git a/src/walletcmd.cpp b/src/walletcmd.cpp
index 36f6783..d1bad98 100644
--- a/src/walletcmd.cpp
+++ b/src/walletcmd.cpp
@@ -844,6 +844,52 @@ public:
844 } 844 }
845}; 845};
846 846
847class CWalletRawRestoreDB : public CWalletDB
848{
849public:
850 CWalletRawRestoreDB(const char* pszMode="cr+", bool fTxn=true)
851 : CWalletDB(pszMode, fTxn) { }
852
853 bool WriteRaw(CDataStream ssKey, CDataStream ssValue)
854 {
855 if (!pdb || ssKey.empty())
856 return false;
857
858 Dbt datKey((void*)&ssKey[0], ssKey.size());
859 Dbt datValue(ssValue.empty() ? NULL : (void*)&ssValue[0],
860 ssValue.size());
861 int ret = pdb->put(GetTxn(), &datKey, &datValue, 0);
862
863 memset(datKey.get_data(), 0, datKey.get_size());
864 if (datValue.get_data())
865 memset(datValue.get_data(), 0, datValue.get_size());
866 return ret == 0;
867 }
868};
869
870class CWalletSQLiteRestoreVisitor : public CWalletRecordVisitor
871{
872public:
873 CWalletRawRestoreDB& db;
874 int& nCopied;
875
876 CWalletSQLiteRestoreVisitor(CWalletRawRestoreDB& dbIn, int& nCopiedIn)
877 : db(dbIn), nCopied(nCopiedIn) { }
878
879 bool VisitWalletRecord(const CDataStream& ssKey,
880 const CDataStream& ssValue,
881 string& strErrorRet)
882 {
883 if (!db.WriteRaw(ssKey, ssValue))
884 {
885 strErrorRet = "could not write raw wallet record";
886 return false;
887 }
888 nCopied++;
889 return true;
890 }
891};
892
847} // namespace 893} // namespace
848 894
849int CmdWalletSQLiteExport(const std::string& strDest) 895int CmdWalletSQLiteExport(const std::string& strDest)
@@ -994,6 +1040,86 @@ int CmdWalletSQLiteVerify(const std::string& strPath)
994 return 2; 1040 return 2;
995} 1041}
996 1042
1043int CmdWalletSQLiteRestore(const std::string& strPath)
1044{
1045 AttachTerminal();
1046
1047 if (strPath.empty())
1048 {
1049 fprintf(stderr, "Missing SQLite wallet export path.\n");
1050 return 1;
1051 }
1052 if (!FileExists(strPath.c_str()))
1053 {
1054 fprintf(stderr, "SQLite wallet export does not exist: %s\n",
1055 strPath.c_str());
1056 return 1;
1057 }
1058
1059 string strWalletPath = GetAppDir() + "/wallet.dat";
1060 if (FileExists(strWalletPath.c_str()))
1061 {
1062 fprintf(stderr, "Refusing to overwrite existing wallet.dat: %s\n",
1063 strWalletPath.c_str());
1064 return 1;
1065 }
1066
1067 string strError;
1068 CWalletDBSQLite sqlite;
1069 if (!sqlite.Open(strPath, strError))
1070 {
1071 fprintf(stderr, "Cannot open SQLite wallet export: %s\n", strError.c_str());
1072 return 1;
1073 }
1074
1075 int nSQLiteRecords = 0;
1076 if (!sqlite.CountRecords(nSQLiteRecords, strError))
1077 {
1078 fprintf(stderr, "Cannot count SQLite wallet export records: %s\n",
1079 strError.c_str());
1080 return 1;
1081 }
1082 if (nSQLiteRecords <= 0)
1083 {
1084 fprintf(stderr, "SQLite wallet export contains no records.\n");
1085 return 2;
1086 }
1087
1088 CWalletRawRestoreDB bdb("cr+", true);
1089 if (!bdb.TxnBegin())
1090 {
1091 fprintf(stderr, "Cannot begin Berkeley DB restore transaction.\n");
1092 return 1;
1093 }
1094
1095 int nCopied = 0;
1096 CWalletSQLiteRestoreVisitor visitor(bdb, nCopied);
1097 if (!sqlite.ScanRecords(visitor, strError))
1098 {
1099 bdb.TxnAbort();
1100 fprintf(stderr, "Cannot restore SQLite wallet export: %s\n",
1101 strError.c_str());
1102 return 1;
1103 }
1104 if (nCopied != nSQLiteRecords)
1105 {
1106 bdb.TxnAbort();
1107 fprintf(stderr, "SQLite wallet restore copied %d records, expected %d.\n",
1108 nCopied, nSQLiteRecords);
1109 return 2;
1110 }
1111 if (!bdb.TxnCommit())
1112 {
1113 fprintf(stderr, "Cannot commit Berkeley DB restore transaction.\n");
1114 return 1;
1115 }
1116
1117 printf("SQLite wallet export restored to wallet.dat\n");
1118 printf(" records restored: %d\n", nCopied);
1119 fflush(stdout);
1120 return 0;
1121}
1122
997int CmdEncryptWallet(const std::string& strPassphrase) 1123int CmdEncryptWallet(const std::string& strPassphrase)
998{ 1124{
999 AttachTerminal(); 1125 AttachTerminal();
diff --git a/src/walletcmd.h b/src/walletcmd.h
index 784c74c..50dc69e 100644
--- a/src/walletcmd.h
+++ b/src/walletcmd.h
@@ -79,6 +79,10 @@ int CmdWalletSQLiteExport(const std::string& strDest);
79// wallet.dat raw records. Prints counts only, never record keys or values. 79// wallet.dat raw records. Prints counts only, never record keys or values.
80int CmdWalletSQLiteVerify(const std::string& strPath); 80int CmdWalletSQLiteVerify(const std::string& strPath);
81 81
82// Diagnostic/migration staging: rebuild wallet.dat from a SQLite export in an
83// empty data directory. Refuses to overwrite an existing wallet.dat.
84int CmdWalletSQLiteRestore(const std::string& strPath);
85
82// Rewrite wallet.dat so private keys and the HD seed are encrypted with the 86// Rewrite wallet.dat so private keys and the HD seed are encrypted with the
83// given passphrase. Returns 0 on success and exits without starting the node. 87// given passphrase. Returns 0 on success and exits without starting the node.
84int CmdEncryptWallet(const std::string& strPassphrase); 88int CmdEncryptWallet(const std::string& strPassphrase);