1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2014 The Bitcoin Core developers
3 // Distributed under the MIT software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
8 #include "chainparams.h"
16 #include <boost/thread.hpp>
20 static const char DB_COINS = 'c';
21 static const char DB_BLOCK_FILES = 'f';
22 static const char DB_TXINDEX = 't';
23 static const char DB_BLOCK_INDEX = 'b';
25 static const char DB_BEST_BLOCK = 'B';
26 static const char DB_FLAG = 'F';
27 static const char DB_REINDEX_FLAG = 'R';
28 static const char DB_LAST_BLOCK = 'l';
31 void static BatchWriteCoins(CLevelDBBatch &batch, const uint256 &hash, const CCoins &coins) {
33 batch.Erase(make_pair(DB_COINS, hash));
35 batch.Write(make_pair(DB_COINS, hash), coins);
38 void static BatchWriteHashBestChain(CLevelDBBatch &batch, const uint256 &hash) {
39 batch.Write(DB_BEST_BLOCK, hash);
42 CCoinsViewDB::CCoinsViewDB(size_t nCacheSize, bool fMemory, bool fWipe) : db(GetDataDir() / "chainstate", nCacheSize, fMemory, fWipe) {
45 bool CCoinsViewDB::GetCoins(const uint256 &txid, CCoins &coins) const {
46 return db.Read(make_pair(DB_COINS, txid), coins);
49 bool CCoinsViewDB::HaveCoins(const uint256 &txid) const {
50 return db.Exists(make_pair(DB_COINS, txid));
53 uint256 CCoinsViewDB::GetBestBlock() const {
54 uint256 hashBestChain;
55 if (!db.Read(DB_BEST_BLOCK, hashBestChain))
60 bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) {
64 for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) {
65 if (it->second.flags & CCoinsCacheEntry::DIRTY) {
66 BatchWriteCoins(batch, it->first, it->second.coins);
70 CCoinsMap::iterator itOld = it++;
71 mapCoins.erase(itOld);
73 if (!hashBlock.IsNull())
74 BatchWriteHashBestChain(batch, hashBlock);
76 LogPrint("coindb", "Committing %u changed transactions (out of %u) to coin database...\n", (unsigned int)changed, (unsigned int)count);
77 return db.WriteBatch(batch);
80 CBlockTreeDB::CBlockTreeDB(size_t nCacheSize, bool fMemory, bool fWipe) : CLevelDBWrapper(GetDataDir() / "blocks" / "index", nCacheSize, fMemory, fWipe) {
83 bool CBlockTreeDB::ReadBlockFileInfo(int nFile, CBlockFileInfo &info) {
84 return Read(make_pair(DB_BLOCK_FILES, nFile), info);
87 bool CBlockTreeDB::WriteReindexing(bool fReindexing) {
89 return Write(DB_REINDEX_FLAG, '1');
91 return Erase(DB_REINDEX_FLAG);
94 bool CBlockTreeDB::ReadReindexing(bool &fReindexing) {
95 fReindexing = Exists(DB_REINDEX_FLAG);
99 bool CBlockTreeDB::ReadLastBlockFile(int &nFile) {
100 return Read(DB_LAST_BLOCK, nFile);
103 bool CCoinsViewDB::GetStats(CCoinsStats &stats) const {
104 /* It seems that there are no "const iterators" for LevelDB. Since we
105 only need read operations on it, use a const-cast to get around
107 boost::scoped_ptr<leveldb::Iterator> pcursor(const_cast<CLevelDBWrapper*>(&db)->NewIterator());
108 pcursor->SeekToFirst();
110 CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION);
111 stats.hashBlock = GetBestBlock();
112 ss << stats.hashBlock;
113 CAmount nTotalAmount = 0;
114 while (pcursor->Valid()) {
115 boost::this_thread::interruption_point();
117 leveldb::Slice slKey = pcursor->key();
118 CDataStream ssKey(slKey.data(), slKey.data()+slKey.size(), SER_DISK, CLIENT_VERSION);
121 if (chType == DB_COINS) {
122 leveldb::Slice slValue = pcursor->value();
123 CDataStream ssValue(slValue.data(), slValue.data()+slValue.size(), SER_DISK, CLIENT_VERSION);
129 ss << VARINT(coins.nVersion);
130 ss << (coins.fCoinBase ? 'c' : 'n');
131 ss << VARINT(coins.nHeight);
132 stats.nTransactions++;
133 for (unsigned int i=0; i<coins.vout.size(); i++) {
134 const CTxOut &out = coins.vout[i];
136 stats.nTransactionOutputs++;
139 nTotalAmount += out.nValue;
142 stats.nSerializedSize += 32 + slValue.size();
146 } catch (const std::exception& e) {
147 return error("%s: Deserialize or I/O error - %s", __func__, e.what());
150 stats.nHeight = mapBlockIndex.find(GetBestBlock())->second->nHeight;
151 stats.hashSerialized = ss.GetHash();
152 stats.nTotalAmount = nTotalAmount;
156 bool CBlockTreeDB::WriteBatchSync(const std::vector<std::pair<int, const CBlockFileInfo*> >& fileInfo, int nLastFile, const std::vector<const CBlockIndex*>& blockinfo) {
158 for (std::vector<std::pair<int, const CBlockFileInfo*> >::const_iterator it=fileInfo.begin(); it != fileInfo.end(); it++) {
159 batch.Write(make_pair(DB_BLOCK_FILES, it->first), *it->second);
161 batch.Write(DB_LAST_BLOCK, nLastFile);
162 for (std::vector<const CBlockIndex*>::const_iterator it=blockinfo.begin(); it != blockinfo.end(); it++) {
163 batch.Write(make_pair(DB_BLOCK_INDEX, (*it)->GetBlockHash()), CDiskBlockIndex(*it));
165 return WriteBatch(batch, true);
168 bool CBlockTreeDB::ReadTxIndex(const uint256 &txid, CDiskTxPos &pos) {
169 return Read(make_pair(DB_TXINDEX, txid), pos);
172 bool CBlockTreeDB::WriteTxIndex(const std::vector<std::pair<uint256, CDiskTxPos> >&vect) {
174 for (std::vector<std::pair<uint256,CDiskTxPos> >::const_iterator it=vect.begin(); it!=vect.end(); it++)
175 batch.Write(make_pair(DB_TXINDEX, it->first), it->second);
176 return WriteBatch(batch);
179 bool CBlockTreeDB::WriteFlag(const std::string &name, bool fValue) {
180 return Write(std::make_pair(DB_FLAG, name), fValue ? '1' : '0');
183 bool CBlockTreeDB::ReadFlag(const std::string &name, bool &fValue) {
185 if (!Read(std::make_pair(DB_FLAG, name), ch))
191 bool CBlockTreeDB::LoadBlockIndexGuts()
193 boost::scoped_ptr<leveldb::Iterator> pcursor(NewIterator());
195 CDataStream ssKeySet(SER_DISK, CLIENT_VERSION);
196 ssKeySet << make_pair(DB_BLOCK_INDEX, uint256());
197 pcursor->Seek(ssKeySet.str());
199 // Load mapBlockIndex
200 while (pcursor->Valid()) {
201 boost::this_thread::interruption_point();
203 leveldb::Slice slKey = pcursor->key();
204 CDataStream ssKey(slKey.data(), slKey.data()+slKey.size(), SER_DISK, CLIENT_VERSION);
207 if (chType == DB_BLOCK_INDEX) {
208 leveldb::Slice slValue = pcursor->value();
209 CDataStream ssValue(slValue.data(), slValue.data()+slValue.size(), SER_DISK, CLIENT_VERSION);
210 CDiskBlockIndex diskindex;
211 ssValue >> diskindex;
213 // Construct block index object
214 CBlockIndex* pindexNew = InsertBlockIndex(diskindex.GetBlockHash());
215 pindexNew->pprev = InsertBlockIndex(diskindex.hashPrev);
216 pindexNew->nHeight = diskindex.nHeight;
217 pindexNew->nFile = diskindex.nFile;
218 pindexNew->nDataPos = diskindex.nDataPos;
219 pindexNew->nUndoPos = diskindex.nUndoPos;
220 pindexNew->nVersion = diskindex.nVersion;
221 pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot;
222 pindexNew->nTime = diskindex.nTime;
223 pindexNew->nBits = diskindex.nBits;
224 pindexNew->nNonce = diskindex.nNonce;
225 pindexNew->nStatus = diskindex.nStatus;
226 pindexNew->nTx = diskindex.nTx;
228 if (!CheckProofOfWork(pindexNew->GetBlockHash(), pindexNew->nBits, Params().GetConsensus()))
229 return error("LoadBlockIndex(): CheckProofOfWork failed: %s", pindexNew->ToString());
233 break; // if shutdown requested or finished loading block index
235 } catch (const std::exception& e) {
236 return error("%s: Deserialize or I/O error - %s", __func__, e.what());