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.
9 #include "chainparams.h"
17 #include <boost/thread.hpp>
21 static const char DB_COINS
= 'c';
22 static const char DB_BLOCK_FILES
= 'f';
23 static const char DB_TXINDEX
= 't';
24 static const char DB_BLOCK_INDEX
= 'b';
26 static const char DB_BEST_BLOCK
= 'B';
27 static const char DB_FLAG
= 'F';
28 static const char DB_REINDEX_FLAG
= 'R';
29 static const char DB_LAST_BLOCK
= 'l';
32 CCoinsViewDB::CCoinsViewDB(size_t nCacheSize
, bool fMemory
, bool fWipe
) : db(GetDataDir() / "chainstate", nCacheSize
, fMemory
, fWipe
, true)
36 bool CCoinsViewDB::GetCoins(const uint256
&txid
, CCoins
&coins
) const {
37 return db
.Read(make_pair(DB_COINS
, txid
), coins
);
40 bool CCoinsViewDB::HaveCoins(const uint256
&txid
) const {
41 return db
.Exists(make_pair(DB_COINS
, txid
));
44 uint256
CCoinsViewDB::GetBestBlock() const {
45 uint256 hashBestChain
;
46 if (!db
.Read(DB_BEST_BLOCK
, hashBestChain
))
51 bool CCoinsViewDB::BatchWrite(CCoinsMap
&mapCoins
, const uint256
&hashBlock
) {
52 CDBBatch
batch(&db
.GetObfuscateKey());
55 for (CCoinsMap::iterator it
= mapCoins
.begin(); it
!= mapCoins
.end();) {
56 if (it
->second
.flags
& CCoinsCacheEntry::DIRTY
) {
57 if (it
->second
.coins
.IsPruned())
58 batch
.Erase(make_pair(DB_COINS
, it
->first
));
60 batch
.Write(make_pair(DB_COINS
, it
->first
), it
->second
.coins
);
64 CCoinsMap::iterator itOld
= it
++;
65 mapCoins
.erase(itOld
);
67 if (!hashBlock
.IsNull())
68 batch
.Write(DB_BEST_BLOCK
, hashBlock
);
70 LogPrint("coindb", "Committing %u changed transactions (out of %u) to coin database...\n", (unsigned int)changed
, (unsigned int)count
);
71 return db
.WriteBatch(batch
);
74 CBlockTreeDB::CBlockTreeDB(size_t nCacheSize
, bool fMemory
, bool fWipe
) : CDBWrapper(GetDataDir() / "blocks" / "index", nCacheSize
, fMemory
, fWipe
) {
77 bool CBlockTreeDB::ReadBlockFileInfo(int nFile
, CBlockFileInfo
&info
) {
78 return Read(make_pair(DB_BLOCK_FILES
, nFile
), info
);
81 bool CBlockTreeDB::WriteReindexing(bool fReindexing
) {
83 return Write(DB_REINDEX_FLAG
, '1');
85 return Erase(DB_REINDEX_FLAG
);
88 bool CBlockTreeDB::ReadReindexing(bool &fReindexing
) {
89 fReindexing
= Exists(DB_REINDEX_FLAG
);
93 bool CBlockTreeDB::ReadLastBlockFile(int &nFile
) {
94 return Read(DB_LAST_BLOCK
, nFile
);
97 bool CCoinsViewDB::GetStats(CCoinsStats
&stats
) const {
98 /* It seems that there are no "const iterators" for LevelDB. Since we
99 only need read operations on it, use a const-cast to get around
101 boost::scoped_ptr
<CDBIterator
> pcursor(const_cast<CDBWrapper
*>(&db
)->NewIterator());
102 pcursor
->Seek(DB_COINS
);
104 CHashWriter
ss(SER_GETHASH
, PROTOCOL_VERSION
);
105 stats
.hashBlock
= GetBestBlock();
106 ss
<< stats
.hashBlock
;
107 CAmount nTotalAmount
= 0;
108 while (pcursor
->Valid()) {
109 boost::this_thread::interruption_point();
110 std::pair
<char, uint256
> key
;
112 if (pcursor
->GetKey(key
) && key
.first
== DB_COINS
) {
113 if (pcursor
->GetValue(coins
)) {
114 stats
.nTransactions
++;
115 for (unsigned int i
=0; i
<coins
.vout
.size(); i
++) {
116 const CTxOut
&out
= coins
.vout
[i
];
118 stats
.nTransactionOutputs
++;
121 nTotalAmount
+= out
.nValue
;
124 stats
.nSerializedSize
+= 32 + pcursor
->GetValueSize();
127 return error("CCoinsViewDB::GetStats() : unable to read value");
136 stats
.nHeight
= mapBlockIndex
.find(stats
.hashBlock
)->second
->nHeight
;
138 stats
.hashSerialized
= ss
.GetHash();
139 stats
.nTotalAmount
= nTotalAmount
;
143 bool CBlockTreeDB::WriteBatchSync(const std::vector
<std::pair
<int, const CBlockFileInfo
*> >& fileInfo
, int nLastFile
, const std::vector
<const CBlockIndex
*>& blockinfo
) {
144 CDBBatch
batch(&GetObfuscateKey());
145 for (std::vector
<std::pair
<int, const CBlockFileInfo
*> >::const_iterator it
=fileInfo
.begin(); it
!= fileInfo
.end(); it
++) {
146 batch
.Write(make_pair(DB_BLOCK_FILES
, it
->first
), *it
->second
);
148 batch
.Write(DB_LAST_BLOCK
, nLastFile
);
149 for (std::vector
<const CBlockIndex
*>::const_iterator it
=blockinfo
.begin(); it
!= blockinfo
.end(); it
++) {
150 batch
.Write(make_pair(DB_BLOCK_INDEX
, (*it
)->GetBlockHash()), CDiskBlockIndex(*it
));
152 return WriteBatch(batch
, true);
155 bool CBlockTreeDB::ReadTxIndex(const uint256
&txid
, CDiskTxPos
&pos
) {
156 return Read(make_pair(DB_TXINDEX
, txid
), pos
);
159 bool CBlockTreeDB::WriteTxIndex(const std::vector
<std::pair
<uint256
, CDiskTxPos
> >&vect
) {
160 CDBBatch
batch(&GetObfuscateKey());
161 for (std::vector
<std::pair
<uint256
,CDiskTxPos
> >::const_iterator it
=vect
.begin(); it
!=vect
.end(); it
++)
162 batch
.Write(make_pair(DB_TXINDEX
, it
->first
), it
->second
);
163 return WriteBatch(batch
);
166 bool CBlockTreeDB::WriteFlag(const std::string
&name
, bool fValue
) {
167 return Write(std::make_pair(DB_FLAG
, name
), fValue
? '1' : '0');
170 bool CBlockTreeDB::ReadFlag(const std::string
&name
, bool &fValue
) {
172 if (!Read(std::make_pair(DB_FLAG
, name
), ch
))
178 bool CBlockTreeDB::LoadBlockIndexGuts()
180 boost::scoped_ptr
<CDBIterator
> pcursor(NewIterator());
182 pcursor
->Seek(make_pair(DB_BLOCK_INDEX
, uint256()));
184 // Load mapBlockIndex
185 while (pcursor
->Valid()) {
186 boost::this_thread::interruption_point();
187 std::pair
<char, uint256
> key
;
188 if (pcursor
->GetKey(key
) && key
.first
== DB_BLOCK_INDEX
) {
189 CDiskBlockIndex diskindex
;
190 if (pcursor
->GetValue(diskindex
)) {
191 // Construct block index object
192 CBlockIndex
* pindexNew
= InsertBlockIndex(diskindex
.GetBlockHash());
193 pindexNew
->pprev
= InsertBlockIndex(diskindex
.hashPrev
);
194 pindexNew
->nHeight
= diskindex
.nHeight
;
195 pindexNew
->nFile
= diskindex
.nFile
;
196 pindexNew
->nDataPos
= diskindex
.nDataPos
;
197 pindexNew
->nUndoPos
= diskindex
.nUndoPos
;
198 pindexNew
->nVersion
= diskindex
.nVersion
;
199 pindexNew
->hashMerkleRoot
= diskindex
.hashMerkleRoot
;
200 pindexNew
->nTime
= diskindex
.nTime
;
201 pindexNew
->nBits
= diskindex
.nBits
;
202 pindexNew
->nNonce
= diskindex
.nNonce
;
203 pindexNew
->nStatus
= diskindex
.nStatus
;
204 pindexNew
->nTx
= diskindex
.nTx
;
206 if (!CheckProofOfWork(pindexNew
->GetBlockHash(), pindexNew
->nBits
, Params().GetConsensus()))
207 return error("LoadBlockIndex(): CheckProofOfWork failed: %s", pindexNew
->ToString());
211 return error("LoadBlockIndex() : failed to read value");