Changeset View
Changeset View
Standalone View
Standalone View
src/test/avalanche_tests.cpp
Show First 20 Lines • Show All 146 Lines • ▼ Show 20 Lines | BOOST_AUTO_TEST_CASE(block_register) { | ||||
const uint256 blockHash = block.GetHash(); | const uint256 blockHash = block.GetHash(); | ||||
const CBlockIndex *pindex = mapBlockIndex[blockHash]; | const CBlockIndex *pindex = mapBlockIndex[blockHash]; | ||||
const Config &config = GetConfig(); | const Config &config = GetConfig(); | ||||
// Create a node that supports avalanche. | // Create a node that supports avalanche. | ||||
auto avanode = ConnectNode(config, NODE_AVALANCHE, *peerLogic); | auto avanode = ConnectNode(config, NODE_AVALANCHE, *peerLogic); | ||||
NodeId nodeid = avanode->GetId(); | NodeId nodeid = avanode->GetId(); | ||||
BOOST_CHECK(p.addPeer(nodeid, 0)); | |||||
// Querying for random block returns false. | // Querying for random block returns false. | ||||
BOOST_CHECK(!p.isAccepted(pindex)); | BOOST_CHECK(!p.isAccepted(pindex)); | ||||
// 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(pindex)); | BOOST_CHECK(p.addBlockToReconcile(pindex)); | ||||
auto invs = AvalancheTest::getInvsForNextPoll(p); | auto invs = AvalancheTest::getInvsForNextPoll(p); | ||||
BOOST_CHECK_EQUAL(invs.size(), 1); | BOOST_CHECK_EQUAL(invs.size(), 1); | ||||
▲ Show 20 Lines • Show All 105 Lines • ▼ Show 20 Lines | BOOST_AUTO_TEST_CASE(multi_block_register) { | ||||
CBlockIndex indexA, indexB; | CBlockIndex indexA, indexB; | ||||
std::vector<AvalancheBlockUpdate> updates; | std::vector<AvalancheBlockUpdate> updates; | ||||
const Config &config = GetConfig(); | const Config &config = GetConfig(); | ||||
// Create a node that supports avalanche. | // Create a node that supports avalanche. | ||||
auto node0 = ConnectNode(config, NODE_AVALANCHE, *peerLogic); | auto node0 = ConnectNode(config, NODE_AVALANCHE, *peerLogic); | ||||
BOOST_CHECK(p.addPeer(node0->GetId(), 0)); | |||||
auto node1 = ConnectNode(config, NODE_AVALANCHE, *peerLogic); | auto node1 = ConnectNode(config, NODE_AVALANCHE, *peerLogic); | ||||
BOOST_CHECK(p.addPeer(node1->GetId(), 0)); | |||||
// Make sure the block has a hash. | // Make sure the block has a hash. | ||||
CBlock blockA = CreateAndProcessBlock({}, CScript()); | CBlock blockA = CreateAndProcessBlock({}, CScript()); | ||||
const uint256 blockHashA = blockA.GetHash(); | const uint256 blockHashA = blockA.GetHash(); | ||||
const CBlockIndex *pindexA = mapBlockIndex[blockHashA]; | const CBlockIndex *pindexA = mapBlockIndex[blockHashA]; | ||||
CBlock blockB = CreateAndProcessBlock({}, CScript()); | CBlock blockB = CreateAndProcessBlock({}, CScript()); | ||||
const uint256 blockHashB = blockB.GetHash(); | const uint256 blockHashB = blockB.GetHash(); | ||||
Show All 28 Lines | BOOST_AUTO_TEST_CASE(multi_block_register) { | ||||
// Ensure B comes before A because it has accumulated more PoW. | // Ensure B comes before A because it has accumulated more PoW. | ||||
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); | ||||
BOOST_CHECK_EQUAL(invs[1].type, MSG_BLOCK); | BOOST_CHECK_EQUAL(invs[1].type, MSG_BLOCK); | ||||
BOOST_CHECK(invs[1].hash == blockHashA); | BOOST_CHECK(invs[1].hash == blockHashA); | ||||
// Let's vote for these blocks a few times. | // Let's vote for these blocks a few times. | ||||
for (int i = 0; i < 3; i++) { | for (int i = 0; i < 3; i++) { | ||||
NodeId nodeid = AvalancheTest::getSuitableNodeToQuery(p); | |||||
AvalancheTest::runEventLoop(p); | AvalancheTest::runEventLoop(p); | ||||
BOOST_CHECK(p.registerVotes(node0->GetId(), next(resp), updates)); | BOOST_CHECK(p.registerVotes(nodeid, next(resp), updates)); | ||||
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 = AvalancheTest::getSuitableNodeToQuery(p); | |||||
AvalancheTest::runEventLoop(p); | AvalancheTest::runEventLoop(p); | ||||
BOOST_CHECK(p.registerVotes(node0->GetId(), next(resp), updates)); | BOOST_CHECK(p.registerVotes(nodeid, next(resp), updates)); | ||||
BOOST_CHECK_EQUAL(updates.size(), 0); | BOOST_CHECK_EQUAL(updates.size(), 0); | ||||
} | } | ||||
// Running two iterration of the event loop so that vote gets triggerd on A | // Running two iterration of the event loop so that vote gets triggerd on A | ||||
// and B. | // and B. | ||||
NodeId secondNodeid = AvalancheTest::getSuitableNodeToQuery(p); | NodeId firstNodeid = AvalancheTest::getSuitableNodeToQuery(p); | ||||
// NB: getSuitableNodeToQuery remove the node from the candidate list, so it | |||||
// has returned the node that will be queried second. The other one is the | |||||
// first. | |||||
NodeId firstNodeid = | |||||
(node0->GetId() == secondNodeid) ? node1->GetId() : node0->GetId(); | |||||
AvalancheTest::runEventLoop(p); | AvalancheTest::runEventLoop(p); | ||||
NodeId secondNodeid = AvalancheTest::getSuitableNodeToQuery(p); | |||||
AvalancheTest::runEventLoop(p); | AvalancheTest::runEventLoop(p); | ||||
BOOST_CHECK(firstNodeid != secondNodeid); | |||||
// Next vote will finalize block A. | // Next vote will finalize block A. | ||||
BOOST_CHECK(p.registerVotes(firstNodeid, next(resp), updates)); | BOOST_CHECK(p.registerVotes(firstNodeid, next(resp), updates)); | ||||
BOOST_CHECK_EQUAL(updates.size(), 1); | BOOST_CHECK_EQUAL(updates.size(), 1); | ||||
BOOST_CHECK(updates[0].getBlockIndex() == pindexA); | BOOST_CHECK(updates[0].getBlockIndex() == pindexA); | ||||
BOOST_CHECK_EQUAL(updates[0].getStatus(), | BOOST_CHECK_EQUAL(updates[0].getStatus(), | ||||
AvalancheBlockUpdate::Status::Finalized); | AvalancheBlockUpdate::Status::Finalized); | ||||
updates = {}; | updates = {}; | ||||
Show All 25 Lines | BOOST_AUTO_TEST_CASE(poll_and_response) { | ||||
CBlock block = CreateAndProcessBlock({}, CScript()); | CBlock block = CreateAndProcessBlock({}, CScript()); | ||||
const uint256 blockHash = block.GetHash(); | const uint256 blockHash = block.GetHash(); | ||||
const CBlockIndex *pindex = mapBlockIndex[blockHash]; | const CBlockIndex *pindex = mapBlockIndex[blockHash]; | ||||
const Config &config = GetConfig(); | const Config &config = GetConfig(); | ||||
// There is no node to query. | // There is no node to query. | ||||
BOOST_CHECK_EQUAL(AvalancheTest::getSuitableNodeToQuery(p), -1); | BOOST_CHECK_EQUAL(AvalancheTest::getSuitableNodeToQuery(p), NO_NODE); | ||||
// Create a node that supports avalanche and one that doesn't. | // Create a node that supports avalanche and one that doesn't. | ||||
auto oldnode = ConnectNode(config, NODE_NONE, *peerLogic); | auto oldnode = ConnectNode(config, NODE_NONE, *peerLogic); | ||||
auto avanode = ConnectNode(config, NODE_AVALANCHE, *peerLogic); | auto avanode = ConnectNode(config, NODE_AVALANCHE, *peerLogic); | ||||
NodeId avanodeid = avanode->GetId(); | NodeId avanodeid = avanode->GetId(); | ||||
BOOST_CHECK(p.addPeer(avanodeid, 0)); | |||||
// It returns the avalanche peer. | // It returns the avalanche peer. | ||||
BOOST_CHECK_EQUAL(AvalancheTest::getSuitableNodeToQuery(p), avanodeid); | BOOST_CHECK_EQUAL(AvalancheTest::getSuitableNodeToQuery(p), avanodeid); | ||||
// Register a block and check it is added to the list of elements to poll. | // Register a block and check it is added to the list of elements to poll. | ||||
BOOST_CHECK(p.addBlockToReconcile(pindex)); | BOOST_CHECK(p.addBlockToReconcile(pindex)); | ||||
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); | ||||
// Trigger a poll on avanode. | // Trigger a poll on avanode. | ||||
uint64_t round = AvalancheTest::getRound(p); | uint64_t round = AvalancheTest::getRound(p); | ||||
AvalancheTest::runEventLoop(p); | AvalancheTest::runEventLoop(p); | ||||
// There is no more suitable peer available, so return nothing. | // There is no more suitable peer available, so return nothing. | ||||
BOOST_CHECK_EQUAL(AvalancheTest::getSuitableNodeToQuery(p), -1); | BOOST_CHECK_EQUAL(AvalancheTest::getSuitableNodeToQuery(p), NO_NODE); | ||||
// Respond to the request. | // Respond to the request. | ||||
AvalancheResponse resp = {round, 0, {AvalancheVote(0, blockHash)}}; | AvalancheResponse resp = {round, 0, {AvalancheVote(0, blockHash)}}; | ||||
BOOST_CHECK(p.registerVotes(avanodeid, resp, updates)); | BOOST_CHECK(p.registerVotes(avanodeid, resp, updates)); | ||||
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(AvalancheTest::getSuitableNodeToQuery(p), avanodeid); | BOOST_CHECK_EQUAL(AvalancheTest::getSuitableNodeToQuery(p), avanodeid); | ||||
// Sending a response when not polled fails. | // Sending a response when not polled fails. | ||||
BOOST_CHECK(!p.registerVotes(avanodeid, next(resp), updates)); | BOOST_CHECK(!p.registerVotes(avanodeid, next(resp), updates)); | ||||
BOOST_CHECK_EQUAL(updates.size(), 0); | BOOST_CHECK_EQUAL(updates.size(), 0); | ||||
// Trigger a poll on avanode. | // Trigger a poll on avanode. | ||||
round = AvalancheTest::getRound(p); | round = AvalancheTest::getRound(p); | ||||
AvalancheTest::runEventLoop(p); | AvalancheTest::runEventLoop(p); | ||||
BOOST_CHECK_EQUAL(AvalancheTest::getSuitableNodeToQuery(p), -1); | BOOST_CHECK_EQUAL(AvalancheTest::getSuitableNodeToQuery(p), NO_NODE); | ||||
// Sending responses that do not match the request also fails. | // Sending responses that do not match the request also fails. | ||||
// 1. Too many results. | // 1. Too many results. | ||||
resp = { | resp = { | ||||
round, 0, {AvalancheVote(0, blockHash), AvalancheVote(0, blockHash)}}; | round, 0, {AvalancheVote(0, blockHash), AvalancheVote(0, blockHash)}}; | ||||
AvalancheTest::runEventLoop(p); | AvalancheTest::runEventLoop(p); | ||||
BOOST_CHECK(!p.registerVotes(avanodeid, resp, updates)); | BOOST_CHECK(!p.registerVotes(avanodeid, resp, updates)); | ||||
BOOST_CHECK_EQUAL(updates.size(), 0); | BOOST_CHECK_EQUAL(updates.size(), 0); | ||||
BOOST_CHECK_EQUAL(AvalancheTest::getSuitableNodeToQuery(p), avanodeid); | BOOST_CHECK_EQUAL(AvalancheTest::getSuitableNodeToQuery(p), avanodeid); | ||||
// 2. Not enough results. | // 2. Not enough results. | ||||
resp = {AvalancheTest::getRound(p), 0, {}}; | resp = {AvalancheTest::getRound(p), 0, {}}; | ||||
AvalancheTest::runEventLoop(p); | AvalancheTest::runEventLoop(p); | ||||
BOOST_CHECK(!p.registerVotes(avanodeid, resp, updates)); | BOOST_CHECK(!p.registerVotes(avanodeid, resp, updates)); | ||||
BOOST_CHECK_EQUAL(updates.size(), 0); | BOOST_CHECK_EQUAL(updates.size(), 0); | ||||
BOOST_CHECK_EQUAL(AvalancheTest::getSuitableNodeToQuery(p), avanodeid); | BOOST_CHECK_EQUAL(AvalancheTest::getSuitableNodeToQuery(p), avanodeid); | ||||
// 3. Do not match the poll. | // 3. Do not match the poll. | ||||
resp = {AvalancheTest::getRound(p), 0, {AvalancheVote()}}; | resp = {AvalancheTest::getRound(p), 0, {AvalancheVote()}}; | ||||
AvalancheTest::runEventLoop(p); | AvalancheTest::runEventLoop(p); | ||||
BOOST_CHECK(!p.registerVotes(avanodeid, resp, updates)); | BOOST_CHECK(!p.registerVotes(avanodeid, resp, updates)); | ||||
BOOST_CHECK_EQUAL(updates.size(), 0); | BOOST_CHECK_EQUAL(updates.size(), 0); | ||||
BOOST_CHECK_EQUAL(AvalancheTest::getSuitableNodeToQuery(p), avanodeid); | BOOST_CHECK_EQUAL(AvalancheTest::getSuitableNodeToQuery(p), avanodeid); | ||||
// 4. Invalid round count. Node is not returned to the pool. | // 4. Invalid round count. Request is not discarded. | ||||
uint64_t queryRound = AvalancheTest::getRound(p); | uint64_t queryRound = AvalancheTest::getRound(p); | ||||
AvalancheTest::runEventLoop(p); | AvalancheTest::runEventLoop(p); | ||||
resp = {queryRound + 1, 0, {AvalancheVote()}}; | resp = {queryRound + 1, 0, {AvalancheVote()}}; | ||||
BOOST_CHECK(!p.registerVotes(avanodeid, resp, updates)); | BOOST_CHECK(!p.registerVotes(avanodeid, resp, updates)); | ||||
BOOST_CHECK_EQUAL(updates.size(), 0); | BOOST_CHECK_EQUAL(updates.size(), 0); | ||||
BOOST_CHECK_EQUAL(AvalancheTest::getSuitableNodeToQuery(p), -1); | |||||
resp = {queryRound - 1, 0, {AvalancheVote()}}; | resp = {queryRound - 1, 0, {AvalancheVote()}}; | ||||
BOOST_CHECK(!p.registerVotes(avanodeid, resp, updates)); | BOOST_CHECK(!p.registerVotes(avanodeid, resp, updates)); | ||||
BOOST_CHECK_EQUAL(updates.size(), 0); | BOOST_CHECK_EQUAL(updates.size(), 0); | ||||
BOOST_CHECK_EQUAL(AvalancheTest::getSuitableNodeToQuery(p), -1); | |||||
// 5. Making request for invalid nodes do not work. Node is not returned to | // 5. Making request for invalid nodes do not work. Request is not | ||||
// the pool. | // discarded. | ||||
resp = {queryRound, 0, {AvalancheVote(0, blockHash)}}; | resp = {queryRound, 0, {AvalancheVote(0, blockHash)}}; | ||||
BOOST_CHECK(!p.registerVotes(avanodeid + 1234, resp, updates)); | BOOST_CHECK(!p.registerVotes(avanodeid + 1234, resp, updates)); | ||||
BOOST_CHECK_EQUAL(updates.size(), 0); | BOOST_CHECK_EQUAL(updates.size(), 0); | ||||
BOOST_CHECK_EQUAL(AvalancheTest::getSuitableNodeToQuery(p), -1); | |||||
// Proper response gets processed and avanode is available again. | // Proper response gets processed and avanode is available again. | ||||
resp = {queryRound, 0, {AvalancheVote(0, blockHash)}}; | resp = {queryRound, 0, {AvalancheVote(0, blockHash)}}; | ||||
BOOST_CHECK(p.registerVotes(avanodeid, resp, updates)); | BOOST_CHECK(p.registerVotes(avanodeid, resp, updates)); | ||||
BOOST_CHECK_EQUAL(updates.size(), 0); | BOOST_CHECK_EQUAL(updates.size(), 0); | ||||
BOOST_CHECK_EQUAL(AvalancheTest::getSuitableNodeToQuery(p), avanodeid); | BOOST_CHECK_EQUAL(AvalancheTest::getSuitableNodeToQuery(p), avanodeid); | ||||
// Out of order response are rejected. | // Out of order response are rejected. | ||||
Show All 10 Lines | BOOST_AUTO_TEST_CASE(poll_and_response) { | ||||
BOOST_CHECK_EQUAL(updates.size(), 0); | BOOST_CHECK_EQUAL(updates.size(), 0); | ||||
BOOST_CHECK_EQUAL(AvalancheTest::getSuitableNodeToQuery(p), avanodeid); | BOOST_CHECK_EQUAL(AvalancheTest::getSuitableNodeToQuery(p), avanodeid); | ||||
// But they are accepted in order. | // But they are accepted in order. | ||||
resp = {AvalancheTest::getRound(p), | resp = {AvalancheTest::getRound(p), | ||||
0, | 0, | ||||
{AvalancheVote(0, blockHash2), AvalancheVote(0, blockHash)}}; | {AvalancheVote(0, blockHash2), AvalancheVote(0, blockHash)}}; | ||||
AvalancheTest::runEventLoop(p); | AvalancheTest::runEventLoop(p); | ||||
BOOST_CHECK(p.registerVotes(avanode->GetId(), resp, updates)); | BOOST_CHECK(p.registerVotes(avanodeid, resp, updates)); | ||||
BOOST_CHECK_EQUAL(updates.size(), 0); | BOOST_CHECK_EQUAL(updates.size(), 0); | ||||
BOOST_CHECK_EQUAL(AvalancheTest::getSuitableNodeToQuery(p), avanodeid); | BOOST_CHECK_EQUAL(AvalancheTest::getSuitableNodeToQuery(p), avanodeid); | ||||
// When a block is marked invalid, stop polling. | // When a block is marked invalid, stop polling. | ||||
pindex2->nStatus = pindex2->nStatus.withFailed(); | pindex2->nStatus = pindex2->nStatus.withFailed(); | ||||
resp = {AvalancheTest::getRound(p), 0, {AvalancheVote(0, blockHash)}}; | resp = {AvalancheTest::getRound(p), 0, {AvalancheVote(0, blockHash)}}; | ||||
AvalancheTest::runEventLoop(p); | AvalancheTest::runEventLoop(p); | ||||
BOOST_CHECK(p.registerVotes(avanodeid, resp, updates)); | BOOST_CHECK(p.registerVotes(avanodeid, resp, updates)); | ||||
Show All 25 Lines | BOOST_AUTO_TEST_CASE(event_loop) { | ||||
std::thread schedulerThread(std::bind(&CScheduler::serviceQueue, &s)); | std::thread schedulerThread(std::bind(&CScheduler::serviceQueue, &s)); | ||||
// Create a node and a block to query. | // Create a node and a block to query. | ||||
const Config &config = GetConfig(); | const Config &config = GetConfig(); | ||||
// Create a node that supports avalanche. | // Create a node that supports avalanche. | ||||
auto avanode = ConnectNode(config, NODE_AVALANCHE, *peerLogic); | auto avanode = ConnectNode(config, NODE_AVALANCHE, *peerLogic); | ||||
NodeId nodeid = avanode->GetId(); | NodeId nodeid = avanode->GetId(); | ||||
BOOST_CHECK(p.addPeer(nodeid, 0)); | |||||
// There is no query in flight at the moment. | // There is no query in flight at the moment. | ||||
BOOST_CHECK_EQUAL(AvalancheTest::getSuitableNodeToQuery(p), nodeid); | BOOST_CHECK_EQUAL(AvalancheTest::getSuitableNodeToQuery(p), nodeid); | ||||
// 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 = AvalancheTest::getRound(p); | uint64_t queryRound = AvalancheTest::getRound(p); | ||||
BOOST_CHECK(p.addBlockToReconcile(pindex)); | BOOST_CHECK(p.addBlockToReconcile(pindex)); | ||||
▲ Show 20 Lines • Show All 70 Lines • Show Last 20 Lines |