31 auto &pview =
peers.get<by_proofid>();
32 auto it = pview.find(proofid);
33 if (it == pview.end()) {
48 const PeerId peerid = it->peerid;
50 auto nit =
nodes.find(nodeid);
51 if (nit ==
nodes.end()) {
52 if (!
nodes.emplace(nodeid, peerid).second) {
56 const PeerId oldpeerid = nit->peerid;
57 if (!
nodes.modify(nit, [&](
Node &n) { n.peerid = peerid; })) {
74 const ProofId &proofid = it->getProofId();
98 slots.emplace_back(start, score, it->peerid);
109 auto [begin, end] = remoteProofsView.equal_range(nodeid);
110 remoteProofsView.erase(begin, end);
117 auto it =
nodes.find(nodeid);
118 if (it ==
nodes.end()) {
122 const PeerId peerid = it->peerid;
137 if (it ==
peers.end()) {
141 assert(count <= it->node_count);
147 const uint32_t new_count = it->node_count -
count;
148 if (!
peers.modify(it, [&](
Peer &p) { p.node_count = new_count; })) {
165 const size_t i = it->index;
170 if (i + 1 ==
slots.size()) {
183 auto it =
nodes.find(nodeid);
184 if (it ==
nodes.end()) {
192 auto it =
nodes.find(nodeid);
193 if (it ==
nodes.end()) {
197 return !it->avaproofsSent &&
206 PeerId peerid,
const std::chrono::seconds &nextTime) {
207 auto it =
peers.find(peerid);
208 if (it ==
peers.end()) {
219 return it->nextPossibleConflictTime == nextTime;
223 auto it =
peers.find(peerid);
224 if (it ==
peers.end()) {
234 template <
typename ProofContainer>
236 auto &peersView =
peers.get<by_proofid>();
237 for (
const ProofRef &proof : proofs) {
238 auto it = peersView.find(proof->getId());
239 if (it != peersView.end()) {
252 const ProofId &proofid = proof->getId();
255 const std::string &message) {
256 return registrationState.
Invalid(
267 "proof-already-registered");
299 "utxo-missing-or-spent");
306 auto now = GetTime<std::chrono::seconds>();
307 auto nextCooldownTimePoint =
309 "-avalancheconflictingproofcooldown",
314 case ProofPool::AddProofStatus::REJECTED: {
316 auto bestPossibleConflictTime = std::chrono::seconds(0);
317 auto &pview =
peers.get<by_proofid>();
318 for (
auto &conflictingProof : conflictingProofs) {
319 auto it = pview.find(conflictingProof->getId());
320 assert(it != pview.end());
323 bestPossibleConflictTime = std::max(
324 bestPossibleConflictTime, it->nextPossibleConflictTime);
327 nextCooldownTimePoint);
330 if (bestPossibleConflictTime > now) {
334 "cooldown-not-elapsed");
350 ProofPool::AddProofStatus::REJECTED
354 "conflicting-utxos");
364 assert(status == ProofPool::AddProofStatus::SUCCEED);
368 case ProofPool::AddProofStatus::DUPLICATED:
371 "proof-already-registered");
372 case ProofPool::AddProofStatus::SUCCEED:
386 auto inserted =
peers.emplace(peerid, proof, nextCooldownTimePoint);
400 auto &pendingNodesView =
pendingNodes.get<by_proofid>();
401 auto range = pendingNodesView.equal_range(proofid);
406 std::vector<NodeId> nodeids;
407 nodeids.reserve(std::distance(range.first, range.second));
408 std::transform(range.first, range.second, std::back_inserter(nodeids),
411 for (
const NodeId &nodeid : nodeids) {
444 auto &pview =
peers.get<by_proofid>();
445 auto it = pview.find(proofid);
446 assert(it != pview.end());
458 if (!conflictingProof) {
474 std::unordered_set<ProofRef, SaltedProofHasher> ®isteredProofs) {
475 registeredProofs.clear();
476 const auto now = GetTime<std::chrono::seconds>();
478 std::vector<ProofRef> newlyDanglingProofs;
483 peer.node_count == 0 &&
490 newlyDanglingProofs.push_back(peer.proof);
497 std::vector<ProofRef> previouslyDanglingProofs;
500 previouslyDanglingProofs.push_back(proof);
503 for (
const ProofRef &proof : previouslyDanglingProofs) {
506 registeredProofs.insert(proof);
510 for (
const ProofRef &proof : newlyDanglingProofs) {
517 "Proof dangling for too long (no connected node): %s\n",
518 proof->getId().GetHex());
541 if (it != nview.end() && it->peerid == p &&
542 it->nextRequestTime <= Now<SteadyMilliseconds>()) {
554 std::vector<ProofId> invalidProofIds;
555 std::vector<ProofRef> newImmatures;
560 for (
const auto &p :
peers) {
564 newImmatures.push_back(p.proof);
566 invalidProofIds.push_back(p.getProofId());
569 "Invalidating proof %s: verification failed (%s)\n",
570 p.proof->getId().GetHex(), state.
ToString());
578 for (
const ProofId &invalidProofId : invalidProofIds) {
584 for (
auto &p : newImmatures) {
588 return registeredProofs;
611 auto &pview =
peers.get<by_proofid>();
612 return pview.find(proofid) != pview.end();
640 const bool present) {
642 auto &remoteProofsByLastUpdate =
remoteProofs.get<by_lastUpdate>();
643 auto [begin, end] = remoteProofsByLastUpdate.equal_range(nodeid);
650 while (
size_t(std::distance(begin, end)) >=
653 begin = remoteProofsByLastUpdate.erase(begin);
656 auto it =
remoteProofs.find(boost::make_tuple(proofid, nodeid));
662 .emplace(
RemoteProof{proofid, nodeid, GetTime<std::chrono::seconds>(),
667 std::vector<RemoteProof>
669 std::vector<RemoteProof> nodeRemoteProofs;
671 auto &remoteProofsByLastUpdate =
remoteProofs.get<by_lastUpdate>();
672 auto [begin, end] = remoteProofsByLastUpdate.equal_range(nodeid);
674 for (
auto &it = begin; it != end; it++) {
675 nodeRemoteProofs.emplace_back(*it);
678 return nodeRemoteProofs;
682 auto it =
peers.find(peerid);
683 if (it ==
peers.end()) {
693 auto range = nview.equal_range(peerid);
694 for (
auto &nit = range.first; nit != range.second; ++nit) {
704 boost::make_tuple(peerid, Now<SteadyMilliseconds>())));
744 std::vector<Slot> newslots;
745 newslots.reserve(
peers.size());
747 uint64_t prevStop = 0;
749 for (
auto it =
peers.begin(); it !=
peers.end(); it++) {
750 if (it->node_count == 0) {
754 newslots.emplace_back(prevStop, it->getScore(), it->peerid);
755 prevStop =
slots[i].getStop();
756 if (!
peers.modify(it, [&](
Peer &p) { p.index = i++; })) {
761 slots = std::move(newslots);
763 const uint64_t saved =
slotCount - prevStop;
771 uint64_t prevStop = 0;
772 uint32_t scoreFromSlots = 0;
773 for (
size_t i = 0; i <
slots.size(); i++) {
790 if (it ==
peers.end() || it->index != i) {
795 scoreFromSlots +=
slots[i].getScore();
803 uint32_t scoreFromAllPeers = 0;
804 uint32_t scoreFromPeersWithNodes = 0;
806 std::unordered_set<COutPoint, SaltedOutpointHasher> peersUtxos;
807 for (
const auto &p :
peers) {
809 scoreFromAllPeers += p.getScore();
817 for (
const auto &ss : p.proof->getStakes()) {
818 const COutPoint &outpoint = ss.getStake().getUTXO();
825 if (proof != p.proof) {
830 if (!peersUtxos.emplace(outpoint).second) {
837 const auto count_nodes = [&]() {
840 auto begin = nview.lower_bound(
842 auto end = nview.upper_bound(
845 for (
auto it = begin; it != end; ++it) {
852 if (p.node_count != count_nodes()) {
857 if (p.node_count == 0) {
861 scoreFromPeersWithNodes += p.getScore();
863 if (p.index >=
slots.size() ||
slots[p.index].getPeerId() != p.peerid) {
868 if (
slots[p.index].getScore() != p.getScore()) {
878 if (p.node_count == 0 &&
905 const uint64_t max) {
908 size_t begin = 0, end = slots.size();
909 uint64_t bottom = 0, top = max;
912 while ((end - begin) > 8) {
914 if (slot < bottom || slot >= top) {
919 size_t i = begin + ((slot - bottom) * (end - begin) / (top - bottom));
920 assert(begin <= i && i < end);
923 if (slots[i].contains(slot)) {
924 return slots[i].getPeerId();
928 if (slots[i].precedes(slot)) {
934 bottom = slots[begin].getStart();
939 if (slots[i].follows(slot)) {
941 top = slots[end].getStart();
950 for (
size_t i = begin; i < end; i++) {
952 if (slots[i].contains(slot)) {
953 return slots[i].getPeerId();
973 std::vector<CScript> &winners) {
984 std::chrono::seconds registrationDelay =
985 std::chrono::duration_cast<std::chrono::seconds>(
989 std::chrono::seconds maxRegistrationDelay =
990 std::chrono::duration_cast<std::chrono::seconds>(
994 std::chrono::seconds minRegistrationDelay =
995 std::chrono::duration_cast<std::chrono::seconds>(
1000 const int64_t targetRegistrationTime = refTime - registrationDelay.count();
1001 const int64_t maxRegistrationTime = refTime - minRegistrationDelay.count();
1002 const int64_t minRegistrationTime = refTime - maxRegistrationDelay.count();
1006 std::vector<ProofRef> selectedProofs;
1008 while (selectedProofs.size() <
peers.size()) {
1009 double bestRewardRank = std::numeric_limits<double>::max();
1011 int64_t selectedProofRegistrationTime{0};
1020 if (!peer.hasFinalized ||
1021 peer.registration_time.count() >= maxRegistrationTime) {
1025 if (std::find_if(selectedProofs.begin(), selectedProofs.end(),
1027 return peer.getProofId() == proof->getId();
1028 }) != selectedProofs.end()) {
1034 .
Write(prevblockhash)
1035 .
Write(peer.getProofId())
1043 "Staking reward hash has a suspicious value of zero for "
1044 "proof %s and blockhash %s, skipping\n",
1045 peer.getProofId().ToString(), prevblockhash.
ToString());
1055 double proofRewardRank =
1061 if (proofRewardRank < bestRewardRank) {
1062 bestRewardRank = proofRewardRank;
1063 selectedProof = peer.proof;
1064 selectedProofRegistrationTime = peer.registration_time.count();
1065 bestRewardHash = proofRewardHash;
1070 if (proofRewardRank == bestRewardRank &&
1071 (proofRewardHash < bestRewardHash ||
1072 (proofRewardHash == bestRewardHash &&
1073 peer.getProofId() < selectedProof->getId()))) {
1074 selectedProof = peer.proof;
1075 selectedProofRegistrationTime = peer.registration_time.count();
1076 bestRewardHash = proofRewardHash;
1080 if (!selectedProof) {
1085 if (!firstCompliantProof &&
1086 selectedProofRegistrationTime < targetRegistrationTime) {
1087 firstCompliantProof = selectedProof;
1090 selectedProofs.push_back(selectedProof);
1092 if (selectedProofRegistrationTime < minRegistrationTime &&
1093 !
isFlaky(selectedProof->getId())) {
1100 if (!firstCompliantProof) {
1104 winners.reserve(selectedProofs.size());
1107 for (
const ProofRef &proof : selectedProofs) {
1108 if (proof->getId() == firstCompliantProof->getId()) {
1109 winners.push_back(proof->getPayoutScript());
1113 for (
const ProofRef &proof : selectedProofs) {
1114 if (proof->getId() != firstCompliantProof->getId()) {
1115 winners.push_back(proof->getPayoutScript());
1133 auto &remoteProofsByNodeId =
remoteProofs.get<by_nodeid>();
1136 std::unordered_map<PeerId, std::unordered_set<ProofId, SaltedProofIdHasher>>
1140 double total_score{0};
1146 auto nodes_range = nview.equal_range(peerid);
1147 for (
auto &nit = nodes_range.first; nit != nodes_range.second; ++nit) {
1148 auto proofs_range = remoteProofsByNodeId.equal_range(nit->nodeid);
1149 for (
auto &proofit = proofs_range.first;
1150 proofit != proofs_range.second; ++proofit) {
1151 if (!proofit->present) {
1152 missing_per_peer[peerid].insert(proofit->proofid);
1158 double missing_score{0};
1161 for (
const auto &[peerid, missingProofs] : missing_per_peer) {
1162 if (missingProofs.size() > 3) {
1167 auto pit =
peers.find(peerid);
1168 if (pit ==
peers.end()) {
1173 if (missingProofs.count(proofid) > 0) {
1174 missing_score += pit->getScore();
1178 return (missing_score / total_score) > 0.3;
1183 auto &remoteProofsView =
remoteProofs.get<by_proofid>();
1184 auto [begin, end] = remoteProofsView.equal_range(proofid);
1188 return std::nullopt;
1191 double total_score{0};
1192 double present_score{0};
1193 double missing_score{0};
1195 for (
auto it = begin; it != end; it++) {
1196 auto nit =
nodes.find(it->nodeid);
1197 if (nit ==
nodes.end()) {
1202 const PeerId peerid = nit->peerid;
1204 auto pit =
peers.find(peerid);
1205 if (pit ==
peers.end()) {
1210 uint32_t node_count = pit->node_count;
1216 if (node_count == 0) {
1221 const double score = double(pit->getScore()) / node_count;
1223 total_score += score;
1225 present_score += score;
1227 missing_score += score;
1232 auto &peersByProofid =
peers.get<by_proofid>();
1235 bool present =
false;
1236 auto pit = peersByProofid.find(proofid);
1237 if (pit != peersByProofid.end()) {
1238 present = pit->node_count > 0;
1241 pit = peersByProofid.find(
localProof->getId());
1242 if (pit != peersByProofid.end()) {
1245 const double score =
1246 double(pit->getScore()) / (1 + pit->node_count);
1248 total_score += score;
1250 present_score += score;
1252 missing_score += score;
1257 if (present_score / total_score > 0.55) {
1258 return std::make_optional(
true);
1261 if (missing_score / total_score > 0.55) {
1262 return std::make_optional(
false);
1265 return std::nullopt;
1270 const fs::path dumpPathTmp = dumpPath +
".new";
1278 file << uint64_t(
peers.size());
1281 file << peer.hasFinalized;
1282 file << int64_t(peer.registration_time.count());
1283 file << int64_t(peer.nextPossibleConflictTime.count());
1287 throw std::runtime_error(
strprintf(
"Failed to commit to file %s",
1293 throw std::runtime_error(
strprintf(
"Rename failed from %s to %s",
1297 }
catch (
const std::exception &e) {
1311 std::unordered_set<ProofRef, SaltedProofHasher> ®isteredProofs) {
1312 registeredProofs.clear();
1318 "Failed to open avalanche peers file from disk.\n");
1328 "Unsupported avalanche peers file version.\n");
1335 auto &peersByProofId =
peers.get<by_proofid>();
1337 for (uint64_t i = 0; i < numPeers; i++) {
1340 int64_t registrationTime;
1341 int64_t nextPossibleConflictTime;
1344 file >> hasFinalized;
1345 file >> registrationTime;
1346 file >> nextPossibleConflictTime;
1349 auto it = peersByProofId.find(proof->getId());
1350 if (it == peersByProofId.end()) {
1359 peersByProofId.modify(it, [&](
Peer &p) {
1362 std::chrono::seconds{registrationTime};
1364 std::chrono::seconds{nextPossibleConflictTime};
1367 registeredProofs.insert(proof);
1370 }
catch (
const std::exception &e) {
1372 "Failed to read the avalanche peers file data on disk: %s.\n",
bool IsLeeKuanYewEnabled(const Consensus::Params ¶ms, int64_t nMedianTimePast)
Check if May 15th, 2024 protocol upgrade has activated.
arith_uint256 UintToArith256(const uint256 &a)
static constexpr PeerId NO_PEER
static constexpr size_t AVALANCHE_DEFAULT_CONFLICTING_PROOF_COOLDOWN
Conflicting proofs cooldown time default value in seconds.
int64_t GetIntArg(const std::string &strArg, int64_t nDefault) const
Return integer argument or default value.
bool IsNull() const
Return true if the wrapped FILE* is nullptr, false otherwise.
FILE * Get() const
Get wrapped FILE* without transfer of ownership.
The block chain is a tree shaped structure starting with the genesis block at the root,...
int64_t GetBlockTime() const
BlockHash GetBlockHash() const
A hasher class for Bitcoin's 256-bit hash (double SHA-256).
void Finalize(Span< uint8_t > output)
CHash256 & Write(Span< const uint8_t > input)
An outpoint - a combination of a transaction hash and an index n into its vout.
void insert(Span< const uint8_t > vKey)
bool contains(Span< const uint8_t > vKey) const
const Consensus::Params & GetConsensus() const
bool Invalid(Result result, const std::string &reject_reason="", const std::string &debug_message="")
std::string ToString() const
uint32_t connectedPeersScore
std::vector< RemoteProof > getRemoteProofs(const NodeId nodeid) const
bool removeNode(NodeId nodeid)
bool selectStakingRewardWinner(const CBlockIndex *pprev, std::vector< CScript > &winners)
Deterministically select a list of payout scripts based on the proof set and the previous block hash.
bool setFinalized(PeerId peerid)
Latch on that this peer has a finalized proof.
bool dumpPeersToFile(const fs::path &dumpPath) const
RemoteProofSet remoteProofs
Remember which node sent which proof so we have an image of the proof set of our peers.
bool isDangling(const ProofId &proofid) const
bool updateNextRequestTime(NodeId nodeid, SteadyMilliseconds timeout)
std::optional< bool > getRemotePresenceStatus(const ProofId &proofid) const
Get the presence remote status of a proof.
bool addNodeToPeer(const PeerSet::iterator &it)
bool exists(const ProofId &proofid) const
PendingNodeSet pendingNodes
bool verify() const
Perform consistency check on internal data structures.
bool forPeer(const ProofId &proofid, Callable &&func) const
bool latchAvaproofsSent(NodeId nodeid)
Flag that a node did send its compact proofs.
bool addNode(NodeId nodeid, const ProofId &proofid)
Node API.
static constexpr int SELECT_PEER_MAX_RETRY
ProofIdSet m_unbroadcast_proofids
Track proof ids to broadcast.
bool loadPeersFromFile(const fs::path &dumpPath, std::unordered_set< ProofRef, SaltedProofHasher > ®isteredProofs)
RejectionMode
Rejection mode.
void addUnbroadcastProof(const ProofId &proofid)
Proof broadcast API.
std::unordered_set< ProofRef, SaltedProofHasher > updatedBlockTip()
Update the peer set when a new block is connected.
void removeUnbroadcastProof(const ProofId &proofid)
bool isBoundToPeer(const ProofId &proofid) const
ProofRadixTree shareableProofs
bool saveRemoteProof(const ProofId &proofid, const NodeId nodeid, const bool present)
CRollingBloomFilter invalidProofs
Filter for proofs that are consensus-invalid or were recently invalidated by avalanche (finalized rej...
uint64_t compact()
Trigger maintenance of internal data structures.
std::vector< Slot > slots
uint32_t totalPeersScore
Quorum management.
ProofPool danglingProofPool
void setInvalid(const ProofId &proofid)
bool isFlaky(const ProofId &proofid) const
ChainstateManager & chainman
bool isInvalid(const ProofId &proofid) const
bool removePeer(const PeerId peerid)
Remove an existing peer.
bool isImmature(const ProofId &proofid) const
bool addOrUpdateNode(const PeerSet::iterator &it, NodeId nodeid)
bool rejectProof(const ProofId &proofid, RejectionMode mode=RejectionMode::DEFAULT)
ProofPool immatureProofPool
Amount stakeUtxoDustThreshold
RegistrationMode
Registration mode.
ProofPool conflictingProofPool
static constexpr size_t MAX_REMOTE_PROOFS
std::atomic< bool > needMoreNodes
Flag indicating that we failed to select a node and need to expand our node set.
PeerId selectPeer() const
Randomly select a peer to poll.
bool isInConflictingPool(const ProofId &proofid) const
static constexpr int SELECT_NODE_MAX_RETRY
void cleanupDanglingProofs(std::unordered_set< ProofRef, SaltedProofHasher > ®isteredProofs)
ProofRef getProof(const ProofId &proofid) const
bool registerProof(const ProofRef &proof, ProofRegistrationState ®istrationState, RegistrationMode mode=RegistrationMode::DEFAULT)
bool removeNodeFromPeer(const PeerSet::iterator &it, uint32_t count=1)
bool updateNextPossibleConflictTime(PeerId peerid, const std::chrono::seconds &nextTime)
Proof and Peer related API.
void moveToConflictingPool(const ProofContainer &proofs)
AddProofStatus addProofIfPreferred(const ProofRef &proof, ConflictingProofSet &conflictingProofs)
Attempt to add a proof to the pool.
AddProofStatus addProofIfNoConflict(const ProofRef &proof, ConflictingProofSet &conflictingProofs)
Attempt to add a proof to the pool, and fail if there is a conflict on any UTXO.
size_t countProofs() const
bool removeProof(ProofId proofid)
void forEachProof(Callable &&func) const
ProofRef getProof(const ProofId &proofid) const
std::set< ProofRef, ConflictingProofComparator > ConflictingProofSet
ProofRef getLowestScoreProof() const
std::unordered_set< ProofRef, SaltedProofHasher > rescan(PeerManager &peerManager)
std::string ToString() const
Path class wrapper to block calls to the fs::path(std::string) implicit constructor and the fs::path:...
static const uint256 ZERO
static constexpr int CLIENT_VERSION
bitcoind-res.rc includes this file, but it cannot cope with real c++ code.
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate.
#define LogPrint(category,...)
static constexpr uint32_t AVALANCHE_MAX_IMMATURE_PROOFS
Maximum number of immature proofs the peer manager will accept from the network.
static bool isImmatureState(const ProofValidationState &state)
static constexpr uint64_t PEERS_DUMP_VERSION
PeerId selectPeerImpl(const std::vector< Slot > &slots, const uint64_t slot, const uint64_t max)
Internal methods that are exposed for testing purposes.
RCUPtr< const Proof > ProofRef
static std::string PathToString(const path &path)
Convert path object to byte string.
FILE * fopen(const fs::path &p, const char *mode)
static constexpr NodeId NO_NODE
Special NodeId that represent no node.
T GetRand(T nMax=std::numeric_limits< T >::max()) noexcept
Generate a uniform random integer of type T in the range [0..nMax) nMax defaults to std::numeric_limi...
A BlockHash is a unqiue identifier for a block.
RCUPtr< T > get(const KeyType &key)
Get the value corresponding to a key.
RCUPtr< T > remove(const KeyType &key)
Remove an element from the tree.
bool forEachLeaf(Callable &&func) const
bool insert(const RCUPtr< T > &value)
Insert a value into the tree.
Facility for using an uint256 as a radix tree key.
SteadyMilliseconds nextRequestTime
std::chrono::seconds registration_time
std::chrono::seconds nextPossibleConflictTime
static constexpr auto DANGLING_TIMEOUT
Consider dropping the peer if no node is attached after this timeout expired.
uint32_t getScore() const
uint64_t getStart() const
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
bool RenameOver(fs::path src, fs::path dest)
bool FileCommit(FILE *file)
std::chrono::time_point< std::chrono::steady_clock, std::chrono::milliseconds > SteadyMilliseconds