]>
Commit | Line | Data |
---|---|---|
2d8a4829 | 1 | // Copyright (c) 2009-2010 Satoshi Nakamoto |
f914f1a7 | 2 | // Copyright (c) 2009-2014 The Bitcoin Core developers |
78253fcb | 3 | // Distributed under the MIT software license, see the accompanying |
2d8a4829 PW |
4 | // file COPYING or http://www.opensource.org/licenses/mit-license.php. |
5 | ||
4ca60bba | 6 | #include "txdb.h" |
51ed9ec9 | 7 | |
d698ef69 JT |
8 | #include "chainparams.h" |
9 | #include "hash.h" | |
8a893c94 | 10 | #include "main.h" |
df852d2b | 11 | #include "pow.h" |
51ed9ec9 BD |
12 | #include "uint256.h" |
13 | ||
14 | #include <stdint.h> | |
2d8a4829 | 15 | |
611116d4 PK |
16 | #include <boost/thread.hpp> |
17 | ||
2d8a4829 PW |
18 | using namespace std; |
19 | ||
9f25631d | 20 | static const char DB_ANCHOR = 'A'; |
22de1602 | 21 | static const char DB_NULLIFIER = 's'; |
14d023f1 E |
22 | static const char DB_COINS = 'c'; |
23 | static const char DB_BLOCK_FILES = 'f'; | |
24 | static const char DB_TXINDEX = 't'; | |
8b78a819 T |
25 | static const char DB_ADDRESSINDEX = 'd'; |
26 | static const char DB_ADDRESSUNSPENTINDEX = 'u'; | |
27 | static const char DB_TIMESTAMPINDEX = 'S'; | |
28 | static const char DB_BLOCKHASHINDEX = 'z'; | |
29 | static const char DB_SPENTINDEX = 'p'; | |
14d023f1 E |
30 | static const char DB_BLOCK_INDEX = 'b'; |
31 | ||
32 | static const char DB_BEST_BLOCK = 'B'; | |
9f25631d | 33 | static const char DB_BEST_ANCHOR = 'a'; |
14d023f1 E |
34 | static const char DB_FLAG = 'F'; |
35 | static const char DB_REINDEX_FLAG = 'R'; | |
36 | static const char DB_LAST_BLOCK = 'l'; | |
37 | ||
38 | ||
9f25631d SB |
39 | void static BatchWriteAnchor(CLevelDBBatch &batch, |
40 | const uint256 &croot, | |
434f3284 | 41 | const ZCIncrementalMerkleTree &tree, |
9f25631d SB |
42 | const bool &entered) |
43 | { | |
44 | if (!entered) | |
45 | batch.Erase(make_pair(DB_ANCHOR, croot)); | |
46 | else { | |
434f3284 | 47 | batch.Write(make_pair(DB_ANCHOR, croot), tree); |
9f25631d SB |
48 | } |
49 | } | |
50 | ||
22de1602 | 51 | void static BatchWriteNullifier(CLevelDBBatch &batch, const uint256 &nf, const bool &entered) { |
45d6bee9 | 52 | if (!entered) |
22de1602 | 53 | batch.Erase(make_pair(DB_NULLIFIER, nf)); |
45d6bee9 | 54 | else |
22de1602 | 55 | batch.Write(make_pair(DB_NULLIFIER, nf), true); |
45d6bee9 SB |
56 | } |
57 | ||
2d8a4829 PW |
58 | void static BatchWriteCoins(CLevelDBBatch &batch, const uint256 &hash, const CCoins &coins) { |
59 | if (coins.IsPruned()) | |
14d023f1 | 60 | batch.Erase(make_pair(DB_COINS, hash)); |
2d8a4829 | 61 | else |
14d023f1 | 62 | batch.Write(make_pair(DB_COINS, hash), coins); |
2d8a4829 PW |
63 | } |
64 | ||
65 | void static BatchWriteHashBestChain(CLevelDBBatch &batch, const uint256 &hash) { | |
14d023f1 | 66 | batch.Write(DB_BEST_BLOCK, hash); |
2d8a4829 PW |
67 | } |
68 | ||
9f25631d SB |
69 | void static BatchWriteHashBestAnchor(CLevelDBBatch &batch, const uint256 &hash) { |
70 | batch.Write(DB_BEST_ANCHOR, hash); | |
71 | } | |
72 | ||
c66c731a JG |
73 | CCoinsViewDB::CCoinsViewDB(std::string dbName, size_t nCacheSize, bool fMemory, bool fWipe) : db(GetDataDir() / dbName, nCacheSize, fMemory, fWipe) { |
74 | } | |
75 | ||
8b78a819 | 76 | CCoinsViewDB::CCoinsViewDB(size_t nCacheSize, bool fMemory, bool fWipe) : db(GetDataDir() / "chainstate", nCacheSize, fMemory, fWipe, false, 64) { |
2d8a4829 PW |
77 | } |
78 | ||
9f25631d | 79 | |
434f3284 | 80 | bool CCoinsViewDB::GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const { |
bf2e3122 | 81 | if (rt == ZCIncrementalMerkleTree::empty_root()) { |
434f3284 SB |
82 | ZCIncrementalMerkleTree new_tree; |
83 | tree = new_tree; | |
9f25631d SB |
84 | return true; |
85 | } | |
86 | ||
434f3284 | 87 | bool read = db.Read(make_pair(DB_ANCHOR, rt), tree); |
9f25631d | 88 | |
434f3284 | 89 | return read; |
9f25631d SB |
90 | } |
91 | ||
22de1602 | 92 | bool CCoinsViewDB::GetNullifier(const uint256 &nf) const { |
45d6bee9 | 93 | bool spent = false; |
22de1602 | 94 | bool read = db.Read(make_pair(DB_NULLIFIER, nf), spent); |
45d6bee9 | 95 | |
d66877af | 96 | return read; |
45d6bee9 SB |
97 | } |
98 | ||
a3dc587a | 99 | bool CCoinsViewDB::GetCoins(const uint256 &txid, CCoins &coins) const { |
14d023f1 | 100 | return db.Read(make_pair(DB_COINS, txid), coins); |
2d8a4829 PW |
101 | } |
102 | ||
a3dc587a | 103 | bool CCoinsViewDB::HaveCoins(const uint256 &txid) const { |
14d023f1 | 104 | return db.Exists(make_pair(DB_COINS, txid)); |
2d8a4829 PW |
105 | } |
106 | ||
a3dc587a | 107 | uint256 CCoinsViewDB::GetBestBlock() const { |
2d8a4829 | 108 | uint256 hashBestChain; |
14d023f1 | 109 | if (!db.Read(DB_BEST_BLOCK, hashBestChain)) |
4f152496 | 110 | return uint256(); |
84674082 | 111 | return hashBestChain; |
2d8a4829 PW |
112 | } |
113 | ||
9f25631d SB |
114 | uint256 CCoinsViewDB::GetBestAnchor() const { |
115 | uint256 hashBestAnchor; | |
116 | if (!db.Read(DB_BEST_ANCHOR, hashBestAnchor)) | |
bf2e3122 | 117 | return ZCIncrementalMerkleTree::empty_root(); |
9f25631d SB |
118 | return hashBestAnchor; |
119 | } | |
120 | ||
121 | bool CCoinsViewDB::BatchWrite(CCoinsMap &mapCoins, | |
122 | const uint256 &hashBlock, | |
123 | const uint256 &hashAnchor, | |
45d6bee9 | 124 | CAnchorsMap &mapAnchors, |
bb64be52 | 125 | CNullifiersMap &mapNullifiers) { |
2d8a4829 | 126 | CLevelDBBatch batch; |
058b08c1 PW |
127 | size_t count = 0; |
128 | size_t changed = 0; | |
b0875eb3 | 129 | for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) { |
058b08c1 PW |
130 | if (it->second.flags & CCoinsCacheEntry::DIRTY) { |
131 | BatchWriteCoins(batch, it->first, it->second.coins); | |
132 | changed++; | |
133 | } | |
134 | count++; | |
b0875eb3 PW |
135 | CCoinsMap::iterator itOld = it++; |
136 | mapCoins.erase(itOld); | |
137 | } | |
9f25631d SB |
138 | |
139 | for (CAnchorsMap::iterator it = mapAnchors.begin(); it != mapAnchors.end();) { | |
140 | if (it->second.flags & CAnchorsCacheEntry::DIRTY) { | |
141 | BatchWriteAnchor(batch, it->first, it->second.tree, it->second.entered); | |
142 | // TODO: changed++? | |
143 | } | |
144 | CAnchorsMap::iterator itOld = it++; | |
145 | mapAnchors.erase(itOld); | |
146 | } | |
147 | ||
bb64be52 | 148 | for (CNullifiersMap::iterator it = mapNullifiers.begin(); it != mapNullifiers.end();) { |
9e511dbb | 149 | if (it->second.flags & CNullifiersCacheEntry::DIRTY) { |
22de1602 | 150 | BatchWriteNullifier(batch, it->first, it->second.entered); |
45d6bee9 SB |
151 | // TODO: changed++? |
152 | } | |
d889a287 | 153 | CNullifiersMap::iterator itOld = it++; |
bb64be52 | 154 | mapNullifiers.erase(itOld); |
45d6bee9 SB |
155 | } |
156 | ||
4f152496 | 157 | if (!hashBlock.IsNull()) |
84674082 | 158 | BatchWriteHashBestChain(batch, hashBlock); |
9f25631d SB |
159 | if (!hashAnchor.IsNull()) |
160 | BatchWriteHashBestAnchor(batch, hashAnchor); | |
2d8a4829 | 161 | |
058b08c1 | 162 | LogPrint("coindb", "Committing %u changed transactions (out of %u) to coin database...\n", (unsigned int)changed, (unsigned int)count); |
2d8a4829 PW |
163 | return db.WriteBatch(batch); |
164 | } | |
165 | ||
8b78a819 | 166 | CBlockTreeDB::CBlockTreeDB(size_t nCacheSize, bool fMemory, bool fWipe, bool compression, int maxOpenFiles) : CLevelDBWrapper(GetDataDir() / "blocks" / "index", nCacheSize, fMemory, fWipe, compression, maxOpenFiles) { |
e1bfbab8 PW |
167 | } |
168 | ||
2d8a4829 | 169 | bool CBlockTreeDB::ReadBlockFileInfo(int nFile, CBlockFileInfo &info) { |
14d023f1 | 170 | return Read(make_pair(DB_BLOCK_FILES, nFile), info); |
2d8a4829 PW |
171 | } |
172 | ||
7fea4846 PW |
173 | bool CBlockTreeDB::WriteReindexing(bool fReindexing) { |
174 | if (fReindexing) | |
14d023f1 | 175 | return Write(DB_REINDEX_FLAG, '1'); |
7fea4846 | 176 | else |
14d023f1 | 177 | return Erase(DB_REINDEX_FLAG); |
7fea4846 PW |
178 | } |
179 | ||
180 | bool CBlockTreeDB::ReadReindexing(bool &fReindexing) { | |
14d023f1 | 181 | fReindexing = Exists(DB_REINDEX_FLAG); |
7fea4846 PW |
182 | return true; |
183 | } | |
184 | ||
2d8a4829 | 185 | bool CBlockTreeDB::ReadLastBlockFile(int &nFile) { |
14d023f1 | 186 | return Read(DB_LAST_BLOCK, nFile); |
2d8a4829 PW |
187 | } |
188 | ||
a3dc587a DK |
189 | bool CCoinsViewDB::GetStats(CCoinsStats &stats) const { |
190 | /* It seems that there are no "const iterators" for LevelDB. Since we | |
191 | only need read operations on it, use a const-cast to get around | |
192 | that restriction. */ | |
33dfbf57 | 193 | boost::scoped_ptr<leveldb::Iterator> pcursor(const_cast<CLevelDBWrapper*>(&db)->NewIterator()); |
beeb5761 PW |
194 | pcursor->SeekToFirst(); |
195 | ||
e31aa7c9 | 196 | CHashWriter ss(SER_GETHASH, PROTOCOL_VERSION); |
84674082 | 197 | stats.hashBlock = GetBestBlock(); |
e31aa7c9 | 198 | ss << stats.hashBlock; |
a372168e | 199 | CAmount nTotalAmount = 0; |
beeb5761 | 200 | while (pcursor->Valid()) { |
b31499ec | 201 | boost::this_thread::interruption_point(); |
beeb5761 PW |
202 | try { |
203 | leveldb::Slice slKey = pcursor->key(); | |
204 | CDataStream ssKey(slKey.data(), slKey.data()+slKey.size(), SER_DISK, CLIENT_VERSION); | |
205 | char chType; | |
206 | ssKey >> chType; | |
14d023f1 | 207 | if (chType == DB_COINS) { |
beeb5761 PW |
208 | leveldb::Slice slValue = pcursor->value(); |
209 | CDataStream ssValue(slValue.data(), slValue.data()+slValue.size(), SER_DISK, CLIENT_VERSION); | |
210 | CCoins coins; | |
211 | ssValue >> coins; | |
212 | uint256 txhash; | |
213 | ssKey >> txhash; | |
e31aa7c9 PW |
214 | ss << txhash; |
215 | ss << VARINT(coins.nVersion); | |
96e5f61d | 216 | ss << (coins.fCoinBase ? 'c' : 'n'); |
e31aa7c9 | 217 | ss << VARINT(coins.nHeight); |
beeb5761 | 218 | stats.nTransactions++; |
e31aa7c9 PW |
219 | for (unsigned int i=0; i<coins.vout.size(); i++) { |
220 | const CTxOut &out = coins.vout[i]; | |
221 | if (!out.IsNull()) { | |
beeb5761 | 222 | stats.nTransactionOutputs++; |
e31aa7c9 PW |
223 | ss << VARINT(i+1); |
224 | ss << out; | |
225 | nTotalAmount += out.nValue; | |
226 | } | |
beeb5761 PW |
227 | } |
228 | stats.nSerializedSize += 32 + slValue.size(); | |
e31aa7c9 | 229 | ss << VARINT(0); |
beeb5761 PW |
230 | } |
231 | pcursor->Next(); | |
27df4123 | 232 | } catch (const std::exception& e) { |
5262fde0 | 233 | return error("%s: Deserialize or I/O error - %s", __func__, e.what()); |
beeb5761 PW |
234 | } |
235 | } | |
a0455eca WL |
236 | { |
237 | LOCK(cs_main); | |
238 | stats.nHeight = mapBlockIndex.find(stats.hashBlock)->second->nHeight; | |
239 | } | |
e31aa7c9 PW |
240 | stats.hashSerialized = ss.GetHash(); |
241 | stats.nTotalAmount = nTotalAmount; | |
beeb5761 PW |
242 | return true; |
243 | } | |
244 | ||
63d1ae55 PW |
245 | bool CBlockTreeDB::WriteBatchSync(const std::vector<std::pair<int, const CBlockFileInfo*> >& fileInfo, int nLastFile, const std::vector<const CBlockIndex*>& blockinfo) { |
246 | CLevelDBBatch batch; | |
247 | for (std::vector<std::pair<int, const CBlockFileInfo*> >::const_iterator it=fileInfo.begin(); it != fileInfo.end(); it++) { | |
14d023f1 | 248 | batch.Write(make_pair(DB_BLOCK_FILES, it->first), *it->second); |
63d1ae55 | 249 | } |
14d023f1 | 250 | batch.Write(DB_LAST_BLOCK, nLastFile); |
63d1ae55 | 251 | for (std::vector<const CBlockIndex*>::const_iterator it=blockinfo.begin(); it != blockinfo.end(); it++) { |
14d023f1 | 252 | batch.Write(make_pair(DB_BLOCK_INDEX, (*it)->GetBlockHash()), CDiskBlockIndex(*it)); |
63d1ae55 PW |
253 | } |
254 | return WriteBatch(batch, true); | |
255 | } | |
256 | ||
2d1fa42e | 257 | bool CBlockTreeDB::ReadTxIndex(const uint256 &txid, CDiskTxPos &pos) { |
14d023f1 | 258 | return Read(make_pair(DB_TXINDEX, txid), pos); |
2d1fa42e PW |
259 | } |
260 | ||
261 | bool CBlockTreeDB::WriteTxIndex(const std::vector<std::pair<uint256, CDiskTxPos> >&vect) { | |
262 | CLevelDBBatch batch; | |
263 | for (std::vector<std::pair<uint256,CDiskTxPos> >::const_iterator it=vect.begin(); it!=vect.end(); it++) | |
14d023f1 | 264 | batch.Write(make_pair(DB_TXINDEX, it->first), it->second); |
2d1fa42e PW |
265 | return WriteBatch(batch); |
266 | } | |
267 | ||
8b78a819 T |
268 | bool CBlockTreeDB::ReadSpentIndex(CSpentIndexKey &key, CSpentIndexValue &value) { |
269 | return Read(make_pair(DB_SPENTINDEX, key), value); | |
270 | } | |
271 | ||
272 | bool CBlockTreeDB::UpdateSpentIndex(const std::vector<std::pair<CSpentIndexKey, CSpentIndexValue> >&vect) { | |
273 | CLevelDBBatch batch; | |
274 | for (std::vector<std::pair<CSpentIndexKey,CSpentIndexValue> >::const_iterator it=vect.begin(); it!=vect.end(); it++) { | |
275 | if (it->second.IsNull()) { | |
276 | batch.Erase(make_pair(DB_SPENTINDEX, it->first)); | |
277 | } else { | |
278 | batch.Write(make_pair(DB_SPENTINDEX, it->first), it->second); | |
279 | } | |
280 | } | |
281 | return WriteBatch(batch); | |
282 | } | |
283 | ||
284 | bool CBlockTreeDB::UpdateAddressUnspentIndex(const std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue > >&vect) { | |
285 | CLevelDBBatch batch; | |
286 | for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=vect.begin(); it!=vect.end(); it++) { | |
287 | if (it->second.IsNull()) { | |
288 | batch.Erase(make_pair(DB_ADDRESSUNSPENTINDEX, it->first)); | |
289 | } else { | |
290 | batch.Write(make_pair(DB_ADDRESSUNSPENTINDEX, it->first), it->second); | |
291 | } | |
292 | } | |
293 | return WriteBatch(batch); | |
294 | } | |
295 | ||
296 | bool CBlockTreeDB::ReadAddressUnspentIndex(uint160 addressHash, int type, | |
297 | std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > &unspentOutputs) { | |
298 | ||
299 | boost::scoped_ptr<leveldb::Iterator> pcursor(NewIterator()); | |
300 | ||
301 | CDataStream ssKeySet(SER_DISK, CLIENT_VERSION); | |
302 | ssKeySet << make_pair(DB_ADDRESSUNSPENTINDEX, CAddressIndexIteratorKey(type, addressHash)); | |
303 | pcursor->Seek(ssKeySet.str()); | |
304 | ||
305 | while (pcursor->Valid()) { | |
306 | boost::this_thread::interruption_point(); | |
307 | try { | |
308 | leveldb::Slice slKey = pcursor->key(); | |
309 | CDataStream ssKey(slKey.data(), slKey.data()+slKey.size(), SER_DISK, CLIENT_VERSION); | |
310 | char chType; | |
311 | CAddressUnspentKey indexKey; | |
312 | ssKey >> chType; | |
313 | ssKey >> indexKey; | |
314 | if (chType == DB_ADDRESSUNSPENTINDEX && indexKey.hashBytes == addressHash) { | |
315 | try { | |
316 | leveldb::Slice slValue = pcursor->value(); | |
317 | CDataStream ssValue(slValue.data(), slValue.data()+slValue.size(), SER_DISK, CLIENT_VERSION); | |
318 | CAddressUnspentValue nValue; | |
319 | ssValue >> nValue; | |
320 | unspentOutputs.push_back(make_pair(indexKey, nValue)); | |
321 | pcursor->Next(); | |
322 | } catch (const std::exception& e) { | |
323 | return error("failed to get address unspent value"); | |
324 | } | |
325 | } else { | |
326 | break; | |
327 | } | |
328 | } catch (const std::exception& e) { | |
329 | break; | |
330 | } | |
331 | } | |
332 | ||
333 | return true; | |
334 | } | |
335 | ||
336 | bool CBlockTreeDB::WriteAddressIndex(const std::vector<std::pair<CAddressIndexKey, CAmount > >&vect) { | |
337 | CLevelDBBatch batch; | |
338 | for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=vect.begin(); it!=vect.end(); it++) | |
339 | batch.Write(make_pair(DB_ADDRESSINDEX, it->first), it->second); | |
340 | return WriteBatch(batch); | |
341 | } | |
342 | ||
343 | bool CBlockTreeDB::EraseAddressIndex(const std::vector<std::pair<CAddressIndexKey, CAmount > >&vect) { | |
344 | CLevelDBBatch batch; | |
345 | for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=vect.begin(); it!=vect.end(); it++) | |
346 | batch.Erase(make_pair(DB_ADDRESSINDEX, it->first)); | |
347 | return WriteBatch(batch); | |
348 | } | |
349 | ||
350 | bool CBlockTreeDB::ReadAddressIndex(uint160 addressHash, int type, | |
351 | std::vector<std::pair<CAddressIndexKey, CAmount> > &addressIndex, | |
352 | int start, int end) { | |
353 | ||
354 | boost::scoped_ptr<leveldb::Iterator> pcursor(NewIterator()); | |
355 | ||
356 | CDataStream ssKeySet(SER_DISK, CLIENT_VERSION); | |
357 | if (start > 0 && end > 0) { | |
358 | ssKeySet << make_pair(DB_ADDRESSINDEX, CAddressIndexIteratorHeightKey(type, addressHash, start)); | |
359 | } else { | |
360 | ssKeySet << make_pair(DB_ADDRESSINDEX, CAddressIndexIteratorKey(type, addressHash)); | |
361 | } | |
362 | pcursor->Seek(ssKeySet.str()); | |
363 | ||
364 | while (pcursor->Valid()) { | |
365 | boost::this_thread::interruption_point(); | |
366 | try { | |
367 | leveldb::Slice slKey = pcursor->key(); | |
368 | CDataStream ssKey(slKey.data(), slKey.data()+slKey.size(), SER_DISK, CLIENT_VERSION); | |
369 | char chType; | |
370 | CAddressIndexKey indexKey; | |
371 | ssKey >> chType; | |
372 | ssKey >> indexKey; | |
373 | if (chType == DB_ADDRESSINDEX && indexKey.hashBytes == addressHash) { | |
374 | if (end > 0 && indexKey.blockHeight > end) { | |
375 | break; | |
376 | } | |
377 | try { | |
378 | leveldb::Slice slValue = pcursor->value(); | |
379 | CDataStream ssValue(slValue.data(), slValue.data()+slValue.size(), SER_DISK, CLIENT_VERSION); | |
380 | CAmount nValue; | |
381 | ssValue >> nValue; | |
382 | ||
383 | addressIndex.push_back(make_pair(indexKey, nValue)); | |
384 | pcursor->Next(); | |
385 | } catch (const std::exception& e) { | |
386 | return error("failed to get address index value"); | |
387 | } | |
388 | } else { | |
389 | break; | |
390 | } | |
391 | } catch (const std::exception& e) { | |
392 | break; | |
393 | } | |
394 | } | |
395 | ||
396 | return true; | |
397 | } | |
398 | ||
399 | bool CBlockTreeDB::WriteTimestampIndex(const CTimestampIndexKey ×tampIndex) { | |
400 | CLevelDBBatch batch; | |
401 | batch.Write(make_pair(DB_TIMESTAMPINDEX, timestampIndex), 0); | |
402 | return WriteBatch(batch); | |
403 | } | |
404 | ||
405 | bool CBlockTreeDB::ReadTimestampIndex(const unsigned int &high, const unsigned int &low, const bool fActiveOnly, std::vector<std::pair<uint256, unsigned int> > &hashes) { | |
406 | ||
407 | boost::scoped_ptr<leveldb::Iterator> pcursor(NewIterator()); | |
408 | ||
409 | CDataStream ssKeySet(SER_DISK, CLIENT_VERSION); | |
410 | ssKeySet << make_pair(DB_TIMESTAMPINDEX, CTimestampIndexIteratorKey(low)); | |
411 | pcursor->Seek(ssKeySet.str()); | |
412 | ||
413 | while (pcursor->Valid()) { | |
414 | boost::this_thread::interruption_point(); | |
415 | try { | |
416 | leveldb::Slice slKey = pcursor->key(); | |
417 | CDataStream ssKey(slKey.data(), slKey.data()+slKey.size(), SER_DISK, CLIENT_VERSION); | |
418 | char chType; | |
419 | CTimestampIndexKey indexKey; | |
420 | ssKey >> chType; | |
421 | ssKey >> indexKey; | |
422 | if (chType == DB_TIMESTAMPINDEX && indexKey.timestamp < high) { | |
423 | if (fActiveOnly) { | |
424 | if (blockOnchainActive(indexKey.blockHash)) { | |
425 | hashes.push_back(std::make_pair(indexKey.blockHash, indexKey.timestamp)); | |
426 | } | |
427 | } else { | |
428 | hashes.push_back(std::make_pair(indexKey.blockHash, indexKey.timestamp)); | |
429 | } | |
430 | ||
431 | pcursor->Next(); | |
432 | } else { | |
433 | break; | |
434 | } | |
435 | } catch (const std::exception& e) { | |
436 | break; | |
437 | } | |
438 | } | |
439 | ||
440 | return true; | |
441 | } | |
442 | ||
443 | bool CBlockTreeDB::WriteTimestampBlockIndex(const CTimestampBlockIndexKey &blockhashIndex, const CTimestampBlockIndexValue &logicalts) { | |
444 | CLevelDBBatch batch; | |
445 | batch.Write(make_pair(DB_BLOCKHASHINDEX, blockhashIndex), logicalts); | |
446 | return WriteBatch(batch); | |
447 | } | |
448 | ||
449 | bool CBlockTreeDB::ReadTimestampBlockIndex(const uint256 &hash, unsigned int <imestamp) { | |
450 | ||
451 | CTimestampBlockIndexValue(lts); | |
452 | if (!Read(std::make_pair(DB_BLOCKHASHINDEX, hash), lts)) | |
453 | return false; | |
454 | ||
455 | ltimestamp = lts.ltimestamp; | |
456 | return true; | |
457 | } | |
458 | ||
2d1fa42e | 459 | bool CBlockTreeDB::WriteFlag(const std::string &name, bool fValue) { |
14d023f1 | 460 | return Write(std::make_pair(DB_FLAG, name), fValue ? '1' : '0'); |
2d1fa42e PW |
461 | } |
462 | ||
463 | bool CBlockTreeDB::ReadFlag(const std::string &name, bool &fValue) { | |
464 | char ch; | |
14d023f1 | 465 | if (!Read(std::make_pair(DB_FLAG, name), ch)) |
2d1fa42e PW |
466 | return false; |
467 | fValue = ch == '1'; | |
468 | return true; | |
469 | } | |
470 | ||
5b39c60e | 471 | void komodo_index2pubkey33(uint8_t *pubkey33,CBlockIndex *pindex,int32_t height); |
ddcdf567 | 472 | |
8b78a819 T |
473 | bool CBlockTreeDB::blockOnchainActive(const uint256 &hash) { |
474 | CBlockIndex* pblockindex = mapBlockIndex[hash]; | |
475 | ||
476 | if (!chainActive.Contains(pblockindex)) { | |
477 | return false; | |
478 | } | |
479 | ||
480 | return true; | |
481 | } | |
482 | ||
2d8a4829 PW |
483 | bool CBlockTreeDB::LoadBlockIndexGuts() |
484 | { | |
5cbda4f1 | 485 | boost::scoped_ptr<leveldb::Iterator> pcursor(NewIterator()); |
2d8a4829 PW |
486 | |
487 | CDataStream ssKeySet(SER_DISK, CLIENT_VERSION); | |
14d023f1 | 488 | ssKeySet << make_pair(DB_BLOCK_INDEX, uint256()); |
2d8a4829 PW |
489 | pcursor->Seek(ssKeySet.str()); |
490 | ||
491 | // Load mapBlockIndex | |
492 | while (pcursor->Valid()) { | |
b31499ec | 493 | boost::this_thread::interruption_point(); |
2d8a4829 PW |
494 | try { |
495 | leveldb::Slice slKey = pcursor->key(); | |
496 | CDataStream ssKey(slKey.data(), slKey.data()+slKey.size(), SER_DISK, CLIENT_VERSION); | |
497 | char chType; | |
498 | ssKey >> chType; | |
14d023f1 | 499 | if (chType == DB_BLOCK_INDEX) { |
2d8a4829 PW |
500 | leveldb::Slice slValue = pcursor->value(); |
501 | CDataStream ssValue(slValue.data(), slValue.data()+slValue.size(), SER_DISK, CLIENT_VERSION); | |
502 | CDiskBlockIndex diskindex; | |
503 | ssValue >> diskindex; | |
504 | ||
505 | // Construct block index object | |
506 | CBlockIndex* pindexNew = InsertBlockIndex(diskindex.GetBlockHash()); | |
507 | pindexNew->pprev = InsertBlockIndex(diskindex.hashPrev); | |
508 | pindexNew->nHeight = diskindex.nHeight; | |
509 | pindexNew->nFile = diskindex.nFile; | |
510 | pindexNew->nDataPos = diskindex.nDataPos; | |
511 | pindexNew->nUndoPos = diskindex.nUndoPos; | |
b6961fc1 | 512 | pindexNew->hashAnchor = diskindex.hashAnchor; |
2d8a4829 | 513 | pindexNew->nVersion = diskindex.nVersion; |
81c5ec4f | 514 | pindexNew->hashReserved = diskindex.hashReserved; |
2d8a4829 | 515 | pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot; |
15fb13f6 | 516 | pindexNew->hashReserved = diskindex.hashReserved; |
2d8a4829 PW |
517 | pindexNew->nTime = diskindex.nTime; |
518 | pindexNew->nBits = diskindex.nBits; | |
519 | pindexNew->nNonce = diskindex.nNonce; | |
fdda3c50 | 520 | pindexNew->nSolution = diskindex.nSolution; |
2d8a4829 | 521 | pindexNew->nStatus = diskindex.nStatus; |
828940b1 | 522 | pindexNew->nCachedBranchId = diskindex.nCachedBranchId; |
2d8a4829 | 523 | pindexNew->nTx = diskindex.nTx; |
ad6a36ad | 524 | pindexNew->nSproutValue = diskindex.nSproutValue; |
9000990c | 525 | |
704b7635 | 526 | // Consistency checks |
44488400 | 527 | auto header = pindexNew->GetBlockHeader(); |
704b7635 JG |
528 | if (header.GetHash() != pindexNew->GetBlockHash()) |
529 | return error("LoadBlockIndex(): block header inconsistency detected: on-disk = %s, in-memory = %s", | |
9000990c | 530 | diskindex.ToString(), pindexNew->ToString()); |
103fde35 | 531 | if ( 0 ) // POW will be checked before any block is connected |
532 | { | |
533 | uint8_t pubkey33[33]; | |
534 | komodo_index2pubkey33(pubkey33,pindexNew,pindexNew->nHeight); | |
1f722359 | 535 | if (!CheckProofOfWork(header,pubkey33,pindexNew->nHeight,Params().GetConsensus())) |
103fde35 | 536 | return error("LoadBlockIndex(): CheckProofOfWork failed: %s", pindexNew->ToString()); |
537 | } | |
2d8a4829 PW |
538 | pcursor->Next(); |
539 | } else { | |
540 | break; // if shutdown requested or finished loading block index | |
541 | } | |
27df4123 | 542 | } catch (const std::exception& e) { |
5262fde0 | 543 | return error("%s: Deserialize or I/O error - %s", __func__, e.what()); |
2d8a4829 PW |
544 | } |
545 | } | |
2d8a4829 PW |
546 | |
547 | return true; | |
548 | } |