Bitcoin ABC  0.26.3
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 
7 #include <avalanche/test/util.h>
8 #include <streams.h>
9 
10 #include <test/util/setup_common.h>
11 
12 #include <boost/test/unit_test.hpp>
13 
14 #include <algorithm>
15 
16 namespace avalanche {
17 namespace {
18  struct TestCompactProofs {
19  static std::vector<uint64_t> getShortProofIds(const CompactProofs &cp) {
20  return cp.shortproofids;
21  }
22 
23  static std::vector<PrefilledProof>
24  getPrefilledProofs(const CompactProofs &cp) {
25  return cp.prefilledProofs;
26  }
27 
28  static void addPrefilledProof(CompactProofs &cp, uint32_t index,
29  const ProofRef &proof) {
30  PrefilledProof pp{index, proof};
31  cp.prefilledProofs.push_back(std::move(pp));
32  }
33  };
34 } // namespace
35 } // namespace avalanche
36 
37 using namespace avalanche;
38 
39 // TestingSetup is required for buildRandomProof()
40 BOOST_FIXTURE_TEST_SUITE(compactproofs_tests, TestingSetup)
41 
42 BOOST_AUTO_TEST_CASE(compactproofs_roundtrip) {
43  {
44  CompactProofs cpw;
45  BOOST_CHECK_EQUAL(cpw.size(), 0);
46 
48  BOOST_CHECK_NO_THROW(ss << cpw);
49 
50  CompactProofs cpr;
51  BOOST_CHECK_NO_THROW(ss >> cpr);
52 
53  BOOST_CHECK_EQUAL(cpr.size(), 0);
54  BOOST_CHECK_EQUAL(cpr.getKeys().first, cpw.getKeys().first);
55  BOOST_CHECK_EQUAL(cpr.getKeys().second, cpw.getKeys().second);
56  }
57 
58  Chainstate &active_chainstate = Assert(m_node.chainman)->ActiveChainstate();
59 
60  {
61  // Check index boundaries
62  CompactProofs cp;
63 
64  TestCompactProofs::addPrefilledProof(
65  cp, 0, buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE));
66  TestCompactProofs::addPrefilledProof(
67  cp, std::numeric_limits<uint32_t>::max(),
68  buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE));
69 
71  BOOST_CHECK_NO_THROW(ss << cp);
72 
73  auto prefilledProofs = TestCompactProofs::getPrefilledProofs(cp);
74  BOOST_CHECK_EQUAL(prefilledProofs.size(), 2);
75 
76  BOOST_CHECK_EQUAL(prefilledProofs[0].index, 0);
77  BOOST_CHECK_EQUAL(prefilledProofs[1].index,
78  std::numeric_limits<uint32_t>::max());
79  }
80 
81  auto checkCompactProof = [&](size_t numofProof,
82  size_t numofPrefilledProof) {
84  for (size_t i = 0; i < numofProof; i++) {
85  BOOST_CHECK(proofs.insert(
86  buildRandomProof(active_chainstate, MIN_VALID_PROOF_SCORE)));
87  }
88 
89  CompactProofs cpw(proofs);
90  BOOST_CHECK_EQUAL(cpw.size(), numofProof);
91 
92  uint32_t prefilledProofIndex = 0;
93  for (size_t i = 0; i < numofPrefilledProof; i++) {
94  TestCompactProofs::addPrefilledProof(
95  cpw, prefilledProofIndex++,
97  active_chainstate,
98  GetRand(std::numeric_limits<uint32_t>::max())));
99  }
100  auto prefilledProofs = TestCompactProofs::getPrefilledProofs(cpw);
101  BOOST_CHECK_EQUAL(prefilledProofs.size(), numofPrefilledProof);
102 
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 
150 BOOST_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 
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 
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 
188  BOOST_CHECK_EXCEPTION(ss << cp, std::ios_base::failure,
189  HasReason("differential value overflow"));
190  }
191 
192  {
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  {
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  {
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  {
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  {
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  {
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 
#define Assert(val)
Identity function.
Definition: check.h:65
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:197
Chainstate stores and provides an API to update our local knowledge of the current best chain.
Definition: validation.h:647
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 ProofId & getId() const
Definition: proof.h:169
const SchnorrSig & getSignature() const
Definition: proof.h:167
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:17
NodeContext & m_node
Definition: interfaces.cpp:713
#define BOOST_AUTO_TEST_SUITE_END()
Definition: object.cpp:16
#define BOOST_FIXTURE_TEST_SUITE(a, b)
Definition: object.cpp:14
#define BOOST_CHECK_EQUAL(v1, v2)
Definition: object.cpp:18
#define BOOST_CHECK_NO_THROW(stmt)
Definition: object.cpp:28
#define BOOST_CHECK(expr)
Definition: object.cpp:17
uint64_t GetRand(uint64_t nMax) noexcept
Generate a uniform random integer in the range [0..range).
Definition: random.cpp:650
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:31
@ SER_DISK
Definition: serialize.h:167
@ SER_NETWORK
Definition: serialize.h:166
void WriteCompactSize(CSizeComputer &os, uint64_t nSize)
Definition: serialize.h:1255
Serialization wrapper class for custom integers and enums.
Definition: serialize.h:624
void Ser(Stream &s, I v)
Definition: serialize.h:629
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
static const int PROTOCOL_VERSION
network protocol versioning
Definition: version.h:11