diff options
Diffstat (limited to 'tests/unit_tests/serialization.cpp')
-rw-r--r-- | tests/unit_tests/serialization.cpp | 418 |
1 files changed, 418 insertions, 0 deletions
diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp new file mode 100644 index 000000000..616509d82 --- /dev/null +++ b/tests/unit_tests/serialization.cpp @@ -0,0 +1,418 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include <cstring> +#include <cstdint> +#include <cstdio> +#include <iostream> +#include <vector> +#include <boost/foreach.hpp> +#include "cryptonote_core/cryptonote_basic.h" +#include "cryptonote_core/cryptonote_basic_impl.h" +#include "serialization/serialization.h" +#include "serialization/binary_archive.h" +#include "serialization/json_archive.h" +#include "serialization/debug_archive.h" +#include "serialization/variant.h" +#include "serialization/vector.h" +#include "serialization/binary_utils.h" +#include "gtest/gtest.h" +using namespace std; + +struct Struct +{ + int32_t a; + int32_t b; + char blob[8]; +}; + +template <class Archive> +struct serializer<Archive, Struct> +{ + static bool serialize(Archive &ar, Struct &s) { + ar.begin_object(); + ar.tag("a"); + ar.serialize_int(s.a); + ar.tag("b"); + ar.serialize_int(s.b); + ar.tag("blob"); + ar.serialize_blob(s.blob, sizeof(s.blob)); + ar.end_object(); + return true; + } +}; + +struct Struct1 +{ + vector<boost::variant<Struct, int32_t>> si; + vector<int16_t> vi; + + BEGIN_SERIALIZE_OBJECT() + FIELD(si) + FIELD(vi) + END_SERIALIZE() + /*template <bool W, template <bool> class Archive> + bool do_serialize(Archive<W> &ar) + { + ar.begin_object(); + ar.tag("si"); + ::do_serialize(ar, si); + ar.tag("vi"); + ::do_serialize(ar, vi); + ar.end_object(); + }*/ +}; + +struct Blob +{ + uint64_t a; + uint32_t b; + + bool operator==(const Blob& rhs) const + { + return a == rhs.a; + } +}; + +VARIANT_TAG(binary_archive, Struct, 0xe0); +VARIANT_TAG(binary_archive, int, 0xe1); +VARIANT_TAG(json_archive, Struct, "struct"); +VARIANT_TAG(json_archive, int, "int"); +VARIANT_TAG(debug_archive, Struct1, "struct1"); +VARIANT_TAG(debug_archive, Struct, "struct"); +VARIANT_TAG(debug_archive, int, "int"); + +BLOB_SERIALIZER(Blob); + +bool try_parse(const string &blob) +{ + Struct1 s1; + return serialization::parse_binary(blob, s1); +} + +TEST(Serialization, BinaryArchiveInts) { + uint64_t x = 0xff00000000, x1; + + ostringstream oss; + binary_archive<true> oar(oss); + oar.serialize_int(x); + ASSERT_TRUE(oss.good()); + ASSERT_EQ(8, oss.str().size()); + ASSERT_EQ(string("\0\0\0\0\xff\0\0\0", 8), oss.str()); + + istringstream iss(oss.str()); + binary_archive<false> iar(iss); + iar.serialize_int(x1); + ASSERT_EQ(8, iss.tellg()); + ASSERT_TRUE(iss.good()); + + ASSERT_EQ(x, x1); +} + +TEST(Serialization, BinaryArchiveVarInts) { + uint64_t x = 0xff00000000, x1; + + ostringstream oss; + binary_archive<true> oar(oss); + oar.serialize_varint(x); + ASSERT_TRUE(oss.good()); + ASSERT_EQ(6, oss.str().size()); + ASSERT_EQ(string("\x80\x80\x80\x80\xF0\x1F", 6), oss.str()); + + istringstream iss(oss.str()); + binary_archive<false> iar(iss); + iar.serialize_varint(x1); + ASSERT_TRUE(iss.good()); + ASSERT_EQ(x, x1); +} + +TEST(Serialization, Test1) { + ostringstream str; + binary_archive<true> ar(str); + + Struct1 s1; + s1.si.push_back(0); + { + Struct s; + s.a = 5; + s.b = 65539; + std::memcpy(s.blob, "12345678", 8); + s1.si.push_back(s); + } + s1.si.push_back(1); + s1.vi.push_back(10); + s1.vi.push_back(22); + + string blob; + ASSERT_TRUE(serialization::dump_binary(s1, blob)); + ASSERT_TRUE(try_parse(blob)); + + ASSERT_EQ('\xE0', blob[6]); + blob[6] = '\xE1'; + ASSERT_FALSE(try_parse(blob)); + blob[6] = '\xE2'; + ASSERT_FALSE(try_parse(blob)); +} + +TEST(Serialization, Overflow) { + Blob x = { 0xff00000000 }; + Blob x1; + + string blob; + ASSERT_TRUE(serialization::dump_binary(x, blob)); + ASSERT_EQ(sizeof(Blob), blob.size()); + + ASSERT_TRUE(serialization::parse_binary(blob, x1)); + ASSERT_EQ(x, x1); + + vector<Blob> bigvector; + ASSERT_FALSE(serialization::parse_binary(blob, bigvector)); + ASSERT_EQ(0, bigvector.size()); +} + +TEST(Serialization, serializes_vector_uint64_as_varint) +{ + std::vector<uint64_t> v; + string blob; + + ASSERT_TRUE(serialization::dump_binary(v, blob)); + ASSERT_EQ(1, blob.size()); + + // +1 byte + v.push_back(0); + ASSERT_TRUE(serialization::dump_binary(v, blob)); + ASSERT_EQ(2, blob.size()); + + // +1 byte + v.push_back(1); + ASSERT_TRUE(serialization::dump_binary(v, blob)); + ASSERT_EQ(3, blob.size()); + + // +2 bytes + v.push_back(0x80); + ASSERT_TRUE(serialization::dump_binary(v, blob)); + ASSERT_EQ(5, blob.size()); + + // +2 bytes + v.push_back(0xFF); + ASSERT_TRUE(serialization::dump_binary(v, blob)); + ASSERT_EQ(7, blob.size()); + + // +2 bytes + v.push_back(0x3FFF); + ASSERT_TRUE(serialization::dump_binary(v, blob)); + ASSERT_EQ(9, blob.size()); + + // +3 bytes + v.push_back(0x40FF); + ASSERT_TRUE(serialization::dump_binary(v, blob)); + ASSERT_EQ(12, blob.size()); + + // +10 bytes + v.push_back(0xFFFFFFFFFFFFFFFF); + ASSERT_TRUE(serialization::dump_binary(v, blob)); + ASSERT_EQ(22, blob.size()); +} + +TEST(Serialization, serializes_vector_int64_as_fixed_int) +{ + std::vector<int64_t> v; + string blob; + + ASSERT_TRUE(serialization::dump_binary(v, blob)); + ASSERT_EQ(1, blob.size()); + + // +8 bytes + v.push_back(0); + ASSERT_TRUE(serialization::dump_binary(v, blob)); + ASSERT_EQ(9, blob.size()); + + // +8 bytes + v.push_back(1); + ASSERT_TRUE(serialization::dump_binary(v, blob)); + ASSERT_EQ(17, blob.size()); + + // +8 bytes + v.push_back(0x80); + ASSERT_TRUE(serialization::dump_binary(v, blob)); + ASSERT_EQ(25, blob.size()); + + // +8 bytes + v.push_back(0xFF); + ASSERT_TRUE(serialization::dump_binary(v, blob)); + ASSERT_EQ(33, blob.size()); + + // +8 bytes + v.push_back(0x3FFF); + ASSERT_TRUE(serialization::dump_binary(v, blob)); + ASSERT_EQ(41, blob.size()); + + // +8 bytes + v.push_back(0x40FF); + ASSERT_TRUE(serialization::dump_binary(v, blob)); + ASSERT_EQ(49, blob.size()); + + // +8 bytes + v.push_back(0xFFFFFFFFFFFFFFFF); + ASSERT_TRUE(serialization::dump_binary(v, blob)); + ASSERT_EQ(57, blob.size()); +} + +namespace +{ + template<typename T> + std::vector<T> linearize_vector2(const std::vector< std::vector<T> >& vec_vec) + { + std::vector<T> res; + BOOST_FOREACH(const auto& vec, vec_vec) + { + res.insert(res.end(), vec.begin(), vec.end()); + } + return res; + } +} + +TEST(Serialization, serializes_transacion_signatures_correctly) +{ + using namespace cryptonote; + + transaction tx; + transaction tx1; + string blob; + + // Empty tx + tx.set_null(); + ASSERT_TRUE(serialization::dump_binary(tx, blob)); + ASSERT_EQ(5, blob.size()); // 5 bytes + 0 bytes extra + 0 bytes signatures + ASSERT_TRUE(serialization::parse_binary(blob, tx1)); + ASSERT_EQ(tx, tx1); + ASSERT_EQ(linearize_vector2(tx.signatures), linearize_vector2(tx1.signatures)); + + // Miner tx without signatures + txin_gen txin_gen1; + txin_gen1.height = 0; + tx.set_null(); + tx.vin.push_back(txin_gen1); + ASSERT_TRUE(serialization::dump_binary(tx, blob)); + ASSERT_EQ(7, blob.size()); // 5 bytes + 2 bytes vin[0] + 0 bytes extra + 0 bytes signatures + ASSERT_TRUE(serialization::parse_binary(blob, tx1)); + ASSERT_EQ(tx, tx1); + ASSERT_EQ(linearize_vector2(tx.signatures), linearize_vector2(tx1.signatures)); + + // Miner tx with empty signatures 2nd vector + tx.signatures.resize(1); + ASSERT_TRUE(serialization::dump_binary(tx, blob)); + ASSERT_EQ(7, blob.size()); // 5 bytes + 2 bytes vin[0] + 0 bytes extra + 0 bytes signatures + ASSERT_TRUE(serialization::parse_binary(blob, tx1)); + ASSERT_EQ(tx, tx1); + ASSERT_EQ(linearize_vector2(tx.signatures), linearize_vector2(tx1.signatures)); + + // Miner tx with one signature + tx.signatures[0].resize(1); + ASSERT_FALSE(serialization::dump_binary(tx, blob)); + + // Miner tx with 2 empty vectors + tx.signatures.resize(2); + tx.signatures[0].resize(0); + tx.signatures[1].resize(0); + ASSERT_FALSE(serialization::dump_binary(tx, blob)); + + // Miner tx with 2 signatures + tx.signatures[0].resize(1); + tx.signatures[1].resize(1); + ASSERT_FALSE(serialization::dump_binary(tx, blob)); + + // Two txin_gen, no signatures + tx.vin.push_back(txin_gen1); + tx.signatures.resize(0); + ASSERT_TRUE(serialization::dump_binary(tx, blob)); + ASSERT_EQ(9, blob.size()); // 5 bytes + 2 * 2 bytes vins + 0 bytes extra + 0 bytes signatures + ASSERT_TRUE(serialization::parse_binary(blob, tx1)); + ASSERT_EQ(tx, tx1); + ASSERT_EQ(linearize_vector2(tx.signatures), linearize_vector2(tx1.signatures)); + + // Two txin_gen, signatures vector contains only one empty element + tx.signatures.resize(1); + ASSERT_FALSE(serialization::dump_binary(tx, blob)); + + // Two txin_gen, signatures vector contains two empty elements + tx.signatures.resize(2); + ASSERT_TRUE(serialization::dump_binary(tx, blob)); + ASSERT_EQ(9, blob.size()); // 5 bytes + 2 * 2 bytes vins + 0 bytes extra + 0 bytes signatures + ASSERT_TRUE(serialization::parse_binary(blob, tx1)); + ASSERT_EQ(tx, tx1); + ASSERT_EQ(linearize_vector2(tx.signatures), linearize_vector2(tx1.signatures)); + + // Two txin_gen, signatures vector contains three empty elements + tx.signatures.resize(3); + ASSERT_FALSE(serialization::dump_binary(tx, blob)); + + // Two txin_gen, signatures vector contains two non empty elements + tx.signatures.resize(2); + tx.signatures[0].resize(1); + tx.signatures[1].resize(1); + ASSERT_FALSE(serialization::dump_binary(tx, blob)); + + // A few bytes instead of signature + tx.vin.clear(); + tx.vin.push_back(txin_gen1); + tx.signatures.clear(); + ASSERT_TRUE(serialization::dump_binary(tx, blob)); + blob.append(std::string(sizeof(crypto::signature) / 2, 'x')); + ASSERT_FALSE(serialization::parse_binary(blob, tx1)); + + // blob contains one signature + blob.append(std::string(sizeof(crypto::signature) / 2, 'y')); + ASSERT_FALSE(serialization::parse_binary(blob, tx1)); + + // Not enough signature vectors for all inputs + txin_to_key txin_to_key1; + txin_to_key1.key_offsets.resize(2); + tx.vin.clear(); + tx.vin.push_back(txin_to_key1); + tx.vin.push_back(txin_to_key1); + tx.signatures.resize(1); + tx.signatures[0].resize(2); + ASSERT_FALSE(serialization::dump_binary(tx, blob)); + + // Too much signatures for two inputs + tx.signatures.resize(3); + tx.signatures[0].resize(2); + tx.signatures[1].resize(2); + tx.signatures[2].resize(2); + ASSERT_FALSE(serialization::dump_binary(tx, blob)); + + // First signatures vector contains too little elements + tx.signatures.resize(2); + tx.signatures[0].resize(1); + tx.signatures[1].resize(2); + ASSERT_FALSE(serialization::dump_binary(tx, blob)); + + // First signatures vector contains too much elements + tx.signatures.resize(2); + tx.signatures[0].resize(3); + tx.signatures[1].resize(2); + ASSERT_FALSE(serialization::dump_binary(tx, blob)); + + // There are signatures for each input + tx.signatures.resize(2); + tx.signatures[0].resize(2); + tx.signatures[1].resize(2); + ASSERT_TRUE(serialization::dump_binary(tx, blob)); + ASSERT_TRUE(serialization::parse_binary(blob, tx1)); + ASSERT_EQ(tx, tx1); + ASSERT_EQ(linearize_vector2(tx.signatures), linearize_vector2(tx1.signatures)); + + // Blob doesn't contain enough data + blob.resize(blob.size() - sizeof(crypto::signature) / 2); + ASSERT_FALSE(serialization::parse_binary(blob, tx1)); + + // Blob contains too much data + blob.resize(blob.size() + sizeof(crypto::signature)); + ASSERT_FALSE(serialization::parse_binary(blob, tx1)); + + // Blob contains one excess signature + blob.resize(blob.size() + sizeof(crypto::signature) / 2); + ASSERT_FALSE(serialization::parse_binary(blob, tx1)); +} |