Changeset View
Changeset View
Standalone View
Standalone View
src/avalanche/test/processor_tests.cpp
Show First 20 Lines • Show All 635 Lines • ▼ Show 20 Lines | BOOST_AUTO_TEST_CASE_TEMPLATE(multi_item_register, P, VoteItemProviders) { | ||||
BOOST_CHECK_EQUAL(invs.size(), 0); | BOOST_CHECK_EQUAL(invs.size(), 0); | ||||
} | } | ||||
BOOST_AUTO_TEST_CASE_TEMPLATE(poll_and_response, P, VoteItemProviders) { | BOOST_AUTO_TEST_CASE_TEMPLATE(poll_and_response, P, VoteItemProviders) { | ||||
P provider(this); | P provider(this); | ||||
auto &updates = provider.updates; | auto &updates = provider.updates; | ||||
const uint32_t invType = provider.invType; | const uint32_t invType = provider.invType; | ||||
const auto item = provider.buildVoteItem(); | auto item = provider.buildVoteItem(); | ||||
const auto itemid = provider.getVoteItemId(item); | auto itemid = provider.getVoteItemId(item); | ||||
// There is no node to query. | // There is no node to query. | ||||
BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), NO_NODE); | BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), NO_NODE); | ||||
// Create a node that supports avalanche and one that doesn't. | // Add enough nodes to have a valid quorum, and the same amount with no | ||||
// avalanche support | |||||
std::set<NodeId> avanodeIds; | |||||
auto avanodes = ConnectNodes(); | |||||
for (auto avanode : avanodes) { | |||||
ConnectNode(NODE_NONE); | ConnectNode(NODE_NONE); | ||||
auto avanode = ConnectNode(NODE_AVALANCHE); | avanodeIds.insert(avanode->GetId()); | ||||
NodeId avanodeid = avanode->GetId(); | } | ||||
BOOST_CHECK(addNode(avanodeid)); | |||||
// It returns the avalanche peer. | auto getSelectedAvanodeId = [&]() { | ||||
BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), avanodeid); | NodeId avanodeid = getSuitableNodeToQuery(); | ||||
BOOST_CHECK(avanodeIds.find(avanodeid) != avanodeIds.end()); | |||||
return avanodeid; | |||||
}; | |||||
// It returns one of the avalanche peer. | |||||
NodeId avanodeid = getSelectedAvanodeId(); | |||||
// Register an item and check it is added to the list of elements to poll. | // Register an item and check it is added to the list of elements to poll. | ||||
BOOST_CHECK(provider.addToReconcile(item)); | BOOST_CHECK(provider.addToReconcile(item)); | ||||
auto invs = getInvsForNextPoll(); | auto invs = getInvsForNextPoll(); | ||||
BOOST_CHECK_EQUAL(invs.size(), 1); | BOOST_CHECK_EQUAL(invs.size(), 1); | ||||
BOOST_CHECK_EQUAL(invs[0].type, invType); | BOOST_CHECK_EQUAL(invs[0].type, invType); | ||||
BOOST_CHECK(invs[0].hash == itemid); | BOOST_CHECK(invs[0].hash == itemid); | ||||
// Trigger a poll on avanode. | std::set<NodeId> unselectedNodeids = avanodeIds; | ||||
unselectedNodeids.erase(avanodeid); | |||||
const size_t remainingNodeIds = unselectedNodeids.size(); | |||||
uint64_t round = getRound(); | uint64_t round = getRound(); | ||||
for (size_t i = 0; i < remainingNodeIds; i++) { | |||||
// Trigger a poll on avanode. | |||||
runEventLoop(); | runEventLoop(); | ||||
// Another node is selected | |||||
NodeId nodeid = getSuitableNodeToQuery(); | |||||
BOOST_CHECK(unselectedNodeids.find(nodeid) != avanodeIds.end()); | |||||
unselectedNodeids.erase(nodeid); | |||||
} | |||||
// There is no more suitable peer available, so return nothing. | // There is no more suitable peer available, so return nothing. | ||||
BOOST_CHECK(unselectedNodeids.empty()); | |||||
runEventLoop(); | |||||
BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), NO_NODE); | BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), NO_NODE); | ||||
// Respond to the request. | // Respond to the request. | ||||
Response resp = {round, 0, {Vote(0, itemid)}}; | Response resp = {round, 0, {Vote(0, itemid)}}; | ||||
BOOST_CHECK(provider.registerVotes(avanodeid, resp)); | BOOST_CHECK(provider.registerVotes(avanodeid, resp)); | ||||
BOOST_CHECK_EQUAL(updates.size(), 0); | BOOST_CHECK_EQUAL(updates.size(), 0); | ||||
// Now that avanode fullfilled his request, it is added back to the list of | // Now that avanode fullfilled his request, it is added back to the list of | ||||
Show All 31 Lines | BOOST_AUTO_TEST_CASE_TEMPLATE(poll_and_response, P, VoteItemProviders) { | ||||
BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), avanodeid); | BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), avanodeid); | ||||
// 3. Do not match the poll. | // 3. Do not match the poll. | ||||
resp = {getRound(), 0, {Vote()}}; | resp = {getRound(), 0, {Vote()}}; | ||||
runEventLoop(); | runEventLoop(); | ||||
checkRegisterVotesError(avanodeid, resp, "invalid-ava-response-content"); | checkRegisterVotesError(avanodeid, resp, "invalid-ava-response-content"); | ||||
BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), avanodeid); | BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), avanodeid); | ||||
// At this stage we have reached the max inflight requests for our inv, so | |||||
// it won't be requested anymore until the requests are fullfilled. Let's | |||||
// update our item so the remaining tests makes sense. | |||||
invs = getInvsForNextPoll(); | |||||
BOOST_CHECK(invs.empty()); | |||||
item = provider.buildVoteItem(); | |||||
itemid = provider.getVoteItemId(item); | |||||
BOOST_CHECK(provider.addToReconcile(item)); | |||||
invs = getInvsForNextPoll(); | |||||
BOOST_CHECK_EQUAL(invs.size(), 1); | |||||
// 4. Invalid round count. Request is not discarded. | // 4. Invalid round count. Request is not discarded. | ||||
uint64_t queryRound = getRound(); | uint64_t queryRound = getRound(); | ||||
runEventLoop(); | runEventLoop(); | ||||
resp = {queryRound + 1, 0, {Vote()}}; | resp = {queryRound + 1, 0, {Vote()}}; | ||||
checkRegisterVotesError(avanodeid, resp, "unexpected-ava-response"); | checkRegisterVotesError(avanodeid, resp, "unexpected-ava-response"); | ||||
resp = {queryRound - 1, 0, {Vote()}}; | resp = {queryRound - 1, 0, {Vote()}}; | ||||
▲ Show 20 Lines • Show All 73 Lines • ▼ Show 20 Lines | BOOST_AUTO_TEST_CASE_TEMPLATE(poll_inflight_timeout, P, VoteItemProviders) { | ||||
P provider(this); | P provider(this); | ||||
const auto item = provider.buildVoteItem(); | const auto item = provider.buildVoteItem(); | ||||
const auto itemid = provider.getVoteItemId(item); | const auto itemid = provider.getVoteItemId(item); | ||||
// Add the item | // Add the item | ||||
BOOST_CHECK(provider.addToReconcile(item)); | BOOST_CHECK(provider.addToReconcile(item)); | ||||
// Create a node that supports avalanche. | // Create a quorum of nodes that support avalanche. | ||||
auto avanode = ConnectNode(NODE_AVALANCHE); | ConnectNodes(); | ||||
NodeId avanodeid = avanode->GetId(); | NodeId avanodeid = NO_NODE; | ||||
BOOST_CHECK(addNode(avanodeid)); | |||||
// Expire requests after some time. | // Expire requests after some time. | ||||
auto queryTimeDuration = std::chrono::milliseconds(10); | auto queryTimeDuration = std::chrono::milliseconds(10); | ||||
m_processor->setQueryTimeoutDuration(queryTimeDuration); | m_processor->setQueryTimeoutDuration(queryTimeDuration); | ||||
for (int i = 0; i < 10; i++) { | for (int i = 0; i < 10; i++) { | ||||
Response resp = {getRound(), 0, {Vote(0, itemid)}}; | Response resp = {getRound(), 0, {Vote(0, itemid)}}; | ||||
avanodeid = getSuitableNodeToQuery(); | |||||
auto start = std::chrono::steady_clock::now(); | auto start = std::chrono::steady_clock::now(); | ||||
runEventLoop(); | runEventLoop(); | ||||
// We cannot guarantee that we'll wait for just 1ms, so we have to bail | // We cannot guarantee that we'll wait for just 1ms, so we have to bail | ||||
// if we aren't within the proper time range. | // if we aren't within the proper time range. | ||||
std::this_thread::sleep_for(std::chrono::milliseconds(1)); | std::this_thread::sleep_for(std::chrono::milliseconds(1)); | ||||
runEventLoop(); | runEventLoop(); | ||||
bool ret = provider.registerVotes(avanodeid, next(resp)); | bool ret = provider.registerVotes(avanodeid, next(resp)); | ||||
if (std::chrono::steady_clock::now() > start + queryTimeDuration) { | if (std::chrono::steady_clock::now() > start + queryTimeDuration) { | ||||
// We waited for too long, bail. Because we can't know for sure when | // We waited for too long, bail. Because we can't know for sure when | ||||
// previous steps ran, ret is not deterministic and we do not check | // previous steps ran, ret is not deterministic and we do not check | ||||
// it. | // it. | ||||
i--; | i--; | ||||
continue; | continue; | ||||
} | } | ||||
// We are within time bounds, so the vote should have worked. | // We are within time bounds, so the vote should have worked. | ||||
BOOST_CHECK(ret); | BOOST_CHECK(ret); | ||||
avanodeid = getSuitableNodeToQuery(); | |||||
// Now try again but wait for expiration. | // Now try again but wait for expiration. | ||||
runEventLoop(); | runEventLoop(); | ||||
std::this_thread::sleep_for(queryTimeDuration); | std::this_thread::sleep_for(queryTimeDuration); | ||||
runEventLoop(); | runEventLoop(); | ||||
BOOST_CHECK(!provider.registerVotes(avanodeid, next(resp))); | BOOST_CHECK(!provider.registerVotes(avanodeid, next(resp))); | ||||
} | } | ||||
} | } | ||||
▲ Show 20 Lines • Show All 134 Lines • ▼ Show 20 Lines | BOOST_AUTO_TEST_CASE(event_loop) { | ||||
BOOST_CHECK_EQUAL(s.getQueueInfo(start, stop), 1); | BOOST_CHECK_EQUAL(s.getQueueInfo(start, stop), 1); | ||||
// Starting twice doesn't start it twice. | // Starting twice doesn't start it twice. | ||||
BOOST_CHECK(!m_processor->startEventLoop(s)); | BOOST_CHECK(!m_processor->startEventLoop(s)); | ||||
// Start the scheduler thread. | // Start the scheduler thread. | ||||
std::thread schedulerThread(std::bind(&CScheduler::serviceQueue, &s)); | std::thread schedulerThread(std::bind(&CScheduler::serviceQueue, &s)); | ||||
// Create a node that supports avalanche. | // Create a quorum of nodes that support avalanche. | ||||
auto avanode = ConnectNode(NODE_AVALANCHE); | auto avanodes = ConnectNodes(); | ||||
NodeId nodeid = avanode->GetId(); | |||||
BOOST_CHECK(addNode(nodeid)); | |||||
// There is no query in flight at the moment. | // There is no query in flight at the moment. | ||||
BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), nodeid); | NodeId nodeid = getSuitableNodeToQuery(); | ||||
BOOST_CHECK_NE(nodeid, NO_NODE); | |||||
// Add a new block. Check it is added to the polls. | // Add a new block. Check it is added to the polls. | ||||
uint64_t queryRound = getRound(); | uint64_t queryRound = getRound(); | ||||
BOOST_CHECK(m_processor->addBlockToReconcile(pindex)); | BOOST_CHECK(m_processor->addBlockToReconcile(pindex)); | ||||
// Wait until all nodes got a poll | |||||
for (int i = 0; i < 60 * 1000; i++) { | for (int i = 0; i < 60 * 1000; i++) { | ||||
// Technically, this is a race condition, but this should do just fine | // Technically, this is a race condition, but this should do just fine | ||||
// as we wait up to 1 minute for an event that should take 10ms. | // as we wait up to 1 minute for an event that should take 80ms. | ||||
UninterruptibleSleep(std::chrono::milliseconds(1)); | UninterruptibleSleep(std::chrono::milliseconds(1)); | ||||
if (getRound() != queryRound) { | if (getRound() == queryRound + avanodes.size()) { | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
// Check that we effectively got a request and not timed out. | // Check that we effectively got a request and not timed out. | ||||
BOOST_CHECK(getRound() > queryRound); | BOOST_CHECK(getRound() > queryRound); | ||||
// Respond and check the cooldown time is respected. | // Respond and check the cooldown time is respected. | ||||
uint64_t responseRound = getRound(); | uint64_t responseRound = getRound(); | ||||
auto queryTime = | auto queryTime = | ||||
std::chrono::steady_clock::now() + std::chrono::milliseconds(100); | std::chrono::steady_clock::now() + std::chrono::milliseconds(100); | ||||
std::vector<BlockUpdate> updates; | std::vector<BlockUpdate> updates; | ||||
// Only the first node answers, so it's the only one that gets polled again | |||||
registerVotes(nodeid, {queryRound, 100, {Vote(0, blockHash)}}, updates); | registerVotes(nodeid, {queryRound, 100, {Vote(0, blockHash)}}, updates); | ||||
for (int i = 0; i < 10000; i++) { | for (int i = 0; i < 10000; i++) { | ||||
// We make sure that we do not get a request before queryTime. | // We make sure that we do not get a request before queryTime. | ||||
UninterruptibleSleep(std::chrono::milliseconds(1)); | UninterruptibleSleep(std::chrono::milliseconds(1)); | ||||
if (getRound() != responseRound) { | if (getRound() != responseRound) { | ||||
BOOST_CHECK(std::chrono::steady_clock::now() > queryTime); | BOOST_CHECK(std::chrono::steady_clock::now() > queryTime); | ||||
break; | break; | ||||
} | } | ||||
} | } | ||||
▲ Show 20 Lines • Show All 244 Lines • ▼ Show 20 Lines | BOOST_AUTO_TEST_CASE(quorum_detection) { | ||||
processor->withPeerManager([&](avalanche::PeerManager &pm) { | processor->withPeerManager([&](avalanche::PeerManager &pm) { | ||||
BOOST_CHECK(pm.registerProof(processor->getLocalProof())); | BOOST_CHECK(pm.registerProof(processor->getLocalProof())); | ||||
BOOST_CHECK(pm.isBoundToPeer(processor->getLocalProof()->getId())); | BOOST_CHECK(pm.isBoundToPeer(processor->getLocalProof()->getId())); | ||||
BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), minScore / 4); | BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), minScore / 4); | ||||
BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), 0); | BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), 0); | ||||
}); | }); | ||||
BOOST_CHECK(!processor->isQuorumEstablished()); | BOOST_CHECK(!processor->isQuorumEstablished()); | ||||
// Add enough nodes to get a conclusive vote | |||||
for (NodeId id = 0; id < 8; id++) { | |||||
processor->withPeerManager([&](avalanche::PeerManager &pm) { | |||||
pm.addNode(id, processor->getLocalProof()->getId()); | |||||
BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), minScore / 4); | |||||
BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), minScore / 4); | |||||
}); | |||||
} | |||||
// 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(active_chainstate, minScore / 2); | auto proof1 = buildRandomProof(active_chainstate, 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(), 3 * minScore / 4); | BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), 3 * minScore / 4); | ||||
BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), 0); | BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), minScore / 4); | ||||
}); | }); | ||||
BOOST_CHECK(!processor->isQuorumEstablished()); | BOOST_CHECK(!processor->isQuorumEstablished()); | ||||
// Add the rest of the stake, but we are still lacking connected stake | // Add the rest of the stake, but we are still lacking connected stake | ||||
auto proof2 = buildRandomProof(active_chainstate, minScore / 4); | auto proof2 = buildRandomProof(active_chainstate, 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(), minScore / 4); | ||||
}); | }); | ||||
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, proof2->getId()); | pm.addNode(8, proof2->getId()); | ||||
BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), minScore); | BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), minScore); | ||||
// The peer manager knows that proof2 has a node attached ... | // The peer manager knows that proof2 has a node attached ... | ||||
BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), minScore / 4); | BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), minScore / 2); | ||||
}); | }); | ||||
// ... but the processor also account for the local proof, so we reached 50% | // ... 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 score, but we've already latched | ||||
// the quorum as established | // the quorum as established | ||||
sdulfari: No more latching | |||||
FabienAuthorUnsubmitted Done Inline ActionsThis parameter is still latched, if you were connected to 80% of the stakes and get a new proof that you don't know the node yet you don't want to have a bad quorum and get flip-floping Fabien: This parameter is still latched, if you were connected to 80% of the stakes and get a new proof… | |||||
processor->withPeerManager([&](avalanche::PeerManager &pm) { | processor->withPeerManager([&](avalanche::PeerManager &pm) { | ||||
pm.removeNode(0); | pm.removeNode(8); | ||||
BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), minScore); | BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), minScore); | ||||
BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), 0); | BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), minScore / 4); | ||||
}); | |||||
BOOST_CHECK(processor->isQuorumEstablished()); | |||||
// Removing one more node drops our count below the minimum and the quorum | |||||
// is no longer ready | |||||
processor->withPeerManager( | |||||
[&](avalanche::PeerManager &pm) { pm.removeNode(7); }); | |||||
BOOST_CHECK(!processor->isQuorumEstablished()); | |||||
// It resumes when we have enough nodes again | |||||
processor->withPeerManager([&](avalanche::PeerManager &pm) { | |||||
pm.addNode(7, processor->getLocalProof()->getId()); | |||||
}); | }); | ||||
BOOST_CHECK(processor->isQuorumEstablished()); | BOOST_CHECK(processor->isQuorumEstablished()); | ||||
// Remove peers one at a time and ensure the quorum stays established | // Remove peers one at a time and ensure the quorum stays established | ||||
sdulfariUnsubmitted Not Done Inline ActionsThis isn't true anymore, right? sdulfari: This isn't true anymore, right? | |||||
auto spendProofUtxo = [&processor, &chainman](ProofRef proof) { | auto spendProofUtxo = [&processor, &chainman](ProofRef proof) { | ||||
{ | { | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
CCoinsViewCache &coins = chainman.ActiveChainstate().CoinsTip(); | CCoinsViewCache &coins = chainman.ActiveChainstate().CoinsTip(); | ||||
coins.SpendCoin(proof->getStakes()[0].getStake().getUTXO()); | coins.SpendCoin(proof->getStakes()[0].getStake().getUTXO()); | ||||
} | } | ||||
processor->withPeerManager([&proof](avalanche::PeerManager &pm) { | processor->withPeerManager([&proof](avalanche::PeerManager &pm) { | ||||
pm.updatedBlockTip(); | pm.updatedBlockTip(); | ||||
BOOST_CHECK(!pm.isBoundToPeer(proof->getId())); | BOOST_CHECK(!pm.isBoundToPeer(proof->getId())); | ||||
}); | }); | ||||
}; | }; | ||||
spendProofUtxo(proof2); | spendProofUtxo(proof2); | ||||
processor->withPeerManager([&](avalanche::PeerManager &pm) { | processor->withPeerManager([&](avalanche::PeerManager &pm) { | ||||
BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), 3 * minScore / 4); | BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), 3 * minScore / 4); | ||||
BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), 0); | BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), minScore / 4); | ||||
}); | }); | ||||
BOOST_CHECK(processor->isQuorumEstablished()); | BOOST_CHECK(processor->isQuorumEstablished()); | ||||
spendProofUtxo(proof1); | spendProofUtxo(proof1); | ||||
processor->withPeerManager([&](avalanche::PeerManager &pm) { | processor->withPeerManager([&](avalanche::PeerManager &pm) { | ||||
BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), minScore / 4); | BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), minScore / 4); | ||||
BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), 0); | BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), minScore / 4); | ||||
}); | }); | ||||
BOOST_CHECK(processor->isQuorumEstablished()); | BOOST_CHECK(processor->isQuorumEstablished()); | ||||
spendProofUtxo(processor->getLocalProof()); | spendProofUtxo(processor->getLocalProof()); | ||||
processor->withPeerManager([&](avalanche::PeerManager &pm) { | 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()); | // There is no node left | ||||
BOOST_CHECK(!processor->isQuorumEstablished()); | |||||
gArgs.ClearForcedArg("-avamasterkey"); | gArgs.ClearForcedArg("-avamasterkey"); | ||||
gArgs.ClearForcedArg("-avaproof"); | 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) { | ||||
▲ Show 20 Lines • Show All 68 Lines • ▼ Show 20 Lines | auto checkMinAvaproofsMessages = [&](int64_t minAvaproofsMessages) { | ||||
argsman.ForceSetArg("-avaminavaproofsnodecount", | argsman.ForceSetArg("-avaminavaproofsnodecount", | ||||
ToString(minAvaproofsMessages)); | ToString(minAvaproofsMessages)); | ||||
bilingual_str error; | bilingual_str error; | ||||
auto processor = Processor::MakeProcessor( | auto processor = Processor::MakeProcessor( | ||||
argsman, *m_node.chain, m_node.connman.get(), chainman, | argsman, *m_node.chain, m_node.connman.get(), chainman, | ||||
*m_node.scheduler, error); | *m_node.scheduler, error); | ||||
BOOST_CHECK_EQUAL(processor->isQuorumEstablished(), | |||||
minAvaproofsMessages <= 0); | |||||
auto addNode = [&](NodeId nodeid) { | auto addNode = [&](NodeId nodeid) { | ||||
auto proof = buildRandomProof(chainman.ActiveChainstate(), | auto proof = buildRandomProof(chainman.ActiveChainstate(), | ||||
MIN_VALID_PROOF_SCORE); | MIN_VALID_PROOF_SCORE); | ||||
processor->withPeerManager([&](avalanche::PeerManager &pm) { | processor->withPeerManager([&](avalanche::PeerManager &pm) { | ||||
BOOST_CHECK(pm.registerProof(proof)); | BOOST_CHECK(pm.registerProof(proof)); | ||||
BOOST_CHECK(pm.addNode(nodeid, proof->getId())); | BOOST_CHECK(pm.addNode(nodeid, proof->getId())); | ||||
}); | }); | ||||
}; | }; | ||||
// Add enough node to have a conclusive vote, but don't account any | |||||
// avaproofs. | |||||
// NOTE: we can't use the test facilites like ConnectNodes() because we | |||||
// are not testing on m_processor. | |||||
for (NodeId id = 100; id < 108; id++) { | |||||
addNode(id); | |||||
} | |||||
BOOST_CHECK_EQUAL(processor->isQuorumEstablished(), | |||||
minAvaproofsMessages <= 0); | |||||
for (int64_t i = 0; i < minAvaproofsMessages - 1; i++) { | for (int64_t i = 0; i < minAvaproofsMessages - 1; i++) { | ||||
addNode(i); | addNode(i); | ||||
processor->avaproofsSent(i); | processor->avaproofsSent(i); | ||||
BOOST_CHECK_EQUAL(processor->getAvaproofsNodeCounter(), i + 1); | BOOST_CHECK_EQUAL(processor->getAvaproofsNodeCounter(), i + 1); | ||||
// Receiving again on the same node does not increase the counter | // Receiving again on the same node does not increase the counter | ||||
processor->avaproofsSent(i); | processor->avaproofsSent(i); | ||||
▲ Show 20 Lines • Show All 108 Lines • Show Last 20 Lines |
No more latching