LCOV - code coverage report
Current view: top level - src/test - bls_tests.cpp (source / functions) Hit Total Coverage
Test: test_dash_coverage.info Lines: 380 380 100.0 %
Date: 2026-06-25 07:23:51 Functions: 78 78 100.0 %

          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()

Generated by: LCOV version 1.16