Bitcoin ABC  0.22.12
P2P Digital Currency
processor_tests.cpp
Go to the documentation of this file.
1 // Copyright (c) 2018-2020 The Bitcoin developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 
5 #include <avalanche/processor.h>
6 
10 #include <chain.h>
11 #include <config.h>
12 #include <net_processing.h> // For PeerLogicValidation
13 #include <util/time.h>
14 // D6970 moved LookupBlockIndex from chain.h to validation.h TODO: remove this
15 // when LookupBlockIndex is refactored out of validation
16 #include <validation.h>
17 
18 #include <test/util/setup_common.h>
19 
20 #include <boost/test/unit_test.hpp>
21 
22 using namespace avalanche;
23 
24 namespace avalanche {
25 namespace {
26  struct AvalancheTest {
27  static void runEventLoop(avalanche::Processor &p) { p.runEventLoop(); }
28 
29  static std::vector<CInv> getInvsForNextPoll(Processor &p) {
30  return p.getInvsForNextPoll(false);
31  }
32 
33  static NodeId getSuitableNodeToQuery(Processor &p) {
34  return p.getSuitableNodeToQuery();
35  }
36 
37  static PeerManager &getPeerManager(Processor &p) {
39  return *p.peerManager;
40  }
41 
42  static uint64_t getRound(const Processor &p) { return p.round; }
43  };
44 } // namespace
45 } // namespace avalanche
46 
47 namespace {
48 struct CConnmanTest : public CConnman {
49  using CConnman::CConnman;
50  void AddNode(CNode &node) {
51  LOCK(cs_vNodes);
52  vNodes.push_back(&node);
53  }
54  void ClearNodes() {
55  LOCK(cs_vNodes);
56  for (CNode *node : vNodes) {
57  delete node;
58  }
59  vNodes.clear();
60  }
61 };
62 
63 CService ip(uint32_t i) {
64  struct in_addr s;
65  s.s_addr = i;
66  return CService(CNetAddr(s), Params().GetDefaultPort());
67 }
68 
69 struct AvalancheTestingSetup : public TestChain100Setup {
70  const Config &config;
71  CConnmanTest *m_connman;
72 
73  std::unique_ptr<Processor> m_processor;
74 
75  CKey masterpriv;
76 
77  AvalancheTestingSetup()
78  : TestChain100Setup(), config(GetConfig()), masterpriv() {
79  // Deterministic randomness for tests.
80  auto connman = std::make_unique<CConnmanTest>(config, 0x1337, 0x1337);
81  m_connman = connman.get();
82  m_node.connman = std::move(connman);
83  m_node.peer_logic = std::make_unique<PeerLogicValidation>(
84  *m_connman, m_node.banman.get(), *m_node.scheduler,
86  m_node.chain = interfaces::MakeChain(m_node, config.GetChainParams());
87 
88  // Get the processor ready.
89  m_processor =
90  std::make_unique<Processor>(*m_node.chain, m_node.connman.get());
91 
92  // The master private key we delegate to.
93  masterpriv.MakeNewKey(true);
94  }
95 
96  ~AvalancheTestingSetup() { m_connman->ClearNodes(); }
97 
98  CNode *ConnectNode(ServiceFlags nServices) {
99  static NodeId id = 0;
100 
101  CAddress addr(ip(GetRandInt(0xffffffff)), NODE_NONE);
102  auto node =
103  new CNode(id++, ServiceFlags(NODE_NETWORK), 0, INVALID_SOCKET, addr,
104  0, 0, 0, CAddress(), "", ConnectionType::OUTBOUND);
106  node->nServices = nServices;
107  m_node.peer_logic->InitializeNode(config, node);
108  node->nVersion = 1;
109  node->fSuccessfullyConnected = true;
110 
111  m_connman->AddNode(*node);
112  return node;
113  }
114 
115  size_t next_coinbase = 0;
116  Proof GetProof() {
117  size_t current_coinbase = next_coinbase++;
118  const CTransaction &coinbase = *m_coinbase_txns[current_coinbase];
119  ProofBuilder pb(0, 0, masterpriv.GetPubKey());
120  BOOST_CHECK(pb.addUTXO(COutPoint(coinbase.GetId(), 0),
121  coinbase.vout[0].nValue, current_coinbase + 1,
122  true, coinbaseKey));
123  return pb.build();
124  }
125 
126  bool addNode(NodeId nodeid) {
127  Proof proof = GetProof();
128  return m_processor->addNode(nodeid, proof,
129  DelegationBuilder(proof).build());
130  }
131 
132  std::array<CNode *, 8> ConnectNodes() {
133  PeerManager &pm = getPeerManager();
134  Proof proof = GetProof();
135  Delegation dg = DelegationBuilder(proof).build();
136 
137  std::array<CNode *, 8> nodes;
138  for (CNode *&n : nodes) {
139  n = ConnectNode(NODE_AVALANCHE);
140  BOOST_CHECK(pm.addNode(n->GetId(), proof, dg));
141  }
142 
143  return nodes;
144  }
145 
146  void runEventLoop() { AvalancheTest::runEventLoop(*m_processor); }
147 
148  NodeId getSuitableNodeToQuery() {
149  return AvalancheTest::getSuitableNodeToQuery(*m_processor);
150  }
151 
152  std::vector<CInv> getInvsForNextPoll() {
153  return AvalancheTest::getInvsForNextPoll(*m_processor);
154  }
155 
156  PeerManager &getPeerManager() {
157  return AvalancheTest::getPeerManager(*m_processor);
158  }
159 
160  uint64_t getRound() const { return AvalancheTest::getRound(*m_processor); }
161 };
162 } // namespace
163 
164 BOOST_FIXTURE_TEST_SUITE(processor_tests, AvalancheTestingSetup)
165 
166 #define REGISTER_VOTE_AND_CHECK(vr, vote, state, finalized, confidence) \
167  vr.registerVote(NO_NODE, vote); \
168  BOOST_CHECK_EQUAL(vr.isAccepted(), state); \
169  BOOST_CHECK_EQUAL(vr.hasFinalized(), finalized); \
170  BOOST_CHECK_EQUAL(vr.getConfidence(), confidence);
171 
172 BOOST_AUTO_TEST_CASE(vote_record) {
173  VoteRecord vraccepted(true);
174 
175  // Check initial state.
176  BOOST_CHECK_EQUAL(vraccepted.isAccepted(), true);
177  BOOST_CHECK_EQUAL(vraccepted.hasFinalized(), false);
178  BOOST_CHECK_EQUAL(vraccepted.getConfidence(), 0);
179 
180  VoteRecord vr(false);
181 
182  // Check initial state.
183  BOOST_CHECK_EQUAL(vr.isAccepted(), false);
184  BOOST_CHECK_EQUAL(vr.hasFinalized(), false);
185  BOOST_CHECK_EQUAL(vr.getConfidence(), 0);
186 
187  // We need to register 6 positive votes before we start counting.
188  for (int i = 0; i < 6; i++) {
189  REGISTER_VOTE_AND_CHECK(vr, 0, false, false, 0);
190  }
191 
192  // Next vote will flip state, and confidence will increase as long as we
193  // vote yes.
194  REGISTER_VOTE_AND_CHECK(vr, 0, true, false, 0);
195 
196  // A single neutral vote do not change anything.
197  REGISTER_VOTE_AND_CHECK(vr, -1, true, false, 1);
198  for (int i = 2; i < 8; i++) {
199  REGISTER_VOTE_AND_CHECK(vr, 0, true, false, i);
200  }
201 
202  // Two neutral votes will stall progress.
203  REGISTER_VOTE_AND_CHECK(vr, -1, true, false, 7);
204  REGISTER_VOTE_AND_CHECK(vr, -1, true, false, 7);
205  for (int i = 2; i < 8; i++) {
206  REGISTER_VOTE_AND_CHECK(vr, 0, true, false, 7);
207  }
208 
209  // Now confidence will increase as long as we vote yes.
210  for (int i = 8; i < AVALANCHE_FINALIZATION_SCORE; i++) {
211  REGISTER_VOTE_AND_CHECK(vr, 0, true, false, i);
212  }
213 
214  // The next vote will finalize the decision.
215  REGISTER_VOTE_AND_CHECK(vr, 1, true, true, AVALANCHE_FINALIZATION_SCORE);
216 
217  // Now that we have two no votes, confidence stop increasing.
218  for (int i = 0; i < 5; i++) {
219  REGISTER_VOTE_AND_CHECK(vr, 1, true, true,
220  AVALANCHE_FINALIZATION_SCORE);
221  }
222 
223  // Next vote will flip state, and confidence will increase as long as we
224  // vote no.
225  REGISTER_VOTE_AND_CHECK(vr, 1, false, false, 0);
226 
227  // A single neutral vote do not change anything.
228  REGISTER_VOTE_AND_CHECK(vr, -1, false, false, 1);
229  for (int i = 2; i < 8; i++) {
230  REGISTER_VOTE_AND_CHECK(vr, 1, false, false, i);
231  }
232 
233  // Two neutral votes will stall progress.
234  REGISTER_VOTE_AND_CHECK(vr, -1, false, false, 7);
235  REGISTER_VOTE_AND_CHECK(vr, -1, false, false, 7);
236  for (int i = 2; i < 8; i++) {
237  REGISTER_VOTE_AND_CHECK(vr, 1, false, false, 7);
238  }
239 
240  // Now confidence will increase as long as we vote no.
241  for (int i = 8; i < AVALANCHE_FINALIZATION_SCORE; i++) {
242  REGISTER_VOTE_AND_CHECK(vr, 1, false, false, i);
243  }
244 
245  // The next vote will finalize the decision.
246  REGISTER_VOTE_AND_CHECK(vr, 0, false, true, AVALANCHE_FINALIZATION_SCORE);
247 
248  // Check that inflight accounting work as expected.
249  VoteRecord vrinflight(false);
250  for (int i = 0; i < 2 * AVALANCHE_MAX_INFLIGHT_POLL; i++) {
251  bool shouldPoll = vrinflight.shouldPoll();
252  BOOST_CHECK_EQUAL(shouldPoll, i < AVALANCHE_MAX_INFLIGHT_POLL);
253  BOOST_CHECK_EQUAL(vrinflight.registerPoll(), shouldPoll);
254  }
255 
256  // Clear various number of inflight requests and check everything behaves as
257  // expected.
258  for (int i = 1; i < AVALANCHE_MAX_INFLIGHT_POLL; i++) {
259  vrinflight.clearInflightRequest(i);
260  BOOST_CHECK(vrinflight.shouldPoll());
261 
262  for (int j = 1; j < i; j++) {
263  BOOST_CHECK(vrinflight.registerPoll());
264  BOOST_CHECK(vrinflight.shouldPoll());
265  }
266 
267  BOOST_CHECK(vrinflight.registerPoll());
268  BOOST_CHECK(!vrinflight.shouldPoll());
269  }
270 }
271 
272 BOOST_AUTO_TEST_CASE(block_update) {
273  CBlockIndex index;
274  CBlockIndex *pindex = &index;
275 
276  std::set<BlockUpdate::Status> status{
277  BlockUpdate::Status::Invalid,
278  BlockUpdate::Status::Rejected,
279  BlockUpdate::Status::Accepted,
280  BlockUpdate::Status::Finalized,
281  };
282 
283  for (auto s : status) {
284  BlockUpdate abu(pindex, s);
285  BOOST_CHECK(abu.getBlockIndex() == pindex);
286  BOOST_CHECK_EQUAL(abu.getStatus(), s);
287  }
288 }
289 
290 namespace {
291 Response next(Response &r) {
292  auto copy = r;
293  r = {r.getRound() + 1, r.getCooldown(), r.GetVotes()};
294  return copy;
295 }
296 } // namespace
297 
298 BOOST_AUTO_TEST_CASE(block_register) {
299  std::vector<BlockUpdate> updates;
300 
301  CBlock block = CreateAndProcessBlock({}, CScript());
302  const BlockHash blockHash = block.GetHash();
303  const CBlockIndex *pindex;
304  {
305  LOCK(cs_main);
306  pindex = LookupBlockIndex(blockHash);
307  }
308 
309  // Create nodes that supports avalanche.
310  auto avanodes = ConnectNodes();
311 
312  // Querying for random block returns false.
313  BOOST_CHECK(!m_processor->isAccepted(pindex));
314 
315  // Add a new block. Check it is added to the polls.
316  BOOST_CHECK(m_processor->addBlockToReconcile(pindex));
317  auto invs = getInvsForNextPoll();
318  BOOST_CHECK_EQUAL(invs.size(), 1);
319  BOOST_CHECK_EQUAL(invs[0].type, MSG_BLOCK);
320  BOOST_CHECK(invs[0].hash == blockHash);
321 
322  // Newly added blocks' state reflect the blockchain.
323  BOOST_CHECK(m_processor->isAccepted(pindex));
324 
325  int nextNodeIndex = 0;
326  auto registerNewVote = [&](const Response &resp) {
327  runEventLoop();
328  auto nodeid = avanodes[nextNodeIndex++ % avanodes.size()]->GetId();
329  BOOST_CHECK(m_processor->registerVotes(nodeid, resp, updates));
330  };
331 
332  // Let's vote for this block a few times.
333  Response resp{0, 0, {Vote(0, blockHash)}};
334  for (int i = 0; i < 6; i++) {
335  registerNewVote(next(resp));
336  BOOST_CHECK(m_processor->isAccepted(pindex));
337  BOOST_CHECK_EQUAL(m_processor->getConfidence(pindex), 0);
338  BOOST_CHECK_EQUAL(updates.size(), 0);
339  }
340 
341  // A single neutral vote do not change anything.
342  resp = {getRound(), 0, {Vote(-1, blockHash)}};
343  registerNewVote(next(resp));
344  BOOST_CHECK(m_processor->isAccepted(pindex));
345  BOOST_CHECK_EQUAL(m_processor->getConfidence(pindex), 0);
346  BOOST_CHECK_EQUAL(updates.size(), 0);
347 
348  resp = {getRound(), 0, {Vote(0, blockHash)}};
349  for (int i = 1; i < 7; i++) {
350  registerNewVote(next(resp));
351  BOOST_CHECK(m_processor->isAccepted(pindex));
352  BOOST_CHECK_EQUAL(m_processor->getConfidence(pindex), i);
353  BOOST_CHECK_EQUAL(updates.size(), 0);
354  }
355 
356  // Two neutral votes will stall progress.
357  resp = {getRound(), 0, {Vote(-1, blockHash)}};
358  registerNewVote(next(resp));
359  BOOST_CHECK(m_processor->isAccepted(pindex));
360  BOOST_CHECK_EQUAL(m_processor->getConfidence(pindex), 6);
361  BOOST_CHECK_EQUAL(updates.size(), 0);
362  registerNewVote(next(resp));
363  BOOST_CHECK(m_processor->isAccepted(pindex));
364  BOOST_CHECK_EQUAL(m_processor->getConfidence(pindex), 6);
365  BOOST_CHECK_EQUAL(updates.size(), 0);
366 
367  resp = {getRound(), 0, {Vote(0, blockHash)}};
368  for (int i = 2; i < 8; i++) {
369  registerNewVote(next(resp));
370  BOOST_CHECK(m_processor->isAccepted(pindex));
371  BOOST_CHECK_EQUAL(m_processor->getConfidence(pindex), 6);
372  BOOST_CHECK_EQUAL(updates.size(), 0);
373  }
374 
375  // We vote for it numerous times to finalize it.
376  for (int i = 7; i < AVALANCHE_FINALIZATION_SCORE; i++) {
377  registerNewVote(next(resp));
378  BOOST_CHECK(m_processor->isAccepted(pindex));
379  BOOST_CHECK_EQUAL(m_processor->getConfidence(pindex), i);
380  BOOST_CHECK_EQUAL(updates.size(), 0);
381  }
382 
383  // As long as it is not finalized, we poll.
384  invs = getInvsForNextPoll();
385  BOOST_CHECK_EQUAL(invs.size(), 1);
386  BOOST_CHECK_EQUAL(invs[0].type, MSG_BLOCK);
387  BOOST_CHECK(invs[0].hash == blockHash);
388 
389  // Now finalize the decision.
390  registerNewVote(next(resp));
391  BOOST_CHECK_EQUAL(updates.size(), 1);
392  BOOST_CHECK(updates[0].getBlockIndex() == pindex);
393  BOOST_CHECK_EQUAL(updates[0].getStatus(), BlockUpdate::Status::Finalized);
394  updates = {};
395 
396  // Once the decision is finalized, there is no poll for it.
397  invs = getInvsForNextPoll();
398  BOOST_CHECK_EQUAL(invs.size(), 0);
399 
400  // Now let's undo this and finalize rejection.
401  BOOST_CHECK(m_processor->addBlockToReconcile(pindex));
402  invs = getInvsForNextPoll();
403  BOOST_CHECK_EQUAL(invs.size(), 1);
404  BOOST_CHECK_EQUAL(invs[0].type, MSG_BLOCK);
405  BOOST_CHECK(invs[0].hash == blockHash);
406 
407  resp = {getRound(), 0, {Vote(1, blockHash)}};
408  for (int i = 0; i < 6; i++) {
409  registerNewVote(next(resp));
410  BOOST_CHECK(m_processor->isAccepted(pindex));
411  BOOST_CHECK_EQUAL(updates.size(), 0);
412  }
413 
414  // Now the state will flip.
415  registerNewVote(next(resp));
416  BOOST_CHECK(!m_processor->isAccepted(pindex));
417  BOOST_CHECK_EQUAL(updates.size(), 1);
418  BOOST_CHECK(updates[0].getBlockIndex() == pindex);
419  BOOST_CHECK_EQUAL(updates[0].getStatus(), BlockUpdate::Status::Rejected);
420  updates = {};
421 
422  // Now it is rejected, but we can vote for it numerous times.
423  for (int i = 1; i < AVALANCHE_FINALIZATION_SCORE; i++) {
424  registerNewVote(next(resp));
425  BOOST_CHECK(!m_processor->isAccepted(pindex));
426  BOOST_CHECK_EQUAL(updates.size(), 0);
427  }
428 
429  // As long as it is not finalized, we poll.
430  invs = getInvsForNextPoll();
431  BOOST_CHECK_EQUAL(invs.size(), 1);
432  BOOST_CHECK_EQUAL(invs[0].type, MSG_BLOCK);
433  BOOST_CHECK(invs[0].hash == blockHash);
434 
435  // Now finalize the decision.
436  registerNewVote(next(resp));
437  BOOST_CHECK(!m_processor->isAccepted(pindex));
438  BOOST_CHECK_EQUAL(updates.size(), 1);
439  BOOST_CHECK(updates[0].getBlockIndex() == pindex);
440  BOOST_CHECK_EQUAL(updates[0].getStatus(), BlockUpdate::Status::Invalid);
441  updates = {};
442 
443  // Once the decision is finalized, there is no poll for it.
444  invs = getInvsForNextPoll();
445  BOOST_CHECK_EQUAL(invs.size(), 0);
446 
447  // Adding the block twice does nothing.
448  BOOST_CHECK(m_processor->addBlockToReconcile(pindex));
449  BOOST_CHECK(!m_processor->addBlockToReconcile(pindex));
450  BOOST_CHECK(m_processor->isAccepted(pindex));
451 }
452 
453 BOOST_AUTO_TEST_CASE(multi_block_register) {
454  CBlockIndex indexA, indexB;
455 
456  std::vector<BlockUpdate> updates;
457 
458  // Create several nodes that support avalanche.
459  auto avanodes = ConnectNodes();
460 
461  // Make sure the block has a hash.
462  CBlock blockA = CreateAndProcessBlock({}, CScript());
463  const BlockHash blockHashA = blockA.GetHash();
464 
465  CBlock blockB = CreateAndProcessBlock({}, CScript());
466  const BlockHash blockHashB = blockB.GetHash();
467  const CBlockIndex *pindexA;
468  const CBlockIndex *pindexB;
469  {
470  LOCK(cs_main);
471  pindexA = LookupBlockIndex(blockHashA);
472  pindexB = LookupBlockIndex(blockHashB);
473  }
474 
475  // Querying for random block returns false.
476  BOOST_CHECK(!m_processor->isAccepted(pindexA));
477  BOOST_CHECK(!m_processor->isAccepted(pindexB));
478 
479  // Start voting on block A.
480  BOOST_CHECK(m_processor->addBlockToReconcile(pindexA));
481  auto invs = getInvsForNextPoll();
482  BOOST_CHECK_EQUAL(invs.size(), 1);
483  BOOST_CHECK_EQUAL(invs[0].type, MSG_BLOCK);
484  BOOST_CHECK(invs[0].hash == blockHashA);
485 
486  uint64_t round = getRound();
487  runEventLoop();
488  BOOST_CHECK(m_processor->registerVotes(
489  avanodes[0]->GetId(), {round, 0, {Vote(0, blockHashA)}}, updates));
490  BOOST_CHECK_EQUAL(updates.size(), 0);
491 
492  // Start voting on block B after one vote.
493  Response resp{round + 1, 0, {Vote(0, blockHashB), Vote(0, blockHashA)}};
494  BOOST_CHECK(m_processor->addBlockToReconcile(pindexB));
495  invs = getInvsForNextPoll();
496  BOOST_CHECK_EQUAL(invs.size(), 2);
497 
498  // Ensure B comes before A because it has accumulated more PoW.
499  BOOST_CHECK_EQUAL(invs[0].type, MSG_BLOCK);
500  BOOST_CHECK(invs[0].hash == blockHashB);
501  BOOST_CHECK_EQUAL(invs[1].type, MSG_BLOCK);
502  BOOST_CHECK(invs[1].hash == blockHashA);
503 
504  // Let's vote for these blocks a few times.
505  for (int i = 0; i < 4; i++) {
506  NodeId nodeid = getSuitableNodeToQuery();
507  runEventLoop();
508  BOOST_CHECK(m_processor->registerVotes(nodeid, next(resp), updates));
509  BOOST_CHECK_EQUAL(updates.size(), 0);
510  }
511 
512  // Now it is accepted, but we can vote for it numerous times.
513  for (int i = 0; i < AVALANCHE_FINALIZATION_SCORE; i++) {
514  NodeId nodeid = getSuitableNodeToQuery();
515  runEventLoop();
516  BOOST_CHECK(m_processor->registerVotes(nodeid, next(resp), updates));
517  BOOST_CHECK_EQUAL(updates.size(), 0);
518  }
519 
520  // Running two iterration of the event loop so that vote gets triggered on A
521  // and B.
522  NodeId firstNodeid = getSuitableNodeToQuery();
523  runEventLoop();
524  NodeId secondNodeid = getSuitableNodeToQuery();
525  runEventLoop();
526 
527  BOOST_CHECK(firstNodeid != secondNodeid);
528 
529  // Next vote will finalize block A.
530  BOOST_CHECK(m_processor->registerVotes(firstNodeid, next(resp), updates));
531  BOOST_CHECK_EQUAL(updates.size(), 1);
532  BOOST_CHECK(updates[0].getBlockIndex() == pindexA);
533  BOOST_CHECK_EQUAL(updates[0].getStatus(), BlockUpdate::Status::Finalized);
534  updates = {};
535 
536  // We do not vote on A anymore.
537  invs = getInvsForNextPoll();
538  BOOST_CHECK_EQUAL(invs.size(), 1);
539  BOOST_CHECK_EQUAL(invs[0].type, MSG_BLOCK);
540  BOOST_CHECK(invs[0].hash == blockHashB);
541 
542  // Next vote will finalize block B.
543  BOOST_CHECK(m_processor->registerVotes(secondNodeid, resp, updates));
544  BOOST_CHECK_EQUAL(updates.size(), 1);
545  BOOST_CHECK(updates[0].getBlockIndex() == pindexB);
546  BOOST_CHECK_EQUAL(updates[0].getStatus(), BlockUpdate::Status::Finalized);
547  updates = {};
548 
549  // There is nothing left to vote on.
550  invs = getInvsForNextPoll();
551  BOOST_CHECK_EQUAL(invs.size(), 0);
552 }
553 
554 BOOST_AUTO_TEST_CASE(poll_and_response) {
555  std::vector<BlockUpdate> updates;
556 
557  CBlock block = CreateAndProcessBlock({}, CScript());
558  const BlockHash blockHash = block.GetHash();
559  const CBlockIndex *pindex;
560  {
561  LOCK(cs_main);
562  pindex = LookupBlockIndex(blockHash);
563  }
564 
565  // There is no node to query.
566  BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), NO_NODE);
567 
568  // Create a node that supports avalanche and one that doesn't.
569  ConnectNode(NODE_NONE);
570  auto avanode = ConnectNode(NODE_AVALANCHE);
571  NodeId avanodeid = avanode->GetId();
572  BOOST_CHECK(addNode(avanodeid));
573 
574  // It returns the avalanche peer.
575  BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), avanodeid);
576 
577  // Register a block and check it is added to the list of elements to poll.
578  BOOST_CHECK(m_processor->addBlockToReconcile(pindex));
579  auto invs = getInvsForNextPoll();
580  BOOST_CHECK_EQUAL(invs.size(), 1);
581  BOOST_CHECK_EQUAL(invs[0].type, MSG_BLOCK);
582  BOOST_CHECK(invs[0].hash == blockHash);
583 
584  // Trigger a poll on avanode.
585  uint64_t round = getRound();
586  runEventLoop();
587 
588  // There is no more suitable peer available, so return nothing.
589  BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), NO_NODE);
590 
591  // Respond to the request.
592  Response resp = {round, 0, {Vote(0, blockHash)}};
593  BOOST_CHECK(m_processor->registerVotes(avanodeid, resp, updates));
594  BOOST_CHECK_EQUAL(updates.size(), 0);
595 
596  // Now that avanode fullfilled his request, it is added back to the list of
597  // queriable nodes.
598  BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), avanodeid);
599 
600  // Sending a response when not polled fails.
601  BOOST_CHECK(!m_processor->registerVotes(avanodeid, next(resp), updates));
602  BOOST_CHECK_EQUAL(updates.size(), 0);
603 
604  // Trigger a poll on avanode.
605  round = getRound();
606  runEventLoop();
607  BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), NO_NODE);
608 
609  // Sending responses that do not match the request also fails.
610  // 1. Too many results.
611  resp = {round, 0, {Vote(0, blockHash), Vote(0, blockHash)}};
612  runEventLoop();
613  BOOST_CHECK(!m_processor->registerVotes(avanodeid, resp, updates));
614  BOOST_CHECK_EQUAL(updates.size(), 0);
615  BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), avanodeid);
616 
617  // 2. Not enough results.
618  resp = {getRound(), 0, {}};
619  runEventLoop();
620  BOOST_CHECK(!m_processor->registerVotes(avanodeid, resp, updates));
621  BOOST_CHECK_EQUAL(updates.size(), 0);
622  BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), avanodeid);
623 
624  // 3. Do not match the poll.
625  resp = {getRound(), 0, {Vote()}};
626  runEventLoop();
627  BOOST_CHECK(!m_processor->registerVotes(avanodeid, resp, updates));
628  BOOST_CHECK_EQUAL(updates.size(), 0);
629  BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), avanodeid);
630 
631  // 4. Invalid round count. Request is not discarded.
632  uint64_t queryRound = getRound();
633  runEventLoop();
634 
635  resp = {queryRound + 1, 0, {Vote()}};
636  BOOST_CHECK(!m_processor->registerVotes(avanodeid, resp, updates));
637  BOOST_CHECK_EQUAL(updates.size(), 0);
638 
639  resp = {queryRound - 1, 0, {Vote()}};
640  BOOST_CHECK(!m_processor->registerVotes(avanodeid, resp, updates));
641  BOOST_CHECK_EQUAL(updates.size(), 0);
642 
643  // 5. Making request for invalid nodes do not work. Request is not
644  // discarded.
645  resp = {queryRound, 0, {Vote(0, blockHash)}};
646  BOOST_CHECK(!m_processor->registerVotes(avanodeid + 1234, resp, updates));
647  BOOST_CHECK_EQUAL(updates.size(), 0);
648 
649  // Proper response gets processed and avanode is available again.
650  resp = {queryRound, 0, {Vote(0, blockHash)}};
651  BOOST_CHECK(m_processor->registerVotes(avanodeid, resp, updates));
652  BOOST_CHECK_EQUAL(updates.size(), 0);
653  BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), avanodeid);
654 
655  // Out of order response are rejected.
656  CBlock block2 = CreateAndProcessBlock({}, CScript());
657  const BlockHash blockHash2 = block2.GetHash();
658  CBlockIndex *pindex2;
659  {
660  LOCK(cs_main);
661  pindex2 = LookupBlockIndex(blockHash2);
662  }
663  BOOST_CHECK(m_processor->addBlockToReconcile(pindex2));
664 
665  resp = {getRound(), 0, {Vote(0, blockHash), Vote(0, blockHash2)}};
666  runEventLoop();
667  BOOST_CHECK(!m_processor->registerVotes(avanodeid, resp, updates));
668  BOOST_CHECK_EQUAL(updates.size(), 0);
669  BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), avanodeid);
670 
671  // But they are accepted in order.
672  resp = {getRound(), 0, {Vote(0, blockHash2), Vote(0, blockHash)}};
673  runEventLoop();
674  BOOST_CHECK(m_processor->registerVotes(avanodeid, resp, updates));
675  BOOST_CHECK_EQUAL(updates.size(), 0);
676  BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), avanodeid);
677 
678  // When a block is marked invalid, stop polling.
679  pindex2->nStatus = pindex2->nStatus.withFailed();
680  resp = {getRound(), 0, {Vote(0, blockHash)}};
681  runEventLoop();
682  BOOST_CHECK(m_processor->registerVotes(avanodeid, resp, updates));
683  BOOST_CHECK_EQUAL(updates.size(), 0);
684  BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), avanodeid);
685 }
686 
687 BOOST_AUTO_TEST_CASE(poll_inflight_timeout, *boost::unit_test::timeout(60)) {
688  std::vector<BlockUpdate> updates;
689 
690  CBlock block = CreateAndProcessBlock({}, CScript());
691  const BlockHash blockHash = block.GetHash();
692  const CBlockIndex *pindex;
693  {
694  LOCK(cs_main);
695  pindex = LookupBlockIndex(blockHash);
696  }
697 
698  // Add the block
699  BOOST_CHECK(m_processor->addBlockToReconcile(pindex));
700 
701  // Create a node that supports avalanche.
702  auto avanode = ConnectNode(NODE_AVALANCHE);
703  NodeId avanodeid = avanode->GetId();
704  BOOST_CHECK(addNode(avanodeid));
705 
706  // Expire requests after some time.
707  auto queryTimeDuration = std::chrono::milliseconds(10);
708  m_processor->setQueryTimeoutDuration(queryTimeDuration);
709  for (int i = 0; i < 10; i++) {
710  Response resp = {getRound(), 0, {Vote(0, blockHash)}};
711 
712  auto start = std::chrono::steady_clock::now();
713  runEventLoop();
714  // We cannot guarantee that we'll wait for just 1ms, so we have to bail
715  // if we aren't within the proper time range.
716  std::this_thread::sleep_for(std::chrono::milliseconds(1));
717  runEventLoop();
718 
719  bool ret = m_processor->registerVotes(avanodeid, next(resp), updates);
720  if (std::chrono::steady_clock::now() > start + queryTimeDuration) {
721  // We waited for too long, bail. Because we can't know for sure when
722  // previous steps ran, ret is not deterministic and we do not check
723  // it.
724  i--;
725  continue;
726  }
727 
728  // We are within time bounds, so the vote should have worked.
729  BOOST_CHECK(ret);
730 
731  // Now try again but wait for expiration.
732  runEventLoop();
733  std::this_thread::sleep_for(queryTimeDuration);
734  runEventLoop();
735  BOOST_CHECK(
736  !m_processor->registerVotes(avanodeid, next(resp), updates));
737  }
738 }
739 
740 BOOST_AUTO_TEST_CASE(poll_inflight_count) {
741  // Create enough nodes so that we run into the inflight request limit.
742  PeerManager &pm = getPeerManager();
743  Proof proof = GetProof();
744  Delegation dg = DelegationBuilder(proof).build();
745 
746  std::array<CNode *, AVALANCHE_MAX_INFLIGHT_POLL + 1> nodes;
747  for (auto &n : nodes) {
748  n = ConnectNode(NODE_AVALANCHE);
749  BOOST_CHECK(pm.addNode(n->GetId(), proof, dg));
750  }
751 
752  // Add a block to poll
753  CBlock block = CreateAndProcessBlock({}, CScript());
754  const BlockHash blockHash = block.GetHash();
755  const CBlockIndex *pindex;
756  {
757  LOCK(cs_main);
758  pindex = LookupBlockIndex(blockHash);
759  }
760  BOOST_CHECK(m_processor->addBlockToReconcile(pindex));
761 
762  // Ensure there are enough requests in flight.
763  std::map<NodeId, uint64_t> node_round_map;
764  for (int i = 0; i < AVALANCHE_MAX_INFLIGHT_POLL; i++) {
765  NodeId nodeid = getSuitableNodeToQuery();
766  BOOST_CHECK(node_round_map.find(nodeid) == node_round_map.end());
767  node_round_map[nodeid] = getRound();
768  auto invs = getInvsForNextPoll();
769  BOOST_CHECK_EQUAL(invs.size(), 1);
770  BOOST_CHECK_EQUAL(invs[0].type, MSG_BLOCK);
771  BOOST_CHECK(invs[0].hash == blockHash);
772  runEventLoop();
773  }
774 
775  // Now that we have enough in flight requests, we shouldn't poll.
776  auto suitablenodeid = getSuitableNodeToQuery();
777  BOOST_CHECK(suitablenodeid != NO_NODE);
778  auto invs = getInvsForNextPoll();
779  BOOST_CHECK_EQUAL(invs.size(), 0);
780  runEventLoop();
781  BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), suitablenodeid);
782 
783  std::vector<BlockUpdate> updates;
784 
785  // Send one response, now we can poll again.
786  auto it = node_round_map.begin();
787  Response resp = {it->second, 0, {Vote(0, blockHash)}};
788  BOOST_CHECK(m_processor->registerVotes(it->first, resp, updates));
789  node_round_map.erase(it);
790 
791  invs = getInvsForNextPoll();
792  BOOST_CHECK_EQUAL(invs.size(), 1);
793  BOOST_CHECK_EQUAL(invs[0].type, MSG_BLOCK);
794  BOOST_CHECK(invs[0].hash == blockHash);
795 }
796 
797 BOOST_AUTO_TEST_CASE(quorum_diversity) {
798  std::vector<BlockUpdate> updates;
799 
800  CBlock block = CreateAndProcessBlock({}, CScript());
801  const BlockHash blockHash = block.GetHash();
802  const CBlockIndex *pindex;
803  {
804  LOCK(cs_main);
805  pindex = LookupBlockIndex(blockHash);
806  }
807 
808  // Create nodes that supports avalanche.
809  auto avanodes = ConnectNodes();
810 
811  // Querying for random block returns false.
812  BOOST_CHECK(!m_processor->isAccepted(pindex));
813 
814  // Add a new block. Check it is added to the polls.
815  BOOST_CHECK(m_processor->addBlockToReconcile(pindex));
816 
817  // Do one valid round of voting.
818  uint64_t round = getRound();
819  Response resp{round, 0, {Vote(0, blockHash)}};
820 
821  // Check that all nodes can vote.
822  for (size_t i = 0; i < avanodes.size(); i++) {
823  runEventLoop();
824  BOOST_CHECK(m_processor->registerVotes(avanodes[i]->GetId(), next(resp),
825  updates));
826  }
827 
828  // Generate a query for every single node.
829  const NodeId firstNodeId = getSuitableNodeToQuery();
830  std::map<NodeId, uint64_t> node_round_map;
831  round = getRound();
832  for (size_t i = 0; i < avanodes.size(); i++) {
833  NodeId nodeid = getSuitableNodeToQuery();
834  BOOST_CHECK(node_round_map.find(nodeid) == node_round_map.end());
835  node_round_map[nodeid] = getRound();
836  runEventLoop();
837  }
838 
839  // Now only tge first node can vote. All others would be duplicate in the
840  // quorum.
841  auto confidence = m_processor->getConfidence(pindex);
842  BOOST_REQUIRE(confidence > 0);
843 
844  for (auto &pair : node_round_map) {
845  NodeId nodeid = pair.first;
846  uint64_t r = pair.second;
847 
848  if (nodeid == firstNodeId) {
849  // Node 0 is the only one which can vote at this stage.
850  round = r;
851  continue;
852  }
853 
854  BOOST_CHECK(m_processor->registerVotes(
855  nodeid, {r, 0, {Vote(0, blockHash)}}, updates));
856  BOOST_CHECK_EQUAL(m_processor->getConfidence(pindex), confidence);
857  }
858 
859  BOOST_CHECK(m_processor->registerVotes(
860  firstNodeId, {round, 0, {Vote(0, blockHash)}}, updates));
861  BOOST_CHECK_EQUAL(m_processor->getConfidence(pindex), confidence + 1);
862 }
863 
864 BOOST_AUTO_TEST_CASE(event_loop) {
865  CScheduler s;
866 
867  CBlock block = CreateAndProcessBlock({}, CScript());
868  const BlockHash blockHash = block.GetHash();
869  const CBlockIndex *pindex;
870  {
871  LOCK(cs_main);
872  pindex = LookupBlockIndex(blockHash);
873  }
874 
875  // Starting the event loop.
876  BOOST_CHECK(m_processor->startEventLoop(s));
877 
878  // There is one task planned in the next hour (our event loop).
880  BOOST_CHECK_EQUAL(s.getQueueInfo(start, stop), 1);
881 
882  // Starting twice doesn't start it twice.
883  BOOST_CHECK(!m_processor->startEventLoop(s));
884 
885  // Start the scheduler thread.
886  std::thread schedulerThread(std::bind(&CScheduler::serviceQueue, &s));
887 
888  // Create a node that supports avalanche.
889  auto avanode = ConnectNode(NODE_AVALANCHE);
890  NodeId nodeid = avanode->GetId();
891  BOOST_CHECK(addNode(nodeid));
892 
893  // There is no query in flight at the moment.
894  BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), nodeid);
895 
896  // Add a new block. Check it is added to the polls.
897  uint64_t queryRound = getRound();
898  BOOST_CHECK(m_processor->addBlockToReconcile(pindex));
899 
900  for (int i = 0; i < 60 * 1000; i++) {
901  // Technically, this is a race condition, but this should do just fine
902  // as we wait up to 1 minute for an event that should take 10ms.
903  UninterruptibleSleep(std::chrono::milliseconds(1));
904  if (getRound() != queryRound) {
905  break;
906  }
907  }
908 
909  // Check that we effectively got a request and not timed out.
910  BOOST_CHECK(getRound() > queryRound);
911 
912  // Respond and check the cooldown time is respected.
913  uint64_t responseRound = getRound();
914  auto queryTime =
915  std::chrono::steady_clock::now() + std::chrono::milliseconds(100);
916 
917  std::vector<BlockUpdate> updates;
918  m_processor->registerVotes(nodeid, {queryRound, 100, {Vote(0, blockHash)}},
919  updates);
920  for (int i = 0; i < 10000; i++) {
921  // We make sure that we do not get a request before queryTime.
922  UninterruptibleSleep(std::chrono::milliseconds(1));
923  if (getRound() != responseRound) {
924  BOOST_CHECK(std::chrono::steady_clock::now() > queryTime);
925  break;
926  }
927  }
928 
929  // But we eventually get one.
930  BOOST_CHECK(getRound() > responseRound);
931 
932  // Stop event loop.
933  BOOST_CHECK(m_processor->stopEventLoop());
934 
935  // We don't have any task scheduled anymore.
936  BOOST_CHECK_EQUAL(s.getQueueInfo(start, stop), 0);
937 
938  // Can't stop the event loop twice.
939  BOOST_CHECK(!m_processor->stopEventLoop());
940 
941  // Wait for the scheduler to stop.
942  s.stop(true);
943  schedulerThread.join();
944 }
945 
946 BOOST_AUTO_TEST_CASE(destructor) {
947  CScheduler s;
949 
950  std::thread schedulerThread;
951  BOOST_CHECK(m_processor->startEventLoop(s));
952  BOOST_CHECK_EQUAL(s.getQueueInfo(start, stop), 1);
953 
954  // Start the service thread after the queue size check to prevent a race
955  // condition where the thread may be processing the event loop task during
956  // the check.
957  schedulerThread = std::thread(std::bind(&CScheduler::serviceQueue, &s));
958 
959  // Destroy the processor.
960  m_processor.reset();
961 
962  // Now that avalanche is destroyed, there is no more scheduled tasks.
963  BOOST_CHECK_EQUAL(s.getQueueInfo(start, stop), 0);
964 
965  // Wait for the scheduler to stop.
966  s.stop(true);
967  schedulerThread.join();
968 }
969 
uint32_t getCooldown() const
Definition: protocol.h:50
static constexpr NodeId NO_NODE
Special NodeId that represent no node.
Definition: net.h:104
static unsigned short GetDefaultPort()
Definition: bitcoin.h:15
bool shouldPoll() const
Return if this item is in condition to be polled at the moment.
Definition: processor.h:131
BlockStatus withFailed(bool hasFailed=true) const
Definition: blockstatus.h:67
ServiceFlags
nServices flags.
Definition: protocol.h:320
Definition: block.h:62
std::unique_ptr< BanMan > banman
Definition: context.h:42
Status getStatus() const
Definition: processor.h:172
CPubKey GetPubKey() const
Compute the public key from a private key.
Definition: key.cpp:210
NodeContext & m_node
Definition: chain.cpp:442
Full relay connections (blocks, addrs, txns) made automatically.
BlockStatus nStatus
Verification status of this block. See enum BlockStatus.
Definition: blockindex.h:76
std::vector< CInv > getInvsForNextPoll(bool forPoll=true)
Definition: processor.cpp:440
std::unique_ptr< interfaces::Chain > chain
Definition: context.h:45
bool hasFinalized() const
Definition: processor.h:111
#define INVALID_SOCKET
Definition: compat.h:55
const std::vector< Vote > & GetVotes() const
Definition: protocol.h:51
NodeId getSuitableNodeToQuery()
Definition: processor.cpp:479
std::atomic< ServiceFlags > nServices
Definition: net.h:815
CBlockIndex * getBlockIndex()
Definition: processor.h:174
static constexpr int AVALANCHE_MAX_INFLIGHT_POLL
How many inflight requests can exist for one item.
Definition: processor.h:60
ChainstateManager * chainman
Definition: context.h:41
Definition: config.h:19
CConnman(const Config &configIn, uint64_t seed0, uint64_t seed1)
Definition: net.cpp:2501
std::unique_ptr< CConnman > connman
Definition: context.h:36
void stop(bool drain=false)
Definition: scheduler.cpp:75
#define LOCK(cs)
Definition: sync.h:230
A combination of a network address (CNetAddr) and a (TCP) port.
Definition: netaddress.h:179
BOOST_AUTO_TEST_CASE(vote_record)
void MakeNewKey(bool fCompressed)
Generate a new private key using a cryptographic PRNG.
Definition: key.cpp:183
const std::vector< CTxOut > vout
Definition: transaction.h:228
A CService with information about it as peer.
Definition: protocol.h:427
RecursiveMutex cs_main
Global state.
Definition: validation.cpp:95
const Config & GetConfig()
Definition: config.cpp:34
void serviceQueue()
Definition: scheduler.cpp:21
int64_t NodeId
Definition: net.h:99
Definition: net.h:165
static constexpr int AVALANCHE_FINALIZATION_SCORE
Finalization score.
Definition: processor.h:41
#define REGISTER_VOTE_AND_CHECK(vr, vote, state, finalized, confidence)
An outpoint - a combination of a transaction hash and an index n into its vout.
Definition: transaction.h:22
bool addNode(NodeId nodeid, const Proof &proof, const Delegation &delegation)
Node API.
Definition: peermanager.cpp:16
IP address (IPv6, or IPv4 using mapped IPv6 range (::FFFF:0:0/96))
Definition: netaddress.h:30
Vote history.
Definition: processor.h:71
#define BOOST_FIXTURE_TEST_SUITE(a, b)
Definition: object.cpp:14
void UninterruptibleSleep(const std::chrono::microseconds &n)
Definition: time.cpp:20
A BlockHash is a unqiue identifier for a block.
Definition: blockhash.h:13
#define BOOST_CHECK_EQUAL(v1, v2)
Definition: object.cpp:18
The block chain is a tree shaped structure starting with the genesis block at the root...
Definition: blockindex.h:23
const CChainParams & Params()
Return the currently selected parameters.
BlockHash GetHash() const
Definition: block.cpp:11
Serialized script, used inside transaction inputs and outputs.
Definition: script.h:429
static const int PROTOCOL_VERSION
network protocol versioning
Definition: version.h:11
void SetSendVersion(int nVersionIn)
Definition: net.cpp:649
clock::time_point time_point
Definition: bench.h:54
CBlockIndex * LookupBlockIndex(const BlockHash &hash)
Definition: validation.cpp:145
#define BOOST_AUTO_TEST_SUITE_END()
Definition: object.cpp:16
static UniValue stop(const Config &config, const JSONRPCRequest &jsonRequest)
Definition: server.cpp:198
uint64_t getRound() const
Definition: protocol.h:49
std::atomic_bool fSuccessfullyConnected
Definition: net.h:868
std::unique_ptr< PeerLogicValidation > peer_logic
Definition: context.h:39
std::atomic< int > nVersion
Definition: net.h:845
uint16_t getConfidence() const
Definition: processor.h:110
void clearInflightRequest(uint8_t count=1)
Clear count inflight requests.
Definition: processor.h:136
An encapsulated secp256k1 private key.
Definition: key.h:25
The basic transaction that is broadcasted on the network and contained in blocks. ...
Definition: transaction.h:211
Information about a peer.
Definition: net.h:806
bool registerPoll() const
Register that a request is being made regarding that item.
Definition: processor.cpp:111
Mutex cs_peerManager
Keep track of the peers and associated infos.
Definition: processor.h:209
size_t getQueueInfo(std::chrono::system_clock::time_point &first, std::chrono::system_clock::time_point &last) const
Definition: scheduler.cpp:133
std::atomic< uint64_t > round
Keep track of peers and queries sent.
Definition: processor.h:204
int GetRandInt(int nMax) noexcept
Definition: random.cpp:650
const TxId GetId() const
Definition: transaction.h:261
#define BOOST_CHECK(expr)
Definition: object.cpp:17
std::unique_ptr< CScheduler > scheduler
Definition: context.h:47
bool isAccepted() const
Vote accounting facilities.
Definition: processor.h:108
std::unique_ptr< Chain > MakeChain(NodeContext &node, const CChainParams &params)
Return implementation of Chain interface.
Definition: chain.cpp:448
CTxMemPool * mempool
Definition: context.h:38