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