Changeset View
Changeset View
Standalone View
Standalone View
src/avalanche/test/processor_tests.cpp
Show First 20 Lines • Show All 176 Lines • ▼ Show 20 Lines | bool registerVotes(NodeId nodeid, const avalanche::Response &response, | ||||
int banscore; | int banscore; | ||||
std::string error; | std::string error; | ||||
std::vector<avalanche::ProofUpdate> proofUpdates; | std::vector<avalanche::ProofUpdate> proofUpdates; | ||||
return m_processor->registerVotes(nodeid, response, blockUpdates, | return m_processor->registerVotes(nodeid, response, blockUpdates, | ||||
proofUpdates, banscore, error); | proofUpdates, banscore, error); | ||||
} | } | ||||
}; | }; | ||||
struct BlockOnlyTestingContext { | struct BlockProvider { | ||||
AvalancheTestingSetup *fixture; | AvalancheTestingSetup *fixture; | ||||
std::vector<BlockUpdate> updates; | std::vector<BlockUpdate> updates; | ||||
uint32_t invType; | uint32_t invType; | ||||
BlockOnlyTestingContext(AvalancheTestingSetup *_fixture) | BlockProvider(AvalancheTestingSetup *_fixture) | ||||
: fixture(_fixture), invType(MSG_BLOCK) {} | : fixture(_fixture), invType(MSG_BLOCK) {} | ||||
CBlockIndex *buildVoteItem() const { | CBlockIndex *buildVoteItem() const { | ||||
CBlock block = fixture->CreateAndProcessBlock({}, CScript()); | CBlock block = fixture->CreateAndProcessBlock({}, CScript()); | ||||
const BlockHash blockHash = block.GetHash(); | const BlockHash blockHash = block.GetHash(); | ||||
LOCK(cs_main); | LOCK(cs_main); | ||||
return LookupBlockIndex(blockHash); | return LookupBlockIndex(blockHash); | ||||
Show All 31 Lines | std::vector<Vote> buildVotesForItems(uint32_t error, | ||||
for (auto &item : reverse_iterate(items)) { | for (auto &item : reverse_iterate(items)) { | ||||
votes.emplace_back(error, item->GetBlockHash()); | votes.emplace_back(error, item->GetBlockHash()); | ||||
} | } | ||||
return votes; | return votes; | ||||
} | } | ||||
}; | }; | ||||
struct ProofOnlyTestingContext { | struct ProofProvider { | ||||
AvalancheTestingSetup *fixture; | AvalancheTestingSetup *fixture; | ||||
std::vector<ProofUpdate> updates; | std::vector<ProofUpdate> updates; | ||||
uint32_t invType; | uint32_t invType; | ||||
ProofOnlyTestingContext(AvalancheTestingSetup *_fixture) | ProofProvider(AvalancheTestingSetup *_fixture) | ||||
: fixture(_fixture), invType(MSG_AVA_PROOF) {} | : fixture(_fixture), invType(MSG_AVA_PROOF) {} | ||||
std::shared_ptr<Proof> buildVoteItem() const { return fixture->GetProof(); } | std::shared_ptr<Proof> buildVoteItem() const { return fixture->GetProof(); } | ||||
uint256 getVoteItemId(const std::shared_ptr<Proof> &proof) const { | uint256 getVoteItemId(const std::shared_ptr<Proof> &proof) const { | ||||
return proof->getId(); | return proof->getId(); | ||||
} | } | ||||
Show All 32 Lines | struct ProofProvider { | ||||
} | } | ||||
}; | }; | ||||
} // namespace | } // namespace | ||||
BOOST_FIXTURE_TEST_SUITE(processor_tests, AvalancheTestingSetup) | BOOST_FIXTURE_TEST_SUITE(processor_tests, AvalancheTestingSetup) | ||||
// FIXME A std::tuple can be used instead of boost::mpl::list after boost 1.67 | // FIXME A std::tuple can be used instead of boost::mpl::list after boost 1.67 | ||||
typedef boost::mpl::list<BlockOnlyTestingContext, ProofOnlyTestingContext> | using VoteItemProviders = boost::mpl::list<BlockProvider, ProofProvider>; | ||||
voteItemTestingContexts; | |||||
#define REGISTER_VOTE_AND_CHECK(vr, vote, state, finalized, confidence) \ | #define REGISTER_VOTE_AND_CHECK(vr, vote, state, finalized, confidence) \ | ||||
vr.registerVote(NO_NODE, vote); \ | vr.registerVote(NO_NODE, vote); \ | ||||
BOOST_CHECK_EQUAL(vr.isAccepted(), state); \ | BOOST_CHECK_EQUAL(vr.isAccepted(), state); \ | ||||
BOOST_CHECK_EQUAL(vr.hasFinalized(), finalized); \ | BOOST_CHECK_EQUAL(vr.hasFinalized(), finalized); \ | ||||
BOOST_CHECK_EQUAL(vr.getConfidence(), confidence); | BOOST_CHECK_EQUAL(vr.getConfidence(), confidence); | ||||
BOOST_AUTO_TEST_CASE(vote_record) { | BOOST_AUTO_TEST_CASE(vote_record) { | ||||
▲ Show 20 Lines • Show All 134 Lines • ▼ Show 20 Lines | |||||
namespace { | namespace { | ||||
Response next(Response &r) { | Response next(Response &r) { | ||||
auto copy = r; | auto copy = r; | ||||
r = {r.getRound() + 1, r.getCooldown(), r.GetVotes()}; | r = {r.getRound() + 1, r.getCooldown(), r.GetVotes()}; | ||||
return copy; | return copy; | ||||
} | } | ||||
} // namespace | } // namespace | ||||
BOOST_AUTO_TEST_CASE_TEMPLATE(vote_item_register, T, voteItemTestingContexts) { | BOOST_AUTO_TEST_CASE_TEMPLATE(vote_item_register, P, VoteItemProviders) { | ||||
T context(this); | P provider(this); | ||||
auto &updates = context.updates; | auto &updates = provider.updates; | ||||
const uint32_t invType = context.invType; | const uint32_t invType = provider.invType; | ||||
const auto item = context.buildVoteItem(); | const auto item = provider.buildVoteItem(); | ||||
const auto itemid = context.getVoteItemId(item); | const auto itemid = provider.getVoteItemId(item); | ||||
// Create nodes that supports avalanche. | // Create nodes that supports avalanche. | ||||
auto avanodes = ConnectNodes(); | auto avanodes = ConnectNodes(); | ||||
// Querying for random item returns false. | // Querying for random item returns false. | ||||
BOOST_CHECK(!m_processor->isAccepted(item)); | BOOST_CHECK(!m_processor->isAccepted(item)); | ||||
// Add a new item. Check it is added to the polls. | // Add a new item. Check it is added to the polls. | ||||
BOOST_CHECK(context.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); | ||||
BOOST_CHECK(m_processor->isAccepted(item)); | BOOST_CHECK(m_processor->isAccepted(item)); | ||||
int nextNodeIndex = 0; | int nextNodeIndex = 0; | ||||
auto registerNewVote = [&](const Response &resp) { | auto registerNewVote = [&](const Response &resp) { | ||||
runEventLoop(); | runEventLoop(); | ||||
auto nodeid = avanodes[nextNodeIndex++ % avanodes.size()]->GetId(); | auto nodeid = avanodes[nextNodeIndex++ % avanodes.size()]->GetId(); | ||||
BOOST_CHECK(context.registerVotes(nodeid, resp)); | BOOST_CHECK(provider.registerVotes(nodeid, resp)); | ||||
}; | }; | ||||
// Let's vote for this item a few times. | // Let's vote for this item a few times. | ||||
Response resp{0, 0, {Vote(0, itemid)}}; | Response resp{0, 0, {Vote(0, itemid)}}; | ||||
for (int i = 0; i < 6; i++) { | for (int i = 0; i < 6; i++) { | ||||
registerNewVote(next(resp)); | registerNewVote(next(resp)); | ||||
BOOST_CHECK(m_processor->isAccepted(item)); | BOOST_CHECK(m_processor->isAccepted(item)); | ||||
BOOST_CHECK_EQUAL(m_processor->getConfidence(item), 0); | BOOST_CHECK_EQUAL(m_processor->getConfidence(item), 0); | ||||
▲ Show 20 Lines • Show All 55 Lines • ▼ Show 20 Lines | BOOST_AUTO_TEST_CASE_TEMPLATE(vote_item_register, P, VoteItemProviders) { | ||||
BOOST_CHECK(updates[0].getStatus() == VoteStatus::Finalized); | BOOST_CHECK(updates[0].getStatus() == VoteStatus::Finalized); | ||||
updates.clear(); | updates.clear(); | ||||
// Once the decision is finalized, there is no poll for it. | // Once the decision is finalized, there is no poll for it. | ||||
invs = getInvsForNextPoll(); | invs = getInvsForNextPoll(); | ||||
BOOST_CHECK_EQUAL(invs.size(), 0); | BOOST_CHECK_EQUAL(invs.size(), 0); | ||||
// Now let's undo this and finalize rejection. | // Now let's undo this and finalize rejection. | ||||
BOOST_CHECK(context.addToReconcile(item)); | BOOST_CHECK(provider.addToReconcile(item)); | ||||
invs = getInvsForNextPoll(); | 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); | ||||
resp = {getRound(), 0, {Vote(1, itemid)}}; | resp = {getRound(), 0, {Vote(1, itemid)}}; | ||||
for (int i = 0; i < 6; i++) { | for (int i = 0; i < 6; i++) { | ||||
registerNewVote(next(resp)); | registerNewVote(next(resp)); | ||||
Show All 30 Lines | BOOST_AUTO_TEST_CASE_TEMPLATE(vote_item_register, P, VoteItemProviders) { | ||||
BOOST_CHECK(updates[0].getStatus() == VoteStatus::Invalid); | BOOST_CHECK(updates[0].getStatus() == VoteStatus::Invalid); | ||||
updates.clear(); | updates.clear(); | ||||
// Once the decision is finalized, there is no poll for it. | // Once the decision is finalized, there is no poll for it. | ||||
invs = getInvsForNextPoll(); | invs = getInvsForNextPoll(); | ||||
BOOST_CHECK_EQUAL(invs.size(), 0); | BOOST_CHECK_EQUAL(invs.size(), 0); | ||||
} | } | ||||
BOOST_AUTO_TEST_CASE_TEMPLATE(multi_item_register, T, voteItemTestingContexts) { | BOOST_AUTO_TEST_CASE_TEMPLATE(multi_item_register, P, VoteItemProviders) { | ||||
T context(this); | P provider(this); | ||||
auto &updates = context.updates; | auto &updates = provider.updates; | ||||
const uint32_t invType = context.invType; | const uint32_t invType = provider.invType; | ||||
auto itemA = context.buildVoteItem(); | auto itemA = provider.buildVoteItem(); | ||||
auto itemidA = context.getVoteItemId(itemA); | auto itemidA = provider.getVoteItemId(itemA); | ||||
auto itemB = context.buildVoteItem(); | auto itemB = provider.buildVoteItem(); | ||||
auto itemidB = context.getVoteItemId(itemB); | auto itemidB = provider.getVoteItemId(itemB); | ||||
// Create several nodes that support avalanche. | // Create several nodes that support avalanche. | ||||
auto avanodes = ConnectNodes(); | auto avanodes = ConnectNodes(); | ||||
// Querying for random item returns false. | // Querying for random item returns false. | ||||
BOOST_CHECK(!m_processor->isAccepted(itemA)); | BOOST_CHECK(!m_processor->isAccepted(itemA)); | ||||
BOOST_CHECK(!m_processor->isAccepted(itemB)); | BOOST_CHECK(!m_processor->isAccepted(itemB)); | ||||
// Start voting on item A. | // Start voting on item A. | ||||
BOOST_CHECK(context.addToReconcile(itemA)); | BOOST_CHECK(provider.addToReconcile(itemA)); | ||||
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 == itemidA); | BOOST_CHECK(invs[0].hash == itemidA); | ||||
uint64_t round = getRound(); | uint64_t round = getRound(); | ||||
runEventLoop(); | runEventLoop(); | ||||
BOOST_CHECK(context.registerVotes(avanodes[0]->GetId(), | BOOST_CHECK(provider.registerVotes(avanodes[0]->GetId(), | ||||
{round, 0, {Vote(0, itemidA)}})); | {round, 0, {Vote(0, itemidA)}})); | ||||
BOOST_CHECK_EQUAL(updates.size(), 0); | BOOST_CHECK_EQUAL(updates.size(), 0); | ||||
// Start voting on item B after one vote. | // Start voting on item B after one vote. | ||||
std::vector<Vote> votes = context.buildVotesForItems(0, {itemA, itemB}); | std::vector<Vote> votes = provider.buildVotesForItems(0, {itemA, itemB}); | ||||
Response resp{round + 1, 0, votes}; | Response resp{round + 1, 0, votes}; | ||||
BOOST_CHECK(context.addToReconcile(itemB)); | BOOST_CHECK(provider.addToReconcile(itemB)); | ||||
invs = getInvsForNextPoll(); | invs = getInvsForNextPoll(); | ||||
BOOST_CHECK_EQUAL(invs.size(), 2); | BOOST_CHECK_EQUAL(invs.size(), 2); | ||||
// Ensure the inv ordering is as expected | // Ensure the inv ordering is as expected | ||||
for (size_t i = 0; i < invs.size(); i++) { | for (size_t i = 0; i < invs.size(); i++) { | ||||
BOOST_CHECK_EQUAL(invs[i].type, invType); | BOOST_CHECK_EQUAL(invs[i].type, invType); | ||||
BOOST_CHECK(invs[i].hash == votes[i].GetHash()); | BOOST_CHECK(invs[i].hash == votes[i].GetHash()); | ||||
} | } | ||||
// Let's vote for these items a few times. | // Let's vote for these items a few times. | ||||
for (int i = 0; i < 4; i++) { | for (int i = 0; i < 4; i++) { | ||||
NodeId nodeid = getSuitableNodeToQuery(); | NodeId nodeid = getSuitableNodeToQuery(); | ||||
runEventLoop(); | runEventLoop(); | ||||
BOOST_CHECK(context.registerVotes(nodeid, next(resp))); | BOOST_CHECK(provider.registerVotes(nodeid, next(resp))); | ||||
BOOST_CHECK_EQUAL(updates.size(), 0); | BOOST_CHECK_EQUAL(updates.size(), 0); | ||||
} | } | ||||
// 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(context.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 A | ||||
// 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); | ||||
// Next vote will finalize item A. | // Next vote will finalize item A. | ||||
BOOST_CHECK(context.registerVotes(firstNodeid, next(resp))); | BOOST_CHECK(provider.registerVotes(firstNodeid, next(resp))); | ||||
BOOST_CHECK_EQUAL(updates.size(), 1); | BOOST_CHECK_EQUAL(updates.size(), 1); | ||||
BOOST_CHECK(updates[0].getVoteItem() == itemA); | BOOST_CHECK(updates[0].getVoteItem() == itemA); | ||||
BOOST_CHECK(updates[0].getStatus() == VoteStatus::Finalized); | BOOST_CHECK(updates[0].getStatus() == VoteStatus::Finalized); | ||||
updates = {}; | updates = {}; | ||||
// We do not vote on A anymore. | // We do not vote on A anymore. | ||||
invs = getInvsForNextPoll(); | 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 == itemidB); | BOOST_CHECK(invs[0].hash == itemidB); | ||||
// Next vote will finalize item B. | // Next vote will finalize item B. | ||||
BOOST_CHECK(context.registerVotes(secondNodeid, resp)); | BOOST_CHECK(provider.registerVotes(secondNodeid, resp)); | ||||
BOOST_CHECK_EQUAL(updates.size(), 1); | BOOST_CHECK_EQUAL(updates.size(), 1); | ||||
BOOST_CHECK(updates[0].getVoteItem() == itemB); | BOOST_CHECK(updates[0].getVoteItem() == itemB); | ||||
BOOST_CHECK(updates[0].getStatus() == VoteStatus::Finalized); | BOOST_CHECK(updates[0].getStatus() == VoteStatus::Finalized); | ||||
updates = {}; | updates = {}; | ||||
// There is nothing left to vote on. | // There is nothing left to vote on. | ||||
invs = getInvsForNextPoll(); | invs = getInvsForNextPoll(); | ||||
BOOST_CHECK_EQUAL(invs.size(), 0); | BOOST_CHECK_EQUAL(invs.size(), 0); | ||||
} | } | ||||
BOOST_AUTO_TEST_CASE_TEMPLATE(poll_and_response, T, voteItemTestingContexts) { | BOOST_AUTO_TEST_CASE_TEMPLATE(poll_and_response, P, VoteItemProviders) { | ||||
T context(this); | P provider(this); | ||||
auto &updates = context.updates; | auto &updates = provider.updates; | ||||
const uint32_t invType = context.invType; | const uint32_t invType = provider.invType; | ||||
const auto item = context.buildVoteItem(); | const auto item = provider.buildVoteItem(); | ||||
const auto itemid = context.getVoteItemId(item); | const 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. | // Create a node that supports avalanche and one that doesn't. | ||||
ConnectNode(NODE_NONE); | ConnectNode(NODE_NONE); | ||||
auto avanode = ConnectNode(NODE_AVALANCHE); | auto avanode = ConnectNode(NODE_AVALANCHE); | ||||
NodeId avanodeid = avanode->GetId(); | NodeId avanodeid = avanode->GetId(); | ||||
BOOST_CHECK(addNode(avanodeid)); | BOOST_CHECK(addNode(avanodeid)); | ||||
// It returns the avalanche peer. | // It returns the avalanche peer. | ||||
BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), avanodeid); | BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), avanodeid); | ||||
// 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(context.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. | // Trigger a poll on avanode. | ||||
uint64_t round = getRound(); | uint64_t round = getRound(); | ||||
runEventLoop(); | runEventLoop(); | ||||
// There is no more suitable peer available, so return nothing. | // There is no more suitable peer available, so return nothing. | ||||
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(context.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 | ||||
// queriable nodes. | // queriable nodes. | ||||
BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), avanodeid); | BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), avanodeid); | ||||
auto checkRegisterVotesError = [&](NodeId nodeid, | auto checkRegisterVotesError = [&](NodeId nodeid, | ||||
const avalanche::Response &response, | const avalanche::Response &response, | ||||
const std::string &expectedError) { | const std::string &expectedError) { | ||||
std::string error; | std::string error; | ||||
BOOST_CHECK(!context.registerVotes(nodeid, response, error)); | BOOST_CHECK(!provider.registerVotes(nodeid, response, error)); | ||||
BOOST_CHECK_EQUAL(error, expectedError); | BOOST_CHECK_EQUAL(error, expectedError); | ||||
BOOST_CHECK_EQUAL(updates.size(), 0); | BOOST_CHECK_EQUAL(updates.size(), 0); | ||||
}; | }; | ||||
// Sending a response when not polled fails. | // Sending a response when not polled fails. | ||||
checkRegisterVotesError(avanodeid, next(resp), "unexpected-ava-response"); | checkRegisterVotesError(avanodeid, next(resp), "unexpected-ava-response"); | ||||
// Trigger a poll on avanode. | // Trigger a poll on avanode. | ||||
Show All 32 Lines | BOOST_AUTO_TEST_CASE_TEMPLATE(poll_and_response, P, VoteItemProviders) { | ||||
// 5. Making request for invalid nodes do not work. Request is not | // 5. Making request for invalid nodes do not work. Request is not | ||||
// discarded. | // discarded. | ||||
resp = {queryRound, 0, {Vote(0, itemid)}}; | resp = {queryRound, 0, {Vote(0, itemid)}}; | ||||
checkRegisterVotesError(avanodeid + 1234, resp, "unexpected-ava-response"); | checkRegisterVotesError(avanodeid + 1234, resp, "unexpected-ava-response"); | ||||
// Proper response gets processed and avanode is available again. | // Proper response gets processed and avanode is available again. | ||||
resp = {queryRound, 0, {Vote(0, itemid)}}; | resp = {queryRound, 0, {Vote(0, itemid)}}; | ||||
BOOST_CHECK(context.registerVotes(avanodeid, resp)); | BOOST_CHECK(provider.registerVotes(avanodeid, resp)); | ||||
BOOST_CHECK_EQUAL(updates.size(), 0); | BOOST_CHECK_EQUAL(updates.size(), 0); | ||||
BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), avanodeid); | BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), avanodeid); | ||||
// Out of order response are rejected. | // Out of order response are rejected. | ||||
const auto item2 = context.buildVoteItem(); | const auto item2 = provider.buildVoteItem(); | ||||
BOOST_CHECK(context.addToReconcile(item2)); | BOOST_CHECK(provider.addToReconcile(item2)); | ||||
std::vector<Vote> votes = context.buildVotesForItems(0, {item, item2}); | std::vector<Vote> votes = provider.buildVotesForItems(0, {item, item2}); | ||||
resp = {getRound(), 0, {votes[1], votes[0]}}; | resp = {getRound(), 0, {votes[1], votes[0]}}; | ||||
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); | ||||
// But they are accepted in order. | // But they are accepted in order. | ||||
resp = {getRound(), 0, votes}; | resp = {getRound(), 0, votes}; | ||||
runEventLoop(); | runEventLoop(); | ||||
BOOST_CHECK(context.registerVotes(avanodeid, resp)); | BOOST_CHECK(provider.registerVotes(avanodeid, resp)); | ||||
BOOST_CHECK_EQUAL(updates.size(), 0); | BOOST_CHECK_EQUAL(updates.size(), 0); | ||||
BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), avanodeid); | BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), avanodeid); | ||||
} | } | ||||
BOOST_AUTO_TEST_CASE(dont_poll_invalid_block) { | BOOST_AUTO_TEST_CASE(dont_poll_invalid_block) { | ||||
std::vector<BlockUpdate> updates; | std::vector<BlockUpdate> updates; | ||||
CBlock blockA = CreateAndProcessBlock({}, CScript()); | CBlock blockA = CreateAndProcessBlock({}, CScript()); | ||||
Show All 25 Lines | BOOST_AUTO_TEST_CASE(dont_poll_invalid_block) { | ||||
pindexB->nStatus = pindexB->nStatus.withFailed(); | pindexB->nStatus = pindexB->nStatus.withFailed(); | ||||
Response resp{getRound(), 0, {Vote(0, blockHashA)}}; | Response resp{getRound(), 0, {Vote(0, blockHashA)}}; | ||||
runEventLoop(); | runEventLoop(); | ||||
BOOST_CHECK(registerVotes(avanodes[0]->GetId(), resp, updates)); | BOOST_CHECK(registerVotes(avanodes[0]->GetId(), resp, updates)); | ||||
BOOST_CHECK_EQUAL(updates.size(), 0); | BOOST_CHECK_EQUAL(updates.size(), 0); | ||||
} | } | ||||
BOOST_TEST_DECORATOR(*boost::unit_test::timeout(60)) | BOOST_TEST_DECORATOR(*boost::unit_test::timeout(60)) | ||||
BOOST_AUTO_TEST_CASE_TEMPLATE(poll_inflight_timeout, T, | BOOST_AUTO_TEST_CASE_TEMPLATE(poll_inflight_timeout, P, VoteItemProviders) { | ||||
voteItemTestingContexts) { | P provider(this); | ||||
T context(this); | |||||
const auto item = context.buildVoteItem(); | const auto item = provider.buildVoteItem(); | ||||
const auto itemid = context.getVoteItemId(item); | const auto itemid = provider.getVoteItemId(item); | ||||
// Add the item | // Add the item | ||||
BOOST_CHECK(context.addToReconcile(item)); | BOOST_CHECK(provider.addToReconcile(item)); | ||||
// Create a node that supports avalanche. | // Create a node that supports avalanche. | ||||
auto avanode = ConnectNode(NODE_AVALANCHE); | auto avanode = ConnectNode(NODE_AVALANCHE); | ||||
NodeId avanodeid = avanode->GetId(); | NodeId avanodeid = avanode->GetId(); | ||||
BOOST_CHECK(addNode(avanodeid)); | 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)}}; | ||||
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 = context.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); | ||||
// 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(!context.registerVotes(avanodeid, next(resp))); | BOOST_CHECK(!provider.registerVotes(avanodeid, next(resp))); | ||||
} | } | ||||
} | } | ||||
BOOST_AUTO_TEST_CASE_TEMPLATE(poll_inflight_count, T, voteItemTestingContexts) { | BOOST_AUTO_TEST_CASE_TEMPLATE(poll_inflight_count, P, VoteItemProviders) { | ||||
T context(this); | P provider(this); | ||||
const uint32_t invType = context.invType; | const uint32_t invType = provider.invType; | ||||
// Create enough nodes so that we run into the inflight request limit. | // Create enough nodes so that we run into the inflight request limit. | ||||
auto proof = GetProof(); | auto proof = GetProof(); | ||||
BOOST_CHECK(m_processor->withPeerManager( | BOOST_CHECK(m_processor->withPeerManager( | ||||
[&](avalanche::PeerManager &pm) { return pm.registerProof(proof); })); | [&](avalanche::PeerManager &pm) { return pm.registerProof(proof); })); | ||||
std::array<CNode *, AVALANCHE_MAX_INFLIGHT_POLL + 1> nodes; | std::array<CNode *, AVALANCHE_MAX_INFLIGHT_POLL + 1> nodes; | ||||
for (auto &n : nodes) { | for (auto &n : nodes) { | ||||
n = ConnectNode(NODE_AVALANCHE); | n = ConnectNode(NODE_AVALANCHE); | ||||
BOOST_CHECK(addNode(n->GetId(), proof->getId())); | BOOST_CHECK(addNode(n->GetId(), proof->getId())); | ||||
} | } | ||||
// Add an item to poll | // Add an item to poll | ||||
const auto item = context.buildVoteItem(); | const auto item = provider.buildVoteItem(); | ||||
const auto itemid = context.getVoteItemId(item); | const auto itemid = provider.getVoteItemId(item); | ||||
BOOST_CHECK(context.addToReconcile(item)); | BOOST_CHECK(provider.addToReconcile(item)); | ||||
// Ensure there are enough requests in flight. | // Ensure there are enough requests in flight. | ||||
std::map<NodeId, uint64_t> node_round_map; | std::map<NodeId, uint64_t> node_round_map; | ||||
for (int i = 0; i < AVALANCHE_MAX_INFLIGHT_POLL; i++) { | for (int i = 0; i < AVALANCHE_MAX_INFLIGHT_POLL; i++) { | ||||
NodeId nodeid = getSuitableNodeToQuery(); | NodeId nodeid = getSuitableNodeToQuery(); | ||||
BOOST_CHECK(node_round_map.find(nodeid) == node_round_map.end()); | BOOST_CHECK(node_round_map.find(nodeid) == node_round_map.end()); | ||||
node_round_map.insert(std::pair<NodeId, uint64_t>(nodeid, getRound())); | node_round_map.insert(std::pair<NodeId, uint64_t>(nodeid, getRound())); | ||||
auto invs = getInvsForNextPoll(); | auto invs = getInvsForNextPoll(); | ||||
Show All 9 Lines | BOOST_AUTO_TEST_CASE_TEMPLATE(poll_inflight_count, P, VoteItemProviders) { | ||||
auto invs = getInvsForNextPoll(); | auto invs = getInvsForNextPoll(); | ||||
BOOST_CHECK_EQUAL(invs.size(), 0); | BOOST_CHECK_EQUAL(invs.size(), 0); | ||||
runEventLoop(); | runEventLoop(); | ||||
BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), suitablenodeid); | BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), suitablenodeid); | ||||
// Send one response, now we can poll again. | // Send one response, now we can poll again. | ||||
auto it = node_round_map.begin(); | auto it = node_round_map.begin(); | ||||
Response resp = {it->second, 0, {Vote(0, itemid)}}; | Response resp = {it->second, 0, {Vote(0, itemid)}}; | ||||
BOOST_CHECK(context.registerVotes(it->first, resp)); | BOOST_CHECK(provider.registerVotes(it->first, resp)); | ||||
node_round_map.erase(it); | node_round_map.erase(it); | ||||
invs = getInvsForNextPoll(); | 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); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 243 Lines • Show Last 20 Lines |