Line data Source code
1 : // Copyright (c) 2019-2025 The Dash Core developers
2 : // Distributed under the MIT software license, see the accompanying
3 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 :
5 : #include <bls/bls.h>
6 : #include <bls/bls_batchverifier.h>
7 : #include <util/helpers.h>
8 :
9 : #include <clientversion.h>
10 : #include <random.h>
11 : #include <streams.h>
12 : #include <util/strencodings.h>
13 :
14 : #include <boost/test/unit_test.hpp>
15 :
16 146 : BOOST_AUTO_TEST_SUITE(bls_tests)
17 :
18 2 : void FuncSign(const bool legacy_scheme)
19 : {
20 2 : bls::bls_legacy_scheme.store(legacy_scheme);
21 :
22 2 : CBLSSecretKey sk1, sk2;
23 2 : sk1.MakeNewKey();
24 2 : sk2.MakeNewKey();
25 :
26 2 : uint256 msgHash1 = uint256::ONE;
27 2 : uint256 msgHash2 = uint256::TWO;
28 :
29 2 : auto sig1 = sk1.Sign(msgHash1, legacy_scheme);
30 2 : auto sig2 = sk2.Sign(msgHash1, legacy_scheme);
31 2 : BOOST_CHECK(sig1.VerifyInsecure(sk1.GetPublicKey(), msgHash1));
32 2 : BOOST_CHECK(!sig1.VerifyInsecure(sk1.GetPublicKey(), msgHash2));
33 2 : BOOST_CHECK(!sig2.VerifyInsecure(sk1.GetPublicKey(), msgHash1));
34 2 : BOOST_CHECK(!sig2.VerifyInsecure(sk2.GetPublicKey(), msgHash2));
35 2 : BOOST_CHECK(sig2.VerifyInsecure(sk2.GetPublicKey(), msgHash1));
36 :
37 : return;
38 2 : }
39 :
40 2 : void FuncSerialize(const bool legacy_scheme)
41 : {
42 2 : bls::bls_legacy_scheme.store(legacy_scheme);
43 :
44 2 : CBLSSecretKey sk;
45 2 : CDataStream ds2(SER_DISK, CLIENT_VERSION), ds3(SER_DISK, CLIENT_VERSION);
46 2 : uint256 msgHash = uint256::ONE;
47 :
48 2 : sk.MakeNewKey();
49 2 : CBLSSignature sig1 = sk.Sign(msgHash, legacy_scheme);
50 2 : ds2 << sig1;
51 2 : ds3 << CBLSSignatureVersionWrapper(const_cast<CBLSSignature&>(sig1), !legacy_scheme);
52 :
53 2 : CBLSSignature sig2;
54 2 : ds2 >> sig2;
55 2 : BOOST_CHECK(sig1 == sig2);
56 :
57 2 : CBLSSignature sig3;
58 2 : ds3 >> CBLSSignatureVersionWrapper(const_cast<CBLSSignature&>(sig3), !legacy_scheme);
59 2 : BOOST_CHECK(sig1 == sig3);
60 :
61 : return;
62 2 : }
63 :
64 2 : void FuncSetHexStr(const bool legacy_scheme)
65 : {
66 2 : bls::bls_legacy_scheme.store(legacy_scheme);
67 :
68 : // Note: 2nd bool argument for SetHexStr for bls::PrivateKey has a meaning modOrder, not is-legacy
69 2 : CBLSSecretKey sk;
70 2 : std::string strValidSecret = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f";
71 : // Note: invalid string passed to SetHexStr() should cause it to fail and reset key internal data
72 2 : BOOST_CHECK(sk.SetHexStr(strValidSecret, false));
73 2 : BOOST_CHECK(!sk.SetHexStr("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1g", false)); // non-hex
74 2 : BOOST_CHECK(!sk.IsValid());
75 2 : BOOST_CHECK(sk == CBLSSecretKey());
76 : // Try few more invalid strings
77 2 : BOOST_CHECK(sk.SetHexStr(strValidSecret, false));
78 2 : BOOST_CHECK(!sk.SetHexStr("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e", false)); // hex but too short
79 2 : BOOST_CHECK(!sk.IsValid());
80 2 : BOOST_CHECK(sk.SetHexStr(strValidSecret, false));
81 2 : BOOST_CHECK(
82 : !sk.SetHexStr("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20", false)); // hex but too long
83 2 : BOOST_CHECK(!sk.IsValid());
84 :
85 : return;
86 2 : }
87 :
88 2 : void FuncKeyAgg(const bool legacy_scheme)
89 : {
90 2 : bls::bls_legacy_scheme.store(legacy_scheme);
91 :
92 2 : CBLSSecretKey sk1, sk2;
93 2 : sk1.MakeNewKey();
94 2 : sk2.MakeNewKey();
95 :
96 2 : CBLSPublicKey ag_pk = sk1.GetPublicKey();
97 2 : ag_pk.AggregateInsecure(sk2.GetPublicKey());
98 :
99 2 : CBLSSecretKey ag_sk = sk1;
100 2 : ag_sk.AggregateInsecure(sk2);
101 :
102 2 : BOOST_CHECK(ag_pk == ag_sk.GetPublicKey());
103 :
104 2 : uint256 msgHash1 = uint256::ONE;
105 2 : uint256 msgHash2 = uint256::TWO;
106 :
107 2 : auto sig = ag_sk.Sign(msgHash1, legacy_scheme);
108 2 : BOOST_CHECK(sig.VerifyInsecure(ag_pk, msgHash1));
109 2 : BOOST_CHECK(!sig.VerifyInsecure(ag_pk, msgHash2));
110 2 : }
111 :
112 2 : void FuncKeyAggVec(const bool legacy_scheme)
113 : {
114 2 : bls::bls_legacy_scheme.store(legacy_scheme);
115 :
116 2 : std::vector<CBLSSecretKey> vec_sk;
117 2 : std::vector<CBLSPublicKey> vec_pk;
118 :
119 : {
120 2 : auto ret = CBLSSecretKey::AggregateInsecure(vec_sk);
121 2 : BOOST_CHECK(ret == CBLSSecretKey());
122 2 : }
123 : {
124 2 : auto ret = CBLSPublicKey::AggregateInsecure(vec_pk);
125 2 : BOOST_CHECK(ret == CBLSPublicKey());
126 : }
127 :
128 : // In practice, we only aggregate 400 key shares at any given time, something substantially larger than that should
129 : // be good. Plus this is very very fast, so who cares!
130 2 : int key_count = 10000;
131 2 : vec_sk.reserve(key_count);
132 2 : vec_pk.reserve(key_count);
133 2 : CBLSSecretKey sk;
134 20002 : for (int i = 0; i < key_count; i++) {
135 20000 : sk.MakeNewKey();
136 20000 : vec_sk.push_back(sk);
137 20000 : vec_pk.push_back(sk.GetPublicKey());
138 20000 : }
139 :
140 2 : CBLSSecretKey ag_sk = CBLSSecretKey::AggregateInsecure(vec_sk);
141 2 : CBLSPublicKey ag_pk = CBLSPublicKey::AggregateInsecure(vec_pk);
142 :
143 2 : BOOST_CHECK(ag_sk.IsValid());
144 2 : BOOST_CHECK(ag_pk.IsValid());
145 :
146 2 : uint256 msgHash1 = uint256::ONE;
147 2 : uint256 msgHash2 = uint256::TWO;
148 :
149 2 : auto sig = ag_sk.Sign(msgHash1, legacy_scheme);
150 2 : BOOST_CHECK(sig.VerifyInsecure(ag_pk, msgHash1));
151 2 : BOOST_CHECK(!sig.VerifyInsecure(ag_pk, msgHash2));
152 2 : }
153 :
154 :
155 2 : void FuncSigAggSub(const bool legacy_scheme)
156 : {
157 2 : bls::bls_legacy_scheme.store(legacy_scheme);
158 :
159 2 : int count = 20;
160 2 : std::vector<CBLSPublicKey> vec_pks;
161 2 : std::vector<uint256> vec_hashes;
162 2 : std::vector<CBLSSignature> vec_sigs;
163 2 : vec_pks.reserve(count);
164 2 : vec_hashes.reserve(count);
165 2 : vec_sigs.reserve(count);
166 :
167 2 : CBLSSignature sig;
168 2 : auto sk = CBLSSecretKey();
169 2 : uint256 hash;
170 42 : for (int i = 0; i < count; i++) {
171 40 : sk.MakeNewKey();
172 40 : vec_pks.push_back(sk.GetPublicKey());
173 40 : hash = GetRandHash();
174 40 : vec_hashes.push_back(hash);
175 40 : CBLSSignature sig_i = sk.Sign(hash, legacy_scheme);
176 40 : vec_sigs.push_back(sig_i);
177 40 : if (i == 0) {
178 : // first sig is assigned directly
179 2 : sig = sig_i;
180 2 : } else {
181 : // all other sigs are aggregated into the previously computed/stored sig
182 38 : sig.AggregateInsecure(sig_i);
183 : }
184 40 : BOOST_CHECK(sig.VerifyInsecureAggregated(vec_pks, vec_hashes));
185 40 : }
186 : // Create an aggregated signature from the vector of individual signatures
187 2 : auto vecSig = CBLSSignature::AggregateInsecure(vec_sigs);
188 2 : BOOST_CHECK(vecSig.VerifyInsecureAggregated(vec_pks, vec_hashes));
189 : // Check that these two signatures are equal
190 2 : BOOST_CHECK(sig == vecSig);
191 :
192 : // Test that the sig continues to be valid when subtracting sigs via `SubInsecure`
193 :
194 40 : for (int i = 0; i < count - 1; i++) {
195 38 : auto top_sig = vec_sigs.back();
196 38 : vec_pks.pop_back();
197 38 : vec_hashes.pop_back();
198 38 : BOOST_CHECK(!sig.VerifyInsecureAggregated(vec_pks, vec_hashes));
199 38 : sig.SubInsecure(top_sig);
200 38 : BOOST_CHECK(sig.VerifyInsecureAggregated(vec_pks, vec_hashes));
201 38 : vec_sigs.pop_back();
202 38 : }
203 : // Check that the final left-over sig validates
204 2 : BOOST_CHECK_EQUAL(vec_sigs.size(), 1);
205 2 : BOOST_CHECK_EQUAL(vec_pks.size(), 1);
206 2 : BOOST_CHECK_EQUAL(vec_hashes.size(), 1);
207 2 : BOOST_CHECK(vec_sigs[0].VerifyInsecure(vec_pks[0], vec_hashes[0]));
208 2 : }
209 :
210 :
211 2 : void FuncSigAggSecure(const bool legacy_scheme)
212 : {
213 2 : bls::bls_legacy_scheme.store(legacy_scheme);
214 :
215 2 : int count = 10;
216 :
217 2 : uint256 hash = GetRandHash();
218 :
219 2 : std::vector<CBLSSignature> vec_sigs;
220 2 : std::vector<CBLSPublicKey> vec_pks;
221 :
222 2 : CBLSSecretKey sk;
223 22 : for (int i = 0; i < count; i++) {
224 20 : sk.MakeNewKey();
225 20 : vec_pks.push_back(sk.GetPublicKey());
226 20 : vec_sigs.push_back(sk.Sign(hash, legacy_scheme));
227 20 : }
228 :
229 2 : auto sec_agg_sig = CBLSSignature::AggregateSecure(vec_sigs, vec_pks, hash);
230 2 : BOOST_CHECK(sec_agg_sig.IsValid());
231 2 : BOOST_CHECK(sec_agg_sig.VerifySecureAggregated(vec_pks, hash));
232 2 : }
233 :
234 2 : void FuncDHExchange(const bool legacy_scheme)
235 : {
236 2 : bls::bls_legacy_scheme.store(legacy_scheme);
237 :
238 2 : CBLSSecretKey sk1, sk2;
239 2 : sk1.MakeNewKey();
240 2 : sk2.MakeNewKey();
241 :
242 2 : CBLSPublicKey pk1, pk2;
243 2 : pk1 = sk1.GetPublicKey();
244 2 : pk2 = sk2.GetPublicKey();
245 :
246 : // Perform diffie-helman exchange
247 2 : CBLSPublicKey pke1, pke2;
248 2 : pke1.DHKeyExchange(sk1, pk2);
249 2 : pke2.DHKeyExchange(sk2, pk1);
250 :
251 2 : BOOST_CHECK(pke1.IsValid());
252 2 : BOOST_CHECK(pke2.IsValid());
253 2 : BOOST_CHECK(pke1 == pke2);
254 2 : }
255 :
256 32 : struct Message
257 : {
258 : uint32_t sourceId;
259 : uint32_t msgId;
260 : uint256 msgHash;
261 : CBLSSecretKey sk;
262 : CBLSPublicKey pk;
263 : CBLSSignature sig;
264 : bool valid;
265 : };
266 :
267 32 : static void AddMessage(std::vector<Message>& vec, uint32_t sourceId, uint32_t msgId, uint8_t msgHash, bool valid)
268 : {
269 32 : bool legacy_scheme = bls::bls_legacy_scheme.load();
270 32 : Message m;
271 32 : m.sourceId = sourceId;
272 32 : m.msgId = msgId;
273 32 : m.msgHash = uint256(msgHash);
274 32 : m.sk.MakeNewKey();
275 32 : m.pk = m.sk.GetPublicKey();
276 32 : m.sig = m.sk.Sign(m.msgHash, legacy_scheme);
277 32 : m.valid = valid;
278 :
279 32 : if (!valid) {
280 4 : CBLSSecretKey tmp;
281 4 : tmp.MakeNewKey();
282 4 : m.sig = tmp.Sign(m.msgHash, legacy_scheme);
283 4 : }
284 :
285 32 : vec.emplace_back(m);
286 32 : }
287 :
288 56 : static void Verify(std::vector<Message>& vec, bool secureVerification, bool perMessageFallback)
289 : {
290 56 : CBLSBatchVerifier<uint32_t, uint32_t> batchVerifier(secureVerification, perMessageFallback);
291 :
292 56 : std::set<uint32_t> expectedBadMessages;
293 56 : std::set<uint32_t> expectedBadSources;
294 424 : for (auto& m : vec) {
295 368 : if (!m.valid) {
296 32 : expectedBadMessages.emplace(m.msgId);
297 32 : expectedBadSources.emplace(m.sourceId);
298 32 : }
299 :
300 368 : batchVerifier.PushMessage(m.sourceId, m.msgId, m.msgHash, m.sig, m.pk);
301 : }
302 :
303 56 : batchVerifier.Verify();
304 :
305 56 : BOOST_CHECK(batchVerifier.badSources == expectedBadSources);
306 :
307 56 : if (perMessageFallback) {
308 28 : BOOST_CHECK(batchVerifier.badMessages == expectedBadMessages);
309 28 : } else {
310 28 : BOOST_CHECK(batchVerifier.badMessages.empty());
311 : }
312 56 : }
313 :
314 14 : static void Verify(std::vector<Message>& vec)
315 : {
316 14 : Verify(vec, false, false);
317 14 : Verify(vec, true, false);
318 14 : Verify(vec, false, true);
319 14 : Verify(vec, true, true);
320 14 : }
321 :
322 2 : void FuncBatchVerifier(const bool legacy_scheme)
323 : {
324 2 : bls::bls_legacy_scheme.store(legacy_scheme);
325 :
326 2 : std::vector<Message> msgs;
327 :
328 : // distinct messages from distinct sources
329 2 : AddMessage(msgs, 1, 1, 1, true);
330 2 : AddMessage(msgs, 2, 2, 2, true);
331 2 : AddMessage(msgs, 3, 3, 3, true);
332 2 : Verify(msgs);
333 :
334 : // distinct messages from same source
335 2 : AddMessage(msgs, 4, 4, 4, true);
336 2 : AddMessage(msgs, 4, 5, 5, true);
337 2 : AddMessage(msgs, 4, 6, 6, true);
338 2 : Verify(msgs);
339 :
340 : // invalid sig
341 2 : AddMessage(msgs, 7, 7, 7, false);
342 2 : Verify(msgs);
343 :
344 : // same message as before, but from another source and with valid sig
345 2 : AddMessage(msgs, 8, 8, 7, true);
346 2 : Verify(msgs);
347 :
348 : // same message as before, but from another source and signed with another key
349 2 : AddMessage(msgs, 9, 9, 7, true);
350 2 : Verify(msgs);
351 :
352 2 : msgs.clear();
353 : // same message, signed by multiple keys
354 2 : AddMessage(msgs, 1, 1, 1, true);
355 2 : AddMessage(msgs, 1, 2, 1, true);
356 2 : AddMessage(msgs, 1, 3, 1, true);
357 2 : AddMessage(msgs, 2, 4, 1, true);
358 2 : AddMessage(msgs, 2, 5, 1, true);
359 2 : AddMessage(msgs, 2, 6, 1, true);
360 2 : Verify(msgs);
361 :
362 : // last message invalid from one source
363 2 : AddMessage(msgs, 1, 7, 1, false);
364 2 : Verify(msgs);
365 2 : }
366 :
367 2 : void FuncThresholdSignature(const bool legacy_scheme)
368 : {
369 2 : bls::bls_legacy_scheme.store(legacy_scheme);
370 :
371 2 : [[maybe_unused]] const uint256 hash = GetRandHash();
372 :
373 2 : constexpr size_t m_size = 20;
374 2 : constexpr size_t m_threshold = 15;
375 :
376 2 : std::vector<CBLSSecretKey> v_threshold_sks;
377 2 : std::vector<CBLSPublicKey> v_threshold_pks;
378 32 : for ([[maybe_unused]] const auto i : util::irange(m_threshold)) {
379 30 : CBLSSecretKey sk;
380 30 : sk.MakeNewKey();
381 30 : v_threshold_sks.push_back(sk);
382 30 : v_threshold_pks.emplace_back(sk.GetPublicKey());
383 30 : }
384 :
385 2 : CBLSSecretKey thr_sk = v_threshold_sks[0];
386 2 : CBLSPublicKey thr_pk = v_threshold_pks[0];
387 2 : CBLSSignature thr_sig = thr_sk.Sign(hash, legacy_scheme);
388 :
389 2 : std::vector<CBLSId> v_size_ids;
390 2 : std::vector<CBLSSecretKey> v_size_sk_shares;
391 2 : std::vector<CBLSPublicKey> v_size_pk_shares;
392 42 : for ([[maybe_unused]] const auto m_shares : util::irange(m_size)) {
393 40 : v_size_ids.emplace_back(GetRandHash());
394 40 : CBLSSecretKey sk;
395 40 : BOOST_CHECK(sk.SecretKeyShare(v_threshold_sks, v_size_ids[m_shares]));
396 40 : v_size_sk_shares.push_back(sk);
397 40 : CBLSPublicKey pk;
398 40 : BOOST_CHECK(pk.PublicKeyShare(v_threshold_pks, v_size_ids[m_shares]));
399 40 : v_size_pk_shares.push_back(pk);
400 :
401 40 : std::vector<CBLSSignature> v_share_sigs;
402 40 : std::vector<CBLSId> v_share_ids;
403 420 : for ([[maybe_unused]] const auto j : util::irange(m_shares)) {
404 380 : v_share_sigs.emplace_back(v_size_sk_shares[j].Sign(hash, legacy_scheme));
405 380 : BOOST_CHECK(v_share_sigs.back().VerifyInsecure(v_size_pk_shares[j], hash));
406 380 : v_share_ids.push_back(v_size_ids[j]);
407 : }
408 :
409 40 : CBLSSignature rec_share_sig;
410 40 : BOOST_CHECK_EQUAL(rec_share_sig.Recover(v_share_sigs, v_share_ids), m_shares >= 2);
411 40 : BOOST_CHECK_EQUAL(rec_share_sig.IsValid(), m_shares >= 2);
412 40 : BOOST_CHECK_EQUAL(rec_share_sig == thr_sig, m_shares >= m_threshold);
413 40 : BOOST_CHECK_EQUAL(rec_share_sig.VerifyInsecure(thr_pk, hash), m_shares >= m_threshold);
414 40 : }
415 2 : }
416 :
417 148 : BOOST_AUTO_TEST_CASE(bls_sethexstr_tests)
418 : {
419 1 : FuncSetHexStr(true);
420 1 : FuncSetHexStr(false);
421 1 : }
422 :
423 148 : BOOST_AUTO_TEST_CASE(bls_serialize_tests)
424 : {
425 1 : FuncSerialize(true);
426 1 : FuncSerialize(false);
427 1 : }
428 :
429 148 : BOOST_AUTO_TEST_CASE(bls_sig_tests)
430 : {
431 1 : FuncSign(true);
432 1 : FuncSign(false);
433 1 : }
434 :
435 148 : BOOST_AUTO_TEST_CASE(bls_key_agg_tests)
436 : {
437 1 : FuncKeyAgg(true);
438 1 : FuncKeyAgg(false);
439 1 : }
440 :
441 148 : BOOST_AUTO_TEST_CASE(bls_key_agg_vec_tests)
442 : {
443 1 : FuncKeyAggVec(true);
444 1 : FuncKeyAggVec(false);
445 1 : }
446 :
447 148 : BOOST_AUTO_TEST_CASE(bls_sig_agg_sub_tests)
448 : {
449 1 : FuncSigAggSub(true);
450 1 : FuncSigAggSub(false);
451 1 : }
452 :
453 148 : BOOST_AUTO_TEST_CASE(bls_sig_agg_secure_tests)
454 : {
455 1 : FuncSigAggSecure(true);
456 1 : FuncSigAggSecure(false);
457 1 : }
458 :
459 148 : BOOST_AUTO_TEST_CASE(bls_dh_exchange_tests)
460 : {
461 1 : FuncDHExchange(true);
462 1 : FuncDHExchange(false);
463 1 : }
464 :
465 148 : BOOST_AUTO_TEST_CASE(batch_verifier_tests)
466 : {
467 1 : FuncBatchVerifier(true);
468 1 : FuncBatchVerifier(false);
469 1 : }
470 :
471 148 : BOOST_AUTO_TEST_CASE(bls_threshold_signature_tests)
472 : {
473 1 : FuncThresholdSignature(true);
474 1 : FuncThresholdSignature(false);
475 1 : }
476 :
477 : // A dummy BLS object that satisfies the minimal interface expected by CBLSLazyWrapper.
478 : class DummyBLS
479 : {
480 : public:
481 : // Define a fixed serialization size (for testing purposes).
482 : static const size_t SerSize = 4;
483 18 : std::array<uint8_t, SerSize> data{};
484 :
485 54 : DummyBLS() { data.fill(0); }
486 :
487 : // A dummy validity check: valid if any byte is non-zero.
488 1 : bool IsValid() const
489 : {
490 2 : return std::any_of(data.begin(), data.end(), [](uint8_t c) { return c != 0; });
491 : }
492 :
493 : // Convert to bytes; ignore the legacy flag for simplicity.
494 3 : std::array<uint8_t, SerSize> ToBytes(bool /*legacy*/) const { return data; }
495 :
496 : // Set from bytes; again, ignore the legacy flag.
497 1 : void SetBytes(const std::array<uint8_t, SerSize>& bytes, bool /*legacy*/) { data = bytes; }
498 :
499 : // A dummy malleability check: simply compares the stored data to the given bytes.
500 1 : bool CheckMalleable(const std::array<uint8_t, SerSize>& bytes, bool /*legacy*/) const { return data == bytes; }
501 :
502 : // Reset the object to an "empty" state.
503 : void Reset() { data.fill(0); }
504 :
505 : // Produce a string representation.
506 : std::string ToString(bool /*legacy*/) const { return HexStr(data); }
507 :
508 : // Equality operator.
509 3 : bool operator==(const DummyBLS& other) const { return data == other.data; }
510 : };
511 :
512 : // Define a type alias for our lazy wrapper instantiated with DummyBLS.
513 : using LazyDummyBLS = CBLSLazyWrapper<DummyBLS>;
514 :
515 : // Test 1: Two default (unset) wrappers should compare equal.
516 148 : BOOST_AUTO_TEST_CASE(test_default_equality)
517 : {
518 1 : LazyDummyBLS lazy1;
519 1 : LazyDummyBLS lazy2;
520 : // Neither instance has been set, so they represent the default/null object.
521 1 : BOOST_CHECK(lazy1 == lazy2);
522 1 : }
523 :
524 : // Test 2: A default wrapper and one initialized with a nonzero DummyBLS should compare unequal.
525 148 : BOOST_AUTO_TEST_CASE(test_non_default_vs_default)
526 : {
527 1 : LazyDummyBLS lazy_default;
528 1 : LazyDummyBLS lazy_set;
529 1 : DummyBLS obj;
530 1 : obj.data = {1, 0, 0, 0}; // nonzero data makes the object valid
531 1 : lazy_set.Set(obj, false);
532 1 : BOOST_CHECK(!(lazy_default == lazy_set));
533 1 : BOOST_CHECK(lazy_default != lazy_set);
534 1 : }
535 :
536 : // Test 2: A default wrapper and one initialized with a nonzero DummyBLS should compare unequal.
537 148 : BOOST_AUTO_TEST_CASE(test_non_default_vs_different)
538 : {
539 1 : LazyDummyBLS lazy_a;
540 1 : LazyDummyBLS lazy_b;
541 1 : DummyBLS obj;
542 1 : obj.data = {1, 2, 3, 4}; // nonzero data makes the object valid
543 1 : lazy_a.Set(obj, false);
544 1 : obj.data = {2, 2, 3, 4}; // nonzero data makes the object valid
545 1 : lazy_b.Set(obj, false);
546 1 : BOOST_CHECK(lazy_a != lazy_b);
547 1 : }
548 :
549 : // Test 3: Two wrappers set with the same underlying DummyBLS value compare equal.
550 148 : BOOST_AUTO_TEST_CASE(test_equality_same_value)
551 : {
552 1 : LazyDummyBLS lazy1;
553 1 : LazyDummyBLS lazy2;
554 1 : BOOST_CHECK(lazy1 == lazy2);
555 1 : DummyBLS obj;
556 1 : obj.data = {5, 6, 7, 8};
557 1 : lazy1.Set(obj, false);
558 1 : BOOST_CHECK(lazy1 != lazy2);
559 1 : lazy2.Set(obj, false);
560 1 : BOOST_CHECK(lazy1 == lazy2);
561 1 : }
562 :
563 : // Test 4: Serialization and unserialization preserve the wrapped value.
564 148 : BOOST_AUTO_TEST_CASE(test_serialization_unserialization)
565 : {
566 1 : LazyDummyBLS lazy1;
567 1 : DummyBLS obj;
568 1 : obj.data = {9, 10, 11, 12};
569 : // Set with a specific legacy flag (true in this case)
570 1 : lazy1.Set(obj, true);
571 :
572 : // Serialize the lazy object into a data stream.
573 1 : CDataStream ds(SER_DISK, CLIENT_VERSION);
574 1 : lazy1.Serialize(ds, true);
575 :
576 : // Create a new instance and unserialize the data into it.
577 1 : LazyDummyBLS lazy2;
578 1 : lazy2.Unserialize(ds, true);
579 1 : BOOST_CHECK(lazy1 == lazy2);
580 1 : BOOST_CHECK(lazy2.Get() == obj);
581 1 : }
582 :
583 : // Test 5: Two wrappers wrapping the same object should have the same hash.
584 148 : BOOST_AUTO_TEST_CASE(test_get_hash_consistency)
585 : {
586 1 : LazyDummyBLS lazy1;
587 1 : LazyDummyBLS lazy2;
588 1 : DummyBLS obj;
589 1 : obj.data = {13, 14, 15, 16};
590 1 : lazy1.Set(obj, false);
591 1 : lazy2.Set(obj, false);
592 1 : uint256 hash1 = lazy1.GetHash();
593 1 : uint256 hash2 = lazy2.GetHash();
594 1 : BOOST_CHECK(hash1 == hash2);
595 1 : }
596 :
597 146 : BOOST_AUTO_TEST_SUITE_END()
|