2022-12-24 23:49:50 +00:00
|
|
|
// Copyright (c) 2012-2022 The Bitcoin Core developers
|
2014-12-13 12:09:33 +08:00
|
|
|
// Distributed under the MIT software license, see the accompanying
|
2014-03-18 10:11:00 +01:00
|
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
|
2020-04-16 13:11:54 -04:00
|
|
|
#include <hash.h>
|
2017-11-10 13:57:53 +13:00
|
|
|
#include <serialize.h>
|
|
|
|
#include <streams.h>
|
2019-11-05 15:18:59 -05:00
|
|
|
#include <test/util/setup_common.h>
|
2019-06-24 17:22:28 +02:00
|
|
|
#include <util/strencodings.h>
|
2012-06-15 14:19:11 +02:00
|
|
|
|
2013-04-13 00:13:08 -05:00
|
|
|
#include <stdint.h>
|
2020-07-16 11:00:43 +02:00
|
|
|
#include <string>
|
2012-06-15 14:19:11 +02:00
|
|
|
|
2013-04-13 00:13:08 -05:00
|
|
|
#include <boost/test/unit_test.hpp>
|
2012-06-15 14:19:11 +02:00
|
|
|
|
2015-03-12 09:34:42 +01:00
|
|
|
BOOST_FIXTURE_TEST_SUITE(serialize_tests, BasicTestingSetup)
|
2012-06-15 14:19:11 +02:00
|
|
|
|
2023-11-22 17:56:04 -05:00
|
|
|
// For testing move-semantics, declare a version of datastream that can be moved
|
|
|
|
// but is not copyable.
|
|
|
|
class UncopyableStream : public DataStream
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
using DataStream::DataStream;
|
|
|
|
UncopyableStream(const UncopyableStream&) = delete;
|
|
|
|
UncopyableStream& operator=(const UncopyableStream&) = delete;
|
|
|
|
UncopyableStream(UncopyableStream&&) noexcept = default;
|
|
|
|
UncopyableStream& operator=(UncopyableStream&&) noexcept = default;
|
|
|
|
};
|
|
|
|
|
2016-09-12 14:38:01 -04:00
|
|
|
class CSerializeMethodsTestSingle
|
|
|
|
{
|
|
|
|
protected:
|
|
|
|
int intval;
|
|
|
|
bool boolval;
|
|
|
|
std::string stringval;
|
2018-03-20 17:37:32 -07:00
|
|
|
char charstrval[16];
|
2016-11-11 16:23:17 -08:00
|
|
|
CTransactionRef txval;
|
2016-09-12 14:38:01 -04:00
|
|
|
public:
|
|
|
|
CSerializeMethodsTestSingle() = default;
|
2021-05-31 14:57:32 +02:00
|
|
|
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)
|
2018-03-20 17:37:32 -07:00
|
|
|
{
|
|
|
|
memcpy(charstrval, charstrvalin, sizeof(charstrval));
|
|
|
|
}
|
|
|
|
|
2020-03-11 09:35:50 -07:00
|
|
|
SERIALIZE_METHODS(CSerializeMethodsTestSingle, obj)
|
|
|
|
{
|
|
|
|
READWRITE(obj.intval);
|
|
|
|
READWRITE(obj.boolval);
|
|
|
|
READWRITE(obj.stringval);
|
|
|
|
READWRITE(obj.charstrval);
|
2023-09-07 19:16:57 +10:00
|
|
|
READWRITE(TX_WITH_WITNESS(obj.txval));
|
2016-09-12 14:38:01 -04:00
|
|
|
}
|
|
|
|
|
2022-02-21 16:45:50 +01:00
|
|
|
bool operator==(const CSerializeMethodsTestSingle& rhs) const
|
2016-09-12 14:38:01 -04:00
|
|
|
{
|
2022-02-21 16:45:50 +01:00
|
|
|
return intval == rhs.intval &&
|
|
|
|
boolval == rhs.boolval &&
|
|
|
|
stringval == rhs.stringval &&
|
|
|
|
strcmp(charstrval, rhs.charstrval) == 0 &&
|
|
|
|
*txval == *rhs.txval;
|
2016-09-12 14:38:01 -04:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
class CSerializeMethodsTestMany : public CSerializeMethodsTestSingle
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
using CSerializeMethodsTestSingle::CSerializeMethodsTestSingle;
|
|
|
|
|
2020-03-11 09:35:50 -07:00
|
|
|
SERIALIZE_METHODS(CSerializeMethodsTestMany, obj)
|
|
|
|
{
|
2023-09-07 19:16:57 +10:00
|
|
|
READWRITE(obj.intval, obj.boolval, obj.stringval, obj.charstrval, TX_WITH_WITNESS(obj.txval));
|
2016-09-12 14:38:01 -04:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2014-12-19 11:41:50 +01:00
|
|
|
BOOST_AUTO_TEST_CASE(sizes)
|
|
|
|
{
|
2023-11-01 22:46:17 +10:00
|
|
|
BOOST_CHECK_EQUAL(sizeof(unsigned char), GetSerializeSize((unsigned char)0));
|
|
|
|
BOOST_CHECK_EQUAL(sizeof(int8_t), GetSerializeSize(int8_t(0)));
|
|
|
|
BOOST_CHECK_EQUAL(sizeof(uint8_t), GetSerializeSize(uint8_t(0)));
|
|
|
|
BOOST_CHECK_EQUAL(sizeof(int16_t), GetSerializeSize(int16_t(0)));
|
|
|
|
BOOST_CHECK_EQUAL(sizeof(uint16_t), GetSerializeSize(uint16_t(0)));
|
|
|
|
BOOST_CHECK_EQUAL(sizeof(int32_t), GetSerializeSize(int32_t(0)));
|
|
|
|
BOOST_CHECK_EQUAL(sizeof(uint32_t), GetSerializeSize(uint32_t(0)));
|
|
|
|
BOOST_CHECK_EQUAL(sizeof(int64_t), GetSerializeSize(int64_t(0)));
|
|
|
|
BOOST_CHECK_EQUAL(sizeof(uint64_t), GetSerializeSize(uint64_t(0)));
|
2021-05-31 14:57:32 +02:00
|
|
|
// Bool is serialized as uint8_t
|
2023-11-01 22:46:17 +10:00
|
|
|
BOOST_CHECK_EQUAL(sizeof(uint8_t), GetSerializeSize(bool(0)));
|
2015-01-14 11:07:13 +01:00
|
|
|
|
|
|
|
// Sanity-check GetSerializeSize and c++ type matching
|
2023-11-01 22:46:17 +10:00
|
|
|
BOOST_CHECK_EQUAL(GetSerializeSize((unsigned char)0), 1U);
|
|
|
|
BOOST_CHECK_EQUAL(GetSerializeSize(int8_t(0)), 1U);
|
|
|
|
BOOST_CHECK_EQUAL(GetSerializeSize(uint8_t(0)), 1U);
|
|
|
|
BOOST_CHECK_EQUAL(GetSerializeSize(int16_t(0)), 2U);
|
|
|
|
BOOST_CHECK_EQUAL(GetSerializeSize(uint16_t(0)), 2U);
|
|
|
|
BOOST_CHECK_EQUAL(GetSerializeSize(int32_t(0)), 4U);
|
|
|
|
BOOST_CHECK_EQUAL(GetSerializeSize(uint32_t(0)), 4U);
|
|
|
|
BOOST_CHECK_EQUAL(GetSerializeSize(int64_t(0)), 8U);
|
|
|
|
BOOST_CHECK_EQUAL(GetSerializeSize(uint64_t(0)), 8U);
|
|
|
|
BOOST_CHECK_EQUAL(GetSerializeSize(bool(0)), 1U);
|
|
|
|
BOOST_CHECK_EQUAL(GetSerializeSize(std::array<uint8_t, 1>{0}), 1U);
|
|
|
|
BOOST_CHECK_EQUAL(GetSerializeSize(std::array<uint8_t, 2>{0, 0}), 2U);
|
2014-12-19 11:41:50 +01:00
|
|
|
}
|
|
|
|
|
2012-06-15 14:19:11 +02:00
|
|
|
BOOST_AUTO_TEST_CASE(varints)
|
|
|
|
{
|
|
|
|
// encode
|
|
|
|
|
2023-01-03 13:21:44 +01:00
|
|
|
DataStream ss{};
|
|
|
|
DataStream::size_type size = 0;
|
2012-06-15 14:19:11 +02:00
|
|
|
for (int i = 0; i < 100000; i++) {
|
2020-02-06 19:57:32 -08:00
|
|
|
ss << VARINT_MODE(i, VarIntMode::NONNEGATIVE_SIGNED);
|
2023-11-01 22:46:17 +10:00
|
|
|
size += ::GetSerializeSize(VARINT_MODE(i, VarIntMode::NONNEGATIVE_SIGNED));
|
2012-06-15 14:19:11 +02:00
|
|
|
BOOST_CHECK(size == ss.size());
|
|
|
|
}
|
|
|
|
|
2013-04-13 00:13:08 -05:00
|
|
|
for (uint64_t i = 0; i < 100000000000ULL; i += 999999937) {
|
2012-06-15 14:19:11 +02:00
|
|
|
ss << VARINT(i);
|
2023-11-01 22:46:17 +10:00
|
|
|
size += ::GetSerializeSize(VARINT(i));
|
2012-06-15 14:19:11 +02:00
|
|
|
BOOST_CHECK(size == ss.size());
|
|
|
|
}
|
|
|
|
|
|
|
|
// decode
|
|
|
|
for (int i = 0; i < 100000; i++) {
|
2013-03-07 12:38:25 -05:00
|
|
|
int j = -1;
|
2020-02-06 19:57:32 -08:00
|
|
|
ss >> VARINT_MODE(j, VarIntMode::NONNEGATIVE_SIGNED);
|
2012-06-15 14:19:11 +02:00
|
|
|
BOOST_CHECK_MESSAGE(i == j, "decoded:" << j << " expected:" << i);
|
|
|
|
}
|
|
|
|
|
2013-04-13 00:13:08 -05:00
|
|
|
for (uint64_t i = 0; i < 100000000000ULL; i += 999999937) {
|
2018-12-04 13:16:30 +01:00
|
|
|
uint64_t j = std::numeric_limits<uint64_t>::max();
|
2012-06-15 14:19:11 +02:00
|
|
|
ss >> VARINT(j);
|
|
|
|
BOOST_CHECK_MESSAGE(i == j, "decoded:" << j << " expected:" << i);
|
|
|
|
}
|
Reject non-canonically-encoded sizes
The length of vectors, maps, sets, etc are serialized using
Write/ReadCompactSize -- which, unfortunately, do not use a
unique encoding.
So deserializing and then re-serializing a transaction (for example)
can give you different bits than you started with. That doesn't
cause any problems that we are aware of, but it is exactly the type
of subtle mismatch that can lead to exploits.
With this pull, reading a non-canonical CompactSize throws an
exception, which means nodes will ignore 'tx' or 'block' or
other messages that are not properly encoded.
Please check my logic... but this change is safe with respect to
causing a network split. Old clients that receive
non-canonically-encoded transactions or blocks deserialize
them into CTransaction/CBlock structures in memory, and then
re-serialize them before relaying them to peers.
And please check my logic with respect to causing a blockchain
split: there are no CompactSize fields in the block header, so
the block hash is always canonical. The merkle root in the block
header is computed on a vector<CTransaction>, so
any non-canonical encoding of the transactions in 'tx' or 'block'
messages is erased as they are read into memory by old clients,
and does not affect the block hash. And, as noted above, old
clients re-serialize (with canonical encoding) 'tx' and 'block'
messages before relaying to peers.
2013-08-07 12:21:34 +10:00
|
|
|
}
|
|
|
|
|
2016-04-09 06:52:43 +02:00
|
|
|
BOOST_AUTO_TEST_CASE(varints_bitpatterns)
|
|
|
|
{
|
2023-01-03 13:21:44 +01:00
|
|
|
DataStream ss{};
|
2020-02-06 19:57:32 -08:00
|
|
|
ss << VARINT_MODE(0, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "00"); ss.clear();
|
|
|
|
ss << VARINT_MODE(0x7f, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "7f"); ss.clear();
|
2021-12-18 12:50:58 -05:00
|
|
|
ss << VARINT_MODE(int8_t{0x7f}, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "7f"); ss.clear();
|
2020-02-06 19:57:32 -08:00
|
|
|
ss << VARINT_MODE(0x80, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "8000"); ss.clear();
|
2021-12-18 12:50:58 -05:00
|
|
|
ss << VARINT(uint8_t{0x80}); BOOST_CHECK_EQUAL(HexStr(ss), "8000"); ss.clear();
|
2020-02-06 19:57:32 -08:00
|
|
|
ss << VARINT_MODE(0x1234, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "a334"); ss.clear();
|
2021-12-18 12:50:58 -05:00
|
|
|
ss << VARINT_MODE(int16_t{0x1234}, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "a334"); ss.clear();
|
2020-02-06 19:57:32 -08:00
|
|
|
ss << VARINT_MODE(0xffff, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "82fe7f"); ss.clear();
|
2021-12-18 12:50:58 -05:00
|
|
|
ss << VARINT(uint16_t{0xffff}); BOOST_CHECK_EQUAL(HexStr(ss), "82fe7f"); ss.clear();
|
2020-02-06 19:57:32 -08:00
|
|
|
ss << VARINT_MODE(0x123456, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "c7e756"); ss.clear();
|
2021-12-18 12:50:58 -05:00
|
|
|
ss << VARINT_MODE(int32_t{0x123456}, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "c7e756"); ss.clear();
|
2016-04-09 06:52:43 +02:00
|
|
|
ss << VARINT(0x80123456U); BOOST_CHECK_EQUAL(HexStr(ss), "86ffc7e756"); ss.clear();
|
2021-12-18 12:50:58 -05:00
|
|
|
ss << VARINT(uint32_t{0x80123456U}); BOOST_CHECK_EQUAL(HexStr(ss), "86ffc7e756"); ss.clear();
|
2016-04-09 06:52:43 +02:00
|
|
|
ss << VARINT(0xffffffff); BOOST_CHECK_EQUAL(HexStr(ss), "8efefefe7f"); ss.clear();
|
2020-02-06 19:57:32 -08:00
|
|
|
ss << VARINT_MODE(0x7fffffffffffffffLL, VarIntMode::NONNEGATIVE_SIGNED); BOOST_CHECK_EQUAL(HexStr(ss), "fefefefefefefefe7f"); ss.clear();
|
2016-04-09 06:52:43 +02:00
|
|
|
ss << VARINT(0xffffffffffffffffULL); BOOST_CHECK_EQUAL(HexStr(ss), "80fefefefefefefefe7f"); ss.clear();
|
|
|
|
}
|
|
|
|
|
Reject non-canonically-encoded sizes
The length of vectors, maps, sets, etc are serialized using
Write/ReadCompactSize -- which, unfortunately, do not use a
unique encoding.
So deserializing and then re-serializing a transaction (for example)
can give you different bits than you started with. That doesn't
cause any problems that we are aware of, but it is exactly the type
of subtle mismatch that can lead to exploits.
With this pull, reading a non-canonical CompactSize throws an
exception, which means nodes will ignore 'tx' or 'block' or
other messages that are not properly encoded.
Please check my logic... but this change is safe with respect to
causing a network split. Old clients that receive
non-canonically-encoded transactions or blocks deserialize
them into CTransaction/CBlock structures in memory, and then
re-serialize them before relaying them to peers.
And please check my logic with respect to causing a blockchain
split: there are no CompactSize fields in the block header, so
the block hash is always canonical. The merkle root in the block
header is computed on a vector<CTransaction>, so
any non-canonical encoding of the transactions in 'tx' or 'block'
messages is erased as they are read into memory by old clients,
and does not affect the block hash. And, as noted above, old
clients re-serialize (with canonical encoding) 'tx' and 'block'
messages before relaying to peers.
2013-08-07 12:21:34 +10:00
|
|
|
BOOST_AUTO_TEST_CASE(compactsize)
|
|
|
|
{
|
2023-01-03 13:21:44 +01:00
|
|
|
DataStream ss{};
|
2016-12-05 16:03:53 +09:00
|
|
|
std::vector<char>::size_type i, j;
|
Reject non-canonically-encoded sizes
The length of vectors, maps, sets, etc are serialized using
Write/ReadCompactSize -- which, unfortunately, do not use a
unique encoding.
So deserializing and then re-serializing a transaction (for example)
can give you different bits than you started with. That doesn't
cause any problems that we are aware of, but it is exactly the type
of subtle mismatch that can lead to exploits.
With this pull, reading a non-canonical CompactSize throws an
exception, which means nodes will ignore 'tx' or 'block' or
other messages that are not properly encoded.
Please check my logic... but this change is safe with respect to
causing a network split. Old clients that receive
non-canonically-encoded transactions or blocks deserialize
them into CTransaction/CBlock structures in memory, and then
re-serialize them before relaying them to peers.
And please check my logic with respect to causing a blockchain
split: there are no CompactSize fields in the block header, so
the block hash is always canonical. The merkle root in the block
header is computed on a vector<CTransaction>, so
any non-canonical encoding of the transactions in 'tx' or 'block'
messages is erased as they are read into memory by old clients,
and does not affect the block hash. And, as noted above, old
clients re-serialize (with canonical encoding) 'tx' and 'block'
messages before relaying to peers.
2013-08-07 12:21:34 +10:00
|
|
|
|
|
|
|
for (i = 1; i <= MAX_SIZE; i *= 2)
|
|
|
|
{
|
|
|
|
WriteCompactSize(ss, i-1);
|
|
|
|
WriteCompactSize(ss, i);
|
|
|
|
}
|
|
|
|
for (i = 1; i <= MAX_SIZE; i *= 2)
|
|
|
|
{
|
|
|
|
j = ReadCompactSize(ss);
|
|
|
|
BOOST_CHECK_MESSAGE((i-1) == j, "decoded:" << j << " expected:" << (i-1));
|
|
|
|
j = ReadCompactSize(ss);
|
|
|
|
BOOST_CHECK_MESSAGE(i == j, "decoded:" << j << " expected:" << i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool isCanonicalException(const std::ios_base::failure& ex)
|
|
|
|
{
|
2013-12-16 09:26:04 +09:00
|
|
|
std::ios_base::failure expectedException("non-canonical ReadCompactSize()");
|
2013-12-15 23:25:41 +09:00
|
|
|
|
2013-12-16 09:26:04 +09:00
|
|
|
// The string returned by what() can be different for different platforms.
|
|
|
|
// Instead of directly comparing the ex.what() with an expected string,
|
2018-07-24 16:59:49 +01:00
|
|
|
// create an instance of exception to see if ex.what() matches
|
|
|
|
// the expected explanatory string returned by the exception instance.
|
2013-12-16 09:26:04 +09:00
|
|
|
return strcmp(expectedException.what(), ex.what()) == 0;
|
Reject non-canonically-encoded sizes
The length of vectors, maps, sets, etc are serialized using
Write/ReadCompactSize -- which, unfortunately, do not use a
unique encoding.
So deserializing and then re-serializing a transaction (for example)
can give you different bits than you started with. That doesn't
cause any problems that we are aware of, but it is exactly the type
of subtle mismatch that can lead to exploits.
With this pull, reading a non-canonical CompactSize throws an
exception, which means nodes will ignore 'tx' or 'block' or
other messages that are not properly encoded.
Please check my logic... but this change is safe with respect to
causing a network split. Old clients that receive
non-canonically-encoded transactions or blocks deserialize
them into CTransaction/CBlock structures in memory, and then
re-serialize them before relaying them to peers.
And please check my logic with respect to causing a blockchain
split: there are no CompactSize fields in the block header, so
the block hash is always canonical. The merkle root in the block
header is computed on a vector<CTransaction>, so
any non-canonical encoding of the transactions in 'tx' or 'block'
messages is erased as they are read into memory by old clients,
and does not affect the block hash. And, as noted above, old
clients re-serialize (with canonical encoding) 'tx' and 'block'
messages before relaying to peers.
2013-08-07 12:21:34 +10:00
|
|
|
}
|
|
|
|
|
2019-08-26 11:32:47 -07:00
|
|
|
BOOST_AUTO_TEST_CASE(vector_bool)
|
|
|
|
{
|
|
|
|
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};
|
|
|
|
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};
|
|
|
|
|
|
|
|
BOOST_CHECK(vec1 == std::vector<uint8_t>(vec2.begin(), vec2.end()));
|
2023-09-11 15:47:52 +02:00
|
|
|
BOOST_CHECK((HashWriter{} << vec1).GetHash() == (HashWriter{} << vec2).GetHash());
|
2019-08-26 11:32:47 -07:00
|
|
|
}
|
2013-12-15 23:25:41 +09:00
|
|
|
|
2023-11-11 13:21:01 +01:00
|
|
|
BOOST_AUTO_TEST_CASE(array)
|
|
|
|
{
|
|
|
|
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};
|
|
|
|
DataStream ds;
|
|
|
|
ds << array1;
|
|
|
|
std::array<uint8_t, 32> array2;
|
|
|
|
ds >> array2;
|
|
|
|
BOOST_CHECK(array1 == array2);
|
|
|
|
}
|
|
|
|
|
Reject non-canonically-encoded sizes
The length of vectors, maps, sets, etc are serialized using
Write/ReadCompactSize -- which, unfortunately, do not use a
unique encoding.
So deserializing and then re-serializing a transaction (for example)
can give you different bits than you started with. That doesn't
cause any problems that we are aware of, but it is exactly the type
of subtle mismatch that can lead to exploits.
With this pull, reading a non-canonical CompactSize throws an
exception, which means nodes will ignore 'tx' or 'block' or
other messages that are not properly encoded.
Please check my logic... but this change is safe with respect to
causing a network split. Old clients that receive
non-canonically-encoded transactions or blocks deserialize
them into CTransaction/CBlock structures in memory, and then
re-serialize them before relaying them to peers.
And please check my logic with respect to causing a blockchain
split: there are no CompactSize fields in the block header, so
the block hash is always canonical. The merkle root in the block
header is computed on a vector<CTransaction>, so
any non-canonical encoding of the transactions in 'tx' or 'block'
messages is erased as they are read into memory by old clients,
and does not affect the block hash. And, as noted above, old
clients re-serialize (with canonical encoding) 'tx' and 'block'
messages before relaying to peers.
2013-08-07 12:21:34 +10:00
|
|
|
BOOST_AUTO_TEST_CASE(noncanonical)
|
|
|
|
{
|
|
|
|
// Write some non-canonical CompactSize encodings, and
|
|
|
|
// make sure an exception is thrown when read back.
|
2023-01-03 13:21:44 +01:00
|
|
|
DataStream ss{};
|
2016-12-05 16:03:53 +09:00
|
|
|
std::vector<char>::size_type n;
|
Reject non-canonically-encoded sizes
The length of vectors, maps, sets, etc are serialized using
Write/ReadCompactSize -- which, unfortunately, do not use a
unique encoding.
So deserializing and then re-serializing a transaction (for example)
can give you different bits than you started with. That doesn't
cause any problems that we are aware of, but it is exactly the type
of subtle mismatch that can lead to exploits.
With this pull, reading a non-canonical CompactSize throws an
exception, which means nodes will ignore 'tx' or 'block' or
other messages that are not properly encoded.
Please check my logic... but this change is safe with respect to
causing a network split. Old clients that receive
non-canonically-encoded transactions or blocks deserialize
them into CTransaction/CBlock structures in memory, and then
re-serialize them before relaying them to peers.
And please check my logic with respect to causing a blockchain
split: there are no CompactSize fields in the block header, so
the block hash is always canonical. The merkle root in the block
header is computed on a vector<CTransaction>, so
any non-canonical encoding of the transactions in 'tx' or 'block'
messages is erased as they are read into memory by old clients,
and does not affect the block hash. And, as noted above, old
clients re-serialize (with canonical encoding) 'tx' and 'block'
messages before relaying to peers.
2013-08-07 12:21:34 +10:00
|
|
|
|
|
|
|
// zero encoded with three bytes:
|
2023-06-22 17:02:28 +02:00
|
|
|
ss << Span{"\xfd\x00\x00"}.first(3);
|
Reject non-canonically-encoded sizes
The length of vectors, maps, sets, etc are serialized using
Write/ReadCompactSize -- which, unfortunately, do not use a
unique encoding.
So deserializing and then re-serializing a transaction (for example)
can give you different bits than you started with. That doesn't
cause any problems that we are aware of, but it is exactly the type
of subtle mismatch that can lead to exploits.
With this pull, reading a non-canonical CompactSize throws an
exception, which means nodes will ignore 'tx' or 'block' or
other messages that are not properly encoded.
Please check my logic... but this change is safe with respect to
causing a network split. Old clients that receive
non-canonically-encoded transactions or blocks deserialize
them into CTransaction/CBlock structures in memory, and then
re-serialize them before relaying them to peers.
And please check my logic with respect to causing a blockchain
split: there are no CompactSize fields in the block header, so
the block hash is always canonical. The merkle root in the block
header is computed on a vector<CTransaction>, so
any non-canonical encoding of the transactions in 'tx' or 'block'
messages is erased as they are read into memory by old clients,
and does not affect the block hash. And, as noted above, old
clients re-serialize (with canonical encoding) 'tx' and 'block'
messages before relaying to peers.
2013-08-07 12:21:34 +10:00
|
|
|
BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException);
|
|
|
|
|
|
|
|
// 0xfc encoded with three bytes:
|
2023-06-22 17:02:28 +02:00
|
|
|
ss << Span{"\xfd\xfc\x00"}.first(3);
|
Reject non-canonically-encoded sizes
The length of vectors, maps, sets, etc are serialized using
Write/ReadCompactSize -- which, unfortunately, do not use a
unique encoding.
So deserializing and then re-serializing a transaction (for example)
can give you different bits than you started with. That doesn't
cause any problems that we are aware of, but it is exactly the type
of subtle mismatch that can lead to exploits.
With this pull, reading a non-canonical CompactSize throws an
exception, which means nodes will ignore 'tx' or 'block' or
other messages that are not properly encoded.
Please check my logic... but this change is safe with respect to
causing a network split. Old clients that receive
non-canonically-encoded transactions or blocks deserialize
them into CTransaction/CBlock structures in memory, and then
re-serialize them before relaying them to peers.
And please check my logic with respect to causing a blockchain
split: there are no CompactSize fields in the block header, so
the block hash is always canonical. The merkle root in the block
header is computed on a vector<CTransaction>, so
any non-canonical encoding of the transactions in 'tx' or 'block'
messages is erased as they are read into memory by old clients,
and does not affect the block hash. And, as noted above, old
clients re-serialize (with canonical encoding) 'tx' and 'block'
messages before relaying to peers.
2013-08-07 12:21:34 +10:00
|
|
|
BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException);
|
|
|
|
|
|
|
|
// 0xfd encoded with three bytes is OK:
|
2023-06-22 17:02:28 +02:00
|
|
|
ss << Span{"\xfd\xfd\x00"}.first(3);
|
Reject non-canonically-encoded sizes
The length of vectors, maps, sets, etc are serialized using
Write/ReadCompactSize -- which, unfortunately, do not use a
unique encoding.
So deserializing and then re-serializing a transaction (for example)
can give you different bits than you started with. That doesn't
cause any problems that we are aware of, but it is exactly the type
of subtle mismatch that can lead to exploits.
With this pull, reading a non-canonical CompactSize throws an
exception, which means nodes will ignore 'tx' or 'block' or
other messages that are not properly encoded.
Please check my logic... but this change is safe with respect to
causing a network split. Old clients that receive
non-canonically-encoded transactions or blocks deserialize
them into CTransaction/CBlock structures in memory, and then
re-serialize them before relaying them to peers.
And please check my logic with respect to causing a blockchain
split: there are no CompactSize fields in the block header, so
the block hash is always canonical. The merkle root in the block
header is computed on a vector<CTransaction>, so
any non-canonical encoding of the transactions in 'tx' or 'block'
messages is erased as they are read into memory by old clients,
and does not affect the block hash. And, as noted above, old
clients re-serialize (with canonical encoding) 'tx' and 'block'
messages before relaying to peers.
2013-08-07 12:21:34 +10:00
|
|
|
n = ReadCompactSize(ss);
|
|
|
|
BOOST_CHECK(n == 0xfd);
|
|
|
|
|
|
|
|
// zero encoded with five bytes:
|
2023-06-22 17:02:28 +02:00
|
|
|
ss << Span{"\xfe\x00\x00\x00\x00"}.first(5);
|
Reject non-canonically-encoded sizes
The length of vectors, maps, sets, etc are serialized using
Write/ReadCompactSize -- which, unfortunately, do not use a
unique encoding.
So deserializing and then re-serializing a transaction (for example)
can give you different bits than you started with. That doesn't
cause any problems that we are aware of, but it is exactly the type
of subtle mismatch that can lead to exploits.
With this pull, reading a non-canonical CompactSize throws an
exception, which means nodes will ignore 'tx' or 'block' or
other messages that are not properly encoded.
Please check my logic... but this change is safe with respect to
causing a network split. Old clients that receive
non-canonically-encoded transactions or blocks deserialize
them into CTransaction/CBlock structures in memory, and then
re-serialize them before relaying them to peers.
And please check my logic with respect to causing a blockchain
split: there are no CompactSize fields in the block header, so
the block hash is always canonical. The merkle root in the block
header is computed on a vector<CTransaction>, so
any non-canonical encoding of the transactions in 'tx' or 'block'
messages is erased as they are read into memory by old clients,
and does not affect the block hash. And, as noted above, old
clients re-serialize (with canonical encoding) 'tx' and 'block'
messages before relaying to peers.
2013-08-07 12:21:34 +10:00
|
|
|
BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException);
|
|
|
|
|
|
|
|
// 0xffff encoded with five bytes:
|
2023-06-22 17:02:28 +02:00
|
|
|
ss << Span{"\xfe\xff\xff\x00\x00"}.first(5);
|
Reject non-canonically-encoded sizes
The length of vectors, maps, sets, etc are serialized using
Write/ReadCompactSize -- which, unfortunately, do not use a
unique encoding.
So deserializing and then re-serializing a transaction (for example)
can give you different bits than you started with. That doesn't
cause any problems that we are aware of, but it is exactly the type
of subtle mismatch that can lead to exploits.
With this pull, reading a non-canonical CompactSize throws an
exception, which means nodes will ignore 'tx' or 'block' or
other messages that are not properly encoded.
Please check my logic... but this change is safe with respect to
causing a network split. Old clients that receive
non-canonically-encoded transactions or blocks deserialize
them into CTransaction/CBlock structures in memory, and then
re-serialize them before relaying them to peers.
And please check my logic with respect to causing a blockchain
split: there are no CompactSize fields in the block header, so
the block hash is always canonical. The merkle root in the block
header is computed on a vector<CTransaction>, so
any non-canonical encoding of the transactions in 'tx' or 'block'
messages is erased as they are read into memory by old clients,
and does not affect the block hash. And, as noted above, old
clients re-serialize (with canonical encoding) 'tx' and 'block'
messages before relaying to peers.
2013-08-07 12:21:34 +10:00
|
|
|
BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException);
|
|
|
|
|
|
|
|
// zero encoded with nine bytes:
|
2023-06-22 17:02:28 +02:00
|
|
|
ss << Span{"\xff\x00\x00\x00\x00\x00\x00\x00\x00"}.first(9);
|
Reject non-canonically-encoded sizes
The length of vectors, maps, sets, etc are serialized using
Write/ReadCompactSize -- which, unfortunately, do not use a
unique encoding.
So deserializing and then re-serializing a transaction (for example)
can give you different bits than you started with. That doesn't
cause any problems that we are aware of, but it is exactly the type
of subtle mismatch that can lead to exploits.
With this pull, reading a non-canonical CompactSize throws an
exception, which means nodes will ignore 'tx' or 'block' or
other messages that are not properly encoded.
Please check my logic... but this change is safe with respect to
causing a network split. Old clients that receive
non-canonically-encoded transactions or blocks deserialize
them into CTransaction/CBlock structures in memory, and then
re-serialize them before relaying them to peers.
And please check my logic with respect to causing a blockchain
split: there are no CompactSize fields in the block header, so
the block hash is always canonical. The merkle root in the block
header is computed on a vector<CTransaction>, so
any non-canonical encoding of the transactions in 'tx' or 'block'
messages is erased as they are read into memory by old clients,
and does not affect the block hash. And, as noted above, old
clients re-serialize (with canonical encoding) 'tx' and 'block'
messages before relaying to peers.
2013-08-07 12:21:34 +10:00
|
|
|
BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException);
|
2012-06-15 14:19:11 +02:00
|
|
|
|
Reject non-canonically-encoded sizes
The length of vectors, maps, sets, etc are serialized using
Write/ReadCompactSize -- which, unfortunately, do not use a
unique encoding.
So deserializing and then re-serializing a transaction (for example)
can give you different bits than you started with. That doesn't
cause any problems that we are aware of, but it is exactly the type
of subtle mismatch that can lead to exploits.
With this pull, reading a non-canonical CompactSize throws an
exception, which means nodes will ignore 'tx' or 'block' or
other messages that are not properly encoded.
Please check my logic... but this change is safe with respect to
causing a network split. Old clients that receive
non-canonically-encoded transactions or blocks deserialize
them into CTransaction/CBlock structures in memory, and then
re-serialize them before relaying them to peers.
And please check my logic with respect to causing a blockchain
split: there are no CompactSize fields in the block header, so
the block hash is always canonical. The merkle root in the block
header is computed on a vector<CTransaction>, so
any non-canonical encoding of the transactions in 'tx' or 'block'
messages is erased as they are read into memory by old clients,
and does not affect the block hash. And, as noted above, old
clients re-serialize (with canonical encoding) 'tx' and 'block'
messages before relaying to peers.
2013-08-07 12:21:34 +10:00
|
|
|
// 0x01ffffff encoded with nine bytes:
|
2023-06-22 17:02:28 +02:00
|
|
|
ss << Span{"\xff\xff\xff\xff\x01\x00\x00\x00\x00"}.first(9);
|
Reject non-canonically-encoded sizes
The length of vectors, maps, sets, etc are serialized using
Write/ReadCompactSize -- which, unfortunately, do not use a
unique encoding.
So deserializing and then re-serializing a transaction (for example)
can give you different bits than you started with. That doesn't
cause any problems that we are aware of, but it is exactly the type
of subtle mismatch that can lead to exploits.
With this pull, reading a non-canonical CompactSize throws an
exception, which means nodes will ignore 'tx' or 'block' or
other messages that are not properly encoded.
Please check my logic... but this change is safe with respect to
causing a network split. Old clients that receive
non-canonically-encoded transactions or blocks deserialize
them into CTransaction/CBlock structures in memory, and then
re-serialize them before relaying them to peers.
And please check my logic with respect to causing a blockchain
split: there are no CompactSize fields in the block header, so
the block hash is always canonical. The merkle root in the block
header is computed on a vector<CTransaction>, so
any non-canonical encoding of the transactions in 'tx' or 'block'
messages is erased as they are read into memory by old clients,
and does not affect the block hash. And, as noted above, old
clients re-serialize (with canonical encoding) 'tx' and 'block'
messages before relaying to peers.
2013-08-07 12:21:34 +10:00
|
|
|
BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException);
|
2012-06-15 14:19:11 +02:00
|
|
|
}
|
|
|
|
|
2016-09-12 14:38:01 -04:00
|
|
|
BOOST_AUTO_TEST_CASE(class_methods)
|
|
|
|
{
|
|
|
|
int intval(100);
|
|
|
|
bool boolval(true);
|
|
|
|
std::string stringval("testing");
|
2021-05-31 14:57:32 +02:00
|
|
|
const uint8_t charstrval[16]{"testing charstr"};
|
2016-09-12 14:38:01 -04:00
|
|
|
CMutableTransaction txval;
|
2018-04-11 13:51:28 -04:00
|
|
|
CTransactionRef tx_ref{MakeTransactionRef(txval)};
|
|
|
|
CSerializeMethodsTestSingle methodtest1(intval, boolval, stringval, charstrval, tx_ref);
|
|
|
|
CSerializeMethodsTestMany methodtest2(intval, boolval, stringval, charstrval, tx_ref);
|
2016-09-12 14:38:01 -04:00
|
|
|
CSerializeMethodsTestSingle methodtest3;
|
|
|
|
CSerializeMethodsTestMany methodtest4;
|
2023-09-07 19:16:57 +10:00
|
|
|
DataStream ss;
|
2016-09-12 14:38:01 -04:00
|
|
|
BOOST_CHECK(methodtest1 == methodtest2);
|
|
|
|
ss << methodtest1;
|
|
|
|
ss >> methodtest4;
|
|
|
|
ss << methodtest2;
|
|
|
|
ss >> methodtest3;
|
|
|
|
BOOST_CHECK(methodtest1 == methodtest2);
|
|
|
|
BOOST_CHECK(methodtest2 == methodtest3);
|
|
|
|
BOOST_CHECK(methodtest3 == methodtest4);
|
|
|
|
|
2023-09-07 19:16:57 +10:00
|
|
|
DataStream ss2;
|
|
|
|
ss2 << intval << boolval << stringval << charstrval << TX_WITH_WITNESS(txval);
|
2016-09-12 14:38:01 -04:00
|
|
|
ss2 >> methodtest3;
|
|
|
|
BOOST_CHECK(methodtest3 == methodtest4);
|
2023-06-21 10:13:08 +02:00
|
|
|
{
|
|
|
|
DataStream ds;
|
|
|
|
const std::string in{"ab"};
|
2023-06-30 12:00:20 +02:00
|
|
|
ds << Span{in} << std::byte{'c'};
|
2023-06-21 10:13:08 +02:00
|
|
|
std::array<std::byte, 2> out;
|
2023-06-30 12:00:20 +02:00
|
|
|
std::byte out_3;
|
|
|
|
ds >> Span{out} >> out_3;
|
2023-06-21 10:13:08 +02:00
|
|
|
BOOST_CHECK_EQUAL(out.at(0), std::byte{'a'});
|
|
|
|
BOOST_CHECK_EQUAL(out.at(1), std::byte{'b'});
|
2023-06-30 12:00:20 +02:00
|
|
|
BOOST_CHECK_EQUAL(out_3, std::byte{'c'});
|
2023-06-21 10:13:08 +02:00
|
|
|
}
|
2016-09-12 14:38:01 -04:00
|
|
|
}
|
|
|
|
|
2023-09-18 13:28:07 +02:00
|
|
|
struct BaseFormat {
|
|
|
|
const enum {
|
|
|
|
RAW,
|
|
|
|
HEX,
|
|
|
|
} m_base_format;
|
|
|
|
SER_PARAMS_OPFUNC
|
2020-07-16 11:00:43 +02:00
|
|
|
};
|
2023-09-18 13:28:07 +02:00
|
|
|
constexpr BaseFormat RAW{BaseFormat::RAW};
|
|
|
|
constexpr BaseFormat HEX{BaseFormat::HEX};
|
2020-07-16 11:00:43 +02:00
|
|
|
|
|
|
|
/// (Un)serialize a number as raw byte or 2 hexadecimal chars.
|
|
|
|
class Base
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
uint8_t m_base_data;
|
|
|
|
|
|
|
|
Base() : m_base_data(17) {}
|
|
|
|
explicit Base(uint8_t data) : m_base_data(data) {}
|
|
|
|
|
|
|
|
template <typename Stream>
|
|
|
|
void Serialize(Stream& s) const
|
|
|
|
{
|
2023-11-22 14:47:00 -05:00
|
|
|
if (s.template GetParams<BaseFormat>().m_base_format == BaseFormat::RAW) {
|
2020-07-16 11:00:43 +02:00
|
|
|
s << m_base_data;
|
|
|
|
} else {
|
|
|
|
s << Span{HexStr(Span{&m_base_data, 1})};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename Stream>
|
|
|
|
void Unserialize(Stream& s)
|
|
|
|
{
|
2023-11-22 14:47:00 -05:00
|
|
|
if (s.template GetParams<BaseFormat>().m_base_format == BaseFormat::RAW) {
|
2020-07-16 11:00:43 +02:00
|
|
|
s >> m_base_data;
|
|
|
|
} else {
|
|
|
|
std::string hex{"aa"};
|
|
|
|
s >> Span{hex}.first(hex.size());
|
|
|
|
m_base_data = TryParseHex<uint8_t>(hex).value().at(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
class DerivedAndBaseFormat
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
BaseFormat m_base_format;
|
|
|
|
|
|
|
|
enum class DerivedFormat {
|
|
|
|
LOWER,
|
|
|
|
UPPER,
|
|
|
|
} m_derived_format;
|
2023-09-18 13:28:07 +02:00
|
|
|
|
|
|
|
SER_PARAMS_OPFUNC
|
2020-07-16 11:00:43 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
class Derived : public Base
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
std::string m_derived_data;
|
|
|
|
|
2023-11-22 14:47:00 -05:00
|
|
|
SERIALIZE_METHODS(Derived, obj)
|
2020-07-16 11:00:43 +02:00
|
|
|
{
|
2023-11-22 14:47:00 -05:00
|
|
|
auto& fmt = SER_PARAMS(DerivedAndBaseFormat);
|
2023-09-18 13:28:07 +02:00
|
|
|
READWRITE(fmt.m_base_format(AsBase<Base>(obj)));
|
2020-07-16 11:00:43 +02:00
|
|
|
|
|
|
|
if (ser_action.ForRead()) {
|
|
|
|
std::string str;
|
|
|
|
s >> str;
|
|
|
|
SER_READ(obj, obj.m_derived_data = str);
|
|
|
|
} else {
|
|
|
|
s << (fmt.m_derived_format == DerivedAndBaseFormat::DerivedFormat::LOWER ?
|
|
|
|
ToLower(obj.m_derived_data) :
|
|
|
|
ToUpper(obj.m_derived_data));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2023-11-22 17:56:04 -05:00
|
|
|
struct OtherParam {
|
|
|
|
uint8_t param;
|
|
|
|
SER_PARAMS_OPFUNC
|
|
|
|
};
|
|
|
|
|
|
|
|
//! Checker for value of OtherParam. When being serialized, serializes the
|
|
|
|
//! param to the stream. When being unserialized, verifies the value in the
|
|
|
|
//! stream matches the param.
|
|
|
|
class OtherParamChecker
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
template <typename Stream>
|
|
|
|
void Serialize(Stream& s) const
|
|
|
|
{
|
|
|
|
const uint8_t param = s.template GetParams<OtherParam>().param;
|
|
|
|
s << param;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename Stream>
|
|
|
|
void Unserialize(Stream& s) const
|
|
|
|
{
|
|
|
|
const uint8_t param = s.template GetParams<OtherParam>().param;
|
|
|
|
uint8_t value;
|
|
|
|
s >> value;
|
|
|
|
BOOST_CHECK_EQUAL(value, param);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
//! Test creating a stream with multiple parameters and making sure
|
|
|
|
//! serialization code requiring different parameters can retrieve them. Also
|
|
|
|
//! test that earlier parameters take precedence if the same parameter type is
|
|
|
|
//! specified twice. (Choice of whether earlier or later values take precedence
|
|
|
|
//! or multiple values of the same type are allowed was arbitrary, and just
|
|
|
|
//! decided based on what would require smallest amount of ugly C++ template
|
|
|
|
//! code. Intent of the test is to just ensure there is no unexpected behavior.)
|
|
|
|
BOOST_AUTO_TEST_CASE(with_params_multi)
|
|
|
|
{
|
|
|
|
const OtherParam other_param_used{.param = 0x10};
|
|
|
|
const OtherParam other_param_ignored{.param = 0x11};
|
|
|
|
const OtherParam other_param_override{.param = 0x12};
|
|
|
|
const OtherParamChecker check;
|
|
|
|
DataStream stream;
|
|
|
|
ParamsStream pstream{stream, RAW, other_param_used, other_param_ignored};
|
|
|
|
|
|
|
|
Base base1{0x20};
|
|
|
|
pstream << base1 << check << other_param_override(check);
|
|
|
|
BOOST_CHECK_EQUAL(stream.str(), "\x20\x10\x12");
|
|
|
|
|
|
|
|
Base base2;
|
|
|
|
pstream >> base2 >> check >> other_param_override(check);
|
|
|
|
BOOST_CHECK_EQUAL(base2.m_base_data, 0x20);
|
|
|
|
}
|
|
|
|
|
|
|
|
//! Test creating a ParamsStream that moves from a stream argument.
|
|
|
|
BOOST_AUTO_TEST_CASE(with_params_move)
|
|
|
|
{
|
|
|
|
UncopyableStream stream{};
|
|
|
|
ParamsStream pstream{std::move(stream), RAW, HEX, RAW};
|
|
|
|
|
|
|
|
Base base1{0x20};
|
|
|
|
pstream << base1;
|
|
|
|
|
|
|
|
Base base2;
|
|
|
|
pstream >> base2;
|
|
|
|
BOOST_CHECK_EQUAL(base2.m_base_data, 0x20);
|
|
|
|
}
|
|
|
|
|
2020-07-16 11:00:43 +02:00
|
|
|
BOOST_AUTO_TEST_CASE(with_params_base)
|
|
|
|
{
|
|
|
|
Base b{0x0F};
|
|
|
|
|
|
|
|
DataStream stream;
|
|
|
|
|
2023-09-18 13:28:07 +02:00
|
|
|
stream << RAW(b);
|
2020-07-16 11:00:43 +02:00
|
|
|
BOOST_CHECK_EQUAL(stream.str(), "\x0F");
|
|
|
|
|
|
|
|
b.m_base_data = 0;
|
2023-09-18 13:28:07 +02:00
|
|
|
stream >> RAW(b);
|
2020-07-16 11:00:43 +02:00
|
|
|
BOOST_CHECK_EQUAL(b.m_base_data, 0x0F);
|
|
|
|
|
|
|
|
stream.clear();
|
|
|
|
|
2023-09-18 13:28:07 +02:00
|
|
|
stream << HEX(b);
|
2020-07-16 11:00:43 +02:00
|
|
|
BOOST_CHECK_EQUAL(stream.str(), "0f");
|
|
|
|
|
|
|
|
b.m_base_data = 0;
|
2023-09-18 13:28:07 +02:00
|
|
|
stream >> HEX(b);
|
2020-07-16 11:00:43 +02:00
|
|
|
BOOST_CHECK_EQUAL(b.m_base_data, 0x0F);
|
|
|
|
}
|
|
|
|
|
|
|
|
BOOST_AUTO_TEST_CASE(with_params_vector_of_base)
|
|
|
|
{
|
|
|
|
std::vector<Base> v{Base{0x0F}, Base{0xFF}};
|
|
|
|
|
|
|
|
DataStream stream;
|
|
|
|
|
2023-09-18 13:28:07 +02:00
|
|
|
stream << RAW(v);
|
2020-07-16 11:00:43 +02:00
|
|
|
BOOST_CHECK_EQUAL(stream.str(), "\x02\x0F\xFF");
|
|
|
|
|
|
|
|
v[0].m_base_data = 0;
|
|
|
|
v[1].m_base_data = 0;
|
2023-09-18 13:28:07 +02:00
|
|
|
stream >> RAW(v);
|
2020-07-16 11:00:43 +02:00
|
|
|
BOOST_CHECK_EQUAL(v[0].m_base_data, 0x0F);
|
|
|
|
BOOST_CHECK_EQUAL(v[1].m_base_data, 0xFF);
|
|
|
|
|
|
|
|
stream.clear();
|
|
|
|
|
2023-09-18 13:28:07 +02:00
|
|
|
stream << HEX(v);
|
2020-07-16 11:00:43 +02:00
|
|
|
BOOST_CHECK_EQUAL(stream.str(), "\x02"
|
|
|
|
"0fff");
|
|
|
|
|
|
|
|
v[0].m_base_data = 0;
|
|
|
|
v[1].m_base_data = 0;
|
2023-09-18 13:28:07 +02:00
|
|
|
stream >> HEX(v);
|
2020-07-16 11:00:43 +02:00
|
|
|
BOOST_CHECK_EQUAL(v[0].m_base_data, 0x0F);
|
|
|
|
BOOST_CHECK_EQUAL(v[1].m_base_data, 0xFF);
|
|
|
|
}
|
|
|
|
|
2023-09-18 13:28:07 +02:00
|
|
|
constexpr DerivedAndBaseFormat RAW_LOWER{{BaseFormat::RAW}, DerivedAndBaseFormat::DerivedFormat::LOWER};
|
|
|
|
constexpr DerivedAndBaseFormat HEX_UPPER{{BaseFormat::HEX}, DerivedAndBaseFormat::DerivedFormat::UPPER};
|
|
|
|
|
2020-07-16 11:00:43 +02:00
|
|
|
BOOST_AUTO_TEST_CASE(with_params_derived)
|
|
|
|
{
|
|
|
|
Derived d;
|
|
|
|
d.m_base_data = 0x0F;
|
|
|
|
d.m_derived_data = "xY";
|
|
|
|
|
|
|
|
DataStream stream;
|
|
|
|
|
2023-09-18 13:28:07 +02:00
|
|
|
stream << RAW_LOWER(d);
|
2020-07-16 11:00:43 +02:00
|
|
|
|
2023-09-18 13:28:07 +02:00
|
|
|
stream << HEX_UPPER(d);
|
2020-07-16 11:00:43 +02:00
|
|
|
|
|
|
|
BOOST_CHECK_EQUAL(stream.str(), "\x0F\x02xy"
|
|
|
|
"0f\x02XY");
|
|
|
|
}
|
|
|
|
|
2012-06-15 14:19:11 +02:00
|
|
|
BOOST_AUTO_TEST_SUITE_END()
|