Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F10615252
D2046.id5758.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
24 KB
Subscribers
None
D2046.id5758.diff
View Options
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
Details
Attached
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)
Attached To
D2046: [avalanche] Implement the challenge/response protocol
Event Timeline
Log In to Comment