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