diff --git a/src/avalanche/processor.h b/src/avalanche/processor.h --- a/src/avalanche/processor.h +++ b/src/avalanche/processor.h @@ -25,6 +25,7 @@ #include #include #include +#include #include class ArgsManager; @@ -184,8 +185,10 @@ } bool addBlockToReconcile(const CBlockIndex *pindex); - void addProofToReconcile(const std::shared_ptr &proof, - bool isAccepted); + void + addProofToReconcile(const std::shared_ptr &proof, + const std::unordered_set &conflictingPeerIds, + bool isAccepted); bool isAccepted(const CBlockIndex *pindex) const; int getConfidence(const CBlockIndex *pindex) const; @@ -225,7 +228,11 @@ */ uint256 buildLocalSighash(CNode *pfrom) const; + /** Test API */ friend struct ::avalanche::AvalancheTest; + + void setPeerData(const std::shared_ptr proof, const Delegation &dg, + PeerId peerid); }; } // namespace avalanche diff --git a/src/avalanche/processor.cpp b/src/avalanche/processor.cpp --- a/src/avalanche/processor.cpp +++ b/src/avalanche/processor.cpp @@ -108,6 +108,9 @@ struct Processor::PeerData { std::shared_ptr proof; Delegation delegation; + PeerId peerid; + + PeerData() : peerid(NO_PEER) {} }; class Processor::NotificationsHandler @@ -120,12 +123,16 @@ void updatedBlockTip() override { LOCK(m_processor->cs_peerManager); + m_processor->peerManager->updatedBlockTip(); + + // Fetch or create a peer id for our local proof and cache it. + // This needs to happen after the peer manager updatedBlockTip() in + // order to get the correct peerid in the case our proof just left the + // orphan pool. if (m_processor->peerData && m_processor->peerData->proof) { - m_processor->peerManager->registerProof( + m_processor->peerData->peerid = m_processor->peerManager->getPeerId( m_processor->peerData->proof); } - - m_processor->peerManager->updatedBlockTip(); } }; @@ -261,13 +268,16 @@ .second; } -void Processor::addProofToReconcile(const std::shared_ptr &proof, - bool isAccepted) { +void Processor::addProofToReconcile( + const std::shared_ptr &proof, + const std::unordered_set &conflictingPeerIds, bool isAccepted) { // TODO We don't want to accept an infinite number of conflicting proofs. // They should be some rules to make them expensive and/or limited by // design. - proofsVoteRecords.getWriteView()->insert( - std::make_pair(proof, VoteRecord(isAccepted))); + proofsVoteRecords.getWriteView()->insert(std::make_pair( + proof, + VoteRecord(isAccepted && !conflictingPeerIds.count( + peerData ? peerData->peerid : NO_PEER)))); } bool Processor::isAccepted(const CBlockIndex *pindex) const { @@ -657,4 +667,15 @@ } while (nodeid != NO_NODE); } +void Processor::setPeerData(const std::shared_ptr proof, + const Delegation &dg, PeerId peerid) { + if (!peerData) { + peerData = std::make_unique(); + } + + peerData->proof = proof; + peerData->delegation = dg; + peerData->peerid = peerid; +} + } // namespace avalanche diff --git a/src/avalanche/test/processor_tests.cpp b/src/avalanche/test/processor_tests.cpp --- a/src/avalanche/test/processor_tests.cpp +++ b/src/avalanche/test/processor_tests.cpp @@ -39,6 +39,23 @@ } static uint64_t getRound(const Processor &p) { return p.round; } + + static void setLocalPeerId(Processor &p, PeerId peerid) { + p.setPeerData(std::make_shared(), Delegation(), peerid); + } + + static std::optional + getVoteForProof(const Processor &p, + const std::shared_ptr &proof) { + auto conflictingProofsReadView = p.proofsVoteRecords.getReadView(); + for (auto it = conflictingProofsReadView.begin(); + it != conflictingProofsReadView.end(); it++) { + if (it->first->getId() == proof->getId()) { + return std::make_optional(VoteRecord(it->second)); + } + } + return std::nullopt; + } }; } // namespace } // namespace avalanche @@ -988,7 +1005,7 @@ auto addProofToReconcile = [&](uint32_t proofScore) { auto proof = std::make_shared(buildRandomProof(proofScore)); - m_processor->addProofToReconcile(proof, GetRandInt(1)); + m_processor->addProofToReconcile(proof, {}, GetRandInt(1)); return proof; }; @@ -1032,4 +1049,24 @@ } } +BOOST_AUTO_TEST_CASE(localProofConflict) { + const PeerId localPeerId = 42; + AvalancheTest::setLocalPeerId(*m_processor, localPeerId); + + auto checkVote = [&](const std::unordered_set &conflictingPeerIds, + bool expectedVote) { + auto proof = GetProof(); + m_processor->addProofToReconcile(proof, conflictingPeerIds, true); + auto vote = AvalancheTest::getVoteForProof(*m_processor, proof); + BOOST_CHECK(vote); + BOOST_CHECK_EQUAL(vote->isAccepted(), expectedVote); + }; + + checkVote({41}, true); + checkVote({42}, false); + checkVote({43}, true); + checkVote({41, 43}, true); + checkVote({41, 42, 43}, false); +} + BOOST_AUTO_TEST_SUITE_END()