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 <net_processing.h> // For ::PeerManager | #include <net_processing.h> // For ::PeerManager | ||||
#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> | ||||
#include <avalanche/test/util.h> | #include <avalanche/test/util.h> | ||||
▲ Show 20 Lines • Show All 188 Lines • ▼ Show 20 Lines | struct BlockOnlyTestingContext { | ||||
bool registerVotes(NodeId nodeid, const avalanche::Response &response) { | bool registerVotes(NodeId nodeid, const avalanche::Response &response) { | ||||
std::string error; | std::string error; | ||||
return registerVotes(nodeid, response, error); | return registerVotes(nodeid, response, error); | ||||
} | } | ||||
bool addToReconcile(const CBlockIndex *pindex) { | bool addToReconcile(const CBlockIndex *pindex) { | ||||
return fixture->m_processor->addBlockToReconcile(pindex); | return fixture->m_processor->addBlockToReconcile(pindex); | ||||
} | } | ||||
std::vector<Vote> buildVotesForItems(uint32_t error, | |||||
std::vector<CBlockIndex *> &&items) { | |||||
size_t numItems = items.size(); | |||||
std::vector<Vote> votes; | |||||
votes.reserve(numItems); | |||||
// Votes are sorted by most work first | |||||
std::sort(items.begin(), items.end(), CBlockIndexWorkComparator()); | |||||
for (auto &item : reverse_iterate(items)) { | |||||
votes.emplace_back(error, item->GetBlockHash()); | |||||
} | |||||
return votes; | |||||
} | |||||
}; | }; | ||||
} // 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> voteItemTestingContexts; | typedef boost::mpl::list<BlockOnlyTestingContext> voteItemTestingContexts; | ||||
▲ Show 20 Lines • Show All 288 Lines • ▼ Show 20 Lines | BOOST_AUTO_TEST_CASE_TEMPLATE(vote_item_register, T, voteItemTestingContexts) { | ||||
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(multi_block_register) { | BOOST_AUTO_TEST_CASE_TEMPLATE(multi_item_register, T, voteItemTestingContexts) { | ||||
CBlockIndex indexA, indexB; | T context(this); | ||||
auto &updates = context.updates; | |||||
const uint32_t invType = context.invType; | |||||
std::vector<BlockUpdate> updates; | auto itemA = context.buildVoteItem(); | ||||
auto itemidA = context.getVoteItemId(itemA); | |||||
auto itemB = context.buildVoteItem(); | |||||
auto itemidB = context.getVoteItemId(itemB); | |||||
// Create several nodes that support avalanche. | // Create several nodes that support avalanche. | ||||
auto avanodes = ConnectNodes(); | auto avanodes = ConnectNodes(); | ||||
// Make sure the block has a hash. | // Querying for random item returns false. | ||||
CBlock blockA = CreateAndProcessBlock({}, CScript()); | BOOST_CHECK(!m_processor->isAccepted(itemA)); | ||||
const BlockHash blockHashA = blockA.GetHash(); | BOOST_CHECK(!m_processor->isAccepted(itemB)); | ||||
CBlock blockB = CreateAndProcessBlock({}, CScript()); | |||||
const BlockHash blockHashB = blockB.GetHash(); | |||||
const CBlockIndex *pindexA; | |||||
const CBlockIndex *pindexB; | |||||
{ | |||||
LOCK(cs_main); | |||||
pindexA = LookupBlockIndex(blockHashA); | |||||
pindexB = LookupBlockIndex(blockHashB); | |||||
} | |||||
// Querying for random block returns false. | |||||
BOOST_CHECK(!m_processor->isAccepted(pindexA)); | |||||
BOOST_CHECK(!m_processor->isAccepted(pindexB)); | |||||
// Start voting on block A. | // Start voting on item A. | ||||
BOOST_CHECK(m_processor->addBlockToReconcile(pindexA)); | BOOST_CHECK(context.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, MSG_BLOCK); | BOOST_CHECK_EQUAL(invs[0].type, invType); | ||||
BOOST_CHECK(invs[0].hash == blockHashA); | BOOST_CHECK(invs[0].hash == itemidA); | ||||
uint64_t round = getRound(); | uint64_t round = getRound(); | ||||
runEventLoop(); | runEventLoop(); | ||||
BOOST_CHECK(registerVotes(avanodes[0]->GetId(), | BOOST_CHECK(context.registerVotes(avanodes[0]->GetId(), | ||||
{round, 0, {Vote(0, blockHashA)}}, updates)); | {round, 0, {Vote(0, itemidA)}})); | ||||
BOOST_CHECK_EQUAL(updates.size(), 0); | BOOST_CHECK_EQUAL(updates.size(), 0); | ||||
// Start voting on block B after one vote. | // Start voting on item B after one vote. | ||||
Response resp{round + 1, 0, {Vote(0, blockHashB), Vote(0, blockHashA)}}; | std::vector<Vote> votes = context.buildVotesForItems(0, {itemA, itemB}); | ||||
BOOST_CHECK(m_processor->addBlockToReconcile(pindexB)); | Response resp{round + 1, 0, votes}; | ||||
BOOST_CHECK(context.addToReconcile(itemB)); | |||||
invs = getInvsForNextPoll(); | invs = getInvsForNextPoll(); | ||||
BOOST_CHECK_EQUAL(invs.size(), 2); | BOOST_CHECK_EQUAL(invs.size(), 2); | ||||
// Ensure B comes before A because it has accumulated more PoW. | // Ensure the inv ordering is as expected | ||||
BOOST_CHECK_EQUAL(invs[0].type, MSG_BLOCK); | for (size_t i = 0; i < invs.size(); i++) { | ||||
BOOST_CHECK(invs[0].hash == blockHashB); | BOOST_CHECK_EQUAL(invs[i].type, invType); | ||||
BOOST_CHECK_EQUAL(invs[1].type, MSG_BLOCK); | BOOST_CHECK(invs[i].hash == votes[i].GetHash()); | ||||
BOOST_CHECK(invs[1].hash == blockHashA); | } | ||||
// Let's vote for these blocks 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(registerVotes(nodeid, next(resp), updates)); | BOOST_CHECK(context.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(registerVotes(nodeid, next(resp), updates)); | BOOST_CHECK(context.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 block A. | // Next vote will finalize item A. | ||||
BOOST_CHECK(registerVotes(firstNodeid, next(resp), updates)); | BOOST_CHECK(context.registerVotes(firstNodeid, next(resp))); | ||||
BOOST_CHECK_EQUAL(updates.size(), 1); | BOOST_CHECK_EQUAL(updates.size(), 1); | ||||
BOOST_CHECK(updates[0].getVoteItem() == pindexA); | 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, MSG_BLOCK); | BOOST_CHECK_EQUAL(invs[0].type, invType); | ||||
BOOST_CHECK(invs[0].hash == blockHashB); | BOOST_CHECK(invs[0].hash == itemidB); | ||||
// Next vote will finalize block B. | // Next vote will finalize item B. | ||||
BOOST_CHECK(registerVotes(secondNodeid, resp, updates)); | BOOST_CHECK(context.registerVotes(secondNodeid, resp)); | ||||
BOOST_CHECK_EQUAL(updates.size(), 1); | BOOST_CHECK_EQUAL(updates.size(), 1); | ||||
BOOST_CHECK(updates[0].getVoteItem() == pindexB); | 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); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 463 Lines • Show Last 20 Lines |