Bitcoin ABC 0.30.5
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
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
20using namespace avalanche;
21
22BOOST_FIXTURE_TEST_SUITE(proofpool_tests, TestChain100Setup)
23
24BOOST_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
46BOOST_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) {
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);
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
149BOOST_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) {
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
229BOOST_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);
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++) {
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);
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
343BOOST_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);
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);
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++) {
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
410BOOST_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
Chainstate stores and provides an API to update our local knowledge of the current best chain.
Definition: validation.h:699
void forEachPeer(Callable &&func) const
Definition: peermanager.h:420
bool addUTXO(COutPoint utxo, Amount amount, uint32_t height, bool is_coinbase, CKey key)
uint32_t getScore() const
Definition: proof.h:174
const ProofId & getId() const
Definition: proof.h:169
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
Definition: amount.h:19
A TxId is the identifier of a transaction.
Definition: txid.h:14
ProofRef proof
Definition: peermanager.h:89