Bitcoin ABC 0.30.13
P2P Digital Currency
stakecontendercache.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
9
10#include <algorithm>
11
12namespace avalanche {
13
14void StakeContenderCache::cleanup(const int requestedMinHeight) {
15 // Do not cleanup past the last promoted height, otherwise we lose cached
16 // remote proof data.
17 const int minHeight = std::min(lastPromotedHeight, requestedMinHeight);
18
19 std::set<BlockHash> hashesToErase;
20 auto &mwHeightView = manualWinners.get<by_blockheight>();
21 for (auto it = mwHeightView.begin();
22 it != mwHeightView.lower_bound(minHeight); it++) {
23 hashesToErase.insert(it->prevblockhash);
24 }
25
26 auto &cHeightView = contenders.get<by_blockheight>();
27 for (auto it = cHeightView.begin();
28 it != cHeightView.lower_bound(minHeight); it++) {
29 hashesToErase.insert(it->prevblockhash);
30 }
31
32 for (const auto &blockhash : hashesToErase) {
33 auto &mwHashView = manualWinners.get<by_prevblockhash>();
34 auto [mwHashBegin, mwHashEnd] = mwHashView.equal_range(blockhash);
35 mwHashView.erase(mwHashBegin, mwHashEnd);
36
37 auto &cHashView = contenders.get<by_prevblockhash>();
38 auto [cHashBegin, cHashEnd] = cHashView.equal_range(blockhash);
39 cHashView.erase(cHashBegin, cHashEnd);
40 }
41}
42
43bool StakeContenderCache::add(const CBlockIndex *pindex, const ProofRef &proof,
44 uint8_t status) {
45 return contenders
46 .emplace(pindex->GetBlockHash(), pindex->nHeight, proof->getId(),
47 status, proof->getPayoutScript(), proof->getScore())
48 .second;
49}
50
52 PeerManager &pm) {
53 // "Promote" past contenders to activeTip and check that those contenders
54 // are still valid proofs to be stake winners. This is done because new
55 // stake contenders are only added when a new proof is seen for the first
56 // time. We need to persist the cached payout scripts and proof scores since
57 // they are not guaranteed to be stored by peerManager.
58 const BlockHash &blockhash = activeTip->GetBlockHash();
59 const int height = activeTip->nHeight;
60 lastPromotedHeight = height;
61 for (auto &contender : contenders) {
62 const ProofId &proofid = contender.proofid;
63 if (pm.isRemoteProof(proofid) &&
64 (pm.isBoundToPeer(proofid) || pm.isDangling(proofid))) {
65 contenders.emplace(blockhash, height, proofid,
67 contender.payoutScriptPubkey, contender.score);
68 }
69 }
70}
71
73 const CBlockIndex *pindex, const std::vector<CScript> &payoutScripts) {
74 const BlockHash &prevblockhash = pindex->GetBlockHash();
75 auto &view = manualWinners.get<by_prevblockhash>();
76 auto it = view.find(prevblockhash);
77 if (it == view.end()) {
78 return manualWinners
79 .emplace(prevblockhash, pindex->nHeight, payoutScripts)
80 .second;
81 }
82 return manualWinners.replace(
83 it, ManualWinners(prevblockhash, pindex->nHeight, payoutScripts));
84}
85
87 auto &view = contenders.get<by_stakecontenderid>();
88 auto it = view.find(contenderId);
89 if (it == view.end()) {
90 return false;
91 }
92
93 return contenders.modify(it, [&](StakeContenderCacheEntry &entry) {
95 });
96}
97
99 auto &view = contenders.get<by_stakecontenderid>();
100 auto it = view.find(contenderId);
101 if (it == view.end()) {
102 return false;
103 }
104
105 return contenders.modify(it, [&](StakeContenderCacheEntry &entry) {
108 });
109}
110
112 auto &view = contenders.get<by_stakecontenderid>();
113 auto it = view.find(contenderId);
114 if (it == view.end()) {
115 return false;
116 }
117
118 return contenders.modify(it, [&](StakeContenderCacheEntry &entry) {
120 });
121}
122
124 BlockHash &prevblockhashout) const {
125 auto &view = contenders.get<by_stakecontenderid>();
126 auto it = view.find(contenderId);
127 if (it == view.end()) {
128 return -1;
129 }
130
131 prevblockhashout = it->prevblockhash;
132
133 // Contender is accepted
134 if (it->isAccepted()) {
135 return 0;
136 }
137
138 // If the contender matches a manual winner, it is accepted.
139 auto &manualWinnersView = manualWinners.get<by_prevblockhash>();
140 auto manualWinnerIt = manualWinnersView.find(it->prevblockhash);
141 if (manualWinnerIt != manualWinners.end()) {
142 for (auto &payoutScript : manualWinnerIt->payoutScripts) {
143 if (payoutScript == it->payoutScriptPubkey) {
144 return 0;
145 }
146 }
147 }
148
149 // Contender is rejected
150 return 1;
151}
152
154 const BlockHash &prevblockhash, size_t maxPollable,
155 std::vector<StakeContenderId> &pollableContenders) const {
156 std::vector<const StakeContenderCacheEntry *> rankedContenders;
157 auto &view = contenders.get<by_prevblockhash>();
158 auto [begin, end] = view.equal_range(prevblockhash);
159 for (auto it = begin; it != end; it++) {
160 rankedContenders.push_back(&(*it));
161 }
162
163 // First sort all contenders with accepted contenders first
164 std::sort(rankedContenders.begin(), rankedContenders.end(),
165 [](const StakeContenderCacheEntry *left,
166 const StakeContenderCacheEntry *right) {
167 if (left->isAccepted() != right->isAccepted()) {
168 // Accepted contenders sort first
169 return left->isAccepted();
170 }
171
172 double leftRank = left->computeRewardRank();
173 double rightRank = right->computeRewardRank();
174 const StakeContenderId &leftContenderId =
175 left->getStakeContenderId();
176 const StakeContenderId &rightContenderId =
177 right->getStakeContenderId();
178 return RewardRankComparator()(leftContenderId, leftRank,
179 left->proofid, rightContenderId,
180 rightRank, right->proofid);
181 });
182
183 // Sort again, only by reward rank, and only up to the max number of
184 // pollable contenders.
185 size_t numPollable = std::min(rankedContenders.size(), maxPollable);
186 std::sort(rankedContenders.begin(), rankedContenders.begin() + numPollable,
187 [](const StakeContenderCacheEntry *left,
188 const StakeContenderCacheEntry *right) {
189 double leftRank = left->computeRewardRank();
190 double rightRank = right->computeRewardRank();
191 const StakeContenderId &leftContenderId =
192 left->getStakeContenderId();
193 const StakeContenderId &rightContenderId =
194 right->getStakeContenderId();
195 return RewardRankComparator()(leftContenderId, leftRank,
196 left->proofid, rightContenderId,
197 rightRank, right->proofid);
198 });
199
200 // Only return up to the maximum number of contenders
201 pollableContenders.clear();
202 pollableContenders.reserve(numPollable);
203 for (size_t i = 0; i < numPollable; i++) {
204 pollableContenders.push_back(
205 rankedContenders[i]->getStakeContenderId());
206 }
207
208 return pollableContenders.size();
209}
210
211bool StakeContenderCache::getWinners(
212 const BlockHash &prevblockhash,
213 std::vector<std::pair<ProofId, CScript>> &winners) const {
214 // Winners determined by avalanche are sorted by reward rank
215 std::vector<const StakeContenderCacheEntry *> rankedWinners;
216 auto &view = contenders.get<by_prevblockhash>();
217 auto [begin, end] = view.equal_range(prevblockhash);
218 for (auto it = begin; it != end; it++) {
219 if (it->isInWinnerSet()) {
220 rankedWinners.push_back(&(*it));
221 }
222 }
223
224 std::sort(rankedWinners.begin(), rankedWinners.end(),
225 [](const StakeContenderCacheEntry *left,
226 const StakeContenderCacheEntry *right) {
227 if (left->isAccepted() != right->isAccepted()) {
228 // Accepted contenders sort first
229 return left->isAccepted();
230 }
231
232 double leftRank = left->computeRewardRank();
233 double rightRank = right->computeRewardRank();
234 const StakeContenderId &leftContenderId =
235 left->getStakeContenderId();
236 const StakeContenderId &rightContenderId =
237 right->getStakeContenderId();
238 return RewardRankComparator()(leftContenderId, leftRank,
239 left->proofid, rightContenderId,
240 rightRank, right->proofid);
241 });
242
243 winners.clear();
244
245 // Add manual winners first, preserving order
246 auto &manualWinnersView = manualWinners.get<by_prevblockhash>();
247 auto manualWinnerIt = manualWinnersView.find(prevblockhash);
248 if (manualWinnerIt != manualWinners.end()) {
249 winners.reserve(manualWinnerIt->payoutScripts.size() +
250 rankedWinners.size());
251
252 for (auto &payoutScript : manualWinnerIt->payoutScripts) {
253 winners.push_back({ProofId(), payoutScript});
254 }
255 } else {
256 winners.reserve(rankedWinners.size());
257 }
258
259 // Add ranked winners, preserving reward rank order
260 for (const auto &rankedWinner : rankedWinners) {
261 winners.push_back(
262 {rankedWinner->proofid, rankedWinner->payoutScriptPubkey});
263 }
264
265 return winners.size() > 0;
266}
267
268} // namespace avalanche
The block chain is a tree shaped structure starting with the genesis block at the root,...
Definition: blockindex.h:25
BlockHash GetBlockHash() const
Definition: blockindex.h:146
int nHeight
height of the entry in the chain. The genesis block has height 0
Definition: blockindex.h:38
bool isDangling(const ProofId &proofid) const
bool isRemoteProof(const ProofId &proofid) const
bool isBoundToPeer(const ProofId &proofid) 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.
int getVoteStatus(const StakeContenderId &contenderId, BlockHash &prevblockhashout) const
Get contender acceptance state for avalanche voting.
bool finalize(const StakeContenderId &contenderId)
void promoteToBlock(const CBlockIndex *activeTip, PeerManager &pm)
Promote cache entries to a the active chain tip.
A BlockHash is a unqiue identifier for a block.
Definition: blockhash.h:13
uint8_t status
ProofId proofid
double computeRewardRank() const
StakeContenderId getStakeContenderId() const
StakeContenderIds are unique for each block to ensure that the peer polling for their acceptance has ...