Merge #10522: [wallet] Remove unused variables
[bitcoinplatinum.git] / src / txdb.cpp
blobc8f509029324e7da3fd3d4a572401414908956a9
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.
6 #include "txdb.h"
8 #include "chainparams.h"
9 #include "hash.h"
10 #include "pow.h"
11 #include "uint256.h"
13 #include <stdint.h>
15 #include <boost/thread.hpp>
17 static const char DB_COIN = 'C';
18 static const char DB_COINS = 'c';
19 static const char DB_BLOCK_FILES = 'f';
20 static const char DB_TXINDEX = 't';
21 static const char DB_BLOCK_INDEX = 'b';
23 static const char DB_BEST_BLOCK = 'B';
24 static const char DB_FLAG = 'F';
25 static const char DB_REINDEX_FLAG = 'R';
26 static const char DB_LAST_BLOCK = 'l';
28 namespace {
30 struct CoinEntry {
31 COutPoint* outpoint;
32 char key;
33 CoinEntry(const COutPoint* ptr) : outpoint(const_cast<COutPoint*>(ptr)), key(DB_COIN) {}
35 template<typename Stream>
36 void Serialize(Stream &s) const {
37 s << key;
38 s << outpoint->hash;
39 s << VARINT(outpoint->n);
42 template<typename Stream>
43 void Unserialize(Stream& s) {
44 s >> key;
45 s >> outpoint->hash;
46 s >> VARINT(outpoint->n);
52 CCoinsViewDB::CCoinsViewDB(size_t nCacheSize, bool fMemory, bool fWipe) : db(GetDataDir() / "chainstate", nCacheSize, fMemory, fWipe, true)
56 bool CCoinsViewDB::GetCoin(const COutPoint &outpoint, Coin &coin) const {
57 return db.Read(CoinEntry(&outpoint), coin);
60 bool CCoinsViewDB::HaveCoin(const COutPoint &outpoint) const {
61 return db.Exists(CoinEntry(&outpoint));
64 uint256 CCoinsViewDB::GetBestBlock() const {
65 uint256 hashBestChain;
66 if (!db.Read(DB_BEST_BLOCK, hashBestChain))
67 return uint256();
68 return hashBestChain;
71 bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlock) {
72 CDBBatch batch(db);
73 size_t count = 0;
74 size_t changed = 0;
75 for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) {
76 if (it->second.flags & CCoinsCacheEntry::DIRTY) {
77 CoinEntry entry(&it->first);
78 if (it->second.coin.IsSpent())
79 batch.Erase(entry);
80 else
81 batch.Write(entry, it->second.coin);
82 changed++;
84 count++;
85 CCoinsMap::iterator itOld = it++;
86 mapCoins.erase(itOld);
88 if (!hashBlock.IsNull())
89 batch.Write(DB_BEST_BLOCK, hashBlock);
91 bool ret = db.WriteBatch(batch);
92 LogPrint(BCLog::COINDB, "Committed %u changed transaction outputs (out of %u) to coin database...\n", (unsigned int)changed, (unsigned int)count);
93 return ret;
96 size_t CCoinsViewDB::EstimateSize() const
98 return db.EstimateSize(DB_COIN, (char)(DB_COIN+1));
101 CBlockTreeDB::CBlockTreeDB(size_t nCacheSize, bool fMemory, bool fWipe) : CDBWrapper(GetDataDir() / "blocks" / "index", nCacheSize, fMemory, fWipe) {
104 bool CBlockTreeDB::ReadBlockFileInfo(int nFile, CBlockFileInfo &info) {
105 return Read(std::make_pair(DB_BLOCK_FILES, nFile), info);
108 bool CBlockTreeDB::WriteReindexing(bool fReindexing) {
109 if (fReindexing)
110 return Write(DB_REINDEX_FLAG, '1');
111 else
112 return Erase(DB_REINDEX_FLAG);
115 bool CBlockTreeDB::ReadReindexing(bool &fReindexing) {
116 fReindexing = Exists(DB_REINDEX_FLAG);
117 return true;
120 bool CBlockTreeDB::ReadLastBlockFile(int &nFile) {
121 return Read(DB_LAST_BLOCK, nFile);
124 CCoinsViewCursor *CCoinsViewDB::Cursor() const
126 CCoinsViewDBCursor *i = new CCoinsViewDBCursor(const_cast<CDBWrapper*>(&db)->NewIterator(), GetBestBlock());
127 /* It seems that there are no "const iterators" for LevelDB. Since we
128 only need read operations on it, use a const-cast to get around
129 that restriction. */
130 i->pcursor->Seek(DB_COIN);
131 // Cache key of first record
132 if (i->pcursor->Valid()) {
133 CoinEntry entry(&i->keyTmp.second);
134 i->pcursor->GetKey(entry);
135 i->keyTmp.first = entry.key;
136 } else {
137 i->keyTmp.first = 0; // Make sure Valid() and GetKey() return false
139 return i;
142 bool CCoinsViewDBCursor::GetKey(COutPoint &key) const
144 // Return cached key
145 if (keyTmp.first == DB_COIN) {
146 key = keyTmp.second;
147 return true;
149 return false;
152 bool CCoinsViewDBCursor::GetValue(Coin &coin) const
154 return pcursor->GetValue(coin);
157 unsigned int CCoinsViewDBCursor::GetValueSize() const
159 return pcursor->GetValueSize();
162 bool CCoinsViewDBCursor::Valid() const
164 return keyTmp.first == DB_COIN;
167 void CCoinsViewDBCursor::Next()
169 pcursor->Next();
170 CoinEntry entry(&keyTmp.second);
171 if (!pcursor->Valid() || !pcursor->GetKey(entry)) {
172 keyTmp.first = 0; // Invalidate cached key after last record so that Valid() and GetKey() return false
173 } else {
174 keyTmp.first = entry.key;
178 bool CBlockTreeDB::WriteBatchSync(const std::vector<std::pair<int, const CBlockFileInfo*> >& fileInfo, int nLastFile, const std::vector<const CBlockIndex*>& blockinfo) {
179 CDBBatch batch(*this);
180 for (std::vector<std::pair<int, const CBlockFileInfo*> >::const_iterator it=fileInfo.begin(); it != fileInfo.end(); it++) {
181 batch.Write(std::make_pair(DB_BLOCK_FILES, it->first), *it->second);
183 batch.Write(DB_LAST_BLOCK, nLastFile);
184 for (std::vector<const CBlockIndex*>::const_iterator it=blockinfo.begin(); it != blockinfo.end(); it++) {
185 batch.Write(std::make_pair(DB_BLOCK_INDEX, (*it)->GetBlockHash()), CDiskBlockIndex(*it));
187 return WriteBatch(batch, true);
190 bool CBlockTreeDB::ReadTxIndex(const uint256 &txid, CDiskTxPos &pos) {
191 return Read(std::make_pair(DB_TXINDEX, txid), pos);
194 bool CBlockTreeDB::WriteTxIndex(const std::vector<std::pair<uint256, CDiskTxPos> >&vect) {
195 CDBBatch batch(*this);
196 for (std::vector<std::pair<uint256,CDiskTxPos> >::const_iterator it=vect.begin(); it!=vect.end(); it++)
197 batch.Write(std::make_pair(DB_TXINDEX, it->first), it->second);
198 return WriteBatch(batch);
201 bool CBlockTreeDB::WriteFlag(const std::string &name, bool fValue) {
202 return Write(std::make_pair(DB_FLAG, name), fValue ? '1' : '0');
205 bool CBlockTreeDB::ReadFlag(const std::string &name, bool &fValue) {
206 char ch;
207 if (!Read(std::make_pair(DB_FLAG, name), ch))
208 return false;
209 fValue = ch == '1';
210 return true;
213 bool CBlockTreeDB::LoadBlockIndexGuts(std::function<CBlockIndex*(const uint256&)> insertBlockIndex)
215 std::unique_ptr<CDBIterator> pcursor(NewIterator());
217 pcursor->Seek(std::make_pair(DB_BLOCK_INDEX, uint256()));
219 // Load mapBlockIndex
220 while (pcursor->Valid()) {
221 boost::this_thread::interruption_point();
222 std::pair<char, uint256> key;
223 if (pcursor->GetKey(key) && key.first == DB_BLOCK_INDEX) {
224 CDiskBlockIndex diskindex;
225 if (pcursor->GetValue(diskindex)) {
226 // Construct block index object
227 CBlockIndex* pindexNew = insertBlockIndex(diskindex.GetBlockHash());
228 pindexNew->pprev = insertBlockIndex(diskindex.hashPrev);
229 pindexNew->nHeight = diskindex.nHeight;
230 pindexNew->nFile = diskindex.nFile;
231 pindexNew->nDataPos = diskindex.nDataPos;
232 pindexNew->nUndoPos = diskindex.nUndoPos;
233 pindexNew->nVersion = diskindex.nVersion;
234 pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot;
235 pindexNew->nTime = diskindex.nTime;
236 pindexNew->nBits = diskindex.nBits;
237 pindexNew->nNonce = diskindex.nNonce;
238 pindexNew->nStatus = diskindex.nStatus;
239 pindexNew->nTx = diskindex.nTx;
241 if (!CheckProofOfWork(pindexNew->GetBlockHash(), pindexNew->nBits, Params().GetConsensus()))
242 return error("LoadBlockIndex(): CheckProofOfWork failed: %s", pindexNew->ToString());
244 pcursor->Next();
245 } else {
246 return error("LoadBlockIndex() : failed to read value");
248 } else {
249 break;
253 return true;
256 namespace {
258 //! Legacy class to deserialize pre-pertxout database entries without reindex.
259 class CCoins
261 public:
262 //! whether transaction is a coinbase
263 bool fCoinBase;
265 //! unspent transaction outputs; spent outputs are .IsNull(); spent outputs at the end of the array are dropped
266 std::vector<CTxOut> vout;
268 //! at which height this transaction was included in the active block chain
269 int nHeight;
271 //! empty constructor
272 CCoins() : fCoinBase(false), vout(0), nHeight(0) { }
274 template<typename Stream>
275 void Unserialize(Stream &s) {
276 unsigned int nCode = 0;
277 // version
278 int nVersionDummy;
279 ::Unserialize(s, VARINT(nVersionDummy));
280 // header code
281 ::Unserialize(s, VARINT(nCode));
282 fCoinBase = nCode & 1;
283 std::vector<bool> vAvail(2, false);
284 vAvail[0] = (nCode & 2) != 0;
285 vAvail[1] = (nCode & 4) != 0;
286 unsigned int nMaskCode = (nCode / 8) + ((nCode & 6) != 0 ? 0 : 1);
287 // spentness bitmask
288 while (nMaskCode > 0) {
289 unsigned char chAvail = 0;
290 ::Unserialize(s, chAvail);
291 for (unsigned int p = 0; p < 8; p++) {
292 bool f = (chAvail & (1 << p)) != 0;
293 vAvail.push_back(f);
295 if (chAvail != 0)
296 nMaskCode--;
298 // txouts themself
299 vout.assign(vAvail.size(), CTxOut());
300 for (unsigned int i = 0; i < vAvail.size(); i++) {
301 if (vAvail[i])
302 ::Unserialize(s, REF(CTxOutCompressor(vout[i])));
304 // coinbase height
305 ::Unserialize(s, VARINT(nHeight));
311 /** Upgrade the database from older formats.
313 * Currently implemented: from the per-tx utxo model (0.8..0.14.x) to per-txout.
315 bool CCoinsViewDB::Upgrade() {
316 std::unique_ptr<CDBIterator> pcursor(db.NewIterator());
317 pcursor->Seek(std::make_pair(DB_COINS, uint256()));
318 if (!pcursor->Valid()) {
319 return true;
322 LogPrintf("Upgrading database...\n");
323 size_t batch_size = 1 << 24;
324 CDBBatch batch(db);
325 while (pcursor->Valid()) {
326 boost::this_thread::interruption_point();
327 std::pair<unsigned char, uint256> key;
328 if (pcursor->GetKey(key) && key.first == DB_COINS) {
329 CCoins old_coins;
330 if (!pcursor->GetValue(old_coins)) {
331 return error("%s: cannot parse CCoins record", __func__);
333 COutPoint outpoint(key.second, 0);
334 for (size_t i = 0; i < old_coins.vout.size(); ++i) {
335 if (!old_coins.vout[i].IsNull() && !old_coins.vout[i].scriptPubKey.IsUnspendable()) {
336 Coin newcoin(std::move(old_coins.vout[i]), old_coins.nHeight, old_coins.fCoinBase);
337 outpoint.n = i;
338 CoinEntry entry(&outpoint);
339 batch.Write(entry, newcoin);
342 batch.Erase(key);
343 if (batch.SizeEstimate() > batch_size) {
344 db.WriteBatch(batch);
345 batch.Clear();
347 pcursor->Next();
348 } else {
349 break;
352 db.WriteBatch(batch);
353 return true;