Page MenuHomePhabricator

D17479.diff
No OneTemporary

D17479.diff

diff --git a/src/avalanche/stakecontendercache.h b/src/avalanche/stakecontendercache.h
--- a/src/avalanche/stakecontendercache.h
+++ b/src/avalanche/stakecontendercache.h
@@ -171,6 +171,15 @@
int getVoteStatus(const StakeContenderId &contenderId,
BlockHash &prevblockhashout) const;
+ /**
+ * Get the best ranking contenders, accepted contenders ranking first. The
+ * output of this function is only reliable to select contenders to
+ * reconcile and should not be called after contender polling begins.
+ */
+ size_t getPollableContenders(
+ const BlockHash &prevblockhash, size_t maxPollable,
+ std::vector<StakeContenderId> &pollableContenders) const;
+
/**
* Get payout scripts of the winning proofs.
*/
diff --git a/src/avalanche/stakecontendercache.cpp b/src/avalanche/stakecontendercache.cpp
--- a/src/avalanche/stakecontendercache.cpp
+++ b/src/avalanche/stakecontendercache.cpp
@@ -162,6 +162,56 @@
return 1;
}
+size_t StakeContenderCache::getPollableContenders(
+ const BlockHash &prevblockhash, size_t maxPollable,
+ std::vector<StakeContenderId> &pollableContenders) const {
+ std::vector<const StakeContenderCacheEntry *> rankedContenders;
+ auto &view = contenders.get<by_prevblockhash>();
+ auto [begin, end] = view.equal_range(prevblockhash);
+ for (auto it = begin; it != end; it++) {
+ rankedContenders.push_back(&(*it));
+ }
+
+ std::sort(rankedContenders.begin(), rankedContenders.end(),
+ [](const StakeContenderCacheEntry *left,
+ const StakeContenderCacheEntry *right) {
+ if (left->isAccepted() != right->isAccepted()) {
+ // Accepted contenders sort first
+ return left->isAccepted();
+ }
+
+ double leftRank = left->computeRewardRank();
+ double rightRank = right->computeRewardRank();
+ if (leftRank != rightRank) {
+ // Lowest rank is best
+ return leftRank < rightRank;
+ }
+
+ // If there's a collision in rank, sort by contender id
+ const StakeContenderId &leftContenderId =
+ left->getStakeContenderId();
+ const StakeContenderId &rightContenderId =
+ right->getStakeContenderId();
+ if (leftContenderId != rightContenderId) {
+ return leftContenderId < rightContenderId;
+ }
+
+ // If there's a collision in contender id, sort by proof id
+ return left->proofid < right->proofid;
+ });
+
+ // Only return up to some maximum number of contenders
+ pollableContenders.clear();
+ size_t numPollable = std::min(rankedContenders.size(), maxPollable);
+ pollableContenders.reserve(numPollable);
+ for (size_t i = 0; i < numPollable; i++) {
+ pollableContenders.push_back(
+ rankedContenders[i]->getStakeContenderId());
+ }
+
+ return pollableContenders.size();
+}
+
bool StakeContenderCache::getWinners(const BlockHash &prevblockhash,
std::vector<CScript> &payouts) const {
// Winners determined by avalanche are sorted by reward rank
diff --git a/src/avalanche/test/stakecontendercache_tests.cpp b/src/avalanche/test/stakecontendercache_tests.cpp
--- a/src/avalanche/test/stakecontendercache_tests.cpp
+++ b/src/avalanche/test/stakecontendercache_tests.cpp
@@ -569,4 +569,51 @@
CheckWinners(cache, blockhashes[0], {}, {});
}
+BOOST_AUTO_TEST_CASE(pollable_contenders_tests) {
+ Chainstate &active_chainstate = Assert(m_node.chainman)->ActiveChainstate();
+ StakeContenderCache cache;
+
+ CBlockIndex *pindex = active_chainstate.m_chain.Tip();
+ const BlockHash &blockhash = pindex->GetBlockHash();
+
+ const size_t maxPollable = 12;
+ std::vector<StakeContenderId> contenders;
+ BOOST_CHECK_EQUAL(
+ cache.getPollableContenders(blockhash, maxPollable, contenders), 0);
+
+ for (size_t c = 0; c < maxPollable * 2; c++) {
+ // Add a new contender with random initial state
+ auto proof = buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE);
+ BOOST_CHECK(cache.add(pindex, proof, InsecureRandBits(2)));
+
+ // We should never get more contenders than we can poll for in a single
+ // message.
+ BOOST_CHECK(cache.getPollableContenders(blockhash, maxPollable,
+ contenders) <= maxPollable);
+ BOOST_CHECK(contenders.size() <= maxPollable);
+
+ bool acceptanceLatch = true;
+ double lastRank = 0;
+ for (const auto &contender : contenders) {
+ // Check if contender is accepted
+ BlockHash dummy;
+ int voteStatus = cache.getVoteStatus(contender, dummy);
+
+ // Accepted contenders should always rank first, so latch off once
+ // we hit our first rejected contender.
+ if (acceptanceLatch && voteStatus != 0) {
+ acceptanceLatch = false;
+ lastRank = 0;
+ }
+ BOOST_CHECK_EQUAL(voteStatus, acceptanceLatch ? 0 : 1);
+
+ // Check the contender rank is sorted as we expect
+ double rank =
+ contender.ComputeProofRewardRank(MIN_VALID_PROOF_SCORE);
+ BOOST_CHECK(lastRank <= rank);
+ lastRank = rank;
+ }
+ }
+}
+
BOOST_AUTO_TEST_SUITE_END()

File Metadata

Mime Type
text/plain
Expires
Sat, Apr 26, 10:36 (20 m, 8 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5573295
Default Alt Text
D17479.diff (5 KB)

Event Timeline