diff options
Diffstat (limited to 'src/cryptonote_core')
-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; |