Files
hush3/src/test/serialize_tests.cpp
Sean Bowe e1ff849d8d New implementation of incremental merkle tree
This is a new implementation of the incremental merkle tree used by our
scheme to witness commitments to spendable value. It serves as a fixed-sized
accumulator.

This new construction has a much simpler API surface area, avoids memory
safety issues, remains pruned at all times, avoids serialization edge cases,
has more efficient insertion, and is abstract over the depth and hash
function used at the type level.

Further, it lays the groundwork for efficient "fast-forwarding" of witnesses
into the tree as the treestate is updated.
2016-04-28 16:07:52 -06:00

350 lines
12 KiB
C++

// Copyright (c) 2012-2013 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "serialize.h"
#include "streams.h"
#include "hash.h"
#include "test/test_bitcoin.h"
#include "utilstrencodings.h"
#include <stdint.h>
#include <boost/test/unit_test.hpp>
#include <boost/optional.hpp>
using namespace std;
template<typename T>
void check_ser_rep(T thing, std::vector<unsigned char> expected)
{
CDataStream ss(SER_DISK, 0);
ss << thing;
BOOST_CHECK(GetSerializeSize(thing, 0, 0) == ss.size());
std::vector<unsigned char> serialized_representation(ss.begin(), ss.end());
BOOST_CHECK(serialized_representation == expected);
T thing_deserialized;
ss >> thing_deserialized;
BOOST_CHECK(thing_deserialized == thing);
}
BOOST_FIXTURE_TEST_SUITE(serialize_tests, BasicTestingSetup)
BOOST_AUTO_TEST_CASE(boost_optional)
{
check_ser_rep<boost::optional<unsigned char>>(0xff, {0x01, 0xff});
check_ser_rep<boost::optional<unsigned char>>(boost::none, {0x00});
check_ser_rep<boost::optional<std::string>>(std::string("Test"), {0x01, 0x04, 'T', 'e', 's', 't'});
{
// Ensure that canonical optional discriminant is used
CDataStream ss(SER_DISK, 0);
ss.write("\x02\x04Test", 6);
boost::optional<std::string> into;
BOOST_CHECK_THROW(ss >> into, std::ios_base::failure);
}
}
BOOST_AUTO_TEST_CASE(boost_arrays)
{
boost::array<std::string, 2> test_case = {string("zub"), string("baz")};
CDataStream ss(SER_DISK, 0);
ss << test_case;
auto hash = Hash(ss.begin(), ss.end());
BOOST_CHECK_MESSAGE("037a75620362617a" == HexStr(ss.begin(), ss.end()), HexStr(ss.begin(), ss.end()));
BOOST_CHECK_MESSAGE(hash == uint256S("13cb12b2dd098dced0064fe4897c97f907ba3ed36ae470c2e7fc2b1111eba35a"), "actually got: " << hash.ToString());
{
// note: boost array of size 2 should serialize to be the same as a tuple
std::pair<std::string, std::string> test_case_2 = {string("zub"), string("baz")};
CDataStream ss2(SER_DISK, 0);
ss2 << test_case_2;
auto hash2 = Hash(ss2.begin(), ss2.end());
BOOST_CHECK(hash == hash2);
}
boost::array<std::string, 2> decoded_test_case;
ss >> decoded_test_case;
BOOST_CHECK(decoded_test_case == test_case);
boost::array<int32_t, 2> test = {100, 200};
BOOST_CHECK_EQUAL(GetSerializeSize(test, 0, 0), 8);
}
BOOST_AUTO_TEST_CASE(sizes)
{
BOOST_CHECK_EQUAL(sizeof(char), GetSerializeSize(char(0), 0));
BOOST_CHECK_EQUAL(sizeof(int8_t), GetSerializeSize(int8_t(0), 0));
BOOST_CHECK_EQUAL(sizeof(uint8_t), GetSerializeSize(uint8_t(0), 0));
BOOST_CHECK_EQUAL(sizeof(int16_t), GetSerializeSize(int16_t(0), 0));
BOOST_CHECK_EQUAL(sizeof(uint16_t), GetSerializeSize(uint16_t(0), 0));
BOOST_CHECK_EQUAL(sizeof(int32_t), GetSerializeSize(int32_t(0), 0));
BOOST_CHECK_EQUAL(sizeof(uint32_t), GetSerializeSize(uint32_t(0), 0));
BOOST_CHECK_EQUAL(sizeof(int64_t), GetSerializeSize(int64_t(0), 0));
BOOST_CHECK_EQUAL(sizeof(uint64_t), GetSerializeSize(uint64_t(0), 0));
BOOST_CHECK_EQUAL(sizeof(float), GetSerializeSize(float(0), 0));
BOOST_CHECK_EQUAL(sizeof(double), GetSerializeSize(double(0), 0));
// Bool is serialized as char
BOOST_CHECK_EQUAL(sizeof(char), GetSerializeSize(bool(0), 0));
// Sanity-check GetSerializeSize and c++ type matching
BOOST_CHECK_EQUAL(GetSerializeSize(char(0), 0), 1);
BOOST_CHECK_EQUAL(GetSerializeSize(int8_t(0), 0), 1);
BOOST_CHECK_EQUAL(GetSerializeSize(uint8_t(0), 0), 1);
BOOST_CHECK_EQUAL(GetSerializeSize(int16_t(0), 0), 2);
BOOST_CHECK_EQUAL(GetSerializeSize(uint16_t(0), 0), 2);
BOOST_CHECK_EQUAL(GetSerializeSize(int32_t(0), 0), 4);
BOOST_CHECK_EQUAL(GetSerializeSize(uint32_t(0), 0), 4);
BOOST_CHECK_EQUAL(GetSerializeSize(int64_t(0), 0), 8);
BOOST_CHECK_EQUAL(GetSerializeSize(uint64_t(0), 0), 8);
BOOST_CHECK_EQUAL(GetSerializeSize(float(0), 0), 4);
BOOST_CHECK_EQUAL(GetSerializeSize(double(0), 0), 8);
BOOST_CHECK_EQUAL(GetSerializeSize(bool(0), 0), 1);
}
BOOST_AUTO_TEST_CASE(floats_conversion)
{
// Choose values that map unambigiously to binary floating point to avoid
// rounding issues at the compiler side.
BOOST_CHECK_EQUAL(ser_uint32_to_float(0x00000000), 0.0F);
BOOST_CHECK_EQUAL(ser_uint32_to_float(0x3f000000), 0.5F);
BOOST_CHECK_EQUAL(ser_uint32_to_float(0x3f800000), 1.0F);
BOOST_CHECK_EQUAL(ser_uint32_to_float(0x40000000), 2.0F);
BOOST_CHECK_EQUAL(ser_uint32_to_float(0x40800000), 4.0F);
BOOST_CHECK_EQUAL(ser_uint32_to_float(0x44444444), 785.066650390625F);
BOOST_CHECK_EQUAL(ser_float_to_uint32(0.0F), 0x00000000);
BOOST_CHECK_EQUAL(ser_float_to_uint32(0.5F), 0x3f000000);
BOOST_CHECK_EQUAL(ser_float_to_uint32(1.0F), 0x3f800000);
BOOST_CHECK_EQUAL(ser_float_to_uint32(2.0F), 0x40000000);
BOOST_CHECK_EQUAL(ser_float_to_uint32(4.0F), 0x40800000);
BOOST_CHECK_EQUAL(ser_float_to_uint32(785.066650390625F), 0x44444444);
}
BOOST_AUTO_TEST_CASE(doubles_conversion)
{
// Choose values that map unambigiously to binary floating point to avoid
// rounding issues at the compiler side.
BOOST_CHECK_EQUAL(ser_uint64_to_double(0x0000000000000000ULL), 0.0);
BOOST_CHECK_EQUAL(ser_uint64_to_double(0x3fe0000000000000ULL), 0.5);
BOOST_CHECK_EQUAL(ser_uint64_to_double(0x3ff0000000000000ULL), 1.0);
BOOST_CHECK_EQUAL(ser_uint64_to_double(0x4000000000000000ULL), 2.0);
BOOST_CHECK_EQUAL(ser_uint64_to_double(0x4010000000000000ULL), 4.0);
BOOST_CHECK_EQUAL(ser_uint64_to_double(0x4088888880000000ULL), 785.066650390625);
BOOST_CHECK_EQUAL(ser_double_to_uint64(0.0), 0x0000000000000000ULL);
BOOST_CHECK_EQUAL(ser_double_to_uint64(0.5), 0x3fe0000000000000ULL);
BOOST_CHECK_EQUAL(ser_double_to_uint64(1.0), 0x3ff0000000000000ULL);
BOOST_CHECK_EQUAL(ser_double_to_uint64(2.0), 0x4000000000000000ULL);
BOOST_CHECK_EQUAL(ser_double_to_uint64(4.0), 0x4010000000000000ULL);
BOOST_CHECK_EQUAL(ser_double_to_uint64(785.066650390625), 0x4088888880000000ULL);
}
/*
Python code to generate the below hashes:
def reversed_hex(x):
return binascii.hexlify(''.join(reversed(x)))
def dsha256(x):
return hashlib.sha256(hashlib.sha256(x).digest()).digest()
reversed_hex(dsha256(''.join(struct.pack('<f', x) for x in range(0,1000)))) == '8e8b4cf3e4df8b332057e3e23af42ebc663b61e0495d5e7e32d85099d7f3fe0c'
reversed_hex(dsha256(''.join(struct.pack('<d', x) for x in range(0,1000)))) == '43d0c82591953c4eafe114590d392676a01585d25b25d433557f0d7878b23f96'
*/
BOOST_AUTO_TEST_CASE(floats)
{
CDataStream ss(SER_DISK, 0);
// encode
for (int i = 0; i < 1000; i++) {
ss << float(i);
}
BOOST_CHECK(Hash(ss.begin(), ss.end()) == uint256S("8e8b4cf3e4df8b332057e3e23af42ebc663b61e0495d5e7e32d85099d7f3fe0c"));
// decode
for (int i = 0; i < 1000; i++) {
float j;
ss >> j;
BOOST_CHECK_MESSAGE(i == j, "decoded:" << j << " expected:" << i);
}
}
BOOST_AUTO_TEST_CASE(doubles)
{
CDataStream ss(SER_DISK, 0);
// encode
for (int i = 0; i < 1000; i++) {
ss << double(i);
}
BOOST_CHECK(Hash(ss.begin(), ss.end()) == uint256S("43d0c82591953c4eafe114590d392676a01585d25b25d433557f0d7878b23f96"));
// decode
for (int i = 0; i < 1000; i++) {
double j;
ss >> j;
BOOST_CHECK_MESSAGE(i == j, "decoded:" << j << " expected:" << i);
}
}
BOOST_AUTO_TEST_CASE(varints)
{
// encode
CDataStream ss(SER_DISK, 0);
CDataStream::size_type size = 0;
for (int i = 0; i < 100000; i++) {
ss << VARINT(i);
size += ::GetSerializeSize(VARINT(i), 0, 0);
BOOST_CHECK(size == ss.size());
}
for (uint64_t i = 0; i < 100000000000ULL; i += 999999937) {
ss << VARINT(i);
size += ::GetSerializeSize(VARINT(i), 0, 0);
BOOST_CHECK(size == ss.size());
}
// decode
for (int i = 0; i < 100000; i++) {
int j = -1;
ss >> VARINT(j);
BOOST_CHECK_MESSAGE(i == j, "decoded:" << j << " expected:" << i);
}
for (uint64_t i = 0; i < 100000000000ULL; i += 999999937) {
uint64_t j = -1;
ss >> VARINT(j);
BOOST_CHECK_MESSAGE(i == j, "decoded:" << j << " expected:" << i);
}
}
BOOST_AUTO_TEST_CASE(compactsize)
{
CDataStream ss(SER_DISK, 0);
vector<char>::size_type i, j;
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)
{
std::ios_base::failure expectedException("non-canonical ReadCompactSize()");
// The string returned by what() can be different for different platforms.
// Instead of directly comparing the ex.what() with an expected string,
// create an instance of exception to see if ex.what() matches
// the expected explanatory string returned by the exception instance.
return strcmp(expectedException.what(), ex.what()) == 0;
}
BOOST_AUTO_TEST_CASE(noncanonical)
{
// Write some non-canonical CompactSize encodings, and
// make sure an exception is thrown when read back.
CDataStream ss(SER_DISK, 0);
vector<char>::size_type n;
// zero encoded with three bytes:
ss.write("\xfd\x00\x00", 3);
BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException);
// 0xfc encoded with three bytes:
ss.write("\xfd\xfc\x00", 3);
BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException);
// 0xfd encoded with three bytes is OK:
ss.write("\xfd\xfd\x00", 3);
n = ReadCompactSize(ss);
BOOST_CHECK(n == 0xfd);
// zero encoded with five bytes:
ss.write("\xfe\x00\x00\x00\x00", 5);
BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException);
// 0xffff encoded with five bytes:
ss.write("\xfe\xff\xff\x00\x00", 5);
BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException);
// zero encoded with nine bytes:
ss.write("\xff\x00\x00\x00\x00\x00\x00\x00\x00", 9);
BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException);
// 0x01ffffff encoded with nine bytes:
ss.write("\xff\xff\xff\xff\x01\x00\x00\x00\x00", 9);
BOOST_CHECK_EXCEPTION(ReadCompactSize(ss), std::ios_base::failure, isCanonicalException);
}
BOOST_AUTO_TEST_CASE(insert_delete)
{
// Test inserting/deleting bytes.
CDataStream ss(SER_DISK, 0);
BOOST_CHECK_EQUAL(ss.size(), 0);
ss.write("\x00\x01\x02\xff", 4);
BOOST_CHECK_EQUAL(ss.size(), 4);
char c = (char)11;
// Inserting at beginning/end/middle:
ss.insert(ss.begin(), c);
BOOST_CHECK_EQUAL(ss.size(), 5);
BOOST_CHECK_EQUAL(ss[0], c);
BOOST_CHECK_EQUAL(ss[1], 0);
ss.insert(ss.end(), c);
BOOST_CHECK_EQUAL(ss.size(), 6);
BOOST_CHECK_EQUAL(ss[4], (char)0xff);
BOOST_CHECK_EQUAL(ss[5], c);
ss.insert(ss.begin()+2, c);
BOOST_CHECK_EQUAL(ss.size(), 7);
BOOST_CHECK_EQUAL(ss[2], c);
// Delete at beginning/end/middle
ss.erase(ss.begin());
BOOST_CHECK_EQUAL(ss.size(), 6);
BOOST_CHECK_EQUAL(ss[0], 0);
ss.erase(ss.begin()+ss.size()-1);
BOOST_CHECK_EQUAL(ss.size(), 5);
BOOST_CHECK_EQUAL(ss[4], (char)0xff);
ss.erase(ss.begin()+1);
BOOST_CHECK_EQUAL(ss.size(), 4);
BOOST_CHECK_EQUAL(ss[0], 0);
BOOST_CHECK_EQUAL(ss[1], 1);
BOOST_CHECK_EQUAL(ss[2], 2);
BOOST_CHECK_EQUAL(ss[3], (char)0xff);
// Make sure GetAndClear does the right thing:
CSerializeData d;
ss.GetAndClear(d);
BOOST_CHECK_EQUAL(ss.size(), 0);
}
BOOST_AUTO_TEST_SUITE_END()