Changeset View
Changeset View
Standalone View
Standalone View
src/avalanche/test/processor_tests.cpp
| Show All 26 Lines | |||||
| #include <functional> | #include <functional> | ||||
| #include <vector> | #include <vector> | ||||
| using namespace avalanche; | using namespace avalanche; | ||||
| namespace avalanche { | namespace avalanche { | ||||
| namespace { | namespace { | ||||
| struct TestPeerManager { | |||||
tyler-smith: @Fabien I need to be able to get the peerid in tests for isQuorumEstablished, and am not sure… | |||||
FabienUnsubmitted Not Done Inline ActionsI'm not sure you need the peer id here. My assumption is that you want it so you can call removePeer(peerid) is that correct ? Fabien: I'm not sure you need the peer id here. My assumption is that you want it so you can call… | |||||
| static PeerId getPeerIdForProofId(PeerManager &pm, | |||||
| const ProofId &proofid) { | |||||
| auto &pview = pm.peers.get<by_proofid>(); | |||||
| auto it = pview.find(proofid); | |||||
| return it == pview.end() ? NO_PEER : it->peerid; | |||||
| } | |||||
| static PeerId registerAndGetPeerId(PeerManager &pm, | |||||
| const ProofRef &proof) { | |||||
| pm.registerProof(proof); | |||||
| return getPeerIdForProofId(pm, proof->getId()); | |||||
| } | |||||
| }; | |||||
| struct AvalancheTest { | struct AvalancheTest { | ||||
| static void runEventLoop(avalanche::Processor &p) { p.runEventLoop(); } | static void runEventLoop(avalanche::Processor &p) { p.runEventLoop(); } | ||||
| static std::vector<CInv> getInvsForNextPoll(Processor &p) { | static std::vector<CInv> getInvsForNextPoll(Processor &p) { | ||||
| return p.getInvsForNextPoll(false); | return p.getInvsForNextPoll(false); | ||||
| } | } | ||||
| static NodeId getSuitableNodeToQuery(Processor &p) { | static NodeId getSuitableNodeToQuery(Processor &p) { | ||||
| return p.getSuitableNodeToQuery(); | return p.getSuitableNodeToQuery(); | ||||
| } | } | ||||
| static uint64_t getRound(const Processor &p) { return p.round; } | static uint64_t getRound(const Processor &p) { return p.round; } | ||||
| static uint32_t getMinQuorumScore(const Processor &p) { | |||||
| return p.minQuorumScore; | |||||
| } | |||||
| static double getMinQuorumConnectedScoreRatio(const Processor &p) { | |||||
| return p.minQuorumConnectedScoreRatio; | |||||
| } | |||||
| }; | }; | ||||
| } // namespace | } // namespace | ||||
| } // namespace avalanche | } // namespace avalanche | ||||
| namespace { | namespace { | ||||
| struct CConnmanTest : public CConnman { | struct CConnmanTest : public CConnman { | ||||
| using CConnman::CConnman; | using CConnman::CConnman; | ||||
| void AddNode(CNode &node) { | void AddNode(CNode &node) { | ||||
| ▲ Show 20 Lines • Show All 600 Lines • ▼ Show 20 Lines | BOOST_AUTO_TEST_CASE_TEMPLATE(multi_item_register, P, VoteItemProviders) { | ||||
| // Now it is accepted, but we can vote for it numerous times. | // Now it is accepted, but we can vote for it numerous times. | ||||
| for (int i = 0; i < AVALANCHE_FINALIZATION_SCORE; i++) { | for (int i = 0; i < AVALANCHE_FINALIZATION_SCORE; i++) { | ||||
| NodeId nodeid = getSuitableNodeToQuery(); | NodeId nodeid = getSuitableNodeToQuery(); | ||||
| runEventLoop(); | runEventLoop(); | ||||
| BOOST_CHECK(provider.registerVotes(nodeid, next(resp))); | BOOST_CHECK(provider.registerVotes(nodeid, next(resp))); | ||||
| BOOST_CHECK_EQUAL(updates.size(), 0); | BOOST_CHECK_EQUAL(updates.size(), 0); | ||||
| } | } | ||||
| // Running two iterration of the event loop so that vote gets triggered on A | // Running two iterration of the event loop so that vote gets triggered on | ||||
| // and B. | // and B. | ||||
| NodeId firstNodeid = getSuitableNodeToQuery(); | NodeId firstNodeid = getSuitableNodeToQuery(); | ||||
| runEventLoop(); | runEventLoop(); | ||||
| NodeId secondNodeid = getSuitableNodeToQuery(); | NodeId secondNodeid = getSuitableNodeToQuery(); | ||||
| runEventLoop(); | runEventLoop(); | ||||
| BOOST_CHECK(firstNodeid != secondNodeid); | BOOST_CHECK(firstNodeid != secondNodeid); | ||||
| ▲ Show 20 Lines • Show All 517 Lines • ▼ Show 20 Lines | BOOST_AUTO_TEST_CASE(proof_record) { | ||||
| }); | }); | ||||
| m_processor->addProofToReconcile(proofB); | m_processor->addProofToReconcile(proofB); | ||||
| BOOST_CHECK(!m_processor->isAccepted(proofA)); | BOOST_CHECK(!m_processor->isAccepted(proofA)); | ||||
| BOOST_CHECK(m_processor->isAccepted(proofB)); | BOOST_CHECK(m_processor->isAccepted(proofB)); | ||||
| BOOST_CHECK_EQUAL(m_processor->getConfidence(proofA), 0); | BOOST_CHECK_EQUAL(m_processor->getConfidence(proofA), 0); | ||||
| BOOST_CHECK_EQUAL(m_processor->getConfidence(proofB), 0); | BOOST_CHECK_EQUAL(m_processor->getConfidence(proofB), 0); | ||||
| } | } | ||||
| BOOST_AUTO_TEST_CASE(quorum_detection) { | |||||
| // Set min quorum parameters on a new node that hasn't latched an | |||||
| // established quorum yet | |||||
| int minStake = 2000000; | |||||
| const auto currency = Currency::get(); | |||||
| uint32_t minScore = Proof::amountToScore(minStake * currency.baseunit); | |||||
| gArgs.ForceSetArg("-avaminquorumstake", std::to_string(minStake)); | |||||
Lint: Locale dependent function Unnecessary locale dependence can cause bugs and should be avoided. Lint: Locale dependent function: Unnecessary locale dependence can cause bugs and should be avoided.
Otherwise an exception can… | |||||
FabienUnsubmitted Not Done Inline ActionsYou can use ToString() from util/string.h instead to fix the linter Fabien: You can use `ToString()` from util/string.h instead to fix the linter | |||||
| gArgs.ForceSetArg("-avaminquorumconnectedstakeratio", "0.5"); | |||||
| bilingual_str error; | |||||
| std::unique_ptr<Processor> p = Processor::MakeProcessor( | |||||
| *m_node.args, *m_node.chain, m_node.connman.get(), error); | |||||
| BOOST_CHECK(!p->isQuorumEstablished()); | |||||
| BOOST_CHECK_EQUAL(AvalancheTest::getMinQuorumScore(*p), minScore); | |||||
| BOOST_CHECK_EQUAL(AvalancheTest::getMinQuorumConnectedScoreRatio(*p), 0.5); | |||||
| // Add part of the required stake and make sure we still report no quorum | |||||
| PeerId peerid1; | |||||
| auto proof1 = buildRandomProof(minScore / 2); | |||||
| p->withPeerManager([&](avalanche::PeerManager &pm) { | |||||
| peerid1 = TestPeerManager::registerAndGetPeerId(pm, proof1); | |||||
| BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), minScore / 2); | |||||
| BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), 0); | |||||
| }); | |||||
| BOOST_CHECK(!p->isQuorumEstablished()); | |||||
| // Add the rest of the stake, but we still have no connected stake | |||||
| PeerId peerid2; | |||||
| auto proof2 = buildRandomProof(minScore / 2); | |||||
| p->withPeerManager([&](avalanche::PeerManager &pm) { | |||||
| peerid2 = TestPeerManager::registerAndGetPeerId(pm, proof2); | |||||
| BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), minScore); | |||||
| BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), 0); | |||||
| }); | |||||
| BOOST_CHECK(!p->isQuorumEstablished()); | |||||
| // Adding a node should cause the quorum to be detected and locked-in | |||||
| p->withPeerManager([&](avalanche::PeerManager &pm) { | |||||
| pm.addNode(0, proof1->getId()); | |||||
| BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), minScore); | |||||
| BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), minScore / 2); | |||||
| }); | |||||
| BOOST_CHECK(p->isQuorumEstablished()); | |||||
| // Go back to not having enough connected nodes, but we've already latched | |||||
| // the quorum as established | |||||
| p->withPeerManager([&](avalanche::PeerManager &pm) { | |||||
| pm.removeNode(0); | |||||
| BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), minScore); | |||||
| BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), 0); | |||||
| }); | |||||
| BOOST_CHECK(p->isQuorumEstablished()); | |||||
| // Remove peers one at a time and ensure the quorum stays established | |||||
| p->withPeerManager([&](avalanche::PeerManager &pm) { | |||||
| pm.removePeer(peerid2); | |||||
| BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), minScore / 2); | |||||
| BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), 0); | |||||
| }); | |||||
| BOOST_CHECK(p->isQuorumEstablished()); | |||||
| p->withPeerManager([&](avalanche::PeerManager &pm) { | |||||
| pm.removePeer(peerid1); | |||||
| BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), 0); | |||||
| BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), 0); | |||||
| }); | |||||
| BOOST_CHECK(p->isQuorumEstablished()); | |||||
| gArgs.ClearForcedArg("-avaminquorumstake"); | |||||
| gArgs.ClearForcedArg("-avaminquorumconnectedstakeratio"); | |||||
| } | |||||
| BOOST_AUTO_TEST_SUITE_END() | BOOST_AUTO_TEST_SUITE_END() | ||||
@Fabien I need to be able to get the peerid in tests for isQuorumEstablished, and am not sure how to do that without copying this logic to be in scope. How would you solve this?
The best idea I have so far is to put the logic in the PeerManager, and re-implement the latching/check in the Processor and have it delegate to PeerManager for the core logic.