1 // Copyright (c) 2014-2016 The Bitcoin Core developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
6 #include "script/standard.h"
9 #include "utilstrencodings.h"
10 #include "test/test_bitcoin.h"
11 #include "test/test_random.h"
12 #include "validation.h"
13 #include "consensus/validation.h"
18 #include <boost/test/unit_test.hpp>
20 int ApplyTxInUndo(const CTxInUndo
& undo
, CCoinsViewCache
& view
, const COutPoint
& out
);
21 void UpdateCoins(const CTransaction
& tx
, CCoinsViewCache
& inputs
, CTxUndo
&txundo
, int nHeight
);
25 class CCoinsViewTest
: public CCoinsView
27 uint256 hashBestBlock_
;
28 std::map
<uint256
, CCoins
> map_
;
31 bool GetCoins(const uint256
& txid
, CCoins
& coins
) const
33 std::map
<uint256
, CCoins
>::const_iterator it
= map_
.find(txid
);
34 if (it
== map_
.end()) {
38 if (coins
.IsPruned() && insecure_rand() % 2 == 0) {
39 // Randomly return false in case of an empty entry.
45 bool HaveCoins(const uint256
& txid
) const
48 return GetCoins(txid
, coins
);
51 uint256
GetBestBlock() const { return hashBestBlock_
; }
53 bool BatchWrite(CCoinsMap
& mapCoins
, const uint256
& hashBlock
)
55 for (CCoinsMap::iterator it
= mapCoins
.begin(); it
!= mapCoins
.end(); ) {
56 if (it
->second
.flags
& CCoinsCacheEntry::DIRTY
) {
57 // Same optimization used in CCoinsViewDB is to only write dirty entries.
58 map_
[it
->first
] = it
->second
.coins
;
59 if (it
->second
.coins
.IsPruned() && insecure_rand() % 3 == 0) {
60 // Randomly delete empty entries on write.
61 map_
.erase(it
->first
);
66 if (!hashBlock
.IsNull())
67 hashBestBlock_
= hashBlock
;
72 class CCoinsViewCacheTest
: public CCoinsViewCache
75 CCoinsViewCacheTest(CCoinsView
* _base
) : CCoinsViewCache(_base
) {}
79 // Manually recompute the dynamic usage of the whole data, and compare it.
80 size_t ret
= memusage::DynamicUsage(cacheCoins
);
81 for (CCoinsMap::iterator it
= cacheCoins
.begin(); it
!= cacheCoins
.end(); it
++) {
82 ret
+= it
->second
.coins
.DynamicMemoryUsage();
84 BOOST_CHECK_EQUAL(DynamicMemoryUsage(), ret
);
87 CCoinsMap
& map() { return cacheCoins
; }
88 size_t& usage() { return cachedCoinsUsage
; }
93 BOOST_FIXTURE_TEST_SUITE(coins_tests
, BasicTestingSetup
)
95 static const unsigned int NUM_SIMULATION_ITERATIONS
= 40000;
97 // This is a large randomized insert/remove simulation test on a variable-size
98 // stack of caches on top of CCoinsViewTest.
100 // It will randomly create/update/delete CCoins entries to a tip of caches, with
101 // txids picked from a limited list of random 256-bit hashes. Occasionally, a
102 // new tip is added to the stack of caches, or the tip is flushed and removed.
104 // During the process, booleans are kept to make sure that the randomized
105 // operation hits all branches.
106 BOOST_AUTO_TEST_CASE(coins_cache_simulation_test
)
108 // Various coverage trackers.
109 bool removed_all_caches
= false;
110 bool reached_4_caches
= false;
111 bool added_an_entry
= false;
112 bool removed_an_entry
= false;
113 bool updated_an_entry
= false;
114 bool found_an_entry
= false;
115 bool missed_an_entry
= false;
117 // A simple map to track what we expect the cache stack to represent.
118 std::map
<uint256
, CCoins
> result
;
121 CCoinsViewTest base
; // A CCoinsViewTest at the bottom.
122 std::vector
<CCoinsViewCacheTest
*> stack
; // A stack of CCoinsViewCaches on top.
123 stack
.push_back(new CCoinsViewCacheTest(&base
)); // Start with one cache.
125 // Use a limited set of random transaction ids, so we do test overwriting entries.
126 std::vector
<uint256
> txids
;
127 txids
.resize(NUM_SIMULATION_ITERATIONS
/ 8);
128 for (unsigned int i
= 0; i
< txids
.size(); i
++) {
129 txids
[i
] = GetRandHash();
132 for (unsigned int i
= 0; i
< NUM_SIMULATION_ITERATIONS
; i
++) {
133 // Do a random modification.
135 uint256 txid
= txids
[insecure_rand() % txids
.size()]; // txid we're going to modify in this iteration.
136 CCoins
& coins
= result
[txid
];
137 CCoinsModifier entry
= stack
.back()->ModifyCoins(txid
);
138 BOOST_CHECK(coins
== *entry
);
139 if (insecure_rand() % 5 == 0 || coins
.IsPruned()) {
140 if (coins
.IsPruned()) {
141 added_an_entry
= true;
143 updated_an_entry
= true;
145 coins
.vout
.resize(1);
146 coins
.vout
[0].nValue
= insecure_rand();
151 removed_an_entry
= true;
155 // Once every 1000 iterations and at the end, verify the full cache.
156 if (insecure_rand() % 1000 == 1 || i
== NUM_SIMULATION_ITERATIONS
- 1) {
157 for (std::map
<uint256
, CCoins
>::iterator it
= result
.begin(); it
!= result
.end(); it
++) {
158 const CCoins
* coins
= stack
.back()->AccessCoins(it
->first
);
160 BOOST_CHECK(*coins
== it
->second
);
161 found_an_entry
= true;
163 BOOST_CHECK(it
->second
.IsPruned());
164 missed_an_entry
= true;
167 BOOST_FOREACH(const CCoinsViewCacheTest
*test
, stack
) {
172 if (insecure_rand() % 100 == 0) {
173 // Every 100 iterations, flush an intermediate cache
174 if (stack
.size() > 1 && insecure_rand() % 2 == 0) {
175 unsigned int flushIndex
= insecure_rand() % (stack
.size() - 1);
176 stack
[flushIndex
]->Flush();
179 if (insecure_rand() % 100 == 0) {
180 // Every 100 iterations, change the cache stack.
181 if (stack
.size() > 0 && insecure_rand() % 2 == 0) {
182 //Remove the top cache
183 stack
.back()->Flush();
187 if (stack
.size() == 0 || (stack
.size() < 4 && insecure_rand() % 2)) {
189 CCoinsView
* tip
= &base
;
190 if (stack
.size() > 0) {
193 removed_all_caches
= true;
195 stack
.push_back(new CCoinsViewCacheTest(tip
));
196 if (stack
.size() == 4) {
197 reached_4_caches
= true;
203 // Clean up the stack.
204 while (stack
.size() > 0) {
210 BOOST_CHECK(removed_all_caches
);
211 BOOST_CHECK(reached_4_caches
);
212 BOOST_CHECK(added_an_entry
);
213 BOOST_CHECK(removed_an_entry
);
214 BOOST_CHECK(updated_an_entry
);
215 BOOST_CHECK(found_an_entry
);
216 BOOST_CHECK(missed_an_entry
);
219 typedef std::tuple
<CTransaction
,CTxUndo
,CCoins
> TxData
;
220 // Store of all necessary tx and undo data for next test
221 std::map
<uint256
, TxData
> alltxs
;
223 TxData
&FindRandomFrom(const std::set
<uint256
> &txidset
) {
224 assert(txidset
.size());
225 std::set
<uint256
>::iterator txIt
= txidset
.lower_bound(GetRandHash());
226 if (txIt
== txidset
.end()) {
227 txIt
= txidset
.begin();
229 std::map
<uint256
, TxData
>::iterator txdit
= alltxs
.find(*txIt
);
230 assert(txdit
!= alltxs
.end());
231 return txdit
->second
;
235 // This test is similar to the previous test
236 // except the emphasis is on testing the functionality of UpdateCoins
237 // random txs are created and UpdateCoins is used to update the cache stack
238 // In particular it is tested that spending a duplicate coinbase tx
239 // has the expected effect (the other duplicate is overwitten at all cache levels)
240 BOOST_AUTO_TEST_CASE(updatecoins_simulation_test
)
242 bool spent_a_duplicate_coinbase
= false;
243 // A simple map to track what we expect the cache stack to represent.
244 std::map
<uint256
, CCoins
> result
;
247 CCoinsViewTest base
; // A CCoinsViewTest at the bottom.
248 std::vector
<CCoinsViewCacheTest
*> stack
; // A stack of CCoinsViewCaches on top.
249 stack
.push_back(new CCoinsViewCacheTest(&base
)); // Start with one cache.
251 // Track the txids we've used in various sets
252 std::set
<uint256
> coinbaseids
;
253 std::set
<uint256
> disconnectedids
;
254 std::set
<uint256
> duplicateids
;
255 std::set
<uint256
> utxoset
;
257 for (unsigned int i
= 0; i
< NUM_SIMULATION_ITERATIONS
; i
++) {
258 uint32_t randiter
= insecure_rand();
260 // 19/20 txs add a new transaction
261 if (randiter
% 20 < 19) {
262 CMutableTransaction tx
;
265 tx
.vout
[0].nValue
= i
; //Keep txs unique unless intended to duplicate
266 unsigned int height
= insecure_rand();
269 // 2/20 times create a new coinbase
270 if (randiter
% 20 < 2 || coinbaseids
.size() < 10) {
271 // 1/10 of those times create a duplicate coinbase
272 if (insecure_rand() % 10 == 0 && coinbaseids
.size()) {
273 TxData
&txd
= FindRandomFrom(coinbaseids
);
274 // Reuse the exact same coinbase
275 tx
= std::get
<0>(txd
);
276 // shouldn't be available for reconnection if its been duplicated
277 disconnectedids
.erase(tx
.GetHash());
279 duplicateids
.insert(tx
.GetHash());
282 coinbaseids
.insert(tx
.GetHash());
284 assert(CTransaction(tx
).IsCoinBase());
287 // 17/20 times reconnect previous or add a regular tx
291 // 1/20 times reconnect a previously disconnected tx
292 if (randiter
% 20 == 2 && disconnectedids
.size()) {
293 TxData
&txd
= FindRandomFrom(disconnectedids
);
294 tx
= std::get
<0>(txd
);
295 prevouthash
= tx
.vin
[0].prevout
.hash
;
296 if (!CTransaction(tx
).IsCoinBase() && !utxoset
.count(prevouthash
)) {
297 disconnectedids
.erase(tx
.GetHash());
301 // If this tx is already IN the UTXO, then it must be a coinbase, and it must be a duplicate
302 if (utxoset
.count(tx
.GetHash())) {
303 assert(CTransaction(tx
).IsCoinBase());
304 assert(duplicateids
.count(tx
.GetHash()));
306 disconnectedids
.erase(tx
.GetHash());
309 // 16/20 times create a regular tx
311 TxData
&txd
= FindRandomFrom(utxoset
);
312 prevouthash
= std::get
<0>(txd
).GetHash();
314 // Construct the tx to spend the coins of prevouthash
315 tx
.vin
[0].prevout
.hash
= prevouthash
;
316 tx
.vin
[0].prevout
.n
= 0;
317 assert(!CTransaction(tx
).IsCoinBase());
319 // In this simple test coins only have two states, spent or unspent, save the unspent state to restore
320 oldcoins
= result
[prevouthash
];
321 // Update the expected result of prevouthash to know these coins are spent
322 result
[prevouthash
].Clear();
324 utxoset
.erase(prevouthash
);
326 // The test is designed to ensure spending a duplicate coinbase will work properly
327 // if that ever happens and not resurrect the previously overwritten coinbase
328 if (duplicateids
.count(prevouthash
))
329 spent_a_duplicate_coinbase
= true;
332 // Update the expected result to know about the new output coins
333 result
[tx
.GetHash()].FromTx(tx
, height
);
335 // Call UpdateCoins on the top cache
337 UpdateCoins(tx
, *(stack
.back()), undo
, height
);
339 // Update the utxo set for future spends
340 utxoset
.insert(tx
.GetHash());
342 // Track this tx and undo info to use later
343 alltxs
.insert(std::make_pair(tx
.GetHash(),std::make_tuple(tx
,undo
,oldcoins
)));
346 //1/20 times undo a previous transaction
347 else if (utxoset
.size()) {
348 TxData
&txd
= FindRandomFrom(utxoset
);
350 CTransaction
&tx
= std::get
<0>(txd
);
351 CTxUndo
&undo
= std::get
<1>(txd
);
352 CCoins
&origcoins
= std::get
<2>(txd
);
354 uint256 undohash
= tx
.GetHash();
356 // Update the expected result
357 // Remove new outputs
358 result
[undohash
].Clear();
359 // If not coinbase restore prevout
360 if (!tx
.IsCoinBase()) {
361 result
[tx
.vin
[0].prevout
.hash
] = origcoins
;
364 // Disconnect the tx from the current UTXO
365 // See code in DisconnectBlock
368 CCoinsModifier outs
= stack
.back()->ModifyCoins(undohash
);
372 if (!tx
.IsCoinBase()) {
373 const COutPoint
&out
= tx
.vin
[0].prevout
;
374 const CTxInUndo
&undoin
= undo
.vprevout
[0];
375 ApplyTxInUndo(undoin
, *(stack
.back()), out
);
377 // Store as a candidate for reconnection
378 disconnectedids
.insert(undohash
);
380 // Update the utxoset
381 utxoset
.erase(undohash
);
382 if (!tx
.IsCoinBase())
383 utxoset
.insert(tx
.vin
[0].prevout
.hash
);
386 // Once every 1000 iterations and at the end, verify the full cache.
387 if (insecure_rand() % 1000 == 1 || i
== NUM_SIMULATION_ITERATIONS
- 1) {
388 for (std::map
<uint256
, CCoins
>::iterator it
= result
.begin(); it
!= result
.end(); it
++) {
389 const CCoins
* coins
= stack
.back()->AccessCoins(it
->first
);
391 BOOST_CHECK(*coins
== it
->second
);
393 BOOST_CHECK(it
->second
.IsPruned());
398 if (insecure_rand() % 100 == 0) {
399 // Every 100 iterations, flush an intermediate cache
400 if (stack
.size() > 1 && insecure_rand() % 2 == 0) {
401 unsigned int flushIndex
= insecure_rand() % (stack
.size() - 1);
402 stack
[flushIndex
]->Flush();
405 if (insecure_rand() % 100 == 0) {
406 // Every 100 iterations, change the cache stack.
407 if (stack
.size() > 0 && insecure_rand() % 2 == 0) {
408 stack
.back()->Flush();
412 if (stack
.size() == 0 || (stack
.size() < 4 && insecure_rand() % 2)) {
413 CCoinsView
* tip
= &base
;
414 if (stack
.size() > 0) {
417 stack
.push_back(new CCoinsViewCacheTest(tip
));
422 // Clean up the stack.
423 while (stack
.size() > 0) {
429 BOOST_CHECK(spent_a_duplicate_coinbase
);
432 BOOST_AUTO_TEST_CASE(ccoins_serialization
)
435 CDataStream
ss1(ParseHex("0104835800816115944e077fe7c803cfa57f29b36bf87c1d358bb85e"), SER_DISK
, CLIENT_VERSION
);
438 BOOST_CHECK_EQUAL(cc1
.fCoinBase
, false);
439 BOOST_CHECK_EQUAL(cc1
.nHeight
, 203998);
440 BOOST_CHECK_EQUAL(cc1
.vout
.size(), 2);
441 BOOST_CHECK_EQUAL(cc1
.IsAvailable(0), false);
442 BOOST_CHECK_EQUAL(cc1
.IsAvailable(1), true);
443 BOOST_CHECK_EQUAL(cc1
.vout
[1].nValue
, 60000000000ULL);
444 BOOST_CHECK_EQUAL(HexStr(cc1
.vout
[1].scriptPubKey
), HexStr(GetScriptForDestination(CKeyID(uint160(ParseHex("816115944e077fe7c803cfa57f29b36bf87c1d35"))))));
447 CDataStream
ss2(ParseHex("0109044086ef97d5790061b01caab50f1b8e9c50a5057eb43c2d9563a4eebbd123008c988f1a4a4de2161e0f50aac7f17e7f9555caa486af3b"), SER_DISK
, CLIENT_VERSION
);
450 BOOST_CHECK_EQUAL(cc2
.fCoinBase
, true);
451 BOOST_CHECK_EQUAL(cc2
.nHeight
, 120891);
452 BOOST_CHECK_EQUAL(cc2
.vout
.size(), 17);
453 for (int i
= 0; i
< 17; i
++) {
454 BOOST_CHECK_EQUAL(cc2
.IsAvailable(i
), i
== 4 || i
== 16);
456 BOOST_CHECK_EQUAL(cc2
.vout
[4].nValue
, 234925952);
457 BOOST_CHECK_EQUAL(HexStr(cc2
.vout
[4].scriptPubKey
), HexStr(GetScriptForDestination(CKeyID(uint160(ParseHex("61b01caab50f1b8e9c50a5057eb43c2d9563a4ee"))))));
458 BOOST_CHECK_EQUAL(cc2
.vout
[16].nValue
, 110397);
459 BOOST_CHECK_EQUAL(HexStr(cc2
.vout
[16].scriptPubKey
), HexStr(GetScriptForDestination(CKeyID(uint160(ParseHex("8c988f1a4a4de2161e0f50aac7f17e7f9555caa4"))))));
461 // Smallest possible example
462 CDataStream
ssx(SER_DISK
, CLIENT_VERSION
);
463 BOOST_CHECK_EQUAL(HexStr(ssx
.begin(), ssx
.end()), "");
465 CDataStream
ss3(ParseHex("0002000600"), SER_DISK
, CLIENT_VERSION
);
468 BOOST_CHECK_EQUAL(cc3
.fCoinBase
, false);
469 BOOST_CHECK_EQUAL(cc3
.nHeight
, 0);
470 BOOST_CHECK_EQUAL(cc3
.vout
.size(), 1);
471 BOOST_CHECK_EQUAL(cc3
.IsAvailable(0), true);
472 BOOST_CHECK_EQUAL(cc3
.vout
[0].nValue
, 0);
473 BOOST_CHECK_EQUAL(cc3
.vout
[0].scriptPubKey
.size(), 0);
475 // scriptPubKey that ends beyond the end of the stream
476 CDataStream
ss4(ParseHex("0002000800"), SER_DISK
, CLIENT_VERSION
);
480 BOOST_CHECK_MESSAGE(false, "We should have thrown");
481 } catch (const std::ios_base::failure
& e
) {
484 // Very large scriptPubKey (3*10^9 bytes) past the end of the stream
485 CDataStream
tmp(SER_DISK
, CLIENT_VERSION
);
486 uint64_t x
= 3000000000ULL;
488 BOOST_CHECK_EQUAL(HexStr(tmp
.begin(), tmp
.end()), "8a95c0bb00");
489 CDataStream
ss5(ParseHex("0002008a95c0bb0000"), SER_DISK
, CLIENT_VERSION
);
493 BOOST_CHECK_MESSAGE(false, "We should have thrown");
494 } catch (const std::ios_base::failure
& e
) {
498 const static uint256 TXID
;
499 const static CAmount PRUNED
= -1;
500 const static CAmount ABSENT
= -2;
501 const static CAmount FAIL
= -3;
502 const static CAmount VALUE1
= 100;
503 const static CAmount VALUE2
= 200;
504 const static CAmount VALUE3
= 300;
505 const static char DIRTY
= CCoinsCacheEntry::DIRTY
;
506 const static char FRESH
= CCoinsCacheEntry::FRESH
;
507 const static char NO_ENTRY
= -1;
509 const static auto FLAGS
= {char(0), FRESH
, DIRTY
, char(DIRTY
| FRESH
)};
510 const static auto CLEAN_FLAGS
= {char(0), FRESH
};
511 const static auto ABSENT_FLAGS
= {NO_ENTRY
};
513 void SetCoinsValue(CAmount value
, CCoins
& coins
)
515 assert(value
!= ABSENT
);
517 assert(coins
.IsPruned());
518 if (value
!= PRUNED
) {
519 coins
.vout
.emplace_back();
520 coins
.vout
.back().nValue
= value
;
521 assert(!coins
.IsPruned());
525 size_t InsertCoinsMapEntry(CCoinsMap
& map
, CAmount value
, char flags
)
527 if (value
== ABSENT
) {
528 assert(flags
== NO_ENTRY
);
531 assert(flags
!= NO_ENTRY
);
532 CCoinsCacheEntry entry
;
534 SetCoinsValue(value
, entry
.coins
);
535 auto inserted
= map
.emplace(TXID
, std::move(entry
));
536 assert(inserted
.second
);
537 return inserted
.first
->second
.coins
.DynamicMemoryUsage();
540 void GetCoinsMapEntry(const CCoinsMap
& map
, CAmount
& value
, char& flags
)
542 auto it
= map
.find(TXID
);
543 if (it
== map
.end()) {
547 if (it
->second
.coins
.IsPruned()) {
548 assert(it
->second
.coins
.vout
.size() == 0);
551 assert(it
->second
.coins
.vout
.size() == 1);
552 value
= it
->second
.coins
.vout
[0].nValue
;
554 flags
= it
->second
.flags
;
555 assert(flags
!= NO_ENTRY
);
559 void WriteCoinsViewEntry(CCoinsView
& view
, CAmount value
, char flags
)
562 InsertCoinsMapEntry(map
, value
, flags
);
563 view
.BatchWrite(map
, {});
566 class SingleEntryCacheTest
569 SingleEntryCacheTest(CAmount base_value
, CAmount cache_value
, char cache_flags
)
571 WriteCoinsViewEntry(base
, base_value
, base_value
== ABSENT
? NO_ENTRY
: DIRTY
);
572 cache
.usage() += InsertCoinsMapEntry(cache
.map(), cache_value
, cache_flags
);
576 CCoinsViewCacheTest base
{&root
};
577 CCoinsViewCacheTest cache
{&base
};
580 void CheckAccessCoins(CAmount base_value
, CAmount cache_value
, CAmount expected_value
, char cache_flags
, char expected_flags
)
582 SingleEntryCacheTest
test(base_value
, cache_value
, cache_flags
);
583 test
.cache
.AccessCoins(TXID
);
584 test
.cache
.SelfTest();
586 CAmount result_value
;
588 GetCoinsMapEntry(test
.cache
.map(), result_value
, result_flags
);
589 BOOST_CHECK_EQUAL(result_value
, expected_value
);
590 BOOST_CHECK_EQUAL(result_flags
, expected_flags
);
593 BOOST_AUTO_TEST_CASE(ccoins_access
)
595 /* Check AccessCoin behavior, requesting a coin from a cache view layered on
596 * top of a base view, and checking the resulting entry in the cache after
599 * Base Cache Result Cache Result
600 * Value Value Value Flags Flags
602 CheckAccessCoins(ABSENT
, ABSENT
, ABSENT
, NO_ENTRY
, NO_ENTRY
);
603 CheckAccessCoins(ABSENT
, PRUNED
, PRUNED
, 0 , 0 );
604 CheckAccessCoins(ABSENT
, PRUNED
, PRUNED
, FRESH
, FRESH
);
605 CheckAccessCoins(ABSENT
, PRUNED
, PRUNED
, DIRTY
, DIRTY
);
606 CheckAccessCoins(ABSENT
, PRUNED
, PRUNED
, DIRTY
|FRESH
, DIRTY
|FRESH
);
607 CheckAccessCoins(ABSENT
, VALUE2
, VALUE2
, 0 , 0 );
608 CheckAccessCoins(ABSENT
, VALUE2
, VALUE2
, FRESH
, FRESH
);
609 CheckAccessCoins(ABSENT
, VALUE2
, VALUE2
, DIRTY
, DIRTY
);
610 CheckAccessCoins(ABSENT
, VALUE2
, VALUE2
, DIRTY
|FRESH
, DIRTY
|FRESH
);
611 CheckAccessCoins(PRUNED
, ABSENT
, PRUNED
, NO_ENTRY
, FRESH
);
612 CheckAccessCoins(PRUNED
, PRUNED
, PRUNED
, 0 , 0 );
613 CheckAccessCoins(PRUNED
, PRUNED
, PRUNED
, FRESH
, FRESH
);
614 CheckAccessCoins(PRUNED
, PRUNED
, PRUNED
, DIRTY
, DIRTY
);
615 CheckAccessCoins(PRUNED
, PRUNED
, PRUNED
, DIRTY
|FRESH
, DIRTY
|FRESH
);
616 CheckAccessCoins(PRUNED
, VALUE2
, VALUE2
, 0 , 0 );
617 CheckAccessCoins(PRUNED
, VALUE2
, VALUE2
, FRESH
, FRESH
);
618 CheckAccessCoins(PRUNED
, VALUE2
, VALUE2
, DIRTY
, DIRTY
);
619 CheckAccessCoins(PRUNED
, VALUE2
, VALUE2
, DIRTY
|FRESH
, DIRTY
|FRESH
);
620 CheckAccessCoins(VALUE1
, ABSENT
, VALUE1
, NO_ENTRY
, 0 );
621 CheckAccessCoins(VALUE1
, PRUNED
, PRUNED
, 0 , 0 );
622 CheckAccessCoins(VALUE1
, PRUNED
, PRUNED
, FRESH
, FRESH
);
623 CheckAccessCoins(VALUE1
, PRUNED
, PRUNED
, DIRTY
, DIRTY
);
624 CheckAccessCoins(VALUE1
, PRUNED
, PRUNED
, DIRTY
|FRESH
, DIRTY
|FRESH
);
625 CheckAccessCoins(VALUE1
, VALUE2
, VALUE2
, 0 , 0 );
626 CheckAccessCoins(VALUE1
, VALUE2
, VALUE2
, FRESH
, FRESH
);
627 CheckAccessCoins(VALUE1
, VALUE2
, VALUE2
, DIRTY
, DIRTY
);
628 CheckAccessCoins(VALUE1
, VALUE2
, VALUE2
, DIRTY
|FRESH
, DIRTY
|FRESH
);
631 void CheckModifyCoins(CAmount base_value
, CAmount cache_value
, CAmount modify_value
, CAmount expected_value
, char cache_flags
, char expected_flags
)
633 SingleEntryCacheTest
test(base_value
, cache_value
, cache_flags
);
634 SetCoinsValue(modify_value
, *test
.cache
.ModifyCoins(TXID
));
635 test
.cache
.SelfTest();
637 CAmount result_value
;
639 GetCoinsMapEntry(test
.cache
.map(), result_value
, result_flags
);
640 BOOST_CHECK_EQUAL(result_value
, expected_value
);
641 BOOST_CHECK_EQUAL(result_flags
, expected_flags
);
644 BOOST_AUTO_TEST_CASE(ccoins_modify
)
646 /* Check ModifyCoin behavior, requesting a coin from a cache view layered on
647 * top of a base view, writing a modification to the coin, and then checking
648 * the resulting entry in the cache after the modification.
650 * Base Cache Write Result Cache Result
651 * Value Value Value Value Flags Flags
653 CheckModifyCoins(ABSENT
, ABSENT
, PRUNED
, ABSENT
, NO_ENTRY
, NO_ENTRY
);
654 CheckModifyCoins(ABSENT
, ABSENT
, VALUE3
, VALUE3
, NO_ENTRY
, DIRTY
|FRESH
);
655 CheckModifyCoins(ABSENT
, PRUNED
, PRUNED
, PRUNED
, 0 , DIRTY
);
656 CheckModifyCoins(ABSENT
, PRUNED
, PRUNED
, ABSENT
, FRESH
, NO_ENTRY
);
657 CheckModifyCoins(ABSENT
, PRUNED
, PRUNED
, PRUNED
, DIRTY
, DIRTY
);
658 CheckModifyCoins(ABSENT
, PRUNED
, PRUNED
, ABSENT
, DIRTY
|FRESH
, NO_ENTRY
);
659 CheckModifyCoins(ABSENT
, PRUNED
, VALUE3
, VALUE3
, 0 , DIRTY
);
660 CheckModifyCoins(ABSENT
, PRUNED
, VALUE3
, VALUE3
, FRESH
, DIRTY
|FRESH
);
661 CheckModifyCoins(ABSENT
, PRUNED
, VALUE3
, VALUE3
, DIRTY
, DIRTY
);
662 CheckModifyCoins(ABSENT
, PRUNED
, VALUE3
, VALUE3
, DIRTY
|FRESH
, DIRTY
|FRESH
);
663 CheckModifyCoins(ABSENT
, VALUE2
, PRUNED
, PRUNED
, 0 , DIRTY
);
664 CheckModifyCoins(ABSENT
, VALUE2
, PRUNED
, ABSENT
, FRESH
, NO_ENTRY
);
665 CheckModifyCoins(ABSENT
, VALUE2
, PRUNED
, PRUNED
, DIRTY
, DIRTY
);
666 CheckModifyCoins(ABSENT
, VALUE2
, PRUNED
, ABSENT
, DIRTY
|FRESH
, NO_ENTRY
);
667 CheckModifyCoins(ABSENT
, VALUE2
, VALUE3
, VALUE3
, 0 , DIRTY
);
668 CheckModifyCoins(ABSENT
, VALUE2
, VALUE3
, VALUE3
, FRESH
, DIRTY
|FRESH
);
669 CheckModifyCoins(ABSENT
, VALUE2
, VALUE3
, VALUE3
, DIRTY
, DIRTY
);
670 CheckModifyCoins(ABSENT
, VALUE2
, VALUE3
, VALUE3
, DIRTY
|FRESH
, DIRTY
|FRESH
);
671 CheckModifyCoins(PRUNED
, ABSENT
, PRUNED
, ABSENT
, NO_ENTRY
, NO_ENTRY
);
672 CheckModifyCoins(PRUNED
, ABSENT
, VALUE3
, VALUE3
, NO_ENTRY
, DIRTY
|FRESH
);
673 CheckModifyCoins(PRUNED
, PRUNED
, PRUNED
, PRUNED
, 0 , DIRTY
);
674 CheckModifyCoins(PRUNED
, PRUNED
, PRUNED
, ABSENT
, FRESH
, NO_ENTRY
);
675 CheckModifyCoins(PRUNED
, PRUNED
, PRUNED
, PRUNED
, DIRTY
, DIRTY
);
676 CheckModifyCoins(PRUNED
, PRUNED
, PRUNED
, ABSENT
, DIRTY
|FRESH
, NO_ENTRY
);
677 CheckModifyCoins(PRUNED
, PRUNED
, VALUE3
, VALUE3
, 0 , DIRTY
);
678 CheckModifyCoins(PRUNED
, PRUNED
, VALUE3
, VALUE3
, FRESH
, DIRTY
|FRESH
);
679 CheckModifyCoins(PRUNED
, PRUNED
, VALUE3
, VALUE3
, DIRTY
, DIRTY
);
680 CheckModifyCoins(PRUNED
, PRUNED
, VALUE3
, VALUE3
, DIRTY
|FRESH
, DIRTY
|FRESH
);
681 CheckModifyCoins(PRUNED
, VALUE2
, PRUNED
, PRUNED
, 0 , DIRTY
);
682 CheckModifyCoins(PRUNED
, VALUE2
, PRUNED
, ABSENT
, FRESH
, NO_ENTRY
);
683 CheckModifyCoins(PRUNED
, VALUE2
, PRUNED
, PRUNED
, DIRTY
, DIRTY
);
684 CheckModifyCoins(PRUNED
, VALUE2
, PRUNED
, ABSENT
, DIRTY
|FRESH
, NO_ENTRY
);
685 CheckModifyCoins(PRUNED
, VALUE2
, VALUE3
, VALUE3
, 0 , DIRTY
);
686 CheckModifyCoins(PRUNED
, VALUE2
, VALUE3
, VALUE3
, FRESH
, DIRTY
|FRESH
);
687 CheckModifyCoins(PRUNED
, VALUE2
, VALUE3
, VALUE3
, DIRTY
, DIRTY
);
688 CheckModifyCoins(PRUNED
, VALUE2
, VALUE3
, VALUE3
, DIRTY
|FRESH
, DIRTY
|FRESH
);
689 CheckModifyCoins(VALUE1
, ABSENT
, PRUNED
, PRUNED
, NO_ENTRY
, DIRTY
);
690 CheckModifyCoins(VALUE1
, ABSENT
, VALUE3
, VALUE3
, NO_ENTRY
, DIRTY
);
691 CheckModifyCoins(VALUE1
, PRUNED
, PRUNED
, PRUNED
, 0 , DIRTY
);
692 CheckModifyCoins(VALUE1
, PRUNED
, PRUNED
, ABSENT
, FRESH
, NO_ENTRY
);
693 CheckModifyCoins(VALUE1
, PRUNED
, PRUNED
, PRUNED
, DIRTY
, DIRTY
);
694 CheckModifyCoins(VALUE1
, PRUNED
, PRUNED
, ABSENT
, DIRTY
|FRESH
, NO_ENTRY
);
695 CheckModifyCoins(VALUE1
, PRUNED
, VALUE3
, VALUE3
, 0 , DIRTY
);
696 CheckModifyCoins(VALUE1
, PRUNED
, VALUE3
, VALUE3
, FRESH
, DIRTY
|FRESH
);
697 CheckModifyCoins(VALUE1
, PRUNED
, VALUE3
, VALUE3
, DIRTY
, DIRTY
);
698 CheckModifyCoins(VALUE1
, PRUNED
, VALUE3
, VALUE3
, DIRTY
|FRESH
, DIRTY
|FRESH
);
699 CheckModifyCoins(VALUE1
, VALUE2
, PRUNED
, PRUNED
, 0 , DIRTY
);
700 CheckModifyCoins(VALUE1
, VALUE2
, PRUNED
, ABSENT
, FRESH
, NO_ENTRY
);
701 CheckModifyCoins(VALUE1
, VALUE2
, PRUNED
, PRUNED
, DIRTY
, DIRTY
);
702 CheckModifyCoins(VALUE1
, VALUE2
, PRUNED
, ABSENT
, DIRTY
|FRESH
, NO_ENTRY
);
703 CheckModifyCoins(VALUE1
, VALUE2
, VALUE3
, VALUE3
, 0 , DIRTY
);
704 CheckModifyCoins(VALUE1
, VALUE2
, VALUE3
, VALUE3
, FRESH
, DIRTY
|FRESH
);
705 CheckModifyCoins(VALUE1
, VALUE2
, VALUE3
, VALUE3
, DIRTY
, DIRTY
);
706 CheckModifyCoins(VALUE1
, VALUE2
, VALUE3
, VALUE3
, DIRTY
|FRESH
, DIRTY
|FRESH
);
709 void CheckModifyNewCoinsBase(CAmount base_value
, CAmount cache_value
, CAmount modify_value
, CAmount expected_value
, char cache_flags
, char expected_flags
, bool coinbase
)
711 SingleEntryCacheTest
test(base_value
, cache_value
, cache_flags
);
713 CAmount result_value
;
716 SetCoinsValue(modify_value
, *test
.cache
.ModifyNewCoins(TXID
, coinbase
));
717 GetCoinsMapEntry(test
.cache
.map(), result_value
, result_flags
);
718 } catch (std::logic_error
& e
) {
720 result_flags
= NO_ENTRY
;
723 BOOST_CHECK_EQUAL(result_value
, expected_value
);
724 BOOST_CHECK_EQUAL(result_flags
, expected_flags
);
727 // Simple wrapper for CheckModifyNewCoinsBase function above that loops through
728 // different possible base_values, making sure each one gives the same results.
729 // This wrapper lets the modify_new test below be shorter and less repetitive,
730 // while still verifying that the CoinsViewCache::ModifyNewCoins implementation
731 // ignores base values.
732 template <typename
... Args
>
733 void CheckModifyNewCoins(Args
&&... args
)
735 for (CAmount base_value
: {ABSENT
, PRUNED
, VALUE1
})
736 CheckModifyNewCoinsBase(base_value
, std::forward
<Args
>(args
)...);
739 BOOST_AUTO_TEST_CASE(ccoins_modify_new
)
741 /* Check ModifyNewCoin behavior, requesting a new coin from a cache view,
742 * writing a modification to the coin, and then checking the resulting
743 * entry in the cache after the modification. Verify behavior with the
744 * with the ModifyNewCoin coinbase argument set to false, and to true.
746 * Cache Write Result Cache Result Coinbase
747 * Value Value Value Flags Flags
749 CheckModifyNewCoins(ABSENT
, PRUNED
, ABSENT
, NO_ENTRY
, NO_ENTRY
, false);
750 CheckModifyNewCoins(ABSENT
, PRUNED
, PRUNED
, NO_ENTRY
, DIRTY
, true );
751 CheckModifyNewCoins(ABSENT
, VALUE3
, VALUE3
, NO_ENTRY
, DIRTY
|FRESH
, false);
752 CheckModifyNewCoins(ABSENT
, VALUE3
, VALUE3
, NO_ENTRY
, DIRTY
, true );
753 CheckModifyNewCoins(PRUNED
, PRUNED
, ABSENT
, 0 , NO_ENTRY
, false);
754 CheckModifyNewCoins(PRUNED
, PRUNED
, PRUNED
, 0 , DIRTY
, true );
755 CheckModifyNewCoins(PRUNED
, PRUNED
, ABSENT
, FRESH
, NO_ENTRY
, false);
756 CheckModifyNewCoins(PRUNED
, PRUNED
, ABSENT
, FRESH
, NO_ENTRY
, true );
757 CheckModifyNewCoins(PRUNED
, PRUNED
, PRUNED
, DIRTY
, DIRTY
, false);
758 CheckModifyNewCoins(PRUNED
, PRUNED
, PRUNED
, DIRTY
, DIRTY
, true );
759 CheckModifyNewCoins(PRUNED
, PRUNED
, ABSENT
, DIRTY
|FRESH
, NO_ENTRY
, false);
760 CheckModifyNewCoins(PRUNED
, PRUNED
, ABSENT
, DIRTY
|FRESH
, NO_ENTRY
, true );
761 CheckModifyNewCoins(PRUNED
, VALUE3
, VALUE3
, 0 , DIRTY
|FRESH
, false);
762 CheckModifyNewCoins(PRUNED
, VALUE3
, VALUE3
, 0 , DIRTY
, true );
763 CheckModifyNewCoins(PRUNED
, VALUE3
, VALUE3
, FRESH
, DIRTY
|FRESH
, false);
764 CheckModifyNewCoins(PRUNED
, VALUE3
, VALUE3
, FRESH
, DIRTY
|FRESH
, true );
765 CheckModifyNewCoins(PRUNED
, VALUE3
, VALUE3
, DIRTY
, DIRTY
, false);
766 CheckModifyNewCoins(PRUNED
, VALUE3
, VALUE3
, DIRTY
, DIRTY
, true );
767 CheckModifyNewCoins(PRUNED
, VALUE3
, VALUE3
, DIRTY
|FRESH
, DIRTY
|FRESH
, false);
768 CheckModifyNewCoins(PRUNED
, VALUE3
, VALUE3
, DIRTY
|FRESH
, DIRTY
|FRESH
, true );
769 CheckModifyNewCoins(VALUE2
, PRUNED
, FAIL
, 0 , NO_ENTRY
, false);
770 CheckModifyNewCoins(VALUE2
, PRUNED
, PRUNED
, 0 , DIRTY
, true );
771 CheckModifyNewCoins(VALUE2
, PRUNED
, FAIL
, FRESH
, NO_ENTRY
, false);
772 CheckModifyNewCoins(VALUE2
, PRUNED
, ABSENT
, FRESH
, NO_ENTRY
, true );
773 CheckModifyNewCoins(VALUE2
, PRUNED
, FAIL
, DIRTY
, NO_ENTRY
, false);
774 CheckModifyNewCoins(VALUE2
, PRUNED
, PRUNED
, DIRTY
, DIRTY
, true );
775 CheckModifyNewCoins(VALUE2
, PRUNED
, FAIL
, DIRTY
|FRESH
, NO_ENTRY
, false);
776 CheckModifyNewCoins(VALUE2
, PRUNED
, ABSENT
, DIRTY
|FRESH
, NO_ENTRY
, true );
777 CheckModifyNewCoins(VALUE2
, VALUE3
, FAIL
, 0 , NO_ENTRY
, false);
778 CheckModifyNewCoins(VALUE2
, VALUE3
, VALUE3
, 0 , DIRTY
, true );
779 CheckModifyNewCoins(VALUE2
, VALUE3
, FAIL
, FRESH
, NO_ENTRY
, false);
780 CheckModifyNewCoins(VALUE2
, VALUE3
, VALUE3
, FRESH
, DIRTY
|FRESH
, true );
781 CheckModifyNewCoins(VALUE2
, VALUE3
, FAIL
, DIRTY
, NO_ENTRY
, false);
782 CheckModifyNewCoins(VALUE2
, VALUE3
, VALUE3
, DIRTY
, DIRTY
, true );
783 CheckModifyNewCoins(VALUE2
, VALUE3
, FAIL
, DIRTY
|FRESH
, NO_ENTRY
, false);
784 CheckModifyNewCoins(VALUE2
, VALUE3
, VALUE3
, DIRTY
|FRESH
, DIRTY
|FRESH
, true );
787 void CheckWriteCoins(CAmount parent_value
, CAmount child_value
, CAmount expected_value
, char parent_flags
, char child_flags
, char expected_flags
)
789 SingleEntryCacheTest
test(ABSENT
, parent_value
, parent_flags
);
791 CAmount result_value
;
794 WriteCoinsViewEntry(test
.cache
, child_value
, child_flags
);
795 test
.cache
.SelfTest();
796 GetCoinsMapEntry(test
.cache
.map(), result_value
, result_flags
);
797 } catch (std::logic_error
& e
) {
799 result_flags
= NO_ENTRY
;
802 BOOST_CHECK_EQUAL(result_value
, expected_value
);
803 BOOST_CHECK_EQUAL(result_flags
, expected_flags
);
806 BOOST_AUTO_TEST_CASE(ccoins_write
)
808 /* Check BatchWrite behavior, flushing one entry from a child cache to a
809 * parent cache, and checking the resulting entry in the parent cache
812 * Parent Child Result Parent Child Result
813 * Value Value Value Flags Flags Flags
815 CheckWriteCoins(ABSENT
, ABSENT
, ABSENT
, NO_ENTRY
, NO_ENTRY
, NO_ENTRY
);
816 CheckWriteCoins(ABSENT
, PRUNED
, PRUNED
, NO_ENTRY
, DIRTY
, DIRTY
);
817 CheckWriteCoins(ABSENT
, PRUNED
, ABSENT
, NO_ENTRY
, DIRTY
|FRESH
, NO_ENTRY
);
818 CheckWriteCoins(ABSENT
, VALUE2
, VALUE2
, NO_ENTRY
, DIRTY
, DIRTY
);
819 CheckWriteCoins(ABSENT
, VALUE2
, VALUE2
, NO_ENTRY
, DIRTY
|FRESH
, DIRTY
|FRESH
);
820 CheckWriteCoins(PRUNED
, ABSENT
, PRUNED
, 0 , NO_ENTRY
, 0 );
821 CheckWriteCoins(PRUNED
, ABSENT
, PRUNED
, FRESH
, NO_ENTRY
, FRESH
);
822 CheckWriteCoins(PRUNED
, ABSENT
, PRUNED
, DIRTY
, NO_ENTRY
, DIRTY
);
823 CheckWriteCoins(PRUNED
, ABSENT
, PRUNED
, DIRTY
|FRESH
, NO_ENTRY
, DIRTY
|FRESH
);
824 CheckWriteCoins(PRUNED
, PRUNED
, PRUNED
, 0 , DIRTY
, DIRTY
);
825 CheckWriteCoins(PRUNED
, PRUNED
, PRUNED
, 0 , DIRTY
|FRESH
, DIRTY
);
826 CheckWriteCoins(PRUNED
, PRUNED
, ABSENT
, FRESH
, DIRTY
, NO_ENTRY
);
827 CheckWriteCoins(PRUNED
, PRUNED
, ABSENT
, FRESH
, DIRTY
|FRESH
, NO_ENTRY
);
828 CheckWriteCoins(PRUNED
, PRUNED
, PRUNED
, DIRTY
, DIRTY
, DIRTY
);
829 CheckWriteCoins(PRUNED
, PRUNED
, PRUNED
, DIRTY
, DIRTY
|FRESH
, DIRTY
);
830 CheckWriteCoins(PRUNED
, PRUNED
, ABSENT
, DIRTY
|FRESH
, DIRTY
, NO_ENTRY
);
831 CheckWriteCoins(PRUNED
, PRUNED
, ABSENT
, DIRTY
|FRESH
, DIRTY
|FRESH
, NO_ENTRY
);
832 CheckWriteCoins(PRUNED
, VALUE2
, VALUE2
, 0 , DIRTY
, DIRTY
);
833 CheckWriteCoins(PRUNED
, VALUE2
, VALUE2
, 0 , DIRTY
|FRESH
, DIRTY
);
834 CheckWriteCoins(PRUNED
, VALUE2
, VALUE2
, FRESH
, DIRTY
, DIRTY
|FRESH
);
835 CheckWriteCoins(PRUNED
, VALUE2
, VALUE2
, FRESH
, DIRTY
|FRESH
, DIRTY
|FRESH
);
836 CheckWriteCoins(PRUNED
, VALUE2
, VALUE2
, DIRTY
, DIRTY
, DIRTY
);
837 CheckWriteCoins(PRUNED
, VALUE2
, VALUE2
, DIRTY
, DIRTY
|FRESH
, DIRTY
);
838 CheckWriteCoins(PRUNED
, VALUE2
, VALUE2
, DIRTY
|FRESH
, DIRTY
, DIRTY
|FRESH
);
839 CheckWriteCoins(PRUNED
, VALUE2
, VALUE2
, DIRTY
|FRESH
, DIRTY
|FRESH
, DIRTY
|FRESH
);
840 CheckWriteCoins(VALUE1
, ABSENT
, VALUE1
, 0 , NO_ENTRY
, 0 );
841 CheckWriteCoins(VALUE1
, ABSENT
, VALUE1
, FRESH
, NO_ENTRY
, FRESH
);
842 CheckWriteCoins(VALUE1
, ABSENT
, VALUE1
, DIRTY
, NO_ENTRY
, DIRTY
);
843 CheckWriteCoins(VALUE1
, ABSENT
, VALUE1
, DIRTY
|FRESH
, NO_ENTRY
, DIRTY
|FRESH
);
844 CheckWriteCoins(VALUE1
, PRUNED
, PRUNED
, 0 , DIRTY
, DIRTY
);
845 CheckWriteCoins(VALUE1
, PRUNED
, FAIL
, 0 , DIRTY
|FRESH
, NO_ENTRY
);
846 CheckWriteCoins(VALUE1
, PRUNED
, ABSENT
, FRESH
, DIRTY
, NO_ENTRY
);
847 CheckWriteCoins(VALUE1
, PRUNED
, FAIL
, FRESH
, DIRTY
|FRESH
, NO_ENTRY
);
848 CheckWriteCoins(VALUE1
, PRUNED
, PRUNED
, DIRTY
, DIRTY
, DIRTY
);
849 CheckWriteCoins(VALUE1
, PRUNED
, FAIL
, DIRTY
, DIRTY
|FRESH
, NO_ENTRY
);
850 CheckWriteCoins(VALUE1
, PRUNED
, ABSENT
, DIRTY
|FRESH
, DIRTY
, NO_ENTRY
);
851 CheckWriteCoins(VALUE1
, PRUNED
, FAIL
, DIRTY
|FRESH
, DIRTY
|FRESH
, NO_ENTRY
);
852 CheckWriteCoins(VALUE1
, VALUE2
, VALUE2
, 0 , DIRTY
, DIRTY
);
853 CheckWriteCoins(VALUE1
, VALUE2
, FAIL
, 0 , DIRTY
|FRESH
, NO_ENTRY
);
854 CheckWriteCoins(VALUE1
, VALUE2
, VALUE2
, FRESH
, DIRTY
, DIRTY
|FRESH
);
855 CheckWriteCoins(VALUE1
, VALUE2
, FAIL
, FRESH
, DIRTY
|FRESH
, NO_ENTRY
);
856 CheckWriteCoins(VALUE1
, VALUE2
, VALUE2
, DIRTY
, DIRTY
, DIRTY
);
857 CheckWriteCoins(VALUE1
, VALUE2
, FAIL
, DIRTY
, DIRTY
|FRESH
, NO_ENTRY
);
858 CheckWriteCoins(VALUE1
, VALUE2
, VALUE2
, DIRTY
|FRESH
, DIRTY
, DIRTY
|FRESH
);
859 CheckWriteCoins(VALUE1
, VALUE2
, FAIL
, DIRTY
|FRESH
, DIRTY
|FRESH
, NO_ENTRY
);
861 // The checks above omit cases where the child flags are not DIRTY, since
862 // they would be too repetitive (the parent cache is never updated in these
863 // cases). The loop below covers these cases and makes sure the parent cache
864 // is always left unchanged.
865 for (CAmount parent_value
: {ABSENT
, PRUNED
, VALUE1
})
866 for (CAmount child_value
: {ABSENT
, PRUNED
, VALUE2
})
867 for (char parent_flags
: parent_value
== ABSENT
? ABSENT_FLAGS
: FLAGS
)
868 for (char child_flags
: child_value
== ABSENT
? ABSENT_FLAGS
: CLEAN_FLAGS
)
869 CheckWriteCoins(parent_value
, child_value
, parent_value
, parent_flags
, child_flags
, parent_flags
);
872 BOOST_AUTO_TEST_SUITE_END()