Bitcoin ABC 0.31.2
P2P Digital Currency
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
stakecontendercache_tests.cpp
Go to the documentation of this file.
1// Copyright (c) 2024 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
8#include <script/script.h>
9
10#include <avalanche/test/util.h>
11#include <test/util/random.h>
12#include <test/util/setup_common.h>
13#include <util/time.h>
14#include <validation.h>
15
16#include <boost/test/unit_test.hpp>
17
18#include <limits>
19
20using namespace avalanche;
21
22namespace {
23struct PeerManagerFixture : public TestChain100Setup {
24 PeerManagerFixture() {
25 gArgs.ForceSetArg("-avaproofstakeutxoconfirmations", "1");
26 }
27 ~PeerManagerFixture() {
28 gArgs.ClearForcedArg("-avaproofstakeutxoconfirmations");
29 }
30};
31} // namespace
32
33BOOST_FIXTURE_TEST_SUITE(stakecontendercache_tests, TestChain100Setup)
34
36 const BlockHash &prevblockhash,
37 std::vector<CScript> manualWinners,
38 std::vector<ProofRef> acceptedWinners,
39 std::vector<ProofRef> rejectedWinners) {
40 std::vector<std::pair<ProofId, CScript>> winners;
41 size_t expectedSize =
42 manualWinners.size() + acceptedWinners.size() + rejectedWinners.size();
43 if (expectedSize == 0) {
44 BOOST_CHECK(!cache.getWinners(prevblockhash, winners));
45 return;
46 }
47
48 BOOST_CHECK(cache.getWinners(prevblockhash, winners));
49 BOOST_CHECK_EQUAL(winners.size(), expectedSize);
50
51 // Manual winners are always first and in order
52 for (size_t i = 0; i < manualWinners.size(); i++) {
53 BOOST_CHECK(winners[i].second == manualWinners[i]);
54 }
55
56 // Rest of the the winners are only those determined by avalanche.
57 for (auto &proof : acceptedWinners) {
59 std::find_if(std::next(winners.begin(), manualWinners.size()),
60 std::next(winners.begin(), manualWinners.size() +
61 acceptedWinners.size()),
62 [&](std::pair<ProofId, CScript> &p) {
63 return p.first == proof->getId();
64 }) != winners.end());
65 }
66 for (auto &proof : rejectedWinners) {
68 std::find_if(std::next(winners.begin(), manualWinners.size() +
69 acceptedWinners.size()),
70 winners.end(), [&](std::pair<ProofId, CScript> &p) {
71 return p.first == proof->getId();
72 }) != winners.end());
73 }
74
75 // Verify the winner order such that the best (lowest) reward ranked proof's
76 // payout script is always before payout scripts from proofs with worse
77 // (higher) reward ranks.
78 double previousRank = 0;
79 for (auto it = std::next(winners.begin(), manualWinners.size());
80 it != std::next(winners.begin(),
81 manualWinners.size() + acceptedWinners.size());
82 it++) {
83 double proofRank = StakeContenderId(prevblockhash, it->first)
85 BOOST_CHECK(previousRank < proofRank);
86 previousRank = proofRank;
87 }
88 previousRank = 0;
89 for (auto it = std::next(winners.begin(),
90 manualWinners.size() + acceptedWinners.size());
91 it != winners.end(); it++) {
92 double proofRank = StakeContenderId(prevblockhash, it->first)
94 BOOST_CHECK(previousRank < proofRank);
95 previousRank = proofRank;
96 }
97}
98
100 const BlockHash &prevblockhash,
101 const ProofRef &proof, int expected) {
102 BlockHash checkprevblockhash;
104 cache.getVoteStatus(StakeContenderId(prevblockhash, proof->getId()),
105 checkprevblockhash),
106 expected);
107 if (expected != -1) {
108 BOOST_CHECK_EQUAL(prevblockhash, checkprevblockhash);
109 }
110}
111
112BOOST_AUTO_TEST_CASE(vote_status_tests) {
113 Chainstate &active_chainstate = Assert(m_node.chainman)->ActiveChainstate();
115
116 CBlockIndex *pindex = active_chainstate.m_chain.Tip();
117 const BlockHash &blockhash = pindex->GetBlockHash();
118
119 std::vector<int> initialStatuses = {
123 for (uint8_t initialStatus : initialStatuses) {
124 auto proof = buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE);
125
126 // Unknown contender
127 CheckVoteStatus(cache, blockhash, proof, -1);
128
129 // Add the contender and check its vote after avalanche updates
130 BOOST_CHECK(cache.add(pindex, proof, initialStatus));
131 CheckVoteStatus(cache, blockhash, proof,
132 !(initialStatus & StakeContenderStatus::ACCEPTED));
133
134 cache.accept(StakeContenderId(blockhash, proof->getId()));
135 CheckVoteStatus(cache, blockhash, proof, 0);
136
137 cache.reject(StakeContenderId(blockhash, proof->getId()));
138 CheckVoteStatus(cache, blockhash, proof, 1);
139
140 cache.finalize(StakeContenderId(blockhash, proof->getId()));
141 CheckVoteStatus(cache, blockhash, proof, 0);
142
143 // Add the proof as a manual winner. It should always be accepted.
144 BOOST_CHECK(cache.setWinners(pindex, {proof->getPayoutScript()}));
145 CheckVoteStatus(cache, blockhash, proof, 0);
146 }
147}
148
149BOOST_AUTO_TEST_CASE(winners_tests) {
150 Chainstate &active_chainstate = Assert(m_node.chainman)->ActiveChainstate();
152
153 std::vector<CScript> manualWinners = {
154 CScript() << OP_TRUE,
155 CScript() << OP_FALSE,
156 };
157
158 std::vector<ProofRef> proofs;
159 for (int i = 0; i < 4; i++) {
160 proofs.push_back(
161 buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE));
162 }
163
164 // Repeat these tests with multiple block hashes to ensure no unintended
165 // modifications are made to other entries
166 CBlockIndex *pindex = active_chainstate.m_chain.Tip();
167 for (int i = 0; i < 5; i++) {
168 const BlockHash &blockhash = pindex->GetBlockHash();
169 CheckWinners(cache, blockhash, {}, {}, {});
170
171 // Add a winner manually
172 BOOST_CHECK(cache.setWinners(pindex, {manualWinners[0]}));
173 CheckWinners(cache, blockhash, {manualWinners[0]}, {}, {});
174
175 // Before adding contenders, check that vote status is unknown
176 for (int p = 0; p < 4; p++) {
177 CheckVoteStatus(cache, blockhash, proofs[p], -1);
178 }
179
180 // Add some contenders
181 // Local winner
182 BOOST_CHECK(cache.add(pindex, proofs[0],
185 CheckVoteStatus(cache, blockhash, proofs[0], 0);
186
187 // Potential winner other than the local winner
189 cache.add(pindex, proofs[1], StakeContenderStatus::ACCEPTED));
190 CheckVoteStatus(cache, blockhash, proofs[1], 0);
191
192 // Local winner that has been rejected by avalanche so far
194 cache.add(pindex, proofs[2], StakeContenderStatus::IN_WINNER_SET));
195 CheckVoteStatus(cache, blockhash, proofs[2], 1);
196
197 // Some other contender
198 BOOST_CHECK(cache.add(pindex, proofs[3]));
199 CheckVoteStatus(cache, blockhash, proofs[3], 1);
200
201 // Attempting to add duplicates fails, even if status is different than
202 // the successfully added entries.
203 for (const auto &proof : proofs) {
204 BOOST_CHECK(!cache.add(pindex, proof));
206 !cache.add(pindex, proof, StakeContenderStatus::ACCEPTED));
207 BOOST_CHECK(!cache.add(pindex, proof,
211 !cache.add(pindex, proof, StakeContenderStatus::IN_WINNER_SET));
212 }
213
214 CheckWinners(cache, blockhash, {manualWinners[0]}, {proofs[0]},
215 {proofs[2]});
216
217 // Add another manual winner. It always comes before contenders in the
218 // winner set.
219 BOOST_CHECK(cache.setWinners(pindex, manualWinners));
220 CheckWinners(cache, blockhash, manualWinners, {proofs[0]}, {proofs[2]});
221
222 // Adding manual winners with the same payout scripts as contenders in
223 // any state never causes conflicts
224 std::vector<CScript> moreManualWinners = manualWinners;
225 for (const auto &proof : proofs) {
226 moreManualWinners.push_back(proof->getPayoutScript());
227 BOOST_CHECK(cache.setWinners(pindex, moreManualWinners));
228 CheckVoteStatus(cache, blockhash, proof, 0);
229 CheckWinners(cache, blockhash, moreManualWinners, {proofs[0]},
230 {proofs[2]});
231 }
232 CheckWinners(cache, blockhash, moreManualWinners, {proofs[0]},
233 {proofs[2]});
234
235 // Avalanche accepting all of the contenders does not change the winners
236 // yet
237 for (const auto &proof : proofs) {
238 cache.accept(StakeContenderId(blockhash, proof->getId()));
239 }
240 CheckWinners(cache, blockhash, moreManualWinners,
241 {proofs[0], proofs[2]}, {});
242
243 // Avalanche rejecting all of the contenders does not change the winners
244 // yet
245 for (const auto &proof : proofs) {
246 cache.reject(StakeContenderId(blockhash, proof->getId()));
247 }
248 CheckWinners(cache, blockhash, moreManualWinners, {},
249 {proofs[0], proofs[2]});
250
251 // Avalanche finalizing a contender already in the winner set makes no
252 // difference
253 cache.finalize(StakeContenderId(blockhash, proofs[0]->getId()));
254 CheckWinners(cache, blockhash, moreManualWinners, {proofs[0]},
255 {proofs[2]});
256
257 // Avalanche finalizing a contender that wasn't in the winner set before
258 // makes a new winner
259 cache.finalize(StakeContenderId(blockhash, proofs[1]->getId()));
260 CheckWinners(cache, blockhash, moreManualWinners,
261 {proofs[0], proofs[1]}, {proofs[2]});
262
263 // Avalanche invalidating a contender that was finalized has no effect
264 // other than ordering.
265 cache.reject(StakeContenderId(blockhash, proofs[1]->getId()));
266 CheckWinners(cache, blockhash, moreManualWinners, {proofs[0]},
267 {proofs[1], proofs[2]});
268
269 pindex = pindex->pprev;
270 }
271
272 // All contenders were added as manual winners at some point in this test,
273 // so reflect that here.
274 for (const auto &proof : proofs) {
275 manualWinners.push_back(proof->getPayoutScript());
276 }
277
278 // Sanity check that past cached state was not poisoned
279 pindex = active_chainstate.m_chain.Tip();
280 for (int i = 0; i < 5; i++) {
281 CheckWinners(cache, pindex->GetBlockHash(), manualWinners, {proofs[0]},
282 {proofs[1], proofs[2]});
283 for (int p = 0; p < 4; p++) {
284 CheckVoteStatus(cache, pindex->GetBlockHash(), proofs[p], 0);
285 }
286 pindex = pindex->pprev;
287 }
288}
289
290BOOST_AUTO_TEST_CASE(cleanup_tests) {
291 Chainstate &active_chainstate = Assert(m_node.chainman)->ActiveChainstate();
293
294 std::vector<ProofRef> proofs;
295 for (int i = 0; i < 10; i++) {
296 proofs.push_back(
297 buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE));
298 }
299
300 CBlockIndex *pindex = active_chainstate.m_chain.Tip();
301 std::vector<BlockHash> blockhashes{pindex->GetBlockHash()};
302 pindex = pindex->pprev;
303 for (int i = 0; i < 3; i++) {
304 BlockHash blockhash = pindex->GetBlockHash();
305 blockhashes.push_back(blockhash);
306 for (const auto &proof : proofs) {
307 cache.add(pindex, proof, StakeContenderStatus::IN_WINNER_SET);
308 }
309 CheckWinners(cache, blockhash, {}, {}, proofs);
310 pindex = pindex->pprev;
311 }
312
313 // Promote up to the height that we will allow cleanup of the cache. Note
314 // that no entries are actually promoted so this test has fine tuned control
315 // over which blocks have entries.
316 pindex = active_chainstate.m_chain.Tip()->pprev->pprev->pprev;
317 BOOST_CHECK_EQUAL(pindex->nHeight, 97);
318 cache.promoteToBlock(pindex, [](const ProofId &proofid) { return false; });
319
320 // Cleaning up nonexistant entries has no impact
321 for (int height : {0, 10, 50, 90, 97}) {
322 cache.cleanup(height);
323 CheckWinners(cache, blockhashes[0], {}, {}, {});
324 CheckWinners(cache, blockhashes[1], {}, {}, proofs);
325 CheckWinners(cache, blockhashes[2], {}, {}, proofs);
326 CheckWinners(cache, blockhashes[3], {}, {}, proofs);
327 }
328
329 // Try to cleanup oldest block in the cache, except promotion at that height
330 // hasn't happened yet so cleanup has no effect.
331 cache.cleanup(98);
332 CheckWinners(cache, blockhashes[0], {}, {}, {});
333 CheckWinners(cache, blockhashes[1], {}, {}, proofs);
334 CheckWinners(cache, blockhashes[2], {}, {}, proofs);
335 CheckWinners(cache, blockhashes[3], {}, {}, proofs);
336
337 // Promote up to that height
338 cache.promoteToBlock(active_chainstate.m_chain.Tip()->pprev->pprev,
339 [](const ProofId &proofid) { return false; });
340
341 // Cleaning up the oldest block in the cache succeeds now
342 cache.cleanup(98);
343 CheckWinners(cache, blockhashes[0], {}, {}, {});
344 CheckWinners(cache, blockhashes[1], {}, {}, proofs);
345 CheckWinners(cache, blockhashes[2], {}, {}, proofs);
346 CheckWinners(cache, blockhashes[3], {}, {}, {});
347
348 // Add only a local winner to the recently cleared block
349 cache.setWinners(active_chainstate.m_chain.Tip()->pprev->pprev->pprev,
350 {CScript()});
351 CheckWinners(cache, blockhashes[0], {}, {}, {});
352 CheckWinners(cache, blockhashes[1], {}, {}, proofs);
353 CheckWinners(cache, blockhashes[2], {}, {}, proofs);
354 CheckWinners(cache, blockhashes[3], {CScript()}, {}, {});
355
356 // Clean it up again
357 cache.cleanup(98);
358 CheckWinners(cache, blockhashes[0], {}, {}, {});
359 CheckWinners(cache, blockhashes[1], {}, {}, proofs);
360 CheckWinners(cache, blockhashes[2], {}, {}, proofs);
361 CheckWinners(cache, blockhashes[3], {}, {}, {});
362
363 // Add a local winner to a block with winners already there, then clear it
364 cache.setWinners(active_chainstate.m_chain.Tip()->pprev->pprev,
365 {CScript()});
366 CheckWinners(cache, blockhashes[0], {}, {}, {});
367 CheckWinners(cache, blockhashes[1], {}, {}, proofs);
368 CheckWinners(cache, blockhashes[2], {CScript()}, {}, proofs);
369 CheckWinners(cache, blockhashes[3], {}, {}, {});
370
371 cache.promoteToBlock(active_chainstate.m_chain.Tip()->pprev,
372 [](const ProofId &proofid) { return false; });
373 cache.cleanup(99);
374 CheckWinners(cache, blockhashes[0], {}, {}, {});
375 CheckWinners(cache, blockhashes[1], {}, {}, proofs);
376 CheckWinners(cache, blockhashes[2], {}, {}, {});
377 CheckWinners(cache, blockhashes[3], {}, {}, {});
378
379 // Clean up the remaining block and the cache should be empty now
380 cache.promoteToBlock(active_chainstate.m_chain.Tip(),
381 [](const ProofId &proofid) { return false; });
382 cache.cleanup(100);
383 BOOST_CHECK(cache.isEmpty());
384 CheckWinners(cache, blockhashes[0], {}, {}, {});
385 CheckWinners(cache, blockhashes[1], {}, {}, {});
386 CheckWinners(cache, blockhashes[2], {}, {}, {});
387 CheckWinners(cache, blockhashes[3], {}, {}, {});
388
389 // Cleaning up again has no effect
390 cache.cleanup(100);
391 BOOST_CHECK(cache.isEmpty());
392 CheckWinners(cache, blockhashes[0], {}, {}, {});
393 CheckWinners(cache, blockhashes[1], {}, {}, {});
394 CheckWinners(cache, blockhashes[2], {}, {}, {});
395 CheckWinners(cache, blockhashes[3], {}, {}, {});
396
397 // Add winners back with random states and sanity check that higher heights
398 // clear the cache as we expect.
399 for (int height : {102, 200, 1000, 1000000}) {
400 pindex = active_chainstate.m_chain.Tip()->pprev;
401 for (size_t i = 1; i < 3; i++) {
402 for (const auto &proof : proofs) {
403 cache.add(pindex, proof, InsecureRandBits(2));
404 cache.setWinners(pindex, {CScript()});
405 }
406
407 // Sanity check there are some winners
408 std::vector<std::pair<ProofId, CScript>> winners;
409 BOOST_CHECK(cache.getWinners(blockhashes[i], winners));
410 BOOST_CHECK(winners.size() >= 1);
411 pindex = pindex->pprev;
412 }
413
414 // Cleaning up the cache at a height higher than any cache entry results
415 // in an empty cache and no winners.
416 cache.cleanup(height);
417 BOOST_CHECK(cache.isEmpty());
418 CheckWinners(cache, blockhashes[0], {}, {}, {});
419 CheckWinners(cache, blockhashes[1], {}, {}, {});
420 CheckWinners(cache, blockhashes[2], {}, {}, {});
421 CheckWinners(cache, blockhashes[3], {}, {}, {});
422 }
423
424 // But note that the cache will never cleanup higher than the last promoted
425 // block.
426 cache.add(active_chainstate.m_chain.Tip(), proofs[0],
428 for (int height : {102, 200, 1000, 1000000}) {
429 cache.cleanup(height);
430 CheckWinners(cache, blockhashes[0], {}, {}, {proofs[0]});
431 CheckWinners(cache, blockhashes[1], {}, {}, {});
432 CheckWinners(cache, blockhashes[2], {}, {}, {});
433 CheckWinners(cache, blockhashes[3], {}, {}, {});
434 }
435}
436
437BOOST_FIXTURE_TEST_CASE(promote_tests, PeerManagerFixture) {
438 Chainstate &active_chainstate = Assert(m_node.chainman)->ActiveChainstate();
440
441 std::vector<ProofRef> proofs;
442 for (size_t i = 0; i < 3; i++) {
443 proofs.push_back(
444 buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE));
445 }
446
447 CBlockIndex *pindex = active_chainstate.m_chain.Tip();
448 const CBlockIndex *tip = pindex;
449 std::vector<BlockHash> blockhashes;
450 for (size_t i = 0; i < 3; i++) {
451 blockhashes.push_back(pindex->GetBlockHash());
452 pindex = pindex->pprev;
453 }
454
455 // Add one proof each to the cache for some early blocks
456 for (size_t i = 0; i < 3; i++) {
457 BlockHash blockhash = pindex->GetBlockHash();
458 blockhashes.push_back(blockhash);
459 cache.add(pindex, proofs[i], StakeContenderStatus::IN_WINNER_SET);
460 CheckWinners(cache, blockhash, {}, {}, {proofs[i]});
461 pindex = pindex->pprev;
462 }
463
464 // Attempting to cleanup the cache before promotion has occurred has no
465 // effect.
466 for (int height = 95; height <= 100; height++) {
467 cache.cleanup(height);
468 CheckWinners(cache, blockhashes[0], {}, {}, {});
469 CheckWinners(cache, blockhashes[1], {}, {}, {});
470 CheckWinners(cache, blockhashes[2], {}, {}, {});
471 CheckWinners(cache, blockhashes[3], {}, {}, {proofs[0]});
472 CheckWinners(cache, blockhashes[4], {}, {}, {proofs[1]});
473 CheckWinners(cache, blockhashes[5], {}, {}, {proofs[2]});
474 }
475
476 // Promote contenders, but they are not winners at that block yet
477 cache.promoteToBlock(tip->pprev->pprev,
478 [](const ProofId &proofid) { return true; });
479 CheckWinners(cache, blockhashes[0], {}, {}, {});
480 CheckWinners(cache, blockhashes[1], {}, {}, {});
481 CheckWinners(cache, blockhashes[2], {}, {}, {});
482 for (auto &proof : proofs) {
483 // Contenders are unknown for blocks with no cache entries
484 CheckVoteStatus(cache, blockhashes[0], proof, -1);
485 CheckVoteStatus(cache, blockhashes[1], proof, -1);
486 // Contenders are not winners yet at the promoted block
487 CheckVoteStatus(cache, blockhashes[2], proof, 1);
488 }
489
490 // The contenders are still winners for their respective blocks
491 CheckWinners(cache, blockhashes[3], {}, {}, {proofs[0]});
492 CheckWinners(cache, blockhashes[4], {}, {}, {proofs[1]});
493 CheckWinners(cache, blockhashes[5], {}, {}, {proofs[2]});
494
495 // Cleaning up the cache leaves most recent promoted entries alone
496 cache.cleanup(98);
497 CheckWinners(cache, blockhashes[0], {}, {}, {});
498 CheckWinners(cache, blockhashes[1], {}, {}, {});
499 CheckWinners(cache, blockhashes[2], {}, {}, {});
500 for (auto &proof : proofs) {
501 // Contenders are unknown for blocks with no cache entries
502 CheckVoteStatus(cache, blockhashes[0], proof, -1);
503 CheckVoteStatus(cache, blockhashes[1], proof, -1);
504 // Contenders at the promoted block are rejected
505 CheckVoteStatus(cache, blockhashes[2], proof, 1);
506 }
507 CheckWinners(cache, blockhashes[3], {}, {}, {});
508 CheckWinners(cache, blockhashes[4], {}, {}, {});
509 CheckWinners(cache, blockhashes[5], {}, {}, {});
510
511 // Finalize those proofs
512 for (auto &proof : proofs) {
513 cache.finalize(StakeContenderId(blockhashes[2], proof->getId()));
514 // Contenders are unknown for blocks with no cache entries
515 CheckVoteStatus(cache, blockhashes[0], proof, -1);
516 CheckVoteStatus(cache, blockhashes[1], proof, -1);
517 // Contenders at the promoted block are now accepted
518 CheckVoteStatus(cache, blockhashes[2], proof, 0);
519 }
520 CheckWinners(cache, blockhashes[0], {}, {}, {});
521 CheckWinners(cache, blockhashes[1], {}, {}, {});
522 CheckWinners(cache, blockhashes[2], {}, {}, proofs);
523
524 // Attempting to promote to the same block again is a no-op and statuses
525 // remain unchanged.
526 cache.promoteToBlock(tip->pprev->pprev,
527 [](const ProofId &proofid) { return true; });
528 for (auto &proof : proofs) {
529 // Contenders are unknown for blocks with no cache entries
530 CheckVoteStatus(cache, blockhashes[0], proof, -1);
531 CheckVoteStatus(cache, blockhashes[1], proof, -1);
532 // Contenders at the promoted block are still accepted
533 CheckVoteStatus(cache, blockhashes[2], proof, 0);
534 }
535 CheckWinners(cache, blockhashes[0], {}, {}, {});
536 CheckWinners(cache, blockhashes[1], {}, {}, {});
537 CheckWinners(cache, blockhashes[2], {}, {}, proofs);
538
539 // Now advance the tip and invalidate a proof
540 cache.promoteToBlock(tip->pprev, [&](const ProofId &proofid) {
541 return proofid != proofs[2]->getId();
542 });
543 for (auto &proof : proofs) {
544 // Contenders are unknown for blocks with no cache entries
545 CheckVoteStatus(cache, blockhashes[0], proof, -1);
546 }
547 CheckVoteStatus(cache, blockhashes[1], proofs[0], 1);
548 CheckVoteStatus(cache, blockhashes[1], proofs[1], 1);
549 CheckVoteStatus(cache, blockhashes[1], proofs[2], -1);
550}
551
552BOOST_AUTO_TEST_CASE(pollable_contenders_tests) {
553 Chainstate &active_chainstate = Assert(m_node.chainman)->ActiveChainstate();
555
556 CBlockIndex *pindex = active_chainstate.m_chain.Tip();
557 const BlockHash &blockhash = pindex->GetBlockHash();
558
559 const size_t maxPollable = 12;
560 std::vector<StakeContenderId> contenders;
562 cache.getPollableContenders(blockhash, maxPollable, contenders), 0);
563
564 size_t numAccepted = 0;
565 for (size_t c = 0; c < maxPollable * 2; c++) {
566 // Add a new contender with random initial state
567 auto proof = buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE);
568 BOOST_CHECK(cache.add(pindex, proof, InsecureRandBits(2)));
569
570 BlockHash dummy;
571 StakeContenderId contenderId(blockhash, proof->getId());
572 numAccepted += cache.getVoteStatus(contenderId, dummy) == 0 ? 1 : 0;
573
574 // We should never get more contenders than we can poll for in a single
575 // message.
576 BOOST_CHECK(cache.getPollableContenders(blockhash, maxPollable,
577 contenders) <= maxPollable);
578 BOOST_CHECK(contenders.size() <= maxPollable);
579
580 double lastRank = 0;
581 size_t countAccepted = 0;
582 for (const auto &contender : contenders) {
583 // Check the contender rank is sorted as we expect
584 double rank =
585 contender.ComputeProofRewardRank(MIN_VALID_PROOF_SCORE);
586 BOOST_CHECK(lastRank <= rank);
587 lastRank = rank;
588
589 countAccepted += cache.getVoteStatus(contender, dummy) == 0 ? 1 : 0;
590 }
591
592 // All accepted contenders should always be returned (up to the max)
593 BOOST_CHECK_EQUAL(countAccepted, std::min(numAccepted, maxPollable));
594 }
595}
596
597BOOST_AUTO_TEST_SUITE_END()
ArgsManager gArgs
Definition: args.cpp:40
#define Assert(val)
Identity function.
Definition: check.h:84
void ForceSetArg(const std::string &strArg, const std::string &strValue)
Definition: args.cpp:599
void ClearForcedArg(const std::string &strArg)
Remove a forced arg setting, used only in testing.
Definition: args.cpp:650
The block chain is a tree shaped structure starting with the genesis block at the root,...
Definition: blockindex.h:25
CBlockIndex * pprev
pointer to the index of the predecessor of this block
Definition: blockindex.h:32
BlockHash GetBlockHash() const
Definition: blockindex.h:130
int nHeight
height of the entry in the chain. The genesis block has height 0
Definition: blockindex.h:38
CBlockIndex * Tip() const
Returns the index entry for the tip of this chain, or nullptr if none.
Definition: chain.h:150
Chainstate stores and provides an API to update our local knowledge of the current best chain.
Definition: validation.h:700
CChain m_chain
The current chain of blockheaders we consult and build on.
Definition: validation.h:799
const ProofId & getId() const
Definition: proof.h:170
Cache to track stake contenders for recent blocks.
bool getWinners(const BlockHash &prevblockhash, std::vector< std::pair< ProofId, CScript > > &winners) const
bool accept(const StakeContenderId &contenderId)
Helpers to set avalanche state of a contender.
void cleanup(const int requestedMinHeight)
size_t getPollableContenders(const BlockHash &prevblockhash, size_t maxPollable, std::vector< StakeContenderId > &pollableContenders) const
Get the best ranking contenders, accepted contenders ranking first.
bool reject(const StakeContenderId &contenderId)
bool setWinners(const CBlockIndex *pindex, const std::vector< CScript > &payoutScripts)
Set proof(s) that should be treated as winners (already finalized).
bool add(const CBlockIndex *pindex, const ProofRef &proof, uint8_t status=StakeContenderStatus::UNKNOWN)
Add a proof to consider in staking rewards pre-consensus.
void promoteToBlock(const CBlockIndex *activeTip, std::function< bool(const ProofId &proofid)> const &shouldPromote)
Promote cache entries to a the active chain tip.
int getVoteStatus(const StakeContenderId &contenderId, BlockHash &prevblockhashout) const
Get contender acceptance state for avalanche voting.
bool finalize(const StakeContenderId &contenderId)
ProofRef buildRandomProof(Chainstate &active_chainstate, uint32_t score, int height, const CKey &masterKey)
Definition: util.cpp:20
constexpr uint32_t MIN_VALID_PROOF_SCORE
Definition: util.h:20
Implement std::hash so RCUPtr can be used as a key for maps or sets.
Definition: rcu.h:259
NodeContext & m_node
Definition: interfaces.cpp:815
#define BOOST_CHECK_EQUAL(v1, v2)
Definition: object.cpp:18
#define BOOST_CHECK(expr)
Definition: object.cpp:17
@ OP_FALSE
Definition: script.h:50
@ OP_TRUE
Definition: script.h:57
static void CheckVoteStatus(StakeContenderCache &cache, const BlockHash &prevblockhash, const ProofRef &proof, int expected)
BOOST_FIXTURE_TEST_CASE(promote_tests, PeerManagerFixture)
BOOST_AUTO_TEST_CASE(vote_status_tests)
static void CheckWinners(StakeContenderCache &cache, const BlockHash &prevblockhash, std::vector< CScript > manualWinners, std::vector< ProofRef > acceptedWinners, std::vector< ProofRef > rejectedWinners)
A BlockHash is a unqiue identifier for a block.
Definition: blockhash.h:13
StakeContenderIds are unique for each block to ensure that the peer polling for their acceptance has ...
double ComputeProofRewardRank(uint32_t proofScore) const
To make sure the selection is properly weighted according to the proof score, we normalize the conten...