]>
Commit | Line | Data |
---|---|---|
f914f1a7 | 1 | // Copyright (c) 2012-2014 The Bitcoin Core developers |
fa94b9d5 | 2 | // Distributed under the MIT software license, see the accompanying |
bc909a7a | 3 | // file COPYING or https://www.opensource.org/licenses/mit-license.php . |
a0fa20a1 PW |
4 | |
5 | #include "coins.h" | |
6 | ||
046392dc | 7 | #include "memusage.h" |
bc42503f | 8 | #include "random.h" |
520ced14 | 9 | #include "version.h" |
4d4a7cd5 | 10 | #include "policy/fees.h" |
997cf5a1 | 11 | #include "komodo_defs.h" |
20c3ac51 | 12 | #include "importcoin.h" |
8577896f | 13 | #include "pbaas/notarization.h" |
989b1de1 | 14 | #include "pbaas/reserves.h" |
bc42503f | 15 | |
a0fa20a1 PW |
16 | #include <assert.h> |
17 | ||
fa94b9d5 MF |
18 | /** |
19 | * calculate number of bytes for the bitmask, and its number of non-zero bytes | |
20 | * each bit in the bitmask represents the availability of one output, but the | |
21 | * availabilities of the first two outputs are encoded separately | |
22 | */ | |
a0fa20a1 PW |
23 | void CCoins::CalcMaskSize(unsigned int &nBytes, unsigned int &nNonzeroBytes) const { |
24 | unsigned int nLastUsedByte = 0; | |
25 | for (unsigned int b = 0; 2+b*8 < vout.size(); b++) { | |
26 | bool fZero = true; | |
27 | for (unsigned int i = 0; i < 8 && 2+b*8+i < vout.size(); i++) { | |
28 | if (!vout[2+b*8+i].IsNull()) { | |
29 | fZero = false; | |
30 | continue; | |
31 | } | |
32 | } | |
33 | if (!fZero) { | |
34 | nLastUsedByte = b + 1; | |
35 | nNonzeroBytes++; | |
36 | } | |
37 | } | |
38 | nBytes += nLastUsedByte; | |
39 | } | |
40 | ||
c444c620 | 41 | bool CCoins::Spend(uint32_t nPos) |
42 | { | |
43 | if (nPos >= vout.size() || vout[nPos].IsNull()) | |
a0fa20a1 | 44 | return false; |
c444c620 | 45 | vout[nPos].SetNull(); |
a0fa20a1 | 46 | Cleanup(); |
a0fa20a1 PW |
47 | return true; |
48 | } | |
4fc309f0 EOW |
49 | bool CCoinsView::GetSproutAnchorAt(const uint256 &rt, SproutMerkleTree &tree) const { return false; } |
50 | bool CCoinsView::GetSaplingAnchorAt(const uint256 &rt, SaplingMerkleTree &tree) const { return false; } | |
28d20bdb | 51 | bool CCoinsView::GetNullifier(const uint256 &nullifier, ShieldedType type) const { return false; } |
a3dc587a | 52 | bool CCoinsView::GetCoins(const uint256 &txid, CCoins &coins) const { return false; } |
a3dc587a | 53 | bool CCoinsView::HaveCoins(const uint256 &txid) const { return false; } |
4f152496 | 54 | uint256 CCoinsView::GetBestBlock() const { return uint256(); } |
18322f07 | 55 | uint256 CCoinsView::GetBestAnchor(ShieldedType type) const { return uint256(); }; |
9f25631d SB |
56 | bool CCoinsView::BatchWrite(CCoinsMap &mapCoins, |
57 | const uint256 &hashBlock, | |
08f07288 | 58 | const uint256 &hashSproutAnchor, |
18322f07 | 59 | const uint256 &hashSaplingAnchor, |
d455828f | 60 | CAnchorsSproutMap &mapSproutAnchors, |
27616b9a | 61 | CAnchorsSaplingMap &mapSaplingAnchors, |
9669920f | 62 | CNullifiersMap &mapSproutNullifiers, |
685e936c | 63 | CNullifiersMap &mapSaplingNullifiers) { return false; } |
a3dc587a | 64 | bool CCoinsView::GetStats(CCoinsStats &stats) const { return false; } |
a0fa20a1 PW |
65 | |
66 | ||
7c70438d | 67 | CCoinsViewBacked::CCoinsViewBacked(CCoinsView *viewIn) : base(viewIn) { } |
9f25631d | 68 | |
4fc309f0 EOW |
69 | bool CCoinsViewBacked::GetSproutAnchorAt(const uint256 &rt, SproutMerkleTree &tree) const { return base->GetSproutAnchorAt(rt, tree); } |
70 | bool CCoinsViewBacked::GetSaplingAnchorAt(const uint256 &rt, SaplingMerkleTree &tree) const { return base->GetSaplingAnchorAt(rt, tree); } | |
28d20bdb | 71 | bool CCoinsViewBacked::GetNullifier(const uint256 &nullifier, ShieldedType type) const { return base->GetNullifier(nullifier, type); } |
a3dc587a | 72 | bool CCoinsViewBacked::GetCoins(const uint256 &txid, CCoins &coins) const { return base->GetCoins(txid, coins); } |
a3dc587a DK |
73 | bool CCoinsViewBacked::HaveCoins(const uint256 &txid) const { return base->HaveCoins(txid); } |
74 | uint256 CCoinsViewBacked::GetBestBlock() const { return base->GetBestBlock(); } | |
18322f07 | 75 | uint256 CCoinsViewBacked::GetBestAnchor(ShieldedType type) const { return base->GetBestAnchor(type); } |
a0fa20a1 | 76 | void CCoinsViewBacked::SetBackend(CCoinsView &viewIn) { base = &viewIn; } |
9f25631d SB |
77 | bool CCoinsViewBacked::BatchWrite(CCoinsMap &mapCoins, |
78 | const uint256 &hashBlock, | |
08f07288 | 79 | const uint256 &hashSproutAnchor, |
18322f07 | 80 | const uint256 &hashSaplingAnchor, |
d455828f | 81 | CAnchorsSproutMap &mapSproutAnchors, |
27616b9a | 82 | CAnchorsSaplingMap &mapSaplingAnchors, |
9669920f | 83 | CNullifiersMap &mapSproutNullifiers, |
27616b9a | 84 | CNullifiersMap &mapSaplingNullifiers) { return base->BatchWrite(mapCoins, hashBlock, hashSproutAnchor, hashSaplingAnchor, mapSproutAnchors, mapSaplingAnchors, mapSproutNullifiers, mapSaplingNullifiers); } |
a3dc587a | 85 | bool CCoinsViewBacked::GetStats(CCoinsStats &stats) const { return base->GetStats(stats); } |
a0fa20a1 | 86 | |
bc42503f PW |
87 | CCoinsKeyHasher::CCoinsKeyHasher() : salt(GetRandHash()) {} |
88 | ||
046392dc | 89 | CCoinsViewCache::CCoinsViewCache(CCoinsView *baseIn) : CCoinsViewBacked(baseIn), hasModifier(false), cachedCoinsUsage(0) { } |
a0fa20a1 | 90 | |
f28aec01 PW |
91 | CCoinsViewCache::~CCoinsViewCache() |
92 | { | |
93 | assert(!hasModifier); | |
a0fa20a1 PW |
94 | } |
95 | ||
046392dc | 96 | size_t CCoinsViewCache::DynamicMemoryUsage() const { |
7f3c7a68 | 97 | return memusage::DynamicUsage(cacheCoins) + |
d455828f | 98 | memusage::DynamicUsage(cacheSproutAnchors) + |
27616b9a | 99 | memusage::DynamicUsage(cacheSaplingAnchors) + |
9669920f | 100 | memusage::DynamicUsage(cacheSproutNullifiers) + |
685e936c | 101 | memusage::DynamicUsage(cacheSaplingNullifiers) + |
7f3c7a68 | 102 | cachedCoinsUsage; |
046392dc PW |
103 | } |
104 | ||
f28aec01 | 105 | CCoinsMap::const_iterator CCoinsViewCache::FetchCoins(const uint256 &txid) const { |
bc42503f PW |
106 | CCoinsMap::iterator it = cacheCoins.find(txid); |
107 | if (it != cacheCoins.end()) | |
a0fa20a1 PW |
108 | return it; |
109 | CCoins tmp; | |
058b08c1 | 110 | if (!base->GetCoins(txid, tmp)) |
a0fa20a1 | 111 | return cacheCoins.end(); |
058b08c1 PW |
112 | CCoinsMap::iterator ret = cacheCoins.insert(std::make_pair(txid, CCoinsCacheEntry())).first; |
113 | tmp.swap(ret->second.coins); | |
114 | if (ret->second.coins.IsPruned()) { | |
115 | // The parent only has an empty entry for this txid; we can consider our | |
116 | // version as fresh. | |
117 | ret->second.flags = CCoinsCacheEntry::FRESH; | |
118 | } | |
6bd1d60c | 119 | cachedCoinsUsage += ret->second.coins.DynamicMemoryUsage(); |
a0fa20a1 PW |
120 | return ret; |
121 | } | |
122 | ||
9f25631d | 123 | |
4fc309f0 | 124 | bool CCoinsViewCache::GetSproutAnchorAt(const uint256 &rt, SproutMerkleTree &tree) const { |
d455828f SB |
125 | CAnchorsSproutMap::const_iterator it = cacheSproutAnchors.find(rt); |
126 | if (it != cacheSproutAnchors.end()) { | |
9f25631d | 127 | if (it->second.entered) { |
434f3284 | 128 | tree = it->second.tree; |
9f25631d SB |
129 | return true; |
130 | } else { | |
131 | return false; | |
132 | } | |
133 | } | |
134 | ||
008f4ee8 | 135 | if (!base->GetSproutAnchorAt(rt, tree)) { |
9f25631d SB |
136 | return false; |
137 | } | |
138 | ||
d455828f | 139 | CAnchorsSproutMap::iterator ret = cacheSproutAnchors.insert(std::make_pair(rt, CAnchorsSproutCacheEntry())).first; |
cf471983 | 140 | ret->second.entered = true; |
434f3284 | 141 | ret->second.tree = tree; |
43a03e3b | 142 | cachedCoinsUsage += ret->second.tree.DynamicMemoryUsage(); |
9f25631d SB |
143 | |
144 | return true; | |
145 | } | |
146 | ||
4fc309f0 | 147 | bool CCoinsViewCache::GetSaplingAnchorAt(const uint256 &rt, SaplingMerkleTree &tree) const { |
27616b9a SB |
148 | CAnchorsSaplingMap::const_iterator it = cacheSaplingAnchors.find(rt); |
149 | if (it != cacheSaplingAnchors.end()) { | |
150 | if (it->second.entered) { | |
151 | tree = it->second.tree; | |
152 | return true; | |
153 | } else { | |
154 | return false; | |
155 | } | |
156 | } | |
157 | ||
158 | if (!base->GetSaplingAnchorAt(rt, tree)) { | |
159 | return false; | |
160 | } | |
161 | ||
162 | CAnchorsSaplingMap::iterator ret = cacheSaplingAnchors.insert(std::make_pair(rt, CAnchorsSaplingCacheEntry())).first; | |
163 | ret->second.entered = true; | |
164 | ret->second.tree = tree; | |
165 | cachedCoinsUsage += ret->second.tree.DynamicMemoryUsage(); | |
166 | ||
167 | return true; | |
168 | } | |
169 | ||
28d20bdb | 170 | bool CCoinsViewCache::GetNullifier(const uint256 &nullifier, ShieldedType type) const { |
708c87f1 EOW |
171 | CNullifiersMap* cacheToUse; |
172 | switch (type) { | |
28d20bdb | 173 | case SPROUT: |
9669920f | 174 | cacheToUse = &cacheSproutNullifiers; |
708c87f1 | 175 | break; |
28d20bdb | 176 | case SAPLING: |
708c87f1 EOW |
177 | cacheToUse = &cacheSaplingNullifiers; |
178 | break; | |
179 | default: | |
8c57bbac | 180 | throw std::runtime_error("Unknown shielded type"); |
708c87f1 | 181 | } |
685e936c EOW |
182 | CNullifiersMap::iterator it = cacheToUse->find(nullifier); |
183 | if (it != cacheToUse->end()) | |
45d6bee9 SB |
184 | return it->second.entered; |
185 | ||
9e511dbb | 186 | CNullifiersCacheEntry entry; |
708c87f1 | 187 | bool tmp = base->GetNullifier(nullifier, type); |
45d6bee9 SB |
188 | entry.entered = tmp; |
189 | ||
685e936c | 190 | cacheToUse->insert(std::make_pair(nullifier, entry)); |
45d6bee9 | 191 | |
45d6bee9 SB |
192 | return tmp; |
193 | } | |
9f25631d | 194 | |
7703a673 SB |
195 | template<typename Tree, typename Cache, typename CacheIterator, typename CacheEntry> |
196 | void CCoinsViewCache::AbstractPushAnchor( | |
197 | const Tree &tree, | |
198 | ShieldedType type, | |
199 | Cache &cacheAnchors, | |
200 | uint256 &hash | |
201 | ) | |
202 | { | |
434f3284 | 203 | uint256 newrt = tree.root(); |
9f25631d | 204 | |
7703a673 | 205 | auto currentRoot = GetBestAnchor(type); |
9f25631d SB |
206 | |
207 | // We don't want to overwrite an anchor we already have. | |
208 | // This occurs when a block doesn't modify mapAnchors at all, | |
b7e4abd6 | 209 | // because there are no joinsplits. We could get around this a |
9f25631d SB |
210 | // different way (make all blocks modify mapAnchors somehow) |
211 | // but this is simpler to reason about. | |
212 | if (currentRoot != newrt) { | |
7703a673 SB |
213 | auto insertRet = cacheAnchors.insert(std::make_pair(newrt, CacheEntry())); |
214 | CacheIterator ret = insertRet.first; | |
9f25631d SB |
215 | |
216 | ret->second.entered = true; | |
434f3284 | 217 | ret->second.tree = tree; |
7703a673 | 218 | ret->second.flags = CacheEntry::DIRTY; |
9f25631d | 219 | |
7f3c7a68 SB |
220 | if (insertRet.second) { |
221 | // An insert took place | |
43a03e3b | 222 | cachedCoinsUsage += ret->second.tree.DynamicMemoryUsage(); |
7f3c7a68 SB |
223 | } |
224 | ||
7703a673 | 225 | hash = newrt; |
9f25631d SB |
226 | } |
227 | } | |
228 | ||
4fc309f0 | 229 | template<> void CCoinsViewCache::PushAnchor(const SproutMerkleTree &tree) |
3182b4ab | 230 | { |
4fc309f0 | 231 | AbstractPushAnchor<SproutMerkleTree, CAnchorsSproutMap, CAnchorsSproutMap::iterator, CAnchorsSproutCacheEntry>( |
7703a673 SB |
232 | tree, |
233 | SPROUT, | |
234 | cacheSproutAnchors, | |
235 | hashSproutAnchor | |
236 | ); | |
237 | } | |
238 | ||
4fc309f0 | 239 | template<> void CCoinsViewCache::PushAnchor(const SaplingMerkleTree &tree) |
3182b4ab | 240 | { |
4fc309f0 | 241 | AbstractPushAnchor<SaplingMerkleTree, CAnchorsSaplingMap, CAnchorsSaplingMap::iterator, CAnchorsSaplingCacheEntry>( |
85b39f57 SB |
242 | tree, |
243 | SAPLING, | |
244 | cacheSaplingAnchors, | |
245 | hashSaplingAnchor | |
246 | ); | |
247 | } | |
248 | ||
27616b9a SB |
249 | template<> |
250 | void CCoinsViewCache::BringBestAnchorIntoCache( | |
251 | const uint256 ¤tRoot, | |
4fc309f0 | 252 | SproutMerkleTree &tree |
27616b9a SB |
253 | ) |
254 | { | |
255 | assert(GetSproutAnchorAt(currentRoot, tree)); | |
256 | } | |
257 | ||
258 | template<> | |
259 | void CCoinsViewCache::BringBestAnchorIntoCache( | |
260 | const uint256 ¤tRoot, | |
4fc309f0 | 261 | SaplingMerkleTree &tree |
27616b9a SB |
262 | ) |
263 | { | |
264 | assert(GetSaplingAnchorAt(currentRoot, tree)); | |
265 | } | |
266 | ||
9ea4e387 SB |
267 | template<typename Tree, typename Cache, typename CacheEntry> |
268 | void CCoinsViewCache::AbstractPopAnchor( | |
269 | const uint256 &newrt, | |
270 | ShieldedType type, | |
271 | Cache &cacheAnchors, | |
272 | uint256 &hash | |
273 | ) | |
274 | { | |
275 | auto currentRoot = GetBestAnchor(type); | |
9f25631d SB |
276 | |
277 | // Blocks might not change the commitment tree, in which | |
278 | // case restoring the "old" anchor during a reorg must | |
279 | // have no effect. | |
280 | if (currentRoot != newrt) { | |
ebd9aa11 SB |
281 | // Bring the current best anchor into our local cache |
282 | // so that its tree exists in memory. | |
283 | { | |
9ea4e387 | 284 | Tree tree; |
27616b9a | 285 | BringBestAnchorIntoCache(currentRoot, tree); |
ebd9aa11 | 286 | } |
9f25631d | 287 | |
ebd9aa11 SB |
288 | // Mark the anchor as unentered, removing it from view |
289 | cacheAnchors[currentRoot].entered = false; | |
290 | ||
291 | // Mark the cache entry as dirty so it's propagated | |
9ea4e387 | 292 | cacheAnchors[currentRoot].flags = CacheEntry::DIRTY; |
9f25631d | 293 | |
ebd9aa11 | 294 | // Mark the new root as the best anchor |
9ea4e387 | 295 | hash = newrt; |
9f25631d SB |
296 | } |
297 | } | |
298 | ||
c643ff0b SB |
299 | void CCoinsViewCache::PopAnchor(const uint256 &newrt, ShieldedType type) { |
300 | switch (type) { | |
301 | case SPROUT: | |
4fc309f0 | 302 | AbstractPopAnchor<SproutMerkleTree, CAnchorsSproutMap, CAnchorsSproutCacheEntry>( |
c643ff0b SB |
303 | newrt, |
304 | SPROUT, | |
305 | cacheSproutAnchors, | |
306 | hashSproutAnchor | |
307 | ); | |
308 | break; | |
309 | case SAPLING: | |
4fc309f0 | 310 | AbstractPopAnchor<SaplingMerkleTree, CAnchorsSaplingMap, CAnchorsSaplingCacheEntry>( |
c643ff0b SB |
311 | newrt, |
312 | SAPLING, | |
313 | cacheSaplingAnchors, | |
314 | hashSaplingAnchor | |
315 | ); | |
316 | break; | |
317 | default: | |
8c57bbac | 318 | throw std::runtime_error("Unknown shielded type"); |
9f25631d SB |
319 | } |
320 | } | |
321 | ||
685e936c | 322 | void CCoinsViewCache::SetNullifiers(const CTransaction& tx, bool spent) { |
f57f76d7 | 323 | for (const JSDescription &joinsplit : tx.vJoinSplit) { |
685e936c | 324 | for (const uint256 &nullifier : joinsplit.nullifiers) { |
9669920f | 325 | std::pair<CNullifiersMap::iterator, bool> ret = cacheSproutNullifiers.insert(std::make_pair(nullifier, CNullifiersCacheEntry())); |
685e936c EOW |
326 | ret.first->second.entered = spent; |
327 | ret.first->second.flags |= CNullifiersCacheEntry::DIRTY; | |
328 | } | |
329 | } | |
cab341e1 EOW |
330 | for (const SpendDescription &spendDescription : tx.vShieldedSpend) { |
331 | std::pair<CNullifiersMap::iterator, bool> ret = cacheSaplingNullifiers.insert(std::make_pair(spendDescription.nullifier, CNullifiersCacheEntry())); | |
332 | ret.first->second.entered = spent; | |
333 | ret.first->second.flags |= CNullifiersCacheEntry::DIRTY; | |
334 | } | |
45d6bee9 SB |
335 | } |
336 | ||
058b08c1 PW |
337 | bool CCoinsViewCache::GetCoins(const uint256 &txid, CCoins &coins) const { |
338 | CCoinsMap::const_iterator it = FetchCoins(txid); | |
339 | if (it != cacheCoins.end()) { | |
340 | coins = it->second.coins; | |
341 | return true; | |
342 | } | |
343 | return false; | |
a3dc587a DK |
344 | } |
345 | ||
f28aec01 PW |
346 | CCoinsModifier CCoinsViewCache::ModifyCoins(const uint256 &txid) { |
347 | assert(!hasModifier); | |
058b08c1 | 348 | std::pair<CCoinsMap::iterator, bool> ret = cacheCoins.insert(std::make_pair(txid, CCoinsCacheEntry())); |
046392dc | 349 | size_t cachedCoinUsage = 0; |
f28aec01 | 350 | if (ret.second) { |
058b08c1 PW |
351 | if (!base->GetCoins(txid, ret.first->second.coins)) { |
352 | // The parent view does not have this entry; mark it as fresh. | |
353 | ret.first->second.coins.Clear(); | |
354 | ret.first->second.flags = CCoinsCacheEntry::FRESH; | |
355 | } else if (ret.first->second.coins.IsPruned()) { | |
356 | // The parent view only has a pruned entry for this; mark it as fresh. | |
357 | ret.first->second.flags = CCoinsCacheEntry::FRESH; | |
358 | } | |
046392dc | 359 | } else { |
6bd1d60c | 360 | cachedCoinUsage = ret.first->second.coins.DynamicMemoryUsage(); |
f28aec01 | 361 | } |
058b08c1 PW |
362 | // Assume that whenever ModifyCoins is called, the entry will be modified. |
363 | ret.first->second.flags |= CCoinsCacheEntry::DIRTY; | |
046392dc | 364 | return CCoinsModifier(*this, ret.first, cachedCoinUsage); |
a0fa20a1 PW |
365 | } |
366 | ||
7335afab AM |
367 | CCoinsModifier CCoinsViewCache::ModifyNewCoins(const uint256 &txid) { |
368 | assert(!hasModifier); | |
369 | std::pair<CCoinsMap::iterator, bool> ret = cacheCoins.insert(std::make_pair(txid, CCoinsCacheEntry())); | |
370 | ret.first->second.coins.Clear(); | |
371 | ret.first->second.flags = CCoinsCacheEntry::FRESH; | |
372 | ret.first->second.flags |= CCoinsCacheEntry::DIRTY; | |
373 | return CCoinsModifier(*this, ret.first, 0); | |
374 | } | |
375 | ||
629d75fa PW |
376 | const CCoins* CCoinsViewCache::AccessCoins(const uint256 &txid) const { |
377 | CCoinsMap::const_iterator it = FetchCoins(txid); | |
378 | if (it == cacheCoins.end()) { | |
379 | return NULL; | |
380 | } else { | |
058b08c1 | 381 | return &it->second.coins; |
629d75fa | 382 | } |
a3dc587a DK |
383 | } |
384 | ||
a3dc587a DK |
385 | bool CCoinsViewCache::HaveCoins(const uint256 &txid) const { |
386 | CCoinsMap::const_iterator it = FetchCoins(txid); | |
d4d3fbd8 | 387 | // We're using vtx.empty() instead of IsPruned here for performance reasons, |
fa94b9d5 | 388 | // as we only care about the case where a transaction was replaced entirely |
d4d3fbd8 PW |
389 | // in a reorganization (which wipes vout entirely, as opposed to spending |
390 | // which just cleans individual outputs). | |
058b08c1 | 391 | return (it != cacheCoins.end() && !it->second.coins.vout.empty()); |
a0fa20a1 PW |
392 | } |
393 | ||
a3dc587a | 394 | uint256 CCoinsViewCache::GetBestBlock() const { |
4f152496 | 395 | if (hashBlock.IsNull()) |
37ad6886 MT |
396 | { |
397 | if (base) | |
398 | { | |
399 | hashBlock = base->GetBestBlock(); | |
400 | } | |
401 | else | |
402 | { | |
403 | hashBlock = uint256(); | |
404 | } | |
405 | } | |
a0fa20a1 PW |
406 | return hashBlock; |
407 | } | |
408 | ||
9f25631d | 409 | |
18322f07 SB |
410 | uint256 CCoinsViewCache::GetBestAnchor(ShieldedType type) const { |
411 | switch (type) { | |
412 | case SPROUT: | |
413 | if (hashSproutAnchor.IsNull()) | |
414 | hashSproutAnchor = base->GetBestAnchor(type); | |
415 | return hashSproutAnchor; | |
416 | break; | |
417 | case SAPLING: | |
418 | if (hashSaplingAnchor.IsNull()) | |
419 | hashSaplingAnchor = base->GetBestAnchor(type); | |
420 | return hashSaplingAnchor; | |
421 | break; | |
422 | default: | |
8c57bbac | 423 | throw std::runtime_error("Unknown shielded type"); |
18322f07 | 424 | } |
9f25631d SB |
425 | } |
426 | ||
c9d1a81c | 427 | void CCoinsViewCache::SetBestBlock(const uint256 &hashBlockIn) { |
a0fa20a1 | 428 | hashBlock = hashBlockIn; |
a0fa20a1 PW |
429 | } |
430 | ||
685e936c EOW |
431 | void BatchWriteNullifiers(CNullifiersMap &mapNullifiers, CNullifiersMap &cacheNullifiers) |
432 | { | |
433 | for (CNullifiersMap::iterator child_it = mapNullifiers.begin(); child_it != mapNullifiers.end();) { | |
434 | if (child_it->second.flags & CNullifiersCacheEntry::DIRTY) { // Ignore non-dirty entries (optimization). | |
435 | CNullifiersMap::iterator parent_it = cacheNullifiers.find(child_it->first); | |
436 | ||
437 | if (parent_it == cacheNullifiers.end()) { | |
438 | CNullifiersCacheEntry& entry = cacheNullifiers[child_it->first]; | |
439 | entry.entered = child_it->second.entered; | |
440 | entry.flags = CNullifiersCacheEntry::DIRTY; | |
441 | } else { | |
442 | if (parent_it->second.entered != child_it->second.entered) { | |
443 | parent_it->second.entered = child_it->second.entered; | |
444 | parent_it->second.flags |= CNullifiersCacheEntry::DIRTY; | |
445 | } | |
446 | } | |
447 | } | |
448 | CNullifiersMap::iterator itOld = child_it++; | |
449 | mapNullifiers.erase(itOld); | |
450 | } | |
451 | } | |
452 | ||
27616b9a SB |
453 | template<typename Map, typename MapIterator, typename MapEntry> |
454 | void BatchWriteAnchors( | |
455 | Map &mapAnchors, | |
456 | Map &cacheAnchors, | |
457 | size_t &cachedCoinsUsage | |
458 | ) | |
459 | { | |
460 | for (MapIterator child_it = mapAnchors.begin(); child_it != mapAnchors.end();) | |
461 | { | |
462 | if (child_it->second.flags & MapEntry::DIRTY) { | |
463 | MapIterator parent_it = cacheAnchors.find(child_it->first); | |
464 | ||
465 | if (parent_it == cacheAnchors.end()) { | |
466 | MapEntry& entry = cacheAnchors[child_it->first]; | |
467 | entry.entered = child_it->second.entered; | |
468 | entry.tree = child_it->second.tree; | |
469 | entry.flags = MapEntry::DIRTY; | |
470 | ||
471 | cachedCoinsUsage += entry.tree.DynamicMemoryUsage(); | |
472 | } else { | |
473 | if (parent_it->second.entered != child_it->second.entered) { | |
474 | // The parent may have removed the entry. | |
475 | parent_it->second.entered = child_it->second.entered; | |
476 | parent_it->second.flags |= MapEntry::DIRTY; | |
477 | } | |
478 | } | |
479 | } | |
480 | ||
481 | MapIterator itOld = child_it++; | |
482 | mapAnchors.erase(itOld); | |
483 | } | |
484 | } | |
485 | ||
9f25631d SB |
486 | bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, |
487 | const uint256 &hashBlockIn, | |
08f07288 | 488 | const uint256 &hashSproutAnchorIn, |
18322f07 | 489 | const uint256 &hashSaplingAnchorIn, |
d455828f | 490 | CAnchorsSproutMap &mapSproutAnchors, |
27616b9a | 491 | CAnchorsSaplingMap &mapSaplingAnchors, |
9669920f | 492 | CNullifiersMap &mapSproutNullifiers, |
685e936c | 493 | CNullifiersMap &mapSaplingNullifiers) { |
f28aec01 | 494 | assert(!hasModifier); |
b0875eb3 | 495 | for (CCoinsMap::iterator it = mapCoins.begin(); it != mapCoins.end();) { |
058b08c1 PW |
496 | if (it->second.flags & CCoinsCacheEntry::DIRTY) { // Ignore non-dirty entries (optimization). |
497 | CCoinsMap::iterator itUs = cacheCoins.find(it->first); | |
498 | if (itUs == cacheCoins.end()) { | |
499 | if (!it->second.coins.IsPruned()) { | |
500 | // The parent cache does not have an entry, while the child | |
501 | // cache does have (a non-pruned) one. Move the data up, and | |
502 | // mark it as fresh (if the grandparent did have it, we | |
503 | // would have pulled it in at first GetCoins). | |
504 | assert(it->second.flags & CCoinsCacheEntry::FRESH); | |
505 | CCoinsCacheEntry& entry = cacheCoins[it->first]; | |
506 | entry.coins.swap(it->second.coins); | |
6bd1d60c | 507 | cachedCoinsUsage += entry.coins.DynamicMemoryUsage(); |
058b08c1 PW |
508 | entry.flags = CCoinsCacheEntry::DIRTY | CCoinsCacheEntry::FRESH; |
509 | } | |
510 | } else { | |
511 | if ((itUs->second.flags & CCoinsCacheEntry::FRESH) && it->second.coins.IsPruned()) { | |
512 | // The grandparent does not have an entry, and the child is | |
513 | // modified and being pruned. This means we can just delete | |
514 | // it from the parent. | |
6bd1d60c | 515 | cachedCoinsUsage -= itUs->second.coins.DynamicMemoryUsage(); |
058b08c1 PW |
516 | cacheCoins.erase(itUs); |
517 | } else { | |
518 | // A normal modification. | |
6bd1d60c | 519 | cachedCoinsUsage -= itUs->second.coins.DynamicMemoryUsage(); |
058b08c1 | 520 | itUs->second.coins.swap(it->second.coins); |
6bd1d60c | 521 | cachedCoinsUsage += itUs->second.coins.DynamicMemoryUsage(); |
058b08c1 PW |
522 | itUs->second.flags |= CCoinsCacheEntry::DIRTY; |
523 | } | |
524 | } | |
525 | } | |
b0875eb3 PW |
526 | CCoinsMap::iterator itOld = it++; |
527 | mapCoins.erase(itOld); | |
528 | } | |
9f25631d | 529 | |
27616b9a SB |
530 | ::BatchWriteAnchors<CAnchorsSproutMap, CAnchorsSproutMap::iterator, CAnchorsSproutCacheEntry>(mapSproutAnchors, cacheSproutAnchors, cachedCoinsUsage); |
531 | ::BatchWriteAnchors<CAnchorsSaplingMap, CAnchorsSaplingMap::iterator, CAnchorsSaplingCacheEntry>(mapSaplingAnchors, cacheSaplingAnchors, cachedCoinsUsage); | |
45d6bee9 | 532 | |
9669920f | 533 | ::BatchWriteNullifiers(mapSproutNullifiers, cacheSproutNullifiers); |
685e936c | 534 | ::BatchWriteNullifiers(mapSaplingNullifiers, cacheSaplingNullifiers); |
45d6bee9 | 535 | |
08f07288 | 536 | hashSproutAnchor = hashSproutAnchorIn; |
18322f07 | 537 | hashSaplingAnchor = hashSaplingAnchorIn; |
a0fa20a1 PW |
538 | hashBlock = hashBlockIn; |
539 | return true; | |
540 | } | |
541 | ||
542 | bool CCoinsViewCache::Flush() { | |
27616b9a | 543 | bool fOk = base->BatchWrite(cacheCoins, hashBlock, hashSproutAnchor, hashSaplingAnchor, cacheSproutAnchors, cacheSaplingAnchors, cacheSproutNullifiers, cacheSaplingNullifiers); |
b0875eb3 | 544 | cacheCoins.clear(); |
d455828f | 545 | cacheSproutAnchors.clear(); |
27616b9a | 546 | cacheSaplingAnchors.clear(); |
9669920f | 547 | cacheSproutNullifiers.clear(); |
685e936c | 548 | cacheSaplingNullifiers.clear(); |
046392dc | 549 | cachedCoinsUsage = 0; |
a0fa20a1 PW |
550 | return fOk; |
551 | } | |
552 | ||
a3dc587a | 553 | unsigned int CCoinsViewCache::GetCacheSize() const { |
a0fa20a1 PW |
554 | return cacheCoins.size(); |
555 | } | |
556 | ||
a3dc587a | 557 | const CTxOut &CCoinsViewCache::GetOutputFor(const CTxIn& input) const |
a0fa20a1 | 558 | { |
629d75fa PW |
559 | const CCoins* coins = AccessCoins(input.prevout.hash); |
560 | assert(coins && coins->IsAvailable(input.prevout.n)); | |
561 | return coins->vout[input.prevout.n]; | |
a0fa20a1 PW |
562 | } |
563 | ||
b0e452de MT |
564 | //uint64_t komodo_interest(int32_t txheight,uint64_t nValue,uint32_t nLockTime,uint32_t tiptime); |
565 | uint64_t komodo_accrued_interest(int32_t *txheightp,uint32_t *locktimep,uint256 hash,int32_t n,int32_t checkheight,uint64_t checkvalue,int32_t tipheight); | |
566 | extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; | |
567 | ||
568 | const CScript &CCoinsViewCache::GetSpendFor(const CCoins *coins, const CTxIn& input) | |
6c8e1cdf MT |
569 | { |
570 | assert(coins); | |
b0e452de | 571 | if (coins->nHeight < 6400 && !strcmp(ASSETCHAINS_SYMBOL, "VRSC")) |
6c8e1cdf | 572 | { |
b0e452de MT |
573 | std::string hc = input.prevout.hash.ToString(); |
574 | if (LaunchMap().lmap.count(hc)) | |
575 | { | |
c6e21843 MT |
576 | CTransactionExceptionData &txData = LaunchMap().lmap[hc]; |
577 | if ((txData.voutMask & (((uint64_t)1) << (uint64_t)input.prevout.n)) != 0) | |
578 | { | |
579 | return txData.scriptPubKey; | |
580 | } | |
b0e452de | 581 | } |
6c8e1cdf | 582 | } |
c1e71947 | 583 | return coins->vout[input.prevout.n].scriptPubKey; |
6c8e1cdf MT |
584 | } |
585 | ||
e4ca0031 | 586 | const CScript &CCoinsViewCache::GetSpendFor(const CTxIn& input) const |
587 | { | |
588 | const CCoins* coins = AccessCoins(input.prevout.hash); | |
6c8e1cdf | 589 | return GetSpendFor(coins, input); |
e4ca0031 | 590 | } |
591 | ||
56fe75cb | 592 | CAmount CCoinsViewCache::GetValueIn(int32_t nHeight, int64_t *interestp, const CTransaction& tx, uint32_t tiptime) const |
a0fa20a1 | 593 | { |
0cb91a8d | 594 | CAmount value,nResult = 0; |
5d2f557e | 595 | if ( interestp != 0 ) |
596 | *interestp = 0; | |
0cb91a8d SS |
597 | if ( tx.IsCoinImport() ) |
598 | return GetCoinImportValue(tx); | |
63a9f526 | 599 | if ( tx.IsCoinBase() != 0 ) |
a0fa20a1 | 600 | return 0; |
a0fa20a1 | 601 | for (unsigned int i = 0; i < tx.vin.size(); i++) |
602dc744 | 602 | { |
4a98cf25 MT |
603 | value = 0; |
604 | const CCoins* coins = AccessCoins(tx.vin[i].prevout.hash); | |
605 | if (coins && coins->IsAvailable(tx.vin[i].prevout.n)) | |
606 | { | |
ebbd0d96 | 607 | // if we are a PBaaS chain tx with a coinbase currency state input, all non-shielded inputs are effectively considered burned, since this must be the |
45d7e5d5 | 608 | // block's conversion transaction and they are assumed to all be converted |
609 | COptCCParams p; | |
8496077f | 610 | if (!_IsVerusActive() && coins->fCoinBase && coins->vout[tx.vin[i].prevout.n].scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid() && p.evalCode == EVAL_CURRENCYSTATE) |
45d7e5d5 | 611 | { |
612 | CCoinbaseCurrencyState cbcs; | |
613 | if (p.vData.size() && (cbcs = CCoinbaseCurrencyState(p.vData[0])).IsValid() && cbcs.IsReserve()) | |
614 | { | |
615 | nResult = coins->vout[tx.vin[i].prevout.n].nValue; | |
616 | break; | |
617 | } | |
618 | } | |
619 | else | |
620 | { | |
621 | value = coins->vout[tx.vin[i].prevout.n].nValue; | |
622 | } | |
4a98cf25 MT |
623 | } |
624 | else | |
625 | { | |
626 | return 0; | |
627 | } | |
628 | ||
63a9f526 | 629 | nResult += value; |
fdbf481f | 630 | #ifdef KOMODO_ENABLE_INTEREST |
1141b678 | 631 | if ( ASSETCHAINS_SYMBOL[0] == 0 && nHeight >= 60000 ) |
4a4e912b | 632 | { |
98f68225 | 633 | if ( value >= 10*COIN ) |
3959e9f3 | 634 | { |
798f28c7 | 635 | int64_t interest; int32_t txheight; uint32_t locktime; |
c60397dd | 636 | interest = komodo_accrued_interest(&txheight,&locktime,tx.vin[i].prevout.hash,tx.vin[i].prevout.n,0,value,(int32_t)nHeight); |
f6356642 | 637 | //printf("nResult %.8f += val %.8f interest %.8f ht.%d lock.%u tip.%u\n",(double)nResult/COIN,(double)value/COIN,(double)interest/COIN,txheight,locktime,tiptime); |
638 | //fprintf(stderr,"nResult %.8f += val %.8f interest %.8f ht.%d lock.%u tip.%u\n",(double)nResult/COIN,(double)value/COIN,(double)interest/COIN,txheight,locktime,tiptime); | |
3b97ccc9 | 639 | nResult += interest; |
a6e612cc MT |
640 | if (interestp) |
641 | (*interestp) += interest; | |
3959e9f3 | 642 | } |
4a4e912b | 643 | } |
a130c5cb | 644 | #endif |
602dc744 | 645 | } |
97b46f00 | 646 | nResult += tx.GetShieldedValueIn(); |
f512cf7c | 647 | |
a0fa20a1 PW |
648 | return nResult; |
649 | } | |
650 | ||
56fe75cb | 651 | CCurrencyValueMap CCoinsViewCache::GetReserveValueIn(int32_t nHeight, const CTransaction& tx) const |
989b1de1 | 652 | { |
56fe75cb | 653 | CCurrencyValueMap retMap; |
4b7d1485 | 654 | |
45d7e5d5 | 655 | CAmount nResult = 0; |
989b1de1 | 656 | |
e7e14f44 | 657 | // coinbases have no inputs |
989b1de1 | 658 | if ( tx.IsCoinBase() != 0 ) |
56fe75cb | 659 | return retMap; |
989b1de1 MT |
660 | |
661 | for (unsigned int i = 0; i < tx.vin.size(); i++) | |
662 | { | |
989b1de1 MT |
663 | const CCoins* coins = AccessCoins(tx.vin[i].prevout.hash); |
664 | if (coins && coins->IsAvailable(tx.vin[i].prevout.n)) | |
665 | { | |
adeede49 | 666 | retMap += coins->vout[tx.vin[i].prevout.n].scriptPubKey.ReserveOutValue(); |
989b1de1 MT |
667 | } |
668 | else | |
669 | { | |
adeede49 | 670 | // if coins aren't available, fail all |
56fe75cb | 671 | return CCurrencyValueMap(); |
989b1de1 | 672 | } |
989b1de1 | 673 | } |
56fe75cb | 674 | return retMap; |
989b1de1 | 675 | } |
0cb91a8d | 676 | |
88d014d0 | 677 | //bool CCoinsViewCache::HaveJoinSplitRequirements(const CTransaction& tx) const |
06affbca | 678 | bool CCoinsViewCache::HaveShieldedRequirements(const CTransaction& tx) const |
a8ac403d | 679 | { |
4fc309f0 | 680 | boost::unordered_map<uint256, SproutMerkleTree, CCoinsKeyHasher> intermediates; |
6c59778a | 681 | |
f57f76d7 | 682 | BOOST_FOREACH(const JSDescription &joinsplit, tx.vJoinSplit) |
a8ac403d | 683 | { |
9e511dbb | 684 | BOOST_FOREACH(const uint256& nullifier, joinsplit.nullifiers) |
d66877af | 685 | { |
28d20bdb | 686 | if (GetNullifier(nullifier, SPROUT)) { |
9e511dbb | 687 | // If the nullifier is set, this transaction |
d66877af SB |
688 | // double-spends! |
689 | return false; | |
690 | } | |
691 | } | |
692 | ||
4fc309f0 | 693 | SproutMerkleTree tree; |
b7e4abd6 | 694 | auto it = intermediates.find(joinsplit.anchor); |
6c59778a SB |
695 | if (it != intermediates.end()) { |
696 | tree = it->second; | |
008f4ee8 | 697 | } else if (!GetSproutAnchorAt(joinsplit.anchor, tree)) { |
a8ac403d SB |
698 | return false; |
699 | } | |
6c59778a | 700 | |
b7e4abd6 | 701 | BOOST_FOREACH(const uint256& commitment, joinsplit.commitments) |
6c59778a SB |
702 | { |
703 | tree.append(commitment); | |
704 | } | |
705 | ||
706 | intermediates.insert(std::make_pair(tree.root(), tree)); | |
a8ac403d SB |
707 | } |
708 | ||
cab341e1 | 709 | for (const SpendDescription &spendDescription : tx.vShieldedSpend) { |
28d20bdb | 710 | if (GetNullifier(spendDescription.nullifier, SAPLING)) // Prevent double spends |
cab341e1 | 711 | return false; |
a8ac403d | 712 | |
4fc309f0 | 713 | SaplingMerkleTree tree; |
1f8be05b SB |
714 | if (!GetSaplingAnchorAt(spendDescription.anchor, tree)) { |
715 | return false; | |
716 | } | |
717 | } | |
27616b9a | 718 | |
a8ac403d SB |
719 | return true; |
720 | } | |
721 | ||
a3dc587a | 722 | bool CCoinsViewCache::HaveInputs(const CTransaction& tx) const |
a0fa20a1 | 723 | { |
0cb91a8d | 724 | if (!tx.IsMint()) { |
6f45d897 | 725 | for (unsigned int i = 0; i < tx.vin.size(); i++) { |
a0fa20a1 | 726 | const COutPoint &prevout = tx.vin[i].prevout; |
629d75fa PW |
727 | const CCoins* coins = AccessCoins(prevout.hash); |
728 | if (!coins || !coins->IsAvailable(prevout.n)) { | |
d6f8eeb9 | 729 | //fprintf(stderr,"HaveInputs missing input %s/v%d\n",prevout.hash.ToString().c_str(),prevout.n); |
a0fa20a1 | 730 | return false; |
629d75fa | 731 | } |
a0fa20a1 PW |
732 | } |
733 | } | |
734 | return true; | |
735 | } | |
4d707d51 | 736 | |
c8c677c9 | 737 | double CCoinsViewCache::GetPriority(const CTransaction &tx, int nHeight, const CReserveTransactionDescriptor *desc, const CCurrencyState *currencyState) const |
4d707d51 GA |
738 | { |
739 | if (tx.IsCoinBase()) | |
740 | return 0.0; | |
ec19e8e2 | 741 | |
a1349274 | 742 | // Shielded transfers do not reveal any information about the value or age of a note, so we |
4d4a7cd5 | 743 | // cannot apply the priority algorithm used for transparent utxos. Instead, we just |
a1349274 EOW |
744 | // use the maximum priority for all (partially or fully) shielded transactions. |
745 | // (Note that coinbase transactions cannot contain JoinSplits, or Sapling shielded Spends or Outputs.) | |
ec19e8e2 | 746 | |
88d014d0 | 747 | if (tx.vJoinSplit.size() > 0 || tx.vShieldedSpend.size() > 0 || tx.vShieldedOutput.size() > 0 || tx.IsCoinImport()) { |
0cb91a8d SS |
748 | return MAX_PRIORITY; |
749 | } | |
4d4a7cd5 | 750 | |
51e6ed61 | 751 | // FIXME: this logic is partially duplicated between here and CreateNewBlock in miner.cpp. |
4d707d51 GA |
752 | double dResult = 0.0; |
753 | BOOST_FOREACH(const CTxIn& txin, tx.vin) | |
754 | { | |
629d75fa PW |
755 | const CCoins* coins = AccessCoins(txin.prevout.hash); |
756 | assert(coins); | |
757 | if (!coins->IsAvailable(txin.prevout.n)) continue; | |
758 | if (coins->nHeight < nHeight) { | |
56fe75cb | 759 | if (currencyState && desc) |
760 | { | |
761 | dResult += (coins->vout[txin.prevout.n].nValue + currencyState->ReserveToNative(coins->vout[txin.prevout.n].ReserveOutValue())) * | |
762 | (nHeight-coins->nHeight); | |
763 | } | |
764 | else | |
765 | { | |
766 | dResult += coins->vout[txin.prevout.n].nValue * (nHeight-coins->nHeight); | |
767 | } | |
4d707d51 GA |
768 | } |
769 | } | |
770 | return tx.ComputePriority(dResult); | |
771 | } | |
f28aec01 | 772 | |
046392dc | 773 | CCoinsModifier::CCoinsModifier(CCoinsViewCache& cache_, CCoinsMap::iterator it_, size_t usage) : cache(cache_), it(it_), cachedCoinUsage(usage) { |
02bced16 PW |
774 | assert(!cache.hasModifier); |
775 | cache.hasModifier = true; | |
776 | } | |
f28aec01 | 777 | |
058b08c1 PW |
778 | CCoinsModifier::~CCoinsModifier() |
779 | { | |
f28aec01 PW |
780 | assert(cache.hasModifier); |
781 | cache.hasModifier = false; | |
058b08c1 | 782 | it->second.coins.Cleanup(); |
046392dc | 783 | cache.cachedCoinsUsage -= cachedCoinUsage; // Subtract the old usage |
058b08c1 PW |
784 | if ((it->second.flags & CCoinsCacheEntry::FRESH) && it->second.coins.IsPruned()) { |
785 | cache.cacheCoins.erase(it); | |
046392dc PW |
786 | } else { |
787 | // If the coin still exists after the modification, add the new usage | |
6bd1d60c | 788 | cache.cachedCoinsUsage += it->second.coins.DynamicMemoryUsage(); |
058b08c1 | 789 | } |
f28aec01 | 790 | } |