rct: rework serialization to avoid storing vector sizes

This commit is contained in:
moneromooo-monero 2016-09-14 20:23:06 +01:00
parent 0ce79ef10e
commit 7d413f635f
No known key found for this signature in database
GPG key ID: 686F07454D6CEFC3
6 changed files with 191 additions and 60 deletions

View file

@ -230,24 +230,22 @@ namespace cryptonote
} }
else else
{ {
FIELD(rct_signatures) ar.tag("rct_signatures");
switch (rct_signatures.type) if (!vin.empty())
{ {
case rct::RCTTypeNull: ar.begin_object();
break; bool r = rct_signatures.serialize_rctsig_base(ar, vin.size(), vout.size());
case rct::RCTTypeSimple: if (!r || !ar.stream().good()) return false;
if (rct_signatures.mixRing.size() && rct_signatures.mixRing.size() != vin.size()) ar.end_object();
return false; if (rct_signatures.type != rct::RCTTypeNull)
break;
case rct::RCTTypeFull:
for (size_t i = 0; i < rct_signatures.mixRing.size(); ++i)
{ {
if (rct_signatures.mixRing[i].size() != vin.size()) ar.tag("rctsig_prunable");
return false; ar.begin_object();
r = rct_signatures.p.serialize_rctsig_prunable(ar, rct_signatures.type, vin.size(), vout.size(),
vin[0].type() == typeid(txin_to_key) ? boost::get<txin_to_key>(vin[0]).key_offsets.size() - 1 : 0);
if (!r || !ar.stream().good()) return false;
ar.end_object();
} }
break;
default:
return false;
} }
} }
END_SERIALIZE() END_SERIALIZE()

View file

@ -162,11 +162,16 @@ namespace boost
a & x.vout; a & x.vout;
a & x.extra; a & x.extra;
if (x.version == 1) if (x.version == 1)
{
a & x.signatures; a & x.signatures;
else
a & x.rct_signatures;
} }
else
{
a & (rct::rctSigBase&)x.rct_signatures;
if (x.rct_signatures.type != rct::RCTTypeNull)
a & x.rct_signatures.p;
}
}
template <class Archive> template <class Archive>
inline void serialize(Archive &a, cryptonote::block &b, const boost::serialization::version_type ver) inline void serialize(Archive &a, cryptonote::block &b, const boost::serialization::version_type ver)
@ -262,6 +267,13 @@ namespace boost
a & x.txnFee; a & x.txnFee;
} }
template <class Archive>
inline void serialize(Archive &a, rct::rctSigPrunable &x, const boost::serialization::version_type ver)
{
a & x.rangeSigs;
a & x.MGs;
}
template <class Archive> template <class Archive>
inline void serialize(Archive &a, rct::rctSig &x, const boost::serialization::version_type ver) inline void serialize(Archive &a, rct::rctSig &x, const boost::serialization::version_type ver)
{ {

View file

@ -475,7 +475,7 @@ namespace cryptonote
tx.vin.clear(); tx.vin.clear();
tx.vout.clear(); tx.vout.clear();
tx.signatures.clear(); tx.signatures.clear();
tx.rct_signatures = rct::rctSig(); tx.rct_signatures.type = rct::RCTTypeNull;
amount_keys.clear(); amount_keys.clear();
tx.version = rct ? 2 : 1; tx.version = rct ? 2 : 1;
@ -948,11 +948,35 @@ namespace cryptonote
// prefix // prefix
get_transaction_prefix_hash(t, hashes[0]); get_transaction_prefix_hash(t, hashes[0]);
// base rct data transaction &tt = const_cast<transaction&>(t);
get_blob_hash(t_serializable_object_to_blob((const rct::rctSigBase&)t.rct_signatures), hashes[1]);
// prunable rct data // base rct
get_blob_hash(t_serializable_object_to_blob(t.rct_signatures.p), hashes[2]); {
std::stringstream ss;
binary_archive<true> ba(ss);
const size_t inputs = t.vin.size();
const size_t outputs = t.vout.size();
bool r = tt.rct_signatures.serialize_rctsig_base(ba, inputs, outputs);
CHECK_AND_ASSERT_MES(r, false, "Failed to serialize rct signatures base");
cryptonote::get_blob_hash(ss.str(), hashes[1]);
}
// prunable rct
if (t.rct_signatures.type == rct::RCTTypeNull)
{
hashes[2] = cryptonote::null_hash;
}
else
{
std::stringstream ss;
binary_archive<true> ba(ss);
const size_t inputs = t.vin.size();
const size_t outputs = t.vout.size();
const size_t mixin = t.vin.empty() ? 0 : t.vin[0].type() == typeid(txin_to_key) ? boost::get<txin_to_key>(t.vin[0]).key_offsets.size() - 1 : 0;
bool r = tt.rct_signatures.p.serialize_rctsig_prunable(ba, t.rct_signatures.type, inputs, outputs, mixin);
CHECK_AND_ASSERT_MES(r, false, "Failed to serialize rct signatures prunable");
cryptonote::get_blob_hash(ss.str(), hashes[2]);
}
// the tx hash is the hash of the 3 hashes // the tx hash is the hash of the 3 hashes
res = cn_fast_hash(hashes, sizeof(hashes)); res = cn_fast_hash(hashes, sizeof(hashes));

View file

@ -350,8 +350,16 @@ namespace rct {
keyV hashes; keyV hashes;
hashes.push_back(rv.message); hashes.push_back(rv.message);
crypto::hash h; crypto::hash h;
cryptonote::get_blob_hash(cryptonote::t_serializable_object_to_blob((const rctSigBase&)rv), h);
std::stringstream ss;
binary_archive<true> ba(ss);
const size_t inputs = rv.pseudoOuts.size();
const size_t outputs = rv.ecdhInfo.size();
CHECK_AND_ASSERT_THROW_MES(const_cast<rctSig&>(rv).serialize_rctsig_base(ba, inputs, outputs),
"Failed to serialize rctSigBase");
cryptonote::get_blob_hash(ss.str(), h);
hashes.push_back(hash2rct(h)); hashes.push_back(hash2rct(h));
keyV kv; keyV kv;
for (auto r: rv.p.rangeSigs) for (auto r: rv.p.rangeSigs)
{ {
@ -364,7 +372,6 @@ namespace rct {
kv.push_back(r.Ci[n]); kv.push_back(r.Ci[n]);
} }
hashes.push_back(cn_fast_hash(kv)); hashes.push_back(cn_fast_hash(kv));
return cn_fast_hash(hashes); return cn_fast_hash(hashes);
} }

View file

@ -188,49 +188,136 @@ namespace rct {
ctkeyV outPk; ctkeyV outPk;
xmr_amount txnFee; // contains b xmr_amount txnFee; // contains b
BEGIN_SERIALIZE() template<bool W, template <bool> class Archive>
bool serialize_rctsig_base(Archive<W> &ar, size_t inputs, size_t outputs)
{
FIELD(type) FIELD(type)
if (type == RCTTypeNull) if (type == RCTTypeNull)
return true; return true;
if (type != RCTTypeFull && type != RCTTypeSimple)
return false;
VARINT_FIELD(txnFee)
// inputs/outputs not saved, only here for serialization help
// FIELD(message) - not serialized, it can be reconstructed // FIELD(message) - not serialized, it can be reconstructed
// FIELD(mixRing) - not serialized, it can be reconstructed // FIELD(mixRing) - not serialized, it can be reconstructed
if (type == RCTTypeSimple) if (type == RCTTypeSimple)
FIELD(pseudoOuts) {
FIELD(ecdhInfo) ar.tag("pseudoOuts");
if (typename Archive<W>::is_saving()) { ar.begin_array();
keyV outPk(this->outPk.size()); PREPARE_CUSTOM_VECTOR_SERIALIZATION(inputs, pseudoOuts);
for (size_t n = 0; n < outPk.size(); ++n) if (pseudoOuts.size() != inputs)
outPk[n] = this->outPk[n].mask; return false;
FIELD(outPk) for (size_t i = 0; i < inputs; ++i)
{
FIELDS(pseudoOuts[i])
if (inputs - i > 1)
ar.delimit_array();
} }
else { ar.end_array();
keyV outPk; }
FIELD(outPk)
this->outPk.resize(outPk.size()); ar.tag("ecdhInfo");
for (size_t n = 0; n < outPk.size(); ++n) ar.begin_array();
this->outPk[n].mask = outPk[n]; PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, ecdhInfo);
if (ecdhInfo.size() != outputs)
return false;
for (size_t i = 0; i < outputs; ++i)
{
FIELDS(ecdhInfo[i])
if (outputs - i > 1)
ar.delimit_array();
}
ar.end_array();
ar.tag("outPk");
ar.begin_array();
PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, outPk);
if (outPk.size() != outputs)
return false;
for (size_t i = 0; i < outputs; ++i)
{
FIELDS(outPk[i].mask)
if (outputs - i > 1)
ar.delimit_array();
}
ar.end_array();
return true;
} }
VARINT_FIELD(txnFee)
END_SERIALIZE()
}; };
struct rctSigPrunable { struct rctSigPrunable {
vector<rangeSig> rangeSigs; vector<rangeSig> rangeSigs;
vector<mgSig> MGs; // simple rct has N, full has 1 vector<mgSig> MGs; // simple rct has N, full has 1
BEGIN_SERIALIZE() template<bool W, template <bool> class Archive>
FIELD(rangeSigs) bool serialize_rctsig_prunable(Archive<W> &ar, uint8_t type, size_t inputs, size_t outputs, size_t mixin)
FIELD(MGs) {
END_SERIALIZE() if (type == RCTTypeNull)
return true;
if (type != RCTTypeFull && type != RCTTypeSimple)
return false;
ar.tag("rangeSigs");
ar.begin_array();
PREPARE_CUSTOM_VECTOR_SERIALIZATION(outputs, rangeSigs);
if (rangeSigs.size() != outputs)
return false;
for (size_t i = 0; i < outputs; ++i)
{
FIELDS(rangeSigs[i])
if (outputs - i > 1)
ar.delimit_array();
}
ar.end_array();
ar.tag("MGs");
ar.begin_array();
// we keep a byte for size of MGs, because we don't know whether this is
// a simple or full rct signature, and it's starting to annoy the hell out of me
size_t mg_elements = type == RCTTypeSimple ? inputs : 1;
PREPARE_CUSTOM_VECTOR_SERIALIZATION(mg_elements, MGs);
if (MGs.size() != mg_elements)
return false;
for (size_t i = 0; i < mg_elements; ++i)
{
// we save the MGs contents directly, because we want it to save its
// arrays and matrices without the size prefixes, and the load can't
// know what size to expect if it's not in the data
ar.tag("ss");
ar.begin_array();
PREPARE_CUSTOM_VECTOR_SERIALIZATION(mixin + 1, MGs[i].ss);
if (MGs[i].ss.size() != mixin + 1)
return false;
for (size_t j = 0; j < mixin + 1; ++j)
{
ar.begin_array();
size_t mg_ss2_elements = (type == RCTTypeSimple ? 1 : inputs) + 1;
PREPARE_CUSTOM_VECTOR_SERIALIZATION(mg_ss2_elements, MGs[i].ss[j]);
if (MGs[i].ss[j].size() != mg_ss2_elements)
return false;
for (size_t k = 0; k < mg_ss2_elements; ++k)
{
FIELDS(MGs[i].ss[j][k])
if (mg_ss2_elements - j > 1)
ar.delimit_array();
}
ar.end_array();
if (mixin + 1 - j > 1)
ar.delimit_array();
}
ar.end_array();
FIELDS(MGs[i].cc)
// MGs[i].II not saved, it can be reconstructed
if (mg_elements - i > 1)
ar.delimit_array();
}
ar.end_array();
return true;
}
}; };
struct rctSig: public rctSigBase { struct rctSig: public rctSigBase {
rctSigPrunable p; rctSigPrunable p;
BEGIN_SERIALIZE_OBJECT()
FIELDS(*static_cast<rctSigBase *>(this))
if (type == RCTTypeNull)
return true;
FIELDS(p);
END_SERIALIZE()
}; };
//other basepoint H = toPoint(cn_fast_hash(G)), G the basepoint //other basepoint H = toPoint(cn_fast_hash(G)), G the basepoint

View file

@ -589,6 +589,7 @@ TEST(Serialization, serializes_ringct_types)
ASSERT_TRUE(serialization::parse_binary(blob, rg1)); ASSERT_TRUE(serialization::parse_binary(blob, rg1));
ASSERT_TRUE(!memcmp(&rg0, &rg1, sizeof(rg0))); ASSERT_TRUE(!memcmp(&rg0, &rg1, sizeof(rg0)));
#if 0
ASSERT_TRUE(serialization::dump_binary(s0, blob)); ASSERT_TRUE(serialization::dump_binary(s0, blob));
ASSERT_TRUE(serialization::parse_binary(blob, s1)); ASSERT_TRUE(serialization::parse_binary(blob, s1));
ASSERT_TRUE(s0.type == s1.type); ASSERT_TRUE(s0.type == s1.type);
@ -621,16 +622,18 @@ TEST(Serialization, serializes_ringct_types)
// serialization only does the mask // serialization only does the mask
ASSERT_TRUE(!memcmp(&s0.outPk[n].mask, &s1.outPk[n].mask, sizeof(s0.outPk[n].mask))); ASSERT_TRUE(!memcmp(&s0.outPk[n].mask, &s1.outPk[n].mask, sizeof(s0.outPk[n].mask)));
} }
#endif
tx0.set_null(); tx0.set_null();
tx0.version = 2; tx0.version = 2;
cryptonote::txin_to_key txin_to_key1; cryptonote::txin_to_key txin_to_key1;
txin_to_key1.key_offsets.resize(2); txin_to_key1.key_offsets.resize(4);
cryptonote::txin_to_key txin_to_key2; cryptonote::txin_to_key txin_to_key2;
txin_to_key2.key_offsets.resize(2); txin_to_key2.key_offsets.resize(4);
tx0.vin.push_back(txin_to_key1); tx0.vin.push_back(txin_to_key1);
tx0.vin.push_back(txin_to_key2); tx0.vin.push_back(txin_to_key2);
tx0.vout.push_back(cryptonote::tx_out()); tx0.vout.push_back(cryptonote::tx_out());
tx0.vout.push_back(cryptonote::tx_out());
tx0.rct_signatures = s0; tx0.rct_signatures = s0;
ASSERT_EQ(tx0.rct_signatures.p.rangeSigs.size(), 2); ASSERT_EQ(tx0.rct_signatures.p.rangeSigs.size(), 2);
ASSERT_TRUE(serialization::dump_binary(tx0, blob)); ASSERT_TRUE(serialization::dump_binary(tx0, blob));