From b809058993621b2645a5cd553a0caecb8ec68d2e Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 16 Dec 2017 11:58:58 +0000 Subject: [PATCH] ringct: pseudoOuts moved to prunable in the simple bulletproof case Saves 64 bytes non prunable data per typical tx This breaks v7 consensus, will require a testnet reorg from v6 --- .../cryptonote_boost_serialization.h | 8 ++- src/ringct/rctSigs.cpp | 60 +++++++++++++++---- src/ringct/rctTypes.h | 18 +++++- 3 files changed, 72 insertions(+), 14 deletions(-) diff --git a/src/cryptonote_basic/cryptonote_boost_serialization.h b/src/cryptonote_basic/cryptonote_boost_serialization.h index 80bd2fdc9..143133163 100644 --- a/src/cryptonote_basic/cryptonote_boost_serialization.h +++ b/src/cryptonote_basic/cryptonote_boost_serialization.h @@ -299,7 +299,7 @@ namespace boost throw boost::archive::archive_exception(boost::archive::archive_exception::other_exception, "Unsupported rct type"); // a & x.message; message is not serialized, as it can be reconstructed from the tx data // a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets - if (x.type == rct::RCTTypeSimple || x.type == rct::RCTTypeSimpleBulletproof) + if (x.type == rct::RCTTypeSimple) // moved to prunable with bulletproofs a & x.pseudoOuts; a & x.ecdhInfo; serializeOutPk(a, x.outPk, ver); @@ -313,6 +313,8 @@ namespace boost if (x.rangeSigs.empty()) a & x.bulletproofs; a & x.MGs; + if (x.rangeSigs.empty()) + a & x.pseudoOuts; } template @@ -325,7 +327,7 @@ namespace boost throw boost::archive::archive_exception(boost::archive::archive_exception::other_exception, "Unsupported rct type"); // a & x.message; message is not serialized, as it can be reconstructed from the tx data // a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets - if (x.type == rct::RCTTypeSimple || x.type == rct::RCTTypeSimpleBulletproof) + if (x.type == rct::RCTTypeSimple) a & x.pseudoOuts; a & x.ecdhInfo; serializeOutPk(a, x.outPk, ver); @@ -335,6 +337,8 @@ namespace boost if (x.p.rangeSigs.empty()) a & x.p.bulletproofs; a & x.p.MGs; + if (x.type == rct::RCTTypeSimpleBulletproof) + a & x.p.pseudoOuts; } } } diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index 3c34a5637..0c2be5add 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -43,6 +43,30 @@ using namespace std; #define MONERO_DEFAULT_LOG_CATEGORY "ringct" namespace rct { + bool is_simple(int type) + { + switch (type) + { + case RCTTypeSimple: + case RCTTypeSimpleBulletproof: + return true; + default: + return false; + } + } + + bool is_bulletproof(int type) + { + switch (type) + { + case RCTTypeSimpleBulletproof: + case RCTTypeFullBulletproof: + return true; + default: + return false; + } + } + Bulletproof proveRangeBulletproof(key &C, key &mask, uint64_t amount) { mask = rct::skGen(); @@ -357,7 +381,8 @@ namespace rct { std::stringstream ss; binary_archive ba(ss); - const size_t inputs = rv.pseudoOuts.size(); + CHECK_AND_ASSERT_THROW_MES(!rv.mixRing.empty(), "Empty mixRing"); + const size_t inputs = is_simple(rv.type) ? rv.mixRing.size() : rv.mixRing[0].size(); const size_t outputs = rv.ecdhInfo.size(); CHECK_AND_ASSERT_THROW_MES(const_cast(rv).serialize_rctsig_base(ba, inputs, outputs), "Failed to serialize rctSigBase"); @@ -750,25 +775,26 @@ namespace rct { // TODO: unused ?? // key txnFeeKey = scalarmultH(d2h(rv.txnFee)); rv.mixRing = mixRing; - rv.pseudoOuts.resize(inamounts.size()); + keyV &pseudoOuts = bulletproof ? rv.p.pseudoOuts : rv.pseudoOuts; + pseudoOuts.resize(inamounts.size()); rv.p.MGs.resize(inamounts.size()); key sumpouts = zero(); //sum pseudoOut masks keyV a(inamounts.size()); for (i = 0 ; i < inamounts.size() - 1; i++) { skGen(a[i]); sc_add(sumpouts.bytes, a[i].bytes, sumpouts.bytes); - genC(rv.pseudoOuts[i], a[i], inamounts[i]); + genC(pseudoOuts[i], a[i], inamounts[i]); } rv.mixRing = mixRing; sc_sub(a[i].bytes, sumout.bytes, sumpouts.bytes); - genC(rv.pseudoOuts[i], a[i], inamounts[i]); - DP(rv.pseudoOuts[i]); + genC(pseudoOuts[i], a[i], inamounts[i]); + DP(pseudoOuts[i]); key full_message = get_pre_mlsag_hash(rv); if (msout) msout->c.resize(inamounts.size()); for (i = 0 ; i < inamounts.size(); i++) { - rv.p.MGs[i] = proveRctMGSimple(full_message, rv.mixRing[i], inSk[i], a[i], rv.pseudoOuts[i], kLRki ? &(*kLRki)[i]: NULL, msout ? &msout->c[i] : NULL, index[i]); + rv.p.MGs[i] = proveRctMGSimple(full_message, rv.mixRing[i], inSk[i], a[i], pseudoOuts[i], kLRki ? &(*kLRki)[i]: NULL, msout ? &msout->c[i] : NULL, index[i]); } return rv; } @@ -876,16 +902,26 @@ namespace rct { if (semantics) { if (rv.type == RCTTypeSimpleBulletproof) + { CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.bulletproofs.size(), false, "Mismatched sizes of outPk and rv.p.bulletproofs"); + CHECK_AND_ASSERT_MES(rv.p.pseudoOuts.size() == rv.p.MGs.size(), false, "Mismatched sizes of rv.p.pseudoOuts and rv.p.MGs"); + CHECK_AND_ASSERT_MES(rv.pseudoOuts.empty(), false, "rv.pseudoOuts is not empty"); + } else + { CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.p.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.p.rangeSigs"); + CHECK_AND_ASSERT_MES(rv.pseudoOuts.size() == rv.p.MGs.size(), false, "Mismatched sizes of rv.pseudoOuts and rv.p.MGs"); + CHECK_AND_ASSERT_MES(rv.p.pseudoOuts.empty(), false, "rv.p.pseudoOuts is not empty"); + } CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.ecdhInfo.size(), false, "Mismatched sizes of outPk and rv.ecdhInfo"); - CHECK_AND_ASSERT_MES(rv.pseudoOuts.size() == rv.p.MGs.size(), false, "Mismatched sizes of rv.pseudoOuts and rv.p.MGs"); } else { // semantics check is early, and mixRing/MGs aren't resolved yet - CHECK_AND_ASSERT_MES(rv.pseudoOuts.size() == rv.mixRing.size(), false, "Mismatched sizes of rv.pseudoOuts and mixRing"); + if (rv.type == RCTTypeSimpleBulletproof) + CHECK_AND_ASSERT_MES(rv.p.pseudoOuts.size() == rv.mixRing.size(), false, "Mismatched sizes of rv.p.pseudoOuts and mixRing"); + else + CHECK_AND_ASSERT_MES(rv.pseudoOuts.size() == rv.mixRing.size(), false, "Mismatched sizes of rv.pseudoOuts and mixRing"); } const size_t threads = std::max(rv.outPk.size(), rv.mixRing.size()); @@ -894,6 +930,8 @@ namespace rct { tools::threadpool& tpool = tools::threadpool::getInstance(); tools::threadpool::waiter waiter; + const keyV &pseudoOuts = is_bulletproof(rv.type) ? rv.p.pseudoOuts : rv.pseudoOuts; + if (semantics) { key sumOutpks = identity(); for (size_t i = 0; i < rv.outPk.size(); i++) { @@ -904,8 +942,8 @@ namespace rct { addKeys(sumOutpks, txnFeeKey, sumOutpks); key sumPseudoOuts = identity(); - for (size_t i = 0 ; i < rv.pseudoOuts.size() ; i++) { - addKeys(sumPseudoOuts, sumPseudoOuts, rv.pseudoOuts[i]); + for (size_t i = 0 ; i < pseudoOuts.size() ; i++) { + addKeys(sumPseudoOuts, sumPseudoOuts, pseudoOuts[i]); } DP(sumPseudoOuts); @@ -941,7 +979,7 @@ namespace rct { results.resize(rv.mixRing.size()); for (size_t i = 0 ; i < rv.mixRing.size() ; i++) { tpool.submit(&waiter, [&, i] { - results[i] = verRctMGSimple(message, rv.p.MGs[i], rv.mixRing[i], rv.pseudoOuts[i]); + results[i] = verRctMGSimple(message, rv.p.MGs[i], rv.mixRing[i], pseudoOuts[i]); }); } waiter.wait(); diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h index 2df797360..eba1e3d93 100644 --- a/src/ringct/rctTypes.h +++ b/src/ringct/rctTypes.h @@ -246,7 +246,7 @@ namespace rct { // inputs/outputs not saved, only here for serialization help // FIELD(message) - not serialized, it can be reconstructed // FIELD(mixRing) - not serialized, it can be reconstructed - if (type == RCTTypeSimple || type == RCTTypeSimpleBulletproof) + if (type == RCTTypeSimple) // moved to prunable with bulletproofs { ar.tag("pseudoOuts"); ar.begin_array(); @@ -294,6 +294,7 @@ namespace rct { std::vector rangeSigs; std::vector bulletproofs; std::vector MGs; // simple rct has N, full has 1 + keyV pseudoOuts; //C - for simple rct template class Archive> bool serialize_rctsig_prunable(Archive &ar, uint8_t type, size_t inputs, size_t outputs, size_t mixin) @@ -381,6 +382,21 @@ namespace rct { ar.delimit_array(); } ar.end_array(); + if (type == RCTTypeSimpleBulletproof) + { + ar.tag("pseudoOuts"); + ar.begin_array(); + PREPARE_CUSTOM_VECTOR_SERIALIZATION(inputs, pseudoOuts); + if (pseudoOuts.size() != inputs) + return false; + for (size_t i = 0; i < inputs; ++i) + { + FIELDS(pseudoOuts[i]) + if (inputs - i > 1) + ar.delimit_array(); + } + ar.end_array(); + } return true; }