diff options
author | moneromooo-monero <moneromooo-monero@users.noreply.github.com> | 2016-08-09 11:38:54 +0100 |
---|---|---|
committer | moneromooo-monero <moneromooo-monero@users.noreply.github.com> | 2016-08-28 21:30:16 +0100 |
commit | d93746b6d37dd5b99d16331ec8e24e8a1f7e4652 (patch) | |
tree | a8b5718a5bf1dbde9feab7c1f09c187bbada3363 /src/cryptonote_core | |
parent | rct: change the simple flag to a type (diff) | |
download | monero-d93746b6d37dd5b99d16331ec8e24e8a1f7e4652.tar.xz |
rct: rework the verification preparation process
The whole rct data apart from the MLSAGs is now included in
the signed message, to avoid malleability issues.
Instead of passing the data that's not serialized as extra
parameters to the verification API, the transaction is modified
to fill all that information. This means the transaction can
not be const anymore, but it cleaner in other ways.
Diffstat (limited to '')
-rw-r--r-- | src/cryptonote_core/blockchain.cpp | 222 | ||||
-rw-r--r-- | src/cryptonote_core/blockchain.h | 15 | ||||
-rw-r--r-- | src/cryptonote_core/tx_pool.cpp | 10 |
3 files changed, 114 insertions, 133 deletions
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<u // This function overloads its sister function with // an extra value (hash of highest block that holds an output used as input) // as a return-by-reference. -bool Blockchain::check_tx_inputs(const transaction& tx, uint64_t& max_used_block_height, crypto::hash& max_used_block_id, tx_verification_context &tvc, bool kept_by_block) +bool Blockchain::check_tx_inputs(transaction& tx, uint64_t& max_used_block_height, crypto::hash& max_used_block_id, tx_verification_context &tvc, bool kept_by_block) { LOG_PRINT_L3("Blockchain::" << __func__); CRITICAL_REGION_LOCAL(m_blockchain_lock); @@ -2235,13 +2235,82 @@ bool Blockchain::have_tx_keyimges_as_spent(const transaction &tx) const } return false; } +bool Blockchain::expand_transaction_2(transaction &tx, const crypto::hash &tx_prefix_hash, const std::vector<std::vector<rct::ctkey>> &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<std::string>(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<txin_to_key>(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<txin_to_key>(tx.vin[n]).k_image); + } + } + else + { + CHECK_AND_ASSERT_MES(false, false, "Unsupported rct tx type: " + boost::lexical_cast<std::string>(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<txout_to_key>(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<rct::keyV> 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<txin_to_key>(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<txout_to_key>(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<txin_to_key>(tx.vin[n]).k_image, &reconstructed_II[n][0], 32)) + if (memcmp(&boost::get<txin_to_key>(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<txin_to_key>(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<txout_to_key>(tx.vout[n].target).key); - reconstructed_outPk[n].mask = tx.rct_signatures.outPk[n].mask; - } - // check all this, either recontructed (so should really pass), or not { 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<txin_to_key>(tx.vin[n]).k_image, &II[n], 32)) + if (memcmp(&boost::get<txin_to_key>(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<std::vector<rct::ctkey>> &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; |