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