diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index c81445f1d..08cb85a55 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -2156,7 +2156,7 @@ bool Blockchain::get_tx_outputs_gindexs(const crypto::hash& tx_id, std::vector> &pubkeys) +{ + CHECK_AND_ASSERT_MES(tx.version == 2, false, "Transaction version is not 2"); + + rct::rctSig &rv = tx.rct_signatures; + + // message - hash of the transaction prefix + rv.message = rct::hash2rct(tx_prefix_hash); + + // mixRing - full and simple store it in opposite ways + if (rv.type == rct::RCTTypeFull) + { + rv.mixRing.resize(pubkeys[0].size()); + for (size_t m = 0; m < pubkeys[0].size(); ++m) + rv.mixRing[m].clear(); + for (size_t n = 0; n < pubkeys.size(); ++n) + { + CHECK_AND_ASSERT_MES(pubkeys[n].size() <= pubkeys[0].size(), false, "More inputs that first ring"); + for (size_t m = 0; m < pubkeys[n].size(); ++m) + { + rv.mixRing[m].push_back(pubkeys[n][m]); + } + } + } + else if (rv.type == rct::RCTTypeSimple) + { + rv.mixRing.resize(pubkeys.size()); + for (size_t n = 0; n < pubkeys.size(); ++n) + { + rv.mixRing[n].clear(); + for (size_t m = 0; m < pubkeys[n].size(); ++m) + { + rv.mixRing[n].push_back(pubkeys[n][m]); + } + } + } + else + { + CHECK_AND_ASSERT_MES(false, false, "Unsupported rct tx type: " + boost::lexical_cast(rv.type)); + } + + // II + if (rv.type == rct::RCTTypeFull) + { + rv.MG.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); + } + else if (rv.type == rct::RCTTypeSimple) + { + CHECK_AND_ASSERT_MES(rv.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); + } + } + else + { + CHECK_AND_ASSERT_MES(false, false, "Unsupported rct tx type: " + boost::lexical_cast(rv.type)); + } + + // outPk + CHECK_AND_ASSERT_MES(rv.outPk.size() == tx.vout.size(), false, "Bad outPk size"); + for (size_t n = 0; n < tx.rct_signatures.outPk.size(); ++n) + rv.outPk[n].dest = rct::pk2rct(boost::get(tx.vout[n].target).key); + + return true; +} //------------------------------------------------------------------ // This function validates transaction inputs and their keys. // FIXME: consider moving functionality specific to one input into // check_tx_input() rather than here, and use this function simply // to iterate the inputs as necessary (splitting the task // using threads, etc.) -bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context &tvc, uint64_t* pmax_used_block_height) +bool Blockchain::check_tx_inputs(transaction& tx, tx_verification_context &tvc, uint64_t* pmax_used_block_height) { LOG_PRINT_L3("Blockchain::" << __func__); size_t sig_index = 0; @@ -2461,71 +2530,29 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context } else { + if (!expand_transaction_2(tx, tx_prefix_hash, pubkeys)) + { + LOG_PRINT_L1("Failed to expand rct signatures!"); + return false; + } + // 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... - switch (tx.rct_signatures.type) + const rct::rctSig &rv = tx.rct_signatures; + switch (rv.type) { case rct::RCTTypeSimple: { - 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, and always do outPk - - // 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() != 0) - { - LOG_PRINT_L1("Failed to check ringct signatures: non empty MGs II"); - return false; - } - } - - reconstructed_II.resize(tx.vin.size()); - for (size_t n = 0; n < tx.vin.size(); ++n) - { - reconstructed_II[n].push_back(rct::ki2rct(boost::get(tx.vin[n]).k_image)); - } - - 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()); - 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]); - } - } - } - // check all this, either recontructed (so should really pass), or not { - if (pubkeys.size() != mixRing.size()) + if (pubkeys.size() != rv.mixRing.size()) { 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()) + if (pubkeys[i].size() != rv.mixRing[i].size()) { LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkeys/mixRing size"); return false; @@ -2536,12 +2563,12 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context { for (size_t m = 0; m < pubkeys[n].size(); ++m) { - if (pubkeys[n][m].dest != rct::rct2pk(mixRing[n][m].dest)) + if (pubkeys[n][m].dest != rct::rct2pk(rv.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)) + if (pubkeys[n][m].mask != rct::rct2pk(rv.mixRing[n][m].mask)) { LOG_PRINT_L1("Failed to check ringct signatures: mismatched commitment at vin " << n << ", index " << m); return false; @@ -2550,21 +2577,21 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context } } - if (tx.rct_signatures.MGs.size() != tx.vin.size()) + if (rv.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)) + if (memcmp(&boost::get(tx.vin[n]).k_image, &rv.MGs[n].II[0], 32)) { LOG_PRINT_L1("Failed to check ringct signatures: mismatched key image"); return false; } } - if (!rct::verRctSimple(tx.rct_signatures, mixRing, &reconstructed_II, reconstructed_outPk, rct::hash2rct(tx_prefix_hash))) + if (!rct::verRctSimple(rv)) { LOG_PRINT_L1("Failed to check ringct signatures!"); return false; @@ -2572,66 +2599,13 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context break; } case rct::RCTTypeFull: { - 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. 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.empty() ? 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) - { - 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.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); - } - } - - 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; 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(); + size_matches &= pubkeys[i].size() == rv.mixRing.size(); + for (size_t i = 0; i < rv.mixRing.size(); ++i) + size_matches &= pubkeys.size() == rv.mixRing[i].size(); if (!size_matches) { LOG_PRINT_L1("Failed to check ringct signatures: mismatched pubkeys/mixRing size"); @@ -2642,12 +2616,12 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context { for (size_t m = 0; m < pubkeys[n].size(); ++m) { - if (pubkeys[n][m].dest != rct::rct2pk(mixRing[m][n].dest)) + if (pubkeys[n][m].dest != rct::rct2pk(rv.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)) + if (pubkeys[n][m].mask != rct::rct2pk(rv.mixRing[m][n].mask)) { LOG_PRINT_L1("Failed to check ringct signatures: mismatched commitment at vin " << n << ", index " << m); return false; @@ -2656,21 +2630,21 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context } } - if (II.size() != tx.vin.size()) + if (rv.MG.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, &II[n], 32)) + if (memcmp(&boost::get(tx.vin[n]).k_image, &rv.MG.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, outPk, rct::hash2rct(tx_prefix_hash))) + if (!rct::verRct(rv)) { LOG_PRINT_L1("Failed to check ringct signatures!"); return false; @@ -2678,7 +2652,7 @@ bool Blockchain::check_tx_inputs(const transaction& tx, tx_verification_context break; } default: - LOG_PRINT_L1("Unsupported rct type: " << tx.rct_signatures.type); + LOG_PRINT_L1("Unsupported rct type: " << rv.type); return false; } } @@ -2787,11 +2761,7 @@ bool Blockchain::check_tx_input(size_t tx_version, const txin_to_key& txin, cons if (tx_version == 1) { CHECK_AND_ASSERT_MES(sig.size() == output_keys.size(), false, "internal error: tx signatures count=" << sig.size() << " mismatch with outputs keys count for inputs=" << output_keys.size()); } - else - { - // 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()); - } + // rct_signatures will be expanded after this return true; } //------------------------------------------------------------------ diff --git a/src/cryptonote_core/blockchain.h b/src/cryptonote_core/blockchain.h index b22074c57..94701608e 100644 --- a/src/cryptonote_core/blockchain.h +++ b/src/cryptonote_core/blockchain.h @@ -500,6 +500,7 @@ namespace cryptonote * validates a transaction's inputs as correctly used and not previously * spent. also returns the hash and height of the most recent block * which contains an output that was used as an input to the transaction. + * The transaction's rct signatures, if any, are expanded. * * @param tx the transaction to validate * @param pmax_used_block_height return-by-reference block height of most recent input @@ -509,7 +510,7 @@ namespace cryptonote * * @return false if any input is invalid, otherwise true */ - bool check_tx_inputs(const transaction& tx, uint64_t& pmax_used_block_height, crypto::hash& max_used_block_id, tx_verification_context &tvc, bool kept_by_block = false); + bool check_tx_inputs(transaction& tx, uint64_t& pmax_used_block_height, crypto::hash& max_used_block_id, tx_verification_context &tvc, bool kept_by_block = false); /** * @brief check that a transaction's outputs conform to current standards @@ -919,6 +920,7 @@ namespace cryptonote * This function validates transaction inputs and their keys. Previously * it also performed double spend checking, but that has been moved to its * own function. + * The transaction's rct signatures, if any, are expanded. * * If pmax_related_block_height is not NULL, its value is set to the height * of the most recent block which contains an output used in any input set @@ -932,7 +934,7 @@ namespace cryptonote * * @return false if any validation step fails, otherwise true */ - bool check_tx_inputs(const transaction& tx, tx_verification_context &tvc, uint64_t* pmax_used_block_height = NULL); + bool check_tx_inputs(transaction& tx, tx_verification_context &tvc, uint64_t* pmax_used_block_height = NULL); /** * @brief performs a blockchain reorganization according to the longest chain rule @@ -1211,5 +1213,14 @@ namespace cryptonote * a useful state. */ void load_compiled_in_block_hashes(); + + /** + * @brief expands v2 transaction data from blockchain + * + * RingCT transactions do not transmit some of their data if it + * can be reconstituted by the receiver. This function expands + * that implicit data. + */ + bool expand_transaction_2(transaction &tx, const crypto::hash &tx_prefix_hash, const std::vector> &pubkeys); }; } // namespace cryptonote diff --git a/src/cryptonote_core/tx_pool.cpp b/src/cryptonote_core/tx_pool.cpp index dcbb7cc04..2ed12eb5c 100644 --- a/src/cryptonote_core/tx_pool.cpp +++ b/src/cryptonote_core/tx_pool.cpp @@ -190,7 +190,9 @@ namespace cryptonote crypto::hash max_used_block_id = null_hash; uint64_t max_used_block_height = 0; - bool ch_inp_res = m_blockchain.check_tx_inputs(tx, max_used_block_height, max_used_block_id, tvc, kept_by_block); + tx_details txd; + txd.tx = tx; + bool ch_inp_res = m_blockchain.check_tx_inputs(txd.tx, max_used_block_height, max_used_block_id, tvc, kept_by_block); CRITICAL_REGION_LOCAL(m_transactions_lock); if(!ch_inp_res) { @@ -198,10 +200,9 @@ namespace cryptonote // may become valid again, so ignore the failed inputs check. if(kept_by_block) { - auto txd_p = m_transactions.insert(transactions_container::value_type(id, tx_details())); + auto txd_p = m_transactions.insert(transactions_container::value_type(id, txd)); CHECK_AND_ASSERT_MES(txd_p.second, false, "transaction already exists at inserting in memory pool"); txd_p.first->second.blob_size = blob_size; - txd_p.first->second.tx = tx; txd_p.first->second.fee = fee; txd_p.first->second.max_used_block_id = null_hash; txd_p.first->second.max_used_block_height = 0; @@ -220,10 +221,9 @@ namespace cryptonote }else { //update transactions container - auto txd_p = m_transactions.insert(transactions_container::value_type(id, tx_details())); + auto txd_p = m_transactions.insert(transactions_container::value_type(id, txd)); CHECK_AND_ASSERT_MES(txd_p.second, false, "intrnal error: transaction already exists at inserting in memorypool"); txd_p.first->second.blob_size = blob_size; - txd_p.first->second.tx = tx; txd_p.first->second.kept_by_block = kept_by_block; txd_p.first->second.fee = fee; txd_p.first->second.max_used_block_id = max_used_block_id; diff --git a/src/ringct/rctSigs.cpp b/src/ringct/rctSigs.cpp index d031f6c79..ca38f13dd 100644 --- a/src/ringct/rctSigs.cpp +++ b/src/ringct/rctSigs.cpp @@ -237,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, size_t dsRows) { + bool MLSAG_Ver(key message, const keyM & pk, const mgSig & rv, size_t dsRows) { size_t cols = pk.size(); CHECK_AND_ASSERT_MES(cols >= 2, false, "Error! What is c if cols = 1!"); @@ -246,7 +246,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(II.size() == dsRows, false, "Bad II size"); + CHECK_AND_ASSERT_MES(rv.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"); @@ -258,7 +258,7 @@ namespace rct { key c_old = copy(rv.cc); vector Ip(dsRows); for (i = 0 ; i < dsRows ; i++) { - precomp(Ip[i].k, II[i]); + precomp(Ip[i].k, rv.II[i]); } size_t ndsRows = 3 * dsRows; //non Double Spendable Rows (see identity chains paper keyV toHash(1 + 3 * dsRows + 2 * (rows - dsRows)); @@ -341,6 +341,43 @@ namespace rct { return (reb && rab); } + key get_pre_mlsag_hash(const rctSig &rv) + { + keyV kv; + kv.push_back(d2h(rv.type)); + kv.push_back(rv.message); + for (auto r: rv.rangeSigs) + { + for (size_t n = 0; n < 64; ++n) + kv.push_back(r.asig.L1[n]); + for (size_t n = 0; n < 64; ++n) + kv.push_back(r.asig.s2[n]); + kv.push_back(r.asig.s); + for (size_t n = 0; n < 64; ++n) + kv.push_back(r.Ci[n]); + } + // no MG/MGs, that's what will sign all this + // no mixRing, it's part of the vin already + for (auto o: rv.pseudoOuts) + { + kv.push_back(o); + } + for (auto i: rv.ecdhInfo) + { + kv.push_back(i.mask); + kv.push_back(i.amount); + // no senderPk, unused here + } + for (auto o: rv.outPk) + { + kv.push_back(o.dest); + kv.push_back(o.mask); + } + kv.push_back(d2h(rv.txnFee)); + + return cn_fast_hash(kv); + } + //Ring-ct MG sigs //Prove: // c.f. http://eprint.iacr.org/2015/1098 section 4. definition 10. @@ -393,10 +430,7 @@ 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.. } - 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, rows); + return MLSAG_Gen(message, M, sk, index, rows); } @@ -435,7 +469,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 &message) { + bool verRctMG(mgSig mg, 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"); @@ -466,19 +500,14 @@ namespace rct { //subtract txn fee output in last row subKeys(M[i][rows], M[i][rows], txnFeeKey); } - ctkeyV signed_data = outPk; - signed_data.push_back(ctkey({message, identity()})); - key msg = cn_fast_hash(signed_data); - DP("message:"); - DP(msg); - return MLSAG_Ver(msg, M, mg, II, rows); + return MLSAG_Ver(message, M, mg, rows); } //Ring-ct Simple MG sigs //Ver: //This does a simplified version, assuming only post Rct //inputs - bool verRctMGSimple(const key &message, const mgSig &mg, const keyV &II, const ctkeyV & pubs, const key & C) { + bool verRctMGSimple(const key &message, const mgSig &mg, const ctkeyV & pubs, const key & C) { //setup vars size_t rows = 1; size_t cols = pubs.size(); @@ -492,7 +521,7 @@ namespace rct { subKeys(M[i][1], pubs[i].mask, C); } //DP(C); - return MLSAG_Ver(message, M, mg, II, rows); + return MLSAG_Ver(message, M, mg, rows); } //These functions get keys from blockchain @@ -599,8 +628,7 @@ namespace rct { key txnFeeKey = scalarmultH(d2h(rv.txnFee)); rv.mixRing = mixRing; - rv.message = message; - rv.MG = proveRctMG(message, rv.mixRing, inSk, outSk, rv.outPk, index, txnFeeKey); + rv.MG = proveRctMG(get_pre_mlsag_hash(rv), rv.mixRing, inSk, outSk, rv.outPk, index, txnFeeKey); return rv; } @@ -626,7 +654,6 @@ namespace rct { rctSig rv; rv.type = RCTTypeSimple; - rv.message = message; rv.outPk.resize(destinations.size()); rv.rangeSigs.resize(destinations.size()); rv.ecdhInfo.resize(destinations.size()); @@ -661,18 +688,21 @@ namespace rct { rv.pseudoOuts.resize(inamounts.size()); rv.MGs.resize(inamounts.size()); key sumpouts = zero(); //sum pseudoOut masks - key a; + keyV a(inamounts.size()); 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.MGs[i] = proveRctMGSimple(message, rv.mixRing[i], inSk[i], a, rv.pseudoOuts[i], index[i]); + skGen(a[i]); + sc_add(sumpouts.bytes, a[i].bytes, sumpouts.bytes); + genC(rv.pseudoOuts[i], a[i], inamounts[i]); } rv.mixRing = mixRing; - sc_sub(a.bytes, sumout.bytes, sumpouts.bytes); - genC(rv.pseudoOuts[i], a, inamounts[i]); + sc_sub(a[i].bytes, sumout.bytes, sumpouts.bytes); + genC(rv.pseudoOuts[i], a[i], inamounts[i]); DP(rv.pseudoOuts[i]); - rv.MGs[i] = proveRctMGSimple(message, rv.mixRing[i], inSk[i], a, rv.pseudoOuts[i], index[i]); + + 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]); + } return rv; } @@ -699,10 +729,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 ctkeyV &outPk, const key &message) { + bool verRct(const rctSig & rv) { 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"); + 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.ecdhInfo.size(), false, "Mismatched sizes of outPk and rv.ecdhInfo"); // some rct ops can throw try @@ -711,14 +741,14 @@ namespace rct { bool rvb = true; bool tmp; DP("range proofs verified?"); - for (i = 0; i < outPk.size(); i++) { - tmp = verRange(outPk[i].mask, rv.rangeSigs[i]); + for (i = 0; i < rv.outPk.size(); i++) { + tmp = verRange(rv.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, outPk, txnFeeKey, message); + bool mgVerd = verRctMG(rv.MG, rv.mixRing, rv.outPk, txnFeeKey, get_pre_mlsag_hash(rv)); DP("mg sig verified?"); DP(mgVerd); @@ -729,45 +759,35 @@ namespace rct { return false; } } - bool verRct(const rctSig & rv) { - 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 ctkeyV &outPk, const key &message) { + bool verRctSimple(const rctSig & rv) { size_t i = 0; bool rvb = true; - + 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.outPk.size() == rv.rangeSigs.size(), false, "Mismatched sizes of outPk and rv.rangeSigs"); + CHECK_AND_ASSERT_MES(rv.outPk.size() == rv.ecdhInfo.size(), false, "Mismatched sizes of 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"); - if (II) - { - for (size_t n = 0; n < II->size(); ++n) - { - CHECK_AND_ASSERT_MES((*II)[n].size() == 1, false, "Bad II size"); - } - } + 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 < outPk.size(); i++) { - if (!verRange(outPk[i].mask, rv.rangeSigs[i])) { + for (i = 0; i < rv.outPk.size(); i++) { + if (!verRange(rv.outPk[i].mask, rv.rangeSigs[i])) { return false; } - addKeys(sumOutpks, sumOutpks, outPk[i].mask); + addKeys(sumOutpks, sumOutpks, rv.outPk[i].mask); } DP(sumOutpks); key txnFeeKey = scalarmultH(d2h(rv.txnFee)); addKeys(sumOutpks, txnFeeKey, sumOutpks); bool tmpb = false; + key message = get_pre_mlsag_hash(rv); key sumPseudoOuts = identity(); - 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]); + for (i = 0 ; i < rv.mixRing.size() ; i++) { + tmpb = verRctMGSimple(message, rv.MGs[i], rv.mixRing[i], rv.pseudoOuts[i]); addKeys(sumPseudoOuts, sumPseudoOuts, rv.pseudoOuts[i]); DP(tmpb); if (!tmpb) { @@ -788,10 +808,6 @@ namespace rct { return (rvb && mgVerd); } - bool verRctSimple(const rctSig & rv) { - return verRctSimple(rv, rv.mixRing, NULL, rv.outPk, rv.message); - } - //RingCT protocol //genRct: // creates an rctSig with all data necessary to verify the rangeProofs and that the signer owns one of the diff --git a/src/ringct/rctSigs.h b/src/ringct/rctSigs.h index 558af22fd..bf9d4be81 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, size_t dsRows); - bool MLSAG_Ver(key message, const keyM &pk, const mgSig &sig, const keyV &II, size_t dsRows); + bool MLSAG_Ver(key message, const keyM &pk, const mgSig &sig, size_t dsRows); //mgSig MLSAG_Gen_Old(const keyM & pk, const keyV & xx, const int index); //proveRange and verRange @@ -115,7 +115,7 @@ namespace rct { 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 &message); - bool verRctMGSimple(const key &message, const mgSig &mg, const keyV &II, const ctkeyV & pubs, const key & C); + bool verRctMGSimple(const key &message, const mgSig &mg, const ctkeyV & pubs, const key & C); //These functions get keys from blockchain //replace these when connecting blockchain @@ -140,9 +140,7 @@ namespace rct { 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); diff --git a/tests/unit_tests/ringct.cpp b/tests/unit_tests/ringct.cpp index 81a63b502..5be74ec7e 100644 --- a/tests/unit_tests/ringct.cpp +++ b/tests/unit_tests/ringct.cpp @@ -130,8 +130,8 @@ TEST(ringct, MG_sigs) sk[j] = xm[ind][j]; } key message = identity(); - mgSig IIccss = MLSAG_Gen(message, P, sk, ind); - ASSERT_TRUE(MLSAG_Ver(message, P, IIccss, IIccss.II)); + mgSig IIccss = MLSAG_Gen(message, P, sk, ind, R); + ASSERT_TRUE(MLSAG_Ver(message, P, IIccss, R)); //#MG sig: false one N = 3;// #cols @@ -151,8 +151,8 @@ TEST(ringct, MG_sigs) sk[j] = xx[ind][j]; } 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, IIccss.II)); + IIccss = MLSAG_Gen(message, P, sk, ind, R); + ASSERT_FALSE(MLSAG_Ver(message, P, IIccss, R)); } TEST(ringct, range_proofs)