Bitcoin ABC 0.32.4
P2P Digital Currency
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 proofs.reserve(4);
160 for (int i = 0; i < 4; i++) {
161 proofs.push_back(
162 buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE));
163 }
164
165 // Repeat these tests with multiple block hashes to ensure no unintended
166 // modifications are made to other entries
167 CBlockIndex *pindex = active_chainstate.m_chain.Tip();
168 for (int i = 0; i < 5; i++) {
169 const BlockHash &blockhash = pindex->GetBlockHash();
170 CheckWinners(cache, blockhash, {}, {}, {});
171
172 // Add a winner manually
173 BOOST_CHECK(cache.setWinners(pindex, {manualWinners[0]}));
174 CheckWinners(cache, blockhash, {manualWinners[0]}, {}, {});
175
176 // Before adding contenders, check that vote status is unknown
177 for (int p = 0; p < 4; p++) {
178 CheckVoteStatus(cache, blockhash, proofs[p], -1);
179 }
180
181 // Add some contenders
182 // Local winner
183 BOOST_CHECK(cache.add(pindex, proofs[0],
186 CheckVoteStatus(cache, blockhash, proofs[0], 0);
187
188 // Potential winner other than the local winner
190 cache.add(pindex, proofs[1], StakeContenderStatus::ACCEPTED));
191 CheckVoteStatus(cache, blockhash, proofs[1], 0);
192
193 // Local winner that has been rejected by avalanche so far
195 cache.add(pindex, proofs[2], StakeContenderStatus::IN_WINNER_SET));
196 CheckVoteStatus(cache, blockhash, proofs[2], 1);
197
198 // Some other contender
199 BOOST_CHECK(cache.add(pindex, proofs[3]));
200 CheckVoteStatus(cache, blockhash, proofs[3], 1);
201
202 // Attempting to add duplicates fails, even if status is different than
203 // the successfully added entries.
204 for (const auto &proof : proofs) {
205 BOOST_CHECK(!cache.add(pindex, proof));
207 !cache.add(pindex, proof, StakeContenderStatus::ACCEPTED));
208 BOOST_CHECK(!cache.add(pindex, proof,
212 !cache.add(pindex, proof, StakeContenderStatus::IN_WINNER_SET));
213 }
214
215 CheckWinners(cache, blockhash, {manualWinners[0]}, {proofs[0]},
216 {proofs[2]});
217
218 // Add another manual winner. It always comes before contenders in the
219 // winner set.
220 BOOST_CHECK(cache.setWinners(pindex, manualWinners));
221 CheckWinners(cache, blockhash, manualWinners, {proofs[0]}, {proofs[2]});
222
223 // Adding manual winners with the same payout scripts as contenders in
224 // any state never causes conflicts
225 std::vector<CScript> moreManualWinners = manualWinners;
226 for (const auto &proof : proofs) {
227 moreManualWinners.push_back(proof->getPayoutScript());
228 BOOST_CHECK(cache.setWinners(pindex, moreManualWinners));
229 CheckVoteStatus(cache, blockhash, proof, 0);
230 CheckWinners(cache, blockhash, moreManualWinners, {proofs[0]},
231 {proofs[2]});
232 }
233 CheckWinners(cache, blockhash, moreManualWinners, {proofs[0]},
234 {proofs[2]});
235
236 // Avalanche accepting all of the contenders does not change the winners
237 // yet
238 for (const auto &proof : proofs) {
239 cache.accept(StakeContenderId(blockhash, proof->getId()));
240 }
241 CheckWinners(cache, blockhash, moreManualWinners,
242 {proofs[0], proofs[2]}, {});
243
244 // Avalanche rejecting all of the contenders does not change the winners
245 // yet
246 for (const auto &proof : proofs) {
247 cache.reject(StakeContenderId(blockhash, proof->getId()));
248 }
249 CheckWinners(cache, blockhash, moreManualWinners, {},
250 {proofs[0], proofs[2]});
251
252 // Avalanche finalizing a contender already in the winner set makes no
253 // difference
254 cache.finalize(StakeContenderId(blockhash, proofs[0]->getId()));
255 CheckWinners(cache, blockhash, moreManualWinners, {proofs[0]},
256 {proofs[2]});
257
258 // Avalanche finalizing a contender that wasn't in the winner set before
259 // makes a new winner
260 cache.finalize(StakeContenderId(blockhash, proofs[1]->getId()));
261 CheckWinners(cache, blockhash, moreManualWinners,
262 {proofs[0], proofs[1]}, {proofs[2]});
263
264 // Avalanche invalidating a contender that was finalized has no effect
265 // other than ordering.
266 cache.reject(StakeContenderId(blockhash, proofs[1]->getId()));
267 CheckWinners(cache, blockhash, moreManualWinners, {proofs[0]},
268 {proofs[1], proofs[2]});
269
270 pindex = pindex->pprev;
271 }
272
273 // All contenders were added as manual winners at some point in this test,
274 // so reflect that here.
275 for (const auto &proof : proofs) {
276 manualWinners.push_back(proof->getPayoutScript());
277 }
278
279 // Sanity check that past cached state was not poisoned
280 pindex = active_chainstate.m_chain.Tip();
281 for (int i = 0; i < 5; i++) {
282 CheckWinners(cache, pindex->GetBlockHash(), manualWinners, {proofs[0]},
283 {proofs[1], proofs[2]});
284 for (int p = 0; p < 4; p++) {
285 CheckVoteStatus(cache, pindex->GetBlockHash(), proofs[p], 0);
286 }
287 pindex = pindex->pprev;
288 }
289}
290
291BOOST_AUTO_TEST_CASE(cleanup_tests) {
292 Chainstate &active_chainstate = Assert(m_node.chainman)->ActiveChainstate();
294
295 std::vector<ProofRef> proofs;
296 proofs.reserve(10);
297 for (int i = 0; i < 10; i++) {
298 proofs.push_back(
299 buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE));
300 }
301
302 CBlockIndex *pindex = active_chainstate.m_chain.Tip();
303 std::vector<BlockHash> blockhashes{pindex->GetBlockHash()};
304 pindex = pindex->pprev;
305 for (int i = 0; i < 3; i++) {
306 BlockHash blockhash = pindex->GetBlockHash();
307 blockhashes.push_back(blockhash);
308 for (const auto &proof : proofs) {
309 cache.add(pindex, proof, StakeContenderStatus::IN_WINNER_SET);
310 }
311 CheckWinners(cache, blockhash, {}, {}, proofs);
312 pindex = pindex->pprev;
313 }
314
315 // Promote up to the height that we will allow cleanup of the cache. Note
316 // that no entries are actually promoted so this test has fine tuned control
317 // over which blocks have entries.
318 pindex = active_chainstate.m_chain.Tip()->pprev->pprev->pprev;
319 BOOST_CHECK_EQUAL(pindex->nHeight, 97);
320 cache.promoteToBlock(pindex, [](const ProofId &proofid) { return false; });
321
322 // Cleaning up nonexistant entries has no impact
323 for (int height : {0, 10, 50, 90, 97}) {
324 cache.cleanup(height);
325 CheckWinners(cache, blockhashes[0], {}, {}, {});
326 CheckWinners(cache, blockhashes[1], {}, {}, proofs);
327 CheckWinners(cache, blockhashes[2], {}, {}, proofs);
328 CheckWinners(cache, blockhashes[3], {}, {}, proofs);
329 }
330
331 // Try to cleanup oldest block in the cache, except promotion at that height
332 // hasn't happened yet so cleanup has no effect.
333 cache.cleanup(98);
334 CheckWinners(cache, blockhashes[0], {}, {}, {});
335 CheckWinners(cache, blockhashes[1], {}, {}, proofs);
336 CheckWinners(cache, blockhashes[2], {}, {}, proofs);
337 CheckWinners(cache, blockhashes[3], {}, {}, proofs);
338
339 // Promote up to that height
340 cache.promoteToBlock(active_chainstate.m_chain.Tip()->pprev->pprev,
341 [](const ProofId &proofid) { return false; });
342
343 // Cleaning up the oldest block in the cache succeeds now
344 cache.cleanup(98);
345 CheckWinners(cache, blockhashes[0], {}, {}, {});
346 CheckWinners(cache, blockhashes[1], {}, {}, proofs);
347 CheckWinners(cache, blockhashes[2], {}, {}, proofs);
348 CheckWinners(cache, blockhashes[3], {}, {}, {});
349
350 // Add only a local winner to the recently cleared block
351 cache.setWinners(active_chainstate.m_chain.Tip()->pprev->pprev->pprev,
352 {CScript()});
353 CheckWinners(cache, blockhashes[0], {}, {}, {});
354 CheckWinners(cache, blockhashes[1], {}, {}, proofs);
355 CheckWinners(cache, blockhashes[2], {}, {}, proofs);
356 CheckWinners(cache, blockhashes[3], {CScript()}, {}, {});
357
358 // Clean it up again
359 cache.cleanup(98);
360 CheckWinners(cache, blockhashes[0], {}, {}, {});
361 CheckWinners(cache, blockhashes[1], {}, {}, proofs);
362 CheckWinners(cache, blockhashes[2], {}, {}, proofs);
363 CheckWinners(cache, blockhashes[3], {}, {}, {});
364
365 // Add a local winner to a block with winners already there, then clear it
366 cache.setWinners(active_chainstate.m_chain.Tip()->pprev->pprev,
367 {CScript()});
368 CheckWinners(cache, blockhashes[0], {}, {}, {});
369 CheckWinners(cache, blockhashes[1], {}, {}, proofs);
370 CheckWinners(cache, blockhashes[2], {CScript()}, {}, proofs);
371 CheckWinners(cache, blockhashes[3], {}, {}, {});
372
373 cache.promoteToBlock(active_chainstate.m_chain.Tip()->pprev,
374 [](const ProofId &proofid) { return false; });
375 cache.cleanup(99);
376 CheckWinners(cache, blockhashes[0], {}, {}, {});
377 CheckWinners(cache, blockhashes[1], {}, {}, proofs);
378 CheckWinners(cache, blockhashes[2], {}, {}, {});
379 CheckWinners(cache, blockhashes[3], {}, {}, {});
380
381 // Clean up the remaining block and the cache should be empty now
382 cache.promoteToBlock(active_chainstate.m_chain.Tip(),
383 [](const ProofId &proofid) { return false; });
384 cache.cleanup(100);
385 BOOST_CHECK(cache.isEmpty());
386 CheckWinners(cache, blockhashes[0], {}, {}, {});
387 CheckWinners(cache, blockhashes[1], {}, {}, {});
388 CheckWinners(cache, blockhashes[2], {}, {}, {});
389 CheckWinners(cache, blockhashes[3], {}, {}, {});
390
391 // Cleaning up again has no effect
392 cache.cleanup(100);
393 BOOST_CHECK(cache.isEmpty());
394 CheckWinners(cache, blockhashes[0], {}, {}, {});
395 CheckWinners(cache, blockhashes[1], {}, {}, {});
396 CheckWinners(cache, blockhashes[2], {}, {}, {});
397 CheckWinners(cache, blockhashes[3], {}, {}, {});
398
399 // Add winners back with random states and sanity check that higher heights
400 // clear the cache as we expect.
401 for (int height : {102, 200, 1000, 1000000}) {
402 pindex = active_chainstate.m_chain.Tip()->pprev;
403 for (size_t i = 1; i < 3; i++) {
404 for (const auto &proof : proofs) {
405 cache.add(pindex, proof, InsecureRandBits(2));
406 cache.setWinners(pindex, {CScript()});
407 }
408
409 // Sanity check there are some winners
410 std::vector<std::pair<ProofId, CScript>> winners;
411 BOOST_CHECK(cache.getWinners(blockhashes[i], winners));
412 BOOST_CHECK(winners.size() >= 1);
413 pindex = pindex->pprev;
414 }
415
416 // Cleaning up the cache at a height higher than any cache entry results
417 // in an empty cache and no winners.
418 cache.cleanup(height);
419 BOOST_CHECK(cache.isEmpty());
420 CheckWinners(cache, blockhashes[0], {}, {}, {});
421 CheckWinners(cache, blockhashes[1], {}, {}, {});
422 CheckWinners(cache, blockhashes[2], {}, {}, {});
423 CheckWinners(cache, blockhashes[3], {}, {}, {});
424 }
425
426 // But note that the cache will never cleanup higher than the last promoted
427 // block.
428 cache.add(active_chainstate.m_chain.Tip(), proofs[0],
430 for (int height : {102, 200, 1000, 1000000}) {
431 cache.cleanup(height);
432 CheckWinners(cache, blockhashes[0], {}, {}, {proofs[0]});
433 CheckWinners(cache, blockhashes[1], {}, {}, {});
434 CheckWinners(cache, blockhashes[2], {}, {}, {});
435 CheckWinners(cache, blockhashes[3], {}, {}, {});
436 }
437}
438
439BOOST_FIXTURE_TEST_CASE(promote_tests, PeerManagerFixture) {
440 Chainstate &active_chainstate = Assert(m_node.chainman)->ActiveChainstate();
442
443 std::vector<ProofRef> proofs;
444 proofs.reserve(3);
445 for (size_t i = 0; i < 3; i++) {
446 proofs.push_back(
447 buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE));
448 }
449
450 CBlockIndex *pindex = active_chainstate.m_chain.Tip();
451 const CBlockIndex *tip = pindex;
452 std::vector<BlockHash> blockhashes;
453 for (size_t i = 0; i < 3; i++) {
454 blockhashes.push_back(pindex->GetBlockHash());
455 pindex = pindex->pprev;
456 }
457
458 // Add one proof each to the cache for some early blocks
459 for (size_t i = 0; i < 3; i++) {
460 BlockHash blockhash = pindex->GetBlockHash();
461 blockhashes.push_back(blockhash);
462 cache.add(pindex, proofs[i], StakeContenderStatus::IN_WINNER_SET);
463 CheckWinners(cache, blockhash, {}, {}, {proofs[i]});
464 pindex = pindex->pprev;
465 }
466
467 // Attempting to cleanup the cache before promotion has occurred has no
468 // effect.
469 for (int height = 95; height <= 100; height++) {
470 cache.cleanup(height);
471 CheckWinners(cache, blockhashes[0], {}, {}, {});
472 CheckWinners(cache, blockhashes[1], {}, {}, {});
473 CheckWinners(cache, blockhashes[2], {}, {}, {});
474 CheckWinners(cache, blockhashes[3], {}, {}, {proofs[0]});
475 CheckWinners(cache, blockhashes[4], {}, {}, {proofs[1]});
476 CheckWinners(cache, blockhashes[5], {}, {}, {proofs[2]});
477 }
478
479 // Promote contenders, but they are not winners at that block yet
480 cache.promoteToBlock(tip->pprev->pprev,
481 [](const ProofId &proofid) { return true; });
482 CheckWinners(cache, blockhashes[0], {}, {}, {});
483 CheckWinners(cache, blockhashes[1], {}, {}, {});
484 CheckWinners(cache, blockhashes[2], {}, {}, {});
485 for (auto &proof : proofs) {
486 // Contenders are unknown for blocks with no cache entries
487 CheckVoteStatus(cache, blockhashes[0], proof, -1);
488 CheckVoteStatus(cache, blockhashes[1], proof, -1);
489 // Contenders are not winners yet at the promoted block
490 CheckVoteStatus(cache, blockhashes[2], proof, 1);
491 }
492
493 // The contenders are still winners for their respective blocks
494 CheckWinners(cache, blockhashes[3], {}, {}, {proofs[0]});
495 CheckWinners(cache, blockhashes[4], {}, {}, {proofs[1]});
496 CheckWinners(cache, blockhashes[5], {}, {}, {proofs[2]});
497
498 // Cleaning up the cache leaves most recent promoted entries alone
499 cache.cleanup(98);
500 CheckWinners(cache, blockhashes[0], {}, {}, {});
501 CheckWinners(cache, blockhashes[1], {}, {}, {});
502 CheckWinners(cache, blockhashes[2], {}, {}, {});
503 for (auto &proof : proofs) {
504 // Contenders are unknown for blocks with no cache entries
505 CheckVoteStatus(cache, blockhashes[0], proof, -1);
506 CheckVoteStatus(cache, blockhashes[1], proof, -1);
507 // Contenders at the promoted block are rejected
508 CheckVoteStatus(cache, blockhashes[2], proof, 1);
509 }
510 CheckWinners(cache, blockhashes[3], {}, {}, {});
511 CheckWinners(cache, blockhashes[4], {}, {}, {});
512 CheckWinners(cache, blockhashes[5], {}, {}, {});
513
514 // Finalize those proofs
515 for (auto &proof : proofs) {
516 cache.finalize(StakeContenderId(blockhashes[2], proof->getId()));
517 // Contenders are unknown for blocks with no cache entries
518 CheckVoteStatus(cache, blockhashes[0], proof, -1);
519 CheckVoteStatus(cache, blockhashes[1], proof, -1);
520 // Contenders at the promoted block are now accepted
521 CheckVoteStatus(cache, blockhashes[2], proof, 0);
522 }
523 CheckWinners(cache, blockhashes[0], {}, {}, {});
524 CheckWinners(cache, blockhashes[1], {}, {}, {});
525 CheckWinners(cache, blockhashes[2], {}, {}, proofs);
526
527 // Attempting to promote to the same block again is a no-op and statuses
528 // remain unchanged.
529 cache.promoteToBlock(tip->pprev->pprev,
530 [](const ProofId &proofid) { return true; });
531 for (auto &proof : proofs) {
532 // Contenders are unknown for blocks with no cache entries
533 CheckVoteStatus(cache, blockhashes[0], proof, -1);
534 CheckVoteStatus(cache, blockhashes[1], proof, -1);
535 // Contenders at the promoted block are still accepted
536 CheckVoteStatus(cache, blockhashes[2], proof, 0);
537 }
538 CheckWinners(cache, blockhashes[0], {}, {}, {});
539 CheckWinners(cache, blockhashes[1], {}, {}, {});
540 CheckWinners(cache, blockhashes[2], {}, {}, proofs);
541
542 // Now advance the tip and invalidate a proof
543 cache.promoteToBlock(tip->pprev, [&](const ProofId &proofid) {
544 return proofid != proofs[2]->getId();
545 });
546 for (auto &proof : proofs) {
547 // Contenders are unknown for blocks with no cache entries
548 CheckVoteStatus(cache, blockhashes[0], proof, -1);
549 }
550 CheckVoteStatus(cache, blockhashes[1], proofs[0], 1);
551 CheckVoteStatus(cache, blockhashes[1], proofs[1], 1);
552 CheckVoteStatus(cache, blockhashes[1], proofs[2], -1);
553}
554
555BOOST_AUTO_TEST_CASE(pollable_contenders_tests) {
556 Chainstate &active_chainstate = Assert(m_node.chainman)->ActiveChainstate();
558
559 CBlockIndex *pindex = active_chainstate.m_chain.Tip();
560 const BlockHash &blockhash = pindex->GetBlockHash();
561
562 const size_t maxPollable = 12;
563 std::vector<StakeContenderId> contenders;
565 cache.getPollableContenders(blockhash, maxPollable, contenders), 0);
566
567 size_t numAccepted = 0;
568 for (size_t c = 0; c < maxPollable * 2; c++) {
569 // Add a new contender with random initial state
570 auto proof = buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE);
571 BOOST_CHECK(cache.add(pindex, proof, InsecureRandBits(2)));
572
573 BlockHash dummy;
574 StakeContenderId contenderId(blockhash, proof->getId());
575 numAccepted += cache.getVoteStatus(contenderId, dummy) == 0 ? 1 : 0;
576
577 // We should never get more contenders than we can poll for in a single
578 // message.
579 BOOST_CHECK(cache.getPollableContenders(blockhash, maxPollable,
580 contenders) <= maxPollable);
581 BOOST_CHECK(contenders.size() <= maxPollable);
582
583 double lastRank = 0;
584 size_t countAccepted = 0;
585 for (const auto &contender : contenders) {
586 // Check the contender rank is sorted as we expect
587 double rank =
588 contender.ComputeProofRewardRank(MIN_VALID_PROOF_SCORE);
589 BOOST_CHECK(lastRank <= rank);
590 lastRank = rank;
591
592 countAccepted += cache.getVoteStatus(contender, dummy) == 0 ? 1 : 0;
593 }
594
595 // All accepted contenders should always be returned (up to the max)
596 BOOST_CHECK_EQUAL(countAccepted, std::min(numAccepted, maxPollable));
597 }
598}
599
600BOOST_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:566
void ClearForcedArg(const std::string &strArg)
Remove a forced arg setting, used only in testing.
Definition: args.cpp:617
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:734
CChain m_chain
The current chain of blockheaders we consult and build on.
Definition: validation.h:833
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:822
#define BOOST_CHECK_EQUAL(v1, v2)
Definition: object.cpp:18
#define BOOST_CHECK(expr)
Definition: object.cpp:17
@ OP_FALSE
Definition: script.h:54
@ OP_TRUE
Definition: script.h:61
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...