Bitcoin ABC  0.26.3
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 
7 #include <arith_uint256.h>
8 #include <avalanche/avalanche.h>
10 #include <avalanche/peermanager.h>
11 #include <avalanche/proofbuilder.h>
12 #include <avalanche/voterecord.h>
13 #include <chain.h>
14 #include <config.h>
15 #include <key_io.h>
16 #include <net_processing.h> // For ::PeerManager
17 #include <reverse_iterator.h>
18 #include <scheduler.h>
19 #include <util/time.h>
20 #include <util/translation.h> // For bilingual_str
21 // D6970 moved LookupBlockIndex from chain.h to validation.h TODO: remove this
22 // when LookupBlockIndex is refactored out of validation
23 #include <validation.h>
24 
25 #include <avalanche/test/util.h>
26 #include <test/util/setup_common.h>
27 
28 #include <boost/mpl/list.hpp>
29 #include <boost/test/unit_test.hpp>
30 
31 #include <functional>
32 #include <type_traits>
33 #include <vector>
34 
35 using namespace avalanche;
36 
37 namespace avalanche {
38 namespace {
39  struct AvalancheTest {
40  static void runEventLoop(avalanche::Processor &p) { p.runEventLoop(); }
41 
42  static std::vector<CInv> getInvsForNextPoll(Processor &p) {
43  return p.getInvsForNextPoll(false);
44  }
45 
46  static NodeId getSuitableNodeToQuery(Processor &p) {
47  return WITH_LOCK(p.cs_peerManager,
48  return p.peerManager->selectNode());
49  }
50 
51  static uint64_t getRound(const Processor &p) { return p.round; }
52 
53  static uint32_t getMinQuorumScore(const Processor &p) {
54  return p.minQuorumScore;
55  }
56 
57  static double getMinQuorumConnectedScoreRatio(const Processor &p) {
59  }
60 
61  static void clearavaproofsNodeCounter(Processor &p) {
63  }
64 
65  static void addVoteRecord(Processor &p, AnyVoteItem &item,
66  VoteRecord &voteRecord) {
67  p.voteRecords.getWriteView()->insert(
68  std::make_pair(item, voteRecord));
69  }
70 
71  static void setFinalizationTip(Processor &p,
72  const CBlockIndex *pindex) {
74  p.finalizationTip = pindex;
75  }
76  };
77 } // namespace
78 
79 struct TestVoteRecord : public VoteRecord {
80  explicit TestVoteRecord(uint16_t conf) : VoteRecord(true) {
81  confidence |= conf << 1;
82  }
83 };
84 } // namespace avalanche
85 
86 namespace {
87 struct CConnmanTest : public CConnman {
88  using CConnman::CConnman;
89  void AddNode(CNode &node) {
90  LOCK(m_nodes_mutex);
91  m_nodes.push_back(&node);
92  }
93  void ClearNodes() {
94  LOCK(m_nodes_mutex);
95  for (CNode *node : m_nodes) {
96  delete node;
97  }
98  m_nodes.clear();
99  }
100 };
101 
102 CService ip(uint32_t i) {
103  struct in_addr s;
104  s.s_addr = i;
105  return CService(CNetAddr(s), Params().GetDefaultPort());
106 }
107 
108 struct AvalancheTestingSetup : public TestChain100Setup {
109  const ::Config &config;
110  CConnmanTest *m_connman;
111 
112  std::unique_ptr<Processor> m_processor;
113 
114  // The master private key we delegate to.
115  CKey masterpriv;
116 
117  std::unordered_set<std::string> m_overridden_args;
118 
119  AvalancheTestingSetup()
120  : TestChain100Setup(), config(GetConfig()),
121  masterpriv(CKey::MakeCompressedKey()) {
122  // Deterministic randomness for tests.
123  auto connman = std::make_unique<CConnmanTest>(config, 0x1337, 0x1337,
124  *m_node.addrman);
125  m_connman = connman.get();
126  m_node.connman = std::move(connman);
127  m_node.peerman = ::PeerManager::make(
128  config.GetChainParams(), *m_connman, *m_node.addrman,
129  m_node.banman.get(), *m_node.chainman, *m_node.mempool, false);
130  m_node.chain = interfaces::MakeChain(m_node, config.GetChainParams());
131 
132  // Get the processor ready.
133  setArg("-avaminquorumstake", "0");
134  setArg("-avaminquorumconnectedstakeratio", "0");
135  setArg("-avaminavaproofsnodecount", "0");
136  setArg("-avaproofstakeutxoconfirmations", "1");
138  m_processor = Processor::MakeProcessor(
139  *m_node.args, *m_node.chain, m_node.connman.get(),
140  *Assert(m_node.chainman), m_node.mempool.get(), *m_node.scheduler,
141  error);
142  BOOST_CHECK(m_processor);
143  }
144 
145  ~AvalancheTestingSetup() {
146  m_connman->ClearNodes();
148 
149  ArgsManager &argsman = *Assert(m_node.args);
150  for (const std::string &key : m_overridden_args) {
151  argsman.ClearForcedArg(key);
152  }
153  m_overridden_args.clear();
154  }
155 
156  CNode *ConnectNode(ServiceFlags nServices) {
157  static NodeId id = 0;
158 
159  CAddress addr(ip(GetRandInt(0xffffffff)), NODE_NONE);
160  auto node =
161  new CNode(id++, ServiceFlags(NODE_NETWORK), INVALID_SOCKET, addr,
162  /* nKeyedNetGroupIn */ 0,
163  /* nLocalHostNonceIn */ 0,
164  /* nLocalExtraEntropyIn */ 0, CAddress(),
165  /* pszDest */ "", ConnectionType::OUTBOUND_FULL_RELAY,
166  /* inbound_onion */ false);
167  node->SetCommonVersion(PROTOCOL_VERSION);
168  node->nServices = nServices;
169  m_node.peerman->InitializeNode(config, node);
170  node->nVersion = 1;
171  node->fSuccessfullyConnected = true;
172 
173  m_connman->AddNode(*node);
174  return node;
175  }
176 
177  ProofRef GetProof() {
178  const CKey key = CKey::MakeCompressedKey();
179  const COutPoint outpoint{TxId(GetRandHash()), 0};
181  const Amount amount = PROOF_DUST_THRESHOLD;
182  const uint32_t height = 100;
183 
184  LOCK(cs_main);
185  CCoinsViewCache &coins =
186  Assert(m_node.chainman)->ActiveChainstate().CoinsTip();
187  coins.AddCoin(outpoint, Coin(CTxOut(amount, script), height, false),
188  false);
189 
190  ProofBuilder pb(0, 0, masterpriv, UNSPENDABLE_ECREG_PAYOUT_SCRIPT);
191  BOOST_CHECK(pb.addUTXO(outpoint, amount, height, false, key));
192  return pb.build();
193  }
194 
195  bool addNode(NodeId nodeid, const ProofId &proofid) {
196  return m_processor->withPeerManager([&](avalanche::PeerManager &pm) {
197  return pm.addNode(nodeid, proofid);
198  });
199  }
200 
201  bool addNode(NodeId nodeid) {
202  auto proof = GetProof();
203  return m_processor->withPeerManager([&](avalanche::PeerManager &pm) {
204  return pm.registerProof(proof) &&
205  pm.addNode(nodeid, proof->getId());
206  });
207  }
208 
209  std::array<CNode *, 8> ConnectNodes() {
210  auto proof = GetProof();
211  BOOST_CHECK(
212  m_processor->withPeerManager([&](avalanche::PeerManager &pm) {
213  return pm.registerProof(proof);
214  }));
215  const ProofId &proofid = proof->getId();
216 
217  std::array<CNode *, 8> nodes;
218  for (CNode *&n : nodes) {
219  n = ConnectNode(NODE_AVALANCHE);
220  BOOST_CHECK(addNode(n->GetId(), proofid));
221  }
222 
223  return nodes;
224  }
225 
226  void runEventLoop() { AvalancheTest::runEventLoop(*m_processor); }
227 
228  NodeId getSuitableNodeToQuery() {
229  return AvalancheTest::getSuitableNodeToQuery(*m_processor);
230  }
231 
232  std::vector<CInv> getInvsForNextPoll() {
233  return AvalancheTest::getInvsForNextPoll(*m_processor);
234  }
235 
236  uint64_t getRound() const { return AvalancheTest::getRound(*m_processor); }
237 
238  bool registerVotes(NodeId nodeid, const avalanche::Response &response,
239  std::vector<avalanche::VoteItemUpdate> &updates,
240  std::string &error) {
241  int banscore;
242  return m_processor->registerVotes(nodeid, response, updates, banscore,
243  error);
244  }
245 
246  bool registerVotes(NodeId nodeid, const avalanche::Response &response,
247  std::vector<avalanche::VoteItemUpdate> &updates) {
248  int banscore;
249  std::string error;
250  return m_processor->registerVotes(nodeid, response, updates, banscore,
251  error);
252  }
253 
254  void setArg(std::string key, std::string value) {
255  ArgsManager &argsman = *Assert(m_node.args);
256  argsman.ForceSetArg(key, std::move(value));
257  m_overridden_args.emplace(std::move(key));
258  }
259 
260  bool addToReconcile(const AnyVoteItem &item) {
261  return m_processor->addToReconcile(item);
262  }
263 };
264 
265 struct BlockProvider {
266  AvalancheTestingSetup *fixture;
267  uint32_t invType;
268 
269  BlockProvider(AvalancheTestingSetup *_fixture)
270  : fixture(_fixture), invType(MSG_BLOCK) {}
271 
272  CBlockIndex *buildVoteItem() const {
273  CBlock block = fixture->CreateAndProcessBlock({}, CScript());
274  const BlockHash blockHash = block.GetHash();
275 
276  LOCK(cs_main);
277  return Assert(fixture->m_node.chainman)
278  ->m_blockman.LookupBlockIndex(blockHash);
279  }
280 
281  uint256 getVoteItemId(const CBlockIndex *pindex) const {
282  return pindex->GetBlockHash();
283  }
284 
285  std::vector<Vote> buildVotesForItems(uint32_t error,
286  std::vector<CBlockIndex *> &&items) {
287  size_t numItems = items.size();
288 
289  std::vector<Vote> votes;
290  votes.reserve(numItems);
291 
292  // Votes are sorted by most work first
293  std::sort(items.begin(), items.end(), CBlockIndexWorkComparator());
294  for (auto &item : reverse_iterate(items)) {
295  votes.emplace_back(error, item->GetBlockHash());
296  }
297 
298  return votes;
299  }
300 
301  void invalidateItem(CBlockIndex *pindex) {
302  LOCK(::cs_main);
303  pindex->nStatus = pindex->nStatus.withFailed();
304  }
305 
306  const CBlockIndex *fromAnyVoteItem(const AnyVoteItem &item) {
307  return std::get<const CBlockIndex *>(item);
308  }
309 };
310 
311 struct ProofProvider {
312  AvalancheTestingSetup *fixture;
313  uint32_t invType;
314 
315  ProofProvider(AvalancheTestingSetup *_fixture)
316  : fixture(_fixture), invType(MSG_AVA_PROOF) {}
317 
318  ProofRef buildVoteItem() const {
319  const ProofRef proof = fixture->GetProof();
320  fixture->m_processor->withPeerManager([&](avalanche::PeerManager &pm) {
321  BOOST_CHECK(pm.registerProof(proof));
322  });
323  return proof;
324  }
325 
326  uint256 getVoteItemId(const ProofRef &proof) const {
327  return proof->getId();
328  }
329 
330  std::vector<Vote> buildVotesForItems(uint32_t error,
331  std::vector<ProofRef> &&items) {
332  size_t numItems = items.size();
333 
334  std::vector<Vote> votes;
335  votes.reserve(numItems);
336 
337  // Votes are sorted by high score first
338  std::sort(items.begin(), items.end(), ProofComparatorByScore());
339  for (auto &item : items) {
340  votes.emplace_back(error, item->getId());
341  }
342 
343  return votes;
344  }
345 
346  void invalidateItem(const ProofRef &proof) {
347  fixture->m_processor->withPeerManager([&](avalanche::PeerManager &pm) {
348  pm.rejectProof(proof->getId(),
350  });
351  }
352 
353  const ProofRef fromAnyVoteItem(const AnyVoteItem &item) {
354  return std::get<const ProofRef>(item);
355  }
356 };
357 
358 struct TxProvider {
359  AvalancheTestingSetup *fixture;
360 
361  std::vector<avalanche::VoteItemUpdate> updates;
362  uint32_t invType;
363 
364  TxProvider(AvalancheTestingSetup *_fixture)
365  : fixture(_fixture), invType(MSG_TX) {}
366 
367  CTransactionRef buildVoteItem() const {
369  mtx.nVersion = 2;
370  mtx.vin.emplace_back(COutPoint{TxId(FastRandomContext().rand256()), 0});
371  mtx.vout.emplace_back(1 * COIN, CScript() << OP_TRUE);
372 
373  CTransactionRef tx = MakeTransactionRef(std::move(mtx));
374 
375  TestMemPoolEntryHelper mempoolEntryHelper;
376  auto entry = mempoolEntryHelper.FromTx(tx);
377 
378  CTxMemPool *mempool = Assert(fixture->m_node.mempool.get());
379  {
380  LOCK2(cs_main, mempool->cs);
381  mempool->addUnchecked(entry);
382  BOOST_CHECK(mempool->exists(tx->GetId()));
383  }
384 
385  return tx;
386  }
387 
388  uint256 getVoteItemId(const CTransactionRef &tx) const {
389  return tx->GetId();
390  }
391 
392  std::vector<Vote> buildVotesForItems(uint32_t error,
393  std::vector<CTransactionRef> &&items) {
394  size_t numItems = items.size();
395 
396  std::vector<Vote> votes;
397  votes.reserve(numItems);
398 
399  // Transactions are sorted by TxId
400  std::sort(items.begin(), items.end(),
401  [](const CTransactionRef &lhs, const CTransactionRef &rhs) {
402  return lhs->GetId() < rhs->GetId();
403  });
404  for (auto &item : items) {
405  votes.emplace_back(error, item->GetId());
406  }
407 
408  return votes;
409  }
410 
411  void invalidateItem(const CTransactionRef &tx) {
412  BOOST_CHECK(tx != nullptr);
413  CTxMemPool *mempool = Assert(fixture->m_node.mempool.get());
414 
415  LOCK(mempool->cs);
417  BOOST_CHECK(!mempool->exists(tx->GetId()));
418  }
419 
420  const CTransactionRef fromAnyVoteItem(const AnyVoteItem &item) {
421  return std::get<const CTransactionRef>(item);
422  }
423 };
424 
425 } // namespace
426 
427 BOOST_FIXTURE_TEST_SUITE(processor_tests, AvalancheTestingSetup)
428 
429 // FIXME A std::tuple can be used instead of boost::mpl::list after boost 1.67
431  boost::mpl::list<BlockProvider, ProofProvider, TxProvider>;
432 
434  P provider(this);
435 
436  std::set<VoteStatus> status{
439  };
440 
441  auto item = provider.buildVoteItem();
442 
443  for (auto s : status) {
444  VoteItemUpdate itemUpdate(item, s);
445  // The use of BOOST_CHECK instead of BOOST_CHECK_EQUAL prevents from
446  // having to define operator<<() for each argument type.
447  BOOST_CHECK(provider.fromAnyVoteItem(itemUpdate.getVoteItem()) == item);
448  BOOST_CHECK(itemUpdate.getStatus() == s);
449  }
450 }
451 
452 namespace {
453 Response next(Response &r) {
454  auto copy = r;
455  r = {r.getRound() + 1, r.getCooldown(), r.GetVotes()};
456  return copy;
457 }
458 } // namespace
459 
461  P provider(this);
462  const CBlockIndex *chaintip = Assert(m_node.chainman)->ActiveTip();
463 
464  auto item = provider.buildVoteItem();
465  auto itemid = provider.getVoteItemId(item);
466 
467  // Adding the item twice does nothing.
468  BOOST_CHECK(addToReconcile(item));
469  BOOST_CHECK(!addToReconcile(item));
470  BOOST_CHECK(m_processor->isAccepted(item));
471 
472  // Create nodes that supports avalanche so we can finalize the item.
473  auto avanodes = ConnectNodes();
474 
475  int nextNodeIndex = 0;
476  std::vector<avalanche::VoteItemUpdate> updates;
477  auto registerNewVote = [&](const Response &resp) {
478  runEventLoop();
479  auto nodeid = avanodes[nextNodeIndex++ % avanodes.size()]->GetId();
480  BOOST_CHECK(registerVotes(nodeid, resp, updates));
481  };
482 
483  // Finalize the item.
484  auto finalize = [&](const auto finalizeItemId) {
485  Response resp = {getRound(), 0, {Vote(0, finalizeItemId)}};
486  for (int i = 0; i < AVALANCHE_FINALIZATION_SCORE + 6; i++) {
487  registerNewVote(next(resp));
488  if (updates.size() > 0) {
489  break;
490  }
491  }
492  BOOST_CHECK_EQUAL(updates.size(), 1);
493  BOOST_CHECK(updates[0].getStatus() == VoteStatus::Finalized);
494  updates.clear();
495  };
496  finalize(itemid);
497 
498  // The finalized item cannot be reconciled for a while.
499  BOOST_CHECK(!addToReconcile(item));
500 
501  auto finalizeNewItem = [&]() {
502  auto anotherItem = provider.buildVoteItem();
503  AnyVoteItem anotherVoteItem = AnyVoteItem(anotherItem);
504  auto anotherItemId = provider.getVoteItemId(anotherItem);
505 
507  AvalancheTest::addVoteRecord(*m_processor, anotherVoteItem, voteRecord);
508  finalize(anotherItemId);
509  };
510 
511  // The filter can have new items added up to its size and the item will
512  // still not reconcile.
513  for (uint32_t i = 0; i < AVALANCHE_FINALIZED_ITEMS_FILTER_NUM_ELEMENTS;
514  i++) {
515  finalizeNewItem();
516  BOOST_CHECK(!addToReconcile(item));
517  }
518 
519  // But if we keep going it will eventually roll out of the filter and can
520  // be reconciled again.
521  for (uint32_t i = 0; i < AVALANCHE_FINALIZED_ITEMS_FILTER_NUM_ELEMENTS;
522  i++) {
523  finalizeNewItem();
524  }
525 
526  // Roll back the finalization point so that reconciling the old block does
527  // not fail the finalization check. This is a no-op for other types.
528  AvalancheTest::setFinalizationTip(*m_processor, chaintip);
529 
530  BOOST_CHECK(addToReconcile(item));
531 }
532 
534  P provider(this);
535 
536  // Check that null case is handled on the public interface
537  BOOST_CHECK(!m_processor->isAccepted(nullptr));
538  BOOST_CHECK_EQUAL(m_processor->getConfidence(nullptr), -1);
539 
540  auto item = decltype(provider.buildVoteItem())();
541  BOOST_CHECK(item == nullptr);
542  BOOST_CHECK(!addToReconcile(item));
543 
544  // Check that adding item to vote on doesn't change the outcome. A
545  // comparator is used under the hood, and this is skipped if there are no
546  // vote records.
547  item = provider.buildVoteItem();
548  BOOST_CHECK(addToReconcile(item));
549 
550  BOOST_CHECK(!m_processor->isAccepted(nullptr));
551  BOOST_CHECK_EQUAL(m_processor->getConfidence(nullptr), -1);
552 }
553 
555  P provider(this);
556  const uint32_t invType = provider.invType;
557 
558  auto item = provider.buildVoteItem();
559  auto itemid = provider.getVoteItemId(item);
560 
561  // Create nodes that supports avalanche.
562  auto avanodes = ConnectNodes();
563 
564  // Querying for random item returns false.
565  BOOST_CHECK(!m_processor->isAccepted(item));
566 
567  // Add a new item. Check it is added to the polls.
568  BOOST_CHECK(addToReconcile(item));
569  auto invs = getInvsForNextPoll();
570  BOOST_CHECK_EQUAL(invs.size(), 1);
571  BOOST_CHECK_EQUAL(invs[0].type, invType);
572  BOOST_CHECK(invs[0].hash == itemid);
573 
574  BOOST_CHECK(m_processor->isAccepted(item));
575 
576  int nextNodeIndex = 0;
577  std::vector<avalanche::VoteItemUpdate> updates;
578  auto registerNewVote = [&](const Response &resp) {
579  runEventLoop();
580  auto nodeid = avanodes[nextNodeIndex++ % avanodes.size()]->GetId();
581  BOOST_CHECK(registerVotes(nodeid, resp, updates));
582  };
583 
584  // Let's vote for this item a few times.
585  Response resp{0, 0, {Vote(0, itemid)}};
586  for (int i = 0; i < 6; i++) {
587  registerNewVote(next(resp));
588  BOOST_CHECK(m_processor->isAccepted(item));
589  BOOST_CHECK_EQUAL(m_processor->getConfidence(item), 0);
590  BOOST_CHECK_EQUAL(updates.size(), 0);
591  }
592 
593  // A single neutral vote do not change anything.
594  resp = {getRound(), 0, {Vote(-1, itemid)}};
595  registerNewVote(next(resp));
596  BOOST_CHECK(m_processor->isAccepted(item));
597  BOOST_CHECK_EQUAL(m_processor->getConfidence(item), 0);
598  BOOST_CHECK_EQUAL(updates.size(), 0);
599 
600  resp = {getRound(), 0, {Vote(0, itemid)}};
601  for (int i = 1; i < 7; i++) {
602  registerNewVote(next(resp));
603  BOOST_CHECK(m_processor->isAccepted(item));
604  BOOST_CHECK_EQUAL(m_processor->getConfidence(item), i);
605  BOOST_CHECK_EQUAL(updates.size(), 0);
606  }
607 
608  // Two neutral votes will stall progress.
609  resp = {getRound(), 0, {Vote(-1, itemid)}};
610  registerNewVote(next(resp));
611  BOOST_CHECK(m_processor->isAccepted(item));
612  BOOST_CHECK_EQUAL(m_processor->getConfidence(item), 6);
613  BOOST_CHECK_EQUAL(updates.size(), 0);
614  registerNewVote(next(resp));
615  BOOST_CHECK(m_processor->isAccepted(item));
616  BOOST_CHECK_EQUAL(m_processor->getConfidence(item), 6);
617  BOOST_CHECK_EQUAL(updates.size(), 0);
618 
619  resp = {getRound(), 0, {Vote(0, itemid)}};
620  for (int i = 2; i < 8; i++) {
621  registerNewVote(next(resp));
622  BOOST_CHECK(m_processor->isAccepted(item));
623  BOOST_CHECK_EQUAL(m_processor->getConfidence(item), 6);
624  BOOST_CHECK_EQUAL(updates.size(), 0);
625  }
626 
627  // We vote for it numerous times to finalize it.
628  for (int i = 7; i < AVALANCHE_FINALIZATION_SCORE; i++) {
629  registerNewVote(next(resp));
630  BOOST_CHECK(m_processor->isAccepted(item));
631  BOOST_CHECK_EQUAL(m_processor->getConfidence(item), i);
632  BOOST_CHECK_EQUAL(updates.size(), 0);
633  }
634 
635  // As long as it is not finalized, we poll.
636  invs = getInvsForNextPoll();
637  BOOST_CHECK_EQUAL(invs.size(), 1);
638  BOOST_CHECK_EQUAL(invs[0].type, invType);
639  BOOST_CHECK(invs[0].hash == itemid);
640 
641  // Now finalize the decision.
642  registerNewVote(next(resp));
643  BOOST_CHECK_EQUAL(updates.size(), 1);
644  BOOST_CHECK(provider.fromAnyVoteItem(updates[0].getVoteItem()) == item);
645  BOOST_CHECK(updates[0].getStatus() == VoteStatus::Finalized);
646  updates.clear();
647 
648  // Once the decision is finalized, there is no poll for it.
649  invs = getInvsForNextPoll();
650  BOOST_CHECK_EQUAL(invs.size(), 0);
651 
652  // Get a new item to vote on
653  item = provider.buildVoteItem();
654  itemid = provider.getVoteItemId(item);
655  BOOST_CHECK(addToReconcile(item));
656 
657  // Now let's finalize rejection.
658  invs = getInvsForNextPoll();
659  BOOST_CHECK_EQUAL(invs.size(), 1);
660  BOOST_CHECK_EQUAL(invs[0].type, invType);
661  BOOST_CHECK(invs[0].hash == itemid);
662 
663  resp = {getRound(), 0, {Vote(1, itemid)}};
664  for (int i = 0; i < 6; i++) {
665  registerNewVote(next(resp));
666  BOOST_CHECK(m_processor->isAccepted(item));
667  BOOST_CHECK_EQUAL(updates.size(), 0);
668  }
669 
670  // Now the state will flip.
671  registerNewVote(next(resp));
672  BOOST_CHECK(!m_processor->isAccepted(item));
673  BOOST_CHECK_EQUAL(updates.size(), 1);
674  BOOST_CHECK(provider.fromAnyVoteItem(updates[0].getVoteItem()) == item);
675  BOOST_CHECK(updates[0].getStatus() == VoteStatus::Rejected);
676  updates.clear();
677 
678  // Now it is rejected, but we can vote for it numerous times.
679  for (int i = 1; i < AVALANCHE_FINALIZATION_SCORE; i++) {
680  registerNewVote(next(resp));
681  BOOST_CHECK(!m_processor->isAccepted(item));
682  BOOST_CHECK_EQUAL(updates.size(), 0);
683  }
684 
685  // As long as it is not finalized, we poll.
686  invs = getInvsForNextPoll();
687  BOOST_CHECK_EQUAL(invs.size(), 1);
688  BOOST_CHECK_EQUAL(invs[0].type, invType);
689  BOOST_CHECK(invs[0].hash == itemid);
690 
691  // Now finalize the decision.
692  registerNewVote(next(resp));
693  BOOST_CHECK(!m_processor->isAccepted(item));
694  BOOST_CHECK_EQUAL(updates.size(), 1);
695  BOOST_CHECK(provider.fromAnyVoteItem(updates[0].getVoteItem()) == item);
696  BOOST_CHECK(updates[0].getStatus() == VoteStatus::Invalid);
697  updates.clear();
698 
699  // Once the decision is finalized, there is no poll for it.
700  invs = getInvsForNextPoll();
701  BOOST_CHECK_EQUAL(invs.size(), 0);
702 }
703 
705  P provider(this);
706  const uint32_t invType = provider.invType;
707 
708  auto itemA = provider.buildVoteItem();
709  auto itemidA = provider.getVoteItemId(itemA);
710 
711  auto itemB = provider.buildVoteItem();
712  auto itemidB = provider.getVoteItemId(itemB);
713 
714  // Create several nodes that support avalanche.
715  auto avanodes = ConnectNodes();
716 
717  // Querying for random item returns false.
718  BOOST_CHECK(!m_processor->isAccepted(itemA));
719  BOOST_CHECK(!m_processor->isAccepted(itemB));
720 
721  // Start voting on item A.
722  BOOST_CHECK(addToReconcile(itemA));
723  auto invs = getInvsForNextPoll();
724  BOOST_CHECK_EQUAL(invs.size(), 1);
725  BOOST_CHECK_EQUAL(invs[0].type, invType);
726  BOOST_CHECK(invs[0].hash == itemidA);
727 
728  uint64_t round = getRound();
729  runEventLoop();
730  std::vector<avalanche::VoteItemUpdate> updates;
731  BOOST_CHECK(registerVotes(avanodes[0]->GetId(),
732  {round, 0, {Vote(0, itemidA)}}, updates));
733  BOOST_CHECK_EQUAL(updates.size(), 0);
734 
735  // Start voting on item B after one vote.
736  std::vector<Vote> votes = provider.buildVotesForItems(0, {itemA, itemB});
737  Response resp{round + 1, 0, votes};
738  BOOST_CHECK(addToReconcile(itemB));
739  invs = getInvsForNextPoll();
740  BOOST_CHECK_EQUAL(invs.size(), 2);
741 
742  // Ensure the inv ordering is as expected
743  for (size_t i = 0; i < invs.size(); i++) {
744  BOOST_CHECK_EQUAL(invs[i].type, invType);
745  BOOST_CHECK(invs[i].hash == votes[i].GetHash());
746  }
747 
748  // Let's vote for these items a few times.
749  for (int i = 0; i < 4; i++) {
750  NodeId nodeid = getSuitableNodeToQuery();
751  runEventLoop();
752  BOOST_CHECK(registerVotes(nodeid, next(resp), updates));
753  BOOST_CHECK_EQUAL(updates.size(), 0);
754  }
755 
756  // Now it is accepted, but we can vote for it numerous times.
757  for (int i = 0; i < AVALANCHE_FINALIZATION_SCORE; i++) {
758  NodeId nodeid = getSuitableNodeToQuery();
759  runEventLoop();
760  BOOST_CHECK(registerVotes(nodeid, next(resp), updates));
761  BOOST_CHECK_EQUAL(updates.size(), 0);
762  }
763 
764  // Running two iterration of the event loop so that vote gets triggered on A
765  // and B.
766  NodeId firstNodeid = getSuitableNodeToQuery();
767  runEventLoop();
768  NodeId secondNodeid = getSuitableNodeToQuery();
769  runEventLoop();
770 
771  BOOST_CHECK(firstNodeid != secondNodeid);
772 
773  // Next vote will finalize item A.
774  BOOST_CHECK(registerVotes(firstNodeid, next(resp), updates));
775  BOOST_CHECK_EQUAL(updates.size(), 1);
776  BOOST_CHECK(provider.fromAnyVoteItem(updates[0].getVoteItem()) == itemA);
777  BOOST_CHECK(updates[0].getStatus() == VoteStatus::Finalized);
778  updates.clear();
779 
780  // We do not vote on A anymore.
781  invs = getInvsForNextPoll();
782  BOOST_CHECK_EQUAL(invs.size(), 1);
783  BOOST_CHECK_EQUAL(invs[0].type, invType);
784  BOOST_CHECK(invs[0].hash == itemidB);
785 
786  // Next vote will finalize item B.
787  BOOST_CHECK(registerVotes(secondNodeid, resp, updates));
788  BOOST_CHECK_EQUAL(updates.size(), 1);
789  BOOST_CHECK(provider.fromAnyVoteItem(updates[0].getVoteItem()) == itemB);
790  BOOST_CHECK(updates[0].getStatus() == VoteStatus::Finalized);
791  updates.clear();
792 
793  // There is nothing left to vote on.
794  invs = getInvsForNextPoll();
795  BOOST_CHECK_EQUAL(invs.size(), 0);
796 }
797 
799  P provider(this);
800  const uint32_t invType = provider.invType;
801 
802  auto item = provider.buildVoteItem();
803  auto itemid = provider.getVoteItemId(item);
804 
805  // There is no node to query.
806  BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), NO_NODE);
807 
808  // Add enough nodes to have a valid quorum, and the same amount with no
809  // avalanche support
810  std::set<NodeId> avanodeIds;
811  auto avanodes = ConnectNodes();
812  for (auto avanode : avanodes) {
813  ConnectNode(NODE_NONE);
814  avanodeIds.insert(avanode->GetId());
815  }
816 
817  auto getSelectedAvanodeId = [&]() {
818  NodeId avanodeid = getSuitableNodeToQuery();
819  BOOST_CHECK(avanodeIds.find(avanodeid) != avanodeIds.end());
820  return avanodeid;
821  };
822 
823  // It returns one of the avalanche peer.
824  NodeId avanodeid = getSelectedAvanodeId();
825 
826  // Register an item and check it is added to the list of elements to poll.
827  BOOST_CHECK(addToReconcile(item));
828  auto invs = getInvsForNextPoll();
829  BOOST_CHECK_EQUAL(invs.size(), 1);
830  BOOST_CHECK_EQUAL(invs[0].type, invType);
831  BOOST_CHECK(invs[0].hash == itemid);
832 
833  std::set<NodeId> unselectedNodeids = avanodeIds;
834  unselectedNodeids.erase(avanodeid);
835  const size_t remainingNodeIds = unselectedNodeids.size();
836 
837  uint64_t round = getRound();
838  for (size_t i = 0; i < remainingNodeIds; i++) {
839  // Trigger a poll on avanode.
840  runEventLoop();
841 
842  // Another node is selected
843  NodeId nodeid = getSuitableNodeToQuery();
844  BOOST_CHECK(unselectedNodeids.find(nodeid) != avanodeIds.end());
845  unselectedNodeids.erase(nodeid);
846  }
847 
848  // There is no more suitable peer available, so return nothing.
849  BOOST_CHECK(unselectedNodeids.empty());
850  runEventLoop();
851  BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), NO_NODE);
852 
853  // Respond to the request.
854  Response resp = {round, 0, {Vote(0, itemid)}};
855  std::vector<avalanche::VoteItemUpdate> updates;
856  BOOST_CHECK(registerVotes(avanodeid, resp, updates));
857  BOOST_CHECK_EQUAL(updates.size(), 0);
858 
859  // Now that avanode fullfilled his request, it is added back to the list of
860  // queriable nodes.
861  BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), avanodeid);
862 
863  auto checkRegisterVotesError = [&](NodeId nodeid,
865  const std::string &expectedError) {
866  std::string error;
867  BOOST_CHECK(!registerVotes(nodeid, response, updates, error));
868  BOOST_CHECK_EQUAL(error, expectedError);
869  BOOST_CHECK_EQUAL(updates.size(), 0);
870  };
871 
872  // Sending a response when not polled fails.
873  checkRegisterVotesError(avanodeid, next(resp), "unexpected-ava-response");
874 
875  // Trigger a poll on avanode.
876  round = getRound();
877  runEventLoop();
878  BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), NO_NODE);
879 
880  // Sending responses that do not match the request also fails.
881  // 1. Too many results.
882  resp = {round, 0, {Vote(0, itemid), Vote(0, itemid)}};
883  runEventLoop();
884  checkRegisterVotesError(avanodeid, resp, "invalid-ava-response-size");
885  BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), avanodeid);
886 
887  // 2. Not enough results.
888  resp = {getRound(), 0, {}};
889  runEventLoop();
890  checkRegisterVotesError(avanodeid, resp, "invalid-ava-response-size");
891  BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), avanodeid);
892 
893  // 3. Do not match the poll.
894  resp = {getRound(), 0, {Vote()}};
895  runEventLoop();
896  checkRegisterVotesError(avanodeid, resp, "invalid-ava-response-content");
897  BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), avanodeid);
898 
899  // At this stage we have reached the max inflight requests for our inv, so
900  // it won't be requested anymore until the requests are fullfilled. Let's
901  // vote on another item with no inflight request so the remaining tests
902  // makes sense.
903  invs = getInvsForNextPoll();
904  BOOST_CHECK(invs.empty());
905 
906  item = provider.buildVoteItem();
907  itemid = provider.getVoteItemId(item);
908  BOOST_CHECK(addToReconcile(item));
909 
910  invs = getInvsForNextPoll();
911  BOOST_CHECK_EQUAL(invs.size(), 1);
912 
913  // 4. Invalid round count. Request is not discarded.
914  uint64_t queryRound = getRound();
915  runEventLoop();
916 
917  resp = {queryRound + 1, 0, {Vote()}};
918  checkRegisterVotesError(avanodeid, resp, "unexpected-ava-response");
919 
920  resp = {queryRound - 1, 0, {Vote()}};
921  checkRegisterVotesError(avanodeid, resp, "unexpected-ava-response");
922 
923  // 5. Making request for invalid nodes do not work. Request is not
924  // discarded.
925  resp = {queryRound, 0, {Vote(0, itemid)}};
926  checkRegisterVotesError(avanodeid + 1234, resp, "unexpected-ava-response");
927 
928  // Proper response gets processed and avanode is available again.
929  resp = {queryRound, 0, {Vote(0, itemid)}};
930  BOOST_CHECK(registerVotes(avanodeid, resp, updates));
931  BOOST_CHECK_EQUAL(updates.size(), 0);
932  BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), avanodeid);
933 
934  // Out of order response are rejected.
935  const auto item2 = provider.buildVoteItem();
936  BOOST_CHECK(addToReconcile(item2));
937 
938  std::vector<Vote> votes = provider.buildVotesForItems(0, {item, item2});
939  resp = {getRound(), 0, {votes[1], votes[0]}};
940  runEventLoop();
941  checkRegisterVotesError(avanodeid, resp, "invalid-ava-response-content");
942  BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), avanodeid);
943 
944  // But they are accepted in order.
945  resp = {getRound(), 0, votes};
946  runEventLoop();
947  BOOST_CHECK(registerVotes(avanodeid, resp, updates));
948  BOOST_CHECK_EQUAL(updates.size(), 0);
949  BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), avanodeid);
950 }
951 
953  P provider(this);
954  const uint32_t invType = provider.invType;
955 
956  auto itemA = provider.buildVoteItem();
957  auto itemB = provider.buildVoteItem();
958 
959  auto avanodes = ConnectNodes();
960 
961  // Build votes to get proper ordering
962  std::vector<Vote> votes = provider.buildVotesForItems(0, {itemA, itemB});
963 
964  // Register the items and check they are added to the list of elements to
965  // poll.
966  BOOST_CHECK(addToReconcile(itemA));
967  BOOST_CHECK(addToReconcile(itemB));
968  auto invs = getInvsForNextPoll();
969  BOOST_CHECK_EQUAL(invs.size(), 2);
970  for (size_t i = 0; i < invs.size(); i++) {
971  BOOST_CHECK_EQUAL(invs[i].type, invType);
972  BOOST_CHECK(invs[i].hash == votes[i].GetHash());
973  }
974 
975  // When an item is marked invalid, stop polling.
976  provider.invalidateItem(itemB);
977 
978  Response goodResp{getRound(), 0, {Vote(0, provider.getVoteItemId(itemA))}};
979  std::vector<avalanche::VoteItemUpdate> updates;
980  runEventLoop();
981  BOOST_CHECK(registerVotes(avanodes[0]->GetId(), goodResp, updates));
982  BOOST_CHECK_EQUAL(updates.size(), 0);
983 
984  // Votes including itemB are rejected
985  Response badResp{getRound(), 0, votes};
986  runEventLoop();
987  std::string error;
988  BOOST_CHECK(!registerVotes(avanodes[1]->GetId(), badResp, updates, error));
989  BOOST_CHECK_EQUAL(error, "invalid-ava-response-size");
990 }
991 
992 BOOST_TEST_DECORATOR(*boost::unit_test::timeout(60))
994  P provider(this);
995  ChainstateManager &chainman = *Assert(m_node.chainman);
996 
997  auto queryTimeDuration = std::chrono::milliseconds(10);
998  setArg("-avatimeout", ToString(queryTimeDuration.count()));
999 
1001  m_processor = Processor::MakeProcessor(
1002  *m_node.args, *m_node.chain, m_node.connman.get(), chainman,
1003  m_node.mempool.get(), *m_node.scheduler, error);
1004 
1005  const auto item = provider.buildVoteItem();
1006  const auto itemid = provider.getVoteItemId(item);
1007 
1008  // Add the item
1009  BOOST_CHECK(addToReconcile(item));
1010 
1011  // Create a quorum of nodes that support avalanche.
1012  ConnectNodes();
1013  NodeId avanodeid = NO_NODE;
1014 
1015  // Expire requests after some time.
1016  for (int i = 0; i < 10; i++) {
1017  Response resp = {getRound(), 0, {Vote(0, itemid)}};
1018  avanodeid = getSuitableNodeToQuery();
1019 
1020  auto start = std::chrono::steady_clock::now();
1021  runEventLoop();
1022  // We cannot guarantee that we'll wait for just 1ms, so we have to bail
1023  // if we aren't within the proper time range.
1024  std::this_thread::sleep_for(std::chrono::milliseconds(1));
1025  runEventLoop();
1026 
1027  std::vector<avalanche::VoteItemUpdate> updates;
1028  bool ret = registerVotes(avanodeid, next(resp), updates);
1029  if (std::chrono::steady_clock::now() > start + queryTimeDuration) {
1030  // We waited for too long, bail. Because we can't know for sure when
1031  // previous steps ran, ret is not deterministic and we do not check
1032  // it.
1033  i--;
1034  continue;
1035  }
1036 
1037  // We are within time bounds, so the vote should have worked.
1038  BOOST_CHECK(ret);
1039 
1040  avanodeid = getSuitableNodeToQuery();
1041 
1042  // Now try again but wait for expiration.
1043  runEventLoop();
1044  std::this_thread::sleep_for(queryTimeDuration);
1045  runEventLoop();
1046  BOOST_CHECK(!registerVotes(avanodeid, next(resp), updates));
1047  }
1048 }
1049 
1051  P provider(this);
1052  const uint32_t invType = provider.invType;
1053 
1054  // Create enough nodes so that we run into the inflight request limit.
1055  auto proof = GetProof();
1056  BOOST_CHECK(m_processor->withPeerManager(
1057  [&](avalanche::PeerManager &pm) { return pm.registerProof(proof); }));
1058 
1059  std::array<CNode *, AVALANCHE_MAX_INFLIGHT_POLL + 1> nodes;
1060  for (auto &n : nodes) {
1061  n = ConnectNode(NODE_AVALANCHE);
1062  BOOST_CHECK(addNode(n->GetId(), proof->getId()));
1063  }
1064 
1065  // Add an item to poll
1066  const auto item = provider.buildVoteItem();
1067  const auto itemid = provider.getVoteItemId(item);
1068  BOOST_CHECK(addToReconcile(item));
1069 
1070  // Ensure there are enough requests in flight.
1071  std::map<NodeId, uint64_t> node_round_map;
1072  for (int i = 0; i < AVALANCHE_MAX_INFLIGHT_POLL; i++) {
1073  NodeId nodeid = getSuitableNodeToQuery();
1074  BOOST_CHECK(node_round_map.find(nodeid) == node_round_map.end());
1075  node_round_map.insert(std::pair<NodeId, uint64_t>(nodeid, getRound()));
1076  auto invs = getInvsForNextPoll();
1077  BOOST_CHECK_EQUAL(invs.size(), 1);
1078  BOOST_CHECK_EQUAL(invs[0].type, invType);
1079  BOOST_CHECK(invs[0].hash == itemid);
1080  runEventLoop();
1081  }
1082 
1083  // Now that we have enough in flight requests, we shouldn't poll.
1084  auto suitablenodeid = getSuitableNodeToQuery();
1085  BOOST_CHECK(suitablenodeid != NO_NODE);
1086  auto invs = getInvsForNextPoll();
1087  BOOST_CHECK_EQUAL(invs.size(), 0);
1088  runEventLoop();
1089  BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), suitablenodeid);
1090 
1091  // Send one response, now we can poll again.
1092  auto it = node_round_map.begin();
1093  Response resp = {it->second, 0, {Vote(0, itemid)}};
1094  std::vector<avalanche::VoteItemUpdate> updates;
1095  BOOST_CHECK(registerVotes(it->first, resp, updates));
1096  node_round_map.erase(it);
1097 
1098  invs = getInvsForNextPoll();
1099  BOOST_CHECK_EQUAL(invs.size(), 1);
1100  BOOST_CHECK_EQUAL(invs[0].type, invType);
1101  BOOST_CHECK(invs[0].hash == itemid);
1102 }
1103 
1104 BOOST_AUTO_TEST_CASE(quorum_diversity) {
1105  std::vector<VoteItemUpdate> updates;
1106 
1107  CBlock block = CreateAndProcessBlock({}, CScript());
1108  const BlockHash blockHash = block.GetHash();
1109  const CBlockIndex *pindex;
1110  {
1111  LOCK(cs_main);
1112  pindex =
1113  Assert(m_node.chainman)->m_blockman.LookupBlockIndex(blockHash);
1114  }
1115 
1116  // Create nodes that supports avalanche.
1117  auto avanodes = ConnectNodes();
1118 
1119  // Querying for random block returns false.
1120  BOOST_CHECK(!m_processor->isAccepted(pindex));
1121 
1122  // Add a new block. Check it is added to the polls.
1123  BOOST_CHECK(m_processor->addToReconcile(pindex));
1124 
1125  // Do one valid round of voting.
1126  uint64_t round = getRound();
1127  Response resp{round, 0, {Vote(0, blockHash)}};
1128 
1129  // Check that all nodes can vote.
1130  for (size_t i = 0; i < avanodes.size(); i++) {
1131  runEventLoop();
1132  BOOST_CHECK(registerVotes(avanodes[i]->GetId(), next(resp), updates));
1133  }
1134 
1135  // Generate a query for every single node.
1136  const NodeId firstNodeId = getSuitableNodeToQuery();
1137  std::map<NodeId, uint64_t> node_round_map;
1138  round = getRound();
1139  for (size_t i = 0; i < avanodes.size(); i++) {
1140  NodeId nodeid = getSuitableNodeToQuery();
1141  BOOST_CHECK(node_round_map.find(nodeid) == node_round_map.end());
1142  node_round_map[nodeid] = getRound();
1143  runEventLoop();
1144  }
1145 
1146  // Now only the first node can vote. All others would be duplicate in the
1147  // quorum.
1148  auto confidence = m_processor->getConfidence(pindex);
1149  BOOST_REQUIRE(confidence > 0);
1150 
1151  for (auto &[nodeid, r] : node_round_map) {
1152  if (nodeid == firstNodeId) {
1153  // Node 0 is the only one which can vote at this stage.
1154  round = r;
1155  continue;
1156  }
1157 
1158  BOOST_CHECK(
1159  registerVotes(nodeid, {r, 0, {Vote(0, blockHash)}}, updates));
1160  BOOST_CHECK_EQUAL(m_processor->getConfidence(pindex), confidence);
1161  }
1162 
1163  BOOST_CHECK(
1164  registerVotes(firstNodeId, {round, 0, {Vote(0, blockHash)}}, updates));
1165  BOOST_CHECK_EQUAL(m_processor->getConfidence(pindex), confidence + 1);
1166 }
1167 
1169  CScheduler s;
1170 
1171  CBlock block = CreateAndProcessBlock({}, CScript());
1172  const BlockHash blockHash = block.GetHash();
1173  const CBlockIndex *pindex;
1174  {
1175  LOCK(cs_main);
1176  pindex =
1177  Assert(m_node.chainman)->m_blockman.LookupBlockIndex(blockHash);
1178  }
1179 
1180  // Starting the event loop.
1181  BOOST_CHECK(m_processor->startEventLoop(s));
1182 
1183  // There is one task planned in the next hour (our event loop).
1184  std::chrono::steady_clock::time_point start, stop;
1185  BOOST_CHECK_EQUAL(s.getQueueInfo(start, stop), 1);
1186 
1187  // Starting twice doesn't start it twice.
1188  BOOST_CHECK(!m_processor->startEventLoop(s));
1189 
1190  // Start the scheduler thread.
1191  std::thread schedulerThread(std::bind(&CScheduler::serviceQueue, &s));
1192 
1193  // Create a quorum of nodes that support avalanche.
1194  auto avanodes = ConnectNodes();
1195 
1196  // There is no query in flight at the moment.
1197  NodeId nodeid = getSuitableNodeToQuery();
1198  BOOST_CHECK_NE(nodeid, NO_NODE);
1199 
1200  // Add a new block. Check it is added to the polls.
1201  uint64_t queryRound = getRound();
1202  BOOST_CHECK(m_processor->addToReconcile(pindex));
1203 
1204  // Wait until all nodes got a poll
1205  for (int i = 0; i < 60 * 1000; i++) {
1206  // Technically, this is a race condition, but this should do just fine
1207  // as we wait up to 1 minute for an event that should take 80ms.
1208  UninterruptibleSleep(std::chrono::milliseconds(1));
1209  if (getRound() == queryRound + avanodes.size()) {
1210  break;
1211  }
1212  }
1213 
1214  // Check that we effectively got a request and not timed out.
1215  BOOST_CHECK(getRound() > queryRound);
1216 
1217  // Respond and check the cooldown time is respected.
1218  uint64_t responseRound = getRound();
1219  auto queryTime =
1220  std::chrono::steady_clock::now() + std::chrono::milliseconds(100);
1221 
1222  std::vector<VoteItemUpdate> updates;
1223  // Only the first node answers, so it's the only one that gets polled again
1224  BOOST_CHECK(registerVotes(nodeid, {queryRound, 100, {Vote(0, blockHash)}},
1225  updates));
1226 
1227  for (int i = 0; i < 10000; i++) {
1228  // We make sure that we do not get a request before queryTime.
1229  UninterruptibleSleep(std::chrono::milliseconds(1));
1230  if (getRound() != responseRound) {
1231  BOOST_CHECK(std::chrono::steady_clock::now() > queryTime);
1232  break;
1233  }
1234  }
1235 
1236  // But we eventually get one.
1237  BOOST_CHECK(getRound() > responseRound);
1238 
1239  // Stop event loop.
1240  BOOST_CHECK(m_processor->stopEventLoop());
1241 
1242  // We don't have any task scheduled anymore.
1243  BOOST_CHECK_EQUAL(s.getQueueInfo(start, stop), 0);
1244 
1245  // Can't stop the event loop twice.
1246  BOOST_CHECK(!m_processor->stopEventLoop());
1247 
1248  // Wait for the scheduler to stop.
1249  s.StopWhenDrained();
1250  schedulerThread.join();
1251 }
1252 
1254  CScheduler s;
1255  std::chrono::steady_clock::time_point start, stop;
1256 
1257  std::thread schedulerThread;
1258  BOOST_CHECK(m_processor->startEventLoop(s));
1259  BOOST_CHECK_EQUAL(s.getQueueInfo(start, stop), 1);
1260 
1261  // Start the service thread after the queue size check to prevent a race
1262  // condition where the thread may be processing the event loop task during
1263  // the check.
1264  schedulerThread = std::thread(std::bind(&CScheduler::serviceQueue, &s));
1265 
1266  // Destroy the processor.
1267  m_processor.reset();
1268 
1269  // Now that avalanche is destroyed, there is no more scheduled tasks.
1270  BOOST_CHECK_EQUAL(s.getQueueInfo(start, stop), 0);
1271 
1272  // Wait for the scheduler to stop.
1273  s.StopWhenDrained();
1274  schedulerThread.join();
1275 }
1276 
1277 BOOST_AUTO_TEST_CASE(add_proof_to_reconcile) {
1278  uint32_t score = MIN_VALID_PROOF_SCORE;
1279  Chainstate &active_chainstate = Assert(m_node.chainman)->ActiveChainstate();
1280 
1281  auto addProofToReconcile = [&](uint32_t proofScore) {
1282  auto proof = buildRandomProof(active_chainstate, proofScore);
1283  m_processor->withPeerManager([&](avalanche::PeerManager &pm) {
1284  BOOST_CHECK(pm.registerProof(proof));
1285  });
1286  BOOST_CHECK(m_processor->addToReconcile(proof));
1287  return proof;
1288  };
1289 
1290  for (size_t i = 0; i < AVALANCHE_MAX_ELEMENT_POLL; i++) {
1291  auto proof = addProofToReconcile(++score);
1292 
1293  auto invs = AvalancheTest::getInvsForNextPoll(*m_processor);
1294  BOOST_CHECK_EQUAL(invs.size(), i + 1);
1295  BOOST_CHECK(invs.front().IsMsgProof());
1296  BOOST_CHECK_EQUAL(invs.front().hash, proof->getId());
1297  }
1298 
1299  // From here a new proof is only polled if its score is in the top
1300  // AVALANCHE_MAX_ELEMENT_POLL
1301  ProofId lastProofId;
1302  for (size_t i = 0; i < 10; i++) {
1303  auto proof = addProofToReconcile(++score);
1304 
1305  auto invs = AvalancheTest::getInvsForNextPoll(*m_processor);
1307  BOOST_CHECK(invs.front().IsMsgProof());
1308  BOOST_CHECK_EQUAL(invs.front().hash, proof->getId());
1309 
1310  lastProofId = proof->getId();
1311  }
1312 
1313  for (size_t i = 0; i < 10; i++) {
1314  auto proof = addProofToReconcile(--score);
1315 
1316  auto invs = AvalancheTest::getInvsForNextPoll(*m_processor);
1318  BOOST_CHECK(invs.front().IsMsgProof());
1319  BOOST_CHECK_EQUAL(invs.front().hash, lastProofId);
1320  }
1321 
1322  {
1323  // The score is not high enough to get polled
1324  auto proof = addProofToReconcile(--score);
1325  auto invs = AvalancheTest::getInvsForNextPoll(*m_processor);
1326  for (auto &inv : invs) {
1327  BOOST_CHECK_NE(inv.hash, proof->getId());
1328  }
1329  }
1330 }
1331 
1332 BOOST_AUTO_TEST_CASE(proof_record) {
1333  setArg("-avaproofstakeutxoconfirmations", "2");
1334  setArg("-avalancheconflictingproofcooldown", "0");
1335 
1336  BOOST_CHECK(!m_processor->isAccepted(nullptr));
1337  BOOST_CHECK_EQUAL(m_processor->getConfidence(nullptr), -1);
1338 
1339  const CKey key = CKey::MakeCompressedKey();
1340 
1341  const COutPoint conflictingOutpoint{TxId(GetRandHash()), 0};
1342  const COutPoint immatureOutpoint{TxId(GetRandHash()), 0};
1343  {
1345 
1346  LOCK(cs_main);
1347  CCoinsViewCache &coins =
1348  Assert(m_node.chainman)->ActiveChainstate().CoinsTip();
1349  coins.AddCoin(conflictingOutpoint,
1350  Coin(CTxOut(PROOF_DUST_THRESHOLD, script), 10, false),
1351  false);
1352  coins.AddCoin(immatureOutpoint,
1353  Coin(CTxOut(PROOF_DUST_THRESHOLD, script), 100, false),
1354  false);
1355  }
1356 
1357  auto buildProof = [&](const COutPoint &outpoint, uint64_t sequence,
1358  uint32_t height = 10) {
1359  ProofBuilder pb(sequence, 0, key, UNSPENDABLE_ECREG_PAYOUT_SCRIPT);
1360  BOOST_CHECK(
1361  pb.addUTXO(outpoint, PROOF_DUST_THRESHOLD, height, false, key));
1362  return pb.build();
1363  };
1364 
1365  auto conflictingProof = buildProof(conflictingOutpoint, 1);
1366  auto validProof = buildProof(conflictingOutpoint, 2);
1367  auto immatureProof = buildProof(immatureOutpoint, 3, 100);
1368 
1369  BOOST_CHECK(!m_processor->isAccepted(conflictingProof));
1370  BOOST_CHECK(!m_processor->isAccepted(validProof));
1371  BOOST_CHECK(!m_processor->isAccepted(immatureProof));
1372  BOOST_CHECK_EQUAL(m_processor->getConfidence(conflictingProof), -1);
1373  BOOST_CHECK_EQUAL(m_processor->getConfidence(validProof), -1);
1374  BOOST_CHECK_EQUAL(m_processor->getConfidence(immatureProof), -1);
1375 
1376  // Reconciling proofs that don't exist will fail
1377  BOOST_CHECK(!m_processor->addToReconcile(conflictingProof));
1378  BOOST_CHECK(!m_processor->addToReconcile(validProof));
1379  BOOST_CHECK(!m_processor->addToReconcile(immatureProof));
1380 
1381  m_processor->withPeerManager([&](avalanche::PeerManager &pm) {
1382  BOOST_CHECK(pm.registerProof(conflictingProof));
1383  BOOST_CHECK(pm.registerProof(validProof));
1384  BOOST_CHECK(!pm.registerProof(immatureProof));
1385 
1386  BOOST_CHECK(pm.isBoundToPeer(validProof->getId()));
1387  BOOST_CHECK(pm.isInConflictingPool(conflictingProof->getId()));
1388  BOOST_CHECK(pm.isImmature(immatureProof->getId()));
1389  });
1390 
1391  BOOST_CHECK(m_processor->addToReconcile(conflictingProof));
1392  BOOST_CHECK(!m_processor->isAccepted(conflictingProof));
1393  BOOST_CHECK(!m_processor->isAccepted(validProof));
1394  BOOST_CHECK(!m_processor->isAccepted(immatureProof));
1395  BOOST_CHECK_EQUAL(m_processor->getConfidence(conflictingProof), 0);
1396  BOOST_CHECK_EQUAL(m_processor->getConfidence(validProof), -1);
1397  BOOST_CHECK_EQUAL(m_processor->getConfidence(immatureProof), -1);
1398 
1399  BOOST_CHECK(m_processor->addToReconcile(validProof));
1400  BOOST_CHECK(!m_processor->isAccepted(conflictingProof));
1401  BOOST_CHECK(m_processor->isAccepted(validProof));
1402  BOOST_CHECK(!m_processor->isAccepted(immatureProof));
1403  BOOST_CHECK_EQUAL(m_processor->getConfidence(conflictingProof), 0);
1404  BOOST_CHECK_EQUAL(m_processor->getConfidence(validProof), 0);
1405  BOOST_CHECK_EQUAL(m_processor->getConfidence(immatureProof), -1);
1406 
1407  BOOST_CHECK(!m_processor->addToReconcile(immatureProof));
1408  BOOST_CHECK(!m_processor->isAccepted(conflictingProof));
1409  BOOST_CHECK(m_processor->isAccepted(validProof));
1410  BOOST_CHECK(!m_processor->isAccepted(immatureProof));
1411  BOOST_CHECK_EQUAL(m_processor->getConfidence(conflictingProof), 0);
1412  BOOST_CHECK_EQUAL(m_processor->getConfidence(validProof), 0);
1413  BOOST_CHECK_EQUAL(m_processor->getConfidence(immatureProof), -1);
1414 }
1415 
1416 BOOST_AUTO_TEST_CASE(quorum_detection) {
1417  // Set min quorum parameters for our test
1418  int minStake = 400'000'000;
1419  setArg("-avaminquorumstake", ToString(minStake));
1420  setArg("-avaminquorumconnectedstakeratio", "0.5");
1421 
1422  // Create a new processor with our given quorum parameters
1423  const auto currency = Currency::get();
1424  uint32_t minScore = Proof::amountToScore(minStake * currency.baseunit);
1425 
1426  Chainstate &active_chainstate = Assert(m_node.chainman)->ActiveChainstate();
1427 
1428  const CKey key = CKey::MakeCompressedKey();
1429  auto localProof =
1430  buildRandomProof(active_chainstate, minScore / 4, 100, key);
1431  setArg("-avamasterkey", EncodeSecret(key));
1432  setArg("-avaproof", localProof->ToHex());
1433 
1435  ChainstateManager &chainman = *Assert(m_node.chainman);
1436  m_processor = Processor::MakeProcessor(
1437  *m_node.args, *m_node.chain, m_node.connman.get(), chainman,
1438  m_node.mempool.get(), *m_node.scheduler, error);
1439 
1440  BOOST_CHECK(m_processor != nullptr);
1441  BOOST_CHECK(m_processor->getLocalProof() != nullptr);
1442  BOOST_CHECK_EQUAL(m_processor->getLocalProof()->getId(),
1443  localProof->getId());
1444  BOOST_CHECK_EQUAL(AvalancheTest::getMinQuorumScore(*m_processor), minScore);
1446  AvalancheTest::getMinQuorumConnectedScoreRatio(*m_processor), 0.5);
1447 
1448  // The local proof has not been validated yet
1449  m_processor->withPeerManager([&](avalanche::PeerManager &pm) {
1452  });
1453  BOOST_CHECK(!m_processor->isQuorumEstablished());
1454 
1455  // Register the local proof. This is normally done when the chain tip is
1456  // updated. The local proof should be accounted for in the min quorum
1457  // computation but the peer manager doesn't know about that.
1458  m_processor->withPeerManager([&](avalanche::PeerManager &pm) {
1459  BOOST_CHECK(pm.registerProof(m_processor->getLocalProof()));
1460  BOOST_CHECK(pm.isBoundToPeer(m_processor->getLocalProof()->getId()));
1461  BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), minScore / 4);
1463  });
1464  BOOST_CHECK(!m_processor->isQuorumEstablished());
1465 
1466  // Add enough nodes to get a conclusive vote
1467  for (NodeId id = 0; id < 8; id++) {
1468  m_processor->withPeerManager([&](avalanche::PeerManager &pm) {
1469  pm.addNode(id, m_processor->getLocalProof()->getId());
1470  BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), minScore / 4);
1471  BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), minScore / 4);
1472  });
1473  }
1474 
1475  // Add part of the required stake and make sure we still report no quorum
1476  auto proof1 = buildRandomProof(active_chainstate, minScore / 2);
1477  m_processor->withPeerManager([&](avalanche::PeerManager &pm) {
1478  BOOST_CHECK(pm.registerProof(proof1));
1479  BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), 3 * minScore / 4);
1480  BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), minScore / 4);
1481  });
1482  BOOST_CHECK(!m_processor->isQuorumEstablished());
1483 
1484  // Add the rest of the stake, but we are still lacking connected stake
1485  const int64_t tipTime = chainman.ActiveTip()->GetBlockTime();
1486  const COutPoint utxo{TxId(GetRandHash()), 0};
1487  const Amount amount = (int64_t(minScore / 4) * COIN) / 100;
1488  const int height = 100;
1489  const bool isCoinbase = false;
1490  {
1491  LOCK(cs_main);
1492  CCoinsViewCache &coins = active_chainstate.CoinsTip();
1493  coins.AddCoin(utxo,
1495  PKHash(key.GetPubKey()))),
1496  height, isCoinbase),
1497  false);
1498  }
1499  ProofBuilder pb(1, tipTime + 1, key, UNSPENDABLE_ECREG_PAYOUT_SCRIPT);
1500  BOOST_CHECK(pb.addUTXO(utxo, amount, height, isCoinbase, key));
1501  auto proof2 = pb.build();
1502 
1503  m_processor->withPeerManager([&](avalanche::PeerManager &pm) {
1504  BOOST_CHECK(pm.registerProof(proof2));
1505  BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), minScore);
1506  BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), minScore / 4);
1507  });
1508  BOOST_CHECK(!m_processor->isQuorumEstablished());
1509 
1510  // Adding a node should cause the quorum to be detected and locked-in
1511  m_processor->withPeerManager([&](avalanche::PeerManager &pm) {
1512  pm.addNode(8, proof2->getId());
1513  BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), minScore);
1514  // The peer manager knows that proof2 has a node attached ...
1515  BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), minScore / 2);
1516  });
1517  // ... but the processor also account for the local proof, so we reached 50%
1518  BOOST_CHECK(m_processor->isQuorumEstablished());
1519 
1520  // Go back to not having enough connected score, but we've already latched
1521  // the quorum as established
1522  m_processor->withPeerManager([&](avalanche::PeerManager &pm) {
1523  pm.removeNode(8);
1524  BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), minScore);
1525  BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), minScore / 4);
1526  });
1527  BOOST_CHECK(m_processor->isQuorumEstablished());
1528 
1529  // Removing one more node drops our count below the minimum and the quorum
1530  // is no longer ready
1531  m_processor->withPeerManager(
1532  [&](avalanche::PeerManager &pm) { pm.removeNode(7); });
1533  BOOST_CHECK(!m_processor->isQuorumEstablished());
1534 
1535  // It resumes when we have enough nodes again
1536  m_processor->withPeerManager([&](avalanche::PeerManager &pm) {
1537  pm.addNode(7, m_processor->getLocalProof()->getId());
1538  });
1539  BOOST_CHECK(m_processor->isQuorumEstablished());
1540 
1541  // Remove peers one at a time until the quorum is no longer established
1542  auto spendProofUtxo = [&](ProofRef proof) {
1543  {
1544  LOCK(cs_main);
1545  CCoinsViewCache &coins = chainman.ActiveChainstate().CoinsTip();
1546  coins.SpendCoin(proof->getStakes()[0].getStake().getUTXO());
1547  }
1548  m_processor->withPeerManager([&proof](avalanche::PeerManager &pm) {
1549  pm.updatedBlockTip();
1550  BOOST_CHECK(!pm.isBoundToPeer(proof->getId()));
1551  });
1552  };
1553 
1554  // Expire proof2, the quorum is still latched
1555  for (int64_t i = 0; i < 6; i++) {
1556  SetMockTime(proof2->getExpirationTime() + i);
1557  CreateAndProcessBlock({}, CScript());
1558  }
1560  proof2->getExpirationTime());
1561  m_processor->withPeerManager([&](avalanche::PeerManager &pm) {
1562  pm.updatedBlockTip();
1563  BOOST_CHECK(!pm.exists(proof2->getId()));
1564  });
1565  m_processor->withPeerManager([&](avalanche::PeerManager &pm) {
1566  BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), 3 * minScore / 4);
1567  BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), minScore / 4);
1568  });
1569  BOOST_CHECK(m_processor->isQuorumEstablished());
1570 
1571  spendProofUtxo(proof1);
1572  m_processor->withPeerManager([&](avalanche::PeerManager &pm) {
1573  BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), minScore / 4);
1574  BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), minScore / 4);
1575  });
1576  BOOST_CHECK(m_processor->isQuorumEstablished());
1577 
1578  spendProofUtxo(m_processor->getLocalProof());
1579  m_processor->withPeerManager([&](avalanche::PeerManager &pm) {
1582  });
1583  // There is no node left
1584  BOOST_CHECK(!m_processor->isQuorumEstablished());
1585 }
1586 
1587 BOOST_AUTO_TEST_CASE(quorum_detection_parameter_validation) {
1588  // Create vector of tuples of:
1589  // <min stake, min ratio, min avaproofs messages, success bool>
1590  const std::vector<std::tuple<std::string, std::string, std::string, bool>>
1591  testCases = {
1592  // All parameters are invalid
1593  {"", "", "", false},
1594  {"-1", "-1", "-1", false},
1595 
1596  // Min stake is out of range
1597  {"-1", "0", "0", false},
1598  {"-0.01", "0", "0", false},
1599  {"21000000000000.01", "0", "0", false},
1600 
1601  // Min connected ratio is out of range
1602  {"0", "-1", "0", false},
1603  {"0", "1.1", "0", false},
1604 
1605  // Min avaproofs messages ratio is out of range
1606  {"0", "0", "-1", false},
1607 
1608  // All parameters are valid
1609  {"0", "0", "0", true},
1610  {"0.00", "0", "0", true},
1611  {"0.01", "0", "0", true},
1612  {"1", "0.1", "0", true},
1613  {"10", "0.5", "0", true},
1614  {"10", "1", "0", true},
1615  {"21000000000000.00", "0", "0", true},
1616  {"0", "0", "1", true},
1617  {"0", "0", "100", true},
1618  };
1619 
1620  // For each case set the parameters and check that making the processor
1621  // succeeds or fails as expected
1622  for (const auto &[stake, stakeRatio, numProofsMessages, success] :
1623  testCases) {
1624  setArg("-avaminquorumstake", stake);
1625  setArg("-avaminquorumconnectedstakeratio", stakeRatio);
1626  setArg("-avaminavaproofsnodecount", numProofsMessages);
1627 
1629  std::unique_ptr<Processor> processor = Processor::MakeProcessor(
1630  *m_node.args, *m_node.chain, m_node.connman.get(),
1631  *Assert(m_node.chainman), m_node.mempool.get(), *m_node.scheduler,
1632  error);
1633 
1634  if (success) {
1635  BOOST_CHECK(processor != nullptr);
1636  BOOST_CHECK(error.empty());
1637  BOOST_CHECK_EQUAL(error.original, "");
1638  } else {
1639  BOOST_CHECK(processor == nullptr);
1640  BOOST_CHECK(!error.empty());
1641  BOOST_CHECK(error.original != "");
1642  }
1643  }
1644 }
1645 
1646 BOOST_AUTO_TEST_CASE(min_avaproofs_messages) {
1647  ChainstateManager &chainman = *Assert(m_node.chainman);
1648 
1649  auto checkMinAvaproofsMessages = [&](int64_t minAvaproofsMessages) {
1650  setArg("-avaminavaproofsnodecount", ToString(minAvaproofsMessages));
1651 
1653  auto processor = Processor::MakeProcessor(
1654  *m_node.args, *m_node.chain, m_node.connman.get(), chainman,
1655  m_node.mempool.get(), *m_node.scheduler, error);
1656 
1657  auto addNode = [&](NodeId nodeid) {
1658  auto proof = buildRandomProof(chainman.ActiveChainstate(),
1660  processor->withPeerManager([&](avalanche::PeerManager &pm) {
1661  BOOST_CHECK(pm.registerProof(proof));
1662  BOOST_CHECK(pm.addNode(nodeid, proof->getId()));
1663  });
1664  };
1665 
1666  // Add enough node to have a conclusive vote, but don't account any
1667  // avaproofs.
1668  // NOTE: we can't use the test facilites like ConnectNodes() because we
1669  // are not testing on m_processor.
1670  for (NodeId id = 100; id < 108; id++) {
1671  addNode(id);
1672  }
1673 
1674  BOOST_CHECK_EQUAL(processor->isQuorumEstablished(),
1675  minAvaproofsMessages <= 0);
1676 
1677  for (int64_t i = 0; i < minAvaproofsMessages - 1; i++) {
1678  addNode(i);
1679 
1680  processor->avaproofsSent(i);
1681  BOOST_CHECK_EQUAL(processor->getAvaproofsNodeCounter(), i + 1);
1682 
1683  // Receiving again on the same node does not increase the counter
1684  processor->avaproofsSent(i);
1685  BOOST_CHECK_EQUAL(processor->getAvaproofsNodeCounter(), i + 1);
1686 
1687  BOOST_CHECK(!processor->isQuorumEstablished());
1688  }
1689 
1690  addNode(minAvaproofsMessages);
1691  processor->avaproofsSent(minAvaproofsMessages);
1692  BOOST_CHECK(processor->isQuorumEstablished());
1693 
1694  // Check the latch
1695  AvalancheTest::clearavaproofsNodeCounter(*processor);
1696  BOOST_CHECK(processor->isQuorumEstablished());
1697  };
1698 
1699  checkMinAvaproofsMessages(0);
1700  checkMinAvaproofsMessages(1);
1701  checkMinAvaproofsMessages(10);
1702  checkMinAvaproofsMessages(100);
1703 }
1704 
1706  // Check that setting voting parameters has the expected effect
1707  setArg("-avastalevotethreshold",
1709  setArg("-avastalevotefactor", "2");
1710 
1711  const std::vector<std::tuple<int, int>> testCases = {
1712  // {number of yes votes, number of neutral votes}
1715  };
1716 
1718  m_processor = Processor::MakeProcessor(
1719  *m_node.args, *m_node.chain, m_node.connman.get(),
1720  *Assert(m_node.chainman), m_node.mempool.get(), *m_node.scheduler,
1721  error);
1722 
1723  BOOST_CHECK(m_processor != nullptr);
1724  BOOST_CHECK(error.empty());
1725 
1726  P provider(this);
1727  const uint32_t invType = provider.invType;
1728 
1729  const auto item = provider.buildVoteItem();
1730  const auto itemid = provider.getVoteItemId(item);
1731 
1732  // Create nodes that supports avalanche.
1733  auto avanodes = ConnectNodes();
1734  int nextNodeIndex = 0;
1735 
1736  std::vector<avalanche::VoteItemUpdate> updates;
1737  for (const auto &[numYesVotes, numNeutralVotes] : testCases) {
1738  // Add a new item. Check it is added to the polls.
1739  BOOST_CHECK(addToReconcile(item));
1740  auto invs = getInvsForNextPoll();
1741  BOOST_CHECK_EQUAL(invs.size(), 1);
1742  BOOST_CHECK_EQUAL(invs[0].type, invType);
1743  BOOST_CHECK(invs[0].hash == itemid);
1744 
1745  BOOST_CHECK(m_processor->isAccepted(item));
1746 
1747  auto registerNewVote = [&](const Response &resp) {
1748  runEventLoop();
1749  auto nodeid = avanodes[nextNodeIndex++ % avanodes.size()]->GetId();
1750  BOOST_CHECK(registerVotes(nodeid, resp, updates));
1751  };
1752 
1753  // Add some confidence
1754  for (int i = 0; i < numYesVotes; i++) {
1755  Response resp = {getRound(), 0, {Vote(0, itemid)}};
1756  registerNewVote(next(resp));
1757  BOOST_CHECK(m_processor->isAccepted(item));
1758  BOOST_CHECK_EQUAL(m_processor->getConfidence(item),
1759  i >= 6 ? i - 5 : 0);
1760  BOOST_CHECK_EQUAL(updates.size(), 0);
1761  }
1762 
1763  // Vote until just before item goes stale
1764  for (int i = 0; i < numNeutralVotes; i++) {
1765  Response resp = {getRound(), 0, {Vote(-1, itemid)}};
1766  registerNewVote(next(resp));
1767  BOOST_CHECK_EQUAL(updates.size(), 0);
1768  }
1769 
1770  // As long as it is not stale, we poll.
1771  invs = getInvsForNextPoll();
1772  BOOST_CHECK_EQUAL(invs.size(), 1);
1773  BOOST_CHECK_EQUAL(invs[0].type, invType);
1774  BOOST_CHECK(invs[0].hash == itemid);
1775 
1776  // Now stale
1777  Response resp = {getRound(), 0, {Vote(-1, itemid)}};
1778  registerNewVote(next(resp));
1779  BOOST_CHECK_EQUAL(updates.size(), 1);
1780  BOOST_CHECK(provider.fromAnyVoteItem(updates[0].getVoteItem()) == item);
1781  BOOST_CHECK(updates[0].getStatus() == VoteStatus::Stale);
1782  updates.clear();
1783 
1784  // Once stale, there is no poll for it.
1785  invs = getInvsForNextPoll();
1786  BOOST_CHECK_EQUAL(invs.size(), 0);
1787  }
1788 }
1789 
1790 BOOST_AUTO_TEST_CASE(block_vote_finalization_tip) {
1791  BlockProvider provider(this);
1792 
1793  std::vector<CBlockIndex *> blockIndexes;
1794  for (size_t i = 0; i < AVALANCHE_MAX_ELEMENT_POLL; i++) {
1795  CBlockIndex *pindex = provider.buildVoteItem();
1796  BOOST_CHECK(addToReconcile(pindex));
1797  blockIndexes.push_back(pindex);
1798  }
1799 
1800  auto invs = getInvsForNextPoll();
1802  for (size_t i = 0; i < AVALANCHE_MAX_ELEMENT_POLL; i++) {
1804  invs[i].hash,
1805  blockIndexes[AVALANCHE_MAX_ELEMENT_POLL - i - 1]->GetBlockHash());
1806  }
1807 
1808  // Build a vote vector with the 11th block only being accepted and others
1809  // unknown.
1810  const BlockHash eleventhBlockHash =
1811  blockIndexes[AVALANCHE_MAX_ELEMENT_POLL - 10 - 1]->GetBlockHash();
1812  std::vector<Vote> votes;
1813  votes.reserve(AVALANCHE_MAX_ELEMENT_POLL);
1814  for (size_t i = AVALANCHE_MAX_ELEMENT_POLL; i > 0; i--) {
1815  BlockHash blockhash = blockIndexes[i - 1]->GetBlockHash();
1816  votes.emplace_back(blockhash == eleventhBlockHash ? 0 : -1, blockhash);
1817  }
1818 
1819  auto avanodes = ConnectNodes();
1820  int nextNodeIndex = 0;
1821 
1822  std::vector<avalanche::VoteItemUpdate> updates;
1823  auto registerNewVote = [&]() {
1824  Response resp = {getRound(), 0, votes};
1825  runEventLoop();
1826  auto nodeid = avanodes[nextNodeIndex++ % avanodes.size()]->GetId();
1827  BOOST_CHECK(registerVotes(nodeid, resp, updates));
1828  };
1829 
1830  // Vote for the blocks until the one being accepted finalizes
1831  bool eleventhBlockFinalized = false;
1832  for (size_t i = 0; i < 10000 && !eleventhBlockFinalized; i++) {
1833  registerNewVote();
1834 
1835  for (auto &update : updates) {
1836  if (update.getStatus() == VoteStatus::Finalized &&
1837  provider.fromAnyVoteItem(update.getVoteItem())
1838  ->GetBlockHash() == eleventhBlockHash) {
1839  eleventhBlockFinalized = true;
1840  }
1841  }
1842  }
1843  BOOST_CHECK(eleventhBlockFinalized);
1844 
1845  // From now only the 10 blocks with more work are polled for
1846  invs = getInvsForNextPoll();
1847  BOOST_CHECK_EQUAL(invs.size(), 10);
1848  for (size_t i = 0; i < 10; i++) {
1850  invs[i].hash,
1851  blockIndexes[AVALANCHE_MAX_ELEMENT_POLL - i - 1]->GetBlockHash());
1852  }
1853 
1854  // Adding ancestor blocks to reconcile will fail
1855  for (size_t i = 0; i < AVALANCHE_MAX_ELEMENT_POLL - 10 - 1; i++) {
1856  BOOST_CHECK(!addToReconcile(blockIndexes[i]));
1857  }
1858 
1859  // Create a couple concurrent chain tips
1860  CBlockIndex *tip = provider.buildVoteItem();
1861 
1862  const auto &config = GetConfig();
1863  auto &activeChainstate = m_node.chainman->ActiveChainstate();
1864  BlockValidationState state;
1865  activeChainstate.InvalidateBlock(config, state, tip);
1866 
1867  // Use another script to make sure we don't generate the same block again
1868  CBlock altblock = CreateAndProcessBlock({}, CScript() << OP_TRUE);
1869  auto alttip = WITH_LOCK(
1870  cs_main, return Assert(m_node.chainman)
1871  ->m_blockman.LookupBlockIndex(altblock.GetHash()));
1872  BOOST_CHECK(alttip);
1873  BOOST_CHECK(alttip->pprev == tip->pprev);
1874  BOOST_CHECK(alttip->GetBlockHash() != tip->GetBlockHash());
1875 
1876  // Reconsider the previous tip valid, so we have concurrent tip candidates
1877  {
1878  LOCK(cs_main);
1879  activeChainstate.ResetBlockFailureFlags(tip);
1880  }
1881  activeChainstate.ActivateBestChain(config, state);
1882 
1883  BOOST_CHECK(addToReconcile(tip));
1884  BOOST_CHECK(addToReconcile(alttip));
1885  invs = getInvsForNextPoll();
1886  BOOST_CHECK_EQUAL(invs.size(), 12);
1887 
1888  // Vote for the tip until it finalizes
1889  BlockHash tiphash = tip->GetBlockHash();
1890  votes.clear();
1891  votes.reserve(12);
1892  for (auto &inv : invs) {
1893  votes.emplace_back(inv.hash == tiphash ? 0 : -1, inv.hash);
1894  }
1895 
1896  bool tipFinalized = false;
1897  for (size_t i = 0; i < 10000 && !tipFinalized; i++) {
1898  registerNewVote();
1899 
1900  for (auto &update : updates) {
1901  if (update.getStatus() == VoteStatus::Finalized &&
1902  provider.fromAnyVoteItem(update.getVoteItem())
1903  ->GetBlockHash() == tiphash) {
1904  tipFinalized = true;
1905  }
1906  }
1907  }
1908  BOOST_CHECK(tipFinalized);
1909 
1910  // Now the tip and all its ancestors will be removed from polls. Only the
1911  // alttip remains because it is on a forked chain so we want to keep polling
1912  // for that one until it's invalidated or stalled.
1913  invs = getInvsForNextPoll();
1914  BOOST_CHECK_EQUAL(invs.size(), 1);
1915  BOOST_CHECK_EQUAL(invs[0].hash, alttip->GetBlockHash());
1916 
1917  // Cannot reconcile a finalized block
1918  BOOST_CHECK(!addToReconcile(tip));
1919 
1920  // Vote for alttip until it invalidates
1921  BlockHash alttiphash = alttip->GetBlockHash();
1922  votes = {{1, alttiphash}};
1923 
1924  bool alttipInvalidated = false;
1925  for (size_t i = 0; i < 10000 && !alttipInvalidated; i++) {
1926  registerNewVote();
1927 
1928  for (auto &update : updates) {
1929  if (update.getStatus() == VoteStatus::Invalid &&
1930  provider.fromAnyVoteItem(update.getVoteItem())
1931  ->GetBlockHash() == alttiphash) {
1932  alttipInvalidated = true;
1933  }
1934  }
1935  }
1936  BOOST_CHECK(alttipInvalidated);
1937  invs = getInvsForNextPoll();
1938  BOOST_CHECK_EQUAL(invs.size(), 0);
1939 
1940  // Cannot reconcile an invalidated block
1941  BOOST_CHECK(!addToReconcile(alttip));
1942 }
1943 
1944 BOOST_AUTO_TEST_CASE(vote_map_comparator) {
1945  ChainstateManager &chainman = *Assert(m_node.chainman);
1946  Chainstate &activeChainState = chainman.ActiveChainstate();
1947 
1948  const int numberElementsEachType = 100;
1949  FastRandomContext rng;
1950 
1951  std::vector<ProofRef> proofs;
1952  for (size_t i = 1; i <= numberElementsEachType; i++) {
1953  auto proof =
1954  buildRandomProof(activeChainState, i * MIN_VALID_PROOF_SCORE);
1955  BOOST_CHECK(proof != nullptr);
1956  proofs.emplace_back(std::move(proof));
1957  }
1958  Shuffle(proofs.begin(), proofs.end(), rng);
1959 
1960  std::vector<CBlockIndex> indexes;
1961  for (size_t i = 1; i <= numberElementsEachType; i++) {
1962  CBlockIndex index;
1963  index.nChainWork = i;
1964  indexes.emplace_back(std::move(index));
1965  }
1966  Shuffle(indexes.begin(), indexes.end(), rng);
1967 
1968  auto allItems = std::make_tuple(std::move(proofs), std::move(indexes));
1969  static const size_t numTypes = std::tuple_size<decltype(allItems)>::value;
1970 
1971  RWCollection<VoteMap> voteMap;
1972 
1973  {
1974  auto writeView = voteMap.getWriteView();
1975  for (size_t i = 0; i < numberElementsEachType; i++) {
1976  // Randomize the insert order at each loop increment
1977  const size_t firstType = rng.randrange(numTypes);
1978 
1979  for (size_t j = 0; j < numTypes; j++) {
1980  switch ((firstType + j) % numTypes) {
1981  // ProofRef
1982  case 0:
1983  writeView->insert(std::make_pair(
1984  std::get<0>(allItems)[i], VoteRecord(true)));
1985  break;
1986  // CBlockIndex *
1987  case 1:
1988  writeView->insert(std::make_pair(
1989  &std::get<1>(allItems)[i], VoteRecord(true)));
1990  break;
1991  default:
1992  break;
1993  }
1994  }
1995  }
1996  }
1997 
1998  {
1999  // Check ordering
2000  auto readView = voteMap.getReadView();
2001  auto it = readView.begin();
2002 
2003  // The first batch of items is the proofs ordered by score (descending)
2004  uint32_t lastScore = std::numeric_limits<uint32_t>::max();
2005  for (size_t i = 0; i < numberElementsEachType; i++) {
2006  BOOST_CHECK(std::holds_alternative<const ProofRef>(it->first));
2007 
2008  uint32_t currentScore =
2009  std::get<const ProofRef>(it->first)->getScore();
2010  BOOST_CHECK_LT(currentScore, lastScore);
2011  lastScore = currentScore;
2012 
2013  it++;
2014  }
2015 
2016  // The next batch of items is the block indexes ordered by work
2017  // (descending)
2018  arith_uint256 lastWork = -1;
2019  for (size_t i = 0; i < numberElementsEachType; i++) {
2020  BOOST_CHECK(std::holds_alternative<const CBlockIndex *>(it->first));
2021 
2022  arith_uint256 currentWork =
2023  std::get<const CBlockIndex *>(it->first)->nChainWork;
2024  BOOST_CHECK(currentWork < lastWork);
2025  lastWork = currentWork;
2026 
2027  it++;
2028  }
2029 
2030  BOOST_CHECK(it == readView.end());
2031  }
2032 }
2033 
2034 BOOST_AUTO_TEST_CASE(block_reconcile_initial_vote) {
2035  const auto &config = GetConfig();
2036  auto &chainman = Assert(m_node.chainman);
2037  Chainstate &chainstate = chainman->ActiveChainstate();
2038 
2039  const auto block = std::make_shared<const CBlock>(
2040  this->CreateBlock({}, CScript(), chainstate));
2041  const BlockHash blockhash = block->GetHash();
2042 
2043  BlockValidationState state;
2044  CBlockIndex *blockindex;
2045  {
2046  LOCK(cs_main);
2047  BOOST_CHECK(chainstate.AcceptBlock(config, block, state,
2048  /*fRequested=*/true, /*dbp=*/nullptr,
2049  /*fNewBlock=*/nullptr));
2050 
2051  blockindex = chainman->m_blockman.LookupBlockIndex(blockhash);
2052  BOOST_CHECK(blockindex);
2053  }
2054 
2055  // ActivateBestChain() interacts with g_avalanche, so make it happy
2056  g_avalanche = std::move(m_processor);
2057 
2058  // The block is not connected yet, and not added to the poll list yet
2059  BOOST_CHECK(AvalancheTest::getInvsForNextPoll(*g_avalanche).empty());
2060  BOOST_CHECK(!g_avalanche->isAccepted(blockindex));
2061 
2062  // Call ActivateBestChain to connect the new block
2063  BOOST_CHECK(chainstate.ActivateBestChain(config, state, block));
2064  // It is a valid block so the tip is updated
2065  BOOST_CHECK_EQUAL(chainstate.m_chain.Tip(), blockindex);
2066 
2067  // Check the block is added to the poll
2068  auto invs = AvalancheTest::getInvsForNextPoll(*g_avalanche);
2069  BOOST_CHECK_EQUAL(invs.size(), 1);
2070  BOOST_CHECK_EQUAL(invs[0].type, MSG_BLOCK);
2071  BOOST_CHECK_EQUAL(invs[0].hash, blockhash);
2072 
2073  // This block is our new tip so we should vote "yes"
2074  BOOST_CHECK(g_avalanche->isAccepted(blockindex));
2075 
2076  // Prevent a data race between UpdatedBlockTip and the Processor destructor
2078 
2079  g_avalanche.reset(nullptr);
2080 }
2081 
static constexpr Amount COIN
Definition: amount.h:144
std::unique_ptr< avalanche::Processor > g_avalanche
Global avalanche instance.
Definition: processor.cpp:34
RecursiveMutex cs_main
Global state.
Definition: validation.cpp:113
const CChainParams & Params()
Return the currently selected parameters.
#define Assert(val)
Identity function.
Definition: check.h:65
void ForceSetArg(const std::string &strArg, const std::string &strValue)
Definition: system.cpp:626
void ClearForcedArg(const std::string &strArg)
Remove a forced arg setting, used only in testing.
Definition: system.cpp:677
A CService with information about it as peer.
Definition: protocol.h:445
BlockHash GetHash() const
Definition: block.cpp:11
Definition: block.h:55
The block chain is a tree shaped structure starting with the genesis block at the root,...
Definition: blockindex.h:26
CBlockIndex * pprev
pointer to the index of the predecessor of this block
Definition: blockindex.h:33
arith_uint256 nChainWork
(memory only) Total amount of work (expected number of hashes) in the chain up to and including this ...
Definition: blockindex.h:52
int64_t GetBlockTime() const
Definition: blockindex.h:174
int64_t GetMedianTimePast() const
Definition: blockindex.h:186
BlockHash GetBlockHash() const
Definition: blockindex.h:147
CBlockIndex * Tip() const
Returns the index entry for the tip of this chain, or nullptr if none.
Definition: chain.h:157
CCoinsView that adds a memory cache for transactions to another CCoinsView.
Definition: coins.h:203
void AddCoin(const COutPoint &outpoint, Coin coin, bool possible_overwrite)
Add a coin.
Definition: coins.cpp:100
bool SpendCoin(const COutPoint &outpoint, Coin *moveto=nullptr)
Spend a coin.
Definition: coins.cpp:168
Definition: net.h:907
CConnman(const Config &configIn, uint64_t seed0, uint64_t seed1, AddrMan &addrmanIn, bool network_active=true)
Definition: net.cpp:2840
An encapsulated secp256k1 private key.
Definition: key.h:28
static CKey MakeCompressedKey()
Produce a valid compressed key.
Definition: key.cpp:466
CPubKey GetPubKey() const
Compute the public key from a private key.
Definition: key.cpp:210
A mutable version of CTransaction.
Definition: transaction.h:280
std::vector< CTxOut > vout
Definition: transaction.h:283
std::vector< CTxIn > vin
Definition: transaction.h:282
Network address.
Definition: netaddress.h:121
Information about a peer.
Definition: net.h:443
An outpoint - a combination of a transaction hash and an index n into its vout.
Definition: transaction.h:22
Simple class for background tasks that should be run periodically or once "after a while".
Definition: scheduler.h:41
void serviceQueue()
Services the queue 'forever'.
Definition: scheduler.cpp:23
size_t getQueueInfo(std::chrono::steady_clock::time_point &first, std::chrono::steady_clock::time_point &last) const
Returns number of tasks waiting to be serviced, and first and last task times.
Definition: scheduler.cpp:120
void StopWhenDrained()
Tell any threads running serviceQueue to stop when there is no work left to be done.
Definition: scheduler.h:96
Serialized script, used inside transaction inputs and outputs.
Definition: script.h:431
A combination of a network address (CNetAddr) and a (TCP) port.
Definition: netaddress.h:544
CTxMemPool stores valid-according-to-the-current-best-chain transactions that may be included in the ...
Definition: txmempool.h:355
RecursiveMutex cs
This mutex needs to be locked when accessing mapTx or other members that are guarded by it.
Definition: txmempool.h:441
void removeRecursive(const CTransaction &tx, MemPoolRemovalReason reason) EXCLUSIVE_LOCKS_REQUIRED(cs)
Definition: txmempool.cpp:539
bool exists(const TxId &txid) const
Definition: txmempool.h:707
void check(const CCoinsViewCache &active_coins_tip, int64_t spendheight) const EXCLUSIVE_LOCKS_REQUIRED(void addUnchecked(const CTxMemPoolEntry &entry) EXCLUSIVE_LOCKS_REQUIRED(cs
If sanity-checking is turned on, check makes sure the pool is consistent (does not contain two transa...
Definition: txmempool.h:522
An output of a transaction.
Definition: transaction.h:130
Chainstate stores and provides an API to update our local knowledge of the current best chain.
Definition: validation.h:646
bool AcceptBlock(const Config &config, const std::shared_ptr< const CBlock > &pblock, BlockValidationState &state, bool fRequested, const FlatFilePos *dbp, bool *fNewBlock) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
Store a block on disk.
CChain m_chain
The current chain of blockheaders we consult and build on.
Definition: validation.h:744
CCoinsViewCache & CoinsTip() EXCLUSIVE_LOCKS_REQUIRED(
Definition: validation.h:770
bool ActivateBestChain(const Config &config, BlockValidationState &state, std::shared_ptr< const CBlock > pblock=nullptr) EXCLUSIVE_LOCKS_REQUIRED(!m_chainstate_mutex) LOCKS_EXCLUDED(cs_main)
Find the best known block, and make it the tip of the block chain.
Provides an interface for creating and interacting with one or two chainstates: an IBD chainstate gen...
Definition: validation.h:1060
Chainstate & ActiveChainstate() const
The most-work chain.
CBlockIndex * ActiveTip() const
Definition: validation.h:1201
A UTXO entry.
Definition: coins.h:27
Fast randomness source.
Definition: random.h:129
uint64_t randrange(uint64_t range) noexcept
Generate a random integer in the range [0..range).
Definition: random.h:204
static std::unique_ptr< PeerManager > make(const CChainParams &chainparams, CConnman &connman, AddrMan &addrman, BanMan *banman, ChainstateManager &chainman, CTxMemPool &pool, bool ignore_incoming_txs)
ReadView getReadView() const
Definition: rwcollection.h:76
WriteView getWriteView()
Definition: rwcollection.h:82
iterator begin()
Definition: rwcollection.h:41
256-bit unsigned big integer.
bool removeNode(NodeId nodeid)
Definition: peermanager.cpp:83
uint32_t getConnectedPeersScore() const
Definition: peermanager.h:381
bool exists(const ProofId &proofid) const
Definition: peermanager.h:346
uint32_t getTotalPeersScore() const
Definition: peermanager.h:380
bool addNode(NodeId nodeid, const ProofId &proofid)
Node API.
Definition: peermanager.cpp:18
std::unordered_set< ProofRef, SaltedProofHasher > updatedBlockTip()
Update the peer set when a new block is connected.
bool isBoundToPeer(const ProofId &proofid) const
bool isImmature(const ProofId &proofid) const
bool rejectProof(const ProofId &proofid, RejectionMode mode=RejectionMode::DEFAULT)
bool isInConflictingPool(const ProofId &proofid) const
bool registerProof(const ProofRef &proof, ProofRegistrationState &registrationState, RegistrationMode mode=RegistrationMode::DEFAULT)
std::atomic< uint64_t > round
Keep track of peers and queries sent.
Definition: processor.h:160
static std::unique_ptr< Processor > MakeProcessor(const ArgsManager &argsman, interfaces::Chain &chain, CConnman *connman, ChainstateManager &chainman, CTxMemPool *mempoolIn, CScheduler &scheduler, bilingual_str &error)
Definition: processor.cpp:200
RWCollection< VoteMap > voteRecords
Items to run avalanche on.
Definition: processor.h:155
uint32_t minQuorumScore
Quorum management.
Definition: processor.h:209
std::atomic< int64_t > avaproofsNodeCounter
Definition: processor.h:214
std::vector< CInv > getInvsForNextPoll(bool forPoll=true)
Definition: processor.cpp:929
Mutex cs_peerManager
Keep track of the peers and associated infos.
Definition: processor.h:165
double minQuorumConnectedScoreRatio
Definition: processor.h:210
bool addUTXO(COutPoint utxo, Amount amount, uint32_t height, bool is_coinbase, CKey key)
const ProofId & getId() const
Definition: proof.h:169
const std::vector< SignedStake > & getStakes() const
Definition: proof.h:165
static uint32_t amountToScore(Amount amount)
Definition: proof.cpp:99
uint32_t getCooldown() const
Definition: protocol.h:44
uint64_t getRound() const
Definition: protocol.h:43
const std::vector< Vote > & GetVotes() const
Definition: protocol.h:45
const VoteStatus & getStatus() const
Definition: processor.h:95
const AnyVoteItem & getVoteItem() const
Definition: processor.h:96
unsigned int size() const
Definition: uint256.h:91
256-bit opaque blob.
Definition: uint256.h:127
#define INVALID_SOCKET
Definition: compat.h:52
const Config & GetConfig()
Definition: config.cpp:34
std::string EncodeSecret(const CKey &key)
Definition: key_io.cpp:105
static constexpr Amount PROOF_DUST_THRESHOLD
Minimum amount per utxo.
Definition: proof.h:40
const CScript UNSPENDABLE_ECREG_PAYOUT_SCRIPT
Definition: util.h:19
ProofRef buildRandomProof(Chainstate &active_chainstate, uint32_t score, int height, const CKey &masterKey)
Definition: util.cpp:20
constexpr uint32_t MIN_VALID_PROOF_SCORE
Definition: util.h:17
std::variant< const ProofRef, const CBlockIndex *, const CTransactionRef > AnyVoteItem
Definition: processor.h:85
std::unique_ptr< Chain > MakeChain(node::NodeContext &node, const CChainParams &params)
Return implementation of Chain interface.
Definition: interfaces.cpp:778
Definition: init.h:28
@ OUTBOUND_FULL_RELAY
These are the default connections that we use to connect with the network.
NodeContext & m_node
Definition: interfaces.cpp:768
static constexpr NodeId NO_NODE
Special NodeId that represent no node.
Definition: nodeid.h:15
int64_t NodeId
Definition: nodeid.h:10
#define BOOST_AUTO_TEST_SUITE_END()
Definition: object.cpp:16
#define BOOST_FIXTURE_TEST_SUITE(a, b)
Definition: object.cpp:14
#define BOOST_CHECK_EQUAL(v1, v2)
Definition: object.cpp:18
#define BOOST_CHECK(expr)
Definition: object.cpp:17
static CTransactionRef MakeTransactionRef()
Definition: transaction.h:322
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:321
Response response
Definition: processor.cpp:440
static constexpr size_t AVALANCHE_MAX_ELEMENT_POLL
Maximum item that can be polled at once.
Definition: processor.h:50
static constexpr uint32_t AVALANCHE_FINALIZED_ITEMS_FILTER_NUM_ELEMENTS
The size of the finalized items filter.
Definition: processor.h:66
BOOST_AUTO_TEST_CASE_TEMPLATE(voteitemupdate, P, VoteItemProviders)
BOOST_AUTO_TEST_CASE(quorum_diversity)
boost::mpl::list< BlockProvider, ProofProvider, TxProvider > VoteItemProviders
@ MSG_TX
Definition: protocol.h:501
@ MSG_AVA_PROOF
Definition: protocol.h:508
@ MSG_BLOCK
Definition: protocol.h:502
ServiceFlags
nServices flags.
Definition: protocol.h:338
@ NODE_NONE
Definition: protocol.h:341
@ NODE_NETWORK
Definition: protocol.h:345
@ NODE_AVALANCHE
Definition: protocol.h:383
uint256 GetRandHash() noexcept
Definition: random.cpp:658
int GetRandInt(int nMax) noexcept
Definition: random.cpp:654
void Shuffle(I first, I last, R &&rng)
More efficient than using std::shuffle on a FastRandomContext.
Definition: random.h:251
reverse_range< T > reverse_iterate(T &x)
@ OP_TRUE
Definition: script.h:57
static uint16_t GetDefaultPort()
Definition: bitcoin.h:16
static RPCHelpMan stop()
Definition: server.cpp:206
CScript GetScriptForDestination(const CTxDestination &dest)
Generate a Bitcoin scriptPubKey for the given CTxDestination.
Definition: standard.cpp:243
std::string ToString(const T &t)
Locale-independent version of std::to_string.
Definition: string.h:79
Definition: amount.h:19
A BlockHash is a unqiue identifier for a block.
Definition: blockhash.h:13
static const Currency & get()
Definition: amount.cpp:18
A TxId is the identifier of a transaction.
Definition: txid.h:14
Compare proofs by score, then by id in case of equality.
Vote history.
Definition: voterecord.h:49
Bilingual messages:
Definition: translation.h:17
#define LOCK2(cs1, cs2)
Definition: sync.h:246
#define LOCK(cs)
Definition: sync.h:243
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
Definition: sync.h:276
bool error(const char *fmt, const Args &...args)
Definition: system.h:45
void UninterruptibleSleep(const std::chrono::microseconds &n)
Definition: time.cpp:23
void SetMockTime(int64_t nMockTimeIn)
DEPRECATED Use SetMockTime with chrono type.
Definition: time.cpp:94
@ CONFLICT
Removed for conflict with in-block transaction.
void SyncWithValidationInterfaceQueue()
This is a synonym for the following, which asserts certain locks are not held: std::promise<void> pro...
static const int PROTOCOL_VERSION
network protocol versioning
Definition: version.h:11
static constexpr int AVALANCHE_MAX_INFLIGHT_POLL
How many inflight requests can exist for one item.
Definition: voterecord.h:40
static constexpr uint32_t AVALANCHE_VOTE_STALE_MIN_THRESHOLD
Lowest configurable staleness threshold (finalization score + necessary votes to increase confidence ...
Definition: voterecord.h:28
static constexpr int AVALANCHE_FINALIZATION_SCORE
Finalization score.
Definition: voterecord.h:17