mirror of
https://github.com/dashpay/dash.git
synced 2024-12-25 03:52:49 +01:00
Merge #18410: Docs: Improve commenting for coins.cpp|h
21fa0a44abe8c1b5c452e097eab20cf0ae988805 [docs] use consistent naming for possible_overwrite (John Newbery) 2685c214cce4b07695273503e60350e3f05fe3e2 [tests] small whitespace fixup (John Newbery) e9936966c08bd8a6ac02828131f619ddaa1ced13 scripted-diff: Rename PRUNED to SPENT in coins tests (John Newbery) c205979031ff4e8e32a5f05bae813405f233fccd [docs] Improve commenting in coins.cpp|h (John Newbery) Pull request description: - Add full commenting for spentness / DIRTYness / FRESHness and which combinations are valid - Remove the 'pruned' terminology, which doesn't make sense since per-txout chainstate db was merged (#10195). - Rename `potential_overwrite` to `possible_overwrite` to standardize terminology (there were previously examples of both, which made searching the codebase difficult). - Make other minor improvements to the comments ACKs for top commit: jonatack: Re-ACK 21fa0a4 per `git diff 98bee55 21fa0a4` the only change since my previous review is the following code commenting diff in `src/coins.cpp::L177-179`; rebuilt/ran unit tests anyway as a sanity check on the unit test changes. Tree-SHA512: 391e01588ef5edb417250080cec17361f982c4454bc5f8c6d78bbd528c68a2bb94373297760691295c24660ce1022ad3ef7599762f736c8eed772ce096d38c3d
This commit is contained in:
parent
aaf2a17767
commit
d0b0f08432
@ -76,8 +76,21 @@ void CCoinsViewCache::AddCoin(const COutPoint &outpoint, Coin&& coin, bool possi
|
||||
}
|
||||
if (!possible_overwrite) {
|
||||
if (!it->second.coin.IsSpent()) {
|
||||
throw std::logic_error("Adding new coin that replaces non-pruned entry");
|
||||
throw std::logic_error("Attempted to overwrite an unspent coin (when possible_overwrite is false)");
|
||||
}
|
||||
// If the coin exists in this cache as a spent coin and is DIRTY, then
|
||||
// its spentness hasn't been flushed to the parent cache. We're
|
||||
// re-adding the coin to this cache now but we can't mark it as FRESH.
|
||||
// If we mark it FRESH and then spend it before the cache is flushed
|
||||
// we would remove it from this cache and would never flush spentness
|
||||
// to the parent cache.
|
||||
//
|
||||
// Re-adding a spent coin can happen in the case of a re-org (the coin
|
||||
// is 'spent' when the block adding it is disconnected and then
|
||||
// re-added when it is also added in a newly connected block).
|
||||
//
|
||||
// If the coin doesn't exist in the current cache, or is spent but not
|
||||
// DIRTY, then it can be marked FRESH.
|
||||
fresh = !(it->second.flags & CCoinsCacheEntry::DIRTY);
|
||||
}
|
||||
it->second.coin = std::move(coin);
|
||||
@ -85,12 +98,12 @@ void CCoinsViewCache::AddCoin(const COutPoint &outpoint, Coin&& coin, bool possi
|
||||
cachedCoinsUsage += it->second.coin.DynamicMemoryUsage();
|
||||
}
|
||||
|
||||
void AddCoins(CCoinsViewCache& cache, const CTransaction &tx, int nHeight, bool check) {
|
||||
void AddCoins(CCoinsViewCache& cache, const CTransaction &tx, int nHeight, bool check_for_overwrite) {
|
||||
bool fCoinbase = tx.IsCoinBase();
|
||||
const uint256& txid = tx.GetHash();
|
||||
for (size_t i = 0; i < tx.vout.size(); ++i) {
|
||||
bool overwrite = check ? cache.HaveCoin(COutPoint(txid, i)) : fCoinbase;
|
||||
// Always set the possible_overwrite flag to AddCoin for coinbase txn, in order to correctly
|
||||
bool overwrite = check_for_overwrite ? cache.HaveCoin(COutPoint(txid, i)) : fCoinbase;
|
||||
// Coinbase transactions can always be overwritten, in order to correctly
|
||||
// deal with the pre-BIP30 occurrences of duplicate coinbase transactions.
|
||||
cache.AddCoin(COutPoint(txid, i), Coin(tx.vout[i], nHeight, fCoinbase), overwrite);
|
||||
}
|
||||
@ -151,11 +164,11 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn
|
||||
}
|
||||
CCoinsMap::iterator itUs = cacheCoins.find(it->first);
|
||||
if (itUs == cacheCoins.end()) {
|
||||
// The parent cache does not have an entry, while the child does
|
||||
// We can ignore it if it's both FRESH and pruned in the child
|
||||
// The parent cache does not have an entry, while the child cache does.
|
||||
// We can ignore it if it's both spent and FRESH in the child
|
||||
if (!(it->second.flags & CCoinsCacheEntry::FRESH && it->second.coin.IsSpent())) {
|
||||
// Otherwise we will need to create it in the parent
|
||||
// and move the data up and mark it as dirty
|
||||
// Create the coin in the parent cache, move the data up
|
||||
// and mark it as dirty.
|
||||
CCoinsCacheEntry& entry = cacheCoins[it->first];
|
||||
entry.coin = std::move(it->second.coin);
|
||||
cachedCoinsUsage += entry.coin.DynamicMemoryUsage();
|
||||
@ -168,19 +181,18 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Assert that the child cache entry was not marked FRESH if the
|
||||
// parent cache entry has unspent outputs. If this ever happens,
|
||||
// it means the FRESH flag was misapplied and there is a logic
|
||||
// error in the calling code.
|
||||
// Found the entry in the parent cache
|
||||
if ((it->second.flags & CCoinsCacheEntry::FRESH) && !itUs->second.coin.IsSpent()) {
|
||||
throw std::logic_error("FRESH flag misapplied to cache entry for base transaction with spendable outputs");
|
||||
// The coin was marked FRESH in the child cache, but the coin
|
||||
// exists in the parent cache. If this ever happens, it means
|
||||
// the FRESH flag was misapplied and there is a logic error in
|
||||
// the calling code.
|
||||
throw std::logic_error("FRESH flag misapplied to coin that exists in parent cache");
|
||||
}
|
||||
|
||||
// Found the entry in the parent cache
|
||||
if ((itUs->second.flags & CCoinsCacheEntry::FRESH) && it->second.coin.IsSpent()) {
|
||||
// The grandparent does not have an entry, and the child is
|
||||
// modified and being pruned. This means we can just delete
|
||||
// it from the parent.
|
||||
// The grandparent cache does not have an entry, and the coin
|
||||
// has been spent. We can just delete it from the parent cache.
|
||||
cachedCoinsUsage -= itUs->second.coin.DynamicMemoryUsage();
|
||||
cacheCoins.erase(itUs);
|
||||
} else {
|
||||
@ -189,11 +201,10 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn
|
||||
itUs->second.coin = std::move(it->second.coin);
|
||||
cachedCoinsUsage += itUs->second.coin.DynamicMemoryUsage();
|
||||
itUs->second.flags |= CCoinsCacheEntry::DIRTY;
|
||||
// NOTE: It is possible the child has a FRESH flag here in
|
||||
// the event the entry we found in the parent is pruned. But
|
||||
// we must not copy that FRESH flag to the parent as that
|
||||
// pruned state likely still needs to be communicated to the
|
||||
// grandparent.
|
||||
// NOTE: It isn't safe to mark the coin as FRESH in the parent
|
||||
// cache. If it already existed and was spent in the parent
|
||||
// cache then marking it FRESH would prevent that spentness
|
||||
// from being flushed to the grandparent.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
46
src/coins.h
46
src/coins.h
@ -111,19 +111,45 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A Coin in one level of the coins database caching hierarchy.
|
||||
*
|
||||
* A coin can either be:
|
||||
* - unspent or spent (in which case the Coin object will be nulled out - see Coin.Clear())
|
||||
* - DIRTY or not DIRTY
|
||||
* - FRESH or not FRESH
|
||||
*
|
||||
* Out of these 2^3 = 8 states, only some combinations are valid:
|
||||
* - unspent, FRESH, DIRTY (e.g. a new coin created in the cache)
|
||||
* - unspent, not FRESH, DIRTY (e.g. a coin changed in the cache during a reorg)
|
||||
* - unspent, not FRESH, not DIRTY (e.g. an unspent coin fetched from the parent cache)
|
||||
* - spent, FRESH, not DIRTY (e.g. a spent coin fetched from the parent cache)
|
||||
* - spent, not FRESH, DIRTY (e.g. a coin is spent and spentness needs to be flushed to the parent)
|
||||
*/
|
||||
struct CCoinsCacheEntry
|
||||
{
|
||||
Coin coin; // The actual cached data.
|
||||
unsigned char flags;
|
||||
|
||||
enum Flags {
|
||||
DIRTY = (1 << 0), // This cache entry is potentially different from the version in the parent view.
|
||||
FRESH = (1 << 1), // The parent view does not have this entry (or it is pruned).
|
||||
/* Note that FRESH is a performance optimization with which we can
|
||||
* erase coins that are fully spent if we know we do not need to
|
||||
* flush the changes to the parent cache. It is always safe to
|
||||
* not mark FRESH if that condition is not guaranteed.
|
||||
/**
|
||||
* DIRTY means the CCoinsCacheEntry is potentially different from the
|
||||
* version in the parent cache. Failure to mark a coin as DIRTY when
|
||||
* it is potentially different from the parent cache will cause a
|
||||
* consensus failure, since the coin's state won't get written to the
|
||||
* parent when the cache is flushed.
|
||||
*/
|
||||
DIRTY = (1 << 0),
|
||||
/**
|
||||
* FRESH means the parent cache does not have this coin or that it is a
|
||||
* spent coin in the parent cache. If a FRESH coin in the cache is
|
||||
* later spent, it can be deleted entirely and doesn't ever need to be
|
||||
* flushed to the parent. This is a performance optimization. Marking a
|
||||
* coin as FRESH when it exists unspent in the parent cache will cause a
|
||||
* consensus failure, since it might not be deleted from the parent
|
||||
* when this cache is flushed.
|
||||
*/
|
||||
FRESH = (1 << 1),
|
||||
};
|
||||
|
||||
CCoinsCacheEntry() : flags(0) {}
|
||||
@ -248,7 +274,7 @@ public:
|
||||
bool HaveCoinInCache(const COutPoint &outpoint) const;
|
||||
|
||||
/**
|
||||
* Return a reference to Coin in the cache, or a pruned one if not found. This is
|
||||
* Return a reference to Coin in the cache, or coinEmpty if not found. This is
|
||||
* more efficient than GetCoin.
|
||||
*
|
||||
* Generally, do not hold the reference returned for more than a short scope.
|
||||
@ -260,10 +286,10 @@ public:
|
||||
const Coin& AccessCoin(const COutPoint &output) const;
|
||||
|
||||
/**
|
||||
* Add a coin. Set potential_overwrite to true if a non-pruned version may
|
||||
* already exist.
|
||||
* Add a coin. Set possible_overwrite to true if an unspent version may
|
||||
* already exist in the cache.
|
||||
*/
|
||||
void AddCoin(const COutPoint& outpoint, Coin&& coin, bool potential_overwrite);
|
||||
void AddCoin(const COutPoint& outpoint, Coin&& coin, bool possible_overwrite);
|
||||
|
||||
/**
|
||||
* Spend a coin. Pass moveto in order to get the deleted data.
|
||||
|
@ -532,7 +532,7 @@ BOOST_AUTO_TEST_CASE(ccoins_serialization)
|
||||
}
|
||||
|
||||
const static COutPoint OUTPOINT;
|
||||
const static CAmount PRUNED = -1;
|
||||
const static CAmount SPENT = -1;
|
||||
const static CAmount ABSENT = -2;
|
||||
const static CAmount FAIL = -3;
|
||||
const static CAmount VALUE1 = 100;
|
||||
@ -551,7 +551,7 @@ static void SetCoinsValue(CAmount value, Coin& coin)
|
||||
assert(value != ABSENT);
|
||||
coin.Clear();
|
||||
assert(coin.IsSpent());
|
||||
if (value != PRUNED) {
|
||||
if (value != SPENT) {
|
||||
coin.out.nValue = value;
|
||||
coin.nHeight = 1;
|
||||
assert(!coin.IsSpent());
|
||||
@ -581,7 +581,7 @@ void GetCoinsMapEntry(const CCoinsMap& map, CAmount& value, char& flags)
|
||||
flags = NO_ENTRY;
|
||||
} else {
|
||||
if (it->second.coin.IsSpent()) {
|
||||
value = PRUNED;
|
||||
value = SPENT;
|
||||
} else {
|
||||
value = it->second.coin.out.nValue;
|
||||
}
|
||||
@ -634,28 +634,28 @@ BOOST_AUTO_TEST_CASE(ccoins_access)
|
||||
* Value Value Value Flags Flags
|
||||
*/
|
||||
CheckAccessCoin(ABSENT, ABSENT, ABSENT, NO_ENTRY , NO_ENTRY );
|
||||
CheckAccessCoin(ABSENT, PRUNED, PRUNED, 0 , 0 );
|
||||
CheckAccessCoin(ABSENT, PRUNED, PRUNED, FRESH , FRESH );
|
||||
CheckAccessCoin(ABSENT, PRUNED, PRUNED, DIRTY , DIRTY );
|
||||
CheckAccessCoin(ABSENT, PRUNED, PRUNED, DIRTY|FRESH, DIRTY|FRESH);
|
||||
CheckAccessCoin(ABSENT, SPENT , SPENT , 0 , 0 );
|
||||
CheckAccessCoin(ABSENT, SPENT , SPENT , FRESH , FRESH );
|
||||
CheckAccessCoin(ABSENT, SPENT , SPENT , DIRTY , DIRTY );
|
||||
CheckAccessCoin(ABSENT, SPENT , SPENT , DIRTY|FRESH, DIRTY|FRESH);
|
||||
CheckAccessCoin(ABSENT, VALUE2, VALUE2, 0 , 0 );
|
||||
CheckAccessCoin(ABSENT, VALUE2, VALUE2, FRESH , FRESH );
|
||||
CheckAccessCoin(ABSENT, VALUE2, VALUE2, DIRTY , DIRTY );
|
||||
CheckAccessCoin(ABSENT, VALUE2, VALUE2, DIRTY|FRESH, DIRTY|FRESH);
|
||||
CheckAccessCoin(PRUNED, ABSENT, ABSENT, NO_ENTRY , NO_ENTRY );
|
||||
CheckAccessCoin(PRUNED, PRUNED, PRUNED, 0 , 0 );
|
||||
CheckAccessCoin(PRUNED, PRUNED, PRUNED, FRESH , FRESH );
|
||||
CheckAccessCoin(PRUNED, PRUNED, PRUNED, DIRTY , DIRTY );
|
||||
CheckAccessCoin(PRUNED, PRUNED, PRUNED, DIRTY|FRESH, DIRTY|FRESH);
|
||||
CheckAccessCoin(PRUNED, VALUE2, VALUE2, 0 , 0 );
|
||||
CheckAccessCoin(PRUNED, VALUE2, VALUE2, FRESH , FRESH );
|
||||
CheckAccessCoin(PRUNED, VALUE2, VALUE2, DIRTY , DIRTY );
|
||||
CheckAccessCoin(PRUNED, VALUE2, VALUE2, DIRTY|FRESH, DIRTY|FRESH);
|
||||
CheckAccessCoin(SPENT , ABSENT, ABSENT, NO_ENTRY , NO_ENTRY );
|
||||
CheckAccessCoin(SPENT , SPENT , SPENT , 0 , 0 );
|
||||
CheckAccessCoin(SPENT , SPENT , SPENT , FRESH , FRESH );
|
||||
CheckAccessCoin(SPENT , SPENT , SPENT , DIRTY , DIRTY );
|
||||
CheckAccessCoin(SPENT , SPENT , SPENT , DIRTY|FRESH, DIRTY|FRESH);
|
||||
CheckAccessCoin(SPENT , VALUE2, VALUE2, 0 , 0 );
|
||||
CheckAccessCoin(SPENT , VALUE2, VALUE2, FRESH , FRESH );
|
||||
CheckAccessCoin(SPENT , VALUE2, VALUE2, DIRTY , DIRTY );
|
||||
CheckAccessCoin(SPENT , VALUE2, VALUE2, DIRTY|FRESH, DIRTY|FRESH);
|
||||
CheckAccessCoin(VALUE1, ABSENT, VALUE1, NO_ENTRY , 0 );
|
||||
CheckAccessCoin(VALUE1, PRUNED, PRUNED, 0 , 0 );
|
||||
CheckAccessCoin(VALUE1, PRUNED, PRUNED, FRESH , FRESH );
|
||||
CheckAccessCoin(VALUE1, PRUNED, PRUNED, DIRTY , DIRTY );
|
||||
CheckAccessCoin(VALUE1, PRUNED, PRUNED, DIRTY|FRESH, DIRTY|FRESH);
|
||||
CheckAccessCoin(VALUE1, SPENT , SPENT , 0 , 0 );
|
||||
CheckAccessCoin(VALUE1, SPENT , SPENT , FRESH , FRESH );
|
||||
CheckAccessCoin(VALUE1, SPENT , SPENT , DIRTY , DIRTY );
|
||||
CheckAccessCoin(VALUE1, SPENT , SPENT , DIRTY|FRESH, DIRTY|FRESH);
|
||||
CheckAccessCoin(VALUE1, VALUE2, VALUE2, 0 , 0 );
|
||||
CheckAccessCoin(VALUE1, VALUE2, VALUE2, FRESH , FRESH );
|
||||
CheckAccessCoin(VALUE1, VALUE2, VALUE2, DIRTY , DIRTY );
|
||||
@ -685,31 +685,31 @@ BOOST_AUTO_TEST_CASE(ccoins_spend)
|
||||
* Value Value Value Flags Flags
|
||||
*/
|
||||
CheckSpendCoins(ABSENT, ABSENT, ABSENT, NO_ENTRY , NO_ENTRY );
|
||||
CheckSpendCoins(ABSENT, PRUNED, PRUNED, 0 , DIRTY );
|
||||
CheckSpendCoins(ABSENT, PRUNED, ABSENT, FRESH , NO_ENTRY );
|
||||
CheckSpendCoins(ABSENT, PRUNED, PRUNED, DIRTY , DIRTY );
|
||||
CheckSpendCoins(ABSENT, PRUNED, ABSENT, DIRTY|FRESH, NO_ENTRY );
|
||||
CheckSpendCoins(ABSENT, VALUE2, PRUNED, 0 , DIRTY );
|
||||
CheckSpendCoins(ABSENT, SPENT , SPENT , 0 , DIRTY );
|
||||
CheckSpendCoins(ABSENT, SPENT , ABSENT, FRESH , NO_ENTRY );
|
||||
CheckSpendCoins(ABSENT, SPENT , SPENT , DIRTY , DIRTY );
|
||||
CheckSpendCoins(ABSENT, SPENT , ABSENT, DIRTY|FRESH, NO_ENTRY );
|
||||
CheckSpendCoins(ABSENT, VALUE2, SPENT , 0 , DIRTY );
|
||||
CheckSpendCoins(ABSENT, VALUE2, ABSENT, FRESH , NO_ENTRY );
|
||||
CheckSpendCoins(ABSENT, VALUE2, PRUNED, DIRTY , DIRTY );
|
||||
CheckSpendCoins(ABSENT, VALUE2, SPENT , DIRTY , DIRTY );
|
||||
CheckSpendCoins(ABSENT, VALUE2, ABSENT, DIRTY|FRESH, NO_ENTRY );
|
||||
CheckSpendCoins(PRUNED, ABSENT, ABSENT, NO_ENTRY , NO_ENTRY );
|
||||
CheckSpendCoins(PRUNED, PRUNED, PRUNED, 0 , DIRTY );
|
||||
CheckSpendCoins(PRUNED, PRUNED, ABSENT, FRESH , NO_ENTRY );
|
||||
CheckSpendCoins(PRUNED, PRUNED, PRUNED, DIRTY , DIRTY );
|
||||
CheckSpendCoins(PRUNED, PRUNED, ABSENT, DIRTY|FRESH, NO_ENTRY );
|
||||
CheckSpendCoins(PRUNED, VALUE2, PRUNED, 0 , DIRTY );
|
||||
CheckSpendCoins(PRUNED, VALUE2, ABSENT, FRESH , NO_ENTRY );
|
||||
CheckSpendCoins(PRUNED, VALUE2, PRUNED, DIRTY , DIRTY );
|
||||
CheckSpendCoins(PRUNED, VALUE2, ABSENT, DIRTY|FRESH, NO_ENTRY );
|
||||
CheckSpendCoins(VALUE1, ABSENT, PRUNED, NO_ENTRY , DIRTY );
|
||||
CheckSpendCoins(VALUE1, PRUNED, PRUNED, 0 , DIRTY );
|
||||
CheckSpendCoins(VALUE1, PRUNED, ABSENT, FRESH , NO_ENTRY );
|
||||
CheckSpendCoins(VALUE1, PRUNED, PRUNED, DIRTY , DIRTY );
|
||||
CheckSpendCoins(VALUE1, PRUNED, ABSENT, DIRTY|FRESH, NO_ENTRY );
|
||||
CheckSpendCoins(VALUE1, VALUE2, PRUNED, 0 , DIRTY );
|
||||
CheckSpendCoins(SPENT , ABSENT, ABSENT, NO_ENTRY , NO_ENTRY );
|
||||
CheckSpendCoins(SPENT , SPENT , SPENT , 0 , DIRTY );
|
||||
CheckSpendCoins(SPENT , SPENT , ABSENT, FRESH , NO_ENTRY );
|
||||
CheckSpendCoins(SPENT , SPENT , SPENT , DIRTY , DIRTY );
|
||||
CheckSpendCoins(SPENT , SPENT , ABSENT, DIRTY|FRESH, NO_ENTRY );
|
||||
CheckSpendCoins(SPENT , VALUE2, SPENT , 0 , DIRTY );
|
||||
CheckSpendCoins(SPENT , VALUE2, ABSENT, FRESH , NO_ENTRY );
|
||||
CheckSpendCoins(SPENT , VALUE2, SPENT , DIRTY , DIRTY );
|
||||
CheckSpendCoins(SPENT , VALUE2, ABSENT, DIRTY|FRESH, NO_ENTRY );
|
||||
CheckSpendCoins(VALUE1, ABSENT, SPENT , NO_ENTRY , DIRTY );
|
||||
CheckSpendCoins(VALUE1, SPENT , SPENT , 0 , DIRTY );
|
||||
CheckSpendCoins(VALUE1, SPENT , ABSENT, FRESH , NO_ENTRY );
|
||||
CheckSpendCoins(VALUE1, SPENT , SPENT , DIRTY , DIRTY );
|
||||
CheckSpendCoins(VALUE1, SPENT , ABSENT, DIRTY|FRESH, NO_ENTRY );
|
||||
CheckSpendCoins(VALUE1, VALUE2, SPENT , 0 , DIRTY );
|
||||
CheckSpendCoins(VALUE1, VALUE2, ABSENT, FRESH , NO_ENTRY );
|
||||
CheckSpendCoins(VALUE1, VALUE2, PRUNED, DIRTY , DIRTY );
|
||||
CheckSpendCoins(VALUE1, VALUE2, SPENT , DIRTY , DIRTY );
|
||||
CheckSpendCoins(VALUE1, VALUE2, ABSENT, DIRTY|FRESH, NO_ENTRY );
|
||||
}
|
||||
|
||||
@ -742,7 +742,7 @@ static void CheckAddCoinBase(CAmount base_value, CAmount cache_value, CAmount mo
|
||||
template <typename... Args>
|
||||
static void CheckAddCoin(Args&&... args)
|
||||
{
|
||||
for (const CAmount base_value : {ABSENT, PRUNED, VALUE1})
|
||||
for (const CAmount base_value : {ABSENT, SPENT, VALUE1})
|
||||
CheckAddCoinBase(base_value, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
@ -751,21 +751,21 @@ BOOST_AUTO_TEST_CASE(ccoins_add)
|
||||
/* Check AddCoin 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 AddCoin potential_overwrite argument set to false, and to true.
|
||||
* AddCoin possible_overwrite argument set to false, and to true.
|
||||
*
|
||||
* Cache Write Result Cache Result potential_overwrite
|
||||
* Cache Write Result Cache Result possible_overwrite
|
||||
* Value Value Value Flags Flags
|
||||
*/
|
||||
CheckAddCoin(ABSENT, VALUE3, VALUE3, NO_ENTRY , DIRTY|FRESH, false);
|
||||
CheckAddCoin(ABSENT, VALUE3, VALUE3, NO_ENTRY , DIRTY , true );
|
||||
CheckAddCoin(PRUNED, VALUE3, VALUE3, 0 , DIRTY|FRESH, false);
|
||||
CheckAddCoin(PRUNED, VALUE3, VALUE3, 0 , DIRTY , true );
|
||||
CheckAddCoin(PRUNED, VALUE3, VALUE3, FRESH , DIRTY|FRESH, false);
|
||||
CheckAddCoin(PRUNED, VALUE3, VALUE3, FRESH , DIRTY|FRESH, true );
|
||||
CheckAddCoin(PRUNED, VALUE3, VALUE3, DIRTY , DIRTY , false);
|
||||
CheckAddCoin(PRUNED, VALUE3, VALUE3, DIRTY , DIRTY , true );
|
||||
CheckAddCoin(PRUNED, VALUE3, VALUE3, DIRTY|FRESH, DIRTY|FRESH, false);
|
||||
CheckAddCoin(PRUNED, VALUE3, VALUE3, DIRTY|FRESH, DIRTY|FRESH, true );
|
||||
CheckAddCoin(SPENT , VALUE3, VALUE3, 0 , DIRTY|FRESH, false);
|
||||
CheckAddCoin(SPENT , VALUE3, VALUE3, 0 , DIRTY , true );
|
||||
CheckAddCoin(SPENT , VALUE3, VALUE3, FRESH , DIRTY|FRESH, false);
|
||||
CheckAddCoin(SPENT , VALUE3, VALUE3, FRESH , DIRTY|FRESH, true );
|
||||
CheckAddCoin(SPENT , VALUE3, VALUE3, DIRTY , DIRTY , false);
|
||||
CheckAddCoin(SPENT , VALUE3, VALUE3, DIRTY , DIRTY , true );
|
||||
CheckAddCoin(SPENT , VALUE3, VALUE3, DIRTY|FRESH, DIRTY|FRESH, false);
|
||||
CheckAddCoin(SPENT , VALUE3, VALUE3, DIRTY|FRESH, DIRTY|FRESH, true );
|
||||
CheckAddCoin(VALUE2, VALUE3, FAIL , 0 , NO_ENTRY , false);
|
||||
CheckAddCoin(VALUE2, VALUE3, VALUE3, 0 , DIRTY , true );
|
||||
CheckAddCoin(VALUE2, VALUE3, FAIL , FRESH , NO_ENTRY , false);
|
||||
@ -805,42 +805,42 @@ BOOST_AUTO_TEST_CASE(ccoins_write)
|
||||
* 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, SPENT , SPENT , NO_ENTRY , DIRTY , DIRTY );
|
||||
CheckWriteCoins(ABSENT, SPENT , 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(SPENT , ABSENT, SPENT , 0 , NO_ENTRY , 0 );
|
||||
CheckWriteCoins(SPENT , ABSENT, SPENT , FRESH , NO_ENTRY , FRESH );
|
||||
CheckWriteCoins(SPENT , ABSENT, SPENT , DIRTY , NO_ENTRY , DIRTY );
|
||||
CheckWriteCoins(SPENT , ABSENT, SPENT , DIRTY|FRESH, NO_ENTRY , DIRTY|FRESH);
|
||||
CheckWriteCoins(SPENT , SPENT , SPENT , 0 , DIRTY , DIRTY );
|
||||
CheckWriteCoins(SPENT , SPENT , SPENT , 0 , DIRTY|FRESH, DIRTY );
|
||||
CheckWriteCoins(SPENT , SPENT , ABSENT, FRESH , DIRTY , NO_ENTRY );
|
||||
CheckWriteCoins(SPENT , SPENT , ABSENT, FRESH , DIRTY|FRESH, NO_ENTRY );
|
||||
CheckWriteCoins(SPENT , SPENT , SPENT , DIRTY , DIRTY , DIRTY );
|
||||
CheckWriteCoins(SPENT , SPENT , SPENT , DIRTY , DIRTY|FRESH, DIRTY );
|
||||
CheckWriteCoins(SPENT , SPENT , ABSENT, DIRTY|FRESH, DIRTY , NO_ENTRY );
|
||||
CheckWriteCoins(SPENT , SPENT , ABSENT, DIRTY|FRESH, DIRTY|FRESH, NO_ENTRY );
|
||||
CheckWriteCoins(SPENT , VALUE2, VALUE2, 0 , DIRTY , DIRTY );
|
||||
CheckWriteCoins(SPENT , VALUE2, VALUE2, 0 , DIRTY|FRESH, DIRTY );
|
||||
CheckWriteCoins(SPENT , VALUE2, VALUE2, FRESH , DIRTY , DIRTY|FRESH);
|
||||
CheckWriteCoins(SPENT , VALUE2, VALUE2, FRESH , DIRTY|FRESH, DIRTY|FRESH);
|
||||
CheckWriteCoins(SPENT , VALUE2, VALUE2, DIRTY , DIRTY , DIRTY );
|
||||
CheckWriteCoins(SPENT , VALUE2, VALUE2, DIRTY , DIRTY|FRESH, DIRTY );
|
||||
CheckWriteCoins(SPENT , VALUE2, VALUE2, DIRTY|FRESH, DIRTY , DIRTY|FRESH);
|
||||
CheckWriteCoins(SPENT , 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, FAIL , 0 , DIRTY|FRESH, NO_ENTRY );
|
||||
CheckWriteCoins(VALUE1, PRUNED, ABSENT, FRESH , DIRTY , NO_ENTRY );
|
||||
CheckWriteCoins(VALUE1, PRUNED, FAIL , FRESH , DIRTY|FRESH, NO_ENTRY );
|
||||
CheckWriteCoins(VALUE1, PRUNED, PRUNED, DIRTY , DIRTY , DIRTY );
|
||||
CheckWriteCoins(VALUE1, PRUNED, FAIL , DIRTY , DIRTY|FRESH, NO_ENTRY );
|
||||
CheckWriteCoins(VALUE1, PRUNED, ABSENT, DIRTY|FRESH, DIRTY , NO_ENTRY );
|
||||
CheckWriteCoins(VALUE1, PRUNED, FAIL , DIRTY|FRESH, DIRTY|FRESH, NO_ENTRY );
|
||||
CheckWriteCoins(VALUE1, SPENT , SPENT , 0 , DIRTY , DIRTY );
|
||||
CheckWriteCoins(VALUE1, SPENT , FAIL , 0 , DIRTY|FRESH, NO_ENTRY );
|
||||
CheckWriteCoins(VALUE1, SPENT , ABSENT, FRESH , DIRTY , NO_ENTRY );
|
||||
CheckWriteCoins(VALUE1, SPENT , FAIL , FRESH , DIRTY|FRESH, NO_ENTRY );
|
||||
CheckWriteCoins(VALUE1, SPENT , SPENT , DIRTY , DIRTY , DIRTY );
|
||||
CheckWriteCoins(VALUE1, SPENT , FAIL , DIRTY , DIRTY|FRESH, NO_ENTRY );
|
||||
CheckWriteCoins(VALUE1, SPENT , ABSENT, DIRTY|FRESH, DIRTY , NO_ENTRY );
|
||||
CheckWriteCoins(VALUE1, SPENT , FAIL , DIRTY|FRESH, DIRTY|FRESH, NO_ENTRY );
|
||||
CheckWriteCoins(VALUE1, VALUE2, VALUE2, 0 , DIRTY , DIRTY );
|
||||
CheckWriteCoins(VALUE1, VALUE2, FAIL , 0 , DIRTY|FRESH, NO_ENTRY );
|
||||
CheckWriteCoins(VALUE1, VALUE2, VALUE2, FRESH , DIRTY , DIRTY|FRESH);
|
||||
@ -854,8 +854,8 @@ BOOST_AUTO_TEST_CASE(ccoins_write)
|
||||
// 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 (const CAmount parent_value : {ABSENT, PRUNED, VALUE1})
|
||||
for (const CAmount child_value : {ABSENT, PRUNED, VALUE2})
|
||||
for (const CAmount parent_value : {ABSENT, SPENT, VALUE1})
|
||||
for (const CAmount child_value : {ABSENT, SPENT, VALUE2})
|
||||
for (const char parent_flags : parent_value == ABSENT ? ABSENT_FLAGS : FLAGS)
|
||||
for (const char child_flags : child_value == ABSENT ? ABSENT_FLAGS : CLEAN_FLAGS)
|
||||
CheckWriteCoins(parent_value, child_value, parent_value, parent_flags, child_flags, parent_flags);
|
||||
|
@ -1655,10 +1655,11 @@ int ApplyTxInUndo(Coin&& undo, CCoinsViewCache& view, const COutPoint& out)
|
||||
return DISCONNECT_FAILED; // adding output for transaction without known metadata
|
||||
}
|
||||
}
|
||||
// The potential_overwrite parameter to AddCoin is only allowed to be false if we know for
|
||||
// sure that the coin did not already exist in the cache. As we have queried for that above
|
||||
// using HaveCoin, we don't need to guess. When fClean is false, a coin already existed and
|
||||
// it is an overwrite.
|
||||
// If the coin already exists as an unspent coin in the cache, then the
|
||||
// possible_overwrite parameter to AddCoin must be set to true. We have
|
||||
// already checked whether an unspent coin exists above using HaveCoin, so
|
||||
// we don't need to guess. When fClean is false, an unspent coin already
|
||||
// existed and it is an overwrite.
|
||||
view.AddCoin(out, std::move(undo), !fClean);
|
||||
|
||||
return fClean ? DISCONNECT_OK : DISCONNECT_UNCLEAN;
|
||||
|
Loading…
Reference in New Issue
Block a user