Bitcoin ABC  0.26.3
P2P Digital Currency
proofpool_tests.cpp
Go to the documentation of this file.
1 // Copyright (c) 2021 The Bitcoin developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 
5 #include <avalanche/proofpool.h>
6 
9 #include <key.h>
10 #include <primitives/transaction.h>
11 #include <primitives/txid.h>
12 #include <random.h>
13 
14 #include <avalanche/test/util.h>
15 #include <test/util/random.h>
16 #include <test/util/setup_common.h>
17 
18 #include <boost/test/unit_test.hpp>
19 
20 using namespace avalanche;
21 
22 BOOST_FIXTURE_TEST_SUITE(proofpool_tests, TestChain100Setup)
23 
24 BOOST_AUTO_TEST_CASE(get_proof_ids) {
25  ProofPool testPool;
26  Chainstate &active_chainstate = Assert(m_node.chainman)->ActiveChainstate();
27 
28  BOOST_CHECK_EQUAL(testPool.getProofIds().size(), 0);
29 
30  ProofIdSet proofIds;
31  for (size_t i = 0; i < 10; i++) {
32  auto proof = buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE);
34  ProofPool::AddProofStatus::SUCCEED);
35  proofIds.insert(proof->getId());
36  }
37 
38  auto fetchedProofIds = testPool.getProofIds();
39  BOOST_CHECK_EQUAL(testPool.countProofs(), 10);
40  BOOST_CHECK_EQUAL(fetchedProofIds.size(), 10);
41  for (auto proofid : proofIds) {
42  BOOST_CHECK_EQUAL(fetchedProofIds.count(proofid), 1);
43  }
44 }
45 
46 BOOST_AUTO_TEST_CASE(add_remove_proof_no_conflict) {
47  ProofPool testPool;
48 
49  Chainstate &active_chainstate = Assert(m_node.chainman)->ActiveChainstate();
50 
51  std::vector<ProofRef> proofs;
52  for (size_t i = 0; i < 10; i++) {
53  // Add a bunch of random proofs
54  auto proof = buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE);
56  ProofPool::AddProofStatus::SUCCEED);
57  BOOST_CHECK_EQUAL(testPool.countProofs(), i + 1);
58  BOOST_CHECK_EQUAL(testPool.getProofIds().size(), i + 1);
59 
60  // Trying to add them again will return a duplicated status
61  for (size_t j = 0; j < 10; j++) {
63  ProofPool::AddProofStatus::DUPLICATED);
64  BOOST_CHECK_EQUAL(testPool.countProofs(), i + 1);
65  BOOST_CHECK_EQUAL(testPool.getProofIds().size(), i + 1);
66  }
67  proofs.push_back(std::move(proof));
68  }
69 
70  const CKey key = CKey::MakeCompressedKey();
71  const COutPoint conflictingOutpoint{TxId(GetRandHash()), 0};
72 
73  auto buildProofWithSequence = [&](uint64_t sequence) {
74  ProofBuilder pb(sequence, 0, key, UNSPENDABLE_ECREG_PAYOUT_SCRIPT);
76  pb.addUTXO(conflictingOutpoint, 10 * COIN, 123456, false, key));
77  return pb.build();
78  };
79 
80  auto proof_seq10 = buildProofWithSequence(10);
81  BOOST_CHECK_EQUAL(testPool.addProofIfNoConflict(proof_seq10),
82  ProofPool::AddProofStatus::SUCCEED);
83  BOOST_CHECK_EQUAL(testPool.countProofs(), 11);
84  BOOST_CHECK_EQUAL(testPool.getProofIds().size(), 11);
85  proofs.push_back(std::move(proof_seq10));
86 
87  auto proof_seq20 = buildProofWithSequence(20);
88  BOOST_CHECK_EQUAL(testPool.addProofIfNoConflict(proof_seq20),
89  ProofPool::AddProofStatus::REJECTED);
90  BOOST_CHECK_EQUAL(testPool.countProofs(), 11);
91  BOOST_CHECK_EQUAL(testPool.getProofIds().size(), 11);
92 
93  // Removing proofs which are not in the pool will fail
94  for (size_t i = 0; i < 10; i++) {
96  }
97  BOOST_CHECK_EQUAL(testPool.countProofs(), 11);
98  BOOST_CHECK_EQUAL(testPool.getProofIds().size(), 11);
99 
100  for (auto proof : proofs) {
101  BOOST_CHECK(testPool.removeProof(proof->getId()));
102  }
103  BOOST_CHECK_EQUAL(testPool.size(), 0);
104  BOOST_CHECK_EQUAL(testPool.countProofs(), 0);
105  BOOST_CHECK_EQUAL(testPool.getProofIds().size(), 0);
106 }
107 
109  gArgs.ForceSetArg("-avaproofstakeutxoconfirmations", "1");
110  ProofPool testPool;
112 
113  testPool.rescan(pm);
114  BOOST_CHECK_EQUAL(testPool.size(), 0);
115  BOOST_CHECK_EQUAL(testPool.countProofs(), 0);
116  BOOST_CHECK_EQUAL(testPool.getProofIds().size(), 0);
117 
118  // No peer should be created
119  bool hasPeer = false;
120  pm.forEachPeer([&](const Peer &p) { hasPeer = true; });
121  BOOST_CHECK(!hasPeer);
122 
123  Chainstate &active_chainstate = Assert(m_node.chainman)->ActiveChainstate();
124 
125  std::set<ProofRef, ProofRefComparatorByAddress> poolProofs;
126  for (size_t i = 0; i < 10; i++) {
127  auto proof = buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE);
128  BOOST_CHECK_EQUAL(testPool.addProofIfNoConflict(proof),
129  ProofPool::AddProofStatus::SUCCEED);
130  poolProofs.insert(std::move(proof));
131  BOOST_CHECK_EQUAL(testPool.countProofs(), i + 1);
132  BOOST_CHECK_EQUAL(testPool.getProofIds().size(), i + 1);
133  }
134 
135  testPool.rescan(pm);
136 
137  // All the proofs should be registered as peer
138  std::set<ProofRef, ProofRefComparatorByAddress> pmProofs;
139  pm.forEachPeer([&](const Peer &p) { pmProofs.insert(p.proof); });
140  BOOST_CHECK_EQUAL_COLLECTIONS(poolProofs.begin(), poolProofs.end(),
141  pmProofs.begin(), pmProofs.end());
142  BOOST_CHECK_EQUAL(testPool.size(), 0);
143  BOOST_CHECK_EQUAL(testPool.countProofs(), 0);
144  BOOST_CHECK_EQUAL(testPool.getProofIds().size(), 0);
145 
146  gArgs.ClearForcedArg("-avaproofstakeutxoconfirmations");
147 }
148 
149 BOOST_AUTO_TEST_CASE(proof_override) {
150  ProofPool testPool;
151 
152  const CKey key = CKey::MakeCompressedKey();
153 
154  auto buildProofWithSequenceAndOutpoints =
155  [&](uint64_t sequence, const std::vector<COutPoint> &outpoints) {
156  ProofBuilder pb(sequence, 0, key, UNSPENDABLE_ECREG_PAYOUT_SCRIPT);
157  for (const COutPoint &outpoint : outpoints) {
158  BOOST_CHECK(
159  pb.addUTXO(outpoint, 10 * COIN, 123456, false, key));
160  }
161  return pb.build();
162  };
163 
164  const COutPoint outpoint1{TxId(GetRandHash()), 0};
165  const COutPoint outpoint2{TxId(GetRandHash()), 0};
166  const COutPoint outpoint3{TxId(GetRandHash()), 0};
167 
168  // Build and register 3 proofs with a single utxo
169  auto proof_seq10 = buildProofWithSequenceAndOutpoints(10, {outpoint1});
170  auto proof_seq20 = buildProofWithSequenceAndOutpoints(20, {outpoint2});
171  auto proof_seq30 = buildProofWithSequenceAndOutpoints(30, {outpoint3});
172 
173  BOOST_CHECK_EQUAL(testPool.addProofIfPreferred(proof_seq10),
174  ProofPool::AddProofStatus::SUCCEED);
175  BOOST_CHECK(testPool.getProof(proof_seq10->getId()));
176  BOOST_CHECK_EQUAL(testPool.countProofs(), 1);
177  BOOST_CHECK_EQUAL(testPool.getProofIds().size(), 1);
178 
179  BOOST_CHECK_EQUAL(testPool.addProofIfPreferred(proof_seq20),
180  ProofPool::AddProofStatus::SUCCEED);
181  BOOST_CHECK(testPool.getProof(proof_seq20->getId()));
182  BOOST_CHECK_EQUAL(testPool.countProofs(), 2);
183  BOOST_CHECK_EQUAL(testPool.getProofIds().size(), 2);
184 
185  BOOST_CHECK_EQUAL(testPool.addProofIfPreferred(proof_seq30),
186  ProofPool::AddProofStatus::SUCCEED);
187  BOOST_CHECK(testPool.getProof(proof_seq30->getId()));
188  BOOST_CHECK_EQUAL(testPool.countProofs(), 3);
189  BOOST_CHECK_EQUAL(testPool.getProofIds().size(), 3);
190 
191  // Build a proof that conflicts with the above 3, but has a higher sequence
192  auto proof_seq123 = buildProofWithSequenceAndOutpoints(
193  123, {outpoint1, outpoint2, outpoint3});
194  ProofPool::ConflictingProofSet expectedConflictingProofs = {
195  proof_seq10, proof_seq20, proof_seq30};
196 
197  // The no conflict call should reject our candidate and not alter the 3
198  // conflicting proofs
199  ProofPool::ConflictingProofSet conflictingProofs;
201  testPool.addProofIfNoConflict(proof_seq123, conflictingProofs),
202  ProofPool::AddProofStatus::REJECTED);
203  BOOST_CHECK_EQUAL_COLLECTIONS(
204  conflictingProofs.begin(), conflictingProofs.end(),
205  expectedConflictingProofs.begin(), expectedConflictingProofs.end());
206  BOOST_CHECK_EQUAL(testPool.countProofs(), 3);
207  BOOST_CHECK_EQUAL(testPool.getProofIds().size(), 3);
208  BOOST_CHECK(!testPool.getProof(proof_seq123->getId()));
209  BOOST_CHECK(testPool.getProof(proof_seq10->getId()));
210  BOOST_CHECK(testPool.getProof(proof_seq20->getId()));
211  BOOST_CHECK(testPool.getProof(proof_seq30->getId()));
212 
213  // The conflict handling call will override the 3 conflicting proofs
214  conflictingProofs.clear();
216  testPool.addProofIfPreferred(proof_seq123, conflictingProofs),
217  ProofPool::AddProofStatus::SUCCEED);
218  BOOST_CHECK_EQUAL_COLLECTIONS(
219  conflictingProofs.begin(), conflictingProofs.end(),
220  expectedConflictingProofs.begin(), expectedConflictingProofs.end());
221  BOOST_CHECK_EQUAL(testPool.countProofs(), 1);
222  BOOST_CHECK_EQUAL(testPool.getProofIds().size(), 1);
223  BOOST_CHECK(testPool.getProof(proof_seq123->getId()));
224  BOOST_CHECK(!testPool.getProof(proof_seq10->getId()));
225  BOOST_CHECK(!testPool.getProof(proof_seq20->getId()));
226  BOOST_CHECK(!testPool.getProof(proof_seq30->getId()));
227 }
228 
229 BOOST_AUTO_TEST_CASE(conflicting_proofs_set) {
230  ProofPool testPool;
231 
232  const CKey key = CKey::MakeCompressedKey();
233  const COutPoint conflictingOutpoint{TxId(GetRandHash()), 0};
234 
235  auto buildProofWithSequence = [&](uint64_t sequence) {
236  ProofBuilder pb(sequence, 0, key, UNSPENDABLE_ECREG_PAYOUT_SCRIPT);
237  BOOST_CHECK(
238  pb.addUTXO(conflictingOutpoint, 10 * COIN, 123456, false, key));
239  return pb.build();
240  };
241 
242  auto proofSeq10 = buildProofWithSequence(10);
243  auto proofSeq20 = buildProofWithSequence(20);
244  auto proofSeq30 = buildProofWithSequence(30);
245 
246  BOOST_CHECK_EQUAL(testPool.addProofIfNoConflict(proofSeq20),
247  ProofPool::AddProofStatus::SUCCEED);
248 
249  Chainstate &active_chainstate = Assert(m_node.chainman)->ActiveChainstate();
250 
251  auto getRandomConflictingProofSet = [&active_chainstate]() {
253  buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE),
254  buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE),
255  buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE),
256  };
257  };
258 
259  auto checkConflictingProofs =
260  [&](const ProofPool::ConflictingProofSet &conflictingProofs,
261  const ProofPool::ConflictingProofSet &expectedConflictingProofs) {
262  BOOST_CHECK_EQUAL_COLLECTIONS(conflictingProofs.begin(),
263  conflictingProofs.end(),
264  expectedConflictingProofs.begin(),
265  expectedConflictingProofs.end());
266  };
267 
268  {
269  // Without override, duplicated proof
270  auto conflictingProofs = getRandomConflictingProofSet();
272  testPool.addProofIfNoConflict(proofSeq20, conflictingProofs),
273  ProofPool::AddProofStatus::DUPLICATED);
274  checkConflictingProofs(conflictingProofs, {});
275  }
276 
277  {
278  // With override, duplicated proof
279  auto conflictingProofs = getRandomConflictingProofSet();
281  testPool.addProofIfPreferred(proofSeq20, conflictingProofs),
282  ProofPool::AddProofStatus::DUPLICATED);
283  checkConflictingProofs(conflictingProofs, {});
284  }
285 
286  {
287  // Without override, worst proof
288  auto conflictingProofs = getRandomConflictingProofSet();
290  testPool.addProofIfNoConflict(proofSeq10, conflictingProofs),
291  ProofPool::AddProofStatus::REJECTED);
292  checkConflictingProofs(conflictingProofs, {proofSeq20});
293  }
294 
295  {
296  // Without override, better proof
297  auto conflictingProofs = getRandomConflictingProofSet();
299  testPool.addProofIfNoConflict(proofSeq30, conflictingProofs),
300  ProofPool::AddProofStatus::REJECTED);
301  checkConflictingProofs(conflictingProofs, {proofSeq20});
302  }
303 
304  {
305  // With override, worst proof
306  auto conflictingProofs = getRandomConflictingProofSet();
308  testPool.addProofIfPreferred(proofSeq10, conflictingProofs),
309  ProofPool::AddProofStatus::REJECTED);
310  checkConflictingProofs(conflictingProofs, {proofSeq20});
311  }
312 
313  {
314  // With override, better proof
315  auto conflictingProofs = getRandomConflictingProofSet();
317  testPool.addProofIfPreferred(proofSeq30, conflictingProofs),
318  ProofPool::AddProofStatus::SUCCEED);
319  checkConflictingProofs(conflictingProofs, {proofSeq20});
320  }
321 }
322 
324  ProofPool testPool;
325 
326  for (size_t i = 0; i < 10; i++) {
327  BOOST_CHECK(!testPool.getProof(ProofId(GetRandHash())));
328  }
329 
330  Chainstate &active_chainstate = Assert(m_node.chainman)->ActiveChainstate();
331 
332  for (size_t i = 0; i < 10; i++) {
333  auto proof = buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE);
334  BOOST_CHECK_EQUAL(testPool.addProofIfNoConflict(proof),
335  ProofPool::AddProofStatus::SUCCEED);
336 
337  auto retrievedProof = testPool.getProof(proof->getId());
338  BOOST_CHECK_NE(retrievedProof, nullptr);
339  BOOST_CHECK_EQUAL(retrievedProof->getId(), proof->getId());
340  }
341 }
342 
343 BOOST_AUTO_TEST_CASE(get_lowest_score_proof) {
344  ProofPool testPool;
345  BOOST_CHECK_EQUAL(testPool.getLowestScoreProof(), nullptr);
346 
347  const CKey key = CKey::MakeCompressedKey();
348  auto buildProofWithRandomOutpoints = [&](uint32_t score) {
349  int numOutpoints = InsecureRand32() % 10 + 1;
351  for (int i = 0; i < numOutpoints; i++) {
352  Amount amount = 1 * COIN;
353  if (i == numOutpoints - 1) {
354  // Last UTXO is the remainder
355  amount =
356  (int64_t(score) * COIN) / 100 - (numOutpoints - 1) * COIN;
357  }
358  const COutPoint outpoint{TxId(GetRandHash()), 0};
359  BOOST_CHECK(pb.addUTXO(outpoint, amount, 123456, false, key));
360  }
361  return pb.build();
362  };
363 
364  // Add some proofs with different scores and check the lowest scoring proof
365  for (int i = 9; i >= 0; i--) {
366  auto proof = buildProofWithRandomOutpoints(MIN_VALID_PROOF_SCORE + i);
367  BOOST_CHECK_EQUAL(testPool.addProofIfNoConflict(proof),
368  ProofPool::AddProofStatus::SUCCEED);
369  auto checkLowestScoreProof = testPool.getLowestScoreProof();
370  BOOST_CHECK_EQUAL(checkLowestScoreProof->getScore(),
372  BOOST_CHECK_EQUAL(checkLowestScoreProof->getId(), proof->getId());
373  }
374  BOOST_CHECK_EQUAL(testPool.countProofs(), 10);
375  BOOST_CHECK_EQUAL(testPool.getProofIds().size(), 10);
376 
377  auto lowestScoreProof = testPool.getLowestScoreProof();
378 
379  // Adding more proofs doesn't change the lowest scoring proof
380  for (size_t i = 1; i < 10; i++) {
381  auto proof = buildProofWithRandomOutpoints(MIN_VALID_PROOF_SCORE + i);
382  BOOST_CHECK_EQUAL(testPool.addProofIfNoConflict(proof),
383  ProofPool::AddProofStatus::SUCCEED);
384  auto checkLowestScoreProof = testPool.getLowestScoreProof();
385  BOOST_CHECK_EQUAL(checkLowestScoreProof->getScore(),
387  BOOST_CHECK_EQUAL(checkLowestScoreProof->getId(),
388  lowestScoreProof->getId());
389  }
390  BOOST_CHECK_EQUAL(testPool.countProofs(), 19);
391  BOOST_CHECK_EQUAL(testPool.getProofIds().size(), 19);
392 
393  // Remove proofs by lowest score, checking the lowest score as we go
394  for (int scoreCount = 1; scoreCount < 10; scoreCount++) {
395  for (size_t i = 0; i < 2; i++) {
396  BOOST_CHECK(
397  testPool.removeProof(testPool.getLowestScoreProof()->getId()));
399  MIN_VALID_PROOF_SCORE + scoreCount);
400  }
401  }
402 
403  // Remove the last proof
404  BOOST_CHECK(testPool.removeProof(testPool.getLowestScoreProof()->getId()));
405  BOOST_CHECK_EQUAL(testPool.getLowestScoreProof(), nullptr);
406  BOOST_CHECK_EQUAL(testPool.countProofs(), 0);
407  BOOST_CHECK_EQUAL(testPool.getProofIds().size(), 0);
408 }
409 
410 BOOST_AUTO_TEST_SUITE_END()
static constexpr Amount COIN
Definition: amount.h:144
ArgsManager gArgs
Definition: args.cpp:38
#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
An encapsulated secp256k1 private key.
Definition: key.h:28
static CKey MakeCompressedKey()
Produce a valid compressed key.
Definition: key.cpp:466
An outpoint - a combination of a transaction hash and an index n into its vout.
Definition: transaction.h:20
Chainstate stores and provides an API to update our local knowledge of the current best chain.
Definition: validation.h:695
void forEachPeer(Callable &&func) const
Definition: peermanager.h:416
bool addUTXO(COutPoint utxo, Amount amount, uint32_t height, bool is_coinbase, CKey key)
const ProofId & getId() const
Definition: proof.h:169
uint32_t getScore() const
Definition: proof.h:174
Map a proof to each utxo.
Definition: proofpool.h:57
AddProofStatus addProofIfPreferred(const ProofRef &proof, ConflictingProofSet &conflictingProofs)
Attempt to add a proof to the pool.
Definition: proofpool.cpp:54
size_t size() const
Definition: proofpool.h:135
AddProofStatus addProofIfNoConflict(const ProofRef &proof, ConflictingProofSet &conflictingProofs)
Attempt to add a proof to the pool, and fail if there is a conflict on any UTXO.
Definition: proofpool.cpp:13
size_t countProofs() const
Definition: proofpool.cpp:129
bool removeProof(ProofId proofid)
Definition: proofpool.cpp:79
ProofRef getProof(const ProofId &proofid) const
Definition: proofpool.cpp:112
std::set< ProofRef, ConflictingProofComparator > ConflictingProofSet
Definition: proofpool.h:88
ProofRef getLowestScoreProof() const
Definition: proofpool.cpp:123
std::unordered_set< ProofRef, SaltedProofHasher > rescan(PeerManager &peerManager)
Definition: proofpool.cpp:86
ProofIdSet getProofIds() const
Definition: proofpool.cpp:101
static constexpr Amount PROOF_DUST_THRESHOLD
Minimum amount per utxo.
Definition: proof.h:40
const CScript UNSPENDABLE_ECREG_PAYOUT_SCRIPT
Definition: util.h:19
ProofRef buildRandomProof(Chainstate &active_chainstate, uint32_t score, int height, const CKey &masterKey)
Definition: util.cpp:20
constexpr uint32_t MIN_VALID_PROOF_SCORE
Definition: util.h:17
std::unordered_set< ProofId, SaltedProofIdHasher > ProofIdSet
Definition: proofpool.h:52
NodeContext & m_node
Definition: interfaces.cpp:785
#define BOOST_CHECK_EQUAL(v1, v2)
Definition: object.cpp:18
#define BOOST_CHECK(expr)
Definition: object.cpp:17
BOOST_AUTO_TEST_CASE(get_proof_ids)
uint256 GetRandHash() noexcept
Definition: random.cpp:659
BOOST_FIXTURE_TEST_SUITE(stakingrewards_tests, StakingRewardsActivationTestingSetup) BOOST_AUTO_TEST_CASE(isstakingrewardsactivated)
Definition: amount.h:19
A TxId is the identifier of a transaction.
Definition: txid.h:14
ProofRef proof
Definition: peermanager.h:89