Page MenuHomePhabricator

D2046.id5758.diff
No OneTemporary

D2046.id5758.diff

diff --git a/src/avalanche.h b/src/avalanche.h
--- a/src/avalanche.h
+++ b/src/avalanche.h
@@ -149,11 +149,32 @@
class AvalancheProcessor {
private:
+ CConnman *connman;
+
/**
* Blocks to run avalanche on.
*/
RWCollection<std::map<uint256, VoteRecord>> vote_records;
+ /**
+ * Keep track of peers and quesries sent.
+ */
+ struct RequestReccord {
+ private:
+ int64_t timestamp;
+ std::vector<CInv> invs;
+
+ public:
+ RequestReccord(int64_t timestampIn, std::vector<CInv> invIn)
+ : timestamp(timestampIn), invs(std::move(invIn)) {}
+
+ int64_t GetTimestamp() const { return timestamp; }
+ const std::vector<CInv> &GetInvs() const { return invs; }
+ };
+
+ RWCollection<std::set<NodeId>> nodeids;
+ RWCollection<std::map<NodeId, RequestReccord>> queries;
+
/**
* Start stop machinery.
*/
@@ -164,13 +185,14 @@
std::condition_variable cond_running;
public:
- AvalancheProcessor() : stopRequest(false), running(false) {}
+ AvalancheProcessor(CConnman *connmanIn)
+ : connman(connmanIn), stopRequest(false), running(false) {}
~AvalancheProcessor() { stopEventLoop(); }
bool addBlockToReconcile(const CBlockIndex *pindex);
bool isAccepted(const CBlockIndex *pindex) const;
- bool registerVotes(const AvalancheResponse &response,
+ bool registerVotes(NodeId nodeid, const AvalancheResponse &response,
std::vector<uint256> &accepted,
std::vector<uint256> &rejected);
@@ -180,6 +202,7 @@
private:
void runEventLoop();
std::vector<CInv> getInvsForNextPoll() const;
+ NodeId getSuitableNodeToQuery();
friend struct AvalancheTest;
};
diff --git a/src/avalanche.cpp b/src/avalanche.cpp
--- a/src/avalanche.cpp
+++ b/src/avalanche.cpp
@@ -34,38 +34,76 @@
return false;
}
-bool AvalancheProcessor::registerVotes(const AvalancheResponse &response,
+bool AvalancheProcessor::registerVotes(NodeId nodeid,
+ const AvalancheResponse &response,
std::vector<uint256> &accepted,
std::vector<uint256> &rejected) {
- const std::vector<AvalancheVote> &votes = response.GetVotes();
+ RequestReccord r{0, {}};
- // Register votes.
- auto w = vote_records.getWriteView();
- for (auto &v : votes) {
- auto it = w->find(v.GetHash());
+ {
+ // Check that the query exists.
+ auto w = queries.getWriteView();
+ auto it = w->find(nodeid);
if (it == w.end()) {
- // We are not voting on that item anymore.
- continue;
+ // NB: The request may be old, so we don't increase banscore.
+ return false;
}
- auto &vr = it->second;
- vr.registerVote(v.IsValid());
- if (!vr.hasFinalized()) {
- // This item has note been finalized, so we have nothing more to do.
- continue;
- }
+ r = std::move(it->second);
+ w->erase(it);
+ }
- // We just finalized a vote. If it is valid, then let the caller know.
- // Either way, remove the item from the map.
- if (vr.isValid()) {
- accepted.push_back(v.GetHash());
- } else {
- rejected.push_back(v.GetHash());
+ // Verify that the request and the vote are consistent.
+ const std::vector<CInv> &invs = r.GetInvs();
+ const std::vector<AvalancheVote> &votes = response.GetVotes();
+ size_t size = invs.size();
+ if (votes.size() != size) {
+ // TODO: increase banscore for inconsistent response.
+ // NB: This isn't timeout but actually node misbehaving.
+ return false;
+ }
+
+ for (size_t i = 0; i < size; i++) {
+ if (invs[i].hash != votes[i].GetHash()) {
+ // TODO: increase banscore for inconsistent response.
+ // NB: This isn't timeout but actually node misbehaving.
+ return false;
}
+ }
- w->erase(it);
+ {
+ // Register votes.
+ auto w = vote_records.getWriteView();
+ for (auto &v : votes) {
+ auto it = w->find(v.GetHash());
+ if (it == w.end()) {
+ // We are not voting on that item anymore.
+ continue;
+ }
+
+ auto &vr = it->second;
+ vr.registerVote(v.IsValid());
+ if (!vr.hasFinalized()) {
+ // This item has note been finalized, so we have nothing more to
+ // do.
+ continue;
+ }
+
+ // We just finalized a vote. If it is valid, then let the caller
+ // know. Either way, remove the item from the map.
+ if (vr.isValid()) {
+ accepted.push_back(v.GetHash());
+ } else {
+ rejected.push_back(v.GetHash());
+ }
+
+ w->erase(it);
+ }
}
+ // Put the node back in the list of queriable nodes.
+ auto w = nodeids.getWriteView();
+ w->insert(nodeid);
return true;
}
@@ -92,6 +130,7 @@
// Start the event loop.
scheduler.scheduleEvery(
[this]() -> bool {
+ runEventLoop();
if (!stopRequest) {
return true;
}
@@ -148,6 +187,65 @@
return invs;
}
+NodeId AvalancheProcessor::getSuitableNodeToQuery() {
+ auto w = nodeids.getWriteView();
+ if (w->empty()) {
+ auto r = queries.getReadView();
+
+ // We don't have any candidate node, so let's try to find some.
+ connman->ForEachNode([&w, &r](CNode *pnode) {
+ // If this node doesn't support avalanche, we remove.
+ if (!(pnode->nServices & NODE_AVALANCHE)) {
+ return;
+ }
+
+ // if we have a request in flight for that node.
+ if (r->find(pnode->GetId()) != r.end()) {
+ return;
+ }
+
+ w->insert(pnode->GetId());
+ });
+ }
+
+ // We don't have any suitable candidate.
+ if (w->empty()) {
+ return -1;
+ }
+
+ auto it = w.begin();
+ NodeId nodeid = *it;
+ w->erase(it);
+
+ return nodeid;
+}
+
void AvalancheProcessor::runEventLoop() {
std::vector<CInv> invs = getInvsForNextPoll();
+ if (invs.empty()) {
+ // If there are no invs to poll, we are done.
+ return;
+ }
+
+ NodeId nodeid = getSuitableNodeToQuery();
+
+ /**
+ * If we lost contact to that node, then we remive it from nodeids, but
+ * never add the request to queries, which ensures bad nodes get cleaned up
+ * over time.
+ */
+ connman->ForNode(nodeid, [this, &invs](CNode *pfrom) {
+ {
+ // Register the query.
+ queries.getWriteView()->emplace(
+ pfrom->GetId(), RequestReccord(GetAdjustedTime(), invs));
+ }
+
+ // Send the query to the node.
+ connman->PushMessage(
+ pfrom,
+ CNetMsgMaker(pfrom->GetSendVersion())
+ .Make(NetMsgType::AVA_POLL, AvalanchePoll(std::move(invs))));
+ return true;
+ });
}
diff --git a/src/net.h b/src/net.h
--- a/src/net.h
+++ b/src/net.h
@@ -444,6 +444,7 @@
friend struct CConnmanTest;
};
+
extern std::unique_ptr<CConnman> g_connman;
void Discover();
void StartMapPort();
diff --git a/src/protocol.h b/src/protocol.h
--- a/src/protocol.h
+++ b/src/protocol.h
@@ -252,6 +252,16 @@
* @since protocol version 70014 as described by BIP 152
*/
extern const char *BLOCKTXN;
+/**
+ * Contains a AvalanchePoll.
+ * Peer should respond with "ava_vote" message.
+ */
+extern const char *AVA_POLL;
+/**
+ * Contains a vector of AvalancheVote.
+ * Sent in response to a "ava_poll" message.
+ */
+extern const char *AVA_VOTE;
/**
* Indicate if the message is used to transmit the content of a block.
@@ -294,6 +304,9 @@
// TODO: remove (free up) the NODE_BITCOIN_CASH service bit once no longer
// needed.
NODE_BITCOIN_CASH = (1 << 5),
+ // NODE_AVALANCHE means the node supports Bitcoin Cash's avalanche
+ // preconsensus mechanism.
+ NODE_AVALANCHE = (1 << 6),
// Bits 24-31 are reserved for temporary experiments. Just pick a bit that
// isn't getting used, or one not being used much, and notify the
diff --git a/src/protocol.cpp b/src/protocol.cpp
--- a/src/protocol.cpp
+++ b/src/protocol.cpp
@@ -41,6 +41,8 @@
const char *CMPCTBLOCK = "cmpctblock";
const char *GETBLOCKTXN = "getblocktxn";
const char *BLOCKTXN = "blocktxn";
+const char *AVA_POLL = "ava_poll";
+const char *AVA_VOTE = "ava_vote";
bool IsBlockLike(const std::string &strCommand) {
return strCommand == NetMsgType::BLOCK ||
diff --git a/src/test/avalanche_tests.cpp b/src/test/avalanche_tests.cpp
--- a/src/test/avalanche_tests.cpp
+++ b/src/test/avalanche_tests.cpp
@@ -3,6 +3,9 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "avalanche.h"
+
+#include "config.h"
+#include "net_processing.h"
#include "scheduler.h"
#include "test/test_bitcoin.h"
@@ -10,12 +13,18 @@
#include <boost/test/unit_test.hpp>
struct AvalancheTest {
+ static void runEventLoop(AvalancheProcessor &p) { p.runEventLoop(); }
+
static std::vector<CInv> getInvsForNextPoll(const AvalancheProcessor &p) {
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) \
vr.registerVote(vote); \
@@ -73,11 +82,42 @@
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) {
- AvalancheProcessor p;
+ AvalancheProcessor p(g_connman.get());
CBlockIndex index;
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.
static const uint256 blockHash(uint256S(
@@ -100,7 +140,8 @@
// Let's vote for this block a few times.
AvalancheResponse resp{{AvalancheVote(blockHash, 0)}};
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_EQUAL(accepted.size(), 0);
BOOST_CHECK_EQUAL(rejected.size(), 0);
@@ -108,7 +149,8 @@
// Now it is accepeted, but we can vote for it numerous times.
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_EQUAL(accepted.size(), 0);
BOOST_CHECK_EQUAL(rejected.size(), 0);
@@ -121,7 +163,8 @@
BOOST_CHECK(invs[0].hash == blockHash);
// 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(rejected.size(), 0);
BOOST_CHECK(accepted[0] == blockHash);
@@ -141,7 +184,8 @@
// Only 3 here as we don't need to flip state.
resp = {{AvalancheVote(blockHash, 1)}};
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_EQUAL(accepted.size(), 0);
BOOST_CHECK_EQUAL(rejected.size(), 0);
@@ -149,7 +193,8 @@
// Now it is rejected, but we can vote for it numerous times.
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_EQUAL(accepted.size(), 0);
BOOST_CHECK_EQUAL(rejected.size(), 0);
@@ -162,7 +207,8 @@
BOOST_CHECK(invs[0].hash == blockHash);
// 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_EQUAL(accepted.size(), 0);
BOOST_CHECK_EQUAL(rejected.size(), 1);
@@ -177,13 +223,20 @@
BOOST_CHECK(p.addBlockToReconcile(&index));
BOOST_CHECK(!p.addBlockToReconcile(&index));
BOOST_CHECK(!p.isAccepted(&index));
+
+ CConnmanTest::ClearNodes();
}
BOOST_AUTO_TEST_CASE(multi_block_register) {
- AvalancheProcessor p;
+ AvalancheProcessor p(g_connman.get());
CBlockIndex indexA, indexB;
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.
static const uint256 blockHashA(uint256S(
@@ -204,13 +257,15 @@
BOOST_CHECK_EQUAL(invs[0].type, MSG_BLOCK);
BOOST_CHECK(invs[0].hash == blockHashA);
- AvalancheResponse resp{
- {AvalancheVote(blockHashA, 0), AvalancheVote(blockHashB, 0)}};
- p.registerVotes(resp, accepted, rejected);
+ AvalancheTest::runEventLoop(p);
+ BOOST_CHECK(p.registerVotes(
+ node0->GetId(), {{AvalancheVote(blockHashA, 0)}}, accepted, rejected));
BOOST_CHECK_EQUAL(accepted.size(), 0);
BOOST_CHECK_EQUAL(rejected.size(), 0);
// Start voing on block B after one vote.
+ AvalancheResponse resp{
+ {AvalancheVote(blockHashA, 0), AvalancheVote(blockHashB, 0)}};
BOOST_CHECK(p.addBlockToReconcile(&indexB));
invs = AvalancheTest::getInvsForNextPoll(p);
BOOST_CHECK_EQUAL(invs.size(), 2);
@@ -221,13 +276,19 @@
// Now it is rejected, but we can vote for it numerous times.
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(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.
- p.registerVotes(resp, accepted, rejected);
+ BOOST_CHECK(p.registerVotes(node1->GetId(), resp, accepted, rejected));
BOOST_CHECK_EQUAL(accepted.size(), 1);
BOOST_CHECK_EQUAL(rejected.size(), 0);
BOOST_CHECK(accepted[0] == blockHashA);
@@ -240,7 +301,7 @@
BOOST_CHECK(invs[0].hash == blockHashB);
// 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(rejected.size(), 0);
BOOST_CHECK(accepted[0] == blockHashB);
@@ -249,59 +310,190 @@
// There is nothing left to vote on.
invs = AvalancheTest::getInvsForNextPoll(p);
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(blockHash, 0)}};
+ 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, ti 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(blockHash, 0), AvalancheVote(blockHash, 0)}};
+ 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(uint256(), 0)}};
+ 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(blockHash, 0)}};
+ 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) {
- AvalancheProcessor p;
- CScheduler scheduler;
+ AvalancheProcessor p(g_connman.get());
+ CScheduler s;
// 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).
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.
- BOOST_CHECK(!p.startEventLoop(scheduler));
+ BOOST_CHECK(!p.startEventLoop(s));
// Start the scheduler thread.
- std::thread schedulerThread(
- std::bind(&CScheduler::serviceQueue, &scheduler));
+ std::thread schedulerThread(std::bind(&CScheduler::serviceQueue, &s));
+
+ // 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.
BOOST_CHECK(p.stopEventLoop());
// 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.
BOOST_CHECK(!p.stopEventLoop());
// Wait for the scheduler to stop.
- scheduler.stop(true);
+ s.stop(true);
schedulerThread.join();
+
+ CConnmanTest::ClearNodes();
}
BOOST_AUTO_TEST_CASE(destructor) {
- CScheduler scheduler;
+ CScheduler s;
boost::chrono::system_clock::time_point start, stop;
// Start the scheduler thread.
- std::thread schedulerThread(
- std::bind(&CScheduler::serviceQueue, &scheduler));
+ std::thread schedulerThread(std::bind(&CScheduler::serviceQueue, &s));
{
- AvalancheProcessor p;
- BOOST_CHECK(p.startEventLoop(scheduler));
- BOOST_CHECK_EQUAL(scheduler.getQueueInfo(start, stop), 1);
+ AvalancheProcessor p(g_connman.get());
+ BOOST_CHECK(p.startEventLoop(s));
+ BOOST_CHECK_EQUAL(s.getQueueInfo(start, stop), 1);
}
- // We don't have any task scheduled anymore taht avalanche is destroyed.
- BOOST_CHECK_EQUAL(scheduler.getQueueInfo(start, stop), 0);
+ // We don't have any task scheduled anymore that avalanche is destroyed.
+ BOOST_CHECK_EQUAL(s.getQueueInfo(start, stop), 0);
// Wait for the scheduler to stop.
- scheduler.stop(true);
+ s.stop(true);
schedulerThread.join();
}

File Metadata

Mime Type
text/plain
Expires
Fri, Nov 22, 10:02 (20 h, 49 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
4559223
Default Alt Text
D2046.id5758.diff (24 KB)

Event Timeline