From 769ae42a7b0e7e73a13ef578ec9a0ecb06df22c1 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Mon, 10 Sep 2018 13:08:34 +0000 Subject: [PATCH] wallet2: faster output and key image import/export --- src/wallet/wallet2.cpp | 94 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 76 insertions(+), 18 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index c27e4e820..31246d0b1 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -69,6 +69,7 @@ using namespace epee; #include "common/base58.h" #include "common/dns_utils.h" #include "common/notify.h" +#include "common/perf_timer.h" #include "ringct/rctSigs.h" #include "ringdb.h" #include "device/device_cold.hpp" @@ -10517,11 +10518,13 @@ crypto::public_key wallet2::get_tx_pub_key_from_received_outs(const tools::walle bool wallet2::export_key_images(const std::string &filename) const { + PERF_TIMER(export_key_images); std::vector> ski = export_key_images(); std::string magic(KEY_IMAGE_EXPORT_FILE_MAGIC, strlen(KEY_IMAGE_EXPORT_FILE_MAGIC)); const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address; std::string data; + data.reserve(ski.size() * (sizeof(crypto::key_image) + sizeof(crypto::signature)) + 2 * sizeof(crypto::public_key)); data += std::string((const char *)&keys.m_spend_public_key, sizeof(crypto::public_key)); data += std::string((const char *)&keys.m_view_public_key, sizeof(crypto::public_key)); for (const auto &i: ski) @@ -10531,6 +10534,7 @@ bool wallet2::export_key_images(const std::string &filename) const } // encrypt data, keep magic plaintext + PERF_TIMER(export_key_images_encrypt); std::string ciphertext = encrypt_with_view_secret_key(data); return epee::file_io_utils::save_string_to_file(filename, magic + ciphertext); } @@ -10538,6 +10542,7 @@ bool wallet2::export_key_images(const std::string &filename) const //---------------------------------------------------------------------------------------------------- std::vector> wallet2::export_key_images() const { + PERF_TIMER(export_key_images_raw); std::vector> ski; ski.reserve(m_transfers.size()); @@ -10590,6 +10595,7 @@ std::vector> wallet2::export_key uint64_t wallet2::import_key_images(const std::string &filename, uint64_t &spent, uint64_t &unspent) { + PERF_TIMER(import_key_images_fsu); std::string data; bool r = epee::file_io_utils::load_file_to_string(filename, data); @@ -10603,6 +10609,7 @@ uint64_t wallet2::import_key_images(const std::string &filename, uint64_t &spent try { + PERF_TIMER(import_key_images_decrypt); data = decrypt_with_view_secret_key(std::string(data, magiclen)); } catch (const std::exception &e) @@ -10641,6 +10648,7 @@ uint64_t wallet2::import_key_images(const std::string &filename, uint64_t &spent //---------------------------------------------------------------------------------------------------- uint64_t wallet2::import_key_images(const std::vector> &signed_key_images, uint64_t &spent, uint64_t &unspent, bool check_spent) { + PERF_TIMER(import_key_images_lots); COMMAND_RPC_IS_KEY_IMAGE_SPENT::request req = AUTO_VAL_INIT(req); COMMAND_RPC_IS_KEY_IMAGE_SPENT::response daemon_resp = AUTO_VAL_INIT(daemon_resp); @@ -10654,6 +10662,9 @@ uint64_t wallet2::import_key_images(const std::vector(out.target); const crypto::public_key pkey = o.key; - std::vector pkeys; - pkeys.push_back(&pkey); - THROW_WALLET_EXCEPTION_IF(!(rct::scalarmultKey(rct::ki2rct(key_image), rct::curveOrder()) == rct::identity()), - error::wallet_internal_error, "Key image out of validity domain: input " + boost::lexical_cast(n) + "/" - + boost::lexical_cast(signed_key_images.size()) + ", key image " + epee::string_tools::pod_to_hex(key_image)); - - THROW_WALLET_EXCEPTION_IF(!crypto::check_ring_signature((const crypto::hash&)key_image, key_image, pkeys, &signature), - error::signature_check_failed, boost::lexical_cast(n) + "/" - + boost::lexical_cast(signed_key_images.size()) + ", key image " + epee::string_tools::pod_to_hex(key_image) - + ", signature " + epee::string_tools::pod_to_hex(signature) + ", pubkey " + epee::string_tools::pod_to_hex(*pkeys[0])); + if (!td.m_key_image_known || !(key_image == td.m_key_image)) + { + std::vector pkeys; + pkeys.push_back(&pkey); + THROW_WALLET_EXCEPTION_IF(!(rct::scalarmultKey(rct::ki2rct(key_image), rct::curveOrder()) == rct::identity()), + error::wallet_internal_error, "Key image out of validity domain: input " + boost::lexical_cast(n) + "/" + + boost::lexical_cast(signed_key_images.size()) + ", key image " + epee::string_tools::pod_to_hex(key_image)); + THROW_WALLET_EXCEPTION_IF(!crypto::check_ring_signature((const crypto::hash&)key_image, key_image, pkeys, &signature), + error::signature_check_failed, boost::lexical_cast(n) + "/" + + boost::lexical_cast(signed_key_images.size()) + ", key image " + epee::string_tools::pod_to_hex(key_image) + + ", signature " + epee::string_tools::pod_to_hex(signature) + ", pubkey " + epee::string_tools::pod_to_hex(*pkeys[0])); + } req.key_images.push_back(epee::string_tools::pod_to_hex(key_image)); } + PERF_TIMER_STOP(import_key_images_A); + PERF_TIMER_START(import_key_images_B); for (size_t n = 0; n < signed_key_images.size(); ++n) { m_transfers[n].m_key_image = signed_key_images[n].first; @@ -10688,9 +10703,11 @@ uint64_t wallet2::import_key_images(const std::vector spent_key_images; + PERF_TIMER_START(import_key_images_C); for (const transfer_details &td: m_transfers) { for (const cryptonote::txin_v& in : td.m_tx.vin) @@ -10721,10 +10739,12 @@ uint64_t wallet2::import_key_images(const std::vector(in).k_image, td.m_txid)); } } + PERF_TIMER_STOP(import_key_images_C); + PERF_TIMER_START(import_key_images_D); for(size_t i = 0; i < signed_key_images.size(); ++i) { - transfer_details &td = m_transfers[i]; + const transfer_details &td = m_transfers[i]; uint64_t amount = td.amount(); if (td.m_spent) spent += amount; @@ -10742,6 +10762,8 @@ uint64_t wallet2::import_key_images(const std::vectorsecond); } } + PERF_TIMER_STOP(import_key_images_D); + MDEBUG("Total: " << print_money(spent) << " spent, " << print_money(unspent) << " unspent"); if (check_spent) @@ -10751,8 +10773,12 @@ uint64_t wallet2::import_key_images(const std::vector wallet2::export_outputs() const { + PERF_TIMER(export_outputs); std::vector outs; outs.reserve(m_transfers.size()); @@ -10970,29 +11002,53 @@ std::vector wallet2::export_outputs() const //---------------------------------------------------------------------------------------------------- std::string wallet2::export_outputs_to_str() const { - std::vector outs = export_outputs(); + PERF_TIMER(export_outputs_to_str); std::stringstream oss; boost::archive::portable_binary_oarchive ar(oss); - ar << outs; + ar << m_transfers; std::string magic(OUTPUT_EXPORT_FILE_MAGIC, strlen(OUTPUT_EXPORT_FILE_MAGIC)); const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address; std::string header; header += std::string((const char *)&keys.m_spend_public_key, sizeof(crypto::public_key)); header += std::string((const char *)&keys.m_view_public_key, sizeof(crypto::public_key)); + PERF_TIMER(export_outputs_encryption); std::string ciphertext = encrypt_with_view_secret_key(header + oss.str()); return magic + ciphertext; } //---------------------------------------------------------------------------------------------------- size_t wallet2::import_outputs(const std::vector &outputs) { - m_transfers.clear(); - m_transfers.reserve(outputs.size()); + PERF_TIMER(import_outputs); + const size_t original_size = m_transfers.size(); + m_transfers.resize(outputs.size()); for (size_t i = 0; i < outputs.size(); ++i) { transfer_details td = outputs[i]; + // skip those we've already imported, or which have different data + if (i < original_size) + { + // compare the data used to create the key image below + const transfer_details &org_td = m_transfers[i]; + if (!org_td.m_key_image_known) + goto process; +#define CMPF(f) if (!(td.f == org_td.f)) goto process + CMPF(m_txid); + CMPF(m_key_image); + CMPF(m_internal_output_index); +#undef CMPF + if (!(get_transaction_prefix_hash(td.m_tx) == get_transaction_prefix_hash(org_td.m_tx))) + goto process; + + // copy anyway, since the comparison does not include ancillary fields which may have changed + m_transfers[i] = std::move(td); + continue; + } + +process: + // the hot wallet wouldn't have known about key images (except if we already exported them) cryptonote::keypair in_ephemeral; @@ -11011,9 +11067,9 @@ size_t wallet2::import_outputs(const std::vector(i)); - m_key_images[td.m_key_image] = m_transfers.size(); - m_pub_keys[td.get_public_key()] = m_transfers.size(); - m_transfers.push_back(std::move(td)); + m_key_images[td.m_key_image] = i; + m_pub_keys[td.get_public_key()] = i; + m_transfers[i] = std::move(td); } return m_transfers.size(); @@ -11021,6 +11077,7 @@ size_t wallet2::import_outputs(const std::vector