Bitcoin ABC  0.29.2
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/setup_common.h>
16 
17 #include <boost/test/unit_test.hpp>
18 
19 using namespace avalanche;
20 
21 BOOST_FIXTURE_TEST_SUITE(proofpool_tests, TestChain100Setup)
22 
23 BOOST_AUTO_TEST_CASE(get_proof_ids) {
24  ProofPool testPool;
25  Chainstate &active_chainstate = Assert(m_node.chainman)->ActiveChainstate();
26 
27  BOOST_CHECK_EQUAL(testPool.getProofIds().size(), 0);
28 
29  ProofIdSet proofIds;
30  for (size_t i = 0; i < 10; i++) {
31  auto proof = buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE);
33  ProofPool::AddProofStatus::SUCCEED);
34  proofIds.insert(proof->getId());
35  }
36 
37  auto fetchedProofIds = testPool.getProofIds();
38  BOOST_CHECK_EQUAL(testPool.countProofs(), 10);
39  BOOST_CHECK_EQUAL(fetchedProofIds.size(), 10);
40  for (auto proofid : proofIds) {
41  BOOST_CHECK_EQUAL(fetchedProofIds.count(proofid), 1);
42  }
43 }
44 
45 BOOST_AUTO_TEST_CASE(add_remove_proof_no_conflict) {
46  ProofPool testPool;
47 
48  Chainstate &active_chainstate = Assert(m_node.chainman)->ActiveChainstate();
49 
50  std::vector<ProofRef> proofs;
51  for (size_t i = 0; i < 10; i++) {
52  // Add a bunch of random proofs
53  auto proof = buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE);
55  ProofPool::AddProofStatus::SUCCEED);
56  BOOST_CHECK_EQUAL(testPool.countProofs(), i + 1);
57  BOOST_CHECK_EQUAL(testPool.getProofIds().size(), i + 1);
58 
59  // Trying to add them again will return a duplicated status
60  for (size_t j = 0; j < 10; j++) {
62  ProofPool::AddProofStatus::DUPLICATED);
63  BOOST_CHECK_EQUAL(testPool.countProofs(), i + 1);
64  BOOST_CHECK_EQUAL(testPool.getProofIds().size(), i + 1);
65  }
66  proofs.push_back(std::move(proof));
67  }
68 
69  const CKey key = CKey::MakeCompressedKey();
70  const COutPoint conflictingOutpoint{TxId(GetRandHash()), 0};
71 
72  auto buildProofWithSequence = [&](uint64_t sequence) {
73  ProofBuilder pb(sequence, 0, key, UNSPENDABLE_ECREG_PAYOUT_SCRIPT);
75  pb.addUTXO(conflictingOutpoint, 10 * COIN, 123456, false, key));
76  return pb.build();
77  };
78 
79  auto proof_seq10 = buildProofWithSequence(10);
80  BOOST_CHECK_EQUAL(testPool.addProofIfNoConflict(proof_seq10),
81  ProofPool::AddProofStatus::SUCCEED);
82  BOOST_CHECK_EQUAL(testPool.countProofs(), 11);
83  BOOST_CHECK_EQUAL(testPool.getProofIds().size(), 11);
84  proofs.push_back(std::move(proof_seq10));
85 
86  auto proof_seq20 = buildProofWithSequence(20);
87  BOOST_CHECK_EQUAL(testPool.addProofIfNoConflict(proof_seq20),
88  ProofPool::AddProofStatus::REJECTED);
89  BOOST_CHECK_EQUAL(testPool.countProofs(), 11);
90  BOOST_CHECK_EQUAL(testPool.getProofIds().size(), 11);
91 
92  // Removing proofs which are not in the pool will fail
93  for (size_t i = 0; i < 10; i++) {
95  }
96  BOOST_CHECK_EQUAL(testPool.countProofs(), 11);
97  BOOST_CHECK_EQUAL(testPool.getProofIds().size(), 11);
98 
99  for (auto proof : proofs) {
100  BOOST_CHECK(testPool.removeProof(proof->getId()));
101  }
102  BOOST_CHECK_EQUAL(testPool.size(), 0);
103  BOOST_CHECK_EQUAL(testPool.countProofs(), 0);
104  BOOST_CHECK_EQUAL(testPool.getProofIds().size(), 0);
105 }
106 
108  gArgs.ForceSetArg("-avaproofstakeutxoconfirmations", "1");
109  ProofPool testPool;
111 
112  testPool.rescan(pm);
113  BOOST_CHECK_EQUAL(testPool.size(), 0);
114  BOOST_CHECK_EQUAL(testPool.countProofs(), 0);
115  BOOST_CHECK_EQUAL(testPool.getProofIds().size(), 0);
116 
117  // No peer should be created
118  bool hasPeer = false;
119  pm.forEachPeer([&](const Peer &p) { hasPeer = true; });
120  BOOST_CHECK(!hasPeer);
121 
122  Chainstate &active_chainstate = Assert(m_node.chainman)->ActiveChainstate();
123 
124  std::set<ProofRef, ProofRefComparatorByAddress> poolProofs;
125  for (size_t i = 0; i < 10; i++) {
126  auto proof = buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE);
127  BOOST_CHECK_EQUAL(testPool.addProofIfNoConflict(proof),
128  ProofPool::AddProofStatus::SUCCEED);
129  poolProofs.insert(std::move(proof));
130  BOOST_CHECK_EQUAL(testPool.countProofs(), i + 1);
131  BOOST_CHECK_EQUAL(testPool.getProofIds().size(), i + 1);
132  }
133 
134  testPool.rescan(pm);
135 
136  // All the proofs should be registered as peer
137  std::set<ProofRef, ProofRefComparatorByAddress> pmProofs;
138  pm.forEachPeer([&](const Peer &p) { pmProofs.insert(p.proof); });
139  BOOST_CHECK_EQUAL_COLLECTIONS(poolProofs.begin(), poolProofs.end(),
140  pmProofs.begin(), pmProofs.end());
141  BOOST_CHECK_EQUAL(testPool.size(), 0);
142  BOOST_CHECK_EQUAL(testPool.countProofs(), 0);
143  BOOST_CHECK_EQUAL(testPool.getProofIds().size(), 0);
144 
145  gArgs.ClearForcedArg("-avaproofstakeutxoconfirmations");
146 }
147 
148 BOOST_AUTO_TEST_CASE(proof_override) {
149  ProofPool testPool;
150 
151  const CKey key = CKey::MakeCompressedKey();
152 
153  auto buildProofWithSequenceAndOutpoints =
154  [&](uint64_t sequence, const std::vector<COutPoint> &outpoints) {
155  ProofBuilder pb(sequence, 0, key, UNSPENDABLE_ECREG_PAYOUT_SCRIPT);
156  for (const COutPoint &outpoint : outpoints) {
157  BOOST_CHECK(
158  pb.addUTXO(outpoint, 10 * COIN, 123456, false, key));
159  }
160  return pb.build();
161  };
162 
163  const COutPoint outpoint1{TxId(GetRandHash()), 0};
164  const COutPoint outpoint2{TxId(GetRandHash()), 0};
165  const COutPoint outpoint3{TxId(GetRandHash()), 0};
166 
167  // Build and register 3 proofs with a single utxo
168  auto proof_seq10 = buildProofWithSequenceAndOutpoints(10, {outpoint1});
169  auto proof_seq20 = buildProofWithSequenceAndOutpoints(20, {outpoint2});
170  auto proof_seq30 = buildProofWithSequenceAndOutpoints(30, {outpoint3});
171 
172  BOOST_CHECK_EQUAL(testPool.addProofIfPreferred(proof_seq10),
173  ProofPool::AddProofStatus::SUCCEED);
174  BOOST_CHECK(testPool.getProof(proof_seq10->getId()));
175  BOOST_CHECK_EQUAL(testPool.countProofs(), 1);
176  BOOST_CHECK_EQUAL(testPool.getProofIds().size(), 1);
177 
178  BOOST_CHECK_EQUAL(testPool.addProofIfPreferred(proof_seq20),
179  ProofPool::AddProofStatus::SUCCEED);
180  BOOST_CHECK(testPool.getProof(proof_seq20->getId()));
181  BOOST_CHECK_EQUAL(testPool.countProofs(), 2);
182  BOOST_CHECK_EQUAL(testPool.getProofIds().size(), 2);
183 
184  BOOST_CHECK_EQUAL(testPool.addProofIfPreferred(proof_seq30),
185  ProofPool::AddProofStatus::SUCCEED);
186  BOOST_CHECK(testPool.getProof(proof_seq30->getId()));
187  BOOST_CHECK_EQUAL(testPool.countProofs(), 3);
188  BOOST_CHECK_EQUAL(testPool.getProofIds().size(), 3);
189 
190  // Build a proof that conflicts with the above 3, but has a higher sequence
191  auto proof_seq123 = buildProofWithSequenceAndOutpoints(
192  123, {outpoint1, outpoint2, outpoint3});
193  ProofPool::ConflictingProofSet expectedConflictingProofs = {
194  proof_seq10, proof_seq20, proof_seq30};
195 
196  // The no conflict call should reject our candidate and not alter the 3
197  // conflicting proofs
198  ProofPool::ConflictingProofSet conflictingProofs;
200  testPool.addProofIfNoConflict(proof_seq123, conflictingProofs),
201  ProofPool::AddProofStatus::REJECTED);
202  BOOST_CHECK_EQUAL_COLLECTIONS(
203  conflictingProofs.begin(), conflictingProofs.end(),
204  expectedConflictingProofs.begin(), expectedConflictingProofs.end());
205  BOOST_CHECK_EQUAL(testPool.countProofs(), 3);
206  BOOST_CHECK_EQUAL(testPool.getProofIds().size(), 3);
207  BOOST_CHECK(!testPool.getProof(proof_seq123->getId()));
208  BOOST_CHECK(testPool.getProof(proof_seq10->getId()));
209  BOOST_CHECK(testPool.getProof(proof_seq20->getId()));
210  BOOST_CHECK(testPool.getProof(proof_seq30->getId()));
211 
212  // The conflict handling call will override the 3 conflicting proofs
213  conflictingProofs.clear();
215  testPool.addProofIfPreferred(proof_seq123, conflictingProofs),
216  ProofPool::AddProofStatus::SUCCEED);
217  BOOST_CHECK_EQUAL_COLLECTIONS(
218  conflictingProofs.begin(), conflictingProofs.end(),
219  expectedConflictingProofs.begin(), expectedConflictingProofs.end());
220  BOOST_CHECK_EQUAL(testPool.countProofs(), 1);
221  BOOST_CHECK_EQUAL(testPool.getProofIds().size(), 1);
222  BOOST_CHECK(testPool.getProof(proof_seq123->getId()));
223  BOOST_CHECK(!testPool.getProof(proof_seq10->getId()));
224  BOOST_CHECK(!testPool.getProof(proof_seq20->getId()));
225  BOOST_CHECK(!testPool.getProof(proof_seq30->getId()));
226 }
227 
228 BOOST_AUTO_TEST_CASE(conflicting_proofs_set) {
229  ProofPool testPool;
230 
231  const CKey key = CKey::MakeCompressedKey();
232  const COutPoint conflictingOutpoint{TxId(GetRandHash()), 0};
233 
234  auto buildProofWithSequence = [&](uint64_t sequence) {
235  ProofBuilder pb(sequence, 0, key, UNSPENDABLE_ECREG_PAYOUT_SCRIPT);
236  BOOST_CHECK(
237  pb.addUTXO(conflictingOutpoint, 10 * COIN, 123456, false, key));
238  return pb.build();
239  };
240 
241  auto proofSeq10 = buildProofWithSequence(10);
242  auto proofSeq20 = buildProofWithSequence(20);
243  auto proofSeq30 = buildProofWithSequence(30);
244 
245  BOOST_CHECK_EQUAL(testPool.addProofIfNoConflict(proofSeq20),
246  ProofPool::AddProofStatus::SUCCEED);
247 
248  Chainstate &active_chainstate = Assert(m_node.chainman)->ActiveChainstate();
249 
250  auto getRandomConflictingProofSet = [&active_chainstate]() {
252  buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE),
253  buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE),
254  buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE),
255  };
256  };
257 
258  auto checkConflictingProofs =
259  [&](const ProofPool::ConflictingProofSet &conflictingProofs,
260  const ProofPool::ConflictingProofSet &expectedConflictingProofs) {
261  BOOST_CHECK_EQUAL_COLLECTIONS(conflictingProofs.begin(),
262  conflictingProofs.end(),
263  expectedConflictingProofs.begin(),
264  expectedConflictingProofs.end());
265  };
266 
267  {
268  // Without override, duplicated proof
269  auto conflictingProofs = getRandomConflictingProofSet();
271  testPool.addProofIfNoConflict(proofSeq20, conflictingProofs),
272  ProofPool::AddProofStatus::DUPLICATED);
273  checkConflictingProofs(conflictingProofs, {});
274  }
275 
276  {
277  // With override, duplicated proof
278  auto conflictingProofs = getRandomConflictingProofSet();
280  testPool.addProofIfPreferred(proofSeq20, conflictingProofs),
281  ProofPool::AddProofStatus::DUPLICATED);
282  checkConflictingProofs(conflictingProofs, {});
283  }
284 
285  {
286  // Without override, worst proof
287  auto conflictingProofs = getRandomConflictingProofSet();
289  testPool.addProofIfNoConflict(proofSeq10, conflictingProofs),
290  ProofPool::AddProofStatus::REJECTED);
291  checkConflictingProofs(conflictingProofs, {proofSeq20});
292  }
293 
294  {
295  // Without override, better proof
296  auto conflictingProofs = getRandomConflictingProofSet();
298  testPool.addProofIfNoConflict(proofSeq30, conflictingProofs),
299  ProofPool::AddProofStatus::REJECTED);
300  checkConflictingProofs(conflictingProofs, {proofSeq20});
301  }
302 
303  {
304  // With override, worst proof
305  auto conflictingProofs = getRandomConflictingProofSet();
307  testPool.addProofIfPreferred(proofSeq10, conflictingProofs),
308  ProofPool::AddProofStatus::REJECTED);
309  checkConflictingProofs(conflictingProofs, {proofSeq20});
310  }
311 
312  {
313  // With override, better proof
314  auto conflictingProofs = getRandomConflictingProofSet();
316  testPool.addProofIfPreferred(proofSeq30, conflictingProofs),
317  ProofPool::AddProofStatus::SUCCEED);
318  checkConflictingProofs(conflictingProofs, {proofSeq20});
319  }
320 }
321 
323  ProofPool testPool;
324 
325  for (size_t i = 0; i < 10; i++) {
326  BOOST_CHECK(!testPool.getProof(ProofId(GetRandHash())));
327  }
328 
329  Chainstate &active_chainstate = Assert(m_node.chainman)->ActiveChainstate();
330 
331  for (size_t i = 0; i < 10; i++) {
332  auto proof = buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE);
333  BOOST_CHECK_EQUAL(testPool.addProofIfNoConflict(proof),
334  ProofPool::AddProofStatus::SUCCEED);
335 
336  auto retrievedProof = testPool.getProof(proof->getId());
337  BOOST_CHECK_NE(retrievedProof, nullptr);
338  BOOST_CHECK_EQUAL(retrievedProof->getId(), proof->getId());
339  }
340 }
341 
342 BOOST_AUTO_TEST_CASE(get_lowest_score_proof) {
343  ProofPool testPool;
344  BOOST_CHECK_EQUAL(testPool.getLowestScoreProof(), nullptr);
345 
346  const CKey key = CKey::MakeCompressedKey();
347  auto buildProofWithRandomOutpoints = [&](uint32_t score) {
348  int numOutpoints = InsecureRand32() % 10 + 1;
350  for (int i = 0; i < numOutpoints; i++) {
351  Amount amount = 1 * COIN;
352  if (i == numOutpoints - 1) {
353  // Last UTXO is the remainder
354  amount =
355  (int64_t(score) * COIN) / 100 - (numOutpoints - 1) * COIN;
356  }
357  const COutPoint outpoint{TxId(GetRandHash()), 0};
358  BOOST_CHECK(pb.addUTXO(outpoint, amount, 123456, false, key));
359  }
360  return pb.build();
361  };
362 
363  // Add some proofs with different scores and check the lowest scoring proof
364  for (int i = 9; i >= 0; i--) {
365  auto proof = buildProofWithRandomOutpoints(MIN_VALID_PROOF_SCORE + i);
366  BOOST_CHECK_EQUAL(testPool.addProofIfNoConflict(proof),
367  ProofPool::AddProofStatus::SUCCEED);
368  auto checkLowestScoreProof = testPool.getLowestScoreProof();
369  BOOST_CHECK_EQUAL(checkLowestScoreProof->getScore(),
371  BOOST_CHECK_EQUAL(checkLowestScoreProof->getId(), proof->getId());
372  }
373  BOOST_CHECK_EQUAL(testPool.countProofs(), 10);
374  BOOST_CHECK_EQUAL(testPool.getProofIds().size(), 10);
375 
376  auto lowestScoreProof = testPool.getLowestScoreProof();
377 
378  // Adding more proofs doesn't change the lowest scoring proof
379  for (size_t i = 1; i < 10; i++) {
380  auto proof = buildProofWithRandomOutpoints(MIN_VALID_PROOF_SCORE + i);
381  BOOST_CHECK_EQUAL(testPool.addProofIfNoConflict(proof),
382  ProofPool::AddProofStatus::SUCCEED);
383  auto checkLowestScoreProof = testPool.getLowestScoreProof();
384  BOOST_CHECK_EQUAL(checkLowestScoreProof->getScore(),
386  BOOST_CHECK_EQUAL(checkLowestScoreProof->getId(),
387  lowestScoreProof->getId());
388  }
389  BOOST_CHECK_EQUAL(testPool.countProofs(), 19);
390  BOOST_CHECK_EQUAL(testPool.getProofIds().size(), 19);
391 
392  // Remove proofs by lowest score, checking the lowest score as we go
393  for (int scoreCount = 1; scoreCount < 10; scoreCount++) {
394  for (size_t i = 0; i < 2; i++) {
395  BOOST_CHECK(
396  testPool.removeProof(testPool.getLowestScoreProof()->getId()));
398  MIN_VALID_PROOF_SCORE + scoreCount);
399  }
400  }
401 
402  // Remove the last proof
403  BOOST_CHECK(testPool.removeProof(testPool.getLowestScoreProof()->getId()));
404  BOOST_CHECK_EQUAL(testPool.getLowestScoreProof(), nullptr);
405  BOOST_CHECK_EQUAL(testPool.countProofs(), 0);
406  BOOST_CHECK_EQUAL(testPool.getProofIds().size(), 0);
407 }
408 
static constexpr Amount COIN
Definition: amount.h:144
#define Assert(val)
Identity function.
Definition: check.h:84
void ForceSetArg(const std::string &strArg, const std::string &strValue)
Definition: system.cpp:706
void ClearForcedArg(const std::string &strArg)
Remove a forced arg setting, used only in testing.
Definition: system.cpp:757
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:628
void forEachPeer(Callable &&func) const
Definition: peermanager.h:414
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:778
#define BOOST_AUTO_TEST_SUITE_END()
Definition: object.cpp:16
#define BOOST_CHECK_EQUAL(v1, v2)
Definition: object.cpp:18
#define BOOST_CHECK(expr)
Definition: object.cpp:17
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
ArgsManager gArgs
Definition: system.cpp:80