From cf33e1a52a0cf20a7cec619d85d68f000b2e1f40 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 23 Jul 2016 12:09:33 +0100 Subject: [PATCH] rct: do not serialize public keys in outPk They can be reconstructed from vout --- src/cryptonote_core/blockchain.cpp | 35 ++++++++++++++++--- .../cryptonote_boost_serialization.h | 23 +++++++++++- src/cryptonote_core/cryptonote_core.cpp | 8 ----- src/ringct/rctSigs.cpp | 28 +++++++-------- src/ringct/rctSigs.h | 4 +-- src/ringct/rctTypes.h | 14 +++++++- src/wallet/wallet2.cpp | 6 ++-- tests/unit_tests/serialization.cpp | 3 +- 8 files changed, 87 insertions(+), 34 deletions(-) diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 456a78eaf..9972b25e5 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -2468,11 +2468,12 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context { rct::ctkeyM reconstructed_mixRing; std::vector reconstructed_II; + rct::ctkeyV reconstructed_outPk; // if the tx already has a non empty mixRing, use them, // else reconstruct them const rct::ctkeyM &mixRing = tx.rct_signatures.mixRing.empty() ? reconstructed_mixRing : tx.rct_signatures.mixRing; - // always do II, because it's split in the simple version + // always do II, because it's split in the simple version, and always do outPk // all MGs should have the same II size (1) for (size_t n = 0; n < tx.rct_signatures.MGs.size(); ++n) @@ -2491,6 +2492,18 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context reconstructed_II[n].push_back(tx.rct_signatures.MGs[n].II[0]); } + if (tx.rct_signatures.outPk.size() != tx.vout.size()) + { + LOG_PRINT_L1("Failed to check ringct signatures: outPk and vout have different sizes"); + return false; + } + reconstructed_outPk.resize(tx.vout.size()); + for (size_t n = 0; n < tx.vout.size(); ++n) + { + reconstructed_outPk[n].dest = rct::pk2rct(boost::get(tx.vout[n].target).key); + reconstructed_outPk[n].mask = tx.rct_signatures.outPk[n].mask; + } + if (tx.rct_signatures.mixRing.empty()) { reconstructed_mixRing.resize(pubkeys.size()); @@ -2551,7 +2564,7 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context } } - if (!rct::verRctSimple(tx.rct_signatures, mixRing, &reconstructed_II, rct::hash2rct(tx_prefix_hash))) + if (!rct::verRctSimple(tx.rct_signatures, mixRing, &reconstructed_II, reconstructed_outPk, rct::hash2rct(tx_prefix_hash))) { LOG_PRINT_L1("Failed to check ringct signatures!"); return false; @@ -2561,11 +2574,13 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context { rct::ctkeyM reconstructed_mixRing; rct::keyV reconstructed_II; + rct::ctkeyV reconstructed_outPk; // if the tx already has a non empty mixRing and/or II, use them, - // else reconstruct them + // else reconstruct them. Always do outPk. const rct::ctkeyM &mixRing = tx.rct_signatures.mixRing.empty() ? reconstructed_mixRing : tx.rct_signatures.mixRing; const rct::keyV &II = tx.rct_signatures.MG.II.size() == 1 ? reconstructed_II : tx.rct_signatures.MG.II; + const rct::ctkeyV outPk = reconstructed_outPk; // RCT needs the same mixin for all inputs for (size_t n = 1; n < pubkeys.size(); ++n) @@ -2599,6 +2614,18 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context reconstructed_II.push_back(tx.rct_signatures.MG.II.back()); } + if (tx.rct_signatures.outPk.size() != tx.vout.size()) + { + LOG_PRINT_L1("Failed to check ringct signatures: outPk and vout have different sizes"); + return false; + } + reconstructed_outPk.resize(tx.vout.size()); + for (size_t n = 0; n < tx.vout.size(); ++n) + { + reconstructed_outPk[n].dest = rct::pk2rct(boost::get(tx.vout[n].target).key); + reconstructed_outPk[n].mask = tx.rct_signatures.outPk[n].mask; + } + // check all this, either recontructed (so should really pass), or not { bool size_matches = true; @@ -2644,7 +2671,7 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context } } - if (!rct::verRct(tx.rct_signatures, mixRing, II, rct::hash2rct(tx_prefix_hash))) + if (!rct::verRct(tx.rct_signatures, mixRing, II, outPk, rct::hash2rct(tx_prefix_hash))) { LOG_PRINT_L1("Failed to check ringct signatures!"); return false; diff --git a/src/cryptonote_core/cryptonote_boost_serialization.h b/src/cryptonote_core/cryptonote_boost_serialization.h index 35fabe7fb..81f5f081a 100644 --- a/src/cryptonote_core/cryptonote_boost_serialization.h +++ b/src/cryptonote_core/cryptonote_boost_serialization.h @@ -43,6 +43,7 @@ #include "common/unordered_containers_boost_serialization.h" #include "crypto/crypto.h" #include "ringct/rctTypes.h" +#include "ringct/rctOps.h" //namespace cryptonote { namespace boost @@ -221,6 +222,26 @@ namespace boost a & x.senderPk; } + inline void serializeOutPk(boost::archive::binary_iarchive &a, rct::ctkeyV &outPk_, const boost::serialization::version_type ver) + { + rct::keyV outPk; + a & outPk; + outPk_.resize(outPk.size()); + for (size_t n = 0; n < outPk_.size(); ++n) + { + outPk_[n].dest = rct::identity(); + outPk_[n].mask = outPk[n]; + } + } + + inline void serializeOutPk(boost::archive::binary_oarchive &a, rct::ctkeyV &outPk_, const boost::serialization::version_type ver) + { + rct::keyV outPk(outPk_.size()); + for (size_t n = 0; n < outPk_.size(); ++n) + outPk[n] = outPk_[n].mask; + a & outPk; + } + template inline void serialize(Archive &a, rct::rctSig &x, const boost::serialization::version_type ver) { @@ -235,7 +256,7 @@ namespace boost if (x.simple) a & x.pseudoOuts; a & x.ecdhInfo; - a & x.outPk; + serializeOutPk(a, x.outPk, ver); a & x.txnFee; } } diff --git a/src/cryptonote_core/cryptonote_core.cpp b/src/cryptonote_core/cryptonote_core.cpp index 880162ed2..511f50616 100644 --- a/src/cryptonote_core/cryptonote_core.cpp +++ b/src/cryptonote_core/cryptonote_core.cpp @@ -569,14 +569,6 @@ namespace cryptonote LOG_PRINT_RED_L1("tx with mismatched vout/outPk count, rejected for tx id= " << get_transaction_hash(tx)); return false; } - for (size_t n = 0; n < tx.vout.size(); ++n) - { - if (tx.rct_signatures.outPk[n].dest != boost::get(tx.vout[n].target).key) - { - LOG_PRINT_RED_L1("tx ringct public key does not match output public key for tx id= " << get_transaction_hash(tx)); - return false; - } - } } if(!check_money_overflow(tx)) diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index c4a297190..fa9c833dd 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -679,10 +679,10 @@ namespace rct { //decodeRct: (c.f. http://eprint.iacr.org/2015/1098 section 5.1.1) // uses the attached ecdh info to find the amounts represented by each output commitment // must know the destination private key to find the correct amount, else will return a random number - bool verRct(const rctSig & rv, const ctkeyM &mixRing, const keyV &II, const key &message) { + bool verRct(const rctSig & rv, const ctkeyM &mixRing, const keyV &II, const ctkeyV &outPk, const key &message) { CHECK_AND_ASSERT_MES(!rv.simple, false, "verRct called on simple rctSig"); - CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.rangeSigs.size(), false, "Mismatched sizes of rv.outPk and rv.rangeSigs"); - CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.ecdhInfo.size(), false, "Mismatched sizes of rv.outPk and rv.ecdhInfo"); + CHECK_AND_ASSERT_MES(outPk.size() == rv.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.rangeSigs"); + CHECK_AND_ASSERT_MES(outPk.size() == rv.ecdhInfo.size(), false, "Mismatched sizes of outPk and rv.ecdhInfo"); // some rct ops can throw try @@ -691,14 +691,14 @@ namespace rct { bool rvb = true; bool tmp; DP("range proofs verified?"); - for (i = 0; i < rv.outPk.size(); i++) { - tmp = verRange(rv.outPk[i].mask, rv.rangeSigs[i]); + for (i = 0; i < outPk.size(); i++) { + tmp = verRange(outPk[i].mask, rv.rangeSigs[i]); DP(tmp); rvb = (rvb && tmp); } //compute txn fee key txnFeeKey = scalarmultH(d2h(rv.txnFee)); - bool mgVerd = verRctMG(rv.MG, II, mixRing, rv.outPk, txnFeeKey, message); + bool mgVerd = verRctMG(rv.MG, II, mixRing, outPk, txnFeeKey, message); DP("mg sig verified?"); DP(mgVerd); @@ -710,18 +710,18 @@ namespace rct { } } bool verRct(const rctSig & rv) { - return verRct(rv, rv.mixRing, rv.MG.II, rv.message); + return verRct(rv, rv.mixRing, rv.MG.II, rv.outPk, rv.message); } //ver RingCT simple //assumes only post-rct style inputs (at least for max anonymity) - bool verRctSimple(const rctSig & rv, const ctkeyM &mixRing, const std::vector *II, const key &message) { + bool verRctSimple(const rctSig & rv, const ctkeyM &mixRing, const std::vector *II, const ctkeyV &outPk, const key &message) { size_t i = 0; bool rvb = true; CHECK_AND_ASSERT_MES(rv.simple, false, "verRctSimple called on non simple rctSig"); - CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.rangeSigs.size(), false, "Mismatched sizes of rv.outPk and rv.rangeSigs"); - CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.ecdhInfo.size(), false, "Mismatched sizes of rv.outPk and rv.ecdhInfo"); + CHECK_AND_ASSERT_MES(outPk.size() == rv.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.rangeSigs"); + CHECK_AND_ASSERT_MES(outPk.size() == rv.ecdhInfo.size(), false, "Mismatched sizes of outPk and rv.ecdhInfo"); CHECK_AND_ASSERT_MES(rv.pseudoOuts.size() == rv.MGs.size(), false, "Mismatched sizes of rv.pseudoOuts and rv.MGs"); CHECK_AND_ASSERT_MES(rv.pseudoOuts.size() == mixRing.size(), false, "Mismatched sizes of rv.pseudoOuts and mixRing"); CHECK_AND_ASSERT_MES(!II || II->size() == mixRing.size(), false, "Mismatched II/mixRing size"); @@ -734,11 +734,11 @@ namespace rct { } key sumOutpks = identity(); - for (i = 0; i < rv.outPk.size(); i++) { - if (!verRange(rv.outPk[i].mask, rv.rangeSigs[i])) { + for (i = 0; i < outPk.size(); i++) { + if (!verRange(outPk[i].mask, rv.rangeSigs[i])) { return false; } - addKeys(sumOutpks, sumOutpks, rv.outPk[i].mask); + addKeys(sumOutpks, sumOutpks, outPk[i].mask); } DP(sumOutpks); key txnFeeKey = scalarmultH(d2h(rv.txnFee)); @@ -769,7 +769,7 @@ namespace rct { } bool verRctSimple(const rctSig & rv) { - return verRctSimple(rv, rv.mixRing, NULL, rv.message); + return verRctSimple(rv, rv.mixRing, NULL, rv.outPk, rv.message); } //RingCT protocol diff --git a/src/ringct/rctSigs.h b/src/ringct/rctSigs.h index 57f852d68..2064962c3 100644 --- a/src/ringct/rctSigs.h +++ b/src/ringct/rctSigs.h @@ -140,9 +140,9 @@ namespace rct { rctSig genRctSimple(const key & message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector & inamounts, const vector & outamounts, xmr_amount txnFee, unsigned int mixin); rctSig genRctSimple(const key & message, const ctkeyV & inSk, const keyV & destinations, const vector & inamounts, const vector & outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const std::vector & index, ctkeyV &outSk); bool verRct(const rctSig & rv); - bool verRct(const rctSig & rv, const ctkeyM &mixRing, const keyV &II, const key &message); + bool verRct(const rctSig & rv, const ctkeyM &mixRing, const keyV &II, const ctkeyV &outPk, const key &message); bool verRctSimple(const rctSig & rv); - bool verRctSimple(const rctSig & rv, const ctkeyM &mixRing, const std::vector *II, const key &message); + bool verRctSimple(const rctSig & rv, const ctkeyM &mixRing, const std::vector *II, const ctkeyV &outPk, const key &message); xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, key & mask); xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i); xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i); diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h index a376980fd..7e31f679d 100644 --- a/src/ringct/rctTypes.h +++ b/src/ringct/rctTypes.h @@ -207,7 +207,19 @@ namespace rct { if (simple) FIELD(pseudoOuts) FIELD(ecdhInfo) - FIELD(outPk) + if (typename Archive::is_saving()) { + keyV outPk(this->outPk.size()); + for (size_t n = 0; n < outPk.size(); ++n) + outPk[n] = this->outPk[n].mask; + FIELD(outPk) + } + else { + keyV outPk; + FIELD(outPk) + this->outPk.resize(outPk.size()); + for (size_t n = 0; n < outPk.size(); ++n) + this->outPk[n].mask = outPk[n]; + } FIELD(txnFee) END_SERIALIZE() }; diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 440aec42c..70f9043f0 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -3122,12 +3122,12 @@ static size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs) size += 32 * n_outputs; // ecdhInfo size += 3 * 32 * n_outputs; - // outPk - size += 2 * 32 * n_outputs; + // outPk - only commitment is saved + size += 1 * 32 * n_outputs; // txnFee size += 4; - LOG_PRINT_L2("estimated rct tx size for " << n_inputs << " at mixin " << mixin << " and " << n_outputs << ": " << size << " (" << (32 * n_inputs + 2 * 32 * (mixin+1) * n_inputs) << " saved)"); + LOG_PRINT_L2("estimated rct tx size for " << n_inputs << " at mixin " << mixin << " and " << n_outputs << ": " << size << " (" << ((32 * n_inputs/*+1*/) + 2 * 32 * (mixin+1) * n_inputs + 32 * n_outputs) << " saved)"); return size; } diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp index 88e67bd73..380c2140f 100644 --- a/tests/unit_tests/serialization.cpp +++ b/tests/unit_tests/serialization.cpp @@ -615,7 +615,8 @@ TEST(Serialization, serializes_ringct_types) ASSERT_TRUE(s0.outPk.size() == s1.outPk.size()); for (size_t n = 0; n < s0.outPk.size(); ++n) { - ASSERT_TRUE(!memcmp(&s0.outPk[n], &s1.outPk[n], sizeof(s0.outPk[n]))); + // serialization only does the mask + ASSERT_TRUE(!memcmp(&s0.outPk[n].mask, &s1.outPk[n].mask, sizeof(s0.outPk[n].mask))); } tx0.set_null();