|
|
@ -80,6 +80,8 @@ public: |
|
|
|
BOOST_CHECK_EQUAL(DynamicMemoryUsage(), ret); |
|
|
|
BOOST_CHECK_EQUAL(DynamicMemoryUsage(), ret); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CCoinsMap& map() { return cacheCoins; } |
|
|
|
|
|
|
|
size_t& usage() { return cachedCoinsUsage; } |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
@ -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() |
|
|
|
BOOST_AUTO_TEST_SUITE_END() |
|
|
|