serialize_tests.cpp
1 // Copyright (c) 2012-2022 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 #include <string> 13 14 #include <boost/test/unit_test.hpp> 15 16 BOOST_FIXTURE_TEST_SUITE(serialize_tests, BasicTestingSetup) 17 18 class CSerializeMethodsTestSingle 19 { 20 protected: 21 int intval; 22 bool boolval; 23 std::string stringval; 24 char charstrval[16]; 25 CTransactionRef txval; 26 public: 27 CSerializeMethodsTestSingle() = default; 28 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) 29 { 30 memcpy(charstrval, charstrvalin, sizeof(charstrval)); 31 } 32 33 SERIALIZE_METHODS(CSerializeMethodsTestSingle, obj) 34 { 35 READWRITE(obj.intval); 36 READWRITE(obj.boolval); 37 READWRITE(obj.stringval); 38 READWRITE(obj.charstrval); 39 READWRITE(TX_WITH_WITNESS(obj.txval)); 40 } 41 42 bool operator==(const CSerializeMethodsTestSingle& rhs) const 43 { 44 return intval == rhs.intval && 45 boolval == rhs.boolval && 46 stringval == rhs.stringval && 47 strcmp(charstrval, rhs.charstrval) == 0 && 48 *txval == *rhs.txval; 49 } 50 }; 51 52 class CSerializeMethodsTestMany : public CSerializeMethodsTestSingle 53 { 54 public: 55 using CSerializeMethodsTestSingle::CSerializeMethodsTestSingle; 56 57 SERIALIZE_METHODS(CSerializeMethodsTestMany, obj) 58 { 59 READWRITE(obj.intval, obj.boolval, obj.stringval, obj.charstrval, TX_WITH_WITNESS(obj.txval)); 60 } 61 }; 62 63 BOOST_AUTO_TEST_CASE(sizes) 64 { 65 BOOST_CHECK_EQUAL(sizeof(unsigned char), GetSerializeSize((unsigned char)0)); 66 BOOST_CHECK_EQUAL(sizeof(int8_t), GetSerializeSize(int8_t(0))); 67 BOOST_CHECK_EQUAL(sizeof(uint8_t), GetSerializeSize(uint8_t(0))); 68 BOOST_CHECK_EQUAL(sizeof(int16_t), GetSerializeSize(int16_t(0))); 69 BOOST_CHECK_EQUAL(sizeof(uint16_t), GetSerializeSize(uint16_t(0))); 70 BOOST_CHECK_EQUAL(sizeof(int32_t), GetSerializeSize(int32_t(0))); 71 BOOST_CHECK_EQUAL(sizeof(uint32_t), GetSerializeSize(uint32_t(0))); 72 BOOST_CHECK_EQUAL(sizeof(int64_t), GetSerializeSize(int64_t(0))); 73 BOOST_CHECK_EQUAL(sizeof(uint64_t), GetSerializeSize(uint64_t(0))); 74 // Bool is serialized as uint8_t 75 BOOST_CHECK_EQUAL(sizeof(uint8_t), GetSerializeSize(bool(0))); 76 77 // Sanity-check GetSerializeSize and c++ type matching 78 BOOST_CHECK_EQUAL(GetSerializeSize((unsigned char)0), 1U); 79 BOOST_CHECK_EQUAL(GetSerializeSize(int8_t(0)), 1U); 80 BOOST_CHECK_EQUAL(GetSerializeSize(uint8_t(0)), 1U); 81 BOOST_CHECK_EQUAL(GetSerializeSize(int16_t(0)), 2U); 82 BOOST_CHECK_EQUAL(GetSerializeSize(uint16_t(0)), 2U); 83 BOOST_CHECK_EQUAL(GetSerializeSize(int32_t(0)), 4U); 84 BOOST_CHECK_EQUAL(GetSerializeSize(uint32_t(0)), 4U); 85 BOOST_CHECK_EQUAL(GetSerializeSize(int64_t(0)), 8U); 86 BOOST_CHECK_EQUAL(GetSerializeSize(uint64_t(0)), 8U); 87 BOOST_CHECK_EQUAL(GetSerializeSize(bool(0)), 1U); 88 BOOST_CHECK_EQUAL(GetSerializeSize(std::array<uint8_t, 1>{0}), 1U); 89 BOOST_CHECK_EQUAL(GetSerializeSize(std::array<uint8_t, 2>{0, 0}), 2U); 90 } 91 92 BOOST_AUTO_TEST_CASE(varints) 93 { 94 // encode 95 96 DataStream ss{}; 97 DataStream::size_type size = 0; 98 for (int i = 0; i < 100000; i++) { 99 ss << VARINT_MODE(i, VarIntMode::NONNEGATIVE_SIGNED); 100 size += ::GetSerializeSize(VARINT_MODE(i, VarIntMode::NONNEGATIVE_SIGNED)); 101 BOOST_CHECK(size == ss.size()); 102 } 103 104 for (uint64_t i = 0; i < 100000000000ULL; i += 999999937) { 105 ss << VARINT(i); 106 size += ::GetSerializeSize(VARINT(i)); 107 BOOST_CHECK(size == ss.size()); 108 } 109 110 // decode 111 for (int i = 0; i < 100000; i++) { 112 int j = -1; 113 ss >> VARINT_MODE(j, VarIntMode::NONNEGATIVE_SIGNED); 114 BOOST_CHECK_MESSAGE(i == j, "decoded:" << j << " expected:" << i); 115 } 116 117 for (uint64_t i = 0; i < 100000000000ULL; i += 999999937) { 118 uint64_t j = std::numeric_limits<uint64_t>::max(); 119 ss >> VARINT(j); 120 BOOST_CHECK_MESSAGE(i == j, "decoded:" << j << " expected:" << i); 121 } 122 } 123 124 BOOST_AUTO_TEST_CASE(varints_bitpatterns) 125 { 126 DataStream ss{}; 127 ss << VARINT_MODE(0, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "00"); ss.clear(); 128 ss << VARINT_MODE(0x7f, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "7f"); ss.clear(); 129 ss << VARINT_MODE(int8_t{0x7f}, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "7f"); ss.clear(); 130 ss << VARINT_MODE(0x80, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "8000"); ss.clear(); 131 ss << VARINT(uint8_t{0x80}); BOOST_CHECK_EQUAL(HexStr(ss), "8000"); ss.clear(); 132 ss << VARINT_MODE(0x1234, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "a334"); ss.clear(); 133 ss << VARINT_MODE(int16_t{0x1234}, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "a334"); ss.clear(); 134 ss << VARINT_MODE(0xffff, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "82fe7f"); ss.clear(); 135 ss << VARINT(uint16_t{0xffff}); BOOST_CHECK_EQUAL(HexStr(ss), "82fe7f"); ss.clear(); 136 ss << VARINT_MODE(0x123456, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "c7e756"); ss.clear(); 137 ss << VARINT_MODE(int32_t{0x123456}, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "c7e756"); ss.clear(); 138 ss << VARINT(0x80123456U); BOOST_CHECK_EQUAL(HexStr(ss), "86ffc7e756"); ss.clear(); 139 ss << VARINT(uint32_t{0x80123456U}); BOOST_CHECK_EQUAL(HexStr(ss), "86ffc7e756"); ss.clear(); 140 ss << VARINT(0xffffffff); BOOST_CHECK_EQUAL(HexStr(ss), "8efefefe7f"); ss.clear(); 141 ss << VARINT_MODE(0x7fffffffffffffffLL, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "fefefefefefefefe7f"); ss.clear(); 142 ss << VARINT(0xffffffffffffffffULL); BOOST_CHECK_EQUAL(HexStr(ss), "80fefefefefefefefe7f"); ss.clear(); 143 } 144 145 BOOST_AUTO_TEST_CASE(compactsize) 146 { 147 DataStream ss{}; 148 std::vector<char>::size_type i, j; 149 150 for (i = 1; i <= MAX_SIZE; i *= 2) 151 { 152 WriteCompactSize(ss, i-1); 153 WriteCompactSize(ss, i); 154 } 155 for (i = 1; i <= MAX_SIZE; i *= 2) 156 { 157 j = ReadCompactSize(ss); 158 BOOST_CHECK_MESSAGE((i-1) == j, "decoded:" << j << " expected:" << (i-1)); 159 j = ReadCompactSize(ss); 160 BOOST_CHECK_MESSAGE(i == j, "decoded:" << j << " expected:" << i); 161 } 162 } 163 164 static bool isCanonicalException(const std::ios_base::failure& ex) 165 { 166 std::ios_base::failure expectedException("non-canonical ReadCompactSize()"); 167 168 // The string returned by what() can be different for different platforms. 169 // Instead of directly comparing the ex.what() with an expected string, 170 // create an instance of exception to see if ex.what() matches 171 // the expected explanatory string returned by the exception instance. 172 return strcmp(expectedException.what(), ex.what()) == 0; 173 } 174 175 BOOST_AUTO_TEST_CASE(vector_bool) 176 { 177 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}; 178 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}; 179 180 BOOST_CHECK(vec1 == std::vector<uint8_t>(vec2.begin(), vec2.end())); 181 BOOST_CHECK((HashWriter{} << vec1).GetHash() == (HashWriter{} << vec2).GetHash()); 182 } 183 184 BOOST_AUTO_TEST_CASE(array) 185 { 186 std::array<uint8_t, 32> array1{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}; 187 DataStream ds; 188 ds << array1; 189 std::array<uint8_t, 32> array2; 190 ds >> array2; 191 BOOST_CHECK(array1 == array2); 192 } 193 194 BOOST_AUTO_TEST_CASE(noncanonical) 195 { 196 // Write some non-canonical CompactSize encodings, and 197 // make sure an exception is thrown when read back. 198 DataStream ss{}; 199 std::vector<char>::size_type n; 200 201 // zero encoded with three bytes: 202 ss << Span{"\xfd\x00\x00"}.first(3); 203 BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException); 204 205 // 0xfc encoded with three bytes: 206 ss << Span{"\xfd\xfc\x00"}.first(3); 207 BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException); 208 209 // 0xfd encoded with three bytes is OK: 210 ss << Span{"\xfd\xfd\x00"}.first(3); 211 n = ReadCompactSize(ss); 212 BOOST_CHECK(n == 0xfd); 213 214 // zero encoded with five bytes: 215 ss << Span{"\xfe\x00\x00\x00\x00"}.first(5); 216 BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException); 217 218 // 0xffff encoded with five bytes: 219 ss << Span{"\xfe\xff\xff\x00\x00"}.first(5); 220 BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException); 221 222 // zero encoded with nine bytes: 223 ss << Span{"\xff\x00\x00\x00\x00\x00\x00\x00\x00"}.first(9); 224 BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException); 225 226 // 0x01ffffff encoded with nine bytes: 227 ss << Span{"\xff\xff\xff\xff\x01\x00\x00\x00\x00"}.first(9); 228 BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException); 229 } 230 231 BOOST_AUTO_TEST_CASE(class_methods) 232 { 233 int intval(100); 234 bool boolval(true); 235 std::string stringval("testing"); 236 const uint8_t charstrval[16]{"testing charstr"}; 237 CMutableTransaction txval; 238 CTransactionRef tx_ref{MakeTransactionRef(txval)}; 239 CSerializeMethodsTestSingle methodtest1(intval, boolval, stringval, charstrval, tx_ref); 240 CSerializeMethodsTestMany methodtest2(intval, boolval, stringval, charstrval, tx_ref); 241 CSerializeMethodsTestSingle methodtest3; 242 CSerializeMethodsTestMany methodtest4; 243 DataStream ss; 244 BOOST_CHECK(methodtest1 == methodtest2); 245 ss << methodtest1; 246 ss >> methodtest4; 247 ss << methodtest2; 248 ss >> methodtest3; 249 BOOST_CHECK(methodtest1 == methodtest2); 250 BOOST_CHECK(methodtest2 == methodtest3); 251 BOOST_CHECK(methodtest3 == methodtest4); 252 253 DataStream ss2; 254 ss2 << intval << boolval << stringval << charstrval << TX_WITH_WITNESS(txval); 255 ss2 >> methodtest3; 256 BOOST_CHECK(methodtest3 == methodtest4); 257 { 258 DataStream ds; 259 const std::string in{"ab"}; 260 ds << Span{in} << std::byte{'c'}; 261 std::array<std::byte, 2> out; 262 std::byte out_3; 263 ds >> Span{out} >> out_3; 264 BOOST_CHECK_EQUAL(out.at(0), std::byte{'a'}); 265 BOOST_CHECK_EQUAL(out.at(1), std::byte{'b'}); 266 BOOST_CHECK_EQUAL(out_3, std::byte{'c'}); 267 } 268 } 269 270 struct BaseFormat { 271 const enum { 272 RAW, 273 HEX, 274 } m_base_format; 275 SER_PARAMS_OPFUNC 276 }; 277 constexpr BaseFormat RAW{BaseFormat::RAW}; 278 constexpr BaseFormat HEX{BaseFormat::HEX}; 279 280 /// (Un)serialize a number as raw byte or 2 hexadecimal chars. 281 class Base 282 { 283 public: 284 uint8_t m_base_data; 285 286 Base() : m_base_data(17) {} 287 explicit Base(uint8_t data) : m_base_data(data) {} 288 289 template <typename Stream> 290 void Serialize(Stream& s) const 291 { 292 if (s.GetParams().m_base_format == BaseFormat::RAW) { 293 s << m_base_data; 294 } else { 295 s << Span{HexStr(Span{&m_base_data, 1})}; 296 } 297 } 298 299 template <typename Stream> 300 void Unserialize(Stream& s) 301 { 302 if (s.GetParams().m_base_format == BaseFormat::RAW) { 303 s >> m_base_data; 304 } else { 305 std::string hex{"aa"}; 306 s >> Span{hex}.first(hex.size()); 307 m_base_data = TryParseHex<uint8_t>(hex).value().at(0); 308 } 309 } 310 }; 311 312 class DerivedAndBaseFormat 313 { 314 public: 315 BaseFormat m_base_format; 316 317 enum class DerivedFormat { 318 LOWER, 319 UPPER, 320 } m_derived_format; 321 322 SER_PARAMS_OPFUNC 323 }; 324 325 class Derived : public Base 326 { 327 public: 328 std::string m_derived_data; 329 330 SERIALIZE_METHODS_PARAMS(Derived, obj, DerivedAndBaseFormat, fmt) 331 { 332 READWRITE(fmt.m_base_format(AsBase<Base>(obj))); 333 334 if (ser_action.ForRead()) { 335 std::string str; 336 s >> str; 337 SER_READ(obj, obj.m_derived_data = str); 338 } else { 339 s << (fmt.m_derived_format == DerivedAndBaseFormat::DerivedFormat::LOWER ? 340 ToLower(obj.m_derived_data) : 341 ToUpper(obj.m_derived_data)); 342 } 343 } 344 }; 345 346 BOOST_AUTO_TEST_CASE(with_params_base) 347 { 348 Base b{0x0F}; 349 350 DataStream stream; 351 352 stream << RAW(b); 353 BOOST_CHECK_EQUAL(stream.str(), "\x0F"); 354 355 b.m_base_data = 0; 356 stream >> RAW(b); 357 BOOST_CHECK_EQUAL(b.m_base_data, 0x0F); 358 359 stream.clear(); 360 361 stream << HEX(b); 362 BOOST_CHECK_EQUAL(stream.str(), "0f"); 363 364 b.m_base_data = 0; 365 stream >> HEX(b); 366 BOOST_CHECK_EQUAL(b.m_base_data, 0x0F); 367 } 368 369 BOOST_AUTO_TEST_CASE(with_params_vector_of_base) 370 { 371 std::vector<Base> v{Base{0x0F}, Base{0xFF}}; 372 373 DataStream stream; 374 375 stream << RAW(v); 376 BOOST_CHECK_EQUAL(stream.str(), "\x02\x0F\xFF"); 377 378 v[0].m_base_data = 0; 379 v[1].m_base_data = 0; 380 stream >> RAW(v); 381 BOOST_CHECK_EQUAL(v[0].m_base_data, 0x0F); 382 BOOST_CHECK_EQUAL(v[1].m_base_data, 0xFF); 383 384 stream.clear(); 385 386 stream << HEX(v); 387 BOOST_CHECK_EQUAL(stream.str(), "\x02" 388 "0fff"); 389 390 v[0].m_base_data = 0; 391 v[1].m_base_data = 0; 392 stream >> HEX(v); 393 BOOST_CHECK_EQUAL(v[0].m_base_data, 0x0F); 394 BOOST_CHECK_EQUAL(v[1].m_base_data, 0xFF); 395 } 396 397 constexpr DerivedAndBaseFormat RAW_LOWER{{BaseFormat::RAW}, DerivedAndBaseFormat::DerivedFormat::LOWER}; 398 constexpr DerivedAndBaseFormat HEX_UPPER{{BaseFormat::HEX}, DerivedAndBaseFormat::DerivedFormat::UPPER}; 399 400 BOOST_AUTO_TEST_CASE(with_params_derived) 401 { 402 Derived d; 403 d.m_base_data = 0x0F; 404 d.m_derived_data = "xY"; 405 406 DataStream stream; 407 408 stream << RAW_LOWER(d); 409 410 stream << HEX_UPPER(d); 411 412 BOOST_CHECK_EQUAL(stream.str(), "\x0F\x02xy" 413 "0f\x02XY"); 414 } 415 416 BOOST_AUTO_TEST_SUITE_END()