Bitcoin ABC 0.32.5
P2P Digital Currency
compactproofs_tests.cpp
Go to the documentation of this file.
1// Copyright (c) 2022 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
8#include <streams.h>
9#include <validation.h>
10
11#include <test/util/setup_common.h>
12
13#include <boost/test/unit_test.hpp>
14
15#include <algorithm>
16
17namespace avalanche {
18namespace {
19 struct TestCompactProofs {
20 static std::vector<uint64_t> getShortProofIds(const CompactProofs &cp) {
21 return cp.shortproofids;
22 }
23
24 static std::vector<PrefilledProof>
25 getPrefilledProofs(const CompactProofs &cp) {
26 return cp.prefilledProofs;
27 }
28
29 static void addPrefilledProof(CompactProofs &cp, uint32_t index,
30 const ProofRef &proof) {
31 PrefilledProof pp{index, proof};
32 cp.prefilledProofs.push_back(std::move(pp));
33 }
34 };
35} // namespace
36} // namespace avalanche
37
38using namespace avalanche;
39
40// TestingSetup is required for buildRandomProof()
41BOOST_FIXTURE_TEST_SUITE(compactproofs_tests, TestingSetup)
42
43BOOST_AUTO_TEST_CASE(compactproofs_roundtrip) {
44 {
45 CompactProofs cpw;
46 BOOST_CHECK_EQUAL(cpw.size(), 0);
47
48 DataStream ss{};
49 BOOST_CHECK_NO_THROW(ss << cpw);
50
51 CompactProofs cpr;
52 BOOST_CHECK_NO_THROW(ss >> cpr);
53
54 BOOST_CHECK_EQUAL(cpr.size(), 0);
55 BOOST_CHECK_EQUAL(cpr.getKeys().first, cpw.getKeys().first);
56 BOOST_CHECK_EQUAL(cpr.getKeys().second, cpw.getKeys().second);
57 }
58
59 Chainstate &active_chainstate = Assert(m_node.chainman)->ActiveChainstate();
60
61 {
62 // Check index boundaries
64
65 TestCompactProofs::addPrefilledProof(
66 cp, 0, buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE));
67 TestCompactProofs::addPrefilledProof(
68 cp, std::numeric_limits<uint32_t>::max(),
69 buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE));
70
71 DataStream ss{};
72 BOOST_CHECK_NO_THROW(ss << cp);
73
74 auto prefilledProofs = TestCompactProofs::getPrefilledProofs(cp);
75 BOOST_CHECK_EQUAL(prefilledProofs.size(), 2);
76
77 BOOST_CHECK_EQUAL(prefilledProofs[0].index, 0);
78 BOOST_CHECK_EQUAL(prefilledProofs[1].index,
79 std::numeric_limits<uint32_t>::max());
80 }
81
82 auto checkCompactProof = [&](size_t numofProof,
83 size_t numofPrefilledProof) {
85 for (size_t i = 0; i < numofProof; i++) {
86 BOOST_CHECK(proofs.insert(
87 buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE)));
88 }
89
90 CompactProofs cpw(proofs);
91 BOOST_CHECK_EQUAL(cpw.size(), numofProof);
92
93 uint32_t prefilledProofIndex = 0;
94 for (size_t i = 0; i < numofPrefilledProof; i++) {
95 TestCompactProofs::addPrefilledProof(
96 cpw, prefilledProofIndex++,
97 buildRandomProof(active_chainstate,
98 FastRandomContext().rand<uint32_t>()));
99 }
100 auto prefilledProofs = TestCompactProofs::getPrefilledProofs(cpw);
101 BOOST_CHECK_EQUAL(prefilledProofs.size(), numofPrefilledProof);
102
103 DataStream ss{};
104 BOOST_CHECK_NO_THROW(ss << cpw);
105
106 CompactProofs cpr;
107 BOOST_CHECK_NO_THROW(ss >> cpr);
108
109 BOOST_CHECK_EQUAL(cpr.size(), numofProof + numofPrefilledProof);
110 BOOST_CHECK_EQUAL(cpr.getKeys().first, cpw.getKeys().first);
111 BOOST_CHECK_EQUAL(cpr.getKeys().second, cpw.getKeys().second);
112
113 auto comparePrefilledProof = [](const PrefilledProof &lhs,
114 const PrefilledProof &rhs) {
115 return lhs.index == rhs.index &&
116 lhs.proof->getId() == rhs.proof->getId() &&
117 lhs.proof->getSignature() == rhs.proof->getSignature();
118 };
119
120 auto prefilledProofsCpr = TestCompactProofs::getPrefilledProofs(cpr);
121 BOOST_CHECK(std::equal(prefilledProofsCpr.begin(),
122 prefilledProofsCpr.end(),
123 prefilledProofs.begin(), comparePrefilledProof));
124
125 auto shortIds = TestCompactProofs::getShortProofIds(cpr);
126 size_t index = 0;
127 proofs.forEachLeaf([&](auto pLeaf) {
128 const ProofId &proofid = pLeaf->getId();
129 BOOST_CHECK_EQUAL(cpr.getShortID(proofid), cpw.getShortID(proofid));
130 BOOST_CHECK_EQUAL(cpr.getShortID(proofid), shortIds[index]);
131 ++index;
132
133 return true;
134 });
135 };
136
137 // No proof at all
138 checkCompactProof(0, 0);
139
140 // No prefilled proofs
141 checkCompactProof(1000, 0);
142
143 // Only prefilled proofs
144 checkCompactProof(0, 1000);
145
146 // Mixed case
147 checkCompactProof(1000, 1000);
148}
149
150BOOST_AUTO_TEST_CASE(compactproofs_overflow) {
151 Chainstate &active_chainstate = Assert(m_node.chainman)->ActiveChainstate();
152 {
153 CompactProofs cp;
154
155 TestCompactProofs::addPrefilledProof(
156 cp, 0, buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE));
157 TestCompactProofs::addPrefilledProof(
158 cp, 0, buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE));
159
160 DataStream ss{};
161 BOOST_CHECK_EXCEPTION(ss << cp, std::ios_base::failure,
162 HasReason("differential value overflow"));
163 }
164
165 {
166 CompactProofs cp;
167
168 TestCompactProofs::addPrefilledProof(
169 cp, 1, buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE));
170 TestCompactProofs::addPrefilledProof(
171 cp, 0, buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE));
172
173 DataStream ss{};
174 BOOST_CHECK_EXCEPTION(ss << cp, std::ios_base::failure,
175 HasReason("differential value overflow"));
176 }
177
178 {
179 CompactProofs cp;
180
181 TestCompactProofs::addPrefilledProof(
182 cp, std::numeric_limits<uint32_t>::max(),
183 buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE));
184 TestCompactProofs::addPrefilledProof(
185 cp, 0, buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE));
186
187 DataStream ss{};
188 BOOST_CHECK_EXCEPTION(ss << cp, std::ios_base::failure,
189 HasReason("differential value overflow"));
190 }
191
192 {
193 DataStream ss{};
194 // shortproofidk0, shortproofidk1
195 ss << uint64_t(0) << uint64_t(0);
196 // shortproofids.size()
197 WriteCompactSize(ss, MAX_SIZE + 1);
198
199 CompactProofs cp;
200 BOOST_CHECK_EXCEPTION(ss >> cp, std::ios_base::failure,
201 HasReason("ReadCompactSize(): size too large"));
202 }
203
204 {
205 DataStream ss{};
206 // shortproofidk0, shortproofidk1
207 ss << uint64_t(0) << uint64_t(0);
208 // shortproofids.size()
209 WriteCompactSize(ss, 0);
210 // prefilledProofs.size()
211 WriteCompactSize(ss, MAX_SIZE + 1);
212
213 CompactProofs cp;
214 BOOST_CHECK_EXCEPTION(ss >> cp, std::ios_base::failure,
215 HasReason("ReadCompactSize(): size too large"));
216 }
217
218 {
219 DataStream ss{};
220 // shortproofidk0, shortproofidk1
221 ss << uint64_t(0) << uint64_t(0);
222 // shortproofids.size()
223 WriteCompactSize(ss, 0);
224 // prefilledProofs.size()
225 WriteCompactSize(ss, 1);
226 // prefilledProofs[0].index
227 WriteCompactSize(ss, MAX_SIZE + 1);
228 // prefilledProofs[0].proof
229 ss << buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE);
230
231 CompactProofs cp;
232 BOOST_CHECK_EXCEPTION(ss >> cp, std::ios_base::failure,
233 HasReason("ReadCompactSize(): size too large"));
234 }
235
236 // Compute the number of MAX_SIZE increment we need to cause an overflow
237 const uint64_t overflow =
238 uint64_t(std::numeric_limits<uint32_t>::max()) + 1;
239 // Due to differential encoding, a value of MAX_SIZE bumps the index by
240 // MAX_SIZE + 1
241 BOOST_CHECK_GE(overflow, MAX_SIZE + 1);
242 const uint64_t overflowIter = overflow / (MAX_SIZE + 1);
243
244 // Make sure the iteration fits in an uint32_t and is <= MAX_SIZE
245 BOOST_CHECK_LE(overflowIter, std::numeric_limits<uint32_t>::max());
246 BOOST_CHECK_LE(overflowIter, MAX_SIZE);
247 uint32_t remainder = uint32_t(overflow - ((MAX_SIZE + 1) * overflowIter));
248
249 {
250 DataStream ss{};
251 // shortproofidk0, shortproofidk1
252 ss << uint64_t(0) << uint64_t(0);
253 // shortproofids.size()
254 WriteCompactSize(ss, 0);
255 // prefilledProofs.size()
256 WriteCompactSize(ss, overflowIter + 1);
257 for (uint32_t i = 0; i < overflowIter; i++) {
258 // prefilledProofs[i].index
260 // prefilledProofs[i].proof
261 ss << buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE);
262 }
263 // This is the prefilled proof causing the overflow
264 WriteCompactSize(ss, remainder);
265 ss << buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE);
266
267 CompactProofs cp;
268 BOOST_CHECK_EXCEPTION(ss >> cp, std::ios_base::failure,
269 HasReason("differential value overflow"));
270 }
271
272 {
273 DataStream ss{};
274 // shortproofidk0, shortproofidk1
275 ss << uint64_t(0) << uint64_t(0);
276 // shortproofids.size()
277 WriteCompactSize(ss, 1);
278 // shortproofids[0]
280 // prefilledProofs.size()
281 WriteCompactSize(ss, overflowIter + 1);
282 for (uint32_t i = 0; i < overflowIter; i++) {
283 // prefilledProofs[i].index
285 // prefilledProofs[i].proof
286 ss << buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE);
287 }
288 // This prefilled proof isn't enough to cause the overflow alone, but it
289 // overflows due to the extra shortid.
290 WriteCompactSize(ss, remainder - 1);
291 ss << buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE);
292
293 CompactProofs cp;
294 // ss >> cp;
295 BOOST_CHECK_EXCEPTION(ss >> cp, std::ios_base::failure,
296 HasReason("indexes overflowed 32 bits"));
297 }
298
299 {
300 DataStream ss{};
301 // shortproofidk0, shortproofidk1
302 ss << uint64_t(0) << uint64_t(0);
303 // shortproofids.size()
304 WriteCompactSize(ss, 0);
305 // prefilledProofs.size()
306 WriteCompactSize(ss, 2);
307 // prefilledProofs[0].index
308 WriteCompactSize(ss, 0);
309 // prefilledProofs[0].proof
310 ss << buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE);
311 // prefilledProofs[1].index = 1 is differentially encoded, which means
312 // it has an absolute index of 2. This leaves no proof at index 1.
313 WriteCompactSize(ss, 1);
314 // prefilledProofs[1].proof
315 ss << buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE);
316
317 CompactProofs cp;
318 BOOST_CHECK_EXCEPTION(ss >> cp, std::ios_base::failure,
319 HasReason("non contiguous indexes"));
320 }
321}
322
323BOOST_AUTO_TEST_SUITE_END()
#define Assert(val)
Identity function.
Definition: check.h:84
Chainstate stores and provides an API to update our local knowledge of the current best chain.
Definition: validation.h:734
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:174
Fast randomness source.
Definition: random.h:411
std::vector< PrefilledProof > prefilledProofs
Definition: compactproofs.h:58
uint64_t getShortID(const ProofId &proofid) const
std::pair< uint64_t, uint64_t > getKeys() const
Definition: compactproofs.h:73
std::vector< uint64_t > shortproofids
Definition: compactproofs.h:57
const SchnorrSig & getSignature() const
Definition: proof.h:168
const ProofId & getId() const
Definition: proof.h:170
BOOST_AUTO_TEST_CASE(compactproofs_roundtrip)
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
NodeContext & m_node
Definition: interfaces.cpp:822
#define BOOST_CHECK_EQUAL(v1, v2)
Definition: object.cpp:18
#define BOOST_CHECK_NO_THROW(stmt)
Definition: object.cpp:29
#define BOOST_CHECK(expr)
Definition: object.cpp:17
static constexpr uint64_t MAX_SIZE
The maximum size of a serialized object in bytes or number of elements (for eg vectors) when the size...
Definition: serialize.h:34
void WriteCompactSize(CSizeComputer &os, uint64_t nSize)
Definition: serialize.h:1276
Serialization wrapper class for custom integers and enums.
Definition: serialize.h:670
void Ser(Stream &s, I v)
Definition: serialize.h:675
bool forEachLeaf(Callable &&func) const
Definition: radix.h:144
bool insert(const RCUPtr< T > &value)
Insert a value into the tree.
Definition: radix.h:112
avalanche::ProofRef proof
Definition: compactproofs.h:33