1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2016 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>
17 static const char DB_COINS
= 'c';
18 static const char DB_BLOCK_FILES
= 'f';
19 static const char DB_TXINDEX
= 't';
20 static const char DB_BLOCK_INDEX
= 'b';
22 static const char DB_BEST_BLOCK
= 'B';
23 static const char DB_FLAG
= 'F';
24 static const char DB_REINDEX_FLAG
= 'R';
25 static const char DB_LAST_BLOCK
= 'l';
28 CCoinsViewDB::CCoinsViewDB(size_t nCacheSize
, bool fMemory
, bool fWipe
) : db(GetDataDir() / "chainstate", nCacheSize
, fMemory
, fWipe
, true)
32 bool CCoinsViewDB::GetCoins(const uint256
&txid
, CCoins
&coins
) const {
33 return db
.Read(std::make_pair(DB_COINS
, txid
), coins
);
36 bool CCoinsViewDB::HaveCoins(const uint256
&txid
) const {
37 return db
.Exists(std::make_pair(DB_COINS
, txid
));
40 uint256
CCoinsViewDB::GetBestBlock() const {
41 uint256 hashBestChain
;
42 if (!db
.Read(DB_BEST_BLOCK
, hashBestChain
))
47 bool CCoinsViewDB::BatchWrite(CCoinsMap
&mapCoins
, const uint256
&hashBlock
) {
51 for (CCoinsMap::iterator it
= mapCoins
.begin(); it
!= mapCoins
.end();) {
52 if (it
->second
.flags
& CCoinsCacheEntry::DIRTY
) {
53 if (it
->second
.coins
.IsPruned())
54 batch
.Erase(std::make_pair(DB_COINS
, it
->first
));
56 batch
.Write(std::make_pair(DB_COINS
, it
->first
), it
->second
.coins
);
60 CCoinsMap::iterator itOld
= it
++;
61 mapCoins
.erase(itOld
);
63 if (!hashBlock
.IsNull())
64 batch
.Write(DB_BEST_BLOCK
, hashBlock
);
66 LogPrint(BCLog::COINDB
, "Committing %u changed transactions (out of %u) to coin database...\n", (unsigned int)changed
, (unsigned int)count
);
67 return db
.WriteBatch(batch
);
70 CBlockTreeDB::CBlockTreeDB(size_t nCacheSize
, bool fMemory
, bool fWipe
) : CDBWrapper(GetDataDir() / "blocks" / "index", nCacheSize
, fMemory
, fWipe
) {
73 bool CBlockTreeDB::ReadBlockFileInfo(int nFile
, CBlockFileInfo
&info
) {
74 return Read(std::make_pair(DB_BLOCK_FILES
, nFile
), info
);
77 bool CBlockTreeDB::WriteReindexing(bool fReindexing
) {
79 return Write(DB_REINDEX_FLAG
, '1');
81 return Erase(DB_REINDEX_FLAG
);
84 bool CBlockTreeDB::ReadReindexing(bool &fReindexing
) {
85 fReindexing
= Exists(DB_REINDEX_FLAG
);
89 bool CBlockTreeDB::ReadLastBlockFile(int &nFile
) {
90 return Read(DB_LAST_BLOCK
, nFile
);
93 CCoinsViewCursor
*CCoinsViewDB::Cursor() const
95 CCoinsViewDBCursor
*i
= new CCoinsViewDBCursor(const_cast<CDBWrapper
*>(&db
)->NewIterator(), GetBestBlock());
96 /* It seems that there are no "const iterators" for LevelDB. Since we
97 only need read operations on it, use a const-cast to get around
99 i
->pcursor
->Seek(DB_COINS
);
100 // Cache key of first record
101 i
->pcursor
->GetKey(i
->keyTmp
);
105 bool CCoinsViewDBCursor::GetKey(uint256
&key
) const
108 if (keyTmp
.first
== DB_COINS
) {
115 bool CCoinsViewDBCursor::GetValue(CCoins
&coins
) const
117 return pcursor
->GetValue(coins
);
120 unsigned int CCoinsViewDBCursor::GetValueSize() const
122 return pcursor
->GetValueSize();
125 bool CCoinsViewDBCursor::Valid() const
127 return keyTmp
.first
== DB_COINS
;
130 void CCoinsViewDBCursor::Next()
133 if (!pcursor
->Valid() || !pcursor
->GetKey(keyTmp
))
134 keyTmp
.first
= 0; // Invalidate cached key after last record so that Valid() and GetKey() return false
137 bool CBlockTreeDB::WriteBatchSync(const std::vector
<std::pair
<int, const CBlockFileInfo
*> >& fileInfo
, int nLastFile
, const std::vector
<const CBlockIndex
*>& blockinfo
) {
138 CDBBatch
batch(*this);
139 for (std::vector
<std::pair
<int, const CBlockFileInfo
*> >::const_iterator it
=fileInfo
.begin(); it
!= fileInfo
.end(); it
++) {
140 batch
.Write(std::make_pair(DB_BLOCK_FILES
, it
->first
), *it
->second
);
142 batch
.Write(DB_LAST_BLOCK
, nLastFile
);
143 for (std::vector
<const CBlockIndex
*>::const_iterator it
=blockinfo
.begin(); it
!= blockinfo
.end(); it
++) {
144 batch
.Write(std::make_pair(DB_BLOCK_INDEX
, (*it
)->GetBlockHash()), CDiskBlockIndex(*it
));
146 return WriteBatch(batch
, true);
149 bool CBlockTreeDB::ReadTxIndex(const uint256
&txid
, CDiskTxPos
&pos
) {
150 return Read(std::make_pair(DB_TXINDEX
, txid
), pos
);
153 bool CBlockTreeDB::WriteTxIndex(const std::vector
<std::pair
<uint256
, CDiskTxPos
> >&vect
) {
154 CDBBatch
batch(*this);
155 for (std::vector
<std::pair
<uint256
,CDiskTxPos
> >::const_iterator it
=vect
.begin(); it
!=vect
.end(); it
++)
156 batch
.Write(std::make_pair(DB_TXINDEX
, it
->first
), it
->second
);
157 return WriteBatch(batch
);
160 bool CBlockTreeDB::WriteFlag(const std::string
&name
, bool fValue
) {
161 return Write(std::make_pair(DB_FLAG
, name
), fValue
? '1' : '0');
164 bool CBlockTreeDB::ReadFlag(const std::string
&name
, bool &fValue
) {
166 if (!Read(std::make_pair(DB_FLAG
, name
), ch
))
172 bool CBlockTreeDB::LoadBlockIndexGuts(std::function
<CBlockIndex
*(const uint256
&)> insertBlockIndex
)
174 std::unique_ptr
<CDBIterator
> pcursor(NewIterator());
176 pcursor
->Seek(std::make_pair(DB_BLOCK_INDEX
, uint256()));
178 // Load mapBlockIndex
179 while (pcursor
->Valid()) {
180 boost::this_thread::interruption_point();
181 std::pair
<char, uint256
> key
;
182 if (pcursor
->GetKey(key
) && key
.first
== DB_BLOCK_INDEX
) {
183 CDiskBlockIndex diskindex
;
184 if (pcursor
->GetValue(diskindex
)) {
185 // Construct block index object
186 CBlockIndex
* pindexNew
= insertBlockIndex(diskindex
.GetBlockHash());
187 pindexNew
->pprev
= insertBlockIndex(diskindex
.hashPrev
);
188 pindexNew
->nHeight
= diskindex
.nHeight
;
189 pindexNew
->nFile
= diskindex
.nFile
;
190 pindexNew
->nDataPos
= diskindex
.nDataPos
;
191 pindexNew
->nUndoPos
= diskindex
.nUndoPos
;
192 pindexNew
->nVersion
= diskindex
.nVersion
;
193 pindexNew
->hashMerkleRoot
= diskindex
.hashMerkleRoot
;
194 pindexNew
->nTime
= diskindex
.nTime
;
195 pindexNew
->nBits
= diskindex
.nBits
;
196 pindexNew
->nNonce
= diskindex
.nNonce
;
197 pindexNew
->nStatus
= diskindex
.nStatus
;
198 pindexNew
->nTx
= diskindex
.nTx
;
200 if (!CheckProofOfWork(pindexNew
->GetBlockHash(), pindexNew
->nBits
, Params().GetConsensus()))
201 return error("LoadBlockIndex(): CheckProofOfWork failed: %s", pindexNew
->ToString());
205 return error("LoadBlockIndex() : failed to read value");