Bitcoin ABC 0.32.6
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 VoteRecord vraccepted(true);
39
40 // Check initial state.
41 BOOST_CHECK_EQUAL(vraccepted.isAccepted(), true);
42 BOOST_CHECK_EQUAL(vraccepted.hasFinalized(), false);
43 BOOST_CHECK_EQUAL(vraccepted.isStale(), false);
44 BOOST_CHECK_EQUAL(vraccepted.getConfidence(), 0);
45
46 VoteRecord vr(false);
47
48 // Check initial state.
49 BOOST_CHECK_EQUAL(vr.isAccepted(), false);
51 BOOST_CHECK_EQUAL(vr.isStale(), false);
53
54 // We need to register 6 positive votes before we start counting.
55 for (int i = 0; i < 6; i++) {
56 REGISTER_VOTE_AND_CHECK(vr, 0, false, false, false, 0);
57 }
58
59 // Next vote will flip state, and confidence will increase as long as we
60 // vote yes.
61 REGISTER_VOTE_AND_CHECK(vr, 0, true, false, false, 0);
62
63 // A single neutral vote do not change anything.
64 REGISTER_VOTE_AND_CHECK(vr, -1, true, false, false, 1);
65 for (int i = 2; i < 8; i++) {
66 REGISTER_VOTE_AND_CHECK(vr, 0, true, false, false, i);
67 }
68
69 // Two neutral votes will stall progress.
70 REGISTER_VOTE_AND_CHECK(vr, -1, true, false, false, 7);
71 REGISTER_VOTE_AND_CHECK(vr, -1, true, false, false, 7);
72 for (int i = 2; i < 8; i++) {
73 REGISTER_VOTE_AND_CHECK(vr, 0, true, false, false, 7);
74 }
75
76 // Now confidence will increase as long as we vote yes.
77 for (int i = 8; i < AVALANCHE_FINALIZATION_SCORE; i++) {
78 REGISTER_VOTE_AND_CHECK(vr, 0, true, false, false, i);
79 }
80
81 // The next vote will finalize the decision.
82 REGISTER_VOTE_AND_CHECK(vr, 1, true, true, false,
84
85 // Now that we have two no votes, confidence stop increasing.
86 for (int i = 0; i < 5; i++) {
87 REGISTER_VOTE_AND_CHECK(vr, 1, true, true, false,
89 }
90
91 // Next vote will flip state, and confidence will increase as long as we
92 // vote no.
93 REGISTER_VOTE_AND_CHECK(vr, 1, false, false, false, 0);
94
95 // A single neutral vote do not change anything.
96 REGISTER_VOTE_AND_CHECK(vr, -1, false, false, false, 1);
97 for (int i = 2; i < 8; i++) {
98 REGISTER_VOTE_AND_CHECK(vr, 1, false, false, false, i);
99 }
100
101 // Two neutral votes will stall progress.
102 REGISTER_VOTE_AND_CHECK(vr, -1, false, false, false, 7);
103 REGISTER_VOTE_AND_CHECK(vr, -1, false, false, false, 7);
104 for (int i = 2; i < 8; i++) {
105 REGISTER_VOTE_AND_CHECK(vr, 1, false, false, false, 7);
106 }
107
108 // Now confidence will increase as long as we vote no.
109 for (int i = 8; i < AVALANCHE_FINALIZATION_SCORE; i++) {
110 REGISTER_VOTE_AND_CHECK(vr, 1, false, false, false, i);
111 }
112
113 // The next vote will finalize the decision.
114 REGISTER_VOTE_AND_CHECK(vr, 0, false, true, false,
116
117 // Check that inflight accounting work as expected.
118 VoteRecord vrinflight(false);
119 for (int i = 0; i < 2 * AVALANCHE_MAX_INFLIGHT_POLL; i++) {
120 bool shouldPoll = vrinflight.shouldPoll();
122 BOOST_CHECK_EQUAL(vrinflight.registerPoll(), shouldPoll);
123 }
124
125 // Clear various number of inflight requests and check everything behaves as
126 // expected.
127 for (int i = 1; i < AVALANCHE_MAX_INFLIGHT_POLL; i++) {
128 vrinflight.clearInflightRequest(i);
129 BOOST_CHECK(vrinflight.shouldPoll());
130
131 for (int j = 1; j < i; j++) {
132 BOOST_CHECK(vrinflight.registerPoll());
133 BOOST_CHECK(vrinflight.shouldPoll());
134 }
135
136 BOOST_CHECK(vrinflight.registerPoll());
137 BOOST_CHECK(!vrinflight.shouldPoll());
138 }
139}
140
141// Test some cases where confidence never advances
142BOOST_AUTO_TEST_CASE(stale_vote_always_inconclusive) {
143 // Setup a record that is inconclusive so far
144 VoteRecord vr(false);
145
146 for (uint32_t i = 0; i < AVALANCHE_VOTE_STALE_THRESHOLD / 8; i++) {
147 // Vote randomly, but such that there's always enough neutral votes to
148 // not gain confidence.
149 for (auto j = 0; j < 6; j++) {
150 REGISTER_VOTE_AND_CHECK(vr, m_rng.rand32(), false, false, false, 0);
151 }
152 REGISTER_VOTE_AND_CHECK(vr, -1, false, false, false, 0);
153 REGISTER_VOTE_AND_CHECK(vr, -1, false, false, false, 0);
154 }
155
156 // Vote record becomes stale after too many rounds of inconclusive voting
157 REGISTER_VOTE_AND_CHECK(vr, -1, false, false, true, 0);
158}
159
160// Test all cases where records reach a specific confidence level and then go
161// stale.
162BOOST_AUTO_TEST_CASE(stale_vote_at_all_confidence_levels) {
163 for (uint32_t vote = 0; vote <= 1; vote++) {
164 for (uint32_t confidence = 0; confidence < AVALANCHE_FINALIZATION_SCORE;
165 confidence++) {
166 VoteRecord vr(!vote);
167
168 // Prepare to increase confidence with some votes
169 for (auto i = 0; i < 5; i++) {
170 REGISTER_VOTE_AND_CHECK(vr, vote, !vote, false, false, 0);
171 }
172
173 // Increase to target confidence
174 for (uint32_t i = 0; i < confidence; i++) {
175 REGISTER_VOTE_AND_CHECK(vr, vote, !vote, false, false, i);
176 }
177
178 uint32_t remainingVotes =
179 AVALANCHE_VOTE_STALE_THRESHOLD - confidence - 5;
180
181 // Special case where staying at confidence of 1 requires a
182 // different vote between agreeing votes
183 if (confidence == 1) {
184 REGISTER_VOTE_AND_CHECK(vr, -1, !vote, false, false, 0);
185 REGISTER_VOTE_AND_CHECK(vr, vote, !vote, false, false, 1);
186 remainingVotes -= 2;
187 }
188
189 // Vote neutral until stale
190 if (confidence >
192 remainingVotes =
193 confidence * AVALANCHE_VOTE_STALE_FACTOR - confidence - 5;
194 }
195 for (uint32_t i = 0; i < remainingVotes; i++) {
196 REGISTER_VOTE_AND_CHECK(vr, -1, !vote, false, false,
197 confidence);
198 }
199 REGISTER_VOTE_AND_CHECK(vr, -1, !vote, false, true, confidence);
200 }
201 }
202}
203
204// Test some cases where confidence may flip flop and then goes stale.
205BOOST_AUTO_TEST_CASE(stale_vote_random_then_inconclusive) {
206 VoteRecord vr(false);
207
208 for (uint32_t i = 0; i < AVALANCHE_FINALIZATION_SCORE - 14; i++) {
209 // Vote randomly. Confidence changes are ok.
210 vr.registerVote(nextNodeId(), m_rng.rand32());
211 BOOST_CHECK_EQUAL(vr.hasFinalized(), false);
212 BOOST_CHECK_EQUAL(vr.isStale(), false);
213 }
214
215 // Reset confidence, no matter what it is right now
216 for (uint32_t i = 0; i < 7; i++) {
217 vr.registerVote(nextNodeId(), 0);
218 }
219 for (uint32_t i = 0; i < 7; i++) {
220 vr.registerVote(nextNodeId(), 1);
221 }
222 BOOST_CHECK_EQUAL(vr.hasFinalized(), false);
223 BOOST_CHECK_EQUAL(vr.isStale(), false);
224
225 // Remainder of votes are neutral
226 for (uint32_t i = 0;
228 i++) {
229 REGISTER_VOTE_AND_CHECK(vr, -1, false, false, false, 1);
230 }
231
232 // Vote record becomes stale after too many rounds of voting
233 REGISTER_VOTE_AND_CHECK(vr, -1, false, false, true, 1);
234}
235
236// Test all cases where confidence flips as much as possible, ending at all
237// possible confidence levels.
238BOOST_AUTO_TEST_CASE(stale_vote_with_confidence_flips) {
239 // Start testing with yes or no votes
240 for (uint32_t voteInit = 0; voteInit <= 1; voteInit++) {
241 // Test stalling at all confidence levels
242 for (auto offset = 0; offset < AVALANCHE_FINALIZATION_SCORE; offset++) {
243 uint32_t vote = voteInit;
244 VoteRecord vr(!vote);
245 uint32_t count = 0;
246
247 // Offset with neutral votes
248 for (auto i = 0; i < offset; i++) {
249 REGISTER_VOTE_AND_CHECK(vr, -1, !vote, false, false, 0);
250 count++;
251 }
252
253 // Prepare to increase confidence with some votes
254 for (auto i = 0; i < 5; i++) {
255 REGISTER_VOTE_AND_CHECK(vr, vote, !vote, false, false, 0);
256 count++;
257 }
258
259 while (true) {
260 // Increase confidence as fast as possible
261 for (uint32_t i = 0; i < AVALANCHE_FINALIZATION_SCORE - 1;
262 i++) {
266 REGISTER_VOTE_AND_CHECK(vr, vote, !vote, false, true,
267 i);
268 goto finalsanitycheck;
269 }
273 REGISTER_VOTE_AND_CHECK(vr, vote, !vote, false, true,
274 i);
275 goto finalsanitycheck;
276 }
277
278 REGISTER_VOTE_AND_CHECK(vr, vote, !vote, false, false, i);
279 count++;
280 }
281
282 // Flip the vote
283 if (vote++ >= 1) {
284 vote = 0;
285 }
286
287 // Reset confidence
288 for (auto i = 0; i < 6; i++) {
291 REGISTER_VOTE_AND_CHECK(vr, vote, vote, false, true,
293 goto finalsanitycheck;
294 }
295
296 REGISTER_VOTE_AND_CHECK(vr, vote, vote, false, false,
298 count++;
299 }
300
301 // If this fails, we are probably infinite looping for some
302 // reason
305 }
306
307 finalsanitycheck:
308 BOOST_CHECK(vr.isStale());
309 }
310 }
311}
312
313BOOST_AUTO_TEST_CASE(duplicate_votes) {
314 VoteRecord vr(true);
315
316 // Register some votes, expecting confidence to increase
317 for (auto i = 0; i < 7; i++) {
319 BOOST_CHECK(!vr.registerVote(nextNodeId(), 0));
320 }
322
323 // Multiple duplicate votes do not advance confidence
324 for (auto i = 0; i < 8; i++) {
325 BOOST_CHECK(!vr.registerVote(currentNodeId, 0));
327 }
328
329 // Register more votes with duplicates mixed in. Confidence should only
330 // increase when duplicates are not used.
331 auto expectedConfidence = 1;
332 for (auto i = 0; i < 8; i++) {
333 BOOST_CHECK(!vr.registerVote(currentNodeId, 0));
334 BOOST_CHECK_EQUAL(vr.getConfidence(), expectedConfidence);
335 for (auto j = i; j < 8; j++) {
336 BOOST_CHECK(!vr.registerVote(nextNodeId(), 0));
337 BOOST_CHECK_EQUAL(vr.getConfidence(), ++expectedConfidence);
338 }
339 }
340
341 // Register enough votes to get just before finalization
342 for (auto i = 0; i < 90; i++) {
343 BOOST_CHECK(!vr.registerVote(nextNodeId(), 0));
344 BOOST_CHECK_EQUAL(vr.getConfidence(), ++expectedConfidence);
345 }
346
347 // Sanity check that finalization occurs on the expected vote
348 BOOST_CHECK(vr.registerVote(nextNodeId(), 0));
350}
351
352BOOST_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:49
uint16_t getConfidence() const
Definition: voterecord.h:88
void clearInflightRequest(uint8_t count=1)
Clear count inflight requests.
Definition: voterecord.h:120
bool hasFinalized() const
Definition: voterecord.h:89
bool shouldPoll() const
Return if this item is in condition to be polled at the moment.
Definition: voterecord.h:115
bool registerVote(NodeId nodeid, uint32_t error)
Register a new vote for an item and update confidence accordingly.
Definition: voterecord.cpp:13
bool isStale(uint32_t staleThreshold=AVALANCHE_VOTE_STALE_THRESHOLD, uint32_t staleFactor=AVALANCHE_VOTE_STALE_FACTOR) const
Definition: voterecord.h:93
bool registerPoll() const
Register that a request is being made regarding that item.
Definition: voterecord.cpp:87
bool isAccepted() const
Vote accounting facilities.
Definition: voterecord.h:86
static int count
Definition: tests.c:31
static constexpr uint32_t AVALANCHE_VOTE_STALE_FACTOR
Scaling factor applied to confidence to determine staleness threshold.
Definition: voterecord.h:35
static constexpr int AVALANCHE_MAX_INFLIGHT_POLL
How many inflight requests can exist for one item.
Definition: voterecord.h:40
static constexpr uint32_t AVALANCHE_VOTE_STALE_THRESHOLD
Number of votes before a record may be considered as stale.
Definition: voterecord.h:22
static constexpr int AVALANCHE_FINALIZATION_SCORE
Finalization score.
Definition: voterecord.h:17
#define REGISTER_VOTE_AND_CHECK(vr, vote, state, finalized, stale, confidence)
BOOST_AUTO_TEST_CASE(vote_record)