@@ -565,10 +565,23 @@ const static CAmount FAIL = -3;
565565const static CAmount VALUE1 = 100 ;
566566const static CAmount VALUE2 = 200 ;
567567const static CAmount VALUE3 = 300 ;
568+
568569const static char DIRTY = CCoinsCacheEntry::DIRTY;
569570const static char FRESH = CCoinsCacheEntry::FRESH;
570571const static char NO_ENTRY = -1 ;
571572
573+ struct CoinEntry {
574+ const CAmount value;
575+ const char flags;
576+
577+ constexpr CoinEntry (const CAmount v, const char s) : value{v}, flags{s} {}
578+
579+ bool operator ==(const CoinEntry& o) const = default ;
580+ friend std::ostream& operator <<(std::ostream& os, const CoinEntry& e) { return os << e.value << " , " << e.flags ; }
581+ };
582+
583+ using MaybeCoin = std::optional<CoinEntry>;
584+
572585const static auto FLAGS = {char (0 ), FRESH, DIRTY, char (DIRTY | FRESH)};
573586const static auto CLEAN_FLAGS = {char (0 ), FRESH};
574587const static auto ABSENT_FLAGS = {NO_ENTRY};
@@ -585,77 +598,67 @@ static void SetCoinsValue(CAmount value, Coin& coin)
585598 }
586599}
587600
588- static size_t InsertCoinsMapEntry (CCoinsMap& map, CoinsCachePair& sentinel, CAmount value, char flags )
601+ static size_t InsertCoinsMapEntry (CCoinsMap& map, CoinsCachePair& sentinel, const CoinEntry& cache_coin )
589602{
590- if (value == ABSENT) {
591- assert (flags == NO_ENTRY);
603+ if (cache_coin. value == ABSENT) {
604+ assert (cache_coin. flags == NO_ENTRY);
592605 return 0 ;
593606 }
594- assert (flags != NO_ENTRY);
607+ assert (cache_coin. flags != NO_ENTRY);
595608 CCoinsCacheEntry entry;
596- SetCoinsValue (value, entry.coin );
609+ SetCoinsValue (cache_coin. value , entry.coin );
597610 auto inserted = map.emplace (OUTPOINT, std::move (entry));
598611 assert (inserted.second );
599- if (flags & DIRTY) CCoinsCacheEntry::SetDirty (*inserted.first , sentinel);
600- if (flags & FRESH) CCoinsCacheEntry::SetFresh (*inserted.first , sentinel);
612+ if (cache_coin. flags & DIRTY) CCoinsCacheEntry::SetDirty (*inserted.first , sentinel);
613+ if (cache_coin. flags & FRESH) CCoinsCacheEntry::SetFresh (*inserted.first , sentinel);
601614 return inserted.first ->second .coin .DynamicMemoryUsage ();
602615}
603616
604- void GetCoinsMapEntry (const CCoinsMap& map, CAmount& value, char & flags , const COutPoint& outp = OUTPOINT)
617+ MaybeCoin GetCoinsMapEntry (const CCoinsMap& map, const COutPoint& outp = OUTPOINT)
605618{
606- auto it = map.find (outp);
607- if (it == map.end ()) {
608- value = ABSENT;
609- flags = NO_ENTRY;
610- } else {
611- if (it->second .coin .IsSpent ()) {
612- value = SPENT;
613- } else {
614- value = it->second .coin .out .nValue ;
615- }
616- flags = 0 ;
617- if (it->second .IsDirty ()) flags |= DIRTY;
618- if (it->second .IsFresh ()) flags |= FRESH;
619- assert (flags != NO_ENTRY);
619+ if (auto it{map.find (outp)}; it != map.end ()) {
620+ return CoinEntry{
621+ it->second .coin .IsSpent () ? SPENT : it->second .coin .out .nValue ,
622+ static_cast <char >((it->second .IsDirty () ? DIRTY : 0 ) | (it->second .IsFresh () ? FRESH : 0 ))};
620623 }
624+ return CoinEntry{ABSENT, NO_ENTRY}; // TODO empty
621625}
622626
623- void WriteCoinsViewEntry (CCoinsView& view, CAmount value, char flags )
627+ void WriteCoinsViewEntry (CCoinsView& view, const CoinEntry& cache_coin )
624628{
625629 CoinsCachePair sentinel{};
626630 sentinel.second .SelfRef (sentinel);
627631 CCoinsMapMemoryResource resource;
628632 CCoinsMap map{0 , CCoinsMap::hasher{}, CCoinsMap::key_equal{}, &resource};
629- auto usage{InsertCoinsMapEntry (map, sentinel, value, flags )};
633+ auto usage{InsertCoinsMapEntry (map, sentinel, cache_coin )};
630634 auto cursor{CoinsViewCacheCursor (usage, sentinel, map, /* will_erase=*/ true )};
631635 BOOST_CHECK (view.BatchWrite (cursor, {}));
632636}
633637
634638class SingleEntryCacheTest
635639{
636640public:
637- SingleEntryCacheTest (CAmount base_value, CAmount cache_value, char cache_flags )
641+ SingleEntryCacheTest (CAmount base_value, const CoinEntry& cache_coin )
638642 {
639- WriteCoinsViewEntry (base, base_value, base_value == ABSENT ? NO_ENTRY : DIRTY);
640- cache.usage () += InsertCoinsMapEntry (cache.map (), cache.sentinel (), cache_value, cache_flags );
643+ WriteCoinsViewEntry (base, CoinEntry{ base_value, base_value == ABSENT ? NO_ENTRY : DIRTY}); // TODO complete state should be absent
644+ cache.usage () += InsertCoinsMapEntry (cache.map (), cache.sentinel (), cache_coin );
641645 }
642646
643647 CCoinsView root;
644648 CCoinsViewCacheTest base{&root};
645649 CCoinsViewCacheTest cache{&base};
646650};
647651
648- static void CheckAccessCoin (CAmount base_value, CAmount cache_value, CAmount expected_value, char cache_flags, char expected_flags )
652+ static void CheckAccessCoin (CAmount base_value, const CoinEntry& cache_coin, const CoinEntry& expected )
649653{
650- SingleEntryCacheTest test ( base_value, cache_value, cache_flags) ;
654+ SingleEntryCacheTest test{ base_value, cache_coin} ;
651655 test.cache .AccessCoin (OUTPOINT);
652656 test.cache .SelfTest (/* sanity_check=*/ false );
653-
654- CAmount result_value;
655- char result_flags;
656- GetCoinsMapEntry (test.cache .map (), result_value, result_flags);
657- BOOST_CHECK_EQUAL (result_value, expected_value);
658- BOOST_CHECK_EQUAL (result_flags, expected_flags);
657+ BOOST_CHECK_EQUAL (GetCoinsMapEntry (test.cache .map ()), expected);
658+ }
659+ static void CheckAccessCoin (CAmount base_value, CAmount cache_value, CAmount expected_value, char cache_flags, char expected_flags)
660+ {
661+ CheckAccessCoin (base_value, CoinEntry{cache_value, cache_flags}, CoinEntry{expected_value, expected_flags});
659662}
660663
661664BOOST_AUTO_TEST_CASE (ccoins_access)
@@ -696,18 +699,17 @@ BOOST_AUTO_TEST_CASE(ccoins_access)
696699 CheckAccessCoin (VALUE1, VALUE2, VALUE2, DIRTY|FRESH, DIRTY|FRESH);
697700}
698701
699- static void CheckSpendCoins (CAmount base_value, CAmount cache_value, CAmount expected_value, char cache_flags, char expected_flags )
702+ static void CheckSpendCoins (CAmount base_value, const CoinEntry& cache_coin, const CoinEntry& expected )
700703{
701- SingleEntryCacheTest test ( base_value, cache_value, cache_flags) ;
704+ SingleEntryCacheTest test{ base_value, cache_coin} ;
702705 test.cache .SpendCoin (OUTPOINT);
703706 test.cache .SelfTest ();
704-
705- CAmount result_value;
706- char result_flags;
707- GetCoinsMapEntry (test.cache .map (), result_value, result_flags);
708- BOOST_CHECK_EQUAL (result_value, expected_value);
709- BOOST_CHECK_EQUAL (result_flags, expected_flags);
710- };
707+ BOOST_CHECK_EQUAL (GetCoinsMapEntry (test.cache .map ()), expected);
708+ }
709+ static void CheckSpendCoins (CAmount base_value, CAmount cache_value, CAmount expected_value, char cache_flags, char expected_flags)
710+ {
711+ CheckSpendCoins (base_value, CoinEntry{cache_value, cache_flags}, CoinEntry{expected_value, expected_flags});
712+ }
711713
712714BOOST_AUTO_TEST_CASE (ccoins_spend)
713715{
@@ -747,25 +749,22 @@ BOOST_AUTO_TEST_CASE(ccoins_spend)
747749 CheckSpendCoins (VALUE1, VALUE2, ABSENT, DIRTY|FRESH, NO_ENTRY );
748750}
749751
750- static void CheckAddCoinBase (CAmount base_value, CAmount cache_value, CAmount modify_value, CAmount expected_value, char cache_flags, char expected_flags , bool coinbase)
752+ static void CheckAddCoinBase (CAmount base_value, CAmount modify_value, const CoinEntry& cache_coin, const CoinEntry& expected , bool coinbase)
751753{
752- SingleEntryCacheTest test (base_value, cache_value, cache_flags);
753-
754- CAmount result_value;
755- char result_flags;
754+ SingleEntryCacheTest test{base_value, cache_coin};
756755 try {
757756 CTxOut output;
758757 output.nValue = modify_value;
759758 test.cache .AddCoin (OUTPOINT, Coin (std::move (output), 1 , coinbase), coinbase);
760759 test.cache .SelfTest ();
761- GetCoinsMapEntry (test.cache .map (), result_value, result_flags );
760+ BOOST_CHECK_EQUAL ( GetCoinsMapEntry (test.cache .map ()), expected );
762761 } catch (std::logic_error&) {
763- result_value = FAIL;
764- result_flags = NO_ENTRY;
762+ BOOST_CHECK_EQUAL (CoinEntry (FAIL, NO_ENTRY), expected); // TODO empty
765763 }
766-
767- BOOST_CHECK_EQUAL (result_value, expected_value);
768- BOOST_CHECK_EQUAL (result_flags, expected_flags);
764+ }
765+ static void CheckAddCoinBase (CAmount base_value, CAmount cache_value, CAmount modify_value, CAmount expected_value, char cache_flags, char expected_flags, bool coinbase)
766+ {
767+ CheckAddCoinBase (base_value, modify_value, CoinEntry{cache_value, cache_flags}, CoinEntry{expected_value, expected_flags}, coinbase);
769768}
770769
771770// Simple wrapper for CheckAddCoinBase function above that loops through
@@ -810,23 +809,20 @@ BOOST_AUTO_TEST_CASE(ccoins_add)
810809 CheckAddCoin (VALUE2, VALUE3, VALUE3, DIRTY|FRESH, DIRTY|FRESH, true );
811810}
812811
813- void CheckWriteCoins (CAmount parent_value, CAmount child_value, CAmount expected_value, char parent_flags, char child_flags, char expected_flags )
812+ void CheckWriteCoins (const CoinEntry& parent, const CoinEntry& child, const CoinEntry& expected )
814813{
815- SingleEntryCacheTest test (ABSENT, parent_value, parent_flags);
816-
817- CAmount result_value;
818- char result_flags;
814+ SingleEntryCacheTest test{ABSENT, parent};
819815 try {
820- WriteCoinsViewEntry (test.cache , child_value, child_flags );
816+ WriteCoinsViewEntry (test.cache , child );
821817 test.cache .SelfTest (/* sanity_check=*/ false );
822- GetCoinsMapEntry (test.cache .map (), result_value, result_flags );
818+ BOOST_CHECK_EQUAL ( GetCoinsMapEntry (test.cache .map ()), expected );
823819 } catch (std::logic_error&) {
824- result_value = FAIL;
825- result_flags = NO_ENTRY;
820+ BOOST_CHECK_EQUAL (CoinEntry (FAIL, NO_ENTRY), expected); // TODO empty
826821 }
827-
828- BOOST_CHECK_EQUAL (result_value, expected_value);
829- BOOST_CHECK_EQUAL (result_flags, expected_flags);
822+ }
823+ void CheckWriteCoins (CAmount parent_value, CAmount child_value, CAmount expected_value, char parent_flags, char child_flags, char expected_flags)
824+ {
825+ CheckWriteCoins (CoinEntry{parent_value, parent_flags}, CoinEntry{child_value, child_flags}, CoinEntry{expected_value, expected_flags});
830826}
831827
832828BOOST_AUTO_TEST_CASE (ccoins_write)
@@ -923,8 +919,6 @@ void TestFlushBehavior(
923919 std::vector<std::unique_ptr<CCoinsViewCacheTest>>& all_caches,
924920 bool do_erasing_flush)
925921{
926- CAmount value;
927- char flags;
928922 size_t cache_usage;
929923 size_t cache_size;
930924
@@ -958,9 +952,7 @@ void TestFlushBehavior(
958952 BOOST_CHECK (!base.HaveCoin (outp));
959953 BOOST_CHECK (view->HaveCoin (outp));
960954
961- GetCoinsMapEntry (view->map (), value, flags, outp);
962- BOOST_CHECK_EQUAL (value, coin.out .nValue );
963- BOOST_CHECK_EQUAL (flags, DIRTY|FRESH);
955+ BOOST_CHECK_EQUAL (GetCoinsMapEntry (view->map (), outp), CoinEntry (coin.out .nValue , DIRTY|FRESH));
964956
965957 // --- 2. Flushing all caches (without erasing)
966958 //
@@ -972,9 +964,7 @@ void TestFlushBehavior(
972964
973965 // --- 3. Ensuring the entry still exists in the cache and has been written to parent
974966 //
975- GetCoinsMapEntry (view->map (), value, flags, outp);
976- BOOST_CHECK_EQUAL (value, coin.out .nValue );
977- BOOST_CHECK_EQUAL (flags, 0 ); // Flags should have been wiped.
967+ BOOST_CHECK_EQUAL (GetCoinsMapEntry (view->map (), outp), CoinEntry (coin.out .nValue , 0 )); // Flags should have been wiped.
978968
979969 // Both views should now have the coin.
980970 BOOST_CHECK (base.HaveCoin (outp));
@@ -992,14 +982,9 @@ void TestFlushBehavior(
992982
993983 // --- 5. Ensuring the entry is no longer in the cache
994984 //
995- GetCoinsMapEntry (view->map (), value, flags, outp);
996- BOOST_CHECK_EQUAL (value, ABSENT);
997- BOOST_CHECK_EQUAL (flags, NO_ENTRY);
998-
985+ BOOST_CHECK_EQUAL (GetCoinsMapEntry (view->map (), outp), CoinEntry (ABSENT, NO_ENTRY));
999986 view->AccessCoin (outp);
1000- GetCoinsMapEntry (view->map (), value, flags, outp);
1001- BOOST_CHECK_EQUAL (value, coin.out .nValue );
1002- BOOST_CHECK_EQUAL (flags, 0 );
987+ BOOST_CHECK_EQUAL (GetCoinsMapEntry (view->map (), outp), CoinEntry (coin.out .nValue , 0 ));
1003988 }
1004989
1005990 // Can't overwrite an entry without specifying that an overwrite is
@@ -1013,9 +998,7 @@ void TestFlushBehavior(
1013998 BOOST_CHECK (view->SpendCoin (outp));
1014999
10151000 // The coin should be in the cache, but spent and marked dirty.
1016- GetCoinsMapEntry (view->map (), value, flags, outp);
1017- BOOST_CHECK_EQUAL (value, SPENT);
1018- BOOST_CHECK_EQUAL (flags, DIRTY);
1001+ BOOST_CHECK_EQUAL (GetCoinsMapEntry (view->map (), outp), CoinEntry (SPENT, DIRTY));
10191002 BOOST_CHECK (!view->HaveCoin (outp)); // Coin should be considered spent in `view`.
10201003 BOOST_CHECK (base.HaveCoin (outp)); // But coin should still be unspent in `base`.
10211004
@@ -1066,20 +1049,15 @@ void TestFlushBehavior(
10661049 all_caches[0 ]->AddCoin (outp, std::move (coin), false );
10671050
10681051 // Coin should be FRESH in the cache.
1069- GetCoinsMapEntry (all_caches[0 ]->map (), value, flags, outp);
1070- BOOST_CHECK_EQUAL (value, coin_val);
1071- BOOST_CHECK_EQUAL (flags, DIRTY|FRESH);
1072-
1052+ BOOST_CHECK_EQUAL (GetCoinsMapEntry (all_caches[0 ]->map (), outp), CoinEntry (coin_val, DIRTY|FRESH));
10731053 // Base shouldn't have seen coin.
10741054 BOOST_CHECK (!base.HaveCoin (outp));
10751055
10761056 BOOST_CHECK (all_caches[0 ]->SpendCoin (outp));
10771057 all_caches[0 ]->Sync ();
10781058
10791059 // Ensure there is no sign of the coin after spend/flush.
1080- GetCoinsMapEntry (all_caches[0 ]->map (), value, flags, outp);
1081- BOOST_CHECK_EQUAL (value, ABSENT);
1082- BOOST_CHECK_EQUAL (flags, NO_ENTRY);
1060+ BOOST_CHECK_EQUAL (GetCoinsMapEntry (all_caches[0 ]->map (), outp), CoinEntry (ABSENT, NO_ENTRY));
10831061 BOOST_CHECK (!all_caches[0 ]->HaveCoinInCache (outp));
10841062 BOOST_CHECK (!base.HaveCoin (outp));
10851063}
0 commit comments