Bitcoin ABC 0.33.11
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
6
9#include <key.h>
11#include <primitives/txid.h>
12#include <random.h>
13#include <validation.h>
14
15#include <avalanche/test/util.h>
16#include <test/util/random.h>
17#include <test/util/setup_common.h>
18
19#include <boost/test/unit_test.hpp>
20
21using namespace avalanche;
22
23BOOST_FIXTURE_TEST_SUITE(proofpool_tests, TestChain100Setup)
24
25BOOST_AUTO_TEST_CASE(get_conflicts) {
26 ProofPool testPool;
27
28 const CKey key = CKey::MakeCompressedKey();
29 const COutPoint outpointA{TxId(GetRandHash()), 0};
30 const COutPoint outpointB{TxId(GetRandHash()), 0};
31 const COutPoint otherOutpoint{TxId(GetRandHash()), 0};
32
33 auto buildProof = [&](uint64_t sequence,
34 const std::vector<COutPoint> &outpoints) {
36 for (const COutPoint &outpoint : outpoints) {
37 BOOST_CHECK(pb.addUTXO(outpoint, 10 * COIN, 123456, false, key));
38 }
39 return pb.build();
40 };
41
42 auto proofA = buildProof(1, {outpointA});
43 auto proofB = buildProof(1, {outpointB});
44 auto proofAB = buildProof(2, {outpointA, outpointB});
45 auto proofOther = buildProof(1, {otherOutpoint});
46
47 BOOST_CHECK(testPool.getConflicts(proofA).empty());
48 BOOST_CHECK(testPool.getConflicts(ProofRef()).empty());
49
51 ProofPool::AddProofStatus::SUCCEED);
52
53 // Same proof is excluded from conflicts
54 BOOST_CHECK(testPool.getConflicts(proofA).empty());
55
56 auto conflicts = testPool.getConflicts(proofAB);
57 BOOST_CHECK_EQUAL(conflicts.size(), 1);
58 BOOST_CHECK_EQUAL((*conflicts.begin())->getId(), proofA->getId());
59
60 BOOST_CHECK(testPool.getConflicts(proofOther).empty());
61
63 ProofPool::AddProofStatus::SUCCEED);
64
65 // A proof that shares UTXOs with several pool entries returns all of them
66 conflicts = testPool.getConflicts(proofAB);
67 BOOST_CHECK_EQUAL(conflicts.size(), 2);
68 BOOST_CHECK_EQUAL(conflicts.count(proofA), 1);
69 BOOST_CHECK_EQUAL(conflicts.count(proofB), 1);
70}
71
72BOOST_AUTO_TEST_CASE(get_proof_ids) {
73 ProofPool testPool;
74 Chainstate &active_chainstate = Assert(m_node.chainman)->ActiveChainstate();
75
76 BOOST_CHECK_EQUAL(testPool.getProofIds().size(), 0);
77
78 ProofIdSet proofIds;
79 for (size_t i = 0; i < 10; i++) {
80 auto proof = buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE);
82 ProofPool::AddProofStatus::SUCCEED);
83 proofIds.insert(proof->getId());
84 }
85
86 auto fetchedProofIds = testPool.getProofIds();
87 BOOST_CHECK_EQUAL(testPool.countProofs(), 10);
88 BOOST_CHECK_EQUAL(fetchedProofIds.size(), 10);
89 for (auto proofid : proofIds) {
90 BOOST_CHECK_EQUAL(fetchedProofIds.count(proofid), 1);
91 }
92}
93
94BOOST_AUTO_TEST_CASE(add_remove_proof_no_conflict) {
95 ProofPool testPool;
96
97 Chainstate &active_chainstate = Assert(m_node.chainman)->ActiveChainstate();
98
99 std::vector<ProofRef> proofs;
100 for (size_t i = 0; i < 10; i++) {
101 // Add a bunch of random proofs
102 auto proof = buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE);
104 ProofPool::AddProofStatus::SUCCEED);
105 BOOST_CHECK_EQUAL(testPool.countProofs(), i + 1);
106 BOOST_CHECK_EQUAL(testPool.getProofIds().size(), i + 1);
107
108 // Trying to add them again will return a duplicated status
109 for (size_t j = 0; j < 10; j++) {
111 ProofPool::AddProofStatus::DUPLICATED);
112 BOOST_CHECK_EQUAL(testPool.countProofs(), i + 1);
113 BOOST_CHECK_EQUAL(testPool.getProofIds().size(), i + 1);
114 }
115 proofs.push_back(std::move(proof));
116 }
117
118 const CKey key = CKey::MakeCompressedKey();
119 const COutPoint conflictingOutpoint{TxId(GetRandHash()), 0};
120
121 auto buildProofWithSequence = [&](uint64_t sequence) {
122 ProofBuilder pb(sequence, 0, key, UNSPENDABLE_ECREG_PAYOUT_SCRIPT);
124 pb.addUTXO(conflictingOutpoint, 10 * COIN, 123456, false, key));
125 return pb.build();
126 };
127
128 auto proof_seq10 = buildProofWithSequence(10);
129 BOOST_CHECK_EQUAL(testPool.addProofIfNoConflict(proof_seq10),
130 ProofPool::AddProofStatus::SUCCEED);
131 BOOST_CHECK_EQUAL(testPool.countProofs(), 11);
132 BOOST_CHECK_EQUAL(testPool.getProofIds().size(), 11);
133 proofs.push_back(std::move(proof_seq10));
134
135 auto proof_seq20 = buildProofWithSequence(20);
136 BOOST_CHECK_EQUAL(testPool.addProofIfNoConflict(proof_seq20),
137 ProofPool::AddProofStatus::REJECTED);
138 BOOST_CHECK_EQUAL(testPool.countProofs(), 11);
139 BOOST_CHECK_EQUAL(testPool.getProofIds().size(), 11);
140
141 // Removing proofs which are not in the pool will fail
142 for (size_t i = 0; i < 10; i++) {
144 }
145 BOOST_CHECK_EQUAL(testPool.countProofs(), 11);
146 BOOST_CHECK_EQUAL(testPool.getProofIds().size(), 11);
147
148 for (auto proof : proofs) {
149 BOOST_CHECK(testPool.removeProof(proof->getId()));
150 }
151 BOOST_CHECK_EQUAL(testPool.size(), 0);
152 BOOST_CHECK_EQUAL(testPool.countProofs(), 0);
153 BOOST_CHECK_EQUAL(testPool.getProofIds().size(), 0);
154}
155
157 gArgs.ForceSetArg("-avaproofstakeutxoconfirmations", "1");
158 ProofPool testPool;
160
161 testPool.rescan(pm);
162 BOOST_CHECK_EQUAL(testPool.size(), 0);
163 BOOST_CHECK_EQUAL(testPool.countProofs(), 0);
164 BOOST_CHECK_EQUAL(testPool.getProofIds().size(), 0);
165
166 // No peer should be created
167 bool hasPeer = false;
168 pm.forEachPeer([&](const Peer &p) { hasPeer = true; });
169 BOOST_CHECK(!hasPeer);
170
171 Chainstate &active_chainstate = Assert(m_node.chainman)->ActiveChainstate();
172
173 std::set<ProofRef, ProofRefComparatorByAddress> poolProofs;
174 for (size_t i = 0; i < 10; i++) {
175 auto proof = buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE);
177 ProofPool::AddProofStatus::SUCCEED);
178 poolProofs.insert(std::move(proof));
179 BOOST_CHECK_EQUAL(testPool.countProofs(), i + 1);
180 BOOST_CHECK_EQUAL(testPool.getProofIds().size(), i + 1);
181 }
182
183 testPool.rescan(pm);
184
185 // All the proofs should be registered as peer
186 std::set<ProofRef, ProofRefComparatorByAddress> pmProofs;
187 pm.forEachPeer([&](const Peer &p) { pmProofs.insert(p.proof); });
188 BOOST_CHECK_EQUAL_COLLECTIONS(poolProofs.begin(), poolProofs.end(),
189 pmProofs.begin(), pmProofs.end());
190 BOOST_CHECK_EQUAL(testPool.size(), 0);
191 BOOST_CHECK_EQUAL(testPool.countProofs(), 0);
192 BOOST_CHECK_EQUAL(testPool.getProofIds().size(), 0);
193
194 gArgs.ClearForcedArg("-avaproofstakeutxoconfirmations");
195}
196
197BOOST_AUTO_TEST_CASE(proof_override) {
198 ProofPool testPool;
199
200 const CKey key = CKey::MakeCompressedKey();
201
202 auto buildProofWithSequenceAndOutpoints =
203 [&](uint64_t sequence, const std::vector<COutPoint> &outpoints) {
204 ProofBuilder pb(sequence, 0, key, UNSPENDABLE_ECREG_PAYOUT_SCRIPT);
205 for (const COutPoint &outpoint : outpoints) {
207 pb.addUTXO(outpoint, 10 * COIN, 123456, false, key));
208 }
209 return pb.build();
210 };
211
212 const COutPoint outpoint1{TxId(GetRandHash()), 0};
213 const COutPoint outpoint2{TxId(GetRandHash()), 0};
214 const COutPoint outpoint3{TxId(GetRandHash()), 0};
215
216 // Build and register 3 proofs with a single utxo
217 auto proof_seq10 = buildProofWithSequenceAndOutpoints(10, {outpoint1});
218 auto proof_seq20 = buildProofWithSequenceAndOutpoints(20, {outpoint2});
219 auto proof_seq30 = buildProofWithSequenceAndOutpoints(30, {outpoint3});
220
221 BOOST_CHECK_EQUAL(testPool.addProofIfPreferred(proof_seq10),
222 ProofPool::AddProofStatus::SUCCEED);
223 BOOST_CHECK(testPool.getProof(proof_seq10->getId()));
224 BOOST_CHECK_EQUAL(testPool.countProofs(), 1);
225 BOOST_CHECK_EQUAL(testPool.getProofIds().size(), 1);
226
227 BOOST_CHECK_EQUAL(testPool.addProofIfPreferred(proof_seq20),
228 ProofPool::AddProofStatus::SUCCEED);
229 BOOST_CHECK(testPool.getProof(proof_seq20->getId()));
230 BOOST_CHECK_EQUAL(testPool.countProofs(), 2);
231 BOOST_CHECK_EQUAL(testPool.getProofIds().size(), 2);
232
233 BOOST_CHECK_EQUAL(testPool.addProofIfPreferred(proof_seq30),
234 ProofPool::AddProofStatus::SUCCEED);
235 BOOST_CHECK(testPool.getProof(proof_seq30->getId()));
236 BOOST_CHECK_EQUAL(testPool.countProofs(), 3);
237 BOOST_CHECK_EQUAL(testPool.getProofIds().size(), 3);
238
239 // Build a proof that conflicts with the above 3, but has a higher sequence
240 auto proof_seq123 = buildProofWithSequenceAndOutpoints(
241 123, {outpoint1, outpoint2, outpoint3});
242 ProofPool::ConflictingProofSet expectedConflictingProofs = {
243 proof_seq10, proof_seq20, proof_seq30};
244
245 // The no conflict call should reject our candidate and not alter the 3
246 // conflicting proofs
247 ProofPool::ConflictingProofSet conflictingProofs;
249 testPool.addProofIfNoConflict(proof_seq123, conflictingProofs),
250 ProofPool::AddProofStatus::REJECTED);
251 BOOST_CHECK_EQUAL_COLLECTIONS(
252 conflictingProofs.begin(), conflictingProofs.end(),
253 expectedConflictingProofs.begin(), expectedConflictingProofs.end());
254 BOOST_CHECK_EQUAL(testPool.countProofs(), 3);
255 BOOST_CHECK_EQUAL(testPool.getProofIds().size(), 3);
256 BOOST_CHECK(!testPool.getProof(proof_seq123->getId()));
257 BOOST_CHECK(testPool.getProof(proof_seq10->getId()));
258 BOOST_CHECK(testPool.getProof(proof_seq20->getId()));
259 BOOST_CHECK(testPool.getProof(proof_seq30->getId()));
260
261 // The conflict handling call will override the 3 conflicting proofs
262 conflictingProofs.clear();
264 testPool.addProofIfPreferred(proof_seq123, conflictingProofs),
265 ProofPool::AddProofStatus::SUCCEED);
266 BOOST_CHECK_EQUAL_COLLECTIONS(
267 conflictingProofs.begin(), conflictingProofs.end(),
268 expectedConflictingProofs.begin(), expectedConflictingProofs.end());
269 BOOST_CHECK_EQUAL(testPool.countProofs(), 1);
270 BOOST_CHECK_EQUAL(testPool.getProofIds().size(), 1);
271 BOOST_CHECK(testPool.getProof(proof_seq123->getId()));
272 BOOST_CHECK(!testPool.getProof(proof_seq10->getId()));
273 BOOST_CHECK(!testPool.getProof(proof_seq20->getId()));
274 BOOST_CHECK(!testPool.getProof(proof_seq30->getId()));
275}
276
277BOOST_AUTO_TEST_CASE(conflicting_proofs_set) {
278 ProofPool testPool;
279
280 const CKey key = CKey::MakeCompressedKey();
281 const COutPoint conflictingOutpoint{TxId(GetRandHash()), 0};
282
283 auto buildProofWithSequence = [&](uint64_t sequence) {
284 ProofBuilder pb(sequence, 0, key, UNSPENDABLE_ECREG_PAYOUT_SCRIPT);
286 pb.addUTXO(conflictingOutpoint, 10 * COIN, 123456, false, key));
287 return pb.build();
288 };
289
290 auto proofSeq10 = buildProofWithSequence(10);
291 auto proofSeq20 = buildProofWithSequence(20);
292 auto proofSeq30 = buildProofWithSequence(30);
293
294 BOOST_CHECK_EQUAL(testPool.addProofIfNoConflict(proofSeq20),
295 ProofPool::AddProofStatus::SUCCEED);
296
297 Chainstate &active_chainstate = Assert(m_node.chainman)->ActiveChainstate();
298
299 auto getRandomConflictingProofSet = [&active_chainstate]() {
301 buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE),
302 buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE),
303 buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE),
304 };
305 };
306
307 auto checkConflictingProofs =
308 [&](const ProofPool::ConflictingProofSet &conflictingProofs,
309 const ProofPool::ConflictingProofSet &expectedConflictingProofs) {
310 BOOST_CHECK_EQUAL_COLLECTIONS(conflictingProofs.begin(),
311 conflictingProofs.end(),
312 expectedConflictingProofs.begin(),
313 expectedConflictingProofs.end());
314 };
315
316 {
317 // Without override, duplicated proof
318 auto conflictingProofs = getRandomConflictingProofSet();
320 testPool.addProofIfNoConflict(proofSeq20, conflictingProofs),
321 ProofPool::AddProofStatus::DUPLICATED);
322 checkConflictingProofs(conflictingProofs, {});
323 }
324
325 {
326 // With override, duplicated proof
327 auto conflictingProofs = getRandomConflictingProofSet();
329 testPool.addProofIfPreferred(proofSeq20, conflictingProofs),
330 ProofPool::AddProofStatus::DUPLICATED);
331 checkConflictingProofs(conflictingProofs, {});
332 }
333
334 {
335 // Without override, worst proof
336 auto conflictingProofs = getRandomConflictingProofSet();
338 testPool.addProofIfNoConflict(proofSeq10, conflictingProofs),
339 ProofPool::AddProofStatus::REJECTED);
340 checkConflictingProofs(conflictingProofs, {proofSeq20});
341 }
342
343 {
344 // Without override, better proof
345 auto conflictingProofs = getRandomConflictingProofSet();
347 testPool.addProofIfNoConflict(proofSeq30, conflictingProofs),
348 ProofPool::AddProofStatus::REJECTED);
349 checkConflictingProofs(conflictingProofs, {proofSeq20});
350 }
351
352 {
353 // With override, worst proof
354 auto conflictingProofs = getRandomConflictingProofSet();
356 testPool.addProofIfPreferred(proofSeq10, conflictingProofs),
357 ProofPool::AddProofStatus::REJECTED);
358 checkConflictingProofs(conflictingProofs, {proofSeq20});
359 }
360
361 {
362 // With override, better proof
363 auto conflictingProofs = getRandomConflictingProofSet();
365 testPool.addProofIfPreferred(proofSeq30, conflictingProofs),
366 ProofPool::AddProofStatus::SUCCEED);
367 checkConflictingProofs(conflictingProofs, {proofSeq20});
368 }
369}
370
372 ProofPool testPool;
373
374 for (size_t i = 0; i < 10; i++) {
376 }
377
378 Chainstate &active_chainstate = Assert(m_node.chainman)->ActiveChainstate();
379
380 for (size_t i = 0; i < 10; i++) {
381 auto proof = buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE);
383 ProofPool::AddProofStatus::SUCCEED);
384
385 auto retrievedProof = testPool.getProof(proof->getId());
386 BOOST_CHECK_NE(retrievedProof, nullptr);
387 BOOST_CHECK_EQUAL(retrievedProof->getId(), proof->getId());
388 }
389}
390
391BOOST_AUTO_TEST_CASE(get_lowest_score_proof) {
392 ProofPool testPool;
393 BOOST_CHECK_EQUAL(testPool.getLowestScoreProof(), nullptr);
394
395 const CKey key = CKey::MakeCompressedKey();
396 auto buildProofWithRandomOutpoints = [&](uint32_t score) {
397 int numOutpoints = m_rng.rand32() % 10 + 1;
399 for (int i = 0; i < numOutpoints; i++) {
400 Amount amount = 1 * COIN;
401 if (i == numOutpoints - 1) {
402 // Last UTXO is the remainder
403 amount =
404 (int64_t(score) * COIN) / 100 - (numOutpoints - 1) * COIN;
405 }
406 const COutPoint outpoint{TxId(GetRandHash()), 0};
407 BOOST_CHECK(pb.addUTXO(outpoint, amount, 123456, false, key));
408 }
409 return pb.build();
410 };
411
412 // Add some proofs with different scores and check the lowest scoring proof
413 for (int i = 9; i >= 0; i--) {
414 auto proof = buildProofWithRandomOutpoints(MIN_VALID_PROOF_SCORE + i);
416 ProofPool::AddProofStatus::SUCCEED);
417 auto checkLowestScoreProof = testPool.getLowestScoreProof();
418 BOOST_CHECK_EQUAL(checkLowestScoreProof->getScore(),
420 BOOST_CHECK_EQUAL(checkLowestScoreProof->getId(), proof->getId());
421 }
422 BOOST_CHECK_EQUAL(testPool.countProofs(), 10);
423 BOOST_CHECK_EQUAL(testPool.getProofIds().size(), 10);
424
425 auto lowestScoreProof = testPool.getLowestScoreProof();
426
427 // Adding more proofs doesn't change the lowest scoring proof
428 for (size_t i = 1; i < 10; i++) {
429 auto proof = buildProofWithRandomOutpoints(MIN_VALID_PROOF_SCORE + i);
431 ProofPool::AddProofStatus::SUCCEED);
432 auto checkLowestScoreProof = testPool.getLowestScoreProof();
433 BOOST_CHECK_EQUAL(checkLowestScoreProof->getScore(),
435 BOOST_CHECK_EQUAL(checkLowestScoreProof->getId(),
436 lowestScoreProof->getId());
437 }
438 BOOST_CHECK_EQUAL(testPool.countProofs(), 19);
439 BOOST_CHECK_EQUAL(testPool.getProofIds().size(), 19);
440
441 // Remove proofs by lowest score, checking the lowest score as we go
442 for (int scoreCount = 1; scoreCount < 10; scoreCount++) {
443 for (size_t i = 0; i < 2; i++) {
445 testPool.removeProof(testPool.getLowestScoreProof()->getId()));
447 MIN_VALID_PROOF_SCORE + scoreCount);
448 }
449 }
450
451 // Remove the last proof
452 BOOST_CHECK(testPool.removeProof(testPool.getLowestScoreProof()->getId()));
453 BOOST_CHECK_EQUAL(testPool.getLowestScoreProof(), nullptr);
454 BOOST_CHECK_EQUAL(testPool.countProofs(), 0);
455 BOOST_CHECK_EQUAL(testPool.getProofIds().size(), 0);
456}
457
458BOOST_AUTO_TEST_SUITE_END()
static constexpr Amount COIN
Definition: amount.h:154
ArgsManager gArgs
Definition: args.cpp:39
#define Assert(val)
Identity function.
Definition: check.h:87
void ForceSetArg(const std::string &strArg, const std::string &strValue)
Definition: args.cpp:565
void ClearForcedArg(const std::string &strArg)
Remove a forced arg setting, used only in testing.
Definition: args.cpp:616
An encapsulated secp256k1 private key.
Definition: key.h:28
static CKey MakeCompressedKey()
Produce a valid compressed key.
Definition: key.cpp:465
Chainstate stores and provides an API to update our local knowledge of the current best chain.
Definition: validation.h:725
void forEachPeer(Callable &&func) const
Definition: peermanager.h:427
bool addUTXO(COutPoint utxo, Amount amount, uint32_t height, bool is_coinbase, CKey key)
uint32_t getScore() const
Definition: proof.h:172
const ProofId & getId() const
Definition: proof.h:167
Map a proof to each utxo.
Definition: proofpool.h:56
AddProofStatus addProofIfPreferred(const ProofRef &proof, ConflictingProofSet &conflictingProofs)
Attempt to add a proof to the pool.
Definition: proofpool.cpp:58
size_t size() const
Definition: proofpool.h:140
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:30
size_t countProofs() const
Definition: proofpool.cpp:133
bool removeProof(ProofId proofid)
Definition: proofpool.cpp:83
ProofRef getProof(const ProofId &proofid) const
Definition: proofpool.cpp:116
std::set< ProofRef, ConflictingProofComparator > ConflictingProofSet
Definition: proofpool.h:87
ProofRef getLowestScoreProof() const
Definition: proofpool.cpp:127
std::unordered_set< ProofRef, SaltedProofHasher > rescan(PeerManager &peerManager)
Definition: proofpool.cpp:90
ConflictingProofSet getConflicts(const ProofRef &proof) const
Return proofs in this pool that share any UTXO with proof.
Definition: proofpool.cpp:14
ProofIdSet getProofIds() const
Definition: proofpool.cpp:105
static constexpr Amount PROOF_DUST_THRESHOLD
Minimum amount per utxo.
Definition: proof.h:38
const CScript UNSPENDABLE_ECREG_PAYOUT_SCRIPT
Definition: util.h:22
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:20
std::unordered_set< ProofId, SaltedProofIdHasher > ProofIdSet
Definition: proofpool.h:51
NodeContext & m_node
Definition: interfaces.cpp:825
#define BOOST_CHECK_EQUAL(v1, v2)
Definition: object.cpp:18
#define BOOST_CHECK(expr)
Definition: object.cpp:17
BOOST_AUTO_TEST_CASE(get_conflicts)
uint256 GetRandHash() noexcept
========== CONVENIENCE FUNCTIONS FOR COMMONLY USED RANDOMNESS ==========
Definition: random.h:494
Definition: amount.h:23
A TxId is the identifier of a transaction.
Definition: txid.h:14
ProofRef proof
Definition: peermanager.h:89