-
+ B5486B3CB349B8AC9F3062359D595D28D076062390699FAC3F5AD50E89C3C82B1842524C153B09859A46F7F1776C40BE134F787B71761962D7170A7CDF8C3459
bitcoin/src/db.cpp
(0 . 0)(1 . 1069)
3848 // Copyright (c) 2009-2010 Satoshi Nakamoto
3849 // Copyright (c) 2009-2012 The Bitcoin developers
3850 // Distributed under the MIT/X11 software license, see the accompanying
3851 // file license.txt or http://www.opensource.org/licenses/mit-license.php.
3852
3853 #include "headers.h"
3854 #include "db.h"
3855 #include "net.h"
3856 #include <boost/filesystem.hpp>
3857 #include <boost/filesystem/fstream.hpp>
3858
3859 using namespace std;
3860 using namespace boost;
3861
3862
3863 unsigned int nWalletDBUpdated;
3864 uint64 nAccountingEntryNumber = 0;
3865
3866
3867
3868 //
3869 // CDB
3870 //
3871
3872 static CCriticalSection cs_db;
3873 static bool fDbEnvInit = false;
3874 DbEnv dbenv(0);
3875 static map<string, int> mapFileUseCount;
3876 static map<string, Db*> mapDb;
3877
3878 static void EnvShutdown()
3879 {
3880 if (!fDbEnvInit)
3881 return;
3882
3883 fDbEnvInit = false;
3884 try
3885 {
3886 dbenv.close(0);
3887 }
3888 catch (const DbException& e)
3889 {
3890 printf("EnvShutdown exception: %s (%d)\n", e.what(), e.get_errno());
3891 }
3892 DbEnv(0).remove(GetDataDir().c_str(), 0);
3893 }
3894
3895 class CDBInit
3896 {
3897 public:
3898 CDBInit()
3899 {
3900 }
3901 ~CDBInit()
3902 {
3903 EnvShutdown();
3904 }
3905 }
3906 instance_of_cdbinit;
3907
3908
3909 CDB::CDB(const char* pszFile, const char* pszMode) : pdb(NULL)
3910 {
3911 int ret;
3912 if (pszFile == NULL)
3913 return;
3914
3915 fReadOnly = (!strchr(pszMode, '+') && !strchr(pszMode, 'w'));
3916 bool fCreate = strchr(pszMode, 'c');
3917 unsigned int nFlags = DB_THREAD;
3918 if (fCreate)
3919 nFlags |= DB_CREATE;
3920
3921 CRITICAL_BLOCK(cs_db)
3922 {
3923 if (!fDbEnvInit)
3924 {
3925 if (fShutdown)
3926 return;
3927 string strDataDir = GetDataDir();
3928 string strLogDir = strDataDir + "/database";
3929 filesystem::create_directory(strLogDir.c_str());
3930 string strErrorFile = strDataDir + "/db.log";
3931 printf("dbenv.open strLogDir=%s strErrorFile=%s\n", strLogDir.c_str(), strErrorFile.c_str());
3932
3933 dbenv.set_lg_dir(strLogDir.c_str());
3934 dbenv.set_lg_max(10000000);
3935 dbenv.set_lk_max_locks(10000);
3936 dbenv.set_lk_max_objects(10000);
3937 dbenv.set_errfile(fopen(strErrorFile.c_str(), "a")); /// debug
3938 dbenv.set_flags(DB_AUTO_COMMIT, 1);
3939 ret = dbenv.open(strDataDir.c_str(),
3940 DB_CREATE |
3941 DB_INIT_LOCK |
3942 DB_INIT_LOG |
3943 DB_INIT_MPOOL |
3944 DB_INIT_TXN |
3945 DB_THREAD |
3946 DB_RECOVER,
3947 S_IRUSR | S_IWUSR);
3948 if (ret > 0)
3949 throw runtime_error(strprintf("CDB() : error %d opening database environment", ret));
3950 fDbEnvInit = true;
3951 }
3952
3953 strFile = pszFile;
3954 ++mapFileUseCount[strFile];
3955 pdb = mapDb[strFile];
3956 if (pdb == NULL)
3957 {
3958 pdb = new Db(&dbenv, 0);
3959
3960 ret = pdb->open(NULL, // Txn pointer
3961 pszFile, // Filename
3962 "main", // Logical db name
3963 DB_BTREE, // Database type
3964 nFlags, // Flags
3965 0);
3966
3967 if (ret > 0)
3968 {
3969 delete pdb;
3970 pdb = NULL;
3971 CRITICAL_BLOCK(cs_db)
3972 --mapFileUseCount[strFile];
3973 strFile = "";
3974 throw runtime_error(strprintf("CDB() : can't open database file %s, error %d", pszFile, ret));
3975 }
3976
3977 if (fCreate && !Exists(string("version")))
3978 {
3979 bool fTmp = fReadOnly;
3980 fReadOnly = false;
3981 WriteVersion(VERSION);
3982 fReadOnly = fTmp;
3983 }
3984
3985 mapDb[strFile] = pdb;
3986 }
3987 }
3988 }
3989
3990 void CDB::Close()
3991 {
3992 if (!pdb)
3993 return;
3994 if (!vTxn.empty())
3995 vTxn.front()->abort();
3996 vTxn.clear();
3997 pdb = NULL;
3998
3999 // Flush database activity from memory pool to disk log
4000 unsigned int nMinutes = 0;
4001 if (fReadOnly)
4002 nMinutes = 1;
4003 if (strFile == "addr.dat")
4004 nMinutes = 2;
4005 if (strFile == "blkindex.dat" && IsInitialBlockDownload() && nBestHeight % 500 != 0)
4006 nMinutes = 1;
4007 dbenv.txn_checkpoint(0, nMinutes, 0);
4008
4009 CRITICAL_BLOCK(cs_db)
4010 --mapFileUseCount[strFile];
4011 }
4012
4013 void static CloseDb(const string& strFile)
4014 {
4015 CRITICAL_BLOCK(cs_db)
4016 {
4017 if (mapDb[strFile] != NULL)
4018 {
4019 // Close the database handle
4020 Db* pdb = mapDb[strFile];
4021 pdb->close(0);
4022 delete pdb;
4023 mapDb[strFile] = NULL;
4024 }
4025 }
4026 }
4027
4028 bool CDB::Rewrite(const string& strFile, const char* pszSkip)
4029 {
4030 while (!fShutdown)
4031 {
4032 CRITICAL_BLOCK(cs_db)
4033 {
4034 if (!mapFileUseCount.count(strFile) || mapFileUseCount[strFile] == 0)
4035 {
4036 // Flush log data to the dat file
4037 CloseDb(strFile);
4038 dbenv.txn_checkpoint(0, 0, 0);
4039 dbenv.lsn_reset(strFile.c_str(), 0);
4040 mapFileUseCount.erase(strFile);
4041
4042 bool fSuccess = true;
4043 printf("Rewriting %s...\n", strFile.c_str());
4044 string strFileRes = strFile + ".rewrite";
4045 { // surround usage of db with extra {}
4046 CDB db(strFile.c_str(), "r");
4047 Db* pdbCopy = new Db(&dbenv, 0);
4048
4049 int ret = pdbCopy->open(NULL, // Txn pointer
4050 strFileRes.c_str(), // Filename
4051 "main", // Logical db name
4052 DB_BTREE, // Database type
4053 DB_CREATE, // Flags
4054 0);
4055 if (ret > 0)
4056 {
4057 printf("Cannot create database file %s\n", strFileRes.c_str());
4058 fSuccess = false;
4059 }
4060
4061 Dbc* pcursor = db.GetCursor();
4062 if (pcursor)
4063 while (fSuccess)
4064 {
4065 CDataStream ssKey;
4066 CDataStream ssValue;
4067 int ret = db.ReadAtCursor(pcursor, ssKey, ssValue, DB_NEXT);
4068 if (ret == DB_NOTFOUND)
4069 {
4070 pcursor->close();
4071 break;
4072 }
4073 else if (ret != 0)
4074 {
4075 pcursor->close();
4076 fSuccess = false;
4077 break;
4078 }
4079 if (pszSkip &&
4080 strncmp(&ssKey[0], pszSkip, std::min(ssKey.size(), strlen(pszSkip))) == 0)
4081 continue;
4082 if (strncmp(&ssKey[0], "\x07version", 8) == 0)
4083 {
4084 // Update version:
4085 ssValue.clear();
4086 ssValue << VERSION;
4087 }
4088 Dbt datKey(&ssKey[0], ssKey.size());
4089 Dbt datValue(&ssValue[0], ssValue.size());
4090 int ret2 = pdbCopy->put(NULL, &datKey, &datValue, DB_NOOVERWRITE);
4091 if (ret2 > 0)
4092 fSuccess = false;
4093 }
4094 if (fSuccess)
4095 {
4096 db.Close();
4097 CloseDb(strFile);
4098 if (pdbCopy->close(0))
4099 fSuccess = false;
4100 delete pdbCopy;
4101 }
4102 }
4103 if (fSuccess)
4104 {
4105 Db dbA(&dbenv, 0);
4106 if (dbA.remove(strFile.c_str(), NULL, 0))
4107 fSuccess = false;
4108 Db dbB(&dbenv, 0);
4109 if (dbB.rename(strFileRes.c_str(), NULL, strFile.c_str(), 0))
4110 fSuccess = false;
4111 }
4112 if (!fSuccess)
4113 printf("Rewriting of %s FAILED!\n", strFileRes.c_str());
4114 return fSuccess;
4115 }
4116 }
4117 Sleep(100);
4118 }
4119 return false;
4120 }
4121
4122
4123 void DBFlush(bool fShutdown)
4124 {
4125 // Flush log data to the actual data file
4126 // on all files that are not in use
4127 printf("DBFlush(%s)%s\n", fShutdown ? "true" : "false", fDbEnvInit ? "" : " db not started");
4128 if (!fDbEnvInit)
4129 return;
4130 CRITICAL_BLOCK(cs_db)
4131 {
4132 map<string, int>::iterator mi = mapFileUseCount.begin();
4133 while (mi != mapFileUseCount.end())
4134 {
4135 string strFile = (*mi).first;
4136 int nRefCount = (*mi).second;
4137 printf("%s refcount=%d\n", strFile.c_str(), nRefCount);
4138 if (nRefCount == 0)
4139 {
4140 // Move log data to the dat file
4141 CloseDb(strFile);
4142 dbenv.txn_checkpoint(0, 0, 0);
4143 printf("%s flush\n", strFile.c_str());
4144 dbenv.lsn_reset(strFile.c_str(), 0);
4145 mapFileUseCount.erase(mi++);
4146 }
4147 else
4148 mi++;
4149 }
4150 if (fShutdown)
4151 {
4152 char** listp;
4153 if (mapFileUseCount.empty())
4154 {
4155 dbenv.log_archive(&listp, DB_ARCH_REMOVE);
4156 EnvShutdown();
4157 }
4158 }
4159 }
4160 }
4161
4162
4163
4164
4165
4166
4167 //
4168 // CTxDB
4169 //
4170
4171 bool CTxDB::ReadTxIndex(uint256 hash, CTxIndex& txindex)
4172 {
4173 assert(!fClient);
4174 txindex.SetNull();
4175 return Read(make_pair(string("tx"), hash), txindex);
4176 }
4177
4178 bool CTxDB::UpdateTxIndex(uint256 hash, const CTxIndex& txindex)
4179 {
4180 assert(!fClient);
4181 return Write(make_pair(string("tx"), hash), txindex);
4182 }
4183
4184 bool CTxDB::AddTxIndex(const CTransaction& tx, const CDiskTxPos& pos, int nHeight)
4185 {
4186 assert(!fClient);
4187
4188 // Add to tx index
4189 uint256 hash = tx.GetHash();
4190 CTxIndex txindex(pos, tx.vout.size());
4191 return Write(make_pair(string("tx"), hash), txindex);
4192 }
4193
4194 bool CTxDB::EraseTxIndex(const CTransaction& tx)
4195 {
4196 assert(!fClient);
4197 uint256 hash = tx.GetHash();
4198
4199 return Erase(make_pair(string("tx"), hash));
4200 }
4201
4202 bool CTxDB::ContainsTx(uint256 hash)
4203 {
4204 assert(!fClient);
4205 return Exists(make_pair(string("tx"), hash));
4206 }
4207
4208 bool CTxDB::ReadOwnerTxes(uint160 hash160, int nMinHeight, vector<CTransaction>& vtx)
4209 {
4210 assert(!fClient);
4211 vtx.clear();
4212
4213 // Get cursor
4214 Dbc* pcursor = GetCursor();
4215 if (!pcursor)
4216 return false;
4217
4218 unsigned int fFlags = DB_SET_RANGE;
4219 loop
4220 {
4221 // Read next record
4222 CDataStream ssKey;
4223 if (fFlags == DB_SET_RANGE)
4224 ssKey << string("owner") << hash160 << CDiskTxPos(0, 0, 0);
4225 CDataStream ssValue;
4226 int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags);
4227 fFlags = DB_NEXT;
4228 if (ret == DB_NOTFOUND)
4229 break;
4230 else if (ret != 0)
4231 {
4232 pcursor->close();
4233 return false;
4234 }
4235
4236 // Unserialize
4237 string strType;
4238 uint160 hashItem;
4239 CDiskTxPos pos;
4240 ssKey >> strType >> hashItem >> pos;
4241 int nItemHeight;
4242 ssValue >> nItemHeight;
4243
4244 // Read transaction
4245 if (strType != "owner" || hashItem != hash160)
4246 break;
4247 if (nItemHeight >= nMinHeight)
4248 {
4249 vtx.resize(vtx.size()+1);
4250 if (!vtx.back().ReadFromDisk(pos))
4251 {
4252 pcursor->close();
4253 return false;
4254 }
4255 }
4256 }
4257
4258 pcursor->close();
4259 return true;
4260 }
4261
4262 bool CTxDB::ReadDiskTx(uint256 hash, CTransaction& tx, CTxIndex& txindex)
4263 {
4264 assert(!fClient);
4265 tx.SetNull();
4266 if (!ReadTxIndex(hash, txindex))
4267 return false;
4268 return (tx.ReadFromDisk(txindex.pos));
4269 }
4270
4271 bool CTxDB::ReadDiskTx(uint256 hash, CTransaction& tx)
4272 {
4273 CTxIndex txindex;
4274 return ReadDiskTx(hash, tx, txindex);
4275 }
4276
4277 bool CTxDB::ReadDiskTx(COutPoint outpoint, CTransaction& tx, CTxIndex& txindex)
4278 {
4279 return ReadDiskTx(outpoint.hash, tx, txindex);
4280 }
4281
4282 bool CTxDB::ReadDiskTx(COutPoint outpoint, CTransaction& tx)
4283 {
4284 CTxIndex txindex;
4285 return ReadDiskTx(outpoint.hash, tx, txindex);
4286 }
4287
4288 bool CTxDB::WriteBlockIndex(const CDiskBlockIndex& blockindex)
4289 {
4290 return Write(make_pair(string("blockindex"), blockindex.GetBlockHash()), blockindex);
4291 }
4292
4293 bool CTxDB::EraseBlockIndex(uint256 hash)
4294 {
4295 return Erase(make_pair(string("blockindex"), hash));
4296 }
4297
4298 bool CTxDB::ReadHashBestChain(uint256& hashBestChain)
4299 {
4300 return Read(string("hashBestChain"), hashBestChain);
4301 }
4302
4303 bool CTxDB::WriteHashBestChain(uint256 hashBestChain)
4304 {
4305 return Write(string("hashBestChain"), hashBestChain);
4306 }
4307
4308 bool CTxDB::ReadBestInvalidWork(CBigNum& bnBestInvalidWork)
4309 {
4310 return Read(string("bnBestInvalidWork"), bnBestInvalidWork);
4311 }
4312
4313 bool CTxDB::WriteBestInvalidWork(CBigNum bnBestInvalidWork)
4314 {
4315 return Write(string("bnBestInvalidWork"), bnBestInvalidWork);
4316 }
4317
4318 CBlockIndex static * InsertBlockIndex(uint256 hash)
4319 {
4320 if (hash == 0)
4321 return NULL;
4322
4323 // Return existing
4324 map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hash);
4325 if (mi != mapBlockIndex.end())
4326 return (*mi).second;
4327
4328 // Create new
4329 CBlockIndex* pindexNew = new CBlockIndex();
4330 if (!pindexNew)
4331 throw runtime_error("LoadBlockIndex() : new CBlockIndex failed");
4332 mi = mapBlockIndex.insert(make_pair(hash, pindexNew)).first;
4333 pindexNew->phashBlock = &((*mi).first);
4334
4335 return pindexNew;
4336 }
4337
4338 bool CTxDB::LoadBlockIndex()
4339 {
4340 // Get database cursor
4341 Dbc* pcursor = GetCursor();
4342 if (!pcursor)
4343 return false;
4344
4345 // Load mapBlockIndex
4346 unsigned int fFlags = DB_SET_RANGE;
4347 loop
4348 {
4349 // Read next record
4350 CDataStream ssKey;
4351 if (fFlags == DB_SET_RANGE)
4352 ssKey << make_pair(string("blockindex"), uint256(0));
4353 CDataStream ssValue;
4354 int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags);
4355 fFlags = DB_NEXT;
4356 if (ret == DB_NOTFOUND)
4357 break;
4358 else if (ret != 0)
4359 return false;
4360
4361 // Unserialize
4362 string strType;
4363 ssKey >> strType;
4364 if (strType == "blockindex")
4365 {
4366 CDiskBlockIndex diskindex;
4367 ssValue >> diskindex;
4368
4369 // Construct block index object
4370 CBlockIndex* pindexNew = InsertBlockIndex(diskindex.GetBlockHash());
4371 pindexNew->pprev = InsertBlockIndex(diskindex.hashPrev);
4372 pindexNew->pnext = InsertBlockIndex(diskindex.hashNext);
4373 pindexNew->nFile = diskindex.nFile;
4374 pindexNew->nBlockPos = diskindex.nBlockPos;
4375 pindexNew->nHeight = diskindex.nHeight;
4376 pindexNew->nVersion = diskindex.nVersion;
4377 pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot;
4378 pindexNew->nTime = diskindex.nTime;
4379 pindexNew->nBits = diskindex.nBits;
4380 pindexNew->nNonce = diskindex.nNonce;
4381
4382 // Watch for genesis block
4383 if (pindexGenesisBlock == NULL && diskindex.GetBlockHash() == hashGenesisBlock)
4384 pindexGenesisBlock = pindexNew;
4385
4386 if (!pindexNew->CheckIndex())
4387 return error("LoadBlockIndex() : CheckIndex failed at %d", pindexNew->nHeight);
4388 }
4389 else
4390 {
4391 break;
4392 }
4393 }
4394 pcursor->close();
4395
4396 // Calculate bnChainWork
4397 vector<pair<int, CBlockIndex*> > vSortedByHeight;
4398 vSortedByHeight.reserve(mapBlockIndex.size());
4399 BOOST_FOREACH(const PAIRTYPE(uint256, CBlockIndex*)& item, mapBlockIndex)
4400 {
4401 CBlockIndex* pindex = item.second;
4402 vSortedByHeight.push_back(make_pair(pindex->nHeight, pindex));
4403 }
4404 sort(vSortedByHeight.begin(), vSortedByHeight.end());
4405 BOOST_FOREACH(const PAIRTYPE(int, CBlockIndex*)& item, vSortedByHeight)
4406 {
4407 CBlockIndex* pindex = item.second;
4408 pindex->bnChainWork = (pindex->pprev ? pindex->pprev->bnChainWork : 0) + pindex->GetBlockWork();
4409 }
4410
4411 // Load hashBestChain pointer to end of best chain
4412 if (!ReadHashBestChain(hashBestChain))
4413 {
4414 if (pindexGenesisBlock == NULL)
4415 return true;
4416 return error("CTxDB::LoadBlockIndex() : hashBestChain not loaded");
4417 }
4418 if (!mapBlockIndex.count(hashBestChain))
4419 return error("CTxDB::LoadBlockIndex() : hashBestChain not found in the block index");
4420 pindexBest = mapBlockIndex[hashBestChain];
4421 nBestHeight = pindexBest->nHeight;
4422 bnBestChainWork = pindexBest->bnChainWork;
4423 printf("LoadBlockIndex(): hashBestChain=%s height=%d\n", hashBestChain.ToString().substr(0,20).c_str(), nBestHeight);
4424
4425 // Load bnBestInvalidWork, OK if it doesn't exist
4426 ReadBestInvalidWork(bnBestInvalidWork);
4427
4428 // Verify blocks in the best chain
4429 CBlockIndex* pindexFork = NULL;
4430 for (CBlockIndex* pindex = pindexBest; pindex && pindex->pprev; pindex = pindex->pprev)
4431 {
4432 if (pindex->nHeight < nBestHeight-2500 && !mapArgs.count("-checkblocks"))
4433 break;
4434 CBlock block;
4435 if (!block.ReadFromDisk(pindex))
4436 return error("LoadBlockIndex() : block.ReadFromDisk failed");
4437 if (!block.CheckBlock())
4438 {
4439 printf("LoadBlockIndex() : *** found bad block at %d, hash=%s\n", pindex->nHeight, pindex->GetBlockHash().ToString().c_str());
4440 pindexFork = pindex->pprev;
4441 }
4442 }
4443 if (pindexFork)
4444 {
4445 // Reorg back to the fork
4446 printf("LoadBlockIndex() : *** moving best chain pointer back to block %d\n", pindexFork->nHeight);
4447 CBlock block;
4448 if (!block.ReadFromDisk(pindexFork))
4449 return error("LoadBlockIndex() : block.ReadFromDisk failed");
4450 CTxDB txdb;
4451 block.SetBestChain(txdb, pindexFork);
4452 }
4453
4454 return true;
4455 }
4456
4457
4458
4459
4460
4461 //
4462 // CAddrDB
4463 //
4464
4465 bool CAddrDB::WriteAddress(const CAddress& addr)
4466 {
4467 return Write(make_pair(string("addr"), addr.GetKey()), addr);
4468 }
4469
4470 bool CAddrDB::EraseAddress(const CAddress& addr)
4471 {
4472 return Erase(make_pair(string("addr"), addr.GetKey()));
4473 }
4474
4475 bool CAddrDB::LoadAddresses()
4476 {
4477 CRITICAL_BLOCK(cs_mapAddresses)
4478 {
4479 // Get cursor
4480 Dbc* pcursor = GetCursor();
4481 if (!pcursor)
4482 return false;
4483
4484 loop
4485 {
4486 // Read next record
4487 CDataStream ssKey;
4488 CDataStream ssValue;
4489 int ret = ReadAtCursor(pcursor, ssKey, ssValue);
4490 if (ret == DB_NOTFOUND)
4491 break;
4492 else if (ret != 0)
4493 return false;
4494
4495 // Unserialize
4496 string strType;
4497 ssKey >> strType;
4498 if (strType == "addr")
4499 {
4500 CAddress addr;
4501 ssValue >> addr;
4502 mapAddresses.insert(make_pair(addr.GetKey(), addr));
4503 }
4504 }
4505 pcursor->close();
4506
4507 printf("Loaded %d addresses\n", mapAddresses.size());
4508 }
4509
4510 return true;
4511 }
4512
4513 bool LoadAddresses()
4514 {
4515 return CAddrDB("cr+").LoadAddresses();
4516 }
4517
4518
4519
4520
4521 //
4522 // CWalletDB
4523 //
4524
4525 bool CWalletDB::WriteName(const string& strAddress, const string& strName)
4526 {
4527 nWalletDBUpdated++;
4528 return Write(make_pair(string("name"), strAddress), strName);
4529 }
4530
4531 bool CWalletDB::EraseName(const string& strAddress)
4532 {
4533 // This should only be used for sending addresses, never for receiving addresses,
4534 // receiving addresses must always have an address book entry if they're not change return.
4535 nWalletDBUpdated++;
4536 return Erase(make_pair(string("name"), strAddress));
4537 }
4538
4539 bool CWalletDB::ReadAccount(const string& strAccount, CAccount& account)
4540 {
4541 account.SetNull();
4542 return Read(make_pair(string("acc"), strAccount), account);
4543 }
4544
4545 bool CWalletDB::WriteAccount(const string& strAccount, const CAccount& account)
4546 {
4547 return Write(make_pair(string("acc"), strAccount), account);
4548 }
4549
4550 bool CWalletDB::WriteAccountingEntry(const CAccountingEntry& acentry)
4551 {
4552 return Write(boost::make_tuple(string("acentry"), acentry.strAccount, ++nAccountingEntryNumber), acentry);
4553 }
4554
4555 int64 CWalletDB::GetAccountCreditDebit(const string& strAccount)
4556 {
4557 list<CAccountingEntry> entries;
4558 ListAccountCreditDebit(strAccount, entries);
4559
4560 int64 nCreditDebit = 0;
4561 BOOST_FOREACH (const CAccountingEntry& entry, entries)
4562 nCreditDebit += entry.nCreditDebit;
4563
4564 return nCreditDebit;
4565 }
4566
4567 void CWalletDB::ListAccountCreditDebit(const string& strAccount, list<CAccountingEntry>& entries)
4568 {
4569 bool fAllAccounts = (strAccount == "*");
4570
4571 Dbc* pcursor = GetCursor();
4572 if (!pcursor)
4573 throw runtime_error("CWalletDB::ListAccountCreditDebit() : cannot create DB cursor");
4574 unsigned int fFlags = DB_SET_RANGE;
4575 loop
4576 {
4577 // Read next record
4578 CDataStream ssKey;
4579 if (fFlags == DB_SET_RANGE)
4580 ssKey << boost::make_tuple(string("acentry"), (fAllAccounts? string("") : strAccount), uint64(0));
4581 CDataStream ssValue;
4582 int ret = ReadAtCursor(pcursor, ssKey, ssValue, fFlags);
4583 fFlags = DB_NEXT;
4584 if (ret == DB_NOTFOUND)
4585 break;
4586 else if (ret != 0)
4587 {
4588 pcursor->close();
4589 throw runtime_error("CWalletDB::ListAccountCreditDebit() : error scanning DB");
4590 }
4591
4592 // Unserialize
4593 string strType;
4594 ssKey >> strType;
4595 if (strType != "acentry")
4596 break;
4597 CAccountingEntry acentry;
4598 ssKey >> acentry.strAccount;
4599 if (!fAllAccounts && acentry.strAccount != strAccount)
4600 break;
4601
4602 ssValue >> acentry;
4603 entries.push_back(acentry);
4604 }
4605
4606 pcursor->close();
4607 }
4608
4609
4610 int CWalletDB::LoadWallet(CWallet* pwallet)
4611 {
4612 pwallet->vchDefaultKey.clear();
4613 int nFileVersion = 0;
4614 vector<uint256> vWalletUpgrade;
4615 bool fIsEncrypted = false;
4616
4617 // Modify defaults
4618 #ifndef WIN32
4619 // Tray icon sometimes disappears on 9.10 karmic koala 64-bit, leaving no way to access the program
4620 fMinimizeToTray = false;
4621 fMinimizeOnClose = false;
4622 #endif
4623
4624 //// todo: shouldn't we catch exceptions and try to recover and continue?
4625 CRITICAL_BLOCK(pwallet->cs_wallet)
4626 {
4627 // Get cursor
4628 Dbc* pcursor = GetCursor();
4629 if (!pcursor)
4630 return DB_CORRUPT;
4631
4632 loop
4633 {
4634 // Read next record
4635 CDataStream ssKey;
4636 CDataStream ssValue;
4637 int ret = ReadAtCursor(pcursor, ssKey, ssValue);
4638 if (ret == DB_NOTFOUND)
4639 break;
4640 else if (ret != 0)
4641 return DB_CORRUPT;
4642
4643 // Unserialize
4644 // Taking advantage of the fact that pair serialization
4645 // is just the two items serialized one after the other
4646 string strType;
4647 ssKey >> strType;
4648 if (strType == "name")
4649 {
4650 string strAddress;
4651 ssKey >> strAddress;
4652 ssValue >> pwallet->mapAddressBook[strAddress];
4653 }
4654 else if (strType == "tx")
4655 {
4656 uint256 hash;
4657 ssKey >> hash;
4658 CWalletTx& wtx = pwallet->mapWallet[hash];
4659 ssValue >> wtx;
4660 wtx.pwallet = pwallet;
4661
4662 if (wtx.GetHash() != hash)
4663 printf("Error in wallet.dat, hash mismatch\n");
4664
4665 // Undo serialize changes in 31600
4666 if (31404 <= wtx.fTimeReceivedIsTxTime && wtx.fTimeReceivedIsTxTime <= 31703)
4667 {
4668 if (!ssValue.empty())
4669 {
4670 char fTmp;
4671 char fUnused;
4672 ssValue >> fTmp >> fUnused >> wtx.strFromAccount;
4673 printf("LoadWallet() upgrading tx ver=%d %d '%s' %s\n", wtx.fTimeReceivedIsTxTime, fTmp, wtx.strFromAccount.c_str(), hash.ToString().c_str());
4674 wtx.fTimeReceivedIsTxTime = fTmp;
4675 }
4676 else
4677 {
4678 printf("LoadWallet() repairing tx ver=%d %s\n", wtx.fTimeReceivedIsTxTime, hash.ToString().c_str());
4679 wtx.fTimeReceivedIsTxTime = 0;
4680 }
4681 vWalletUpgrade.push_back(hash);
4682 }
4683
4684 //// debug print
4685 //printf("LoadWallet %s\n", wtx.GetHash().ToString().c_str());
4686 //printf(" %12I64d %s %s %s\n",
4687 // wtx.vout[0].nValue,
4688 // DateTimeStrFormat("%x %H:%M:%S", wtx.GetBlockTime()).c_str(),
4689 // wtx.hashBlock.ToString().substr(0,20).c_str(),
4690 // wtx.mapValue["message"].c_str());
4691 }
4692 else if (strType == "acentry")
4693 {
4694 string strAccount;
4695 ssKey >> strAccount;
4696 uint64 nNumber;
4697 ssKey >> nNumber;
4698 if (nNumber > nAccountingEntryNumber)
4699 nAccountingEntryNumber = nNumber;
4700 }
4701 else if (strType == "key" || strType == "wkey")
4702 {
4703 vector<unsigned char> vchPubKey;
4704 ssKey >> vchPubKey;
4705 CKey key;
4706 if (strType == "key")
4707 {
4708 CPrivKey pkey;
4709 ssValue >> pkey;
4710 key.SetPrivKey(pkey);
4711 if (key.GetPubKey() != vchPubKey || !key.IsValid())
4712 return DB_CORRUPT;
4713 }
4714 else
4715 {
4716 CWalletKey wkey;
4717 ssValue >> wkey;
4718 key.SetPrivKey(wkey.vchPrivKey);
4719 if (key.GetPubKey() != vchPubKey || !key.IsValid())
4720 return DB_CORRUPT;
4721 }
4722 if (!pwallet->LoadKey(key))
4723 return DB_CORRUPT;
4724 }
4725 else if (strType == "mkey")
4726 {
4727 unsigned int nID;
4728 ssKey >> nID;
4729 CMasterKey kMasterKey;
4730 ssValue >> kMasterKey;
4731 if(pwallet->mapMasterKeys.count(nID) != 0)
4732 return DB_CORRUPT;
4733 pwallet->mapMasterKeys[nID] = kMasterKey;
4734 if (pwallet->nMasterKeyMaxID < nID)
4735 pwallet->nMasterKeyMaxID = nID;
4736 }
4737 else if (strType == "ckey")
4738 {
4739 vector<unsigned char> vchPubKey;
4740 ssKey >> vchPubKey;
4741 vector<unsigned char> vchPrivKey;
4742 ssValue >> vchPrivKey;
4743 if (!pwallet->LoadCryptedKey(vchPubKey, vchPrivKey))
4744 return DB_CORRUPT;
4745 fIsEncrypted = true;
4746 }
4747 else if (strType == "defaultkey")
4748 {
4749 ssValue >> pwallet->vchDefaultKey;
4750 }
4751 else if (strType == "pool")
4752 {
4753 int64 nIndex;
4754 ssKey >> nIndex;
4755 pwallet->setKeyPool.insert(nIndex);
4756 }
4757 else if (strType == "version")
4758 {
4759 ssValue >> nFileVersion;
4760 if (nFileVersion == 10300)
4761 nFileVersion = 300;
4762 }
4763 else if (strType == "setting")
4764 {
4765 string strKey;
4766 ssKey >> strKey;
4767
4768 // Options
4769 #ifndef QT_GUI
4770 if (strKey == "fGenerateBitcoins") ssValue >> fGenerateBitcoins;
4771 #endif
4772 if (strKey == "nTransactionFee") ssValue >> nTransactionFee;
4773 if (strKey == "fLimitProcessors") ssValue >> fLimitProcessors;
4774 if (strKey == "nLimitProcessors") ssValue >> nLimitProcessors;
4775 if (strKey == "fMinimizeToTray") ssValue >> fMinimizeToTray;
4776 if (strKey == "fMinimizeOnClose") ssValue >> fMinimizeOnClose;
4777 if (strKey == "fUseProxy") ssValue >> fUseProxy;
4778 if (strKey == "addrProxy") ssValue >> addrProxy;
4779 if (fHaveUPnP && strKey == "fUseUPnP") ssValue >> fUseUPnP;
4780 }
4781 else if (strType == "minversion")
4782 {
4783 int nMinVersion = 0;
4784 ssValue >> nMinVersion;
4785 if (nMinVersion > VERSION)
4786 return DB_TOO_NEW;
4787 }
4788 }
4789 pcursor->close();
4790 }
4791
4792 BOOST_FOREACH(uint256 hash, vWalletUpgrade)
4793 WriteTx(hash, pwallet->mapWallet[hash]);
4794
4795 printf("nFileVersion = %d\n", nFileVersion);
4796 printf("fGenerateBitcoins = %d\n", fGenerateBitcoins);
4797 printf("nTransactionFee = %"PRI64d"\n", nTransactionFee);
4798 printf("fMinimizeToTray = %d\n", fMinimizeToTray);
4799 printf("fMinimizeOnClose = %d\n", fMinimizeOnClose);
4800 printf("fUseProxy = %d\n", fUseProxy);
4801 printf("addrProxy = %s\n", addrProxy.ToString().c_str());
4802 if (fHaveUPnP)
4803 printf("fUseUPnP = %d\n", fUseUPnP);
4804
4805
4806 // Rewrite encrypted wallets of versions 0.4.0 and 0.5.0rc:
4807 if (fIsEncrypted && (nFileVersion == 40000 || nFileVersion == 50000))
4808 return DB_NEED_REWRITE;
4809
4810 if (nFileVersion < VERSION) // Update
4811 {
4812 // Get rid of old debug.log file in current directory
4813 if (nFileVersion <= 105 && !pszSetDataDir[0])
4814 unlink("debug.log");
4815
4816 WriteVersion(VERSION);
4817 }
4818
4819 return DB_LOAD_OK;
4820 }
4821
4822 void ThreadFlushWalletDB(void* parg)
4823 {
4824 const string& strFile = ((const string*)parg)[0];
4825 static bool fOneThread;
4826 if (fOneThread)
4827 return;
4828 fOneThread = true;
4829 if (mapArgs.count("-noflushwallet"))
4830 return;
4831
4832 unsigned int nLastSeen = nWalletDBUpdated;
4833 unsigned int nLastFlushed = nWalletDBUpdated;
4834 int64 nLastWalletUpdate = GetTime();
4835 while (!fShutdown)
4836 {
4837 Sleep(500);
4838
4839 if (nLastSeen != nWalletDBUpdated)
4840 {
4841 nLastSeen = nWalletDBUpdated;
4842 nLastWalletUpdate = GetTime();
4843 }
4844
4845 if (nLastFlushed != nWalletDBUpdated && GetTime() - nLastWalletUpdate >= 2)
4846 {
4847 TRY_CRITICAL_BLOCK(cs_db)
4848 {
4849 // Don't do this if any databases are in use
4850 int nRefCount = 0;
4851 map<string, int>::iterator mi = mapFileUseCount.begin();
4852 while (mi != mapFileUseCount.end())
4853 {
4854 nRefCount += (*mi).second;
4855 mi++;
4856 }
4857
4858 if (nRefCount == 0 && !fShutdown)
4859 {
4860 map<string, int>::iterator mi = mapFileUseCount.find(strFile);
4861 if (mi != mapFileUseCount.end())
4862 {
4863 printf("%s ", DateTimeStrFormat("%x %H:%M:%S", GetTime()).c_str());
4864 printf("Flushing wallet.dat\n");
4865 nLastFlushed = nWalletDBUpdated;
4866 int64 nStart = GetTimeMillis();
4867
4868 // Flush wallet.dat so it's self contained
4869 CloseDb(strFile);
4870 dbenv.txn_checkpoint(0, 0, 0);
4871 dbenv.lsn_reset(strFile.c_str(), 0);
4872
4873 mapFileUseCount.erase(mi++);
4874 printf("Flushed wallet.dat %"PRI64d"ms\n", GetTimeMillis() - nStart);
4875 }
4876 }
4877 }
4878 }
4879 }
4880 }
4881
4882 bool BackupWallet(const CWallet& wallet, const string& strDest)
4883 {
4884 if (!wallet.fFileBacked)
4885 return false;
4886 while (!fShutdown)
4887 {
4888 CRITICAL_BLOCK(cs_db)
4889 {
4890 if (!mapFileUseCount.count(wallet.strWalletFile) || mapFileUseCount[wallet.strWalletFile] == 0)
4891 {
4892 // Flush log data to the dat file
4893 CloseDb(wallet.strWalletFile);
4894 dbenv.txn_checkpoint(0, 0, 0);
4895 dbenv.lsn_reset(wallet.strWalletFile.c_str(), 0);
4896 mapFileUseCount.erase(wallet.strWalletFile);
4897
4898 // Copy wallet.dat
4899 filesystem::path pathSrc(GetDataDir() + "/" + wallet.strWalletFile);
4900 filesystem::path pathDest(strDest);
4901 if (filesystem::is_directory(pathDest))
4902 pathDest = pathDest / wallet.strWalletFile;
4903 #if BOOST_VERSION >= 104000
4904 filesystem::copy_file(pathSrc, pathDest, filesystem::copy_option::overwrite_if_exists);
4905 #else
4906 filesystem::copy_file(pathSrc, pathDest);
4907 #endif
4908 printf("copied wallet.dat to %s\n", pathDest.string().c_str());
4909
4910 return true;
4911 }
4912 }
4913 Sleep(100);
4914 }
4915 return false;
4916 }