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