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