Changeset View
Changeset View
Standalone View
Standalone View
src/avalanche/test/processor_tests.cpp
// Copyright (c) 2018-2020 The Bitcoin developers | // Copyright (c) 2018-2020 The Bitcoin developers | ||||
// Distributed under the MIT software license, see the accompanying | // Distributed under the MIT software license, see the accompanying | ||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php. | // file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
#include <avalanche/processor.h> | #include <avalanche/processor.h> | ||||
#include <avalanche/delegationbuilder.h> | #include <avalanche/delegationbuilder.h> | ||||
#include <avalanche/peermanager.h> | #include <avalanche/peermanager.h> | ||||
#include <avalanche/proofbuilder.h> | #include <avalanche/proofbuilder.h> | ||||
#include <avalanche/voterecord.h> | #include <avalanche/voterecord.h> | ||||
#include <chain.h> | #include <chain.h> | ||||
#include <config.h> | #include <config.h> | ||||
#include <key_io.h> | |||||
#include <net_processing.h> // For ::PeerManager | #include <net_processing.h> // For ::PeerManager | ||||
#include <reverse_iterator.h> | #include <reverse_iterator.h> | ||||
#include <scheduler.h> | #include <scheduler.h> | ||||
#include <util/time.h> | #include <util/time.h> | ||||
#include <util/translation.h> // For bilingual_str | #include <util/translation.h> // For bilingual_str | ||||
// D6970 moved LookupBlockIndex from chain.h to validation.h TODO: remove this | // D6970 moved LookupBlockIndex from chain.h to validation.h TODO: remove this | ||||
// when LookupBlockIndex is refactored out of validation | // when LookupBlockIndex is refactored out of validation | ||||
#include <validation.h> | #include <validation.h> | ||||
▲ Show 20 Lines • Show All 1,178 Lines • ▼ Show 20 Lines | BOOST_AUTO_TEST_CASE(proof_record) { | ||||
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) { | BOOST_AUTO_TEST_CASE(quorum_detection) { | ||||
// Set min quorum parameters for our test | // Set min quorum parameters for our test | ||||
int minStake = 2000000; | int minStake = 4'000'000; | ||||
gArgs.ForceSetArg("-avaminquorumstake", ToString(minStake)); | gArgs.ForceSetArg("-avaminquorumstake", ToString(minStake)); | ||||
gArgs.ForceSetArg("-avaminquorumconnectedstakeratio", "0.5"); | gArgs.ForceSetArg("-avaminquorumconnectedstakeratio", "0.5"); | ||||
// Create a new processor with our given quorum parameters | // Create a new processor with our given quorum parameters | ||||
const auto currency = Currency::get(); | const auto currency = Currency::get(); | ||||
uint32_t minScore = Proof::amountToScore(minStake * currency.baseunit); | uint32_t minScore = Proof::amountToScore(minStake * currency.baseunit); | ||||
const CKey key = CKey::MakeCompressedKey(); | |||||
auto localProof = buildRandomProof(minScore / 4, key); | |||||
gArgs.ForceSetArg("-avamasterkey", EncodeSecret(key)); | |||||
gArgs.ForceSetArg("-avaproof", localProof->ToHex()); | |||||
bilingual_str error; | bilingual_str error; | ||||
std::unique_ptr<Processor> processor = Processor::MakeProcessor( | std::unique_ptr<Processor> processor = Processor::MakeProcessor( | ||||
*m_node.args, *m_node.chain, m_node.connman.get(), error); | *m_node.args, *m_node.chain, m_node.connman.get(), error); | ||||
BOOST_CHECK(processor != nullptr); | BOOST_CHECK(processor != nullptr); | ||||
BOOST_CHECK(!processor->isQuorumEstablished()); | BOOST_CHECK(processor->getLocalProof() != nullptr); | ||||
BOOST_CHECK_EQUAL(processor->getLocalProof()->getId(), localProof->getId()); | |||||
BOOST_CHECK_EQUAL(AvalancheTest::getMinQuorumScore(*processor), minScore); | BOOST_CHECK_EQUAL(AvalancheTest::getMinQuorumScore(*processor), minScore); | ||||
BOOST_CHECK_EQUAL( | BOOST_CHECK_EQUAL( | ||||
AvalancheTest::getMinQuorumConnectedScoreRatio(*processor), 0.5); | AvalancheTest::getMinQuorumConnectedScoreRatio(*processor), 0.5); | ||||
// The local proof has not been validated yet | |||||
processor->withPeerManager([&](avalanche::PeerManager &pm) { | |||||
BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), 0); | |||||
BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), 0); | |||||
}); | |||||
BOOST_CHECK(!processor->isQuorumEstablished()); | |||||
// Register the local proof. This is normally done when the chain tip is | |||||
// updated. The local proof should be accounted for in the min quorum | |||||
// computation but the peer manager doesn't know about that. | |||||
processor->withPeerManager([&](avalanche::PeerManager &pm) { | |||||
BOOST_CHECK(pm.registerProof(processor->getLocalProof())); | |||||
BOOST_CHECK(pm.isBoundToPeer(processor->getLocalProof()->getId())); | |||||
BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), minScore / 4); | |||||
BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), 0); | |||||
}); | |||||
BOOST_CHECK(!processor->isQuorumEstablished()); | |||||
// Add part of the required stake and make sure we still report no quorum | // Add part of the required stake and make sure we still report no quorum | ||||
auto proof1 = buildRandomProof(minScore / 2); | auto proof1 = buildRandomProof(minScore / 2); | ||||
processor->withPeerManager([&](avalanche::PeerManager &pm) { | processor->withPeerManager([&](avalanche::PeerManager &pm) { | ||||
BOOST_CHECK(pm.registerProof(proof1)); | BOOST_CHECK(pm.registerProof(proof1)); | ||||
BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), minScore / 2); | BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), 3 * minScore / 4); | ||||
BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), 0); | BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), 0); | ||||
}); | }); | ||||
BOOST_CHECK(!processor->isQuorumEstablished()); | BOOST_CHECK(!processor->isQuorumEstablished()); | ||||
// Add the rest of the stake, but we still have no connected stake | // Add the rest of the stake, but we are still lacking connected stake | ||||
auto proof2 = buildRandomProof(minScore / 2); | auto proof2 = buildRandomProof(minScore / 4); | ||||
processor->withPeerManager([&](avalanche::PeerManager &pm) { | processor->withPeerManager([&](avalanche::PeerManager &pm) { | ||||
BOOST_CHECK(pm.registerProof(proof2)); | BOOST_CHECK(pm.registerProof(proof2)); | ||||
BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), minScore); | BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), minScore); | ||||
BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), 0); | BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), 0); | ||||
}); | }); | ||||
BOOST_CHECK(!processor->isQuorumEstablished()); | BOOST_CHECK(!processor->isQuorumEstablished()); | ||||
// Adding a node should cause the quorum to be detected and locked-in | // Adding a node should cause the quorum to be detected and locked-in | ||||
processor->withPeerManager([&](avalanche::PeerManager &pm) { | processor->withPeerManager([&](avalanche::PeerManager &pm) { | ||||
pm.addNode(0, proof1->getId()); | pm.addNode(0, proof2->getId()); | ||||
BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), minScore); | BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), minScore); | ||||
BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), minScore / 2); | // The peer manager knows that proof2 has a node attached ... | ||||
BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), minScore / 4); | |||||
}); | }); | ||||
// ... but the processor also account for the local proof, so we reached 50% | |||||
BOOST_CHECK(processor->isQuorumEstablished()); | BOOST_CHECK(processor->isQuorumEstablished()); | ||||
// Go back to not having enough connected nodes, but we've already latched | // Go back to not having enough connected nodes, but we've already latched | ||||
// the quorum as established | // the quorum as established | ||||
processor->withPeerManager([&](avalanche::PeerManager &pm) { | processor->withPeerManager([&](avalanche::PeerManager &pm) { | ||||
pm.removeNode(0); | pm.removeNode(0); | ||||
BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), minScore); | BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), minScore); | ||||
BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), 0); | BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), 0); | ||||
Show All 12 Lines | auto orphanProof = [&processor](ProofRef proof) { | ||||
pm.updatedBlockTip(); | pm.updatedBlockTip(); | ||||
BOOST_CHECK(pm.isOrphan(proof->getId())); | BOOST_CHECK(pm.isOrphan(proof->getId())); | ||||
BOOST_CHECK(!pm.isBoundToPeer(proof->getId())); | BOOST_CHECK(!pm.isBoundToPeer(proof->getId())); | ||||
}); | }); | ||||
}; | }; | ||||
orphanProof(proof2); | orphanProof(proof2); | ||||
processor->withPeerManager([&](avalanche::PeerManager &pm) { | processor->withPeerManager([&](avalanche::PeerManager &pm) { | ||||
BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), minScore / 2); | BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), 3 * minScore / 4); | ||||
BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), 0); | BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), 0); | ||||
}); | }); | ||||
BOOST_CHECK(processor->isQuorumEstablished()); | BOOST_CHECK(processor->isQuorumEstablished()); | ||||
orphanProof(proof1); | orphanProof(proof1); | ||||
processor->withPeerManager([&](avalanche::PeerManager &pm) { | processor->withPeerManager([&](avalanche::PeerManager &pm) { | ||||
BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), minScore / 4); | |||||
BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), 0); | |||||
}); | |||||
BOOST_CHECK(processor->isQuorumEstablished()); | |||||
orphanProof(processor->getLocalProof()); | |||||
processor->withPeerManager([&](avalanche::PeerManager &pm) { | |||||
BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), 0); | BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), 0); | ||||
BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), 0); | BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), 0); | ||||
}); | }); | ||||
BOOST_CHECK(processor->isQuorumEstablished()); | BOOST_CHECK(processor->isQuorumEstablished()); | ||||
gArgs.ClearForcedArg("-avamasterkey"); | |||||
gArgs.ClearForcedArg("-avaproof"); | |||||
gArgs.ClearForcedArg("-avaminquorumstake"); | gArgs.ClearForcedArg("-avaminquorumstake"); | ||||
gArgs.ClearForcedArg("-avaminquorumconnectedstakeratio"); | gArgs.ClearForcedArg("-avaminquorumconnectedstakeratio"); | ||||
} | } | ||||
BOOST_AUTO_TEST_CASE(quorum_detection_parameter_validation) { | BOOST_AUTO_TEST_CASE(quorum_detection_parameter_validation) { | ||||
// Create vector of tuples of <min stake, min ratio, success bool> | // Create vector of tuples of <min stake, min ratio, success bool> | ||||
std::vector<std::tuple<std::string, std::string, bool>> tests = { | std::vector<std::tuple<std::string, std::string, bool>> tests = { | ||||
// Both parameters are invalid | // Both parameters are invalid | ||||
▲ Show 20 Lines • Show All 48 Lines • Show Last 20 Lines |