]> Git Repo - VerusCoin.git/blame - src/coins.cpp
test
[VerusCoin.git] / src / coins.cpp
CommitLineData
f914f1a7 1// Copyright (c) 2012-2014 The Bitcoin Core developers
fa94b9d5 2// Distributed under the MIT software license, see the accompanying
a0fa20a1
PW
3// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5#include "coins.h"
6
046392dc 7#include "memusage.h"
bc42503f
PW
8#include "random.h"
9
a0fa20a1
PW
10#include <assert.h>
11
fa94b9d5
MF
12/**
13 * calculate number of bytes for the bitmask, and its number of non-zero bytes
14 * each bit in the bitmask represents the availability of one output, but the
15 * availabilities of the first two outputs are encoded separately
16 */
a0fa20a1
PW
17void CCoins::CalcMaskSize(unsigned int &nBytes, unsigned int &nNonzeroBytes) const {
18 unsigned int nLastUsedByte = 0;
19 for (unsigned int b = 0; 2+b*8 < vout.size(); b++) {
20 bool fZero = true;
21 for (unsigned int i = 0; i < 8 && 2+b*8+i < vout.size(); i++) {
22 if (!vout[2+b*8+i].IsNull()) {
23 fZero = false;
24 continue;
25 }
26 }
27 if (!fZero) {
28 nLastUsedByte = b + 1;
29 nNonzeroBytes++;
30 }
31 }
32 nBytes += nLastUsedByte;
33}
34
c444c620 35bool CCoins::Spend(uint32_t nPos)
36{
37 if (nPos >= vout.size() || vout[nPos].IsNull())
a0fa20a1 38 return false;
c444c620 39 vout[nPos].SetNull();
a0fa20a1 40 Cleanup();
a0fa20a1
PW
41 return true;
42}
434f3284 43bool CCoinsView::GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const { return false; }
9e511dbb 44bool CCoinsView::GetNullifier(const uint256 &nullifier) const { return false; }
a3dc587a 45bool CCoinsView::GetCoins(const uint256 &txid, CCoins &coins) const { return false; }
a3dc587a 46bool CCoinsView::HaveCoins(const uint256 &txid) const { return false; }
4f152496 47uint256 CCoinsView::GetBestBlock() const { return uint256(); }
9f25631d
SB
48uint256 CCoinsView::GetBestAnchor() const { return uint256(); };
49bool CCoinsView::BatchWrite(CCoinsMap &mapCoins,
50 const uint256 &hashBlock,
51 const uint256 &hashAnchor,
45d6bee9 52 CAnchorsMap &mapAnchors,
bb64be52 53 CNullifiersMap &mapNullifiers) { return false; }
a3dc587a 54bool CCoinsView::GetStats(CCoinsStats &stats) const { return false; }
a0fa20a1
PW
55
56
7c70438d 57CCoinsViewBacked::CCoinsViewBacked(CCoinsView *viewIn) : base(viewIn) { }
9f25631d 58
434f3284 59bool CCoinsViewBacked::GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const { return base->GetAnchorAt(rt, tree); }
9e511dbb 60bool CCoinsViewBacked::GetNullifier(const uint256 &nullifier) const { return base->GetNullifier(nullifier); }
a3dc587a 61bool CCoinsViewBacked::GetCoins(const uint256 &txid, CCoins &coins) const { return base->GetCoins(txid, coins); }
a3dc587a
DK
62bool CCoinsViewBacked::HaveCoins(const uint256 &txid) const { return base->HaveCoins(txid); }
63uint256 CCoinsViewBacked::GetBestBlock() const { return base->GetBestBlock(); }
9f25631d 64uint256 CCoinsViewBacked::GetBestAnchor() const { return base->GetBestAnchor(); }
a0fa20a1 65void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; }
9f25631d
SB
66bool CCoinsViewBacked::BatchWrite(CCoinsMap &mapCoins,
67 const uint256 &hashBlock,
68 const uint256 &hashAnchor,
45d6bee9 69 CAnchorsMap &mapAnchors,
bb64be52 70 CNullifiersMap &mapNullifiers) { return base->BatchWrite(mapCoins, hashBlock, hashAnchor, mapAnchors, mapNullifiers); }
a3dc587a 71bool CCoinsViewBacked::GetStats(CCoinsStats &stats) const { return base->GetStats(stats); }
a0fa20a1 72
bc42503f
PW
73CCoinsKeyHasher::CCoinsKeyHasher() : salt(GetRandHash()) {}
74
046392dc 75CCoinsViewCache::CCoinsViewCache(CCoinsView *baseIn) : CCoinsViewBacked(baseIn), hasModifier(false), cachedCoinsUsage(0) { }
a0fa20a1 76
f28aec01
PW
77CCoinsViewCache::~CCoinsViewCache()
78{
79 assert(!hasModifier);
a0fa20a1
PW
80}
81
046392dc 82size_t CCoinsViewCache::DynamicMemoryUsage() const {
7f3c7a68
SB
83 return memusage::DynamicUsage(cacheCoins) +
84 memusage::DynamicUsage(cacheAnchors) +
1d184d53 85 memusage::DynamicUsage(cacheNullifiers) +
7f3c7a68 86 cachedCoinsUsage;
046392dc
PW
87}
88
f28aec01 89CCoinsMap::const_iterator CCoinsViewCache::FetchCoins(const uint256 &txid) const {
bc42503f
PW
90 CCoinsMap::iterator it = cacheCoins.find(txid);
91 if (it != cacheCoins.end())
a0fa20a1
PW
92 return it;
93 CCoins tmp;
058b08c1 94 if (!base->GetCoins(txid, tmp))
a0fa20a1 95 return cacheCoins.end();
058b08c1
PW
96 CCoinsMap::iterator ret = cacheCoins.insert(std::make_pair(txid, CCoinsCacheEntry())).first;
97 tmp.swap(ret->second.coins);
98 if (ret->second.coins.IsPruned()) {
99 // The parent only has an empty entry for this txid; we can consider our
100 // version as fresh.
101 ret->second.flags = CCoinsCacheEntry::FRESH;
102 }
046392dc 103 cachedCoinsUsage += memusage::DynamicUsage(ret->second.coins);
a0fa20a1
PW
104 return ret;
105}
106
9f25631d 107
434f3284 108bool CCoinsViewCache::GetAnchorAt(const uint256 &rt, ZCIncrementalMerkleTree &tree) const {
9f25631d
SB
109 CAnchorsMap::const_iterator it = cacheAnchors.find(rt);
110 if (it != cacheAnchors.end()) {
111 if (it->second.entered) {
434f3284 112 tree = it->second.tree;
9f25631d
SB
113 return true;
114 } else {
115 return false;
116 }
117 }
118
9f25631d
SB
119 if (!base->GetAnchorAt(rt, tree)) {
120 return false;
121 }
122
cf471983
SB
123 CAnchorsMap::iterator ret = cacheAnchors.insert(std::make_pair(rt, CAnchorsCacheEntry())).first;
124 ret->second.entered = true;
434f3284 125 ret->second.tree = tree;
7f3c7a68 126 cachedCoinsUsage += memusage::DynamicUsage(ret->second.tree);
9f25631d
SB
127
128 return true;
129}
130
9e511dbb
SB
131bool CCoinsViewCache::GetNullifier(const uint256 &nullifier) const {
132 CNullifiersMap::iterator it = cacheNullifiers.find(nullifier);
1d184d53 133 if (it != cacheNullifiers.end())
45d6bee9
SB
134 return it->second.entered;
135
9e511dbb
SB
136 CNullifiersCacheEntry entry;
137 bool tmp = base->GetNullifier(nullifier);
45d6bee9
SB
138 entry.entered = tmp;
139
9e511dbb 140 cacheNullifiers.insert(std::make_pair(nullifier, entry));
45d6bee9 141
45d6bee9
SB
142 return tmp;
143}
9f25631d 144
434f3284
SB
145void CCoinsViewCache::PushAnchor(const ZCIncrementalMerkleTree &tree) {
146 uint256 newrt = tree.root();
9f25631d
SB
147
148 auto currentRoot = GetBestAnchor();
149
150 // We don't want to overwrite an anchor we already have.
151 // This occurs when a block doesn't modify mapAnchors at all,
b7e4abd6 152 // because there are no joinsplits. We could get around this a
9f25631d
SB
153 // different way (make all blocks modify mapAnchors somehow)
154 // but this is simpler to reason about.
155 if (currentRoot != newrt) {
7f3c7a68
SB
156 auto insertRet = cacheAnchors.insert(std::make_pair(newrt, CAnchorsCacheEntry()));
157 CAnchorsMap::iterator ret = insertRet.first;
9f25631d
SB
158
159 ret->second.entered = true;
434f3284 160 ret->second.tree = tree;
9f25631d
SB
161 ret->second.flags = CAnchorsCacheEntry::DIRTY;
162
7f3c7a68
SB
163 if (insertRet.second) {
164 // An insert took place
165 cachedCoinsUsage += memusage::DynamicUsage(ret->second.tree);
166 }
167
9f25631d
SB
168 hashAnchor = newrt;
169 }
170}
171
172void CCoinsViewCache::PopAnchor(const uint256 &newrt) {
173 auto currentRoot = GetBestAnchor();
174
175 // Blocks might not change the commitment tree, in which
176 // case restoring the "old" anchor during a reorg must
177 // have no effect.
178 if (currentRoot != newrt) {
179 CAnchorsMap::iterator ret = cacheAnchors.insert(std::make_pair(currentRoot, CAnchorsCacheEntry())).first;
180
181 ret->second.entered = false;
182 ret->second.flags = CAnchorsCacheEntry::DIRTY;
183
184 hashAnchor = newrt;
185 }
186}
187
9e511dbb
SB
188void CCoinsViewCache::SetNullifier(const uint256 &nullifier, bool spent) {
189 std::pair<CNullifiersMap::iterator, bool> ret = cacheNullifiers.insert(std::make_pair(nullifier, CNullifiersCacheEntry()));
45d6bee9 190 ret.first->second.entered = spent;
9e511dbb 191 ret.first->second.flags |= CNullifiersCacheEntry::DIRTY;
45d6bee9
SB
192}
193
058b08c1
PW
194bool CCoinsViewCache::GetCoins(const uint256 &txid, CCoins &coins) const {
195 CCoinsMap::const_iterator it = FetchCoins(txid);
196 if (it != cacheCoins.end()) {
197 coins = it->second.coins;
198 return true;
199 }
200 return false;
a3dc587a
DK
201}
202
f28aec01
PW
203CCoinsModifier CCoinsViewCache::ModifyCoins(const uint256 &txid) {
204 assert(!hasModifier);
058b08c1 205 std::pair<CCoinsMap::iterator, bool> ret = cacheCoins.insert(std::make_pair(txid, CCoinsCacheEntry()));
046392dc 206 size_t cachedCoinUsage = 0;
f28aec01 207 if (ret.second) {
058b08c1
PW
208 if (!base->GetCoins(txid, ret.first->second.coins)) {
209 // The parent view does not have this entry; mark it as fresh.
210 ret.first->second.coins.Clear();
211 ret.first->second.flags = CCoinsCacheEntry::FRESH;
212 } else if (ret.first->second.coins.IsPruned()) {
213 // The parent view only has a pruned entry for this; mark it as fresh.
214 ret.first->second.flags = CCoinsCacheEntry::FRESH;
215 }
046392dc
PW
216 } else {
217 cachedCoinUsage = memusage::DynamicUsage(ret.first->second.coins);
f28aec01 218 }
058b08c1
PW
219 // Assume that whenever ModifyCoins is called, the entry will be modified.
220 ret.first->second.flags |= CCoinsCacheEntry::DIRTY;
046392dc 221 return CCoinsModifier(*this, ret.first, cachedCoinUsage);
a0fa20a1
PW
222}
223
629d75fa
PW
224const CCoins* CCoinsViewCache::AccessCoins(const uint256 &txid) const {
225 CCoinsMap::const_iterator it = FetchCoins(txid);
226 if (it == cacheCoins.end()) {
227 return NULL;
228 } else {
058b08c1 229 return &it->second.coins;
629d75fa 230 }
a3dc587a
DK
231}
232
a3dc587a
DK
233bool CCoinsViewCache::HaveCoins(const uint256 &txid) const {
234 CCoinsMap::const_iterator it = FetchCoins(txid);
d4d3fbd8 235 // We're using vtx.empty() instead of IsPruned here for performance reasons,
fa94b9d5 236 // as we only care about the case where a transaction was replaced entirely
d4d3fbd8
PW
237 // in a reorganization (which wipes vout entirely, as opposed to spending
238 // which just cleans individual outputs).
058b08c1 239 return (it != cacheCoins.end() && !it->second.coins.vout.empty());
a0fa20a1
PW
240}
241
a3dc587a 242uint256 CCoinsViewCache::GetBestBlock() const {
4f152496 243 if (hashBlock.IsNull())
a0fa20a1
PW
244 hashBlock = base->GetBestBlock();
245 return hashBlock;
246}
247
9f25631d
SB
248
249uint256 CCoinsViewCache::GetBestAnchor() const {
250 if (hashAnchor.IsNull())
251 hashAnchor = base->GetBestAnchor();
252 return hashAnchor;
253}
254
c9d1a81c 255void CCoinsViewCache::SetBestBlock(const uint256 &hashBlockIn) {
a0fa20a1 256 hashBlock = hashBlockIn;
a0fa20a1
PW
257}
258
9f25631d
SB
259bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins,
260 const uint256 &hashBlockIn,
261 const uint256 &hashAnchorIn,
45d6bee9 262 CAnchorsMap &mapAnchors,
bb64be52 263 CNullifiersMap &mapNullifiers) {
f28aec01 264 assert(!hasModifier);
b0875eb3 265 for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) {
058b08c1
PW
266 if (it->second.flags & CCoinsCacheEntry::DIRTY) { // Ignore non-dirty entries (optimization).
267 CCoinsMap::iterator itUs = cacheCoins.find(it->first);
268 if (itUs == cacheCoins.end()) {
269 if (!it->second.coins.IsPruned()) {
270 // The parent cache does not have an entry, while the child
271 // cache does have (a non-pruned) one. Move the data up, and
272 // mark it as fresh (if the grandparent did have it, we
273 // would have pulled it in at first GetCoins).
274 assert(it->second.flags & CCoinsCacheEntry::FRESH);
275 CCoinsCacheEntry& entry = cacheCoins[it->first];
276 entry.coins.swap(it->second.coins);
046392dc 277 cachedCoinsUsage += memusage::DynamicUsage(entry.coins);
058b08c1
PW
278 entry.flags = CCoinsCacheEntry::DIRTY | CCoinsCacheEntry::FRESH;
279 }
280 } else {
281 if ((itUs->second.flags & CCoinsCacheEntry::FRESH) && it->second.coins.IsPruned()) {
282 // The grandparent does not have an entry, and the child is
283 // modified and being pruned. This means we can just delete
284 // it from the parent.
046392dc 285 cachedCoinsUsage -= memusage::DynamicUsage(itUs->second.coins);
058b08c1
PW
286 cacheCoins.erase(itUs);
287 } else {
288 // A normal modification.
046392dc 289 cachedCoinsUsage -= memusage::DynamicUsage(itUs->second.coins);
058b08c1 290 itUs->second.coins.swap(it->second.coins);
046392dc 291 cachedCoinsUsage += memusage::DynamicUsage(itUs->second.coins);
058b08c1
PW
292 itUs->second.flags |= CCoinsCacheEntry::DIRTY;
293 }
294 }
295 }
b0875eb3
PW
296 CCoinsMap::iterator itOld = it++;
297 mapCoins.erase(itOld);
298 }
9f25631d
SB
299
300 for (CAnchorsMap::iterator child_it = mapAnchors.begin(); child_it != mapAnchors.end();)
301 {
302 if (child_it->second.flags & CAnchorsCacheEntry::DIRTY) {
303 CAnchorsMap::iterator parent_it = cacheAnchors.find(child_it->first);
304
305 if (parent_it == cacheAnchors.end()) {
306 if (child_it->second.entered) {
307 // Parent doesn't have an entry, but child has a new commitment root.
308
309 CAnchorsCacheEntry& entry = cacheAnchors[child_it->first];
310 entry.entered = true;
434f3284 311 entry.tree = child_it->second.tree;
9f25631d
SB
312 entry.flags = CAnchorsCacheEntry::DIRTY;
313
7f3c7a68 314 cachedCoinsUsage += memusage::DynamicUsage(entry.tree);
9f25631d
SB
315 }
316 } else {
317 if (parent_it->second.entered != child_it->second.entered) {
318 // The parent may have removed the entry.
319 parent_it->second.entered = child_it->second.entered;
320 parent_it->second.flags |= CAnchorsCacheEntry::DIRTY;
321 }
322 }
323 }
324
325 CAnchorsMap::iterator itOld = child_it++;
326 mapAnchors.erase(itOld);
327 }
45d6bee9 328
bb64be52 329 for (CNullifiersMap::iterator child_it = mapNullifiers.begin(); child_it != mapNullifiers.end();)
45d6bee9 330 {
9e511dbb 331 if (child_it->second.flags & CNullifiersCacheEntry::DIRTY) { // Ignore non-dirty entries (optimization).
1d184d53 332 CNullifiersMap::iterator parent_it = cacheNullifiers.find(child_it->first);
45d6bee9 333
1d184d53 334 if (parent_it == cacheNullifiers.end()) {
45d6bee9 335 if (child_it->second.entered) {
9e511dbb
SB
336 // Parent doesn't have an entry, but child has a SPENT nullifier.
337 // Move the spent nullifier up.
45d6bee9 338
9e511dbb 339 CNullifiersCacheEntry& entry = cacheNullifiers[child_it->first];
45d6bee9 340 entry.entered = true;
9e511dbb 341 entry.flags = CNullifiersCacheEntry::DIRTY;
45d6bee9
SB
342 }
343 } else {
344 if (parent_it->second.entered != child_it->second.entered) {
345 parent_it->second.entered = child_it->second.entered;
9e511dbb 346 parent_it->second.flags |= CNullifiersCacheEntry::DIRTY;
45d6bee9
SB
347 }
348 }
349 }
d889a287 350 CNullifiersMap::iterator itOld = child_it++;
bb64be52 351 mapNullifiers.erase(itOld);
45d6bee9
SB
352 }
353
9f25631d 354 hashAnchor = hashAnchorIn;
a0fa20a1
PW
355 hashBlock = hashBlockIn;
356 return true;
357}
358
359bool CCoinsViewCache::Flush() {
1d184d53 360 bool fOk = base->BatchWrite(cacheCoins, hashBlock, hashAnchor, cacheAnchors, cacheNullifiers);
b0875eb3 361 cacheCoins.clear();
9f25631d 362 cacheAnchors.clear();
1d184d53 363 cacheNullifiers.clear();
046392dc 364 cachedCoinsUsage = 0;
a0fa20a1
PW
365 return fOk;
366}
367
a3dc587a 368unsigned int CCoinsViewCache::GetCacheSize() const {
a0fa20a1
PW
369 return cacheCoins.size();
370}
371
a3dc587a 372const CTxOut &CCoinsViewCache::GetOutputFor(const CTxIn& input) const
a0fa20a1 373{
629d75fa
PW
374 const CCoins* coins = AccessCoins(input.prevout.hash);
375 assert(coins && coins->IsAvailable(input.prevout.n));
376 return coins->vout[input.prevout.n];
a0fa20a1
PW
377}
378
e4ca0031 379const CScript &CCoinsViewCache::GetSpendFor(const CTxIn& input) const
380{
381 const CCoins* coins = AccessCoins(input.prevout.hash);
5fbcb130 382 assert(coins);
e4ca0031 383 return coins->vout[input.prevout.n].scriptPubKey;
384}
385
17878015 386uint64_t komodo_interest(int32_t txheight,uint64_t nValue,uint32_t nLockTime,uint32_t tiptime);
fd8dd3c2 387extern char ASSETCHAINS_SYMBOL[16];
3127a480 388
17878015 389CAmount CCoinsViewCache::GetValueIn(int32_t nHeight,int64_t *interestp,const CTransaction& tx,uint32_t tiptime) const
a0fa20a1 390{
355ca565 391 uint32_t timestamp,minutes; int64_t interest;
392 *interestp = 0;
63a9f526 393 if ( tx.IsCoinBase() != 0 )
a0fa20a1 394 return 0;
63a9f526 395 CAmount value,nResult = 0;
a0fa20a1 396 for (unsigned int i = 0; i < tx.vin.size(); i++)
602dc744 397 {
63a9f526 398 value = GetOutputFor(tx.vin[i]).nValue;
399 nResult += value;
17878015 400 interest = komodo_interest(nHeight,value,tx.nLockTime,tiptime);
fdbf481f 401#ifdef KOMODO_ENABLE_INTEREST
fd8dd3c2 402 if ( ASSETCHAINS_SYMBOL[0] == 0 && nHeight >= 60000 )
4a4e912b 403 {
404 printf("nResult %.8f += interest %.8f ht.%d lock.%u tip.%u\n",(double)nResult/COIN,(double)interest/COIN,nHeight,tx.nLockTime,tiptime);
2d4c201c 405 nResult += interest;
4a4e912b 406 }
fdbf481f 407#endif
355ca565 408 (*interestp) += interest;
602dc744 409 }
942bc467 410 nResult += tx.GetJoinSplitValueIn();
f512cf7c 411
a0fa20a1
PW
412 return nResult;
413}
414
ee964faf 415bool CCoinsViewCache::HaveJoinSplitRequirements(const CTransaction& tx) const
a8ac403d 416{
6c59778a
SB
417 boost::unordered_map<uint256, ZCIncrementalMerkleTree, CCoinsKeyHasher> intermediates;
418
b7e4abd6 419 BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit)
a8ac403d 420 {
9e511dbb 421 BOOST_FOREACH(const uint256& nullifier, joinsplit.nullifiers)
d66877af 422 {
9e511dbb
SB
423 if (GetNullifier(nullifier)) {
424 // If the nullifier is set, this transaction
d66877af
SB
425 // double-spends!
426 return false;
427 }
428 }
429
434f3284 430 ZCIncrementalMerkleTree tree;
b7e4abd6 431 auto it = intermediates.find(joinsplit.anchor);
6c59778a
SB
432 if (it != intermediates.end()) {
433 tree = it->second;
b7e4abd6 434 } else if (!GetAnchorAt(joinsplit.anchor, tree)) {
a8ac403d
SB
435 return false;
436 }
6c59778a 437
b7e4abd6 438 BOOST_FOREACH(const uint256& commitment, joinsplit.commitments)
6c59778a
SB
439 {
440 tree.append(commitment);
441 }
442
443 intermediates.insert(std::make_pair(tree.root(), tree));
a8ac403d
SB
444 }
445
446 return true;
447}
448
a3dc587a 449bool CCoinsViewCache::HaveInputs(const CTransaction& tx) const
a0fa20a1
PW
450{
451 if (!tx.IsCoinBase()) {
a0fa20a1
PW
452 for (unsigned int i = 0; i < tx.vin.size(); i++) {
453 const COutPoint &prevout = tx.vin[i].prevout;
629d75fa
PW
454 const CCoins* coins = AccessCoins(prevout.hash);
455 if (!coins || !coins->IsAvailable(prevout.n)) {
a0fa20a1 456 return false;
629d75fa 457 }
a0fa20a1
PW
458 }
459 }
460 return true;
461}
4d707d51 462
a3dc587a 463double CCoinsViewCache::GetPriority(const CTransaction &tx, int nHeight) const
4d707d51
GA
464{
465 if (tx.IsCoinBase())
466 return 0.0;
467 double dResult = 0.0;
468 BOOST_FOREACH(const CTxIn& txin, tx.vin)
469 {
629d75fa
PW
470 const CCoins* coins = AccessCoins(txin.prevout.hash);
471 assert(coins);
472 if (!coins->IsAvailable(txin.prevout.n)) continue;
473 if (coins->nHeight < nHeight) {
474 dResult += coins->vout[txin.prevout.n].nValue * (nHeight-coins->nHeight);
4d707d51
GA
475 }
476 }
477 return tx.ComputePriority(dResult);
478}
f28aec01 479
046392dc 480CCoinsModifier::CCoinsModifier(CCoinsViewCache& cache_, CCoinsMap::iterator it_, size_t usage) : cache(cache_), it(it_), cachedCoinUsage(usage) {
02bced16
PW
481 assert(!cache.hasModifier);
482 cache.hasModifier = true;
483}
f28aec01 484
058b08c1
PW
485CCoinsModifier::~CCoinsModifier()
486{
f28aec01
PW
487 assert(cache.hasModifier);
488 cache.hasModifier = false;
058b08c1 489 it->second.coins.Cleanup();
046392dc 490 cache.cachedCoinsUsage -= cachedCoinUsage; // Subtract the old usage
058b08c1
PW
491 if ((it->second.flags & CCoinsCacheEntry::FRESH) && it->second.coins.IsPruned()) {
492 cache.cacheCoins.erase(it);
046392dc
PW
493 } else {
494 // If the coin still exists after the modification, add the new usage
495 cache.cachedCoinsUsage += memusage::DynamicUsage(it->second.coins);
058b08c1 496 }
f28aec01 497}
This page took 0.233059 seconds and 4 git commands to generate.