Changeset View
Changeset View
Standalone View
Standalone View
src/test/avalanche_tests.cpp
// Copyright (c) 2010 The Bitcoin developers | // Copyright (c) 2010 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.h" | #include "avalanche.h" | ||||
#include "config.h" | |||||
#include "net_processing.h" | |||||
#include "scheduler.h" | #include "scheduler.h" | ||||
#include "test/test_bitcoin.h" | #include "test/test_bitcoin.h" | ||||
#include <boost/test/unit_test.hpp> | #include <boost/test/unit_test.hpp> | ||||
struct AvalancheTest { | struct AvalancheTest { | ||||
static void runEventLoop(AvalancheProcessor &p) { p.runEventLoop(); } | |||||
static std::vector<CInv> getInvsForNextPoll(const AvalancheProcessor &p) { | static std::vector<CInv> getInvsForNextPoll(const AvalancheProcessor &p) { | ||||
return p.getInvsForNextPoll(); | return p.getInvsForNextPoll(); | ||||
} | } | ||||
static NodeId getSuitableNodeToQuery(AvalancheProcessor &p) { | |||||
return p.getSuitableNodeToQuery(); | |||||
} | |||||
}; | }; | ||||
BOOST_FIXTURE_TEST_SUITE(avalanche_tests, BasicTestingSetup) | BOOST_FIXTURE_TEST_SUITE(avalanche_tests, TestingSetup) | ||||
#define REGISTER_VOTE_AND_CHECK(vr, vote, state, finalized, confidence) \ | #define REGISTER_VOTE_AND_CHECK(vr, vote, state, finalized, confidence) \ | ||||
vr.registerVote(vote); \ | vr.registerVote(vote); \ | ||||
BOOST_CHECK_EQUAL(vr.isValid(), state); \ | BOOST_CHECK_EQUAL(vr.isValid(), 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 41 Lines • ▼ Show 20 Lines | for (int i = 0; i < AVALANCHE_FINALIZATION_SCORE; i++) { | ||||
REGISTER_VOTE_AND_CHECK(vr, false, false, false, i); | REGISTER_VOTE_AND_CHECK(vr, false, false, false, i); | ||||
} | } | ||||
// The next vote will finalize the decision. | // The next vote will finalize the decision. | ||||
REGISTER_VOTE_AND_CHECK(vr, true, false, true, | REGISTER_VOTE_AND_CHECK(vr, true, false, true, | ||||
AVALANCHE_FINALIZATION_SCORE); | AVALANCHE_FINALIZATION_SCORE); | ||||
} | } | ||||
CService ip(uint32_t i) { | |||||
struct in_addr s; | |||||
s.s_addr = i; | |||||
return CService(CNetAddr(s), Params().GetDefaultPort()); | |||||
} | |||||
std::unique_ptr<CNode> ConnectNode(const Config &config, ServiceFlags nServices, | |||||
PeerLogicValidation &peerLogic) { | |||||
static NodeId id = 0; | |||||
CAddress addr(ip(GetRandInt(0xffffffff)), NODE_NONE); | |||||
std::unique_ptr<CNode> nodeptr(new CNode(id++, ServiceFlags(NODE_NETWORK), | |||||
0, INVALID_SOCKET, addr, 0, 0, | |||||
CAddress(), "", | |||||
/*fInboundIn=*/false)); | |||||
CNode &node = *nodeptr; | |||||
node.SetSendVersion(PROTOCOL_VERSION); | |||||
node.nServices = nServices; | |||||
peerLogic.InitializeNode(config, &node); | |||||
node.nVersion = 1; | |||||
node.fSuccessfullyConnected = true; | |||||
CConnmanTest::AddNode(node); | |||||
return nodeptr; | |||||
} | |||||
BOOST_AUTO_TEST_CASE(block_register) { | BOOST_AUTO_TEST_CASE(block_register) { | ||||
AvalancheProcessor p; | AvalancheProcessor p(g_connman.get()); | ||||
CBlockIndex index; | CBlockIndex index; | ||||
std::vector<uint256> accepted, rejected; | std::vector<uint256> accepted, rejected; | ||||
const Config &config = GetConfig(); | |||||
// Create a node that supports avalanche. | |||||
auto avanode = ConnectNode(config, NODE_AVALANCHE, *peerLogic); | |||||
NodeId nodeid = avanode->GetId(); | |||||
// Make sure the block has a hash. | // Make sure the block has a hash. | ||||
static const uint256 blockHash(uint256S( | static const uint256 blockHash(uint256S( | ||||
"abcdef0000000000000000000000000000000000000000000000000000000001")); | "abcdef0000000000000000000000000000000000000000000000000000000001")); | ||||
index.phashBlock = &blockHash; | index.phashBlock = &blockHash; | ||||
// Querying for random block returns false. | // Querying for random block returns false. | ||||
BOOST_CHECK(!p.isAccepted(&index)); | BOOST_CHECK(!p.isAccepted(&index)); | ||||
// Add a new block. Check it is added to the polls. | // Add a new block. Check it is added to the polls. | ||||
BOOST_CHECK(p.addBlockToReconcile(&index)); | BOOST_CHECK(p.addBlockToReconcile(&index)); | ||||
auto invs = AvalancheTest::getInvsForNextPoll(p); | auto invs = AvalancheTest::getInvsForNextPoll(p); | ||||
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, MSG_BLOCK); | ||||
BOOST_CHECK(invs[0].hash == blockHash); | BOOST_CHECK(invs[0].hash == blockHash); | ||||
// Newly added blocks are also considered rejected. | // Newly added blocks are also considered rejected. | ||||
BOOST_CHECK(!p.isAccepted(&index)); | BOOST_CHECK(!p.isAccepted(&index)); | ||||
// Let's vote for this block a few times. | // Let's vote for this block a few times. | ||||
AvalancheResponse resp{{AvalancheVote(0, blockHash)}}; | AvalancheResponse resp{{AvalancheVote(0, blockHash)}}; | ||||
for (int i = 0; i < 5; i++) { | for (int i = 0; i < 5; i++) { | ||||
p.registerVotes(resp, accepted, rejected); | AvalancheTest::runEventLoop(p); | ||||
BOOST_CHECK(p.registerVotes(nodeid, resp, accepted, rejected)); | |||||
BOOST_CHECK(!p.isAccepted(&index)); | BOOST_CHECK(!p.isAccepted(&index)); | ||||
BOOST_CHECK_EQUAL(accepted.size(), 0); | BOOST_CHECK_EQUAL(accepted.size(), 0); | ||||
BOOST_CHECK_EQUAL(rejected.size(), 0); | BOOST_CHECK_EQUAL(rejected.size(), 0); | ||||
} | } | ||||
// Now it is accepeted, but we can vote for it numerous times. | // Now it is accepeted, 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++) { | ||||
p.registerVotes(resp, accepted, rejected); | AvalancheTest::runEventLoop(p); | ||||
BOOST_CHECK(p.registerVotes(nodeid, resp, accepted, rejected)); | |||||
BOOST_CHECK(p.isAccepted(&index)); | BOOST_CHECK(p.isAccepted(&index)); | ||||
BOOST_CHECK_EQUAL(accepted.size(), 0); | BOOST_CHECK_EQUAL(accepted.size(), 0); | ||||
BOOST_CHECK_EQUAL(rejected.size(), 0); | BOOST_CHECK_EQUAL(rejected.size(), 0); | ||||
} | } | ||||
// As long as it is not finalized, we poll. | // As long as it is not finalized, we poll. | ||||
invs = AvalancheTest::getInvsForNextPoll(p); | invs = AvalancheTest::getInvsForNextPoll(p); | ||||
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, MSG_BLOCK); | ||||
BOOST_CHECK(invs[0].hash == blockHash); | BOOST_CHECK(invs[0].hash == blockHash); | ||||
// Now finalize the decision. | // Now finalize the decision. | ||||
p.registerVotes(resp, accepted, rejected); | AvalancheTest::runEventLoop(p); | ||||
BOOST_CHECK(p.registerVotes(nodeid, resp, accepted, rejected)); | |||||
BOOST_CHECK_EQUAL(accepted.size(), 1); | BOOST_CHECK_EQUAL(accepted.size(), 1); | ||||
BOOST_CHECK_EQUAL(rejected.size(), 0); | BOOST_CHECK_EQUAL(rejected.size(), 0); | ||||
BOOST_CHECK(accepted[0] == blockHash); | BOOST_CHECK(accepted[0] == blockHash); | ||||
accepted = {}; | accepted = {}; | ||||
// Once the decision is finalized, there is no poll for it. | // Once the decision is finalized, there is no poll for it. | ||||
invs = AvalancheTest::getInvsForNextPoll(p); | invs = AvalancheTest::getInvsForNextPoll(p); | ||||
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(p.addBlockToReconcile(&index)); | BOOST_CHECK(p.addBlockToReconcile(&index)); | ||||
invs = AvalancheTest::getInvsForNextPoll(p); | invs = AvalancheTest::getInvsForNextPoll(p); | ||||
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, MSG_BLOCK); | ||||
BOOST_CHECK(invs[0].hash == blockHash); | BOOST_CHECK(invs[0].hash == blockHash); | ||||
// Only 3 here as we don't need to flip state. | // Only 3 here as we don't need to flip state. | ||||
resp = {{AvalancheVote(1, blockHash)}}; | resp = {{AvalancheVote(1, blockHash)}}; | ||||
for (int i = 0; i < 3; i++) { | for (int i = 0; i < 3; i++) { | ||||
p.registerVotes(resp, accepted, rejected); | AvalancheTest::runEventLoop(p); | ||||
BOOST_CHECK(p.registerVotes(nodeid, resp, accepted, rejected)); | |||||
BOOST_CHECK(!p.isAccepted(&index)); | BOOST_CHECK(!p.isAccepted(&index)); | ||||
BOOST_CHECK_EQUAL(accepted.size(), 0); | BOOST_CHECK_EQUAL(accepted.size(), 0); | ||||
BOOST_CHECK_EQUAL(rejected.size(), 0); | BOOST_CHECK_EQUAL(rejected.size(), 0); | ||||
} | } | ||||
// Now it is rejected, but we can vote for it numerous times. | // Now it is rejected, 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++) { | ||||
p.registerVotes(resp, accepted, rejected); | AvalancheTest::runEventLoop(p); | ||||
BOOST_CHECK(p.registerVotes(nodeid, resp, accepted, rejected)); | |||||
BOOST_CHECK(!p.isAccepted(&index)); | BOOST_CHECK(!p.isAccepted(&index)); | ||||
BOOST_CHECK_EQUAL(accepted.size(), 0); | BOOST_CHECK_EQUAL(accepted.size(), 0); | ||||
BOOST_CHECK_EQUAL(rejected.size(), 0); | BOOST_CHECK_EQUAL(rejected.size(), 0); | ||||
} | } | ||||
// As long as it is not finalized, we poll. | // As long as it is not finalized, we poll. | ||||
invs = AvalancheTest::getInvsForNextPoll(p); | invs = AvalancheTest::getInvsForNextPoll(p); | ||||
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, MSG_BLOCK); | ||||
BOOST_CHECK(invs[0].hash == blockHash); | BOOST_CHECK(invs[0].hash == blockHash); | ||||
// Now finalize the decision. | // Now finalize the decision. | ||||
p.registerVotes(resp, accepted, rejected); | AvalancheTest::runEventLoop(p); | ||||
BOOST_CHECK(p.registerVotes(nodeid, resp, accepted, rejected)); | |||||
BOOST_CHECK(!p.isAccepted(&index)); | BOOST_CHECK(!p.isAccepted(&index)); | ||||
BOOST_CHECK_EQUAL(accepted.size(), 0); | BOOST_CHECK_EQUAL(accepted.size(), 0); | ||||
BOOST_CHECK_EQUAL(rejected.size(), 1); | BOOST_CHECK_EQUAL(rejected.size(), 1); | ||||
BOOST_CHECK(rejected[0] == blockHash); | BOOST_CHECK(rejected[0] == blockHash); | ||||
rejected = {}; | rejected = {}; | ||||
// Once the decision is finalized, there is no poll for it. | // Once the decision is finalized, there is no poll for it. | ||||
invs = AvalancheTest::getInvsForNextPoll(p); | invs = AvalancheTest::getInvsForNextPoll(p); | ||||
BOOST_CHECK_EQUAL(invs.size(), 0); | BOOST_CHECK_EQUAL(invs.size(), 0); | ||||
// Adding the block twice does nothing. | // Adding the block twice does nothing. | ||||
BOOST_CHECK(p.addBlockToReconcile(&index)); | BOOST_CHECK(p.addBlockToReconcile(&index)); | ||||
BOOST_CHECK(!p.addBlockToReconcile(&index)); | BOOST_CHECK(!p.addBlockToReconcile(&index)); | ||||
BOOST_CHECK(!p.isAccepted(&index)); | BOOST_CHECK(!p.isAccepted(&index)); | ||||
CConnmanTest::ClearNodes(); | |||||
} | } | ||||
BOOST_AUTO_TEST_CASE(multi_block_register) { | BOOST_AUTO_TEST_CASE(multi_block_register) { | ||||
AvalancheProcessor p; | AvalancheProcessor p(g_connman.get()); | ||||
CBlockIndex indexA, indexB; | CBlockIndex indexA, indexB; | ||||
std::vector<uint256> accepted, rejected; | std::vector<uint256> accepted, rejected; | ||||
const Config &config = GetConfig(); | |||||
// Create a node that supports avalanche. | |||||
auto node0 = ConnectNode(config, NODE_AVALANCHE, *peerLogic); | |||||
auto node1 = ConnectNode(config, NODE_AVALANCHE, *peerLogic); | |||||
// Make sure the block has a hash. | // Make sure the block has a hash. | ||||
static const uint256 blockHashA(uint256S( | static const uint256 blockHashA(uint256S( | ||||
"abcdef0000000000000000000000000000000000000000000000000000000001")); | "abcdef0000000000000000000000000000000000000000000000000000000001")); | ||||
indexA.phashBlock = &blockHashA; | indexA.phashBlock = &blockHashA; | ||||
static const uint256 blockHashB(uint256S( | static const uint256 blockHashB(uint256S( | ||||
"abcdef0000000000000000000000000000000000000000000000000987654321")); | "abcdef0000000000000000000000000000000000000000000000000987654321")); | ||||
indexB.phashBlock = &blockHashB; | indexB.phashBlock = &blockHashB; | ||||
// Querying for random block returns false. | // Querying for random block returns false. | ||||
BOOST_CHECK(!p.isAccepted(&indexA)); | BOOST_CHECK(!p.isAccepted(&indexA)); | ||||
BOOST_CHECK(!p.isAccepted(&indexB)); | BOOST_CHECK(!p.isAccepted(&indexB)); | ||||
// Start voting on block A. | // Start voting on block A. | ||||
BOOST_CHECK(p.addBlockToReconcile(&indexA)); | BOOST_CHECK(p.addBlockToReconcile(&indexA)); | ||||
auto invs = AvalancheTest::getInvsForNextPoll(p); | auto invs = AvalancheTest::getInvsForNextPoll(p); | ||||
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, MSG_BLOCK); | ||||
BOOST_CHECK(invs[0].hash == blockHashA); | BOOST_CHECK(invs[0].hash == blockHashA); | ||||
AvalancheResponse resp{ | AvalancheTest::runEventLoop(p); | ||||
{AvalancheVote(0, blockHashA), AvalancheVote(0, blockHashB)}}; | BOOST_CHECK(p.registerVotes( | ||||
p.registerVotes(resp, accepted, rejected); | node0->GetId(), {{AvalancheVote(0, blockHashA)}}, accepted, rejected)); | ||||
BOOST_CHECK_EQUAL(accepted.size(), 0); | BOOST_CHECK_EQUAL(accepted.size(), 0); | ||||
BOOST_CHECK_EQUAL(rejected.size(), 0); | BOOST_CHECK_EQUAL(rejected.size(), 0); | ||||
// Start voing on block B after one vote. | // Start voing on block B after one vote. | ||||
AvalancheResponse resp{ | |||||
{AvalancheVote(0, blockHashA), AvalancheVote(0, blockHashB)}}; | |||||
BOOST_CHECK(p.addBlockToReconcile(&indexB)); | BOOST_CHECK(p.addBlockToReconcile(&indexB)); | ||||
invs = AvalancheTest::getInvsForNextPoll(p); | invs = AvalancheTest::getInvsForNextPoll(p); | ||||
BOOST_CHECK_EQUAL(invs.size(), 2); | BOOST_CHECK_EQUAL(invs.size(), 2); | ||||
BOOST_CHECK_EQUAL(invs[0].type, MSG_BLOCK); | BOOST_CHECK_EQUAL(invs[0].type, MSG_BLOCK); | ||||
BOOST_CHECK(invs[0].hash == blockHashA); | BOOST_CHECK(invs[0].hash == blockHashA); | ||||
BOOST_CHECK_EQUAL(invs[1].type, MSG_BLOCK); | BOOST_CHECK_EQUAL(invs[1].type, MSG_BLOCK); | ||||
BOOST_CHECK(invs[1].hash == blockHashB); | BOOST_CHECK(invs[1].hash == blockHashB); | ||||
// Now it is rejected, but we can vote for it numerous times. | // Now it is rejected, but we can vote for it numerous times. | ||||
for (int i = 0; i < AVALANCHE_FINALIZATION_SCORE + 4; i++) { | for (int i = 0; i < AVALANCHE_FINALIZATION_SCORE + 4; i++) { | ||||
p.registerVotes(resp, accepted, rejected); | AvalancheTest::runEventLoop(p); | ||||
BOOST_CHECK(p.registerVotes(node0->GetId(), resp, accepted, rejected)); | |||||
BOOST_CHECK_EQUAL(accepted.size(), 0); | BOOST_CHECK_EQUAL(accepted.size(), 0); | ||||
BOOST_CHECK_EQUAL(rejected.size(), 0); | BOOST_CHECK_EQUAL(rejected.size(), 0); | ||||
} | } | ||||
// Running two iterration of the event loop so that vote gets triggerd on A | |||||
// and B. | |||||
AvalancheTest::runEventLoop(p); | |||||
AvalancheTest::runEventLoop(p); | |||||
// Next vote will finalize block A. | // Next vote will finalize block A. | ||||
p.registerVotes(resp, accepted, rejected); | BOOST_CHECK(p.registerVotes(node1->GetId(), resp, accepted, rejected)); | ||||
BOOST_CHECK_EQUAL(accepted.size(), 1); | BOOST_CHECK_EQUAL(accepted.size(), 1); | ||||
BOOST_CHECK_EQUAL(rejected.size(), 0); | BOOST_CHECK_EQUAL(rejected.size(), 0); | ||||
BOOST_CHECK(accepted[0] == blockHashA); | BOOST_CHECK(accepted[0] == blockHashA); | ||||
accepted = {}; | accepted = {}; | ||||
// We do not vote on A anymore. | // We do not vote on A anymore. | ||||
invs = AvalancheTest::getInvsForNextPoll(p); | invs = AvalancheTest::getInvsForNextPoll(p); | ||||
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, MSG_BLOCK); | ||||
BOOST_CHECK(invs[0].hash == blockHashB); | BOOST_CHECK(invs[0].hash == blockHashB); | ||||
// Next vote will finalize block B. | // Next vote will finalize block B. | ||||
p.registerVotes(resp, accepted, rejected); | BOOST_CHECK(p.registerVotes(node0->GetId(), resp, accepted, rejected)); | ||||
BOOST_CHECK_EQUAL(accepted.size(), 1); | BOOST_CHECK_EQUAL(accepted.size(), 1); | ||||
BOOST_CHECK_EQUAL(rejected.size(), 0); | BOOST_CHECK_EQUAL(rejected.size(), 0); | ||||
BOOST_CHECK(accepted[0] == blockHashB); | BOOST_CHECK(accepted[0] == blockHashB); | ||||
accepted = {}; | accepted = {}; | ||||
// There is nothing left to vote on. | // There is nothing left to vote on. | ||||
invs = AvalancheTest::getInvsForNextPoll(p); | invs = AvalancheTest::getInvsForNextPoll(p); | ||||
BOOST_CHECK_EQUAL(invs.size(), 0); | BOOST_CHECK_EQUAL(invs.size(), 0); | ||||
CConnmanTest::ClearNodes(); | |||||
} | |||||
BOOST_AUTO_TEST_CASE(poll_and_response) { | |||||
AvalancheProcessor p(g_connman.get()); | |||||
std::vector<uint256> accepted, rejected; | |||||
const Config &config = GetConfig(); | |||||
// Let's register a block to vote on and do one round of event loop. | |||||
static const uint256 blockHash(uint256S( | |||||
"abcdef0000000000000000000000000000000000000000000000000000000001")); | |||||
CBlockIndex index; | |||||
index.phashBlock = &blockHash; | |||||
// There is no node to query. | |||||
BOOST_CHECK_EQUAL(AvalancheTest::getSuitableNodeToQuery(p), -1); | |||||
// Create a node that supports avalanche and one that doesn't. | |||||
auto oldnode = ConnectNode(config, NODE_NONE, *peerLogic); | |||||
auto avanode = ConnectNode(config, NODE_AVALANCHE, *peerLogic); | |||||
NodeId avanodeid = avanode->GetId(); | |||||
// It returns the avalanche peer. | |||||
BOOST_CHECK_EQUAL(AvalancheTest::getSuitableNodeToQuery(p), avanodeid); | |||||
// Register a block and check it is added to the list of elements to poll. | |||||
BOOST_CHECK(p.addBlockToReconcile(&index)); | |||||
auto invs = AvalancheTest::getInvsForNextPoll(p); | |||||
BOOST_CHECK_EQUAL(invs.size(), 1); | |||||
BOOST_CHECK_EQUAL(invs[0].type, MSG_BLOCK); | |||||
BOOST_CHECK(invs[0].hash == blockHash); | |||||
// Trigger a poll on avanode. | |||||
AvalancheTest::runEventLoop(p); | |||||
// There is no more suitable peer available, so return nothing. | |||||
BOOST_CHECK_EQUAL(AvalancheTest::getSuitableNodeToQuery(p), -1); | |||||
// Respond to the request. | |||||
AvalancheResponse resp = {{AvalancheVote(0, blockHash)}}; | |||||
BOOST_CHECK(p.registerVotes(avanode->GetId(), resp, accepted, rejected)); | |||||
BOOST_CHECK_EQUAL(accepted.size(), 0); | |||||
BOOST_CHECK_EQUAL(rejected.size(), 0); | |||||
// Now that avanode fullfilled his request, it is added back to the list of | |||||
// queriable nodes. | |||||
BOOST_CHECK_EQUAL(AvalancheTest::getSuitableNodeToQuery(p), avanodeid); | |||||
// Sending a response when not polled fails. | |||||
BOOST_CHECK(!p.registerVotes(avanode->GetId(), resp, accepted, rejected)); | |||||
BOOST_CHECK_EQUAL(accepted.size(), 0); | |||||
BOOST_CHECK_EQUAL(rejected.size(), 0); | |||||
// Trigger a poll on avanode. | |||||
AvalancheTest::runEventLoop(p); | |||||
BOOST_CHECK_EQUAL(AvalancheTest::getSuitableNodeToQuery(p), -1); | |||||
// Sending responses that do not match the request also fails. | |||||
// 1. Too many results. | |||||
resp = {{AvalancheVote(0, blockHash), AvalancheVote(0, blockHash)}}; | |||||
AvalancheTest::runEventLoop(p); | |||||
BOOST_CHECK(!p.registerVotes(avanode->GetId(), resp, accepted, rejected)); | |||||
BOOST_CHECK_EQUAL(accepted.size(), 0); | |||||
BOOST_CHECK_EQUAL(rejected.size(), 0); | |||||
BOOST_CHECK_EQUAL(AvalancheTest::getSuitableNodeToQuery(p), avanodeid); | |||||
// 2. Not enough results. | |||||
resp = {{}}; | |||||
AvalancheTest::runEventLoop(p); | |||||
BOOST_CHECK(!p.registerVotes(avanode->GetId(), resp, accepted, rejected)); | |||||
BOOST_CHECK_EQUAL(accepted.size(), 0); | |||||
BOOST_CHECK_EQUAL(rejected.size(), 0); | |||||
BOOST_CHECK_EQUAL(AvalancheTest::getSuitableNodeToQuery(p), avanodeid); | |||||
// 3. Do not match the poll. | |||||
resp = {{AvalancheVote()}}; | |||||
AvalancheTest::runEventLoop(p); | |||||
BOOST_CHECK(!p.registerVotes(avanode->GetId(), resp, accepted, rejected)); | |||||
BOOST_CHECK_EQUAL(accepted.size(), 0); | |||||
BOOST_CHECK_EQUAL(rejected.size(), 0); | |||||
BOOST_CHECK_EQUAL(AvalancheTest::getSuitableNodeToQuery(p), avanodeid); | |||||
// Proper response gets processed and avanode is available again. | |||||
resp = {{AvalancheVote(0, blockHash)}}; | |||||
AvalancheTest::runEventLoop(p); | |||||
BOOST_CHECK(p.registerVotes(avanode->GetId(), resp, accepted, rejected)); | |||||
BOOST_CHECK_EQUAL(accepted.size(), 0); | |||||
BOOST_CHECK_EQUAL(rejected.size(), 0); | |||||
BOOST_CHECK_EQUAL(AvalancheTest::getSuitableNodeToQuery(p), avanodeid); | |||||
// Making request for invalid nodes do not work. | |||||
BOOST_CHECK( | |||||
!p.registerVotes(avanode->GetId() + 1234, resp, accepted, rejected)); | |||||
BOOST_CHECK_EQUAL(accepted.size(), 0); | |||||
BOOST_CHECK_EQUAL(rejected.size(), 0); | |||||
CConnmanTest::ClearNodes(); | |||||
} | } | ||||
BOOST_AUTO_TEST_CASE(event_loop) { | BOOST_AUTO_TEST_CASE(event_loop) { | ||||
AvalancheProcessor p; | AvalancheProcessor p(g_connman.get()); | ||||
CScheduler scheduler; | CScheduler s; | ||||
// Starting the event loop. | // Starting the event loop. | ||||
BOOST_CHECK(p.startEventLoop(scheduler)); | BOOST_CHECK(p.startEventLoop(s)); | ||||
// There is one task planned in the next hour (our event loop). | // There is one task planned in the next hour (our event loop). | ||||
boost::chrono::system_clock::time_point start, stop; | boost::chrono::system_clock::time_point start, stop; | ||||
BOOST_CHECK_EQUAL(scheduler.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(!p.startEventLoop(scheduler)); | BOOST_CHECK(!p.startEventLoop(s)); | ||||
// Start the scheduler thread. | // Start the scheduler thread. | ||||
std::thread schedulerThread( | std::thread schedulerThread(std::bind(&CScheduler::serviceQueue, &s)); | ||||
std::bind(&CScheduler::serviceQueue, &scheduler)); | |||||
// Create a node and a block to query. | |||||
const Config &config = GetConfig(); | |||||
CBlockIndex index; | |||||
// Create a node that supports avalanche. | |||||
auto avanode = ConnectNode(config, NODE_AVALANCHE, *peerLogic); | |||||
NodeId nodeid = avanode->GetId(); | |||||
// Make sure the block has a hash. | |||||
static const uint256 blockHash(uint256S( | |||||
"abcdef0000000000000000000000000000000000000000000000000000000001")); | |||||
index.phashBlock = &blockHash; | |||||
// There is no query in flight at the moment. | |||||
BOOST_CHECK_EQUAL(AvalancheTest::getSuitableNodeToQuery(p), nodeid); | |||||
// Add a new block. Check it is added to the polls. | |||||
BOOST_CHECK(p.addBlockToReconcile(&index)); | |||||
bool hasQueried = false; | |||||
for (int i = 0; i < 1000; i++) { | |||||
// Technically, this is a race condition, but this should do just fine | |||||
// as we wait up to 1s for an event that should take 10ms. | |||||
boost::this_thread::sleep_for(boost::chrono::milliseconds(1)); | |||||
if (AvalancheTest::getSuitableNodeToQuery(p) == -1) { | |||||
hasQueried = true; | |||||
break; | |||||
} | |||||
} | |||||
BOOST_CHECK(hasQueried); | |||||
// Stop event loop. | // Stop event loop. | ||||
BOOST_CHECK(p.stopEventLoop()); | BOOST_CHECK(p.stopEventLoop()); | ||||
// We don't have any task scheduled anymore. | // We don't have any task scheduled anymore. | ||||
BOOST_CHECK_EQUAL(scheduler.getQueueInfo(start, stop), 0); | BOOST_CHECK_EQUAL(s.getQueueInfo(start, stop), 0); | ||||
// Can't stop the event loop twice. | // Can't stop the event loop twice. | ||||
BOOST_CHECK(!p.stopEventLoop()); | BOOST_CHECK(!p.stopEventLoop()); | ||||
// Wait for the scheduler to stop. | // Wait for the scheduler to stop. | ||||
scheduler.stop(true); | s.stop(true); | ||||
schedulerThread.join(); | schedulerThread.join(); | ||||
CConnmanTest::ClearNodes(); | |||||
} | } | ||||
BOOST_AUTO_TEST_CASE(destructor) { | BOOST_AUTO_TEST_CASE(destructor) { | ||||
CScheduler scheduler; | CScheduler s; | ||||
boost::chrono::system_clock::time_point start, stop; | boost::chrono::system_clock::time_point start, stop; | ||||
// Start the scheduler thread. | // Start the scheduler thread. | ||||
std::thread schedulerThread( | std::thread schedulerThread(std::bind(&CScheduler::serviceQueue, &s)); | ||||
std::bind(&CScheduler::serviceQueue, &scheduler)); | |||||
{ | { | ||||
AvalancheProcessor p; | AvalancheProcessor p(g_connman.get()); | ||||
BOOST_CHECK(p.startEventLoop(scheduler)); | BOOST_CHECK(p.startEventLoop(s)); | ||||
BOOST_CHECK_EQUAL(scheduler.getQueueInfo(start, stop), 1); | BOOST_CHECK_EQUAL(s.getQueueInfo(start, stop), 1); | ||||
} | } | ||||
// Now that avalanche is destroyed, there is scheduled task anymore. | // We don't have any task scheduled anymore that avalanche is destroyed. | ||||
BOOST_CHECK_EQUAL(scheduler.getQueueInfo(start, stop), 0); | BOOST_CHECK_EQUAL(s.getQueueInfo(start, stop), 0); | ||||
// Wait for the scheduler to stop. | // Wait for the scheduler to stop. | ||||
scheduler.stop(true); | s.stop(true); | ||||
schedulerThread.join(); | schedulerThread.join(); | ||||
} | } | ||||
BOOST_AUTO_TEST_SUITE_END() | BOOST_AUTO_TEST_SUITE_END() |