diff options
Diffstat (limited to 'src/db.cpp')
| -rw-r--r-- | src/db.cpp | 1646 |
1 files changed, 1646 insertions, 0 deletions
diff --git a/src/db.cpp b/src/db.cpp new file mode 100644 index 0000000..c8642e9 --- /dev/null +++ b/src/db.cpp | |||
| @@ -0,0 +1,1646 @@ | |||
| 1 | // Copyright (c) 2009 Satoshi Nakamoto | ||
| 2 | // Distributed under the MIT/X11 software license, see the accompanying | ||
| 3 | // file license.txt or http://www.opensource.org/licenses/mit-license.php. | ||
| 4 | |||
| 5 | #include "headers.h" | ||
| 6 | |||
| 7 | |||
| 8 | |||
| 9 | |||
| 10 | |||
| 11 | |||
| 12 | // | ||
| 13 | // CDB | ||
| 14 | // | ||
| 15 | |||
| 16 | // Closes a Berkeley DB cursor however the function exits. | ||
| 17 | // | ||
| 18 | // Three functions opened one and none of them ever closed it. Berkeley DB | ||
| 19 | // requires every cursor to be closed before the handle it came from, and | ||
| 20 | // ~CDB() calls Close(), which calls pdb->close() inside a catch-all that | ||
| 21 | // swallows whatever it returns. So the violation was real and silent. | ||
| 22 | // | ||
| 23 | // An open cursor also holds read locks. That is what made the first version of | ||
| 24 | // the startup chain repair (#61) hang: it opened a write transaction beside | ||
| 25 | // the cursor CTxDB::LoadBlockIndex was still holding, and waited on it forever | ||
| 26 | // with nothing in any log. The repair had to be moved to the caller. | ||
| 27 | // | ||
| 28 | // Each of these functions has several early returns, so a guard is safer than | ||
| 29 | // remembering to close on every one of them. Same fix Bitcoin made in | ||
| 30 | // c5c7911da, alongside its zombie-socket and double-close work. | ||
| 31 | class CAutoCursor | ||
| 32 | { | ||
| 33 | public: | ||
| 34 | explicit CAutoCursor(Dbc* pcursorIn) : pcursor(pcursorIn) { } | ||
| 35 | ~CAutoCursor() | ||
| 36 | { | ||
| 37 | if (pcursor) | ||
| 38 | { | ||
| 39 | try { pcursor->close(); } | ||
| 40 | catch (...) { } // closing on the way out must not throw | ||
| 41 | } | ||
| 42 | } | ||
| 43 | private: | ||
| 44 | Dbc* pcursor; | ||
| 45 | CAutoCursor(const CAutoCursor&); | ||
| 46 | void operator=(const CAutoCursor&); | ||
| 47 | }; | ||
| 48 | |||
| 49 | static CCriticalSection cs_db; | ||
| 50 | static bool fDbEnvInit = false; | ||
| 51 | |||
| 52 | // Why LoadWallet() refused, in words meant for the person who has to act on it. | ||
| 53 | // Empty unless the reason is one we can name. | ||
| 54 | string strWalletLoadError; | ||
| 55 | DbEnv dbenv(0u); | ||
| 56 | static map<string, int> mapFileUseCount; | ||
| 57 | |||
| 58 | class CDBInit | ||
| 59 | { | ||
| 60 | public: | ||
| 61 | CDBInit() | ||
| 62 | { | ||
| 63 | } | ||
| 64 | ~CDBInit() | ||
| 65 | { | ||
| 66 | if (fDbEnvInit) | ||
| 67 | { | ||
| 68 | try { dbenv.close(0); } | ||
| 69 | catch (...) { } | ||
| 70 | fDbEnvInit = false; | ||
| 71 | } | ||
| 72 | } | ||
| 73 | } | ||
| 74 | instance_of_cdbinit; | ||
| 75 | |||
| 76 | |||
| 77 | CDB::CDB(const char* pszFile, const char* pszMode, bool fTxn) : pdb(NULL) | ||
| 78 | { | ||
| 79 | int ret; | ||
| 80 | if (pszFile == NULL) | ||
| 81 | return; | ||
| 82 | |||
| 83 | bool fCreate = strchr(pszMode, 'c'); | ||
| 84 | bool fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w')); | ||
| 85 | unsigned int nFlags = DB_THREAD; | ||
| 86 | if (fCreate) | ||
| 87 | nFlags |= DB_CREATE; | ||
| 88 | else if (fReadOnly) | ||
| 89 | nFlags |= DB_RDONLY; | ||
| 90 | if (!fReadOnly || fTxn) | ||
| 91 | nFlags |= DB_AUTO_COMMIT; | ||
| 92 | |||
| 93 | CRITICAL_BLOCK(cs_db) | ||
| 94 | { | ||
| 95 | if (!fDbEnvInit) | ||
| 96 | { | ||
| 97 | string strAppDir = GetAppDir(); | ||
| 98 | string strLogDir = strAppDir + "/database"; | ||
| 99 | |||
| 100 | // _mkdir returns 0 only when it actually created the directory, so | ||
| 101 | // this is also the test for "this environment did not exist until | ||
| 102 | // now". See the recovery block after dbenv.open() for why we care. | ||
| 103 | bool fFreshEnv = (_mkdir(strLogDir.c_str()) == 0); | ||
| 104 | |||
| 105 | printf("dbenv.open strAppDir=%s\n", strAppDir.c_str()); | ||
| 106 | |||
| 107 | dbenv.set_lg_dir(strLogDir.c_str()); | ||
| 108 | dbenv.set_lg_max(10000000); | ||
| 109 | dbenv.set_lk_max_locks(10000); | ||
| 110 | dbenv.set_lk_max_objects(10000); | ||
| 111 | dbenv.set_errfile(fopen("db.log", "a")); /// debug | ||
| 112 | ///dbenv.log_set_config(DB_LOG_AUTO_REMOVE, 1); /// causes corruption | ||
| 113 | ret = dbenv.open(strAppDir.c_str(), | ||
| 114 | DB_CREATE | | ||
| 115 | DB_INIT_LOCK | | ||
| 116 | DB_INIT_LOG | | ||
| 117 | DB_INIT_MPOOL | | ||
| 118 | DB_INIT_TXN | | ||
| 119 | DB_THREAD | | ||
| 120 | DB_PRIVATE | | ||
| 121 | DB_RECOVER, | ||
| 122 | 0); | ||
| 123 | if (ret > 0) | ||
| 124 | throw runtime_error(strprintf("CDB() : error %d opening database environment\n", ret)); | ||
| 125 | fDbEnvInit = true; | ||
| 126 | |||
| 127 | // A Berkeley DB file records, in every page header, a log sequence | ||
| 128 | // number belonging to the environment that wrote it. Carried into a | ||
| 129 | // different environment those numbers refer to logs that do not | ||
| 130 | // exist, and BDB refuses to write: "Db::put: Invalid argument", | ||
| 131 | // thrown from a path that used to abort the process. | ||
| 132 | // | ||
| 133 | // That is what happens when someone backs up wallet.dat on its own. | ||
| 134 | // It is the obviously important file, it is the only one people | ||
| 135 | // copy, and the environment it is welded to lives in a database/ | ||
| 136 | // subdirectory nobody has reason to suspect. The file is intact -- | ||
| 137 | // same bytes, same keys -- and the wallet still will not open. | ||
| 138 | // Issue #40 reports exactly this, after the fact. | ||
| 139 | // | ||
| 140 | // lsn_reset() exists for precisely this migration, so do it for | ||
| 141 | // them. A fresh environment beside pre-existing .dat files means | ||
| 142 | // those files came from somewhere else; in the normal case the | ||
| 143 | // environment is already there and none of this runs. | ||
| 144 | if (fFreshEnv) | ||
| 145 | { | ||
| 146 | static const char* pszBdbFiles[] = { "wallet.dat", "blkindex.dat" }; | ||
| 147 | for (int i = 0; i < (int)(sizeof(pszBdbFiles)/sizeof(pszBdbFiles[0])); i++) | ||
| 148 | { | ||
| 149 | const char* pszName = pszBdbFiles[i]; | ||
| 150 | if (!FileExists((strAppDir + "/" + pszName).c_str())) | ||
| 151 | continue; | ||
| 152 | try | ||
| 153 | { | ||
| 154 | if (dbenv.lsn_reset(pszName, 0) == 0) | ||
| 155 | printf("CDB() : adopted %s from another environment " | ||
| 156 | "(log sequence numbers reset)\n", pszName); | ||
| 157 | } | ||
| 158 | catch (...) { } // best effort; the open below reports real trouble | ||
| 159 | } | ||
| 160 | } | ||
| 161 | } | ||
| 162 | |||
| 163 | strFile = pszFile; | ||
| 164 | ++mapFileUseCount[strFile]; | ||
| 165 | } | ||
| 166 | |||
| 167 | pdb = new Db(&dbenv, 0); | ||
| 168 | |||
| 169 | ret = pdb->open(NULL, // Txn pointer | ||
| 170 | pszFile, // Filename | ||
| 171 | "main", // Logical db name | ||
| 172 | DB_BTREE, // Database type | ||
| 173 | nFlags, // Flags | ||
| 174 | 0); | ||
| 175 | |||
| 176 | if (ret > 0) | ||
| 177 | { | ||
| 178 | delete pdb; | ||
| 179 | pdb = NULL; | ||
| 180 | CRITICAL_BLOCK(cs_db) | ||
| 181 | --mapFileUseCount[strFile]; | ||
| 182 | strFile = ""; | ||
| 183 | throw runtime_error(strprintf("CDB() : can't open database file %s, error %d\n", pszFile, ret)); | ||
| 184 | } | ||
| 185 | |||
| 186 | if (fCreate && !Exists(string("version"))) | ||
| 187 | WriteVersion(VERSION); | ||
| 188 | |||
| 189 | RandAddSeed(); | ||
| 190 | } | ||
| 191 | |||
| 192 | void CDB::Close() | ||
| 193 | { | ||
| 194 | if (!pdb) | ||
| 195 | return; | ||
| 196 | if (!vTxn.empty()) | ||
| 197 | vTxn.front()->abort(); | ||
| 198 | vTxn.clear(); | ||
| 199 | try { pdb->close(0); } catch (...) { } | ||
| 200 | delete pdb; | ||
| 201 | pdb = NULL; | ||
| 202 | try { dbenv.txn_checkpoint(0, 0, 0); } catch (...) { } | ||
| 203 | |||
| 204 | CRITICAL_BLOCK(cs_db) | ||
| 205 | --mapFileUseCount[strFile]; | ||
| 206 | |||
| 207 | RandAddSeed(); | ||
| 208 | } | ||
| 209 | |||
| 210 | // Close the Berkeley DB environment and delete its write-ahead logs. | ||
| 211 | // | ||
| 212 | // Encrypting the wallet rewrites wallet.dat, and that file does come out clean. | ||
| 213 | // The logs in database/ do not: every record this wallet has written since it | ||
| 214 | // was created passed through them, so the plaintext private keys are still | ||
| 215 | // there afterwards. Measured -- wallet.dat held no trace of a known key while | ||
| 216 | // database/log.0000000002 held all 118 bytes of it, and still did after a full | ||
| 217 | // node restart. | ||
| 218 | // | ||
| 219 | // It matters because our own backup advice, in the README and in the LEIA-ME | ||
| 220 | // shipped with the desktop folders, is to copy the whole directory rather than | ||
| 221 | // wallet.dat alone. That advice is right, and it exists because of #40 -- but | ||
| 222 | // following it after encrypting would carry the unencrypted key along. | ||
| 223 | // | ||
| 224 | // log_archive(DB_ARCH_REMOVE) cannot fix this. It removes only logs Berkeley DB | ||
| 225 | // considers obsolete, and never the log it is currently writing to. With one | ||
| 226 | // 10 MB log per data directory, the plaintext lives in exactly that file. The | ||
| 227 | // keys did not get there when the wallet was encrypted; they got there over the | ||
| 228 | // wallet's whole life, so nothing done at encryption time can archive them out. | ||
| 229 | // | ||
| 230 | // So the logs go, after a full flush. DBFlush(true) checkpoints, runs lsn_reset | ||
| 231 | // on every file and closes the environment -- lsn_reset is what makes a .dat | ||
| 232 | // self-contained rather than welded to this directory, which is the same | ||
| 233 | // property a backup depends on. Once that has happened there is nothing left in | ||
| 234 | // the logs to recover, and they can be removed rather than archived. | ||
| 235 | // | ||
| 236 | // This is not secure erasure: the blocks are unlinked, not overwritten, so disk | ||
| 237 | // forensics could still reach them. It closes the case this feature is actually | ||
| 238 | // for -- someone reading the copied data directory. | ||
| 239 | void PurgeDbEnvironmentLogs() | ||
| 240 | { | ||
| 241 | // Everything below assumes nothing is mid-write, which is why this is only | ||
| 242 | // reached from the one-shot encrypt command, on its way out. | ||
| 243 | DBFlush(true); | ||
| 244 | |||
| 245 | string strDbDir = GetAppDir() + "/database"; | ||
| 246 | int nRemoved = 0; | ||
| 247 | for (int i = 1; i < 1000; i++) | ||
| 248 | { | ||
| 249 | string strPath = strprintf("%s/log.%010d", strDbDir.c_str(), i); | ||
| 250 | if (!FileExists(strPath.c_str())) | ||
| 251 | continue; | ||
| 252 | if (remove(strPath.c_str()) == 0) | ||
| 253 | nRemoved++; | ||
| 254 | else | ||
| 255 | printf("PurgeDbEnvironmentLogs() : could not remove %s\n", strPath.c_str()); | ||
| 256 | } | ||
| 257 | printf("PurgeDbEnvironmentLogs() : removed %d database log file(s)\n", nRemoved); | ||
| 258 | } | ||
| 259 | |||
| 260 | void DBFlush(bool fShutdown) | ||
| 261 | { | ||
| 262 | // Flush log data to the actual data file | ||
| 263 | // on all files that are not in use | ||
| 264 | printf("DBFlush(%s)\n", fShutdown ? "true" : "false"); | ||
| 265 | // Calling this twice is not idempotent, it is fatal: the first shutdown | ||
| 266 | // flush closes the environment, and txn_checkpoint() on a closed | ||
| 267 | // environment segfaults rather than returning an error, so the try/catch | ||
| 268 | // below never sees it. EncryptWallet() already flushes before renaming the | ||
| 269 | // wallet file, and every one-shot command flushes again on its way out -- | ||
| 270 | // which is exactly the pair that crashed while this was being written. | ||
| 271 | if (!fDbEnvInit) | ||
| 272 | { | ||
| 273 | printf("DBFlush() : environment already closed, nothing to flush\n"); | ||
| 274 | return; | ||
| 275 | } | ||
| 276 | CRITICAL_BLOCK(cs_db) | ||
| 277 | { | ||
| 278 | // This runs on the way out. Anything that throws here and is not caught | ||
| 279 | // takes the process down before the wallet has been put down cleanly, | ||
| 280 | // which is the difference between a portable wallet.dat and one welded | ||
| 281 | // to this directory -- the lsn_reset just below is what frees it. | ||
| 282 | try { dbenv.txn_checkpoint(0, 0, 0); } | ||
| 283 | catch (const std::exception& e) | ||
| 284 | { printf("DBFlush() : checkpoint failed: %s\n", e.what()); } | ||
| 285 | |||
| 286 | map<string, int>::iterator mi = mapFileUseCount.begin(); | ||
| 287 | while (mi != mapFileUseCount.end()) | ||
| 288 | { | ||
| 289 | string strFile = (*mi).first; | ||
| 290 | int nRefCount = (*mi).second; | ||
| 291 | if (nRefCount == 0) | ||
| 292 | { | ||
| 293 | try { dbenv.lsn_reset(strFile.c_str(), 0); } | ||
| 294 | catch (const std::exception& e) | ||
| 295 | { printf("DBFlush() : lsn_reset(%s) failed: %s\n", strFile.c_str(), e.what()); } | ||
| 296 | mapFileUseCount.erase(mi++); | ||
| 297 | } | ||
| 298 | else | ||
| 299 | mi++; | ||
| 300 | } | ||
| 301 | if (fShutdown) | ||
| 302 | { | ||
| 303 | // listp must be NULL when DB_ARCH_REMOVE is set. This passed the | ||
| 304 | // address of an UNINITIALISED pointer, and Berkeley DB is entitled | ||
| 305 | // to write through it -- which segfaults. It fires on every clean | ||
| 306 | // shutdown, because the loop just above empties mapFileUseCount, | ||
| 307 | // and a structured fault on Windows never reaches the catch(...) | ||
| 308 | // wrapped around it. Inherited from 0.1.0 and wrong since then. | ||
| 309 | // | ||
| 310 | // Found while making wallet encryption clean up after itself: | ||
| 311 | // calling DBFlush(true) a second time crashed here every time. | ||
| 312 | if (mapFileUseCount.empty()) | ||
| 313 | try { dbenv.log_archive(NULL, DB_ARCH_REMOVE); } catch (...) { } | ||
| 314 | try { dbenv.close(0); } catch (...) { } | ||
| 315 | fDbEnvInit = false; | ||
| 316 | } | ||
| 317 | } | ||
| 318 | } | ||
| 319 | |||
| 320 | |||
| 321 | |||
| 322 | |||
| 323 | |||
| 324 | |||
| 325 | // | ||
| 326 | // CTxDB | ||
| 327 | // | ||
| 328 | |||
| 329 | bool CTxDB::ReadTxIndex(uint256 hash, CTxIndex& txindex) | ||
| 330 | { | ||
| 331 | assert(!fClient); | ||
| 332 | txindex.SetNull(); | ||
| 333 | return Read(make_pair(string("tx"), hash), txindex); | ||
| 334 | } | ||
| 335 | |||
| 336 | bool CTxDB::UpdateTxIndex(uint256 hash, const CTxIndex& txindex) | ||
| 337 | { | ||
| 338 | assert(!fClient); | ||
| 339 | return Write(make_pair(string("tx"), hash), txindex); | ||
| 340 | } | ||
| 341 | |||
| 342 | bool CTxDB::AddTxIndex(const CTransaction& tx, const CDiskTxPos& pos, int nHeight) | ||
| 343 | { | ||
| 344 | assert(!fClient); | ||
| 345 | |||
| 346 | // Add to tx index | ||
| 347 | uint256 hash = tx.GetHash(); | ||
| 348 | CTxIndex txindex(pos, tx.vout.size()); | ||
| 349 | return Write(make_pair(string("tx"), hash), txindex); | ||
| 350 | } | ||
| 351 | |||
| 352 | bool CTxDB::EraseTxIndex(const CTransaction& tx) | ||
| 353 | { | ||
| 354 | assert(!fClient); | ||
| 355 | uint256 hash = tx.GetHash(); | ||
| 356 | |||
| 357 | return Erase(make_pair(string("tx"), hash)); | ||
| 358 | } | ||
| 359 | |||
| 360 | bool CTxDB::ContainsTx(uint256 hash) | ||
| 361 | { | ||
| 362 | assert(!fClient); | ||
| 363 | return Exists(make_pair(string("tx"), hash)); | ||
| 364 | } | ||
| 365 | |||
| 366 | bool CTxDB::ReadOwnerTxes(uint160 hash160, int nMinHeight, vector<CTransaction>& vtx) | ||
| 367 | { | ||
| 368 | assert(!fClient); | ||
| 369 | vtx.clear(); | ||
| 370 | |||
| 371 | // Get cursor | ||
| 372 | Dbc* pcursor = GetCursor(); | ||
| 373 | if (!pcursor) | ||
| 374 | return false; | ||
| 375 | CAutoCursor cursorGuard(pcursor); | ||
| 376 | |||
| 377 | unsigned int fFlags = DB_SET_RANGE; | ||
| 378 | loop | ||
| 379 | { | ||
| 380 | // Read next record | ||
| 381 | CDataStream ssKey; | ||
| 382 | if (fFlags == DB_SET_RANGE) | ||
| 383 | ssKey << string("owner") << hash160 << CDiskTxPos(0, 0, 0); | ||
| 384 | CDataStream ssValue; | ||
| 385 | int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags); | ||
| 386 | fFlags = DB_NEXT; | ||
| 387 | if (ret == DB_NOTFOUND) | ||
| 388 | break; | ||
| 389 | else if (ret != 0) | ||
| 390 | return false; | ||
| 391 | |||
| 392 | // Unserialize | ||
| 393 | string strType; | ||
| 394 | uint160 hashItem; | ||
| 395 | CDiskTxPos pos; | ||
| 396 | ssKey >> strType >> hashItem >> pos; | ||
| 397 | int nItemHeight; | ||
| 398 | ssValue >> nItemHeight; | ||
| 399 | |||
| 400 | // Read transaction | ||
| 401 | if (strType != "owner" || hashItem != hash160) | ||
| 402 | break; | ||
| 403 | if (nItemHeight >= nMinHeight) | ||
| 404 | { | ||
| 405 | vtx.resize(vtx.size()+1); | ||
| 406 | if (!vtx.back().ReadFromDisk(pos)) | ||
| 407 | return false; | ||
| 408 | } | ||
| 409 | } | ||
| 410 | return true; | ||
| 411 | } | ||
| 412 | |||
| 413 | bool CTxDB::ReadDiskTx(uint256 hash, CTransaction& tx, CTxIndex& txindex) | ||
| 414 | { | ||
| 415 | assert(!fClient); | ||
| 416 | tx.SetNull(); | ||
| 417 | if (!ReadTxIndex(hash, txindex)) | ||
| 418 | return false; | ||
| 419 | return (tx.ReadFromDisk(txindex.pos)); | ||
| 420 | } | ||
| 421 | |||
| 422 | bool CTxDB::ReadDiskTx(uint256 hash, CTransaction& tx) | ||
| 423 | { | ||
| 424 | CTxIndex txindex; | ||
| 425 | return ReadDiskTx(hash, tx, txindex); | ||
| 426 | } | ||
| 427 | |||
| 428 | bool CTxDB::ReadDiskTx(COutPoint outpoint, CTransaction& tx, CTxIndex& txindex) | ||
| 429 | { | ||
| 430 | return ReadDiskTx(outpoint.hash, tx, txindex); | ||
| 431 | } | ||
| 432 | |||
| 433 | bool CTxDB::ReadDiskTx(COutPoint outpoint, CTransaction& tx) | ||
| 434 | { | ||
| 435 | CTxIndex txindex; | ||
| 436 | return ReadDiskTx(outpoint.hash, tx, txindex); | ||
| 437 | } | ||
| 438 | |||
| 439 | bool CTxDB::WriteBlockIndex(const CDiskBlockIndex& blockindex) | ||
| 440 | { | ||
| 441 | return Write(make_pair(string("blockindex"), blockindex.GetBlockHash()), blockindex); | ||
| 442 | } | ||
| 443 | |||
| 444 | bool CTxDB::EraseBlockIndex(uint256 hash) | ||
| 445 | { | ||
| 446 | return Erase(make_pair(string("blockindex"), hash)); | ||
| 447 | } | ||
| 448 | |||
| 449 | bool CTxDB::ReadHashBestChain(uint256& hashBestChain) | ||
| 450 | { | ||
| 451 | return Read(string("hashBestChain"), hashBestChain); | ||
| 452 | } | ||
| 453 | |||
| 454 | bool CTxDB::WriteHashBestChain(uint256 hashBestChain) | ||
| 455 | { | ||
| 456 | return Write(string("hashBestChain"), hashBestChain); | ||
| 457 | } | ||
| 458 | |||
| 459 | CBlockIndex* InsertBlockIndex(uint256 hash) | ||
| 460 | { | ||
| 461 | if (hash == 0) | ||
| 462 | return NULL; | ||
| 463 | |||
| 464 | // Return existing | ||
| 465 | map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hash); | ||
| 466 | if (mi != mapBlockIndex.end()) | ||
| 467 | return (*mi).second; | ||
| 468 | |||
| 469 | // Create new | ||
| 470 | CBlockIndex* pindexNew = new CBlockIndex(); | ||
| 471 | if (!pindexNew) | ||
| 472 | throw runtime_error("LoadBlockIndex() : new CBlockIndex failed"); | ||
| 473 | mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; | ||
| 474 | pindexNew->phashBlock = &((*mi).first); | ||
| 475 | |||
| 476 | return pindexNew; | ||
| 477 | } | ||
| 478 | |||
| 479 | bool CTxDB::LoadBlockIndex() | ||
| 480 | { | ||
| 481 | // Get cursor | ||
| 482 | Dbc* pcursor = GetCursor(); | ||
| 483 | if (!pcursor) | ||
| 484 | return false; | ||
| 485 | CAutoCursor cursorGuard(pcursor); | ||
| 486 | |||
| 487 | unsigned int fFlags = DB_SET_RANGE; | ||
| 488 | loop | ||
| 489 | { | ||
| 490 | // Read next record | ||
| 491 | CDataStream ssKey; | ||
| 492 | if (fFlags == DB_SET_RANGE) | ||
| 493 | ssKey << make_pair(string("blockindex"), uint256(0)); | ||
| 494 | CDataStream ssValue; | ||
| 495 | int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags); | ||
| 496 | fFlags = DB_NEXT; | ||
| 497 | if (ret == DB_NOTFOUND) | ||
| 498 | break; | ||
| 499 | else if (ret != 0) | ||
| 500 | return false; | ||
| 501 | |||
| 502 | // Unserialize | ||
| 503 | string strType; | ||
| 504 | ssKey >> strType; | ||
| 505 | if (strType == "blockindex") | ||
| 506 | { | ||
| 507 | CDiskBlockIndex diskindex; | ||
| 508 | ssValue >> diskindex; | ||
| 509 | |||
| 510 | // Construct block index object | ||
| 511 | CBlockIndex* pindexNew = InsertBlockIndex(diskindex.GetBlockHash()); | ||
| 512 | pindexNew->pprev = InsertBlockIndex(diskindex.hashPrev); | ||
| 513 | pindexNew->pnext = InsertBlockIndex(diskindex.hashNext); | ||
| 514 | pindexNew->nFile = diskindex.nFile; | ||
| 515 | pindexNew->nBlockPos = diskindex.nBlockPos; | ||
| 516 | pindexNew->nHeight = diskindex.nHeight; | ||
| 517 | pindexNew->nVersion = diskindex.nVersion; | ||
| 518 | pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot; | ||
| 519 | pindexNew->nTime = diskindex.nTime; | ||
| 520 | pindexNew->nBits = diskindex.nBits; | ||
| 521 | pindexNew->nNonce = diskindex.nNonce; | ||
| 522 | |||
| 523 | // Watch for genesis block and best block | ||
| 524 | if (pindexGenesisBlock == NULL && diskindex.GetBlockHash() == hashGenesisBlock) | ||
| 525 | pindexGenesisBlock = pindexNew; | ||
| 526 | } | ||
| 527 | else | ||
| 528 | { | ||
| 529 | break; | ||
| 530 | } | ||
| 531 | } | ||
| 532 | |||
| 533 | if (!ReadHashBestChain(hashBestChain)) | ||
| 534 | { | ||
| 535 | if (pindexGenesisBlock == NULL) | ||
| 536 | return true; | ||
| 537 | return error("CTxDB::LoadBlockIndex() : hashBestChain not found\n"); | ||
| 538 | } | ||
| 539 | |||
| 540 | if (!mapBlockIndex.count(hashBestChain)) | ||
| 541 | return error("CTxDB::LoadBlockIndex() : blockindex for hashBestChain not found\n"); | ||
| 542 | pindexBest = mapBlockIndex[hashBestChain]; | ||
| 543 | nBestHeight = pindexBest->nHeight; | ||
| 544 | printf("LoadBlockIndex(): hashBestChain=%s height=%d\n", hashBestChain.ToString().substr(0,14).c_str(), nBestHeight); | ||
| 545 | |||
| 546 | // Verify the tail of the best chain, and fall back to the last good block | ||
| 547 | // if any of it fails. | ||
| 548 | // | ||
| 549 | // A node that accepted blocks its current rules would reject -- because it | ||
| 550 | // was running an older build, or because the tip it landed on was never | ||
| 551 | // valid -- has no way to notice on its own. It keeps the branch, keeps | ||
| 552 | // building on it, and keeps mining coins that the rest of the network | ||
| 553 | // throws away. The only cure used to be deleting blk*.dat by hand, which | ||
| 554 | // requires knowing something is wrong in the first place. That is how a | ||
| 555 | // node here once mined a 323-block fork for five days. | ||
| 556 | // | ||
| 557 | // Checking every block on every start would mean a RandomX hash per block, | ||
| 558 | // so only the tail is checked by default. /checkblocks=0 does the lot. | ||
| 559 | int nCheckBlocks = nCheckBlocksOnLoad; | ||
| 560 | CBlockIndex* pindexFork = NULL; | ||
| 561 | int nChecked = 0; | ||
| 562 | for (CBlockIndex* pindex = pindexBest; pindex && pindex->pprev; pindex = pindex->pprev) | ||
| 563 | { | ||
| 564 | if (nCheckBlocks > 0 && nChecked >= nCheckBlocks) | ||
| 565 | break; | ||
| 566 | nChecked++; | ||
| 567 | CBlock block; | ||
| 568 | if (!block.ReadFromDisk(pindex->nFile, pindex->nBlockPos, true)) | ||
| 569 | return error("LoadBlockIndex() : ReadFromDisk failed at height %d", pindex->nHeight); | ||
| 570 | if (!block.CheckBlock()) | ||
| 571 | { | ||
| 572 | printf("LoadBlockIndex() : *** bad block at height %d, hash=%s\n", | ||
| 573 | pindex->nHeight, pindex->GetBlockHash().ToString().substr(0,14).c_str()); | ||
| 574 | // Keep going: an older bad block further back wins, because | ||
| 575 | // everything after it has to come off too. | ||
| 576 | pindexFork = pindex->pprev; | ||
| 577 | } | ||
| 578 | } | ||
| 579 | printf("LoadBlockIndex(): verified %d block(s)\n", nChecked); | ||
| 580 | |||
| 581 | // The repair itself happens in the caller, after this handle is closed. | ||
| 582 | // The cursor above is still open and holds read locks, so a write | ||
| 583 | // transaction opened here waits on it forever -- measured: the node came | ||
| 584 | // up, printed the diagnosis, and hung with no error anywhere. | ||
| 585 | pindexBadChainFork = pindexFork; | ||
| 586 | |||
| 587 | return true; | ||
| 588 | } | ||
| 589 | |||
| 590 | |||
| 591 | |||
| 592 | |||
| 593 | |||
| 594 | // Legacy IP-based peer address database (CAddrDB / addr.dat) removed -- | ||
| 595 | // peer discovery is entirely Nostr/.btf-based now. | ||
| 596 | |||
| 597 | |||
| 598 | |||
| 599 | |||
| 600 | // | ||
| 601 | // CReviewDB | ||
| 602 | // | ||
| 603 | |||
| 604 | bool CReviewDB::ReadReviews(uint256 hash, vector<CReview>& vReviews) | ||
| 605 | { | ||
| 606 | vReviews.size(); // msvc workaround, just need to do anything with vReviews | ||
| 607 | return Read(make_pair(string("reviews"), hash), vReviews); | ||
| 608 | } | ||
| 609 | |||
| 610 | bool CReviewDB::WriteReviews(uint256 hash, const vector<CReview>& vReviews) | ||
| 611 | { | ||
| 612 | return Write(make_pair(string("reviews"), hash), vReviews); | ||
| 613 | } | ||
| 614 | |||
| 615 | |||
| 616 | |||
| 617 | |||
| 618 | |||
| 619 | |||
| 620 | |||
| 621 | // | ||
| 622 | // CWalletDB | ||
| 623 | // | ||
| 624 | |||
| 625 | bool CWalletDB::LoadWallet(vector<unsigned char>& vchDefaultKeyRet) | ||
| 626 | { | ||
| 627 | // Whether wallet.dat actually carried a mining mode, as opposed to just the | ||
| 628 | // ancient fGenerateBitcoins flag. See the reconciliation further down. | ||
| 629 | bool fHaveStoredMineMode = false; | ||
| 630 | bool fHaveStoredHDCoinType = false; | ||
| 631 | vchDefaultKeyRet.clear(); | ||
| 632 | strWalletLoadError.clear(); | ||
| 633 | |||
| 634 | // Satoshi's "todo: shouldn't we catch exceptions" sat here since 2009, and | ||
| 635 | // it was not a nicety. Nothing on this path caught anything, so a record | ||
| 636 | // this build could not read -- a wallet from another platform's Berkeley | ||
| 637 | // DB, a truncated file, a field it does not understand -- threw out of | ||
| 638 | // LoadWallet, out of main(), into std::terminate and abort(). On Windows | ||
| 639 | // that surfaces as STATUS_STACK_BUFFER_OVERRUN (0xC0000409) in | ||
| 640 | // ucrtbase.dll: no message, no log line past "Loading wallet...", and a | ||
| 641 | // faulting module that has nothing to do with the actual problem. | ||
| 642 | // | ||
| 643 | // The program knew exactly what had gone wrong and threw the reason away. | ||
| 644 | // Now it says so and stops cleanly, which is the difference between "your | ||
| 645 | // wallet is unreadable and here is why" and a crash nobody can act on. | ||
| 646 | // | ||
| 647 | // Deliberately a hard failure rather than skipping the bad record: a | ||
| 648 | // partially loaded wallet is worse than one that refuses to open, because | ||
| 649 | // the balance looks plausible while keys or transactions are missing. | ||
| 650 | string strLastType; | ||
| 651 | try | ||
| 652 | { | ||
| 653 | CRITICAL_BLOCK(cs_mapKeys) | ||
| 654 | CRITICAL_BLOCK(cs_mapWallet) | ||
| 655 | { | ||
| 656 | // Get cursor | ||
| 657 | Dbc* pcursor = GetCursor(); | ||
| 658 | if (!pcursor) | ||
| 659 | return false; | ||
| 660 | CAutoCursor cursorGuard(pcursor); | ||
| 661 | |||
| 662 | loop | ||
| 663 | { | ||
| 664 | // Read next record | ||
| 665 | CDataStream ssKey; | ||
| 666 | CDataStream ssValue; | ||
| 667 | int ret = ReadAtCursor(pcursor, ssKey, ssValue); | ||
| 668 | if (ret == DB_NOTFOUND) | ||
| 669 | break; | ||
| 670 | else if (ret != 0) | ||
| 671 | return false; | ||
| 672 | |||
| 673 | // Unserialize | ||
| 674 | // Taking advantage of the fact that pair serialization | ||
| 675 | // is just the two items serialized one after the other | ||
| 676 | string strType; | ||
| 677 | ssKey >> strType; | ||
| 678 | strLastType = strType; | ||
| 679 | if (strType == "name") | ||
| 680 | { | ||
| 681 | string strAddress; | ||
| 682 | ssKey >> strAddress; | ||
| 683 | ssValue >> mapAddressBook[strAddress]; | ||
| 684 | } | ||
| 685 | else if (strType == "tx") | ||
| 686 | { | ||
| 687 | uint256 hash; | ||
| 688 | ssKey >> hash; | ||
| 689 | CWalletTx& wtx = mapWallet[hash]; | ||
| 690 | ssValue >> wtx; | ||
| 691 | |||
| 692 | if (wtx.GetHash() != hash) | ||
| 693 | printf("Error in wallet.dat, hash mismatch\n"); | ||
| 694 | |||
| 695 | //// debug print | ||
| 696 | //printf("LoadWallet %s\n", wtx.GetHash().ToString().c_str()); | ||
| 697 | //printf(" %12lld %s %s %s\n", | ||
| 698 | // wtx.vout[0].nValue, | ||
| 699 | // DateTimeStr(wtx.nTime).c_str(), | ||
| 700 | // wtx.hashBlock.ToString().substr(0,14).c_str(), | ||
| 701 | // wtx.mapValue["message"].c_str()); | ||
| 702 | } | ||
| 703 | else if (strType == "key") | ||
| 704 | { | ||
| 705 | vector<unsigned char> vchPubKey; | ||
| 706 | ssKey >> vchPubKey; | ||
| 707 | CPrivKey vchPrivKey; | ||
| 708 | ssValue >> vchPrivKey; | ||
| 709 | |||
| 710 | mapKeys[vchPubKey] = vchPrivKey; | ||
| 711 | mapPubKeys[Hash160(vchPubKey)] = vchPubKey; | ||
| 712 | } | ||
| 713 | else if (strType == "mkey") | ||
| 714 | { | ||
| 715 | unsigned int nID = 0; | ||
| 716 | ssKey >> nID; | ||
| 717 | CWalletMasterKey kMasterKey; | ||
| 718 | ssValue >> kMasterKey; | ||
| 719 | mapMasterKeys[nID] = kMasterKey; | ||
| 720 | nWalletMasterKeyMaxID = max(nWalletMasterKeyMaxID, nID); | ||
| 721 | fWalletEncrypted = true; | ||
| 722 | fWalletLocked = true; | ||
| 723 | } | ||
| 724 | else if (strType == "ckey") | ||
| 725 | { | ||
| 726 | vector<unsigned char> vchPubKey; | ||
| 727 | ssKey >> vchPubKey; | ||
| 728 | vector<unsigned char> vchCryptedSecret; | ||
| 729 | ssValue >> vchCryptedSecret; | ||
| 730 | if (!AddCryptedKey(vchPubKey, vchCryptedSecret)) | ||
| 731 | { | ||
| 732 | printf("LoadWallet: wallet.dat has an unreadable encrypted key record\n"); | ||
| 733 | return false; | ||
| 734 | } | ||
| 735 | } | ||
| 736 | else if (strType == "defaultkey") | ||
| 737 | { | ||
| 738 | ssValue >> vchDefaultKeyRet; | ||
| 739 | } | ||
| 740 | else if (strType == "hdmaster") | ||
| 741 | { | ||
| 742 | ssValue >> vchHDMaster; | ||
| 743 | } | ||
| 744 | else if (strType == "hdchaincode") | ||
| 745 | { | ||
| 746 | ssValue >> vchHDChainCode; | ||
| 747 | } | ||
| 748 | else if (strType == "hdnext") | ||
| 749 | { | ||
| 750 | ssValue >> nHDNext; | ||
| 751 | } | ||
| 752 | else if (strType == "hdschema") | ||
| 753 | { | ||
| 754 | ssValue >> nHDKeySchema; | ||
| 755 | } | ||
| 756 | else if (strType == "hdcointype") | ||
| 757 | { | ||
| 758 | ssValue >> nHDCoinType; | ||
| 759 | fHaveStoredHDCoinType = true; | ||
| 760 | } | ||
| 761 | else if (strType == "hdreceivenext") | ||
| 762 | { | ||
| 763 | ssValue >> nHDReceiveNext; | ||
| 764 | } | ||
| 765 | else if (strType == "hdchangenext") | ||
| 766 | { | ||
| 767 | ssValue >> nHDChangeNext; | ||
| 768 | } | ||
| 769 | else if (strType == "cryptedhdmaster") | ||
| 770 | { | ||
| 771 | ssValue >> vchCryptedHDMaster; | ||
| 772 | fWalletEncrypted = true; | ||
| 773 | fWalletLocked = true; | ||
| 774 | } | ||
| 775 | else if (strType == "cryptedhdchaincode") | ||
| 776 | { | ||
| 777 | ssValue >> vchCryptedHDChainCode; | ||
| 778 | fWalletEncrypted = true; | ||
| 779 | fWalletLocked = true; | ||
| 780 | } | ||
| 781 | else if (strType == "walletminversion") | ||
| 782 | { | ||
| 783 | int nMinVersion = 0; | ||
| 784 | ssValue >> nMinVersion; | ||
| 785 | if (nMinVersion > WALLET_FORMAT_SUPPORTED) | ||
| 786 | { | ||
| 787 | // Said out loud, not only into debug.log: this is the one | ||
| 788 | // refusal whose whole purpose is to be understood, and the | ||
| 789 | // caller puts it in front of the user. | ||
| 790 | strWalletLoadError = strprintf( | ||
| 791 | "This wallet.dat was written by a newer version of Bitflash " | ||
| 792 | "(wallet format %d; this build understands %d). Upgrade " | ||
| 793 | "Bitflash before opening it.", | ||
| 794 | nMinVersion, WALLET_FORMAT_SUPPORTED); | ||
| 795 | printf("LoadWallet: %s\n", strWalletLoadError.c_str()); | ||
| 796 | return false; | ||
| 797 | } | ||
| 798 | } | ||
| 799 | else if (strType == "pool") | ||
| 800 | { | ||
| 801 | int64 nIndex; | ||
| 802 | ssKey >> nIndex; | ||
| 803 | vector<unsigned char> vchPubKey; | ||
| 804 | ssValue >> vchPubKey; | ||
| 805 | mapKeyPool[nIndex] = vchPubKey; | ||
| 806 | } | ||
| 807 | else if (strType == "setting") /// or settings or option or options or config? | ||
| 808 | { | ||
| 809 | string strKey; | ||
| 810 | ssKey >> strKey; | ||
| 811 | if (strKey == "nTransactionFee") ssValue >> nTransactionFee; | ||
| 812 | if (strKey == "addrIncoming") ssValue >> addrIncoming; | ||
| 813 | |||
| 814 | // Mining mode is restored, but only when no flag chose one. | ||
| 815 | // | ||
| 816 | // These settings used to be restored unconditionally. LoadWallet() | ||
| 817 | // runs after main_gui.cpp has parsed the command line, so a stored | ||
| 818 | // value silently overwrote what had just been asked for -- no error, | ||
| 819 | // no log line -- and the fix at the time was to stop restoring them | ||
| 820 | // at all. That traded a silent override for a different silent | ||
| 821 | // failure: a machine set to mine came back from every restart as a | ||
| 822 | // plain relay, looking exactly like one that had been asked to | ||
| 823 | // relay. A 32-core miner sat idle for hours that way, and the only | ||
| 824 | // evidence was memory usage that never climbed. | ||
| 825 | // | ||
| 826 | // Precedence is now explicit rather than accidental: a flag wins, | ||
| 827 | // and it says so in the log below. Only the two fields that decide | ||
| 828 | // whether this node mines are restored, plus the pool address a | ||
| 829 | // participant cannot work without. | ||
| 830 | if (!fMineModeFromCommandLine) | ||
| 831 | { | ||
| 832 | if (strKey == "nMineMode") { ssValue >> nMineMode; fHaveStoredMineMode = true; } | ||
| 833 | if (strKey == "fGenerateBitcoins") ssValue >> fGenerateBitcoins; | ||
| 834 | if (strKey == "strParticipantPool") ssValue >> strParticipantPool; | ||
| 835 | } | ||
| 836 | } | ||
| 837 | } | ||
| 838 | } | ||
| 839 | } | ||
| 840 | catch (const std::exception& e) | ||
| 841 | { | ||
| 842 | printf("LoadWallet: wallet.dat could not be read. Failed while handling " | ||
| 843 | "a '%s' record: %s\n", strLastType.c_str(), e.what()); | ||
| 844 | printf("LoadWallet: the file was left untouched. A wallet.dat written by " | ||
| 845 | "a different platform's Berkeley DB is the usual cause.\n"); | ||
| 846 | return false; | ||
| 847 | } | ||
| 848 | catch (...) | ||
| 849 | { | ||
| 850 | printf("LoadWallet: wallet.dat could not be read. Unknown failure while " | ||
| 851 | "handling a '%s' record.\n", strLastType.c_str()); | ||
| 852 | return false; | ||
| 853 | } | ||
| 854 | |||
| 855 | // An encrypted wallet has a seed; it just has not been decrypted yet. | ||
| 856 | // | ||
| 857 | // The reset in the else branch was written when "no seed in memory after | ||
| 858 | // loading" could only mean "this wallet has no seed". With encryption that | ||
| 859 | // is no longer true, and taking the else branch here wiped the schema and | ||
| 860 | // the derivation counters that had just been read correctly out of the | ||
| 861 | // file. Unlocking later restores the seed but nothing restores those, so a | ||
| 862 | // BIP44 wallet came back deriving legacy addresses -- different addresses | ||
| 863 | // from the ones its own recovery phrase produces, while the audit reported | ||
| 864 | // the phrase as covering nothing. | ||
| 865 | if (HaveHDSeed() || !vchCryptedHDMaster.empty()) | ||
| 866 | { | ||
| 867 | if (nHDKeySchema == HD_SCHEMA_NONE) | ||
| 868 | { | ||
| 869 | nHDKeySchema = HD_SCHEMA_LEGACY; | ||
| 870 | printf("LoadWallet: deterministic seed has no schema metadata; " | ||
| 871 | "treating it as legacy m/index'\n"); | ||
| 872 | } | ||
| 873 | else if (nHDKeySchema != HD_SCHEMA_LEGACY && | ||
| 874 | nHDKeySchema != HD_SCHEMA_BIP44) | ||
| 875 | { | ||
| 876 | printf("LoadWallet: deterministic wallet schema %d (%s) is not " | ||
| 877 | "supported by this build\n", | ||
| 878 | nHDKeySchema, HDKeySchemaName(nHDKeySchema).c_str()); | ||
| 879 | return false; | ||
| 880 | } | ||
| 881 | if (!fHaveStoredHDCoinType) | ||
| 882 | { | ||
| 883 | nHDCoinType = HD_BIP44_COIN_TYPE_BITFLASH_PROVISIONAL; | ||
| 884 | printf("LoadWallet: deterministic seed has no BIP44 coin type metadata; " | ||
| 885 | "using provisional Bitflash coin type %u\n", nHDCoinType); | ||
| 886 | } | ||
| 887 | } | ||
| 888 | else | ||
| 889 | { | ||
| 890 | nHDKeySchema = HD_SCHEMA_NONE; | ||
| 891 | nHDNext = 0; | ||
| 892 | nHDReceiveNext = 0; | ||
| 893 | nHDChangeNext = 0; | ||
| 894 | nHDCoinType = HD_BIP44_COIN_TYPE_BITFLASH_PROVISIONAL; | ||
| 895 | } | ||
| 896 | |||
| 897 | // fGenerateBitcoins and nMineMode only mean anything together, and a | ||
| 898 | // wallet.dat can easily hold one without the other: Bitcoin 0.1.0 already | ||
| 899 | // wrote fGenerateBitcoins when mining was toggled, years before this fork | ||
| 900 | // had a mode at all. Restoring that lone flag put a node into the one state | ||
| 901 | // the miner cannot act on -- generate on, mode relay -- so every miner | ||
| 902 | // thread started and returned at the relay guard, and the machine sat there | ||
| 903 | // looking like it was mining while hashing nothing. That happened to a | ||
| 904 | // 32-core node on the first 1.2.11 start. | ||
| 905 | // | ||
| 906 | // So the flag is only believed when the mode it belongs to was stored with | ||
| 907 | // it, and the pair is made coherent either way. | ||
| 908 | if (!fMineModeFromCommandLine) | ||
| 909 | { | ||
| 910 | if (!fHaveStoredMineMode) | ||
| 911 | { | ||
| 912 | if (fGenerateBitcoins) | ||
| 913 | printf("LoadWallet: ignoring a stored fGenerateBitcoins with no mining mode " | ||
| 914 | "beside it -- it predates this setting and cannot be read on its own\n"); | ||
| 915 | fGenerateBitcoins = 0; | ||
| 916 | } | ||
| 917 | else | ||
| 918 | { | ||
| 919 | fGenerateBitcoins = (nMineMode != MINE_RELAY) ? 1 : 0; | ||
| 920 | } | ||
| 921 | } | ||
| 922 | |||
| 923 | printf("nTransactionFee = %lld\n", nTransactionFee); | ||
| 924 | printf("addrIncoming = %s\n", addrIncoming.ToString().c_str()); | ||
| 925 | printf("nMineMode = %d, strParticipantPool = %s, fGenerateBitcoins = %d (%s)\n", | ||
| 926 | nMineMode, strParticipantPool.c_str(), fGenerateBitcoins, | ||
| 927 | fMineModeFromCommandLine ? "from the command line, wallet.dat ignored" | ||
| 928 | : "remembered from wallet.dat, or the default"); | ||
| 929 | printf("strPoolName = %s, dPoolFeePercent = %.2f, strPoolDashboardUrl = %s (from CLI/default, not wallet.dat)\n", | ||
| 930 | strPoolName.c_str(), dPoolFeePercent, strPoolDashboardUrl.c_str()); | ||
| 931 | |||
| 932 | return true; | ||
| 933 | } | ||
| 934 | |||
| 935 | bool LoadWallet() | ||
| 936 | { | ||
| 937 | vector<unsigned char> vchDefaultKey; | ||
| 938 | if (!CWalletDB("cr").LoadWallet(vchDefaultKey)) | ||
| 939 | return false; | ||
| 940 | |||
| 941 | if (!vchDefaultKey.empty() && mapPubKeys.count(Hash160(vchDefaultKey))) | ||
| 942 | { | ||
| 943 | // Set keyUser | ||
| 944 | keyUser.SetPubKey(vchDefaultKey); | ||
| 945 | if (mapKeys.count(vchDefaultKey)) | ||
| 946 | keyUser.SetPrivKey(mapKeys[vchDefaultKey]); | ||
| 947 | } | ||
| 948 | else if (IsWalletEncrypted()) | ||
| 949 | { | ||
| 950 | printf("LoadWallet: encrypted wallet has no usable default public key. " | ||
| 951 | "The file was left untouched.\n"); | ||
| 952 | return false; | ||
| 953 | } | ||
| 954 | else | ||
| 955 | { | ||
| 956 | // Create new keyUser and set as default key | ||
| 957 | keyUser.MakeNewKey(); | ||
| 958 | if (!AddKey(keyUser)) | ||
| 959 | return false; | ||
| 960 | if (!SetAddressBookName(PubKeyToAddress(keyUser.GetPubKey()), "Your Address")) | ||
| 961 | return false; | ||
| 962 | CWalletDB().WriteDefaultKey(keyUser.GetPubKey()); | ||
| 963 | } | ||
| 964 | |||
| 965 | return true; | ||
| 966 | } | ||
| 967 | |||
| 968 | |||
| 969 | // Write a copy of wallet.dat that can be opened anywhere. | ||
| 970 | // | ||
| 971 | // A plain file copy is not a backup here. Berkeley DB stamps every page with a | ||
| 972 | // log sequence number tied to the environment that wrote it, so the copy only | ||
| 973 | // works beside the database/ directory it grew up with -- which is why backing | ||
| 974 | // up "just wallet.dat", the one file anybody would think to save, produces | ||
| 975 | // something that will not open. See the recovery block in CDB::CDB and #40. | ||
| 976 | // | ||
| 977 | // So: flush outstanding writes, copy, then clear the copy's log sequence | ||
| 978 | // numbers. What lands on disk is a wallet.dat that stands on its own. | ||
| 979 | // | ||
| 980 | // This is still a point-in-time snapshot. Keys are generated as they are | ||
| 981 | // needed, not derived from a seed, so coins paid to an address created after | ||
| 982 | // this file was written are not spendable from it. Backing up once is not | ||
| 983 | // enough, and that is a property of the wallet format rather than of this | ||
| 984 | // function. | ||
| 985 | bool BackupWallet(const string& strDest) | ||
| 986 | { | ||
| 987 | if (strDest.empty()) | ||
| 988 | return error("BackupWallet() : no destination given\n"); | ||
| 989 | |||
| 990 | string strSrc = GetAppDir() + "/wallet.dat"; | ||
| 991 | if (!FileExists(strSrc.c_str())) | ||
| 992 | return error("BackupWallet() : %s does not exist\n", strSrc.c_str()); | ||
| 993 | |||
| 994 | CRITICAL_BLOCK(cs_db) | ||
| 995 | { | ||
| 996 | // Committing first, so the copy is not missing the newest records. | ||
| 997 | // Both calls can throw, and a failed backup must not take the node | ||
| 998 | // down with it. | ||
| 999 | try { dbenv.txn_checkpoint(0, 0, 0); } | ||
| 1000 | catch (const std::exception& e) | ||
| 1001 | { return error("BackupWallet() : checkpoint failed: %s\n", e.what()); } | ||
| 1002 | |||
| 1003 | try | ||
| 1004 | { | ||
| 1005 | FILE* pfIn = fopen(strSrc.c_str(), "rb"); | ||
| 1006 | if (!pfIn) | ||
| 1007 | return error("BackupWallet() : cannot read %s\n", strSrc.c_str()); | ||
| 1008 | FILE* pfOut = fopen(strDest.c_str(), "wb"); | ||
| 1009 | if (!pfOut) | ||
| 1010 | { | ||
| 1011 | fclose(pfIn); | ||
| 1012 | return error("BackupWallet() : cannot write %s\n", strDest.c_str()); | ||
| 1013 | } | ||
| 1014 | |||
| 1015 | char buf[65536]; | ||
| 1016 | size_t n; | ||
| 1017 | bool fOk = true; | ||
| 1018 | while ((n = fread(buf, 1, sizeof(buf), pfIn)) > 0) | ||
| 1019 | if (fwrite(buf, 1, n, pfOut) != n) { fOk = false; break; } | ||
| 1020 | if (ferror(pfIn)) | ||
| 1021 | fOk = false; | ||
| 1022 | fclose(pfIn); | ||
| 1023 | // Flushed and closed before lsn_reset touches it. | ||
| 1024 | if (fclose(pfOut) != 0) | ||
| 1025 | fOk = false; | ||
| 1026 | |||
| 1027 | if (!fOk) | ||
| 1028 | { | ||
| 1029 | remove(strDest.c_str()); // half a wallet is worse than none | ||
| 1030 | return error("BackupWallet() : copy to %s failed\n", strDest.c_str()); | ||
| 1031 | } | ||
| 1032 | |||
| 1033 | // Without this the copy is welded to this node's database/ dir. | ||
| 1034 | int ret = dbenv.lsn_reset(strDest.c_str(), 0); | ||
| 1035 | if (ret != 0) | ||
| 1036 | printf("BackupWallet() : warning -- lsn_reset returned %d; the copy " | ||
| 1037 | "may only open beside this node's database/ directory\n", ret); | ||
| 1038 | } | ||
| 1039 | catch (const std::exception& e) | ||
| 1040 | { return error("BackupWallet() : %s\n", e.what()); } | ||
| 1041 | } | ||
| 1042 | |||
| 1043 | // Recorded here rather than by whoever called, so that a backup taken with | ||
| 1044 | // /backupwallet counts as much as one taken from the GUI. When the GUI | ||
| 1045 | // wrote this itself, anyone following the README -- which documents the | ||
| 1046 | // command line first -- was told forever that they had never backed up. | ||
| 1047 | // Outside the cs_db block above: this opens the wallet for writing, and | ||
| 1048 | // there is no reason to do that while holding the database lock. | ||
| 1049 | try { CWalletDB().WriteSetting("nLastWalletBackup", (int64)GetTime()); } | ||
| 1050 | catch (...) { } // the backup is written; failing to note it is not fatal | ||
| 1051 | |||
| 1052 | printf("BackupWallet() : wrote %s\n", strDest.c_str()); | ||
| 1053 | return true; | ||
| 1054 | } | ||
| 1055 | |||
| 1056 | |||
| 1057 | // ---- plain-text key export/import ------------------------------------------ | ||
| 1058 | // | ||
| 1059 | // The last door out. Everything else in this file assumes Berkeley DB will | ||
| 1060 | // cooperate; these two do not. A wallet.dat can be unreadable for reasons that | ||
| 1061 | // have nothing to do with your keys being gone -- a version this build does not | ||
| 1062 | // understand, a file truncated by a bad copy, an environment that cannot be | ||
| 1063 | // recovered -- and until now that meant the coins were unreachable even though | ||
| 1064 | // the secrets were sitting right there. Issue #40 is one person's account of | ||
| 1065 | // exactly that. | ||
| 1066 | // | ||
| 1067 | // The format is deliberately dull: a text file, one key per line, no database, | ||
| 1068 | // no environment, no framing. Anything that can read a line can recover from it. | ||
| 1069 | |||
| 1070 | static int HexDigitValue(char c) | ||
| 1071 | { | ||
| 1072 | if (c >= '0' && c <= '9') return c - '0'; | ||
| 1073 | if (c >= 'a' && c <= 'f') return c - 'a' + 10; | ||
| 1074 | if (c >= 'A' && c <= 'F') return c - 'A' + 10; | ||
| 1075 | return -1; | ||
| 1076 | } | ||
| 1077 | |||
| 1078 | static bool ParseHexInto(const string& str, vector<unsigned char>& vchRet) | ||
| 1079 | { | ||
| 1080 | vchRet.clear(); | ||
| 1081 | if (str.empty() || (str.size() % 2) != 0) | ||
| 1082 | return false; | ||
| 1083 | for (size_t i = 0; i < str.size(); i += 2) | ||
| 1084 | { | ||
| 1085 | int hi = HexDigitValue(str[i]); | ||
| 1086 | int lo = HexDigitValue(str[i + 1]); | ||
| 1087 | if (hi < 0 || lo < 0) | ||
| 1088 | return false; | ||
| 1089 | vchRet.push_back((unsigned char)((hi << 4) | lo)); | ||
| 1090 | } | ||
| 1091 | return true; | ||
| 1092 | } | ||
| 1093 | |||
| 1094 | bool DumpWallet(const string& strDest) | ||
| 1095 | { | ||
| 1096 | if (strDest.empty()) | ||
| 1097 | return error("DumpWallet() : no destination given\n"); | ||
| 1098 | if (IsWalletLocked()) | ||
| 1099 | return error("DumpWallet() : wallet is encrypted and locked; unlock it with /walletpassphrase first\n"); | ||
| 1100 | |||
| 1101 | // Never over an existing file. The whole point is to hold the only copy of | ||
| 1102 | // something irreplaceable, and a mistyped path must not eat one. | ||
| 1103 | if (FileExists(strDest.c_str())) | ||
| 1104 | return error("DumpWallet() : %s already exists -- refusing to overwrite\n", | ||
| 1105 | strDest.c_str()); | ||
| 1106 | |||
| 1107 | FILE* pf = fopen(strDest.c_str(), "w"); | ||
| 1108 | if (!pf) | ||
| 1109 | return error("DumpWallet() : cannot write %s\n", strDest.c_str()); | ||
| 1110 | #ifndef _WIN32 | ||
| 1111 | // Before a single key reaches it. | ||
| 1112 | chmod(strDest.c_str(), S_IRUSR | S_IWUSR); | ||
| 1113 | #endif | ||
| 1114 | |||
| 1115 | fprintf(pf, "# Bitflash wallet key dump\n"); | ||
| 1116 | fprintf(pf, "# %s\n", DateTimeStr(GetTime()).c_str()); | ||
| 1117 | fprintf(pf, "#\n"); | ||
| 1118 | fprintf(pf, "# THIS FILE CONTAINS PRIVATE KEYS IN PLAIN TEXT.\n"); | ||
| 1119 | fprintf(pf, "# Anyone who reads it can spend these coins. Treat it as cash.\n"); | ||
| 1120 | fprintf(pf, "#\n"); | ||
| 1121 | fprintf(pf, "# One key per line: <private-key-hex> <address> [label]\n"); | ||
| 1122 | fprintf(pf, "# The key is the DER encoding, hex; restore with -importwallet=FILE.\n"); | ||
| 1123 | fprintf(pf, "#\n"); | ||
| 1124 | |||
| 1125 | vector<vector<unsigned char> > vPubKeys; | ||
| 1126 | { | ||
| 1127 | CRITICAL_BLOCK(cs_mapKeys) | ||
| 1128 | { | ||
| 1129 | for (map<vector<unsigned char>, CPrivKey>::iterator mi = mapKeys.begin(); | ||
| 1130 | mi != mapKeys.end(); ++mi) | ||
| 1131 | vPubKeys.push_back((*mi).first); | ||
| 1132 | for (map<vector<unsigned char>, vector<unsigned char> >::iterator mi = mapCryptedKeys.begin(); | ||
| 1133 | mi != mapCryptedKeys.end(); ++mi) | ||
| 1134 | if (!mapKeys.count((*mi).first)) | ||
| 1135 | vPubKeys.push_back((*mi).first); | ||
| 1136 | } | ||
| 1137 | } | ||
| 1138 | |||
| 1139 | int nKeys = 0; | ||
| 1140 | bool fOk = true; | ||
| 1141 | for (size_t i = 0; i < vPubKeys.size(); i++) | ||
| 1142 | { | ||
| 1143 | const vector<unsigned char>& vchPubKey = vPubKeys[i]; | ||
| 1144 | CPrivKey vchPrivKey; | ||
| 1145 | string strError; | ||
| 1146 | if (!GetWalletPrivKey(vchPubKey, vchPrivKey, strError)) | ||
| 1147 | { | ||
| 1148 | fOk = false; | ||
| 1149 | break; | ||
| 1150 | } | ||
| 1151 | |||
| 1152 | string strAddress = PubKeyToAddress(vchPubKey); | ||
| 1153 | string strLabel; | ||
| 1154 | map<string, string>::iterator mia = mapAddressBook.find(strAddress); | ||
| 1155 | if (mia != mapAddressBook.end()) | ||
| 1156 | strLabel = (*mia).second; | ||
| 1157 | |||
| 1158 | // Labels are user text and could contain a newline, which would forge | ||
| 1159 | // a second entry on read-back. | ||
| 1160 | for (size_t j = 0; j < strLabel.size(); j++) | ||
| 1161 | if (strLabel[j] == '\n' || strLabel[j] == '\r') | ||
| 1162 | strLabel[j] = ' '; | ||
| 1163 | |||
| 1164 | if (fprintf(pf, "%s %s %s\n", | ||
| 1165 | HexStr(vchPrivKey.begin(), vchPrivKey.end(), false).c_str(), | ||
| 1166 | strAddress.c_str(), | ||
| 1167 | strLabel.c_str()) < 0) | ||
| 1168 | { fOk = false; break; } | ||
| 1169 | nKeys++; | ||
| 1170 | } | ||
| 1171 | |||
| 1172 | if (fclose(pf) != 0) | ||
| 1173 | fOk = false; | ||
| 1174 | if (!fOk) | ||
| 1175 | { | ||
| 1176 | remove(strDest.c_str()); | ||
| 1177 | return error("DumpWallet() : writing %s failed\n", strDest.c_str()); | ||
| 1178 | } | ||
| 1179 | |||
| 1180 | printf("DumpWallet() : wrote %d key(s) to %s\n", nKeys, strDest.c_str()); | ||
| 1181 | return true; | ||
| 1182 | } | ||
| 1183 | |||
| 1184 | bool ImportWallet(const string& strSrc, int& nAddedRet, int& nSkippedRet) | ||
| 1185 | { | ||
| 1186 | nAddedRet = 0; | ||
| 1187 | nSkippedRet = 0; | ||
| 1188 | |||
| 1189 | FILE* pf = fopen(strSrc.c_str(), "r"); | ||
| 1190 | if (!pf) | ||
| 1191 | return error("ImportWallet() : cannot read %s\n", strSrc.c_str()); | ||
| 1192 | |||
| 1193 | char buf[8192]; | ||
| 1194 | int nLine = 0; | ||
| 1195 | int nBad = 0; | ||
| 1196 | while (fgets(buf, sizeof(buf), pf)) | ||
| 1197 | { | ||
| 1198 | nLine++; | ||
| 1199 | string strLine = buf; | ||
| 1200 | while (!strLine.empty() && (strLine[strLine.size()-1] == '\n' || | ||
| 1201 | strLine[strLine.size()-1] == '\r')) | ||
| 1202 | strLine.resize(strLine.size() - 1); | ||
| 1203 | if (strLine.empty() || strLine[0] == '#') | ||
| 1204 | continue; | ||
| 1205 | |||
| 1206 | string strHex = strLine.substr(0, strLine.find(' ')); | ||
| 1207 | vector<unsigned char> vchPrivKey; | ||
| 1208 | if (!ParseHexInto(strHex, vchPrivKey)) | ||
| 1209 | { | ||
| 1210 | printf("ImportWallet() : line %d is not a hex key, skipped\n", nLine); | ||
| 1211 | nBad++; | ||
| 1212 | continue; | ||
| 1213 | } | ||
| 1214 | |||
| 1215 | CKey key; | ||
| 1216 | // A key this build cannot load is reported and stepped over rather than | ||
| 1217 | // aborting the run: one damaged line in a salvage file must not cost | ||
| 1218 | // the reader every other key in it. | ||
| 1219 | try | ||
| 1220 | { | ||
| 1221 | CPrivKey vchSecure(vchPrivKey.begin(), vchPrivKey.end()); | ||
| 1222 | if (!key.SetPrivKey(vchSecure)) | ||
| 1223 | { | ||
| 1224 | printf("ImportWallet() : line %d is not a usable key, skipped\n", nLine); | ||
| 1225 | nBad++; | ||
| 1226 | continue; | ||
| 1227 | } | ||
| 1228 | } | ||
| 1229 | catch (const std::exception& e) | ||
| 1230 | { | ||
| 1231 | printf("ImportWallet() : line %d rejected (%s), skipped\n", nLine, e.what()); | ||
| 1232 | nBad++; | ||
| 1233 | continue; | ||
| 1234 | } | ||
| 1235 | |||
| 1236 | if (WalletCanSpendKey(key.GetPubKey())) | ||
| 1237 | { | ||
| 1238 | nSkippedRet++; // already ours; importing twice is not an error | ||
| 1239 | continue; | ||
| 1240 | } | ||
| 1241 | |||
| 1242 | if (!AddKey(key)) | ||
| 1243 | { | ||
| 1244 | fclose(pf); | ||
| 1245 | return error("ImportWallet() : failed to store key from line %d\n", nLine); | ||
| 1246 | } | ||
| 1247 | nAddedRet++; | ||
| 1248 | } | ||
| 1249 | fclose(pf); | ||
| 1250 | |||
| 1251 | printf("ImportWallet() : %d added, %d already present, %d unreadable\n", | ||
| 1252 | nAddedRet, nSkippedRet, nBad); | ||
| 1253 | |||
| 1254 | // Imported keys are only useful once their transactions are noticed, and | ||
| 1255 | // that scan is a full pass over the chain the node does at startup. | ||
| 1256 | if (nAddedRet > 0) | ||
| 1257 | printf("ImportWallet() : restart the node so it can find transactions " | ||
| 1258 | "belonging to the new keys\n"); | ||
| 1259 | return true; | ||
| 1260 | } | ||
| 1261 | |||
| 1262 | |||
| 1263 | // ---- wallet encryption rewrite ------------------------------------------- | ||
| 1264 | |||
| 1265 | class CWalletRewriteDB : public CDB | ||
| 1266 | { | ||
| 1267 | public: | ||
| 1268 | CWalletRewriteDB(const char* pszFile, const char* pszMode="r+", bool fTxn=false) | ||
| 1269 | : CDB(pszFile, pszMode, fTxn) { } | ||
| 1270 | |||
| 1271 | bool WriteRaw(CDataStream ssKey, CDataStream ssValue) | ||
| 1272 | { | ||
| 1273 | if (!pdb || ssKey.empty()) | ||
| 1274 | return false; | ||
| 1275 | |||
| 1276 | Dbt datKey(&ssKey[0], ssKey.size()); | ||
| 1277 | Dbt datValue(ssValue.empty() ? NULL : &ssValue[0], ssValue.size()); | ||
| 1278 | int ret = pdb->put(GetTxn(), &datKey, &datValue, 0); | ||
| 1279 | |||
| 1280 | memset(datKey.get_data(), 0, datKey.get_size()); | ||
| 1281 | if (datValue.get_data()) | ||
| 1282 | memset(datValue.get_data(), 0, datValue.get_size()); | ||
| 1283 | return ret == 0; | ||
| 1284 | } | ||
| 1285 | |||
| 1286 | bool WriteWalletMinVersion(int nVersion) | ||
| 1287 | { | ||
| 1288 | return Write(string("walletminversion"), nVersion); | ||
| 1289 | } | ||
| 1290 | |||
| 1291 | bool WriteMasterKey(unsigned int nID, const CWalletMasterKey& kMasterKey) | ||
| 1292 | { | ||
| 1293 | return Write(make_pair(string("mkey"), nID), kMasterKey, true); | ||
| 1294 | } | ||
| 1295 | |||
| 1296 | bool WriteCryptedKey(const vector<unsigned char>& vchPubKey, | ||
| 1297 | const vector<unsigned char>& vchCryptedSecret) | ||
| 1298 | { | ||
| 1299 | return Write(make_pair(string("ckey"), vchPubKey), vchCryptedSecret, false); | ||
| 1300 | } | ||
| 1301 | |||
| 1302 | bool WriteCryptedHDMaster(const vector<unsigned char>& vchCryptedMaster, | ||
| 1303 | const vector<unsigned char>& vchCryptedChainCode) | ||
| 1304 | { | ||
| 1305 | return Write(string("cryptedhdmaster"), vchCryptedMaster) | ||
| 1306 | && Write(string("cryptedhdchaincode"), vchCryptedChainCode); | ||
| 1307 | } | ||
| 1308 | |||
| 1309 | bool WriteHDNext(unsigned int nNext) | ||
| 1310 | { | ||
| 1311 | return Write(string("hdnext"), nNext); | ||
| 1312 | } | ||
| 1313 | |||
| 1314 | bool WritePool(int64 nIndex, const vector<unsigned char>& vchPubKey) | ||
| 1315 | { | ||
| 1316 | return Write(make_pair(string("pool"), nIndex), vchPubKey); | ||
| 1317 | } | ||
| 1318 | |||
| 1319 | bool CopyPublicRecordsTo(CWalletRewriteDB& dbTo, int& nCopiedRet, string& strErrorRet) | ||
| 1320 | { | ||
| 1321 | nCopiedRet = 0; | ||
| 1322 | Dbc* pcursor = GetCursor(); | ||
| 1323 | if (!pcursor) | ||
| 1324 | { | ||
| 1325 | strErrorRet = "could not open a wallet cursor"; | ||
| 1326 | return false; | ||
| 1327 | } | ||
| 1328 | |||
| 1329 | unsigned int fFlags = DB_NEXT; | ||
| 1330 | loop | ||
| 1331 | { | ||
| 1332 | CDataStream ssKey; | ||
| 1333 | CDataStream ssValue; | ||
| 1334 | int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags); | ||
| 1335 | if (ret == DB_NOTFOUND) | ||
| 1336 | break; | ||
| 1337 | if (ret != 0) | ||
| 1338 | { | ||
| 1339 | pcursor->close(); | ||
| 1340 | strErrorRet = strprintf("could not read wallet record: Berkeley DB error %d", ret); | ||
| 1341 | return false; | ||
| 1342 | } | ||
| 1343 | |||
| 1344 | string strType; | ||
| 1345 | try | ||
| 1346 | { | ||
| 1347 | CDataStream ssType = ssKey; | ||
| 1348 | ssType >> strType; | ||
| 1349 | } | ||
| 1350 | catch (...) | ||
| 1351 | { | ||
| 1352 | pcursor->close(); | ||
| 1353 | strErrorRet = "wallet.dat has a record whose key type cannot be read"; | ||
| 1354 | return false; | ||
| 1355 | } | ||
| 1356 | |||
| 1357 | if (strType == "key" || | ||
| 1358 | strType == "pool" || | ||
| 1359 | strType == "hdmaster" || | ||
| 1360 | strType == "hdchaincode" || | ||
| 1361 | strType == "mkey" || | ||
| 1362 | strType == "ckey" || | ||
| 1363 | strType == "cryptedhdmaster" || | ||
| 1364 | strType == "cryptedhdchaincode" || | ||
| 1365 | strType == "walletminversion") | ||
| 1366 | continue; | ||
| 1367 | |||
| 1368 | if (!dbTo.WriteRaw(ssKey, ssValue)) | ||
| 1369 | { | ||
| 1370 | pcursor->close(); | ||
| 1371 | strErrorRet = strprintf("could not copy wallet record '%s'", strType.c_str()); | ||
| 1372 | return false; | ||
| 1373 | } | ||
| 1374 | nCopiedRet++; | ||
| 1375 | } | ||
| 1376 | |||
| 1377 | pcursor->close(); | ||
| 1378 | return true; | ||
| 1379 | } | ||
| 1380 | }; | ||
| 1381 | |||
| 1382 | static string WalletEncryptBackupPath() | ||
| 1383 | { | ||
| 1384 | string strBase = GetAppDir() + "/wallet.dat.before-encrypt."; | ||
| 1385 | string strPath = strBase + i64tostr(GetTime()) + ".bak"; | ||
| 1386 | for (int i = 1; FileExists(strPath.c_str()); i++) | ||
| 1387 | strPath = strBase + i64tostr(GetTime()) + "." + itostr(i) + ".bak"; | ||
| 1388 | return strPath; | ||
| 1389 | } | ||
| 1390 | |||
| 1391 | static bool EncryptPrivateKeyForWallet(const CKeyingMaterial& vchMasterKey, | ||
| 1392 | const vector<unsigned char>& vchPubKey, | ||
| 1393 | const CPrivKey& vchPrivKey, | ||
| 1394 | vector<unsigned char>& vchCryptedRet, | ||
| 1395 | string& strErrorRet) | ||
| 1396 | { | ||
| 1397 | if (!EncryptSecret(vchMasterKey, | ||
| 1398 | vector<unsigned char>(vchPrivKey.begin(), vchPrivKey.end()), | ||
| 1399 | WalletKeyIV(vchPubKey), vchCryptedRet)) | ||
| 1400 | { | ||
| 1401 | strErrorRet = "could not encrypt a private key"; | ||
| 1402 | return false; | ||
| 1403 | } | ||
| 1404 | return true; | ||
| 1405 | } | ||
| 1406 | |||
| 1407 | bool EncryptWallet(const string& strPassphrase, string& strBackupRet, string& strErrorRet) | ||
| 1408 | { | ||
| 1409 | strBackupRet.clear(); | ||
| 1410 | strErrorRet.clear(); | ||
| 1411 | |||
| 1412 | if (IsWalletEncrypted()) | ||
| 1413 | { | ||
| 1414 | strErrorRet = "wallet is already encrypted"; | ||
| 1415 | return false; | ||
| 1416 | } | ||
| 1417 | if (strPassphrase.size() < 8) | ||
| 1418 | { | ||
| 1419 | strErrorRet = "passphrase must be at least 8 characters"; | ||
| 1420 | return false; | ||
| 1421 | } | ||
| 1422 | |||
| 1423 | map<vector<unsigned char>, CPrivKey> mapPlainKeys; | ||
| 1424 | vector<unsigned char> vchPlainHDMaster; | ||
| 1425 | vector<unsigned char> vchPlainHDChainCode; | ||
| 1426 | unsigned int nHDNextSnapshot = 0; | ||
| 1427 | { | ||
| 1428 | CRITICAL_BLOCK(cs_mapKeys) | ||
| 1429 | mapPlainKeys = mapKeys; | ||
| 1430 | } | ||
| 1431 | { | ||
| 1432 | CRITICAL_BLOCK(cs_keyPool) | ||
| 1433 | { | ||
| 1434 | vchPlainHDMaster = vchHDMaster; | ||
| 1435 | vchPlainHDChainCode = vchHDChainCode; | ||
| 1436 | nHDNextSnapshot = nHDNext; | ||
| 1437 | } | ||
| 1438 | } | ||
| 1439 | if (mapPlainKeys.empty()) | ||
| 1440 | { | ||
| 1441 | strErrorRet = "wallet has no private keys to encrypt"; | ||
| 1442 | return false; | ||
| 1443 | } | ||
| 1444 | |||
| 1445 | CKeyingMaterial vchMasterKey; | ||
| 1446 | vchMasterKey.resize(32); | ||
| 1447 | if (RAND_bytes(&vchMasterKey[0], vchMasterKey.size()) != 1) | ||
| 1448 | { | ||
| 1449 | strErrorRet = "could not create a random wallet master key"; | ||
| 1450 | return false; | ||
| 1451 | } | ||
| 1452 | |||
| 1453 | CWalletMasterKey kMasterKey; | ||
| 1454 | if (RAND_bytes(&kMasterKey.vchSalt[0], kMasterKey.vchSalt.size()) != 1) | ||
| 1455 | { | ||
| 1456 | strErrorRet = "could not create a wallet encryption salt"; | ||
| 1457 | return false; | ||
| 1458 | } | ||
| 1459 | |||
| 1460 | CKeyingMaterial vchPassKey; | ||
| 1461 | vector<unsigned char> vchPassIV; | ||
| 1462 | if (!DeriveWalletPassphraseKey(strPassphrase, kMasterKey.vchSalt, | ||
| 1463 | kMasterKey.nDeriveIterations, | ||
| 1464 | vchPassKey, vchPassIV) || | ||
| 1465 | !EncryptSecret(vchPassKey, | ||
| 1466 | vector<unsigned char>(vchMasterKey.begin(), vchMasterKey.end()), | ||
| 1467 | vchPassIV, kMasterKey.vchCryptedKey)) | ||
| 1468 | { | ||
| 1469 | strErrorRet = "could not encrypt the wallet master key"; | ||
| 1470 | return false; | ||
| 1471 | } | ||
| 1472 | |||
| 1473 | vector<pair<vector<unsigned char>, vector<unsigned char> > > vCryptedKeys; | ||
| 1474 | for (map<vector<unsigned char>, CPrivKey>::const_iterator mi = mapPlainKeys.begin(); | ||
| 1475 | mi != mapPlainKeys.end(); ++mi) | ||
| 1476 | { | ||
| 1477 | vector<unsigned char> vchCrypted; | ||
| 1478 | if (!EncryptPrivateKeyForWallet(vchMasterKey, (*mi).first, (*mi).second, | ||
| 1479 | vchCrypted, strErrorRet)) | ||
| 1480 | return false; | ||
| 1481 | vCryptedKeys.push_back(make_pair((*mi).first, vchCrypted)); | ||
| 1482 | } | ||
| 1483 | |||
| 1484 | vector<unsigned char> vchCryptedHDMasterNew; | ||
| 1485 | vector<unsigned char> vchCryptedHDChainCodeNew; | ||
| 1486 | if (!vchPlainHDMaster.empty() || !vchPlainHDChainCode.empty()) | ||
| 1487 | { | ||
| 1488 | if (vchPlainHDMaster.size() != 32 || vchPlainHDChainCode.size() != 32) | ||
| 1489 | { | ||
| 1490 | strErrorRet = "wallet has an incomplete HD seed"; | ||
| 1491 | return false; | ||
| 1492 | } | ||
| 1493 | if (!EncryptSecret(vchMasterKey, vchPlainHDMaster, | ||
| 1494 | WalletSecretIV("hdmaster"), vchCryptedHDMasterNew) || | ||
| 1495 | !EncryptSecret(vchMasterKey, vchPlainHDChainCode, | ||
| 1496 | WalletSecretIV("hdchaincode"), vchCryptedHDChainCodeNew)) | ||
| 1497 | { | ||
| 1498 | strErrorRet = "could not encrypt the HD seed"; | ||
| 1499 | return false; | ||
| 1500 | } | ||
| 1501 | } | ||
| 1502 | |||
| 1503 | vector<pair<int64, vector<unsigned char> > > vNewPool; | ||
| 1504 | vector<pair<vector<unsigned char>, vector<unsigned char> > > vNewPoolKeys; | ||
| 1505 | unsigned int nHDNextNew = nHDNextSnapshot; | ||
| 1506 | for (int i = 0; i < KEYPOOL_SIZE; i++) | ||
| 1507 | { | ||
| 1508 | CKey key; | ||
| 1509 | if (!vchPlainHDMaster.empty()) | ||
| 1510 | { | ||
| 1511 | string strDeriveError; | ||
| 1512 | if (!DeriveHDKey(nHDNextNew, key, strDeriveError)) | ||
| 1513 | { | ||
| 1514 | strErrorRet = strprintf("could not derive replacement key-pool key %u: %s", | ||
| 1515 | nHDNextNew, strDeriveError.c_str()); | ||
| 1516 | return false; | ||
| 1517 | } | ||
| 1518 | nHDNextNew++; | ||
| 1519 | } | ||
| 1520 | else | ||
| 1521 | { | ||
| 1522 | key.MakeNewKey(); | ||
| 1523 | } | ||
| 1524 | |||
| 1525 | vector<unsigned char> vchPubKey = key.GetPubKey(); | ||
| 1526 | if (!mapPlainKeys.count(vchPubKey)) | ||
| 1527 | { | ||
| 1528 | vector<unsigned char> vchCrypted; | ||
| 1529 | if (!EncryptPrivateKeyForWallet(vchMasterKey, vchPubKey, key.GetPrivKey(), | ||
| 1530 | vchCrypted, strErrorRet)) | ||
| 1531 | return false; | ||
| 1532 | vNewPoolKeys.push_back(make_pair(vchPubKey, vchCrypted)); | ||
| 1533 | } | ||
| 1534 | vNewPool.push_back(make_pair((int64)i + 1, vchPubKey)); | ||
| 1535 | } | ||
| 1536 | |||
| 1537 | string strTempFile = strprintf("wallet.encrypting.%lld.dat", GetTime()); | ||
| 1538 | string strTempPath = GetAppDir() + "/" + strTempFile; | ||
| 1539 | remove(strTempPath.c_str()); | ||
| 1540 | |||
| 1541 | int nCopied = 0; | ||
| 1542 | { | ||
| 1543 | CWalletRewriteDB dbSource("wallet.dat", "r"); | ||
| 1544 | CWalletRewriteDB dbTarget(strTempFile.c_str(), "c+"); | ||
| 1545 | if (!dbSource.CopyPublicRecordsTo(dbTarget, nCopied, strErrorRet)) | ||
| 1546 | { | ||
| 1547 | remove(strTempPath.c_str()); | ||
| 1548 | return false; | ||
| 1549 | } | ||
| 1550 | if (!dbTarget.WriteWalletMinVersion(WALLET_FORMAT_ENCRYPTED) || | ||
| 1551 | !dbTarget.WriteMasterKey(nWalletMasterKeyMaxID + 1, kMasterKey)) | ||
| 1552 | { | ||
| 1553 | strErrorRet = "could not write encrypted wallet metadata"; | ||
| 1554 | remove(strTempPath.c_str()); | ||
| 1555 | return false; | ||
| 1556 | } | ||
| 1557 | for (size_t i = 0; i < vCryptedKeys.size(); i++) | ||
| 1558 | { | ||
| 1559 | if (!dbTarget.WriteCryptedKey(vCryptedKeys[i].first, vCryptedKeys[i].second)) | ||
| 1560 | { | ||
| 1561 | strErrorRet = "could not write an encrypted private key"; | ||
| 1562 | remove(strTempPath.c_str()); | ||
| 1563 | return false; | ||
| 1564 | } | ||
| 1565 | } | ||
| 1566 | for (size_t i = 0; i < vNewPoolKeys.size(); i++) | ||
| 1567 | { | ||
| 1568 | if (!dbTarget.WriteCryptedKey(vNewPoolKeys[i].first, vNewPoolKeys[i].second)) | ||
| 1569 | { | ||
| 1570 | strErrorRet = "could not write an encrypted replacement key-pool key"; | ||
| 1571 | remove(strTempPath.c_str()); | ||
| 1572 | return false; | ||
| 1573 | } | ||
| 1574 | } | ||
| 1575 | if (!vchCryptedHDMasterNew.empty() && | ||
| 1576 | !dbTarget.WriteCryptedHDMaster(vchCryptedHDMasterNew, vchCryptedHDChainCodeNew)) | ||
| 1577 | { | ||
| 1578 | strErrorRet = "could not write the encrypted HD seed"; | ||
| 1579 | remove(strTempPath.c_str()); | ||
| 1580 | return false; | ||
| 1581 | } | ||
| 1582 | if (!dbTarget.WriteHDNext(nHDNextNew)) | ||
| 1583 | { | ||
| 1584 | strErrorRet = "could not write the replacement HD derivation counter"; | ||
| 1585 | remove(strTempPath.c_str()); | ||
| 1586 | return false; | ||
| 1587 | } | ||
| 1588 | for (size_t i = 0; i < vNewPool.size(); i++) | ||
| 1589 | { | ||
| 1590 | if (!dbTarget.WritePool(vNewPool[i].first, vNewPool[i].second)) | ||
| 1591 | { | ||
| 1592 | strErrorRet = "could not write replacement key-pool records"; | ||
| 1593 | remove(strTempPath.c_str()); | ||
| 1594 | return false; | ||
| 1595 | } | ||
| 1596 | } | ||
| 1597 | } | ||
| 1598 | |||
| 1599 | // The source and target handles are closed. Close the environment too | ||
| 1600 | // before replacing files on Windows; the command exits immediately after | ||
| 1601 | // this, and tests verify reload through a fresh process. | ||
| 1602 | DBFlush(true); | ||
| 1603 | |||
| 1604 | string strWalletPath = GetAppDir() + "/wallet.dat"; | ||
| 1605 | string strBackupPath = WalletEncryptBackupPath(); | ||
| 1606 | if (rename(strWalletPath.c_str(), strBackupPath.c_str()) != 0) | ||
| 1607 | { | ||
| 1608 | strErrorRet = strprintf("could not move the original wallet to %s", strBackupPath.c_str()); | ||
| 1609 | remove(strTempPath.c_str()); | ||
| 1610 | return false; | ||
| 1611 | } | ||
| 1612 | if (rename(strTempPath.c_str(), strWalletPath.c_str()) != 0) | ||
| 1613 | { | ||
| 1614 | rename(strBackupPath.c_str(), strWalletPath.c_str()); | ||
| 1615 | strErrorRet = "could not install the encrypted wallet; the original was restored"; | ||
| 1616 | remove(strTempPath.c_str()); | ||
| 1617 | return false; | ||
| 1618 | } | ||
| 1619 | |||
| 1620 | strBackupRet = strBackupPath; | ||
| 1621 | printf("EncryptWallet() : copied %d public record(s), encrypted %u key(s), " | ||
| 1622 | "rebuilt %d key-pool entry(s)\n", | ||
| 1623 | nCopied, (unsigned int)(vCryptedKeys.size() + vNewPoolKeys.size()), | ||
| 1624 | (int)vNewPool.size()); | ||
| 1625 | |||
| 1626 | // The rewritten wallet.dat is clean, and that is not enough: every record | ||
| 1627 | // this wallet ever wrote also passed through the environment's write-ahead | ||
| 1628 | // log in database/, and Berkeley DB keeps those files. Measured on a real | ||
| 1629 | // wallet -- wallet.dat held no trace of a known private key afterwards, | ||
| 1630 | // while database/log.0000000002 still held all 118 bytes of it, and still | ||
| 1631 | // did after a full node restart. | ||
| 1632 | // | ||
| 1633 | // That matters more here than it looks, because our own backup advice | ||
| 1634 | // (README, and the LEIA-ME in the desktop folders) is to copy the whole | ||
| 1635 | // directory rather than wallet.dat alone -- which is the right advice, and | ||
| 1636 | // the reason for issue #40. Following it after encrypting would carry the | ||
| 1637 | // unencrypted key along with the backup. | ||
| 1638 | // | ||
| 1639 | // DB_LOG_AUTO_REMOVE is deliberately not the answer: it sits commented out | ||
| 1640 | // where this environment is opened, marked "causes corruption", and that | ||
| 1641 | // note predates this fork. This is the conservative form instead -- force a | ||
| 1642 | // checkpoint, then ask Berkeley DB to drop only the logs it says are no | ||
| 1643 | // longer needed for recovery, once, at the one moment we know the wallet | ||
| 1644 | // has just been rewritten. | ||
| 1645 | return true; | ||
| 1646 | } | ||