Projects : bitcoin : bitcoin_tx_fee_cleanup
1 | // Copyright (c) 2009-2010 Satoshi Nakamoto |
2 | // Copyright (c) 2009-2012 The Bitcoin developers |
3 | // Distributed under the MIT/X11 software license, see the accompanying |
4 | // file license.txt or http://www.opensource.org/licenses/mit-license.php. |
5 | |
6 | #include "headers.h" |
7 | #include "db.h" |
8 | #include "net.h" |
9 | #include <boost/filesystem.hpp> |
10 | #include <boost/filesystem/fstream.hpp> |
11 | |
12 | // v0.5.4 RELEASE (keccak) |
13 | |
14 | using namespace std; |
15 | using namespace boost; |
16 | |
17 | |
18 | unsigned int nWalletDBUpdated; |
19 | uint64 nAccountingEntryNumber = 0; |
20 | |
21 | |
22 | |
23 | // |
24 | // CDB |
25 | // |
26 | |
27 | static CCriticalSection cs_db; |
28 | static bool fDbEnvInit = false; |
29 | DbEnv dbenv(0); |
30 | static map<string, int> mapFileUseCount; |
31 | static map<string, Db*> mapDb; |
32 | |
33 | static void EnvShutdown() |
34 | { |
35 | if (!fDbEnvInit) |
36 | return; |
37 | |
38 | fDbEnvInit = false; |
39 | try |
40 | { |
41 | dbenv.close(0); |
42 | } |
43 | catch (const DbException& e) |
44 | { |
45 | printf("EnvShutdown exception: %s (%d)\n", e.what(), e.get_errno()); |
46 | } |
47 | DbEnv(0).remove(GetDataDir().c_str(), 0); |
48 | } |
49 | |
50 | class CDBInit |
51 | { |
52 | public: |
53 | CDBInit() |
54 | { |
55 | } |
56 | ~CDBInit() |
57 | { |
58 | EnvShutdown(); |
59 | } |
60 | } |
61 | instance_of_cdbinit; |
62 | |
63 | |
64 | CDB::CDB(const char* pszFile, const char* pszMode) : pdb(NULL) |
65 | { |
66 | int ret; |
67 | if (pszFile == NULL) |
68 | return; |
69 | |
70 | fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w')); |
71 | bool fCreate = strchr(pszMode, 'c'); |
72 | unsigned int nFlags = DB_THREAD; |
73 | if (fCreate) |
74 | nFlags |= DB_CREATE; |
75 | |
76 | CRITICAL_BLOCK(cs_db) |
77 | { |
78 | if (!fDbEnvInit) |
79 | { |
80 | if (fShutdown) |
81 | return; |
82 | string strDataDir = GetDataDir(); |
83 | string strLogDir = strDataDir + "/database"; |
84 | filesystem::create_directory(strLogDir.c_str()); |
85 | string strErrorFile = strDataDir + "/db.log"; |
86 | printf("dbenv.open strLogDir=%s strErrorFile=%s\n", strLogDir.c_str(), strErrorFile.c_str()); |
87 | |
88 | dbenv.set_lg_dir(strLogDir.c_str()); |
89 | dbenv.set_lg_max(1000000); |
90 | dbenv.set_lk_max_locks(2737000); |
91 | dbenv.set_lk_max_objects(1119200); |
92 | dbenv.set_lk_max_lockers(1119200); |
93 | dbenv.set_errfile(fopen(strErrorFile.c_str(), "a")); /// debug |
94 | dbenv.set_flags(DB_AUTO_COMMIT, 1); |
95 | dbenv.set_flags(DB_TXN_WRITE_NOSYNC, 1); |
96 | dbenv.log_set_config(DB_LOG_AUTO_REMOVE, 1); |
97 | ret = dbenv.open(strDataDir.c_str(), |
98 | DB_CREATE | |
99 | DB_INIT_LOCK | |
100 | DB_INIT_LOG | |
101 | DB_INIT_MPOOL | |
102 | DB_INIT_TXN | |
103 | DB_THREAD | |
104 | DB_RECOVER, |
105 | S_IRUSR | S_IWUSR); |
106 | if (ret > 0) |
107 | throw runtime_error(strprintf("CDB() : error %d opening database environment", ret)); |
108 | fDbEnvInit = true; |
109 | } |
110 | |
111 | strFile = pszFile; |
112 | ++mapFileUseCount[strFile]; |
113 | pdb = mapDb[strFile]; |
114 | if (pdb == NULL) |
115 | { |
116 | pdb = new Db(&dbenv, 0); |
117 | |
118 | ret = pdb->open(NULL, // Txn pointer |
119 | pszFile, // Filename |
120 | "main", // Logical db name |
121 | DB_BTREE, // Database type |
122 | nFlags, // Flags |
123 | 0); |
124 | |
125 | if (ret > 0) |
126 | { |
127 | delete pdb; |
128 | pdb = NULL; |
129 | CRITICAL_BLOCK(cs_db) |
130 | --mapFileUseCount[strFile]; |
131 | strFile = ""; |
132 | throw runtime_error(strprintf("CDB() : can't open database file %s, error %d", pszFile, ret)); |
133 | } |
134 | |
135 | if (fCreate && !Exists(string("version"))) |
136 | { |
137 | bool fTmp = fReadOnly; |
138 | fReadOnly = false; |
139 | WriteVersion(VERSION); |
140 | fReadOnly = fTmp; |
141 | } |
142 | |
143 | mapDb[strFile] = pdb; |
144 | } |
145 | } |
146 | } |
147 | |
148 | void CDB::Close() |
149 | { |
150 | if (!pdb) |
151 | return; |
152 | if (!vTxn.empty()) |
153 | vTxn.front()->abort(); |
154 | vTxn.clear(); |
155 | pdb = NULL; |
156 | |
157 | // Flush database activity from memory pool to disk log |
158 | unsigned int nMinutes = 0; |
159 | if (fReadOnly) |
160 | nMinutes = 1; |
161 | if (strFile == "addr.dat") |
162 | nMinutes = 2; |
163 | if (strFile == "blkindex.dat" && IsInitialBlockDownload() && nBestHeight % 500 != 0) |
164 | nMinutes = 1; |
165 | dbenv.txn_checkpoint(0, nMinutes, 0); |
166 | |
167 | CRITICAL_BLOCK(cs_db) |
168 | --mapFileUseCount[strFile]; |
169 | } |
170 | |
171 | void static CloseDb(const string& strFile) |
172 | { |
173 | CRITICAL_BLOCK(cs_db) |
174 | { |
175 | if (mapDb[strFile] != NULL) |
176 | { |
177 | // Close the database handle |
178 | Db* pdb = mapDb[strFile]; |
179 | pdb->close(0); |
180 | delete pdb; |
181 | mapDb[strFile] = NULL; |
182 | } |
183 | } |
184 | } |
185 | |
186 | bool CDB::Rewrite(const string& strFile, const char* pszSkip) |
187 | { |
188 | while (!fShutdown) |
189 | { |
190 | CRITICAL_BLOCK(cs_db) |
191 | { |
192 | if (!mapFileUseCount.count(strFile) || mapFileUseCount[strFile] == 0) |
193 | { |
194 | // Flush log data to the dat file |
195 | CloseDb(strFile); |
196 | dbenv.txn_checkpoint(0, 0, 0); |
197 | dbenv.lsn_reset(strFile.c_str(), 0); |
198 | mapFileUseCount.erase(strFile); |
199 | |
200 | bool fSuccess = true; |
201 | printf("Rewriting %s...\n", strFile.c_str()); |
202 | string strFileRes = strFile + ".rewrite"; |
203 | { // surround usage of db with extra {} |
204 | CDB db(strFile.c_str(), "r"); |
205 | Db* pdbCopy = new Db(&dbenv, 0); |
206 | |
207 | int ret = pdbCopy->open(NULL, // Txn pointer |
208 | strFileRes.c_str(), // Filename |
209 | "main", // Logical db name |
210 | DB_BTREE, // Database type |
211 | DB_CREATE, // Flags |
212 | 0); |
213 | if (ret > 0) |
214 | { |
215 | printf("Cannot create database file %s\n", strFileRes.c_str()); |
216 | fSuccess = false; |
217 | } |
218 | |
219 | Dbc* pcursor = db.GetCursor(); |
220 | if (pcursor) |
221 | while (fSuccess) |
222 | { |
223 | CDataStream ssKey; |
224 | CDataStream ssValue; |
225 | int ret = db.ReadAtCursor(pcursor, ssKey, ssValue, DB_NEXT); |
226 | if (ret == DB_NOTFOUND) |
227 | { |
228 | pcursor->close(); |
229 | break; |
230 | } |
231 | else if (ret != 0) |
232 | { |
233 | pcursor->close(); |
234 | fSuccess = false; |
235 | break; |
236 | } |
237 | if (pszSkip && |
238 | strncmp(&ssKey[0], pszSkip, std::min(ssKey.size(), strlen(pszSkip))) == 0) |
239 | continue; |
240 | if (strncmp(&ssKey[0], "\x07version", 8) == 0) |
241 | { |
242 | // Update version: |
243 | ssValue.clear(); |
244 | ssValue << VERSION; |
245 | } |
246 | Dbt datKey(&ssKey[0], ssKey.size()); |
247 | Dbt datValue(&ssValue[0], ssValue.size()); |
248 | int ret2 = pdbCopy->put(NULL, &datKey, &datValue, DB_NOOVERWRITE); |
249 | if (ret2 > 0) |
250 | fSuccess = false; |
251 | } |
252 | if (fSuccess) |
253 | { |
254 | db.Close(); |
255 | CloseDb(strFile); |
256 | if (pdbCopy->close(0)) |
257 | fSuccess = false; |
258 | delete pdbCopy; |
259 | } |
260 | } |
261 | if (fSuccess) |
262 | { |
263 | Db dbA(&dbenv, 0); |
264 | if (dbA.remove(strFile.c_str(), NULL, 0)) |
265 | fSuccess = false; |
266 | Db dbB(&dbenv, 0); |
267 | if (dbB.rename(strFileRes.c_str(), NULL, strFile.c_str(), 0)) |
268 | fSuccess = false; |
269 | } |
270 | if (!fSuccess) |
271 | printf("Rewriting of %s FAILED!\n", strFileRes.c_str()); |
272 | return fSuccess; |
273 | } |
274 | } |
275 | Sleep(100); |
276 | } |
277 | return false; |
278 | } |
279 | |
280 | |
281 | void DBFlush(bool fShutdown) |
282 | { |
283 | // Flush log data to the actual data file |
284 | // on all files that are not in use |
285 | printf("DBFlush(%s)%s\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " db not started"); |
286 | if (!fDbEnvInit) |
287 | return; |
288 | CRITICAL_BLOCK(cs_db) |
289 | { |
290 | map<string, int>::iterator mi = mapFileUseCount.begin(); |
291 | while (mi != mapFileUseCount.end()) |
292 | { |
293 | string strFile = (*mi).first; |
294 | int nRefCount = (*mi).second; |
295 | printf("%s refcount=%d\n", strFile.c_str(), nRefCount); |
296 | if (nRefCount == 0) |
297 | { |
298 | // Move log data to the dat file |
299 | CloseDb(strFile); |
300 | dbenv.txn_checkpoint(0, 0, 0); |
301 | printf("%s flush\n", strFile.c_str()); |
302 | dbenv.lsn_reset(strFile.c_str(), 0); |
303 | mapFileUseCount.erase(mi++); |
304 | } |
305 | else |
306 | mi++; |
307 | } |
308 | if (fShutdown) |
309 | { |
310 | char** listp; |
311 | if (mapFileUseCount.empty()) |
312 | { |
313 | dbenv.log_archive(&listp, DB_ARCH_REMOVE); |
314 | EnvShutdown(); |
315 | } |
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 | |
376 | unsigned int fFlags = DB_SET_RANGE; |
377 | loop |
378 | { |
379 | // Read next record |
380 | CDataStream ssKey; |
381 | if (fFlags == DB_SET_RANGE) |
382 | ssKey << string("owner") << hash160 << CDiskTxPos(0, 0, 0); |
383 | CDataStream ssValue; |
384 | int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags); |
385 | fFlags = DB_NEXT; |
386 | if (ret == DB_NOTFOUND) |
387 | break; |
388 | else if (ret != 0) |
389 | { |
390 | pcursor->close(); |
391 | return false; |
392 | } |
393 | |
394 | // Unserialize |
395 | string strType; |
396 | uint160 hashItem; |
397 | CDiskTxPos pos; |
398 | ssKey >> strType >> hashItem >> pos; |
399 | int nItemHeight; |
400 | ssValue >> nItemHeight; |
401 | |
402 | // Read transaction |
403 | if (strType != "owner" || hashItem != hash160) |
404 | break; |
405 | if (nItemHeight >= nMinHeight) |
406 | { |
407 | vtx.resize(vtx.size()+1); |
408 | if (!vtx.back().ReadFromDisk(pos)) |
409 | { |
410 | pcursor->close(); |
411 | return false; |
412 | } |
413 | } |
414 | } |
415 | |
416 | pcursor->close(); |
417 | return true; |
418 | } |
419 | |
420 | bool CTxDB::ReadDiskTx(uint256 hash, CTransaction& tx, CTxIndex& txindex) |
421 | { |
422 | assert(!fClient); |
423 | tx.SetNull(); |
424 | if (!ReadTxIndex(hash, txindex)) |
425 | return false; |
426 | return (tx.ReadFromDisk(txindex.pos)); |
427 | } |
428 | |
429 | bool CTxDB::ReadDiskTx(uint256 hash, CTransaction& tx) |
430 | { |
431 | CTxIndex txindex; |
432 | return ReadDiskTx(hash, tx, txindex); |
433 | } |
434 | |
435 | bool CTxDB::ReadDiskTx(COutPoint outpoint, CTransaction& tx, CTxIndex& txindex) |
436 | { |
437 | return ReadDiskTx(outpoint.hash, tx, txindex); |
438 | } |
439 | |
440 | bool CTxDB::ReadDiskTx(COutPoint outpoint, CTransaction& tx) |
441 | { |
442 | CTxIndex txindex; |
443 | return ReadDiskTx(outpoint.hash, tx, txindex); |
444 | } |
445 | |
446 | bool CTxDB::WriteBlockIndex(const CDiskBlockIndex& blockindex) |
447 | { |
448 | return Write(make_pair(string("blockindex"), blockindex.GetBlockHash()), blockindex); |
449 | } |
450 | |
451 | bool CTxDB::EraseBlockIndex(uint256 hash) |
452 | { |
453 | return Erase(make_pair(string("blockindex"), hash)); |
454 | } |
455 | |
456 | bool CTxDB::ReadHashBestChain(uint256& hashBestChain) |
457 | { |
458 | return Read(string("hashBestChain"), hashBestChain); |
459 | } |
460 | |
461 | bool CTxDB::WriteHashBestChain(uint256 hashBestChain) |
462 | { |
463 | return Write(string("hashBestChain"), hashBestChain); |
464 | } |
465 | |
466 | bool CTxDB::ReadBestInvalidWork(CBigNum& bnBestInvalidWork) |
467 | { |
468 | return Read(string("bnBestInvalidWork"), bnBestInvalidWork); |
469 | } |
470 | |
471 | bool CTxDB::WriteBestInvalidWork(CBigNum bnBestInvalidWork) |
472 | { |
473 | return Write(string("bnBestInvalidWork"), bnBestInvalidWork); |
474 | } |
475 | |
476 | CBlockIndex static * InsertBlockIndex(uint256 hash) |
477 | { |
478 | if (hash == 0) |
479 | return NULL; |
480 | |
481 | // Return existing |
482 | map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hash); |
483 | if (mi != mapBlockIndex.end()) |
484 | return (*mi).second; |
485 | |
486 | // Create new |
487 | CBlockIndex* pindexNew = new CBlockIndex(); |
488 | if (!pindexNew) |
489 | throw runtime_error("LoadBlockIndex() : new CBlockIndex failed"); |
490 | mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first; |
491 | pindexNew->phashBlock = &((*mi).first); |
492 | |
493 | return pindexNew; |
494 | } |
495 | |
496 | bool CTxDB::LoadBlockIndex() |
497 | { |
498 | // Get database cursor |
499 | Dbc* pcursor = GetCursor(); |
500 | if (!pcursor) |
501 | return false; |
502 | |
503 | // Load mapBlockIndex |
504 | unsigned int fFlags = DB_SET_RANGE; |
505 | loop |
506 | { |
507 | // Read next record |
508 | CDataStream ssKey; |
509 | if (fFlags == DB_SET_RANGE) |
510 | ssKey << make_pair(string("blockindex"), uint256(0)); |
511 | CDataStream ssValue; |
512 | int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags); |
513 | fFlags = DB_NEXT; |
514 | if (ret == DB_NOTFOUND) |
515 | break; |
516 | else if (ret != 0) |
517 | return false; |
518 | |
519 | // Unserialize |
520 | string strType; |
521 | ssKey >> strType; |
522 | if (strType == "blockindex") |
523 | { |
524 | CDiskBlockIndex diskindex; |
525 | ssValue >> diskindex; |
526 | |
527 | // Construct block index object |
528 | CBlockIndex* pindexNew = InsertBlockIndex(diskindex.GetBlockHash()); |
529 | pindexNew->pprev = InsertBlockIndex(diskindex.hashPrev); |
530 | pindexNew->pnext = InsertBlockIndex(diskindex.hashNext); |
531 | pindexNew->nFile = diskindex.nFile; |
532 | pindexNew->nBlockPos = diskindex.nBlockPos; |
533 | pindexNew->nHeight = diskindex.nHeight; |
534 | pindexNew->nVersion = diskindex.nVersion; |
535 | pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot; |
536 | pindexNew->nTime = diskindex.nTime; |
537 | pindexNew->nBits = diskindex.nBits; |
538 | pindexNew->nNonce = diskindex.nNonce; |
539 | |
540 | // Watch for genesis block |
541 | if (pindexGenesisBlock == NULL && diskindex.GetBlockHash() == hashGenesisBlock) |
542 | pindexGenesisBlock = pindexNew; |
543 | |
544 | if (!pindexNew->CheckIndex()) |
545 | return error("LoadBlockIndex() : CheckIndex failed at %d", pindexNew->nHeight); |
546 | } |
547 | else |
548 | { |
549 | break; |
550 | } |
551 | } |
552 | pcursor->close(); |
553 | |
554 | // Calculate bnChainWork |
555 | vector<pair<int, CBlockIndex*> > vSortedByHeight; |
556 | vSortedByHeight.reserve(mapBlockIndex.size()); |
557 | BOOST_FOREACH(const PAIRTYPE(uint256, CBlockIndex*)& item, mapBlockIndex) |
558 | { |
559 | CBlockIndex* pindex = item.second; |
560 | vSortedByHeight.push_back(make_pair(pindex->nHeight, pindex)); |
561 | } |
562 | sort(vSortedByHeight.begin(), vSortedByHeight.end()); |
563 | BOOST_FOREACH(const PAIRTYPE(int, CBlockIndex*)& item, vSortedByHeight) |
564 | { |
565 | CBlockIndex* pindex = item.second; |
566 | pindex->bnChainWork = (pindex->pprev ? pindex->pprev->bnChainWork : 0) + pindex->GetBlockWork(); |
567 | } |
568 | |
569 | // Load hashBestChain pointer to end of best chain |
570 | if (!ReadHashBestChain(hashBestChain)) |
571 | { |
572 | if (pindexGenesisBlock == NULL) |
573 | return true; |
574 | return error("CTxDB::LoadBlockIndex() : hashBestChain not loaded"); |
575 | } |
576 | if (!mapBlockIndex.count(hashBestChain)) |
577 | return error("CTxDB::LoadBlockIndex() : hashBestChain not found in the block index"); |
578 | pindexBest = mapBlockIndex[hashBestChain]; |
579 | nBestHeight = pindexBest->nHeight; |
580 | bnBestChainWork = pindexBest->bnChainWork; |
581 | printf("LoadBlockIndex(): hashBestChain=%s height=%d\n", hashBestChain.ToString().c_str(), nBestHeight); |
582 | |
583 | // Load bnBestInvalidWork, OK if it doesn't exist |
584 | ReadBestInvalidWork(bnBestInvalidWork); |
585 | |
586 | // Verify blocks in the best chain |
587 | CBlockIndex* pindexFork = NULL; |
588 | for (CBlockIndex* pindex = pindexBest; pindex && pindex->pprev; pindex = pindex->pprev) |
589 | { |
590 | if (pindex->nHeight < nBestHeight-2500 && !mapArgs.count("-checkblocks")) |
591 | break; |
592 | CBlock block; |
593 | if (!block.ReadFromDisk(pindex)) |
594 | return error("LoadBlockIndex() : block.ReadFromDisk failed"); |
595 | if (!block.CheckBlock()) |
596 | { |
597 | printf("LoadBlockIndex() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str()); |
598 | pindexFork = pindex->pprev; |
599 | } |
600 | } |
601 | if (pindexFork) |
602 | { |
603 | // Reorg back to the fork |
604 | printf("LoadBlockIndex() : *** moving best chain pointer back to block %d\n", pindexFork->nHeight); |
605 | CBlock block; |
606 | if (!block.ReadFromDisk(pindexFork)) |
607 | return error("LoadBlockIndex() : block.ReadFromDisk failed"); |
608 | CTxDB txdb; |
609 | block.SetBestChain(txdb, pindexFork); |
610 | } |
611 | |
612 | return true; |
613 | } |
614 | |
615 | |
616 | |
617 | |
618 | |
619 | // |
620 | // CAddrDB |
621 | // |
622 | |
623 | bool CAddrDB::WriteAddress(const CAddress& addr) |
624 | { |
625 | return Write(make_pair(string("addr"), addr.GetKey()), addr); |
626 | } |
627 | |
628 | bool CAddrDB::EraseAddress(const CAddress& addr) |
629 | { |
630 | return Erase(make_pair(string("addr"), addr.GetKey())); |
631 | } |
632 | |
633 | bool CAddrDB::LoadAddresses() |
634 | { |
635 | CRITICAL_BLOCK(cs_mapAddresses) |
636 | { |
637 | // Get cursor |
638 | Dbc* pcursor = GetCursor(); |
639 | if (!pcursor) |
640 | return false; |
641 | |
642 | loop |
643 | { |
644 | // Read next record |
645 | CDataStream ssKey; |
646 | CDataStream ssValue; |
647 | int ret = ReadAtCursor(pcursor, ssKey, ssValue); |
648 | if (ret == DB_NOTFOUND) |
649 | break; |
650 | else if (ret != 0) |
651 | return false; |
652 | |
653 | // Unserialize |
654 | string strType; |
655 | ssKey >> strType; |
656 | if (strType == "addr") |
657 | { |
658 | CAddress addr; |
659 | ssValue >> addr; |
660 | mapAddresses.insert(make_pair(addr.GetKey(), addr)); |
661 | } |
662 | } |
663 | pcursor->close(); |
664 | |
665 | printf("Loaded %d addresses\n", mapAddresses.size()); |
666 | } |
667 | |
668 | return true; |
669 | } |
670 | |
671 | bool LoadAddresses() |
672 | { |
673 | return CAddrDB("cr+").LoadAddresses(); |
674 | } |
675 | |
676 | |
677 | |
678 | |
679 | // |
680 | // CWalletDB |
681 | // |
682 | |
683 | bool CWalletDB::WriteName(const string& strAddress, const string& strName) |
684 | { |
685 | nWalletDBUpdated++; |
686 | return Write(make_pair(string("name"), strAddress), strName); |
687 | } |
688 | |
689 | bool CWalletDB::EraseName(const string& strAddress) |
690 | { |
691 | // This should only be used for sending addresses, never for receiving addresses, |
692 | // receiving addresses must always have an address book entry if they're not change return. |
693 | nWalletDBUpdated++; |
694 | return Erase(make_pair(string("name"), strAddress)); |
695 | } |
696 | |
697 | bool CWalletDB::ReadAccount(const string& strAccount, CAccount& account) |
698 | { |
699 | account.SetNull(); |
700 | return Read(make_pair(string("acc"), strAccount), account); |
701 | } |
702 | |
703 | bool CWalletDB::WriteAccount(const string& strAccount, const CAccount& account) |
704 | { |
705 | return Write(make_pair(string("acc"), strAccount), account); |
706 | } |
707 | |
708 | bool CWalletDB::WriteAccountingEntry(const CAccountingEntry& acentry) |
709 | { |
710 | return Write(boost::make_tuple(string("acentry"), acentry.strAccount, ++nAccountingEntryNumber), acentry); |
711 | } |
712 | |
713 | int64 CWalletDB::GetAccountCreditDebit(const string& strAccount) |
714 | { |
715 | list<CAccountingEntry> entries; |
716 | ListAccountCreditDebit(strAccount, entries); |
717 | |
718 | int64 nCreditDebit = 0; |
719 | BOOST_FOREACH (const CAccountingEntry& entry, entries) |
720 | nCreditDebit += entry.nCreditDebit; |
721 | |
722 | return nCreditDebit; |
723 | } |
724 | |
725 | void CWalletDB::ListAccountCreditDebit(const string& strAccount, list<CAccountingEntry>& entries) |
726 | { |
727 | bool fAllAccounts = (strAccount == "*"); |
728 | |
729 | Dbc* pcursor = GetCursor(); |
730 | if (!pcursor) |
731 | throw runtime_error("CWalletDB::ListAccountCreditDebit() : cannot create DB cursor"); |
732 | unsigned int fFlags = DB_SET_RANGE; |
733 | loop |
734 | { |
735 | // Read next record |
736 | CDataStream ssKey; |
737 | if (fFlags == DB_SET_RANGE) |
738 | ssKey << boost::make_tuple(string("acentry"), (fAllAccounts? string("") : strAccount), uint64(0)); |
739 | CDataStream ssValue; |
740 | int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags); |
741 | fFlags = DB_NEXT; |
742 | if (ret == DB_NOTFOUND) |
743 | break; |
744 | else if (ret != 0) |
745 | { |
746 | pcursor->close(); |
747 | throw runtime_error("CWalletDB::ListAccountCreditDebit() : error scanning DB"); |
748 | } |
749 | |
750 | // Unserialize |
751 | string strType; |
752 | ssKey >> strType; |
753 | if (strType != "acentry") |
754 | break; |
755 | CAccountingEntry acentry; |
756 | ssKey >> acentry.strAccount; |
757 | if (!fAllAccounts && acentry.strAccount != strAccount) |
758 | break; |
759 | |
760 | ssValue >> acentry; |
761 | entries.push_back(acentry); |
762 | } |
763 | |
764 | pcursor->close(); |
765 | } |
766 | |
767 | |
768 | int CWalletDB::LoadWallet(CWallet* pwallet) |
769 | { |
770 | pwallet->vchDefaultKey.clear(); |
771 | int nFileVersion = 0; |
772 | vector<uint256> vWalletUpgrade; |
773 | bool fIsEncrypted = false; |
774 | |
775 | // Modify defaults |
776 | // Tray icon sometimes disappears on 9.10 karmic koala 64-bit, leaving no way to access the program |
777 | fMinimizeToTray = false; |
778 | fMinimizeOnClose = false; |
779 | |
780 | //// todo: shouldn't we catch exceptions and try to recover and continue? |
781 | CRITICAL_BLOCK(pwallet->cs_wallet) |
782 | { |
783 | // Get cursor |
784 | Dbc* pcursor = GetCursor(); |
785 | if (!pcursor) |
786 | return DB_CORRUPT; |
787 | |
788 | loop |
789 | { |
790 | // Read next record |
791 | CDataStream ssKey; |
792 | CDataStream ssValue; |
793 | int ret = ReadAtCursor(pcursor, ssKey, ssValue); |
794 | if (ret == DB_NOTFOUND) |
795 | break; |
796 | else if (ret != 0) |
797 | return DB_CORRUPT; |
798 | |
799 | // Unserialize |
800 | // Taking advantage of the fact that pair serialization |
801 | // is just the two items serialized one after the other |
802 | string strType; |
803 | ssKey >> strType; |
804 | if (strType == "name") |
805 | { |
806 | string strAddress; |
807 | ssKey >> strAddress; |
808 | ssValue >> pwallet->mapAddressBook[strAddress]; |
809 | } |
810 | else if (strType == "tx") |
811 | { |
812 | uint256 hash; |
813 | ssKey >> hash; |
814 | CWalletTx& wtx = pwallet->mapWallet[hash]; |
815 | ssValue >> wtx; |
816 | wtx.pwallet = pwallet; |
817 | |
818 | if (wtx.GetHash() != hash) |
819 | printf("Error in wallet.dat, hash mismatch\n"); |
820 | |
821 | // Undo serialize changes in 31600 |
822 | if (31404 <= wtx.fTimeReceivedIsTxTime && wtx.fTimeReceivedIsTxTime <= 31703) |
823 | { |
824 | if (!ssValue.empty()) |
825 | { |
826 | char fTmp; |
827 | char fUnused; |
828 | ssValue >> fTmp >> fUnused >> wtx.strFromAccount; |
829 | printf("LoadWallet() upgrading tx ver=%d %d '%s' %s\n", wtx.fTimeReceivedIsTxTime, fTmp, wtx.strFromAccount.c_str(), hash.ToString().c_str()); |
830 | wtx.fTimeReceivedIsTxTime = fTmp; |
831 | } |
832 | else |
833 | { |
834 | printf("LoadWallet() repairing tx ver=%d %s\n", wtx.fTimeReceivedIsTxTime, hash.ToString().c_str()); |
835 | wtx.fTimeReceivedIsTxTime = 0; |
836 | } |
837 | vWalletUpgrade.push_back(hash); |
838 | } |
839 | } |
840 | else if (strType == "acentry") |
841 | { |
842 | string strAccount; |
843 | ssKey >> strAccount; |
844 | uint64 nNumber; |
845 | ssKey >> nNumber; |
846 | if (nNumber > nAccountingEntryNumber) |
847 | nAccountingEntryNumber = nNumber; |
848 | } |
849 | else if (strType == "key" || strType == "wkey") |
850 | { |
851 | vector<unsigned char> vchPubKey; |
852 | ssKey >> vchPubKey; |
853 | CKey key; |
854 | if (strType == "key") |
855 | { |
856 | CPrivKey pkey; |
857 | ssValue >> pkey; |
858 | key.SetPrivKey(pkey); |
859 | if (key.GetPubKey() != vchPubKey || !key.IsValid()) |
860 | return DB_CORRUPT; |
861 | } |
862 | else |
863 | { |
864 | CWalletKey wkey; |
865 | ssValue >> wkey; |
866 | key.SetPrivKey(wkey.vchPrivKey); |
867 | if (key.GetPubKey() != vchPubKey || !key.IsValid()) |
868 | return DB_CORRUPT; |
869 | } |
870 | if (!pwallet->LoadKey(key)) |
871 | return DB_CORRUPT; |
872 | } |
873 | else if (strType == "mkey") |
874 | { |
875 | unsigned int nID; |
876 | ssKey >> nID; |
877 | CMasterKey kMasterKey; |
878 | ssValue >> kMasterKey; |
879 | if(pwallet->mapMasterKeys.count(nID) != 0) |
880 | return DB_CORRUPT; |
881 | pwallet->mapMasterKeys[nID] = kMasterKey; |
882 | if (pwallet->nMasterKeyMaxID < nID) |
883 | pwallet->nMasterKeyMaxID = nID; |
884 | } |
885 | else if (strType == "ckey") |
886 | { |
887 | vector<unsigned char> vchPubKey; |
888 | ssKey >> vchPubKey; |
889 | vector<unsigned char> vchPrivKey; |
890 | ssValue >> vchPrivKey; |
891 | if (!pwallet->LoadCryptedKey(vchPubKey, vchPrivKey)) |
892 | return DB_CORRUPT; |
893 | fIsEncrypted = true; |
894 | } |
895 | else if (strType == "defaultkey") |
896 | { |
897 | ssValue >> pwallet->vchDefaultKey; |
898 | } |
899 | else if (strType == "pool") |
900 | { |
901 | int64 nIndex; |
902 | ssKey >> nIndex; |
903 | pwallet->setKeyPool.insert(nIndex); |
904 | } |
905 | else if (strType == "version") |
906 | { |
907 | ssValue >> nFileVersion; |
908 | if (nFileVersion == 10300) |
909 | nFileVersion = 300; |
910 | } |
911 | else if (strType == "setting") |
912 | { |
913 | string strKey; |
914 | ssKey >> strKey; |
915 | |
916 | // Options |
917 | if (strKey == "fGenerateBitcoins") ssValue >> fGenerateBitcoins; |
918 | if (strKey == "nTransactionFee") |
919 | { |
920 | int64 nFee = 0; |
921 | ssValue >> nFee; |
922 | SetTransactionFee(nFee); |
923 | } |
924 | if (strKey == "fLimitProcessors") ssValue >> fLimitProcessors; |
925 | if (strKey == "nLimitProcessors") ssValue >> nLimitProcessors; |
926 | if (strKey == "fMinimizeToTray") ssValue >> fMinimizeToTray; |
927 | if (strKey == "fMinimizeOnClose") ssValue >> fMinimizeOnClose; |
928 | if (strKey == "fUseProxy") ssValue >> fUseProxy; |
929 | if (strKey == "addrProxy") ssValue >> addrProxy; |
930 | } |
931 | else if (strType == "minversion") |
932 | { |
933 | int nMinVersion = 0; |
934 | ssValue >> nMinVersion; |
935 | if (nMinVersion > VERSION) |
936 | return DB_TOO_NEW; |
937 | } |
938 | } |
939 | pcursor->close(); |
940 | } |
941 | |
942 | BOOST_FOREACH(uint256 hash, vWalletUpgrade) |
943 | WriteTx(hash, pwallet->mapWallet[hash]); |
944 | |
945 | printf("nFileVersion = %d\n", nFileVersion); |
946 | printf("fGenerateBitcoins = %d\n", fGenerateBitcoins); |
947 | printf("nTransactionFee = %"PRI64d"\n", GetTransactionFee()); |
948 | printf("fMinimizeToTray = %d\n", fMinimizeToTray); |
949 | printf("fMinimizeOnClose = %d\n", fMinimizeOnClose); |
950 | printf("fUseProxy = %d\n", fUseProxy); |
951 | printf("addrProxy = %s\n", addrProxy.ToString().c_str()); |
952 | |
953 | // Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc: |
954 | if (fIsEncrypted && (nFileVersion == 40000 || nFileVersion == 50000)) |
955 | return DB_NEED_REWRITE; |
956 | |
957 | if (nFileVersion < VERSION) // Update |
958 | { |
959 | // Get rid of old debug.log file in current directory |
960 | if (nFileVersion <= 105 && !pszSetDataDir[0]) |
961 | unlink("debug.log"); |
962 | |
963 | WriteVersion(VERSION); |
964 | } |
965 | |
966 | return DB_LOAD_OK; |
967 | } |
968 | |
969 | void ThreadFlushWalletDB(void* parg) |
970 | { |
971 | const string& strFile = ((const string*)parg)[0]; |
972 | static bool fOneThread; |
973 | if (fOneThread) |
974 | return; |
975 | fOneThread = true; |
976 | if (mapArgs.count("-noflushwallet")) |
977 | return; |
978 | |
979 | unsigned int nLastSeen = nWalletDBUpdated; |
980 | unsigned int nLastFlushed = nWalletDBUpdated; |
981 | int64 nLastWalletUpdate = GetTime(); |
982 | while (!fShutdown) |
983 | { |
984 | Sleep(500); |
985 | |
986 | if (nLastSeen != nWalletDBUpdated) |
987 | { |
988 | nLastSeen = nWalletDBUpdated; |
989 | nLastWalletUpdate = GetTime(); |
990 | } |
991 | |
992 | if (nLastFlushed != nWalletDBUpdated && GetTime() - nLastWalletUpdate >= 2) |
993 | { |
994 | TRY_CRITICAL_BLOCK(cs_db) |
995 | { |
996 | // Don't do this if any databases are in use |
997 | int nRefCount = 0; |
998 | map<string, int>::iterator mi = mapFileUseCount.begin(); |
999 | while (mi != mapFileUseCount.end()) |
1000 | { |
1001 | nRefCount += (*mi).second; |
1002 | mi++; |
1003 | } |
1004 | |
1005 | if (nRefCount == 0 && !fShutdown) |
1006 | { |
1007 | map<string, int>::iterator mi = mapFileUseCount.find(strFile); |
1008 | if (mi != mapFileUseCount.end()) |
1009 | { |
1010 | printf("%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str()); |
1011 | printf("Flushing wallet.dat\n"); |
1012 | nLastFlushed = nWalletDBUpdated; |
1013 | int64 nStart = GetTimeMillis(); |
1014 | |
1015 | // Flush wallet.dat so it's self contained |
1016 | CloseDb(strFile); |
1017 | dbenv.txn_checkpoint(0, 0, 0); |
1018 | dbenv.lsn_reset(strFile.c_str(), 0); |
1019 | |
1020 | mapFileUseCount.erase(mi++); |
1021 | printf("Flushed wallet.dat %"PRI64d"ms\n", GetTimeMillis() - nStart); |
1022 | } |
1023 | } |
1024 | } |
1025 | } |
1026 | } |
1027 | } |
1028 | |
1029 | bool BackupWallet(const CWallet& wallet, const string& strDest) |
1030 | { |
1031 | if (!wallet.fFileBacked) |
1032 | return false; |
1033 | while (!fShutdown) |
1034 | { |
1035 | CRITICAL_BLOCK(cs_db) |
1036 | { |
1037 | if (!mapFileUseCount.count(wallet.strWalletFile) || mapFileUseCount[wallet.strWalletFile] == 0) |
1038 | { |
1039 | // Flush log data to the dat file |
1040 | CloseDb(wallet.strWalletFile); |
1041 | dbenv.txn_checkpoint(0, 0, 0); |
1042 | dbenv.lsn_reset(wallet.strWalletFile.c_str(), 0); |
1043 | mapFileUseCount.erase(wallet.strWalletFile); |
1044 | |
1045 | // Copy wallet.dat |
1046 | filesystem::path pathSrc(GetDataDir() + "/" + wallet.strWalletFile); |
1047 | filesystem::path pathDest(strDest); |
1048 | if (filesystem::is_directory(pathDest)) |
1049 | pathDest = pathDest / wallet.strWalletFile; |
1050 | #if BOOST_VERSION >= 104000 |
1051 | filesystem::copy_file(pathSrc, pathDest, filesystem::copy_option::overwrite_if_exists); |
1052 | #else |
1053 | filesystem::copy_file(pathSrc, pathDest); |
1054 | #endif |
1055 | printf("copied wallet.dat to %s\n", pathDest.string().c_str()); |
1056 | |
1057 | return true; |
1058 | } |
1059 | } |
1060 | Sleep(100); |
1061 | } |
1062 | return false; |
1063 | } |