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 <core_io.h>
16 #include <key_io.h>
17 #include <net_processing.h> // For ::PeerManager
18 #include <reverse_iterator.h>
19 #include <scheduler.h>
20 #include <util/time.h>
21 #include <util/translation.h> // For bilingual_str
22 // D6970 moved LookupBlockIndex from chain.h to validation.h TODO: remove this
23 // when LookupBlockIndex is refactored out of validation
24 #include <validation.h>
25 
26 #include <avalanche/test/util.h>
27 #include <test/util/setup_common.h>
28 
29 #include <boost/mpl/list.hpp>
30 #include <boost/test/unit_test.hpp>
31 
32 #include <functional>
33 #include <limits>
34 #include <type_traits>
35 #include <vector>
36 
37 using namespace avalanche;
38 
39 namespace avalanche {
40 namespace {
41  struct AvalancheTest {
42  static void runEventLoop(avalanche::Processor &p) { p.runEventLoop(); }
43 
44  static std::vector<CInv> getInvsForNextPoll(Processor &p) {
45  return p.getInvsForNextPoll(false);
46  }
47 
48  static NodeId getSuitableNodeToQuery(Processor &p) {
49  return WITH_LOCK(p.cs_peerManager,
50  return p.peerManager->selectNode());
51  }
52 
53  static uint64_t getRound(const Processor &p) { return p.round; }
54 
55  static uint32_t getMinQuorumScore(const Processor &p) {
56  return p.minQuorumScore;
57  }
58 
59  static double getMinQuorumConnectedScoreRatio(const Processor &p) {
61  }
62 
63  static void clearavaproofsNodeCounter(Processor &p) {
65  }
66 
67  static void addVoteRecord(Processor &p, AnyVoteItem &item,
68  VoteRecord &voteRecord) {
69  p.voteRecords.getWriteView()->insert(
70  std::make_pair(item, voteRecord));
71  }
72 
73  static void setFinalizationTip(Processor &p,
74  const CBlockIndex *pindex) {
76  p.finalizationTip = pindex;
77  }
78 
79  static void setLocalProofShareable(Processor &p, bool shareable) {
80  p.m_canShareLocalProof = shareable;
81  }
82 
83  static void updatedBlockTip(Processor &p) { p.updatedBlockTip(); }
84 
85  static void addProofToRecentfinalized(Processor &p,
86  const ProofId &proofid) {
88  return p.finalizedItems.insert(proofid));
89  }
90  };
91 } // namespace
92 
93 struct TestVoteRecord : public VoteRecord {
94  explicit TestVoteRecord(uint16_t conf) : VoteRecord(true) {
95  confidence |= conf << 1;
96  }
97 };
98 } // namespace avalanche
99 
100 namespace {
101 struct CConnmanTest : public CConnman {
102  using CConnman::CConnman;
103  void AddNode(CNode &node) {
104  LOCK(m_nodes_mutex);
105  m_nodes.push_back(&node);
106  }
107  void ClearNodes() {
108  LOCK(m_nodes_mutex);
109  for (CNode *node : m_nodes) {
110  delete node;
111  }
112  m_nodes.clear();
113  }
114 };
115 
116 CService ip(uint32_t i) {
117  struct in_addr s;
118  s.s_addr = i;
119  return CService(CNetAddr(s), Params().GetDefaultPort());
120 }
121 
122 struct AvalancheTestingSetup : public TestChain100Setup {
123  const ::Config &config;
124  CConnmanTest *m_connman;
125 
126  std::unique_ptr<Processor> m_processor;
127 
128  // The master private key we delegate to.
129  CKey masterpriv;
130 
131  std::unordered_set<std::string> m_overridden_args;
132 
133  AvalancheTestingSetup()
134  : TestChain100Setup(), config(GetConfig()),
135  masterpriv(CKey::MakeCompressedKey()) {
136  // Deterministic randomness for tests.
137  auto connman = std::make_unique<CConnmanTest>(config, 0x1337, 0x1337,
138  *m_node.addrman);
139  m_connman = connman.get();
140  m_node.connman = std::move(connman);
141  m_node.peerman = ::PeerManager::make(
142  *m_connman, *m_node.addrman, m_node.banman.get(), *m_node.chainman,
143  *m_node.mempool, false);
144  m_node.chain = interfaces::MakeChain(m_node, config.GetChainParams());
145 
146  // Get the processor ready.
147  setArg("-avaminquorumstake", "0");
148  setArg("-avaminquorumconnectedstakeratio", "0");
149  setArg("-avaminavaproofsnodecount", "0");
150  setArg("-avaproofstakeutxoconfirmations", "1");
152  m_processor = Processor::MakeProcessor(
153  *m_node.args, *m_node.chain, m_node.connman.get(),
154  *Assert(m_node.chainman), m_node.mempool.get(), *m_node.scheduler,
155  error);
156  BOOST_CHECK(m_processor);
157  }
158 
159  ~AvalancheTestingSetup() {
160  m_connman->ClearNodes();
162 
163  ArgsManager &argsman = *Assert(m_node.args);
164  for (const std::string &key : m_overridden_args) {
165  argsman.ClearForcedArg(key);
166  }
167  m_overridden_args.clear();
168  }
169 
170  CNode *ConnectNode(ServiceFlags nServices) {
171  static NodeId id = 0;
172 
173  CAddress addr(ip(GetRand<uint32_t>()), NODE_NONE);
174  auto node =
175  new CNode(id++, INVALID_SOCKET, addr,
176  /* nKeyedNetGroupIn */ 0,
177  /* nLocalHostNonceIn */ 0,
178  /* nLocalExtraEntropyIn */ 0, CAddress(),
179  /* pszDest */ "", ConnectionType::OUTBOUND_FULL_RELAY,
180  /* inbound_onion */ false);
181  node->SetCommonVersion(PROTOCOL_VERSION);
182  node->m_has_all_wanted_services =
183  HasAllDesirableServiceFlags(nServices);
184  m_node.peerman->InitializeNode(config, *node, NODE_NETWORK);
185  node->nVersion = 1;
186  node->fSuccessfullyConnected = true;
187 
188  m_connman->AddNode(*node);
189  return node;
190  }
191 
192  ProofRef GetProof(CScript payoutScript = UNSPENDABLE_ECREG_PAYOUT_SCRIPT) {
193  const CKey key = CKey::MakeCompressedKey();
194  const COutPoint outpoint{TxId(GetRandHash()), 0};
196  const Amount amount = PROOF_DUST_THRESHOLD;
197  const uint32_t height = 100;
198 
199  LOCK(cs_main);
200  CCoinsViewCache &coins =
201  Assert(m_node.chainman)->ActiveChainstate().CoinsTip();
202  coins.AddCoin(outpoint, Coin(CTxOut(amount, script), height, false),
203  false);
204 
205  ProofBuilder pb(0, 0, masterpriv, payoutScript);
206  BOOST_CHECK(pb.addUTXO(outpoint, amount, height, false, key));
207  return pb.build();
208  }
209 
210  bool addNode(NodeId nodeid, const ProofId &proofid) {
211  return m_processor->withPeerManager([&](avalanche::PeerManager &pm) {
212  return pm.addNode(nodeid, proofid);
213  });
214  }
215 
216  bool addNode(NodeId nodeid) {
217  auto proof = GetProof();
218  return m_processor->withPeerManager([&](avalanche::PeerManager &pm) {
219  return pm.registerProof(proof) &&
220  pm.addNode(nodeid, proof->getId());
221  });
222  }
223 
224  std::array<CNode *, 8> ConnectNodes() {
225  auto proof = GetProof();
226  BOOST_CHECK(
227  m_processor->withPeerManager([&](avalanche::PeerManager &pm) {
228  return pm.registerProof(proof);
229  }));
230  const ProofId &proofid = proof->getId();
231 
232  std::array<CNode *, 8> nodes;
233  for (CNode *&n : nodes) {
234  n = ConnectNode(NODE_AVALANCHE);
235  BOOST_CHECK(addNode(n->GetId(), proofid));
236  }
237 
238  return nodes;
239  }
240 
241  void runEventLoop() { AvalancheTest::runEventLoop(*m_processor); }
242 
243  NodeId getSuitableNodeToQuery() {
244  return AvalancheTest::getSuitableNodeToQuery(*m_processor);
245  }
246 
247  std::vector<CInv> getInvsForNextPoll() {
248  return AvalancheTest::getInvsForNextPoll(*m_processor);
249  }
250 
251  uint64_t getRound() const { return AvalancheTest::getRound(*m_processor); }
252 
253  bool registerVotes(NodeId nodeid, const avalanche::Response &response,
254  std::vector<avalanche::VoteItemUpdate> &updates,
255  std::string &error) {
256  int banscore;
257  return m_processor->registerVotes(nodeid, response, updates, banscore,
258  error);
259  }
260 
261  bool registerVotes(NodeId nodeid, const avalanche::Response &response,
262  std::vector<avalanche::VoteItemUpdate> &updates) {
263  int banscore;
264  std::string error;
265  return m_processor->registerVotes(nodeid, response, updates, banscore,
266  error);
267  }
268 
269  void setArg(std::string key, std::string value) {
270  ArgsManager &argsman = *Assert(m_node.args);
271  argsman.ForceSetArg(key, std::move(value));
272  m_overridden_args.emplace(std::move(key));
273  }
274 
275  bool addToReconcile(const AnyVoteItem &item) {
276  return m_processor->addToReconcile(item);
277  }
278 };
279 
280 struct BlockProvider {
281  AvalancheTestingSetup *fixture;
282  uint32_t invType;
283 
284  BlockProvider(AvalancheTestingSetup *_fixture)
285  : fixture(_fixture), invType(MSG_BLOCK) {}
286 
287  CBlockIndex *buildVoteItem() const {
288  CBlock block = fixture->CreateAndProcessBlock({}, CScript());
289  const BlockHash blockHash = block.GetHash();
290 
291  LOCK(cs_main);
292  return Assert(fixture->m_node.chainman)
293  ->m_blockman.LookupBlockIndex(blockHash);
294  }
295 
296  uint256 getVoteItemId(const CBlockIndex *pindex) const {
297  return pindex->GetBlockHash();
298  }
299 
300  std::vector<Vote> buildVotesForItems(uint32_t error,
301  std::vector<CBlockIndex *> &&items) {
302  size_t numItems = items.size();
303 
304  std::vector<Vote> votes;
305  votes.reserve(numItems);
306 
307  // Votes are sorted by most work first
308  std::sort(items.begin(), items.end(), CBlockIndexWorkComparator());
309  for (auto &item : reverse_iterate(items)) {
310  votes.emplace_back(error, item->GetBlockHash());
311  }
312 
313  return votes;
314  }
315 
316  void invalidateItem(CBlockIndex *pindex) {
317  LOCK(::cs_main);
318  pindex->nStatus = pindex->nStatus.withFailed();
319  }
320 
321  const CBlockIndex *fromAnyVoteItem(const AnyVoteItem &item) {
322  return std::get<const CBlockIndex *>(item);
323  }
324 };
325 
326 struct ProofProvider {
327  AvalancheTestingSetup *fixture;
328  uint32_t invType;
329 
330  ProofProvider(AvalancheTestingSetup *_fixture)
331  : fixture(_fixture), invType(MSG_AVA_PROOF) {}
332 
333  ProofRef buildVoteItem() const {
334  const ProofRef proof = fixture->GetProof();
335  fixture->m_processor->withPeerManager([&](avalanche::PeerManager &pm) {
336  BOOST_CHECK(pm.registerProof(proof));
337  });
338  return proof;
339  }
340 
341  uint256 getVoteItemId(const ProofRef &proof) const {
342  return proof->getId();
343  }
344 
345  std::vector<Vote> buildVotesForItems(uint32_t error,
346  std::vector<ProofRef> &&items) {
347  size_t numItems = items.size();
348 
349  std::vector<Vote> votes;
350  votes.reserve(numItems);
351 
352  // Votes are sorted by high score first
353  std::sort(items.begin(), items.end(), ProofComparatorByScore());
354  for (auto &item : items) {
355  votes.emplace_back(error, item->getId());
356  }
357 
358  return votes;
359  }
360 
361  void invalidateItem(const ProofRef &proof) {
362  fixture->m_processor->withPeerManager([&](avalanche::PeerManager &pm) {
363  pm.rejectProof(proof->getId(),
365  });
366  }
367 
368  const ProofRef fromAnyVoteItem(const AnyVoteItem &item) {
369  return std::get<const ProofRef>(item);
370  }
371 };
372 
373 struct TxProvider {
374  AvalancheTestingSetup *fixture;
375 
376  std::vector<avalanche::VoteItemUpdate> updates;
377  uint32_t invType;
378 
379  TxProvider(AvalancheTestingSetup *_fixture)
380  : fixture(_fixture), invType(MSG_TX) {}
381 
382  CTransactionRef buildVoteItem() const {
383  auto rng = FastRandomContext();
385  mtx.nVersion = 2;
386  mtx.vin.emplace_back(COutPoint{TxId(rng.rand256()), 0});
387  mtx.vout.emplace_back(10 * COIN, CScript() << OP_TRUE);
388 
389  CTransactionRef tx = MakeTransactionRef(std::move(mtx));
390 
391  TestMemPoolEntryHelper mempoolEntryHelper;
392  auto entry = mempoolEntryHelper.Fee(int64_t(rng.randrange(10)) * COIN)
393  .FromTx(tx);
394 
395  CTxMemPool *mempool = Assert(fixture->m_node.mempool.get());
396  {
397  LOCK2(cs_main, mempool->cs);
398  mempool->addUnchecked(entry);
399  BOOST_CHECK(mempool->exists(tx->GetId()));
400  }
401 
402  return tx;
403  }
404 
405  uint256 getVoteItemId(const CTransactionRef &tx) const {
406  return tx->GetId();
407  }
408 
409  std::vector<Vote> buildVotesForItems(uint32_t error,
410  std::vector<CTransactionRef> &&items) {
411  size_t numItems = items.size();
412 
413  std::vector<Vote> votes;
414  votes.reserve(numItems);
415 
416  CTxMemPool *mempool = Assert(fixture->m_node.mempool.get());
417 
418  {
419  LOCK(mempool->cs);
420 
421  // Transactions are sorted by modified fee rate as long as they are
422  // in the mempool. Let's keep it simple here and assume it's the
423  // case.
424  std::sort(items.begin(), items.end(),
425  [mempool](const CTransactionRef &lhs,
426  const CTransactionRef &rhs)
427  EXCLUSIVE_LOCKS_REQUIRED(mempool->cs) {
428  auto lhsIter = mempool->GetIter(lhs->GetId());
429  auto rhsIter = mempool->GetIter(rhs->GetId());
430  BOOST_CHECK(lhsIter);
431  BOOST_CHECK(rhsIter);
432 
433  return CompareTxMemPoolEntryByModifiedFeeRate{}(
434  **lhsIter, **rhsIter);
435  });
436  }
437 
438  for (auto &item : items) {
439  votes.emplace_back(error, item->GetId());
440  }
441 
442  return votes;
443  }
444 
445  void invalidateItem(const CTransactionRef &tx) {
446  BOOST_CHECK(tx != nullptr);
447  CTxMemPool *mempool = Assert(fixture->m_node.mempool.get());
448 
449  LOCK(mempool->cs);
451  BOOST_CHECK(!mempool->exists(tx->GetId()));
452  }
453 
454  const CTransactionRef fromAnyVoteItem(const AnyVoteItem &item) {
455  return std::get<const CTransactionRef>(item);
456  }
457 };
458 
459 } // namespace
460 
461 BOOST_FIXTURE_TEST_SUITE(processor_tests, AvalancheTestingSetup)
462 
463 // FIXME A std::tuple can be used instead of boost::mpl::list after boost 1.67
465  boost::mpl::list<BlockProvider, ProofProvider, TxProvider>;
466 
468  P provider(this);
469 
470  std::set<VoteStatus> status{
471  VoteStatus::Invalid, VoteStatus::Rejected, VoteStatus::Accepted,
472  VoteStatus::Finalized, VoteStatus::Stale,
473  };
474 
475  auto item = provider.buildVoteItem();
476 
477  for (auto s : status) {
478  VoteItemUpdate itemUpdate(item, s);
479  // The use of BOOST_CHECK instead of BOOST_CHECK_EQUAL prevents from
480  // having to define operator<<() for each argument type.
481  BOOST_CHECK(provider.fromAnyVoteItem(itemUpdate.getVoteItem()) == item);
482  BOOST_CHECK(itemUpdate.getStatus() == s);
483  }
484 }
485 
486 namespace {
487 Response next(Response &r) {
488  auto copy = r;
489  r = {r.getRound() + 1, r.getCooldown(), r.GetVotes()};
490  return copy;
491 }
492 } // namespace
493 
495  P provider(this);
496  ChainstateManager &chainman = *Assert(m_node.chainman);
497  const CBlockIndex *chaintip =
498  WITH_LOCK(chainman.GetMutex(), return chainman.ActiveTip());
499 
500  auto item = provider.buildVoteItem();
501  auto itemid = provider.getVoteItemId(item);
502 
503  // Adding the item twice does nothing.
504  BOOST_CHECK(addToReconcile(item));
505  BOOST_CHECK(!addToReconcile(item));
506  BOOST_CHECK(m_processor->isAccepted(item));
507 
508  // Create nodes that supports avalanche so we can finalize the item.
509  auto avanodes = ConnectNodes();
510 
511  int nextNodeIndex = 0;
512  std::vector<avalanche::VoteItemUpdate> updates;
513  auto registerNewVote = [&](const Response &resp) {
514  runEventLoop();
515  auto nodeid = avanodes[nextNodeIndex++ % avanodes.size()]->GetId();
516  BOOST_CHECK(registerVotes(nodeid, resp, updates));
517  };
518 
519  // Finalize the item.
520  auto finalize = [&](const auto finalizeItemId) {
521  Response resp = {getRound(), 0, {Vote(0, finalizeItemId)}};
522  for (int i = 0; i < AVALANCHE_FINALIZATION_SCORE + 6; i++) {
523  registerNewVote(next(resp));
524  if (updates.size() > 0) {
525  break;
526  }
527  }
528  BOOST_CHECK_EQUAL(updates.size(), 1);
529  BOOST_CHECK(updates[0].getStatus() == VoteStatus::Finalized);
530  updates.clear();
531  };
532  finalize(itemid);
533 
534  // The finalized item cannot be reconciled for a while.
535  BOOST_CHECK(!addToReconcile(item));
536 
537  auto finalizeNewItem = [&]() {
538  auto anotherItem = provider.buildVoteItem();
539  AnyVoteItem anotherVoteItem = AnyVoteItem(anotherItem);
540  auto anotherItemId = provider.getVoteItemId(anotherItem);
541 
543  AvalancheTest::addVoteRecord(*m_processor, anotherVoteItem, voteRecord);
544  finalize(anotherItemId);
545  };
546 
547  // The filter can have new items added up to its size and the item will
548  // still not reconcile.
549  for (uint32_t i = 0; i < AVALANCHE_FINALIZED_ITEMS_FILTER_NUM_ELEMENTS;
550  i++) {
551  finalizeNewItem();
552  BOOST_CHECK(!addToReconcile(item));
553  }
554 
555  // But if we keep going it will eventually roll out of the filter and can
556  // be reconciled again.
557  for (uint32_t i = 0; i < AVALANCHE_FINALIZED_ITEMS_FILTER_NUM_ELEMENTS;
558  i++) {
559  finalizeNewItem();
560  }
561 
562  // Roll back the finalization point so that reconciling the old block does
563  // not fail the finalization check. This is a no-op for other types.
564  AvalancheTest::setFinalizationTip(*m_processor, chaintip);
565 
566  BOOST_CHECK(addToReconcile(item));
567 }
568 
570  P provider(this);
571 
572  // Check that null case is handled on the public interface
573  BOOST_CHECK(!m_processor->isAccepted(nullptr));
574  BOOST_CHECK_EQUAL(m_processor->getConfidence(nullptr), -1);
575 
576  auto item = decltype(provider.buildVoteItem())();
577  BOOST_CHECK(item == nullptr);
578  BOOST_CHECK(!addToReconcile(item));
579 
580  // Check that adding item to vote on doesn't change the outcome. A
581  // comparator is used under the hood, and this is skipped if there are no
582  // vote records.
583  item = provider.buildVoteItem();
584  BOOST_CHECK(addToReconcile(item));
585 
586  BOOST_CHECK(!m_processor->isAccepted(nullptr));
587  BOOST_CHECK_EQUAL(m_processor->getConfidence(nullptr), -1);
588 }
589 
591  P provider(this);
592  const uint32_t invType = provider.invType;
593 
594  auto item = provider.buildVoteItem();
595  auto itemid = provider.getVoteItemId(item);
596 
597  // Create nodes that supports avalanche.
598  auto avanodes = ConnectNodes();
599 
600  // Querying for random item returns false.
601  BOOST_CHECK(!m_processor->isAccepted(item));
602 
603  // Add a new item. Check it is added to the polls.
604  BOOST_CHECK(addToReconcile(item));
605  auto invs = getInvsForNextPoll();
606  BOOST_CHECK_EQUAL(invs.size(), 1);
607  BOOST_CHECK_EQUAL(invs[0].type, invType);
608  BOOST_CHECK(invs[0].hash == itemid);
609 
610  BOOST_CHECK(m_processor->isAccepted(item));
611 
612  int nextNodeIndex = 0;
613  std::vector<avalanche::VoteItemUpdate> updates;
614  auto registerNewVote = [&](const Response &resp) {
615  runEventLoop();
616  auto nodeid = avanodes[nextNodeIndex++ % avanodes.size()]->GetId();
617  BOOST_CHECK(registerVotes(nodeid, resp, updates));
618  };
619 
620  // Let's vote for this item a few times.
621  Response resp{0, 0, {Vote(0, itemid)}};
622  for (int i = 0; i < 6; i++) {
623  registerNewVote(next(resp));
624  BOOST_CHECK(m_processor->isAccepted(item));
625  BOOST_CHECK_EQUAL(m_processor->getConfidence(item), 0);
626  BOOST_CHECK_EQUAL(updates.size(), 0);
627  }
628 
629  // A single neutral vote do not change anything.
630  resp = {getRound(), 0, {Vote(-1, itemid)}};
631  registerNewVote(next(resp));
632  BOOST_CHECK(m_processor->isAccepted(item));
633  BOOST_CHECK_EQUAL(m_processor->getConfidence(item), 0);
634  BOOST_CHECK_EQUAL(updates.size(), 0);
635 
636  resp = {getRound(), 0, {Vote(0, itemid)}};
637  for (int i = 1; i < 7; i++) {
638  registerNewVote(next(resp));
639  BOOST_CHECK(m_processor->isAccepted(item));
640  BOOST_CHECK_EQUAL(m_processor->getConfidence(item), i);
641  BOOST_CHECK_EQUAL(updates.size(), 0);
642  }
643 
644  // Two neutral votes will stall progress.
645  resp = {getRound(), 0, {Vote(-1, itemid)}};
646  registerNewVote(next(resp));
647  BOOST_CHECK(m_processor->isAccepted(item));
648  BOOST_CHECK_EQUAL(m_processor->getConfidence(item), 6);
649  BOOST_CHECK_EQUAL(updates.size(), 0);
650  registerNewVote(next(resp));
651  BOOST_CHECK(m_processor->isAccepted(item));
652  BOOST_CHECK_EQUAL(m_processor->getConfidence(item), 6);
653  BOOST_CHECK_EQUAL(updates.size(), 0);
654 
655  resp = {getRound(), 0, {Vote(0, itemid)}};
656  for (int i = 2; i < 8; i++) {
657  registerNewVote(next(resp));
658  BOOST_CHECK(m_processor->isAccepted(item));
659  BOOST_CHECK_EQUAL(m_processor->getConfidence(item), 6);
660  BOOST_CHECK_EQUAL(updates.size(), 0);
661  }
662 
663  // We vote for it numerous times to finalize it.
664  for (int i = 7; i < AVALANCHE_FINALIZATION_SCORE; i++) {
665  registerNewVote(next(resp));
666  BOOST_CHECK(m_processor->isAccepted(item));
667  BOOST_CHECK_EQUAL(m_processor->getConfidence(item), i);
668  BOOST_CHECK_EQUAL(updates.size(), 0);
669  }
670 
671  // As long as it is not finalized, we poll.
672  invs = getInvsForNextPoll();
673  BOOST_CHECK_EQUAL(invs.size(), 1);
674  BOOST_CHECK_EQUAL(invs[0].type, invType);
675  BOOST_CHECK(invs[0].hash == itemid);
676 
677  // Now finalize the decision.
678  registerNewVote(next(resp));
679  BOOST_CHECK_EQUAL(updates.size(), 1);
680  BOOST_CHECK(provider.fromAnyVoteItem(updates[0].getVoteItem()) == item);
681  BOOST_CHECK(updates[0].getStatus() == VoteStatus::Finalized);
682  updates.clear();
683 
684  // Once the decision is finalized, there is no poll for it.
685  invs = getInvsForNextPoll();
686  BOOST_CHECK_EQUAL(invs.size(), 0);
687 
688  // Get a new item to vote on
689  item = provider.buildVoteItem();
690  itemid = provider.getVoteItemId(item);
691  BOOST_CHECK(addToReconcile(item));
692 
693  // Now let's finalize rejection.
694  invs = getInvsForNextPoll();
695  BOOST_CHECK_EQUAL(invs.size(), 1);
696  BOOST_CHECK_EQUAL(invs[0].type, invType);
697  BOOST_CHECK(invs[0].hash == itemid);
698 
699  resp = {getRound(), 0, {Vote(1, itemid)}};
700  for (int i = 0; i < 6; i++) {
701  registerNewVote(next(resp));
702  BOOST_CHECK(m_processor->isAccepted(item));
703  BOOST_CHECK_EQUAL(updates.size(), 0);
704  }
705 
706  // Now the state will flip.
707  registerNewVote(next(resp));
708  BOOST_CHECK(!m_processor->isAccepted(item));
709  BOOST_CHECK_EQUAL(updates.size(), 1);
710  BOOST_CHECK(provider.fromAnyVoteItem(updates[0].getVoteItem()) == item);
711  BOOST_CHECK(updates[0].getStatus() == VoteStatus::Rejected);
712  updates.clear();
713 
714  // Now it is rejected, but we can vote for it numerous times.
715  for (int i = 1; i < AVALANCHE_FINALIZATION_SCORE; i++) {
716  registerNewVote(next(resp));
717  BOOST_CHECK(!m_processor->isAccepted(item));
718  BOOST_CHECK_EQUAL(updates.size(), 0);
719  }
720 
721  // As long as it is not finalized, we poll.
722  invs = getInvsForNextPoll();
723  BOOST_CHECK_EQUAL(invs.size(), 1);
724  BOOST_CHECK_EQUAL(invs[0].type, invType);
725  BOOST_CHECK(invs[0].hash == itemid);
726 
727  // Now finalize the decision.
728  registerNewVote(next(resp));
729  BOOST_CHECK(!m_processor->isAccepted(item));
730  BOOST_CHECK_EQUAL(updates.size(), 1);
731  BOOST_CHECK(provider.fromAnyVoteItem(updates[0].getVoteItem()) == item);
732  BOOST_CHECK(updates[0].getStatus() == VoteStatus::Invalid);
733  updates.clear();
734 
735  // Once the decision is finalized, there is no poll for it.
736  invs = getInvsForNextPoll();
737  BOOST_CHECK_EQUAL(invs.size(), 0);
738 }
739 
741  P provider(this);
742  const uint32_t invType = provider.invType;
743 
744  auto itemA = provider.buildVoteItem();
745  auto itemidA = provider.getVoteItemId(itemA);
746 
747  auto itemB = provider.buildVoteItem();
748  auto itemidB = provider.getVoteItemId(itemB);
749 
750  // Create several nodes that support avalanche.
751  auto avanodes = ConnectNodes();
752 
753  // Querying for random item returns false.
754  BOOST_CHECK(!m_processor->isAccepted(itemA));
755  BOOST_CHECK(!m_processor->isAccepted(itemB));
756 
757  // Start voting on item A.
758  BOOST_CHECK(addToReconcile(itemA));
759  auto invs = getInvsForNextPoll();
760  BOOST_CHECK_EQUAL(invs.size(), 1);
761  BOOST_CHECK_EQUAL(invs[0].type, invType);
762  BOOST_CHECK(invs[0].hash == itemidA);
763 
764  uint64_t round = getRound();
765  runEventLoop();
766  std::vector<avalanche::VoteItemUpdate> updates;
767  BOOST_CHECK(registerVotes(avanodes[0]->GetId(),
768  {round, 0, {Vote(0, itemidA)}}, updates));
769  BOOST_CHECK_EQUAL(updates.size(), 0);
770 
771  // Start voting on item B after one vote.
772  std::vector<Vote> votes = provider.buildVotesForItems(0, {itemA, itemB});
773  Response resp{round + 1, 0, votes};
774  BOOST_CHECK(addToReconcile(itemB));
775  invs = getInvsForNextPoll();
776  BOOST_CHECK_EQUAL(invs.size(), 2);
777 
778  // Ensure the inv ordering is as expected
779  for (size_t i = 0; i < invs.size(); i++) {
780  BOOST_CHECK_EQUAL(invs[i].type, invType);
781  BOOST_CHECK(invs[i].hash == votes[i].GetHash());
782  }
783 
784  // Let's vote for these items a few times.
785  for (int i = 0; i < 4; i++) {
786  NodeId nodeid = getSuitableNodeToQuery();
787  runEventLoop();
788  BOOST_CHECK(registerVotes(nodeid, next(resp), updates));
789  BOOST_CHECK_EQUAL(updates.size(), 0);
790  }
791 
792  // Now it is accepted, but we can vote for it numerous times.
793  for (int i = 0; i < AVALANCHE_FINALIZATION_SCORE; i++) {
794  NodeId nodeid = getSuitableNodeToQuery();
795  runEventLoop();
796  BOOST_CHECK(registerVotes(nodeid, next(resp), updates));
797  BOOST_CHECK_EQUAL(updates.size(), 0);
798  }
799 
800  // Running two iterration of the event loop so that vote gets triggered on A
801  // and B.
802  NodeId firstNodeid = getSuitableNodeToQuery();
803  runEventLoop();
804  NodeId secondNodeid = getSuitableNodeToQuery();
805  runEventLoop();
806 
807  BOOST_CHECK(firstNodeid != secondNodeid);
808 
809  // Next vote will finalize item A.
810  BOOST_CHECK(registerVotes(firstNodeid, next(resp), updates));
811  BOOST_CHECK_EQUAL(updates.size(), 1);
812  BOOST_CHECK(provider.fromAnyVoteItem(updates[0].getVoteItem()) == itemA);
813  BOOST_CHECK(updates[0].getStatus() == VoteStatus::Finalized);
814  updates.clear();
815 
816  // We do not vote on A anymore.
817  invs = getInvsForNextPoll();
818  BOOST_CHECK_EQUAL(invs.size(), 1);
819  BOOST_CHECK_EQUAL(invs[0].type, invType);
820  BOOST_CHECK(invs[0].hash == itemidB);
821 
822  // Next vote will finalize item B.
823  BOOST_CHECK(registerVotes(secondNodeid, resp, updates));
824  BOOST_CHECK_EQUAL(updates.size(), 1);
825  BOOST_CHECK(provider.fromAnyVoteItem(updates[0].getVoteItem()) == itemB);
826  BOOST_CHECK(updates[0].getStatus() == VoteStatus::Finalized);
827  updates.clear();
828 
829  // There is nothing left to vote on.
830  invs = getInvsForNextPoll();
831  BOOST_CHECK_EQUAL(invs.size(), 0);
832 }
833 
835  P provider(this);
836  const uint32_t invType = provider.invType;
837 
838  auto item = provider.buildVoteItem();
839  auto itemid = provider.getVoteItemId(item);
840 
841  // There is no node to query.
842  BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), NO_NODE);
843 
844  // Add enough nodes to have a valid quorum, and the same amount with no
845  // avalanche support
846  std::set<NodeId> avanodeIds;
847  auto avanodes = ConnectNodes();
848  for (auto avanode : avanodes) {
849  ConnectNode(NODE_NONE);
850  avanodeIds.insert(avanode->GetId());
851  }
852 
853  auto getSelectedAvanodeId = [&]() {
854  NodeId avanodeid = getSuitableNodeToQuery();
855  BOOST_CHECK(avanodeIds.find(avanodeid) != avanodeIds.end());
856  return avanodeid;
857  };
858 
859  // It returns one of the avalanche peer.
860  NodeId avanodeid = getSelectedAvanodeId();
861 
862  // Register an item and check it is added to the list of elements to poll.
863  BOOST_CHECK(addToReconcile(item));
864  auto invs = getInvsForNextPoll();
865  BOOST_CHECK_EQUAL(invs.size(), 1);
866  BOOST_CHECK_EQUAL(invs[0].type, invType);
867  BOOST_CHECK(invs[0].hash == itemid);
868 
869  std::set<NodeId> unselectedNodeids = avanodeIds;
870  unselectedNodeids.erase(avanodeid);
871  const size_t remainingNodeIds = unselectedNodeids.size();
872 
873  uint64_t round = getRound();
874  for (size_t i = 0; i < remainingNodeIds; i++) {
875  // Trigger a poll on avanode.
876  runEventLoop();
877 
878  // Another node is selected
879  NodeId nodeid = getSuitableNodeToQuery();
880  BOOST_CHECK(unselectedNodeids.find(nodeid) != avanodeIds.end());
881  unselectedNodeids.erase(nodeid);
882  }
883 
884  // There is no more suitable peer available, so return nothing.
885  BOOST_CHECK(unselectedNodeids.empty());
886  runEventLoop();
887  BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), NO_NODE);
888 
889  // Respond to the request.
890  Response resp = {round, 0, {Vote(0, itemid)}};
891  std::vector<avalanche::VoteItemUpdate> updates;
892  BOOST_CHECK(registerVotes(avanodeid, resp, updates));
893  BOOST_CHECK_EQUAL(updates.size(), 0);
894 
895  // Now that avanode fullfilled his request, it is added back to the list of
896  // queriable nodes.
897  BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), avanodeid);
898 
899  auto checkRegisterVotesError = [&](NodeId nodeid,
901  const std::string &expectedError) {
902  std::string error;
903  BOOST_CHECK(!registerVotes(nodeid, response, updates, error));
904  BOOST_CHECK_EQUAL(error, expectedError);
905  BOOST_CHECK_EQUAL(updates.size(), 0);
906  };
907 
908  // Sending a response when not polled fails.
909  checkRegisterVotesError(avanodeid, next(resp), "unexpected-ava-response");
910 
911  // Trigger a poll on avanode.
912  round = getRound();
913  runEventLoop();
914  BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), NO_NODE);
915 
916  // Sending responses that do not match the request also fails.
917  // 1. Too many results.
918  resp = {round, 0, {Vote(0, itemid), Vote(0, itemid)}};
919  runEventLoop();
920  checkRegisterVotesError(avanodeid, resp, "invalid-ava-response-size");
921  BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), avanodeid);
922 
923  // 2. Not enough results.
924  resp = {getRound(), 0, {}};
925  runEventLoop();
926  checkRegisterVotesError(avanodeid, resp, "invalid-ava-response-size");
927  BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), avanodeid);
928 
929  // 3. Do not match the poll.
930  resp = {getRound(), 0, {Vote()}};
931  runEventLoop();
932  checkRegisterVotesError(avanodeid, resp, "invalid-ava-response-content");
933  BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), avanodeid);
934 
935  // At this stage we have reached the max inflight requests for our inv, so
936  // it won't be requested anymore until the requests are fullfilled. Let's
937  // vote on another item with no inflight request so the remaining tests
938  // makes sense.
939  invs = getInvsForNextPoll();
940  BOOST_CHECK(invs.empty());
941 
942  item = provider.buildVoteItem();
943  itemid = provider.getVoteItemId(item);
944  BOOST_CHECK(addToReconcile(item));
945 
946  invs = getInvsForNextPoll();
947  BOOST_CHECK_EQUAL(invs.size(), 1);
948 
949  // 4. Invalid round count. Request is not discarded.
950  uint64_t queryRound = getRound();
951  runEventLoop();
952 
953  resp = {queryRound + 1, 0, {Vote()}};
954  checkRegisterVotesError(avanodeid, resp, "unexpected-ava-response");
955 
956  resp = {queryRound - 1, 0, {Vote()}};
957  checkRegisterVotesError(avanodeid, resp, "unexpected-ava-response");
958 
959  // 5. Making request for invalid nodes do not work. Request is not
960  // discarded.
961  resp = {queryRound, 0, {Vote(0, itemid)}};
962  checkRegisterVotesError(avanodeid + 1234, resp, "unexpected-ava-response");
963 
964  // Proper response gets processed and avanode is available again.
965  resp = {queryRound, 0, {Vote(0, itemid)}};
966  BOOST_CHECK(registerVotes(avanodeid, resp, updates));
967  BOOST_CHECK_EQUAL(updates.size(), 0);
968  BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), avanodeid);
969 
970  // Out of order response are rejected.
971  const auto item2 = provider.buildVoteItem();
972  BOOST_CHECK(addToReconcile(item2));
973 
974  std::vector<Vote> votes = provider.buildVotesForItems(0, {item, item2});
975  resp = {getRound(), 0, {votes[1], votes[0]}};
976  runEventLoop();
977  checkRegisterVotesError(avanodeid, resp, "invalid-ava-response-content");
978  BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), avanodeid);
979 
980  // But they are accepted in order.
981  resp = {getRound(), 0, votes};
982  runEventLoop();
983  BOOST_CHECK(registerVotes(avanodeid, resp, updates));
984  BOOST_CHECK_EQUAL(updates.size(), 0);
985  BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), avanodeid);
986 }
987 
989  P provider(this);
990  const uint32_t invType = provider.invType;
991 
992  auto itemA = provider.buildVoteItem();
993  auto itemB = provider.buildVoteItem();
994 
995  auto avanodes = ConnectNodes();
996 
997  // Build votes to get proper ordering
998  std::vector<Vote> votes = provider.buildVotesForItems(0, {itemA, itemB});
999 
1000  // Register the items and check they are added to the list of elements to
1001  // poll.
1002  BOOST_CHECK(addToReconcile(itemA));
1003  BOOST_CHECK(addToReconcile(itemB));
1004  auto invs = getInvsForNextPoll();
1005  BOOST_CHECK_EQUAL(invs.size(), 2);
1006  for (size_t i = 0; i < invs.size(); i++) {
1007  BOOST_CHECK_EQUAL(invs[i].type, invType);
1008  BOOST_CHECK(invs[i].hash == votes[i].GetHash());
1009  }
1010 
1011  // When an item is marked invalid, stop polling.
1012  provider.invalidateItem(itemB);
1013 
1014  Response goodResp{getRound(), 0, {Vote(0, provider.getVoteItemId(itemA))}};
1015  std::vector<avalanche::VoteItemUpdate> updates;
1016  runEventLoop();
1017  BOOST_CHECK(registerVotes(avanodes[0]->GetId(), goodResp, updates));
1018  BOOST_CHECK_EQUAL(updates.size(), 0);
1019 
1020  // Votes including itemB are rejected
1021  Response badResp{getRound(), 0, votes};
1022  runEventLoop();
1023  std::string error;
1024  BOOST_CHECK(!registerVotes(avanodes[1]->GetId(), badResp, updates, error));
1025  BOOST_CHECK_EQUAL(error, "invalid-ava-response-size");
1026 }
1027 
1028 BOOST_TEST_DECORATOR(*boost::unit_test::timeout(60))
1030  P provider(this);
1031  ChainstateManager &chainman = *Assert(m_node.chainman);
1032 
1033  auto queryTimeDuration = std::chrono::milliseconds(10);
1034  setArg("-avatimeout", ToString(queryTimeDuration.count()));
1035 
1037  m_processor = Processor::MakeProcessor(
1038  *m_node.args, *m_node.chain, m_node.connman.get(), chainman,
1039  m_node.mempool.get(), *m_node.scheduler, error);
1040 
1041  const auto item = provider.buildVoteItem();
1042  const auto itemid = provider.getVoteItemId(item);
1043 
1044  // Add the item
1045  BOOST_CHECK(addToReconcile(item));
1046 
1047  // Create a quorum of nodes that support avalanche.
1048  ConnectNodes();
1049  NodeId avanodeid = NO_NODE;
1050 
1051  // Expire requests after some time.
1052  for (int i = 0; i < 10; i++) {
1053  Response resp = {getRound(), 0, {Vote(0, itemid)}};
1054  avanodeid = getSuitableNodeToQuery();
1055 
1056  auto start = Now<SteadyMilliseconds>();
1057  runEventLoop();
1058  // We cannot guarantee that we'll wait for just 1ms, so we have to bail
1059  // if we aren't within the proper time range.
1060  std::this_thread::sleep_for(std::chrono::milliseconds(1));
1061  runEventLoop();
1062 
1063  std::vector<avalanche::VoteItemUpdate> updates;
1064  bool ret = registerVotes(avanodeid, next(resp), updates);
1065  if (Now<SteadyMilliseconds>() > start + queryTimeDuration) {
1066  // We waited for too long, bail. Because we can't know for sure when
1067  // previous steps ran, ret is not deterministic and we do not check
1068  // it.
1069  i--;
1070  continue;
1071  }
1072 
1073  // We are within time bounds, so the vote should have worked.
1074  BOOST_CHECK(ret);
1075 
1076  avanodeid = getSuitableNodeToQuery();
1077 
1078  // Now try again but wait for expiration.
1079  runEventLoop();
1080  std::this_thread::sleep_for(queryTimeDuration);
1081  runEventLoop();
1082  BOOST_CHECK(!registerVotes(avanodeid, next(resp), updates));
1083  }
1084 }
1085 
1087  P provider(this);
1088  const uint32_t invType = provider.invType;
1089 
1090  // Create enough nodes so that we run into the inflight request limit.
1091  auto proof = GetProof();
1092  BOOST_CHECK(m_processor->withPeerManager(
1093  [&](avalanche::PeerManager &pm) { return pm.registerProof(proof); }));
1094 
1095  std::array<CNode *, AVALANCHE_MAX_INFLIGHT_POLL + 1> nodes;
1096  for (auto &n : nodes) {
1097  n = ConnectNode(NODE_AVALANCHE);
1098  BOOST_CHECK(addNode(n->GetId(), proof->getId()));
1099  }
1100 
1101  // Add an item to poll
1102  const auto item = provider.buildVoteItem();
1103  const auto itemid = provider.getVoteItemId(item);
1104  BOOST_CHECK(addToReconcile(item));
1105 
1106  // Ensure there are enough requests in flight.
1107  std::map<NodeId, uint64_t> node_round_map;
1108  for (int i = 0; i < AVALANCHE_MAX_INFLIGHT_POLL; i++) {
1109  NodeId nodeid = getSuitableNodeToQuery();
1110  BOOST_CHECK(node_round_map.find(nodeid) == node_round_map.end());
1111  node_round_map.insert(std::pair<NodeId, uint64_t>(nodeid, getRound()));
1112  auto invs = getInvsForNextPoll();
1113  BOOST_CHECK_EQUAL(invs.size(), 1);
1114  BOOST_CHECK_EQUAL(invs[0].type, invType);
1115  BOOST_CHECK(invs[0].hash == itemid);
1116  runEventLoop();
1117  }
1118 
1119  // Now that we have enough in flight requests, we shouldn't poll.
1120  auto suitablenodeid = getSuitableNodeToQuery();
1121  BOOST_CHECK(suitablenodeid != NO_NODE);
1122  auto invs = getInvsForNextPoll();
1123  BOOST_CHECK_EQUAL(invs.size(), 0);
1124  runEventLoop();
1125  BOOST_CHECK_EQUAL(getSuitableNodeToQuery(), suitablenodeid);
1126 
1127  // Send one response, now we can poll again.
1128  auto it = node_round_map.begin();
1129  Response resp = {it->second, 0, {Vote(0, itemid)}};
1130  std::vector<avalanche::VoteItemUpdate> updates;
1131  BOOST_CHECK(registerVotes(it->first, resp, updates));
1132  node_round_map.erase(it);
1133 
1134  invs = getInvsForNextPoll();
1135  BOOST_CHECK_EQUAL(invs.size(), 1);
1136  BOOST_CHECK_EQUAL(invs[0].type, invType);
1137  BOOST_CHECK(invs[0].hash == itemid);
1138 }
1139 
1140 BOOST_AUTO_TEST_CASE(quorum_diversity) {
1141  std::vector<VoteItemUpdate> updates;
1142 
1143  CBlock block = CreateAndProcessBlock({}, CScript());
1144  const BlockHash blockHash = block.GetHash();
1145  const CBlockIndex *pindex;
1146  {
1147  LOCK(cs_main);
1148  pindex =
1149  Assert(m_node.chainman)->m_blockman.LookupBlockIndex(blockHash);
1150  }
1151 
1152  // Create nodes that supports avalanche.
1153  auto avanodes = ConnectNodes();
1154 
1155  // Querying for random block returns false.
1156  BOOST_CHECK(!m_processor->isAccepted(pindex));
1157 
1158  // Add a new block. Check it is added to the polls.
1159  BOOST_CHECK(m_processor->addToReconcile(pindex));
1160 
1161  // Do one valid round of voting.
1162  uint64_t round = getRound();
1163  Response resp{round, 0, {Vote(0, blockHash)}};
1164 
1165  // Check that all nodes can vote.
1166  for (size_t i = 0; i < avanodes.size(); i++) {
1167  runEventLoop();
1168  BOOST_CHECK(registerVotes(avanodes[i]->GetId(), next(resp), updates));
1169  }
1170 
1171  // Generate a query for every single node.
1172  const NodeId firstNodeId = getSuitableNodeToQuery();
1173  std::map<NodeId, uint64_t> node_round_map;
1174  round = getRound();
1175  for (size_t i = 0; i < avanodes.size(); i++) {
1176  NodeId nodeid = getSuitableNodeToQuery();
1177  BOOST_CHECK(node_round_map.find(nodeid) == node_round_map.end());
1178  node_round_map[nodeid] = getRound();
1179  runEventLoop();
1180  }
1181 
1182  // Now only the first node can vote. All others would be duplicate in the
1183  // quorum.
1184  auto confidence = m_processor->getConfidence(pindex);
1185  BOOST_REQUIRE(confidence > 0);
1186 
1187  for (auto &[nodeid, r] : node_round_map) {
1188  if (nodeid == firstNodeId) {
1189  // Node 0 is the only one which can vote at this stage.
1190  round = r;
1191  continue;
1192  }
1193 
1194  BOOST_CHECK(
1195  registerVotes(nodeid, {r, 0, {Vote(0, blockHash)}}, updates));
1196  BOOST_CHECK_EQUAL(m_processor->getConfidence(pindex), confidence);
1197  }
1198 
1199  BOOST_CHECK(
1200  registerVotes(firstNodeId, {round, 0, {Vote(0, blockHash)}}, updates));
1201  BOOST_CHECK_EQUAL(m_processor->getConfidence(pindex), confidence + 1);
1202 }
1203 
1205  CScheduler s;
1206 
1207  CBlock block = CreateAndProcessBlock({}, CScript());
1208  const BlockHash blockHash = block.GetHash();
1209  const CBlockIndex *pindex;
1210  {
1211  LOCK(cs_main);
1212  pindex =
1213  Assert(m_node.chainman)->m_blockman.LookupBlockIndex(blockHash);
1214  }
1215 
1216  // Starting the event loop.
1217  BOOST_CHECK(m_processor->startEventLoop(s));
1218 
1219  // There is one task planned in the next hour (our event loop).
1220  std::chrono::steady_clock::time_point start, stop;
1221  BOOST_CHECK_EQUAL(s.getQueueInfo(start, stop), 1);
1222 
1223  // Starting twice doesn't start it twice.
1224  BOOST_CHECK(!m_processor->startEventLoop(s));
1225 
1226  // Start the scheduler thread.
1227  std::thread schedulerThread(std::bind(&CScheduler::serviceQueue, &s));
1228 
1229  // Create a quorum of nodes that support avalanche.
1230  auto avanodes = ConnectNodes();
1231 
1232  // There is no query in flight at the moment.
1233  NodeId nodeid = getSuitableNodeToQuery();
1234  BOOST_CHECK_NE(nodeid, NO_NODE);
1235 
1236  // Add a new block. Check it is added to the polls.
1237  uint64_t queryRound = getRound();
1238  BOOST_CHECK(m_processor->addToReconcile(pindex));
1239 
1240  // Wait until all nodes got a poll
1241  for (int i = 0; i < 60 * 1000; i++) {
1242  // Technically, this is a race condition, but this should do just fine
1243  // as we wait up to 1 minute for an event that should take 80ms.
1244  UninterruptibleSleep(std::chrono::milliseconds(1));
1245  if (getRound() == queryRound + avanodes.size()) {
1246  break;
1247  }
1248  }
1249 
1250  // Check that we effectively got a request and not timed out.
1251  BOOST_CHECK(getRound() > queryRound);
1252 
1253  // Respond and check the cooldown time is respected.
1254  uint64_t responseRound = getRound();
1255  auto queryTime = Now<SteadyMilliseconds>() + std::chrono::milliseconds(100);
1256 
1257  std::vector<VoteItemUpdate> updates;
1258  // Only the first node answers, so it's the only one that gets polled again
1259  BOOST_CHECK(registerVotes(nodeid, {queryRound, 100, {Vote(0, blockHash)}},
1260  updates));
1261 
1262  for (int i = 0; i < 10000; i++) {
1263  // We make sure that we do not get a request before queryTime.
1264  UninterruptibleSleep(std::chrono::milliseconds(1));
1265  if (getRound() != responseRound) {
1266  BOOST_CHECK(Now<SteadyMilliseconds>() >= queryTime);
1267  break;
1268  }
1269  }
1270 
1271  // But we eventually get one.
1272  BOOST_CHECK(getRound() > responseRound);
1273 
1274  // Stop event loop.
1275  BOOST_CHECK(m_processor->stopEventLoop());
1276 
1277  // We don't have any task scheduled anymore.
1278  BOOST_CHECK_EQUAL(s.getQueueInfo(start, stop), 0);
1279 
1280  // Can't stop the event loop twice.
1281  BOOST_CHECK(!m_processor->stopEventLoop());
1282 
1283  // Wait for the scheduler to stop.
1284  s.StopWhenDrained();
1285  schedulerThread.join();
1286 }
1287 
1289  CScheduler s;
1290  std::chrono::steady_clock::time_point start, stop;
1291 
1292  std::thread schedulerThread;
1293  BOOST_CHECK(m_processor->startEventLoop(s));
1294  BOOST_CHECK_EQUAL(s.getQueueInfo(start, stop), 1);
1295 
1296  // Start the service thread after the queue size check to prevent a race
1297  // condition where the thread may be processing the event loop task during
1298  // the check.
1299  schedulerThread = std::thread(std::bind(&CScheduler::serviceQueue, &s));
1300 
1301  // Destroy the processor.
1302  m_processor.reset();
1303 
1304  // Now that avalanche is destroyed, there is no more scheduled tasks.
1305  BOOST_CHECK_EQUAL(s.getQueueInfo(start, stop), 0);
1306 
1307  // Wait for the scheduler to stop.
1308  s.StopWhenDrained();
1309  schedulerThread.join();
1310 }
1311 
1312 BOOST_AUTO_TEST_CASE(add_proof_to_reconcile) {
1313  uint32_t score = MIN_VALID_PROOF_SCORE;
1314  Chainstate &active_chainstate = Assert(m_node.chainman)->ActiveChainstate();
1315 
1316  auto addProofToReconcile = [&](uint32_t proofScore) {
1317  auto proof = buildRandomProof(active_chainstate, proofScore);
1318  m_processor->withPeerManager([&](avalanche::PeerManager &pm) {
1319  BOOST_CHECK(pm.registerProof(proof));
1320  });
1321  BOOST_CHECK(m_processor->addToReconcile(proof));
1322  return proof;
1323  };
1324 
1325  for (size_t i = 0; i < AVALANCHE_MAX_ELEMENT_POLL; i++) {
1326  auto proof = addProofToReconcile(++score);
1327 
1328  auto invs = AvalancheTest::getInvsForNextPoll(*m_processor);
1329  BOOST_CHECK_EQUAL(invs.size(), i + 1);
1330  BOOST_CHECK(invs.front().IsMsgProof());
1331  BOOST_CHECK_EQUAL(invs.front().hash, proof->getId());
1332  }
1333 
1334  // From here a new proof is only polled if its score is in the top
1335  // AVALANCHE_MAX_ELEMENT_POLL
1336  ProofId lastProofId;
1337  for (size_t i = 0; i < 10; i++) {
1338  auto proof = addProofToReconcile(++score);
1339 
1340  auto invs = AvalancheTest::getInvsForNextPoll(*m_processor);
1342  BOOST_CHECK(invs.front().IsMsgProof());
1343  BOOST_CHECK_EQUAL(invs.front().hash, proof->getId());
1344 
1345  lastProofId = proof->getId();
1346  }
1347 
1348  for (size_t i = 0; i < 10; i++) {
1349  auto proof = addProofToReconcile(--score);
1350 
1351  auto invs = AvalancheTest::getInvsForNextPoll(*m_processor);
1353  BOOST_CHECK(invs.front().IsMsgProof());
1354  BOOST_CHECK_EQUAL(invs.front().hash, lastProofId);
1355  }
1356 
1357  {
1358  // The score is not high enough to get polled
1359  auto proof = addProofToReconcile(--score);
1360  auto invs = AvalancheTest::getInvsForNextPoll(*m_processor);
1361  for (auto &inv : invs) {
1362  BOOST_CHECK_NE(inv.hash, proof->getId());
1363  }
1364  }
1365 }
1366 
1367 BOOST_AUTO_TEST_CASE(proof_record) {
1368  setArg("-avaproofstakeutxoconfirmations", "2");
1369  setArg("-avalancheconflictingproofcooldown", "0");
1370 
1371  BOOST_CHECK(!m_processor->isAccepted(nullptr));
1372  BOOST_CHECK_EQUAL(m_processor->getConfidence(nullptr), -1);
1373 
1374  const CKey key = CKey::MakeCompressedKey();
1375 
1376  const COutPoint conflictingOutpoint{TxId(GetRandHash()), 0};
1377  const COutPoint immatureOutpoint{TxId(GetRandHash()), 0};
1378  {
1380 
1381  LOCK(cs_main);
1382  CCoinsViewCache &coins =
1383  Assert(m_node.chainman)->ActiveChainstate().CoinsTip();
1384  coins.AddCoin(conflictingOutpoint,
1385  Coin(CTxOut(PROOF_DUST_THRESHOLD, script), 10, false),
1386  false);
1387  coins.AddCoin(immatureOutpoint,
1388  Coin(CTxOut(PROOF_DUST_THRESHOLD, script), 100, false),
1389  false);
1390  }
1391 
1392  auto buildProof = [&](const COutPoint &outpoint, uint64_t sequence,
1393  uint32_t height = 10) {
1394  ProofBuilder pb(sequence, 0, key, UNSPENDABLE_ECREG_PAYOUT_SCRIPT);
1395  BOOST_CHECK(
1396  pb.addUTXO(outpoint, PROOF_DUST_THRESHOLD, height, false, key));
1397  return pb.build();
1398  };
1399 
1400  auto conflictingProof = buildProof(conflictingOutpoint, 1);
1401  auto validProof = buildProof(conflictingOutpoint, 2);
1402  auto immatureProof = buildProof(immatureOutpoint, 3, 100);
1403 
1404  BOOST_CHECK(!m_processor->isAccepted(conflictingProof));
1405  BOOST_CHECK(!m_processor->isAccepted(validProof));
1406  BOOST_CHECK(!m_processor->isAccepted(immatureProof));
1407  BOOST_CHECK_EQUAL(m_processor->getConfidence(conflictingProof), -1);
1408  BOOST_CHECK_EQUAL(m_processor->getConfidence(validProof), -1);
1409  BOOST_CHECK_EQUAL(m_processor->getConfidence(immatureProof), -1);
1410 
1411  // Reconciling proofs that don't exist will fail
1412  BOOST_CHECK(!m_processor->addToReconcile(conflictingProof));
1413  BOOST_CHECK(!m_processor->addToReconcile(validProof));
1414  BOOST_CHECK(!m_processor->addToReconcile(immatureProof));
1415 
1416  m_processor->withPeerManager([&](avalanche::PeerManager &pm) {
1417  BOOST_CHECK(pm.registerProof(conflictingProof));
1418  BOOST_CHECK(pm.registerProof(validProof));
1419  BOOST_CHECK(!pm.registerProof(immatureProof));
1420 
1421  BOOST_CHECK(pm.isBoundToPeer(validProof->getId()));
1422  BOOST_CHECK(pm.isInConflictingPool(conflictingProof->getId()));
1423  BOOST_CHECK(pm.isImmature(immatureProof->getId()));
1424  });
1425 
1426  BOOST_CHECK(m_processor->addToReconcile(conflictingProof));
1427  BOOST_CHECK(!m_processor->isAccepted(conflictingProof));
1428  BOOST_CHECK(!m_processor->isAccepted(validProof));
1429  BOOST_CHECK(!m_processor->isAccepted(immatureProof));
1430  BOOST_CHECK_EQUAL(m_processor->getConfidence(conflictingProof), 0);
1431  BOOST_CHECK_EQUAL(m_processor->getConfidence(validProof), -1);
1432  BOOST_CHECK_EQUAL(m_processor->getConfidence(immatureProof), -1);
1433 
1434  BOOST_CHECK(m_processor->addToReconcile(validProof));
1435  BOOST_CHECK(!m_processor->isAccepted(conflictingProof));
1436  BOOST_CHECK(m_processor->isAccepted(validProof));
1437  BOOST_CHECK(!m_processor->isAccepted(immatureProof));
1438  BOOST_CHECK_EQUAL(m_processor->getConfidence(conflictingProof), 0);
1439  BOOST_CHECK_EQUAL(m_processor->getConfidence(validProof), 0);
1440  BOOST_CHECK_EQUAL(m_processor->getConfidence(immatureProof), -1);
1441 
1442  BOOST_CHECK(!m_processor->addToReconcile(immatureProof));
1443  BOOST_CHECK(!m_processor->isAccepted(conflictingProof));
1444  BOOST_CHECK(m_processor->isAccepted(validProof));
1445  BOOST_CHECK(!m_processor->isAccepted(immatureProof));
1446  BOOST_CHECK_EQUAL(m_processor->getConfidence(conflictingProof), 0);
1447  BOOST_CHECK_EQUAL(m_processor->getConfidence(validProof), 0);
1448  BOOST_CHECK_EQUAL(m_processor->getConfidence(immatureProof), -1);
1449 }
1450 
1451 BOOST_AUTO_TEST_CASE(quorum_detection) {
1452  // Set min quorum parameters for our test
1453  int minStake = 400'000'000;
1454  setArg("-avaminquorumstake", ToString(minStake));
1455  setArg("-avaminquorumconnectedstakeratio", "0.5");
1456 
1457  // Create a new processor with our given quorum parameters
1458  const auto currency = Currency::get();
1459  uint32_t minScore = Proof::amountToScore(minStake * currency.baseunit);
1460 
1461  Chainstate &active_chainstate = Assert(m_node.chainman)->ActiveChainstate();
1462 
1463  const CKey key = CKey::MakeCompressedKey();
1464  auto localProof =
1465  buildRandomProof(active_chainstate, minScore / 4, 100, key);
1466  setArg("-avamasterkey", EncodeSecret(key));
1467  setArg("-avaproof", localProof->ToHex());
1468 
1470  ChainstateManager &chainman = *Assert(m_node.chainman);
1471  m_processor = Processor::MakeProcessor(
1472  *m_node.args, *m_node.chain, m_node.connman.get(), chainman,
1473  m_node.mempool.get(), *m_node.scheduler, error);
1474 
1475  BOOST_CHECK(m_processor != nullptr);
1476  BOOST_CHECK(m_processor->getLocalProof() != nullptr);
1477  BOOST_CHECK_EQUAL(m_processor->getLocalProof()->getId(),
1478  localProof->getId());
1479  BOOST_CHECK_EQUAL(AvalancheTest::getMinQuorumScore(*m_processor), minScore);
1481  AvalancheTest::getMinQuorumConnectedScoreRatio(*m_processor), 0.5);
1482 
1483  // The local proof has not been validated yet
1484  m_processor->withPeerManager([&](avalanche::PeerManager &pm) {
1487  });
1488  BOOST_CHECK(!m_processor->isQuorumEstablished());
1489 
1490  // Register the local proof. This is normally done when the chain tip is
1491  // updated. The local proof should be accounted for in the min quorum
1492  // computation but the peer manager doesn't know about that.
1493  m_processor->withPeerManager([&](avalanche::PeerManager &pm) {
1494  BOOST_CHECK(pm.registerProof(m_processor->getLocalProof()));
1495  BOOST_CHECK(pm.isBoundToPeer(m_processor->getLocalProof()->getId()));
1496  BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), minScore / 4);
1498  });
1499  BOOST_CHECK(!m_processor->isQuorumEstablished());
1500 
1501  // Add enough nodes to get a conclusive vote
1502  for (NodeId id = 0; id < 8; id++) {
1503  m_processor->withPeerManager([&](avalanche::PeerManager &pm) {
1504  pm.addNode(id, m_processor->getLocalProof()->getId());
1505  BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), minScore / 4);
1506  BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), minScore / 4);
1507  });
1508  }
1509 
1510  // Add part of the required stake and make sure we still report no quorum
1511  auto proof1 = buildRandomProof(active_chainstate, minScore / 2);
1512  m_processor->withPeerManager([&](avalanche::PeerManager &pm) {
1513  BOOST_CHECK(pm.registerProof(proof1));
1514  BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), 3 * minScore / 4);
1515  BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), minScore / 4);
1516  });
1517  BOOST_CHECK(!m_processor->isQuorumEstablished());
1518 
1519  // Add the rest of the stake, but we are still lacking connected stake
1520  const int64_t tipTime =
1521  WITH_LOCK(chainman.GetMutex(), return chainman.ActiveTip())
1522  ->GetBlockTime();
1523  const COutPoint utxo{TxId(GetRandHash()), 0};
1524  const Amount amount = (int64_t(minScore / 4) * COIN) / 100;
1525  const int height = 100;
1526  const bool isCoinbase = false;
1527  {
1528  LOCK(cs_main);
1529  CCoinsViewCache &coins = active_chainstate.CoinsTip();
1530  coins.AddCoin(utxo,
1532  PKHash(key.GetPubKey()))),
1533  height, isCoinbase),
1534  false);
1535  }
1536  ProofBuilder pb(1, tipTime + 1, key, UNSPENDABLE_ECREG_PAYOUT_SCRIPT);
1537  BOOST_CHECK(pb.addUTXO(utxo, amount, height, isCoinbase, key));
1538  auto proof2 = pb.build();
1539 
1540  m_processor->withPeerManager([&](avalanche::PeerManager &pm) {
1541  BOOST_CHECK(pm.registerProof(proof2));
1542  BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), minScore);
1543  BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), minScore / 4);
1544  });
1545  BOOST_CHECK(!m_processor->isQuorumEstablished());
1546 
1547  // Adding a node should cause the quorum to be detected and locked-in
1548  m_processor->withPeerManager([&](avalanche::PeerManager &pm) {
1549  pm.addNode(8, proof2->getId());
1550  BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), minScore);
1551  // The peer manager knows that proof2 has a node attached ...
1552  BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), minScore / 2);
1553  });
1554  // ... but the processor also account for the local proof, so we reached 50%
1555  BOOST_CHECK(m_processor->isQuorumEstablished());
1556 
1557  // Go back to not having enough connected score, but we've already latched
1558  // the quorum as established
1559  m_processor->withPeerManager([&](avalanche::PeerManager &pm) {
1560  pm.removeNode(8);
1561  BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), minScore);
1562  BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), minScore / 4);
1563  });
1564  BOOST_CHECK(m_processor->isQuorumEstablished());
1565 
1566  // Removing one more node drops our count below the minimum and the quorum
1567  // is no longer ready
1568  m_processor->withPeerManager(
1569  [&](avalanche::PeerManager &pm) { pm.removeNode(7); });
1570  BOOST_CHECK(!m_processor->isQuorumEstablished());
1571 
1572  // It resumes when we have enough nodes again
1573  m_processor->withPeerManager([&](avalanche::PeerManager &pm) {
1574  pm.addNode(7, m_processor->getLocalProof()->getId());
1575  });
1576  BOOST_CHECK(m_processor->isQuorumEstablished());
1577 
1578  // Remove peers one at a time until the quorum is no longer established
1579  auto spendProofUtxo = [&](ProofRef proof) {
1580  {
1581  LOCK(cs_main);
1582  CCoinsViewCache &coins = chainman.ActiveChainstate().CoinsTip();
1583  coins.SpendCoin(proof->getStakes()[0].getStake().getUTXO());
1584  }
1585  m_processor->withPeerManager([&proof](avalanche::PeerManager &pm) {
1586  pm.updatedBlockTip();
1587  BOOST_CHECK(!pm.isBoundToPeer(proof->getId()));
1588  });
1589  };
1590 
1591  // Expire proof2, the quorum is still latched
1592  for (int64_t i = 0; i < 6; i++) {
1593  SetMockTime(proof2->getExpirationTime() + i);
1594  CreateAndProcessBlock({}, CScript());
1595  }
1597  WITH_LOCK(chainman.GetMutex(), return chainman.ActiveTip())
1598  ->GetMedianTimePast(),
1599  proof2->getExpirationTime());
1600  m_processor->withPeerManager([&](avalanche::PeerManager &pm) {
1601  pm.updatedBlockTip();
1602  BOOST_CHECK(!pm.exists(proof2->getId()));
1603  });
1604  m_processor->withPeerManager([&](avalanche::PeerManager &pm) {
1605  BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), 3 * minScore / 4);
1606  BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), minScore / 4);
1607  });
1608  BOOST_CHECK(m_processor->isQuorumEstablished());
1609 
1610  spendProofUtxo(proof1);
1611  m_processor->withPeerManager([&](avalanche::PeerManager &pm) {
1612  BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), minScore / 4);
1613  BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), minScore / 4);
1614  });
1615  BOOST_CHECK(m_processor->isQuorumEstablished());
1616 
1617  spendProofUtxo(m_processor->getLocalProof());
1618  m_processor->withPeerManager([&](avalanche::PeerManager &pm) {
1621  });
1622  // There is no node left
1623  BOOST_CHECK(!m_processor->isQuorumEstablished());
1624 }
1625 
1626 BOOST_AUTO_TEST_CASE(quorum_detection_parameter_validation) {
1627  // Create vector of tuples of:
1628  // <min stake, min ratio, min avaproofs messages, success bool>
1629  const std::vector<std::tuple<std::string, std::string, std::string, bool>>
1630  testCases = {
1631  // All parameters are invalid
1632  {"", "", "", false},
1633  {"-1", "-1", "-1", false},
1634 
1635  // Min stake is out of range
1636  {"-1", "0", "0", false},
1637  {"-0.01", "0", "0", false},
1638  {"21000000000000.01", "0", "0", false},
1639 
1640  // Min connected ratio is out of range
1641  {"0", "-1", "0", false},
1642  {"0", "1.1", "0", false},
1643 
1644  // Min avaproofs messages ratio is out of range
1645  {"0", "0", "-1", false},
1646 
1647  // All parameters are valid
1648  {"0", "0", "0", true},
1649  {"0.00", "0", "0", true},
1650  {"0.01", "0", "0", true},
1651  {"1", "0.1", "0", true},
1652  {"10", "0.5", "0", true},
1653  {"10", "1", "0", true},
1654  {"21000000000000.00", "0", "0", true},
1655  {"0", "0", "1", true},
1656  {"0", "0", "100", true},
1657  };
1658 
1659  // For each case set the parameters and check that making the processor
1660  // succeeds or fails as expected
1661  for (const auto &[stake, stakeRatio, numProofsMessages, success] :
1662  testCases) {
1663  setArg("-avaminquorumstake", stake);
1664  setArg("-avaminquorumconnectedstakeratio", stakeRatio);
1665  setArg("-avaminavaproofsnodecount", numProofsMessages);
1666 
1668  std::unique_ptr<Processor> processor = Processor::MakeProcessor(
1669  *m_node.args, *m_node.chain, m_node.connman.get(),
1670  *Assert(m_node.chainman), m_node.mempool.get(), *m_node.scheduler,
1671  error);
1672 
1673  if (success) {
1674  BOOST_CHECK(processor != nullptr);
1675  BOOST_CHECK(error.empty());
1676  BOOST_CHECK_EQUAL(error.original, "");
1677  } else {
1678  BOOST_CHECK(processor == nullptr);
1679  BOOST_CHECK(!error.empty());
1680  BOOST_CHECK(error.original != "");
1681  }
1682  }
1683 }
1684 
1685 BOOST_AUTO_TEST_CASE(min_avaproofs_messages) {
1686  ChainstateManager &chainman = *Assert(m_node.chainman);
1687 
1688  auto checkMinAvaproofsMessages = [&](int64_t minAvaproofsMessages) {
1689  setArg("-avaminavaproofsnodecount", ToString(minAvaproofsMessages));
1690 
1692  auto processor = Processor::MakeProcessor(
1693  *m_node.args, *m_node.chain, m_node.connman.get(), chainman,
1694  m_node.mempool.get(), *m_node.scheduler, error);
1695 
1696  auto addNode = [&](NodeId nodeid) {
1697  auto proof = buildRandomProof(chainman.ActiveChainstate(),
1699  processor->withPeerManager([&](avalanche::PeerManager &pm) {
1700  BOOST_CHECK(pm.registerProof(proof));
1701  BOOST_CHECK(pm.addNode(nodeid, proof->getId()));
1702  });
1703  };
1704 
1705  // Add enough node to have a conclusive vote, but don't account any
1706  // avaproofs.
1707  // NOTE: we can't use the test facilites like ConnectNodes() because we
1708  // are not testing on m_processor.
1709  for (NodeId id = 100; id < 108; id++) {
1710  addNode(id);
1711  }
1712 
1713  BOOST_CHECK_EQUAL(processor->isQuorumEstablished(),
1714  minAvaproofsMessages <= 0);
1715 
1716  for (int64_t i = 0; i < minAvaproofsMessages - 1; i++) {
1717  addNode(i);
1718 
1719  processor->avaproofsSent(i);
1720  BOOST_CHECK_EQUAL(processor->getAvaproofsNodeCounter(), i + 1);
1721 
1722  // Receiving again on the same node does not increase the counter
1723  processor->avaproofsSent(i);
1724  BOOST_CHECK_EQUAL(processor->getAvaproofsNodeCounter(), i + 1);
1725 
1726  BOOST_CHECK(!processor->isQuorumEstablished());
1727  }
1728 
1729  addNode(minAvaproofsMessages);
1730  processor->avaproofsSent(minAvaproofsMessages);
1731  BOOST_CHECK(processor->isQuorumEstablished());
1732 
1733  // Check the latch
1734  AvalancheTest::clearavaproofsNodeCounter(*processor);
1735  BOOST_CHECK(processor->isQuorumEstablished());
1736  };
1737 
1738  checkMinAvaproofsMessages(0);
1739  checkMinAvaproofsMessages(1);
1740  checkMinAvaproofsMessages(10);
1741  checkMinAvaproofsMessages(100);
1742 }
1743 
1745  // Check that setting voting parameters has the expected effect
1746  setArg("-avastalevotethreshold",
1748  setArg("-avastalevotefactor", "2");
1749 
1750  const std::vector<std::tuple<int, int>> testCases = {
1751  // {number of yes votes, number of neutral votes}
1754  };
1755 
1757  m_processor = Processor::MakeProcessor(
1758  *m_node.args, *m_node.chain, m_node.connman.get(),
1759  *Assert(m_node.chainman), m_node.mempool.get(), *m_node.scheduler,
1760  error);
1761 
1762  BOOST_CHECK(m_processor != nullptr);
1763  BOOST_CHECK(error.empty());
1764 
1765  P provider(this);
1766  const uint32_t invType = provider.invType;
1767 
1768  const auto item = provider.buildVoteItem();
1769  const auto itemid = provider.getVoteItemId(item);
1770 
1771  // Create nodes that supports avalanche.
1772  auto avanodes = ConnectNodes();
1773  int nextNodeIndex = 0;
1774 
1775  std::vector<avalanche::VoteItemUpdate> updates;
1776  for (const auto &[numYesVotes, numNeutralVotes] : testCases) {
1777  // Add a new item. Check it is added to the polls.
1778  BOOST_CHECK(addToReconcile(item));
1779  auto invs = getInvsForNextPoll();
1780  BOOST_CHECK_EQUAL(invs.size(), 1);
1781  BOOST_CHECK_EQUAL(invs[0].type, invType);
1782  BOOST_CHECK(invs[0].hash == itemid);
1783 
1784  BOOST_CHECK(m_processor->isAccepted(item));
1785 
1786  auto registerNewVote = [&](const Response &resp) {
1787  runEventLoop();
1788  auto nodeid = avanodes[nextNodeIndex++ % avanodes.size()]->GetId();
1789  BOOST_CHECK(registerVotes(nodeid, resp, updates));
1790  };
1791 
1792  // Add some confidence
1793  for (int i = 0; i < numYesVotes; i++) {
1794  Response resp = {getRound(), 0, {Vote(0, itemid)}};
1795  registerNewVote(next(resp));
1796  BOOST_CHECK(m_processor->isAccepted(item));
1797  BOOST_CHECK_EQUAL(m_processor->getConfidence(item),
1798  i >= 6 ? i - 5 : 0);
1799  BOOST_CHECK_EQUAL(updates.size(), 0);
1800  }
1801 
1802  // Vote until just before item goes stale
1803  for (int i = 0; i < numNeutralVotes; i++) {
1804  Response resp = {getRound(), 0, {Vote(-1, itemid)}};
1805  registerNewVote(next(resp));
1806  BOOST_CHECK_EQUAL(updates.size(), 0);
1807  }
1808 
1809  // As long as it is not stale, we poll.
1810  invs = getInvsForNextPoll();
1811  BOOST_CHECK_EQUAL(invs.size(), 1);
1812  BOOST_CHECK_EQUAL(invs[0].type, invType);
1813  BOOST_CHECK(invs[0].hash == itemid);
1814 
1815  // Now stale
1816  Response resp = {getRound(), 0, {Vote(-1, itemid)}};
1817  registerNewVote(next(resp));
1818  BOOST_CHECK_EQUAL(updates.size(), 1);
1819  BOOST_CHECK(provider.fromAnyVoteItem(updates[0].getVoteItem()) == item);
1820  BOOST_CHECK(updates[0].getStatus() == VoteStatus::Stale);
1821  updates.clear();
1822 
1823  // Once stale, there is no poll for it.
1824  invs = getInvsForNextPoll();
1825  BOOST_CHECK_EQUAL(invs.size(), 0);
1826  }
1827 }
1828 
1829 BOOST_AUTO_TEST_CASE(block_vote_finalization_tip) {
1830  BlockProvider provider(this);
1831 
1832  std::vector<CBlockIndex *> blockIndexes;
1833  for (size_t i = 0; i < AVALANCHE_MAX_ELEMENT_POLL; i++) {
1834  CBlockIndex *pindex = provider.buildVoteItem();
1835  BOOST_CHECK(addToReconcile(pindex));
1836  blockIndexes.push_back(pindex);
1837  }
1838 
1839  auto invs = getInvsForNextPoll();
1841  for (size_t i = 0; i < AVALANCHE_MAX_ELEMENT_POLL; i++) {
1843  invs[i].hash,
1844  blockIndexes[AVALANCHE_MAX_ELEMENT_POLL - i - 1]->GetBlockHash());
1845  }
1846 
1847  // Build a vote vector with the 11th block only being accepted and others
1848  // unknown.
1849  const BlockHash eleventhBlockHash =
1850  blockIndexes[AVALANCHE_MAX_ELEMENT_POLL - 10 - 1]->GetBlockHash();
1851  std::vector<Vote> votes;
1852  votes.reserve(AVALANCHE_MAX_ELEMENT_POLL);
1853  for (size_t i = AVALANCHE_MAX_ELEMENT_POLL; i > 0; i--) {
1854  BlockHash blockhash = blockIndexes[i - 1]->GetBlockHash();
1855  votes.emplace_back(blockhash == eleventhBlockHash ? 0 : -1, blockhash);
1856  }
1857 
1858  auto avanodes = ConnectNodes();
1859  int nextNodeIndex = 0;
1860 
1861  std::vector<avalanche::VoteItemUpdate> updates;
1862  auto registerNewVote = [&]() {
1863  Response resp = {getRound(), 0, votes};
1864  runEventLoop();
1865  auto nodeid = avanodes[nextNodeIndex++ % avanodes.size()]->GetId();
1866  BOOST_CHECK(registerVotes(nodeid, resp, updates));
1867  };
1868 
1869  // Vote for the blocks until the one being accepted finalizes
1870  bool eleventhBlockFinalized = false;
1871  for (size_t i = 0; i < 10000 && !eleventhBlockFinalized; i++) {
1872  registerNewVote();
1873 
1874  for (auto &update : updates) {
1875  if (update.getStatus() == VoteStatus::Finalized &&
1876  provider.fromAnyVoteItem(update.getVoteItem())
1877  ->GetBlockHash() == eleventhBlockHash) {
1878  eleventhBlockFinalized = true;
1879  }
1880  }
1881  }
1882  BOOST_CHECK(eleventhBlockFinalized);
1883 
1884  // From now only the 10 blocks with more work are polled for
1885  invs = getInvsForNextPoll();
1886  BOOST_CHECK_EQUAL(invs.size(), 10);
1887  for (size_t i = 0; i < 10; i++) {
1889  invs[i].hash,
1890  blockIndexes[AVALANCHE_MAX_ELEMENT_POLL - i - 1]->GetBlockHash());
1891  }
1892 
1893  // Adding ancestor blocks to reconcile will fail
1894  for (size_t i = 0; i < AVALANCHE_MAX_ELEMENT_POLL - 10 - 1; i++) {
1895  BOOST_CHECK(!addToReconcile(blockIndexes[i]));
1896  }
1897 
1898  // Create a couple concurrent chain tips
1899  CBlockIndex *tip = provider.buildVoteItem();
1900 
1901  auto &activeChainstate = m_node.chainman->ActiveChainstate();
1902  BlockValidationState state;
1903  activeChainstate.InvalidateBlock(state, tip);
1904 
1905  // Use another script to make sure we don't generate the same block again
1906  CBlock altblock = CreateAndProcessBlock({}, CScript() << OP_TRUE);
1907  auto alttip = WITH_LOCK(
1908  cs_main, return Assert(m_node.chainman)
1909  ->m_blockman.LookupBlockIndex(altblock.GetHash()));
1910  BOOST_CHECK(alttip);
1911  BOOST_CHECK(alttip->pprev == tip->pprev);
1912  BOOST_CHECK(alttip->GetBlockHash() != tip->GetBlockHash());
1913 
1914  // Reconsider the previous tip valid, so we have concurrent tip candidates
1915  {
1916  LOCK(cs_main);
1917  activeChainstate.ResetBlockFailureFlags(tip);
1918  }
1919  activeChainstate.ActivateBestChain(state);
1920 
1921  BOOST_CHECK(addToReconcile(tip));
1922  BOOST_CHECK(addToReconcile(alttip));
1923  invs = getInvsForNextPoll();
1924  BOOST_CHECK_EQUAL(invs.size(), 12);
1925 
1926  // Vote for the tip until it finalizes
1927  BlockHash tiphash = tip->GetBlockHash();
1928  votes.clear();
1929  votes.reserve(12);
1930  for (auto &inv : invs) {
1931  votes.emplace_back(inv.hash == tiphash ? 0 : -1, inv.hash);
1932  }
1933 
1934  bool tipFinalized = false;
1935  for (size_t i = 0; i < 10000 && !tipFinalized; i++) {
1936  registerNewVote();
1937 
1938  for (auto &update : updates) {
1939  if (update.getStatus() == VoteStatus::Finalized &&
1940  provider.fromAnyVoteItem(update.getVoteItem())
1941  ->GetBlockHash() == tiphash) {
1942  tipFinalized = true;
1943  }
1944  }
1945  }
1946  BOOST_CHECK(tipFinalized);
1947 
1948  // Now the tip and all its ancestors will be removed from polls. Only the
1949  // alttip remains because it is on a forked chain so we want to keep polling
1950  // for that one until it's invalidated or stalled.
1951  invs = getInvsForNextPoll();
1952  BOOST_CHECK_EQUAL(invs.size(), 1);
1953  BOOST_CHECK_EQUAL(invs[0].hash, alttip->GetBlockHash());
1954 
1955  // Cannot reconcile a finalized block
1956  BOOST_CHECK(!addToReconcile(tip));
1957 
1958  // Vote for alttip until it invalidates
1959  BlockHash alttiphash = alttip->GetBlockHash();
1960  votes = {{1, alttiphash}};
1961 
1962  bool alttipInvalidated = false;
1963  for (size_t i = 0; i < 10000 && !alttipInvalidated; i++) {
1964  registerNewVote();
1965 
1966  for (auto &update : updates) {
1967  if (update.getStatus() == VoteStatus::Invalid &&
1968  provider.fromAnyVoteItem(update.getVoteItem())
1969  ->GetBlockHash() == alttiphash) {
1970  alttipInvalidated = true;
1971  }
1972  }
1973  }
1974  BOOST_CHECK(alttipInvalidated);
1975  invs = getInvsForNextPoll();
1976  BOOST_CHECK_EQUAL(invs.size(), 0);
1977 
1978  // Cannot reconcile an invalidated block
1979  BOOST_CHECK(!addToReconcile(alttip));
1980 }
1981 
1982 BOOST_AUTO_TEST_CASE(vote_map_comparator) {
1983  ChainstateManager &chainman = *Assert(m_node.chainman);
1984  Chainstate &activeChainState = chainman.ActiveChainstate();
1985 
1986  const int numberElementsEachType = 100;
1987  FastRandomContext rng;
1988 
1989  std::vector<ProofRef> proofs;
1990  for (size_t i = 1; i <= numberElementsEachType; i++) {
1991  auto proof =
1992  buildRandomProof(activeChainState, i * MIN_VALID_PROOF_SCORE);
1993  BOOST_CHECK(proof != nullptr);
1994  proofs.emplace_back(std::move(proof));
1995  }
1996  Shuffle(proofs.begin(), proofs.end(), rng);
1997 
1998  std::vector<CBlockIndex> indexes;
1999  for (size_t i = 1; i <= numberElementsEachType; i++) {
2000  CBlockIndex index;
2001  index.nChainWork = i;
2002  indexes.emplace_back(std::move(index));
2003  }
2004  Shuffle(indexes.begin(), indexes.end(), rng);
2005 
2006  CTxMemPool *mempool = Assert(m_node.mempool.get());
2007  TestMemPoolEntryHelper mempoolEntryHelper;
2008  std::vector<CTransactionRef> txs;
2009  for (size_t i = 1; i <= numberElementsEachType; i++) {
2010  CMutableTransaction mtx;
2011  mtx.nVersion = 2;
2012  mtx.vin.emplace_back(COutPoint{TxId(rng.rand256()), 0});
2013  mtx.vout.emplace_back(1000 * COIN, CScript() << OP_TRUE);
2014 
2015  CTransactionRef tx = MakeTransactionRef(std::move(mtx));
2016 
2017  auto entry = mempoolEntryHelper.Fee(int64_t(i) * COIN).FromTx(tx);
2018  {
2019  LOCK2(cs_main, mempool->cs);
2020  mempool->addUnchecked(entry);
2021  BOOST_CHECK(mempool->exists(tx->GetId()));
2022  }
2023 
2024  txs.emplace_back(std::move(tx));
2025  }
2026 
2027  auto allItems =
2028  std::make_tuple(std::move(proofs), std::move(indexes), std::move(txs));
2029  static const size_t numTypes = std::tuple_size<decltype(allItems)>::value;
2030 
2031  RWCollection<VoteMap> voteMap(VoteMap(m_node.mempool.get()));
2032 
2033  {
2034  auto writeView = voteMap.getWriteView();
2035  for (size_t i = 0; i < numberElementsEachType; i++) {
2036  // Randomize the insert order at each loop increment
2037  const size_t firstType = rng.randrange(numTypes);
2038 
2039  for (size_t j = 0; j < numTypes; j++) {
2040  switch ((firstType + j) % numTypes) {
2041  // ProofRef
2042  case 0:
2043  writeView->insert(std::make_pair(
2044  std::get<0>(allItems)[i], VoteRecord(true)));
2045  break;
2046  // CBlockIndex *
2047  case 1:
2048  writeView->insert(std::make_pair(
2049  &std::get<1>(allItems)[i], VoteRecord(true)));
2050  break;
2051  // CTransactionRef
2052  case 2:
2053  writeView->insert(std::make_pair(
2054  std::get<2>(allItems)[i], VoteRecord(true)));
2055  break;
2056  default:
2057  break;
2058  }
2059  }
2060  }
2061  }
2062 
2063  {
2064  // Check ordering
2065  auto readView = voteMap.getReadView();
2066  auto it = readView.begin();
2067 
2068  // The first batch of items is the proofs ordered by score
2069  // (descending)
2070  uint32_t lastScore = std::numeric_limits<uint32_t>::max();
2071  for (size_t i = 0; i < numberElementsEachType; i++) {
2072  BOOST_CHECK(std::holds_alternative<const ProofRef>(it->first));
2073 
2074  uint32_t currentScore =
2075  std::get<const ProofRef>(it->first)->getScore();
2076  BOOST_CHECK_LT(currentScore, lastScore);
2077  lastScore = currentScore;
2078 
2079  it++;
2080  }
2081 
2082  // The next batch of items is the block indexes ordered by work
2083  // (descending)
2084  arith_uint256 lastWork = ~arith_uint256(0);
2085  for (size_t i = 0; i < numberElementsEachType; i++) {
2086  BOOST_CHECK(std::holds_alternative<const CBlockIndex *>(it->first));
2087 
2088  arith_uint256 currentWork =
2089  std::get<const CBlockIndex *>(it->first)->nChainWork;
2090  BOOST_CHECK(currentWork < lastWork);
2091  lastWork = currentWork;
2092 
2093  it++;
2094  }
2095 
2096  // The last batch of items is the txs ordered by modified fee rate
2097  CFeeRate lastFeeRate{MAX_MONEY};
2098  {
2099  LOCK(mempool->cs);
2100 
2101  for (size_t i = 0; i < numberElementsEachType; i++) {
2102  BOOST_CHECK(
2103  std::holds_alternative<const CTransactionRef>(it->first));
2104 
2105  auto iter = mempool->GetIter(
2106  std::get<const CTransactionRef>(it->first)->GetId());
2107  BOOST_CHECK(iter.has_value());
2108 
2109  CFeeRate currentFeeRate = (**iter)->GetModifiedFeeRate();
2110 
2111  BOOST_CHECK(currentFeeRate < lastFeeRate);
2112  lastFeeRate = currentFeeRate;
2113 
2114  it++;
2115  }
2116  }
2117 
2118  BOOST_CHECK(it == readView.end());
2119  }
2120 }
2121 
2122 BOOST_AUTO_TEST_CASE(vote_map_tx_comparator) {
2123  CTxMemPool *mempool = Assert(m_node.mempool.get());
2124  TestMemPoolEntryHelper mempoolEntryHelper;
2125  TxProvider provider(this);
2126 
2127  std::vector<CTransactionRef> txs;
2128  for (size_t i = 0; i < 5; i++) {
2129  txs.emplace_back(provider.buildVoteItem());
2130  }
2131 
2132  {
2133  // When there is no mempool, the txs are sorted by txid
2134  RWCollection<VoteMap> voteMap(VoteMap(nullptr));
2135  {
2136  auto writeView = voteMap.getWriteView();
2137  for (const auto &tx : txs) {
2138  writeView->insert(std::make_pair(tx, VoteRecord(true)));
2139  }
2140  }
2141 
2142  auto readView = voteMap.getReadView();
2143  TxId lastTxId{uint256::ZERO};
2144  for (const auto &[item, vote] : readView) {
2145  auto tx = std::get<const CTransactionRef>(item);
2146  BOOST_CHECK_GT(tx->GetId(), lastTxId);
2147  lastTxId = tx->GetId();
2148  }
2149  }
2150 
2151  // Remove the 5 first txs from the mempool, and add 5 more
2152  mempool->clear();
2153  for (size_t i = 0; i < 5; i++) {
2154  txs.emplace_back(provider.buildVoteItem());
2155  }
2156 
2157  {
2158  RWCollection<VoteMap> voteMap((VoteMap(mempool)));
2159 
2160  {
2161  auto writeView = voteMap.getWriteView();
2162  for (const auto &tx : txs) {
2163  writeView->insert(std::make_pair(tx, VoteRecord(true)));
2164  }
2165  }
2166 
2167  auto readView = voteMap.getReadView();
2168  auto it = readView.begin();
2169 
2170  LOCK(mempool->cs);
2171 
2172  // The first 5 txs are sorted by fee
2173  CFeeRate lastFeeRate{MAX_MONEY};
2174  for (size_t i = 0; i < 5; i++) {
2175  auto tx = std::get<const CTransactionRef>(it->first);
2176 
2177  auto iter = mempool->GetIter(tx->GetId());
2178  BOOST_CHECK(iter.has_value());
2179 
2180  BOOST_CHECK((**iter)->GetModifiedFeeRate() <= lastFeeRate);
2181  lastFeeRate = (**iter)->GetModifiedFeeRate();
2182  it++;
2183  }
2184 
2185  // The last 5 txs are sorted by txid
2186  TxId lastTxId{uint256::ZERO};
2187  for (size_t i = 0; i < 5; i++) {
2188  auto tx = std::get<const CTransactionRef>(it->first);
2189 
2190  BOOST_CHECK(!mempool->exists(tx->GetId()));
2191 
2192  BOOST_CHECK_GT(tx->GetId(), lastTxId);
2193  lastTxId = tx->GetId();
2194  it++;
2195  }
2196  }
2197 }
2198 
2199 BOOST_AUTO_TEST_CASE(block_reconcile_initial_vote) {
2200  auto &chainman = Assert(m_node.chainman);
2201  Chainstate &chainstate = chainman->ActiveChainstate();
2202 
2203  const auto block = std::make_shared<const CBlock>(
2204  this->CreateBlock({}, CScript(), chainstate));
2205  const BlockHash blockhash = block->GetHash();
2206 
2207  BlockValidationState state;
2208  CBlockIndex *blockindex;
2209  {
2210  LOCK(cs_main);
2211  BOOST_CHECK(chainstate.AcceptBlock(block, state,
2212  /*fRequested=*/true, /*dbp=*/nullptr,
2213  /*fNewBlock=*/nullptr,
2214  /*min_pow_checked=*/true));
2215 
2216  blockindex = chainman->m_blockman.LookupBlockIndex(blockhash);
2217  BOOST_CHECK(blockindex);
2218  }
2219 
2220  // ActivateBestChain() interacts with g_avalanche, so make it happy
2221  g_avalanche = std::move(m_processor);
2222 
2223  // The block is not connected yet, and not added to the poll list yet
2224  BOOST_CHECK(AvalancheTest::getInvsForNextPoll(*g_avalanche).empty());
2225  BOOST_CHECK(!g_avalanche->isAccepted(blockindex));
2226 
2227  // Call ActivateBestChain to connect the new block
2228  BOOST_CHECK(chainstate.ActivateBestChain(state, block));
2229  // It is a valid block so the tip is updated
2230  BOOST_CHECK_EQUAL(chainstate.m_chain.Tip(), blockindex);
2231 
2232  // Check the block is added to the poll
2233  auto invs = AvalancheTest::getInvsForNextPoll(*g_avalanche);
2234  BOOST_CHECK_EQUAL(invs.size(), 1);
2235  BOOST_CHECK_EQUAL(invs[0].type, MSG_BLOCK);
2236  BOOST_CHECK_EQUAL(invs[0].hash, blockhash);
2237 
2238  // This block is our new tip so we should vote "yes"
2239  BOOST_CHECK(g_avalanche->isAccepted(blockindex));
2240 
2241  // Prevent a data race between UpdatedBlockTip and the Processor destructor
2243 
2244  g_avalanche.reset(nullptr);
2245 }
2246 
2247 BOOST_AUTO_TEST_CASE(compute_staking_rewards) {
2248  auto now = GetTime<std::chrono::seconds>();
2249  SetMockTime(now);
2250 
2251  // Pick in the middle
2252  BlockHash prevBlockHash{uint256::ZERO};
2253 
2254  std::vector<CScript> winners;
2255 
2256  BOOST_CHECK(!m_processor->getStakingRewardWinners(prevBlockHash, winners));
2257 
2258  // Null index
2259  BOOST_CHECK(!m_processor->computeStakingReward(nullptr));
2260  BOOST_CHECK(!m_processor->getStakingRewardWinners(prevBlockHash, winners));
2261 
2262  CBlockIndex prevBlock;
2263  prevBlock.phashBlock = &prevBlockHash;
2264  prevBlock.nHeight = 100;
2265  prevBlock.nTime = now.count();
2266 
2267  // No quorum
2268  BOOST_CHECK(!m_processor->computeStakingReward(&prevBlock));
2269  BOOST_CHECK(!m_processor->getStakingRewardWinners(prevBlockHash, winners));
2270 
2271  setArg("-avaminquorumstake", "0");
2272  setArg("-avaminquorumconnectedstakeratio", "0");
2273  setArg("-avaminavaproofsnodecount", "0");
2274 
2275  // Setup a bunch of proofs
2276  size_t numProofs = 10;
2277  std::vector<ProofRef> proofs;
2278  proofs.reserve(numProofs);
2279  for (size_t i = 0; i < numProofs; i++) {
2280  const CKey key = CKey::MakeCompressedKey();
2281  CScript payoutScript = GetScriptForRawPubKey(key.GetPubKey());
2282 
2283  auto proof = GetProof(payoutScript);
2284  m_processor->withPeerManager([&](avalanche::PeerManager &pm) {
2285  BOOST_CHECK(pm.registerProof(proof));
2286  BOOST_CHECK(pm.addNode(i, proof->getId()));
2287  // Finalize the proof
2288  BOOST_CHECK(pm.forPeer(proof->getId(), [&](const Peer peer) {
2289  return pm.setFinalized(peer.peerid);
2290  }));
2291  });
2292 
2293  proofs.emplace_back(std::move(proof));
2294  }
2295 
2296  BOOST_CHECK(m_processor->isQuorumEstablished());
2297 
2298  // Proofs are too recent so we still have no winner
2299  BOOST_CHECK(!m_processor->computeStakingReward(&prevBlock));
2300  BOOST_CHECK(!m_processor->getStakingRewardWinners(prevBlockHash, winners));
2301 
2302  // Make sure we picked a payout script from one of our proofs
2303  auto winnerExists = [&](const CScript &expectedWinner) {
2304  const std::string winnerString = FormatScript(expectedWinner);
2305 
2306  for (const ProofRef &proof : proofs) {
2307  if (winnerString == FormatScript(proof->getPayoutScript())) {
2308  return true;
2309  }
2310  }
2311  return false;
2312  };
2313 
2314  // Elapse some time
2315  now += 1h;
2316  SetMockTime(now);
2317  prevBlock.nTime = now.count();
2318 
2319  // Now we successfully inserted a winner in our map
2320  BOOST_CHECK(m_processor->computeStakingReward(&prevBlock));
2321  BOOST_CHECK(m_processor->getStakingRewardWinners(prevBlockHash, winners));
2322  BOOST_CHECK(winnerExists(winners[0]));
2323 
2324  // Subsequent calls are a no-op
2325  BOOST_CHECK(m_processor->computeStakingReward(&prevBlock));
2326  BOOST_CHECK(m_processor->getStakingRewardWinners(prevBlockHash, winners));
2327  BOOST_CHECK(winnerExists(winners[0]));
2328 
2329  CBlockIndex prevBlockHigh = prevBlock;
2330  BlockHash prevBlockHashHigh =
2331  BlockHash(ArithToUint256({std::numeric_limits<uint64_t>::max()}));
2332  prevBlockHigh.phashBlock = &prevBlockHashHigh;
2333  prevBlockHigh.nHeight = 101;
2334  BOOST_CHECK(m_processor->computeStakingReward(&prevBlockHigh));
2335  BOOST_CHECK(
2336  m_processor->getStakingRewardWinners(prevBlockHashHigh, winners));
2337  BOOST_CHECK(winnerExists(winners[0]));
2338 
2339  // No impact on previous winner so far
2340  BOOST_CHECK(m_processor->getStakingRewardWinners(prevBlockHash, winners));
2341  BOOST_CHECK(winnerExists(winners[0]));
2342 
2343  // Cleanup to height 101
2344  m_processor->cleanupStakingRewards(101);
2345 
2346  // Now the previous winner has been cleared
2347  BOOST_CHECK(!m_processor->getStakingRewardWinners(prevBlockHash, winners));
2348 
2349  // But the last one remain
2350  BOOST_CHECK(
2351  m_processor->getStakingRewardWinners(prevBlockHashHigh, winners));
2352  BOOST_CHECK(winnerExists(winners[0]));
2353 
2354  // We can add it again
2355  BOOST_CHECK(m_processor->computeStakingReward(&prevBlock));
2356  BOOST_CHECK(m_processor->getStakingRewardWinners(prevBlockHash, winners));
2357  BOOST_CHECK(winnerExists(winners[0]));
2358 
2359  // Cleanup to higher height
2360  m_processor->cleanupStakingRewards(200);
2361 
2362  // No winner anymore
2363  BOOST_CHECK(!m_processor->getStakingRewardWinners(prevBlockHash, winners));
2364  BOOST_CHECK(
2365  !m_processor->getStakingRewardWinners(prevBlockHashHigh, winners));
2366 }
2367 
2368 BOOST_AUTO_TEST_CASE(local_proof_status) {
2369  const CKey key = CKey::MakeCompressedKey();
2370 
2371  const COutPoint outpoint{TxId(GetRandHash()), 0};
2372  {
2374 
2375  LOCK(cs_main);
2376  CCoinsViewCache &coins =
2377  Assert(m_node.chainman)->ActiveChainstate().CoinsTip();
2378  coins.AddCoin(outpoint,
2379  Coin(CTxOut(PROOF_DUST_THRESHOLD, script), 100, false),
2380  false);
2381  }
2382 
2383  auto buildProof = [&](const COutPoint &outpoint, uint64_t sequence,
2384  uint32_t height) {
2385  ProofBuilder pb(sequence, 0, key, UNSPENDABLE_ECREG_PAYOUT_SCRIPT);
2386  BOOST_CHECK(
2387  pb.addUTXO(outpoint, PROOF_DUST_THRESHOLD, height, false, key));
2388  return pb.build();
2389  };
2390 
2391  auto localProof = buildProof(outpoint, 1, 100);
2392 
2393  setArg("-avamasterkey", EncodeSecret(key));
2394  setArg("-avaproof", localProof->ToHex());
2395  setArg("-avalancheconflictingproofcooldown", "0");
2396  setArg("-avalanchepeerreplacementcooldown", "0");
2397  setArg("-avaproofstakeutxoconfirmations", "3");
2398 
2400  ChainstateManager &chainman = *Assert(m_node.chainman);
2401  m_processor = Processor::MakeProcessor(
2402  *m_node.args, *m_node.chain, m_node.connman.get(), chainman,
2403  m_node.mempool.get(), *m_node.scheduler, error);
2404 
2405  BOOST_CHECK_EQUAL(m_processor->getLocalProof()->getId(),
2406  localProof->getId());
2407 
2408  auto checkLocalProofState =
2409  [&](const bool boundToPeer,
2410  const ProofRegistrationResult expectedResult) {
2412  m_processor->withPeerManager([&](avalanche::PeerManager &pm) {
2413  return pm.isBoundToPeer(localProof->getId());
2414  }),
2415  boundToPeer);
2416  BOOST_CHECK_MESSAGE(
2417  m_processor->getLocalProofRegistrationState().GetResult() ==
2418  expectedResult,
2419  m_processor->getLocalProofRegistrationState().ToString());
2420  };
2421 
2422  checkLocalProofState(false, ProofRegistrationResult::NONE);
2423 
2424  // Not ready to share, the local proof isn't registered
2425  BOOST_CHECK(!m_processor->canShareLocalProof());
2426  AvalancheTest::updatedBlockTip(*m_processor);
2427  checkLocalProofState(false, ProofRegistrationResult::NONE);
2428 
2429  // Ready to share, but the proof is immature
2430  AvalancheTest::setLocalProofShareable(*m_processor, true);
2431  BOOST_CHECK(m_processor->canShareLocalProof());
2432  AvalancheTest::updatedBlockTip(*m_processor);
2433  checkLocalProofState(false, ProofRegistrationResult::IMMATURE);
2434 
2435  // Mine a block to re-evaluate the proof, it remains immature
2436  mineBlocks(1);
2437  AvalancheTest::updatedBlockTip(*m_processor);
2438  checkLocalProofState(false, ProofRegistrationResult::IMMATURE);
2439 
2440  // One more block and the proof turns mature
2441  mineBlocks(1);
2442  AvalancheTest::updatedBlockTip(*m_processor);
2443  checkLocalProofState(true, ProofRegistrationResult::NONE);
2444 
2445  // Build a conflicting proof and check the status is updated accordingly
2446  auto conflictingProof = buildProof(outpoint, 2, 100);
2447  m_processor->withPeerManager([&](avalanche::PeerManager &pm) {
2448  BOOST_CHECK(pm.registerProof(conflictingProof));
2449  BOOST_CHECK(pm.isBoundToPeer(conflictingProof->getId()));
2450  BOOST_CHECK(pm.isInConflictingPool(localProof->getId()));
2451  });
2452  AvalancheTest::updatedBlockTip(*m_processor);
2453  checkLocalProofState(false, ProofRegistrationResult::CONFLICTING);
2454 }
2455 
2456 BOOST_AUTO_TEST_CASE(reconcileOrFinalize) {
2457  setArg("-avalancheconflictingproofcooldown", "0");
2458  setArg("-avalanchepeerreplacementcooldown", "0");
2459 
2460  // Proof is null
2461  BOOST_CHECK(!m_processor->reconcileOrFinalize(ProofRef()));
2462 
2463  ChainstateManager &chainman = *Assert(m_node.chainman);
2464  Chainstate &activeChainState = chainman.ActiveChainstate();
2465 
2466  const CKey key = CKey::MakeCompressedKey();
2467  const COutPoint outpoint{TxId(GetRandHash()), 0};
2468  {
2470 
2471  LOCK(cs_main);
2472  CCoinsViewCache &coins = activeChainState.CoinsTip();
2473  coins.AddCoin(outpoint,
2474  Coin(CTxOut(PROOF_DUST_THRESHOLD, script), 100, false),
2475  false);
2476  }
2477 
2478  auto buildProof = [&](const COutPoint &outpoint, uint64_t sequence) {
2479  ProofBuilder pb(sequence, 0, key, UNSPENDABLE_ECREG_PAYOUT_SCRIPT);
2480  BOOST_CHECK(
2481  pb.addUTXO(outpoint, PROOF_DUST_THRESHOLD, 100, false, key));
2482  return pb.build();
2483  };
2484 
2485  auto proof = buildProof(outpoint, 1);
2486  BOOST_CHECK(proof);
2487 
2488  // Not a peer nor conflicting
2489  BOOST_CHECK(!m_processor->reconcileOrFinalize(proof));
2490 
2491  // Register the proof
2492  m_processor->withPeerManager([&](avalanche::PeerManager &pm) {
2493  BOOST_CHECK(pm.registerProof(proof));
2494  BOOST_CHECK(pm.isBoundToPeer(proof->getId()));
2495  BOOST_CHECK(!pm.isInConflictingPool(proof->getId()));
2496  });
2497 
2498  // Reconcile works
2499  BOOST_CHECK(m_processor->reconcileOrFinalize(proof));
2500  // Repeated calls fail and do nothing
2501  BOOST_CHECK(!m_processor->reconcileOrFinalize(proof));
2502 
2503  // Finalize
2504  AvalancheTest::addProofToRecentfinalized(*m_processor, proof->getId());
2505  BOOST_CHECK(m_processor->isRecentlyFinalized(proof->getId()));
2506  BOOST_CHECK(m_processor->reconcileOrFinalize(proof));
2507 
2508  m_processor->withPeerManager([&](avalanche::PeerManager &pm) {
2509  // The peer is marked as final
2510  BOOST_CHECK(pm.forPeer(proof->getId(), [&](const Peer &peer) {
2511  return peer.hasFinalized;
2512  }));
2513  BOOST_CHECK(pm.isBoundToPeer(proof->getId()));
2514  BOOST_CHECK(!pm.isInConflictingPool(proof->getId()));
2515  });
2516 
2517  // Same proof with a higher sequence number
2518  auto betterProof = buildProof(outpoint, 2);
2519  BOOST_CHECK(betterProof);
2520 
2521  // Not registered nor conflicting yet
2522  BOOST_CHECK(!m_processor->reconcileOrFinalize(betterProof));
2523 
2524  m_processor->withPeerManager([&](avalanche::PeerManager &pm) {
2525  BOOST_CHECK(pm.registerProof(betterProof));
2526  BOOST_CHECK(pm.isBoundToPeer(betterProof->getId()));
2527  BOOST_CHECK(!pm.isInConflictingPool(betterProof->getId()));
2528 
2529  BOOST_CHECK(!pm.isBoundToPeer(proof->getId()));
2530  BOOST_CHECK(pm.isInConflictingPool(proof->getId()));
2531  });
2532 
2533  // Recently finalized, not worth polling
2534  BOOST_CHECK(!m_processor->reconcileOrFinalize(proof));
2535  // But the better proof can be polled
2536  BOOST_CHECK(m_processor->reconcileOrFinalize(betterProof));
2537 }
2538 
static constexpr Amount MAX_MONEY
No amount larger than this (in satoshi) is valid.
Definition: amount.h:165
static constexpr Amount COIN
Definition: amount.h:144
uint256 ArithToUint256(const arith_uint256 &a)
std::unique_ptr< avalanche::Processor > g_avalanche
Global avalanche instance.
Definition: processor.cpp:38
const CChainParams & Params()
Return the currently selected parameters.
#define Assert(val)
Identity function.
Definition: check.h:84
void ForceSetArg(const std::string &strArg, const std::string &strValue)
Definition: system.cpp:706
void ClearForcedArg(const std::string &strArg)
Remove a forced arg setting, used only in testing.
Definition: system.cpp:757
A CService with information about it as peer.
Definition: protocol.h:442
BlockHash GetHash() const
Definition: block.cpp:11
Definition: block.h:60
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
const BlockHash * phashBlock
pointer to the hash of the block, if any.
Definition: blockindex.h:30
uint32_t nTime
Definition: blockindex.h:93
BlockHash GetBlockHash() const
Definition: blockindex.h:147
int nHeight
height of the entry in the chain. The genesis block has height 0
Definition: blockindex.h:39
CBlockIndex * Tip() const
Returns the index entry for the tip of this chain, or nullptr if none.
Definition: chain.h:156
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:845
CConnman(const Config &configIn, uint64_t seed0, uint64_t seed1, AddrMan &addrmanIn, bool network_active=true)
Definition: net.cpp:2856
Fee rate in satoshis per kilobyte: Amount / kB.
Definition: feerate.h:21
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:274
std::vector< CTxOut > vout
Definition: transaction.h:277
std::vector< CTxIn > vin
Definition: transaction.h:276
Network address.
Definition: netaddress.h:121
Information about a peer.
Definition: net.h:456
An outpoint - a combination of a transaction hash and an index n into its vout.
Definition: transaction.h:20
Simple class for background tasks that should be run periodically or once "after a while".
Definition: scheduler.h:41
void serviceQueue() EXCLUSIVE_LOCKS_REQUIRED(!newTaskMutex)
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 EXCLUSIVE_LOCKS_REQUIRED(!newTaskMutex)
Returns number of tasks waiting to be serviced, and first and last task times.
Definition: scheduler.cpp:120
void StopWhenDrained() EXCLUSIVE_LOCKS_REQUIRED(!newTaskMutex)
Tell any threads running serviceQueue to stop when there is no work left to be done.
Definition: scheduler.h:100
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:545
CTxMemPool stores valid-according-to-the-current-best-chain transactions that may be included in the ...
Definition: txmempool.h:209
RecursiveMutex cs
This mutex needs to be locked when accessing mapTx or other members that are guarded by it.
Definition: txmempool.h:296
void removeRecursive(const CTransaction &tx, MemPoolRemovalReason reason) EXCLUSIVE_LOCKS_REQUIRED(cs)
Definition: txmempool.cpp:258
void clear()
Definition: txmempool.cpp:339
bool exists(const TxId &txid) const
Definition: txmempool.h:492
void check(const CCoinsViewCache &active_coins_tip, int64_t spendheight) const EXCLUSIVE_LOCKS_REQUIRED(void addUnchecked(CTxMemPoolEntryRef 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:361
std::optional< txiter > GetIter(const TxId &txid) const EXCLUSIVE_LOCKS_REQUIRED(cs)
Returns an iterator to the given txid, if found.
Definition: txmempool.cpp:572
An output of a transaction.
Definition: transaction.h:128
Chainstate stores and provides an API to update our local knowledge of the current best chain.
Definition: validation.h:629
CChain m_chain
The current chain of blockheaders we consult and build on.
Definition: validation.h:738
bool ActivateBestChain(BlockValidationState &state, std::shared_ptr< const CBlock > pblock=nullptr, bool skip_checkblockindex=false) EXCLUSIVE_LOCKS_REQUIRED(!m_chainstate_mutex
Find the best known block, and make it the tip of the block chain.
CCoinsViewCache & CoinsTip() EXCLUSIVE_LOCKS_REQUIRED(
Definition: validation.h:764
bool AcceptBlock(const std::shared_ptr< const CBlock > &pblock, BlockValidationState &state, bool fRequested, const FlatFilePos *dbp, bool *fNewBlock, bool min_pow_checked) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
Store a block on disk.
Provides an interface for creating and interacting with one or two chainstates: an IBD chainstate gen...
Definition: validation.h:1144
RecursiveMutex & GetMutex() const LOCK_RETURNED(
Alias for cs_main.
Definition: validation.h:1265
CBlockIndex * ActiveTip() const EXCLUSIVE_LOCKS_REQUIRED(GetMutex())
Definition: validation.h:1357
A UTXO entry.
Definition: coins.h:27
Fast randomness source.
Definition: random.h:156
uint256 rand256() noexcept
generate a random uint256.
Definition: random.cpp:681
uint64_t randrange(uint64_t range) noexcept
Generate a random integer in the range [0..range).
Definition: random.h:231
static std::unique_ptr< PeerManager > make(CConnman &connman, AddrMan &addrman, BanMan *banman, ChainstateManager &chainman, CTxMemPool &pool, bool ignore_incoming_txs)
ReadView getReadView() const
Definition: rwcollection.h:78
WriteView getWriteView()
Definition: rwcollection.h:84
iterator begin()
Definition: rwcollection.h:41
256-bit unsigned big integer.
bool removeNode(NodeId nodeid)
uint32_t getConnectedPeersScore() const
Definition: peermanager.h:436
bool exists(const ProofId &proofid) const
Definition: peermanager.h:400
bool forPeer(const ProofId &proofid, Callable &&func) const
Definition: peermanager.h:408
uint32_t getTotalPeersScore() const
Definition: peermanager.h:435
bool addNode(NodeId nodeid, const ProofId &proofid)
Node API.
Definition: peermanager.cpp:30
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)
Mutex cs_finalizedItems
Rolling bloom filter to track recently finalized inventory items of any type.
Definition: processor.h:417
std::vector< CInv > getInvsForNextPoll(bool forPoll=true) EXCLUSIVE_LOCKS_REQUIRED(!cs_peerManager
Definition: processor.cpp:1119
std::atomic< uint64_t > round
Keep track of peers and queries sent.
Definition: processor.h:196
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:220
void runEventLoop() EXCLUSIVE_LOCKS_REQUIRED(!cs_peerManager
Definition: processor.cpp:1012
void updatedBlockTip() EXCLUSIVE_LOCKS_REQUIRED(!cs_peerManager
Definition: processor.cpp:958
RWCollection< VoteMap > voteRecords
Items to run avalanche on.
Definition: processor.h:191
uint32_t minQuorumScore
Quorum management.
Definition: processor.h:245
std::atomic< bool > m_canShareLocalProof
Definition: processor.h:248
std::atomic< int64_t > avaproofsNodeCounter
Definition: processor.h:250
Mutex cs_peerManager
Keep track of the peers and associated infos.
Definition: processor.h:201
double minQuorumConnectedScoreRatio
Definition: processor.h:246
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
const CScript & getPayoutScript() const
Definition: proof.h:166
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:97
const AnyVoteItem & getVoteItem() const
Definition: processor.h:98
unsigned int size() const
Definition: uint256.h:93
256-bit opaque blob.
Definition: uint256.h:129
static const uint256 ZERO
Definition: uint256.h:134
#define INVALID_SOCKET
Definition: compat.h:52
const Config & GetConfig()
Definition: config.cpp:34
std::string FormatScript(const CScript &script)
Definition: core_write.cpp:24
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate.
Definition: cs_main.cpp:7
std::string EncodeSecret(const CKey &key)
Definition: key_io.cpp:102
@ NONE
Definition: logging.h:39
static constexpr Amount PROOF_DUST_THRESHOLD
Minimum amount per utxo.
Definition: proof.h:40
ProofRegistrationResult
Definition: peermanager.h:145
std::map< AnyVoteItem, VoteRecord, VoteMapComparator > VoteMap
Definition: processor.h:172
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:87
std::unique_ptr< Chain > MakeChain(node::NodeContext &node, const CChainParams &params)
Return implementation of Chain interface.
Definition: interfaces.cpp:789
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:779
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_CHECK_EQUAL(v1, v2)
Definition: object.cpp:18
#define BOOST_CHECK(expr)
Definition: object.cpp:17
static CTransactionRef MakeTransactionRef()
Definition: transaction.h:316
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:315
Response response
Definition: processor.cpp:490
static constexpr size_t AVALANCHE_MAX_ELEMENT_POLL
Maximum item that can be polled at once.
Definition: processor.h:52
static constexpr uint32_t AVALANCHE_FINALIZED_ITEMS_FILTER_NUM_ELEMENTS
The size of the finalized items filter.
Definition: processor.h:68
BOOST_AUTO_TEST_CASE_TEMPLATE(voteitemupdate, P, VoteItemProviders)
BOOST_AUTO_TEST_CASE(quorum_diversity)
boost::mpl::list< BlockProvider, ProofProvider, TxProvider > VoteItemProviders
static bool HasAllDesirableServiceFlags(ServiceFlags services)
A shortcut for (services & GetDesirableServiceFlags(services)) == GetDesirableServiceFlags(services),...
Definition: protocol.h:427
@ MSG_TX
Definition: protocol.h:565
@ MSG_AVA_PROOF
Definition: protocol.h:572
@ MSG_BLOCK
Definition: protocol.h:566
ServiceFlags
nServices flags.
Definition: protocol.h:335
@ NODE_NONE
Definition: protocol.h:338
@ NODE_NETWORK
Definition: protocol.h:342
@ NODE_AVALANCHE
Definition: protocol.h:380
uint256 GetRandHash() noexcept
Definition: random.cpp:659
void Shuffle(I first, I last, R &&rng)
More efficient than using std::shuffle on a FastRandomContext.
Definition: random.h:291
reverse_range< T > reverse_iterate(T &x)
@ OP_TRUE
Definition: script.h:57
static uint16_t GetDefaultPort()
Definition: bitcoin.h:18
static RPCHelpMan stop()
Definition: server.cpp:209
BOOST_FIXTURE_TEST_SUITE(stakingrewards_tests, StakingRewardsActivationTestingSetup) BOOST_AUTO_TEST_CASE(isstakingrewardsactivated)
CScript GetScriptForRawPubKey(const CPubKey &pubKey)
Generate a P2PK script for the given pubkey.
Definition: standard.cpp:244
CScript GetScriptForDestination(const CTxDestination &dest)
Generate a Bitcoin scriptPubKey for the given CTxDestination.
Definition: standard.cpp:240
std::string ToString(const T &t)
Locale-independent version of std::to_string.
Definition: string.h:87
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:309
#define LOCK(cs)
Definition: sync.h:306
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
Definition: sync.h:357
bool error(const char *fmt, const Args &...args)
Definition: system.h:45
#define EXCLUSIVE_LOCKS_REQUIRED(...)
Definition: threadsafety.h:56
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:89
@ 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