445 lines
13 KiB
C++
445 lines
13 KiB
C++
// Copyright (c) 2014-2015, The Monero Project
|
|
//
|
|
// All rights reserved.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without modification, are
|
|
// permitted provided that the following conditions are met:
|
|
//
|
|
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
|
// conditions and the following disclaimer.
|
|
//
|
|
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
|
// of conditions and the following disclaimer in the documentation and/or other
|
|
// materials provided with the distribution.
|
|
//
|
|
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
|
// used to endorse or promote products derived from this software without specific
|
|
// prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
|
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
|
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
|
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
//
|
|
// Parts of this file are originally copyright (c) 2012-2013 The Cryptonote developers
|
|
|
|
#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));
|
|
}
|