-
+ 470D1A940F0BBB26B40B7A30562BF5F7B929ACA78162358C65BC4EED8FE5E7B91DD1660058B7864E1B76E7B7E62A623661FA15F79373DA7F6BBC1A7D4D0DB2B2
bitcoin/src/db.cpp
(0 . 0)(1 . 1100)
4158 // /****************************\
4159 // * EXPERIMENTAL BRANCH. *
4160 // * FOR LABORATORY USE ONLY. *
4161 // ********************************
4162 // ************
4163 // **************
4164 // ****************
4165 // **** **** ****
4166 // *** *** ***
4167 // *** *** ***
4168 // *** * * **
4169 // ******** ********
4170 // ******* ******
4171 // *** **
4172 // * ******* **
4173 // ** * * * * *
4174 // ** * * ***
4175 // **** * * * * ****
4176 // **** *** * * ** ***
4177 // **** ********* ******
4178 // ******* ***** *******
4179 // ********* ****** **
4180 // ** ****** ******
4181 // ** ******* **
4182 // ** ******* ***
4183 // **** ******** ************
4184 // ************ ************
4185 // ******** *******
4186 // ****** ****
4187 // *** ***
4188 // ********************************
4189 // Copyright (c) 2009-2010 Satoshi Nakamoto
4190 // Copyright (c) 2009-2012 The Bitcoin developers
4191 // Distributed under the MIT/X11 software license, see the accompanying
4192 // file license.txt or http://www.opensource.org/licenses/mit-license.php.
4193
4194 #include "headers.h"
4195 #include "db.h"
4196 #include "net.h"
4197 #include <boost/filesystem.hpp>
4198 #include <boost/filesystem/fstream.hpp>
4199
4200 using namespace std;
4201 using namespace boost;
4202
4203
4204 unsigned int nWalletDBUpdated;
4205 uint64 nAccountingEntryNumber = 0;
4206
4207
4208
4209 //
4210 // CDB
4211 //
4212
4213 static CCriticalSection cs_db;
4214 static bool fDbEnvInit = false;
4215 DbEnv dbenv(0);
4216 static map<string, int> mapFileUseCount;
4217 static map<string, Db*> mapDb;
4218
4219 static void EnvShutdown()
4220 {
4221 if (!fDbEnvInit)
4222 return;
4223
4224 fDbEnvInit = false;
4225 try
4226 {
4227 dbenv.close(0);
4228 }
4229 catch (const DbException& e)
4230 {
4231 printf("EnvShutdown exception: %s (%d)\n", e.what(), e.get_errno());
4232 }
4233 DbEnv(0).remove(GetDataDir().c_str(), 0);
4234 }
4235
4236 class CDBInit
4237 {
4238 public:
4239 CDBInit()
4240 {
4241 }
4242 ~CDBInit()
4243 {
4244 EnvShutdown();
4245 }
4246 }
4247 instance_of_cdbinit;
4248
4249
4250 CDB::CDB(const char* pszFile, const char* pszMode) : pdb(NULL)
4251 {
4252 int ret;
4253 if (pszFile == NULL)
4254 return;
4255
4256 fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w'));
4257 bool fCreate = strchr(pszMode, 'c');
4258 unsigned int nFlags = DB_THREAD;
4259 if (fCreate)
4260 nFlags |= DB_CREATE;
4261
4262 CRITICAL_BLOCK(cs_db)
4263 {
4264 if (!fDbEnvInit)
4265 {
4266 if (fShutdown)
4267 return;
4268 string strDataDir = GetDataDir();
4269 string strLogDir = strDataDir + "/database";
4270 filesystem::create_directory(strLogDir.c_str());
4271 string strErrorFile = strDataDir + "/db.log";
4272 printf("dbenv.open strLogDir=%s strErrorFile=%s\n", strLogDir.c_str(), strErrorFile.c_str());
4273
4274 dbenv.set_lg_dir(strLogDir.c_str());
4275 dbenv.set_lg_max(10000000);
4276 dbenv.set_lk_max_locks(10000);
4277 dbenv.set_lk_max_objects(10000);
4278 dbenv.set_errfile(fopen(strErrorFile.c_str(), "a")); /// debug
4279 dbenv.set_flags(DB_AUTO_COMMIT, 1);
4280 ret = dbenv.open(strDataDir.c_str(),
4281 DB_CREATE |
4282 DB_INIT_LOCK |
4283 DB_INIT_LOG |
4284 DB_INIT_MPOOL |
4285 DB_INIT_TXN |
4286 DB_THREAD |
4287 DB_RECOVER,
4288 S_IRUSR | S_IWUSR);
4289 if (ret > 0)
4290 throw runtime_error(strprintf("CDB() : error %d opening database environment", ret));
4291 fDbEnvInit = true;
4292 }
4293
4294 strFile = pszFile;
4295 ++mapFileUseCount[strFile];
4296 pdb = mapDb[strFile];
4297 if (pdb == NULL)
4298 {
4299 pdb = new Db(&dbenv, 0);
4300
4301 ret = pdb->open(NULL, // Txn pointer
4302 pszFile, // Filename
4303 "main", // Logical db name
4304 DB_BTREE, // Database type
4305 nFlags, // Flags
4306 0);
4307
4308 if (ret > 0)
4309 {
4310 delete pdb;
4311 pdb = NULL;
4312 CRITICAL_BLOCK(cs_db)
4313 --mapFileUseCount[strFile];
4314 strFile = "";
4315 throw runtime_error(strprintf("CDB() : can't open database file %s, error %d", pszFile, ret));
4316 }
4317
4318 if (fCreate && !Exists(string("version")))
4319 {
4320 bool fTmp = fReadOnly;
4321 fReadOnly = false;
4322 WriteVersion(VERSION);
4323 fReadOnly = fTmp;
4324 }
4325
4326 mapDb[strFile] = pdb;
4327 }
4328 }
4329 }
4330
4331 void CDB::Close()
4332 {
4333 if (!pdb)
4334 return;
4335 if (!vTxn.empty())
4336 vTxn.front()->abort();
4337 vTxn.clear();
4338 pdb = NULL;
4339
4340 // Flush database activity from memory pool to disk log
4341 unsigned int nMinutes = 0;
4342 if (fReadOnly)
4343 nMinutes = 1;
4344 if (strFile == "addr.dat")
4345 nMinutes = 2;
4346 if (strFile == "blkindex.dat" && IsInitialBlockDownload() && nBestHeight % 500 != 0)
4347 nMinutes = 1;
4348 dbenv.txn_checkpoint(0, nMinutes, 0);
4349
4350 CRITICAL_BLOCK(cs_db)
4351 --mapFileUseCount[strFile];
4352 }
4353
4354 void static CloseDb(const string& strFile)
4355 {
4356 CRITICAL_BLOCK(cs_db)
4357 {
4358 if (mapDb[strFile] != NULL)
4359 {
4360 // Close the database handle
4361 Db* pdb = mapDb[strFile];
4362 pdb->close(0);
4363 delete pdb;
4364 mapDb[strFile] = NULL;
4365 }
4366 }
4367 }
4368
4369 bool CDB::Rewrite(const string& strFile, const char* pszSkip)
4370 {
4371 while (!fShutdown)
4372 {
4373 CRITICAL_BLOCK(cs_db)
4374 {
4375 if (!mapFileUseCount.count(strFile) || mapFileUseCount[strFile] == 0)
4376 {
4377 // Flush log data to the dat file
4378 CloseDb(strFile);
4379 dbenv.txn_checkpoint(0, 0, 0);
4380 dbenv.lsn_reset(strFile.c_str(), 0);
4381 mapFileUseCount.erase(strFile);
4382
4383 bool fSuccess = true;
4384 printf("Rewriting %s...\n", strFile.c_str());
4385 string strFileRes = strFile + ".rewrite";
4386 { // surround usage of db with extra {}
4387 CDB db(strFile.c_str(), "r");
4388 Db* pdbCopy = new Db(&dbenv, 0);
4389
4390 int ret = pdbCopy->open(NULL, // Txn pointer
4391 strFileRes.c_str(), // Filename
4392 "main", // Logical db name
4393 DB_BTREE, // Database type
4394 DB_CREATE, // Flags
4395 0);
4396 if (ret > 0)
4397 {
4398 printf("Cannot create database file %s\n", strFileRes.c_str());
4399 fSuccess = false;
4400 }
4401
4402 Dbc* pcursor = db.GetCursor();
4403 if (pcursor)
4404 while (fSuccess)
4405 {
4406 CDataStream ssKey;
4407 CDataStream ssValue;
4408 int ret = db.ReadAtCursor(pcursor, ssKey, ssValue, DB_NEXT);
4409 if (ret == DB_NOTFOUND)
4410 {
4411 pcursor->close();
4412 break;
4413 }
4414 else if (ret != 0)
4415 {
4416 pcursor->close();
4417 fSuccess = false;
4418 break;
4419 }
4420 if (pszSkip &&
4421 strncmp(&ssKey[0], pszSkip, std::min(ssKey.size(), strlen(pszSkip))) == 0)
4422 continue;
4423 if (strncmp(&ssKey[0], "\x07version", 8) == 0)
4424 {
4425 // Update version:
4426 ssValue.clear();
4427 ssValue << VERSION;
4428 }
4429 Dbt datKey(&ssKey[0], ssKey.size());
4430 Dbt datValue(&ssValue[0], ssValue.size());
4431 int ret2 = pdbCopy->put(NULL, &datKey, &datValue, DB_NOOVERWRITE);
4432 if (ret2 > 0)
4433 fSuccess = false;
4434 }
4435 if (fSuccess)
4436 {
4437 db.Close();
4438 CloseDb(strFile);
4439 if (pdbCopy->close(0))
4440 fSuccess = false;
4441 delete pdbCopy;
4442 }
4443 }
4444 if (fSuccess)
4445 {
4446 Db dbA(&dbenv, 0);
4447 if (dbA.remove(strFile.c_str(), NULL, 0))
4448 fSuccess = false;
4449 Db dbB(&dbenv, 0);
4450 if (dbB.rename(strFileRes.c_str(), NULL, strFile.c_str(), 0))
4451 fSuccess = false;
4452 }
4453 if (!fSuccess)
4454 printf("Rewriting of %s FAILED!\n", strFileRes.c_str());
4455 return fSuccess;
4456 }
4457 }
4458 Sleep(100);
4459 }
4460 return false;
4461 }
4462
4463
4464 void DBFlush(bool fShutdown)
4465 {
4466 // Flush log data to the actual data file
4467 // on all files that are not in use
4468 printf("DBFlush(%s)%s\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " db not started");
4469 if (!fDbEnvInit)
4470 return;
4471 CRITICAL_BLOCK(cs_db)
4472 {
4473 map<string, int>::iterator mi = mapFileUseCount.begin();
4474 while (mi != mapFileUseCount.end())
4475 {
4476 string strFile = (*mi).first;
4477 int nRefCount = (*mi).second;
4478 printf("%s refcount=%d\n", strFile.c_str(), nRefCount);
4479 if (nRefCount == 0)
4480 {
4481 // Move log data to the dat file
4482 CloseDb(strFile);
4483 dbenv.txn_checkpoint(0, 0, 0);
4484 printf("%s flush\n", strFile.c_str());
4485 dbenv.lsn_reset(strFile.c_str(), 0);
4486 mapFileUseCount.erase(mi++);
4487 }
4488 else
4489 mi++;
4490 }
4491 if (fShutdown)
4492 {
4493 char** listp;
4494 if (mapFileUseCount.empty())
4495 {
4496 dbenv.log_archive(&listp, DB_ARCH_REMOVE);
4497 EnvShutdown();
4498 }
4499 }
4500 }
4501 }
4502
4503
4504
4505
4506
4507
4508 //
4509 // CTxDB
4510 //
4511
4512 bool CTxDB::ReadTxIndex(uint256 hash, CTxIndex& txindex)
4513 {
4514 assert(!fClient);
4515 txindex.SetNull();
4516 return Read(make_pair(string("tx"), hash), txindex);
4517 }
4518
4519 bool CTxDB::UpdateTxIndex(uint256 hash, const CTxIndex& txindex)
4520 {
4521 assert(!fClient);
4522 return Write(make_pair(string("tx"), hash), txindex);
4523 }
4524
4525 bool CTxDB::AddTxIndex(const CTransaction& tx, const CDiskTxPos& pos, int nHeight)
4526 {
4527 assert(!fClient);
4528
4529 // Add to tx index
4530 uint256 hash = tx.GetHash();
4531 CTxIndex txindex(pos, tx.vout.size());
4532 return Write(make_pair(string("tx"), hash), txindex);
4533 }
4534
4535 bool CTxDB::EraseTxIndex(const CTransaction& tx)
4536 {
4537 assert(!fClient);
4538 uint256 hash = tx.GetHash();
4539
4540 return Erase(make_pair(string("tx"), hash));
4541 }
4542
4543 bool CTxDB::ContainsTx(uint256 hash)
4544 {
4545 assert(!fClient);
4546 return Exists(make_pair(string("tx"), hash));
4547 }
4548
4549 bool CTxDB::ReadOwnerTxes(uint160 hash160, int nMinHeight, vector<CTransaction>& vtx)
4550 {
4551 assert(!fClient);
4552 vtx.clear();
4553
4554 // Get cursor
4555 Dbc* pcursor = GetCursor();
4556 if (!pcursor)
4557 return false;
4558
4559 unsigned int fFlags = DB_SET_RANGE;
4560 loop
4561 {
4562 // Read next record
4563 CDataStream ssKey;
4564 if (fFlags == DB_SET_RANGE)
4565 ssKey << string("owner") << hash160 << CDiskTxPos(0, 0, 0);
4566 CDataStream ssValue;
4567 int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags);
4568 fFlags = DB_NEXT;
4569 if (ret == DB_NOTFOUND)
4570 break;
4571 else if (ret != 0)
4572 {
4573 pcursor->close();
4574 return false;
4575 }
4576
4577 // Unserialize
4578 string strType;
4579 uint160 hashItem;
4580 CDiskTxPos pos;
4581 ssKey >> strType >> hashItem >> pos;
4582 int nItemHeight;
4583 ssValue >> nItemHeight;
4584
4585 // Read transaction
4586 if (strType != "owner" || hashItem != hash160)
4587 break;
4588 if (nItemHeight >= nMinHeight)
4589 {
4590 vtx.resize(vtx.size()+1);
4591 if (!vtx.back().ReadFromDisk(pos))
4592 {
4593 pcursor->close();
4594 return false;
4595 }
4596 }
4597 }
4598
4599 pcursor->close();
4600 return true;
4601 }
4602
4603 bool CTxDB::ReadDiskTx(uint256 hash, CTransaction& tx, CTxIndex& txindex)
4604 {
4605 assert(!fClient);
4606 tx.SetNull();
4607 if (!ReadTxIndex(hash, txindex))
4608 return false;
4609 return (tx.ReadFromDisk(txindex.pos));
4610 }
4611
4612 bool CTxDB::ReadDiskTx(uint256 hash, CTransaction& tx)
4613 {
4614 CTxIndex txindex;
4615 return ReadDiskTx(hash, tx, txindex);
4616 }
4617
4618 bool CTxDB::ReadDiskTx(COutPoint outpoint, CTransaction& tx, CTxIndex& txindex)
4619 {
4620 return ReadDiskTx(outpoint.hash, tx, txindex);
4621 }
4622
4623 bool CTxDB::ReadDiskTx(COutPoint outpoint, CTransaction& tx)
4624 {
4625 CTxIndex txindex;
4626 return ReadDiskTx(outpoint.hash, tx, txindex);
4627 }
4628
4629 bool CTxDB::WriteBlockIndex(const CDiskBlockIndex& blockindex)
4630 {
4631 return Write(make_pair(string("blockindex"), blockindex.GetBlockHash()), blockindex);
4632 }
4633
4634 bool CTxDB::EraseBlockIndex(uint256 hash)
4635 {
4636 return Erase(make_pair(string("blockindex"), hash));
4637 }
4638
4639 bool CTxDB::ReadHashBestChain(uint256& hashBestChain)
4640 {
4641 return Read(string("hashBestChain"), hashBestChain);
4642 }
4643
4644 bool CTxDB::WriteHashBestChain(uint256 hashBestChain)
4645 {
4646 return Write(string("hashBestChain"), hashBestChain);
4647 }
4648
4649 bool CTxDB::ReadBestInvalidWork(CBigNum& bnBestInvalidWork)
4650 {
4651 return Read(string("bnBestInvalidWork"), bnBestInvalidWork);
4652 }
4653
4654 bool CTxDB::WriteBestInvalidWork(CBigNum bnBestInvalidWork)
4655 {
4656 return Write(string("bnBestInvalidWork"), bnBestInvalidWork);
4657 }
4658
4659 CBlockIndex static * InsertBlockIndex(uint256 hash)
4660 {
4661 if (hash == 0)
4662 return NULL;
4663
4664 // Return existing
4665 map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hash);
4666 if (mi != mapBlockIndex.end())
4667 return (*mi).second;
4668
4669 // Create new
4670 CBlockIndex* pindexNew = new CBlockIndex();
4671 if (!pindexNew)
4672 throw runtime_error("LoadBlockIndex() : new CBlockIndex failed");
4673 mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first;
4674 pindexNew->phashBlock = &((*mi).first);
4675
4676 return pindexNew;
4677 }
4678
4679 bool CTxDB::LoadBlockIndex()
4680 {
4681 // Get database cursor
4682 Dbc* pcursor = GetCursor();
4683 if (!pcursor)
4684 return false;
4685
4686 // Load mapBlockIndex
4687 unsigned int fFlags = DB_SET_RANGE;
4688 loop
4689 {
4690 // Read next record
4691 CDataStream ssKey;
4692 if (fFlags == DB_SET_RANGE)
4693 ssKey << make_pair(string("blockindex"), uint256(0));
4694 CDataStream ssValue;
4695 int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags);
4696 fFlags = DB_NEXT;
4697 if (ret == DB_NOTFOUND)
4698 break;
4699 else if (ret != 0)
4700 return false;
4701
4702 // Unserialize
4703 string strType;
4704 ssKey >> strType;
4705 if (strType == "blockindex")
4706 {
4707 CDiskBlockIndex diskindex;
4708 ssValue >> diskindex;
4709
4710 // Construct block index object
4711 CBlockIndex* pindexNew = InsertBlockIndex(diskindex.GetBlockHash());
4712 pindexNew->pprev = InsertBlockIndex(diskindex.hashPrev);
4713 pindexNew->pnext = InsertBlockIndex(diskindex.hashNext);
4714 pindexNew->nFile = diskindex.nFile;
4715 pindexNew->nBlockPos = diskindex.nBlockPos;
4716 pindexNew->nHeight = diskindex.nHeight;
4717 pindexNew->nVersion = diskindex.nVersion;
4718 pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot;
4719 pindexNew->nTime = diskindex.nTime;
4720 pindexNew->nBits = diskindex.nBits;
4721 pindexNew->nNonce = diskindex.nNonce;
4722
4723 // Watch for genesis block
4724 if (pindexGenesisBlock == NULL && diskindex.GetBlockHash() == hashGenesisBlock)
4725 pindexGenesisBlock = pindexNew;
4726
4727 if (!pindexNew->CheckIndex())
4728 return error("LoadBlockIndex() : CheckIndex failed at %d", pindexNew->nHeight);
4729 }
4730 else
4731 {
4732 break;
4733 }
4734 }
4735 pcursor->close();
4736
4737 // Calculate bnChainWork
4738 vector<pair<int, CBlockIndex*> > vSortedByHeight;
4739 vSortedByHeight.reserve(mapBlockIndex.size());
4740 BOOST_FOREACH(const PAIRTYPE(uint256, CBlockIndex*)& item, mapBlockIndex)
4741 {
4742 CBlockIndex* pindex = item.second;
4743 vSortedByHeight.push_back(make_pair(pindex->nHeight, pindex));
4744 }
4745 sort(vSortedByHeight.begin(), vSortedByHeight.end());
4746 BOOST_FOREACH(const PAIRTYPE(int, CBlockIndex*)& item, vSortedByHeight)
4747 {
4748 CBlockIndex* pindex = item.second;
4749 pindex->bnChainWork = (pindex->pprev ? pindex->pprev->bnChainWork : 0) + pindex->GetBlockWork();
4750 }
4751
4752 // Load hashBestChain pointer to end of best chain
4753 if (!ReadHashBestChain(hashBestChain))
4754 {
4755 if (pindexGenesisBlock == NULL)
4756 return true;
4757 return error("CTxDB::LoadBlockIndex() : hashBestChain not loaded");
4758 }
4759 if (!mapBlockIndex.count(hashBestChain))
4760 return error("CTxDB::LoadBlockIndex() : hashBestChain not found in the block index");
4761 pindexBest = mapBlockIndex[hashBestChain];
4762 nBestHeight = pindexBest->nHeight;
4763 bnBestChainWork = pindexBest->bnChainWork;
4764 printf("LoadBlockIndex(): hashBestChain=%s height=%d\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight);
4765
4766 // Load bnBestInvalidWork, OK if it doesn't exist
4767 ReadBestInvalidWork(bnBestInvalidWork);
4768
4769 // Verify blocks in the best chain
4770 CBlockIndex* pindexFork = NULL;
4771 for (CBlockIndex* pindex = pindexBest; pindex && pindex->pprev; pindex = pindex->pprev)
4772 {
4773 if (pindex->nHeight < nBestHeight-2500 && !mapArgs.count("-checkblocks"))
4774 break;
4775 CBlock block;
4776 if (!block.ReadFromDisk(pindex))
4777 return error("LoadBlockIndex() : block.ReadFromDisk failed");
4778 if (!block.CheckBlock())
4779 {
4780 printf("LoadBlockIndex() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str());
4781 pindexFork = pindex->pprev;
4782 }
4783 }
4784 if (pindexFork)
4785 {
4786 // Reorg back to the fork
4787 printf("LoadBlockIndex() : *** moving best chain pointer back to block %d\n", pindexFork->nHeight);
4788 CBlock block;
4789 if (!block.ReadFromDisk(pindexFork))
4790 return error("LoadBlockIndex() : block.ReadFromDisk failed");
4791 CTxDB txdb;
4792 block.SetBestChain(txdb, pindexFork);
4793 }
4794
4795 return true;
4796 }
4797
4798
4799
4800
4801
4802 //
4803 // CAddrDB
4804 //
4805
4806 bool CAddrDB::WriteAddress(const CAddress& addr)
4807 {
4808 return Write(make_pair(string("addr"), addr.GetKey()), addr);
4809 }
4810
4811 bool CAddrDB::EraseAddress(const CAddress& addr)
4812 {
4813 return Erase(make_pair(string("addr"), addr.GetKey()));
4814 }
4815
4816 bool CAddrDB::LoadAddresses()
4817 {
4818 CRITICAL_BLOCK(cs_mapAddresses)
4819 {
4820 // Get cursor
4821 Dbc* pcursor = GetCursor();
4822 if (!pcursor)
4823 return false;
4824
4825 loop
4826 {
4827 // Read next record
4828 CDataStream ssKey;
4829 CDataStream ssValue;
4830 int ret = ReadAtCursor(pcursor, ssKey, ssValue);
4831 if (ret == DB_NOTFOUND)
4832 break;
4833 else if (ret != 0)
4834 return false;
4835
4836 // Unserialize
4837 string strType;
4838 ssKey >> strType;
4839 if (strType == "addr")
4840 {
4841 CAddress addr;
4842 ssValue >> addr;
4843 mapAddresses.insert(make_pair(addr.GetKey(), addr));
4844 }
4845 }
4846 pcursor->close();
4847
4848 printf("Loaded %d addresses\n", mapAddresses.size());
4849 }
4850
4851 return true;
4852 }
4853
4854 bool LoadAddresses()
4855 {
4856 return CAddrDB("cr+").LoadAddresses();
4857 }
4858
4859
4860
4861
4862 //
4863 // CWalletDB
4864 //
4865
4866 bool CWalletDB::WriteName(const string& strAddress, const string& strName)
4867 {
4868 nWalletDBUpdated++;
4869 return Write(make_pair(string("name"), strAddress), strName);
4870 }
4871
4872 bool CWalletDB::EraseName(const string& strAddress)
4873 {
4874 // This should only be used for sending addresses, never for receiving addresses,
4875 // receiving addresses must always have an address book entry if they're not change return.
4876 nWalletDBUpdated++;
4877 return Erase(make_pair(string("name"), strAddress));
4878 }
4879
4880 bool CWalletDB::ReadAccount(const string& strAccount, CAccount& account)
4881 {
4882 account.SetNull();
4883 return Read(make_pair(string("acc"), strAccount), account);
4884 }
4885
4886 bool CWalletDB::WriteAccount(const string& strAccount, const CAccount& account)
4887 {
4888 return Write(make_pair(string("acc"), strAccount), account);
4889 }
4890
4891 bool CWalletDB::WriteAccountingEntry(const CAccountingEntry& acentry)
4892 {
4893 return Write(boost::make_tuple(string("acentry"), acentry.strAccount, ++nAccountingEntryNumber), acentry);
4894 }
4895
4896 int64 CWalletDB::GetAccountCreditDebit(const string& strAccount)
4897 {
4898 list<CAccountingEntry> entries;
4899 ListAccountCreditDebit(strAccount, entries);
4900
4901 int64 nCreditDebit = 0;
4902 BOOST_FOREACH (const CAccountingEntry& entry, entries)
4903 nCreditDebit += entry.nCreditDebit;
4904
4905 return nCreditDebit;
4906 }
4907
4908 void CWalletDB::ListAccountCreditDebit(const string& strAccount, list<CAccountingEntry>& entries)
4909 {
4910 bool fAllAccounts = (strAccount == "*");
4911
4912 Dbc* pcursor = GetCursor();
4913 if (!pcursor)
4914 throw runtime_error("CWalletDB::ListAccountCreditDebit() : cannot create DB cursor");
4915 unsigned int fFlags = DB_SET_RANGE;
4916 loop
4917 {
4918 // Read next record
4919 CDataStream ssKey;
4920 if (fFlags == DB_SET_RANGE)
4921 ssKey << boost::make_tuple(string("acentry"), (fAllAccounts? string("") : strAccount), uint64(0));
4922 CDataStream ssValue;
4923 int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags);
4924 fFlags = DB_NEXT;
4925 if (ret == DB_NOTFOUND)
4926 break;
4927 else if (ret != 0)
4928 {
4929 pcursor->close();
4930 throw runtime_error("CWalletDB::ListAccountCreditDebit() : error scanning DB");
4931 }
4932
4933 // Unserialize
4934 string strType;
4935 ssKey >> strType;
4936 if (strType != "acentry")
4937 break;
4938 CAccountingEntry acentry;
4939 ssKey >> acentry.strAccount;
4940 if (!fAllAccounts && acentry.strAccount != strAccount)
4941 break;
4942
4943 ssValue >> acentry;
4944 entries.push_back(acentry);
4945 }
4946
4947 pcursor->close();
4948 }
4949
4950
4951 int CWalletDB::LoadWallet(CWallet* pwallet)
4952 {
4953 pwallet->vchDefaultKey.clear();
4954 int nFileVersion = 0;
4955 vector<uint256> vWalletUpgrade;
4956 bool fIsEncrypted = false;
4957
4958 // Modify defaults
4959 #ifndef WIN32
4960 // Tray icon sometimes disappears on 9.10 karmic koala 64-bit, leaving no way to access the program
4961 fMinimizeToTray = false;
4962 fMinimizeOnClose = false;
4963 #endif
4964
4965 //// todo: shouldn't we catch exceptions and try to recover and continue?
4966 CRITICAL_BLOCK(pwallet->cs_wallet)
4967 {
4968 // Get cursor
4969 Dbc* pcursor = GetCursor();
4970 if (!pcursor)
4971 return DB_CORRUPT;
4972
4973 loop
4974 {
4975 // Read next record
4976 CDataStream ssKey;
4977 CDataStream ssValue;
4978 int ret = ReadAtCursor(pcursor, ssKey, ssValue);
4979 if (ret == DB_NOTFOUND)
4980 break;
4981 else if (ret != 0)
4982 return DB_CORRUPT;
4983
4984 // Unserialize
4985 // Taking advantage of the fact that pair serialization
4986 // is just the two items serialized one after the other
4987 string strType;
4988 ssKey >> strType;
4989 if (strType == "name")
4990 {
4991 string strAddress;
4992 ssKey >> strAddress;
4993 ssValue >> pwallet->mapAddressBook[strAddress];
4994 }
4995 else if (strType == "tx")
4996 {
4997 uint256 hash;
4998 ssKey >> hash;
4999 CWalletTx& wtx = pwallet->mapWallet[hash];
5000 ssValue >> wtx;
5001 wtx.pwallet = pwallet;
5002
5003 if (wtx.GetHash() != hash)
5004 printf("Error in wallet.dat, hash mismatch\n");
5005
5006 // Undo serialize changes in 31600
5007 if (31404 <= wtx.fTimeReceivedIsTxTime && wtx.fTimeReceivedIsTxTime <= 31703)
5008 {
5009 if (!ssValue.empty())
5010 {
5011 char fTmp;
5012 char fUnused;
5013 ssValue >> fTmp >> fUnused >> wtx.strFromAccount;
5014 printf("LoadWallet() upgrading tx ver=%d %d '%s' %s\n", wtx.fTimeReceivedIsTxTime, fTmp, wtx.strFromAccount.c_str(), hash.ToString().c_str());
5015 wtx.fTimeReceivedIsTxTime = fTmp;
5016 }
5017 else
5018 {
5019 printf("LoadWallet() repairing tx ver=%d %s\n", wtx.fTimeReceivedIsTxTime, hash.ToString().c_str());
5020 wtx.fTimeReceivedIsTxTime = 0;
5021 }
5022 vWalletUpgrade.push_back(hash);
5023 }
5024
5025 //// debug print
5026 //printf("LoadWallet %s\n", wtx.GetHash().ToString().c_str());
5027 //printf(" %12I64d %s %s %s\n",
5028 // wtx.vout[0].nValue,
5029 // DateTimeStrFormat("%x %H:%M:%S", wtx.GetBlockTime()).c_str(),
5030 // wtx.hashBlock.ToString().substr(0,20).c_str(),
5031 // wtx.mapValue["message"].c_str());
5032 }
5033 else if (strType == "acentry")
5034 {
5035 string strAccount;
5036 ssKey >> strAccount;
5037 uint64 nNumber;
5038 ssKey >> nNumber;
5039 if (nNumber > nAccountingEntryNumber)
5040 nAccountingEntryNumber = nNumber;
5041 }
5042 else if (strType == "key" || strType == "wkey")
5043 {
5044 vector<unsigned char> vchPubKey;
5045 ssKey >> vchPubKey;
5046 CKey key;
5047 if (strType == "key")
5048 {
5049 CPrivKey pkey;
5050 ssValue >> pkey;
5051 key.SetPrivKey(pkey);
5052 if (key.GetPubKey() != vchPubKey || !key.IsValid())
5053 return DB_CORRUPT;
5054 }
5055 else
5056 {
5057 CWalletKey wkey;
5058 ssValue >> wkey;
5059 key.SetPrivKey(wkey.vchPrivKey);
5060 if (key.GetPubKey() != vchPubKey || !key.IsValid())
5061 return DB_CORRUPT;
5062 }
5063 if (!pwallet->LoadKey(key))
5064 return DB_CORRUPT;
5065 }
5066 else if (strType == "mkey")
5067 {
5068 unsigned int nID;
5069 ssKey >> nID;
5070 CMasterKey kMasterKey;
5071 ssValue >> kMasterKey;
5072 if(pwallet->mapMasterKeys.count(nID) != 0)
5073 return DB_CORRUPT;
5074 pwallet->mapMasterKeys[nID] = kMasterKey;
5075 if (pwallet->nMasterKeyMaxID < nID)
5076 pwallet->nMasterKeyMaxID = nID;
5077 }
5078 else if (strType == "ckey")
5079 {
5080 vector<unsigned char> vchPubKey;
5081 ssKey >> vchPubKey;
5082 vector<unsigned char> vchPrivKey;
5083 ssValue >> vchPrivKey;
5084 if (!pwallet->LoadCryptedKey(vchPubKey, vchPrivKey))
5085 return DB_CORRUPT;
5086 fIsEncrypted = true;
5087 }
5088 else if (strType == "defaultkey")
5089 {
5090 ssValue >> pwallet->vchDefaultKey;
5091 }
5092 else if (strType == "pool")
5093 {
5094 int64 nIndex;
5095 ssKey >> nIndex;
5096 pwallet->setKeyPool.insert(nIndex);
5097 }
5098 else if (strType == "version")
5099 {
5100 ssValue >> nFileVersion;
5101 if (nFileVersion == 10300)
5102 nFileVersion = 300;
5103 }
5104 else if (strType == "setting")
5105 {
5106 string strKey;
5107 ssKey >> strKey;
5108
5109 // Options
5110 #ifndef QT_GUI
5111 if (strKey == "fGenerateBitcoins") ssValue >> fGenerateBitcoins;
5112 #endif
5113 if (strKey == "nTransactionFee") ssValue >> nTransactionFee;
5114 if (strKey == "fLimitProcessors") ssValue >> fLimitProcessors;
5115 if (strKey == "nLimitProcessors") ssValue >> nLimitProcessors;
5116 if (strKey == "fMinimizeToTray") ssValue >> fMinimizeToTray;
5117 if (strKey == "fMinimizeOnClose") ssValue >> fMinimizeOnClose;
5118 if (strKey == "fUseProxy") ssValue >> fUseProxy;
5119 if (strKey == "addrProxy") ssValue >> addrProxy;
5120 if (fHaveUPnP && strKey == "fUseUPnP") ssValue >> fUseUPnP;
5121 }
5122 else if (strType == "minversion")
5123 {
5124 int nMinVersion = 0;
5125 ssValue >> nMinVersion;
5126 if (nMinVersion > VERSION)
5127 return DB_TOO_NEW;
5128 }
5129 }
5130 pcursor->close();
5131 }
5132
5133 BOOST_FOREACH(uint256 hash, vWalletUpgrade)
5134 WriteTx(hash, pwallet->mapWallet[hash]);
5135
5136 printf("nFileVersion = %d\n", nFileVersion);
5137 printf("fGenerateBitcoins = %d\n", fGenerateBitcoins);
5138 printf("nTransactionFee = %"PRI64d"\n", nTransactionFee);
5139 printf("fMinimizeToTray = %d\n", fMinimizeToTray);
5140 printf("fMinimizeOnClose = %d\n", fMinimizeOnClose);
5141 printf("fUseProxy = %d\n", fUseProxy);
5142 printf("addrProxy = %s\n", addrProxy.ToString().c_str());
5143 if (fHaveUPnP)
5144 printf("fUseUPnP = %d\n", fUseUPnP);
5145
5146
5147 // Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc:
5148 if (fIsEncrypted && (nFileVersion == 40000 || nFileVersion == 50000))
5149 return DB_NEED_REWRITE;
5150
5151 if (nFileVersion < VERSION) // Update
5152 {
5153 // Get rid of old debug.log file in current directory
5154 if (nFileVersion <= 105 && !pszSetDataDir[0])
5155 unlink("debug.log");
5156
5157 WriteVersion(VERSION);
5158 }
5159
5160 return DB_LOAD_OK;
5161 }
5162
5163 void ThreadFlushWalletDB(void* parg)
5164 {
5165 const string& strFile = ((const string*)parg)[0];
5166 static bool fOneThread;
5167 if (fOneThread)
5168 return;
5169 fOneThread = true;
5170 if (mapArgs.count("-noflushwallet"))
5171 return;
5172
5173 unsigned int nLastSeen = nWalletDBUpdated;
5174 unsigned int nLastFlushed = nWalletDBUpdated;
5175 int64 nLastWalletUpdate = GetTime();
5176 while (!fShutdown)
5177 {
5178 Sleep(500);
5179
5180 if (nLastSeen != nWalletDBUpdated)
5181 {
5182 nLastSeen = nWalletDBUpdated;
5183 nLastWalletUpdate = GetTime();
5184 }
5185
5186 if (nLastFlushed != nWalletDBUpdated && GetTime() - nLastWalletUpdate >= 2)
5187 {
5188 TRY_CRITICAL_BLOCK(cs_db)
5189 {
5190 // Don't do this if any databases are in use
5191 int nRefCount = 0;
5192 map<string, int>::iterator mi = mapFileUseCount.begin();
5193 while (mi != mapFileUseCount.end())
5194 {
5195 nRefCount += (*mi).second;
5196 mi++;
5197 }
5198
5199 if (nRefCount == 0 && !fShutdown)
5200 {
5201 map<string, int>::iterator mi = mapFileUseCount.find(strFile);
5202 if (mi != mapFileUseCount.end())
5203 {
5204 printf("%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str());
5205 printf("Flushing wallet.dat\n");
5206 nLastFlushed = nWalletDBUpdated;
5207 int64 nStart = GetTimeMillis();
5208
5209 // Flush wallet.dat so it's self contained
5210 CloseDb(strFile);
5211 dbenv.txn_checkpoint(0, 0, 0);
5212 dbenv.lsn_reset(strFile.c_str(), 0);
5213
5214 mapFileUseCount.erase(mi++);
5215 printf("Flushed wallet.dat %"PRI64d"ms\n", GetTimeMillis() - nStart);
5216 }
5217 }
5218 }
5219 }
5220 }
5221 }
5222
5223 bool BackupWallet(const CWallet& wallet, const string& strDest)
5224 {
5225 if (!wallet.fFileBacked)
5226 return false;
5227 while (!fShutdown)
5228 {
5229 CRITICAL_BLOCK(cs_db)
5230 {
5231 if (!mapFileUseCount.count(wallet.strWalletFile) || mapFileUseCount[wallet.strWalletFile] == 0)
5232 {
5233 // Flush log data to the dat file
5234 CloseDb(wallet.strWalletFile);
5235 dbenv.txn_checkpoint(0, 0, 0);
5236 dbenv.lsn_reset(wallet.strWalletFile.c_str(), 0);
5237 mapFileUseCount.erase(wallet.strWalletFile);
5238
5239 // Copy wallet.dat
5240 filesystem::path pathSrc(GetDataDir() + "/" + wallet.strWalletFile);
5241 filesystem::path pathDest(strDest);
5242 if (filesystem::is_directory(pathDest))
5243 pathDest = pathDest / wallet.strWalletFile;
5244 #if BOOST_VERSION >= 104000
5245 filesystem::copy_file(pathSrc, pathDest, filesystem::copy_option::overwrite_if_exists);
5246 #else
5247 filesystem::copy_file(pathSrc, pathDest);
5248 #endif
5249 printf("copied wallet.dat to %s\n", pathDest.string().c_str());
5250
5251 return true;
5252 }
5253 }
5254 Sleep(100);
5255 }
5256 return false;
5257 }