From ae0a840fdaa15054a7c0529869fb20df0e1605ea Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Fri, 11 Feb 2022 18:43:14 +0000 Subject: [PATCH] wallet2: decrease the amount of data exchanged for output export --- src/wallet/wallet2.cpp | 132 ++++++++++++++++++++++++++++++++++++++--- src/wallet/wallet2.h | 45 +++++++++++++- 2 files changed, 167 insertions(+), 10 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index e95e53e0c..b2dece28f 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -6429,7 +6429,7 @@ std::string wallet2::dump_tx_to_str(const std::vector &ptx_vector) c txs.txes.push_back(get_construction_data_with_decrypted_short_payment_id(tx, m_account.get_device())); } - txs.transfers = export_outputs(); + txs.new_transfers = export_outputs(); // save as binary std::ostringstream oss; binary_archive ar(oss); @@ -6570,7 +6570,10 @@ bool wallet2::sign_tx(const std::string &unsigned_filename, const std::string &s //---------------------------------------------------------------------------------------------------- bool wallet2::sign_tx(unsigned_tx_set &exported_txs, std::vector &txs, signed_tx_set &signed_txes) { - import_outputs(exported_txs.transfers); + if (!exported_txs.new_transfers.second.empty()) + import_outputs(exported_txs.new_transfers); + else + import_outputs(exported_txs.transfers); // sign the transactions for (size_t n = 0; n < exported_txs.txes.size(); ++n) @@ -12840,10 +12843,10 @@ void wallet2::import_blockchain(const std::tuple> wallet2::export_outputs(bool all) const +std::pair> wallet2::export_outputs(bool all) const { PERF_TIMER(export_outputs); - std::vector outs; + std::vector outs; size_t offset = 0; if (!all) @@ -12855,7 +12858,22 @@ std::pair> wallet2::expo { const transfer_details &td = m_transfers[n]; - outs.push_back(td); + exported_transfer_details etd; + etd.m_pubkey = td.get_public_key(); + etd.m_tx_pubkey = get_tx_pub_key_from_extra(td.m_tx, td.m_pk_index); + etd.m_internal_output_index = td.m_internal_output_index; + etd.m_global_output_index = td.m_global_output_index; + etd.m_flags.flags = 0; + etd.m_flags.m_spent = td.m_spent; + etd.m_flags.m_frozen = td.m_frozen; + etd.m_flags.m_rct = td.m_rct; + etd.m_flags.m_key_image_known = td.m_key_image_known; + etd.m_flags.m_key_image_request = td.m_key_image_request; + etd.m_flags.m_key_image_partial = td.m_key_image_partial; + etd.m_amount = td.m_amount; + etd.m_additional_tx_keys = get_additional_tx_pub_keys_from_extra(td.m_tx); + + outs.push_back(etd); } return std::make_pair(offset, outs); @@ -12948,6 +12966,93 @@ process: return m_transfers.size(); } //---------------------------------------------------------------------------------------------------- +size_t wallet2::import_outputs(const std::pair> &outputs) +{ + PERF_TIMER(import_outputs); + + THROW_WALLET_EXCEPTION_IF(outputs.first > m_transfers.size(), error::wallet_internal_error, + "Imported outputs omit more outputs that we know of. Try using export_outputs all."); + + const size_t offset = outputs.first; + const size_t original_size = m_transfers.size(); + m_transfers.resize(offset + outputs.second.size()); + for (size_t i = 0; i < offset; ++i) + m_transfers[i].m_key_image_request = false; + for (size_t i = 0; i < outputs.second.size(); ++i) + { + exported_transfer_details etd = outputs.second[i]; + transfer_details &td = m_transfers[i + offset]; + + // setup td with "cheao" loaded data + td.m_block_height = 0; + td.m_txid = crypto::null_hash; + td.m_global_output_index = etd.m_global_output_index; + td.m_spent = etd.m_flags.m_spent; + td.m_frozen = etd.m_flags.m_frozen; + td.m_spent_height = 0; + td.m_mask = rct::identity(); + td.m_amount = etd.m_amount; + td.m_rct = etd.m_flags.m_rct; + td.m_key_image_known = etd.m_flags.m_key_image_known; + td.m_key_image_request = etd.m_flags.m_key_image_request; + td.m_key_image_partial = false; + + // skip those we've already imported, or which have different data + if (i + offset < original_size) + { + bool needs_processing = false; + if (!td.m_key_image_known) + needs_processing = true; + else if (!(etd.m_internal_output_index == td.m_internal_output_index)) + needs_processing = true; + else if (!(etd.m_pubkey == td.get_public_key())) + needs_processing = true; + + if (!needs_processing) + continue; + } + + // construct a synthetix tx prefix that has the info we'll need: the output with its pubkey, the tx pubkey in extra + td.m_tx = {}; + + THROW_WALLET_EXCEPTION_IF(etd.m_internal_output_index >= 65536, error::wallet_internal_error, "internal output index seems outrageously high, rejecting"); + td.m_internal_output_index = etd.m_internal_output_index; + cryptonote::txout_to_key tk; + tk.key = etd.m_pubkey; + cryptonote::tx_out out; + out.amount = etd.m_amount; + out.target = tk; + td.m_tx.vout.resize(etd.m_internal_output_index); + td.m_tx.vout.push_back(out); + + td.m_pk_index = 0; + add_tx_pub_key_to_extra(td.m_tx, etd.m_tx_pubkey); + if (!etd.m_additional_tx_keys.empty()) + add_additional_tx_pub_keys_to_extra(td.m_tx.extra, etd.m_additional_tx_keys); + + // the hot wallet wouldn't have known about key images (except if we already exported them) + cryptonote::keypair in_ephemeral; + + const crypto::public_key &tx_pub_key = etd.m_tx_pubkey; + const std::vector &additional_tx_pub_keys = etd.m_additional_tx_keys; + const crypto::public_key& out_key = etd.m_pubkey; + bool r = cryptonote::generate_key_image_helper(m_account.get_keys(), m_subaddresses, out_key, tx_pub_key, additional_tx_pub_keys, td.m_internal_output_index, in_ephemeral, td.m_key_image, m_account.get_device()); + THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key image"); + if (should_expand(td.m_subaddr_index)) + expand_subaddresses(td.m_subaddr_index); + td.m_key_image_known = true; + td.m_key_image_request = true; + td.m_key_image_partial = false; + THROW_WALLET_EXCEPTION_IF(in_ephemeral.pub != out_key, + error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key at index " + boost::lexical_cast(i + offset)); + + m_key_images[td.m_key_image] = i + offset; + m_pub_keys[td.get_public_key()] = i + offset; + } + + return m_transfers.size(); +} +//---------------------------------------------------------------------------------------------------- size_t wallet2::import_outputs_from_str(const std::string &outputs_st) { PERF_TIMER(import_outputs_from_str); @@ -12986,8 +13091,21 @@ size_t wallet2::import_outputs_from_str(const std::string &outputs_st) try { std::string body(data, headerlen); - std::pair> outputs; + + std::pair> new_outputs; try + { + binary_archive ar{epee::strspan(body)}; + if (::serialization::serialize(ar, new_outputs)) + if (::serialization::check_stream_state(ar)) + loaded = true; + } + catch (...) {} + if (!loaded) + new_outputs.second.clear(); + + std::pair> outputs; + if (!loaded) try { binary_archive ar{epee::strspan(body)}; if (::serialization::serialize(ar, outputs)) @@ -13015,7 +13133,7 @@ size_t wallet2::import_outputs_from_str(const std::string &outputs_st) outputs.second = {}; } - imported_outputs = import_outputs(outputs); + imported_outputs = new_outputs.second.empty() ? import_outputs(outputs) : import_outputs(new_outputs); } catch (const std::exception &e) { diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index d64832b13..d5ce08ef5 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -373,6 +373,40 @@ private: END_SERIALIZE() }; + struct exported_transfer_details + { + crypto::public_key m_pubkey; + uint64_t m_internal_output_index; + uint64_t m_global_output_index; + crypto::public_key m_tx_pubkey; + union + { + struct + { + uint8_t m_spent: 1; + uint8_t m_frozen: 1; + uint8_t m_rct: 1; + uint8_t m_key_image_known: 1; + uint8_t m_key_image_request: 1; // view wallets: we want to request it; cold wallets: it was requested + uint8_t m_key_image_partial: 1; + }; + uint8_t flags; + } m_flags; + uint64_t m_amount; + std::vector m_additional_tx_keys; + + BEGIN_SERIALIZE_OBJECT() + VERSION_FIELD(0) + FIELD(m_pubkey) + VARINT_FIELD(m_internal_output_index) + VARINT_FIELD(m_global_output_index) + FIELD(m_tx_pubkey) + FIELD(m_flags.flags) + VARINT_FIELD(m_amount) + FIELD(m_additional_tx_keys) + END_SERIALIZE() + }; + typedef std::vector amounts_container; struct payment_details { @@ -575,11 +609,15 @@ private: { std::vector txes; std::pair transfers; + std::pair> new_transfers; BEGIN_SERIALIZE_OBJECT() - VERSION_FIELD(0) + VERSION_FIELD(1) FIELD(txes) - FIELD(transfers) + if (version >= 1) + FIELD(new_transfers) + else + FIELD(transfers) END_SERIALIZE() }; @@ -1347,8 +1385,9 @@ private: bool verify_with_public_key(const std::string &data, const crypto::public_key &public_key, const std::string &signature) const; // Import/Export wallet data - std::pair> export_outputs(bool all = false) const; + std::pair> export_outputs(bool all = false) const; std::string export_outputs_to_str(bool all = false) const; + size_t import_outputs(const std::pair> &outputs); size_t import_outputs(const std::pair> &outputs); size_t import_outputs_from_str(const std::string &outputs_st); payment_container export_payments() const;