Bitcoin ABC  0.26.3
P2P Digital Currency
peermanager_tests.cpp
Go to the documentation of this file.
1 // Copyright (c) 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 
9 #include <avalanche/statistics.h>
10 #include <avalanche/test/util.h>
11 #include <cashaddrenc.h>
12 #include <config.h>
13 #include <consensus/activation.h>
14 #include <core_io.h>
15 #include <key_io.h>
16 #include <script/standard.h>
17 #include <uint256.h>
18 #include <util/fs_helpers.h>
19 #include <util/time.h>
20 #include <util/translation.h>
21 #include <validation.h>
22 
23 #include <test/util/blockindex.h>
24 #include <test/util/random.h>
25 #include <test/util/setup_common.h>
26 
27 #include <boost/test/unit_test.hpp>
28 
29 #include <limits>
30 #include <optional>
31 #include <unordered_map>
32 
33 using namespace avalanche;
34 
35 namespace avalanche {
36 namespace {
37  struct TestPeerManager {
38  static bool nodeBelongToPeer(const PeerManager &pm, NodeId nodeid,
39  PeerId peerid) {
40  return pm.forNode(nodeid, [&](const Node &node) {
41  return node.peerid == peerid;
42  });
43  }
44 
45  static bool isNodePending(const PeerManager &pm, NodeId nodeid) {
46  auto &pendingNodesView = pm.pendingNodes.get<by_nodeid>();
47  return pendingNodesView.find(nodeid) != pendingNodesView.end();
48  }
49 
50  static PeerId getPeerIdForProofId(PeerManager &pm,
51  const ProofId &proofid) {
52  auto &pview = pm.peers.get<by_proofid>();
53  auto it = pview.find(proofid);
54  return it == pview.end() ? NO_PEER : it->peerid;
55  }
56 
57  static PeerId registerAndGetPeerId(PeerManager &pm,
58  const ProofRef &proof) {
59  pm.registerProof(proof);
60  return getPeerIdForProofId(pm, proof->getId());
61  }
62 
63  static std::vector<uint32_t> getOrderedScores(const PeerManager &pm) {
64  std::vector<uint32_t> scores;
65 
66  auto &peerView = pm.peers.get<by_score>();
67  for (const Peer &peer : peerView) {
68  scores.push_back(peer.getScore());
69  }
70 
71  return scores;
72  }
73 
74  static void cleanupDanglingProofs(
75  PeerManager &pm,
76  std::unordered_set<ProofRef, SaltedProofHasher> &registeredProofs) {
77  pm.cleanupDanglingProofs(registeredProofs);
78  }
79 
80  static void cleanupDanglingProofs(PeerManager &pm) {
81  std::unordered_set<ProofRef, SaltedProofHasher> dummy;
82  pm.cleanupDanglingProofs(dummy);
83  }
84 
85  static std::optional<RemoteProof> getRemoteProof(const PeerManager &pm,
86  const ProofId &proofid,
87  NodeId nodeid) {
88  auto it = pm.remoteProofs.find(boost::make_tuple(proofid, nodeid));
89  if (it == pm.remoteProofs.end()) {
90  return std::nullopt;
91  }
92  return std::make_optional(*it);
93  }
94 
95  static size_t getPeerCount(const PeerManager &pm) {
96  return pm.peers.size();
97  }
98 
99  static std::optional<bool>
100  getRemotePresenceStatus(const PeerManager &pm, const ProofId &proofid) {
101  return pm.getRemotePresenceStatus(proofid);
102  }
103 
104  static void clearPeers(PeerManager &pm) {
105  std::vector<PeerId> peerIds;
106  for (auto &peer : pm.peers) {
107  peerIds.push_back(peer.peerid);
108  }
109  for (const PeerId &peerid : peerIds) {
110  pm.removePeer(peerid);
111  }
112  BOOST_CHECK_EQUAL(pm.peers.size(), 0);
113  }
114 
115  static void setLocalProof(PeerManager &pm, const ProofRef &proof) {
116  pm.localProof = proof;
117  }
118 
119  static bool isFlaky(const PeerManager &pm, const ProofId &proofid) {
120  return pm.isFlaky(proofid);
121  }
122  };
123 
124  static void addCoin(Chainstate &chainstate, const COutPoint &outpoint,
125  const CKey &key,
126  const Amount amount = PROOF_DUST_THRESHOLD,
127  uint32_t height = 100, bool is_coinbase = false) {
129 
130  LOCK(cs_main);
131  CCoinsViewCache &coins = chainstate.CoinsTip();
132  coins.AddCoin(outpoint,
133  Coin(CTxOut(amount, script), height, is_coinbase), false);
134  }
135 
136  static COutPoint createUtxo(Chainstate &chainstate, const CKey &key,
137  const Amount amount = PROOF_DUST_THRESHOLD,
138  uint32_t height = 100,
139  bool is_coinbase = false) {
140  COutPoint outpoint(TxId(GetRandHash()), 0);
141  addCoin(chainstate, outpoint, key, amount, height, is_coinbase);
142  return outpoint;
143  }
144 
145  static ProofRef
146  buildProof(const CKey &key,
147  const std::vector<std::tuple<COutPoint, Amount>> &outpoints,
148  const CKey &master = CKey::MakeCompressedKey(),
149  int64_t sequence = 1, uint32_t height = 100,
150  bool is_coinbase = false, int64_t expirationTime = 0,
151  const CScript &payoutScript = UNSPENDABLE_ECREG_PAYOUT_SCRIPT) {
152  ProofBuilder pb(sequence, expirationTime, master, payoutScript);
153  for (const auto &[outpoint, amount] : outpoints) {
154  BOOST_CHECK(pb.addUTXO(outpoint, amount, height, is_coinbase, key));
155  }
156  return pb.build();
157  }
158 
159  template <typename... Args>
160  static ProofRef
161  buildProofWithOutpoints(const CKey &key,
162  const std::vector<COutPoint> &outpoints,
163  Amount amount, Args &&...args) {
164  std::vector<std::tuple<COutPoint, Amount>> outpointsWithAmount;
165  std::transform(
166  outpoints.begin(), outpoints.end(),
167  std::back_inserter(outpointsWithAmount),
168  [amount](const auto &o) { return std::make_tuple(o, amount); });
169  return buildProof(key, outpointsWithAmount,
170  std::forward<Args>(args)...);
171  }
172 
173  static ProofRef
174  buildProofWithSequence(const CKey &key,
175  const std::vector<COutPoint> &outpoints,
176  int64_t sequence) {
177  return buildProofWithOutpoints(key, outpoints, PROOF_DUST_THRESHOLD,
178  key, sequence);
179  }
180 } // namespace
181 } // namespace avalanche
182 
183 namespace {
184 struct PeerManagerFixture : public TestChain100Setup {
185  PeerManagerFixture() {
186  gArgs.ForceSetArg("-avaproofstakeutxoconfirmations", "1");
187  }
188  ~PeerManagerFixture() {
189  gArgs.ClearForcedArg("-avaproofstakeutxoconfirmations");
190  }
191 };
192 } // namespace
193 
194 namespace {
195 struct NoCoolDownFixture : public PeerManagerFixture {
196  NoCoolDownFixture() {
197  gArgs.ForceSetArg("-avalancheconflictingproofcooldown", "0");
198  }
199  ~NoCoolDownFixture() {
200  gArgs.ClearForcedArg("-avalancheconflictingproofcooldown");
201  }
202 };
203 } // namespace
204 
205 BOOST_FIXTURE_TEST_SUITE(peermanager_tests, PeerManagerFixture)
206 
207 BOOST_AUTO_TEST_CASE(select_peer_linear) {
208  // No peers.
211 
212  // One peer
213  const std::vector<Slot> oneslot = {{100, 100, 23}};
214 
215  // Undershoot
216  BOOST_CHECK_EQUAL(selectPeerImpl(oneslot, 0, 300), NO_PEER);
217  BOOST_CHECK_EQUAL(selectPeerImpl(oneslot, 42, 300), NO_PEER);
218  BOOST_CHECK_EQUAL(selectPeerImpl(oneslot, 99, 300), NO_PEER);
219 
220  // Nailed it
221  BOOST_CHECK_EQUAL(selectPeerImpl(oneslot, 100, 300), 23);
222  BOOST_CHECK_EQUAL(selectPeerImpl(oneslot, 142, 300), 23);
223  BOOST_CHECK_EQUAL(selectPeerImpl(oneslot, 199, 300), 23);
224 
225  // Overshoot
226  BOOST_CHECK_EQUAL(selectPeerImpl(oneslot, 200, 300), NO_PEER);
227  BOOST_CHECK_EQUAL(selectPeerImpl(oneslot, 242, 300), NO_PEER);
228  BOOST_CHECK_EQUAL(selectPeerImpl(oneslot, 299, 300), NO_PEER);
229 
230  // Two peers
231  const std::vector<Slot> twoslots = {{100, 100, 69}, {300, 100, 42}};
232 
233  // Undershoot
234  BOOST_CHECK_EQUAL(selectPeerImpl(twoslots, 0, 500), NO_PEER);
235  BOOST_CHECK_EQUAL(selectPeerImpl(twoslots, 42, 500), NO_PEER);
236  BOOST_CHECK_EQUAL(selectPeerImpl(twoslots, 99, 500), NO_PEER);
237 
238  // First entry
239  BOOST_CHECK_EQUAL(selectPeerImpl(twoslots, 100, 500), 69);
240  BOOST_CHECK_EQUAL(selectPeerImpl(twoslots, 142, 500), 69);
241  BOOST_CHECK_EQUAL(selectPeerImpl(twoslots, 199, 500), 69);
242 
243  // In between
244  BOOST_CHECK_EQUAL(selectPeerImpl(twoslots, 200, 500), NO_PEER);
245  BOOST_CHECK_EQUAL(selectPeerImpl(twoslots, 242, 500), NO_PEER);
246  BOOST_CHECK_EQUAL(selectPeerImpl(twoslots, 299, 500), NO_PEER);
247 
248  // Second entry
249  BOOST_CHECK_EQUAL(selectPeerImpl(twoslots, 300, 500), 42);
250  BOOST_CHECK_EQUAL(selectPeerImpl(twoslots, 342, 500), 42);
251  BOOST_CHECK_EQUAL(selectPeerImpl(twoslots, 399, 500), 42);
252 
253  // Overshoot
254  BOOST_CHECK_EQUAL(selectPeerImpl(twoslots, 400, 500), NO_PEER);
255  BOOST_CHECK_EQUAL(selectPeerImpl(twoslots, 442, 500), NO_PEER);
256  BOOST_CHECK_EQUAL(selectPeerImpl(twoslots, 499, 500), NO_PEER);
257 }
258 
259 BOOST_AUTO_TEST_CASE(select_peer_dichotomic) {
260  std::vector<Slot> slots;
261 
262  // 100 peers of size 1 with 1 empty element apart.
263  uint64_t max = 1;
264  for (int i = 0; i < 100; i++) {
265  slots.emplace_back(max, 1, i);
266  max += 2;
267  }
268 
269  BOOST_CHECK_EQUAL(selectPeerImpl(slots, 4, max), NO_PEER);
270 
271  // Check that we get what we expect.
272  for (int i = 0; i < 100; i++) {
273  BOOST_CHECK_EQUAL(selectPeerImpl(slots, 2 * i, max), NO_PEER);
274  BOOST_CHECK_EQUAL(selectPeerImpl(slots, 2 * i + 1, max), i);
275  }
276 
277  BOOST_CHECK_EQUAL(selectPeerImpl(slots, max, max), NO_PEER);
278 
279  // Update the slots to be heavily skewed toward the last element.
280  slots[99] = slots[99].withScore(101);
281  max = slots[99].getStop();
282  BOOST_CHECK_EQUAL(max, 300);
283 
284  for (int i = 0; i < 100; i++) {
285  BOOST_CHECK_EQUAL(selectPeerImpl(slots, 2 * i, max), NO_PEER);
286  BOOST_CHECK_EQUAL(selectPeerImpl(slots, 2 * i + 1, max), i);
287  }
288 
289  BOOST_CHECK_EQUAL(selectPeerImpl(slots, 200, max), 99);
290  BOOST_CHECK_EQUAL(selectPeerImpl(slots, 256, max), 99);
291  BOOST_CHECK_EQUAL(selectPeerImpl(slots, 299, max), 99);
292  BOOST_CHECK_EQUAL(selectPeerImpl(slots, 300, max), NO_PEER);
293 
294  // Update the slots to be heavily skewed toward the first element.
295  for (int i = 0; i < 100; i++) {
296  slots[i] = slots[i].withStart(slots[i].getStart() + 100);
297  }
298 
299  slots[0] = Slot(1, slots[0].getStop() - 1, slots[0].getPeerId());
300  slots[99] = slots[99].withScore(1);
301  max = slots[99].getStop();
302  BOOST_CHECK_EQUAL(max, 300);
303 
304  BOOST_CHECK_EQUAL(selectPeerImpl(slots, 0, max), NO_PEER);
305  BOOST_CHECK_EQUAL(selectPeerImpl(slots, 1, max), 0);
306  BOOST_CHECK_EQUAL(selectPeerImpl(slots, 42, max), 0);
307 
308  for (int i = 0; i < 100; i++) {
309  BOOST_CHECK_EQUAL(selectPeerImpl(slots, 100 + 2 * i + 1, max), i);
310  BOOST_CHECK_EQUAL(selectPeerImpl(slots, 100 + 2 * i + 2, max), NO_PEER);
311  }
312 }
313 
314 BOOST_AUTO_TEST_CASE(select_peer_random) {
315  for (int c = 0; c < 1000; c++) {
316  size_t size = InsecureRandBits(10) + 1;
317  std::vector<Slot> slots;
318  slots.reserve(size);
319 
320  uint64_t max = InsecureRandBits(3);
321  auto next = [&]() {
322  uint64_t r = max;
323  max += InsecureRandBits(3);
324  return r;
325  };
326 
327  for (size_t i = 0; i < size; i++) {
328  const uint64_t start = next();
329  const uint32_t score = InsecureRandBits(3);
330  max += score;
331  slots.emplace_back(start, score, i);
332  }
333 
334  for (int k = 0; k < 100; k++) {
335  uint64_t s = max > 0 ? InsecureRandRange(max) : 0;
336  auto i = selectPeerImpl(slots, s, max);
337  // /!\ Because of the way we construct the vector, the peer id is
338  // always the index. This might not be the case in practice.
339  BOOST_CHECK(i == NO_PEER || slots[i].contains(s));
340  }
341  }
342 }
343 
344 static void addNodeWithScore(Chainstate &active_chainstate,
346  uint32_t score) {
347  auto proof = buildRandomProof(active_chainstate, score);
348  BOOST_CHECK(pm.registerProof(proof));
349  BOOST_CHECK(pm.addNode(node, proof->getId()));
350 };
351 
352 BOOST_AUTO_TEST_CASE(peer_probabilities) {
353  ChainstateManager &chainman = *Assert(m_node.chainman);
354  // No peers.
357 
358  const NodeId node0 = 42, node1 = 69, node2 = 37;
359 
360  Chainstate &active_chainstate = chainman.ActiveChainstate();
361  // One peer, we always return it.
362  addNodeWithScore(active_chainstate, pm, node0, MIN_VALID_PROOF_SCORE);
363  BOOST_CHECK_EQUAL(pm.selectNode(), node0);
364 
365  // Two peers, verify ratio.
366  addNodeWithScore(active_chainstate, pm, node1, 2 * MIN_VALID_PROOF_SCORE);
367 
368  std::unordered_map<PeerId, int> results = {};
369  for (int i = 0; i < 10000; i++) {
370  size_t n = pm.selectNode();
371  BOOST_CHECK(n == node0 || n == node1);
372  results[n]++;
373  }
374 
375  BOOST_CHECK(abs(2 * results[0] - results[1]) < 500);
376 
377  // Three peers, verify ratio.
378  addNodeWithScore(active_chainstate, pm, node2, MIN_VALID_PROOF_SCORE);
379 
380  results.clear();
381  for (int i = 0; i < 10000; i++) {
382  size_t n = pm.selectNode();
383  BOOST_CHECK(n == node0 || n == node1 || n == node2);
384  results[n]++;
385  }
386 
387  BOOST_CHECK(abs(results[0] - results[1] + results[2]) < 500);
388 }
389 
390 BOOST_AUTO_TEST_CASE(remove_peer) {
391  ChainstateManager &chainman = *Assert(m_node.chainman);
392  // No peers.
395 
396  Chainstate &active_chainstate = chainman.ActiveChainstate();
397  // Add 4 peers.
398  std::array<PeerId, 8> peerids;
399  for (int i = 0; i < 4; i++) {
400  auto p = buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE);
401  peerids[i] = TestPeerManager::registerAndGetPeerId(pm, p);
402  BOOST_CHECK(pm.addNode(InsecureRand32(), p->getId()));
403  }
404 
405  BOOST_CHECK_EQUAL(pm.getSlotCount(), 40000);
407 
408  for (int i = 0; i < 100; i++) {
409  PeerId p = pm.selectPeer();
410  BOOST_CHECK(p == peerids[0] || p == peerids[1] || p == peerids[2] ||
411  p == peerids[3]);
412  }
413 
414  // Remove one peer, it nevers show up now.
415  BOOST_CHECK(pm.removePeer(peerids[2]));
416  BOOST_CHECK_EQUAL(pm.getSlotCount(), 40000);
417  BOOST_CHECK_EQUAL(pm.getFragmentation(), 10000);
418 
419  // Make sure we compact to never get NO_PEER.
420  BOOST_CHECK_EQUAL(pm.compact(), 10000);
421  BOOST_CHECK(pm.verify());
422  BOOST_CHECK_EQUAL(pm.getSlotCount(), 30000);
424 
425  for (int i = 0; i < 100; i++) {
426  PeerId p = pm.selectPeer();
427  BOOST_CHECK(p == peerids[0] || p == peerids[1] || p == peerids[3]);
428  }
429 
430  // Add 4 more peers.
431  for (int i = 0; i < 4; i++) {
432  auto p = buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE);
433  peerids[i + 4] = TestPeerManager::registerAndGetPeerId(pm, p);
434  BOOST_CHECK(pm.addNode(InsecureRand32(), p->getId()));
435  }
436 
437  BOOST_CHECK_EQUAL(pm.getSlotCount(), 70000);
439 
440  BOOST_CHECK(pm.removePeer(peerids[0]));
441  BOOST_CHECK_EQUAL(pm.getSlotCount(), 70000);
442  BOOST_CHECK_EQUAL(pm.getFragmentation(), 10000);
443 
444  // Removing the last entry do not increase fragmentation.
445  BOOST_CHECK(pm.removePeer(peerids[7]));
446  BOOST_CHECK_EQUAL(pm.getSlotCount(), 60000);
447  BOOST_CHECK_EQUAL(pm.getFragmentation(), 10000);
448 
449  // Make sure we compact to never get NO_PEER.
450  BOOST_CHECK_EQUAL(pm.compact(), 10000);
451  BOOST_CHECK(pm.verify());
452  BOOST_CHECK_EQUAL(pm.getSlotCount(), 50000);
454 
455  for (int i = 0; i < 100; i++) {
456  PeerId p = pm.selectPeer();
457  BOOST_CHECK(p == peerids[1] || p == peerids[3] || p == peerids[4] ||
458  p == peerids[5] || p == peerids[6]);
459  }
460 
461  // Removing non existent peers fails.
462  BOOST_CHECK(!pm.removePeer(peerids[0]));
463  BOOST_CHECK(!pm.removePeer(peerids[2]));
464  BOOST_CHECK(!pm.removePeer(peerids[7]));
466 }
467 
468 BOOST_AUTO_TEST_CASE(compact_slots) {
469  ChainstateManager &chainman = *Assert(m_node.chainman);
471 
472  // Add 4 peers.
473  std::array<PeerId, 4> peerids;
474  for (int i = 0; i < 4; i++) {
475  auto p = buildRandomProof(chainman.ActiveChainstate(),
477  peerids[i] = TestPeerManager::registerAndGetPeerId(pm, p);
478  BOOST_CHECK(pm.addNode(InsecureRand32(), p->getId()));
479  }
480 
481  // Remove all peers.
482  for (auto p : peerids) {
483  pm.removePeer(p);
484  }
485 
486  BOOST_CHECK_EQUAL(pm.getSlotCount(), 30000);
487  BOOST_CHECK_EQUAL(pm.getFragmentation(), 30000);
488 
489  for (int i = 0; i < 100; i++) {
491  }
492 
493  BOOST_CHECK_EQUAL(pm.compact(), 30000);
494  BOOST_CHECK(pm.verify());
497 }
498 
500  ChainstateManager &chainman = *Assert(m_node.chainman);
502 
503  Chainstate &active_chainstate = chainman.ActiveChainstate();
504 
505  // Create one peer.
506  auto proof =
507  buildRandomProof(active_chainstate, 10000000 * MIN_VALID_PROOF_SCORE);
508  BOOST_CHECK(pm.registerProof(proof));
510 
511  // Add 4 nodes.
512  const ProofId &proofid = proof->getId();
513  for (int i = 0; i < 4; i++) {
514  BOOST_CHECK(pm.addNode(i, proofid));
515  }
516 
517  for (int i = 0; i < 100; i++) {
518  NodeId n = pm.selectNode();
519  BOOST_CHECK(n >= 0 && n < 4);
520  BOOST_CHECK(pm.updateNextRequestTime(n, Now<SteadyMilliseconds>()));
521  }
522 
523  // Remove a node, check that it doesn't show up.
524  BOOST_CHECK(pm.removeNode(2));
525 
526  for (int i = 0; i < 100; i++) {
527  NodeId n = pm.selectNode();
528  BOOST_CHECK(n == 0 || n == 1 || n == 3);
529  BOOST_CHECK(pm.updateNextRequestTime(n, Now<SteadyMilliseconds>()));
530  }
531 
532  // Push a node's timeout in the future, so that it doesn't show up.
533  BOOST_CHECK(pm.updateNextRequestTime(1, Now<SteadyMilliseconds>() +
534  std::chrono::hours(24)));
535 
536  for (int i = 0; i < 100; i++) {
537  NodeId n = pm.selectNode();
538  BOOST_CHECK(n == 0 || n == 3);
539  BOOST_CHECK(pm.updateNextRequestTime(n, Now<SteadyMilliseconds>()));
540  }
541 
542  // Move a node from a peer to another. This peer has a very low score such
543  // as chances of being picked are 1 in 10 million.
544  addNodeWithScore(active_chainstate, pm, 3, MIN_VALID_PROOF_SCORE);
545 
546  int node3selected = 0;
547  for (int i = 0; i < 100; i++) {
548  NodeId n = pm.selectNode();
549  if (n == 3) {
550  // Selecting this node should be exceedingly unlikely.
551  BOOST_CHECK(node3selected++ < 1);
552  } else {
553  BOOST_CHECK_EQUAL(n, 0);
554  }
555  BOOST_CHECK(pm.updateNextRequestTime(n, Now<SteadyMilliseconds>()));
556  }
557 }
558 
559 BOOST_AUTO_TEST_CASE(node_binding) {
560  ChainstateManager &chainman = *Assert(m_node.chainman);
562 
563  Chainstate &active_chainstate = chainman.ActiveChainstate();
564 
565  auto proof = buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE);
566  const ProofId &proofid = proof->getId();
567 
570 
571  // Add a bunch of nodes with no associated peer
572  for (int i = 0; i < 10; i++) {
573  BOOST_CHECK(!pm.addNode(i, proofid));
574  BOOST_CHECK(TestPeerManager::isNodePending(pm, i));
577  }
578 
579  // Now create the peer and check all the nodes are bound
580  const PeerId peerid = TestPeerManager::registerAndGetPeerId(pm, proof);
581  BOOST_CHECK_NE(peerid, NO_PEER);
582  for (int i = 0; i < 10; i++) {
583  BOOST_CHECK(!TestPeerManager::isNodePending(pm, i));
584  BOOST_CHECK(TestPeerManager::nodeBelongToPeer(pm, i, peerid));
587  }
588  BOOST_CHECK(pm.verify());
589 
590  // Disconnect some nodes
591  for (int i = 0; i < 5; i++) {
592  BOOST_CHECK(pm.removeNode(i));
593  BOOST_CHECK(!TestPeerManager::isNodePending(pm, i));
594  BOOST_CHECK(!TestPeerManager::nodeBelongToPeer(pm, i, peerid));
595  BOOST_CHECK_EQUAL(pm.getNodeCount(), 10 - i - 1);
597  }
598 
599  // Add nodes when the peer already exists
600  for (int i = 0; i < 5; i++) {
601  BOOST_CHECK(pm.addNode(i, proofid));
602  BOOST_CHECK(!TestPeerManager::isNodePending(pm, i));
603  BOOST_CHECK(TestPeerManager::nodeBelongToPeer(pm, i, peerid));
604  BOOST_CHECK_EQUAL(pm.getNodeCount(), 5 + i + 1);
606  }
607 
608  auto alt_proof = buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE);
609  const ProofId &alt_proofid = alt_proof->getId();
610 
611  // Update some nodes from a known proof to an unknown proof
612  for (int i = 0; i < 5; i++) {
613  BOOST_CHECK(!pm.addNode(i, alt_proofid));
614  BOOST_CHECK(TestPeerManager::isNodePending(pm, i));
615  BOOST_CHECK(!TestPeerManager::nodeBelongToPeer(pm, i, peerid));
616  BOOST_CHECK_EQUAL(pm.getNodeCount(), 10 - i - 1);
618  }
619 
620  auto alt2_proof =
621  buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE);
622  const ProofId &alt2_proofid = alt2_proof->getId();
623 
624  // Update some nodes from an unknown proof to another unknown proof
625  for (int i = 0; i < 5; i++) {
626  BOOST_CHECK(!pm.addNode(i, alt2_proofid));
627  BOOST_CHECK(TestPeerManager::isNodePending(pm, i));
630  }
631 
632  // Update some nodes from an unknown proof to a known proof
633  for (int i = 0; i < 5; i++) {
634  BOOST_CHECK(pm.addNode(i, proofid));
635  BOOST_CHECK(!TestPeerManager::isNodePending(pm, i));
636  BOOST_CHECK(TestPeerManager::nodeBelongToPeer(pm, i, peerid));
637  BOOST_CHECK_EQUAL(pm.getNodeCount(), 5 + i + 1);
638  BOOST_CHECK_EQUAL(pm.getPendingNodeCount(), 5 - i - 1);
639  }
640 
641  // Remove the peer, the nodes should be pending again
642  BOOST_CHECK(pm.removePeer(peerid));
643  BOOST_CHECK(!pm.exists(proof->getId()));
644  for (int i = 0; i < 10; i++) {
645  BOOST_CHECK(TestPeerManager::isNodePending(pm, i));
646  BOOST_CHECK(!TestPeerManager::nodeBelongToPeer(pm, i, peerid));
649  }
650  BOOST_CHECK(pm.verify());
651 
652  // Remove the remaining pending nodes, check the count drops accordingly
653  for (int i = 0; i < 10; i++) {
654  BOOST_CHECK(pm.removeNode(i));
655  BOOST_CHECK(!TestPeerManager::isNodePending(pm, i));
656  BOOST_CHECK(!TestPeerManager::nodeBelongToPeer(pm, i, peerid));
658  BOOST_CHECK_EQUAL(pm.getPendingNodeCount(), 10 - i - 1);
659  }
660 }
661 
662 BOOST_AUTO_TEST_CASE(node_binding_reorg) {
663  gArgs.ForceSetArg("-avaproofstakeutxoconfirmations", "2");
664  ChainstateManager &chainman = *Assert(m_node.chainman);
665 
667 
668  auto proof = buildRandomProof(chainman.ActiveChainstate(),
670  const ProofId &proofid = proof->getId();
671 
672  PeerId peerid = TestPeerManager::registerAndGetPeerId(pm, proof);
673  BOOST_CHECK_NE(peerid, NO_PEER);
674  BOOST_CHECK(pm.verify());
675 
676  // Add nodes to our peer
677  for (int i = 0; i < 10; i++) {
678  BOOST_CHECK(pm.addNode(i, proofid));
679  BOOST_CHECK(!TestPeerManager::isNodePending(pm, i));
680  BOOST_CHECK(TestPeerManager::nodeBelongToPeer(pm, i, peerid));
681  }
682 
683  // Make the proof immature by reorging to a shorter chain
684  {
685  BlockValidationState state;
686  chainman.ActiveChainstate().InvalidateBlock(
687  state, WITH_LOCK(chainman.GetMutex(), return chainman.ActiveTip()));
689  WITH_LOCK(chainman.GetMutex(), return chainman.ActiveHeight()), 99);
690  }
691 
692  pm.updatedBlockTip();
693  BOOST_CHECK(pm.isImmature(proofid));
694  BOOST_CHECK(!pm.isBoundToPeer(proofid));
695  for (int i = 0; i < 10; i++) {
696  BOOST_CHECK(TestPeerManager::isNodePending(pm, i));
697  BOOST_CHECK(!TestPeerManager::nodeBelongToPeer(pm, i, peerid));
698  }
699  BOOST_CHECK(pm.verify());
700 
701  // Make the proof great again
702  {
703  // Advance the clock so the newly mined block won't collide with the
704  // other deterministically-generated blocks
705  SetMockTime(GetTime() + 20);
706  mineBlocks(1);
707  BlockValidationState state;
708  BOOST_CHECK(chainman.ActiveChainstate().ActivateBestChain(state));
709  LOCK(chainman.GetMutex());
710  BOOST_CHECK_EQUAL(chainman.ActiveHeight(), 100);
711  }
712 
713  pm.updatedBlockTip();
714  BOOST_CHECK(!pm.isImmature(proofid));
715  BOOST_CHECK(pm.isBoundToPeer(proofid));
716  // The peerid has certainly been updated
717  peerid = TestPeerManager::registerAndGetPeerId(pm, proof);
718  BOOST_CHECK_NE(peerid, NO_PEER);
719  for (int i = 0; i < 10; i++) {
720  BOOST_CHECK(!TestPeerManager::isNodePending(pm, i));
721  BOOST_CHECK(TestPeerManager::nodeBelongToPeer(pm, i, peerid));
722  }
723  BOOST_CHECK(pm.verify());
724 }
725 
726 BOOST_AUTO_TEST_CASE(proof_conflict) {
727  auto key = CKey::MakeCompressedKey();
728 
729  TxId txid1(GetRandHash());
730  TxId txid2(GetRandHash());
731  BOOST_CHECK(txid1 != txid2);
732 
733  const Amount v = PROOF_DUST_THRESHOLD;
734  const int height = 100;
735 
736  ChainstateManager &chainman = *Assert(m_node.chainman);
737  for (uint32_t i = 0; i < 10; i++) {
738  addCoin(chainman.ActiveChainstate(), {txid1, i}, key);
739  addCoin(chainman.ActiveChainstate(), {txid2, i}, key);
740  }
741 
743  CKey masterKey = CKey::MakeCompressedKey();
744  const auto getPeerId = [&](const std::vector<COutPoint> &outpoints) {
745  return TestPeerManager::registerAndGetPeerId(
746  pm, buildProofWithOutpoints(key, outpoints, v, masterKey, 0, height,
747  false, 0));
748  };
749 
750  // Add one peer.
751  const PeerId peer1 = getPeerId({COutPoint(txid1, 0)});
752  BOOST_CHECK(peer1 != NO_PEER);
753 
754  // Same proof, same peer.
755  BOOST_CHECK_EQUAL(getPeerId({COutPoint(txid1, 0)}), peer1);
756 
757  // Different txid, different proof.
758  const PeerId peer2 = getPeerId({COutPoint(txid2, 0)});
759  BOOST_CHECK(peer2 != NO_PEER && peer2 != peer1);
760 
761  // Different index, different proof.
762  const PeerId peer3 = getPeerId({COutPoint(txid1, 1)});
763  BOOST_CHECK(peer3 != NO_PEER && peer3 != peer1);
764 
765  // Empty proof, no peer.
766  BOOST_CHECK_EQUAL(getPeerId({}), NO_PEER);
767 
768  // Multiple inputs.
769  const PeerId peer4 = getPeerId({COutPoint(txid1, 2), COutPoint(txid2, 2)});
770  BOOST_CHECK(peer4 != NO_PEER && peer4 != peer1);
771 
772  // Duplicated input.
773  {
776  COutPoint o(txid1, 3);
777  BOOST_CHECK(pb.addUTXO(o, v, height, false, key));
778  BOOST_CHECK(
780  }
781 
782  // Multiple inputs, collision on first input.
783  BOOST_CHECK_EQUAL(getPeerId({COutPoint(txid1, 0), COutPoint(txid2, 4)}),
784  NO_PEER);
785 
786  // Mutliple inputs, collision on second input.
787  BOOST_CHECK_EQUAL(getPeerId({COutPoint(txid1, 4), COutPoint(txid2, 0)}),
788  NO_PEER);
789 
790  // Mutliple inputs, collision on both inputs.
791  BOOST_CHECK_EQUAL(getPeerId({COutPoint(txid1, 0), COutPoint(txid2, 2)}),
792  NO_PEER);
793 }
794 
795 BOOST_AUTO_TEST_CASE(immature_proofs) {
796  ChainstateManager &chainman = *Assert(m_node.chainman);
797  gArgs.ForceSetArg("-avaproofstakeutxoconfirmations", "2");
799 
800  auto key = CKey::MakeCompressedKey();
801  int immatureHeight = 100;
802 
803  auto registerImmature = [&](const ProofRef &proof) {
805  BOOST_CHECK(!pm.registerProof(proof, state));
807  };
808 
809  auto checkImmature = [&](const ProofRef &proof, bool expectedImmature) {
810  const ProofId &proofid = proof->getId();
811  BOOST_CHECK(pm.exists(proofid));
812 
813  BOOST_CHECK_EQUAL(pm.isImmature(proofid), expectedImmature);
814  BOOST_CHECK_EQUAL(pm.isBoundToPeer(proofid), !expectedImmature);
815 
816  bool ret = false;
817  pm.forEachPeer([&](const Peer &peer) {
818  if (proof->getId() == peer.proof->getId()) {
819  ret = true;
820  }
821  });
822  BOOST_CHECK_EQUAL(ret, !expectedImmature);
823  };
824 
825  // Track immature proofs so we can test them later
826  std::vector<ProofRef> immatureProofs;
827 
828  // Fill up the immature pool to test the size limit
829  for (int64_t i = 1; i <= AVALANCHE_MAX_IMMATURE_PROOFS; i++) {
830  COutPoint outpoint = COutPoint(TxId(GetRandHash()), 0);
831  auto proof = buildProofWithOutpoints(
832  key, {outpoint}, i * PROOF_DUST_THRESHOLD, key, 0, immatureHeight);
833  addCoin(chainman.ActiveChainstate(), outpoint, key,
834  i * PROOF_DUST_THRESHOLD, immatureHeight);
835  registerImmature(proof);
836  checkImmature(proof, true);
837  immatureProofs.push_back(proof);
838  }
839 
840  // More immature proofs evict lower scoring proofs
841  for (auto i = 0; i < 100; i++) {
842  COutPoint outpoint = COutPoint(TxId(GetRandHash()), 0);
843  auto proof =
844  buildProofWithOutpoints(key, {outpoint}, 200 * PROOF_DUST_THRESHOLD,
845  key, 0, immatureHeight);
846  addCoin(chainman.ActiveChainstate(), outpoint, key,
847  200 * PROOF_DUST_THRESHOLD, immatureHeight);
848  registerImmature(proof);
849  checkImmature(proof, true);
850  immatureProofs.push_back(proof);
851  BOOST_CHECK(!pm.exists(immatureProofs.front()->getId()));
852  immatureProofs.erase(immatureProofs.begin());
853  }
854 
855  // Replacement when the pool is full still works
856  {
857  const COutPoint &outpoint =
858  immatureProofs.front()->getStakes()[0].getStake().getUTXO();
859  auto proof =
860  buildProofWithOutpoints(key, {outpoint}, 101 * PROOF_DUST_THRESHOLD,
861  key, 1, immatureHeight);
862  registerImmature(proof);
863  checkImmature(proof, true);
864  immatureProofs.push_back(proof);
865  BOOST_CHECK(!pm.exists(immatureProofs.front()->getId()));
866  immatureProofs.erase(immatureProofs.begin());
867  }
868 
869  // Mine a block to increase the chain height, turning all immature proofs to
870  // mature
871  mineBlocks(1);
872  pm.updatedBlockTip();
873  for (const auto &proof : immatureProofs) {
874  checkImmature(proof, false);
875  }
876 }
877 
878 BOOST_AUTO_TEST_CASE(dangling_node) {
879  ChainstateManager &chainman = *Assert(m_node.chainman);
881 
882  Chainstate &active_chainstate = chainman.ActiveChainstate();
883 
884  auto proof = buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE);
885  PeerId peerid = TestPeerManager::registerAndGetPeerId(pm, proof);
886  BOOST_CHECK_NE(peerid, NO_PEER);
887 
888  const SteadyMilliseconds theFuture(Now<SteadyMilliseconds>() +
889  std::chrono::hours(24));
890 
891  // Add nodes to this peer and update their request time far in the future
892  for (int i = 0; i < 10; i++) {
893  BOOST_CHECK(pm.addNode(i, proof->getId()));
894  BOOST_CHECK(pm.updateNextRequestTime(i, theFuture));
895  }
896 
897  // Remove the peer
898  BOOST_CHECK(pm.removePeer(peerid));
899 
900  // Check the nodes are still there
901  for (int i = 0; i < 10; i++) {
902  BOOST_CHECK(pm.forNode(i, [](const Node &n) { return true; }));
903  }
904 
905  // Build a new one
906  proof = buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE);
907  peerid = TestPeerManager::registerAndGetPeerId(pm, proof);
908  BOOST_CHECK_NE(peerid, NO_PEER);
909 
910  // Update the nodes with the new proof
911  for (int i = 0; i < 10; i++) {
912  BOOST_CHECK(pm.addNode(i, proof->getId()));
913  BOOST_CHECK(pm.forNode(
914  i, [&](const Node &n) { return n.nextRequestTime == theFuture; }));
915  }
916 
917  // Remove the peer
918  BOOST_CHECK(pm.removePeer(peerid));
919 
920  // Disconnect the nodes
921  for (int i = 0; i < 10; i++) {
922  BOOST_CHECK(pm.removeNode(i));
923  }
924 }
925 
926 BOOST_AUTO_TEST_CASE(proof_accessors) {
927  ChainstateManager &chainman = *Assert(m_node.chainman);
929 
930  constexpr int numProofs = 10;
931 
932  std::vector<ProofRef> proofs;
933  proofs.reserve(numProofs);
934  for (int i = 0; i < numProofs; i++) {
935  proofs.push_back(buildRandomProof(chainman.ActiveChainstate(),
937  }
938 
939  for (int i = 0; i < numProofs; i++) {
940  BOOST_CHECK(pm.registerProof(proofs[i]));
941 
942  {
944  // Fail to add an existing proof
945  BOOST_CHECK(!pm.registerProof(proofs[i], state));
946  BOOST_CHECK(state.GetResult() ==
948  }
949 
950  for (int added = 0; added <= i; added++) {
951  auto proof = pm.getProof(proofs[added]->getId());
952  BOOST_CHECK(proof != nullptr);
953 
954  const ProofId &proofid = proof->getId();
955  BOOST_CHECK_EQUAL(proofid, proofs[added]->getId());
956  }
957  }
958 
959  // No stake, copied from proof_tests.cpp
960  const std::string badProofHex(
961  "96527eae083f1f24625f049d9e54bb9a21023beefdde700a6bc02036335b4df141c8b"
962  "c67bb05a971f5ac2745fd683797dde3002321023beefdde700a6bc02036335b4df141"
963  "c8bc67bb05a971f5ac2745fd683797dde3ac135da984db510334abe41134e3d4ef09a"
964  "d006b1152be8bc413182bf6f947eac1f8580fe265a382195aa2d73935cabf86d90a8f"
965  "666d0a62385ae24732eca51575");
967  auto badProof = RCUPtr<Proof>::make();
968  BOOST_CHECK(Proof::FromHex(*badProof, badProofHex, error));
969 
971  BOOST_CHECK(!pm.registerProof(badProof, state));
973 }
974 
975 BOOST_FIXTURE_TEST_CASE(conflicting_proof_rescan, NoCoolDownFixture) {
976  ChainstateManager &chainman = *Assert(m_node.chainman);
978 
979  const CKey key = CKey::MakeCompressedKey();
980 
981  Chainstate &active_chainstate = chainman.ActiveChainstate();
982 
983  const COutPoint conflictingOutpoint = createUtxo(active_chainstate, key);
984  const COutPoint outpointToSend = createUtxo(active_chainstate, key);
985 
986  ProofRef proofToInvalidate =
987  buildProofWithSequence(key, {conflictingOutpoint, outpointToSend}, 20);
988  BOOST_CHECK(pm.registerProof(proofToInvalidate));
989 
990  ProofRef conflictingProof =
991  buildProofWithSequence(key, {conflictingOutpoint}, 10);
993  BOOST_CHECK(!pm.registerProof(conflictingProof, state));
995  BOOST_CHECK(pm.isInConflictingPool(conflictingProof->getId()));
996 
997  {
998  LOCK(cs_main);
999  CCoinsViewCache &coins = active_chainstate.CoinsTip();
1000  // Make proofToInvalidate invalid
1001  coins.SpendCoin(outpointToSend);
1002  }
1003 
1004  pm.updatedBlockTip();
1005 
1006  BOOST_CHECK(!pm.exists(proofToInvalidate->getId()));
1007 
1008  BOOST_CHECK(!pm.isInConflictingPool(conflictingProof->getId()));
1009  BOOST_CHECK(pm.isBoundToPeer(conflictingProof->getId()));
1010 }
1011 
1012 BOOST_FIXTURE_TEST_CASE(conflicting_proof_selection, NoCoolDownFixture) {
1013  const CKey key = CKey::MakeCompressedKey();
1014 
1015  const Amount amount(PROOF_DUST_THRESHOLD);
1016  const uint32_t height = 100;
1017  const bool is_coinbase = false;
1018 
1019  ChainstateManager &chainman = *Assert(m_node.chainman);
1020  Chainstate &active_chainstate = chainman.ActiveChainstate();
1021 
1022  // This will be the conflicting UTXO for all the following proofs
1023  auto conflictingOutpoint = createUtxo(active_chainstate, key, amount);
1024 
1025  auto proof_base = buildProofWithSequence(key, {conflictingOutpoint}, 10);
1026 
1027  ConflictingProofComparator comparator;
1028  auto checkPreferred = [&](const ProofRef &candidate,
1029  const ProofRef &reference, bool expectAccepted) {
1030  BOOST_CHECK_EQUAL(comparator(candidate, reference), expectAccepted);
1031  BOOST_CHECK_EQUAL(comparator(reference, candidate), !expectAccepted);
1032 
1034  BOOST_CHECK(pm.registerProof(reference));
1035  BOOST_CHECK(pm.isBoundToPeer(reference->getId()));
1036 
1037  ProofRegistrationState state;
1038  BOOST_CHECK_EQUAL(pm.registerProof(candidate, state), expectAccepted);
1039  BOOST_CHECK_EQUAL(state.IsValid(), expectAccepted);
1040  BOOST_CHECK_EQUAL(state.GetResult() ==
1042  !expectAccepted);
1043 
1044  BOOST_CHECK_EQUAL(pm.isBoundToPeer(candidate->getId()), expectAccepted);
1045  BOOST_CHECK_EQUAL(pm.isInConflictingPool(candidate->getId()),
1046  !expectAccepted);
1047 
1048  BOOST_CHECK_EQUAL(pm.isBoundToPeer(reference->getId()),
1049  !expectAccepted);
1050  BOOST_CHECK_EQUAL(pm.isInConflictingPool(reference->getId()),
1051  expectAccepted);
1052  };
1053 
1054  // Same master key, lower sequence number
1055  checkPreferred(buildProofWithSequence(key, {conflictingOutpoint}, 9),
1056  proof_base, false);
1057  // Same master key, higher sequence number
1058  checkPreferred(buildProofWithSequence(key, {conflictingOutpoint}, 11),
1059  proof_base, true);
1060 
1061  auto buildProofFromAmounts = [&](const CKey &master,
1062  std::vector<Amount> &&amounts) {
1063  std::vector<std::tuple<COutPoint, Amount>> outpointsWithAmount{
1064  {conflictingOutpoint, amount}};
1065  std::transform(amounts.begin(), amounts.end(),
1066  std::back_inserter(outpointsWithAmount),
1067  [&key, &active_chainstate](const Amount amount) {
1068  return std::make_tuple(
1069  createUtxo(active_chainstate, key, amount),
1070  amount);
1071  });
1072  return buildProof(key, outpointsWithAmount, master, 0, height,
1073  is_coinbase, 0);
1074  };
1075 
1076  auto proof_multiUtxo = buildProofFromAmounts(
1077  key, {2 * PROOF_DUST_THRESHOLD, 2 * PROOF_DUST_THRESHOLD});
1078 
1079  // Test for both the same master and a different one. The sequence number
1080  // is the same for all these tests.
1081  for (const CKey &k : {key, CKey::MakeCompressedKey()}) {
1082  // Low amount
1083  checkPreferred(buildProofFromAmounts(
1085  proof_multiUtxo, false);
1086  // High amount
1087  checkPreferred(buildProofFromAmounts(k, {2 * PROOF_DUST_THRESHOLD,
1088  3 * PROOF_DUST_THRESHOLD}),
1089  proof_multiUtxo, true);
1090  // Same amount, low stake count
1091  checkPreferred(buildProofFromAmounts(k, {4 * PROOF_DUST_THRESHOLD}),
1092  proof_multiUtxo, true);
1093  // Same amount, high stake count
1094  checkPreferred(buildProofFromAmounts(k, {2 * PROOF_DUST_THRESHOLD,
1097  proof_multiUtxo, false);
1098  // Same amount, same stake count, selection is done on proof id
1099  auto proofSimilar = buildProofFromAmounts(
1101  checkPreferred(proofSimilar, proof_multiUtxo,
1102  proofSimilar->getId() < proof_multiUtxo->getId());
1103  }
1104 }
1105 
1106 BOOST_AUTO_TEST_CASE(conflicting_immature_proofs) {
1107  ChainstateManager &chainman = *Assert(m_node.chainman);
1108  gArgs.ForceSetArg("-avaproofstakeutxoconfirmations", "2");
1110 
1111  const CKey key = CKey::MakeCompressedKey();
1112 
1113  Chainstate &active_chainstate = chainman.ActiveChainstate();
1114 
1115  const COutPoint conflictingOutpoint = createUtxo(active_chainstate, key);
1116  const COutPoint matureOutpoint =
1117  createUtxo(active_chainstate, key, PROOF_DUST_THRESHOLD, 99);
1118 
1119  auto immature10 = buildProofWithSequence(key, {conflictingOutpoint}, 10);
1120  auto immature20 =
1121  buildProofWithSequence(key, {conflictingOutpoint, matureOutpoint}, 20);
1122 
1123  BOOST_CHECK(!pm.registerProof(immature10));
1124  BOOST_CHECK(pm.isImmature(immature10->getId()));
1125 
1126  BOOST_CHECK(!pm.registerProof(immature20));
1127  BOOST_CHECK(pm.isImmature(immature20->getId()));
1128  BOOST_CHECK(!pm.exists(immature10->getId()));
1129 
1130  // Build and register a valid proof that will conflict with the immature one
1131  auto proof30 = buildProofWithOutpoints(key, {matureOutpoint},
1132  PROOF_DUST_THRESHOLD, key, 30, 99);
1133  BOOST_CHECK(pm.registerProof(proof30));
1134  BOOST_CHECK(pm.isBoundToPeer(proof30->getId()));
1135 
1136  // Reorg to a shorter chain to make proof30 immature
1137  {
1138  BlockValidationState state;
1139  active_chainstate.InvalidateBlock(
1140  state, WITH_LOCK(chainman.GetMutex(), return chainman.ActiveTip()));
1142  WITH_LOCK(chainman.GetMutex(), return chainman.ActiveHeight()), 99);
1143  }
1144 
1145  // Check that a rescan will also select the preferred immature proof, in
1146  // this case proof30 will replace immature20.
1147  pm.updatedBlockTip();
1148 
1149  BOOST_CHECK(!pm.isBoundToPeer(proof30->getId()));
1150  BOOST_CHECK(pm.isImmature(proof30->getId()));
1151  BOOST_CHECK(!pm.exists(immature20->getId()));
1152 }
1153 
1154 BOOST_FIXTURE_TEST_CASE(preferred_conflicting_proof, NoCoolDownFixture) {
1155  ChainstateManager &chainman = *Assert(m_node.chainman);
1157 
1158  const CKey key = CKey::MakeCompressedKey();
1159  const COutPoint conflictingOutpoint =
1160  createUtxo(chainman.ActiveChainstate(), key);
1161 
1162  auto proofSeq10 = buildProofWithSequence(key, {conflictingOutpoint}, 10);
1163  auto proofSeq20 = buildProofWithSequence(key, {conflictingOutpoint}, 20);
1164  auto proofSeq30 = buildProofWithSequence(key, {conflictingOutpoint}, 30);
1165 
1166  BOOST_CHECK(pm.registerProof(proofSeq30));
1167  BOOST_CHECK(pm.isBoundToPeer(proofSeq30->getId()));
1168  BOOST_CHECK(!pm.isInConflictingPool(proofSeq30->getId()));
1169 
1170  // proofSeq10 is a worst candidate than proofSeq30, so it goes to the
1171  // conflicting pool.
1172  BOOST_CHECK(!pm.registerProof(proofSeq10));
1173  BOOST_CHECK(pm.isBoundToPeer(proofSeq30->getId()));
1174  BOOST_CHECK(!pm.isBoundToPeer(proofSeq10->getId()));
1175  BOOST_CHECK(pm.isInConflictingPool(proofSeq10->getId()));
1176 
1177  // proofSeq20 is a worst candidate than proofSeq30 but a better one than
1178  // proogSeq10, so it replaces it in the conflicting pool and proofSeq10 is
1179  // evicted.
1180  BOOST_CHECK(!pm.registerProof(proofSeq20));
1181  BOOST_CHECK(pm.isBoundToPeer(proofSeq30->getId()));
1182  BOOST_CHECK(!pm.isBoundToPeer(proofSeq20->getId()));
1183  BOOST_CHECK(pm.isInConflictingPool(proofSeq20->getId()));
1184  BOOST_CHECK(!pm.exists(proofSeq10->getId()));
1185 }
1186 
1187 BOOST_FIXTURE_TEST_CASE(update_next_conflict_time, NoCoolDownFixture) {
1188  ChainstateManager &chainman = *Assert(m_node.chainman);
1190 
1191  auto now = GetTime<std::chrono::seconds>();
1192  SetMockTime(now.count());
1193 
1194  // Updating the time of an unknown peer should fail
1195  for (size_t i = 0; i < 10; i++) {
1197  PeerId(GetRand<int>(1000)), now));
1198  }
1199 
1200  auto proof =
1201  buildRandomProof(chainman.ActiveChainstate(), MIN_VALID_PROOF_SCORE);
1202  PeerId peerid = TestPeerManager::registerAndGetPeerId(pm, proof);
1203 
1204  auto checkNextPossibleConflictTime = [&](std::chrono::seconds expected) {
1205  BOOST_CHECK(pm.forPeer(proof->getId(), [&](const Peer &p) {
1206  return p.nextPossibleConflictTime == expected;
1207  }));
1208  };
1209 
1210  checkNextPossibleConflictTime(now);
1211 
1212  // Move the time in the past is not possible
1214  peerid, now - std::chrono::seconds{1}));
1215  checkNextPossibleConflictTime(now);
1216 
1218  peerid, now + std::chrono::seconds{1}));
1219  checkNextPossibleConflictTime(now + std::chrono::seconds{1});
1220 }
1221 
1222 BOOST_FIXTURE_TEST_CASE(register_force_accept, NoCoolDownFixture) {
1223  ChainstateManager &chainman = *Assert(m_node.chainman);
1225 
1226  const CKey key = CKey::MakeCompressedKey();
1227 
1228  const COutPoint conflictingOutpoint =
1229  createUtxo(chainman.ActiveChainstate(), key);
1230 
1231  auto proofSeq10 = buildProofWithSequence(key, {conflictingOutpoint}, 10);
1232  auto proofSeq20 = buildProofWithSequence(key, {conflictingOutpoint}, 20);
1233  auto proofSeq30 = buildProofWithSequence(key, {conflictingOutpoint}, 30);
1234 
1235  BOOST_CHECK(pm.registerProof(proofSeq30));
1236  BOOST_CHECK(pm.isBoundToPeer(proofSeq30->getId()));
1237  BOOST_CHECK(!pm.isInConflictingPool(proofSeq30->getId()));
1238 
1239  // proofSeq20 is a worst candidate than proofSeq30, so it goes to the
1240  // conflicting pool.
1241  BOOST_CHECK(!pm.registerProof(proofSeq20));
1242  BOOST_CHECK(pm.isBoundToPeer(proofSeq30->getId()));
1243  BOOST_CHECK(pm.isInConflictingPool(proofSeq20->getId()));
1244 
1245  // We can force the acceptance of proofSeq20
1246  using RegistrationMode = avalanche::PeerManager::RegistrationMode;
1247  BOOST_CHECK(pm.registerProof(proofSeq20, RegistrationMode::FORCE_ACCEPT));
1248  BOOST_CHECK(pm.isBoundToPeer(proofSeq20->getId()));
1249  BOOST_CHECK(pm.isInConflictingPool(proofSeq30->getId()));
1250 
1251  // We can also force the acceptance of a proof which is not already in the
1252  // conflicting pool.
1253  BOOST_CHECK(!pm.registerProof(proofSeq10));
1254  BOOST_CHECK(!pm.exists(proofSeq10->getId()));
1255 
1256  BOOST_CHECK(pm.registerProof(proofSeq10, RegistrationMode::FORCE_ACCEPT));
1257  BOOST_CHECK(pm.isBoundToPeer(proofSeq10->getId()));
1258  BOOST_CHECK(!pm.exists(proofSeq20->getId()));
1259  BOOST_CHECK(pm.isInConflictingPool(proofSeq30->getId()));
1260 
1261  // Attempting to register again fails, and has no impact on the pools
1262  for (size_t i = 0; i < 10; i++) {
1263  BOOST_CHECK(!pm.registerProof(proofSeq10));
1264  BOOST_CHECK(
1265  !pm.registerProof(proofSeq10, RegistrationMode::FORCE_ACCEPT));
1266 
1267  BOOST_CHECK(pm.isBoundToPeer(proofSeq10->getId()));
1268  BOOST_CHECK(!pm.exists(proofSeq20->getId()));
1269  BOOST_CHECK(pm.isInConflictingPool(proofSeq30->getId()));
1270  }
1271 
1272  // Revert between proofSeq10 and proofSeq30 a few times
1273  for (size_t i = 0; i < 10; i++) {
1274  BOOST_CHECK(
1275  pm.registerProof(proofSeq30, RegistrationMode::FORCE_ACCEPT));
1276 
1277  BOOST_CHECK(pm.isBoundToPeer(proofSeq30->getId()));
1278  BOOST_CHECK(pm.isInConflictingPool(proofSeq10->getId()));
1279 
1280  BOOST_CHECK(
1281  pm.registerProof(proofSeq10, RegistrationMode::FORCE_ACCEPT));
1282 
1283  BOOST_CHECK(pm.isBoundToPeer(proofSeq10->getId()));
1284  BOOST_CHECK(pm.isInConflictingPool(proofSeq30->getId()));
1285  }
1286 }
1287 
1288 BOOST_FIXTURE_TEST_CASE(evicted_proof, NoCoolDownFixture) {
1289  ChainstateManager &chainman = *Assert(m_node.chainman);
1291 
1292  const CKey key = CKey::MakeCompressedKey();
1293 
1294  const COutPoint conflictingOutpoint =
1295  createUtxo(chainman.ActiveChainstate(), key);
1296 
1297  auto proofSeq10 = buildProofWithSequence(key, {conflictingOutpoint}, 10);
1298  auto proofSeq20 = buildProofWithSequence(key, {conflictingOutpoint}, 20);
1299  auto proofSeq30 = buildProofWithSequence(key, {conflictingOutpoint}, 30);
1300 
1301  {
1302  ProofRegistrationState state;
1303  BOOST_CHECK(pm.registerProof(proofSeq30, state));
1304  BOOST_CHECK(state.IsValid());
1305  }
1306 
1307  {
1308  ProofRegistrationState state;
1309  BOOST_CHECK(!pm.registerProof(proofSeq20, state));
1311  }
1312 
1313  {
1314  ProofRegistrationState state;
1315  BOOST_CHECK(!pm.registerProof(proofSeq10, state));
1317  }
1318 }
1319 
1320 BOOST_AUTO_TEST_CASE(conflicting_proof_cooldown) {
1321  ChainstateManager &chainman = *Assert(m_node.chainman);
1323 
1324  const CKey key = CKey::MakeCompressedKey();
1325 
1326  const COutPoint conflictingOutpoint =
1327  createUtxo(chainman.ActiveChainstate(), key);
1328 
1329  auto proofSeq20 = buildProofWithSequence(key, {conflictingOutpoint}, 20);
1330  auto proofSeq30 = buildProofWithSequence(key, {conflictingOutpoint}, 30);
1331  auto proofSeq40 = buildProofWithSequence(key, {conflictingOutpoint}, 40);
1332 
1333  int64_t conflictingProofCooldown = 100;
1334  gArgs.ForceSetArg("-avalancheconflictingproofcooldown",
1335  strprintf("%d", conflictingProofCooldown));
1336 
1337  int64_t now = GetTime();
1338 
1339  auto increaseMockTime = [&](int64_t s) {
1340  now += s;
1341  SetMockTime(now);
1342  };
1343  increaseMockTime(0);
1344 
1345  BOOST_CHECK(pm.registerProof(proofSeq30));
1346  BOOST_CHECK(pm.isBoundToPeer(proofSeq30->getId()));
1347 
1348  auto checkRegistrationFailure = [&](const ProofRef &proof,
1349  ProofRegistrationResult reason) {
1350  ProofRegistrationState state;
1351  BOOST_CHECK(!pm.registerProof(proof, state));
1352  BOOST_CHECK(state.GetResult() == reason);
1353  };
1354 
1355  // Registering a conflicting proof will fail due to the conflicting proof
1356  // cooldown
1357  checkRegistrationFailure(proofSeq20,
1359  BOOST_CHECK(!pm.exists(proofSeq20->getId()));
1360 
1361  // The cooldown applies as well if the proof is the favorite
1362  checkRegistrationFailure(proofSeq40,
1364  BOOST_CHECK(!pm.exists(proofSeq40->getId()));
1365 
1366  // Elapse the cooldown
1367  increaseMockTime(conflictingProofCooldown);
1368 
1369  // The proof will now be added to conflicting pool
1370  checkRegistrationFailure(proofSeq20, ProofRegistrationResult::CONFLICTING);
1371  BOOST_CHECK(pm.isInConflictingPool(proofSeq20->getId()));
1372 
1373  // But no other
1374  checkRegistrationFailure(proofSeq40,
1376  BOOST_CHECK(!pm.exists(proofSeq40->getId()));
1377  BOOST_CHECK(pm.isInConflictingPool(proofSeq20->getId()));
1378 
1379  // Elapse the cooldown
1380  increaseMockTime(conflictingProofCooldown);
1381 
1382  // The proof will now be accepted to replace proofSeq30, proofSeq30 will
1383  // move to the conflicting pool, and proofSeq20 will be evicted.
1384  BOOST_CHECK(pm.registerProof(proofSeq40));
1385  BOOST_CHECK(pm.isBoundToPeer(proofSeq40->getId()));
1386  BOOST_CHECK(pm.isInConflictingPool(proofSeq30->getId()));
1387  BOOST_CHECK(!pm.exists(proofSeq20->getId()));
1388 
1389  gArgs.ClearForcedArg("-avalancheconflictingproofcooldown");
1390 }
1391 
1392 BOOST_FIXTURE_TEST_CASE(reject_proof, NoCoolDownFixture) {
1393  ChainstateManager &chainman = *Assert(m_node.chainman);
1394  gArgs.ForceSetArg("-avaproofstakeutxoconfirmations", "2");
1396 
1397  const CKey key = CKey::MakeCompressedKey();
1398 
1399  Chainstate &active_chainstate = chainman.ActiveChainstate();
1400 
1401  const COutPoint conflictingOutpoint =
1402  createUtxo(active_chainstate, key, PROOF_DUST_THRESHOLD, 99);
1403  const COutPoint immatureOutpoint = createUtxo(active_chainstate, key);
1404 
1405  // The good, the bad and the ugly
1406  auto proofSeq10 = buildProofWithOutpoints(
1407  key, {conflictingOutpoint}, PROOF_DUST_THRESHOLD, key, 10, 99);
1408  auto proofSeq20 = buildProofWithOutpoints(
1409  key, {conflictingOutpoint}, PROOF_DUST_THRESHOLD, key, 20, 99);
1410  auto immature30 = buildProofWithSequence(
1411  key, {conflictingOutpoint, immatureOutpoint}, 30);
1412 
1413  BOOST_CHECK(pm.registerProof(proofSeq20));
1414  BOOST_CHECK(!pm.registerProof(proofSeq10));
1415  BOOST_CHECK(!pm.registerProof(immature30));
1416 
1417  BOOST_CHECK(pm.isBoundToPeer(proofSeq20->getId()));
1418  BOOST_CHECK(pm.isInConflictingPool(proofSeq10->getId()));
1419  BOOST_CHECK(pm.isImmature(immature30->getId()));
1420 
1421  // Rejecting a proof that doesn't exist should fail
1422  for (size_t i = 0; i < 10; i++) {
1423  BOOST_CHECK(
1426  BOOST_CHECK(
1429  }
1430 
1431  auto checkRejectDefault = [&](const ProofId &proofid) {
1432  BOOST_CHECK(pm.exists(proofid));
1433  const bool isImmature = pm.isImmature(proofid);
1436  BOOST_CHECK(!pm.isBoundToPeer(proofid));
1437  BOOST_CHECK_EQUAL(pm.exists(proofid), !isImmature);
1438  };
1439 
1440  auto checkRejectInvalidate = [&](const ProofId &proofid) {
1441  BOOST_CHECK(pm.exists(proofid));
1444  };
1445 
1446  // Reject from the immature pool
1447  checkRejectDefault(immature30->getId());
1448  BOOST_CHECK(!pm.registerProof(immature30));
1449  BOOST_CHECK(pm.isImmature(immature30->getId()));
1450  checkRejectInvalidate(immature30->getId());
1451 
1452  // Reject from the conflicting pool
1453  checkRejectDefault(proofSeq10->getId());
1454  checkRejectInvalidate(proofSeq10->getId());
1455 
1456  // Add again a proof to the conflicting pool
1457  BOOST_CHECK(!pm.registerProof(proofSeq10));
1458  BOOST_CHECK(pm.isInConflictingPool(proofSeq10->getId()));
1459 
1460  // Reject from the valid pool, default mode
1461  checkRejectDefault(proofSeq20->getId());
1462 
1463  // The conflicting proof should be promoted to a peer
1464  BOOST_CHECK(!pm.isInConflictingPool(proofSeq10->getId()));
1465  BOOST_CHECK(pm.isBoundToPeer(proofSeq10->getId()));
1466 
1467  // Reject from the valid pool, invalidate mode
1468  checkRejectInvalidate(proofSeq10->getId());
1469 
1470  // The conflicting proof should also be promoted to a peer
1471  BOOST_CHECK(!pm.isInConflictingPool(proofSeq20->getId()));
1472  BOOST_CHECK(pm.isBoundToPeer(proofSeq20->getId()));
1473 }
1474 
1475 BOOST_AUTO_TEST_CASE(should_request_more_nodes) {
1476  ChainstateManager &chainman = *Assert(m_node.chainman);
1478 
1479  // Set mock time so that proof registration time is predictable and
1480  // testable.
1481  SetMockTime(GetTime());
1482 
1483  auto proof =
1484  buildRandomProof(chainman.ActiveChainstate(), MIN_VALID_PROOF_SCORE);
1485  BOOST_CHECK(pm.registerProof(proof));
1486  // Not dangling yet, the proof will remain active for some time before it
1487  // turns dangling if no node is connecting in the meantime.
1488  BOOST_CHECK(!pm.isDangling(proof->getId()));
1489 
1490  // We have no nodes, so select node will fail and flag that we need more
1491  // nodes
1494 
1495  for (size_t i = 0; i < 10; i++) {
1496  // The flag will not trigger again until we fail to select nodes again
1498  }
1499 
1500  // Add a few nodes.
1501  const ProofId &proofid = proof->getId();
1502  for (size_t i = 0; i < 10; i++) {
1503  BOOST_CHECK(pm.addNode(i, proofid));
1504  }
1505 
1506  BOOST_CHECK(!pm.isDangling(proof->getId()));
1507 
1508  auto cooldownTimepoint = Now<SteadyMilliseconds>() + 10s;
1509 
1510  // All the nodes can be selected once
1511  for (size_t i = 0; i < 10; i++) {
1512  NodeId selectedId = pm.selectNode();
1513  BOOST_CHECK_NE(selectedId, NO_NODE);
1514  BOOST_CHECK(pm.updateNextRequestTime(selectedId, cooldownTimepoint));
1516  }
1517 
1518  // All the nodes have been requested, next select will fail and the flag
1519  // should trigger
1522 
1523  for (size_t i = 0; i < 10; i++) {
1524  // The flag will not trigger again until we fail to select nodes again
1526  }
1527 
1528  // Make it possible to request a node again
1529  BOOST_CHECK(pm.updateNextRequestTime(0, Now<SteadyMilliseconds>()));
1530  BOOST_CHECK_NE(pm.selectNode(), NO_NODE);
1532 
1533  // Add another proof with no node attached
1534  auto proof2 =
1535  buildRandomProof(chainman.ActiveChainstate(), MIN_VALID_PROOF_SCORE);
1536  BOOST_CHECK(pm.registerProof(proof2));
1537  BOOST_CHECK(!pm.isDangling(proof2->getId()));
1538  TestPeerManager::cleanupDanglingProofs(pm);
1539  BOOST_CHECK(!pm.isDangling(proof2->getId()));
1541 
1542  // After some time the proof will be considered dangling and more nodes will
1543  // be requested.
1544  SetMockTime(GetTime() + 15 * 60);
1545  TestPeerManager::cleanupDanglingProofs(pm);
1546  BOOST_CHECK(pm.isDangling(proof2->getId()));
1548 
1549  for (size_t i = 0; i < 10; i++) {
1550  BOOST_CHECK(pm.isDangling(proof2->getId()));
1551  // The flag will not trigger again until the condition is met again
1553  }
1554 
1555  // Attempt to register the dangling proof again. This should fail but
1556  // trigger a request for more nodes.
1557  ProofRegistrationState state;
1558  BOOST_CHECK(!pm.registerProof(proof2, state));
1560  BOOST_CHECK(pm.isDangling(proof2->getId()));
1562 
1563  for (size_t i = 0; i < 10; i++) {
1564  BOOST_CHECK(pm.isDangling(proof2->getId()));
1565  // The flag will not trigger again until the condition is met again
1567  }
1568 
1569  // Attach a node to that proof
1570  BOOST_CHECK(!pm.addNode(11, proof2->getId()));
1571  BOOST_CHECK(pm.registerProof(proof2));
1572  SetMockTime(GetTime() + 15 * 60);
1573  TestPeerManager::cleanupDanglingProofs(pm);
1574  BOOST_CHECK(!pm.isDangling(proof2->getId()));
1576 
1577  // Disconnect the node, the proof is dangling again
1578  BOOST_CHECK(pm.removeNode(11));
1579  TestPeerManager::cleanupDanglingProofs(pm);
1580  BOOST_CHECK(pm.isDangling(proof2->getId()));
1582 
1583  // Invalidating the proof, removes the proof from the dangling pool but not
1584  // a simple rejection.
1587  BOOST_CHECK(pm.isDangling(proof2->getId()));
1590  BOOST_CHECK(!pm.isDangling(proof2->getId()));
1591 }
1592 
1593 BOOST_AUTO_TEST_CASE(score_ordering) {
1594  ChainstateManager &chainman = *Assert(m_node.chainman);
1596 
1597  std::vector<uint32_t> expectedScores(10);
1598  // Expect the peers to be ordered by descending score
1599  std::generate(expectedScores.rbegin(), expectedScores.rend(),
1600  [n = 1]() mutable { return n++ * MIN_VALID_PROOF_SCORE; });
1601 
1602  std::vector<ProofRef> proofs;
1603  proofs.reserve(expectedScores.size());
1604  for (uint32_t score : expectedScores) {
1605  proofs.push_back(buildRandomProof(chainman.ActiveChainstate(), score));
1606  }
1607 
1608  // Shuffle the proofs so they are registered in a random score order
1609  Shuffle(proofs.begin(), proofs.end(), FastRandomContext());
1610  for (auto &proof : proofs) {
1611  BOOST_CHECK(pm.registerProof(proof));
1612  }
1613 
1614  auto peersScores = TestPeerManager::getOrderedScores(pm);
1615  BOOST_CHECK_EQUAL_COLLECTIONS(peersScores.begin(), peersScores.end(),
1616  expectedScores.begin(), expectedScores.end());
1617 }
1618 
1619 BOOST_FIXTURE_TEST_CASE(known_score_tracking, NoCoolDownFixture) {
1620  ChainstateManager &chainman = *Assert(m_node.chainman);
1621  gArgs.ForceSetArg("-avaproofstakeutxoconfirmations", "2");
1623 
1624  const CKey key = CKey::MakeCompressedKey();
1625 
1626  const Amount amount1(PROOF_DUST_THRESHOLD);
1627  const Amount amount2(2 * PROOF_DUST_THRESHOLD);
1628 
1629  Chainstate &active_chainstate = chainman.ActiveChainstate();
1630 
1631  const COutPoint peer1ConflictingOutput =
1632  createUtxo(active_chainstate, key, amount1, 99);
1633  const COutPoint peer1SecondaryOutpoint =
1634  createUtxo(active_chainstate, key, amount2, 99);
1635 
1636  auto peer1Proof1 = buildProof(
1637  key,
1638  {{peer1ConflictingOutput, amount1}, {peer1SecondaryOutpoint, amount2}},
1639  key, 10, 99);
1640  auto peer1Proof2 =
1641  buildProof(key, {{peer1ConflictingOutput, amount1}}, key, 20, 99);
1642 
1643  // Create a proof with an immature UTXO, so the proof will be immature
1644  auto peer1Proof3 =
1645  buildProof(key,
1646  {{peer1ConflictingOutput, amount1},
1647  {createUtxo(active_chainstate, key, amount1), amount1}},
1648  key, 30);
1649 
1650  const uint32_t peer1Score1 = Proof::amountToScore(amount1 + amount2);
1651  const uint32_t peer1Score2 = Proof::amountToScore(amount1);
1652 
1653  // Add first peer and check that we have its score tracked
1655  BOOST_CHECK(pm.registerProof(peer1Proof2));
1656  BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), peer1Score2);
1657 
1658  // Ensure failing to add conflicting proofs doesn't affect the score, the
1659  // first proof stays bound and counted
1660  BOOST_CHECK(!pm.registerProof(peer1Proof1));
1661  BOOST_CHECK(!pm.registerProof(peer1Proof3));
1662 
1663  BOOST_CHECK(pm.isBoundToPeer(peer1Proof2->getId()));
1664  BOOST_CHECK(pm.isInConflictingPool(peer1Proof1->getId()));
1665  BOOST_CHECK(pm.isImmature(peer1Proof3->getId()));
1666 
1667  BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), peer1Score2);
1668 
1669  auto checkRejectDefault = [&](const ProofId &proofid) {
1670  BOOST_CHECK(pm.exists(proofid));
1671  const bool isImmature = pm.isImmature(proofid);
1674  BOOST_CHECK(!pm.isBoundToPeer(proofid));
1675  BOOST_CHECK_EQUAL(pm.exists(proofid), !isImmature);
1676  };
1677 
1678  auto checkRejectInvalidate = [&](const ProofId &proofid) {
1679  BOOST_CHECK(pm.exists(proofid));
1682  };
1683 
1684  // Reject from the immature pool doesn't affect tracked score
1685  checkRejectDefault(peer1Proof3->getId());
1686  BOOST_CHECK(!pm.registerProof(peer1Proof3));
1687  BOOST_CHECK(pm.isImmature(peer1Proof3->getId()));
1688  BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), peer1Score2);
1689  checkRejectInvalidate(peer1Proof3->getId());
1690  BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), peer1Score2);
1691 
1692  // Reject from the conflicting pool
1693  checkRejectDefault(peer1Proof1->getId());
1694  checkRejectInvalidate(peer1Proof1->getId());
1695 
1696  // Add again a proof to the conflicting pool
1697  BOOST_CHECK(!pm.registerProof(peer1Proof1));
1698  BOOST_CHECK(pm.isInConflictingPool(peer1Proof1->getId()));
1699  BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), peer1Score2);
1700 
1701  // Reject from the valid pool, default mode
1702  // Now the score should change as the new peer is promoted
1703  checkRejectDefault(peer1Proof2->getId());
1704  BOOST_CHECK(!pm.isInConflictingPool(peer1Proof1->getId()));
1705  BOOST_CHECK(pm.isBoundToPeer(peer1Proof1->getId()));
1706  BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), peer1Score1);
1707 
1708  // Reject from the valid pool, invalidate mode
1709  // Now the score should change as the old peer is re-promoted
1710  checkRejectInvalidate(peer1Proof1->getId());
1711 
1712  // The conflicting proof should also be promoted to a peer
1713  BOOST_CHECK(!pm.isInConflictingPool(peer1Proof2->getId()));
1714  BOOST_CHECK(pm.isBoundToPeer(peer1Proof2->getId()));
1715  BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), peer1Score2);
1716 
1717  // Now add another peer and check that combined scores are correct
1718  uint32_t peer2Score = 1 * MIN_VALID_PROOF_SCORE;
1719  auto peer2Proof1 = buildRandomProof(active_chainstate, peer2Score, 99);
1720  PeerId peerid2 = TestPeerManager::registerAndGetPeerId(pm, peer2Proof1);
1721  BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), peer1Score2 + peer2Score);
1722 
1723  // Trying to remove non-existent peer doesn't affect score
1724  BOOST_CHECK(!pm.removePeer(1234));
1725  BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), peer1Score2 + peer2Score);
1726 
1727  // Removing new peer removes its score
1728  BOOST_CHECK(pm.removePeer(peerid2));
1729  BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), peer1Score2);
1730  PeerId peerid1 =
1731  TestPeerManager::getPeerIdForProofId(pm, peer1Proof2->getId());
1732  BOOST_CHECK(pm.removePeer(peerid1));
1734 }
1735 
1736 BOOST_AUTO_TEST_CASE(connected_score_tracking) {
1737  ChainstateManager &chainman = *Assert(m_node.chainman);
1739 
1740  const auto checkScores = [&pm](uint32_t known, uint32_t connected) {
1742  BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), connected);
1743  };
1744 
1745  // Start out with 0s
1746  checkScores(0, 0);
1747 
1748  Chainstate &active_chainstate = chainman.ActiveChainstate();
1749 
1750  // Create one peer without a node. Its score should be registered but not
1751  // connected
1752  uint32_t score1 = 10000000 * MIN_VALID_PROOF_SCORE;
1753  auto proof1 = buildRandomProof(active_chainstate, score1);
1754  PeerId peerid1 = TestPeerManager::registerAndGetPeerId(pm, proof1);
1755  checkScores(score1, 0);
1756 
1757  // Add nodes. We now have a connected score, but it doesn't matter how many
1758  // nodes we add the score is the same
1759  const ProofId &proofid1 = proof1->getId();
1760  const uint8_t nodesToAdd = 10;
1761  for (int i = 0; i < nodesToAdd; i++) {
1762  BOOST_CHECK(pm.addNode(i, proofid1));
1763  checkScores(score1, score1);
1764  }
1765 
1766  // Remove all but 1 node and ensure the score doesn't change
1767  for (int i = 0; i < nodesToAdd - 1; i++) {
1768  BOOST_CHECK(pm.removeNode(i));
1769  checkScores(score1, score1);
1770  }
1771 
1772  // Removing the last node should remove the score from the connected count
1773  BOOST_CHECK(pm.removeNode(nodesToAdd - 1));
1774  checkScores(score1, 0);
1775 
1776  // Add 2 nodes to peer and create peer2. Without a node peer2 has no
1777  // connected score but after adding a node it does.
1778  BOOST_CHECK(pm.addNode(0, proofid1));
1779  BOOST_CHECK(pm.addNode(1, proofid1));
1780  checkScores(score1, score1);
1781 
1782  uint32_t score2 = 1 * MIN_VALID_PROOF_SCORE;
1783  auto proof2 = buildRandomProof(active_chainstate, score2);
1784  PeerId peerid2 = TestPeerManager::registerAndGetPeerId(pm, proof2);
1785  checkScores(score1 + score2, score1);
1786  BOOST_CHECK(pm.addNode(2, proof2->getId()));
1787  checkScores(score1 + score2, score1 + score2);
1788 
1789  // The first peer has two nodes left. Remove one and nothing happens, remove
1790  // the other and its score is no longer in the connected counter..
1791  BOOST_CHECK(pm.removeNode(0));
1792  checkScores(score1 + score2, score1 + score2);
1793  BOOST_CHECK(pm.removeNode(1));
1794  checkScores(score1 + score2, score2);
1795 
1796  // Removing a peer with no allocated score has no affect.
1797  BOOST_CHECK(pm.removePeer(peerid1));
1798  checkScores(score2, score2);
1799 
1800  // Remove the second peer's node removes its allocated score.
1801  BOOST_CHECK(pm.removeNode(2));
1802  checkScores(score2, 0);
1803 
1804  // Removing the second peer takes us back to 0.
1805  BOOST_CHECK(pm.removePeer(peerid2));
1806  checkScores(0, 0);
1807 
1808  // Add 2 peers with nodes and remove them without removing the nodes first.
1809  // Both score counters should be reduced by each peer's score when it's
1810  // removed.
1811  peerid1 = TestPeerManager::registerAndGetPeerId(pm, proof1);
1812  checkScores(score1, 0);
1813  peerid2 = TestPeerManager::registerAndGetPeerId(pm, proof2);
1814  checkScores(score1 + score2, 0);
1815  BOOST_CHECK(pm.addNode(0, proof1->getId()));
1816  checkScores(score1 + score2, score1);
1817  BOOST_CHECK(pm.addNode(1, proof2->getId()));
1818  checkScores(score1 + score2, score1 + score2);
1819 
1820  BOOST_CHECK(pm.removePeer(peerid2));
1821  checkScores(score1, score1);
1822 
1823  BOOST_CHECK(pm.removePeer(peerid1));
1824  checkScores(0, 0);
1825 }
1826 
1827 BOOST_FIXTURE_TEST_CASE(proof_radix_tree, NoCoolDownFixture) {
1828  ChainstateManager &chainman = *Assert(m_node.chainman);
1830 
1831  struct ProofComparatorById {
1832  bool operator()(const ProofRef &lhs, const ProofRef &rhs) const {
1833  return lhs->getId() < rhs->getId();
1834  };
1835  };
1836  using ProofSetById = std::set<ProofRef, ProofComparatorById>;
1837  // Maintain a list of the expected proofs through this test
1838  ProofSetById expectedProofs;
1839 
1840  auto matchExpectedContent = [&](const auto &tree) {
1841  auto it = expectedProofs.begin();
1842  return tree.forEachLeaf([&](auto pLeaf) {
1843  return it != expectedProofs.end() &&
1844  pLeaf->getId() == (*it++)->getId();
1845  });
1846  };
1847 
1848  CKey key = CKey::MakeCompressedKey();
1849  const int64_t sequence = 10;
1850 
1851  Chainstate &active_chainstate = chainman.ActiveChainstate();
1852 
1853  // Add some initial proofs
1854  for (size_t i = 0; i < 10; i++) {
1855  auto outpoint = createUtxo(active_chainstate, key);
1856  auto proof = buildProofWithSequence(key, {{outpoint}}, sequence);
1857  BOOST_CHECK(pm.registerProof(proof));
1858  expectedProofs.insert(std::move(proof));
1859  }
1860 
1861  const auto &treeRef = pm.getShareableProofsSnapshot();
1862  BOOST_CHECK(matchExpectedContent(treeRef));
1863 
1864  // Create a copy
1865  auto tree = pm.getShareableProofsSnapshot();
1866 
1867  // Adding more proofs doesn't change the tree...
1868  ProofSetById addedProofs;
1869  std::vector<COutPoint> outpointsToSpend;
1870  for (size_t i = 0; i < 10; i++) {
1871  auto outpoint = createUtxo(active_chainstate, key);
1872  auto proof = buildProofWithSequence(key, {{outpoint}}, sequence);
1873  BOOST_CHECK(pm.registerProof(proof));
1874  addedProofs.insert(std::move(proof));
1875  outpointsToSpend.push_back(std::move(outpoint));
1876  }
1877 
1878  BOOST_CHECK(matchExpectedContent(tree));
1879 
1880  // ...until we get a new copy
1881  tree = pm.getShareableProofsSnapshot();
1882  expectedProofs.insert(addedProofs.begin(), addedProofs.end());
1883  BOOST_CHECK(matchExpectedContent(tree));
1884 
1885  // Spend some coins to make the associated proofs invalid
1886  {
1887  LOCK(cs_main);
1888  CCoinsViewCache &coins = active_chainstate.CoinsTip();
1889  for (const auto &outpoint : outpointsToSpend) {
1890  coins.SpendCoin(outpoint);
1891  }
1892  }
1893 
1894  pm.updatedBlockTip();
1895 
1896  // This doesn't change the tree...
1897  BOOST_CHECK(matchExpectedContent(tree));
1898 
1899  // ...until we get a new copy
1900  tree = pm.getShareableProofsSnapshot();
1901  for (const auto &proof : addedProofs) {
1902  BOOST_CHECK_EQUAL(expectedProofs.erase(proof), 1);
1903  }
1904  BOOST_CHECK(matchExpectedContent(tree));
1905 
1906  // Add some more proof for which we will create conflicts
1907  std::vector<ProofRef> conflictingProofs;
1908  std::vector<COutPoint> conflictingOutpoints;
1909  for (size_t i = 0; i < 10; i++) {
1910  auto outpoint = createUtxo(active_chainstate, key);
1911  auto proof = buildProofWithSequence(key, {{outpoint}}, sequence);
1912  BOOST_CHECK(pm.registerProof(proof));
1913  conflictingProofs.push_back(std::move(proof));
1914  conflictingOutpoints.push_back(std::move(outpoint));
1915  }
1916 
1917  tree = pm.getShareableProofsSnapshot();
1918  expectedProofs.insert(conflictingProofs.begin(), conflictingProofs.end());
1919  BOOST_CHECK(matchExpectedContent(tree));
1920 
1921  // Build a bunch of conflicting proofs, half better, half worst
1922  for (size_t i = 0; i < 10; i += 2) {
1923  // The worst proof is not added to the expected set
1924  BOOST_CHECK(!pm.registerProof(buildProofWithSequence(
1925  key, {{conflictingOutpoints[i]}}, sequence - 1)));
1926 
1927  // But the better proof should replace its conflicting one
1928  auto replacementProof = buildProofWithSequence(
1929  key, {{conflictingOutpoints[i + 1]}}, sequence + 1);
1930  BOOST_CHECK(pm.registerProof(replacementProof));
1931  BOOST_CHECK_EQUAL(expectedProofs.erase(conflictingProofs[i + 1]), 1);
1932  BOOST_CHECK(expectedProofs.insert(replacementProof).second);
1933  }
1934 
1935  tree = pm.getShareableProofsSnapshot();
1936  BOOST_CHECK(matchExpectedContent(tree));
1937 
1938  // Check for consistency
1939  pm.verify();
1940 }
1941 
1942 BOOST_AUTO_TEST_CASE(received_avaproofs) {
1943  ChainstateManager &chainman = *Assert(m_node.chainman);
1945 
1946  auto addNode = [&](NodeId nodeid) {
1947  auto proof = buildRandomProof(chainman.ActiveChainstate(),
1949  BOOST_CHECK(pm.registerProof(proof));
1950  BOOST_CHECK(pm.addNode(nodeid, proof->getId()));
1951  };
1952 
1953  for (NodeId nodeid = 0; nodeid < 10; nodeid++) {
1954  // Node doesn't exist
1955  BOOST_CHECK(!pm.latchAvaproofsSent(nodeid));
1956 
1957  addNode(nodeid);
1958  BOOST_CHECK(pm.latchAvaproofsSent(nodeid));
1959 
1960  // The flag is already set
1961  BOOST_CHECK(!pm.latchAvaproofsSent(nodeid));
1962  }
1963 }
1964 
1965 BOOST_FIXTURE_TEST_CASE(cleanup_dangling_proof, NoCoolDownFixture) {
1966  ChainstateManager &chainman = *Assert(m_node.chainman);
1967 
1969 
1970  const auto now = GetTime<std::chrono::seconds>();
1971  auto mocktime = now;
1972 
1973  auto elapseTime = [&](std::chrono::seconds seconds) {
1974  mocktime += seconds;
1975  SetMockTime(mocktime.count());
1976  };
1977  elapseTime(0s);
1978 
1979  const CKey key = CKey::MakeCompressedKey();
1980 
1981  const size_t numProofs = 10;
1982 
1983  std::vector<COutPoint> outpoints(numProofs);
1984  std::vector<ProofRef> proofs(numProofs);
1985  std::vector<ProofRef> conflictingProofs(numProofs);
1986  for (size_t i = 0; i < numProofs; i++) {
1987  outpoints[i] = createUtxo(chainman.ActiveChainstate(), key);
1988  proofs[i] = buildProofWithSequence(key, {outpoints[i]}, 2);
1989  conflictingProofs[i] = buildProofWithSequence(key, {outpoints[i]}, 1);
1990 
1991  BOOST_CHECK(pm.registerProof(proofs[i]));
1992  BOOST_CHECK(pm.isBoundToPeer(proofs[i]->getId()));
1993 
1994  BOOST_CHECK(!pm.registerProof(conflictingProofs[i]));
1995  BOOST_CHECK(pm.isInConflictingPool(conflictingProofs[i]->getId()));
1996 
1997  if (i % 2) {
1998  // Odd indexes get a node attached to them
1999  BOOST_CHECK(pm.addNode(i, proofs[i]->getId()));
2000  }
2001  BOOST_CHECK_EQUAL(pm.forPeer(proofs[i]->getId(),
2002  [&](const avalanche::Peer &peer) {
2003  return peer.node_count;
2004  }),
2005  i % 2);
2006 
2007  elapseTime(1s);
2008  }
2009 
2010  // No proof expired yet
2011  TestPeerManager::cleanupDanglingProofs(pm);
2012  for (size_t i = 0; i < numProofs; i++) {
2013  BOOST_CHECK(pm.isBoundToPeer(proofs[i]->getId()));
2014  BOOST_CHECK(pm.isInConflictingPool(conflictingProofs[i]->getId()));
2015  }
2016 
2017  // Elapse the dangling timeout
2019  TestPeerManager::cleanupDanglingProofs(pm);
2020  for (size_t i = 0; i < numProofs; i++) {
2021  const bool hasNodeAttached = i % 2;
2022 
2023  // Only the peers with no nodes attached are getting discarded
2024  BOOST_CHECK_EQUAL(pm.isBoundToPeer(proofs[i]->getId()),
2025  hasNodeAttached);
2026  BOOST_CHECK_EQUAL(!pm.exists(proofs[i]->getId()), !hasNodeAttached);
2027 
2028  // The proofs conflicting with the discarded ones are pulled back
2029  BOOST_CHECK_EQUAL(pm.isInConflictingPool(conflictingProofs[i]->getId()),
2030  hasNodeAttached);
2031  BOOST_CHECK_EQUAL(pm.isBoundToPeer(conflictingProofs[i]->getId()),
2032  !hasNodeAttached);
2033  }
2034 
2035  // Attach a node to the first conflicting proof, which has been promoted
2036  BOOST_CHECK(pm.addNode(42, conflictingProofs[0]->getId()));
2037  BOOST_CHECK(pm.forPeer(
2038  conflictingProofs[0]->getId(),
2039  [&](const avalanche::Peer &peer) { return peer.node_count == 1; }));
2040 
2041  // Elapse the dangling timeout again
2043  TestPeerManager::cleanupDanglingProofs(pm);
2044  for (size_t i = 0; i < numProofs; i++) {
2045  const bool hasNodeAttached = i % 2;
2046 
2047  // The initial peers with a node attached are still there
2048  BOOST_CHECK_EQUAL(pm.isBoundToPeer(proofs[i]->getId()),
2049  hasNodeAttached);
2050  BOOST_CHECK_EQUAL(!pm.exists(proofs[i]->getId()), !hasNodeAttached);
2051 
2052  // This time the previouly promoted conflicting proofs are evicted
2053  // because they have no node attached, except the index 0.
2054  BOOST_CHECK_EQUAL(pm.exists(conflictingProofs[i]->getId()),
2055  hasNodeAttached || i == 0);
2056  BOOST_CHECK_EQUAL(pm.isInConflictingPool(conflictingProofs[i]->getId()),
2057  hasNodeAttached);
2058  BOOST_CHECK_EQUAL(pm.isBoundToPeer(conflictingProofs[i]->getId()),
2059  i == 0);
2060  }
2061 
2062  // Disconnect all the nodes
2063  for (size_t i = 1; i < numProofs; i += 2) {
2064  BOOST_CHECK(pm.removeNode(i));
2065  BOOST_CHECK(
2066  pm.forPeer(proofs[i]->getId(), [&](const avalanche::Peer &peer) {
2067  return peer.node_count == 0;
2068  }));
2069  }
2070  BOOST_CHECK(pm.removeNode(42));
2071  BOOST_CHECK(pm.forPeer(
2072  conflictingProofs[0]->getId(),
2073  [&](const avalanche::Peer &peer) { return peer.node_count == 0; }));
2074 
2075  TestPeerManager::cleanupDanglingProofs(pm);
2076  for (size_t i = 0; i < numProofs; i++) {
2077  const bool hadNodeAttached = i % 2;
2078 
2079  // All initially valid proofs have now been discarded
2080  BOOST_CHECK(!pm.exists(proofs[i]->getId()));
2081 
2082  // The remaining conflicting proofs are promoted
2083  BOOST_CHECK_EQUAL(!pm.exists(conflictingProofs[i]->getId()),
2084  !hadNodeAttached);
2085  BOOST_CHECK(!pm.isInConflictingPool(conflictingProofs[i]->getId()));
2086  BOOST_CHECK_EQUAL(pm.isBoundToPeer(conflictingProofs[i]->getId()),
2087  hadNodeAttached);
2088  }
2089 
2090  // Elapse the timeout for the newly promoted conflicting proofs
2092 
2093  // All other proofs have now been discarded
2094  TestPeerManager::cleanupDanglingProofs(pm);
2095 
2096  for (size_t i = 0; i < numProofs; i++) {
2097  // All proofs have finally been discarded
2098  BOOST_CHECK(!pm.exists(proofs[i]->getId()));
2099  BOOST_CHECK(!pm.exists(conflictingProofs[i]->getId()));
2100  }
2101 }
2102 
2103 BOOST_AUTO_TEST_CASE(register_proof_missing_utxo) {
2104  ChainstateManager &chainman = *Assert(m_node.chainman);
2106 
2107  CKey key = CKey::MakeCompressedKey();
2108  auto proof = buildProofWithOutpoints(key, {{TxId(GetRandHash()), 0}},
2110 
2111  ProofRegistrationState state;
2112  BOOST_CHECK(!pm.registerProof(proof, state));
2113  BOOST_CHECK(state.GetResult() == ProofRegistrationResult::MISSING_UTXO);
2114 }
2115 
2116 BOOST_AUTO_TEST_CASE(proof_expiry) {
2117  gArgs.ForceSetArg("-avalancheconflictingproofcooldown", "0");
2118 
2119  ChainstateManager &chainman = *Assert(m_node.chainman);
2121 
2122  const int64_t tipTime =
2123  WITH_LOCK(chainman.GetMutex(), return chainman.ActiveTip())
2124  ->GetBlockTime();
2125 
2126  CKey key = CKey::MakeCompressedKey();
2127 
2128  auto utxo = createUtxo(chainman.ActiveChainstate(), key);
2129  auto proofToExpire = buildProof(key, {{utxo, PROOF_DUST_THRESHOLD}}, key, 2,
2130  100, false, tipTime + 1);
2131  auto conflictingProof = buildProof(key, {{utxo, PROOF_DUST_THRESHOLD}}, key,
2132  1, 100, false, tipTime + 2);
2133 
2134  // Our proofToExpire is not expired yet, so it registers fine
2135  BOOST_CHECK(pm.registerProof(proofToExpire));
2136  BOOST_CHECK(pm.isBoundToPeer(proofToExpire->getId()));
2137 
2138  // The conflicting proof has a longer expiration time but a lower sequence
2139  // number, so it is moved to the conflicting pool.
2140  BOOST_CHECK(!pm.registerProof(conflictingProof));
2141  BOOST_CHECK(pm.isInConflictingPool(conflictingProof->getId()));
2142 
2143  // Mine blocks until the MTP of the tip moves to the proof expiration
2144  for (int64_t i = 0; i < 6; i++) {
2145  SetMockTime(proofToExpire->getExpirationTime() + i);
2146  CreateAndProcessBlock({}, CScript());
2147  }
2149  WITH_LOCK(chainman.GetMutex(), return chainman.ActiveTip())
2150  ->GetMedianTimePast(),
2151  proofToExpire->getExpirationTime());
2152 
2153  pm.updatedBlockTip();
2154 
2155  // The now expired proof is removed
2156  BOOST_CHECK(!pm.exists(proofToExpire->getId()));
2157 
2158  // The conflicting proof has been pulled back to the valid pool
2159  BOOST_CHECK(pm.isBoundToPeer(conflictingProof->getId()));
2160 
2161  gArgs.ClearForcedArg("-avalancheconflictingproofcooldown");
2162 }
2163 
2164 BOOST_AUTO_TEST_CASE(peer_availability_score) {
2165  ChainstateManager &chainman = *Assert(m_node.chainman);
2167  Chainstate &active_chainstate = chainman.ActiveChainstate();
2168 
2169  const std::vector<std::tuple<uint32_t, uint32_t, double>> testCases = {
2170  // {step, tau, decay_factor}
2171  {10, 100, 1. - std::exp(-1. * 10 / 100)},
2172  // Current defaults
2176  };
2177 
2178  for (const auto &[step, tau, decayFactor] : testCases) {
2179  // Add a peer
2180  auto proof = buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE);
2181  BOOST_CHECK(pm.registerProof(proof));
2182  auto proofid = proof->getId();
2183 
2184  // Add some nodes for this peer
2185  const int numNodesPerPeer = 5;
2186  for (auto nodeid = 0; nodeid < numNodesPerPeer; nodeid++) {
2187  BOOST_CHECK(pm.addNode(nodeid, proofid));
2188  }
2189 
2190  auto getNodeAvailabilityScore = [&](double avgScore,
2191  NodeId nodeid) -> double {
2192  // Spread scores over a range of values such that their average is
2193  // the provided value.
2194  return (nodeid - numNodesPerPeer / 2) * 2 + avgScore;
2195  };
2196 
2197  auto getAvailabilityScore = [&]() {
2198  double score{0.0};
2199  pm.forPeer(proofid, [&](auto &peer) {
2200  score = peer.availabilityScore;
2201  return true;
2202  });
2203  return score;
2204  };
2205 
2206  double previousScore = getAvailabilityScore();
2207  BOOST_CHECK_SMALL(previousScore, 1e-6);
2208 
2209  // Check the statistics follow an exponential response for 1 to 10 tau
2210  for (size_t i = 1; i <= 10; i++) {
2211  for (uint32_t j = 0; j < tau; j += step) {
2212  // Nodes respond to polls > 50% of the time (positive score)
2213  pm.updateAvailabilityScores(decayFactor, [&](auto nodeid) {
2214  return getNodeAvailabilityScore(1.0, nodeid);
2215  });
2216 
2217  // Expect a monotonic rise
2218  double currentScore = getAvailabilityScore();
2219  BOOST_CHECK_GE(currentScore, previousScore);
2220  previousScore = currentScore;
2221  }
2222 
2223  // We expect (1 - e^-i) * numNodesPerPeer after i * tau. The
2224  // tolerance is expressed as a percentage, and we add a (large)
2225  // 0.1% margin to account for floating point errors.
2226  BOOST_CHECK_CLOSE(previousScore,
2227  -1 * std::expm1(-1. * i) * numNodesPerPeer,
2228  100.1 / tau);
2229  }
2230 
2231  // After 10 tau we should be very close to 100% (about 99.995%)
2232  BOOST_CHECK_CLOSE(previousScore, numNodesPerPeer, 0.01);
2233 
2234  // Make the proof invalid
2237  BOOST_CHECK(!pm.isBoundToPeer(proofid));
2238  BOOST_CHECK(!pm.exists(proofid));
2239 
2240  // Re-register the proof
2241  BOOST_CHECK(pm.registerProof(proof));
2242  pm.forPeer(proofid, [&](auto &peer) {
2243  int nodeCount = 0;
2244  pm.forEachNode(peer, [&](const auto &node) { nodeCount++; });
2245  BOOST_CHECK_EQUAL(nodeCount, numNodesPerPeer);
2246  return true;
2247  });
2248 
2249  // Peer score should have reset even though nodes are still connected
2250  previousScore = getAvailabilityScore();
2251  BOOST_CHECK_SMALL(previousScore, 1e-6);
2252 
2253  // Bring the score back up to where we were
2254  for (size_t i = 1; i <= 10; i++) {
2255  for (uint32_t j = 0; j < tau; j += step) {
2256  pm.updateAvailabilityScores(decayFactor, [&](auto nodeid) {
2257  return getNodeAvailabilityScore(1.0, nodeid);
2258  });
2259  }
2260  }
2261  previousScore = getAvailabilityScore();
2262  BOOST_CHECK_CLOSE(previousScore, numNodesPerPeer, 0.01);
2263 
2264  for (size_t i = 1; i <= 3; i++) {
2265  for (uint32_t j = 0; j < tau; j += step) {
2266  // Nodes only respond to polls 50% of the time (0 score)
2267  pm.updateAvailabilityScores(decayFactor, [&](auto nodeid) {
2268  return getNodeAvailabilityScore(0.0, nodeid);
2269  });
2270 
2271  // Expect a monotonic fall
2272  double currentScore = getAvailabilityScore();
2273  BOOST_CHECK_LE(currentScore, previousScore);
2274  previousScore = currentScore;
2275  }
2276 
2277  // There is a slight error in the expected value because we did not
2278  // start the decay at exactly 100%, but the 0.1% margin is at least
2279  // an order of magnitude larger than the expected error so it
2280  // doesn't matter.
2281  BOOST_CHECK_CLOSE(previousScore,
2282  (1. + std::expm1(-1. * i)) * numNodesPerPeer,
2283  100.1 / tau);
2284  }
2285 
2286  // After 3 more tau we should be under 5%
2287  BOOST_CHECK_LT(previousScore, .05 * numNodesPerPeer);
2288 
2289  for (size_t i = 1; i <= 100; i++) {
2290  // Nodes respond to polls < 50% of the time (negative score)
2291  pm.updateAvailabilityScores(decayFactor, [&](auto nodeid) {
2292  return getNodeAvailabilityScore(-10.0, nodeid);
2293  });
2294 
2295  // It's still a monotonic fall, and the score should turn negative.
2296  double currentScore = getAvailabilityScore();
2297  BOOST_CHECK_LE(currentScore, previousScore);
2298  BOOST_CHECK_LE(currentScore, 0.);
2299  previousScore = currentScore;
2300  }
2301  }
2302 }
2303 
2304 BOOST_AUTO_TEST_CASE(select_staking_reward_winner) {
2305  ChainstateManager &chainman = *Assert(m_node.chainman);
2307  Chainstate &active_chainstate = chainman.ActiveChainstate();
2308 
2309  auto buildProofWithAmountAndPayout = [&](Amount amount,
2310  const CScript &payoutScript) {
2311  const CKey key = CKey::MakeCompressedKey();
2312  COutPoint utxo = createUtxo(active_chainstate, key, amount);
2313  return buildProof(key, {{std::move(utxo), amount}},
2314  /*master=*/CKey::MakeCompressedKey(), /*sequence=*/1,
2315  /*height=*/100, /*is_coinbase=*/false,
2316  /*expirationTime=*/0, payoutScript);
2317  };
2318 
2319  std::vector<std::pair<ProofId, CScript>> winners;
2320  // Null pprev
2321  BOOST_CHECK(!pm.selectStakingRewardWinner(nullptr, winners));
2322 
2323  CBlockIndex prevBlock;
2324 
2325  auto now = GetTime<std::chrono::seconds>();
2326  SetMockTime(now);
2327  prevBlock.nTime = now.count();
2328 
2329  BlockHash prevHash{uint256::ONE};
2330  prevBlock.phashBlock = &prevHash;
2331  // No peer
2332  BOOST_CHECK(!pm.selectStakingRewardWinner(&prevBlock, winners));
2333 
2334  // Let's build a list of payout addresses, and register a proofs for each
2335  // address
2336  size_t numProofs = 8;
2337  std::vector<ProofRef> proofs;
2338  proofs.reserve(numProofs);
2339  for (size_t i = 0; i < numProofs; i++) {
2340  const CKey key = CKey::MakeCompressedKey();
2341  CScript payoutScript = GetScriptForRawPubKey(key.GetPubKey());
2342 
2343  auto proof =
2344  buildProofWithAmountAndPayout(PROOF_DUST_THRESHOLD, payoutScript);
2345  PeerId peerid = TestPeerManager::registerAndGetPeerId(pm, proof);
2346  BOOST_CHECK_NE(peerid, NO_PEER);
2347 
2348  // Finalize the proof
2349  BOOST_CHECK(pm.setFinalized(peerid));
2350 
2351  proofs.emplace_back(std::move(proof));
2352  }
2353 
2354  // Make sure the proofs have been registered before the prev block was found
2355  // and before 6x the peer replacement cooldown.
2356  now += 6 * avalanche::Peer::DANGLING_TIMEOUT + 1s;
2357  SetMockTime(now);
2358  prevBlock.nTime = now.count();
2359 
2360  // At this stage we have a set of peers out of which none has any node
2361  // attached, so they're all considered flaky. Note that we have no remote
2362  // proofs status yet.
2363  BOOST_CHECK(pm.selectStakingRewardWinner(&prevBlock, winners));
2364  BOOST_CHECK_LE(winners.size(), numProofs);
2365 
2366  // Let's add a node for each peer
2367  for (size_t i = 0; i < numProofs; i++) {
2368  BOOST_CHECK(TestPeerManager::isFlaky(pm, proofs[i]->getId()));
2369  BOOST_CHECK(pm.selectStakingRewardWinner(&prevBlock, winners));
2370  BOOST_CHECK_LE(winners.size(), numProofs);
2371 
2372  BOOST_CHECK(pm.addNode(NodeId(i), proofs[i]->getId()));
2373 
2374  BOOST_CHECK(!TestPeerManager::isFlaky(pm, proofs[i]->getId()));
2375  BOOST_CHECK(pm.selectStakingRewardWinner(&prevBlock, winners));
2376  BOOST_CHECK_LE(winners.size(), numProofs - i);
2377  }
2378 
2379  // Now we have a single winner
2380  BOOST_CHECK(pm.selectStakingRewardWinner(&prevBlock, winners));
2381  BOOST_CHECK_LE(winners.size(), 1);
2382 
2383  // All proofs have the same amount, so the same probability to get picked.
2384  // Let's compute how many loop iterations we need to have a low false
2385  // negative rate when checking for this. Target false positive rate is
2386  // 10ppm (aka 1/100000).
2387  const size_t loop_iters =
2388  size_t(-1.0 * std::log(100000.0) /
2389  std::log((double(numProofs) - 1) / numProofs)) +
2390  1;
2391  BOOST_CHECK_GT(loop_iters, numProofs);
2392  std::unordered_map<std::string, size_t> winningCounts;
2393  for (size_t i = 0; i < loop_iters; i++) {
2394  BlockHash randomHash = BlockHash(GetRandHash());
2395  prevBlock.phashBlock = &randomHash;
2396  BOOST_CHECK(pm.selectStakingRewardWinner(&prevBlock, winners));
2397  winningCounts[FormatScript(winners[0].second)]++;
2398  }
2399  BOOST_CHECK_EQUAL(winningCounts.size(), numProofs);
2400 
2401  prevBlock.phashBlock = &prevHash;
2402 
2403  // Ensure all nodes have all the proofs
2404  for (size_t i = 0; i < numProofs; i++) {
2405  for (size_t j = 0; j < numProofs; j++) {
2406  BOOST_CHECK(
2407  pm.saveRemoteProof(proofs[j]->getId(), NodeId(i), true));
2408  }
2409  }
2410 
2411  // Make all the proofs flaky. This loop needs to be updated if the threshold
2412  // or the number of proofs change, so assert the test precondition.
2413  BOOST_CHECK_GT(3. / numProofs, 0.3);
2414  for (size_t i = 0; i < numProofs; i++) {
2415  const NodeId nodeid = NodeId(i);
2416 
2418  proofs[(i - 1 + numProofs) % numProofs]->getId(), nodeid, false));
2420  proofs[(i + numProofs) % numProofs]->getId(), nodeid, false));
2422  proofs[(i + 1 + numProofs) % numProofs]->getId(), nodeid, false));
2423  }
2424 
2425  // Now all the proofs are flaky
2426  BOOST_CHECK(pm.selectStakingRewardWinner(&prevBlock, winners));
2427  for (const auto &proof : proofs) {
2428  BOOST_CHECK(TestPeerManager::isFlaky(pm, proof->getId()));
2429  }
2430  BOOST_CHECK_EQUAL(winners.size(), numProofs);
2431 
2432  // Revert flakyness for all proofs
2433  for (const auto &proof : proofs) {
2434  for (NodeId nodeid = 0; nodeid < NodeId(numProofs); nodeid++) {
2435  BOOST_CHECK(pm.saveRemoteProof(proof->getId(), nodeid, true));
2436  }
2437  }
2438 
2439  BOOST_CHECK(pm.selectStakingRewardWinner(&prevBlock, winners));
2440  BOOST_CHECK_EQUAL(winners.size(), 1);
2441 
2442  // Increase the list from 1 to 4 winners by making them flaky
2443  for (size_t numWinner = 1; numWinner < 4; numWinner++) {
2444  // Who is the last possible winner ?
2445  CScript lastWinner = winners[numWinner - 1].second;
2446 
2447  // Make the last winner flaky, the other proofs untouched
2448  ProofId winnerProofId = ProofId(uint256::ZERO);
2449  for (const auto &proof : proofs) {
2450  if (proof->getPayoutScript() == lastWinner) {
2451  winnerProofId = proof->getId();
2452  break;
2453  }
2454  }
2455  BOOST_CHECK_NE(winnerProofId, ProofId(uint256::ZERO));
2456 
2457  for (NodeId nodeid = 0; nodeid < NodeId(numProofs); nodeid++) {
2458  BOOST_CHECK(pm.saveRemoteProof(winnerProofId, nodeid, false));
2459  }
2460  BOOST_CHECK(TestPeerManager::isFlaky(pm, winnerProofId));
2461 
2462  // There should be now exactly numWinner + 1 winners
2463  BOOST_CHECK(pm.selectStakingRewardWinner(&prevBlock, winners));
2464  BOOST_CHECK_EQUAL(winners.size(), numWinner + 1);
2465  }
2466 
2467  // One more time and the nodes will be missing too many proofs, so they are
2468  // no longer considered for flakyness evaluation and we're back to a single
2469  // winner.
2470  CScript lastWinner = winners[3].second;
2471 
2472  ProofId winnerProofId = ProofId(uint256::ZERO);
2473  for (const auto &proof : proofs) {
2474  if (proof->getPayoutScript() == lastWinner) {
2475  winnerProofId = proof->getId();
2476  break;
2477  }
2478  }
2479  BOOST_CHECK_NE(winnerProofId, ProofId(uint256::ZERO));
2480 
2481  for (NodeId nodeid = 0; nodeid < NodeId(numProofs); nodeid++) {
2482  BOOST_CHECK(pm.saveRemoteProof(winnerProofId, nodeid, false));
2483  }
2484 
2485  // We're back to exactly 1 winner
2486  BOOST_CHECK(pm.selectStakingRewardWinner(&prevBlock, winners));
2487  BOOST_CHECK_EQUAL(winners.size(), 1);
2488 
2489  // Remove all proofs
2490  for (auto &proof : proofs) {
2493  }
2494  // No more winner
2495  prevBlock.phashBlock = &prevHash;
2496  BOOST_CHECK(!pm.selectStakingRewardWinner(&prevBlock, winners));
2497 
2498  {
2499  // Add back a single proof
2500  const CKey key = CKey::MakeCompressedKey();
2501  CScript payoutScript = GetScriptForRawPubKey(key.GetPubKey());
2502 
2503  auto proof =
2504  buildProofWithAmountAndPayout(PROOF_DUST_THRESHOLD, payoutScript);
2505  PeerId peerid = TestPeerManager::registerAndGetPeerId(pm, proof);
2506  BOOST_CHECK_NE(peerid, NO_PEER);
2507 
2508  // The single proof should always be selected, but:
2509  // 1. The proof is not finalized, and has been registered after the last
2510  // block was mined.
2511  BOOST_CHECK(!pm.selectStakingRewardWinner(&prevBlock, winners));
2512 
2513  // 2. The proof has has been registered after the last block was mined.
2514  BOOST_CHECK(pm.setFinalized(peerid));
2515  BOOST_CHECK(!pm.selectStakingRewardWinner(&prevBlock, winners));
2516 
2517  // 3. The proof has been registered 60min from the previous block time,
2518  // but the previous block time is in the future.
2519  now += 50min + 1s;
2520  SetMockTime(now);
2521  prevBlock.nTime = (now + 10min).count();
2522  BOOST_CHECK(!pm.selectStakingRewardWinner(&prevBlock, winners));
2523 
2524  // 4. The proof has been registered 60min from now, but only 50min from
2525  // the previous block time.
2526  now += 10min;
2527  SetMockTime(now);
2528  prevBlock.nTime = (now - 10min).count();
2529  BOOST_CHECK(!pm.selectStakingRewardWinner(&prevBlock, winners));
2530 
2531  // 5. Now the proof has it all
2532  prevBlock.nTime = now.count();
2533  BOOST_CHECK(pm.selectStakingRewardWinner(&prevBlock, winners));
2534  // With a single proof, it's easy to determine the winner
2535  BOOST_CHECK_EQUAL(FormatScript(winners[0].second),
2536  FormatScript(payoutScript));
2537 
2538  // Remove the proof
2541  }
2542 
2543  {
2544  BOOST_CHECK_EQUAL(TestPeerManager::getPeerCount(pm), 0);
2545 
2546  proofs.clear();
2547  for (size_t i = 0; i < 4; i++) {
2548  // Add 4 proofs, registered at a 30 minutes interval
2549  SetMockTime(now + i * 30min);
2550 
2551  const CKey key = CKey::MakeCompressedKey();
2552  CScript payoutScript = GetScriptForRawPubKey(key.GetPubKey());
2553 
2554  auto proof = buildProofWithAmountAndPayout(PROOF_DUST_THRESHOLD,
2555  payoutScript);
2556  PeerId peerid = TestPeerManager::registerAndGetPeerId(pm, proof);
2557  BOOST_CHECK_NE(peerid, NO_PEER);
2558  BOOST_CHECK(pm.forPeer(proof->getId(), [&](const Peer &peer) {
2559  return peer.registration_time == now + i * 30min;
2560  }));
2561 
2562  BOOST_CHECK(pm.addNode(NodeId(i), proof->getId()));
2563 
2564  BOOST_CHECK(pm.setFinalized(peerid));
2565 
2566  proofs.push_back(proof);
2567  }
2568 
2569  // No proof has been registered before the previous block time
2570  SetMockTime(now);
2571  prevBlock.nTime = now.count();
2572  BOOST_CHECK(!pm.selectStakingRewardWinner(&prevBlock, winners));
2573 
2574  // 1 proof has been registered > 30min from the previous block time, but
2575  // none > 60 minutes from the previous block time
2576  // => we have no winner.
2577  now += 30min + 1s;
2578  SetMockTime(now);
2579  prevBlock.nTime = now.count();
2580  BOOST_CHECK(!pm.selectStakingRewardWinner(&prevBlock, winners));
2581 
2582  auto checkRegistrationTime =
2583  [&](const std::pair<ProofId, CScript> &winner) {
2584  pm.forEachPeer([&](const Peer &peer) {
2585  if (peer.proof->getPayoutScript() == winner.second) {
2586  BOOST_CHECK_LT(peer.registration_time.count(),
2587  (now - 60min).count());
2588  }
2589  return true;
2590  });
2591  };
2592 
2593  // 1 proof has been registered > 60min but < 90min from the previous
2594  // block time and 1 more has been registered > 30 minutes
2595  // => we have a winner and one acceptable substitute.
2596  now += 30min;
2597  SetMockTime(now);
2598  prevBlock.nTime = now.count();
2599  BOOST_CHECK(pm.selectStakingRewardWinner(&prevBlock, winners));
2600  BOOST_CHECK_EQUAL(winners.size(), 2);
2601  checkRegistrationTime(winners[0]);
2602 
2603  // 1 proof has been registered > 60min but < 90min from the
2604  // previous block time, 1 has been registered > 90 minutes and 1 more
2605  // has been registered > 30 minutes
2606  // => we have 1 winner and up to 2 acceptable substitutes.
2607  now += 30min;
2608  SetMockTime(now);
2609  prevBlock.nTime = now.count();
2610  BOOST_CHECK(pm.selectStakingRewardWinner(&prevBlock, winners));
2611  BOOST_CHECK_LE(winners.size(), 3);
2612  checkRegistrationTime(winners[0]);
2613 
2614  // 1 proofs has been registered > 60min but < 90min from the
2615  // previous block time, 2 has been registered > 90 minutes and 1 more
2616  // has been registered > 30 minutes
2617  // => we have 1 winner, and up to 2 substitutes.
2618  now += 30min;
2619  SetMockTime(now);
2620  prevBlock.nTime = now.count();
2621  BOOST_CHECK(pm.selectStakingRewardWinner(&prevBlock, winners));
2622  BOOST_CHECK_LE(winners.size(), 3);
2623  checkRegistrationTime(winners[0]);
2624 
2625  // 1 proof has been registered > 60min but < 90min from the
2626  // previous block time and 3 more has been registered > 90 minutes
2627  // => we have 1 winner, and up to 1 substitute.
2628  now += 30min;
2629  SetMockTime(now);
2630  prevBlock.nTime = now.count();
2631  BOOST_CHECK(pm.selectStakingRewardWinner(&prevBlock, winners));
2632  BOOST_CHECK_LE(winners.size(), 2);
2633  checkRegistrationTime(winners[0]);
2634 
2635  // All proofs has been registered > 90min from the previous block time
2636  // => we have 1 winner, and no substitute.
2637  now += 30min;
2638  SetMockTime(now);
2639  prevBlock.nTime = now.count();
2640  BOOST_CHECK(pm.selectStakingRewardWinner(&prevBlock, winners));
2641  BOOST_CHECK_EQUAL(winners.size(), 1);
2642  checkRegistrationTime(winners[0]);
2643  }
2644 }
2645 
2646 BOOST_AUTO_TEST_CASE(remote_proof) {
2647  ChainstateManager &chainman = *Assert(m_node.chainman);
2649 
2650  auto mockTime = GetTime<std::chrono::seconds>();
2651  SetMockTime(mockTime);
2652 
2657 
2658  auto checkRemoteProof =
2659  [&](const ProofId &proofid, const NodeId nodeid,
2660  const bool expectedPresent,
2661  const std::chrono::seconds &expectedlastUpdate) {
2662  auto remoteProof =
2663  TestPeerManager::getRemoteProof(pm, proofid, nodeid);
2664  BOOST_CHECK(remoteProof.has_value());
2665  BOOST_CHECK_EQUAL(remoteProof->proofid, proofid);
2666  BOOST_CHECK_EQUAL(remoteProof->nodeid, nodeid);
2667  BOOST_CHECK_EQUAL(remoteProof->present, expectedPresent);
2668  BOOST_CHECK_EQUAL(remoteProof->lastUpdate.count(),
2669  expectedlastUpdate.count());
2670  };
2671 
2672  checkRemoteProof(ProofId(uint256::ZERO), 0, true, mockTime);
2673  checkRemoteProof(ProofId(uint256::ONE), 0, false, mockTime);
2674  checkRemoteProof(ProofId(uint256::ZERO), 1, true, mockTime);
2675  checkRemoteProof(ProofId(uint256::ONE), 1, false, mockTime);
2676 
2677  mockTime += 1s;
2678  SetMockTime(mockTime);
2679 
2680  // Reverse the state
2685 
2686  checkRemoteProof(ProofId(uint256::ZERO), 0, false, mockTime);
2687  checkRemoteProof(ProofId(uint256::ONE), 0, true, mockTime);
2688  checkRemoteProof(ProofId(uint256::ZERO), 1, false, mockTime);
2689  checkRemoteProof(ProofId(uint256::ONE), 1, true, mockTime);
2690 
2691  Chainstate &active_chainstate = chainman.ActiveChainstate();
2692 
2693  // Actually register the nodes
2694  auto proof0 = buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE);
2695  BOOST_CHECK(pm.registerProof(proof0));
2696  BOOST_CHECK(pm.addNode(0, proof0->getId()));
2697  auto proof1 = buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE);
2698  BOOST_CHECK(pm.registerProof(proof1));
2699  BOOST_CHECK(pm.addNode(1, proof1->getId()));
2700 
2701  // Removing the node removes all the associated remote proofs
2702  BOOST_CHECK(pm.removeNode(0));
2703  BOOST_CHECK(
2704  !TestPeerManager::getRemoteProof(pm, ProofId(uint256::ZERO), 0));
2705  BOOST_CHECK(!TestPeerManager::getRemoteProof(pm, ProofId(uint256::ONE), 0));
2706  // Other nodes are left untouched
2707  checkRemoteProof(ProofId(uint256::ZERO), 1, false, mockTime);
2708  checkRemoteProof(ProofId(uint256::ONE), 1, true, mockTime);
2709 
2710  BOOST_CHECK(pm.removeNode(1));
2711  BOOST_CHECK(
2712  !TestPeerManager::getRemoteProof(pm, ProofId(uint256::ZERO), 0));
2713  BOOST_CHECK(!TestPeerManager::getRemoteProof(pm, ProofId(uint256::ONE), 0));
2714  BOOST_CHECK(
2715  !TestPeerManager::getRemoteProof(pm, ProofId(uint256::ZERO), 1));
2716  BOOST_CHECK(!TestPeerManager::getRemoteProof(pm, ProofId(uint256::ONE), 1));
2717 
2718  for (size_t i = 0; i < avalanche::PeerManager::MAX_REMOTE_PROOFS; i++) {
2719  mockTime += 1s;
2720  SetMockTime(mockTime);
2721 
2722  const ProofId proofid{uint256(i)};
2723 
2724  BOOST_CHECK(pm.saveRemoteProof(proofid, 0, true));
2725  checkRemoteProof(proofid, 0, true, mockTime);
2726  }
2727 
2728  // The last updated proof is still there
2729  checkRemoteProof(ProofId(uint256::ZERO), 0, true,
2730  mockTime -
2732 
2733  // If we add one more it gets evicted
2734  mockTime += 1s;
2735  SetMockTime(mockTime);
2736 
2737  ProofId proofid{
2739 
2740  BOOST_CHECK(pm.saveRemoteProof(proofid, 0, true));
2741  checkRemoteProof(proofid, 0, true, mockTime);
2742  // Proof id 0 has been evicted
2743  BOOST_CHECK(
2744  !TestPeerManager::getRemoteProof(pm, ProofId(uint256::ZERO), 0));
2745 
2746  // Proof id 1 is still there
2747  BOOST_CHECK(TestPeerManager::getRemoteProof(pm, ProofId(uint256::ONE), 0));
2748 
2749  // Add MAX_REMOTE_PROOFS / 2 + 1 proofs to our node to bump the limit
2750  // Note that we already have proofs from the beginning of the test.
2751  std::vector<ProofRef> proofs;
2752  for (size_t i = 0; i < avalanche::PeerManager::MAX_REMOTE_PROOFS / 2 - 1;
2753  i++) {
2754  auto proof = buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE);
2755  BOOST_CHECK(pm.registerProof(proof));
2756  proofs.push_back(proof);
2757  }
2758  BOOST_CHECK_EQUAL(TestPeerManager::getPeerCount(pm),
2760 
2761  // We can now add one more without eviction
2762  mockTime += 1s;
2763  SetMockTime(mockTime);
2764 
2765  proofid = ProofId{
2767 
2768  BOOST_CHECK(pm.saveRemoteProof(proofid, 0, true));
2769  checkRemoteProof(proofid, 0, true, mockTime);
2770  // Proof id 1 is still there
2771  BOOST_CHECK(TestPeerManager::getRemoteProof(pm, ProofId(uint256::ONE), 0));
2772 
2773  // Shrink our proofs to MAX_REMOTE_PROOFS / 2 - 1
2778 
2779  BOOST_CHECK_EQUAL(TestPeerManager::getPeerCount(pm),
2781 
2782  // Upon update the first proof got evicted
2783  proofid = ProofId{
2785  BOOST_CHECK(pm.saveRemoteProof(proofid, 0, true));
2786  // Proof id 1 is evicted
2787  BOOST_CHECK(!TestPeerManager::getRemoteProof(pm, ProofId(uint256::ONE), 0));
2788  // So is proof id 2
2789  BOOST_CHECK(!TestPeerManager::getRemoteProof(pm, ProofId(uint256(2)), 0));
2790  // But proof id 3 is still here
2791  BOOST_CHECK(TestPeerManager::getRemoteProof(pm, ProofId(uint256(3)), 0));
2792 }
2793 
2794 BOOST_AUTO_TEST_CASE(get_remote_status) {
2795  ChainstateManager &chainman = *Assert(m_node.chainman);
2797  Chainstate &active_chainstate = chainman.ActiveChainstate();
2798 
2799  auto mockTime = GetTime<std::chrono::seconds>();
2800  SetMockTime(mockTime);
2801 
2802  // No remote proof yet
2803  BOOST_CHECK(
2804  !TestPeerManager::getRemotePresenceStatus(pm, ProofId(uint256::ZERO))
2805  .has_value());
2806 
2807  // 6/12 (50%) of the stakes
2808  for (NodeId nodeid = 0; nodeid < 12; nodeid++) {
2809  auto proof = buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE);
2810  BOOST_CHECK(pm.registerProof(proof));
2811  BOOST_CHECK(pm.addNode(nodeid, proof->getId()));
2813  nodeid % 2 == 0));
2814  }
2815 
2816  BOOST_CHECK(
2817  !TestPeerManager::getRemotePresenceStatus(pm, ProofId(uint256::ZERO))
2818  .has_value());
2819 
2820  // 7/12 (~58%) of the stakes
2821  for (NodeId nodeid = 0; nodeid < 5; nodeid++) {
2822  BOOST_CHECK(pm.saveRemoteProof(ProofId(uint256::ZERO), nodeid, false));
2823  }
2824  for (NodeId nodeid = 5; nodeid < 12; nodeid++) {
2825  BOOST_CHECK(pm.saveRemoteProof(ProofId(uint256::ZERO), nodeid, true));
2826  }
2827  BOOST_CHECK(
2828  TestPeerManager::getRemotePresenceStatus(pm, ProofId(uint256::ZERO))
2829  .value());
2830 
2831  // Add our local proof so we have 7/13 (~54% < 55%)
2832  auto localProof =
2833  buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE);
2834  TestPeerManager::setLocalProof(pm, localProof);
2835  BOOST_CHECK(pm.registerProof(localProof));
2836  BOOST_CHECK(
2837  !TestPeerManager::getRemotePresenceStatus(pm, ProofId(uint256::ZERO))
2838  .has_value());
2839 
2840  // Remove the local proof to revert back to 7/12 (~58%)
2841  pm.rejectProof(localProof->getId());
2842  TestPeerManager::setLocalProof(pm, ProofRef());
2843  BOOST_CHECK(
2844  TestPeerManager::getRemotePresenceStatus(pm, ProofId(uint256::ZERO))
2845  .value());
2846 
2847  // 5/12 (~42%) of the stakes
2848  for (NodeId nodeid = 0; nodeid < 5; nodeid++) {
2849  BOOST_CHECK(pm.saveRemoteProof(ProofId(uint256::ZERO), nodeid, true));
2850  }
2851  for (NodeId nodeid = 5; nodeid < 12; nodeid++) {
2852  BOOST_CHECK(pm.saveRemoteProof(ProofId(uint256::ZERO), nodeid, false));
2853  }
2854  BOOST_CHECK(
2855  !TestPeerManager::getRemotePresenceStatus(pm, ProofId(uint256::ZERO))
2856  .value());
2857 
2858  // Most nodes agree but not enough of the stakes
2859  auto bigProof =
2860  buildRandomProof(active_chainstate, 100 * MIN_VALID_PROOF_SCORE);
2861  BOOST_CHECK(pm.registerProof(bigProof));
2862  // Update the node's proof
2863  BOOST_CHECK(pm.addNode(0, bigProof->getId()));
2864 
2865  // 7/12 (~58%) of the remotes, but < 10% of the stakes => absent
2866  for (NodeId nodeid = 0; nodeid < 5; nodeid++) {
2867  BOOST_CHECK(pm.saveRemoteProof(ProofId(uint256::ZERO), nodeid, false));
2868  }
2869  for (NodeId nodeid = 5; nodeid < 12; nodeid++) {
2870  BOOST_CHECK(pm.saveRemoteProof(ProofId(uint256::ZERO), nodeid, true));
2871  }
2872  BOOST_CHECK(
2873  !TestPeerManager::getRemotePresenceStatus(pm, ProofId(uint256::ZERO))
2874  .value());
2875 
2876  // 5/12 (42%) of the remotes, but > 90% of the stakes => present
2877  for (NodeId nodeid = 0; nodeid < 5; nodeid++) {
2878  BOOST_CHECK(pm.saveRemoteProof(ProofId(uint256::ZERO), nodeid, true));
2879  }
2880  for (NodeId nodeid = 5; nodeid < 12; nodeid++) {
2881  BOOST_CHECK(pm.saveRemoteProof(ProofId(uint256::ZERO), nodeid, false));
2882  }
2883  BOOST_CHECK(
2884  TestPeerManager::getRemotePresenceStatus(pm, ProofId(uint256::ZERO))
2885  .value());
2886 
2887  TestPeerManager::clearPeers(pm);
2888 
2889  // Peer 1 has 1 node (id 0)
2890  auto proof1 = buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE);
2891  BOOST_CHECK(pm.registerProof(proof1));
2892  BOOST_CHECK(pm.addNode(0, proof1->getId()));
2893 
2894  // Peer 2 has 5 nodes (ids 1 to 5)
2895  auto proof2 = buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE);
2896  BOOST_CHECK(pm.registerProof(proof2));
2897  for (NodeId nodeid = 1; nodeid < 6; nodeid++) {
2898  BOOST_CHECK(pm.addNode(nodeid, proof2->getId()));
2899  }
2900 
2901  // Node 0 is missing proofid 0, nodes 1 to 5 have it
2903  for (NodeId nodeid = 1; nodeid < 6; nodeid++) {
2904  BOOST_CHECK(pm.saveRemoteProof(ProofId(uint256::ZERO), nodeid, true));
2905  }
2906 
2907  // At this stage we have 5/6 nodes with the proof, but since all the nodes
2908  // advertising the proof are from the same peer, we only 1/2 peers, i.e. 50%
2909  // of the stakes.
2910  BOOST_CHECK(
2911  !TestPeerManager::getRemotePresenceStatus(pm, ProofId(uint256::ZERO))
2912  .has_value());
2913 }
2914 
2915 BOOST_AUTO_TEST_CASE(dangling_with_remotes) {
2916  ChainstateManager &chainman = *Assert(m_node.chainman);
2918  Chainstate &active_chainstate = chainman.ActiveChainstate();
2919 
2920  auto mockTime = GetTime<std::chrono::seconds>();
2921  SetMockTime(mockTime);
2922 
2923  // Add a few proofs with no node attached
2924  std::vector<ProofRef> proofs;
2925  for (size_t i = 0; i < 10; i++) {
2926  auto proof = buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE);
2927  BOOST_CHECK(pm.registerProof(proof));
2928  proofs.push_back(proof);
2929  }
2930 
2931  // The proofs are recent enough, the cleanup won't make them dangling
2932  TestPeerManager::cleanupDanglingProofs(pm);
2933  for (const auto &proof : proofs) {
2934  BOOST_CHECK(pm.isBoundToPeer(proof->getId()));
2935  BOOST_CHECK(!pm.isDangling(proof->getId()));
2936  }
2937 
2938  // Elapse enough time so we get the proofs dangling
2939  mockTime += avalanche::Peer::DANGLING_TIMEOUT + 1s;
2940  SetMockTime(mockTime);
2941 
2942  // The proofs are now dangling
2943  TestPeerManager::cleanupDanglingProofs(pm);
2944  for (const auto &proof : proofs) {
2945  BOOST_CHECK(!pm.isBoundToPeer(proof->getId()));
2946  BOOST_CHECK(pm.isDangling(proof->getId()));
2947  }
2948 
2949  // Add some remotes having this proof
2950  for (NodeId nodeid = 0; nodeid < 10; nodeid++) {
2951  auto localProof =
2952  buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE);
2953  BOOST_CHECK(pm.registerProof(localProof));
2954  BOOST_CHECK(pm.addNode(nodeid, localProof->getId()));
2955 
2956  for (const auto &proof : proofs) {
2957  BOOST_CHECK(pm.saveRemoteProof(proof->getId(), nodeid, true));
2958  }
2959  }
2960 
2961  // The proofs are all present according to the remote status
2962  for (const auto &proof : proofs) {
2963  BOOST_CHECK(TestPeerManager::getRemotePresenceStatus(pm, proof->getId())
2964  .value());
2965  }
2966 
2967  // The proofs should be added back as a peer
2968  std::unordered_set<ProofRef, SaltedProofHasher> registeredProofs;
2969  TestPeerManager::cleanupDanglingProofs(pm, registeredProofs);
2970  for (const auto &proof : proofs) {
2971  BOOST_CHECK(pm.isBoundToPeer(proof->getId()));
2972  BOOST_CHECK(!pm.isDangling(proof->getId()));
2973  BOOST_CHECK_EQUAL(registeredProofs.count(proof), 1);
2974  }
2975  BOOST_CHECK_EQUAL(proofs.size(), registeredProofs.size());
2976 
2977  // Remove the proofs from the remotes
2978  for (NodeId nodeid = 0; nodeid < 10; nodeid++) {
2979  for (const auto &proof : proofs) {
2980  BOOST_CHECK(pm.saveRemoteProof(proof->getId(), nodeid, false));
2981  }
2982  }
2983 
2984  // The proofs are now all absent according to the remotes
2985  for (const auto &proof : proofs) {
2986  BOOST_CHECK(
2987  !TestPeerManager::getRemotePresenceStatus(pm, proof->getId())
2988  .value());
2989  }
2990 
2991  // The proofs are not dangling yet as they have been registered recently
2992  TestPeerManager::cleanupDanglingProofs(pm, registeredProofs);
2993  BOOST_CHECK(registeredProofs.empty());
2994  for (const auto &proof : proofs) {
2995  BOOST_CHECK(pm.isBoundToPeer(proof->getId()));
2996  BOOST_CHECK(!pm.isDangling(proof->getId()));
2997  }
2998 
2999  // Wait some time then run the cleanup again, the proofs will be dangling
3000  mockTime += avalanche::Peer::DANGLING_TIMEOUT + 1s;
3001  SetMockTime(mockTime);
3002 
3003  TestPeerManager::cleanupDanglingProofs(pm, registeredProofs);
3004  BOOST_CHECK(registeredProofs.empty());
3005  for (const auto &proof : proofs) {
3006  BOOST_CHECK(!pm.isBoundToPeer(proof->getId()));
3007  BOOST_CHECK(pm.isDangling(proof->getId()));
3008  }
3009 
3010  // Pull them back one more time
3011  for (NodeId nodeid = 0; nodeid < 10; nodeid++) {
3012  for (const auto &proof : proofs) {
3013  BOOST_CHECK(pm.saveRemoteProof(proof->getId(), nodeid, true));
3014  }
3015  }
3016 
3017  TestPeerManager::cleanupDanglingProofs(pm, registeredProofs);
3018  for (const auto &proof : proofs) {
3019  BOOST_CHECK(pm.isBoundToPeer(proof->getId()));
3020  BOOST_CHECK(!pm.isDangling(proof->getId()));
3021  BOOST_CHECK_EQUAL(registeredProofs.count(proof), 1);
3022  }
3023  BOOST_CHECK_EQUAL(proofs.size(), registeredProofs.size());
3024 }
3025 
3026 BOOST_AUTO_TEST_CASE(avapeers_dump) {
3027  ChainstateManager &chainman = *Assert(m_node.chainman);
3029  Chainstate &active_chainstate = chainman.ActiveChainstate();
3030 
3031  auto mockTime = GetTime<std::chrono::seconds>();
3032  SetMockTime(mockTime);
3033 
3034  std::vector<ProofRef> proofs;
3035  for (size_t i = 0; i < 10; i++) {
3036  SetMockTime(mockTime + std::chrono::seconds{i});
3037 
3038  auto proof = buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE);
3039  // Registration time is mockTime + i
3040  BOOST_CHECK(pm.registerProof(proof));
3041 
3042  auto peerid = TestPeerManager::getPeerIdForProofId(pm, proof->getId());
3043 
3044  // Next conflict time is mockTime + 100 + i
3046  peerid, mockTime + std::chrono::seconds{100 + i}));
3047 
3048  // The 5 first proofs are finalized
3049  if (i < 5) {
3050  BOOST_CHECK(pm.setFinalized(peerid));
3051  }
3052 
3053  proofs.push_back(proof);
3054  }
3055 
3056  BOOST_CHECK_EQUAL(TestPeerManager::getPeerCount(pm), 10);
3057 
3058  const fs::path testDumpPath = "test_avapeers_dump.dat";
3059  BOOST_CHECK(pm.dumpPeersToFile(testDumpPath));
3060 
3061  TestPeerManager::clearPeers(pm);
3062 
3063  std::unordered_set<ProofRef, SaltedProofHasher> registeredProofs;
3064  BOOST_CHECK(pm.loadPeersFromFile(testDumpPath, registeredProofs));
3065  BOOST_CHECK_EQUAL(registeredProofs.size(), 10);
3066 
3067  auto findProofIndex = [&proofs](const ProofId &proofid) {
3068  for (size_t i = 0; i < proofs.size(); i++) {
3069  if (proofs[i]->getId() == proofid) {
3070  return i;
3071  }
3072  }
3073 
3074  // ProofId not found
3075  BOOST_CHECK(false);
3076  return size_t{0};
3077  };
3078 
3079  for (const auto &proof : registeredProofs) {
3080  const ProofId &proofid = proof->getId();
3081  size_t i = findProofIndex(proofid);
3082  BOOST_CHECK(pm.forPeer(proofid, [&](auto &peer) {
3083  BOOST_CHECK_EQUAL(peer.hasFinalized, i < 5);
3084  BOOST_CHECK_EQUAL(peer.registration_time.count(),
3085  (mockTime + std::chrono::seconds{i}).count());
3087  peer.nextPossibleConflictTime.count(),
3088  (mockTime + std::chrono::seconds{100 + i}).count());
3089  return true;
3090  }));
3091  }
3092 
3093  // No peer: create an empty file but generate no error
3094  TestPeerManager::clearPeers(pm);
3095  BOOST_CHECK(pm.dumpPeersToFile("test_empty_avapeers.dat"));
3096  // We can also load an empty file
3097  BOOST_CHECK(
3098  pm.loadPeersFromFile("test_empty_avapeers.dat", registeredProofs));
3099  BOOST_CHECK(registeredProofs.empty());
3100  BOOST_CHECK_EQUAL(TestPeerManager::getPeerCount(pm), 0);
3101 
3102  // If the file exists, it is overrwritten
3103  BOOST_CHECK(pm.dumpPeersToFile("test_empty_avapeers.dat"));
3104 
3105  // It fails to load if the file does not exist and the registeredProofs is
3106  // cleared
3107  registeredProofs.insert(proofs[0]);
3108  BOOST_CHECK(!registeredProofs.empty());
3109  BOOST_CHECK(!pm.loadPeersFromFile("I_dont_exist.dat", registeredProofs));
3110  BOOST_CHECK(registeredProofs.empty());
3111 
3112  {
3113  // Change the version
3114  FILE *f = fsbridge::fopen("test_bad_version_avapeers.dat", "wb");
3115  BOOST_CHECK(f);
3116  CAutoFile file(f, SER_DISK, CLIENT_VERSION);
3117  file << static_cast<uint64_t>(-1); // Version
3118  file << uint64_t{0}; // Number of peers
3119  BOOST_CHECK(FileCommit(file.Get()));
3120  file.fclose();
3121 
3122  // Check loading fails and the registeredProofs is cleared
3123  registeredProofs.insert(proofs[0]);
3124  BOOST_CHECK(!registeredProofs.empty());
3125  BOOST_CHECK(!pm.loadPeersFromFile("test_bad_version_avapeers.dat",
3126  registeredProofs));
3127  BOOST_CHECK(registeredProofs.empty());
3128  }
3129 
3130  {
3131  // Wrong format, will cause a deserialization error
3132  FILE *f = fsbridge::fopen("test_ill_formed_avapeers.dat", "wb");
3133  BOOST_CHECK(f);
3134  const uint64_t now = GetTime();
3135  CAutoFile file(f, SER_DISK, CLIENT_VERSION);
3136  file << static_cast<uint64_t>(1); // Version
3137  file << uint64_t{2}; // Number of peers
3138  // Single peer content!
3139  file << proofs[0];
3140  file << true;
3141  file << now;
3142  file << now + 100;
3143 
3144  BOOST_CHECK(FileCommit(file.Get()));
3145  file.fclose();
3146 
3147  // Check loading fails and the registeredProofs is fed with our single
3148  // peer
3149  BOOST_CHECK(registeredProofs.empty());
3150  BOOST_CHECK(!pm.loadPeersFromFile("test_ill_formed_avapeers.dat",
3151  registeredProofs));
3152  BOOST_CHECK_EQUAL(registeredProofs.size(), 1);
3153  BOOST_CHECK_EQUAL((*registeredProofs.begin())->getId(),
3154  proofs[0]->getId());
3155  }
3156 }
3157 
3158 BOOST_AUTO_TEST_SUITE_END()
ArgsManager gArgs
Definition: args.cpp:38
static constexpr PeerId NO_PEER
Definition: node.h:16
uint32_t PeerId
Definition: node.h:15
#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
The block chain is a tree shaped structure starting with the genesis block at the root,...
Definition: blockindex.h:25
const BlockHash * phashBlock
pointer to the hash of the block, if any.
Definition: blockindex.h:29
uint32_t nTime
Definition: blockindex.h:92
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
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
An outpoint - a combination of a transaction hash and an index n into its vout.
Definition: transaction.h:20
Serialized script, used inside transaction inputs and outputs.
Definition: script.h:431
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
bool InvalidateBlock(BlockValidationState &state, CBlockIndex *pindex) EXCLUSIVE_LOCKS_REQUIRED(!m_chainstate_mutex
Mark a block as invalid.
CCoinsViewCache & CoinsTip() EXCLUSIVE_LOCKS_REQUIRED(
Definition: validation.h:830
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
int ActiveHeight() const EXCLUSIVE_LOCKS_REQUIRED(GetMutex())
Definition: validation.h:1431
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
static RCUPtr make(Args &&...args)
Construct a new object that is owned by the pointer.
Definition: rcu.h:112
bool IsValid() const
Definition: validation.h:117
Result GetResult() const
Definition: validation.h:120
bool removeNode(NodeId nodeid)
bool setFinalized(PeerId peerid)
Latch on that this peer has a finalized proof.
bool dumpPeersToFile(const fs::path &dumpPath) const
RemoteProofSet remoteProofs
Remember which node sent which proof so we have an image of the proof set of our peers.
Definition: peermanager.h:281
uint64_t getFragmentation() const
Definition: peermanager.h:498
uint32_t getConnectedPeersScore() const
Definition: peermanager.h:438
bool isDangling(const ProofId &proofid) const
bool updateNextRequestTime(NodeId nodeid, SteadyMilliseconds timeout)
std::optional< bool > getRemotePresenceStatus(const ProofId &proofid) const
Get the presence remote status of a proof.
bool shouldRequestMoreNodes()
Returns true if we encountered a lack of node since the last call.
Definition: peermanager.h:331
bool exists(const ProofId &proofid) const
Definition: peermanager.h:402
size_t getNodeCount() const
Definition: peermanager.h:313
PendingNodeSet pendingNodes
Definition: peermanager.h:225
bool verify() const
Perform consistency check on internal data structures.
bool forNode(NodeId nodeid, Callable &&func) const
Definition: peermanager.h:334
bool forPeer(const ProofId &proofid, Callable &&func) const
Definition: peermanager.h:410
uint32_t getTotalPeersScore() const
Definition: peermanager.h:437
bool latchAvaproofsSent(NodeId nodeid)
Flag that a node did send its compact proofs.
bool addNode(NodeId nodeid, const ProofId &proofid)
Node API.
Definition: peermanager.cpp:31
uint64_t getSlotCount() const
Definition: peermanager.h:497
bool loadPeersFromFile(const fs::path &dumpPath, std::unordered_set< ProofRef, SaltedProofHasher > &registeredProofs)
std::unordered_set< ProofRef, SaltedProofHasher > updatedBlockTip()
Update the peer set when a new block is connected.
bool isBoundToPeer(const ProofId &proofid) const
size_t getPendingNodeCount() const
Definition: peermanager.h:314
bool saveRemoteProof(const ProofId &proofid, const NodeId nodeid, const bool present)
uint64_t compact()
Trigger maintenance of internal data structures.
void forEachPeer(Callable &&func) const
Definition: peermanager.h:416
void forEachNode(const Peer &peer, Callable &&func) const
Definition: peermanager.h:340
bool isFlaky(const ProofId &proofid) const
bool removePeer(const PeerId peerid)
Remove an existing peer.
bool isImmature(const ProofId &proofid) const
bool rejectProof(const ProofId &proofid, RejectionMode mode=RejectionMode::DEFAULT)
RegistrationMode
Registration mode.
Definition: peermanager.h:371
static constexpr size_t MAX_REMOTE_PROOFS
Definition: peermanager.h:300
bool selectStakingRewardWinner(const CBlockIndex *pprev, std::vector< std::pair< ProofId, CScript >> &winners)
Deterministically select a list of payout scripts based on the proof set and the previous block hash.
PeerId selectPeer() const
Randomly select a peer to poll.
const ProofRadixTree & getShareableProofsSnapshot() const
Definition: peermanager.h:516
void updateAvailabilityScores(const double decayFactor, Callable &&getNodeAvailabilityScore)
Definition: peermanager.h:453
bool isInConflictingPool(const ProofId &proofid) const
void cleanupDanglingProofs(std::unordered_set< ProofRef, SaltedProofHasher > &registeredProofs)
ProofRef getProof(const ProofId &proofid) const
bool registerProof(const ProofRef &proof, ProofRegistrationState &registrationState, RegistrationMode mode=RegistrationMode::DEFAULT)
bool updateNextPossibleConflictTime(PeerId peerid, const std::chrono::seconds &nextTime)
Proof and Peer related API.
bool addUTXO(COutPoint utxo, Amount amount, uint32_t height, bool is_coinbase, CKey key)
static bool FromHex(Proof &proof, const std::string &hexProof, bilingual_str &errorOut)
Definition: proof.cpp:51
const ProofId & getId() const
Definition: proof.h:169
const CScript & getPayoutScript() const
Definition: proof.h:166
static uint32_t amountToScore(Amount amount)
Definition: proof.cpp:100
uint8_t * begin()
Definition: uint256.h:85
Path class wrapper to block calls to the fs::path(std::string) implicit constructor and the fs::path:...
Definition: fs.h:30
256-bit opaque blob.
Definition: uint256.h:129
static const uint256 ONE
Definition: uint256.h:135
static const uint256 ZERO
Definition: uint256.h:134
static constexpr int CLIENT_VERSION
bitcoind-res.rc includes this file, but it cannot cope with real c++ code.
Definition: clientversion.h:38
static void addCoin(const Amount nValue, const CWallet &wallet, std::vector< std::unique_ptr< CWalletTx >> &wtxs)
std::string FormatScript(const CScript &script)
Definition: core_write.cpp:24
static const uint8_t tau[]
Definition: chacha20.cpp:30
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate.
Definition: cs_main.cpp:7
bool FileCommit(FILE *file)
Ensure file contents are fully committed to disk, using a platform-specific feature analogous to fsyn...
Definition: fs_helpers.cpp:125
bool error(const char *fmt, const Args &...args)
Definition: logging.h:226
static RPCHelpMan generate()
Definition: mining.cpp:291
static constexpr Amount PROOF_DUST_THRESHOLD
Minimum amount per utxo.
Definition: proof.h:40
ProofRegistrationResult
Definition: peermanager.h:145
static constexpr uint32_t AVALANCHE_MAX_IMMATURE_PROOFS
Maximum number of immature proofs the peer manager will accept from the network.
Definition: peermanager.h:44
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
PeerId selectPeerImpl(const std::vector< Slot > &slots, const uint64_t slot, const uint64_t max)
Internal methods that are exposed for testing purposes.
RCUPtr< const Proof > ProofRef
Definition: proof.h:185
FILE * fopen(const fs::path &p, const char *mode)
Definition: fs.cpp:30
Definition: init.h:28
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 void addNodeWithScore(Chainstate &active_chainstate, avalanche::PeerManager &pm, NodeId node, uint32_t score)
BOOST_AUTO_TEST_CASE(select_peer_linear)
BOOST_FIXTURE_TEST_CASE(conflicting_proof_rescan, NoCoolDownFixture)
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
@ SER_DISK
Definition: serialize.h:153
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
static const double AVALANCHE_STATISTICS_DECAY_FACTOR
Pre-computed decay factor for the avalanche statistics computation.
Definition: statistics.h:18
static constexpr std::chrono::minutes AVALANCHE_STATISTICS_TIME_CONSTANT
Time constant for the avalanche statistics computation.
Definition: statistics.h:13
static constexpr std::chrono::minutes AVALANCHE_STATISTICS_REFRESH_PERIOD
Refresh period for the avalanche statistics computation.
Definition: statistics.h:11
Definition: amount.h:19
A BlockHash is a unqiue identifier for a block.
Definition: blockhash.h:13
bool insert(const RCUPtr< T > &value)
Insert a value into the tree.
Definition: radix.h:112
A TxId is the identifier of a transaction.
Definition: txid.h:14
Compare conflicting proofs.
std::chrono::seconds registration_time
Definition: peermanager.h:93
static constexpr auto DANGLING_TIMEOUT
Consider dropping the peer if no node is attached after this timeout expired.
Definition: peermanager.h:102
ProofRef proof
Definition: peermanager.h:89
static ProofRef buildDuplicatedStakes(ProofBuilder &pb)
Definition: util.cpp:107
Bilingual messages:
Definition: translation.h:17
#define LOCK(cs)
Definition: sync.h:306
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
Definition: sync.h:357
static int count
Definition: tests.c:31
int64_t GetTime()
Definition: time.cpp:109
void SetMockTime(int64_t nMockTimeIn)
DEPRECATED Use SetMockTime with chrono type.
Definition: time.cpp:89
std::chrono::time_point< std::chrono::steady_clock, std::chrono::milliseconds > SteadyMilliseconds
Definition: time.h:31
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1202