From cc7f449d57a490d0f3f24cf9584ff280063939a2 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 14 Jun 2016 17:25:00 +0100 Subject: make rct tx serialization work It may be suboptimal, but it's a pain to have to rebuild everything when some of this changes. Also, no clue why there seems to be two different code paths for serializing a tx... --- tests/unit_tests/serialization.cpp | 201 +++++++++++++++++++++++++++++++++++++ 1 file changed, 201 insertions(+) (limited to 'tests/unit_tests/serialization.cpp') diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp index b110a41ab..55a33c83f 100644 --- a/tests/unit_tests/serialization.cpp +++ b/tests/unit_tests/serialization.cpp @@ -36,6 +36,7 @@ #include #include "cryptonote_core/cryptonote_basic.h" #include "cryptonote_core/cryptonote_basic_impl.h" +#include "ringct/rctSigs.h" #include "serialization/serialization.h" #include "serialization/binary_archive.h" #include "serialization/json_archive.h" @@ -442,3 +443,203 @@ TEST(Serialization, serializes_transacion_signatures_correctly) blob.resize(blob.size() + sizeof(crypto::signature) / 2); ASSERT_FALSE(serialization::parse_binary(blob, tx1)); } + +TEST(Serialization, serializes_ringct_types) +{ + string blob; + rct::key key0, key1; + rct::keyV keyv0, keyv1; + rct::keyM keym0, keym1; + rct::ctkey ctkey0, ctkey1; + rct::ctkeyV ctkeyv0, ctkeyv1; + rct::ctkeyM ctkeym0, ctkeym1; + rct::ecdhTuple ecdh0, ecdh1; + rct::asnlSig asnl0, asnl1; + rct::mgSig mg0, mg1; + rct::rangeSig rg0, rg1; + rct::rctSig s0, s1; + cryptonote::transaction tx0, tx1; + + key0 = rct::skGen(); + ASSERT_TRUE(serialization::dump_binary(key0, blob)); + ASSERT_TRUE(serialization::parse_binary(blob, key1)); + ASSERT_TRUE(key0 == key1); + + keyv0 = rct::skvGen(30); + for (size_t n = 0; n < keyv0.size(); ++n) + keyv0[n] = rct::skGen(); + ASSERT_TRUE(serialization::dump_binary(keyv0, blob)); + ASSERT_TRUE(serialization::parse_binary(blob, keyv1)); + ASSERT_TRUE(keyv0.size() == keyv1.size()); + for (size_t n = 0; n < keyv0.size(); ++n) + { + ASSERT_TRUE(keyv0[n] == keyv1[n]); + } + + keym0 = rct::keyMInit(9, 12); + for (size_t n = 0; n < keym0.size(); ++n) + for (size_t i = 0; i < keym0[n].size(); ++i) + keym0[n][i] = rct::skGen(); + ASSERT_TRUE(serialization::dump_binary(keym0, blob)); + ASSERT_TRUE(serialization::parse_binary(blob, keym1)); + ASSERT_TRUE(keym0.size() == keym1.size()); + for (size_t n = 0; n < keym0.size(); ++n) + { + ASSERT_TRUE(keym0[n].size() == keym1[n].size()); + for (size_t i = 0; i < keym0[n].size(); ++i) + { + ASSERT_TRUE(keym0[n][i] == keym1[n][i]); + } + } + + rct::skpkGen(ctkey0.dest, ctkey0.mask); + ASSERT_TRUE(serialization::dump_binary(ctkey0, blob)); + ASSERT_TRUE(serialization::parse_binary(blob, ctkey1)); + ASSERT_TRUE(!memcmp(&ctkey0, &ctkey1, sizeof(ctkey0))); + + ctkeyv0 = std::vector(14); + for (size_t n = 0; n < ctkeyv0.size(); ++n) + rct::skpkGen(ctkeyv0[n].dest, ctkeyv0[n].mask); + ASSERT_TRUE(serialization::dump_binary(ctkeyv0, blob)); + ASSERT_TRUE(serialization::parse_binary(blob, ctkeyv1)); + ASSERT_TRUE(ctkeyv0.size() == ctkeyv1.size()); + for (size_t n = 0; n < ctkeyv0.size(); ++n) + { + ASSERT_TRUE(!memcmp(&ctkeyv0[n], &ctkeyv1[n], sizeof(ctkeyv0[n]))); + } + + ctkeym0 = std::vector(9); + for (size_t n = 0; n < ctkeym0.size(); ++n) + { + ctkeym0[n] = std::vector(11); + for (size_t i = 0; i < ctkeym0[n].size(); ++i) + rct::skpkGen(ctkeym0[n][i].dest, ctkeym0[n][i].mask); + } + ASSERT_TRUE(serialization::dump_binary(ctkeym0, blob)); + ASSERT_TRUE(serialization::parse_binary(blob, ctkeym1)); + ASSERT_TRUE(ctkeym0.size() == ctkeym1.size()); + for (size_t n = 0; n < ctkeym0.size(); ++n) + { + ASSERT_TRUE(ctkeym0[n].size() == ctkeym1[n].size()); + for (size_t i = 0; i < ctkeym0.size(); ++i) + { + ASSERT_TRUE(!memcmp(&ctkeym0[n][i], &ctkeym1[n][i], sizeof(ctkeym0[n][i]))); + } + } + + ecdh0.mask = rct::skGen(); + ecdh0.amount = rct::skGen(); + ecdh0.senderPk = rct::skGen(); + ASSERT_TRUE(serialization::dump_binary(ecdh0, blob)); + ASSERT_TRUE(serialization::parse_binary(blob, ecdh1)); + ASSERT_TRUE(!memcmp(&ecdh0, &ecdh1, sizeof(ecdh0))); + + for (size_t n = 0; n < 64; ++n) + { + asnl0.L1[n] = rct::skGen(); + asnl0.s2[n] = rct::skGen(); + } + asnl0.s = rct::skGen(); + ASSERT_TRUE(serialization::dump_binary(asnl0, blob)); + ASSERT_TRUE(serialization::parse_binary(blob, asnl1)); + ASSERT_TRUE(!memcmp(&asnl0, &asnl1, sizeof(asnl0))); + + // create a full rct signature to use its innards + rct::ctkeyV sc, pc; + rct::ctkey sctmp, pctmp; + tie(sctmp, pctmp) = rct::ctskpkGen(6000); + sc.push_back(sctmp); + pc.push_back(pctmp); + tie(sctmp, pctmp) = rct::ctskpkGen(7000); + sc.push_back(sctmp); + pc.push_back(pctmp); + vector amounts; + //add output 500 + amounts.push_back(500); + rct::keyV destinations; + rct::key Sk, Pk; + rct::skpkGen(Sk, Pk); + destinations.push_back(Pk); + //add output for 12500 + amounts.push_back(12500); + rct::skpkGen(Sk, Pk); + destinations.push_back(Pk); + //compute rct data with mixin 500 + s0 = rct::genRct(sc, pc, destinations, amounts, 3); + + mg0 = s0.MG; + ASSERT_TRUE(serialization::dump_binary(mg0, blob)); + ASSERT_TRUE(serialization::parse_binary(blob, mg1)); + ASSERT_TRUE(mg0.ss.size() == mg1.ss.size()); + for (size_t n = 0; n < mg0.ss.size(); ++n) + { + ASSERT_TRUE(mg0.ss[n] == mg1.ss[n]); + } + ASSERT_TRUE(mg0.cc == mg1.cc); + ASSERT_TRUE(mg0.II.size() == mg1.II.size()); + for (size_t n = 0; n < mg0.II.size(); ++n) + { + ASSERT_TRUE(mg0.II[n] == mg1.II[n]); + } + + rg0 = s0.rangeSigs.front(); + ASSERT_TRUE(serialization::dump_binary(rg0, blob)); + ASSERT_TRUE(serialization::parse_binary(blob, rg1)); + ASSERT_TRUE(!memcmp(&rg0, &rg1, sizeof(rg0))); + + ASSERT_TRUE(serialization::dump_binary(s0, blob)); + ASSERT_TRUE(serialization::parse_binary(blob, s1)); + ASSERT_TRUE(s0.rangeSigs.size() == s1.rangeSigs.size()); + for (size_t n = 0; n < s0.rangeSigs.size(); ++n) + { + ASSERT_TRUE(!memcmp(&s0.rangeSigs[n], &s1.rangeSigs[n], sizeof(s0.rangeSigs[n]))); + } + ASSERT_TRUE(s0.MG.ss.size() == s1.MG.ss.size()); + for (size_t n = 0; n < s0.MG.ss.size(); ++n) + { + ASSERT_TRUE(s0.MG.ss[n] == s1.MG.ss[n]); + } + ASSERT_TRUE(s0.MG.cc == s1.MG.cc); + ASSERT_TRUE(s0.MG.II.size() == s1.MG.II.size()); + for (size_t n = 0; n < s0.MG.II.size(); ++n) + { + ASSERT_TRUE(s0.MG.II[n] == s1.MG.II[n]); + } + ASSERT_TRUE(s0.mixRing.size() == s1.mixRing.size()); + for (size_t n = 0; n < s0.mixRing.size(); ++n) + { + ASSERT_TRUE(s0.mixRing[n].size() == s1.mixRing[n].size()); + for (size_t i = 0; i < s0.mixRing[n].size(); ++i) + { + ASSERT_TRUE(!memcmp(&s0.mixRing[n][i], &s1.mixRing[n][i], sizeof(s0.mixRing[n][i]))); + } + } + ASSERT_TRUE(s0.ecdhInfo.size() == s1.ecdhInfo.size()); + for (size_t n = 0; n < s0.ecdhInfo.size(); ++n) + { + ASSERT_TRUE(!memcmp(&s0.ecdhInfo[n], &s1.ecdhInfo[n], sizeof(s0.ecdhInfo[n]))); + } + 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]))); + } + + tx0.set_null(); + tx0.version = 2; + cryptonote::txin_to_key txin_to_key1; + txin_to_key1.key_offsets.resize(2); + cryptonote::txin_to_key txin_to_key2; + txin_to_key2.key_offsets.resize(2); + tx0.vin.push_back(txin_to_key1); + tx0.vin.push_back(txin_to_key2); + tx0.vout.push_back(cryptonote::tx_out()); + tx0.rct_signatures = s0; + ASSERT_EQ(tx0.rct_signatures.rangeSigs.size(), 2); + ASSERT_TRUE(serialization::dump_binary(tx0, blob)); + ASSERT_TRUE(serialization::parse_binary(blob, tx1)); + ASSERT_EQ(tx1.rct_signatures.rangeSigs.size(), 2); + std::string blob2; + ASSERT_TRUE(serialization::dump_binary(tx1, blob2)); + ASSERT_TRUE(blob == blob2); +} -- cgit v1.2.3 From 20e50ec7f7f4104b5eb177aa30df4f5d4db9c8c3 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Wed, 29 Jun 2016 18:18:18 +0100 Subject: ringct: do not serialize what can be reconstructed The mixRing (output keys and commitments) and II fields (key images) can be reconstructed from vin data. This saves some modest amount of space in the tx. --- src/cryptonote_core/blockchain.cpp | 87 +++++++++++++++++----- .../cryptonote_boost_serialization.h | 18 ++++- src/ringct/rctSigs.cpp | 36 +++++---- src/ringct/rctSigs.h | 11 +-- src/ringct/rctTypes.h | 17 ++++- src/wallet/wallet2.cpp | 13 ++-- tests/unit_tests/ringct.cpp | 4 +- tests/unit_tests/serialization.cpp | 30 +++----- 8 files changed, 145 insertions(+), 71 deletions(-) (limited to 'tests/unit_tests/serialization.cpp') diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 0d7294ffd..82bc6c0d4 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -2461,6 +2461,13 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context else { // from version 2, check ringct signatures + rct::ctkeyM reconstructed_mixRing; + rct::keyV reconstructed_II; + + // if the tx already has a non empty mixRing and/or II, use them, + // else reconstruct them + 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; // RCT needs the same mixin for all inputs for (size_t n = 1; n < pubkeys.size(); ++n) @@ -2472,35 +2479,74 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context } } - bool size_matches = true; - for (size_t i = 0; i < pubkeys.size(); ++i) - size_matches &= pubkeys[i].size() == tx.rct_signatures.mixRing.size(); - for (size_t i = 0; i < tx.rct_signatures.mixRing.size(); ++i) - size_matches &= pubkeys.size() == tx.rct_signatures.mixRing[i].size(); - if (!size_matches) + if (tx.rct_signatures.mixRing.empty()) { - LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkeys/mixRing size"); - return false; + reconstructed_mixRing.resize(pubkeys[0].size()); + for (size_t n = 0; n < pubkeys.size(); ++n) + { + for (size_t m = 0; m < pubkeys[n].size(); ++m) + { + reconstructed_mixRing[m].push_back(pubkeys[n][m]); + } + } } - for (size_t n = 0; n < pubkeys.size(); ++n) + if (tx.rct_signatures.MG.II.size() == 1) { - for (size_t m = 0; m < pubkeys[n].size(); ++m) + reconstructed_II.resize(tx.vin.size()); + for (size_t n = 0; n < tx.vin.size(); ++n) { - if (pubkeys[n][m].dest != rct::rct2pk(tx.rct_signatures.mixRing[m][n].dest)) - { - LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkey at vin " << n << ", index " << m); - return false; - } - if (pubkeys[n][m].mask != rct::rct2pk(tx.rct_signatures.mixRing[m][n].mask)) + reconstructed_II[n] = rct::ki2rct(boost::get(tx.vin[n]).k_image); + } + reconstructed_II.push_back(tx.rct_signatures.MG.II.back()); + } + + // check all this, either recontructed (so should really pass), or not + { + bool size_matches = true; + for (size_t i = 0; i < pubkeys.size(); ++i) + size_matches &= pubkeys[i].size() == mixRing.size(); + for (size_t i = 0; i < tx.rct_signatures.mixRing.size(); ++i) + size_matches &= pubkeys.size() == mixRing[i].size(); + if (!size_matches) + { + LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkeys/mixRing size"); + return false; + } + + for (size_t n = 0; n < pubkeys.size(); ++n) + { + for (size_t m = 0; m < pubkeys[n].size(); ++m) { - LOG_PRINT_L1("Failed to check ringct signatures: mismatched commitment at vin " << n << ", index " << m); - return false; + if (pubkeys[n][m].dest != rct::rct2pk(mixRing[m][n].dest)) + { + LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkey at vin " << n << ", index " << m); + return false; + } + if (pubkeys[n][m].mask != rct::rct2pk(mixRing[m][n].mask)) + { + LOG_PRINT_L1("Failed to check ringct signatures: mismatched commitment at vin " << n << ", index " << m); + return false; + } } } } - if (!rct::verRct(tx.rct_signatures)) + if (II.size() != 1 + tx.vin.size()) + { + LOG_PRINT_L1("Failed to check ringct signatures: mismatched II/vin sizes"); + return false; + } + for (size_t n = 0; n < tx.vin.size(); ++n) + { + if (memcmp(&boost::get(tx.vin[n]).k_image, &II[n], 32)) + { + LOG_PRINT_L1("Failed to check ringct signatures: mismatched II/vin sizes"); + return false; + } + } + + if (!rct::verRct(tx.rct_signatures, mixRing, II, rct::hash2rct(tx_prefix_hash))) { LOG_PRINT_L1("Failed to check ringct signatures!"); return false; @@ -2613,7 +2659,8 @@ bool Blockchain::check_tx_input(size_t tx_version, const txin_to_key& txin, cons } else { - CHECK_AND_ASSERT_MES(rct_signatures.mixRing.size() == output_keys.size(), false, "internal error: tx rct signatures count=" << sig.size() << " mismatch with outputs keys count for inputs=" << output_keys.size()); + // rct signatures may be empty (and will be reconstructed later in the caller if so) + CHECK_AND_ASSERT_MES(rct_signatures.mixRing.empty() || rct_signatures.mixRing.size() == output_keys.size(), false, "internal error: tx rct signatures count=" << sig.size() << " mismatch with outputs keys count for inputs=" << output_keys.size()); } return true; } diff --git a/src/cryptonote_core/cryptonote_boost_serialization.h b/src/cryptonote_core/cryptonote_boost_serialization.h index 6eeb66ec8..ccdd55883 100644 --- a/src/cryptonote_core/cryptonote_boost_serialization.h +++ b/src/cryptonote_core/cryptonote_boost_serialization.h @@ -37,6 +37,8 @@ #include #include #include +#include +#include #include "cryptonote_basic.h" #include "common/unordered_containers_boost_serialization.h" #include "crypto/crypto.h" @@ -196,12 +198,19 @@ namespace boost a & x.s; } - template - inline void serialize(Archive &a, rct::mgSig &x, const boost::serialization::version_type ver) + inline void serialize(boost::archive::binary_iarchive &a, rct::mgSig &x, const boost::serialization::version_type ver) + { + a & x.ss; + a & x.cc; + x.II.resize(1); + a & x.II[0]; + } + + inline void serialize(boost::archive::binary_oarchive &a, rct::mgSig &x, const boost::serialization::version_type ver) { a & x.ss; a & x.cc; - a & x.II; + a & x.II.back(); } template @@ -217,10 +226,11 @@ namespace boost { a & x.rangeSigs; a & x.MG; - a & x.mixRing; + // a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets a & x.ecdhInfo; a & x.outPk; a & x.txnFee; + // a & x.bash_hash; bash_hash is not serialized, as it can be reconstructed from the tx data } } } diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index 89adb2a77..917ac54cd 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -226,7 +226,7 @@ namespace rct { // Gen creates a signature which proves that for some column in the keymatrix "pk" // the signer knows a secret key for each row in that column // Ver verifies that the MG sig was created correctly - bool MLSAG_Ver(key message, const keyM & pk, const mgSig & rv) { + bool MLSAG_Ver(key message, const keyM & pk, const mgSig & rv, const keyV &II) { size_t cols = pk.size(); CHECK_AND_ASSERT_MES(cols >= 2, false, "Error! What is c if cols = 1!"); @@ -235,7 +235,7 @@ namespace rct { for (size_t i = 1; i < cols; ++i) { CHECK_AND_ASSERT_MES(pk[i].size() == rows, false, "pk is not rectangular"); } - CHECK_AND_ASSERT_MES(rv.II.size() == rows, false, "Bad rv.II size"); + CHECK_AND_ASSERT_MES(II.size() == rows, false, "Bad II size"); CHECK_AND_ASSERT_MES(rv.ss.size() == cols, false, "Bad rv.ss size"); for (size_t i = 0; i < cols; ++i) { CHECK_AND_ASSERT_MES(rv.ss[i].size() == rows, false, "rv.ss is not rectangular"); @@ -246,7 +246,7 @@ namespace rct { key c_old = copy(rv.cc); vector Ip(rows); for (i= 0 ; i< rows ; i++) { - precomp(Ip[i].k, rv.II[i]); + precomp(Ip[i].k, II[i]); } unsigned char m2[128]; memcpy(m2, message.bytes, 32); @@ -334,7 +334,7 @@ namespace rct { // this shows that sum inputs = sum outputs //Ver: // verifies the above sig is created corretly - mgSig proveRctMG(const ctkeyM & pubs, const ctkeyV & inSk, const ctkeyV &outSk, const ctkeyV & outPk, unsigned int index, key txnFeeKey) { + mgSig proveRctMG(const ctkeyM & pubs, const ctkeyV & inSk, const ctkeyV &outSk, const ctkeyV & outPk, unsigned int index, key txnFeeKey, const key &base_hash) { mgSig mg; //setup vars size_t cols = pubs.size(); @@ -378,7 +378,9 @@ namespace rct { for (size_t j = 0; j < outPk.size(); j++) { sc_sub(sk[rows].bytes, sk[rows].bytes, outSk[j].mask.bytes); //subtract output masks in last row.. } - key message = cn_fast_hash(outPk); + ctkeyV signed_data = outPk; + signed_data.push_back(ctkey({base_hash, identity()})); + key message = cn_fast_hash(signed_data); return MLSAG_Gen(message, M, sk, index); } @@ -391,7 +393,7 @@ namespace rct { // this shows that sum inputs = sum outputs //Ver: // verifies the above sig is created corretly - bool verRctMG(mgSig mg, const ctkeyM & pubs, const ctkeyV & outPk, key txnFeeKey) { + bool verRctMG(mgSig mg, const keyV &II, const ctkeyM & pubs, const ctkeyV & outPk, key txnFeeKey, const key &base_hash) { //setup vars size_t cols = pubs.size(); CHECK_AND_ASSERT_MES(cols >= 1, false, "Empty pubs"); @@ -422,10 +424,12 @@ namespace rct { //subtract txn fee output in last row subKeys(M[i][rows], M[i][rows], txnFeeKey); } - key message = cn_fast_hash(outPk); + ctkeyV signed_data = outPk; + signed_data.push_back(ctkey({base_hash, identity()})); + key message = cn_fast_hash(signed_data); DP("message:"); DP(message); - return MLSAG_Ver(message, M, mg); + return MLSAG_Ver(message, M, mg, II); } //These functions get keys from blockchain @@ -470,7 +474,7 @@ namespace rct { // must know the destination private key to find the correct amount, else will return a random number // Note: For txn fees, the last index in the amounts vector should contain that // Thus the amounts vector will be "one" longer than the destinations vectort - rctSig genRct(const ctkeyV & inSk, const keyV & destinations, const vector amounts, const ctkeyM &mixRing, unsigned int index) { + rctSig genRct(const ctkeyV & inSk, const keyV & destinations, const vector amounts, const ctkeyM &mixRing, const key &base_hash, unsigned int index) { CHECK_AND_ASSERT_THROW_MES(amounts.size() == destinations.size() || amounts.size() == destinations.size() + 1, "Different number of amounts/destinations"); CHECK_AND_ASSERT_THROW_MES(index < mixRing.size(), "Bad index into mixRing"); for (size_t n = 0; n < mixRing.size(); ++n) { @@ -513,15 +517,16 @@ namespace rct { key txnFeeKey = scalarmultH(d2h(rv.txnFee)); rv.mixRing = mixRing; - rv.MG = proveRctMG(rv.mixRing, inSk, outSk, rv.outPk, index, txnFeeKey); + rv.base_hash = base_hash; + rv.MG = proveRctMG(rv.mixRing, inSk, outSk, rv.outPk, index, txnFeeKey, base_hash); return rv; } - rctSig genRct(const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector amounts, const int mixin) { + rctSig genRct(const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector amounts, const key &base_hash, const int mixin) { unsigned int index; ctkeyM mixRing; tie(mixRing, index) = populateFromBlockchain(inPk, mixin); - return genRct(inSk, destinations, amounts, mixRing, index); + return genRct(inSk, destinations, amounts, mixRing, base_hash, index); } //RingCT protocol @@ -534,7 +539,7 @@ 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) { + bool verRct(const rctSig & rv, const ctkeyM &mixRing, const keyV &II, const key &base_hash) { 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"); @@ -552,7 +557,7 @@ namespace rct { } //compute txn fee key txnFeeKey = scalarmultH(d2h(rv.txnFee)); - bool mgVerd = verRctMG(rv.MG, rv.mixRing, rv.outPk, txnFeeKey); + bool mgVerd = verRctMG(rv.MG, II, mixRing, rv.outPk, txnFeeKey, base_hash); DP("mg sig verified?"); DP(mgVerd); @@ -563,6 +568,9 @@ namespace rct { return false; } } + bool verRct(const rctSig & rv) { + return verRct(rv, rv.mixRing, rv.MG.II, rv.base_hash); + } //RingCT protocol //genRct: diff --git a/src/ringct/rctSigs.h b/src/ringct/rctSigs.h index d5c036910..f87312fa8 100644 --- a/src/ringct/rctSigs.h +++ b/src/ringct/rctSigs.h @@ -91,7 +91,7 @@ namespace rct { // Ver verifies that the MG sig was created correctly keyV keyImageV(const keyV &xx); mgSig MLSAG_Gen(key message, const keyM & pk, const keyV & xx, const unsigned int index); - bool MLSAG_Ver(key message, const keyM &pk, const mgSig &sig); + bool MLSAG_Ver(key message, const keyM &pk, const mgSig &sig, const keyV &II); //mgSig MLSAG_Gen_Old(const keyM & pk, const keyV & xx, const int index); //proveRange and verRange @@ -112,8 +112,8 @@ namespace rct { // this shows that sum inputs = sum outputs //Ver: // verifies the above sig is created corretly - mgSig proveRctMG(const ctkeyM & pubs, const ctkeyV & inSk, const keyV &outMasks, const ctkeyV & outPk, unsigned int index, key txnFee); - bool verRctMG(mgSig mg, const ctkeyM & pubs, const ctkeyV & outPk, key txnFee); + mgSig proveRctMG(const ctkeyM & pubs, const ctkeyV & inSk, const keyV &outMasks, const ctkeyV & outPk, unsigned int index, key txnFee, const key &base_hash); + bool verRctMG(mgSig mg, const ctkeyM & pubs, const ctkeyV & outPk, key txnFee, const key &base_hash); //These functions get keys from blockchain //replace these when connecting blockchain @@ -133,9 +133,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 - rctSig genRct(const ctkeyV & inSk, const keyV & destinations, const vector amounts, const ctkeyM &mixRing, unsigned int index); - rctSig genRct(const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector amounts, const int mixin); + rctSig genRct(const ctkeyV & inSk, const keyV & destinations, const vector amounts, const ctkeyM &mixRing, const key &bash_hash, unsigned int index); + rctSig genRct(const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector amounts, const key &bash_hash, const int mixin); bool verRct(const rctSig & rv); + bool verRct(const rctSig & rv, const ctkeyM &mixRing, const keyV &II, const key &base_hash); 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); diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h index 13e9fb6f2..2dc4718db 100644 --- a/src/ringct/rctTypes.h +++ b/src/ringct/rctTypes.h @@ -148,7 +148,16 @@ namespace rct { BEGIN_SERIALIZE_OBJECT() FIELD(ss) FIELD(cc) - FIELD(II) + if (II.size() == 0) { + // loading + FIELD(II) + } + else { + // saving + keyV II; + II.push_back(this->II.back()); + FIELD(II) + } END_SERIALIZE() }; //contains the data for an asnl sig @@ -181,14 +190,16 @@ namespace rct { vector ecdhInfo; ctkeyV outPk; xmr_amount txnFee; + key base_hash; BEGIN_SERIALIZE_OBJECT() FIELD(rangeSigs) FIELD(MG) - FIELD(mixRing) + // FIELD(mixRing) - not serialized, it can be reconstructed FIELD(ecdhInfo) FIELD(outPk) FIELD(txnFee) + // FIELD(base_hash) - not serialized, it can be reconstructed END_SERIALIZE() }; @@ -296,9 +307,11 @@ namespace rct { static inline rct::key pk2rct(const crypto::public_key &pk) { rct::key k; memcpy(&k, &pk, 32); return k; } static inline rct::key sk2rct(const crypto::secret_key &sk) { rct::key k; memcpy(&k, &sk, 32); return k; } static inline rct::key ki2rct(const crypto::key_image &ki) { rct::key k; memcpy(&k, &ki, 32); return k; } + static inline rct::key hash2rct(const crypto::hash &h) { rct::key k; memcpy(&k, &h, 32); return k; } static inline crypto::public_key rct2pk(const rct::key &k) { crypto::public_key pk; memcpy(&pk, &k, 32); return pk; } static inline crypto::secret_key rct2sk(const rct::key &k) { crypto::secret_key sk; memcpy(&sk, &k, 32); return sk; } static inline crypto::key_image rct2ki(const rct::key &k) { crypto::key_image ki; memcpy(&ki, &k, 32); return ki; } + static inline crypto::hash rct2hash(const rct::key &k) { crypto::hash h; memcpy(&h, &k, 32); return h; } static inline bool operator==(const rct::key &k0, const crypto::public_key &k1) { return !memcmp(&k0, &k1, 32); } static inline bool operator!=(const rct::key &k0, const crypto::public_key &k1) { return memcmp(&k0, &k1, 32); } } diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 59472e648..3565945d4 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -3237,16 +3237,19 @@ static size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs) // rangeSigs size += (2*64*32+32+64*32) * n_outputs; - // MG - size += 32 * (mixin+1) * n_inputs + 32 + 32 * n_inputs; - // mixRing - size += 2 * 32 * (mixin+1) * n_inputs; + + // MG - only the last slot of II is saved, the rest can be reconstructed + size += 32 * (mixin+1) * n_inputs + 32 + 32 * (/*n_inputs+*/1) ; + + // mixRing - not serialized, can be reconstructed + /* size += 2 * 32 * (mixin+1) * n_inputs; */ + // ecdhInfo size += 3 * 32 * n_outputs; // outPk size += 2 * 32 * n_outputs; - LOG_PRINT_L2("estimated rct tx size for " << n_inputs << " at mixin " << mixin << " and " << n_outputs << ": " << size); + 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)"); return size; } diff --git a/tests/unit_tests/ringct.cpp b/tests/unit_tests/ringct.cpp index ac1644c7b..47da05f49 100644 --- a/tests/unit_tests/ringct.cpp +++ b/tests/unit_tests/ringct.cpp @@ -131,7 +131,7 @@ TEST(ringct, MG_sigs) } key message = identity(); mgSig IIccss = MLSAG_Gen(message, P, sk, ind); - ASSERT_TRUE(MLSAG_Ver(message, P, IIccss)); + ASSERT_TRUE(MLSAG_Ver(message, P, IIccss, IIccss.II)); //#MG sig: false one N = 3;// #cols @@ -152,7 +152,7 @@ TEST(ringct, MG_sigs) } sk[2] = skGen();//asume we don't know one of the private keys.. IIccss = MLSAG_Gen(message, P, sk, ind); - ASSERT_FALSE(MLSAG_Ver(message, P, IIccss)); + ASSERT_FALSE(MLSAG_Ver(message, P, IIccss, IIccss.II)); } TEST(ringct, range_proofs) diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp index 55a33c83f..5223298cd 100644 --- a/tests/unit_tests/serialization.cpp +++ b/tests/unit_tests/serialization.cpp @@ -576,11 +576,10 @@ TEST(Serialization, serializes_ringct_types) ASSERT_TRUE(mg0.ss[n] == mg1.ss[n]); } ASSERT_TRUE(mg0.cc == mg1.cc); - ASSERT_TRUE(mg0.II.size() == mg1.II.size()); - for (size_t n = 0; n < mg0.II.size(); ++n) - { - ASSERT_TRUE(mg0.II[n] == mg1.II[n]); - } + + // mixRing and II are not serialized, they are meant to be reconstructed + ASSERT_TRUE(mg1.II.size() == 1); + ASSERT_TRUE(mg1.II[0] == mg0.II.back()); rg0 = s0.rangeSigs.front(); ASSERT_TRUE(serialization::dump_binary(rg0, blob)); @@ -600,20 +599,13 @@ TEST(Serialization, serializes_ringct_types) ASSERT_TRUE(s0.MG.ss[n] == s1.MG.ss[n]); } ASSERT_TRUE(s0.MG.cc == s1.MG.cc); - ASSERT_TRUE(s0.MG.II.size() == s1.MG.II.size()); - for (size_t n = 0; n < s0.MG.II.size(); ++n) - { - ASSERT_TRUE(s0.MG.II[n] == s1.MG.II[n]); - } - ASSERT_TRUE(s0.mixRing.size() == s1.mixRing.size()); - for (size_t n = 0; n < s0.mixRing.size(); ++n) - { - ASSERT_TRUE(s0.mixRing[n].size() == s1.mixRing[n].size()); - for (size_t i = 0; i < s0.mixRing[n].size(); ++i) - { - ASSERT_TRUE(!memcmp(&s0.mixRing[n][i], &s1.mixRing[n][i], sizeof(s0.mixRing[n][i]))); - } - } + // mixRing and II are not serialized, they are meant to be reconstructed + ASSERT_TRUE(s1.MG.II.size() == 1); + ASSERT_TRUE(s1.MG.II[0] == s0.MG.II.back()); + + // mixRing and II are not serialized, they are meant to be reconstructed + ASSERT_TRUE(s1.mixRing.size() == 0); + ASSERT_TRUE(s0.ecdhInfo.size() == s1.ecdhInfo.size()); for (size_t n = 0; n < s0.ecdhInfo.size(); ++n) { -- cgit v1.2.3 From 6d0e47148bc400ca6668860e162fe2f4ad8a3dac Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 28 Jun 2016 16:03:28 +0100 Subject: rct: add the tx prefix hash into the MLSAG to protect the non-signatures parts of the tx from tampering. --- src/cryptonote_core/cryptonote_format_utils.cpp | 7 ++++--- tests/core_tests/chaingen_main.cpp | 2 ++ tests/core_tests/rct.cpp | 18 ++++++++++++++++++ tests/core_tests/rct.h | 13 +++++++++++++ tests/unit_tests/ringct.cpp | 10 +++++----- tests/unit_tests/serialization.cpp | 2 +- 6 files changed, 43 insertions(+), 9 deletions(-) (limited to 'tests/unit_tests/serialization.cpp') diff --git a/src/cryptonote_core/cryptonote_format_utils.cpp b/src/cryptonote_core/cryptonote_format_utils.cpp index 9f0693b30..4cc62c165 100644 --- a/src/cryptonote_core/cryptonote_format_utils.cpp +++ b/src/cryptonote_core/cryptonote_format_utils.cpp @@ -685,9 +685,6 @@ namespace cryptonote if (amount_in > amount_out) amounts.push_back(amount_in - amount_out); - LOG_PRINT_L1("Signing tx: " << obj_to_json_str(tx)); - tx.rct_signatures = rct::genRct(inSk, destinations, amounts, mixRing, sources[0].real_output); // same index assumption - // zero out all amounts to mask rct outputs, real amounts are now encrypted for (size_t i = 0; i < tx.vin.size(); ++i) { @@ -697,6 +694,10 @@ namespace cryptonote for (size_t i = 0; i < tx.vout.size(); ++i) tx.vout[i].amount = 0; + crypto::hash tx_prefix_hash; + get_transaction_prefix_hash(tx, tx_prefix_hash); + tx.rct_signatures = rct::genRct(inSk, destinations, amounts, mixRing, rct::hash2rct(tx_prefix_hash), sources[0].real_output); // same index assumption + LOG_PRINT2("construct_tx.log", "transaction_created: " << get_transaction_hash(tx) << ENDL << obj_to_json_str(tx) << ENDL, LOG_LEVEL_3); } diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp index fc93b2e74..09cdb9227 100644 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -199,6 +199,8 @@ int main(int argc, char* argv[]) GENERATE_AND_PLAY(gen_rct_tx_pre_rct_add_vout); GENERATE_AND_PLAY(gen_rct_tx_rct_add_vout); GENERATE_AND_PLAY(gen_rct_tx_pre_rct_increase_vin_and_fee); + GENERATE_AND_PLAY(gen_rct_tx_pre_rct_altered_extra); + GENERATE_AND_PLAY(gen_rct_tx_rct_altered_extra); std::cout << (failed_tests.empty() ? concolor::green : concolor::magenta); std::cout << "\nREPORT:\n"; diff --git a/tests/core_tests/rct.cpp b/tests/core_tests/rct.cpp index 465d2036f..f16045ef6 100644 --- a/tests/core_tests/rct.cpp +++ b/tests/core_tests/rct.cpp @@ -467,3 +467,21 @@ bool gen_rct_tx_rct_add_vout::generate(std::vector& events) co NULL, [](transaction &tx) {tx.vout.push_back(tx.vout.back());}); } +bool gen_rct_tx_pre_rct_altered_extra::generate(std::vector& events) const +{ + const int mixin = 2; + const int out_idx[] = {0, -1}; + const uint64_t amount_paid = 10000; + return generate_with(events, out_idx, mixin, amount_paid, false, + NULL, [](transaction &tx) {std::string extra_nonce; crypto::hash pid = cryptonote::null_hash; set_payment_id_to_tx_extra_nonce(extra_nonce, pid); add_extra_nonce_to_tx_extra(tx.extra, extra_nonce);}); +} + +bool gen_rct_tx_rct_altered_extra::generate(std::vector& events) const +{ + const int mixin = 2; + const int out_idx[] = {1, -1}; + const uint64_t amount_paid = 10000; + return generate_with(events, out_idx, mixin, amount_paid, false, + NULL, [](transaction &tx) {std::string extra_nonce; crypto::hash pid = cryptonote::null_hash; set_payment_id_to_tx_extra_nonce(extra_nonce, pid); add_extra_nonce_to_tx_extra(tx.extra, extra_nonce);}); +} + diff --git a/tests/core_tests/rct.h b/tests/core_tests/rct.h index 1b79cbd8f..a93996566 100644 --- a/tests/core_tests/rct.h +++ b/tests/core_tests/rct.h @@ -249,3 +249,16 @@ struct gen_rct_tx_rct_add_vout : public gen_rct_tx_validation_base }; template<> struct get_test_options: public get_test_options {}; +// extra +struct gen_rct_tx_pre_rct_altered_extra : public gen_rct_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + +struct gen_rct_tx_rct_altered_extra : public gen_rct_tx_validation_base +{ + bool generate(std::vector& events) const; +}; +template<> struct get_test_options: public get_test_options {}; + diff --git a/tests/unit_tests/ringct.cpp b/tests/unit_tests/ringct.cpp index 47da05f49..4b494edba 100644 --- a/tests/unit_tests/ringct.cpp +++ b/tests/unit_tests/ringct.cpp @@ -187,7 +187,7 @@ TEST(ringct, range_proofs) destinations.push_back(Pk); //compute rct data with mixin 500 - rctSig s = genRct(sc, pc, destinations, amounts, 3); + rctSig s = genRct(sc, pc, destinations, amounts, rct::zero(), 3); //verify rct data ASSERT_TRUE(verRct(s)); @@ -204,7 +204,7 @@ TEST(ringct, range_proofs) //compute rct data with mixin 500 - s = genRct(sc, pc, destinations, amounts, 3); + s = genRct(sc, pc, destinations, amounts, rct::zero(), 3); //verify rct data ASSERT_FALSE(verRct(s)); @@ -248,7 +248,7 @@ TEST(ringct, range_proofs_with_fee) destinations.push_back(Pk); //compute rct data with mixin 500 - rctSig s = genRct(sc, pc, destinations, amounts, 3); + rctSig s = genRct(sc, pc, destinations, amounts, rct::zero(), 3); //verify rct data ASSERT_TRUE(verRct(s)); @@ -265,7 +265,7 @@ TEST(ringct, range_proofs_with_fee) //compute rct data with mixin 500 - s = genRct(sc, pc, destinations, amounts, 3); + s = genRct(sc, pc, destinations, amounts, rct::zero(), 3); //verify rct data ASSERT_FALSE(verRct(s)); @@ -295,7 +295,7 @@ static rct::rctSig make_sample_rct_sig(int n_inputs, const uint64_t input_amount destinations.push_back(Pk); } - return genRct(sc, pc, destinations, amounts, 3);; + return genRct(sc, pc, destinations, amounts, rct::zero(), 3);; } static bool range_proof_test(bool expected_valid, diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp index 5223298cd..01dff3e99 100644 --- a/tests/unit_tests/serialization.cpp +++ b/tests/unit_tests/serialization.cpp @@ -565,7 +565,7 @@ TEST(Serialization, serializes_ringct_types) rct::skpkGen(Sk, Pk); destinations.push_back(Pk); //compute rct data with mixin 500 - s0 = rct::genRct(sc, pc, destinations, amounts, 3); + s0 = rct::genRct(sc, pc, destinations, amounts, rct::zero(), 3); mg0 = s0.MG; ASSERT_TRUE(serialization::dump_binary(mg0, blob)); -- cgit v1.2.3 From a4d4d6194bf9a0826806521d72a7fec5e506a677 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 10 Jul 2016 12:57:22 +0100 Subject: integrate simple rct api --- src/cryptonote_core/blockchain.cpp | 212 +++++++++++++++------ src/cryptonote_core/cryptonote_basic.h | 12 +- .../cryptonote_boost_serialization.h | 21 +- src/cryptonote_core/cryptonote_format_utils.cpp | 86 ++++++--- src/ringct/rctSigs.cpp | 84 +++++--- src/ringct/rctSigs.h | 20 +- src/ringct/rctTypes.h | 39 ++-- src/wallet/wallet2.cpp | 30 ++- tests/core_tests/rct.cpp | 5 +- tests/unit_tests/ringct.cpp | 64 ++++++- tests/unit_tests/serialization.cpp | 3 +- 11 files changed, 394 insertions(+), 182 deletions(-) (limited to 'tests/unit_tests/serialization.cpp') diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index bef880627..456a78eaf 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -2462,95 +2462,193 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context else { // from version 2, check ringct signatures - rct::ctkeyM reconstructed_mixRing; - rct::keyV reconstructed_II; - - // if the tx already has a non empty mixRing and/or II, use them, - // else reconstruct them - 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; - - // RCT needs the same mixin for all inputs - for (size_t n = 1; n < pubkeys.size(); ++n) + // obviously, the original and simple rct APIs use a mixRing that's indexes + // in opposite orders, because it'd be too simple otherwise... + if (tx.rct_signatures.simple) { - if (pubkeys[n].size() != pubkeys[0].size()) - { - LOG_PRINT_L1("Failed to check ringct signatures: mismatched ring sizes"); - return false; - } - } + rct::ctkeyM reconstructed_mixRing; + std::vector reconstructed_II; - if (tx.rct_signatures.mixRing.empty()) - { - reconstructed_mixRing.resize(pubkeys[0].size()); - for (size_t n = 0; n < pubkeys.size(); ++n) + // 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 + + // all MGs should have the same II size (1) + for (size_t n = 0; n < tx.rct_signatures.MGs.size(); ++n) { - for (size_t m = 0; m < pubkeys[n].size(); ++m) + if (tx.rct_signatures.MGs[n].II.size() != 1) { - reconstructed_mixRing[m].push_back(pubkeys[n][m]); + LOG_PRINT_L1("Failed to check ringct signatures: mismatched MGs II sizes"); + return false; } } - } - if (tx.rct_signatures.MG.II.size() == 1) - { reconstructed_II.resize(tx.vin.size()); for (size_t n = 0; n < tx.vin.size(); ++n) { - reconstructed_II[n] = rct::ki2rct(boost::get(tx.vin[n]).k_image); + reconstructed_II[n].push_back(rct::ki2rct(boost::get(tx.vin[n]).k_image)); + reconstructed_II[n].push_back(tx.rct_signatures.MGs[n].II[0]); } - reconstructed_II.push_back(tx.rct_signatures.MG.II.back()); - } - // check all this, either recontructed (so should really pass), or not - { - bool size_matches = true; - for (size_t i = 0; i < pubkeys.size(); ++i) - size_matches &= pubkeys[i].size() == mixRing.size(); - for (size_t i = 0; i < tx.rct_signatures.mixRing.size(); ++i) - size_matches &= pubkeys.size() == mixRing[i].size(); - if (!size_matches) + if (tx.rct_signatures.mixRing.empty()) { - LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkeys/mixRing size"); - return false; + reconstructed_mixRing.resize(pubkeys.size()); + for (size_t n = 0; n < pubkeys.size(); ++n) + { + for (size_t m = 0; m < pubkeys[n].size(); ++m) + { + reconstructed_mixRing[n].push_back(pubkeys[n][m]); + } + } } - for (size_t n = 0; n < pubkeys.size(); ++n) + // check all this, either recontructed (so should really pass), or not { - for (size_t m = 0; m < pubkeys[n].size(); ++m) + if (pubkeys.size() != mixRing.size()) { - if (pubkeys[n][m].dest != rct::rct2pk(mixRing[m][n].dest)) + LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkeys/mixRing size"); + return false; + } + for (size_t i = 0; i < pubkeys.size(); ++i) + { + if (pubkeys[i].size() != mixRing[i].size()) { - LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkey at vin " << n << ", index " << m); + LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkeys/mixRing size"); return false; } - if (pubkeys[n][m].mask != rct::rct2pk(mixRing[m][n].mask)) + } + + for (size_t n = 0; n < pubkeys.size(); ++n) + { + for (size_t m = 0; m < pubkeys[n].size(); ++m) { - LOG_PRINT_L1("Failed to check ringct signatures: mismatched commitment at vin " << n << ", index " << m); - return false; + if (pubkeys[n][m].dest != rct::rct2pk(mixRing[n][m].dest)) + { + LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkey at vin " << n << ", index " << m); + return false; + } + if (pubkeys[n][m].mask != rct::rct2pk(mixRing[n][m].mask)) + { + LOG_PRINT_L1("Failed to check ringct signatures: mismatched commitment at vin " << n << ", index " << m); + return false; + } } } } - } - if (II.size() != 1 + tx.vin.size()) - { - LOG_PRINT_L1("Failed to check ringct signatures: mismatched II/vin sizes"); - return false; + if (tx.rct_signatures.MGs.size() != tx.vin.size()) + { + LOG_PRINT_L1("Failed to check ringct signatures: mismatched MGs/vin sizes"); + return false; + } + for (size_t n = 0; n < tx.vin.size(); ++n) + { + if (memcmp(&boost::get(tx.vin[n]).k_image, &reconstructed_II[n][0], 32)) + { + LOG_PRINT_L1("Failed to check ringct signatures: mismatched key image"); + return false; + } + } + + if (!rct::verRctSimple(tx.rct_signatures, mixRing, &reconstructed_II, rct::hash2rct(tx_prefix_hash))) + { + LOG_PRINT_L1("Failed to check ringct signatures!"); + return false; + } } - for (size_t n = 0; n < tx.vin.size(); ++n) + else { - if (memcmp(&boost::get(tx.vin[n]).k_image, &II[n], 32)) + rct::ctkeyM reconstructed_mixRing; + rct::keyV reconstructed_II; + + // if the tx already has a non empty mixRing and/or II, use them, + // else reconstruct them + 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; + + // RCT needs the same mixin for all inputs + for (size_t n = 1; n < pubkeys.size(); ++n) + { + if (pubkeys[n].size() != pubkeys[0].size()) + { + LOG_PRINT_L1("Failed to check ringct signatures: mismatched ring sizes"); + return false; + } + } + + if (tx.rct_signatures.mixRing.empty()) + { + reconstructed_mixRing.resize(pubkeys[0].size()); + for (size_t n = 0; n < pubkeys.size(); ++n) + { + for (size_t m = 0; m < pubkeys[n].size(); ++m) + { + reconstructed_mixRing[m].push_back(pubkeys[n][m]); + } + } + } + + if (tx.rct_signatures.MG.II.size() == 1) + { + reconstructed_II.resize(tx.vin.size()); + for (size_t n = 0; n < tx.vin.size(); ++n) + { + reconstructed_II[n] = rct::ki2rct(boost::get(tx.vin[n]).k_image); + } + reconstructed_II.push_back(tx.rct_signatures.MG.II.back()); + } + + // check all this, either recontructed (so should really pass), or not + { + bool size_matches = true; + for (size_t i = 0; i < pubkeys.size(); ++i) + size_matches &= pubkeys[i].size() == mixRing.size(); + for (size_t i = 0; i < tx.rct_signatures.mixRing.size(); ++i) + size_matches &= pubkeys.size() == mixRing[i].size(); + if (!size_matches) + { + LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkeys/mixRing size"); + return false; + } + + for (size_t n = 0; n < pubkeys.size(); ++n) + { + for (size_t m = 0; m < pubkeys[n].size(); ++m) + { + if (pubkeys[n][m].dest != rct::rct2pk(mixRing[m][n].dest)) + { + LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkey at vin " << n << ", index " << m); + return false; + } + if (pubkeys[n][m].mask != rct::rct2pk(mixRing[m][n].mask)) + { + LOG_PRINT_L1("Failed to check ringct signatures: mismatched commitment at vin " << n << ", index " << m); + return false; + } + } + } + } + + if (II.size() != 1 + tx.vin.size()) { LOG_PRINT_L1("Failed to check ringct signatures: mismatched II/vin sizes"); return false; } - } + for (size_t n = 0; n < tx.vin.size(); ++n) + { + if (memcmp(&boost::get(tx.vin[n]).k_image, &II[n], 32)) + { + LOG_PRINT_L1("Failed to check ringct signatures: mismatched II/vin sizes"); + return false; + } + } - if (!rct::verRct(tx.rct_signatures, mixRing, II, rct::hash2rct(tx_prefix_hash))) - { - LOG_PRINT_L1("Failed to check ringct signatures!"); - return false; + if (!rct::verRct(tx.rct_signatures, mixRing, II, rct::hash2rct(tx_prefix_hash))) + { + LOG_PRINT_L1("Failed to check ringct signatures!"); + return false; + } } } return true; diff --git a/src/cryptonote_core/cryptonote_basic.h b/src/cryptonote_core/cryptonote_basic.h index 91bcef8c5..afe785eb9 100644 --- a/src/cryptonote_core/cryptonote_basic.h +++ b/src/cryptonote_core/cryptonote_basic.h @@ -231,11 +231,19 @@ namespace cryptonote else { FIELD(rct_signatures) - for (size_t i = 0; i < rct_signatures.mixRing.size(); ++i) + if (rct_signatures.simple) { - if (rct_signatures.mixRing[i].size() != vin.size()) + if (rct_signatures.mixRing.size() && rct_signatures.mixRing.size() != vin.size()) return false; } + else + { + for (size_t i = 0; i < rct_signatures.mixRing.size(); ++i) + { + if (rct_signatures.mixRing[i].size() != vin.size()) + return false; + } + } } END_SERIALIZE() diff --git a/src/cryptonote_core/cryptonote_boost_serialization.h b/src/cryptonote_core/cryptonote_boost_serialization.h index 01239c5ae..35fabe7fb 100644 --- a/src/cryptonote_core/cryptonote_boost_serialization.h +++ b/src/cryptonote_core/cryptonote_boost_serialization.h @@ -224,23 +224,16 @@ namespace boost template inline void serialize(Archive &a, rct::rctSig &x, const boost::serialization::version_type ver) { - a & x.rangeSigs; - a & x.MG; - // a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets - a & x.ecdhInfo; - a & x.outPk; - a & x.txnFee; - // a & x.bash_hash; bash_hash is not serialized, as it can be reconstructed from the tx data - } - - template - inline void serialize(Archive &a, rct::sRctSig &x, const boost::serialization::version_type ver) - { + a & x.simple; // a & x.message; message is not serialized, as it can be reconstructed from the tx data a & x.rangeSigs; - a & x.MG; + if (x.simple) + a & x.MGs; + else + a & x.MG; // a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets - a & x.pseudoOuts; + if (x.simple) + a & x.pseudoOuts; a & x.ecdhInfo; a & x.outPk; a & x.txnFee; diff --git a/src/cryptonote_core/cryptonote_format_utils.cpp b/src/cryptonote_core/cryptonote_format_utils.cpp index 4cc62c165..6578776b4 100644 --- a/src/cryptonote_core/cryptonote_format_utils.cpp +++ b/src/cryptonote_core/cryptonote_format_utils.cpp @@ -516,19 +516,6 @@ namespace cryptonote }; std::vector in_contexts; - if (tx.version > 1) - { - // ringct requires all real inputs to be at the same index for all inputs // TODO - BOOST_FOREACH(const tx_source_entry& src_entr, sources) - { - if(src_entr.real_output != sources.begin()->real_output) - { - LOG_ERROR("All inputs must have the same index for ringct"); - return false; - } - } - } - uint64_t summary_inputs_money = 0; //fill inputs BOOST_FOREACH(const tx_source_entry& src_entr, sources) @@ -641,24 +628,46 @@ namespace cryptonote } else { - // enforce same mixin for all outputs - size_t n_total_outs = sources[0].outputs.size(); - for (size_t i = 1; i < sources.size(); ++i) { - if (n_total_outs != sources[i].outputs.size()) { - LOG_ERROR("Ringct transaction has varying mixin"); - return false; + bool all_rct_inputs = true; + size_t n_total_outs = sources[0].outputs.size(); // only for non-simple rct + BOOST_FOREACH(const tx_source_entry& src_entr, sources) + all_rct_inputs &= !(src_entr.mask == rct::identity()); + bool use_simple_rct = all_rct_inputs; + + if (!use_simple_rct) + { + // non simple ringct requires all real inputs to be at the same index for all inputs + BOOST_FOREACH(const tx_source_entry& src_entr, sources) + { + if(src_entr.real_output != sources.begin()->real_output) + { + LOG_ERROR("All inputs must have the same index for non-simple ringct"); + return false; + } + } + + // enforce same mixin for all outputs + for (size_t i = 1; i < sources.size(); ++i) { + if (n_total_outs != sources[i].outputs.size()) { + LOG_ERROR("Non-simple ringct transaction has varying mixin"); + return false; + } } } uint64_t amount_in = 0, amount_out = 0; rct::ctkeyV inSk; - rct::ctkeyM mixRing(n_total_outs); + // mixRing indexing is done the other way round for simple + rct::ctkeyM mixRing(use_simple_rct ? sources.size() : n_total_outs); rct::keyV destinations; - std::vector amounts; + std::vector inamounts, outamounts; + std::vector index; for (size_t i = 0; i < sources.size(); ++i) { rct::ctkey ctkey; amount_in += sources[i].amount; + inamounts.push_back(sources[i].amount); + index.push_back(sources[i].real_output); // inSk: (secret key, mask) ctkey.dest = rct::sk2rct(in_contexts[i].in_ephemeral.sec); ctkey.mask = sources[i].mask; @@ -669,21 +678,37 @@ namespace cryptonote for (size_t i = 0; i < tx.vout.size(); ++i) { destinations.push_back(rct::pk2rct(boost::get(tx.vout[i].target).key)); - amounts.push_back(tx.vout[i].amount); + outamounts.push_back(tx.vout[i].amount); amount_out += tx.vout[i].amount; } - for (size_t i = 0; i < n_total_outs; ++i) // same index assumption + + if (use_simple_rct) + { + // mixRing indexing is done the other way round for simple + for (size_t i = 0; i < sources.size(); ++i) + { + mixRing[i].resize(sources[i].outputs.size()); + for (size_t n = 0; n < sources[i].outputs.size(); ++n) + { + mixRing[i][n] = sources[i].outputs[n].second; + } + } + } + else { - mixRing[i].resize(sources.size()); - for (size_t n = 0; n < sources.size(); ++n) + for (size_t i = 0; i < n_total_outs; ++i) // same index assumption { - mixRing[i][n] = sources[n].outputs[i].second; + mixRing[i].resize(sources.size()); + for (size_t n = 0; n < sources.size(); ++n) + { + mixRing[i][n] = sources[n].outputs[i].second; + } } } // fee - if (amount_in > amount_out) - amounts.push_back(amount_in - amount_out); + if (!use_simple_rct && amount_in > amount_out) + outamounts.push_back(amount_in - amount_out); // zero out all amounts to mask rct outputs, real amounts are now encrypted for (size_t i = 0; i < tx.vin.size(); ++i) @@ -696,7 +721,10 @@ namespace cryptonote crypto::hash tx_prefix_hash; get_transaction_prefix_hash(tx, tx_prefix_hash); - tx.rct_signatures = rct::genRct(inSk, destinations, amounts, mixRing, rct::hash2rct(tx_prefix_hash), sources[0].real_output); // same index assumption + if (use_simple_rct) + tx.rct_signatures = rct::genRctSimple(rct::hash2rct(tx_prefix_hash), inSk, destinations, inamounts, outamounts, amount_in - amount_out, mixRing, index); + else + tx.rct_signatures = rct::genRct(rct::hash2rct(tx_prefix_hash), inSk, destinations, outamounts, mixRing, sources[0].real_output); // same index assumption LOG_PRINT2("construct_tx.log", "transaction_created: " << get_transaction_hash(tx) << ENDL << obj_to_json_str(tx) << ENDL, LOG_LEVEL_3); } diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index 7fcb8e158..687373fe5 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -329,7 +329,7 @@ namespace rct { // this shows that sum inputs = sum outputs //Ver: // verifies the above sig is created corretly - mgSig proveRctMG(const ctkeyM & pubs, const ctkeyV & inSk, const ctkeyV &outSk, const ctkeyV & outPk, unsigned int index, key txnFeeKey, const key &base_hash) { + mgSig proveRctMG(const key &message, const ctkeyM & pubs, const ctkeyV & inSk, const ctkeyV &outSk, const ctkeyV & outPk, unsigned int index, key txnFeeKey) { mgSig mg; //setup vars size_t cols = pubs.size(); @@ -374,9 +374,9 @@ namespace rct { sc_sub(sk[rows].bytes, sk[rows].bytes, outSk[j].mask.bytes); //subtract output masks in last row.. } ctkeyV signed_data = outPk; - signed_data.push_back(ctkey({base_hash, identity()})); - key message = cn_fast_hash(signed_data); - return MLSAG_Gen(message, M, sk, index); + signed_data.push_back(ctkey({message, identity()})); + key msg = cn_fast_hash(signed_data); + return MLSAG_Gen(msg, M, sk, index); } @@ -415,7 +415,7 @@ namespace rct { // this shows that sum inputs = sum outputs //Ver: // verifies the above sig is created corretly - bool verRctMG(mgSig mg, const keyV &II, const ctkeyM & pubs, const ctkeyV & outPk, key txnFeeKey, const key &base_hash) { + bool verRctMG(mgSig mg, const keyV &II, const ctkeyM & pubs, const ctkeyV & outPk, key txnFeeKey, const key &message) { //setup vars size_t cols = pubs.size(); CHECK_AND_ASSERT_MES(cols >= 1, false, "Empty pubs"); @@ -447,11 +447,11 @@ namespace rct { subKeys(M[i][rows], M[i][rows], txnFeeKey); } ctkeyV signed_data = outPk; - signed_data.push_back(ctkey({base_hash, identity()})); - key message = cn_fast_hash(signed_data); + signed_data.push_back(ctkey({message, identity()})); + key msg = cn_fast_hash(signed_data); DP("message:"); - DP(message); - return MLSAG_Ver(message, M, mg, II); + DP(msg); + return MLSAG_Ver(msg, M, mg, II); } //Ring-ct Simple MG sigs @@ -535,7 +535,7 @@ namespace rct { // must know the destination private key to find the correct amount, else will return a random number // Note: For txn fees, the last index in the amounts vector should contain that // Thus the amounts vector will be "one" longer than the destinations vectort - rctSig genRct(const ctkeyV & inSk, const keyV & destinations, const vector amounts, const ctkeyM &mixRing, const key &base_hash, unsigned int index) { + rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector & amounts, const ctkeyM &mixRing, unsigned int index) { CHECK_AND_ASSERT_THROW_MES(amounts.size() == destinations.size() || amounts.size() == destinations.size() + 1, "Different number of amounts/destinations"); CHECK_AND_ASSERT_THROW_MES(index < mixRing.size(), "Bad index into mixRing"); for (size_t n = 0; n < mixRing.size(); ++n) { @@ -543,6 +543,7 @@ namespace rct { } rctSig rv; + rv.simple = false; rv.outPk.resize(destinations.size()); rv.rangeSigs.resize(destinations.size()); rv.ecdhInfo.resize(destinations.size()); @@ -578,23 +579,22 @@ namespace rct { key txnFeeKey = scalarmultH(d2h(rv.txnFee)); rv.mixRing = mixRing; - rv.base_hash = base_hash; - rv.MG = proveRctMG(rv.mixRing, inSk, outSk, rv.outPk, index, txnFeeKey, base_hash); + rv.message = message; + rv.MG = proveRctMG(message, rv.mixRing, inSk, outSk, rv.outPk, index, txnFeeKey); return rv; } - rctSig genRct(const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector amounts, const key &base_hash, const int mixin) { + rctSig genRct(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector & amounts, const int mixin) { unsigned int index; ctkeyM mixRing; tie(mixRing, index) = populateFromBlockchain(inPk, mixin); - return genRct(inSk, destinations, amounts, mixRing, base_hash, index); + return genRct(message, inSk, destinations, amounts, mixRing, index); } //RCT simple //for post-rct only - sRctSig genRctSimple(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector &inamounts, const vector &outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const std::vector & index) { + 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) { CHECK_AND_ASSERT_THROW_MES(inamounts.size() > 0, "Empty inamounts"); - CHECK_AND_ASSERT_THROW_MES(inPk.size() == inSk.size(), "Different number of inPk/inSk"); CHECK_AND_ASSERT_THROW_MES(inamounts.size() == inSk.size(), "Different number of inamounts/inSk"); CHECK_AND_ASSERT_THROW_MES(outamounts.size() == destinations.size(), "Different number of amounts/destinations"); CHECK_AND_ASSERT_THROW_MES(index.size() == inSk.size(), "Different number of index/inSk"); @@ -603,7 +603,8 @@ namespace rct { CHECK_AND_ASSERT_THROW_MES(index[n] < mixRing[n].size(), "Bad index into mixRing"); } - sRctSig rv; + rctSig rv; + rv.simple = true; rv.message = message; rv.outPk.resize(destinations.size()); rv.rangeSigs.resize(destinations.size()); @@ -637,24 +638,24 @@ namespace rct { // key txnFeeKey = scalarmultH(d2h(rv.txnFee)); rv.mixRing = mixRing; rv.pseudoOuts.resize(inamounts.size()); - rv.MG.resize(inamounts.size()); + rv.MGs.resize(inamounts.size()); key sumpouts = zero(); //sum pseudoOut masks key a; for (i = 0 ; i < inamounts.size() - 1; i++) { skGen(a); sc_add(sumpouts.bytes, a.bytes, sumpouts.bytes); genC(rv.pseudoOuts[i], a, inamounts[i]); - rv.MG[i] = proveRctMGSimple(message, rv.mixRing[i], inSk[i], a, rv.pseudoOuts[i], index[i]); + rv.MGs[i] = proveRctMGSimple(message, rv.mixRing[i], inSk[i], a, rv.pseudoOuts[i], index[i]); } rv.mixRing = mixRing; sc_sub(a.bytes, sumout.bytes, sumpouts.bytes); genC(rv.pseudoOuts[i], a, inamounts[i]); DP(rv.pseudoOuts[i]); - rv.MG[i] = proveRctMGSimple(message, rv.mixRing[i], inSk[i], a, rv.pseudoOuts[i], index[i]); + rv.MGs[i] = proveRctMGSimple(message, rv.mixRing[i], inSk[i], a, rv.pseudoOuts[i], index[i]); return rv; } - sRctSig 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 ctkeyV & inPk, const keyV & destinations, const vector &inamounts, const vector &outamounts, xmr_amount txnFee, unsigned int mixin) { std::vector index; index.resize(inPk.size()); ctkeyM mixRing; @@ -663,7 +664,7 @@ namespace rct { mixRing[i].resize(mixin+1); index[i] = populateFromBlockchainSimple(mixRing[i], inPk[i], mixin); } - return genRctSimple(message, inSk, inPk, destinations, inamounts, outamounts, txnFee, mixRing, index); + return genRctSimple(message, inSk, destinations, inamounts, outamounts, txnFee, mixRing, index); } //RingCT protocol @@ -676,7 +677,8 @@ 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 &base_hash) { + bool verRct(const rctSig & rv, const ctkeyM &mixRing, const keyV &II, 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"); @@ -694,7 +696,7 @@ namespace rct { } //compute txn fee key txnFeeKey = scalarmultH(d2h(rv.txnFee)); - bool mgVerd = verRctMG(rv.MG, II, mixRing, rv.outPk, txnFeeKey, base_hash); + bool mgVerd = verRctMG(rv.MG, II, mixRing, rv.outPk, txnFeeKey, message); DP("mg sig verified?"); DP(mgVerd); @@ -706,19 +708,28 @@ namespace rct { } } bool verRct(const rctSig & rv) { - return verRct(rv, rv.mixRing, rv.MG.II, rv.base_hash); + return verRct(rv, rv.mixRing, rv.MG.II, rv.message); } //ver RingCT simple //assumes only post-rct style inputs (at least for max anonymity) - bool verRctSimple(const sRctSig & rv) { + bool verRctSimple(const rctSig & rv, const ctkeyM &mixRing, const std::vector *II, 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(rv.pseudoOuts.size() == rv.MGs.size(), false, "Mismatched sizes of rv.pseudoOuts and rv.MGs"); - CHECK_AND_ASSERT_MES(rv.pseudoOuts.size() == rv.mixRing.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"); + if (II) + { + for (size_t n = 0; n < II->size(); ++n) + { + CHECK_AND_ASSERT_MES((*II)[n].size() == 2, false, "Bad II size"); + } + } key sumOutpks = identity(); for (i = 0; i < rv.outPk.size(); i++) { @@ -733,8 +744,8 @@ namespace rct { bool tmpb = false; key sumPseudoOuts = identity(); - for (i = 0 ; i < rv.mixRing.size() ; i++) { - tmpb = verRctMGSimple(rv.message, rv.MG[i], rv.MG[i].II, rv.mixRing[i], rv.pseudoOuts[i]); + for (i = 0 ; i < mixRing.size() ; i++) { + tmpb = verRctMGSimple(message, rv.MGs[i], II ? (*II)[i] : rv.MGs[i].II, mixRing[i], rv.pseudoOuts[i]); addKeys(sumPseudoOuts, sumPseudoOuts, rv.pseudoOuts[i]); DP(tmpb); if (!tmpb) { @@ -755,6 +766,10 @@ namespace rct { return (rvb && mgVerd); } + bool verRctSimple(const rctSig & rv) { + return verRctSimple(rv, rv.mixRing, NULL, rv.message); + } + //RingCT protocol //genRct: // creates an rctSig with all data necessary to verify the rangeProofs and that the signer owns one of the @@ -766,6 +781,7 @@ namespace rct { // 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 xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, key & mask) { + CHECK_AND_ASSERT_MES(!rv.simple, false, "decodeRct called on simple rctSig"); CHECK_AND_ASSERT_THROW_MES(rv.rangeSigs.size() > 0, "Empty rv.rangeSigs"); CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.rangeSigs.size(), "Mismatched sizes of rv.outPk and rv.rangeSigs"); CHECK_AND_ASSERT_THROW_MES(i < rv.ecdhInfo.size(), "Bad index"); @@ -793,7 +809,8 @@ namespace rct { return decodeRct(rv, sk, i, mask); } - xmr_amount decodeRct(const sRctSig & rv, const key & sk, unsigned int i) { + xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i, key &mask) { + CHECK_AND_ASSERT_MES(rv.simple, false, "decodeRct called on non simple rctSig"); CHECK_AND_ASSERT_THROW_MES(rv.rangeSigs.size() > 0, "Empty rv.rangeSigs"); CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.rangeSigs.size(), "Mismatched sizes of rv.outPk and rv.rangeSigs"); CHECK_AND_ASSERT_THROW_MES(i < rv.ecdhInfo.size(), "Bad index"); @@ -801,7 +818,7 @@ namespace rct { //mask amount and mask ecdhTuple ecdh_info = rv.ecdhInfo[i]; ecdhDecode(ecdh_info, sk); - key mask = ecdh_info.mask; + mask = ecdh_info.mask; key amount = ecdh_info.amount; key C = rv.outPk[i].mask; DP("C"); @@ -815,4 +832,9 @@ namespace rct { } return h2d(amount); } + + xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i) { + key mask; + return decodeRctSimple(rv, sk, i, mask); + } } diff --git a/src/ringct/rctSigs.h b/src/ringct/rctSigs.h index 7682510cd..94b67f2d9 100644 --- a/src/ringct/rctSigs.h +++ b/src/ringct/rctSigs.h @@ -112,9 +112,9 @@ namespace rct { // this shows that sum inputs = sum outputs //Ver: // verifies the above sig is created corretly - mgSig proveRctMG(const ctkeyM & pubs, const ctkeyV & inSk, const keyV &outMasks, const ctkeyV & outPk, unsigned int index, key txnFee, const key &base_hash); + mgSig proveRctMG(const ctkeyM & pubs, const ctkeyV & inSk, const keyV &outMasks, const ctkeyV & outPk, unsigned int index, key txnFee, const key &message); mgSig proveRctMGSimple(const key & message, const ctkeyV & pubs, const ctkey & inSk, const key &a , const key &Cout, unsigned int index); - bool verRctMG(mgSig mg, const ctkeyM & pubs, const ctkeyV & outPk, key txnFee, const key &base_hash); + bool verRctMG(mgSig mg, const ctkeyM & pubs, const ctkeyV & outPk, key txnFee, const key &message); bool verRctMGSimple(const key &message, const mgSig &mg, const keyV &II, const ctkeyV & pubs, const key & C); //These functions get keys from blockchain @@ -135,16 +135,18 @@ 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 - rctSig genRct(const ctkeyV & inSk, const keyV & destinations, const vector amounts, const ctkeyM &mixRing, const key &bash_hash, unsigned int index); - rctSig genRct(const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector amounts, const key &bash_hash, const int mixin); - sRctSig 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); - sRctSig genRctSimple(const key & message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector & inamounts, const vector & outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const std::vector & index); + rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector & amounts, const ctkeyM &mixRing, unsigned int index); + rctSig genRct(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector & amounts, const int mixin); + 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); bool verRct(const rctSig & rv); - bool verRct(const rctSig & rv, const ctkeyM &mixRing, const keyV &II, const key &base_hash); - bool verRctSimple(const sRctSig & rv); + bool verRct(const rctSig & rv, const ctkeyM &mixRing, const keyV &II, const key &message); + bool verRctSimple(const rctSig & rv); + bool verRctSimple(const rctSig & rv, const ctkeyM &mixRing, const std::vector *II, 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 decodeRct(const sRctSig & 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, key & mask); } #endif /* RCTSIGS_H */ diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h index f270da70a..a376980fd 100644 --- a/src/ringct/rctTypes.h +++ b/src/ringct/rctTypes.h @@ -183,44 +183,29 @@ namespace rct { // outPk contains public keypairs which are destinations (P, C), // P = address, C = commitment to amount struct rctSig { - vector rangeSigs; - mgSig MG; - ctkeyM mixRing; //the set of all pubkeys / copy - //pairs that you mix with - vector ecdhInfo; - ctkeyV outPk; - xmr_amount txnFee; - key base_hash; - - BEGIN_SERIALIZE_OBJECT() - FIELD(rangeSigs) - FIELD(MG) - // FIELD(mixRing) - not serialized, it can be reconstructed - FIELD(ecdhInfo) - FIELD(outPk) - FIELD(txnFee) - // FIELD(base_hash) - not serialized, it can be reconstructed - END_SERIALIZE() - }; - - //rct simple variant - struct sRctSig { + bool simple; key message; vector rangeSigs; - vector MG; - vector mixRing; //the set of all pubkeys / copy + mgSig MG; // for non simple rct + vector MGs; // for simple rct + ctkeyM mixRing; //the set of all pubkeys / copy //pairs that you mix with - keyV pseudoOuts; //C + keyV pseudoOuts; //C - for simple rct vector ecdhInfo; ctkeyV outPk; xmr_amount txnFee; // contains b BEGIN_SERIALIZE_OBJECT() + FIELD(simple) // FIELD(message) - not serialized, it can be reconstructed FIELD(rangeSigs) - FIELD(MG) + if (simple) + FIELD(MGs) + else + FIELD(MG) // FIELD(mixRing) - not serialized, it can be reconstructed - FIELD(pseudoOuts) + if (simple) + FIELD(pseudoOuts) FIELD(ecdhInfo) FIELD(outPk) FIELD(txnFee) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 75c912461..a6c9faa80 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -193,6 +193,14 @@ void wallet2::check_acc_out(const account_keys &acc, const tx_out &o, const cryp error = false; } //---------------------------------------------------------------------------------------------------- +static uint64_t decodeRct(const rct::rctSig & rv, const rct::key & sk, unsigned int i, rct::key & mask) +{ + if (rv.simple) + return rct::decodeRctSimple(rv, sk, i, mask); + else + return rct::decodeRct(rv, sk, i, mask); +} +//---------------------------------------------------------------------------------------------------- void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_t height, uint64_t ts, bool miner_tx, bool pool) { if (!miner_tx) @@ -251,7 +259,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_ outs.push_back(0); if (money_transfered == 0) - money_transfered = rct::decodeRct(tx.rct_signatures, rct::sk2rct(in_ephemeral[0].sec), 0, mask[0]); + money_transfered = tools::decodeRct(tx.rct_signatures, rct::sk2rct(in_ephemeral[0].sec), 0, mask[0]); amount[0] = money_transfered; tx_money_got_in_outs = money_transfered; @@ -290,7 +298,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_ outs.push_back(i); if (money_transfered[i] == 0) - money_transfered[i] = rct::decodeRct(tx.rct_signatures, rct::sk2rct(in_ephemeral[i].sec), i, mask[i]); + money_transfered[i] = tools::decodeRct(tx.rct_signatures, rct::sk2rct(in_ephemeral[i].sec), i, mask[i]); tx_money_got_in_outs += money_transfered[i]; amount[i] = money_transfered[i]; } @@ -334,7 +342,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_ outs.push_back(i); if (money_transfered[i] == 0) - money_transfered[i] = rct::decodeRct(tx.rct_signatures, rct::sk2rct(in_ephemeral[i].sec), i, mask[i]); + money_transfered[i] = tools::decodeRct(tx.rct_signatures, rct::sk2rct(in_ephemeral[i].sec), i, mask[i]); tx_money_got_in_outs += money_transfered[i]; amount[i] = money_transfered[i]; } @@ -362,7 +370,7 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_ outs.push_back(i); if (money_transfered == 0) - money_transfered = rct::decodeRct(tx.rct_signatures, rct::sk2rct(in_ephemeral[i].sec), i, mask[i]); + money_transfered = tools::decodeRct(tx.rct_signatures, rct::sk2rct(in_ephemeral[i].sec), i, mask[i]); amount[i] = money_transfered; tx_money_got_in_outs += money_transfered; } @@ -3008,19 +3016,29 @@ static size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs) // rct signatures + // simple + size += 1; + + // message + size += 32; + // rangeSigs size += (2*64*32+32+64*32) * n_outputs; - // MG - only the last slot of II is saved, the rest can be reconstructed - size += 32 * (mixin+1) * n_inputs + 32 + 32 * (/*n_inputs+*/1) ; + // MGs - only the last slot of II is saved, the rest can be reconstructed + size += n_inputs * (32 * (mixin+1) * n_inputs + 32 + 32 * (/*n_inputs+*/1)); // mixRing - not serialized, can be reconstructed /* size += 2 * 32 * (mixin+1) * n_inputs; */ + // pseudoOuts + size += 32 * n_outputs; // ecdhInfo size += 3 * 32 * n_outputs; // outPk size += 2 * 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)"); return size; diff --git a/tests/core_tests/rct.cpp b/tests/core_tests/rct.cpp index f16045ef6..ef5f72fb0 100644 --- a/tests/core_tests/rct.cpp +++ b/tests/core_tests/rct.cpp @@ -127,7 +127,10 @@ bool gen_rct_tx_validation_base::generate_with(std::vector& ev cryptonote::keypair in_ephemeral; crypto::key_image ki; cryptonote::generate_key_image_helper(miner_accounts[n].get_keys(), tx_pub_key, o, in_ephemeral, ki); - rct::decodeRct(rct_txes[n].rct_signatures, rct::sk2rct(in_ephemeral.sec), o, rct_tx_masks[o+n*4]); + if (rct_txes[n].rct_signatures.simple) + rct::decodeRctSimple(rct_txes[n].rct_signatures, rct::sk2rct(in_ephemeral.sec), o, rct_tx_masks[o+n*4]); + else + rct::decodeRct(rct_txes[n].rct_signatures, rct::sk2rct(in_ephemeral.sec), o, rct_tx_masks[o+n*4]); } CHECK_AND_ASSERT_MES(generator.construct_block_manually(blk_txes[n], blk_last, miner_account, diff --git a/tests/unit_tests/ringct.cpp b/tests/unit_tests/ringct.cpp index 4b494edba..a7011bb2b 100644 --- a/tests/unit_tests/ringct.cpp +++ b/tests/unit_tests/ringct.cpp @@ -187,7 +187,7 @@ TEST(ringct, range_proofs) destinations.push_back(Pk); //compute rct data with mixin 500 - rctSig s = genRct(sc, pc, destinations, amounts, rct::zero(), 3); + rctSig s = genRct(rct::zero(), sc, pc, destinations, amounts, 3); //verify rct data ASSERT_TRUE(verRct(s)); @@ -204,7 +204,7 @@ TEST(ringct, range_proofs) //compute rct data with mixin 500 - s = genRct(sc, pc, destinations, amounts, rct::zero(), 3); + s = genRct(rct::zero(), sc, pc, destinations, amounts, 3); //verify rct data ASSERT_FALSE(verRct(s)); @@ -248,7 +248,7 @@ TEST(ringct, range_proofs_with_fee) destinations.push_back(Pk); //compute rct data with mixin 500 - rctSig s = genRct(sc, pc, destinations, amounts, rct::zero(), 3); + rctSig s = genRct(rct::zero(), sc, pc, destinations, amounts, 3); //verify rct data ASSERT_TRUE(verRct(s)); @@ -265,7 +265,7 @@ TEST(ringct, range_proofs_with_fee) //compute rct data with mixin 500 - s = genRct(sc, pc, destinations, amounts, rct::zero(), 3); + s = genRct(rct::zero(), sc, pc, destinations, amounts, 3); //verify rct data ASSERT_FALSE(verRct(s)); @@ -274,6 +274,60 @@ TEST(ringct, range_proofs_with_fee) ASSERT_TRUE(decodeRct(s, Sk, 1)); } +TEST(ringct, simple) +{ + ctkeyV sc, pc; + ctkey sctmp, pctmp; + //this vector corresponds to output amounts + vectoroutamounts; + //this vector corresponds to input amounts + vectorinamounts; + //this keyV corresponds to destination pubkeys + keyV destinations; + + //add fake input 3000 + //the sc is secret data + //pc is public data + tie(sctmp, pctmp) = ctskpkGen(3000); + sc.push_back(sctmp); + pc.push_back(pctmp); + inamounts.push_back(3000); + + //add fake input 3000 + //the sc is secret data + //pc is public data + tie(sctmp, pctmp) = ctskpkGen(3000); + sc.push_back(sctmp); + pc.push_back(pctmp); + inamounts.push_back(3000); + + //add output 5000 + outamounts.push_back(5000); + //add the corresponding destination pubkey + key Sk, Pk; + skpkGen(Sk, Pk); + destinations.push_back(Pk); + + //add output 999 + outamounts.push_back(999); + //add the corresponding destination pubkey + skpkGen(Sk, Pk); + destinations.push_back(Pk); + + key message = skGen(); //real message later (hash of txn..) + + //compute sig with mixin 2 + xmr_amount txnfee = 1; + + rctSig s = genRctSimple(message, sc, pc, destinations,inamounts, outamounts, txnfee, 2); + + //verify ring ct signature + ASSERT_TRUE(verRctSimple(s)); + + //decode received amount corresponding to output pubkey index 1 + ASSERT_TRUE(decodeRctSimple(s, Sk, 1)); +} + static rct::rctSig make_sample_rct_sig(int n_inputs, const uint64_t input_amounts[], int n_outputs, const uint64_t output_amounts[], bool last_is_fee) { ctkeyV sc, pc; @@ -295,7 +349,7 @@ static rct::rctSig make_sample_rct_sig(int n_inputs, const uint64_t input_amount destinations.push_back(Pk); } - return genRct(sc, pc, destinations, amounts, rct::zero(), 3);; + return genRct(rct::zero(), sc, pc, destinations, amounts, 3);; } static bool range_proof_test(bool expected_valid, diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp index 01dff3e99..88e67bd73 100644 --- a/tests/unit_tests/serialization.cpp +++ b/tests/unit_tests/serialization.cpp @@ -565,7 +565,7 @@ TEST(Serialization, serializes_ringct_types) rct::skpkGen(Sk, Pk); destinations.push_back(Pk); //compute rct data with mixin 500 - s0 = rct::genRct(sc, pc, destinations, amounts, rct::zero(), 3); + s0 = rct::genRct(rct::zero(), sc, pc, destinations, amounts, 3); mg0 = s0.MG; ASSERT_TRUE(serialization::dump_binary(mg0, blob)); @@ -588,6 +588,7 @@ TEST(Serialization, serializes_ringct_types) ASSERT_TRUE(serialization::dump_binary(s0, blob)); ASSERT_TRUE(serialization::parse_binary(blob, s1)); + ASSERT_TRUE(s0.simple == s1.simple); ASSERT_TRUE(s0.rangeSigs.size() == s1.rangeSigs.size()); for (size_t n = 0; n < s0.rangeSigs.size(); ++n) { -- cgit v1.2.3 From cf33e1a52a0cf20a7cec619d85d68f000b2e1f40 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sat, 23 Jul 2016 12:09:33 +0100 Subject: 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(-) (limited to 'tests/unit_tests/serialization.cpp') 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(); -- cgit v1.2.3 From 9b70856ccb97943249f6e76b19f8abce5cd7aabe Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 24 Jul 2016 17:53:34 +0100 Subject: rct: make the amount key derivable by a third party with the tx key Scheme design from luigi1114. --- src/cryptonote_core/cryptonote_format_utils.cpp | 10 ++---- src/ringct/rctOps.cpp | 20 +++++++---- src/ringct/rctOps.h | 2 ++ src/ringct/rctSigs.cpp | 40 ++++++++++++++------- src/ringct/rctSigs.h | 12 ++++--- src/ringct/rctTypes.h | 2 ++ src/simplewallet/simplewallet.cpp | 40 +++++++++------------ src/wallet/wallet2.cpp | 47 +++++++++++++++++-------- src/wallet/wallet2.h | 4 +-- tests/core_tests/rct.cpp | 8 ++--- tests/unit_tests/ringct.cpp | 43 ++++++++++++++-------- tests/unit_tests/serialization.cpp | 4 ++- 12 files changed, 140 insertions(+), 92 deletions(-) (limited to 'tests/unit_tests/serialization.cpp') diff --git a/src/cryptonote_core/cryptonote_format_utils.cpp b/src/cryptonote_core/cryptonote_format_utils.cpp index e0d8d9d08..ee9a00803 100644 --- a/src/cryptonote_core/cryptonote_format_utils.cpp +++ b/src/cryptonote_core/cryptonote_format_utils.cpp @@ -681,6 +681,7 @@ namespace cryptonote destinations.push_back(rct::pk2rct(boost::get(tx.vout[i].target).key)); outamounts.push_back(tx.vout[i].amount); amount_out += tx.vout[i].amount; + amount_keys.push_back(rct::rct2sk(rct::hash_to_scalar(rct::scalarmultKey(rct::pk2rct(shuffled_dsts[i].addr.m_view_public_key), rct::sk2rct(txkey.sec))))); } if (use_simple_rct) @@ -724,16 +725,11 @@ namespace cryptonote get_transaction_prefix_hash(tx, tx_prefix_hash); rct::ctkeyV outSk; if (use_simple_rct) - tx.rct_signatures = rct::genRctSimple(rct::hash2rct(tx_prefix_hash), inSk, destinations, inamounts, outamounts, amount_in - amount_out, mixRing, index, outSk); + tx.rct_signatures = rct::genRctSimple(rct::hash2rct(tx_prefix_hash), inSk, destinations, inamounts, outamounts, amount_in - amount_out, mixRing, (const rct::keyV&)amount_keys, index, outSk); else - tx.rct_signatures = rct::genRct(rct::hash2rct(tx_prefix_hash), inSk, destinations, outamounts, mixRing, sources[0].real_output, outSk); // same index assumption + tx.rct_signatures = rct::genRct(rct::hash2rct(tx_prefix_hash), inSk, destinations, outamounts, mixRing, (const rct::keyV&)amount_keys, sources[0].real_output, outSk); // same index assumption CHECK_AND_ASSERT_MES(tx.vout.size() == outSk.size(), false, "outSk size does not match vout"); - for (size_t i = 0; i < tx.vout.size(); ++i) - { - amount_keys.push_back(rct::rct2sk(rct::d2h(shuffled_dsts[i].amount))); - amount_keys.push_back(rct::rct2sk(outSk[i].mask)); - } LOG_PRINT2("construct_tx.log", "transaction_created: " << get_transaction_hash(tx) << ENDL << obj_to_json_str(tx) << ENDL, LOG_LEVEL_3); } diff --git a/src/ringct/rctOps.cpp b/src/ringct/rctOps.cpp index 06267fd8e..b8a0d26ad 100644 --- a/src/ringct/rctOps.cpp +++ b/src/ringct/rctOps.cpp @@ -741,22 +741,28 @@ void fe_mul(fe h,const fe f,const fe g) //Elliptic Curve Diffie Helman: encodes and decodes the amount b and mask a // where C= aG + bH + void ecdhEncodeFromSharedSecret(ecdhTuple & unmasked, const key & sharedSec1) { + key sharedSec2 = hash_to_scalar(sharedSec1); + //encode + sc_add(unmasked.mask.bytes, unmasked.mask.bytes, sharedSec1.bytes); + sc_add(unmasked.amount.bytes, unmasked.amount.bytes, sharedSec2.bytes); + } void ecdhEncode(ecdhTuple & unmasked, const key & receiverPk) { key esk; //compute shared secret skpkGen(esk, unmasked.senderPk); key sharedSec1 = hash_to_scalar(scalarmultKey(receiverPk, esk)); + ecdhEncodeFromSharedSecret(unmasked, sharedSec1); + } + void ecdhDecodeFromSharedSecret(ecdhTuple & masked, const key & sharedSec1) { key sharedSec2 = hash_to_scalar(sharedSec1); - //encode - sc_add(unmasked.mask.bytes, unmasked.mask.bytes, sharedSec1.bytes); - sc_add(unmasked.amount.bytes, unmasked.amount.bytes, sharedSec2.bytes); + //decode + sc_sub(masked.mask.bytes, masked.mask.bytes, sharedSec1.bytes); + sc_sub(masked.amount.bytes, masked.amount.bytes, sharedSec2.bytes); } void ecdhDecode(ecdhTuple & masked, const key & receiverSk) { //compute shared secret key sharedSec1 = hash_to_scalar(scalarmultKey(masked.senderPk, receiverSk)); - key sharedSec2 = hash_to_scalar(sharedSec1); - //encode - sc_sub(masked.mask.bytes, masked.mask.bytes, sharedSec1.bytes); - sc_sub(masked.amount.bytes, masked.amount.bytes, sharedSec2.bytes); + ecdhDecodeFromSharedSecret(masked, sharedSec1); } } diff --git a/src/ringct/rctOps.h b/src/ringct/rctOps.h index 6438a1f03..225c5abb9 100644 --- a/src/ringct/rctOps.h +++ b/src/ringct/rctOps.h @@ -165,7 +165,9 @@ namespace rct { //Elliptic Curve Diffie Helman: encodes and decodes the amount b and mask a // where C= aG + bH + void ecdhEncodeFromSharedSecret(ecdhTuple & unmasked, const key & sharedSec1); void ecdhEncode(ecdhTuple & unmasked, const key & receiverPk); + void ecdhDecodeFromSharedSecret(ecdhTuple & masked, const key & sharedSec1); void ecdhDecode(ecdhTuple & masked, const key & receiverSk); } #endif /* RCTOPS_H */ diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index fa9c833dd..0d4fbee1a 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -535,7 +535,7 @@ namespace rct { // must know the destination private key to find the correct amount, else will return a random number // Note: For txn fees, the last index in the amounts vector should contain that // Thus the amounts vector will be "one" longer than the destinations vectort - rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector & amounts, const ctkeyM &mixRing, unsigned int index, ctkeyV &outSk) { + rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector & amounts, const ctkeyM &mixRing, const keyV &amount_keys, unsigned int index, ctkeyV &outSk) { CHECK_AND_ASSERT_THROW_MES(amounts.size() == destinations.size() || amounts.size() == destinations.size() + 1, "Different number of amounts/destinations"); CHECK_AND_ASSERT_THROW_MES(index < mixRing.size(), "Bad index into mixRing"); for (size_t n = 0; n < mixRing.size(); ++n) { @@ -563,7 +563,7 @@ namespace rct { //mask amount and mask rv.ecdhInfo[i].mask = copy(outSk[i].mask); rv.ecdhInfo[i].amount = d2h(amounts[i]); - ecdhEncode(rv.ecdhInfo[i], destinations[i]); + ecdhEncodeFromSharedSecret(rv.ecdhInfo[i], amount_keys[i]); } @@ -584,17 +584,17 @@ namespace rct { return rv; } - rctSig genRct(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector & amounts, const int mixin) { + rctSig genRct(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector & amounts, const keyV &amount_keys, const int mixin) { unsigned int index; ctkeyM mixRing; ctkeyV outSk; tie(mixRing, index) = populateFromBlockchain(inPk, mixin); - return genRct(message, inSk, destinations, amounts, mixRing, index, outSk); + return genRct(message, inSk, destinations, amounts, mixRing, amount_keys, index, outSk); } //RCT simple //for post-rct only - 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) { + rctSig genRctSimple(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector &inamounts, const vector &outamounts, xmr_amount txnFee, const ctkeyM & mixRing, const keyV &amount_keys, const std::vector & index, ctkeyV &outSk) { CHECK_AND_ASSERT_THROW_MES(inamounts.size() > 0, "Empty inamounts"); CHECK_AND_ASSERT_THROW_MES(inamounts.size() == inSk.size(), "Different number of inamounts/inSk"); CHECK_AND_ASSERT_THROW_MES(outamounts.size() == destinations.size(), "Different number of amounts/destinations"); @@ -630,7 +630,7 @@ namespace rct { //mask amount and mask rv.ecdhInfo[i].mask = copy(outSk[i].mask); rv.ecdhInfo[i].amount = d2h(outamounts[i]); - ecdhEncode(rv.ecdhInfo[i], destinations[i]); + ecdhEncodeFromSharedSecret(rv.ecdhInfo[i], amount_keys[i]); } //set txn fee @@ -656,7 +656,7 @@ namespace rct { return rv; } - 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 ctkeyV & inPk, const keyV & destinations, const vector &inamounts, const vector &outamounts, const keyV &amount_keys, xmr_amount txnFee, unsigned int mixin) { std::vector index; index.resize(inPk.size()); ctkeyM mixRing; @@ -666,7 +666,7 @@ namespace rct { mixRing[i].resize(mixin+1); index[i] = populateFromBlockchainSimple(mixRing[i], inPk[i], mixin); } - return genRctSimple(message, inSk, destinations, inamounts, outamounts, txnFee, mixRing, index, outSk); + return genRctSimple(message, inSk, destinations, inamounts, outamounts, txnFee, mixRing, amount_keys, index, outSk); } //RingCT protocol @@ -782,7 +782,7 @@ 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 - xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, key & mask) { + static xmr_amount decodeRctMain(const rctSig & rv, const key & sk, unsigned int i, key & mask, void (*decode)(ecdhTuple&, const key&)) { CHECK_AND_ASSERT_MES(!rv.simple, false, "decodeRct called on simple rctSig"); CHECK_AND_ASSERT_THROW_MES(rv.rangeSigs.size() > 0, "Empty rv.rangeSigs"); CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.rangeSigs.size(), "Mismatched sizes of rv.outPk and rv.rangeSigs"); @@ -790,7 +790,7 @@ namespace rct { //mask amount and mask ecdhTuple ecdh_info = rv.ecdhInfo[i]; - ecdhDecode(ecdh_info, sk); + (*decode)(ecdh_info, sk); mask = ecdh_info.mask; key amount = ecdh_info.amount; key C = rv.outPk[i].mask; @@ -806,12 +806,20 @@ namespace rct { return h2d(amount); } + xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, key & mask) { + return decodeRctMain(rv, sk, i, mask, &ecdhDecode); + } + + xmr_amount decodeRctFromSharedSecret(const rctSig & rv, const key & sk, unsigned int i, key & mask) { + return decodeRctMain(rv, sk, i, mask, &ecdhDecodeFromSharedSecret); + } + xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i) { key mask; return decodeRct(rv, sk, i, mask); } - xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i, key &mask) { + static xmr_amount decodeRctSimpleMain(const rctSig & rv, const key & sk, unsigned int i, key &mask, void (*decode)(ecdhTuple &ecdh, const key&)) { CHECK_AND_ASSERT_MES(rv.simple, false, "decodeRct called on non simple rctSig"); CHECK_AND_ASSERT_THROW_MES(rv.rangeSigs.size() > 0, "Empty rv.rangeSigs"); CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.rangeSigs.size(), "Mismatched sizes of rv.outPk and rv.rangeSigs"); @@ -819,7 +827,7 @@ namespace rct { //mask amount and mask ecdhTuple ecdh_info = rv.ecdhInfo[i]; - ecdhDecode(ecdh_info, sk); + (*decode)(ecdh_info, sk); mask = ecdh_info.mask; key amount = ecdh_info.amount; key C = rv.outPk[i].mask; @@ -835,6 +843,14 @@ namespace rct { return h2d(amount); } + xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i, key &mask) { + return decodeRctSimpleMain(rv, sk, i, mask, &ecdhDecode); + } + + xmr_amount decodeRctSimpleFromSharedSecret(const rctSig & rv, const key & sk, unsigned int i, key &mask) { + return decodeRctSimpleMain(rv, sk, i, mask, &ecdhDecodeFromSharedSecret); + } + xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i) { key mask; return decodeRctSimple(rv, sk, i, mask); diff --git a/src/ringct/rctSigs.h b/src/ringct/rctSigs.h index 2064962c3..d150e7180 100644 --- a/src/ringct/rctSigs.h +++ b/src/ringct/rctSigs.h @@ -135,18 +135,20 @@ 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 - rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector & amounts, const ctkeyM &mixRing, unsigned int index, ctkeyV &outSk); - rctSig genRct(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector & amounts, const int mixin); - 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); + rctSig genRct(const key &message, const ctkeyV & inSk, const keyV & destinations, const vector & amounts, const ctkeyM &mixRing, const keyV &amount_keys, unsigned int index, ctkeyV &outSk); + rctSig genRct(const key &message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector & amounts, const keyV &amount_keys, const int mixin); + rctSig genRctSimple(const key & message, const ctkeyV & inSk, const ctkeyV & inPk, const keyV & destinations, const vector & inamounts, const vector & outamounts, const keyV &amount_keys, 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 keyV &amount_keys, const std::vector & index, ctkeyV &outSk); bool verRct(const rctSig & rv); 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 ctkeyV &outPk, const key &message); xmr_amount decodeRct(const rctSig & rv, const key & sk, unsigned int i, key & mask); + xmr_amount decodeRctFromSharedSecret(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); + xmr_amount decodeRctSimpleFromSharedSecret(const rctSig & rv, const key & sk, unsigned int i, key & mask); xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i, key & mask); + xmr_amount decodeRctSimple(const rctSig & rv, const key & sk, unsigned int i); } #endif /* RCTSIGS_H */ diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h index 7e31f679d..d36d3f1b1 100644 --- a/src/ringct/rctTypes.h +++ b/src/ringct/rctTypes.h @@ -341,6 +341,8 @@ namespace rct { namespace cryptonote { static inline bool operator==(const crypto::public_key &k0, const rct::key &k1) { return !memcmp(&k0, &k1, 32); } static inline bool operator!=(const crypto::public_key &k0, const rct::key &k1) { return memcmp(&k0, &k1, 32); } + static inline bool operator==(const crypto::secret_key &k0, const rct::key &k1) { return !memcmp(&k0, &k1, 32); } + static inline bool operator!=(const crypto::secret_key &k0, const rct::key &k1) { return memcmp(&k0, &k1, 32); } } template std::ostream &print256(std::ostream &o, const T &v); diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index c8fae6edd..709990c7c 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -2959,12 +2959,9 @@ bool simple_wallet::get_tx_key(const std::vector &args_) crypto::secret_key tx_key; std::vector amount_keys; - if (m_wallet->get_tx_keys(txid, tx_key, amount_keys)) + if (m_wallet->get_tx_key(txid, tx_key)) { - std::string s = epee::string_tools::pod_to_hex(tx_key); - for (const auto &k: amount_keys) - s += epee::string_tools::pod_to_hex(k); - success_msg_writer() << tr("Tx key: ") << s; + success_msg_writer() << tr("Tx key: ") << epee::string_tools::pod_to_hex(tx_key); return true; } else @@ -3001,17 +2998,14 @@ bool simple_wallet::check_tx_key(const std::vector &args_) fail_msg_writer() << tr("failed to parse tx key"); return true; } - std::vector tx_keys; - for (size_t start = 0; start < local_args[1].size(); start += 64) + crypto::secret_key tx_key; + cryptonote::blobdata tx_key_data; + if(!epee::string_tools::parse_hexstr_to_binbuff(local_args[1], tx_key_data)) { - cryptonote::blobdata tx_key_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(std::string(&local_args[1][start], 64), tx_key_data)) - { - fail_msg_writer() << tr("failed to parse tx key"); - return true; - } - tx_keys.push_back(*reinterpret_cast(tx_key_data.data())); + fail_msg_writer() << tr("failed to parse tx key"); + return true; } + tx_key = *reinterpret_cast(tx_key_data.data()); cryptonote::account_public_address address; bool has_payment_id; @@ -3056,18 +3050,12 @@ bool simple_wallet::check_tx_key(const std::vector &args_) } crypto::key_derivation derivation; - if (!crypto::generate_key_derivation(address.m_view_public_key, tx_keys[0], derivation)) + if (!crypto::generate_key_derivation(address.m_view_public_key, tx_key, derivation)) { fail_msg_writer() << tr("failed to generate key derivation from supplied parameters"); return true; } - if (tx_keys.size() != tx.vout.size() * 2 + 1) - { - fail_msg_writer() << tr("tx keys don't match tx vout"); - return true; - } - uint64_t received = 0; try { for (size_t n = 0; n < tx.vout.size(); ++n) @@ -3089,9 +3077,13 @@ bool simple_wallet::check_tx_key(const std::vector &args_) try { rct::key Ctmp; - rct::addKeys2(Ctmp, rct::sk2rct(tx_keys[n * 2 + 2]), rct::sk2rct(tx_keys[n * 2 + 1]), rct::H); - if (rct::equalKeys(tx.rct_signatures.outPk[n].mask, Ctmp)) - amount = rct::h2d(rct::sk2rct(tx_keys[n * 2 + 1])); + rct::key amount_key = rct::hash_to_scalar(rct::scalarmultKey(rct::pk2rct(address.m_view_public_key), rct::sk2rct(tx_key))); + rct::ecdhTuple ecdh_info = tx.rct_signatures.ecdhInfo[n]; + rct::ecdhDecodeFromSharedSecret(ecdh_info, amount_key); + rct::key C = tx.rct_signatures.outPk[n].mask; + rct::addKeys2(Ctmp, ecdh_info.mask, ecdh_info.amount, rct::H); + if (rct::equalKeys(C, Ctmp)) + amount = rct::h2d(ecdh_info.amount); else amount = 0; } diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 70f9043f0..69063e780 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -195,10 +195,18 @@ void wallet2::check_acc_out(const account_keys &acc, const tx_out &o, const cryp //---------------------------------------------------------------------------------------------------- static uint64_t decodeRct(const rct::rctSig & rv, const rct::key & sk, unsigned int i, rct::key & mask) { - if (rv.simple) - return rct::decodeRctSimple(rv, sk, i, mask); - else - return rct::decodeRct(rv, sk, i, mask); + try + { + if (rv.simple) + return rct::decodeRctSimpleFromSharedSecret(rv, sk, i, mask); + else + return rct::decodeRctFromSharedSecret(rv, sk, i, mask); + } + catch (const std::exception &e) + { + LOG_ERROR("Failed to decode input " << i); + return 0; + } } //---------------------------------------------------------------------------------------------------- void wallet2::process_new_transaction(const cryptonote::transaction& tx, const std::vector &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool) @@ -260,7 +268,11 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s outs.push_back(0); if (money_transfered == 0) - money_transfered = tools::decodeRct(tx.rct_signatures, rct::sk2rct(in_ephemeral[0].sec), 0, mask[0]); + { + const cryptonote::account_keys& keys = m_account.get_keys(); + rct::key amount_key = rct::hash_to_scalar(rct::scalarmultKey(rct::pk2rct(pub_key_field.pub_key), rct::sk2rct(keys.m_view_secret_key))); + money_transfered = tools::decodeRct(tx.rct_signatures, amount_key, 0, mask[0]); + } amount[0] = money_transfered; tx_money_got_in_outs = money_transfered; ++num_vouts_received; @@ -300,7 +312,11 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s outs.push_back(i); if (money_transfered[i] == 0) - money_transfered[i] = tools::decodeRct(tx.rct_signatures, rct::sk2rct(in_ephemeral[i].sec), i, mask[i]); + { + const cryptonote::account_keys& keys = m_account.get_keys(); + rct::key amount_key = rct::hash_to_scalar(rct::scalarmultKey(rct::pk2rct(pub_key_field.pub_key), rct::sk2rct(keys.m_view_secret_key))); + money_transfered[i] = tools::decodeRct(tx.rct_signatures, amount_key, i, mask[i]); + } tx_money_got_in_outs += money_transfered[i]; amount[i] = money_transfered[i]; ++num_vouts_received; @@ -345,7 +361,11 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s outs.push_back(i); if (money_transfered[i] == 0) - money_transfered[i] = tools::decodeRct(tx.rct_signatures, rct::sk2rct(in_ephemeral[i].sec), i, mask[i]); + { + const cryptonote::account_keys& keys = m_account.get_keys(); + rct::key amount_key = rct::hash_to_scalar(rct::scalarmultKey(rct::pk2rct(pub_key_field.pub_key), rct::sk2rct(keys.m_view_secret_key))); + money_transfered[i] = tools::decodeRct(tx.rct_signatures, amount_key, i, mask[i]); + } tx_money_got_in_outs += money_transfered[i]; amount[i] = money_transfered[i]; ++num_vouts_received; @@ -374,7 +394,11 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, const s outs.push_back(i); if (money_transfered == 0) - money_transfered = tools::decodeRct(tx.rct_signatures, rct::sk2rct(in_ephemeral[i].sec), i, mask[i]); + { + const cryptonote::account_keys& keys = m_account.get_keys(); + rct::key amount_key = rct::hash_to_scalar(rct::scalarmultKey(rct::pk2rct(pub_key_field.pub_key), rct::sk2rct(keys.m_view_secret_key))); + money_transfered = tools::decodeRct(tx.rct_signatures, amount_key, i, mask[i]); + } amount[i] = money_transfered; tx_money_got_in_outs += money_transfered; ++num_vouts_received; @@ -1270,7 +1294,6 @@ bool wallet2::clear() m_unconfirmed_txs.clear(); m_payments.clear(); m_tx_keys.clear(); - m_amount_keys.clear(); m_confirmed_txs.clear(); m_local_bc_height = 1; return true; @@ -2429,7 +2452,6 @@ void wallet2::commit_tx(pending_tx& ptx) if (store_tx_info()) { m_tx_keys.insert(std::make_pair(txid, ptx.tx_key)); - m_amount_keys.insert(std::make_pair(txid, ptx.amount_keys)); } LOG_PRINT_L2("transaction " << txid << " generated ok and sent to daemon, key_images: [" << ptx.key_images << "]"); @@ -3974,15 +3996,12 @@ std::vector wallet2::create_unmixable_sweep_transactions(bo } } -bool wallet2::get_tx_keys(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector &amount_keys) const +bool wallet2::get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key) const { const std::unordered_map::const_iterator i = m_tx_keys.find(txid); if (i == m_tx_keys.end()) return false; tx_key = i->second; - const std::unordered_map>::const_iterator j = m_amount_keys.find(txid); - if (j != m_amount_keys.end()) - amount_keys = j->second; return true; } diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 2631f630b..3be46882a 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -354,7 +354,6 @@ namespace tools a & m_unconfirmed_payments; if(ver < 14) return; - a & m_amount_keys; } /*! @@ -390,7 +389,7 @@ namespace tools bool auto_refresh() const { return m_auto_refresh; } void auto_refresh(bool r) { m_auto_refresh = r; } - bool get_tx_keys(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector &amount_keys) const; + bool get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key) const; bool use_fork_rules(uint8_t version); @@ -471,7 +470,6 @@ namespace tools std::unordered_map m_confirmed_txs; std::unordered_map m_unconfirmed_payments; std::unordered_map m_tx_keys; - std::unordered_map> m_amount_keys; transfer_container m_transfers; payment_container m_payments; diff --git a/tests/core_tests/rct.cpp b/tests/core_tests/rct.cpp index a07c72049..8dbf8b272 100644 --- a/tests/core_tests/rct.cpp +++ b/tests/core_tests/rct.cpp @@ -125,13 +125,11 @@ bool gen_rct_tx_validation_base::generate_with(std::vector& ev crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(rct_txes[n]); for (size_t o = 0; o < 4; ++o) { - cryptonote::keypair in_ephemeral; - crypto::key_image ki; - cryptonote::generate_key_image_helper(miner_accounts[n].get_keys(), tx_pub_key, o, in_ephemeral, ki); + rct::key amount_key = rct::hash_to_scalar(rct::scalarmultKey(rct::pk2rct(tx_pub_key), rct::sk2rct(miner_accounts[n].get_keys().m_view_secret_key))); if (rct_txes[n].rct_signatures.simple) - rct::decodeRctSimple(rct_txes[n].rct_signatures, rct::sk2rct(in_ephemeral.sec), o, rct_tx_masks[o+n*4]); + rct::decodeRctSimpleFromSharedSecret(rct_txes[n].rct_signatures, amount_key, o, rct_tx_masks[o+n*4]); else - rct::decodeRct(rct_txes[n].rct_signatures, rct::sk2rct(in_ephemeral.sec), o, rct_tx_masks[o+n*4]); + rct::decodeRctFromSharedSecret(rct_txes[n].rct_signatures, amount_key, o, rct_tx_masks[o+n*4]); } CHECK_AND_ASSERT_MES(generator.construct_block_manually(blk_txes[n], blk_last, miner_account, diff --git a/tests/unit_tests/ringct.cpp b/tests/unit_tests/ringct.cpp index 7970fedb4..81a63b502 100644 --- a/tests/unit_tests/ringct.cpp +++ b/tests/unit_tests/ringct.cpp @@ -171,10 +171,12 @@ TEST(ringct, range_proofs) sc.push_back(sctmp); pc.push_back(pctmp); vectoramounts; - + rct::keyV amount_keys; + key mask; //add output 500 amounts.push_back(500); + amount_keys.push_back(rct::hash_to_scalar(rct::zero())); keyV destinations; key Sk, Pk; skpkGen(Sk, Pk); @@ -183,17 +185,18 @@ TEST(ringct, range_proofs) //add output for 12500 amounts.push_back(12500); + amount_keys.push_back(rct::hash_to_scalar(rct::zero())); skpkGen(Sk, Pk); destinations.push_back(Pk); //compute rct data with mixin 500 - rctSig s = genRct(rct::zero(), sc, pc, destinations, amounts, 3); + rctSig s = genRct(rct::zero(), sc, pc, destinations, amounts, amount_keys, 3); //verify rct data ASSERT_TRUE(verRct(s)); //decode received amount - ASSERT_TRUE(decodeRct(s, Sk, 1)); + ASSERT_TRUE(decodeRctFromSharedSecret(s, amount_keys[1], 1, mask)); // Ring CT with failing MG sig part should not verify! // Since sum of inputs != outputs @@ -204,13 +207,13 @@ TEST(ringct, range_proofs) //compute rct data with mixin 500 - s = genRct(rct::zero(), sc, pc, destinations, amounts, 3); + s = genRct(rct::zero(), sc, pc, destinations, amounts, amount_keys, 3); //verify rct data ASSERT_FALSE(verRct(s)); //decode received amount - ASSERT_TRUE(decodeRct(s, Sk, 1)); + ASSERT_TRUE(decodeRctFromSharedSecret(s, amount_keys[1], 1, mask)); } TEST(ringct, range_proofs_with_fee) @@ -229,10 +232,12 @@ TEST(ringct, range_proofs_with_fee) sc.push_back(sctmp); pc.push_back(pctmp); vectoramounts; - + keyV amount_keys; + key mask; //add output 500 amounts.push_back(500); + amount_keys.push_back(rct::hash_to_scalar(rct::zero())); keyV destinations; key Sk, Pk; skpkGen(Sk, Pk); @@ -241,20 +246,22 @@ TEST(ringct, range_proofs_with_fee) //add txn fee for 1 //has no corresponding destination.. amounts.push_back(1); + amount_keys.push_back(hash_to_scalar(zero())); //add output for 12500 amounts.push_back(12500); + amount_keys.push_back(hash_to_scalar(zero())); skpkGen(Sk, Pk); destinations.push_back(Pk); //compute rct data with mixin 500 - rctSig s = genRct(rct::zero(), sc, pc, destinations, amounts, 3); + rctSig s = genRct(rct::zero(), sc, pc, destinations, amounts, amount_keys, 3); //verify rct data ASSERT_TRUE(verRct(s)); //decode received amount - ASSERT_TRUE(decodeRct(s, Sk, 1)); + ASSERT_TRUE(decodeRctFromSharedSecret(s, amount_keys[1], 1, mask)); // Ring CT with failing MG sig part should not verify! // Since sum of inputs != outputs @@ -265,13 +272,13 @@ TEST(ringct, range_proofs_with_fee) //compute rct data with mixin 500 - s = genRct(rct::zero(), sc, pc, destinations, amounts, 3); + s = genRct(rct::zero(), sc, pc, destinations, amounts, amount_keys, 3); //verify rct data ASSERT_FALSE(verRct(s)); //decode received amount - ASSERT_TRUE(decodeRct(s, Sk, 1)); + ASSERT_TRUE(decodeRctFromSharedSecret(s, amount_keys[1], 1, mask)); } TEST(ringct, simple) @@ -284,6 +291,8 @@ TEST(ringct, simple) vectorinamounts; //this keyV corresponds to destination pubkeys keyV destinations; + keyV amount_keys; + key mask; //add fake input 3000 //the sc is secret data @@ -303,6 +312,7 @@ TEST(ringct, simple) //add output 5000 outamounts.push_back(5000); + amount_keys.push_back(rct::hash_to_scalar(rct::zero())); //add the corresponding destination pubkey key Sk, Pk; skpkGen(Sk, Pk); @@ -310,6 +320,7 @@ TEST(ringct, simple) //add output 999 outamounts.push_back(999); + amount_keys.push_back(rct::hash_to_scalar(rct::zero())); //add the corresponding destination pubkey skpkGen(Sk, Pk); destinations.push_back(Pk); @@ -319,13 +330,13 @@ TEST(ringct, simple) //compute sig with mixin 2 xmr_amount txnfee = 1; - rctSig s = genRctSimple(message, sc, pc, destinations,inamounts, outamounts, txnfee, 2); + rctSig s = genRctSimple(message, sc, pc, destinations,inamounts, outamounts, amount_keys, txnfee, 2); //verify ring ct signature ASSERT_TRUE(verRctSimple(s)); //decode received amount corresponding to output pubkey index 1 - ASSERT_TRUE(decodeRctSimple(s, Sk, 1)); + ASSERT_TRUE(decodeRctSimpleFromSharedSecret(s, amount_keys[1], 1, mask)); } static rct::rctSig make_sample_rct_sig(int n_inputs, const uint64_t input_amounts[], int n_outputs, const uint64_t output_amounts[], bool last_is_fee) @@ -334,6 +345,7 @@ static rct::rctSig make_sample_rct_sig(int n_inputs, const uint64_t input_amount ctkey sctmp, pctmp; vectoramounts; keyV destinations; + keyV amount_keys; key Sk, Pk; for (int n = 0; n < n_inputs; ++n) { @@ -344,12 +356,13 @@ static rct::rctSig make_sample_rct_sig(int n_inputs, const uint64_t input_amount for (int n = 0; n < n_outputs; ++n) { amounts.push_back(output_amounts[n]); + amount_keys.push_back(rct::hash_to_scalar(rct::zero())); skpkGen(Sk, Pk); if (n < n_outputs - 1 || !last_is_fee) destinations.push_back(Pk); } - return genRct(rct::zero(), sc, pc, destinations, amounts, 3);; + return genRct(rct::zero(), sc, pc, destinations, amounts, amount_keys, 3);; } static rct::rctSig make_sample_simple_rct_sig(int n_inputs, const uint64_t input_amounts[], int n_outputs, const uint64_t output_amounts[], uint64_t fee) @@ -358,6 +371,7 @@ static rct::rctSig make_sample_simple_rct_sig(int n_inputs, const uint64_t input ctkey sctmp, pctmp; vector inamounts, outamounts; keyV destinations; + keyV amount_keys; key Sk, Pk; for (int n = 0; n < n_inputs; ++n) { @@ -369,11 +383,12 @@ static rct::rctSig make_sample_simple_rct_sig(int n_inputs, const uint64_t input for (int n = 0; n < n_outputs; ++n) { outamounts.push_back(output_amounts[n]); + amount_keys.push_back(hash_to_scalar(zero())); skpkGen(Sk, Pk); destinations.push_back(Pk); } - return genRctSimple(rct::zero(), sc, pc, destinations, inamounts, outamounts, fee, 3);; + return genRctSimple(rct::zero(), sc, pc, destinations, inamounts, outamounts, amount_keys, fee, 3);; } static bool range_proof_test(bool expected_valid, diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp index 380c2140f..0a53aeae0 100644 --- a/tests/unit_tests/serialization.cpp +++ b/tests/unit_tests/serialization.cpp @@ -554,6 +554,7 @@ TEST(Serialization, serializes_ringct_types) sc.push_back(sctmp); pc.push_back(pctmp); vector amounts; + rct::keyV amount_keys; //add output 500 amounts.push_back(500); rct::keyV destinations; @@ -562,10 +563,11 @@ TEST(Serialization, serializes_ringct_types) destinations.push_back(Pk); //add output for 12500 amounts.push_back(12500); + amount_keys.push_back(rct::hash_to_scalar(rct::zero())); rct::skpkGen(Sk, Pk); destinations.push_back(Pk); //compute rct data with mixin 500 - s0 = rct::genRct(rct::zero(), sc, pc, destinations, amounts, 3); + s0 = rct::genRct(rct::zero(), sc, pc, destinations, amounts, amount_keys, 3); mg0 = s0.MG; ASSERT_TRUE(serialization::dump_binary(mg0, blob)); -- cgit v1.2.3 From b337aea6cce707152143750e14b956a8ae40390f Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Thu, 28 Jul 2016 20:14:15 +0100 Subject: rct: do not serialize senderPk - it is not used anymore --- src/cryptonote_core/cryptonote_boost_serialization.h | 2 +- src/ringct/rctTypes.h | 2 +- tests/unit_tests/serialization.cpp | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) (limited to 'tests/unit_tests/serialization.cpp') diff --git a/src/cryptonote_core/cryptonote_boost_serialization.h b/src/cryptonote_core/cryptonote_boost_serialization.h index 81f5f081a..f222db94e 100644 --- a/src/cryptonote_core/cryptonote_boost_serialization.h +++ b/src/cryptonote_core/cryptonote_boost_serialization.h @@ -219,7 +219,7 @@ namespace boost { a & x.mask; a & x.amount; - a & x.senderPk; + // a & x.senderPk; // not serialized, as we do not use it in monero currently } inline void serializeOutPk(boost::archive::binary_iarchive &a, rct::ctkeyV &outPk_, const boost::serialization::version_type ver) diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h index d36d3f1b1..1ba280aba 100644 --- a/src/ringct/rctTypes.h +++ b/src/ringct/rctTypes.h @@ -116,7 +116,7 @@ namespace rct { BEGIN_SERIALIZE_OBJECT() FIELD(mask) FIELD(amount) - FIELD(senderPk) + // FIELD(senderPk) // not serialized, as we do not use it in monero currently END_SERIALIZE() }; diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp index 0a53aeae0..0da23c0d2 100644 --- a/tests/unit_tests/serialization.cpp +++ b/tests/unit_tests/serialization.cpp @@ -532,7 +532,9 @@ TEST(Serialization, serializes_ringct_types) ecdh0.senderPk = rct::skGen(); ASSERT_TRUE(serialization::dump_binary(ecdh0, blob)); ASSERT_TRUE(serialization::parse_binary(blob, ecdh1)); - ASSERT_TRUE(!memcmp(&ecdh0, &ecdh1, sizeof(ecdh0))); + ASSERT_TRUE(!memcmp(&ecdh0.mask, &ecdh1.mask, sizeof(ecdh0.mask))); + ASSERT_TRUE(!memcmp(&ecdh0.amount, &ecdh1.amount, sizeof(ecdh0.amount))); + // senderPk is not serialized for (size_t n = 0; n < 64; ++n) { -- cgit v1.2.3 From c5be4b0beaaa7a703d4e2b84aa9f3c727bf992df Mon Sep 17 00:00:00 2001 From: Shen Noether Date: Mon, 8 Aug 2016 12:54:00 +0100 Subject: rct: avoid the need for the last II element This element is used in the generation of the MLSAG, but isn't needed in verification. Also misc changes in the cryptonote code to match, by mooo. --- src/cryptonote_core/blockchain.cpp | 14 +++-- .../cryptonote_boost_serialization.h | 13 ++--- src/ringct/rctSigs.cpp | 62 ++++++++++++++-------- src/ringct/rctSigs.h | 4 +- src/ringct/rctTypes.h | 11 +--- tests/unit_tests/serialization.cpp | 6 +-- 6 files changed, 55 insertions(+), 55 deletions(-) (limited to 'tests/unit_tests/serialization.cpp') diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index f6f56b086..91a9d5d6f 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -2475,12 +2475,12 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context 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, and always do outPk - // all MGs should have the same II size (1) + // all MGs should have empty II for (size_t n = 0; n < tx.rct_signatures.MGs.size(); ++n) { - if (tx.rct_signatures.MGs[n].II.size() != 1) + if (tx.rct_signatures.MGs[n].II.size() != 0) { - LOG_PRINT_L1("Failed to check ringct signatures: mismatched MGs II sizes"); + LOG_PRINT_L1("Failed to check ringct signatures: non empty MGs II"); return false; } } @@ -2489,7 +2489,6 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context for (size_t n = 0; n < tx.vin.size(); ++n) { reconstructed_II[n].push_back(rct::ki2rct(boost::get(tx.vin[n]).k_image)); - reconstructed_II[n].push_back(tx.rct_signatures.MGs[n].II[0]); } if (tx.rct_signatures.outPk.size() != tx.vout.size()) @@ -2579,7 +2578,7 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context // if the tx already has a non empty mixRing and/or II, use 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::keyV &II = tx.rct_signatures.MG.II.empty() ? reconstructed_II : tx.rct_signatures.MG.II; const rct::ctkeyV outPk = reconstructed_outPk; // RCT needs the same mixin for all inputs @@ -2604,14 +2603,13 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context } } - if (tx.rct_signatures.MG.II.size() == 1) + if (tx.rct_signatures.MG.II.empty()) { reconstructed_II.resize(tx.vin.size()); for (size_t n = 0; n < tx.vin.size(); ++n) { reconstructed_II[n] = rct::ki2rct(boost::get(tx.vin[n]).k_image); } - reconstructed_II.push_back(tx.rct_signatures.MG.II.back()); } if (tx.rct_signatures.outPk.size() != tx.vout.size()) @@ -2657,7 +2655,7 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context } } - if (II.size() != 1 + tx.vin.size()) + if (II.size() != tx.vin.size()) { LOG_PRINT_L1("Failed to check ringct signatures: mismatched II/vin sizes"); return false; diff --git a/src/cryptonote_core/cryptonote_boost_serialization.h b/src/cryptonote_core/cryptonote_boost_serialization.h index 41f864803..8a82aa7a7 100644 --- a/src/cryptonote_core/cryptonote_boost_serialization.h +++ b/src/cryptonote_core/cryptonote_boost_serialization.h @@ -209,19 +209,12 @@ namespace boost a & x.s; } - inline void serialize(boost::archive::binary_iarchive &a, rct::mgSig &x, const boost::serialization::version_type ver) - { - a & x.ss; - a & x.cc; - x.II.resize(1); - a & x.II[0]; - } - - inline void serialize(boost::archive::binary_oarchive &a, rct::mgSig &x, const boost::serialization::version_type ver) + template + inline void serialize(Archive &a, rct::mgSig &x, const boost::serialization::version_type ver) { a & x.ss; a & x.cc; - a & x.II.back(); + // a & x.II; // not serialized, we can recover it from the tx vin } template diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index 0d4fbee1a..c252645f8 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -148,7 +148,7 @@ namespace rct { // Gen creates a signature which proves that for some column in the keymatrix "pk" // the signer knows a secret key for each row in that column // Ver verifies that the MG sig was created correctly - mgSig MLSAG_Gen(key message, const keyM & pk, const keyV & xx, const unsigned int index) { + mgSig MLSAG_Gen(key message, const keyM & pk, const keyV & xx, const unsigned int index, size_t dsRows) { mgSig rv; size_t cols = pk.size(); CHECK_AND_ASSERT_THROW_MES(cols >= 2, "Error! What is c if cols = 1!"); @@ -159,20 +159,21 @@ namespace rct { CHECK_AND_ASSERT_THROW_MES(pk[i].size() == rows, "pk is not rectangular"); } CHECK_AND_ASSERT_THROW_MES(xx.size() == rows, "Bad xx size"); + CHECK_AND_ASSERT_THROW_MES(dsRows <= rows, "Bad dsRows size"); - size_t i = 0, j = 0; + size_t i = 0, j = 0, ii = 0; key c, c_old, L, R, Hi; sc_0(c_old.bytes); - vector Ip(rows); - rv.II = keyV(rows); - rv.ss = keyM(cols, rv.II); + vector Ip(dsRows); + rv.II = keyV(dsRows); keyV alpha(rows); keyV aG(rows); - keyV aHP(rows); - keyV toHash(1 + 3 * rows); + rv.ss = keyM(cols, aG); + keyV aHP(dsRows); + keyV toHash(1 + 3 * dsRows + 2 * (rows - dsRows)); toHash[0] = message; DP("here1"); - for (i = 0; i < rows; i++) { + for (i = 0; i < dsRows; i++) { skpkGen(alpha[i], aG[i]); //need to save alphas for later.. Hi = hashToPoint(pk[index][i]); aHP[i] = scalarmultKey(Hi, alpha[i]); @@ -182,6 +183,13 @@ namespace rct { rv.II[i] = scalarmultKey(Hi, xx[i]); precomp(Ip[i].k, rv.II[i]); } + size_t ndsRows = 3 * dsRows; //non Double Spendable Rows (see identity chains paper) + for (i = dsRows, ii = 0 ; i < rows ; i++, ii++) { + skpkGen(alpha[i], aG[i]); //need to save alphas for later.. + toHash[ndsRows + 2 * ii + 1] = pk[index][i]; + toHash[ndsRows + 2 * ii + 2] = aG[i]; + } + c_old = hash_to_scalar(toHash); @@ -193,7 +201,7 @@ namespace rct { rv.ss[i] = skvGen(rows); sc_0(c.bytes); - for (j = 0; j < rows; j++) { + for (j = 0; j < dsRows; j++) { addKeys2(L, rv.ss[i][j], c_old, pk[i][j]); hashToPoint(Hi, pk[i][j]); addKeys3(R, rv.ss[i][j], Hi, c_old, Ip[j].k); @@ -201,6 +209,11 @@ namespace rct { toHash[3 * j + 2] = L; toHash[3 * j + 3] = R; } + for (j = dsRows, ii = 0; j < rows; j++, ii++) { + addKeys2(L, rv.ss[i][j], c_old, pk[i][j]); + toHash[ndsRows + 2 * ii + 1] = pk[i][j]; + toHash[ndsRows + 2 * ii + 2] = L; + } c = hash_to_scalar(toHash); copy(c_old, c); i = (i + 1) % cols; @@ -224,7 +237,7 @@ namespace rct { // Gen creates a signature which proves that for some column in the keymatrix "pk" // the signer knows a secret key for each row in that column // Ver verifies that the MG sig was created correctly - bool MLSAG_Ver(key message, const keyM & pk, const mgSig & rv, const keyV &II) { + bool MLSAG_Ver(key message, const keyM & pk, const mgSig & rv, const keyV &II, size_t dsRows) { size_t cols = pk.size(); CHECK_AND_ASSERT_MES(cols >= 2, false, "Error! What is c if cols = 1!"); @@ -233,25 +246,27 @@ namespace rct { for (size_t i = 1; i < cols; ++i) { CHECK_AND_ASSERT_MES(pk[i].size() == rows, false, "pk is not rectangular"); } - CHECK_AND_ASSERT_MES(II.size() == rows, false, "Bad II size"); + CHECK_AND_ASSERT_MES(II.size() == dsRows, false, "Bad II size"); CHECK_AND_ASSERT_MES(rv.ss.size() == cols, false, "Bad rv.ss size"); for (size_t i = 0; i < cols; ++i) { CHECK_AND_ASSERT_MES(rv.ss[i].size() == rows, false, "rv.ss is not rectangular"); } + CHECK_AND_ASSERT_MES(dsRows <= rows, false, "Bad dsRows value"); - size_t i = 0, j = 0; + size_t i = 0, j = 0, ii = 0; key c, L, R, Hi; key c_old = copy(rv.cc); - vector Ip(rows); - for (i= 0 ; i< rows ; i++) { + vector Ip(dsRows); + for (i = 0 ; i < dsRows ; i++) { precomp(Ip[i].k, II[i]); } - keyV toHash(1 + 3 * rows); + size_t ndsRows = 3 * dsRows; //non Double Spendable Rows (see identity chains paper + keyV toHash(1 + 3 * dsRows + 2 * (rows - dsRows)); toHash[0] = message; i = 0; while (i < cols) { sc_0(c.bytes); - for (j = 0; j < rows; j++) { + for (j = 0; j < dsRows; j++) { addKeys2(L, rv.ss[i][j], c_old, pk[i][j]); hashToPoint(Hi, pk[i][j]); addKeys3(R, rv.ss[i][j], Hi, c_old, Ip[j].k); @@ -259,6 +274,11 @@ namespace rct { toHash[3 * j + 2] = L; toHash[3 * j + 3] = R; } + for (j = dsRows, ii = 0 ; j < rows ; j++, ii++) { + addKeys2(L, rv.ss[i][j], c_old, pk[i][j]); + toHash[ndsRows + 2 * ii + 1] = pk[i][j]; + toHash[ndsRows + 2 * ii + 2] = L; + } c = hash_to_scalar(toHash); copy(c_old, c); i = (i + 1); @@ -376,7 +396,7 @@ namespace rct { ctkeyV signed_data = outPk; signed_data.push_back(ctkey({message, identity()})); key msg = cn_fast_hash(signed_data); - return MLSAG_Gen(msg, M, sk, index); + return MLSAG_Gen(msg, M, sk, index, rows); } @@ -403,7 +423,7 @@ namespace rct { sk[0] = copy(inSk.dest); sc_sub(sk[1].bytes, inSk.mask.bytes, a.bytes); } - return MLSAG_Gen(message, M, sk, index); + return MLSAG_Gen(message, M, sk, index, rows); } @@ -451,7 +471,7 @@ namespace rct { key msg = cn_fast_hash(signed_data); DP("message:"); DP(msg); - return MLSAG_Ver(msg, M, mg, II); + return MLSAG_Ver(msg, M, mg, II, rows); } //Ring-ct Simple MG sigs @@ -472,7 +492,7 @@ namespace rct { subKeys(M[i][1], pubs[i].mask, C); } //DP(C); - return MLSAG_Ver(message, M, mg, II); + return MLSAG_Ver(message, M, mg, II, rows); } //These functions get keys from blockchain @@ -729,7 +749,7 @@ namespace rct { { for (size_t n = 0; n < II->size(); ++n) { - CHECK_AND_ASSERT_MES((*II)[n].size() == 2, false, "Bad II size"); + CHECK_AND_ASSERT_MES((*II)[n].size() == 1, false, "Bad II size"); } } diff --git a/src/ringct/rctSigs.h b/src/ringct/rctSigs.h index d150e7180..558af22fd 100644 --- a/src/ringct/rctSigs.h +++ b/src/ringct/rctSigs.h @@ -90,8 +90,8 @@ namespace rct { // the signer knows a secret key for each row in that column // Ver verifies that the MG sig was created correctly keyV keyImageV(const keyV &xx); - mgSig MLSAG_Gen(key message, const keyM & pk, const keyV & xx, const unsigned int index); - bool MLSAG_Ver(key message, const keyM &pk, const mgSig &sig, const keyV &II); + mgSig MLSAG_Gen(key message, const keyM & pk, const keyV & xx, const unsigned int index, size_t dsRows); + bool MLSAG_Ver(key message, const keyM &pk, const mgSig &sig, const keyV &II, size_t dsRows); //mgSig MLSAG_Gen_Old(const keyM & pk, const keyV & xx, const int index); //proveRange and verRange diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h index 1ba280aba..8df403c68 100644 --- a/src/ringct/rctTypes.h +++ b/src/ringct/rctTypes.h @@ -148,16 +148,7 @@ namespace rct { BEGIN_SERIALIZE_OBJECT() FIELD(ss) FIELD(cc) - if (II.size() == 0) { - // loading - FIELD(II) - } - else { - // saving - keyV II; - II.push_back(this->II.back()); - FIELD(II) - } + // FIELD(II) - not serialized, it can be reconstructed END_SERIALIZE() }; //contains the data for an asnl sig diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp index 0da23c0d2..9311ca016 100644 --- a/tests/unit_tests/serialization.cpp +++ b/tests/unit_tests/serialization.cpp @@ -582,8 +582,7 @@ TEST(Serialization, serializes_ringct_types) ASSERT_TRUE(mg0.cc == mg1.cc); // mixRing and II are not serialized, they are meant to be reconstructed - ASSERT_TRUE(mg1.II.size() == 1); - ASSERT_TRUE(mg1.II[0] == mg0.II.back()); + ASSERT_TRUE(mg1.II.empty()); rg0 = s0.rangeSigs.front(); ASSERT_TRUE(serialization::dump_binary(rg0, blob)); @@ -605,8 +604,7 @@ TEST(Serialization, serializes_ringct_types) } ASSERT_TRUE(s0.MG.cc == s1.MG.cc); // mixRing and II are not serialized, they are meant to be reconstructed - ASSERT_TRUE(s1.MG.II.size() == 1); - ASSERT_TRUE(s1.MG.II[0] == s0.MG.II.back()); + ASSERT_TRUE(s1.MGs[0].II.empty()); // mixRing and II are not serialized, they are meant to be reconstructed ASSERT_TRUE(s1.mixRing.size() == 0); -- cgit v1.2.3 From 3ab2ab3e7691dadf91ef39ed477e12f0144b8278 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 8 Aug 2016 13:49:42 +0100 Subject: rct: change the simple flag to a type for future expansion --- src/cryptonote_core/blockchain.cpp | 12 +++++++++--- src/cryptonote_core/cryptonote_basic.h | 11 +++++++---- src/cryptonote_core/cryptonote_boost_serialization.h | 10 ++++++---- src/ringct/rctSigs.cpp | 12 ++++++------ src/ringct/rctTypes.h | 12 ++++++++---- src/wallet/wallet2.cpp | 10 ++++++++-- tests/core_tests/rct.cpp | 2 +- tests/unit_tests/serialization.cpp | 2 +- 8 files changed, 46 insertions(+), 25 deletions(-) (limited to 'tests/unit_tests/serialization.cpp') diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 91a9d5d6f..c81445f1d 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -2464,8 +2464,9 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context // from version 2, check ringct signatures // obviously, the original and simple rct APIs use a mixRing that's indexes // in opposite orders, because it'd be too simple otherwise... - if (tx.rct_signatures.simple) + switch (tx.rct_signatures.type) { + case rct::RCTTypeSimple: { rct::ctkeyM reconstructed_mixRing; std::vector reconstructed_II; rct::ctkeyV reconstructed_outPk; @@ -2568,9 +2569,9 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context LOG_PRINT_L1("Failed to check ringct signatures!"); return false; } + break; } - else - { + case rct::RCTTypeFull: { rct::ctkeyM reconstructed_mixRing; rct::keyV reconstructed_II; rct::ctkeyV reconstructed_outPk; @@ -2674,6 +2675,11 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context LOG_PRINT_L1("Failed to check ringct signatures!"); return false; } + break; + } + default: + LOG_PRINT_L1("Unsupported rct type: " << tx.rct_signatures.type); + return false; } } return true; diff --git a/src/cryptonote_core/cryptonote_basic.h b/src/cryptonote_core/cryptonote_basic.h index e5a5cc6f5..b68040597 100644 --- a/src/cryptonote_core/cryptonote_basic.h +++ b/src/cryptonote_core/cryptonote_basic.h @@ -231,18 +231,21 @@ namespace cryptonote else { FIELD(rct_signatures) - if (rct_signatures.simple) + switch (rct_signatures.type) { + case rct::RCTTypeSimple: if (rct_signatures.mixRing.size() && rct_signatures.mixRing.size() != vin.size()) return false; - } - else - { + break; + case rct::RCTTypeFull: for (size_t i = 0; i < rct_signatures.mixRing.size(); ++i) { if (rct_signatures.mixRing[i].size() != vin.size()) return false; } + break; + default: + return false; } } END_SERIALIZE() diff --git a/src/cryptonote_core/cryptonote_boost_serialization.h b/src/cryptonote_core/cryptonote_boost_serialization.h index 8a82aa7a7..7a7cf8588 100644 --- a/src/cryptonote_core/cryptonote_boost_serialization.h +++ b/src/cryptonote_core/cryptonote_boost_serialization.h @@ -248,15 +248,17 @@ namespace boost template inline void serialize(Archive &a, rct::rctSig &x, const boost::serialization::version_type ver) { - a & x.simple; + a & x.type; + if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple) + 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.rangeSigs; - if (x.simple) + if (x.type == rct::RCTTypeSimple) a & x.MGs; - else + if (x.type == rct::RCTTypeFull) a & x.MG; // a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets - if (x.simple) + if (x.type == rct::RCTTypeSimple) a & x.pseudoOuts; a & x.ecdhInfo; serializeOutPk(a, x.outPk, ver); diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index c252645f8..d031f6c79 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -563,7 +563,7 @@ namespace rct { } rctSig rv; - rv.simple = false; + rv.type = RCTTypeFull; rv.outPk.resize(destinations.size()); rv.rangeSigs.resize(destinations.size()); rv.ecdhInfo.resize(destinations.size()); @@ -625,7 +625,7 @@ namespace rct { } rctSig rv; - rv.simple = true; + rv.type = RCTTypeSimple; rv.message = message; rv.outPk.resize(destinations.size()); rv.rangeSigs.resize(destinations.size()); @@ -700,7 +700,7 @@ namespace rct { // 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 ctkeyV &outPk, const key &message) { - CHECK_AND_ASSERT_MES(!rv.simple, false, "verRct called on simple rctSig"); + CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull, false, "verRct called on non-full rctSig"); 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"); @@ -739,7 +739,7 @@ namespace rct { 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.type == RCTTypeSimple, false, "verRctSimple called on non simple rctSig"); 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"); @@ -803,7 +803,7 @@ namespace rct { // 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 static xmr_amount decodeRctMain(const rctSig & rv, const key & sk, unsigned int i, key & mask, void (*decode)(ecdhTuple&, const key&)) { - CHECK_AND_ASSERT_MES(!rv.simple, false, "decodeRct called on simple rctSig"); + CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull, false, "decodeRct called on non-full rctSig"); CHECK_AND_ASSERT_THROW_MES(rv.rangeSigs.size() > 0, "Empty rv.rangeSigs"); CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.rangeSigs.size(), "Mismatched sizes of rv.outPk and rv.rangeSigs"); CHECK_AND_ASSERT_THROW_MES(i < rv.ecdhInfo.size(), "Bad index"); @@ -840,7 +840,7 @@ namespace rct { } static xmr_amount decodeRctSimpleMain(const rctSig & rv, const key & sk, unsigned int i, key &mask, void (*decode)(ecdhTuple &ecdh, const key&)) { - CHECK_AND_ASSERT_MES(rv.simple, false, "decodeRct called on non simple rctSig"); + CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple, false, "decodeRct called on non simple rctSig"); CHECK_AND_ASSERT_THROW_MES(rv.rangeSigs.size() > 0, "Empty rv.rangeSigs"); CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.rangeSigs.size(), "Mismatched sizes of rv.outPk and rv.rangeSigs"); CHECK_AND_ASSERT_THROW_MES(i < rv.ecdhInfo.size(), "Bad index"); diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h index 8df403c68..98876a08c 100644 --- a/src/ringct/rctTypes.h +++ b/src/ringct/rctTypes.h @@ -173,8 +173,12 @@ namespace rct { // ecdhInfo holds an encoded mask / amount to be passed to each receiver // outPk contains public keypairs which are destinations (P, C), // P = address, C = commitment to amount + enum { + RCTTypeFull = 0, + RCTTypeSimple = 1, + }; struct rctSig { - bool simple; + uint8_t type; key message; vector rangeSigs; mgSig MG; // for non simple rct @@ -187,15 +191,15 @@ namespace rct { xmr_amount txnFee; // contains b BEGIN_SERIALIZE_OBJECT() - FIELD(simple) + FIELD(type) // FIELD(message) - not serialized, it can be reconstructed FIELD(rangeSigs) - if (simple) + if (type == RCTTypeSimple) FIELD(MGs) else FIELD(MG) // FIELD(mixRing) - not serialized, it can be reconstructed - if (simple) + if (type == RCTTypeSimple) FIELD(pseudoOuts) FIELD(ecdhInfo) if (typename Archive::is_saving()) { diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 5d51efc12..c3fb160a0 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -211,10 +211,16 @@ static uint64_t decodeRct(const rct::rctSig & rv, const rct::key & sk, unsigned { try { - if (rv.simple) + switch (rv.type) + { + case rct::RCTTypeSimple: return rct::decodeRctSimpleFromSharedSecret(rv, sk, i, mask); - else + case rct::RCTTypeFull: return rct::decodeRctFromSharedSecret(rv, sk, i, mask); + default: + LOG_ERROR("Unsupported rct type: " << rv.type); + return 0; + } } catch (const std::exception &e) { diff --git a/tests/core_tests/rct.cpp b/tests/core_tests/rct.cpp index bf6ef136a..d03e208b6 100644 --- a/tests/core_tests/rct.cpp +++ b/tests/core_tests/rct.cpp @@ -126,7 +126,7 @@ bool gen_rct_tx_validation_base::generate_with(std::vector& ev for (size_t o = 0; o < 4; ++o) { rct::key amount_key = rct::hash_to_scalar(rct::scalarmultKey(rct::pk2rct(tx_pub_key), rct::sk2rct(miner_accounts[n].get_keys().m_view_secret_key))); - if (rct_txes[n].rct_signatures.simple) + if (rct_txes[n].rct_signatures.type == rct::RCTTypeSimple) rct::decodeRctSimpleFromSharedSecret(rct_txes[n].rct_signatures, amount_key, o, rct_tx_masks[o+n*4]); else rct::decodeRctFromSharedSecret(rct_txes[n].rct_signatures, amount_key, o, rct_tx_masks[o+n*4]); diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp index 9311ca016..039c26fc0 100644 --- a/tests/unit_tests/serialization.cpp +++ b/tests/unit_tests/serialization.cpp @@ -591,7 +591,7 @@ TEST(Serialization, serializes_ringct_types) ASSERT_TRUE(serialization::dump_binary(s0, blob)); ASSERT_TRUE(serialization::parse_binary(blob, s1)); - ASSERT_TRUE(s0.simple == s1.simple); + ASSERT_TRUE(s0.type == s1.type); ASSERT_TRUE(s0.rangeSigs.size() == s1.rangeSigs.size()); for (size_t n = 0; n < s0.rangeSigs.size(); ++n) { -- cgit v1.2.3 From 93f5c625f058ee1f81c02c8bb03744b28bbde90a Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Tue, 9 Aug 2016 21:34:09 +0100 Subject: rct: rework v2 txes into prunable and non prunable data Nothing is pruned, but this allows easier changes later. --- src/cryptonote_core/blockchain.cpp | 24 ++++++---- .../cryptonote_boost_serialization.h | 23 +++++++-- src/cryptonote_core/cryptonote_format_utils.cpp | 41 +++++++++++++--- src/cryptonote_core/cryptonote_format_utils.h | 1 + src/ringct/rctSigs.cpp | 45 +++++++++--------- src/ringct/rctTypes.h | 29 ++++++++---- tests/unit_tests/ringct.cpp | 55 +++++++++++----------- tests/unit_tests/serialization.cpp | 25 +++++----- 8 files changed, 153 insertions(+), 90 deletions(-) (limited to 'tests/unit_tests/serialization.cpp') diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index 08cb85a55..44dde7759 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -2279,17 +2279,18 @@ bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_pr // II if (rv.type == rct::RCTTypeFull) { - rv.MG.II.resize(tx.vin.size()); + rv.p.MGs.resize(1); + rv.p.MGs[0].II.resize(tx.vin.size()); for (size_t n = 0; n < tx.vin.size(); ++n) - rv.MG.II[n] = rct::ki2rct(boost::get(tx.vin[n]).k_image); + rv.p.MGs[0].II[n] = rct::ki2rct(boost::get(tx.vin[n]).k_image); } else if (rv.type == rct::RCTTypeSimple) { - CHECK_AND_ASSERT_MES(rv.MGs.size() == tx.vin.size(), false, "Bad MGs size"); + CHECK_AND_ASSERT_MES(rv.p.MGs.size() == tx.vin.size(), false, "Bad MGs size"); for (size_t n = 0; n < tx.vin.size(); ++n) { - rv.MGs[n].II.resize(1); - rv.MGs[n].II[0] = rct::ki2rct(boost::get(tx.vin[n]).k_image); + rv.p.MGs[n].II.resize(1); + rv.p.MGs[n].II[0] = rct::ki2rct(boost::get(tx.vin[n]).k_image); } } else @@ -2577,14 +2578,14 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, } } - if (rv.MGs.size() != tx.vin.size()) + if (rv.p.MGs.size() != tx.vin.size()) { LOG_PRINT_L1("Failed to check ringct signatures: mismatched MGs/vin sizes"); return false; } for (size_t n = 0; n < tx.vin.size(); ++n) { - if (memcmp(&boost::get(tx.vin[n]).k_image, &rv.MGs[n].II[0], 32)) + if (memcmp(&boost::get(tx.vin[n]).k_image, &rv.p.MGs[n].II[0], 32)) { LOG_PRINT_L1("Failed to check ringct signatures: mismatched key image"); return false; @@ -2630,14 +2631,19 @@ bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, } } - if (rv.MG.II.size() != tx.vin.size()) + if (rv.p.MGs.size() != 1) + { + LOG_PRINT_L1("Failed to check ringct signatures: Bad MGs size"); + return false; + } + if (rv.p.MGs[0].II.size() != tx.vin.size()) { LOG_PRINT_L1("Failed to check ringct signatures: mismatched II/vin sizes"); return false; } for (size_t n = 0; n < tx.vin.size(); ++n) { - if (memcmp(&boost::get(tx.vin[n]).k_image, &rv.MG.II[n], 32)) + if (memcmp(&boost::get(tx.vin[n]).k_image, &rv.p.MGs[0].II[n], 32)) { LOG_PRINT_L1("Failed to check ringct signatures: mismatched II/vin sizes"); return false; diff --git a/src/cryptonote_core/cryptonote_boost_serialization.h b/src/cryptonote_core/cryptonote_boost_serialization.h index 7a7cf8588..09e9a7fa7 100644 --- a/src/cryptonote_core/cryptonote_boost_serialization.h +++ b/src/cryptonote_core/cryptonote_boost_serialization.h @@ -246,23 +246,36 @@ namespace boost } template - inline void serialize(Archive &a, rct::rctSig &x, const boost::serialization::version_type ver) + inline void serialize(Archive &a, rct::rctSigBase &x, const boost::serialization::version_type ver) { a & x.type; if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple) 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.rangeSigs; + // a & x.mixRing; mixRing is not serialized, as it can be reconstructed from the offsets if (x.type == rct::RCTTypeSimple) - a & x.MGs; - if (x.type == rct::RCTTypeFull) - a & x.MG; + a & x.pseudoOuts; + a & x.ecdhInfo; + serializeOutPk(a, x.outPk, ver); + a & x.txnFee; + } + + template + inline void serialize(Archive &a, rct::rctSig &x, const boost::serialization::version_type ver) + { + a & x.type; + if (x.type != rct::RCTTypeFull && x.type != rct::RCTTypeSimple) + 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) a & x.pseudoOuts; a & x.ecdhInfo; serializeOutPk(a, x.outPk, ver); a & x.txnFee; + //-------------- + a & x.p.rangeSigs; + a & x.p.MGs; } } } diff --git a/src/cryptonote_core/cryptonote_format_utils.cpp b/src/cryptonote_core/cryptonote_format_utils.cpp index ddcab4f05..8f4020829 100644 --- a/src/cryptonote_core/cryptonote_format_utils.cpp +++ b/src/cryptonote_core/cryptonote_format_utils.cpp @@ -101,7 +101,7 @@ namespace cryptonote CHECK_AND_ASSERT_MES(r, false, "Failed to parse transaction from blob"); //TODO: validate tx - crypto::cn_fast_hash(tx_blob.data(), tx_blob.size(), tx_hash); + get_transaction_hash(tx, tx_hash); get_transaction_prefix_hash(tx, tx_prefix_hash); return true; } @@ -905,20 +905,49 @@ namespace cryptonote crypto::hash get_transaction_hash(const transaction& t) { crypto::hash h = null_hash; - size_t blob_size = 0; - get_object_hash(t, h, blob_size); + get_transaction_hash(t, h, NULL); return h; } //--------------------------------------------------------------- bool get_transaction_hash(const transaction& t, crypto::hash& res) { - size_t blob_size = 0; - return get_object_hash(t, res, blob_size); + return get_transaction_hash(t, res, NULL); + } + //--------------------------------------------------------------- + bool get_transaction_hash(const transaction& t, crypto::hash& res, size_t* blob_size) + { + // v1 transactions hash the entire blob + if (t.version == 1) + { + size_t ignored_blob_size, &blob_size_ref = blob_size ? *blob_size : ignored_blob_size; + return get_object_hash(t, res, blob_size_ref); + } + + // v2 transactions hash different parts together, than hash the set of those hashes + crypto::hash hashes[3]; + + // prefix + get_transaction_prefix_hash(t, hashes[0]); + + // base rct data + get_blob_hash(t_serializable_object_to_blob((const rct::rctSigBase&)t.rct_signatures), hashes[1]); + + // prunable rct data + get_blob_hash(t_serializable_object_to_blob(t.rct_signatures.p), hashes[2]); + + // the tx hash is the hash of the 3 hashes + res = cn_fast_hash(hashes, sizeof(hashes)); + + // we still need the size + if (blob_size) + *blob_size = get_object_blobsize(t); + + return true; } //--------------------------------------------------------------- bool get_transaction_hash(const transaction& t, crypto::hash& res, size_t& blob_size) { - return get_object_hash(t, res, blob_size); + return get_transaction_hash(t, res, &blob_size); } //--------------------------------------------------------------- blobdata get_block_hashing_blob(const block& b) diff --git a/src/cryptonote_core/cryptonote_format_utils.h b/src/cryptonote_core/cryptonote_format_utils.h index e0ffbed67..f70b22573 100644 --- a/src/cryptonote_core/cryptonote_format_utils.h +++ b/src/cryptonote_core/cryptonote_format_utils.h @@ -111,6 +111,7 @@ namespace cryptonote crypto::hash get_transaction_hash(const transaction& t); bool get_transaction_hash(const transaction& t, crypto::hash& res); bool get_transaction_hash(const transaction& t, crypto::hash& res, size_t& blob_size); + bool get_transaction_hash(const transaction& t, crypto::hash& res, size_t* blob_size); blobdata get_block_hashing_blob(const block& b); bool get_block_hash(const block& b, crypto::hash& res); crypto::hash get_block_hash(const block& b); diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index ca38f13dd..d42be0fcc 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -346,7 +346,7 @@ namespace rct { keyV kv; kv.push_back(d2h(rv.type)); kv.push_back(rv.message); - for (auto r: rv.rangeSigs) + for (auto r: rv.p.rangeSigs) { for (size_t n = 0; n < 64; ++n) kv.push_back(r.asig.L1[n]); @@ -593,8 +593,9 @@ namespace rct { rctSig rv; rv.type = RCTTypeFull; + rv.message = message; rv.outPk.resize(destinations.size()); - rv.rangeSigs.resize(destinations.size()); + rv.p.rangeSigs.resize(destinations.size()); rv.ecdhInfo.resize(destinations.size()); size_t i = 0; @@ -604,9 +605,9 @@ namespace rct { //add destination to sig rv.outPk[i].dest = copy(destinations[i]); //compute range proof - rv.rangeSigs[i] = proveRange(rv.outPk[i].mask, outSk[i].mask, amounts[i]); + rv.p.rangeSigs[i] = proveRange(rv.outPk[i].mask, outSk[i].mask, amounts[i]); #ifdef DBG - CHECK_AND_ASSERT_THROW_MES(verRange(rv.outPk[i].mask, rv.rangeSigs[i]), "verRange failed on newly created proof"); + CHECK_AND_ASSERT_THROW_MES(verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]), "verRange failed on newly created proof"); #endif //mask amount and mask @@ -628,7 +629,7 @@ namespace rct { key txnFeeKey = scalarmultH(d2h(rv.txnFee)); rv.mixRing = mixRing; - rv.MG = proveRctMG(get_pre_mlsag_hash(rv), rv.mixRing, inSk, outSk, rv.outPk, index, txnFeeKey); + rv.p.MGs.push_back(proveRctMG(get_pre_mlsag_hash(rv), rv.mixRing, inSk, outSk, rv.outPk, index, txnFeeKey)); return rv; } @@ -654,8 +655,9 @@ namespace rct { rctSig rv; rv.type = RCTTypeSimple; + rv.message = message; rv.outPk.resize(destinations.size()); - rv.rangeSigs.resize(destinations.size()); + rv.p.rangeSigs.resize(destinations.size()); rv.ecdhInfo.resize(destinations.size()); size_t i; @@ -667,9 +669,9 @@ namespace rct { //add destination to sig rv.outPk[i].dest = copy(destinations[i]); //compute range proof - rv.rangeSigs[i] = proveRange(rv.outPk[i].mask, outSk[i].mask, outamounts[i]); + rv.p.rangeSigs[i] = proveRange(rv.outPk[i].mask, outSk[i].mask, outamounts[i]); #ifdef DBG - verRange(rv.outPk[i].mask, rv.rangeSigs[i]); + verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); #endif sc_add(sumout.bytes, outSk[i].mask.bytes, sumout.bytes); @@ -686,7 +688,7 @@ namespace rct { // key txnFeeKey = scalarmultH(d2h(rv.txnFee)); rv.mixRing = mixRing; rv.pseudoOuts.resize(inamounts.size()); - rv.MGs.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++) { @@ -701,7 +703,7 @@ namespace rct { key full_message = get_pre_mlsag_hash(rv); for (i = 0 ; i < inamounts.size(); i++) { - rv.MGs[i] = proveRctMGSimple(full_message, rv.mixRing[i], inSk[i], a[i], rv.pseudoOuts[i], index[i]); + rv.p.MGs[i] = proveRctMGSimple(full_message, rv.mixRing[i], inSk[i], a[i], rv.pseudoOuts[i], index[i]); } return rv; } @@ -731,8 +733,9 @@ namespace rct { // must know the destination private key to find the correct amount, else will return a random number bool verRct(const rctSig & rv) { CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull, false, "verRct called on non-full rctSig"); - CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.rangeSigs"); + 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.outPk.size() == rv.ecdhInfo.size(), false, "Mismatched sizes of outPk and rv.ecdhInfo"); + CHECK_AND_ASSERT_MES(rv.p.MGs.size() == 1, false, "full rctSig has not one MG"); // some rct ops can throw try @@ -742,13 +745,13 @@ namespace rct { bool tmp; DP("range proofs verified?"); for (i = 0; i < rv.outPk.size(); i++) { - tmp = verRange(rv.outPk[i].mask, rv.rangeSigs[i]); + tmp = verRange(rv.outPk[i].mask, rv.p.rangeSigs[i]); DP(tmp); rvb = (rvb && tmp); } //compute txn fee key txnFeeKey = scalarmultH(d2h(rv.txnFee)); - bool mgVerd = verRctMG(rv.MG, rv.mixRing, rv.outPk, txnFeeKey, get_pre_mlsag_hash(rv)); + bool mgVerd = verRctMG(rv.p.MGs[0], rv.mixRing, rv.outPk, txnFeeKey, get_pre_mlsag_hash(rv)); DP("mg sig verified?"); DP(mgVerd); @@ -767,14 +770,14 @@ namespace rct { bool rvb = true; CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple, false, "verRctSimple called on non simple rctSig"); - CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.rangeSigs"); + 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.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.p.MGs.size(), false, "Mismatched sizes of rv.pseudoOuts and rv.p.MGs"); CHECK_AND_ASSERT_MES(rv.pseudoOuts.size() == rv.mixRing.size(), false, "Mismatched sizes of rv.pseudoOuts and mixRing"); key sumOutpks = identity(); for (i = 0; i < rv.outPk.size(); i++) { - if (!verRange(rv.outPk[i].mask, rv.rangeSigs[i])) { + if (!verRange(rv.outPk[i].mask, rv.p.rangeSigs[i])) { return false; } addKeys(sumOutpks, sumOutpks, rv.outPk[i].mask); @@ -787,7 +790,7 @@ namespace rct { key message = get_pre_mlsag_hash(rv); key sumPseudoOuts = identity(); for (i = 0 ; i < rv.mixRing.size() ; i++) { - tmpb = verRctMGSimple(message, rv.MGs[i], rv.mixRing[i], rv.pseudoOuts[i]); + tmpb = verRctMGSimple(message, rv.p.MGs[i], rv.mixRing[i], rv.pseudoOuts[i]); addKeys(sumPseudoOuts, sumPseudoOuts, rv.pseudoOuts[i]); DP(tmpb); if (!tmpb) { @@ -820,8 +823,8 @@ namespace rct { // must know the destination private key to find the correct amount, else will return a random number static xmr_amount decodeRctMain(const rctSig & rv, const key & sk, unsigned int i, key & mask, void (*decode)(ecdhTuple&, const key&)) { CHECK_AND_ASSERT_MES(rv.type == RCTTypeFull, false, "decodeRct called on non-full rctSig"); - CHECK_AND_ASSERT_THROW_MES(rv.rangeSigs.size() > 0, "Empty rv.rangeSigs"); - CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.rangeSigs.size(), "Mismatched sizes of rv.outPk and rv.rangeSigs"); + CHECK_AND_ASSERT_THROW_MES(rv.p.rangeSigs.size() > 0, "Empty rv.p.rangeSigs"); + CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.p.rangeSigs.size(), "Mismatched sizes of rv.outPk and rv.p.rangeSigs"); CHECK_AND_ASSERT_THROW_MES(i < rv.ecdhInfo.size(), "Bad index"); //mask amount and mask @@ -857,8 +860,8 @@ namespace rct { static xmr_amount decodeRctSimpleMain(const rctSig & rv, const key & sk, unsigned int i, key &mask, void (*decode)(ecdhTuple &ecdh, const key&)) { CHECK_AND_ASSERT_MES(rv.type == RCTTypeSimple, false, "decodeRct called on non simple rctSig"); - CHECK_AND_ASSERT_THROW_MES(rv.rangeSigs.size() > 0, "Empty rv.rangeSigs"); - CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.rangeSigs.size(), "Mismatched sizes of rv.outPk and rv.rangeSigs"); + CHECK_AND_ASSERT_THROW_MES(rv.p.rangeSigs.size() > 0, "Empty rv.p.rangeSigs"); + CHECK_AND_ASSERT_THROW_MES(rv.outPk.size() == rv.p.rangeSigs.size(), "Mismatched sizes of rv.outPk and rv.p.rangeSigs"); CHECK_AND_ASSERT_THROW_MES(i < rv.ecdhInfo.size(), "Bad index"); //mask amount and mask diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h index 98876a08c..8211fbaed 100644 --- a/src/ringct/rctTypes.h +++ b/src/ringct/rctTypes.h @@ -177,12 +177,9 @@ namespace rct { RCTTypeFull = 0, RCTTypeSimple = 1, }; - struct rctSig { + struct rctSigBase { uint8_t type; key message; - vector rangeSigs; - mgSig MG; // for non simple rct - vector MGs; // for simple rct ctkeyM mixRing; //the set of all pubkeys / copy //pairs that you mix with keyV pseudoOuts; //C - for simple rct @@ -190,14 +187,9 @@ namespace rct { ctkeyV outPk; xmr_amount txnFee; // contains b - BEGIN_SERIALIZE_OBJECT() + BEGIN_SERIALIZE() FIELD(type) // FIELD(message) - not serialized, it can be reconstructed - FIELD(rangeSigs) - if (type == RCTTypeSimple) - FIELD(MGs) - else - FIELD(MG) // FIELD(mixRing) - not serialized, it can be reconstructed if (type == RCTTypeSimple) FIELD(pseudoOuts) @@ -218,6 +210,23 @@ namespace rct { FIELD(txnFee) END_SERIALIZE() }; + struct rctSigPrunable { + vector rangeSigs; + vector MGs; // simple rct has N, full has 1 + + BEGIN_SERIALIZE() + FIELD(rangeSigs) + FIELD(MGs) + END_SERIALIZE() + }; + struct rctSig: public rctSigBase { + rctSigPrunable p; + + BEGIN_SERIALIZE_OBJECT() + FIELDS(*static_cast(this)) + FIELDS(p); + END_SERIALIZE() + }; //other basepoint H = toPoint(cn_fast_hash(G)), G the basepoint static const key H = { {0x8b, 0x65, 0x59, 0x70, 0x15, 0x37, 0x99, 0xaf, 0x2a, 0xea, 0xdc, 0x9f, 0xf1, 0xad, 0xd0, 0xea, 0x6c, 0x72, 0x51, 0xd5, 0x41, 0x54, 0xcf, 0xa9, 0x2c, 0x17, 0x3a, 0x0d, 0xd3, 0x9c, 0x1f, 0x94} }; diff --git a/tests/unit_tests/ringct.cpp b/tests/unit_tests/ringct.cpp index 5be74ec7e..16234ce52 100644 --- a/tests/unit_tests/ringct.cpp +++ b/tests/unit_tests/ringct.cpp @@ -985,18 +985,19 @@ TEST(ringct, rctSig_##name) \ ASSERT_FALSE(rct::verRct(sig)); \ } -TEST_rctSig_elements(rangeSigs_empty, sig.rangeSigs.resize(0)); -TEST_rctSig_elements(rangeSigs_too_many, sig.rangeSigs.push_back(sig.rangeSigs.back())); -TEST_rctSig_elements(rangeSigs_too_few, sig.rangeSigs.pop_back()); -TEST_rctSig_elements(mgSig_ss_empty, sig.MG.ss.resize(0)); -TEST_rctSig_elements(mgSig_ss_too_many, sig.MG.ss.push_back(sig.MG.ss.back())); -TEST_rctSig_elements(mgSig_ss_too_few, sig.MG.ss.pop_back()); -TEST_rctSig_elements(mgSig_ss0_empty, sig.MG.ss[0].resize(0)); -TEST_rctSig_elements(mgSig_ss0_too_many, sig.MG.ss[0].push_back(sig.MG.ss[0].back())); -TEST_rctSig_elements(mgSig_ss0_too_few, sig.MG.ss[0].pop_back()); -TEST_rctSig_elements(mgSig_II_empty, sig.MG.II.resize(0)); -TEST_rctSig_elements(mgSig_II_too_many, sig.MG.II.push_back(sig.MG.II.back())); -TEST_rctSig_elements(mgSig_II_too_few, sig.MG.II.pop_back()); +TEST_rctSig_elements(rangeSigs_empty, sig.p.rangeSigs.resize(0)); +TEST_rctSig_elements(rangeSigs_too_many, sig.p.rangeSigs.push_back(sig.p.rangeSigs.back())); +TEST_rctSig_elements(rangeSigs_too_few, sig.p.rangeSigs.pop_back()); +TEST_rctSig_elements(mgSig_MG_empty, sig.p.MGs.resize(0)); +TEST_rctSig_elements(mgSig_ss_empty, sig.p.MGs[0].ss.resize(0)); +TEST_rctSig_elements(mgSig_ss_too_many, sig.p.MGs[0].ss.push_back(sig.p.MGs[0].ss.back())); +TEST_rctSig_elements(mgSig_ss_too_few, sig.p.MGs[0].ss.pop_back()); +TEST_rctSig_elements(mgSig_ss0_empty, sig.p.MGs[0].ss[0].resize(0)); +TEST_rctSig_elements(mgSig_ss0_too_many, sig.p.MGs[0].ss[0].push_back(sig.p.MGs[0].ss[0].back())); +TEST_rctSig_elements(mgSig_ss0_too_few, sig.p.MGs[0].ss[0].pop_back()); +TEST_rctSig_elements(mgSig_II_empty, sig.p.MGs[0].II.resize(0)); +TEST_rctSig_elements(mgSig_II_too_many, sig.p.MGs[0].II.push_back(sig.p.MGs[0].II.back())); +TEST_rctSig_elements(mgSig_II_too_few, sig.p.MGs[0].II.pop_back()); TEST_rctSig_elements(mixRing_empty, sig.mixRing.resize(0)); TEST_rctSig_elements(mixRing_too_many, sig.mixRing.push_back(sig.mixRing.back())); TEST_rctSig_elements(mixRing_too_few, sig.mixRing.pop_back()); @@ -1021,21 +1022,21 @@ TEST(ringct, rctSig_##name##_simple) \ ASSERT_FALSE(rct::verRctSimple(sig)); \ } -TEST_rctSig_elements_simple(rangeSigs_empty, sig.rangeSigs.resize(0)); -TEST_rctSig_elements_simple(rangeSigs_too_many, sig.rangeSigs.push_back(sig.rangeSigs.back())); -TEST_rctSig_elements_simple(rangeSigs_too_few, sig.rangeSigs.pop_back()); -TEST_rctSig_elements_simple(mgSig_empty, sig.MGs.resize(0)); -TEST_rctSig_elements_simple(mgSig_too_many, sig.MGs.push_back(sig.MGs.back())); -TEST_rctSig_elements_simple(mgSig_too_few, sig.MGs.pop_back()); -TEST_rctSig_elements_simple(mgSig0_ss_empty, sig.MGs[0].ss.resize(0)); -TEST_rctSig_elements_simple(mgSig0_ss_too_many, sig.MGs[0].ss.push_back(sig.MGs[0].ss.back())); -TEST_rctSig_elements_simple(mgSig0_ss_too_few, sig.MGs[0].ss.pop_back()); -TEST_rctSig_elements_simple(mgSig_ss0_empty, sig.MGs[0].ss[0].resize(0)); -TEST_rctSig_elements_simple(mgSig_ss0_too_many, sig.MGs[0].ss[0].push_back(sig.MGs[0].ss[0].back())); -TEST_rctSig_elements_simple(mgSig_ss0_too_few, sig.MGs[0].ss[0].pop_back()); -TEST_rctSig_elements_simple(mgSig0_II_empty, sig.MGs[0].II.resize(0)); -TEST_rctSig_elements_simple(mgSig0_II_too_many, sig.MGs[0].II.push_back(sig.MGs[0].II.back())); -TEST_rctSig_elements_simple(mgSig0_II_too_few, sig.MGs[0].II.pop_back()); +TEST_rctSig_elements_simple(rangeSigs_empty, sig.p.rangeSigs.resize(0)); +TEST_rctSig_elements_simple(rangeSigs_too_many, sig.p.rangeSigs.push_back(sig.p.rangeSigs.back())); +TEST_rctSig_elements_simple(rangeSigs_too_few, sig.p.rangeSigs.pop_back()); +TEST_rctSig_elements_simple(mgSig_empty, sig.p.MGs.resize(0)); +TEST_rctSig_elements_simple(mgSig_too_many, sig.p.MGs.push_back(sig.p.MGs.back())); +TEST_rctSig_elements_simple(mgSig_too_few, sig.p.MGs.pop_back()); +TEST_rctSig_elements_simple(mgSig0_ss_empty, sig.p.MGs[0].ss.resize(0)); +TEST_rctSig_elements_simple(mgSig0_ss_too_many, sig.p.MGs[0].ss.push_back(sig.p.MGs[0].ss.back())); +TEST_rctSig_elements_simple(mgSig0_ss_too_few, sig.p.MGs[0].ss.pop_back()); +TEST_rctSig_elements_simple(mgSig_ss0_empty, sig.p.MGs[0].ss[0].resize(0)); +TEST_rctSig_elements_simple(mgSig_ss0_too_many, sig.p.MGs[0].ss[0].push_back(sig.p.MGs[0].ss[0].back())); +TEST_rctSig_elements_simple(mgSig_ss0_too_few, sig.p.MGs[0].ss[0].pop_back()); +TEST_rctSig_elements_simple(mgSig0_II_empty, sig.p.MGs[0].II.resize(0)); +TEST_rctSig_elements_simple(mgSig0_II_too_many, sig.p.MGs[0].II.push_back(sig.p.MGs[0].II.back())); +TEST_rctSig_elements_simple(mgSig0_II_too_few, sig.p.MGs[0].II.pop_back()); TEST_rctSig_elements_simple(mixRing_empty, sig.mixRing.resize(0)); TEST_rctSig_elements_simple(mixRing_too_many, sig.mixRing.push_back(sig.mixRing.back())); TEST_rctSig_elements_simple(mixRing_too_few, sig.mixRing.pop_back()); diff --git a/tests/unit_tests/serialization.cpp b/tests/unit_tests/serialization.cpp index 039c26fc0..40209f6ed 100644 --- a/tests/unit_tests/serialization.cpp +++ b/tests/unit_tests/serialization.cpp @@ -571,7 +571,7 @@ TEST(Serialization, serializes_ringct_types) //compute rct data with mixin 500 s0 = rct::genRct(rct::zero(), sc, pc, destinations, amounts, amount_keys, 3); - mg0 = s0.MG; + mg0 = s0.p.MGs[0]; ASSERT_TRUE(serialization::dump_binary(mg0, blob)); ASSERT_TRUE(serialization::parse_binary(blob, mg1)); ASSERT_TRUE(mg0.ss.size() == mg1.ss.size()); @@ -584,7 +584,7 @@ TEST(Serialization, serializes_ringct_types) // mixRing and II are not serialized, they are meant to be reconstructed ASSERT_TRUE(mg1.II.empty()); - rg0 = s0.rangeSigs.front(); + rg0 = s0.p.rangeSigs.front(); ASSERT_TRUE(serialization::dump_binary(rg0, blob)); ASSERT_TRUE(serialization::parse_binary(blob, rg1)); ASSERT_TRUE(!memcmp(&rg0, &rg1, sizeof(rg0))); @@ -592,19 +592,20 @@ TEST(Serialization, serializes_ringct_types) ASSERT_TRUE(serialization::dump_binary(s0, blob)); ASSERT_TRUE(serialization::parse_binary(blob, s1)); ASSERT_TRUE(s0.type == s1.type); - ASSERT_TRUE(s0.rangeSigs.size() == s1.rangeSigs.size()); - for (size_t n = 0; n < s0.rangeSigs.size(); ++n) + ASSERT_TRUE(s0.p.rangeSigs.size() == s1.p.rangeSigs.size()); + for (size_t n = 0; n < s0.p.rangeSigs.size(); ++n) { - ASSERT_TRUE(!memcmp(&s0.rangeSigs[n], &s1.rangeSigs[n], sizeof(s0.rangeSigs[n]))); + ASSERT_TRUE(!memcmp(&s0.p.rangeSigs[n], &s1.p.rangeSigs[n], sizeof(s0.p.rangeSigs[n]))); } - ASSERT_TRUE(s0.MG.ss.size() == s1.MG.ss.size()); - for (size_t n = 0; n < s0.MG.ss.size(); ++n) + ASSERT_TRUE(s0.p.MGs.size() == s1.p.MGs.size()); + ASSERT_TRUE(s0.p.MGs[0].ss.size() == s1.p.MGs[0].ss.size()); + for (size_t n = 0; n < s0.p.MGs[0].ss.size(); ++n) { - ASSERT_TRUE(s0.MG.ss[n] == s1.MG.ss[n]); + ASSERT_TRUE(s0.p.MGs[0].ss[n] == s1.p.MGs[0].ss[n]); } - ASSERT_TRUE(s0.MG.cc == s1.MG.cc); + ASSERT_TRUE(s0.p.MGs[0].cc == s1.p.MGs[0].cc); // mixRing and II are not serialized, they are meant to be reconstructed - ASSERT_TRUE(s1.MGs[0].II.empty()); + ASSERT_TRUE(s1.p.MGs[0].II.empty()); // mixRing and II are not serialized, they are meant to be reconstructed ASSERT_TRUE(s1.mixRing.size() == 0); @@ -631,10 +632,10 @@ TEST(Serialization, serializes_ringct_types) tx0.vin.push_back(txin_to_key2); tx0.vout.push_back(cryptonote::tx_out()); tx0.rct_signatures = s0; - ASSERT_EQ(tx0.rct_signatures.rangeSigs.size(), 2); + ASSERT_EQ(tx0.rct_signatures.p.rangeSigs.size(), 2); ASSERT_TRUE(serialization::dump_binary(tx0, blob)); ASSERT_TRUE(serialization::parse_binary(blob, tx1)); - ASSERT_EQ(tx1.rct_signatures.rangeSigs.size(), 2); + ASSERT_EQ(tx1.rct_signatures.p.rangeSigs.size(), 2); std::string blob2; ASSERT_TRUE(serialization::dump_binary(tx1, blob2)); ASSERT_TRUE(blob == blob2); -- cgit v1.2.3