@ -80,6 +80,8 @@ public:
@@ -80,6 +80,8 @@ public:
BOOST_CHECK_EQUAL ( DynamicMemoryUsage ( ) , ret ) ;
}
CCoinsMap & map ( ) { return cacheCoins ; }
size_t & usage ( ) { return cachedCoinsUsage ; }
} ;
}
@ -415,4 +417,366 @@ BOOST_AUTO_TEST_CASE(ccoins_serialization)
@@ -415,4 +417,366 @@ BOOST_AUTO_TEST_CASE(ccoins_serialization)
}
}
const static uint256 TXID ;
const static CAmount PRUNED = - 1 ;
const static CAmount ABSENT = - 2 ;
const static CAmount VALUE1 = 100 ;
const static CAmount VALUE2 = 200 ;
const static CAmount VALUE3 = 300 ;
const static char DIRTY = CCoinsCacheEntry : : DIRTY ;
const static char FRESH = CCoinsCacheEntry : : FRESH ;
const static char NO_ENTRY = - 1 ;
const static auto FLAGS = { char ( 0 ) , FRESH , DIRTY , char ( DIRTY | FRESH ) } ;
const static auto CLEAN_FLAGS = { char ( 0 ) , FRESH } ;
const static auto DIRTY_FLAGS = { DIRTY , char ( DIRTY | FRESH ) } ;
const static auto ABSENT_FLAGS = { NO_ENTRY } ;
void SetCoinsValue ( CAmount value , CCoins & coins )
{
assert ( value ! = ABSENT ) ;
coins . Clear ( ) ;
assert ( coins . IsPruned ( ) ) ;
if ( value ! = PRUNED ) {
coins . vout . emplace_back ( ) ;
coins . vout . back ( ) . nValue = value ;
assert ( ! coins . IsPruned ( ) ) ;
}
}
size_t InsertCoinsMapEntry ( CCoinsMap & map , CAmount value , char flags )
{
if ( value = = ABSENT ) {
assert ( flags = = NO_ENTRY ) ;
return 0 ;
}
assert ( flags ! = NO_ENTRY ) ;
CCoinsCacheEntry entry ;
entry . flags = flags ;
SetCoinsValue ( value , entry . coins ) ;
auto inserted = map . emplace ( TXID , std : : move ( entry ) ) ;
assert ( inserted . second ) ;
return inserted . first - > second . coins . DynamicMemoryUsage ( ) ;
}
void GetCoinsMapEntry ( const CCoinsMap & map , CAmount & value , char & flags )
{
auto it = map . find ( TXID ) ;
if ( it = = map . end ( ) ) {
value = ABSENT ;
flags = NO_ENTRY ;
} else {
if ( it - > second . coins . IsPruned ( ) ) {
assert ( it - > second . coins . vout . size ( ) = = 0 ) ;
value = PRUNED ;
} else {
assert ( it - > second . coins . vout . size ( ) = = 1 ) ;
value = it - > second . coins . vout [ 0 ] . nValue ;
}
flags = it - > second . flags ;
assert ( flags ! = NO_ENTRY ) ;
}
}
void WriteCoinsViewEntry ( CCoinsView & view , CAmount value , char flags )
{
CCoinsMap map ;
InsertCoinsMapEntry ( map , value , flags ) ;
view . BatchWrite ( map , { } ) ;
}
class SingleEntryCacheTest
{
public :
SingleEntryCacheTest ( CAmount base_value , CAmount cache_value , char cache_flags )
{
WriteCoinsViewEntry ( base , base_value , base_value = = ABSENT ? NO_ENTRY : DIRTY ) ;
cache . usage ( ) + = InsertCoinsMapEntry ( cache . map ( ) , cache_value , cache_flags ) ;
}
CCoinsView root ;
CCoinsViewCacheTest base { & root } ;
CCoinsViewCacheTest cache { & base } ;
} ;
void CheckAccessCoins ( CAmount base_value , CAmount cache_value , CAmount expected_value , char cache_flags , char expected_flags )
{
SingleEntryCacheTest test ( base_value , cache_value , cache_flags ) ;
test . cache . AccessCoins ( TXID ) ;
test . cache . SelfTest ( ) ;
CAmount result_value ;
char result_flags ;
GetCoinsMapEntry ( test . cache . map ( ) , result_value , result_flags ) ;
BOOST_CHECK_EQUAL ( result_value , expected_value ) ;
BOOST_CHECK_EQUAL ( result_flags , expected_flags ) ;
}
BOOST_AUTO_TEST_CASE ( ccoins_access )
{
/* Check AccessCoin behavior, requesting a coin from a cache view layered on
* top of a base view , and checking the resulting entry in the cache after
* the access .
*
* Base Cache Result Cache Result
* Value Value Value Flags Flags
*/
CheckAccessCoins ( ABSENT , ABSENT , ABSENT , NO_ENTRY , NO_ENTRY ) ;
CheckAccessCoins ( ABSENT , PRUNED , PRUNED , 0 , 0 ) ;
CheckAccessCoins ( ABSENT , PRUNED , PRUNED , FRESH , FRESH ) ;
CheckAccessCoins ( ABSENT , PRUNED , PRUNED , DIRTY , DIRTY ) ;
CheckAccessCoins ( ABSENT , PRUNED , PRUNED , DIRTY | FRESH , DIRTY | FRESH ) ;
CheckAccessCoins ( ABSENT , VALUE2 , VALUE2 , 0 , 0 ) ;
CheckAccessCoins ( ABSENT , VALUE2 , VALUE2 , FRESH , FRESH ) ;
CheckAccessCoins ( ABSENT , VALUE2 , VALUE2 , DIRTY , DIRTY ) ;
CheckAccessCoins ( ABSENT , VALUE2 , VALUE2 , DIRTY | FRESH , DIRTY | FRESH ) ;
CheckAccessCoins ( PRUNED , ABSENT , PRUNED , NO_ENTRY , FRESH ) ;
CheckAccessCoins ( PRUNED , PRUNED , PRUNED , 0 , 0 ) ;
CheckAccessCoins ( PRUNED , PRUNED , PRUNED , FRESH , FRESH ) ;
CheckAccessCoins ( PRUNED , PRUNED , PRUNED , DIRTY , DIRTY ) ;
CheckAccessCoins ( PRUNED , PRUNED , PRUNED , DIRTY | FRESH , DIRTY | FRESH ) ;
CheckAccessCoins ( PRUNED , VALUE2 , VALUE2 , 0 , 0 ) ;
CheckAccessCoins ( PRUNED , VALUE2 , VALUE2 , FRESH , FRESH ) ;
CheckAccessCoins ( PRUNED , VALUE2 , VALUE2 , DIRTY , DIRTY ) ;
CheckAccessCoins ( PRUNED , VALUE2 , VALUE2 , DIRTY | FRESH , DIRTY | FRESH ) ;
CheckAccessCoins ( VALUE1 , ABSENT , VALUE1 , NO_ENTRY , 0 ) ;
CheckAccessCoins ( VALUE1 , PRUNED , PRUNED , 0 , 0 ) ;
CheckAccessCoins ( VALUE1 , PRUNED , PRUNED , FRESH , FRESH ) ;
CheckAccessCoins ( VALUE1 , PRUNED , PRUNED , DIRTY , DIRTY ) ;
CheckAccessCoins ( VALUE1 , PRUNED , PRUNED , DIRTY | FRESH , DIRTY | FRESH ) ;
CheckAccessCoins ( VALUE1 , VALUE2 , VALUE2 , 0 , 0 ) ;
CheckAccessCoins ( VALUE1 , VALUE2 , VALUE2 , FRESH , FRESH ) ;
CheckAccessCoins ( VALUE1 , VALUE2 , VALUE2 , DIRTY , DIRTY ) ;
CheckAccessCoins ( VALUE1 , VALUE2 , VALUE2 , DIRTY | FRESH , DIRTY | FRESH ) ;
}
void CheckModifyCoins ( CAmount base_value , CAmount cache_value , CAmount modify_value , CAmount expected_value , char cache_flags , char expected_flags )
{
SingleEntryCacheTest test ( base_value , cache_value , cache_flags ) ;
SetCoinsValue ( modify_value , * test . cache . ModifyCoins ( TXID ) ) ;
test . cache . SelfTest ( ) ;
CAmount result_value ;
char result_flags ;
GetCoinsMapEntry ( test . cache . map ( ) , result_value , result_flags ) ;
BOOST_CHECK_EQUAL ( result_value , expected_value ) ;
BOOST_CHECK_EQUAL ( result_flags , expected_flags ) ;
} ;
BOOST_AUTO_TEST_CASE ( ccoins_modify )
{
/* Check ModifyCoin behavior, requesting a coin from a cache view layered on
* top of a base view , writing a modification to the coin , and then checking
* the resulting entry in the cache after the modification .
*
* Base Cache Write Result Cache Result
* Value Value Value Value Flags Flags
*/
CheckModifyCoins ( ABSENT , ABSENT , PRUNED , ABSENT , NO_ENTRY , NO_ENTRY ) ;
CheckModifyCoins ( ABSENT , ABSENT , VALUE3 , VALUE3 , NO_ENTRY , DIRTY | FRESH ) ;
CheckModifyCoins ( ABSENT , PRUNED , PRUNED , PRUNED , 0 , DIRTY ) ;
CheckModifyCoins ( ABSENT , PRUNED , PRUNED , ABSENT , FRESH , NO_ENTRY ) ;
CheckModifyCoins ( ABSENT , PRUNED , PRUNED , PRUNED , DIRTY , DIRTY ) ;
CheckModifyCoins ( ABSENT , PRUNED , PRUNED , ABSENT , DIRTY | FRESH , NO_ENTRY ) ;
CheckModifyCoins ( ABSENT , PRUNED , VALUE3 , VALUE3 , 0 , DIRTY ) ;
CheckModifyCoins ( ABSENT , PRUNED , VALUE3 , VALUE3 , FRESH , DIRTY | FRESH ) ;
CheckModifyCoins ( ABSENT , PRUNED , VALUE3 , VALUE3 , DIRTY , DIRTY ) ;
CheckModifyCoins ( ABSENT , PRUNED , VALUE3 , VALUE3 , DIRTY | FRESH , DIRTY | FRESH ) ;
CheckModifyCoins ( ABSENT , VALUE2 , PRUNED , PRUNED , 0 , DIRTY ) ;
CheckModifyCoins ( ABSENT , VALUE2 , PRUNED , ABSENT , FRESH , NO_ENTRY ) ;
CheckModifyCoins ( ABSENT , VALUE2 , PRUNED , PRUNED , DIRTY , DIRTY ) ;
CheckModifyCoins ( ABSENT , VALUE2 , PRUNED , ABSENT , DIRTY | FRESH , NO_ENTRY ) ;
CheckModifyCoins ( ABSENT , VALUE2 , VALUE3 , VALUE3 , 0 , DIRTY ) ;
CheckModifyCoins ( ABSENT , VALUE2 , VALUE3 , VALUE3 , FRESH , DIRTY | FRESH ) ;
CheckModifyCoins ( ABSENT , VALUE2 , VALUE3 , VALUE3 , DIRTY , DIRTY ) ;
CheckModifyCoins ( ABSENT , VALUE2 , VALUE3 , VALUE3 , DIRTY | FRESH , DIRTY | FRESH ) ;
CheckModifyCoins ( PRUNED , ABSENT , PRUNED , ABSENT , NO_ENTRY , NO_ENTRY ) ;
CheckModifyCoins ( PRUNED , ABSENT , VALUE3 , VALUE3 , NO_ENTRY , DIRTY | FRESH ) ;
CheckModifyCoins ( PRUNED , PRUNED , PRUNED , PRUNED , 0 , DIRTY ) ;
CheckModifyCoins ( PRUNED , PRUNED , PRUNED , ABSENT , FRESH , NO_ENTRY ) ;
CheckModifyCoins ( PRUNED , PRUNED , PRUNED , PRUNED , DIRTY , DIRTY ) ;
CheckModifyCoins ( PRUNED , PRUNED , PRUNED , ABSENT , DIRTY | FRESH , NO_ENTRY ) ;
CheckModifyCoins ( PRUNED , PRUNED , VALUE3 , VALUE3 , 0 , DIRTY ) ;
CheckModifyCoins ( PRUNED , PRUNED , VALUE3 , VALUE3 , FRESH , DIRTY | FRESH ) ;
CheckModifyCoins ( PRUNED , PRUNED , VALUE3 , VALUE3 , DIRTY , DIRTY ) ;
CheckModifyCoins ( PRUNED , PRUNED , VALUE3 , VALUE3 , DIRTY | FRESH , DIRTY | FRESH ) ;
CheckModifyCoins ( PRUNED , VALUE2 , PRUNED , PRUNED , 0 , DIRTY ) ;
CheckModifyCoins ( PRUNED , VALUE2 , PRUNED , ABSENT , FRESH , NO_ENTRY ) ;
CheckModifyCoins ( PRUNED , VALUE2 , PRUNED , PRUNED , DIRTY , DIRTY ) ;
CheckModifyCoins ( PRUNED , VALUE2 , PRUNED , ABSENT , DIRTY | FRESH , NO_ENTRY ) ;
CheckModifyCoins ( PRUNED , VALUE2 , VALUE3 , VALUE3 , 0 , DIRTY ) ;
CheckModifyCoins ( PRUNED , VALUE2 , VALUE3 , VALUE3 , FRESH , DIRTY | FRESH ) ;
CheckModifyCoins ( PRUNED , VALUE2 , VALUE3 , VALUE3 , DIRTY , DIRTY ) ;
CheckModifyCoins ( PRUNED , VALUE2 , VALUE3 , VALUE3 , DIRTY | FRESH , DIRTY | FRESH ) ;
CheckModifyCoins ( VALUE1 , ABSENT , PRUNED , PRUNED , NO_ENTRY , DIRTY ) ;
CheckModifyCoins ( VALUE1 , ABSENT , VALUE3 , VALUE3 , NO_ENTRY , DIRTY ) ;
CheckModifyCoins ( VALUE1 , PRUNED , PRUNED , PRUNED , 0 , DIRTY ) ;
CheckModifyCoins ( VALUE1 , PRUNED , PRUNED , ABSENT , FRESH , NO_ENTRY ) ;
CheckModifyCoins ( VALUE1 , PRUNED , PRUNED , PRUNED , DIRTY , DIRTY ) ;
CheckModifyCoins ( VALUE1 , PRUNED , PRUNED , ABSENT , DIRTY | FRESH , NO_ENTRY ) ;
CheckModifyCoins ( VALUE1 , PRUNED , VALUE3 , VALUE3 , 0 , DIRTY ) ;
CheckModifyCoins ( VALUE1 , PRUNED , VALUE3 , VALUE3 , FRESH , DIRTY | FRESH ) ;
CheckModifyCoins ( VALUE1 , PRUNED , VALUE3 , VALUE3 , DIRTY , DIRTY ) ;
CheckModifyCoins ( VALUE1 , PRUNED , VALUE3 , VALUE3 , DIRTY | FRESH , DIRTY | FRESH ) ;
CheckModifyCoins ( VALUE1 , VALUE2 , PRUNED , PRUNED , 0 , DIRTY ) ;
CheckModifyCoins ( VALUE1 , VALUE2 , PRUNED , ABSENT , FRESH , NO_ENTRY ) ;
CheckModifyCoins ( VALUE1 , VALUE2 , PRUNED , PRUNED , DIRTY , DIRTY ) ;
CheckModifyCoins ( VALUE1 , VALUE2 , PRUNED , ABSENT , DIRTY | FRESH , NO_ENTRY ) ;
CheckModifyCoins ( VALUE1 , VALUE2 , VALUE3 , VALUE3 , 0 , DIRTY ) ;
CheckModifyCoins ( VALUE1 , VALUE2 , VALUE3 , VALUE3 , FRESH , DIRTY | FRESH ) ;
CheckModifyCoins ( VALUE1 , VALUE2 , VALUE3 , VALUE3 , DIRTY , DIRTY ) ;
CheckModifyCoins ( VALUE1 , VALUE2 , VALUE3 , VALUE3 , DIRTY | FRESH , DIRTY | FRESH ) ;
}
void CheckModifyNewCoinsBase ( CAmount base_value , CAmount cache_value , CAmount modify_value , CAmount expected_value , char cache_flags , char expected_flags , bool coinbase )
{
SingleEntryCacheTest test ( base_value , cache_value , cache_flags ) ;
SetCoinsValue ( modify_value , * test . cache . ModifyNewCoins ( TXID , coinbase ) ) ;
CAmount result_value ;
char result_flags ;
GetCoinsMapEntry ( test . cache . map ( ) , result_value , result_flags ) ;
BOOST_CHECK_EQUAL ( result_value , expected_value ) ;
BOOST_CHECK_EQUAL ( result_flags , expected_flags ) ;
}
// Simple wrapper for CheckModifyNewCoinsBase function above that loops through
// different possible base_values, making sure each one gives the same results.
// This wrapper lets the modify_new test below be shorter and less repetitive,
// while still verifying that the CoinsViewCache::ModifyNewCoins implementation
// ignores base values.
template < typename . . . Args >
void CheckModifyNewCoins ( Args & & . . . args )
{
for ( CAmount base_value : { ABSENT , PRUNED , VALUE1 } )
CheckModifyNewCoinsBase ( base_value , std : : forward < Args > ( args ) . . . ) ;
}
BOOST_AUTO_TEST_CASE ( ccoins_modify_new )
{
/* Check ModifyNewCoin behavior, requesting a new coin from a cache view,
* writing a modification to the coin , and then checking the resulting
* entry in the cache after the modification . Verify behavior with the
* with the ModifyNewCoin coinbase argument set to false , and to true .
*
* Cache Write Result Cache Result Coinbase
* Value Value Value Flags Flags
*/
CheckModifyNewCoins ( ABSENT , PRUNED , ABSENT , NO_ENTRY , NO_ENTRY , false ) ;
CheckModifyNewCoins ( ABSENT , PRUNED , PRUNED , NO_ENTRY , DIRTY , true ) ;
CheckModifyNewCoins ( ABSENT , VALUE3 , VALUE3 , NO_ENTRY , DIRTY | FRESH , false ) ;
CheckModifyNewCoins ( ABSENT , VALUE3 , VALUE3 , NO_ENTRY , DIRTY , true ) ;
CheckModifyNewCoins ( PRUNED , PRUNED , ABSENT , 0 , NO_ENTRY , false ) ;
CheckModifyNewCoins ( PRUNED , PRUNED , PRUNED , 0 , DIRTY , true ) ;
CheckModifyNewCoins ( PRUNED , PRUNED , ABSENT , FRESH , NO_ENTRY , false ) ;
CheckModifyNewCoins ( PRUNED , PRUNED , ABSENT , FRESH , NO_ENTRY , true ) ;
CheckModifyNewCoins ( PRUNED , PRUNED , ABSENT , DIRTY , NO_ENTRY , false ) ;
CheckModifyNewCoins ( PRUNED , PRUNED , PRUNED , DIRTY , DIRTY , true ) ;
CheckModifyNewCoins ( PRUNED , PRUNED , ABSENT , DIRTY | FRESH , NO_ENTRY , false ) ;
CheckModifyNewCoins ( PRUNED , PRUNED , ABSENT , DIRTY | FRESH , NO_ENTRY , true ) ;
CheckModifyNewCoins ( PRUNED , VALUE3 , VALUE3 , 0 , DIRTY | FRESH , false ) ;
CheckModifyNewCoins ( PRUNED , VALUE3 , VALUE3 , 0 , DIRTY , true ) ;
CheckModifyNewCoins ( PRUNED , VALUE3 , VALUE3 , FRESH , DIRTY | FRESH , false ) ;
CheckModifyNewCoins ( PRUNED , VALUE3 , VALUE3 , FRESH , DIRTY | FRESH , true ) ;
CheckModifyNewCoins ( PRUNED , VALUE3 , VALUE3 , DIRTY , DIRTY | FRESH , false ) ;
CheckModifyNewCoins ( PRUNED , VALUE3 , VALUE3 , DIRTY , DIRTY , true ) ;
CheckModifyNewCoins ( PRUNED , VALUE3 , VALUE3 , DIRTY | FRESH , DIRTY | FRESH , false ) ;
CheckModifyNewCoins ( PRUNED , VALUE3 , VALUE3 , DIRTY | FRESH , DIRTY | FRESH , true ) ;
CheckModifyNewCoins ( VALUE2 , PRUNED , ABSENT , 0 , NO_ENTRY , false ) ;
CheckModifyNewCoins ( VALUE2 , PRUNED , PRUNED , 0 , DIRTY , true ) ;
CheckModifyNewCoins ( VALUE2 , PRUNED , ABSENT , FRESH , NO_ENTRY , false ) ;
CheckModifyNewCoins ( VALUE2 , PRUNED , ABSENT , FRESH , NO_ENTRY , true ) ;
CheckModifyNewCoins ( VALUE2 , PRUNED , ABSENT , DIRTY , NO_ENTRY , false ) ;
CheckModifyNewCoins ( VALUE2 , PRUNED , PRUNED , DIRTY , DIRTY , true ) ;
CheckModifyNewCoins ( VALUE2 , PRUNED , ABSENT , DIRTY | FRESH , NO_ENTRY , false ) ;
CheckModifyNewCoins ( VALUE2 , PRUNED , ABSENT , DIRTY | FRESH , NO_ENTRY , true ) ;
CheckModifyNewCoins ( VALUE2 , VALUE3 , VALUE3 , 0 , DIRTY | FRESH , false ) ;
CheckModifyNewCoins ( VALUE2 , VALUE3 , VALUE3 , 0 , DIRTY , true ) ;
CheckModifyNewCoins ( VALUE2 , VALUE3 , VALUE3 , FRESH , DIRTY | FRESH , false ) ;
CheckModifyNewCoins ( VALUE2 , VALUE3 , VALUE3 , FRESH , DIRTY | FRESH , true ) ;
CheckModifyNewCoins ( VALUE2 , VALUE3 , VALUE3 , DIRTY , DIRTY | FRESH , false ) ;
CheckModifyNewCoins ( VALUE2 , VALUE3 , VALUE3 , DIRTY , DIRTY , true ) ;
CheckModifyNewCoins ( VALUE2 , VALUE3 , VALUE3 , DIRTY | FRESH , DIRTY | FRESH , false ) ;
CheckModifyNewCoins ( VALUE2 , VALUE3 , VALUE3 , DIRTY | FRESH , DIRTY | FRESH , true ) ;
}
void CheckWriteCoins ( CAmount parent_value , CAmount child_value , CAmount expected_value , char parent_flags , char child_flags , char expected_flags )
{
SingleEntryCacheTest test ( ABSENT , parent_value , parent_flags ) ;
WriteCoinsViewEntry ( test . cache , child_value , child_flags ) ;
test . cache . SelfTest ( ) ;
CAmount result_value ;
char result_flags ;
GetCoinsMapEntry ( test . cache . map ( ) , result_value , result_flags ) ;
BOOST_CHECK_EQUAL ( result_value , expected_value ) ;
BOOST_CHECK_EQUAL ( result_flags , expected_flags ) ;
}
BOOST_AUTO_TEST_CASE ( ccoins_write )
{
/* Check BatchWrite behavior, flushing one entry from a child cache to a
* parent cache , and checking the resulting entry in the parent cache
* after the write .
*
* Parent Child Result Parent Child Result
* Value Value Value Flags Flags Flags
*/
CheckWriteCoins ( ABSENT , ABSENT , ABSENT , NO_ENTRY , NO_ENTRY , NO_ENTRY ) ;
CheckWriteCoins ( ABSENT , PRUNED , PRUNED , NO_ENTRY , DIRTY , DIRTY ) ;
CheckWriteCoins ( ABSENT , PRUNED , ABSENT , NO_ENTRY , DIRTY | FRESH , NO_ENTRY ) ;
CheckWriteCoins ( ABSENT , VALUE2 , VALUE2 , NO_ENTRY , DIRTY , DIRTY ) ;
CheckWriteCoins ( ABSENT , VALUE2 , VALUE2 , NO_ENTRY , DIRTY | FRESH , DIRTY | FRESH ) ;
CheckWriteCoins ( PRUNED , ABSENT , PRUNED , 0 , NO_ENTRY , 0 ) ;
CheckWriteCoins ( PRUNED , ABSENT , PRUNED , FRESH , NO_ENTRY , FRESH ) ;
CheckWriteCoins ( PRUNED , ABSENT , PRUNED , DIRTY , NO_ENTRY , DIRTY ) ;
CheckWriteCoins ( PRUNED , ABSENT , PRUNED , DIRTY | FRESH , NO_ENTRY , DIRTY | FRESH ) ;
CheckWriteCoins ( PRUNED , PRUNED , PRUNED , 0 , DIRTY , DIRTY ) ;
CheckWriteCoins ( PRUNED , PRUNED , PRUNED , 0 , DIRTY | FRESH , DIRTY ) ;
CheckWriteCoins ( PRUNED , PRUNED , ABSENT , FRESH , DIRTY , NO_ENTRY ) ;
CheckWriteCoins ( PRUNED , PRUNED , ABSENT , FRESH , DIRTY | FRESH , NO_ENTRY ) ;
CheckWriteCoins ( PRUNED , PRUNED , PRUNED , DIRTY , DIRTY , DIRTY ) ;
CheckWriteCoins ( PRUNED , PRUNED , PRUNED , DIRTY , DIRTY | FRESH , DIRTY ) ;
CheckWriteCoins ( PRUNED , PRUNED , ABSENT , DIRTY | FRESH , DIRTY , NO_ENTRY ) ;
CheckWriteCoins ( PRUNED , PRUNED , ABSENT , DIRTY | FRESH , DIRTY | FRESH , NO_ENTRY ) ;
CheckWriteCoins ( PRUNED , VALUE2 , VALUE2 , 0 , DIRTY , DIRTY ) ;
CheckWriteCoins ( PRUNED , VALUE2 , VALUE2 , 0 , DIRTY | FRESH , DIRTY ) ;
CheckWriteCoins ( PRUNED , VALUE2 , VALUE2 , FRESH , DIRTY , DIRTY | FRESH ) ;
CheckWriteCoins ( PRUNED , VALUE2 , VALUE2 , FRESH , DIRTY | FRESH , DIRTY | FRESH ) ;
CheckWriteCoins ( PRUNED , VALUE2 , VALUE2 , DIRTY , DIRTY , DIRTY ) ;
CheckWriteCoins ( PRUNED , VALUE2 , VALUE2 , DIRTY , DIRTY | FRESH , DIRTY ) ;
CheckWriteCoins ( PRUNED , VALUE2 , VALUE2 , DIRTY | FRESH , DIRTY , DIRTY | FRESH ) ;
CheckWriteCoins ( PRUNED , VALUE2 , VALUE2 , DIRTY | FRESH , DIRTY | FRESH , DIRTY | FRESH ) ;
CheckWriteCoins ( VALUE1 , ABSENT , VALUE1 , 0 , NO_ENTRY , 0 ) ;
CheckWriteCoins ( VALUE1 , ABSENT , VALUE1 , FRESH , NO_ENTRY , FRESH ) ;
CheckWriteCoins ( VALUE1 , ABSENT , VALUE1 , DIRTY , NO_ENTRY , DIRTY ) ;
CheckWriteCoins ( VALUE1 , ABSENT , VALUE1 , DIRTY | FRESH , NO_ENTRY , DIRTY | FRESH ) ;
CheckWriteCoins ( VALUE1 , PRUNED , PRUNED , 0 , DIRTY , DIRTY ) ;
CheckWriteCoins ( VALUE1 , PRUNED , PRUNED , 0 , DIRTY | FRESH , DIRTY ) ;
CheckWriteCoins ( VALUE1 , PRUNED , ABSENT , FRESH , DIRTY , NO_ENTRY ) ;
CheckWriteCoins ( VALUE1 , PRUNED , ABSENT , FRESH , DIRTY | FRESH , NO_ENTRY ) ;
CheckWriteCoins ( VALUE1 , PRUNED , PRUNED , DIRTY , DIRTY , DIRTY ) ;
CheckWriteCoins ( VALUE1 , PRUNED , PRUNED , DIRTY , DIRTY | FRESH , DIRTY ) ;
CheckWriteCoins ( VALUE1 , PRUNED , ABSENT , DIRTY | FRESH , DIRTY , NO_ENTRY ) ;
CheckWriteCoins ( VALUE1 , PRUNED , ABSENT , DIRTY | FRESH , DIRTY | FRESH , NO_ENTRY ) ;
CheckWriteCoins ( VALUE1 , VALUE2 , VALUE2 , 0 , DIRTY , DIRTY ) ;
CheckWriteCoins ( VALUE1 , VALUE2 , VALUE2 , 0 , DIRTY | FRESH , DIRTY ) ;
CheckWriteCoins ( VALUE1 , VALUE2 , VALUE2 , FRESH , DIRTY , DIRTY | FRESH ) ;
CheckWriteCoins ( VALUE1 , VALUE2 , VALUE2 , FRESH , DIRTY | FRESH , DIRTY | FRESH ) ;
CheckWriteCoins ( VALUE1 , VALUE2 , VALUE2 , DIRTY , DIRTY , DIRTY ) ;
CheckWriteCoins ( VALUE1 , VALUE2 , VALUE2 , DIRTY , DIRTY | FRESH , DIRTY ) ;
CheckWriteCoins ( VALUE1 , VALUE2 , VALUE2 , DIRTY | FRESH , DIRTY , DIRTY | FRESH ) ;
CheckWriteCoins ( VALUE1 , VALUE2 , VALUE2 , DIRTY | FRESH , DIRTY | FRESH , DIRTY | FRESH ) ;
// The checks above omit cases where the child flags are not DIRTY, since
// they would be too repetitive (the parent cache is never updated in these
// cases). The loop below covers these cases and makes sure the parent cache
// is always left unchanged.
for ( CAmount parent_value : { ABSENT , PRUNED , VALUE1 } )
for ( CAmount child_value : { ABSENT , PRUNED , VALUE2 } )
for ( char parent_flags : parent_value = = ABSENT ? ABSENT_FLAGS : FLAGS )
for ( char child_flags : child_value = = ABSENT ? ABSENT_FLAGS : CLEAN_FLAGS )
CheckWriteCoins ( parent_value , child_value , parent_value , parent_flags , child_flags , parent_flags ) ;
}
BOOST_AUTO_TEST_SUITE_END ( )