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.cpp54
-rw-r--r--src/walletcmd.cpp209
-rw-r--r--src/walletcmd.h4
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
1198static 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
1198static int RunWalletSQLiteMigrationSelfTest() 1220static 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
906class CWalletSQLiteLoadCheckVisitor : public CWalletRecordVisitor
907{
908public:
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
908int CmdWalletSQLiteExport(const std::string& strDest) 1073int 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
1320int 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
1155int CmdEncryptWallet(const std::string& strPassphrase) 1364int 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.
84int CmdWalletSQLiteRestore(const std::string& strPath); 84int 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.
88int 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.
88int CmdEncryptWallet(const std::string& strPassphrase); 92int CmdEncryptWallet(const std::string& strPassphrase);