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

          Line data    Source code
       1             : // Copyright (c) 2012-2021 The Bitcoin 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 <hash.h>
       6             : #include <serialize.h>
       7             : #include <streams.h>
       8             : #include <test/util/setup_common.h>
       9             : #include <util/strencodings.h>
      10             : 
      11             : #include <stdint.h>
      12             : 
      13             : #include <boost/test/unit_test.hpp>
      14             : 
      15         146 : BOOST_FIXTURE_TEST_SUITE(serialize_tests, BasicTestingSetup)
      16             : 
      17             : class CSerializeMethodsTestSingle
      18             : {
      19             : protected:
      20             :     int intval;
      21             :     bool boolval;
      22             :     std::string stringval;
      23             :     char charstrval[16];
      24             :     CTransactionRef txval;
      25             : public:
      26           3 :     CSerializeMethodsTestSingle() = default;
      27           3 :     CSerializeMethodsTestSingle(int intvalin, bool boolvalin, std::string stringvalin, const uint8_t* charstrvalin, const CTransactionRef& txvalin) : intval(intvalin), boolval(boolvalin), stringval(std::move(stringvalin)), txval(txvalin)
      28           1 :     {
      29           2 :         memcpy(charstrval, charstrvalin, sizeof(charstrval));
      30           3 :     }
      31             : 
      32           9 :     SERIALIZE_METHODS(CSerializeMethodsTestSingle, obj)
      33             :     {
      34           3 :         READWRITE(obj.intval);
      35           3 :         READWRITE(obj.boolval);
      36           3 :         READWRITE(obj.stringval);
      37           3 :         READWRITE(obj.charstrval);
      38           3 :         READWRITE(obj.txval);
      39           3 :     }
      40             : 
      41           5 :     bool operator==(const CSerializeMethodsTestSingle& rhs) const
      42             :     {
      43          10 :         return intval == rhs.intval &&
      44           5 :                boolval == rhs.boolval &&
      45           5 :                stringval == rhs.stringval &&
      46           5 :                strcmp(charstrval, rhs.charstrval) == 0 &&
      47           5 :                *txval == *rhs.txval;
      48             :     }
      49             : };
      50             : 
      51             : class CSerializeMethodsTestMany : public CSerializeMethodsTestSingle
      52             : {
      53             : public:
      54             :     using CSerializeMethodsTestSingle::CSerializeMethodsTestSingle;
      55             : 
      56           6 :     SERIALIZE_METHODS(CSerializeMethodsTestMany, obj)
      57             :     {
      58           2 :         READWRITE(obj.intval, obj.boolval, obj.stringval, obj.charstrval, obj.txval);
      59           2 :     }
      60             : };
      61             : 
      62         149 : BOOST_AUTO_TEST_CASE(sizes)
      63             : {
      64           1 :     BOOST_CHECK_EQUAL(sizeof(unsigned char), GetSerializeSize((unsigned char)0, 0));
      65           1 :     BOOST_CHECK_EQUAL(sizeof(int8_t), GetSerializeSize(int8_t(0), 0));
      66           1 :     BOOST_CHECK_EQUAL(sizeof(uint8_t), GetSerializeSize(uint8_t(0), 0));
      67           1 :     BOOST_CHECK_EQUAL(sizeof(int16_t), GetSerializeSize(int16_t(0), 0));
      68           1 :     BOOST_CHECK_EQUAL(sizeof(uint16_t), GetSerializeSize(uint16_t(0), 0));
      69           1 :     BOOST_CHECK_EQUAL(sizeof(int32_t), GetSerializeSize(int32_t(0), 0));
      70           1 :     BOOST_CHECK_EQUAL(sizeof(uint32_t), GetSerializeSize(uint32_t(0), 0));
      71           1 :     BOOST_CHECK_EQUAL(sizeof(int64_t), GetSerializeSize(int64_t(0), 0));
      72           1 :     BOOST_CHECK_EQUAL(sizeof(uint64_t), GetSerializeSize(uint64_t(0), 0));
      73             :     // Bool is serialized as uint8_t
      74           1 :     BOOST_CHECK_EQUAL(sizeof(uint8_t), GetSerializeSize(bool(0), 0));
      75             : 
      76             :     // Sanity-check GetSerializeSize and c++ type matching
      77           1 :     BOOST_CHECK_EQUAL(GetSerializeSize((unsigned char)0, 0), 1U);
      78           1 :     BOOST_CHECK_EQUAL(GetSerializeSize(int8_t(0), 0), 1U);
      79           1 :     BOOST_CHECK_EQUAL(GetSerializeSize(uint8_t(0), 0), 1U);
      80           1 :     BOOST_CHECK_EQUAL(GetSerializeSize(int16_t(0), 0), 2U);
      81           1 :     BOOST_CHECK_EQUAL(GetSerializeSize(uint16_t(0), 0), 2U);
      82           1 :     BOOST_CHECK_EQUAL(GetSerializeSize(int32_t(0), 0), 4U);
      83           1 :     BOOST_CHECK_EQUAL(GetSerializeSize(uint32_t(0), 0), 4U);
      84           1 :     BOOST_CHECK_EQUAL(GetSerializeSize(int64_t(0), 0), 8U);
      85           1 :     BOOST_CHECK_EQUAL(GetSerializeSize(uint64_t(0), 0), 8U);
      86           1 :     BOOST_CHECK_EQUAL(GetSerializeSize(bool(0), 0), 1U);
      87           1 : }
      88             : 
      89         149 : BOOST_AUTO_TEST_CASE(varints)
      90             : {
      91             :     // encode
      92             : 
      93           1 :     CDataStream ss(SER_DISK, 0);
      94           1 :     CDataStream::size_type size = 0;
      95      100001 :     for (int i = 0; i < 100000; i++) {
      96      100000 :         ss << VARINT_MODE(i, VarIntMode::NONNEGATIVE_SIGNED);
      97      100000 :         size += ::GetSerializeSize(VARINT_MODE(i, VarIntMode::NONNEGATIVE_SIGNED), 0);
      98      100000 :         BOOST_CHECK(size == ss.size());
      99      100000 :     }
     100             : 
     101         102 :     for (uint64_t i = 0;  i < 100000000000ULL; i += 999999937) {
     102         101 :         ss << VARINT(i);
     103         101 :         size += ::GetSerializeSize(VARINT(i), 0);
     104         101 :         BOOST_CHECK(size == ss.size());
     105         101 :     }
     106             : 
     107             :     // decode
     108      100001 :     for (int i = 0; i < 100000; i++) {
     109      100000 :         int j = -1;
     110      100000 :         ss >> VARINT_MODE(j, VarIntMode::NONNEGATIVE_SIGNED);
     111      100000 :         BOOST_CHECK_MESSAGE(i == j, "decoded:" << j << " expected:" << i);
     112      100000 :     }
     113             : 
     114         102 :     for (uint64_t i = 0;  i < 100000000000ULL; i += 999999937) {
     115         101 :         uint64_t j = std::numeric_limits<uint64_t>::max();
     116         101 :         ss >> VARINT(j);
     117         101 :         BOOST_CHECK_MESSAGE(i == j, "decoded:" << j << " expected:" << i);
     118         101 :     }
     119           1 : }
     120             : 
     121         149 : BOOST_AUTO_TEST_CASE(varints_bitpatterns)
     122             : {
     123           1 :     CDataStream ss(SER_DISK, 0);
     124           1 :     ss << VARINT_MODE(0, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "00"); ss.clear();
     125           1 :     ss << VARINT_MODE(0x7f, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "7f"); ss.clear();
     126           1 :     ss << VARINT_MODE(int8_t{0x7f}, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "7f"); ss.clear();
     127           1 :     ss << VARINT_MODE(0x80, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "8000"); ss.clear();
     128           1 :     ss << VARINT(uint8_t{0x80}); BOOST_CHECK_EQUAL(HexStr(ss), "8000"); ss.clear();
     129           1 :     ss << VARINT_MODE(0x1234, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "a334"); ss.clear();
     130           1 :     ss << VARINT_MODE(int16_t{0x1234}, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "a334"); ss.clear();
     131           1 :     ss << VARINT_MODE(0xffff, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "82fe7f"); ss.clear();
     132           1 :     ss << VARINT(uint16_t{0xffff}); BOOST_CHECK_EQUAL(HexStr(ss), "82fe7f"); ss.clear();
     133           1 :     ss << VARINT_MODE(0x123456, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "c7e756"); ss.clear();
     134           1 :     ss << VARINT_MODE(int32_t{0x123456}, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "c7e756"); ss.clear();
     135           1 :     ss << VARINT(0x80123456U); BOOST_CHECK_EQUAL(HexStr(ss), "86ffc7e756"); ss.clear();
     136           1 :     ss << VARINT(uint32_t{0x80123456U}); BOOST_CHECK_EQUAL(HexStr(ss), "86ffc7e756"); ss.clear();
     137           1 :     ss << VARINT(0xffffffff); BOOST_CHECK_EQUAL(HexStr(ss), "8efefefe7f"); ss.clear();
     138           1 :     ss << VARINT_MODE(0x7fffffffffffffffLL, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "fefefefefefefefe7f"); ss.clear();
     139           1 :     ss << VARINT(0xffffffffffffffffULL); BOOST_CHECK_EQUAL(HexStr(ss), "80fefefefefefefefe7f"); ss.clear();
     140           1 : }
     141             : 
     142         149 : BOOST_AUTO_TEST_CASE(compactsize)
     143             : {
     144           1 :     CDataStream ss(SER_DISK, 0);
     145             :     std::vector<char>::size_type i, j;
     146             : 
     147          27 :     for (i = 1; i <= MAX_SIZE; i *= 2)
     148             :     {
     149          26 :         WriteCompactSize(ss, i-1);
     150          26 :         WriteCompactSize(ss, i);
     151          26 :     }
     152          27 :     for (i = 1; i <= MAX_SIZE; i *= 2)
     153             :     {
     154          26 :         j = ReadCompactSize(ss);
     155          26 :         BOOST_CHECK_MESSAGE((i-1) == j, "decoded:" << j << " expected:" << (i-1));
     156          26 :         j = ReadCompactSize(ss);
     157          26 :         BOOST_CHECK_MESSAGE(i == j, "decoded:" << j << " expected:" << i);
     158          26 :     }
     159           1 : }
     160             : 
     161           6 : static bool isCanonicalException(const std::ios_base::failure& ex)
     162             : {
     163           6 :     std::ios_base::failure expectedException("non-canonical ReadCompactSize()");
     164             : 
     165             :     // The string returned by what() can be different for different platforms.
     166             :     // Instead of directly comparing the ex.what() with an expected string,
     167             :     // create an instance of exception to see if ex.what() matches
     168             :     // the expected explanatory string returned by the exception instance.
     169           6 :     return strcmp(expectedException.what(), ex.what()) == 0;
     170           6 : }
     171             : 
     172         149 : BOOST_AUTO_TEST_CASE(vector_bool)
     173             : {
     174           1 :     std::vector<uint8_t> vec1{1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1};
     175           1 :     std::vector<bool> vec2{1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1};
     176             : 
     177           1 :     BOOST_CHECK(vec1 == std::vector<uint8_t>(vec2.begin(), vec2.end()));
     178           1 :     BOOST_CHECK(SerializeHash(vec1) == SerializeHash(vec2));
     179           1 : }
     180             : 
     181         149 : BOOST_AUTO_TEST_CASE(noncanonical)
     182             : {
     183             :     // Write some non-canonical CompactSize encodings, and
     184             :     // make sure an exception is thrown when read back.
     185           1 :     CDataStream ss(SER_DISK, 0);
     186             :     std::vector<char>::size_type n;
     187             : 
     188             :     // zero encoded with three bytes:
     189           1 :     ss.write(MakeByteSpan("\xfd\x00\x00").first(3));
     190           2 :     BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException);
     191             : 
     192             :     // 0xfc encoded with three bytes:
     193           1 :     ss.write(MakeByteSpan("\xfd\xfc\x00").first(3));
     194           2 :     BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException);
     195             : 
     196             :     // 0xfd encoded with three bytes is OK:
     197           1 :     ss.write(MakeByteSpan("\xfd\xfd\x00").first(3));
     198           1 :     n = ReadCompactSize(ss);
     199           1 :     BOOST_CHECK(n == 0xfd);
     200             : 
     201             :     // zero encoded with five bytes:
     202           1 :     ss.write(MakeByteSpan("\xfe\x00\x00\x00\x00").first(5));
     203           2 :     BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException);
     204             : 
     205             :     // 0xffff encoded with five bytes:
     206           1 :     ss.write(MakeByteSpan("\xfe\xff\xff\x00\x00").first(5));
     207           2 :     BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException);
     208             : 
     209             :     // zero encoded with nine bytes:
     210           1 :     ss.write(MakeByteSpan("\xff\x00\x00\x00\x00\x00\x00\x00\x00").first(9));
     211           2 :     BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException);
     212             : 
     213             :     // 0x01ffffff encoded with nine bytes:
     214           1 :     ss.write(MakeByteSpan("\xff\xff\xff\xff\x01\x00\x00\x00\x00").first(9));
     215           2 :     BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException);
     216           7 : }
     217             : 
     218             : // Change struct size and check if it can be deserialized
     219             : // from old version archive and vice versa
     220             : struct old_version
     221             : {
     222             :     int field1;
     223             : 
     224           6 :     SERIALIZE_METHODS(old_version, obj)
     225             :     {
     226           2 :         READWRITE(obj.field1);
     227           2 :     }
     228             : };
     229             : 
     230             : struct new_version
     231             : {
     232             :     int field1;
     233             :     int field2;
     234             : 
     235             :     template<typename Stream>
     236           1 :     void Serialize(Stream &s) const
     237             :     {
     238           1 :         s << field1 << field2;
     239           1 :     }
     240             : 
     241             :     template<typename Stream>
     242           1 :     void Unserialize(Stream &s)
     243             :     {
     244           1 :         s >> field1;
     245           1 :         if (s.size() == 0) {
     246           1 :             field2 = 0;
     247           1 :             return;
     248             :         }
     249           0 :         s >> field2;
     250           1 :     }
     251             : };
     252             : 
     253         149 : BOOST_AUTO_TEST_CASE(check_backward_compatibility)
     254             : {
     255           1 :     CDataStream ss(SER_DISK, 0);
     256           1 :     old_version old_src({5});
     257           1 :     ss << old_src;
     258           1 :     new_version new_dest({6, 7});
     259           1 :     BOOST_REQUIRE_NO_THROW(ss >> new_dest);
     260           1 :     BOOST_REQUIRE(old_src.field1 == new_dest.field1);
     261           1 :     BOOST_REQUIRE(ss.size() == 0);
     262             : 
     263           1 :     new_version new_src({6, 7});
     264           1 :     ss << new_src;
     265           1 :     old_version old_dest({5});
     266           1 :     BOOST_REQUIRE_NO_THROW(ss >> old_dest);
     267           1 :     BOOST_REQUIRE(new_src.field1 == old_dest.field1);
     268           1 : }
     269             : 
     270         149 : BOOST_AUTO_TEST_CASE(class_methods)
     271             : {
     272           1 :     int intval(100);
     273           1 :     bool boolval(true);
     274           1 :     std::string stringval("testing");
     275           1 :     const uint8_t charstrval[16]{"testing charstr"};
     276           1 :     CMutableTransaction txval;
     277           1 :     CTransactionRef tx_ref{MakeTransactionRef(txval)};
     278           1 :     CSerializeMethodsTestSingle methodtest1(intval, boolval, stringval, charstrval, tx_ref);
     279           1 :     CSerializeMethodsTestMany methodtest2(intval, boolval, stringval, charstrval, tx_ref);
     280           1 :     CSerializeMethodsTestSingle methodtest3;
     281           1 :     CSerializeMethodsTestMany methodtest4;
     282           1 :     CDataStream ss(SER_DISK, PROTOCOL_VERSION);
     283           1 :     BOOST_CHECK(methodtest1 == methodtest2);
     284           1 :     ss << methodtest1;
     285           1 :     ss >> methodtest4;
     286           1 :     ss << methodtest2;
     287           1 :     ss >> methodtest3;
     288           1 :     BOOST_CHECK(methodtest1 == methodtest2);
     289           1 :     BOOST_CHECK(methodtest2 == methodtest3);
     290           1 :     BOOST_CHECK(methodtest3 == methodtest4);
     291             : 
     292           1 :     CDataStream ss2{SER_DISK, PROTOCOL_VERSION};
     293           1 :     ss2 << intval << boolval << stringval << charstrval << txval;
     294           1 :     ss2 >> methodtest3;
     295           1 :     BOOST_CHECK(methodtest3 == methodtest4);
     296             :     {
     297           1 :         DataStream ds;
     298           1 :         const std::string in{"ab"};
     299           1 :         ds << Span{in} << std::byte{'c'};
     300             :         std::array<std::byte, 2> out;
     301             :         std::byte out_3;
     302           1 :         ds >> Span{out} >> out_3;
     303           1 :         BOOST_CHECK_EQUAL(out.at(0), std::byte{'a'});
     304           1 :         BOOST_CHECK_EQUAL(out.at(1), std::byte{'b'});
     305           1 :         BOOST_CHECK_EQUAL(out_3, std::byte{'c'});
     306           1 :     }
     307           1 : }
     308             : 
     309         146 : BOOST_AUTO_TEST_SUITE_END()

Generated by: LCOV version 1.16