Changeset View
Changeset View
Standalone View
Standalone View
src/avalanche/processor.cpp
| Show All 29 Lines | |||||
| */ | */ | ||||
| static constexpr std::chrono::milliseconds AVALANCHE_TIME_STEP{10}; | static constexpr std::chrono::milliseconds AVALANCHE_TIME_STEP{10}; | ||||
| // Unfortunately, the bitcoind codebase is full of global and we are kinda | // Unfortunately, the bitcoind codebase is full of global and we are kinda | ||||
| // forced into it here. | // forced into it here. | ||||
| std::unique_ptr<avalanche::Processor> g_avalanche; | std::unique_ptr<avalanche::Processor> g_avalanche; | ||||
| namespace avalanche { | namespace avalanche { | ||||
| static bool IsWorthPolling(const CBlockIndex *pindex) | bool Processor::IsWorthPolling(const CBlockIndex *pindex) const { | ||||
| EXCLUSIVE_LOCKS_REQUIRED(cs_main) { | |||||
| AssertLockHeld(cs_main); | AssertLockHeld(cs_main); | ||||
| if (pindex->nStatus.isInvalid()) { | if (pindex->nStatus.isInvalid()) { | ||||
| // No point polling invalid blocks. | // No point polling invalid blocks. | ||||
| return false; | return false; | ||||
| } | } | ||||
| if (::ChainstateActive().IsBlockFinalized(pindex)) { | if (::ChainstateActive().IsBlockFinalized(pindex)) { | ||||
| // There is no point polling finalized block. | // There is no point polling finalized block. | ||||
| return false; | return false; | ||||
| } | } | ||||
| return true; | return true; | ||||
| } | } | ||||
| bool Processor::IsWorthPolling(const ProofRef &proof) const { | |||||
| AssertLockHeld(cs_peerManager); | |||||
| const ProofId &proofid = proof->getId(); | |||||
| // No point polling orphans or discarded proofs | |||||
| return peerManager->isBoundToPeer(proofid) || | |||||
| peerManager->isInConflictingPool(proofid); | |||||
| } | |||||
| static bool VerifyProof(const Proof &proof, bilingual_str &error) { | static bool VerifyProof(const Proof &proof, bilingual_str &error) { | ||||
| ProofValidationState proof_state; | ProofValidationState proof_state; | ||||
| if (!proof.verify(proof_state)) { | if (!proof.verify(proof_state)) { | ||||
| switch (proof_state.GetResult()) { | switch (proof_state.GetResult()) { | ||||
| case ProofValidationResult::NO_STAKE: | case ProofValidationResult::NO_STAKE: | ||||
| error = _("The avalanche proof has no stake."); | error = _("The avalanche proof has no stake."); | ||||
| return false; | return false; | ||||
| ▲ Show 20 Lines • Show All 285 Lines • ▼ Show 20 Lines | if (!pindex) { | ||||
| isAccepted = ::ChainActive().Contains(pindex); | isAccepted = ::ChainActive().Contains(pindex); | ||||
| } | } | ||||
| return blockVoteRecords.getWriteView() | return blockVoteRecords.getWriteView() | ||||
| ->insert(std::make_pair(pindex, VoteRecord(isAccepted))) | ->insert(std::make_pair(pindex, VoteRecord(isAccepted))) | ||||
| .second; | .second; | ||||
| } | } | ||||
| void Processor::addProofToReconcile(const ProofRef &proof) { | bool Processor::addProofToReconcile(const ProofRef &proof) { | ||||
| // TODO We don't want to accept an infinite number of conflicting proofs. | bool isAccepted; | ||||
| // They should be some rules to make them expensive and/or limited by | |||||
| // design. | if (!proof) { | ||||
| const bool isAccepted = WITH_LOCK( | // IsWorthPolling expects this to be non-null, so bail early. | ||||
| cs_peerManager, return peerManager->isBoundToPeer(proof->getId())); | return false; | ||||
| } | |||||
| { | |||||
| LOCK(cs_peerManager); | |||||
| if (!IsWorthPolling(proof)) { | |||||
| return false; | |||||
| } | |||||
| proofVoteRecords.getWriteView()->insert( | isAccepted = peerManager->isBoundToPeer(proof->getId()); | ||||
| std::make_pair(proof, VoteRecord(isAccepted))); | } | ||||
| return proofVoteRecords.getWriteView() | |||||
| ->insert(std::make_pair(proof, VoteRecord(isAccepted))) | |||||
| .second; | |||||
| } | } | ||||
| bool Processor::isAccepted(const CBlockIndex *pindex) const { | bool Processor::isAccepted(const CBlockIndex *pindex) const { | ||||
| if (!pindex) { | if (!pindex) { | ||||
| // CBlockIndexWorkComparator expects this to be non-null, so bail early. | // CBlockIndexWorkComparator expects this to be non-null, so bail early. | ||||
| return false; | return false; | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 150 Lines • ▼ Show 20 Lines | for (size_t i = 0; i < size; i++) { | ||||
| } | } | ||||
| responseIndex.insert(std::make_pair(pindex, votes[i])); | responseIndex.insert(std::make_pair(pindex, votes[i])); | ||||
| } | } | ||||
| if (invs[i].IsMsgProof()) { | if (invs[i].IsMsgProof()) { | ||||
| const ProofId proofid(votes[i].GetHash()); | const ProofId proofid(votes[i].GetHash()); | ||||
| const ProofRef proof = WITH_LOCK( | ProofRef proof; | ||||
| cs_peerManager, return peerManager->getProof(proofid)); | { | ||||
| LOCK(cs_peerManager); | |||||
| proof = peerManager->getProof(proofid); | |||||
| if (!proof) { | if (!proof) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| if (!IsWorthPolling(proof)) { | |||||
| continue; | |||||
| } | |||||
| } | |||||
| responseProof.insert(std::make_pair(proof, votes[i])); | responseProof.insert(std::make_pair(proof, votes[i])); | ||||
| } | } | ||||
| } | } | ||||
| // Thanks to C++14 generic lambdas, we can apply the same logic to various | // Thanks to C++14 generic lambdas, we can apply the same logic to various | ||||
| // parameter types sharing the same interface. | // parameter types sharing the same interface. | ||||
| auto registerVoteItems = [&](auto voteRecordsWriteView, auto &updates, | auto registerVoteItems = [&](auto voteRecordsWriteView, auto &updates, | ||||
| auto responseItems) { | auto responseItems) { | ||||
| ▲ Show 20 Lines • Show All 97 Lines • ▼ Show 20 Lines | |||||
| bool Processor::stopEventLoop() { | bool Processor::stopEventLoop() { | ||||
| return eventLoop.stopEventLoop(); | return eventLoop.stopEventLoop(); | ||||
| } | } | ||||
| std::vector<CInv> Processor::getInvsForNextPoll(bool forPoll) { | std::vector<CInv> Processor::getInvsForNextPoll(bool forPoll) { | ||||
| std::vector<CInv> invs; | std::vector<CInv> invs; | ||||
| // Use NO_THREAD_SAFETY_ANALYSIS to avoid false positive due to | |||||
| // IsWorthPolling requiring a different lock depending of the prototype. | |||||
| auto removeItemsNotWorthPolling = | |||||
| [&](auto &itemVoteRecords) NO_THREAD_SAFETY_ANALYSIS { | |||||
| auto w = itemVoteRecords.getWriteView(); | |||||
| for (auto it = w->begin(); it != w->end();) { | |||||
| if (!IsWorthPolling(it->first)) { | |||||
| w->erase(it++); | |||||
| } else { | |||||
| ++it; | |||||
sdulfari: Can simplify to remove the else clause:
```
if (!IsWorthPolling(it->first)) {
w->erase(it)… | |||||
sdulfariUnsubmitted Not Done Inline Actionsit becomes invalidated in the erase case, so this is actually wrong. sdulfari: `it` becomes invalidated in the erase case, so this is actually wrong. | |||||
| } | |||||
| } | |||||
| }; | |||||
| auto extractVoteRecordsToInvs = [&](const auto &itemVoteRecordRange, | auto extractVoteRecordsToInvs = [&](const auto &itemVoteRecordRange, | ||||
| auto buildInvFromVoteItem) { | auto buildInvFromVoteItem) { | ||||
| for (const auto &[item, voteRecord] : itemVoteRecordRange) { | for (const auto &[item, voteRecord] : itemVoteRecordRange) { | ||||
| if (invs.size() >= AVALANCHE_MAX_ELEMENT_POLL) { | if (invs.size() >= AVALANCHE_MAX_ELEMENT_POLL) { | ||||
| // Make sure we do not produce more invs than specified by the | // Make sure we do not produce more invs than specified by the | ||||
| // protocol. | // protocol. | ||||
| return true; | return true; | ||||
| } | } | ||||
| const bool shouldPoll = | const bool shouldPoll = | ||||
| forPoll ? voteRecord.registerPoll() : voteRecord.shouldPoll(); | forPoll ? voteRecord.registerPoll() : voteRecord.shouldPoll(); | ||||
| if (!shouldPoll) { | if (!shouldPoll) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| invs.emplace_back(buildInvFromVoteItem(item)); | invs.emplace_back(buildInvFromVoteItem(item)); | ||||
| } | } | ||||
| return invs.size() >= AVALANCHE_MAX_ELEMENT_POLL; | return invs.size() >= AVALANCHE_MAX_ELEMENT_POLL; | ||||
| }; | }; | ||||
| // First remove all proofs that are not worth polling. | |||||
| WITH_LOCK(cs_peerManager, removeItemsNotWorthPolling(proofVoteRecords)); | |||||
| if (extractVoteRecordsToInvs(proofVoteRecords.getReadView(), | if (extractVoteRecordsToInvs(proofVoteRecords.getReadView(), | ||||
| [](const ProofRef &proof) { | [](const ProofRef &proof) { | ||||
| return CInv(MSG_AVA_PROOF, proof->getId()); | return CInv(MSG_AVA_PROOF, proof->getId()); | ||||
| })) { | })) { | ||||
| // The inventory vector is full, we're done | // The inventory vector is full, we're done | ||||
| return invs; | return invs; | ||||
| } | } | ||||
| // First remove all blocks that are not worth polling. | // First remove all blocks that are not worth polling. | ||||
| { | WITH_LOCK(cs_main, removeItemsNotWorthPolling(blockVoteRecords)); | ||||
| LOCK(cs_main); | |||||
| auto w = blockVoteRecords.getWriteView(); | |||||
| for (auto it = w->begin(); it != w->end();) { | |||||
| const CBlockIndex *pindex = it->first; | |||||
| if (!IsWorthPolling(pindex)) { | |||||
| w->erase(it++); | |||||
| } else { | |||||
| ++it; | |||||
| } | |||||
| } | |||||
| } | |||||
| auto r = blockVoteRecords.getReadView(); | auto r = blockVoteRecords.getReadView(); | ||||
| extractVoteRecordsToInvs(reverse_iterate(r), [](const CBlockIndex *pindex) { | extractVoteRecordsToInvs(reverse_iterate(r), [](const CBlockIndex *pindex) { | ||||
| return CInv(MSG_BLOCK, pindex->GetBlockHash()); | return CInv(MSG_BLOCK, pindex->GetBlockHash()); | ||||
| }); | }); | ||||
| return invs; | return invs; | ||||
| } | } | ||||
| ▲ Show 20 Lines • Show All 212 Lines • Show Last 20 Lines | |||||
Can simplify to remove the else clause:
if (!IsWorthPolling(it->first)) { w->erase(it); } ++it;