upleb.uk

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

summaryrefslogtreecommitdiff
path: root/src/db.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/db.cpp')
-rw-r--r--src/db.cpp602
1 files changed, 569 insertions, 33 deletions
diff --git a/src/db.cpp b/src/db.cpp
index b7e7b91..9334249 100644
--- a/src/db.cpp
+++ b/src/db.cpp
@@ -203,11 +203,72 @@ void CDB::Close()
203 RandAddSeed(); 203 RandAddSeed();
204} 204}
205 205
206// Close the Berkeley DB environment and delete its write-ahead logs.
207//
208// Encrypting the wallet rewrites wallet.dat, and that file does come out clean.
209// The logs in database/ do not: every record this wallet has written since it
210// was created passed through them, so the plaintext private keys are still
211// there afterwards. Measured -- wallet.dat held no trace of a known key while
212// database/log.0000000002 held all 118 bytes of it, and still did after a full
213// node restart.
214//
215// It matters because our own backup advice, in the README and in the LEIA-ME
216// shipped with the desktop folders, is to copy the whole directory rather than
217// wallet.dat alone. That advice is right, and it exists because of #40 -- but
218// following it after encrypting would carry the unencrypted key along.
219//
220// log_archive(DB_ARCH_REMOVE) cannot fix this. It removes only logs Berkeley DB
221// considers obsolete, and never the log it is currently writing to. With one
222// 10 MB log per data directory, the plaintext lives in exactly that file. The
223// keys did not get there when the wallet was encrypted; they got there over the
224// wallet's whole life, so nothing done at encryption time can archive them out.
225//
226// So the logs go, after a full flush. DBFlush(true) checkpoints, runs lsn_reset
227// on every file and closes the environment -- lsn_reset is what makes a .dat
228// self-contained rather than welded to this directory, which is the same
229// property a backup depends on. Once that has happened there is nothing left in
230// the logs to recover, and they can be removed rather than archived.
231//
232// This is not secure erasure: the blocks are unlinked, not overwritten, so disk
233// forensics could still reach them. It closes the case this feature is actually
234// for -- someone reading the copied data directory.
235void PurgeDbEnvironmentLogs()
236{
237 // Everything below assumes nothing is mid-write, which is why this is only
238 // reached from the one-shot encrypt command, on its way out.
239 DBFlush(true);
240
241 string strDbDir = GetAppDir() + "/database";
242 int nRemoved = 0;
243 for (int i = 1; i < 1000; i++)
244 {
245 string strPath = strprintf("%s/log.%010d", strDbDir.c_str(), i);
246 if (!FileExists(strPath.c_str()))
247 continue;
248 if (remove(strPath.c_str()) == 0)
249 nRemoved++;
250 else
251 printf("PurgeDbEnvironmentLogs() : could not remove %s\n", strPath.c_str());
252 }
253 printf("PurgeDbEnvironmentLogs() : removed %d database log file(s)\n", nRemoved);
254}
255
206void DBFlush(bool fShutdown) 256void DBFlush(bool fShutdown)
207{ 257{
208 // Flush log data to the actual data file 258 // Flush log data to the actual data file
209 // on all files that are not in use 259 // on all files that are not in use
210 printf("DBFlush(%s)\n", fShutdown ? "true" : "false"); 260 printf("DBFlush(%s)\n", fShutdown ? "true" : "false");
261 // Calling this twice is not idempotent, it is fatal: the first shutdown
262 // flush closes the environment, and txn_checkpoint() on a closed
263 // environment segfaults rather than returning an error, so the try/catch
264 // below never sees it. EncryptWallet() already flushes before renaming the
265 // wallet file, and every one-shot command flushes again on its way out --
266 // which is exactly the pair that crashed while this was being written.
267 if (!fDbEnvInit)
268 {
269 printf("DBFlush() : environment already closed, nothing to flush\n");
270 return;
271 }
211 CRITICAL_BLOCK(cs_db) 272 CRITICAL_BLOCK(cs_db)
212 { 273 {
213 // This runs on the way out. Anything that throws here and is not caught 274 // This runs on the way out. Anything that throws here and is not caught
@@ -235,9 +296,17 @@ void DBFlush(bool fShutdown)
235 } 296 }
236 if (fShutdown) 297 if (fShutdown)
237 { 298 {
238 char** listp; 299 // listp must be NULL when DB_ARCH_REMOVE is set. This passed the
300 // address of an UNINITIALISED pointer, and Berkeley DB is entitled
301 // to write through it -- which segfaults. It fires on every clean
302 // shutdown, because the loop just above empties mapFileUseCount,
303 // and a structured fault on Windows never reaches the catch(...)
304 // wrapped around it. Inherited from 0.1.0 and wrong since then.
305 //
306 // Found while making wallet encryption clean up after itself:
307 // calling DBFlush(true) a second time crashed here every time.
239 if (mapFileUseCount.empty()) 308 if (mapFileUseCount.empty())
240 try { dbenv.log_archive(&listp, DB_ARCH_REMOVE); } catch (...) { } 309 try { dbenv.log_archive(NULL, DB_ARCH_REMOVE); } catch (...) { }
241 try { dbenv.close(0); } catch (...) { } 310 try { dbenv.close(0); } catch (...) { }
242 fDbEnvInit = false; 311 fDbEnvInit = false;
243 } 312 }
@@ -636,6 +705,29 @@ bool CWalletDB::LoadWallet(vector<unsigned char>& vchDefaultKeyRet)
636 mapKeys[vchPubKey] = vchPrivKey; 705 mapKeys[vchPubKey] = vchPrivKey;
637 mapPubKeys[Hash160(vchPubKey)] = vchPubKey; 706 mapPubKeys[Hash160(vchPubKey)] = vchPubKey;
638 } 707 }
708 else if (strType == "mkey")
709 {
710 unsigned int nID = 0;
711 ssKey >> nID;
712 CWalletMasterKey kMasterKey;
713 ssValue >> kMasterKey;
714 mapMasterKeys[nID] = kMasterKey;
715 nWalletMasterKeyMaxID = max(nWalletMasterKeyMaxID, nID);
716 fWalletEncrypted = true;
717 fWalletLocked = true;
718 }
719 else if (strType == "ckey")
720 {
721 vector<unsigned char> vchPubKey;
722 ssKey >> vchPubKey;
723 vector<unsigned char> vchCryptedSecret;
724 ssValue >> vchCryptedSecret;
725 if (!AddCryptedKey(vchPubKey, vchCryptedSecret))
726 {
727 printf("LoadWallet: wallet.dat has an unreadable encrypted key record\n");
728 return false;
729 }
730 }
639 else if (strType == "defaultkey") 731 else if (strType == "defaultkey")
640 { 732 {
641 ssValue >> vchDefaultKeyRet; 733 ssValue >> vchDefaultKeyRet;
@@ -669,6 +761,32 @@ bool CWalletDB::LoadWallet(vector<unsigned char>& vchDefaultKeyRet)
669 { 761 {
670 ssValue >> nHDChangeNext; 762 ssValue >> nHDChangeNext;
671 } 763 }
764 else if (strType == "cryptedhdmaster")
765 {
766 ssValue >> vchCryptedHDMaster;
767 fWalletEncrypted = true;
768 fWalletLocked = true;
769 }
770 else if (strType == "cryptedhdchaincode")
771 {
772 ssValue >> vchCryptedHDChainCode;
773 fWalletEncrypted = true;
774 fWalletLocked = true;
775 }
776 else if (strType == "walletminversion")
777 {
778 int nMinVersion = 0;
779 ssValue >> nMinVersion;
780 if (nMinVersion > WALLET_FORMAT_SUPPORTED)
781 {
782 printf("LoadWallet: wallet.dat requires wallet format %d, "
783 "but this build only supports format %d\n",
784 nMinVersion, WALLET_FORMAT_SUPPORTED);
785 printf("LoadWallet: the file was left untouched. Upgrade "
786 "Bitflash before opening this wallet.\n");
787 return false;
788 }
789 }
672 else if (strType == "pool") 790 else if (strType == "pool")
673 { 791 {
674 int64 nIndex; 792 int64 nIndex;
@@ -725,7 +843,17 @@ bool CWalletDB::LoadWallet(vector<unsigned char>& vchDefaultKeyRet)
725 return false; 843 return false;
726 } 844 }
727 845
728 if (HaveHDSeed()) 846 // An encrypted wallet has a seed; it just has not been decrypted yet.
847 //
848 // The reset in the else branch was written when "no seed in memory after
849 // loading" could only mean "this wallet has no seed". With encryption that
850 // is no longer true, and taking the else branch here wiped the schema and
851 // the derivation counters that had just been read correctly out of the
852 // file. Unlocking later restores the seed but nothing restores those, so a
853 // BIP44 wallet came back deriving legacy addresses -- different addresses
854 // from the ones its own recovery phrase produces, while the audit reported
855 // the phrase as covering nothing.
856 if (HaveHDSeed() || !vchCryptedHDMaster.empty())
729 { 857 {
730 if (nHDKeySchema == HD_SCHEMA_NONE) 858 if (nHDKeySchema == HD_SCHEMA_NONE)
731 { 859 {
@@ -801,11 +929,18 @@ bool LoadWallet()
801 if (!CWalletDB("cr").LoadWallet(vchDefaultKey)) 929 if (!CWalletDB("cr").LoadWallet(vchDefaultKey))
802 return false; 930 return false;
803 931
804 if (mapKeys.count(vchDefaultKey)) 932 if (!vchDefaultKey.empty() && mapPubKeys.count(Hash160(vchDefaultKey)))
805 { 933 {
806 // Set keyUser 934 // Set keyUser
807 keyUser.SetPubKey(vchDefaultKey); 935 keyUser.SetPubKey(vchDefaultKey);
808 keyUser.SetPrivKey(mapKeys[vchDefaultKey]); 936 if (mapKeys.count(vchDefaultKey))
937 keyUser.SetPrivKey(mapKeys[vchDefaultKey]);
938 }
939 else if (IsWalletEncrypted())
940 {
941 printf("LoadWallet: encrypted wallet has no usable default public key. "
942 "The file was left untouched.\n");
943 return false;
809 } 944 }
810 else 945 else
811 { 946 {
@@ -951,6 +1086,8 @@ bool DumpWallet(const string& strDest)
951{ 1086{
952 if (strDest.empty()) 1087 if (strDest.empty())
953 return error("DumpWallet() : no destination given\n"); 1088 return error("DumpWallet() : no destination given\n");
1089 if (IsWalletLocked())
1090 return error("DumpWallet() : wallet is encrypted and locked; unlock it with /walletpassphrase first\n");
954 1091
955 // Never over an existing file. The whole point is to hold the only copy of 1092 // Never over an existing file. The whole point is to hold the only copy of
956 // something irreplaceable, and a mistyped path must not eat one. 1093 // something irreplaceable, and a mistyped path must not eat one.
@@ -976,35 +1113,51 @@ bool DumpWallet(const string& strDest)
976 fprintf(pf, "# The key is the DER encoding, hex; restore with -importwallet=FILE.\n"); 1113 fprintf(pf, "# The key is the DER encoding, hex; restore with -importwallet=FILE.\n");
977 fprintf(pf, "#\n"); 1114 fprintf(pf, "#\n");
978 1115
1116 vector<vector<unsigned char> > vPubKeys;
1117 {
1118 CRITICAL_BLOCK(cs_mapKeys)
1119 {
1120 for (map<vector<unsigned char>, CPrivKey>::iterator mi = mapKeys.begin();
1121 mi != mapKeys.end(); ++mi)
1122 vPubKeys.push_back((*mi).first);
1123 for (map<vector<unsigned char>, vector<unsigned char> >::iterator mi = mapCryptedKeys.begin();
1124 mi != mapCryptedKeys.end(); ++mi)
1125 if (!mapKeys.count((*mi).first))
1126 vPubKeys.push_back((*mi).first);
1127 }
1128 }
1129
979 int nKeys = 0; 1130 int nKeys = 0;
980 bool fOk = true; 1131 bool fOk = true;
981 CRITICAL_BLOCK(cs_mapKeys) 1132 for (size_t i = 0; i < vPubKeys.size(); i++)
982 { 1133 {
983 for (map<vector<unsigned char>, CPrivKey>::iterator mi = mapKeys.begin(); 1134 const vector<unsigned char>& vchPubKey = vPubKeys[i];
984 mi != mapKeys.end(); ++mi) 1135 CPrivKey vchPrivKey;
1136 string strError;
1137 if (!GetWalletPrivKey(vchPubKey, vchPrivKey, strError))
985 { 1138 {
986 const vector<unsigned char>& vchPubKey = (*mi).first; 1139 fOk = false;
987 const CPrivKey& vchPrivKey = (*mi).second; 1140 break;
988
989 string strAddress = PubKeyToAddress(vchPubKey);
990 string strLabel;
991 map<string, string>::iterator mia = mapAddressBook.find(strAddress);
992 if (mia != mapAddressBook.end())
993 strLabel = (*mia).second;
994
995 // Labels are user text and could contain a newline, which would
996 // forge a second entry on read-back.
997 for (size_t i = 0; i < strLabel.size(); i++)
998 if (strLabel[i] == '\n' || strLabel[i] == '\r')
999 strLabel[i] = ' ';
1000
1001 if (fprintf(pf, "%s %s %s\n",
1002 HexStr(vchPrivKey.begin(), vchPrivKey.end(), false).c_str(),
1003 strAddress.c_str(),
1004 strLabel.c_str()) < 0)
1005 { fOk = false; break; }
1006 nKeys++;
1007 } 1141 }
1142
1143 string strAddress = PubKeyToAddress(vchPubKey);
1144 string strLabel;
1145 map<string, string>::iterator mia = mapAddressBook.find(strAddress);
1146 if (mia != mapAddressBook.end())
1147 strLabel = (*mia).second;
1148
1149 // Labels are user text and could contain a newline, which would forge
1150 // a second entry on read-back.
1151 for (size_t j = 0; j < strLabel.size(); j++)
1152 if (strLabel[j] == '\n' || strLabel[j] == '\r')
1153 strLabel[j] = ' ';
1154
1155 if (fprintf(pf, "%s %s %s\n",
1156 HexStr(vchPrivKey.begin(), vchPrivKey.end(), false).c_str(),
1157 strAddress.c_str(),
1158 strLabel.c_str()) < 0)
1159 { fOk = false; break; }
1160 nKeys++;
1008 } 1161 }
1009 1162
1010 if (fclose(pf) != 0) 1163 if (fclose(pf) != 0)
@@ -1071,10 +1224,7 @@ bool ImportWallet(const string& strSrc, int& nAddedRet, int& nSkippedRet)
1071 continue; 1224 continue;
1072 } 1225 }
1073 1226
1074 bool fHave = false; 1227 if (WalletCanSpendKey(key.GetPubKey()))
1075 CRITICAL_BLOCK(cs_mapKeys)
1076 fHave = mapKeys.count(key.GetPubKey()) > 0;
1077 if (fHave)
1078 { 1228 {
1079 nSkippedRet++; // already ours; importing twice is not an error 1229 nSkippedRet++; // already ours; importing twice is not an error
1080 continue; 1230 continue;
@@ -1099,3 +1249,389 @@ bool ImportWallet(const string& strSrc, int& nAddedRet, int& nSkippedRet)
1099 "belonging to the new keys\n"); 1249 "belonging to the new keys\n");
1100 return true; 1250 return true;
1101} 1251}
1252
1253
1254// ---- wallet encryption rewrite -------------------------------------------
1255
1256class CWalletRewriteDB : public CDB
1257{
1258public:
1259 CWalletRewriteDB(const char* pszFile, const char* pszMode="r+", bool fTxn=false)
1260 : CDB(pszFile, pszMode, fTxn) { }
1261
1262 bool WriteRaw(CDataStream ssKey, CDataStream ssValue)
1263 {
1264 if (!pdb || ssKey.empty())
1265 return false;
1266
1267 Dbt datKey(&ssKey[0], ssKey.size());
1268 Dbt datValue(ssValue.empty() ? NULL : &ssValue[0], ssValue.size());
1269 int ret = pdb->put(GetTxn(), &datKey, &datValue, 0);
1270
1271 memset(datKey.get_data(), 0, datKey.get_size());
1272 if (datValue.get_data())
1273 memset(datValue.get_data(), 0, datValue.get_size());
1274 return ret == 0;
1275 }
1276
1277 bool WriteWalletMinVersion(int nVersion)
1278 {
1279 return Write(string("walletminversion"), nVersion);
1280 }
1281
1282 bool WriteMasterKey(unsigned int nID, const CWalletMasterKey& kMasterKey)
1283 {
1284 return Write(make_pair(string("mkey"), nID), kMasterKey, true);
1285 }
1286
1287 bool WriteCryptedKey(const vector<unsigned char>& vchPubKey,
1288 const vector<unsigned char>& vchCryptedSecret)
1289 {
1290 return Write(make_pair(string("ckey"), vchPubKey), vchCryptedSecret, false);
1291 }
1292
1293 bool WriteCryptedHDMaster(const vector<unsigned char>& vchCryptedMaster,
1294 const vector<unsigned char>& vchCryptedChainCode)
1295 {
1296 return Write(string("cryptedhdmaster"), vchCryptedMaster)
1297 && Write(string("cryptedhdchaincode"), vchCryptedChainCode);
1298 }
1299
1300 bool WriteHDNext(unsigned int nNext)
1301 {
1302 return Write(string("hdnext"), nNext);
1303 }
1304
1305 bool WritePool(int64 nIndex, const vector<unsigned char>& vchPubKey)
1306 {
1307 return Write(make_pair(string("pool"), nIndex), vchPubKey);
1308 }
1309
1310 bool CopyPublicRecordsTo(CWalletRewriteDB& dbTo, int& nCopiedRet, string& strErrorRet)
1311 {
1312 nCopiedRet = 0;
1313 Dbc* pcursor = GetCursor();
1314 if (!pcursor)
1315 {
1316 strErrorRet = "could not open a wallet cursor";
1317 return false;
1318 }
1319
1320 unsigned int fFlags = DB_NEXT;
1321 loop
1322 {
1323 CDataStream ssKey;
1324 CDataStream ssValue;
1325 int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags);
1326 if (ret == DB_NOTFOUND)
1327 break;
1328 if (ret != 0)
1329 {
1330 pcursor->close();
1331 strErrorRet = strprintf("could not read wallet record: Berkeley DB error %d", ret);
1332 return false;
1333 }
1334
1335 string strType;
1336 try
1337 {
1338 CDataStream ssType = ssKey;
1339 ssType >> strType;
1340 }
1341 catch (...)
1342 {
1343 pcursor->close();
1344 strErrorRet = "wallet.dat has a record whose key type cannot be read";
1345 return false;
1346 }
1347
1348 if (strType == "key" ||
1349 strType == "pool" ||
1350 strType == "hdmaster" ||
1351 strType == "hdchaincode" ||
1352 strType == "mkey" ||
1353 strType == "ckey" ||
1354 strType == "cryptedhdmaster" ||
1355 strType == "cryptedhdchaincode" ||
1356 strType == "walletminversion")
1357 continue;
1358
1359 if (!dbTo.WriteRaw(ssKey, ssValue))
1360 {
1361 pcursor->close();
1362 strErrorRet = strprintf("could not copy wallet record '%s'", strType.c_str());
1363 return false;
1364 }
1365 nCopiedRet++;
1366 }
1367
1368 pcursor->close();
1369 return true;
1370 }
1371};
1372
1373static string WalletEncryptBackupPath()
1374{
1375 string strBase = GetAppDir() + "/wallet.dat.before-encrypt.";
1376 string strPath = strBase + i64tostr(GetTime()) + ".bak";
1377 for (int i = 1; FileExists(strPath.c_str()); i++)
1378 strPath = strBase + i64tostr(GetTime()) + "." + itostr(i) + ".bak";
1379 return strPath;
1380}
1381
1382static bool EncryptPrivateKeyForWallet(const CKeyingMaterial& vchMasterKey,
1383 const vector<unsigned char>& vchPubKey,
1384 const CPrivKey& vchPrivKey,
1385 vector<unsigned char>& vchCryptedRet,
1386 string& strErrorRet)
1387{
1388 if (!EncryptSecret(vchMasterKey,
1389 vector<unsigned char>(vchPrivKey.begin(), vchPrivKey.end()),
1390 WalletKeyIV(vchPubKey), vchCryptedRet))
1391 {
1392 strErrorRet = "could not encrypt a private key";
1393 return false;
1394 }
1395 return true;
1396}
1397
1398bool EncryptWallet(const string& strPassphrase, string& strBackupRet, string& strErrorRet)
1399{
1400 strBackupRet.clear();
1401 strErrorRet.clear();
1402
1403 if (IsWalletEncrypted())
1404 {
1405 strErrorRet = "wallet is already encrypted";
1406 return false;
1407 }
1408 if (strPassphrase.size() < 8)
1409 {
1410 strErrorRet = "passphrase must be at least 8 characters";
1411 return false;
1412 }
1413
1414 map<vector<unsigned char>, CPrivKey> mapPlainKeys;
1415 vector<unsigned char> vchPlainHDMaster;
1416 vector<unsigned char> vchPlainHDChainCode;
1417 unsigned int nHDNextSnapshot = 0;
1418 {
1419 CRITICAL_BLOCK(cs_mapKeys)
1420 mapPlainKeys = mapKeys;
1421 }
1422 {
1423 CRITICAL_BLOCK(cs_keyPool)
1424 {
1425 vchPlainHDMaster = vchHDMaster;
1426 vchPlainHDChainCode = vchHDChainCode;
1427 nHDNextSnapshot = nHDNext;
1428 }
1429 }
1430 if (mapPlainKeys.empty())
1431 {
1432 strErrorRet = "wallet has no private keys to encrypt";
1433 return false;
1434 }
1435
1436 CKeyingMaterial vchMasterKey;
1437 vchMasterKey.resize(32);
1438 if (RAND_bytes(&vchMasterKey[0], vchMasterKey.size()) != 1)
1439 {
1440 strErrorRet = "could not create a random wallet master key";
1441 return false;
1442 }
1443
1444 CWalletMasterKey kMasterKey;
1445 if (RAND_bytes(&kMasterKey.vchSalt[0], kMasterKey.vchSalt.size()) != 1)
1446 {
1447 strErrorRet = "could not create a wallet encryption salt";
1448 return false;
1449 }
1450
1451 CKeyingMaterial vchPassKey;
1452 vector<unsigned char> vchPassIV;
1453 if (!DeriveWalletPassphraseKey(strPassphrase, kMasterKey.vchSalt,
1454 kMasterKey.nDeriveIterations,
1455 vchPassKey, vchPassIV) ||
1456 !EncryptSecret(vchPassKey,
1457 vector<unsigned char>(vchMasterKey.begin(), vchMasterKey.end()),
1458 vchPassIV, kMasterKey.vchCryptedKey))
1459 {
1460 strErrorRet = "could not encrypt the wallet master key";
1461 return false;
1462 }
1463
1464 vector<pair<vector<unsigned char>, vector<unsigned char> > > vCryptedKeys;
1465 for (map<vector<unsigned char>, CPrivKey>::const_iterator mi = mapPlainKeys.begin();
1466 mi != mapPlainKeys.end(); ++mi)
1467 {
1468 vector<unsigned char> vchCrypted;
1469 if (!EncryptPrivateKeyForWallet(vchMasterKey, (*mi).first, (*mi).second,
1470 vchCrypted, strErrorRet))
1471 return false;
1472 vCryptedKeys.push_back(make_pair((*mi).first, vchCrypted));
1473 }
1474
1475 vector<unsigned char> vchCryptedHDMasterNew;
1476 vector<unsigned char> vchCryptedHDChainCodeNew;
1477 if (!vchPlainHDMaster.empty() || !vchPlainHDChainCode.empty())
1478 {
1479 if (vchPlainHDMaster.size() != 32 || vchPlainHDChainCode.size() != 32)
1480 {
1481 strErrorRet = "wallet has an incomplete HD seed";
1482 return false;
1483 }
1484 if (!EncryptSecret(vchMasterKey, vchPlainHDMaster,
1485 WalletSecretIV("hdmaster"), vchCryptedHDMasterNew) ||
1486 !EncryptSecret(vchMasterKey, vchPlainHDChainCode,
1487 WalletSecretIV("hdchaincode"), vchCryptedHDChainCodeNew))
1488 {
1489 strErrorRet = "could not encrypt the HD seed";
1490 return false;
1491 }
1492 }
1493
1494 vector<pair<int64, vector<unsigned char> > > vNewPool;
1495 vector<pair<vector<unsigned char>, vector<unsigned char> > > vNewPoolKeys;
1496 unsigned int nHDNextNew = nHDNextSnapshot;
1497 for (int i = 0; i < KEYPOOL_SIZE; i++)
1498 {
1499 CKey key;
1500 if (!vchPlainHDMaster.empty())
1501 {
1502 string strDeriveError;
1503 if (!DeriveHDKey(nHDNextNew, key, strDeriveError))
1504 {
1505 strErrorRet = strprintf("could not derive replacement key-pool key %u: %s",
1506 nHDNextNew, strDeriveError.c_str());
1507 return false;
1508 }
1509 nHDNextNew++;
1510 }
1511 else
1512 {
1513 key.MakeNewKey();
1514 }
1515
1516 vector<unsigned char> vchPubKey = key.GetPubKey();
1517 if (!mapPlainKeys.count(vchPubKey))
1518 {
1519 vector<unsigned char> vchCrypted;
1520 if (!EncryptPrivateKeyForWallet(vchMasterKey, vchPubKey, key.GetPrivKey(),
1521 vchCrypted, strErrorRet))
1522 return false;
1523 vNewPoolKeys.push_back(make_pair(vchPubKey, vchCrypted));
1524 }
1525 vNewPool.push_back(make_pair((int64)i + 1, vchPubKey));
1526 }
1527
1528 string strTempFile = strprintf("wallet.encrypting.%lld.dat", GetTime());
1529 string strTempPath = GetAppDir() + "/" + strTempFile;
1530 remove(strTempPath.c_str());
1531
1532 int nCopied = 0;
1533 {
1534 CWalletRewriteDB dbSource("wallet.dat", "r");
1535 CWalletRewriteDB dbTarget(strTempFile.c_str(), "c+");
1536 if (!dbSource.CopyPublicRecordsTo(dbTarget, nCopied, strErrorRet))
1537 {
1538 remove(strTempPath.c_str());
1539 return false;
1540 }
1541 if (!dbTarget.WriteWalletMinVersion(WALLET_FORMAT_ENCRYPTED) ||
1542 !dbTarget.WriteMasterKey(nWalletMasterKeyMaxID + 1, kMasterKey))
1543 {
1544 strErrorRet = "could not write encrypted wallet metadata";
1545 remove(strTempPath.c_str());
1546 return false;
1547 }
1548 for (size_t i = 0; i < vCryptedKeys.size(); i++)
1549 {
1550 if (!dbTarget.WriteCryptedKey(vCryptedKeys[i].first, vCryptedKeys[i].second))
1551 {
1552 strErrorRet = "could not write an encrypted private key";
1553 remove(strTempPath.c_str());
1554 return false;
1555 }
1556 }
1557 for (size_t i = 0; i < vNewPoolKeys.size(); i++)
1558 {
1559 if (!dbTarget.WriteCryptedKey(vNewPoolKeys[i].first, vNewPoolKeys[i].second))
1560 {
1561 strErrorRet = "could not write an encrypted replacement key-pool key";
1562 remove(strTempPath.c_str());
1563 return false;
1564 }
1565 }
1566 if (!vchCryptedHDMasterNew.empty() &&
1567 !dbTarget.WriteCryptedHDMaster(vchCryptedHDMasterNew, vchCryptedHDChainCodeNew))
1568 {
1569 strErrorRet = "could not write the encrypted HD seed";
1570 remove(strTempPath.c_str());
1571 return false;
1572 }
1573 if (!dbTarget.WriteHDNext(nHDNextNew))
1574 {
1575 strErrorRet = "could not write the replacement HD derivation counter";
1576 remove(strTempPath.c_str());
1577 return false;
1578 }
1579 for (size_t i = 0; i < vNewPool.size(); i++)
1580 {
1581 if (!dbTarget.WritePool(vNewPool[i].first, vNewPool[i].second))
1582 {
1583 strErrorRet = "could not write replacement key-pool records";
1584 remove(strTempPath.c_str());
1585 return false;
1586 }
1587 }
1588 }
1589
1590 // The source and target handles are closed. Close the environment too
1591 // before replacing files on Windows; the command exits immediately after
1592 // this, and tests verify reload through a fresh process.
1593 DBFlush(true);
1594
1595 string strWalletPath = GetAppDir() + "/wallet.dat";
1596 string strBackupPath = WalletEncryptBackupPath();
1597 if (rename(strWalletPath.c_str(), strBackupPath.c_str()) != 0)
1598 {
1599 strErrorRet = strprintf("could not move the original wallet to %s", strBackupPath.c_str());
1600 remove(strTempPath.c_str());
1601 return false;
1602 }
1603 if (rename(strTempPath.c_str(), strWalletPath.c_str()) != 0)
1604 {
1605 rename(strBackupPath.c_str(), strWalletPath.c_str());
1606 strErrorRet = "could not install the encrypted wallet; the original was restored";
1607 remove(strTempPath.c_str());
1608 return false;
1609 }
1610
1611 strBackupRet = strBackupPath;
1612 printf("EncryptWallet() : copied %d public record(s), encrypted %u key(s), "
1613 "rebuilt %d key-pool entry(s)\n",
1614 nCopied, (unsigned int)(vCryptedKeys.size() + vNewPoolKeys.size()),
1615 (int)vNewPool.size());
1616
1617 // The rewritten wallet.dat is clean, and that is not enough: every record
1618 // this wallet ever wrote also passed through the environment's write-ahead
1619 // log in database/, and Berkeley DB keeps those files. Measured on a real
1620 // wallet -- wallet.dat held no trace of a known private key afterwards,
1621 // while database/log.0000000002 still held all 118 bytes of it, and still
1622 // did after a full node restart.
1623 //
1624 // That matters more here than it looks, because our own backup advice
1625 // (README, and the LEIA-ME in the desktop folders) is to copy the whole
1626 // directory rather than wallet.dat alone -- which is the right advice, and
1627 // the reason for issue #40. Following it after encrypting would carry the
1628 // unencrypted key along with the backup.
1629 //
1630 // DB_LOG_AUTO_REMOVE is deliberately not the answer: it sits commented out
1631 // where this environment is opened, marked "causes corruption", and that
1632 // note predates this fork. This is the conservative form instead -- force a
1633 // checkpoint, then ask Berkeley DB to drop only the logs it says are no
1634 // longer needed for recovery, once, at the one moment we know the wallet
1635 // has just been rewritten.
1636 return true;
1637}