1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2015 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"
15 #include <boost/thread.hpp>
19 static const char DB_COINS
= 'c';
20 static const char DB_BLOCK_FILES
= 'f';
21 static const char DB_TXINDEX
= 't';
22 static const char DB_BLOCK_INDEX
= 'b';
24 static const char DB_BEST_BLOCK
= 'B';
25 static const char DB_FLAG
= 'F';
26 static const char DB_REINDEX_FLAG
= 'R';
27 static const char DB_LAST_BLOCK
= 'l';
30 CCoinsViewDB::CCoinsViewDB(size_t nCacheSize
, bool fMemory
, bool fWipe
) : db(GetDataDir() / "chainstate", nCacheSize
, fMemory
, fWipe
, true)
34 bool CCoinsViewDB::GetCoins(const uint256
&txid
, CCoins
&coins
) const {
35 return db
.Read(make_pair(DB_COINS
, txid
), coins
);
38 bool CCoinsViewDB::HaveCoins(const uint256
&txid
) const {
39 return db
.Exists(make_pair(DB_COINS
, txid
));
42 uint256
CCoinsViewDB::GetBestBlock() const {
43 uint256 hashBestChain
;
44 if (!db
.Read(DB_BEST_BLOCK
, hashBestChain
))
49 bool CCoinsViewDB::BatchWrite(CCoinsMap
&mapCoins
, const uint256
&hashBlock
) {
53 for (CCoinsMap::iterator it
= mapCoins
.begin(); it
!= mapCoins
.end();) {
54 if (it
->second
.flags
& CCoinsCacheEntry::DIRTY
) {
55 if (it
->second
.coins
.IsPruned())
56 batch
.Erase(make_pair(DB_COINS
, it
->first
));
58 batch
.Write(make_pair(DB_COINS
, it
->first
), it
->second
.coins
);
62 CCoinsMap::iterator itOld
= it
++;
63 mapCoins
.erase(itOld
);
65 if (!hashBlock
.IsNull())
66 batch
.Write(DB_BEST_BLOCK
, hashBlock
);
68 LogPrint("coindb", "Committing %u changed transactions (out of %u) to coin database...\n", (unsigned int)changed
, (unsigned int)count
);
69 return db
.WriteBatch(batch
);
72 CBlockTreeDB::CBlockTreeDB(size_t nCacheSize
, bool fMemory
, bool fWipe
) : CDBWrapper(GetDataDir() / "blocks" / "index", nCacheSize
, fMemory
, fWipe
) {
75 bool CBlockTreeDB::ReadBlockFileInfo(int nFile
, CBlockFileInfo
&info
) {
76 return Read(make_pair(DB_BLOCK_FILES
, nFile
), info
);
79 bool CBlockTreeDB::WriteReindexing(bool fReindexing
) {
81 return Write(DB_REINDEX_FLAG
, '1');
83 return Erase(DB_REINDEX_FLAG
);
86 bool CBlockTreeDB::ReadReindexing(bool &fReindexing
) {
87 fReindexing
= Exists(DB_REINDEX_FLAG
);
91 bool CBlockTreeDB::ReadLastBlockFile(int &nFile
) {
92 return Read(DB_LAST_BLOCK
, nFile
);
95 CCoinsViewCursor
*CCoinsViewDB::Cursor() const
97 CCoinsViewDBCursor
*i
= new CCoinsViewDBCursor(const_cast<CDBWrapper
*>(&db
)->NewIterator(), GetBestBlock());
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 i
->pcursor
->Seek(DB_COINS
);
102 // Cache key of first record
103 i
->pcursor
->GetKey(i
->keyTmp
);
107 bool CCoinsViewDBCursor::GetKey(uint256
&key
) const
110 if (keyTmp
.first
== DB_COINS
) {
117 bool CCoinsViewDBCursor::GetValue(CCoins
&coins
) const
119 return pcursor
->GetValue(coins
);
122 unsigned int CCoinsViewDBCursor::GetValueSize() const
124 return pcursor
->GetValueSize();
127 bool CCoinsViewDBCursor::Valid() const
129 return keyTmp
.first
== DB_COINS
;
132 void CCoinsViewDBCursor::Next()
135 if (!pcursor
->Valid() || !pcursor
->GetKey(keyTmp
))
136 keyTmp
.first
= 0; // Invalidate cached key after last record so that Valid() and GetKey() return false
139 bool CBlockTreeDB::WriteBatchSync(const std::vector
<std::pair
<int, const CBlockFileInfo
*> >& fileInfo
, int nLastFile
, const std::vector
<const CBlockIndex
*>& blockinfo
) {
140 CDBBatch
batch(*this);
141 for (std::vector
<std::pair
<int, const CBlockFileInfo
*> >::const_iterator it
=fileInfo
.begin(); it
!= fileInfo
.end(); it
++) {
142 batch
.Write(make_pair(DB_BLOCK_FILES
, it
->first
), *it
->second
);
144 batch
.Write(DB_LAST_BLOCK
, nLastFile
);
145 for (std::vector
<const CBlockIndex
*>::const_iterator it
=blockinfo
.begin(); it
!= blockinfo
.end(); it
++) {
146 batch
.Write(make_pair(DB_BLOCK_INDEX
, (*it
)->GetBlockHash()), CDiskBlockIndex(*it
));
148 return WriteBatch(batch
, true);
151 bool CBlockTreeDB::ReadTxIndex(const uint256
&txid
, CDiskTxPos
&pos
) {
152 return Read(make_pair(DB_TXINDEX
, txid
), pos
);
155 bool CBlockTreeDB::WriteTxIndex(const std::vector
<std::pair
<uint256
, CDiskTxPos
> >&vect
) {
156 CDBBatch
batch(*this);
157 for (std::vector
<std::pair
<uint256
,CDiskTxPos
> >::const_iterator it
=vect
.begin(); it
!=vect
.end(); it
++)
158 batch
.Write(make_pair(DB_TXINDEX
, it
->first
), it
->second
);
159 return WriteBatch(batch
);
162 bool CBlockTreeDB::WriteFlag(const std::string
&name
, bool fValue
) {
163 return Write(std::make_pair(DB_FLAG
, name
), fValue
? '1' : '0');
166 bool CBlockTreeDB::ReadFlag(const std::string
&name
, bool &fValue
) {
168 if (!Read(std::make_pair(DB_FLAG
, name
), ch
))
174 bool CBlockTreeDB::LoadBlockIndexGuts(boost::function
<CBlockIndex
*(const uint256
&)> insertBlockIndex
)
176 std::unique_ptr
<CDBIterator
> pcursor(NewIterator());
178 pcursor
->Seek(make_pair(DB_BLOCK_INDEX
, uint256()));
180 // Load mapBlockIndex
181 while (pcursor
->Valid()) {
182 boost::this_thread::interruption_point();
183 std::pair
<char, uint256
> key
;
184 if (pcursor
->GetKey(key
) && key
.first
== DB_BLOCK_INDEX
) {
185 CDiskBlockIndex diskindex
;
186 if (pcursor
->GetValue(diskindex
)) {
187 // Construct block index object
188 CBlockIndex
* pindexNew
= insertBlockIndex(diskindex
.GetBlockHash());
189 pindexNew
->pprev
= insertBlockIndex(diskindex
.hashPrev
);
190 pindexNew
->nHeight
= diskindex
.nHeight
;
191 pindexNew
->nFile
= diskindex
.nFile
;
192 pindexNew
->nDataPos
= diskindex
.nDataPos
;
193 pindexNew
->nUndoPos
= diskindex
.nUndoPos
;
194 pindexNew
->nVersion
= diskindex
.nVersion
;
195 pindexNew
->hashMerkleRoot
= diskindex
.hashMerkleRoot
;
196 pindexNew
->nTime
= diskindex
.nTime
;
197 pindexNew
->nBits
= diskindex
.nBits
;
198 pindexNew
->nNonce
= diskindex
.nNonce
;
199 pindexNew
->nStatus
= diskindex
.nStatus
;
200 pindexNew
->nTx
= diskindex
.nTx
;
202 if (!CheckProofOfWork(pindexNew
->GetBlockHash(), pindexNew
->nBits
, Params().GetConsensus()))
203 return error("LoadBlockIndex(): CheckProofOfWork failed: %s", pindexNew
->ToString());
207 return error("LoadBlockIndex() : failed to read value");