Line data Source code
1 : // Copyright (c) 2009-2010 Satoshi Nakamoto
2 : // Copyright (c) 2009-2014 The Bitcoin developers
3 : // Copyright (c) 2016-2022 The PIVX Core developers
4 : // Distributed under the MIT/X11 software license, see the accompanying
5 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
6 :
7 : #include "txdb.h"
8 :
9 : #include "clientversion.h"
10 : #include "pow.h"
11 : #include "random.h"
12 : #include "uint256.h"
13 : #include "util/system.h"
14 : #include "util/vector.h"
15 :
16 : #include <stdint.h>
17 :
18 : #include <boost/thread.hpp>
19 :
20 : static const char DB_COIN = 'C';
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';
25 :
26 : static const char DB_BEST_BLOCK = 'B';
27 : static const char DB_HEAD_BLOCKS = 'H';
28 : static const char DB_FLAG = 'F';
29 : static const char DB_REINDEX_FLAG = 'R';
30 : static const char DB_LAST_BLOCK = 'l';
31 : // static const char DB_MONEY_SUPPLY = 'M';
32 :
33 : namespace {
34 :
35 : struct CoinEntry
36 : {
37 : COutPoint* outpoint;
38 : char key;
39 8869026 : explicit CoinEntry(const COutPoint* ptr) : outpoint(const_cast<COutPoint*>(ptr)), key(DB_COIN) {}
40 :
41 17738052 : SERIALIZE_METHODS(CoinEntry, obj) { READWRITE(obj.key, obj.outpoint->hash, VARINT(obj.outpoint->n)); }
42 : };
43 :
44 : }
45 :
46 :
47 950 : CCoinsViewDB::CCoinsViewDB(size_t nCacheSize, bool fMemory, bool fWipe) : db(GetDataDir() / "chainstate", nCacheSize, fMemory, fWipe)
48 : {
49 475 : }
50 :
51 1473886 : bool CCoinsViewDB::GetCoin(const COutPoint& outpoint, Coin& coin) const
52 : {
53 1473886 : return db.Read(CoinEntry(&outpoint), coin);
54 : }
55 :
56 0 : bool CCoinsViewDB::HaveCoin(const COutPoint& outpoint) const
57 : {
58 0 : return db.Exists(CoinEntry(&outpoint));
59 : }
60 :
61 2450 : uint256 CCoinsViewDB::GetBestBlock() const
62 : {
63 2450 : uint256 hashBestChain;
64 2450 : if (!db.Read(DB_BEST_BLOCK, hashBestChain))
65 1227 : return UINT256_ZERO;
66 1223 : return hashBestChain;
67 : }
68 :
69 562 : std::vector<uint256> CCoinsViewDB::GetHeadBlocks() const {
70 1124 : std::vector<uint256> vhashHeadBlocks;
71 562 : if (!db.Read(DB_HEAD_BLOCKS, vhashHeadBlocks)) {
72 556 : return std::vector<uint256>();
73 : }
74 562 : return vhashHeadBlocks;
75 : }
76 :
77 810 : bool CCoinsViewDB::BatchWrite(CCoinsMap& mapCoins,
78 : const uint256& hashBlock,
79 : const uint256& hashSaplingAnchor,
80 : CAnchorsSaplingMap& mapSaplingAnchors,
81 : CNullifiersMap& mapSaplingNullifiers)
82 : {
83 810 : CDBBatch batch(CLIENT_VERSION);
84 810 : size_t count = 0;
85 810 : size_t changed = 0;
86 810 : size_t batch_size = (size_t) gArgs.GetArg("-dbbatchsize", nDefaultDbBatchSize);
87 810 : int crash_simulate = gArgs.GetArg("-dbcrashratio", 0);
88 1620 : assert(!hashBlock.IsNull());
89 :
90 810 : uint256 old_tip = GetBestBlock();
91 7975 : if (old_tip.IsNull()) {
92 : // We may be in the middle of replaying.
93 410 : std::vector<uint256> old_heads = GetHeadBlocks();
94 205 : if (old_heads.size() == 2) {
95 3 : assert(old_heads[0] == hashBlock);
96 3 : old_tip = old_heads[1];
97 : }
98 : }
99 :
100 : // In the first batch, mark the database as being in the middle of a
101 : // transition from old_tip to hashBlock.
102 : // A vector is used for future extensibility, as we may want to support
103 : // interrupting after partial writes from multiple independent reorgs.
104 810 : batch.Erase(DB_BEST_BLOCK);
105 810 : batch.Write(DB_HEAD_BLOCKS, Vector(hashBlock, old_tip));
106 :
107 1025186 : for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) {
108 1024373 : if (it->second.flags & CCoinsCacheEntry::DIRTY) {
109 1023753 : CoinEntry entry(&it->first);
110 1023753 : if (it->second.coin.IsSpent())
111 328616 : batch.Erase(entry);
112 : else
113 695135 : batch.Write(entry, it->second.coin);
114 1023753 : changed++;
115 : }
116 1024373 : count++;
117 1024373 : CCoinsMap::iterator itOld = it++;
118 1024373 : mapCoins.erase(itOld);
119 1024373 : if (batch.SizeEstimate() > batch_size) {
120 75 : LogPrint(BCLog::COINDB, "Writing partial batch of %.2f MiB\n", batch.SizeEstimate() * (1.0 / 1048576.0));
121 75 : db.WriteBatch(batch);
122 75 : batch.Clear();
123 75 : if (crash_simulate) {
124 75 : static FastRandomContext rng;
125 150 : if (rng.randrange(crash_simulate) == 0) {
126 0 : LogPrintf("Simulating a crash. Goodbye.\n");
127 0 : _Exit(0);
128 : }
129 : }
130 : }
131 : }
132 :
133 : // Write Sapling
134 810 : BatchWriteSapling(hashSaplingAnchor, mapSaplingAnchors, mapSaplingNullifiers, batch);
135 :
136 : // In the last batch, mark the database as consistent with hashBlock again.
137 810 : batch.Erase(DB_HEAD_BLOCKS);
138 810 : batch.Write(DB_BEST_BLOCK, hashBlock);
139 :
140 810 : LogPrint(BCLog::COINDB, "Writing final batch of %.2f MiB\n", batch.SizeEstimate() * (1.0 / 1048576.0));
141 810 : bool ret = db.WriteBatch(batch);
142 810 : LogPrint(BCLog::COINDB, "Committed %u changed transaction outputs (out of %u) to coin database...\n", (unsigned int)changed, (unsigned int)count);
143 810 : return ret;
144 : }
145 :
146 49 : size_t CCoinsViewDB::EstimateSize() const
147 : {
148 49 : return db.EstimateSize(DB_COIN, (char)(DB_COIN+1));
149 : }
150 :
151 1425 : CBlockTreeDB::CBlockTreeDB(size_t nCacheSize, bool fMemory, bool fWipe) : CDBWrapper(GetDataDir() / "blocks" / "index", nCacheSize, fMemory, fWipe)
152 : {
153 475 : }
154 :
155 0 : bool CBlockTreeDB::WriteBlockIndex(const CDiskBlockIndex& blockindex)
156 : {
157 0 : return Write(std::make_pair(DB_BLOCK_INDEX, blockindex.GetBlockHash()), blockindex);
158 : }
159 :
160 704 : bool CBlockTreeDB::ReadBlockFileInfo(int nFile, CBlockFileInfo& info)
161 : {
162 704 : return Read(std::make_pair(DB_BLOCK_FILES, nFile), info);
163 : }
164 :
165 10 : bool CBlockTreeDB::WriteReindexing(bool fReindexing)
166 : {
167 10 : if (fReindexing)
168 5 : return Write(DB_REINDEX_FLAG, '1');
169 : else
170 5 : return Erase(DB_REINDEX_FLAG);
171 : }
172 :
173 352 : bool CBlockTreeDB::ReadReindexing(bool& fReindexing)
174 : {
175 352 : fReindexing = Exists(DB_REINDEX_FLAG);
176 352 : return true;
177 : }
178 :
179 352 : bool CBlockTreeDB::ReadLastBlockFile(int& nFile)
180 : {
181 352 : return Read(DB_LAST_BLOCK, nFile);
182 : }
183 :
184 518 : CCoinsViewCursor *CCoinsViewDB::Cursor() const
185 : {
186 518 : CCoinsViewDBCursor *i = new CCoinsViewDBCursor(const_cast<CDBWrapper&>(db).NewIterator(), GetBestBlock());
187 : /* It seems that there are no "const iterators" for LevelDB. Since we
188 : only need read operations on it, use a const-cast to get around
189 : that restriction. */
190 518 : i->pcursor->Seek(DB_COIN);
191 : // Cache key of first record
192 : // Cache key of first record
193 518 : if (i->pcursor->Valid()) {
194 306 : CoinEntry entry(&i->keyTmp.second);
195 306 : i->pcursor->GetKey(entry);
196 306 : i->keyTmp.first = entry.key;
197 : } else {
198 212 : i->keyTmp.first = 0; // Make sure Valid() and GetKey() return false
199 : }
200 518 : return i;
201 : }
202 :
203 2791730 : bool CCoinsViewDBCursor::GetKey(COutPoint &key) const
204 : {
205 : // Return cached key
206 2791730 : if (keyTmp.first == DB_COIN) {
207 2791730 : key = keyTmp.second;
208 2791730 : return true;
209 : }
210 : return false;
211 : }
212 :
213 6371084 : bool CCoinsViewDBCursor::GetValue(Coin& coin) const
214 : {
215 6371084 : return pcursor->GetValue(coin);
216 : }
217 :
218 0 : unsigned int CCoinsViewDBCursor::GetValueSize() const
219 : {
220 0 : return pcursor->GetValueSize();
221 : }
222 :
223 6371597 : bool CCoinsViewDBCursor::Valid() const
224 : {
225 6371597 : return keyTmp.first == DB_COIN;
226 : }
227 :
228 6371084 : void CCoinsViewDBCursor::Next()
229 : {
230 6371084 : pcursor->Next();
231 6371084 : CoinEntry entry(&keyTmp.second);
232 6371084 : if (!pcursor->Valid() || !pcursor->GetKey(entry)) {
233 306 : keyTmp.first = 0; // Invalidate cached key after last record so that Valid() and GetKey() return false
234 : } else {
235 6370771 : keyTmp.first = entry.key;
236 : }
237 6371084 : }
238 :
239 807 : bool CBlockTreeDB::WriteBatchSync(const std::vector<std::pair<int, const CBlockFileInfo*> >& fileInfo, int nLastFile, const std::vector<const CBlockIndex*>& blockinfo) {
240 1614 : CDBBatch batch(CLIENT_VERSION);
241 1136 : for (std::vector<std::pair<int, const CBlockFileInfo*> >::const_iterator it=fileInfo.begin(); it != fileInfo.end(); it++) {
242 329 : batch.Write(std::make_pair(DB_BLOCK_FILES, it->first), *it->second);
243 : }
244 807 : batch.Write(DB_LAST_BLOCK, nLastFile);
245 40043 : for (std::vector<const CBlockIndex*>::const_iterator it=blockinfo.begin(); it != blockinfo.end(); it++) {
246 78272 : batch.Write(std::make_pair(DB_BLOCK_INDEX, (*it)->GetBlockHash()), CDiskBlockIndex(*it));
247 : }
248 1614 : return WriteBatch(batch, true);
249 : }
250 :
251 204081 : bool CBlockTreeDB::ReadTxIndex(const uint256& txid, CDiskTxPos& pos)
252 : {
253 204081 : return Read(std::make_pair(DB_TXINDEX, txid), pos);
254 : }
255 :
256 41250 : bool CBlockTreeDB::WriteTxIndex(const std::vector<std::pair<uint256, CDiskTxPos> >& vect)
257 : {
258 82500 : CDBBatch batch(CLIENT_VERSION);
259 341502 : for (std::vector<std::pair<uint256, CDiskTxPos> >::const_iterator it = vect.begin(); it != vect.end(); it++)
260 300252 : batch.Write(std::make_pair(DB_TXINDEX, it->first), it->second);
261 82500 : return WriteBatch(batch);
262 : }
263 :
264 907 : bool CBlockTreeDB::WriteFlag(const std::string& name, bool fValue)
265 : {
266 1259 : return Write(std::make_pair(DB_FLAG, name), fValue ? '1' : '0');
267 : }
268 :
269 704 : bool CBlockTreeDB::ReadFlag(const std::string& name, bool& fValue)
270 : {
271 704 : char ch;
272 704 : if (!Read(std::make_pair(DB_FLAG, name), ch))
273 : return false;
274 318 : fValue = ch == '1';
275 318 : return true;
276 : }
277 :
278 0 : bool CBlockTreeDB::WriteInt(const std::string& name, int nValue)
279 : {
280 0 : return Write(std::make_pair('I', name), nValue);
281 : }
282 :
283 0 : bool CBlockTreeDB::ReadInt(const std::string& name, int& nValue)
284 : {
285 0 : return Read(std::make_pair('I', name), nValue);
286 : }
287 :
288 352 : bool CBlockTreeDB::LoadBlockIndexGuts(std::function<CBlockIndex*(const uint256&)> insertBlockIndex)
289 : {
290 704 : std::unique_ptr<CDBIterator> pcursor(NewIterator());
291 :
292 352 : pcursor->Seek(std::make_pair(DB_BLOCK_INDEX, UINT256_ZERO));
293 :
294 : // Load mapBlockIndex
295 33158 : while (pcursor->Valid()) {
296 32965 : boost::this_thread::interruption_point();
297 32965 : std::pair<char, uint256> key;
298 32965 : if (pcursor->GetKey(key) && key.first == DB_BLOCK_INDEX) {
299 65612 : CDiskBlockIndex diskindex;
300 32806 : if (pcursor->GetValue(diskindex)) {
301 : // Construct block index object
302 32806 : CBlockIndex* pindexNew = insertBlockIndex(diskindex.GetBlockHash());
303 32806 : pindexNew->pprev = insertBlockIndex(diskindex.hashPrev);
304 32806 : pindexNew->nHeight = diskindex.nHeight;
305 32806 : pindexNew->nFile = diskindex.nFile;
306 32806 : pindexNew->nDataPos = diskindex.nDataPos;
307 32806 : pindexNew->nUndoPos = diskindex.nUndoPos;
308 32806 : pindexNew->nVersion = diskindex.nVersion;
309 32806 : pindexNew->hashMerkleRoot = diskindex.hashMerkleRoot;
310 32806 : pindexNew->nTime = diskindex.nTime;
311 32806 : pindexNew->nBits = diskindex.nBits;
312 32806 : pindexNew->nNonce = diskindex.nNonce;
313 32806 : pindexNew->nStatus = diskindex.nStatus;
314 32806 : pindexNew->nTx = diskindex.nTx;
315 :
316 : // sapling
317 32806 : pindexNew->nSaplingValue = diskindex.nSaplingValue;
318 32806 : pindexNew->hashFinalSaplingRoot = diskindex.hashFinalSaplingRoot;
319 :
320 : //zerocoin
321 32806 : pindexNew->nAccumulatorCheckpoint = diskindex.nAccumulatorCheckpoint;
322 :
323 : //Proof Of Stake
324 32806 : pindexNew->nFlags = diskindex.nFlags;
325 32806 : pindexNew->vStakeModifier = diskindex.vStakeModifier;
326 :
327 32806 : if (!Params().GetConsensus().NetworkUpgradeActive(pindexNew->nHeight, Consensus::UPGRADE_POS)) {
328 32651 : if (!CheckProofOfWork(pindexNew->GetBlockHash(), pindexNew->nBits))
329 0 : return error("%s : CheckProofOfWork failed: %s", __func__, pindexNew->ToString());
330 : }
331 :
332 32806 : pcursor->Next();
333 : } else {
334 0 : return error("%s : failed to read value", __func__);
335 : }
336 : } else {
337 : break;
338 : }
339 : }
340 :
341 : return true;
342 : }
343 :
344 950 : CZerocoinDB::CZerocoinDB(size_t nCacheSize, bool fMemory, bool fWipe) : CDBWrapper(GetDataDir() / "zerocoin", nCacheSize, fMemory, fWipe)
345 : {
346 475 : }
347 :
348 0 : bool CZerocoinDB::WriteCoinSpendBatch(const std::vector<std::pair<CBigNum, uint256> >& spendInfo)
349 : {
350 0 : CDBBatch batch(CLIENT_VERSION);
351 0 : size_t count = 0;
352 0 : for (std::vector<std::pair<CBigNum, uint256> >::const_iterator it=spendInfo.begin(); it != spendInfo.end(); it++) {
353 0 : CBigNum bnSerial = it->first;
354 0 : CDataStream ss(SER_GETHASH, 0);
355 0 : ss << bnSerial;
356 0 : uint256 hash = Hash(ss.begin(), ss.end());
357 0 : batch.Write(std::make_pair('s', hash), it->second);
358 0 : ++count;
359 : }
360 :
361 0 : LogPrint(BCLog::COINDB, "Writing %u coin spends to db.\n", (unsigned int)count);
362 0 : return WriteBatch(batch, true);
363 : }
364 :
365 0 : bool CZerocoinDB::ReadCoinSpend(const CBigNum& bnSerial, uint256& txHash)
366 : {
367 0 : CDataStream ss(SER_GETHASH, 0);
368 0 : ss << bnSerial;
369 0 : uint256 hash = Hash(ss.begin(), ss.end());
370 :
371 0 : return Read(std::make_pair('s', hash), txHash);
372 : }
373 :
374 0 : bool CZerocoinDB::EraseCoinSpend(const CBigNum& bnSerial)
375 : {
376 0 : CDataStream ss(SER_GETHASH, 0);
377 0 : ss << bnSerial;
378 0 : uint256 hash = Hash(ss.begin(), ss.end());
379 :
380 0 : return Erase(std::make_pair('s', hash));
381 : }
382 :
383 : // Legacy Zerocoin Database
384 : static const char LZC_ACCUMCS = 'A';
385 : //static const char LZC_MAPSUPPLY = 'M'; // TODO: add removal for LZC_MAPSUPPLY key-value if is found in db
386 :
387 160 : bool CZerocoinDB::WriteAccChecksum(const uint32_t nChecksum, const libzerocoin::CoinDenomination denom, const int nHeight)
388 : {
389 160 : return Write(std::make_pair(LZC_ACCUMCS, std::make_pair(nChecksum, denom)), nHeight);
390 : }
391 :
392 0 : bool CZerocoinDB::ReadAccChecksum(const uint32_t nChecksum, const libzerocoin::CoinDenomination denom, int& nHeightRet)
393 : {
394 0 : return Read(std::make_pair(LZC_ACCUMCS, std::make_pair(nChecksum, denom)), nHeightRet);
395 : }
396 :
397 0 : bool CZerocoinDB::EraseAccChecksum(const uint32_t nChecksum, const libzerocoin::CoinDenomination denom)
398 : {
399 0 : return Erase(std::make_pair(LZC_ACCUMCS, std::make_pair(nChecksum, denom)));
400 : }
401 :
402 359 : bool CZerocoinDB::ReadAll(std::map<std::pair<uint32_t, libzerocoin::CoinDenomination>, int>& mapCheckpoints)
403 : {
404 718 : std::unique_ptr<CDBIterator> pcursor(NewIterator());
405 359 : pcursor->Seek(std::make_pair(LZC_ACCUMCS, std::make_pair((uint32_t) 0, libzerocoin::CoinDenomination::ZQ_ERROR)));
406 519 : while (pcursor->Valid()) {
407 160 : boost::this_thread::interruption_point();
408 160 : std::pair<char, std::pair<uint32_t, libzerocoin::CoinDenomination>> key;
409 160 : if (pcursor->GetKey(key) && key.first == LZC_ACCUMCS) {
410 160 : int height;
411 160 : if (pcursor->GetValue(height)) {
412 160 : mapCheckpoints[key.second] = height;
413 160 : pcursor->Next();
414 : } else {
415 0 : return error("%s : failed to read value", __func__);
416 : }
417 : } else {
418 : break;
419 : }
420 : }
421 :
422 359 : LogPrintf("%s: Total acc checksum records: %d\n", __func__, mapCheckpoints.size());
423 359 : return true;
424 : }
425 :
426 20 : void CZerocoinDB::WipeAccChecksums()
427 : {
428 20 : std::unique_ptr<CDBIterator> pcursor(NewIterator());
429 20 : pcursor->Seek(std::make_pair(LZC_ACCUMCS, std::make_pair((uint32_t) 0, libzerocoin::CoinDenomination::ZQ_ERROR)));
430 40 : std::set<std::pair<char, std::pair<uint32_t, libzerocoin::CoinDenomination>>> setDelete;
431 180 : while (pcursor->Valid()) {
432 160 : boost::this_thread::interruption_point();
433 160 : std::pair<char, std::pair<uint32_t, libzerocoin::CoinDenomination>> key;
434 160 : if (pcursor->GetKey(key) && key.first == LZC_ACCUMCS) {
435 160 : setDelete.insert(key);
436 : } else {
437 : break;
438 : }
439 160 : pcursor->Next();
440 : }
441 :
442 20 : int deleted = 0;
443 180 : for (const auto& k : setDelete) {
444 160 : if (!Erase(k)) {
445 0 : LogPrintf("%s: failed to delete acc checksum %d-%d\n", __func__, k.second.first, k.second.second);
446 : } else {
447 160 : deleted++;
448 : }
449 : }
450 :
451 20 : LogPrintf("%s: %d entries to delete. %d entries deleted\n", __func__, setDelete.size(), deleted);
452 20 : }
453 :
454 : namespace {
455 :
456 : //! Legacy class to deserialize pre-pertxout database entries without reindex.
457 0 : class CCoins
458 : {
459 : public:
460 : //! whether transaction is a coinbase
461 : bool fCoinBase;
462 : bool fCoinStake;
463 :
464 : //! unspent transaction outputs; spent outputs are .IsNull(); spent outputs at the end of the array are dropped
465 : std::vector<CTxOut> vout;
466 :
467 : //! at which height this transaction was included in the active block chain
468 : int nHeight;
469 :
470 : template <typename Stream>
471 0 : void Unserialize(Stream& s)
472 : {
473 0 : unsigned int nCode = 0;
474 : // version
475 : unsigned int nVersionDummy;
476 0 : ::Unserialize(s, VARINT(nVersionDummy));
477 : // header code
478 0 : ::Unserialize(s, VARINT(nCode));
479 0 : fCoinBase = nCode & 1; //0001 - means coinbase
480 0 : fCoinStake = (nCode & 2) != 0; //0010 coinstake
481 0 : std::vector<bool> vAvail(2, false);
482 0 : vAvail[0] = (nCode & 4) != 0; // 0100
483 0 : vAvail[1] = (nCode & 8) != 0; // 1000
484 0 : unsigned int nMaskCode = (nCode / 16) + ((nCode & 12) != 0 ? 0 : 1);
485 : // spentness bitmask
486 0 : while (nMaskCode > 0) {
487 0 : unsigned char chAvail = 0;
488 0 : ::Unserialize(s, chAvail);
489 0 : for (unsigned int p = 0; p < 8; p++) {
490 0 : bool f = (chAvail & (1 << p)) != 0;
491 0 : vAvail.push_back(f);
492 : }
493 0 : if (chAvail != 0)
494 0 : nMaskCode--;
495 : }
496 : // txouts themself
497 0 : vout.assign(vAvail.size(), CTxOut());
498 0 : for (unsigned int i = 0; i < vAvail.size(); i++) {
499 0 : if (vAvail[i])
500 0 : ::Unserialize(s, Using<TxOutCompression>(vout[i]));
501 : }
502 : // coinbase height
503 0 : ::Unserialize(s, VARINT_MODE(nHeight, VarIntMode::NONNEGATIVE_SIGNED));
504 0 : }
505 : };
506 :
507 : }
508 :
509 : /** Upgrade the database from older formats.
510 : *
511 : * Currently implemented:
512 : * - from the per-tx utxo model (4.2.0) to per-txout (4.2.99)
513 : */
514 357 : bool CCoinsViewDB::Upgrade() {
515 714 : std::unique_ptr<CDBIterator> pcursor(db.NewIterator());
516 714 : pcursor->Seek(std::make_pair(DB_COINS, uint256()));
517 357 : if (!pcursor->Valid()) {
518 : return true;
519 : }
520 :
521 137 : LogPrintf("Upgrading database...\n");
522 137 : size_t batch_size = 1 << 24;
523 274 : CDBBatch batch(CLIENT_VERSION);
524 137 : while (pcursor->Valid()) {
525 137 : boost::this_thread::interruption_point();
526 137 : std::pair<unsigned char, uint256> key;
527 137 : if (pcursor->GetKey(key) && key.first == DB_COINS) {
528 0 : CCoins old_coins;
529 0 : if (!pcursor->GetValue(old_coins)) {
530 0 : return error("%s: cannot parse CCoins record", __func__);
531 : }
532 0 : COutPoint outpoint(key.second, 0);
533 0 : for (size_t i = 0; i < old_coins.vout.size(); ++i) {
534 0 : if (!old_coins.vout[i].IsNull() && !old_coins.vout[i].scriptPubKey.IsUnspendable()) {
535 0 : Coin newcoin(std::move(old_coins.vout[i]), old_coins.nHeight, old_coins.fCoinBase, old_coins.fCoinStake);
536 0 : outpoint.n = i;
537 0 : CoinEntry entry(&outpoint);
538 0 : batch.Write(entry, newcoin);
539 : }
540 : }
541 0 : batch.Erase(key);
542 0 : if (batch.SizeEstimate() > batch_size) {
543 0 : db.WriteBatch(batch);
544 0 : batch.Clear();
545 : }
546 0 : pcursor->Next();
547 : } else {
548 : break;
549 : }
550 : }
551 137 : db.WriteBatch(batch);
552 : return true;
553 : }
554 :
555 0 : Optional<int> AccumulatorCache::Get(uint32_t checksum, libzerocoin::CoinDenomination denom)
556 : {
557 0 : const auto& p = std::make_pair(checksum, denom);
558 :
559 : // First check the map in-memory.
560 0 : const auto it = mapCheckpoints.find(p);
561 0 : if (it != mapCheckpoints.end()) {
562 0 : return Optional<int>(it->second);
563 : }
564 :
565 : // Not found. Check disk.
566 0 : int checksum_height = 0;
567 0 : if (db->ReadAccChecksum(checksum, denom, checksum_height)) {
568 : // save in memory and return
569 0 : mapCheckpoints[p] = checksum_height;
570 0 : return Optional<int>(checksum_height);
571 : }
572 :
573 : // Not found. Scan the chain.
574 0 : return nullopt;
575 : }
576 :
577 0 : void AccumulatorCache::Set(uint32_t checksum, libzerocoin::CoinDenomination denom, int height)
578 : {
579 : // Update memory cache
580 0 : mapCheckpoints[std::make_pair(checksum, denom)] = height;
581 0 : }
582 :
583 0 : void AccumulatorCache::Erase(uint32_t checksum, libzerocoin::CoinDenomination denom)
584 : {
585 : // Update memory cache and database
586 0 : mapCheckpoints.erase(std::make_pair(checksum, denom));
587 0 : db->EraseAccChecksum(checksum, denom);
588 0 : }
589 :
590 718 : void AccumulatorCache::Flush()
591 : {
592 718 : for (const auto& it : mapCheckpoints) {
593 : // Write to disk
594 0 : db->WriteAccChecksum(it.first.first, it.first.second, it.second);
595 : }
596 718 : }
597 :
598 19 : void AccumulatorCache::Wipe()
599 : {
600 19 : mapCheckpoints.clear();
601 19 : db->WipeAccChecksums();
602 19 : }
|