Bitcoin Core 28.0.0
P2P Digital Currency
Loading...
Searching...
No Matches
coinscachepair_tests.cpp
Go to the documentation of this file.
1// Copyright (c) 2024-present The Bitcoin Core developers
2// Distributed under the MIT software license, see the accompanying
3// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5#include <coins.h>
6
7#include <boost/test/unit_test.hpp>
8
9#include <list>
10
11BOOST_AUTO_TEST_SUITE(coinscachepair_tests)
12
13static constexpr auto NUM_NODES{4};
14
15std::list<CoinsCachePair> CreatePairs(CoinsCachePair& sentinel)
16{
17 std::list<CoinsCachePair> nodes;
18 for (auto i{0}; i < NUM_NODES; ++i) {
19 nodes.emplace_back();
20
21 auto node{std::prev(nodes.end())};
22 node->second.AddFlags(CCoinsCacheEntry::DIRTY, *node, sentinel);
23
25 BOOST_CHECK_EQUAL(node->second.Next(), &sentinel);
26 BOOST_CHECK_EQUAL(sentinel.second.Prev(), &(*node));
27
28 if (i > 0) {
29 BOOST_CHECK_EQUAL(std::prev(node)->second.Next(), &(*node));
30 BOOST_CHECK_EQUAL(node->second.Prev(), &(*std::prev(node)));
31 }
32 }
33 return nodes;
34}
35
36BOOST_AUTO_TEST_CASE(linked_list_iteration)
37{
38 CoinsCachePair sentinel;
39 sentinel.second.SelfRef(sentinel);
40 auto nodes{CreatePairs(sentinel)};
41
42 // Check iterating through pairs is identical to iterating through a list
43 auto node{sentinel.second.Next()};
44 for (const auto& expected : nodes) {
45 BOOST_CHECK_EQUAL(&expected, node);
46 node = node->second.Next();
47 }
48 BOOST_CHECK_EQUAL(node, &sentinel);
49
50 // Check iterating through pairs is identical to iterating through a list
51 // Clear the flags during iteration
52 node = sentinel.second.Next();
53 for (const auto& expected : nodes) {
54 BOOST_CHECK_EQUAL(&expected, node);
55 auto next = node->second.Next();
56 node->second.ClearFlags();
57 node = next;
58 }
59 BOOST_CHECK_EQUAL(node, &sentinel);
60 // Check that sentinel's next and prev are itself
61 BOOST_CHECK_EQUAL(sentinel.second.Next(), &sentinel);
62 BOOST_CHECK_EQUAL(sentinel.second.Prev(), &sentinel);
63
64 // Delete the nodes from the list to make sure there are no dangling pointers
65 for (auto it{nodes.begin()}; it != nodes.end(); it = nodes.erase(it)) {
66 BOOST_CHECK_EQUAL(it->second.GetFlags(), 0);
67 }
68}
69
70BOOST_AUTO_TEST_CASE(linked_list_iterate_erase)
71{
72 CoinsCachePair sentinel;
73 sentinel.second.SelfRef(sentinel);
74 auto nodes{CreatePairs(sentinel)};
75
76 // Check iterating through pairs is identical to iterating through a list
77 // Erase the nodes as we iterate through, but don't clear flags
78 // The flags will be cleared by the CCoinsCacheEntry's destructor
79 auto node{sentinel.second.Next()};
80 for (auto expected{nodes.begin()}; expected != nodes.end(); expected = nodes.erase(expected)) {
81 BOOST_CHECK_EQUAL(&(*expected), node);
82 node = node->second.Next();
83 }
84 BOOST_CHECK_EQUAL(node, &sentinel);
85
86 // Check that sentinel's next and prev are itself
87 BOOST_CHECK_EQUAL(sentinel.second.Next(), &sentinel);
88 BOOST_CHECK_EQUAL(sentinel.second.Prev(), &sentinel);
89}
90
91BOOST_AUTO_TEST_CASE(linked_list_random_deletion)
92{
93 CoinsCachePair sentinel;
94 sentinel.second.SelfRef(sentinel);
95 auto nodes{CreatePairs(sentinel)};
96
97 // Create linked list sentinel->n1->n2->n3->n4->sentinel
98 auto n1{nodes.begin()};
99 auto n2{std::next(n1)};
100 auto n3{std::next(n2)};
101 auto n4{std::next(n3)};
102
103 // Delete n2
104 // sentinel->n1->n3->n4->sentinel
105 nodes.erase(n2);
106 // Check that n1 now points to n3, and n3 still points to n4
107 // Also check that flags were not altered
108 BOOST_CHECK_EQUAL(n1->second.GetFlags(), CCoinsCacheEntry::DIRTY);
109 BOOST_CHECK_EQUAL(n1->second.Next(), &(*n3));
110 BOOST_CHECK_EQUAL(n3->second.GetFlags(), CCoinsCacheEntry::DIRTY);
111 BOOST_CHECK_EQUAL(n3->second.Next(), &(*n4));
112 BOOST_CHECK_EQUAL(n3->second.Prev(), &(*n1));
113
114 // Delete n1
115 // sentinel->n3->n4->sentinel
116 nodes.erase(n1);
117 // Check that sentinel now points to n3, and n3 still points to n4
118 // Also check that flags were not altered
119 BOOST_CHECK_EQUAL(n3->second.GetFlags(), CCoinsCacheEntry::DIRTY);
120 BOOST_CHECK_EQUAL(sentinel.second.Next(), &(*n3));
121 BOOST_CHECK_EQUAL(n3->second.Next(), &(*n4));
122 BOOST_CHECK_EQUAL(n3->second.Prev(), &sentinel);
123
124 // Delete n4
125 // sentinel->n3->sentinel
126 nodes.erase(n4);
127 // Check that sentinel still points to n3, and n3 points to sentinel
128 // Also check that flags were not altered
129 BOOST_CHECK_EQUAL(n3->second.GetFlags(), CCoinsCacheEntry::DIRTY);
130 BOOST_CHECK_EQUAL(sentinel.second.Next(), &(*n3));
131 BOOST_CHECK_EQUAL(n3->second.Next(), &sentinel);
132 BOOST_CHECK_EQUAL(sentinel.second.Prev(), &(*n3));
133
134 // Delete n3
135 // sentinel->sentinel
136 nodes.erase(n3);
137 // Check that sentinel's next and prev are itself
138 BOOST_CHECK_EQUAL(sentinel.second.Next(), &sentinel);
139 BOOST_CHECK_EQUAL(sentinel.second.Prev(), &sentinel);
140}
141
142BOOST_AUTO_TEST_CASE(linked_list_add_flags)
143{
144 CoinsCachePair sentinel;
145 sentinel.second.SelfRef(sentinel);
148
149 // Check that adding 0 flag has no effect
150 n1.second.AddFlags(0, n1, sentinel);
151 BOOST_CHECK_EQUAL(n1.second.GetFlags(), 0);
152 BOOST_CHECK_EQUAL(sentinel.second.Next(), &sentinel);
153 BOOST_CHECK_EQUAL(sentinel.second.Prev(), &sentinel);
154
155 // Check that adding DIRTY flag inserts it into linked list and sets flags
156 n1.second.AddFlags(CCoinsCacheEntry::DIRTY, n1, sentinel);
157 BOOST_CHECK_EQUAL(n1.second.GetFlags(), CCoinsCacheEntry::DIRTY);
158 BOOST_CHECK_EQUAL(n1.second.Next(), &sentinel);
159 BOOST_CHECK_EQUAL(n1.second.Prev(), &sentinel);
160 BOOST_CHECK_EQUAL(sentinel.second.Next(), &n1);
161 BOOST_CHECK_EQUAL(sentinel.second.Prev(), &n1);
162
163 // Check that adding FRESH flag on new node inserts it after n1
164 n2.second.AddFlags(CCoinsCacheEntry::FRESH, n2, sentinel);
165 BOOST_CHECK_EQUAL(n2.second.GetFlags(), CCoinsCacheEntry::FRESH);
166 BOOST_CHECK_EQUAL(n2.second.Next(), &sentinel);
167 BOOST_CHECK_EQUAL(n2.second.Prev(), &n1);
168 BOOST_CHECK_EQUAL(n1.second.Next(), &n2);
169 BOOST_CHECK_EQUAL(sentinel.second.Prev(), &n2);
170
171 // Check that adding 0 flag has no effect, and doesn't change position
172 n1.second.AddFlags(0, n1, sentinel);
173 BOOST_CHECK_EQUAL(n1.second.GetFlags(), CCoinsCacheEntry::DIRTY);
174 BOOST_CHECK_EQUAL(n1.second.Next(), &n2);
175 BOOST_CHECK_EQUAL(n1.second.Prev(), &sentinel);
176 BOOST_CHECK_EQUAL(sentinel.second.Next(), &n1);
177 BOOST_CHECK_EQUAL(n2.second.Prev(), &n1);
178
179 // Check that we can add extra flags, but they don't change our position
180 n1.second.AddFlags(CCoinsCacheEntry::FRESH, n1, sentinel);
182 BOOST_CHECK_EQUAL(n1.second.Next(), &n2);
183 BOOST_CHECK_EQUAL(n1.second.Prev(), &sentinel);
184 BOOST_CHECK_EQUAL(sentinel.second.Next(), &n1);
185 BOOST_CHECK_EQUAL(n2.second.Prev(), &n1);
186
187 // Check that we can clear flags then re-add them
188 n1.second.ClearFlags();
189 BOOST_CHECK_EQUAL(n1.second.GetFlags(), 0);
190 BOOST_CHECK_EQUAL(sentinel.second.Next(), &n2);
191 BOOST_CHECK_EQUAL(sentinel.second.Prev(), &n2);
192 BOOST_CHECK_EQUAL(n2.second.Next(), &sentinel);
193 BOOST_CHECK_EQUAL(n2.second.Prev(), &sentinel);
194
195 // Check that calling `ClearFlags` with 0 flags has no effect
196 n1.second.ClearFlags();
197 BOOST_CHECK_EQUAL(n1.second.GetFlags(), 0);
198 BOOST_CHECK_EQUAL(sentinel.second.Next(), &n2);
199 BOOST_CHECK_EQUAL(sentinel.second.Prev(), &n2);
200 BOOST_CHECK_EQUAL(n2.second.Next(), &sentinel);
201 BOOST_CHECK_EQUAL(n2.second.Prev(), &sentinel);
202
203 // Adding 0 still has no effect
204 n1.second.AddFlags(0, n1, sentinel);
205 BOOST_CHECK_EQUAL(sentinel.second.Next(), &n2);
206 BOOST_CHECK_EQUAL(sentinel.second.Prev(), &n2);
207 BOOST_CHECK_EQUAL(n2.second.Next(), &sentinel);
208 BOOST_CHECK_EQUAL(n2.second.Prev(), &sentinel);
209
210 // But adding DIRTY re-inserts it after n2
211 n1.second.AddFlags(CCoinsCacheEntry::DIRTY, n1, sentinel);
212 BOOST_CHECK_EQUAL(n1.second.GetFlags(), CCoinsCacheEntry::DIRTY);
213 BOOST_CHECK_EQUAL(n2.second.Next(), &n1);
214 BOOST_CHECK_EQUAL(n1.second.Prev(), &n2);
215 BOOST_CHECK_EQUAL(n1.second.Next(), &sentinel);
216 BOOST_CHECK_EQUAL(sentinel.second.Prev(), &n1);
217}
218
std::pair< const COutPoint, CCoinsCacheEntry > CoinsCachePair
Definition coins.h:91
BOOST_AUTO_TEST_CASE(linked_list_iteration)
static constexpr auto NUM_NODES
std::list< CoinsCachePair > CreatePairs(CoinsCachePair &sentinel)
BOOST_AUTO_TEST_SUITE(cuckoocache_tests)
Test Suite for CuckooCache.
BOOST_AUTO_TEST_SUITE_END()
#define BOOST_CHECK_EQUAL(v1, v2)
Definition object.cpp:18
@ FRESH
FRESH means the parent cache does not have this coin or that it is a spent coin in the parent cache.
Definition coins.h:152
@ DIRTY
DIRTY means the CCoinsCacheEntry is potentially different from the version in the parent cache.
Definition coins.h:142