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.cpp1646
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.
31class CAutoCursor
32{
33public:
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 }
43private:
44 Dbc* pcursor;
45 CAutoCursor(const CAutoCursor&);
46 void operator=(const CAutoCursor&);
47};
48
49static CCriticalSection cs_db;
50static 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.
54string strWalletLoadError;
55DbEnv dbenv(0u);
56static map<string, int> mapFileUseCount;
57
58class CDBInit
59{
60public:
61 CDBInit()
62 {
63 }
64 ~CDBInit()
65 {
66 if (fDbEnvInit)
67 {
68 try { dbenv.close(0); }
69 catch (...) { }
70 fDbEnvInit = false;
71 }
72 }
73}
74instance_of_cdbinit;
75
76
77CDB::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
192void 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.
239void 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
260void 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
329bool CTxDB::ReadTxIndex(uint256 hash, CTxIndex& txindex)
330{
331 assert(!fClient);
332 txindex.SetNull();
333 return Read(make_pair(string("tx"), hash), txindex);
334}
335
336bool CTxDB::UpdateTxIndex(uint256 hash, const CTxIndex& txindex)
337{
338 assert(!fClient);
339 return Write(make_pair(string("tx"), hash), txindex);
340}
341
342bool 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
352bool 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
360bool CTxDB::ContainsTx(uint256 hash)
361{
362 assert(!fClient);
363 return Exists(make_pair(string("tx"), hash));
364}
365
366bool 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
413bool 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
422bool CTxDB::ReadDiskTx(uint256 hash, CTransaction& tx)
423{
424 CTxIndex txindex;
425 return ReadDiskTx(hash, tx, txindex);
426}
427
428bool CTxDB::ReadDiskTx(COutPoint outpoint, CTransaction& tx, CTxIndex& txindex)
429{
430 return ReadDiskTx(outpoint.hash, tx, txindex);
431}
432
433bool CTxDB::ReadDiskTx(COutPoint outpoint, CTransaction& tx)
434{
435 CTxIndex txindex;
436 return ReadDiskTx(outpoint.hash, tx, txindex);
437}
438
439bool CTxDB::WriteBlockIndex(const CDiskBlockIndex& blockindex)
440{
441 return Write(make_pair(string("blockindex"), blockindex.GetBlockHash()), blockindex);
442}
443
444bool CTxDB::EraseBlockIndex(uint256 hash)
445{
446 return Erase(make_pair(string("blockindex"), hash));
447}
448
449bool CTxDB::ReadHashBestChain(uint256& hashBestChain)
450{
451 return Read(string("hashBestChain"), hashBestChain);
452}
453
454bool CTxDB::WriteHashBestChain(uint256 hashBestChain)
455{
456 return Write(string("hashBestChain"), hashBestChain);
457}
458
459CBlockIndex* 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
479bool 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
604bool 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
610bool 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
625bool 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
935bool 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.
985bool 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
1070static 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
1078static 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
1094bool 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
1184bool 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
1265class CWalletRewriteDB : public CDB
1266{
1267public:
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
1382static 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
1391static 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
1407bool 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}