Bitcoin ABC 0.34.2
P2P Digital Currency
voterecord_tests.cpp
Go to the documentation of this file.
1// Copyright (c) 2018-2022 The Bitcoin developers
2// Distributed under the MIT software license, see the accompanying
3// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
6
7#include <test/util/random.h>
8#include <test/util/setup_common.h>
9
10#include <boost/test/unit_test.hpp>
11
12using namespace avalanche;
13
15struct VoteRecordFixture : public BasicTestingSetup {
17
20 if (currentNodeId >= 8) {
21 currentNodeId = 0;
22 }
23 return currentNodeId;
24 }
25};
26} // namespace voterecord_tests
27
28BOOST_FIXTURE_TEST_SUITE(voterecord_tests, VoteRecordFixture)
29
30#define REGISTER_VOTE_AND_CHECK(vr, vote, state, finalized, stale, confidence) \
31 vr.registerVote(nextNodeId(), vote); \
32 BOOST_CHECK_EQUAL(vr.isAccepted(), state); \
33 BOOST_CHECK_EQUAL(vr.hasFinalized(), finalized); \
34 BOOST_CHECK_EQUAL(vr.isStale(), stale); \
35 BOOST_CHECK_EQUAL(vr.getConfidence(), confidence);
36
38 // Use a fixed seed so the 8-node cycling fixture has distinct hashes.
39 VoteRecord vraccepted(true, /*seedIn=*/0);
40
41 // Check initial state.
42 BOOST_CHECK_EQUAL(vraccepted.isAccepted(), true);
43 BOOST_CHECK_EQUAL(vraccepted.hasFinalized(), false);
44 BOOST_CHECK_EQUAL(vraccepted.isStale(), false);
45 BOOST_CHECK_EQUAL(vraccepted.getConfidence(), 0);
46
47 VoteRecord vr(false, /*seedIn=*/0);
48
49 // Check initial state.
50 BOOST_CHECK_EQUAL(vr.isAccepted(), false);
52 BOOST_CHECK_EQUAL(vr.isStale(), false);
54
55 // We need to register 6 positive votes before we start counting.
56 for (int i = 0; i < 6; i++) {
57 REGISTER_VOTE_AND_CHECK(vr, 0, false, false, false, 0);
58 }
59
60 // Next vote will flip state, and confidence will increase as long as we
61 // vote yes.
62 REGISTER_VOTE_AND_CHECK(vr, 0, true, false, false, 0);
63
64 // A single neutral vote do not change anything.
65 REGISTER_VOTE_AND_CHECK(vr, -1, true, false, false, 1);
66 for (int i = 2; i < 8; i++) {
67 REGISTER_VOTE_AND_CHECK(vr, 0, true, false, false, i);
68 }
69
70 // Two neutral votes will stall progress.
71 REGISTER_VOTE_AND_CHECK(vr, -1, true, false, false, 7);
72 REGISTER_VOTE_AND_CHECK(vr, -1, true, false, false, 7);
73 for (int i = 2; i < 8; i++) {
74 REGISTER_VOTE_AND_CHECK(vr, 0, true, false, false, 7);
75 }
76
77 // Now confidence will increase as long as we vote yes.
78 for (int i = 8; i < AVALANCHE_FINALIZATION_SCORE; i++) {
79 REGISTER_VOTE_AND_CHECK(vr, 0, true, false, false, i);
80 }
81
82 // The next vote will finalize the decision.
83 REGISTER_VOTE_AND_CHECK(vr, 1, true, true, false,
85
86 // Now that we have two no votes, confidence stop increasing.
87 for (int i = 0; i < 5; i++) {
88 REGISTER_VOTE_AND_CHECK(vr, 1, true, true, false,
90 }
91
92 // Next vote will flip state, and confidence will increase as long as we
93 // vote no.
94 REGISTER_VOTE_AND_CHECK(vr, 1, false, false, false, 0);
95
96 // A single neutral vote do not change anything.
97 REGISTER_VOTE_AND_CHECK(vr, -1, false, false, false, 1);
98 for (int i = 2; i < 8; i++) {
99 REGISTER_VOTE_AND_CHECK(vr, 1, false, false, false, i);
100 }
101
102 // Two neutral votes will stall progress.
103 REGISTER_VOTE_AND_CHECK(vr, -1, false, false, false, 7);
104 REGISTER_VOTE_AND_CHECK(vr, -1, false, false, false, 7);
105 for (int i = 2; i < 8; i++) {
106 REGISTER_VOTE_AND_CHECK(vr, 1, false, false, false, 7);
107 }
108
109 // Now confidence will increase as long as we vote no.
110 for (int i = 8; i < AVALANCHE_FINALIZATION_SCORE; i++) {
111 REGISTER_VOTE_AND_CHECK(vr, 1, false, false, false, i);
112 }
113
114 // The next vote will finalize the decision.
115 REGISTER_VOTE_AND_CHECK(vr, 0, false, true, false,
117
118 // Check that inflight accounting work as expected.
119 VoteRecord vrinflight(false, /*seedIn=*/0);
120 for (int i = 0; i < 2 * AVALANCHE_MAX_INFLIGHT_POLL; i++) {
121 bool shouldPoll = vrinflight.shouldPoll();
123 BOOST_CHECK_EQUAL(vrinflight.registerPoll(), shouldPoll);
124 }
125
126 // Clear various number of inflight requests and check everything behaves as
127 // expected.
128 for (int i = 1; i < AVALANCHE_MAX_INFLIGHT_POLL; i++) {
129 vrinflight.clearInflightRequest(i);
130 BOOST_CHECK(vrinflight.shouldPoll());
131
132 for (int j = 1; j < i; j++) {
133 BOOST_CHECK(vrinflight.registerPoll());
134 BOOST_CHECK(vrinflight.shouldPoll());
135 }
136
137 BOOST_CHECK(vrinflight.registerPoll());
138 BOOST_CHECK(!vrinflight.shouldPoll());
139 }
140
141 // Check for inflight underflow
142 VoteRecord vrunderflow(false, /*seedIn=*/0);
143 BOOST_CHECK(vrunderflow.shouldPoll());
145 // If an underflow occurred, the inflight count would be >
146 // AVALANCHE_MAX_INFLIGHT_POLL
147 BOOST_CHECK(vrunderflow.shouldPoll());
148}
149
150// Test some cases where confidence never advances
151BOOST_AUTO_TEST_CASE(stale_vote_always_inconclusive) {
152 // Setup a record that is inconclusive so far
153 VoteRecord vr(false, /*seedIn=*/0);
154
155 for (uint32_t i = 0; i < AVALANCHE_VOTE_STALE_THRESHOLD / 8; i++) {
156 // Vote randomly, but such that there's always enough neutral votes to
157 // not gain confidence.
158 for (auto j = 0; j < 6; j++) {
159 REGISTER_VOTE_AND_CHECK(vr, m_rng.rand32(), false, false, false, 0);
160 }
161 REGISTER_VOTE_AND_CHECK(vr, -1, false, false, false, 0);
162 REGISTER_VOTE_AND_CHECK(vr, -1, false, false, false, 0);
163 }
164
165 // Vote record becomes stale after too many rounds of inconclusive voting
166 REGISTER_VOTE_AND_CHECK(vr, -1, false, false, true, 0);
167}
168
169// Test all cases where records reach a specific confidence level and then go
170// stale.
171BOOST_AUTO_TEST_CASE(stale_vote_at_all_confidence_levels) {
172 for (uint32_t vote = 0; vote <= 1; vote++) {
173 for (uint32_t confidence = 0; confidence < AVALANCHE_FINALIZATION_SCORE;
174 confidence++) {
175 VoteRecord vr(!vote, /*seedIn=*/0);
176
177 // Prepare to increase confidence with some votes
178 for (auto i = 0; i < 5; i++) {
179 REGISTER_VOTE_AND_CHECK(vr, vote, !vote, false, false, 0);
180 }
181
182 // Increase to target confidence
183 for (uint32_t i = 0; i < confidence; i++) {
184 REGISTER_VOTE_AND_CHECK(vr, vote, !vote, false, false, i);
185 }
186
187 uint32_t remainingVotes =
188 AVALANCHE_VOTE_STALE_THRESHOLD - confidence - 5;
189
190 // Special case where staying at confidence of 1 requires a
191 // different vote between agreeing votes
192 if (confidence == 1) {
193 REGISTER_VOTE_AND_CHECK(vr, -1, !vote, false, false, 0);
194 REGISTER_VOTE_AND_CHECK(vr, vote, !vote, false, false, 1);
195 remainingVotes -= 2;
196 }
197
198 // Vote neutral until stale
199 if (confidence >
201 remainingVotes =
202 confidence * AVALANCHE_VOTE_STALE_FACTOR - confidence - 5;
203 }
204 for (uint32_t i = 0; i < remainingVotes; i++) {
205 REGISTER_VOTE_AND_CHECK(vr, -1, !vote, false, false,
206 confidence);
207 }
208 REGISTER_VOTE_AND_CHECK(vr, -1, !vote, false, true, confidence);
209 }
210 }
211}
212
213// Test some cases where confidence may flip flop and then goes stale.
214BOOST_AUTO_TEST_CASE(stale_vote_random_then_inconclusive) {
215 VoteRecord vr(false, /*seedIn=*/0);
216
217 for (uint32_t i = 0; i < AVALANCHE_FINALIZATION_SCORE - 14; i++) {
218 // Vote randomly. Confidence changes are ok.
219 vr.registerVote(nextNodeId(), m_rng.rand32());
220 BOOST_CHECK_EQUAL(vr.hasFinalized(), false);
221 BOOST_CHECK_EQUAL(vr.isStale(), false);
222 }
223
224 // Reset confidence, no matter what it is right now
225 for (uint32_t i = 0; i < 7; i++) {
226 vr.registerVote(nextNodeId(), 0);
227 }
228 for (uint32_t i = 0; i < 7; i++) {
229 vr.registerVote(nextNodeId(), 1);
230 }
231 BOOST_CHECK_EQUAL(vr.hasFinalized(), false);
232 BOOST_CHECK_EQUAL(vr.isStale(), false);
233
234 // Remainder of votes are neutral
235 for (uint32_t i = 0;
237 i++) {
238 REGISTER_VOTE_AND_CHECK(vr, -1, false, false, false, 1);
239 }
240
241 // Vote record becomes stale after too many rounds of voting
242 REGISTER_VOTE_AND_CHECK(vr, -1, false, false, true, 1);
243}
244
245// Test all cases where confidence flips as much as possible, ending at all
246// possible confidence levels.
247BOOST_AUTO_TEST_CASE(stale_vote_with_confidence_flips) {
248 // Start testing with yes or no votes
249 for (uint32_t voteInit = 0; voteInit <= 1; voteInit++) {
250 // Test stalling at all confidence levels
251 for (auto offset = 0; offset < AVALANCHE_FINALIZATION_SCORE; offset++) {
252 uint32_t vote = voteInit;
253 VoteRecord vr(!vote, /*seedIn=*/0);
254 uint32_t count = 0;
255
256 // Offset with neutral votes
257 for (auto i = 0; i < offset; i++) {
258 REGISTER_VOTE_AND_CHECK(vr, -1, !vote, false, false, 0);
259 count++;
260 }
261
262 // Prepare to increase confidence with some votes
263 for (auto i = 0; i < 5; i++) {
264 REGISTER_VOTE_AND_CHECK(vr, vote, !vote, false, false, 0);
265 count++;
266 }
267
268 while (true) {
269 // Increase confidence as fast as possible
270 for (uint32_t i = 0; i < AVALANCHE_FINALIZATION_SCORE - 1;
271 i++) {
275 REGISTER_VOTE_AND_CHECK(vr, vote, !vote, false, true,
276 i);
277 goto finalsanitycheck;
278 }
282 REGISTER_VOTE_AND_CHECK(vr, vote, !vote, false, true,
283 i);
284 goto finalsanitycheck;
285 }
286
287 REGISTER_VOTE_AND_CHECK(vr, vote, !vote, false, false, i);
288 count++;
289 }
290
291 // Flip the vote
292 if (vote++ >= 1) {
293 vote = 0;
294 }
295
296 // Reset confidence
297 for (auto i = 0; i < 6; i++) {
300 REGISTER_VOTE_AND_CHECK(vr, vote, vote, false, true,
302 goto finalsanitycheck;
303 }
304
305 REGISTER_VOTE_AND_CHECK(vr, vote, vote, false, false,
307 count++;
308 }
309
310 // If this fails, we are probably infinite looping for some
311 // reason
314 }
315
316 finalsanitycheck:
317 BOOST_CHECK(vr.isStale());
318 }
319 }
320}
321
322BOOST_AUTO_TEST_CASE(duplicate_votes) {
323 VoteRecord vr(true, /*seedIn=*/0);
324
325 // Register some votes, expecting confidence to increase
326 for (auto i = 0; i < 7; i++) {
328 BOOST_CHECK(!vr.registerVote(nextNodeId(), 0));
329 }
331
332 // Multiple duplicate votes do not advance confidence
333 for (auto i = 0; i < 8; i++) {
334 BOOST_CHECK(!vr.registerVote(currentNodeId, 0));
336 }
337
338 // Register more votes with duplicates mixed in. Confidence should only
339 // increase when duplicates are not used.
340 auto expectedConfidence = 1;
341 for (auto i = 0; i < 8; i++) {
342 BOOST_CHECK(!vr.registerVote(currentNodeId, 0));
343 BOOST_CHECK_EQUAL(vr.getConfidence(), expectedConfidence);
344 for (auto j = i; j < 8; j++) {
345 BOOST_CHECK(!vr.registerVote(nextNodeId(), 0));
346 BOOST_CHECK_EQUAL(vr.getConfidence(), ++expectedConfidence);
347 }
348 }
349
350 // Register enough votes to get just before finalization
351 for (auto i = 0; i < 90; i++) {
352 BOOST_CHECK(!vr.registerVote(nextNodeId(), 0));
353 BOOST_CHECK_EQUAL(vr.getConfidence(), ++expectedConfidence);
354 }
355
356 // Sanity check that finalization occurs on the expected vote
357 BOOST_CHECK(vr.registerVote(nextNodeId(), 0));
359}
360
361BOOST_AUTO_TEST_SUITE_END()
int64_t NodeId
Definition: eviction.h:16
#define BOOST_CHECK_EQUAL(v1, v2)
Definition: object.cpp:18
#define BOOST_CHECK(expr)
Definition: object.cpp:17
Vote history.
Definition: voterecord.h:50
uint16_t getConfidence() const
Definition: voterecord.h:93
bool hasFinalized() const
Definition: voterecord.h:94
bool shouldPoll() const
Return if this item is in condition to be polled at the moment.
Definition: voterecord.h:120
void clearInflightRequest(uint8_t count=1) const
Clear count inflight requests.
Definition: voterecord.cpp:99
bool registerVote(NodeId nodeid, uint32_t error)
Register a new vote for an item and update confidence accordingly.
Definition: voterecord.cpp:14
bool isStale(uint32_t staleThreshold=AVALANCHE_VOTE_STALE_THRESHOLD, uint32_t staleFactor=AVALANCHE_VOTE_STALE_FACTOR) const
Definition: voterecord.h:98
bool registerPoll() const
Register that a request is being made regarding that item.
Definition: voterecord.cpp:88
bool isAccepted() const
Vote accounting facilities.
Definition: voterecord.h:91
static int count
static constexpr uint32_t AVALANCHE_VOTE_STALE_FACTOR
Scaling factor applied to confidence to determine staleness threshold.
Definition: voterecord.h:36
static constexpr int AVALANCHE_MAX_INFLIGHT_POLL
How many inflight requests can exist for one item.
Definition: voterecord.h:41
static constexpr uint32_t AVALANCHE_VOTE_STALE_THRESHOLD
Number of votes before a record may be considered as stale.
Definition: voterecord.h:23
static constexpr int AVALANCHE_FINALIZATION_SCORE
Finalization score.
Definition: voterecord.h:18
#define REGISTER_VOTE_AND_CHECK(vr, vote, state, finalized, stale, confidence)
BOOST_AUTO_TEST_CASE(vote_record)