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 <config.h>
12 #include <script/standard.h>
13 #include <util/time.h>
14 #include <util/translation.h>
15 #include <validation.h>
16 
17 #include <test/util/setup_common.h>
18 
19 #include <boost/test/unit_test.hpp>
20 
21 using namespace avalanche;
22 
23 namespace avalanche {
24 namespace {
25  struct TestPeerManager {
26  static bool nodeBelongToPeer(const PeerManager &pm, NodeId nodeid,
27  PeerId peerid) {
28  return pm.forNode(nodeid, [&](const Node &node) {
29  return node.peerid == peerid;
30  });
31  }
32 
33  static bool isNodePending(const PeerManager &pm, NodeId nodeid) {
34  auto &pendingNodesView = pm.pendingNodes.get<by_nodeid>();
35  return pendingNodesView.find(nodeid) != pendingNodesView.end();
36  }
37 
38  static PeerId getPeerIdForProofId(PeerManager &pm,
39  const ProofId &proofid) {
40  auto &pview = pm.peers.get<by_proofid>();
41  auto it = pview.find(proofid);
42  return it == pview.end() ? NO_PEER : it->peerid;
43  }
44 
45  static PeerId registerAndGetPeerId(PeerManager &pm,
46  const ProofRef &proof) {
47  pm.registerProof(proof);
48  return getPeerIdForProofId(pm, proof->getId());
49  }
50 
51  static std::vector<uint32_t> getOrderedScores(const PeerManager &pm) {
52  std::vector<uint32_t> scores;
53 
54  auto &peerView = pm.peers.get<by_score>();
55  for (const Peer &peer : peerView) {
56  scores.push_back(peer.getScore());
57  }
58 
59  return scores;
60  }
61 
62  static void
63  cleanupDanglingProofs(PeerManager &pm,
64  const ProofRef &localProof = ProofRef()) {
65  pm.cleanupDanglingProofs(localProof);
66  }
67  };
68 
69  static void addCoin(Chainstate &chainstate, const COutPoint &outpoint,
70  const CKey &key,
71  const Amount amount = PROOF_DUST_THRESHOLD,
72  uint32_t height = 100, bool is_coinbase = false) {
74 
75  LOCK(cs_main);
76  CCoinsViewCache &coins = chainstate.CoinsTip();
77  coins.AddCoin(outpoint,
78  Coin(CTxOut(amount, script), height, is_coinbase), false);
79  }
80 
81  static COutPoint createUtxo(Chainstate &chainstate, const CKey &key,
82  const Amount amount = PROOF_DUST_THRESHOLD,
83  uint32_t height = 100,
84  bool is_coinbase = false) {
85  COutPoint outpoint(TxId(GetRandHash()), 0);
86  addCoin(chainstate, outpoint, key, amount, height, is_coinbase);
87  return outpoint;
88  }
89 
90  static ProofRef
91  buildProof(const CKey &key,
92  const std::vector<std::tuple<COutPoint, Amount>> &outpoints,
93  const CKey &master = CKey::MakeCompressedKey(),
94  int64_t sequence = 1, uint32_t height = 100,
95  bool is_coinbase = false, int64_t expirationTime = 0) {
96  ProofBuilder pb(sequence, expirationTime, master,
98  for (const auto &[outpoint, amount] : outpoints) {
99  BOOST_CHECK(pb.addUTXO(outpoint, amount, height, is_coinbase, key));
100  }
101  return pb.build();
102  }
103 
104  template <typename... Args>
105  static ProofRef
106  buildProofWithOutpoints(const CKey &key,
107  const std::vector<COutPoint> &outpoints,
108  Amount amount, Args &&...args) {
109  std::vector<std::tuple<COutPoint, Amount>> outpointsWithAmount;
110  std::transform(
111  outpoints.begin(), outpoints.end(),
112  std::back_inserter(outpointsWithAmount),
113  [amount](const auto &o) { return std::make_tuple(o, amount); });
114  return buildProof(key, outpointsWithAmount,
115  std::forward<Args>(args)...);
116  }
117 
118  static ProofRef
119  buildProofWithSequence(const CKey &key,
120  const std::vector<COutPoint> &outpoints,
121  int64_t sequence) {
122  return buildProofWithOutpoints(key, outpoints, PROOF_DUST_THRESHOLD,
123  key, sequence);
124  }
125 } // namespace
126 } // namespace avalanche
127 
128 namespace {
129 struct PeerManagerFixture : public TestChain100Setup {
130  PeerManagerFixture() {
131  gArgs.ForceSetArg("-avaproofstakeutxoconfirmations", "1");
132  }
133  ~PeerManagerFixture() {
134  gArgs.ClearForcedArg("-avaproofstakeutxoconfirmations");
135  }
136 };
137 } // namespace
138 
139 namespace {
140 struct NoCoolDownFixture : public PeerManagerFixture {
141  NoCoolDownFixture() {
142  gArgs.ForceSetArg("-avalancheconflictingproofcooldown", "0");
143  }
144  ~NoCoolDownFixture() {
145  gArgs.ClearForcedArg("-avalancheconflictingproofcooldown");
146  }
147 };
148 } // namespace
149 
150 BOOST_FIXTURE_TEST_SUITE(peermanager_tests, PeerManagerFixture)
151 
152 BOOST_AUTO_TEST_CASE(select_peer_linear) {
153  // No peers.
156 
157  // One peer
158  const std::vector<Slot> oneslot = {{100, 100, 23}};
159 
160  // Undershoot
161  BOOST_CHECK_EQUAL(selectPeerImpl(oneslot, 0, 300), NO_PEER);
162  BOOST_CHECK_EQUAL(selectPeerImpl(oneslot, 42, 300), NO_PEER);
163  BOOST_CHECK_EQUAL(selectPeerImpl(oneslot, 99, 300), NO_PEER);
164 
165  // Nailed it
166  BOOST_CHECK_EQUAL(selectPeerImpl(oneslot, 100, 300), 23);
167  BOOST_CHECK_EQUAL(selectPeerImpl(oneslot, 142, 300), 23);
168  BOOST_CHECK_EQUAL(selectPeerImpl(oneslot, 199, 300), 23);
169 
170  // Overshoot
171  BOOST_CHECK_EQUAL(selectPeerImpl(oneslot, 200, 300), NO_PEER);
172  BOOST_CHECK_EQUAL(selectPeerImpl(oneslot, 242, 300), NO_PEER);
173  BOOST_CHECK_EQUAL(selectPeerImpl(oneslot, 299, 300), NO_PEER);
174 
175  // Two peers
176  const std::vector<Slot> twoslots = {{100, 100, 69}, {300, 100, 42}};
177 
178  // Undershoot
179  BOOST_CHECK_EQUAL(selectPeerImpl(twoslots, 0, 500), NO_PEER);
180  BOOST_CHECK_EQUAL(selectPeerImpl(twoslots, 42, 500), NO_PEER);
181  BOOST_CHECK_EQUAL(selectPeerImpl(twoslots, 99, 500), NO_PEER);
182 
183  // First entry
184  BOOST_CHECK_EQUAL(selectPeerImpl(twoslots, 100, 500), 69);
185  BOOST_CHECK_EQUAL(selectPeerImpl(twoslots, 142, 500), 69);
186  BOOST_CHECK_EQUAL(selectPeerImpl(twoslots, 199, 500), 69);
187 
188  // In between
189  BOOST_CHECK_EQUAL(selectPeerImpl(twoslots, 200, 500), NO_PEER);
190  BOOST_CHECK_EQUAL(selectPeerImpl(twoslots, 242, 500), NO_PEER);
191  BOOST_CHECK_EQUAL(selectPeerImpl(twoslots, 299, 500), NO_PEER);
192 
193  // Second entry
194  BOOST_CHECK_EQUAL(selectPeerImpl(twoslots, 300, 500), 42);
195  BOOST_CHECK_EQUAL(selectPeerImpl(twoslots, 342, 500), 42);
196  BOOST_CHECK_EQUAL(selectPeerImpl(twoslots, 399, 500), 42);
197 
198  // Overshoot
199  BOOST_CHECK_EQUAL(selectPeerImpl(twoslots, 400, 500), NO_PEER);
200  BOOST_CHECK_EQUAL(selectPeerImpl(twoslots, 442, 500), NO_PEER);
201  BOOST_CHECK_EQUAL(selectPeerImpl(twoslots, 499, 500), NO_PEER);
202 }
203 
204 BOOST_AUTO_TEST_CASE(select_peer_dichotomic) {
205  std::vector<Slot> slots;
206 
207  // 100 peers of size 1 with 1 empty element apart.
208  uint64_t max = 1;
209  for (int i = 0; i < 100; i++) {
210  slots.emplace_back(max, 1, i);
211  max += 2;
212  }
213 
214  BOOST_CHECK_EQUAL(selectPeerImpl(slots, 4, max), NO_PEER);
215 
216  // Check that we get what we expect.
217  for (int i = 0; i < 100; i++) {
218  BOOST_CHECK_EQUAL(selectPeerImpl(slots, 2 * i, max), NO_PEER);
219  BOOST_CHECK_EQUAL(selectPeerImpl(slots, 2 * i + 1, max), i);
220  }
221 
222  BOOST_CHECK_EQUAL(selectPeerImpl(slots, max, max), NO_PEER);
223 
224  // Update the slots to be heavily skewed toward the last element.
225  slots[99] = slots[99].withScore(101);
226  max = slots[99].getStop();
227  BOOST_CHECK_EQUAL(max, 300);
228 
229  for (int i = 0; i < 100; i++) {
230  BOOST_CHECK_EQUAL(selectPeerImpl(slots, 2 * i, max), NO_PEER);
231  BOOST_CHECK_EQUAL(selectPeerImpl(slots, 2 * i + 1, max), i);
232  }
233 
234  BOOST_CHECK_EQUAL(selectPeerImpl(slots, 200, max), 99);
235  BOOST_CHECK_EQUAL(selectPeerImpl(slots, 256, max), 99);
236  BOOST_CHECK_EQUAL(selectPeerImpl(slots, 299, max), 99);
237  BOOST_CHECK_EQUAL(selectPeerImpl(slots, 300, max), NO_PEER);
238 
239  // Update the slots to be heavily skewed toward the first element.
240  for (int i = 0; i < 100; i++) {
241  slots[i] = slots[i].withStart(slots[i].getStart() + 100);
242  }
243 
244  slots[0] = Slot(1, slots[0].getStop() - 1, slots[0].getPeerId());
245  slots[99] = slots[99].withScore(1);
246  max = slots[99].getStop();
247  BOOST_CHECK_EQUAL(max, 300);
248 
249  BOOST_CHECK_EQUAL(selectPeerImpl(slots, 0, max), NO_PEER);
250  BOOST_CHECK_EQUAL(selectPeerImpl(slots, 1, max), 0);
251  BOOST_CHECK_EQUAL(selectPeerImpl(slots, 42, max), 0);
252 
253  for (int i = 0; i < 100; i++) {
254  BOOST_CHECK_EQUAL(selectPeerImpl(slots, 100 + 2 * i + 1, max), i);
255  BOOST_CHECK_EQUAL(selectPeerImpl(slots, 100 + 2 * i + 2, max), NO_PEER);
256  }
257 }
258 
259 BOOST_AUTO_TEST_CASE(select_peer_random) {
260  for (int c = 0; c < 1000; c++) {
261  size_t size = InsecureRandBits(10) + 1;
262  std::vector<Slot> slots;
263  slots.reserve(size);
264 
265  uint64_t max = InsecureRandBits(3);
266  auto next = [&]() {
267  uint64_t r = max;
268  max += InsecureRandBits(3);
269  return r;
270  };
271 
272  for (size_t i = 0; i < size; i++) {
273  const uint64_t start = next();
274  const uint32_t score = InsecureRandBits(3);
275  max += score;
276  slots.emplace_back(start, score, i);
277  }
278 
279  for (int k = 0; k < 100; k++) {
280  uint64_t s = max > 0 ? InsecureRandRange(max) : 0;
281  auto i = selectPeerImpl(slots, s, max);
282  // /!\ Because of the way we construct the vector, the peer id is
283  // always the index. This might not be the case in practice.
284  BOOST_CHECK(i == NO_PEER || slots[i].contains(s));
285  }
286  }
287 }
288 
289 static void addNodeWithScore(Chainstate &active_chainstate,
291  uint32_t score) {
292  auto proof = buildRandomProof(active_chainstate, score);
293  BOOST_CHECK(pm.registerProof(proof));
294  BOOST_CHECK(pm.addNode(node, proof->getId()));
295 };
296 
297 BOOST_AUTO_TEST_CASE(peer_probabilities) {
298  ChainstateManager &chainman = *Assert(m_node.chainman);
299  // No peers.
302 
303  const NodeId node0 = 42, node1 = 69, node2 = 37;
304 
305  Chainstate &active_chainstate = chainman.ActiveChainstate();
306  // One peer, we always return it.
307  addNodeWithScore(active_chainstate, pm, node0, MIN_VALID_PROOF_SCORE);
308  BOOST_CHECK_EQUAL(pm.selectNode(), node0);
309 
310  // Two peers, verify ratio.
311  addNodeWithScore(active_chainstate, pm, node1, 2 * MIN_VALID_PROOF_SCORE);
312 
313  std::unordered_map<PeerId, int> results = {};
314  for (int i = 0; i < 10000; i++) {
315  size_t n = pm.selectNode();
316  BOOST_CHECK(n == node0 || n == node1);
317  results[n]++;
318  }
319 
320  BOOST_CHECK(abs(2 * results[0] - results[1]) < 500);
321 
322  // Three peers, verify ratio.
323  addNodeWithScore(active_chainstate, pm, node2, MIN_VALID_PROOF_SCORE);
324 
325  results.clear();
326  for (int i = 0; i < 10000; i++) {
327  size_t n = pm.selectNode();
328  BOOST_CHECK(n == node0 || n == node1 || n == node2);
329  results[n]++;
330  }
331 
332  BOOST_CHECK(abs(results[0] - results[1] + results[2]) < 500);
333 }
334 
335 BOOST_AUTO_TEST_CASE(remove_peer) {
336  ChainstateManager &chainman = *Assert(m_node.chainman);
337  // No peers.
340 
341  Chainstate &active_chainstate = chainman.ActiveChainstate();
342  // Add 4 peers.
343  std::array<PeerId, 8> peerids;
344  for (int i = 0; i < 4; i++) {
345  auto p = buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE);
346  peerids[i] = TestPeerManager::registerAndGetPeerId(pm, p);
347  BOOST_CHECK(pm.addNode(InsecureRand32(), p->getId()));
348  }
349 
350  BOOST_CHECK_EQUAL(pm.getSlotCount(), 40000);
352 
353  for (int i = 0; i < 100; i++) {
354  PeerId p = pm.selectPeer();
355  BOOST_CHECK(p == peerids[0] || p == peerids[1] || p == peerids[2] ||
356  p == peerids[3]);
357  }
358 
359  // Remove one peer, it nevers show up now.
360  BOOST_CHECK(pm.removePeer(peerids[2]));
361  BOOST_CHECK_EQUAL(pm.getSlotCount(), 40000);
362  BOOST_CHECK_EQUAL(pm.getFragmentation(), 10000);
363 
364  // Make sure we compact to never get NO_PEER.
365  BOOST_CHECK_EQUAL(pm.compact(), 10000);
366  BOOST_CHECK(pm.verify());
367  BOOST_CHECK_EQUAL(pm.getSlotCount(), 30000);
369 
370  for (int i = 0; i < 100; i++) {
371  PeerId p = pm.selectPeer();
372  BOOST_CHECK(p == peerids[0] || p == peerids[1] || p == peerids[3]);
373  }
374 
375  // Add 4 more peers.
376  for (int i = 0; i < 4; i++) {
377  auto p = buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE);
378  peerids[i + 4] = TestPeerManager::registerAndGetPeerId(pm, p);
379  BOOST_CHECK(pm.addNode(InsecureRand32(), p->getId()));
380  }
381 
382  BOOST_CHECK_EQUAL(pm.getSlotCount(), 70000);
384 
385  BOOST_CHECK(pm.removePeer(peerids[0]));
386  BOOST_CHECK_EQUAL(pm.getSlotCount(), 70000);
387  BOOST_CHECK_EQUAL(pm.getFragmentation(), 10000);
388 
389  // Removing the last entry do not increase fragmentation.
390  BOOST_CHECK(pm.removePeer(peerids[7]));
391  BOOST_CHECK_EQUAL(pm.getSlotCount(), 60000);
392  BOOST_CHECK_EQUAL(pm.getFragmentation(), 10000);
393 
394  // Make sure we compact to never get NO_PEER.
395  BOOST_CHECK_EQUAL(pm.compact(), 10000);
396  BOOST_CHECK(pm.verify());
397  BOOST_CHECK_EQUAL(pm.getSlotCount(), 50000);
399 
400  for (int i = 0; i < 100; i++) {
401  PeerId p = pm.selectPeer();
402  BOOST_CHECK(p == peerids[1] || p == peerids[3] || p == peerids[4] ||
403  p == peerids[5] || p == peerids[6]);
404  }
405 
406  // Removing non existent peers fails.
407  BOOST_CHECK(!pm.removePeer(peerids[0]));
408  BOOST_CHECK(!pm.removePeer(peerids[2]));
409  BOOST_CHECK(!pm.removePeer(peerids[7]));
411 }
412 
413 BOOST_AUTO_TEST_CASE(compact_slots) {
414  ChainstateManager &chainman = *Assert(m_node.chainman);
416 
417  // Add 4 peers.
418  std::array<PeerId, 4> peerids;
419  for (int i = 0; i < 4; i++) {
420  auto p = buildRandomProof(chainman.ActiveChainstate(),
422  peerids[i] = TestPeerManager::registerAndGetPeerId(pm, p);
423  BOOST_CHECK(pm.addNode(InsecureRand32(), p->getId()));
424  }
425 
426  // Remove all peers.
427  for (auto p : peerids) {
428  pm.removePeer(p);
429  }
430 
431  BOOST_CHECK_EQUAL(pm.getSlotCount(), 30000);
432  BOOST_CHECK_EQUAL(pm.getFragmentation(), 30000);
433 
434  for (int i = 0; i < 100; i++) {
436  }
437 
438  BOOST_CHECK_EQUAL(pm.compact(), 30000);
439  BOOST_CHECK(pm.verify());
442 }
443 
445  ChainstateManager &chainman = *Assert(m_node.chainman);
447 
448  Chainstate &active_chainstate = chainman.ActiveChainstate();
449 
450  // Create one peer.
451  auto proof =
452  buildRandomProof(active_chainstate, 10000000 * MIN_VALID_PROOF_SCORE);
453  BOOST_CHECK(pm.registerProof(proof));
455 
456  // Add 4 nodes.
457  const ProofId &proofid = proof->getId();
458  for (int i = 0; i < 4; i++) {
459  BOOST_CHECK(pm.addNode(i, proofid));
460  }
461 
462  for (int i = 0; i < 100; i++) {
463  NodeId n = pm.selectNode();
464  BOOST_CHECK(n >= 0 && n < 4);
465  BOOST_CHECK(
466  pm.updateNextRequestTime(n, std::chrono::steady_clock::now()));
467  }
468 
469  // Remove a node, check that it doesn't show up.
470  BOOST_CHECK(pm.removeNode(2));
471 
472  for (int i = 0; i < 100; i++) {
473  NodeId n = pm.selectNode();
474  BOOST_CHECK(n == 0 || n == 1 || n == 3);
475  BOOST_CHECK(
476  pm.updateNextRequestTime(n, std::chrono::steady_clock::now()));
477  }
478 
479  // Push a node's timeout in the future, so that it doesn't show up.
480  BOOST_CHECK(pm.updateNextRequestTime(1, std::chrono::steady_clock::now() +
481  std::chrono::hours(24)));
482 
483  for (int i = 0; i < 100; i++) {
484  NodeId n = pm.selectNode();
485  BOOST_CHECK(n == 0 || n == 3);
486  BOOST_CHECK(
487  pm.updateNextRequestTime(n, std::chrono::steady_clock::now()));
488  }
489 
490  // Move a node from a peer to another. This peer has a very low score such
491  // as chances of being picked are 1 in 10 million.
492  addNodeWithScore(active_chainstate, pm, 3, MIN_VALID_PROOF_SCORE);
493 
494  int node3selected = 0;
495  for (int i = 0; i < 100; i++) {
496  NodeId n = pm.selectNode();
497  if (n == 3) {
498  // Selecting this node should be exceedingly unlikely.
499  BOOST_CHECK(node3selected++ < 1);
500  } else {
501  BOOST_CHECK_EQUAL(n, 0);
502  }
503  BOOST_CHECK(
504  pm.updateNextRequestTime(n, std::chrono::steady_clock::now()));
505  }
506 }
507 
508 BOOST_AUTO_TEST_CASE(node_binding) {
509  ChainstateManager &chainman = *Assert(m_node.chainman);
511 
512  Chainstate &active_chainstate = chainman.ActiveChainstate();
513 
514  auto proof = buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE);
515  const ProofId &proofid = proof->getId();
516 
519 
520  // Add a bunch of nodes with no associated peer
521  for (int i = 0; i < 10; i++) {
522  BOOST_CHECK(!pm.addNode(i, proofid));
523  BOOST_CHECK(TestPeerManager::isNodePending(pm, i));
526  }
527 
528  // Now create the peer and check all the nodes are bound
529  const PeerId peerid = TestPeerManager::registerAndGetPeerId(pm, proof);
530  BOOST_CHECK_NE(peerid, NO_PEER);
531  for (int i = 0; i < 10; i++) {
532  BOOST_CHECK(!TestPeerManager::isNodePending(pm, i));
533  BOOST_CHECK(TestPeerManager::nodeBelongToPeer(pm, i, peerid));
536  }
537  BOOST_CHECK(pm.verify());
538 
539  // Disconnect some nodes
540  for (int i = 0; i < 5; i++) {
541  BOOST_CHECK(pm.removeNode(i));
542  BOOST_CHECK(!TestPeerManager::isNodePending(pm, i));
543  BOOST_CHECK(!TestPeerManager::nodeBelongToPeer(pm, i, peerid));
544  BOOST_CHECK_EQUAL(pm.getNodeCount(), 10 - i - 1);
546  }
547 
548  // Add nodes when the peer already exists
549  for (int i = 0; i < 5; i++) {
550  BOOST_CHECK(pm.addNode(i, proofid));
551  BOOST_CHECK(!TestPeerManager::isNodePending(pm, i));
552  BOOST_CHECK(TestPeerManager::nodeBelongToPeer(pm, i, peerid));
553  BOOST_CHECK_EQUAL(pm.getNodeCount(), 5 + i + 1);
555  }
556 
557  auto alt_proof = buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE);
558  const ProofId &alt_proofid = alt_proof->getId();
559 
560  // Update some nodes from a known proof to an unknown proof
561  for (int i = 0; i < 5; i++) {
562  BOOST_CHECK(!pm.addNode(i, alt_proofid));
563  BOOST_CHECK(TestPeerManager::isNodePending(pm, i));
564  BOOST_CHECK(!TestPeerManager::nodeBelongToPeer(pm, i, peerid));
565  BOOST_CHECK_EQUAL(pm.getNodeCount(), 10 - i - 1);
567  }
568 
569  auto alt2_proof =
570  buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE);
571  const ProofId &alt2_proofid = alt2_proof->getId();
572 
573  // Update some nodes from an unknown proof to another unknown proof
574  for (int i = 0; i < 5; i++) {
575  BOOST_CHECK(!pm.addNode(i, alt2_proofid));
576  BOOST_CHECK(TestPeerManager::isNodePending(pm, i));
579  }
580 
581  // Update some nodes from an unknown proof to a known proof
582  for (int i = 0; i < 5; i++) {
583  BOOST_CHECK(pm.addNode(i, proofid));
584  BOOST_CHECK(!TestPeerManager::isNodePending(pm, i));
585  BOOST_CHECK(TestPeerManager::nodeBelongToPeer(pm, i, peerid));
586  BOOST_CHECK_EQUAL(pm.getNodeCount(), 5 + i + 1);
587  BOOST_CHECK_EQUAL(pm.getPendingNodeCount(), 5 - i - 1);
588  }
589 
590  // Remove the peer, the nodes should be pending again
591  BOOST_CHECK(pm.removePeer(peerid));
592  BOOST_CHECK(!pm.exists(proof->getId()));
593  for (int i = 0; i < 10; i++) {
594  BOOST_CHECK(TestPeerManager::isNodePending(pm, i));
595  BOOST_CHECK(!TestPeerManager::nodeBelongToPeer(pm, i, peerid));
598  }
599  BOOST_CHECK(pm.verify());
600 
601  // Remove the remaining pending nodes, check the count drops accordingly
602  for (int i = 0; i < 10; i++) {
603  BOOST_CHECK(pm.removeNode(i));
604  BOOST_CHECK(!TestPeerManager::isNodePending(pm, i));
605  BOOST_CHECK(!TestPeerManager::nodeBelongToPeer(pm, i, peerid));
607  BOOST_CHECK_EQUAL(pm.getPendingNodeCount(), 10 - i - 1);
608  }
609 }
610 
611 BOOST_AUTO_TEST_CASE(node_binding_reorg) {
612  gArgs.ForceSetArg("-avaproofstakeutxoconfirmations", "2");
613  ChainstateManager &chainman = *Assert(m_node.chainman);
614 
616 
617  auto proof = buildRandomProof(chainman.ActiveChainstate(),
619  const ProofId &proofid = proof->getId();
620 
621  PeerId peerid = TestPeerManager::registerAndGetPeerId(pm, proof);
622  BOOST_CHECK_NE(peerid, NO_PEER);
623  BOOST_CHECK(pm.verify());
624 
625  // Add nodes to our peer
626  for (int i = 0; i < 10; i++) {
627  BOOST_CHECK(pm.addNode(i, proofid));
628  BOOST_CHECK(!TestPeerManager::isNodePending(pm, i));
629  BOOST_CHECK(TestPeerManager::nodeBelongToPeer(pm, i, peerid));
630  }
631 
632  // Make the proof immature by reorging to a shorter chain
633  {
634  BlockValidationState state;
635  chainman.ActiveChainstate().InvalidateBlock(GetConfig(), state,
636  chainman.ActiveTip());
637  BOOST_CHECK_EQUAL(chainman.ActiveHeight(), 99);
638  }
639 
640  pm.updatedBlockTip();
641  BOOST_CHECK(pm.isImmature(proofid));
642  BOOST_CHECK(!pm.isBoundToPeer(proofid));
643  for (int i = 0; i < 10; i++) {
644  BOOST_CHECK(TestPeerManager::isNodePending(pm, i));
645  BOOST_CHECK(!TestPeerManager::nodeBelongToPeer(pm, i, peerid));
646  }
647  BOOST_CHECK(pm.verify());
648 
649  // Make the proof great again
650  {
651  // Advance the clock so the newly mined block won't collide with the
652  // other deterministically-generated blocks
653  SetMockTime(GetTime() + 20);
654  mineBlocks(1);
655  BlockValidationState state;
656  BOOST_CHECK(
657  chainman.ActiveChainstate().ActivateBestChain(GetConfig(), state));
658  BOOST_CHECK_EQUAL(chainman.ActiveHeight(), 100);
659  }
660 
661  pm.updatedBlockTip();
662  BOOST_CHECK(!pm.isImmature(proofid));
663  BOOST_CHECK(pm.isBoundToPeer(proofid));
664  // The peerid has certainly been updated
665  peerid = TestPeerManager::registerAndGetPeerId(pm, proof);
666  BOOST_CHECK_NE(peerid, NO_PEER);
667  for (int i = 0; i < 10; i++) {
668  BOOST_CHECK(!TestPeerManager::isNodePending(pm, i));
669  BOOST_CHECK(TestPeerManager::nodeBelongToPeer(pm, i, peerid));
670  }
671  BOOST_CHECK(pm.verify());
672 }
673 
674 BOOST_AUTO_TEST_CASE(proof_conflict) {
675  auto key = CKey::MakeCompressedKey();
676 
677  TxId txid1(GetRandHash());
678  TxId txid2(GetRandHash());
679  BOOST_CHECK(txid1 != txid2);
680 
681  const Amount v = PROOF_DUST_THRESHOLD;
682  const int height = 100;
683 
684  ChainstateManager &chainman = *Assert(m_node.chainman);
685  for (uint32_t i = 0; i < 10; i++) {
686  addCoin(chainman.ActiveChainstate(), {txid1, i}, key);
687  addCoin(chainman.ActiveChainstate(), {txid2, i}, key);
688  }
689 
691  CKey masterKey = CKey::MakeCompressedKey();
692  const auto getPeerId = [&](const std::vector<COutPoint> &outpoints) {
693  return TestPeerManager::registerAndGetPeerId(
694  pm, buildProofWithOutpoints(key, outpoints, v, masterKey, 0, height,
695  false, 0));
696  };
697 
698  // Add one peer.
699  const PeerId peer1 = getPeerId({COutPoint(txid1, 0)});
700  BOOST_CHECK(peer1 != NO_PEER);
701 
702  // Same proof, same peer.
703  BOOST_CHECK_EQUAL(getPeerId({COutPoint(txid1, 0)}), peer1);
704 
705  // Different txid, different proof.
706  const PeerId peer2 = getPeerId({COutPoint(txid2, 0)});
707  BOOST_CHECK(peer2 != NO_PEER && peer2 != peer1);
708 
709  // Different index, different proof.
710  const PeerId peer3 = getPeerId({COutPoint(txid1, 1)});
711  BOOST_CHECK(peer3 != NO_PEER && peer3 != peer1);
712 
713  // Empty proof, no peer.
714  BOOST_CHECK_EQUAL(getPeerId({}), NO_PEER);
715 
716  // Multiple inputs.
717  const PeerId peer4 = getPeerId({COutPoint(txid1, 2), COutPoint(txid2, 2)});
718  BOOST_CHECK(peer4 != NO_PEER && peer4 != peer1);
719 
720  // Duplicated input.
721  {
724  COutPoint o(txid1, 3);
725  BOOST_CHECK(pb.addUTXO(o, v, height, false, key));
726  BOOST_CHECK(
728  }
729 
730  // Multiple inputs, collision on first input.
731  BOOST_CHECK_EQUAL(getPeerId({COutPoint(txid1, 0), COutPoint(txid2, 4)}),
732  NO_PEER);
733 
734  // Mutliple inputs, collision on second input.
735  BOOST_CHECK_EQUAL(getPeerId({COutPoint(txid1, 4), COutPoint(txid2, 0)}),
736  NO_PEER);
737 
738  // Mutliple inputs, collision on both inputs.
739  BOOST_CHECK_EQUAL(getPeerId({COutPoint(txid1, 0), COutPoint(txid2, 2)}),
740  NO_PEER);
741 }
742 
743 BOOST_AUTO_TEST_CASE(immature_proofs) {
744  ChainstateManager &chainman = *Assert(m_node.chainman);
745  gArgs.ForceSetArg("-avaproofstakeutxoconfirmations", "2");
747 
748  auto key = CKey::MakeCompressedKey();
749  int immatureHeight = 100;
750 
751  auto registerImmature = [&](const ProofRef &proof) {
753  BOOST_CHECK(!pm.registerProof(proof, state));
755  };
756 
757  auto checkImmature = [&](const ProofRef &proof, bool expectedImmature) {
758  const ProofId &proofid = proof->getId();
759  BOOST_CHECK(pm.exists(proofid));
760 
761  BOOST_CHECK_EQUAL(pm.isImmature(proofid), expectedImmature);
762  BOOST_CHECK_EQUAL(pm.isBoundToPeer(proofid), !expectedImmature);
763 
764  bool ret = false;
765  pm.forEachPeer([&](const Peer &peer) {
766  if (proof->getId() == peer.proof->getId()) {
767  ret = true;
768  }
769  });
770  BOOST_CHECK_EQUAL(ret, !expectedImmature);
771  };
772 
773  // Track immature proofs so we can test them later
774  std::vector<ProofRef> immatureProofs;
775 
776  // Fill up the immature pool to test the size limit
777  for (int64_t i = 1; i <= AVALANCHE_MAX_IMMATURE_PROOFS; i++) {
778  COutPoint outpoint = COutPoint(TxId(GetRandHash()), 0);
779  auto proof = buildProofWithOutpoints(
780  key, {outpoint}, i * PROOF_DUST_THRESHOLD, key, 0, immatureHeight);
781  addCoin(chainman.ActiveChainstate(), outpoint, key,
782  i * PROOF_DUST_THRESHOLD, immatureHeight);
783  registerImmature(proof);
784  checkImmature(proof, true);
785  immatureProofs.push_back(proof);
786  }
787 
788  // More immature proofs evict lower scoring proofs
789  for (auto i = 0; i < 100; i++) {
790  COutPoint outpoint = COutPoint(TxId(GetRandHash()), 0);
791  auto proof =
792  buildProofWithOutpoints(key, {outpoint}, 200 * PROOF_DUST_THRESHOLD,
793  key, 0, immatureHeight);
794  addCoin(chainman.ActiveChainstate(), outpoint, key,
795  200 * PROOF_DUST_THRESHOLD, immatureHeight);
796  registerImmature(proof);
797  checkImmature(proof, true);
798  immatureProofs.push_back(proof);
799  BOOST_CHECK(!pm.exists(immatureProofs.front()->getId()));
800  immatureProofs.erase(immatureProofs.begin());
801  }
802 
803  // Replacement when the pool is full still works
804  {
805  const COutPoint &outpoint =
806  immatureProofs.front()->getStakes()[0].getStake().getUTXO();
807  auto proof =
808  buildProofWithOutpoints(key, {outpoint}, 101 * PROOF_DUST_THRESHOLD,
809  key, 1, immatureHeight);
810  registerImmature(proof);
811  checkImmature(proof, true);
812  immatureProofs.push_back(proof);
813  BOOST_CHECK(!pm.exists(immatureProofs.front()->getId()));
814  immatureProofs.erase(immatureProofs.begin());
815  }
816 
817  // Mine a block to increase the chain height, turning all immature proofs to
818  // mature
819  mineBlocks(1);
820  pm.updatedBlockTip();
821  for (const auto &proof : immatureProofs) {
822  checkImmature(proof, false);
823  }
824 }
825 
826 BOOST_AUTO_TEST_CASE(dangling_node) {
827  ChainstateManager &chainman = *Assert(m_node.chainman);
829 
830  Chainstate &active_chainstate = chainman.ActiveChainstate();
831 
832  auto proof = buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE);
833  PeerId peerid = TestPeerManager::registerAndGetPeerId(pm, proof);
834  BOOST_CHECK_NE(peerid, NO_PEER);
835 
836  const TimePoint theFuture(std::chrono::steady_clock::now() +
837  std::chrono::hours(24));
838 
839  // Add nodes to this peer and update their request time far in the future
840  for (int i = 0; i < 10; i++) {
841  BOOST_CHECK(pm.addNode(i, proof->getId()));
842  BOOST_CHECK(pm.updateNextRequestTime(i, theFuture));
843  }
844 
845  // Remove the peer
846  BOOST_CHECK(pm.removePeer(peerid));
847 
848  // Check the nodes are still there
849  for (int i = 0; i < 10; i++) {
850  BOOST_CHECK(pm.forNode(i, [](const Node &n) { return true; }));
851  }
852 
853  // Build a new one
854  proof = buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE);
855  peerid = TestPeerManager::registerAndGetPeerId(pm, proof);
856  BOOST_CHECK_NE(peerid, NO_PEER);
857 
858  // Update the nodes with the new proof
859  for (int i = 0; i < 10; i++) {
860  BOOST_CHECK(pm.addNode(i, proof->getId()));
861  BOOST_CHECK(pm.forNode(
862  i, [&](const Node &n) { return n.nextRequestTime == theFuture; }));
863  }
864 
865  // Remove the peer
866  BOOST_CHECK(pm.removePeer(peerid));
867 
868  // Disconnect the nodes
869  for (int i = 0; i < 10; i++) {
870  BOOST_CHECK(pm.removeNode(i));
871  }
872 }
873 
874 BOOST_AUTO_TEST_CASE(proof_accessors) {
875  ChainstateManager &chainman = *Assert(m_node.chainman);
877 
878  constexpr int numProofs = 10;
879 
880  std::vector<ProofRef> proofs;
881  proofs.reserve(numProofs);
882  for (int i = 0; i < numProofs; i++) {
883  proofs.push_back(buildRandomProof(chainman.ActiveChainstate(),
885  }
886 
887  for (int i = 0; i < numProofs; i++) {
888  BOOST_CHECK(pm.registerProof(proofs[i]));
889 
890  {
892  // Fail to add an existing proof
893  BOOST_CHECK(!pm.registerProof(proofs[i], state));
894  BOOST_CHECK(state.GetResult() ==
896  }
897 
898  for (int added = 0; added <= i; added++) {
899  auto proof = pm.getProof(proofs[added]->getId());
900  BOOST_CHECK(proof != nullptr);
901 
902  const ProofId &proofid = proof->getId();
903  BOOST_CHECK_EQUAL(proofid, proofs[added]->getId());
904  }
905  }
906 
907  // No stake, copied from proof_tests.cpp
908  const std::string badProofHex(
909  "96527eae083f1f24625f049d9e54bb9a21023beefdde700a6bc02036335b4df141c8b"
910  "c67bb05a971f5ac2745fd683797dde3002321023beefdde700a6bc02036335b4df141"
911  "c8bc67bb05a971f5ac2745fd683797dde3ac135da984db510334abe41134e3d4ef09a"
912  "d006b1152be8bc413182bf6f947eac1f8580fe265a382195aa2d73935cabf86d90a8f"
913  "666d0a62385ae24732eca51575");
915  auto badProof = RCUPtr<Proof>::make();
916  BOOST_CHECK(Proof::FromHex(*badProof, badProofHex, error));
917 
919  BOOST_CHECK(!pm.registerProof(badProof, state));
921 }
922 
923 BOOST_FIXTURE_TEST_CASE(conflicting_proof_rescan, NoCoolDownFixture) {
924  ChainstateManager &chainman = *Assert(m_node.chainman);
926 
927  const CKey key = CKey::MakeCompressedKey();
928 
929  Chainstate &active_chainstate = chainman.ActiveChainstate();
930 
931  const COutPoint conflictingOutpoint = createUtxo(active_chainstate, key);
932  const COutPoint outpointToSend = createUtxo(active_chainstate, key);
933 
934  ProofRef proofToInvalidate =
935  buildProofWithSequence(key, {conflictingOutpoint, outpointToSend}, 20);
936  BOOST_CHECK(pm.registerProof(proofToInvalidate));
937 
938  ProofRef conflictingProof =
939  buildProofWithSequence(key, {conflictingOutpoint}, 10);
941  BOOST_CHECK(!pm.registerProof(conflictingProof, state));
943  BOOST_CHECK(pm.isInConflictingPool(conflictingProof->getId()));
944 
945  {
946  LOCK(cs_main);
947  CCoinsViewCache &coins = active_chainstate.CoinsTip();
948  // Make proofToInvalidate invalid
949  coins.SpendCoin(outpointToSend);
950  }
951 
952  pm.updatedBlockTip();
953 
954  BOOST_CHECK(!pm.exists(proofToInvalidate->getId()));
955 
956  BOOST_CHECK(!pm.isInConflictingPool(conflictingProof->getId()));
957  BOOST_CHECK(pm.isBoundToPeer(conflictingProof->getId()));
958 }
959 
960 BOOST_FIXTURE_TEST_CASE(conflicting_proof_selection, NoCoolDownFixture) {
961  const CKey key = CKey::MakeCompressedKey();
962 
963  const Amount amount(PROOF_DUST_THRESHOLD);
964  const uint32_t height = 100;
965  const bool is_coinbase = false;
966 
967  ChainstateManager &chainman = *Assert(m_node.chainman);
968  Chainstate &active_chainstate = chainman.ActiveChainstate();
969 
970  // This will be the conflicting UTXO for all the following proofs
971  auto conflictingOutpoint = createUtxo(active_chainstate, key, amount);
972 
973  auto proof_base = buildProofWithSequence(key, {conflictingOutpoint}, 10);
974 
975  ConflictingProofComparator comparator;
976  auto checkPreferred = [&](const ProofRef &candidate,
977  const ProofRef &reference, bool expectAccepted) {
978  BOOST_CHECK_EQUAL(comparator(candidate, reference), expectAccepted);
979  BOOST_CHECK_EQUAL(comparator(reference, candidate), !expectAccepted);
980 
982  BOOST_CHECK(pm.registerProof(reference));
983  BOOST_CHECK(pm.isBoundToPeer(reference->getId()));
984 
986  BOOST_CHECK_EQUAL(pm.registerProof(candidate, state), expectAccepted);
987  BOOST_CHECK_EQUAL(state.IsValid(), expectAccepted);
988  BOOST_CHECK_EQUAL(state.GetResult() ==
990  !expectAccepted);
991 
992  BOOST_CHECK_EQUAL(pm.isBoundToPeer(candidate->getId()), expectAccepted);
994  !expectAccepted);
995 
996  BOOST_CHECK_EQUAL(pm.isBoundToPeer(reference->getId()),
997  !expectAccepted);
998  BOOST_CHECK_EQUAL(pm.isInConflictingPool(reference->getId()),
999  expectAccepted);
1000  };
1001 
1002  // Same master key, lower sequence number
1003  checkPreferred(buildProofWithSequence(key, {conflictingOutpoint}, 9),
1004  proof_base, false);
1005  // Same master key, higher sequence number
1006  checkPreferred(buildProofWithSequence(key, {conflictingOutpoint}, 11),
1007  proof_base, true);
1008 
1009  auto buildProofFromAmounts = [&](const CKey &master,
1010  std::vector<Amount> &&amounts) {
1011  std::vector<std::tuple<COutPoint, Amount>> outpointsWithAmount{
1012  {conflictingOutpoint, amount}};
1013  std::transform(amounts.begin(), amounts.end(),
1014  std::back_inserter(outpointsWithAmount),
1015  [&key, &active_chainstate](const Amount amount) {
1016  return std::make_tuple(
1017  createUtxo(active_chainstate, key, amount),
1018  amount);
1019  });
1020  return buildProof(key, outpointsWithAmount, master, 0, height,
1021  is_coinbase, 0);
1022  };
1023 
1024  auto proof_multiUtxo = buildProofFromAmounts(
1025  key, {2 * PROOF_DUST_THRESHOLD, 2 * PROOF_DUST_THRESHOLD});
1026 
1027  // Test for both the same master and a different one. The sequence number
1028  // is the same for all these tests.
1029  for (const CKey &k : {key, CKey::MakeCompressedKey()}) {
1030  // Low amount
1031  checkPreferred(buildProofFromAmounts(
1033  proof_multiUtxo, false);
1034  // High amount
1035  checkPreferred(buildProofFromAmounts(k, {2 * PROOF_DUST_THRESHOLD,
1036  3 * PROOF_DUST_THRESHOLD}),
1037  proof_multiUtxo, true);
1038  // Same amount, low stake count
1039  checkPreferred(buildProofFromAmounts(k, {4 * PROOF_DUST_THRESHOLD}),
1040  proof_multiUtxo, true);
1041  // Same amount, high stake count
1042  checkPreferred(buildProofFromAmounts(k, {2 * PROOF_DUST_THRESHOLD,
1045  proof_multiUtxo, false);
1046  // Same amount, same stake count, selection is done on proof id
1047  auto proofSimilar = buildProofFromAmounts(
1049  checkPreferred(proofSimilar, proof_multiUtxo,
1050  proofSimilar->getId() < proof_multiUtxo->getId());
1051  }
1052 }
1053 
1054 BOOST_AUTO_TEST_CASE(conflicting_immature_proofs) {
1055  ChainstateManager &chainman = *Assert(m_node.chainman);
1056  gArgs.ForceSetArg("-avaproofstakeutxoconfirmations", "2");
1058 
1059  const CKey key = CKey::MakeCompressedKey();
1060 
1061  Chainstate &active_chainstate = chainman.ActiveChainstate();
1062 
1063  const COutPoint conflictingOutpoint = createUtxo(active_chainstate, key);
1064  const COutPoint matureOutpoint =
1065  createUtxo(active_chainstate, key, PROOF_DUST_THRESHOLD, 99);
1066 
1067  auto immature10 = buildProofWithSequence(key, {conflictingOutpoint}, 10);
1068  auto immature20 =
1069  buildProofWithSequence(key, {conflictingOutpoint, matureOutpoint}, 20);
1070 
1071  BOOST_CHECK(!pm.registerProof(immature10));
1072  BOOST_CHECK(pm.isImmature(immature10->getId()));
1073 
1074  BOOST_CHECK(!pm.registerProof(immature20));
1075  BOOST_CHECK(pm.isImmature(immature20->getId()));
1076  BOOST_CHECK(!pm.exists(immature10->getId()));
1077 
1078  // Build and register a valid proof that will conflict with the immature one
1079  auto proof30 = buildProofWithOutpoints(key, {matureOutpoint},
1080  PROOF_DUST_THRESHOLD, key, 30, 99);
1081  BOOST_CHECK(pm.registerProof(proof30));
1082  BOOST_CHECK(pm.isBoundToPeer(proof30->getId()));
1083 
1084  // Reorg to a shorter chain to make proof30 immature
1085  {
1086  BlockValidationState state;
1087  active_chainstate.InvalidateBlock(GetConfig(), state,
1088  chainman.ActiveTip());
1089  BOOST_CHECK_EQUAL(chainman.ActiveHeight(), 99);
1090  }
1091 
1092  // Check that a rescan will also select the preferred immature proof, in
1093  // this case proof30 will replace immature20.
1094  pm.updatedBlockTip();
1095 
1096  BOOST_CHECK(!pm.isBoundToPeer(proof30->getId()));
1097  BOOST_CHECK(pm.isImmature(proof30->getId()));
1098  BOOST_CHECK(!pm.exists(immature20->getId()));
1099 }
1100 
1101 BOOST_FIXTURE_TEST_CASE(preferred_conflicting_proof, NoCoolDownFixture) {
1102  ChainstateManager &chainman = *Assert(m_node.chainman);
1104 
1105  const CKey key = CKey::MakeCompressedKey();
1106  const COutPoint conflictingOutpoint =
1107  createUtxo(chainman.ActiveChainstate(), key);
1108 
1109  auto proofSeq10 = buildProofWithSequence(key, {conflictingOutpoint}, 10);
1110  auto proofSeq20 = buildProofWithSequence(key, {conflictingOutpoint}, 20);
1111  auto proofSeq30 = buildProofWithSequence(key, {conflictingOutpoint}, 30);
1112 
1113  BOOST_CHECK(pm.registerProof(proofSeq30));
1114  BOOST_CHECK(pm.isBoundToPeer(proofSeq30->getId()));
1115  BOOST_CHECK(!pm.isInConflictingPool(proofSeq30->getId()));
1116 
1117  // proofSeq10 is a worst candidate than proofSeq30, so it goes to the
1118  // conflicting pool.
1119  BOOST_CHECK(!pm.registerProof(proofSeq10));
1120  BOOST_CHECK(pm.isBoundToPeer(proofSeq30->getId()));
1121  BOOST_CHECK(!pm.isBoundToPeer(proofSeq10->getId()));
1122  BOOST_CHECK(pm.isInConflictingPool(proofSeq10->getId()));
1123 
1124  // proofSeq20 is a worst candidate than proofSeq30 but a better one than
1125  // proogSeq10, so it replaces it in the conflicting pool and proofSeq10 is
1126  // evicted.
1127  BOOST_CHECK(!pm.registerProof(proofSeq20));
1128  BOOST_CHECK(pm.isBoundToPeer(proofSeq30->getId()));
1129  BOOST_CHECK(!pm.isBoundToPeer(proofSeq20->getId()));
1130  BOOST_CHECK(pm.isInConflictingPool(proofSeq20->getId()));
1131  BOOST_CHECK(!pm.exists(proofSeq10->getId()));
1132 }
1133 
1134 BOOST_FIXTURE_TEST_CASE(update_next_conflict_time, NoCoolDownFixture) {
1135  ChainstateManager &chainman = *Assert(m_node.chainman);
1137 
1138  auto now = GetTime<std::chrono::seconds>();
1139  SetMockTime(now.count());
1140 
1141  // Updating the time of an unknown peer should fail
1142  for (size_t i = 0; i < 10; i++) {
1143  BOOST_CHECK(
1145  }
1146 
1147  auto proof =
1149  PeerId peerid = TestPeerManager::registerAndGetPeerId(pm, proof);
1150 
1151  auto checkNextPossibleConflictTime = [&](std::chrono::seconds expected) {
1152  BOOST_CHECK(pm.forPeer(proof->getId(), [&](const Peer &p) {
1153  return p.nextPossibleConflictTime == expected;
1154  }));
1155  };
1156 
1157  checkNextPossibleConflictTime(now);
1158 
1159  // Move the time in the past is not possible
1161  peerid, now - std::chrono::seconds{1}));
1162  checkNextPossibleConflictTime(now);
1163 
1165  peerid, now + std::chrono::seconds{1}));
1166  checkNextPossibleConflictTime(now + std::chrono::seconds{1});
1167 }
1168 
1169 BOOST_FIXTURE_TEST_CASE(register_force_accept, NoCoolDownFixture) {
1170  ChainstateManager &chainman = *Assert(m_node.chainman);
1172 
1173  const CKey key = CKey::MakeCompressedKey();
1174 
1175  const COutPoint conflictingOutpoint =
1176  createUtxo(chainman.ActiveChainstate(), key);
1177 
1178  auto proofSeq10 = buildProofWithSequence(key, {conflictingOutpoint}, 10);
1179  auto proofSeq20 = buildProofWithSequence(key, {conflictingOutpoint}, 20);
1180  auto proofSeq30 = buildProofWithSequence(key, {conflictingOutpoint}, 30);
1181 
1182  BOOST_CHECK(pm.registerProof(proofSeq30));
1183  BOOST_CHECK(pm.isBoundToPeer(proofSeq30->getId()));
1184  BOOST_CHECK(!pm.isInConflictingPool(proofSeq30->getId()));
1185 
1186  // proofSeq20 is a worst candidate than proofSeq30, so it goes to the
1187  // conflicting pool.
1188  BOOST_CHECK(!pm.registerProof(proofSeq20));
1189  BOOST_CHECK(pm.isBoundToPeer(proofSeq30->getId()));
1190  BOOST_CHECK(pm.isInConflictingPool(proofSeq20->getId()));
1191 
1192  // We can force the acceptance of proofSeq20
1193  using RegistrationMode = avalanche::PeerManager::RegistrationMode;
1194  BOOST_CHECK(pm.registerProof(proofSeq20, RegistrationMode::FORCE_ACCEPT));
1195  BOOST_CHECK(pm.isBoundToPeer(proofSeq20->getId()));
1196  BOOST_CHECK(pm.isInConflictingPool(proofSeq30->getId()));
1197 
1198  // We can also force the acceptance of a proof which is not already in the
1199  // conflicting pool.
1200  BOOST_CHECK(!pm.registerProof(proofSeq10));
1201  BOOST_CHECK(!pm.exists(proofSeq10->getId()));
1202 
1203  BOOST_CHECK(pm.registerProof(proofSeq10, RegistrationMode::FORCE_ACCEPT));
1204  BOOST_CHECK(pm.isBoundToPeer(proofSeq10->getId()));
1205  BOOST_CHECK(!pm.exists(proofSeq20->getId()));
1206  BOOST_CHECK(pm.isInConflictingPool(proofSeq30->getId()));
1207 
1208  // Attempting to register again fails, and has no impact on the pools
1209  for (size_t i = 0; i < 10; i++) {
1210  BOOST_CHECK(!pm.registerProof(proofSeq10));
1211  BOOST_CHECK(
1212  !pm.registerProof(proofSeq10, RegistrationMode::FORCE_ACCEPT));
1213 
1214  BOOST_CHECK(pm.isBoundToPeer(proofSeq10->getId()));
1215  BOOST_CHECK(!pm.exists(proofSeq20->getId()));
1216  BOOST_CHECK(pm.isInConflictingPool(proofSeq30->getId()));
1217  }
1218 
1219  // Revert between proofSeq10 and proofSeq30 a few times
1220  for (size_t i = 0; i < 10; i++) {
1221  BOOST_CHECK(
1222  pm.registerProof(proofSeq30, RegistrationMode::FORCE_ACCEPT));
1223 
1224  BOOST_CHECK(pm.isBoundToPeer(proofSeq30->getId()));
1225  BOOST_CHECK(pm.isInConflictingPool(proofSeq10->getId()));
1226 
1227  BOOST_CHECK(
1228  pm.registerProof(proofSeq10, RegistrationMode::FORCE_ACCEPT));
1229 
1230  BOOST_CHECK(pm.isBoundToPeer(proofSeq10->getId()));
1231  BOOST_CHECK(pm.isInConflictingPool(proofSeq30->getId()));
1232  }
1233 }
1234 
1235 BOOST_FIXTURE_TEST_CASE(evicted_proof, NoCoolDownFixture) {
1236  ChainstateManager &chainman = *Assert(m_node.chainman);
1238 
1239  const CKey key = CKey::MakeCompressedKey();
1240 
1241  const COutPoint conflictingOutpoint =
1242  createUtxo(chainman.ActiveChainstate(), key);
1243 
1244  auto proofSeq10 = buildProofWithSequence(key, {conflictingOutpoint}, 10);
1245  auto proofSeq20 = buildProofWithSequence(key, {conflictingOutpoint}, 20);
1246  auto proofSeq30 = buildProofWithSequence(key, {conflictingOutpoint}, 30);
1247 
1248  {
1249  ProofRegistrationState state;
1250  BOOST_CHECK(pm.registerProof(proofSeq30, state));
1251  BOOST_CHECK(state.IsValid());
1252  }
1253 
1254  {
1255  ProofRegistrationState state;
1256  BOOST_CHECK(!pm.registerProof(proofSeq20, state));
1258  }
1259 
1260  {
1261  ProofRegistrationState state;
1262  BOOST_CHECK(!pm.registerProof(proofSeq10, state));
1264  }
1265 }
1266 
1267 BOOST_AUTO_TEST_CASE(conflicting_proof_cooldown) {
1268  ChainstateManager &chainman = *Assert(m_node.chainman);
1270 
1271  const CKey key = CKey::MakeCompressedKey();
1272 
1273  const COutPoint conflictingOutpoint =
1274  createUtxo(chainman.ActiveChainstate(), key);
1275 
1276  auto proofSeq20 = buildProofWithSequence(key, {conflictingOutpoint}, 20);
1277  auto proofSeq30 = buildProofWithSequence(key, {conflictingOutpoint}, 30);
1278  auto proofSeq40 = buildProofWithSequence(key, {conflictingOutpoint}, 40);
1279 
1280  int64_t conflictingProofCooldown = 100;
1281  gArgs.ForceSetArg("-avalancheconflictingproofcooldown",
1282  strprintf("%d", conflictingProofCooldown));
1283 
1284  int64_t now = GetTime();
1285 
1286  auto increaseMockTime = [&](int64_t s) {
1287  now += s;
1288  SetMockTime(now);
1289  };
1290  increaseMockTime(0);
1291 
1292  BOOST_CHECK(pm.registerProof(proofSeq30));
1293  BOOST_CHECK(pm.isBoundToPeer(proofSeq30->getId()));
1294 
1295  auto checkRegistrationFailure = [&](const ProofRef &proof,
1296  ProofRegistrationResult reason) {
1297  ProofRegistrationState state;
1298  BOOST_CHECK(!pm.registerProof(proof, state));
1299  BOOST_CHECK(state.GetResult() == reason);
1300  };
1301 
1302  // Registering a conflicting proof will fail due to the conflicting proof
1303  // cooldown
1304  checkRegistrationFailure(proofSeq20,
1306  BOOST_CHECK(!pm.exists(proofSeq20->getId()));
1307 
1308  // The cooldown applies as well if the proof is the favorite
1309  checkRegistrationFailure(proofSeq40,
1311  BOOST_CHECK(!pm.exists(proofSeq40->getId()));
1312 
1313  // Elapse the cooldown
1314  increaseMockTime(conflictingProofCooldown);
1315 
1316  // The proof will now be added to conflicting pool
1317  checkRegistrationFailure(proofSeq20, ProofRegistrationResult::CONFLICTING);
1318  BOOST_CHECK(pm.isInConflictingPool(proofSeq20->getId()));
1319 
1320  // But no other
1321  checkRegistrationFailure(proofSeq40,
1323  BOOST_CHECK(!pm.exists(proofSeq40->getId()));
1324  BOOST_CHECK(pm.isInConflictingPool(proofSeq20->getId()));
1325 
1326  // Elapse the cooldown
1327  increaseMockTime(conflictingProofCooldown);
1328 
1329  // The proof will now be accepted to replace proofSeq30, proofSeq30 will
1330  // move to the conflicting pool, and proofSeq20 will be evicted.
1331  BOOST_CHECK(pm.registerProof(proofSeq40));
1332  BOOST_CHECK(pm.isBoundToPeer(proofSeq40->getId()));
1333  BOOST_CHECK(pm.isInConflictingPool(proofSeq30->getId()));
1334  BOOST_CHECK(!pm.exists(proofSeq20->getId()));
1335 
1336  gArgs.ClearForcedArg("-avalancheconflictingproofcooldown");
1337 }
1338 
1339 BOOST_FIXTURE_TEST_CASE(reject_proof, NoCoolDownFixture) {
1340  ChainstateManager &chainman = *Assert(m_node.chainman);
1341  gArgs.ForceSetArg("-avaproofstakeutxoconfirmations", "2");
1343 
1344  const CKey key = CKey::MakeCompressedKey();
1345 
1346  Chainstate &active_chainstate = chainman.ActiveChainstate();
1347 
1348  const COutPoint conflictingOutpoint =
1349  createUtxo(active_chainstate, key, PROOF_DUST_THRESHOLD, 99);
1350  const COutPoint immatureOutpoint = createUtxo(active_chainstate, key);
1351 
1352  // The good, the bad and the ugly
1353  auto proofSeq10 = buildProofWithOutpoints(
1354  key, {conflictingOutpoint}, PROOF_DUST_THRESHOLD, key, 10, 99);
1355  auto proofSeq20 = buildProofWithOutpoints(
1356  key, {conflictingOutpoint}, PROOF_DUST_THRESHOLD, key, 20, 99);
1357  auto immature30 = buildProofWithSequence(
1358  key, {conflictingOutpoint, immatureOutpoint}, 30);
1359 
1360  BOOST_CHECK(pm.registerProof(proofSeq20));
1361  BOOST_CHECK(!pm.registerProof(proofSeq10));
1362  BOOST_CHECK(!pm.registerProof(immature30));
1363 
1364  BOOST_CHECK(pm.isBoundToPeer(proofSeq20->getId()));
1365  BOOST_CHECK(pm.isInConflictingPool(proofSeq10->getId()));
1366  BOOST_CHECK(pm.isImmature(immature30->getId()));
1367 
1368  // Rejecting a proof that doesn't exist should fail
1369  for (size_t i = 0; i < 10; i++) {
1370  BOOST_CHECK(
1373  BOOST_CHECK(
1376  }
1377 
1378  auto checkRejectDefault = [&](const ProofId &proofid) {
1379  BOOST_CHECK(pm.exists(proofid));
1380  const bool isImmature = pm.isImmature(proofid);
1383  BOOST_CHECK(!pm.isBoundToPeer(proofid));
1384  BOOST_CHECK_EQUAL(pm.exists(proofid), !isImmature);
1385  };
1386 
1387  auto checkRejectInvalidate = [&](const ProofId &proofid) {
1388  BOOST_CHECK(pm.exists(proofid));
1391  };
1392 
1393  // Reject from the immature pool
1394  checkRejectDefault(immature30->getId());
1395  BOOST_CHECK(!pm.registerProof(immature30));
1396  BOOST_CHECK(pm.isImmature(immature30->getId()));
1397  checkRejectInvalidate(immature30->getId());
1398 
1399  // Reject from the conflicting pool
1400  checkRejectDefault(proofSeq10->getId());
1401  checkRejectInvalidate(proofSeq10->getId());
1402 
1403  // Add again a proof to the conflicting pool
1404  BOOST_CHECK(!pm.registerProof(proofSeq10));
1405  BOOST_CHECK(pm.isInConflictingPool(proofSeq10->getId()));
1406 
1407  // Reject from the valid pool, default mode
1408  checkRejectDefault(proofSeq20->getId());
1409 
1410  // The conflicting proof should be promoted to a peer
1411  BOOST_CHECK(!pm.isInConflictingPool(proofSeq10->getId()));
1412  BOOST_CHECK(pm.isBoundToPeer(proofSeq10->getId()));
1413 
1414  // Reject from the valid pool, invalidate mode
1415  checkRejectInvalidate(proofSeq10->getId());
1416 
1417  // The conflicting proof should also be promoted to a peer
1418  BOOST_CHECK(!pm.isInConflictingPool(proofSeq20->getId()));
1419  BOOST_CHECK(pm.isBoundToPeer(proofSeq20->getId()));
1420 }
1421 
1422 BOOST_AUTO_TEST_CASE(should_request_more_nodes) {
1423  ChainstateManager &chainman = *Assert(m_node.chainman);
1425 
1426  // Set mock time so that proof registration time is predictable and
1427  // testable.
1428  SetMockTime(GetTime());
1429 
1430  auto proof =
1432  BOOST_CHECK(pm.registerProof(proof));
1433 
1434  // We have no nodes, so select node will fail and flag that we need more
1435  // nodes
1438 
1439  for (size_t i = 0; i < 10; i++) {
1440  // The flag will not trigger again until we fail to select nodes again
1442  }
1443 
1444  // Add a few nodes.
1445  const ProofId &proofid = proof->getId();
1446  for (size_t i = 0; i < 10; i++) {
1447  BOOST_CHECK(pm.addNode(i, proofid));
1448  }
1449 
1450  auto cooldownTimepoint = std::chrono::steady_clock::now() + 10s;
1451 
1452  // All the nodes can be selected once
1453  for (size_t i = 0; i < 10; i++) {
1454  NodeId selectedId = pm.selectNode();
1455  BOOST_CHECK_NE(selectedId, NO_NODE);
1456  BOOST_CHECK(pm.updateNextRequestTime(selectedId, cooldownTimepoint));
1458  }
1459 
1460  // All the nodes have been requested, next select will fail and the flag
1461  // should trigger
1464 
1465  for (size_t i = 0; i < 10; i++) {
1466  // The flag will not trigger again until we fail to select nodes again
1468  }
1469 
1470  // Make it possible to request a node again
1471  BOOST_CHECK(pm.updateNextRequestTime(0, std::chrono::steady_clock::now()));
1472  BOOST_CHECK_NE(pm.selectNode(), NO_NODE);
1474 
1475  // Add another proof with no node attached
1476  auto proof2 =
1478  BOOST_CHECK(pm.registerProof(proof2));
1481 
1482  // After some time the proof will be considered dangling and more nodes will
1483  // be requested.
1484  SetMockTime(GetTime() + 15 * 60);
1487 
1488  for (size_t i = 0; i < 10; i++) {
1489  // The flag will not trigger again until the condition is met again
1491  }
1492 
1493  // Attempt to register the dangling proof again. This should fail but
1494  // trigger a request for more nodes.
1495  ProofRegistrationState state;
1496  BOOST_CHECK(!pm.registerProof(proof2, state));
1499 
1500  for (size_t i = 0; i < 10; i++) {
1501  // The flag will not trigger again until the condition is met again
1503  }
1504 
1505  // Attach a node to that proof
1506  BOOST_CHECK(!pm.addNode(11, proof2->getId()));
1507  BOOST_CHECK(pm.registerProof(proof2));
1508  SetMockTime(GetTime() + 15 * 60);
1511 
1512  // Disconnect the node, the proof is dangling again
1513  BOOST_CHECK(pm.removeNode(11));
1516 }
1517 
1518 BOOST_AUTO_TEST_CASE(score_ordering) {
1519  ChainstateManager &chainman = *Assert(m_node.chainman);
1521 
1522  std::vector<uint32_t> expectedScores(10);
1523  // Expect the peers to be ordered by descending score
1524  std::generate(expectedScores.rbegin(), expectedScores.rend(),
1525  [n = 1]() mutable { return n++ * MIN_VALID_PROOF_SCORE; });
1526 
1527  std::vector<ProofRef> proofs;
1528  proofs.reserve(expectedScores.size());
1529  for (uint32_t score : expectedScores) {
1530  proofs.push_back(buildRandomProof(chainman.ActiveChainstate(), score));
1531  }
1532 
1533  // Shuffle the proofs so they are registered in a random score order
1534  Shuffle(proofs.begin(), proofs.end(), FastRandomContext());
1535  for (auto &proof : proofs) {
1536  BOOST_CHECK(pm.registerProof(proof));
1537  }
1538 
1539  auto peersScores = TestPeerManager::getOrderedScores(pm);
1540  BOOST_CHECK_EQUAL_COLLECTIONS(peersScores.begin(), peersScores.end(),
1541  expectedScores.begin(), expectedScores.end());
1542 }
1543 
1544 BOOST_FIXTURE_TEST_CASE(known_score_tracking, NoCoolDownFixture) {
1545  ChainstateManager &chainman = *Assert(m_node.chainman);
1546  gArgs.ForceSetArg("-avaproofstakeutxoconfirmations", "2");
1548 
1549  const CKey key = CKey::MakeCompressedKey();
1550 
1551  const Amount amount1(PROOF_DUST_THRESHOLD);
1552  const Amount amount2(2 * PROOF_DUST_THRESHOLD);
1553 
1554  Chainstate &active_chainstate = chainman.ActiveChainstate();
1555 
1556  const COutPoint peer1ConflictingOutput =
1557  createUtxo(active_chainstate, key, amount1, 99);
1558  const COutPoint peer1SecondaryOutpoint =
1559  createUtxo(active_chainstate, key, amount2, 99);
1560 
1561  auto peer1Proof1 = buildProof(
1562  key,
1563  {{peer1ConflictingOutput, amount1}, {peer1SecondaryOutpoint, amount2}},
1564  key, 10, 99);
1565  auto peer1Proof2 =
1566  buildProof(key, {{peer1ConflictingOutput, amount1}}, key, 20, 99);
1567 
1568  // Create a proof with an immature UTXO, so the proof will be immature
1569  auto peer1Proof3 =
1570  buildProof(key,
1571  {{peer1ConflictingOutput, amount1},
1572  {createUtxo(active_chainstate, key, amount1), amount1}},
1573  key, 30);
1574 
1575  const uint32_t peer1Score1 = Proof::amountToScore(amount1 + amount2);
1576  const uint32_t peer1Score2 = Proof::amountToScore(amount1);
1577 
1578  // Add first peer and check that we have its score tracked
1580  BOOST_CHECK(pm.registerProof(peer1Proof2));
1581  BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), peer1Score2);
1582 
1583  // Ensure failing to add conflicting proofs doesn't affect the score, the
1584  // first proof stays bound and counted
1585  BOOST_CHECK(!pm.registerProof(peer1Proof1));
1586  BOOST_CHECK(!pm.registerProof(peer1Proof3));
1587 
1588  BOOST_CHECK(pm.isBoundToPeer(peer1Proof2->getId()));
1589  BOOST_CHECK(pm.isInConflictingPool(peer1Proof1->getId()));
1590  BOOST_CHECK(pm.isImmature(peer1Proof3->getId()));
1591 
1592  BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), peer1Score2);
1593 
1594  auto checkRejectDefault = [&](const ProofId &proofid) {
1595  BOOST_CHECK(pm.exists(proofid));
1596  const bool isImmature = pm.isImmature(proofid);
1599  BOOST_CHECK(!pm.isBoundToPeer(proofid));
1600  BOOST_CHECK_EQUAL(pm.exists(proofid), !isImmature);
1601  };
1602 
1603  auto checkRejectInvalidate = [&](const ProofId &proofid) {
1604  BOOST_CHECK(pm.exists(proofid));
1607  };
1608 
1609  // Reject from the immature pool doesn't affect tracked score
1610  checkRejectDefault(peer1Proof3->getId());
1611  BOOST_CHECK(!pm.registerProof(peer1Proof3));
1612  BOOST_CHECK(pm.isImmature(peer1Proof3->getId()));
1613  BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), peer1Score2);
1614  checkRejectInvalidate(peer1Proof3->getId());
1615  BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), peer1Score2);
1616 
1617  // Reject from the conflicting pool
1618  checkRejectDefault(peer1Proof1->getId());
1619  checkRejectInvalidate(peer1Proof1->getId());
1620 
1621  // Add again a proof to the conflicting pool
1622  BOOST_CHECK(!pm.registerProof(peer1Proof1));
1623  BOOST_CHECK(pm.isInConflictingPool(peer1Proof1->getId()));
1624  BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), peer1Score2);
1625 
1626  // Reject from the valid pool, default mode
1627  // Now the score should change as the new peer is promoted
1628  checkRejectDefault(peer1Proof2->getId());
1629  BOOST_CHECK(!pm.isInConflictingPool(peer1Proof1->getId()));
1630  BOOST_CHECK(pm.isBoundToPeer(peer1Proof1->getId()));
1631  BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), peer1Score1);
1632 
1633  // Reject from the valid pool, invalidate mode
1634  // Now the score should change as the old peer is re-promoted
1635  checkRejectInvalidate(peer1Proof1->getId());
1636 
1637  // The conflicting proof should also be promoted to a peer
1638  BOOST_CHECK(!pm.isInConflictingPool(peer1Proof2->getId()));
1639  BOOST_CHECK(pm.isBoundToPeer(peer1Proof2->getId()));
1640  BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), peer1Score2);
1641 
1642  // Now add another peer and check that combined scores are correct
1643  uint32_t peer2Score = 1 * MIN_VALID_PROOF_SCORE;
1644  auto peer2Proof1 = buildRandomProof(active_chainstate, peer2Score, 99);
1645  PeerId peerid2 = TestPeerManager::registerAndGetPeerId(pm, peer2Proof1);
1646  BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), peer1Score2 + peer2Score);
1647 
1648  // Trying to remove non-existent peer doesn't affect score
1649  BOOST_CHECK(!pm.removePeer(1234));
1650  BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), peer1Score2 + peer2Score);
1651 
1652  // Removing new peer removes its score
1653  BOOST_CHECK(pm.removePeer(peerid2));
1654  BOOST_CHECK_EQUAL(pm.getTotalPeersScore(), peer1Score2);
1655  PeerId peerid1 =
1656  TestPeerManager::getPeerIdForProofId(pm, peer1Proof2->getId());
1657  BOOST_CHECK(pm.removePeer(peerid1));
1659 }
1660 
1661 BOOST_AUTO_TEST_CASE(connected_score_tracking) {
1662  ChainstateManager &chainman = *Assert(m_node.chainman);
1664 
1665  const auto checkScores = [&pm](uint32_t known, uint32_t connected) {
1667  BOOST_CHECK_EQUAL(pm.getConnectedPeersScore(), connected);
1668  };
1669 
1670  // Start out with 0s
1671  checkScores(0, 0);
1672 
1673  Chainstate &active_chainstate = chainman.ActiveChainstate();
1674 
1675  // Create one peer without a node. Its score should be registered but not
1676  // connected
1677  uint32_t score1 = 10000000 * MIN_VALID_PROOF_SCORE;
1678  auto proof1 = buildRandomProof(active_chainstate, score1);
1679  PeerId peerid1 = TestPeerManager::registerAndGetPeerId(pm, proof1);
1680  checkScores(score1, 0);
1681 
1682  // Add nodes. We now have a connected score, but it doesn't matter how many
1683  // nodes we add the score is the same
1684  const ProofId &proofid1 = proof1->getId();
1685  const uint8_t nodesToAdd = 10;
1686  for (int i = 0; i < nodesToAdd; i++) {
1687  BOOST_CHECK(pm.addNode(i, proofid1));
1688  checkScores(score1, score1);
1689  }
1690 
1691  // Remove all but 1 node and ensure the score doesn't change
1692  for (int i = 0; i < nodesToAdd - 1; i++) {
1693  BOOST_CHECK(pm.removeNode(i));
1694  checkScores(score1, score1);
1695  }
1696 
1697  // Removing the last node should remove the score from the connected count
1698  BOOST_CHECK(pm.removeNode(nodesToAdd - 1));
1699  checkScores(score1, 0);
1700 
1701  // Add 2 nodes to peer and create peer2. Without a node peer2 has no
1702  // connected score but after adding a node it does.
1703  BOOST_CHECK(pm.addNode(0, proofid1));
1704  BOOST_CHECK(pm.addNode(1, proofid1));
1705  checkScores(score1, score1);
1706 
1707  uint32_t score2 = 1 * MIN_VALID_PROOF_SCORE;
1708  auto proof2 = buildRandomProof(active_chainstate, score2);
1709  PeerId peerid2 = TestPeerManager::registerAndGetPeerId(pm, proof2);
1710  checkScores(score1 + score2, score1);
1711  BOOST_CHECK(pm.addNode(2, proof2->getId()));
1712  checkScores(score1 + score2, score1 + score2);
1713 
1714  // The first peer has two nodes left. Remove one and nothing happens, remove
1715  // the other and its score is no longer in the connected counter..
1716  BOOST_CHECK(pm.removeNode(0));
1717  checkScores(score1 + score2, score1 + score2);
1718  BOOST_CHECK(pm.removeNode(1));
1719  checkScores(score1 + score2, score2);
1720 
1721  // Removing a peer with no allocated score has no affect.
1722  BOOST_CHECK(pm.removePeer(peerid1));
1723  checkScores(score2, score2);
1724 
1725  // Remove the second peer's node removes its allocated score.
1726  BOOST_CHECK(pm.removeNode(2));
1727  checkScores(score2, 0);
1728 
1729  // Removing the second peer takes us back to 0.
1730  BOOST_CHECK(pm.removePeer(peerid2));
1731  checkScores(0, 0);
1732 
1733  // Add 2 peers with nodes and remove them without removing the nodes first.
1734  // Both score counters should be reduced by each peer's score when it's
1735  // removed.
1736  peerid1 = TestPeerManager::registerAndGetPeerId(pm, proof1);
1737  checkScores(score1, 0);
1738  peerid2 = TestPeerManager::registerAndGetPeerId(pm, proof2);
1739  checkScores(score1 + score2, 0);
1740  BOOST_CHECK(pm.addNode(0, proof1->getId()));
1741  checkScores(score1 + score2, score1);
1742  BOOST_CHECK(pm.addNode(1, proof2->getId()));
1743  checkScores(score1 + score2, score1 + score2);
1744 
1745  BOOST_CHECK(pm.removePeer(peerid2));
1746  checkScores(score1, score1);
1747 
1748  BOOST_CHECK(pm.removePeer(peerid1));
1749  checkScores(0, 0);
1750 }
1751 
1752 BOOST_FIXTURE_TEST_CASE(proof_radix_tree, NoCoolDownFixture) {
1753  ChainstateManager &chainman = *Assert(m_node.chainman);
1755 
1756  struct ProofComparatorById {
1757  bool operator()(const ProofRef &lhs, const ProofRef &rhs) const {
1758  return lhs->getId() < rhs->getId();
1759  };
1760  };
1761  using ProofSetById = std::set<ProofRef, ProofComparatorById>;
1762  // Maintain a list of the expected proofs through this test
1763  ProofSetById expectedProofs;
1764 
1765  auto matchExpectedContent = [&](const auto &tree) {
1766  auto it = expectedProofs.begin();
1767  return tree.forEachLeaf([&](auto pLeaf) {
1768  return it != expectedProofs.end() &&
1769  pLeaf->getId() == (*it++)->getId();
1770  });
1771  };
1772 
1773  CKey key = CKey::MakeCompressedKey();
1774  const int64_t sequence = 10;
1775 
1776  Chainstate &active_chainstate = chainman.ActiveChainstate();
1777 
1778  // Add some initial proofs
1779  for (size_t i = 0; i < 10; i++) {
1780  auto outpoint = createUtxo(active_chainstate, key);
1781  auto proof = buildProofWithSequence(key, {{outpoint}}, sequence);
1782  BOOST_CHECK(pm.registerProof(proof));
1783  expectedProofs.insert(std::move(proof));
1784  }
1785 
1786  const auto &treeRef = pm.getShareableProofsSnapshot();
1787  BOOST_CHECK(matchExpectedContent(treeRef));
1788 
1789  // Create a copy
1790  auto tree = pm.getShareableProofsSnapshot();
1791 
1792  // Adding more proofs doesn't change the tree...
1793  ProofSetById addedProofs;
1794  std::vector<COutPoint> outpointsToSpend;
1795  for (size_t i = 0; i < 10; i++) {
1796  auto outpoint = createUtxo(active_chainstate, key);
1797  auto proof = buildProofWithSequence(key, {{outpoint}}, sequence);
1798  BOOST_CHECK(pm.registerProof(proof));
1799  addedProofs.insert(std::move(proof));
1800  outpointsToSpend.push_back(std::move(outpoint));
1801  }
1802 
1803  BOOST_CHECK(matchExpectedContent(tree));
1804 
1805  // ...until we get a new copy
1806  tree = pm.getShareableProofsSnapshot();
1807  expectedProofs.insert(addedProofs.begin(), addedProofs.end());
1808  BOOST_CHECK(matchExpectedContent(tree));
1809 
1810  // Spend some coins to make the associated proofs invalid
1811  {
1812  LOCK(cs_main);
1813  CCoinsViewCache &coins = active_chainstate.CoinsTip();
1814  for (const auto &outpoint : outpointsToSpend) {
1815  coins.SpendCoin(outpoint);
1816  }
1817  }
1818 
1819  pm.updatedBlockTip();
1820 
1821  // This doesn't change the tree...
1822  BOOST_CHECK(matchExpectedContent(tree));
1823 
1824  // ...until we get a new copy
1825  tree = pm.getShareableProofsSnapshot();
1826  for (const auto &proof : addedProofs) {
1827  BOOST_CHECK_EQUAL(expectedProofs.erase(proof), 1);
1828  }
1829  BOOST_CHECK(matchExpectedContent(tree));
1830 
1831  // Add some more proof for which we will create conflicts
1832  std::vector<ProofRef> conflictingProofs;
1833  std::vector<COutPoint> conflictingOutpoints;
1834  for (size_t i = 0; i < 10; i++) {
1835  auto outpoint = createUtxo(active_chainstate, key);
1836  auto proof = buildProofWithSequence(key, {{outpoint}}, sequence);
1837  BOOST_CHECK(pm.registerProof(proof));
1838  conflictingProofs.push_back(std::move(proof));
1839  conflictingOutpoints.push_back(std::move(outpoint));
1840  }
1841 
1842  tree = pm.getShareableProofsSnapshot();
1843  expectedProofs.insert(conflictingProofs.begin(), conflictingProofs.end());
1844  BOOST_CHECK(matchExpectedContent(tree));
1845 
1846  // Build a bunch of conflicting proofs, half better, half worst
1847  for (size_t i = 0; i < 10; i += 2) {
1848  // The worst proof is not added to the expected set
1849  BOOST_CHECK(!pm.registerProof(buildProofWithSequence(
1850  key, {{conflictingOutpoints[i]}}, sequence - 1)));
1851 
1852  // But the better proof should replace its conflicting one
1853  auto replacementProof = buildProofWithSequence(
1854  key, {{conflictingOutpoints[i + 1]}}, sequence + 1);
1855  BOOST_CHECK(pm.registerProof(replacementProof));
1856  BOOST_CHECK_EQUAL(expectedProofs.erase(conflictingProofs[i + 1]), 1);
1857  BOOST_CHECK(expectedProofs.insert(replacementProof).second);
1858  }
1859 
1860  tree = pm.getShareableProofsSnapshot();
1861  BOOST_CHECK(matchExpectedContent(tree));
1862 
1863  // Check for consistency
1864  pm.verify();
1865 }
1866 
1867 BOOST_AUTO_TEST_CASE(received_avaproofs) {
1868  ChainstateManager &chainman = *Assert(m_node.chainman);
1870 
1871  auto addNode = [&](NodeId nodeid) {
1872  auto proof = buildRandomProof(chainman.ActiveChainstate(),
1874  BOOST_CHECK(pm.registerProof(proof));
1875  BOOST_CHECK(pm.addNode(nodeid, proof->getId()));
1876  };
1877 
1878  for (NodeId nodeid = 0; nodeid < 10; nodeid++) {
1879  // Node doesn't exist
1880  BOOST_CHECK(!pm.latchAvaproofsSent(nodeid));
1881 
1882  addNode(nodeid);
1883  BOOST_CHECK(pm.latchAvaproofsSent(nodeid));
1884 
1885  // The flag is already set
1886  BOOST_CHECK(!pm.latchAvaproofsSent(nodeid));
1887  }
1888 }
1889 
1890 BOOST_FIXTURE_TEST_CASE(cleanup_dangling_proof, NoCoolDownFixture) {
1891  ChainstateManager &chainman = *Assert(m_node.chainman);
1892 
1894 
1895  const auto now = GetTime<std::chrono::seconds>();
1896  auto mocktime = now;
1897 
1898  auto elapseTime = [&](std::chrono::seconds seconds) {
1899  mocktime += seconds;
1900  SetMockTime(mocktime.count());
1901  };
1902  elapseTime(0s);
1903 
1904  const CKey key = CKey::MakeCompressedKey();
1905 
1906  const size_t numProofs = 10;
1907 
1908  std::vector<COutPoint> outpoints(numProofs);
1909  std::vector<ProofRef> proofs(numProofs);
1910  std::vector<ProofRef> conflictingProofs(numProofs);
1911  for (size_t i = 0; i < numProofs; i++) {
1912  outpoints[i] = createUtxo(chainman.ActiveChainstate(), key);
1913  proofs[i] = buildProofWithSequence(key, {outpoints[i]}, 2);
1914  conflictingProofs[i] = buildProofWithSequence(key, {outpoints[i]}, 1);
1915 
1916  BOOST_CHECK(pm.registerProof(proofs[i]));
1917  BOOST_CHECK(pm.isBoundToPeer(proofs[i]->getId()));
1918 
1919  BOOST_CHECK(!pm.registerProof(conflictingProofs[i]));
1920  BOOST_CHECK(pm.isInConflictingPool(conflictingProofs[i]->getId()));
1921 
1922  if (i % 2) {
1923  // Odd indexes get a node attached to them
1924  BOOST_CHECK(pm.addNode(i, proofs[i]->getId()));
1925  }
1926  BOOST_CHECK_EQUAL(pm.forPeer(proofs[i]->getId(),
1927  [&](const avalanche::Peer &peer) {
1928  return peer.node_count;
1929  }),
1930  i % 2);
1931 
1932  elapseTime(1s);
1933  }
1934 
1935  // No proof expired yet
1936  TestPeerManager::cleanupDanglingProofs(pm);
1937  for (size_t i = 0; i < numProofs; i++) {
1938  BOOST_CHECK(pm.isBoundToPeer(proofs[i]->getId()));
1939  BOOST_CHECK(pm.isInConflictingPool(conflictingProofs[i]->getId()));
1940  }
1941 
1942  // Elapse the dangling timeout
1944  TestPeerManager::cleanupDanglingProofs(pm);
1945  for (size_t i = 0; i < numProofs; i++) {
1946  const bool hasNodeAttached = i % 2;
1947 
1948  // Only the peers with no nodes attached are getting discarded
1949  BOOST_CHECK_EQUAL(pm.isBoundToPeer(proofs[i]->getId()),
1950  hasNodeAttached);
1951  BOOST_CHECK_EQUAL(!pm.exists(proofs[i]->getId()), !hasNodeAttached);
1952 
1953  // The proofs conflicting with the discarded ones are pulled back
1954  BOOST_CHECK_EQUAL(pm.isInConflictingPool(conflictingProofs[i]->getId()),
1955  hasNodeAttached);
1956  BOOST_CHECK_EQUAL(pm.isBoundToPeer(conflictingProofs[i]->getId()),
1957  !hasNodeAttached);
1958  }
1959 
1960  // Attach a node to the first conflicting proof, which has been promoted
1961  BOOST_CHECK(pm.addNode(42, conflictingProofs[0]->getId()));
1962  BOOST_CHECK(pm.forPeer(
1963  conflictingProofs[0]->getId(),
1964  [&](const avalanche::Peer &peer) { return peer.node_count == 1; }));
1965 
1966  // Elapse the dangling timeout again
1968  TestPeerManager::cleanupDanglingProofs(pm);
1969  for (size_t i = 0; i < numProofs; i++) {
1970  const bool hasNodeAttached = i % 2;
1971 
1972  // The initial peers with a node attached are still there
1973  BOOST_CHECK_EQUAL(pm.isBoundToPeer(proofs[i]->getId()),
1974  hasNodeAttached);
1975  BOOST_CHECK_EQUAL(!pm.exists(proofs[i]->getId()), !hasNodeAttached);
1976 
1977  // This time the previouly promoted conflicting proofs are evicted
1978  // because they have no node attached, except the index 0.
1979  BOOST_CHECK_EQUAL(pm.exists(conflictingProofs[i]->getId()),
1980  hasNodeAttached || i == 0);
1981  BOOST_CHECK_EQUAL(pm.isInConflictingPool(conflictingProofs[i]->getId()),
1982  hasNodeAttached);
1983  BOOST_CHECK_EQUAL(pm.isBoundToPeer(conflictingProofs[i]->getId()),
1984  i == 0);
1985  }
1986 
1987  // Disconnect all the nodes
1988  for (size_t i = 1; i < numProofs; i += 2) {
1989  BOOST_CHECK(pm.removeNode(i));
1990  BOOST_CHECK(
1991  pm.forPeer(proofs[i]->getId(), [&](const avalanche::Peer &peer) {
1992  return peer.node_count == 0;
1993  }));
1994  }
1995  BOOST_CHECK(pm.removeNode(42));
1996  BOOST_CHECK(pm.forPeer(
1997  conflictingProofs[0]->getId(),
1998  [&](const avalanche::Peer &peer) { return peer.node_count == 0; }));
1999 
2000  TestPeerManager::cleanupDanglingProofs(pm);
2001  for (size_t i = 0; i < numProofs; i++) {
2002  const bool hadNodeAttached = i % 2;
2003 
2004  // All initially valid proofs have now been discarded
2005  BOOST_CHECK(!pm.exists(proofs[i]->getId()));
2006 
2007  // The remaining conflicting proofs are promoted
2008  BOOST_CHECK_EQUAL(!pm.exists(conflictingProofs[i]->getId()),
2009  !hadNodeAttached);
2010  BOOST_CHECK(!pm.isInConflictingPool(conflictingProofs[i]->getId()));
2011  BOOST_CHECK_EQUAL(pm.isBoundToPeer(conflictingProofs[i]->getId()),
2012  hadNodeAttached);
2013  }
2014 
2015  // Elapse the timeout for the newly promoted conflicting proofs
2017 
2018  // Use the first conflicting proof as our local proof
2019  TestPeerManager::cleanupDanglingProofs(pm, conflictingProofs[1]);
2020 
2021  for (size_t i = 0; i < numProofs; i++) {
2022  // All other proofs have now been discarded
2023  BOOST_CHECK(!pm.exists(proofs[i]->getId()));
2024  BOOST_CHECK_EQUAL(!pm.exists(conflictingProofs[i]->getId()), i != 1);
2025  }
2026 
2027  // Cleanup one more time with no local proof
2028  TestPeerManager::cleanupDanglingProofs(pm);
2029 
2030  for (size_t i = 0; i < numProofs; i++) {
2031  // All proofs have finally been discarded
2032  BOOST_CHECK(!pm.exists(proofs[i]->getId()));
2033  BOOST_CHECK(!pm.exists(conflictingProofs[i]->getId()));
2034  }
2035 }
2036 
2037 BOOST_AUTO_TEST_CASE(register_proof_missing_utxo) {
2038  ChainstateManager &chainman = *Assert(m_node.chainman);
2040 
2041  CKey key = CKey::MakeCompressedKey();
2042  auto proof = buildProofWithOutpoints(key, {{TxId(GetRandHash()), 0}},
2044 
2045  ProofRegistrationState state;
2046  BOOST_CHECK(!pm.registerProof(proof, state));
2047  BOOST_CHECK(state.GetResult() == ProofRegistrationResult::MISSING_UTXO);
2048 }
2049 
2050 BOOST_AUTO_TEST_CASE(proof_expiry) {
2051  gArgs.ForceSetArg("-avalancheconflictingproofcooldown", "0");
2052 
2053  ChainstateManager &chainman = *Assert(m_node.chainman);
2055 
2056  const int64_t tipTime = chainman.ActiveTip()->GetBlockTime();
2057 
2058  CKey key = CKey::MakeCompressedKey();
2059 
2060  auto utxo = createUtxo(chainman.ActiveChainstate(), key);
2061  auto proofToExpire = buildProof(key, {{utxo, PROOF_DUST_THRESHOLD}}, key, 2,
2062  100, false, tipTime + 1);
2063  auto conflictingProof = buildProof(key, {{utxo, PROOF_DUST_THRESHOLD}}, key,
2064  1, 100, false, tipTime + 2);
2065 
2066  // Our proofToExpire is not expired yet, so it registers fine
2067  BOOST_CHECK(pm.registerProof(proofToExpire));
2068  BOOST_CHECK(pm.isBoundToPeer(proofToExpire->getId()));
2069 
2070  // The conflicting proof has a longer expiration time but a lower sequence
2071  // number, so it is moved to the conflicting pool.
2072  BOOST_CHECK(!pm.registerProof(conflictingProof));
2073  BOOST_CHECK(pm.isInConflictingPool(conflictingProof->getId()));
2074 
2075  // Mine blocks until the MTP of the tip moves to the proof expiration
2076  for (int64_t i = 0; i < 6; i++) {
2077  SetMockTime(proofToExpire->getExpirationTime() + i);
2078  CreateAndProcessBlock({}, CScript());
2079  }
2081  proofToExpire->getExpirationTime());
2082 
2083  pm.updatedBlockTip();
2084 
2085  // The now expired proof is removed
2086  BOOST_CHECK(!pm.exists(proofToExpire->getId()));
2087 
2088  // The conflicting proof has been pulled back to the valid pool
2089  BOOST_CHECK(pm.isBoundToPeer(conflictingProof->getId()));
2090 
2091  gArgs.ClearForcedArg("-avalancheconflictingproofcooldown");
2092 }
2093 
2094 BOOST_AUTO_TEST_CASE(peer_availability_score) {
2095  ChainstateManager &chainman = *Assert(m_node.chainman);
2097  Chainstate &active_chainstate = chainman.ActiveChainstate();
2098 
2099  const std::vector<std::tuple<uint32_t, uint32_t, double>> testCases = {
2100  // {step, tau, decay_factor}
2101  {10, 100, 1. - std::exp(-1. * 10 / 100)},
2102  // Current defaults
2106  };
2107 
2108  for (const auto &[step, tau, decayFactor] : testCases) {
2109  // Add a peer
2110  auto proof = buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE);
2111  BOOST_CHECK(pm.registerProof(proof));
2112  auto proofid = proof->getId();
2113 
2114  // Add some nodes for this peer
2115  const int numNodesPerPeer = 5;
2116  for (auto nodeid = 0; nodeid < numNodesPerPeer; nodeid++) {
2117  BOOST_CHECK(pm.addNode(nodeid, proofid));
2118  }
2119 
2120  auto getNodeAvailabilityScore = [&](double avgScore,
2121  NodeId nodeid) -> double {
2122  // Spread scores over a range of values such that their average is
2123  // the provided value.
2124  return (nodeid - numNodesPerPeer / 2) * 2 + avgScore;
2125  };
2126 
2127  auto getAvailabilityScore = [&]() {
2128  double score{0.0};
2129  pm.forPeer(proofid, [&](auto &peer) {
2130  score = peer.availabilityScore;
2131  return true;
2132  });
2133  return score;
2134  };
2135 
2136  double previousScore = getAvailabilityScore();
2137  BOOST_CHECK_SMALL(previousScore, 1e-6);
2138 
2139  // Check the statistics follow an exponential response for 1 to 10 tau
2140  for (size_t i = 1; i <= 10; i++) {
2141  for (uint32_t j = 0; j < tau; j += step) {
2142  // Nodes respond to polls > 50% of the time (positive score)
2143  pm.updateAvailabilityScores(decayFactor, [&](auto nodeid) {
2144  return getNodeAvailabilityScore(1.0, nodeid);
2145  });
2146 
2147  // Expect a monotonic rise
2148  double currentScore = getAvailabilityScore();
2149  BOOST_CHECK_GE(currentScore, previousScore);
2150  previousScore = currentScore;
2151  }
2152 
2153  // We expect (1 - e^-i) * numNodesPerPeer after i * tau. The
2154  // tolerance is expressed as a percentage, and we add a (large)
2155  // 0.1% margin to account for floating point errors.
2156  BOOST_CHECK_CLOSE(previousScore,
2157  -1 * std::expm1(-1. * i) * numNodesPerPeer,
2158  100.1 / tau);
2159  }
2160 
2161  // After 10 tau we should be very close to 100% (about 99.995%)
2162  BOOST_CHECK_CLOSE(previousScore, numNodesPerPeer, 0.01);
2163 
2164  // Make the proof invalid
2167  BOOST_CHECK(!pm.isBoundToPeer(proofid));
2168  BOOST_CHECK(!pm.exists(proofid));
2169 
2170  // Re-register the proof
2171  BOOST_CHECK(pm.registerProof(proof));
2172  pm.forPeer(proofid, [&](auto &peer) {
2173  int nodeCount = 0;
2174  pm.forEachNode(peer, [&](const auto &node) { nodeCount++; });
2175  BOOST_CHECK_EQUAL(nodeCount, numNodesPerPeer);
2176  return true;
2177  });
2178 
2179  // Peer score should have reset even though nodes are still connected
2180  previousScore = getAvailabilityScore();
2181  BOOST_CHECK_SMALL(previousScore, 1e-6);
2182 
2183  // Bring the score back up to where we were
2184  for (size_t i = 1; i <= 10; i++) {
2185  for (uint32_t j = 0; j < tau; j += step) {
2186  pm.updateAvailabilityScores(decayFactor, [&](auto nodeid) {
2187  return getNodeAvailabilityScore(1.0, nodeid);
2188  });
2189  }
2190  }
2191  previousScore = getAvailabilityScore();
2192  BOOST_CHECK_CLOSE(previousScore, numNodesPerPeer, 0.01);
2193 
2194  for (size_t i = 1; i <= 3; i++) {
2195  for (uint32_t j = 0; j < tau; j += step) {
2196  // Nodes only respond to polls 50% of the time (0 score)
2197  pm.updateAvailabilityScores(decayFactor, [&](auto nodeid) {
2198  return getNodeAvailabilityScore(0.0, nodeid);
2199  });
2200 
2201  // Expect a monotonic fall
2202  double currentScore = getAvailabilityScore();
2203  BOOST_CHECK_LE(currentScore, previousScore);
2204  previousScore = currentScore;
2205  }
2206 
2207  // There is a slight error in the expected value because we did not
2208  // start the decay at exactly 100%, but the 0.1% margin is at least
2209  // an order of magnitude larger than the expected error so it
2210  // doesn't matter.
2211  BOOST_CHECK_CLOSE(previousScore,
2212  (1. + std::expm1(-1. * i)) * numNodesPerPeer,
2213  100.1 / tau);
2214  }
2215 
2216  // After 3 more tau we should be under 5%
2217  BOOST_CHECK_LT(previousScore, .05 * numNodesPerPeer);
2218 
2219  for (size_t i = 1; i <= 100; i++) {
2220  // Nodes respond to polls < 50% of the time (negative score)
2221  pm.updateAvailabilityScores(decayFactor, [&](auto nodeid) {
2222  return getNodeAvailabilityScore(-10.0, nodeid);
2223  });
2224 
2225  // It's still a monotonic fall, and the score should turn negative.
2226  double currentScore = getAvailabilityScore();
2227  BOOST_CHECK_LE(currentScore, previousScore);
2228  BOOST_CHECK_LE(currentScore, 0.);
2229  previousScore = currentScore;
2230  }
2231  }
2232 }
2233 
static constexpr PeerId NO_PEER
Definition: node.h:15
std::chrono::time_point< std::chrono::steady_clock > TimePoint
Definition: node.h:17
uint32_t PeerId
Definition: node.h:14
RecursiveMutex cs_main
Global state.
Definition: validation.cpp:113
#define Assert(val)
Identity function.
Definition: check.h:65
void ForceSetArg(const std::string &strArg, const std::string &strValue)
Definition: system.cpp:626
void ClearForcedArg(const std::string &strArg)
Remove a forced arg setting, used only in testing.
Definition: system.cpp:677
int64_t GetBlockTime() const
Definition: blockindex.h:174
int64_t GetMedianTimePast() const
Definition: blockindex.h:186
CCoinsView that adds a memory cache for transactions to another CCoinsView.
Definition: coins.h:203
void AddCoin(const COutPoint &outpoint, Coin coin, bool possible_overwrite)
Add a coin.
Definition: coins.cpp:100
bool SpendCoin(const COutPoint &outpoint, Coin *moveto=nullptr)
Spend a coin.
Definition: coins.cpp:168
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:22
Serialized script, used inside transaction inputs and outputs.
Definition: script.h:431
An output of a transaction.
Definition: transaction.h:130
Chainstate stores and provides an API to update our local knowledge of the current best chain.
Definition: validation.h:646
bool InvalidateBlock(const Config &config, BlockValidationState &state, CBlockIndex *pindex) LOCKS_EXCLUDED(cs_main) EXCLUSIVE_LOCKS_REQUIRED(!m_chainstate_mutex)
Mark a block as invalid.
CCoinsViewCache & CoinsTip() EXCLUSIVE_LOCKS_REQUIRED(
Definition: validation.h:770
bool ActivateBestChain(const Config &config, BlockValidationState &state, std::shared_ptr< const CBlock > pblock=nullptr) EXCLUSIVE_LOCKS_REQUIRED(!m_chainstate_mutex) LOCKS_EXCLUDED(cs_main)
Find the best known block, and make it the tip of the block chain.
Provides an interface for creating and interacting with one or two chainstates: an IBD chainstate gen...
Definition: validation.h:1060
int ActiveHeight() const
Definition: validation.h:1200
Chainstate & ActiveChainstate() const
The most-work chain.
CBlockIndex * ActiveTip() const
Definition: validation.h:1201
A UTXO entry.
Definition: coins.h:27
Fast randomness source.
Definition: random.h:129
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:120
Result GetResult() const
Definition: validation.h:123
bool removeNode(NodeId nodeid)
Definition: peermanager.cpp:83
uint64_t getFragmentation() const
Definition: peermanager.h:429
uint32_t getConnectedPeersScore() const
Definition: peermanager.h:381
void cleanupDanglingProofs(const ProofRef &localProof)
bool shouldRequestMoreNodes()
Returns true if we encountered a lack of node since the last call.
Definition: peermanager.h:275
bool exists(const ProofId &proofid) const
Definition: peermanager.h:346
bool updateNextRequestTime(NodeId nodeid, TimePoint timeout)
size_t getNodeCount() const
Definition: peermanager.h:257
PendingNodeSet pendingNodes
Definition: peermanager.h:217
bool verify() const
Perform consistency check on internal data structures.
bool forNode(NodeId nodeid, Callable &&func) const
Definition: peermanager.h:278
bool forPeer(const ProofId &proofid, Callable &&func) const
Definition: peermanager.h:353
uint32_t getTotalPeersScore() const
Definition: peermanager.h:380
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:18
uint64_t getSlotCount() const
Definition: peermanager.h:428
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:258
uint64_t compact()
Trigger maintenance of internal data structures.
void forEachPeer(Callable &&func) const
Definition: peermanager.h:359
void forEachNode(const Peer &peer, Callable &&func) const
Definition: peermanager.h:284
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:315
PeerId selectPeer() const
Randomly select a peer to poll.
const ProofRadixTree & getShareableProofsSnapshot() const
Definition: peermanager.h:442
void updateAvailabilityScores(const double decayFactor, Callable &&getNodeAvailabilityScore)
Definition: peermanager.h:384
bool isInConflictingPool(const ProofId &proofid) const
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:50
const ProofId & getId() const
Definition: proof.h:169
static uint32_t amountToScore(Amount amount)
Definition: proof.cpp:99
uint8_t * begin()
Definition: uint256.h:83
static void addCoin(const Amount nValue, const CWallet &wallet, std::vector< std::unique_ptr< CWalletTx >> &wtxs)
const Config & GetConfig()
Definition: config.cpp:34
static const uint8_t tau[]
Definition: chacha20.cpp:30
static RPCHelpMan generate()
Definition: mining.cpp:296
static constexpr Amount PROOF_DUST_THRESHOLD
Minimum amount per utxo.
Definition: proof.h:40
ProofRegistrationResult
Definition: peermanager.h:138
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
Definition: init.h:28
NodeContext & m_node
Definition: interfaces.cpp:768
static constexpr NodeId NO_NODE
Special NodeId that represent no node.
Definition: nodeid.h:15
int64_t NodeId
Definition: nodeid.h:10
#define BOOST_AUTO_TEST_SUITE_END()
Definition: object.cpp:16
#define BOOST_FIXTURE_TEST_SUITE(a, b)
Definition: object.cpp:14
#define BOOST_CHECK_EQUAL(v1, v2)
Definition: object.cpp:18
#define BOOST_CHECK(expr)
Definition: object.cpp:17
static 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:658
int GetRandInt(int nMax) noexcept
Definition: random.cpp:654
void Shuffle(I first, I last, R &&rng)
More efficient than using std::shuffle on a FastRandomContext.
Definition: random.h:251
CScript GetScriptForDestination(const CTxDestination &dest)
Generate a Bitcoin scriptPubKey for the given CTxDestination.
Definition: standard.cpp:243
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
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.
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:243
ArgsManager gArgs
Definition: system.cpp:75
bool error(const char *fmt, const Args &...args)
Definition: system.h:45
void SetMockTime(int64_t nMockTimeIn)
DEPRECATED Use SetMockTime with chrono type.
Definition: time.cpp:94
T GetTime()
Return system time (or mocked time, if set)
Definition: time.cpp:71
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1201