Skip to content

Commit ac10e17

Browse files
committed
Check FRESH validity in CCoinsViewCache::BatchWrite
1 parent e8cfe1e commit ac10e17

File tree

2 files changed

+25
-11
lines changed

2 files changed

+25
-11
lines changed

src/coins.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,13 @@ bool CCoinsViewCache::BatchWrite(CCoinsMap &mapCoins, const uint256 &hashBlockIn
187187
entry.flags |= CCoinsCacheEntry::FRESH;
188188
}
189189
} else {
190+
// Assert that the child cache entry was not marked FRESH if the
191+
// parent cache entry has unspent outputs. If this ever happens,
192+
// it means the FRESH flag was misapplied and there is a logic
193+
// error in the calling code.
194+
if ((it->second.flags & CCoinsCacheEntry::FRESH) && !itUs->second.coins.IsPruned())
195+
throw std::logic_error("FRESH flag misapplied to cache entry for base transaction with spendable outputs");
196+
190197
// Found the entry in the parent cache
191198
if ((itUs->second.flags & CCoinsCacheEntry::FRESH) && it->second.coins.IsPruned()) {
192199
// The grandparent does not have an entry, and the child is

src/test/coins_tests.cpp

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,7 @@ BOOST_AUTO_TEST_CASE(ccoins_serialization)
420420
const static uint256 TXID;
421421
const static CAmount PRUNED = -1;
422422
const static CAmount ABSENT = -2;
423+
const static CAmount FAIL = -3;
423424
const static CAmount VALUE1 = 100;
424425
const static CAmount VALUE2 = 200;
425426
const static CAmount VALUE3 = 300;
@@ -703,12 +704,18 @@ BOOST_AUTO_TEST_CASE(ccoins_modify_new)
703704
void CheckWriteCoins(CAmount parent_value, CAmount child_value, CAmount expected_value, char parent_flags, char child_flags, char expected_flags)
704705
{
705706
SingleEntryCacheTest test(ABSENT, parent_value, parent_flags);
706-
WriteCoinsViewEntry(test.cache, child_value, child_flags);
707-
test.cache.SelfTest();
708707

709708
CAmount result_value;
710709
char result_flags;
711-
GetCoinsMapEntry(test.cache.map(), result_value, result_flags);
710+
try {
711+
WriteCoinsViewEntry(test.cache, child_value, child_flags);
712+
test.cache.SelfTest();
713+
GetCoinsMapEntry(test.cache.map(), result_value, result_flags);
714+
} catch (std::logic_error& e) {
715+
result_value = FAIL;
716+
result_flags = NO_ENTRY;
717+
}
718+
712719
BOOST_CHECK_EQUAL(result_value, expected_value);
713720
BOOST_CHECK_EQUAL(result_flags, expected_flags);
714721
}
@@ -752,21 +759,21 @@ BOOST_AUTO_TEST_CASE(ccoins_write)
752759
CheckWriteCoins(VALUE1, ABSENT, VALUE1, DIRTY , NO_ENTRY , DIRTY );
753760
CheckWriteCoins(VALUE1, ABSENT, VALUE1, DIRTY|FRESH, NO_ENTRY , DIRTY|FRESH);
754761
CheckWriteCoins(VALUE1, PRUNED, PRUNED, 0 , DIRTY , DIRTY );
755-
CheckWriteCoins(VALUE1, PRUNED, PRUNED, 0 , DIRTY|FRESH, DIRTY );
762+
CheckWriteCoins(VALUE1, PRUNED, FAIL , 0 , DIRTY|FRESH, NO_ENTRY );
756763
CheckWriteCoins(VALUE1, PRUNED, ABSENT, FRESH , DIRTY , NO_ENTRY );
757-
CheckWriteCoins(VALUE1, PRUNED, ABSENT, FRESH , DIRTY|FRESH, NO_ENTRY );
764+
CheckWriteCoins(VALUE1, PRUNED, FAIL , FRESH , DIRTY|FRESH, NO_ENTRY );
758765
CheckWriteCoins(VALUE1, PRUNED, PRUNED, DIRTY , DIRTY , DIRTY );
759-
CheckWriteCoins(VALUE1, PRUNED, PRUNED, DIRTY , DIRTY|FRESH, DIRTY );
766+
CheckWriteCoins(VALUE1, PRUNED, FAIL , DIRTY , DIRTY|FRESH, NO_ENTRY );
760767
CheckWriteCoins(VALUE1, PRUNED, ABSENT, DIRTY|FRESH, DIRTY , NO_ENTRY );
761-
CheckWriteCoins(VALUE1, PRUNED, ABSENT, DIRTY|FRESH, DIRTY|FRESH, NO_ENTRY );
768+
CheckWriteCoins(VALUE1, PRUNED, FAIL , DIRTY|FRESH, DIRTY|FRESH, NO_ENTRY );
762769
CheckWriteCoins(VALUE1, VALUE2, VALUE2, 0 , DIRTY , DIRTY );
763-
CheckWriteCoins(VALUE1, VALUE2, VALUE2, 0 , DIRTY|FRESH, DIRTY );
770+
CheckWriteCoins(VALUE1, VALUE2, FAIL , 0 , DIRTY|FRESH, NO_ENTRY );
764771
CheckWriteCoins(VALUE1, VALUE2, VALUE2, FRESH , DIRTY , DIRTY|FRESH);
765-
CheckWriteCoins(VALUE1, VALUE2, VALUE2, FRESH , DIRTY|FRESH, DIRTY|FRESH);
772+
CheckWriteCoins(VALUE1, VALUE2, FAIL , FRESH , DIRTY|FRESH, NO_ENTRY );
766773
CheckWriteCoins(VALUE1, VALUE2, VALUE2, DIRTY , DIRTY , DIRTY );
767-
CheckWriteCoins(VALUE1, VALUE2, VALUE2, DIRTY , DIRTY|FRESH, DIRTY );
774+
CheckWriteCoins(VALUE1, VALUE2, FAIL , DIRTY , DIRTY|FRESH, NO_ENTRY );
768775
CheckWriteCoins(VALUE1, VALUE2, VALUE2, DIRTY|FRESH, DIRTY , DIRTY|FRESH);
769-
CheckWriteCoins(VALUE1, VALUE2, VALUE2, DIRTY|FRESH, DIRTY|FRESH, DIRTY|FRESH);
776+
CheckWriteCoins(VALUE1, VALUE2, FAIL , DIRTY|FRESH, DIRTY|FRESH, NO_ENTRY );
770777

771778
// The checks above omit cases where the child flags are not DIRTY, since
772779
// they would be too repetitive (the parent cache is never updated in these

0 commit comments

Comments
 (0)