Bitcoin ABC 0.30.5
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
8
9#include <algorithm>
10
11namespace avalanche {
12
13void StakeContenderCache::cleanup(const int requestedMinHeight) {
14 // Do not cleanup past the last promoted height, otherwise we lose cached
15 // remote proof data.
16 const int minHeight = std::min(lastPromotedHeight, requestedMinHeight);
17
18 std::set<BlockHash> hashesToErase;
19 auto &mwHeightView = manualWinners.get<by_blockheight>();
20 for (auto it = mwHeightView.begin();
21 it != mwHeightView.lower_bound(minHeight); it++) {
22 hashesToErase.insert(it->prevblockhash);
23 }
24
25 auto &cHeightView = contenders.get<by_blockheight>();
26 for (auto it = cHeightView.begin();
27 it != cHeightView.lower_bound(minHeight); it++) {
28 hashesToErase.insert(it->prevblockhash);
29 }
30
31 for (const auto &blockhash : hashesToErase) {
32 auto &mwHashView = manualWinners.get<by_prevblockhash>();
33 auto [mwHashBegin, mwHashEnd] = mwHashView.equal_range(blockhash);
34 mwHashView.erase(mwHashBegin, mwHashEnd);
35
36 auto &cHashView = contenders.get<by_prevblockhash>();
37 auto [cHashBegin, cHashEnd] = cHashView.equal_range(blockhash);
38 cHashView.erase(cHashBegin, cHashEnd);
39 }
40}
41
42bool StakeContenderCache::add(const CBlockIndex *pindex, const ProofRef &proof,
43 uint8_t status) {
44 return contenders
45 .emplace(pindex->GetBlockHash(), pindex->nHeight, proof->getId(),
46 status, proof->getPayoutScript(), proof->getScore())
47 .second;
48}
49
51 PeerManager &pm) {
52 // "Promote" past contenders to activeTip and check that those contenders
53 // are still valid proofs to be stake winners. This is done because new
54 // stake contenders are only added when a new proof is seen for the first
55 // time. We need to persist the cached payout scripts and proof scores since
56 // they are not guaranteed to be stored by peerManager.
57 const BlockHash &blockhash = activeTip->GetBlockHash();
58 const int height = activeTip->nHeight;
59 lastPromotedHeight = height;
60 for (auto &contender : contenders) {
61 const ProofId &proofid = contender.proofid;
62 if (pm.isRemoteProof(proofid) &&
63 (pm.isBoundToPeer(proofid) || pm.isDangling(proofid))) {
64 contenders.emplace(blockhash, height, proofid,
66 contender.payoutScriptPubkey, contender.score);
67 }
68 }
69}
70
72 const CBlockIndex *pindex, const std::vector<CScript> &payoutScripts) {
73 const BlockHash &prevblockhash = pindex->GetBlockHash();
74 auto &view = manualWinners.get<by_prevblockhash>();
75 auto it = view.find(prevblockhash);
76 if (it == view.end()) {
77 return manualWinners
78 .emplace(prevblockhash, pindex->nHeight, payoutScripts)
79 .second;
80 }
81 return manualWinners.replace(
82 it, ManualWinners(prevblockhash, pindex->nHeight, payoutScripts));
83}
84
86 auto &view = contenders.get<by_stakecontenderid>();
87 auto it = view.find(contenderId);
88 if (it == view.end()) {
89 return false;
90 }
91
92 return contenders.modify(it, [&](StakeContenderCacheEntry &entry) {
94 });
95}
96
98 auto &view = contenders.get<by_stakecontenderid>();
99 auto it = view.find(contenderId);
100 if (it == view.end()) {
101 return false;
102 }
103
104 return contenders.modify(it, [&](StakeContenderCacheEntry &entry) {
107 });
108}
109
111 auto &view = contenders.get<by_stakecontenderid>();
112 auto it = view.find(contenderId);
113 if (it == view.end()) {
114 return false;
115 }
116
117 return contenders.modify(it, [&](StakeContenderCacheEntry &entry) {
119 });
120}
121
123 auto &view = contenders.get<by_stakecontenderid>();
124 auto it = view.find(contenderId);
125 if (it == view.end()) {
126 return false;
127 }
128
129 return contenders.modify(it, [&](StakeContenderCacheEntry &entry) {
132 });
133}
134
136 BlockHash &prevblockhashout) const {
137 auto &view = contenders.get<by_stakecontenderid>();
138 auto it = view.find(contenderId);
139 if (it == view.end()) {
140 return -1;
141 }
142
143 prevblockhashout = it->prevblockhash;
144
145 // Contender is accepted
146 if (it->isAccepted()) {
147 return 0;
148 }
149
150 // If the contender matches a manual winner, it is accepted.
151 auto &manualWinnersView = manualWinners.get<by_prevblockhash>();
152 auto manualWinnerIt = manualWinnersView.find(it->prevblockhash);
153 if (manualWinnerIt != manualWinners.end()) {
154 for (auto &payoutScript : manualWinnerIt->payoutScripts) {
155 if (payoutScript == it->payoutScriptPubkey) {
156 return 0;
157 }
158 }
159 }
160
161 // Contender is rejected
162 return 1;
163}
164
166 std::vector<CScript> &payouts) const {
167 // Winners determined by avalanche are sorted by reward rank
168 std::vector<const StakeContenderCacheEntry *> rankedWinners;
169 auto &view = contenders.get<by_prevblockhash>();
170 auto [begin, end] = view.equal_range(prevblockhash);
171 for (auto it = begin; it != end; it++) {
172 if (it->isInWinnerSet()) {
173 rankedWinners.push_back(&(*it));
174 }
175 }
176
177 std::sort(rankedWinners.begin(), rankedWinners.end(),
178 [](const StakeContenderCacheEntry *left,
179 const StakeContenderCacheEntry *right) {
180 return left->computeRewardRank() < right->computeRewardRank();
181 });
182
183 payouts.clear();
184
185 // Add manual winners first, preserving order
186 auto &manualWinnersView = manualWinners.get<by_prevblockhash>();
187 auto manualWinnerIt = manualWinnersView.find(prevblockhash);
188 if (manualWinnerIt != manualWinners.end()) {
189 payouts.reserve(manualWinnerIt->payoutScripts.size() +
190 rankedWinners.size());
191
192 payouts.insert(payouts.begin(), manualWinnerIt->payoutScripts.begin(),
193 manualWinnerIt->payoutScripts.end());
194 } else {
195 payouts.reserve(rankedWinners.size());
196 }
197
198 // Add ranked winners, preserving reward rank order
199 for (const auto &rankedWinner : rankedWinners) {
200 payouts.push_back(rankedWinner->payoutScriptPubkey);
201 }
202
203 return payouts.size() > 0;
204}
205
206} // 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 invalidate(const StakeContenderId &contenderId)
bool accept(const StakeContenderId &contenderId)
Helpers to set avalanche state of a contender.
void cleanup(const int requestedMinHeight)
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)
bool getWinners(const BlockHash &prevblockhash, std::vector< CScript > &payouts) const
Get payout scripts of the winning proofs.
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
StakeContenderIds are unique for each block to ensure that the peer polling for their acceptance has ...