aboutsummaryrefslogtreecommitdiff
path: root/tests/unit_tests/serialization.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/unit_tests/serialization.cpp')
-rw-r--r--tests/unit_tests/serialization.cpp418
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));
+}