rct: do not serialize public keys in outPk

They can be reconstructed from vout
This commit is contained in:
moneromooo-monero 2016-07-23 12:09:33 +01:00
parent 83ab3151e8
commit cf33e1a52a
No known key found for this signature in database
GPG key ID: 686F07454D6CEFC3
8 changed files with 87 additions and 34 deletions

View file

@ -2468,11 +2468,12 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context
{ {
rct::ctkeyM reconstructed_mixRing; rct::ctkeyM reconstructed_mixRing;
std::vector<rct::keyV> reconstructed_II; std::vector<rct::keyV> reconstructed_II;
rct::ctkeyV reconstructed_outPk;
// if the tx already has a non empty mixRing, use them, // if the tx already has a non empty mixRing, use them,
// else reconstruct them // else reconstruct them
const rct::ctkeyM &mixRing = tx.rct_signatures.mixRing.empty() ? reconstructed_mixRing : tx.rct_signatures.mixRing; 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) // all MGs should have the same II size (1)
for (size_t n = 0; n < tx.rct_signatures.MGs.size(); ++n) 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]); 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<txout_to_key>(tx.vout[n].target).key);
reconstructed_outPk[n].mask = tx.rct_signatures.outPk[n].mask;
}
if (tx.rct_signatures.mixRing.empty()) if (tx.rct_signatures.mixRing.empty())
{ {
reconstructed_mixRing.resize(pubkeys.size()); 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!"); LOG_PRINT_L1("Failed to check ringct signatures!");
return false; return false;
@ -2561,11 +2574,13 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context
{ {
rct::ctkeyM reconstructed_mixRing; rct::ctkeyM reconstructed_mixRing;
rct::keyV reconstructed_II; rct::keyV reconstructed_II;
rct::ctkeyV reconstructed_outPk;
// if the tx already has a non empty mixRing and/or II, use them, // 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::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::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 // RCT needs the same mixin for all inputs
for (size_t n = 1; n < pubkeys.size(); ++n) 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()); 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<txout_to_key>(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 // check all this, either recontructed (so should really pass), or not
{ {
bool size_matches = true; 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!"); LOG_PRINT_L1("Failed to check ringct signatures!");
return false; return false;

View file

@ -43,6 +43,7 @@
#include "common/unordered_containers_boost_serialization.h" #include "common/unordered_containers_boost_serialization.h"
#include "crypto/crypto.h" #include "crypto/crypto.h"
#include "ringct/rctTypes.h" #include "ringct/rctTypes.h"
#include "ringct/rctOps.h"
//namespace cryptonote { //namespace cryptonote {
namespace boost namespace boost
@ -221,6 +222,26 @@ namespace boost
a & x.senderPk; 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 <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)
{ {
@ -235,7 +256,7 @@ namespace boost
if (x.simple) if (x.simple)
a & x.pseudoOuts; a & x.pseudoOuts;
a & x.ecdhInfo; a & x.ecdhInfo;
a & x.outPk; serializeOutPk(a, x.outPk, ver);
a & x.txnFee; a & x.txnFee;
} }
} }

View file

@ -569,14 +569,6 @@ namespace cryptonote
LOG_PRINT_RED_L1("tx with mismatched vout/outPk count, rejected for tx id= " << get_transaction_hash(tx)); LOG_PRINT_RED_L1("tx with mismatched vout/outPk count, rejected for tx id= " << get_transaction_hash(tx));
return false; return false;
} }
for (size_t n = 0; n < tx.vout.size(); ++n)
{
if (tx.rct_signatures.outPk[n].dest != boost::get<txout_to_key>(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)) if(!check_money_overflow(tx))

View file

@ -679,10 +679,10 @@ namespace rct {
//decodeRct: (c.f. http://eprint.iacr.org/2015/1098 section 5.1.1) //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 // 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 // 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.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(outPk.size() == rv.rangeSigs.size(), false, "Mismatched sizes of 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.ecdhInfo.size(), false, "Mismatched sizes of outPk and rv.ecdhInfo");
// some rct ops can throw // some rct ops can throw
try try
@ -691,14 +691,14 @@ namespace rct {
bool rvb = true; bool rvb = true;
bool tmp; bool tmp;
DP("range proofs verified?"); DP("range proofs verified?");
for (i = 0; i < rv.outPk.size(); i++) { for (i = 0; i < outPk.size(); i++) {
tmp = verRange(rv.outPk[i].mask, rv.rangeSigs[i]); tmp = verRange(outPk[i].mask, rv.rangeSigs[i]);
DP(tmp); DP(tmp);
rvb = (rvb && tmp); rvb = (rvb && tmp);
} }
//compute txn fee //compute txn fee
key txnFeeKey = scalarmultH(d2h(rv.txnFee)); 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("mg sig verified?");
DP(mgVerd); DP(mgVerd);
@ -710,18 +710,18 @@ namespace rct {
} }
} }
bool verRct(const rctSig & rv) { 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 //ver RingCT simple
//assumes only post-rct style inputs (at least for max anonymity) //assumes only post-rct style inputs (at least for max anonymity)
bool verRctSimple(const rctSig & rv, const ctkeyM &mixRing, const std::vector<keyV> *II, const key &message) { bool verRctSimple(const rctSig & rv, const ctkeyM &mixRing, const std::vector<keyV> *II, const ctkeyV &outPk, const key &message) {
size_t i = 0; size_t i = 0;
bool rvb = true; bool rvb = true;
CHECK_AND_ASSERT_MES(rv.simple, false, "verRctSimple called on non simple rctSig"); 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(outPk.size() == rv.rangeSigs.size(), false, "Mismatched sizes of 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.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() == 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(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"); CHECK_AND_ASSERT_MES(!II || II->size() == mixRing.size(), false, "Mismatched II/mixRing size");
@ -734,11 +734,11 @@ namespace rct {
} }
key sumOutpks = identity(); key sumOutpks = identity();
for (i = 0; i < rv.outPk.size(); i++) { for (i = 0; i < outPk.size(); i++) {
if (!verRange(rv.outPk[i].mask, rv.rangeSigs[i])) { if (!verRange(outPk[i].mask, rv.rangeSigs[i])) {
return false; return false;
} }
addKeys(sumOutpks, sumOutpks, rv.outPk[i].mask); addKeys(sumOutpks, sumOutpks, outPk[i].mask);
} }
DP(sumOutpks); DP(sumOutpks);
key txnFeeKey = scalarmultH(d2h(rv.txnFee)); key txnFeeKey = scalarmultH(d2h(rv.txnFee));
@ -769,7 +769,7 @@ namespace rct {
} }
bool verRctSimple(const rctSig & rv) { 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 //RingCT protocol

View file

@ -140,9 +140,9 @@ namespace rct {
rctSig genRctSimple(const key & message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector<xmr_amount> & inamounts, const vector<xmr_amount> & outamounts, xmr_amount txnFee, unsigned int mixin); rctSig genRctSimple(const key & message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector<xmr_amount> & inamounts, const vector<xmr_amount> & outamounts, xmr_amount txnFee, unsigned int mixin);
rctSig genRctSimple(const key & message, const ctkeyV & inSk, const keyV & destinations, const vector<xmr_amount> & inamounts, const vector<xmr_amount> & outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const std::vector<unsigned int> & index, ctkeyV &outSk); rctSig genRctSimple(const key & message, const ctkeyV & inSk, const keyV & destinations, const vector<xmr_amount> & inamounts, const vector<xmr_amount> & outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const std::vector<unsigned int> & index, ctkeyV &outSk);
bool verRct(const rctSig & rv); 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);
bool verRctSimple(const rctSig & rv, const ctkeyM &mixRing, const std::vector<keyV> *II, const key &message); bool verRctSimple(const rctSig & rv, const ctkeyM &mixRing, const std::vector<keyV> *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, key & mask);
xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i); xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i);
xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i); xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i);

View file

@ -207,7 +207,19 @@ namespace rct {
if (simple) if (simple)
FIELD(pseudoOuts) FIELD(pseudoOuts)
FIELD(ecdhInfo) FIELD(ecdhInfo)
if (typename Archive<W>::is_saving()) {
keyV outPk(this->outPk.size());
for (size_t n = 0; n < outPk.size(); ++n)
outPk[n] = this->outPk[n].mask;
FIELD(outPk) 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) FIELD(txnFee)
END_SERIALIZE() END_SERIALIZE()
}; };

View file

@ -3122,12 +3122,12 @@ static size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs)
size += 32 * n_outputs; size += 32 * n_outputs;
// ecdhInfo // ecdhInfo
size += 3 * 32 * n_outputs; size += 3 * 32 * n_outputs;
// outPk // outPk - only commitment is saved
size += 2 * 32 * n_outputs; size += 1 * 32 * n_outputs;
// txnFee // txnFee
size += 4; 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; return size;
} }

View file

@ -615,7 +615,8 @@ TEST(Serialization, serializes_ringct_types)
ASSERT_TRUE(s0.outPk.size() == s1.outPk.size()); ASSERT_TRUE(s0.outPk.size() == s1.outPk.size());
for (size_t n = 0; n < s0.outPk.size(); ++n) 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(); tx0.set_null();