diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h index 160929f8..08cf26d5 100644 --- a/src/crypto/crypto.h +++ b/src/crypto/crypto.h @@ -302,10 +302,11 @@ namespace crypto { } // namespace crypto POD_MAKE_HASHABLE(crypto, public_key) +POD_MAKE_LESS_OPERATOR(crypto, public_key) POD_MAKE_COMPARABLE(crypto, secret_key) POD_MAKE_HASHABLE(crypto, key_image) POD_MAKE_COMPARABLE(crypto, signature) POD_MAKE_COMPARABLE(crypto, key_derivation) POD_MAKE_LESS_OPERATOR(crypto, hash) POD_MAKE_LESS_OPERATOR(crypto, key_image) -POP_GCC_WARNINGS \ No newline at end of file +POP_GCC_WARNINGS diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 59107e5f..99504139 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -92,6 +92,11 @@ namespace tools m_core_runtime_config = currency::get_default_core_runtime_config(); } //--------------------------------------------------------------- + wallet2::~wallet2() + { + // do nothing + } + //--------------------------------------------------------------- uint64_t wallet2::get_max_unlock_time_from_receive_indices(const currency::transaction& tx, const wallet_public::employed_tx_entries& td) { uint64_t max_unlock_time = 0; @@ -5231,7 +5236,11 @@ bool wallet2::reset_history() std::wstring file_path = m_wallet_file; account_base acc_tmp = m_account; auto tx_keys = m_tx_keys; + auto pending_key_images = m_pending_key_images; + crypto::hash genesis_id = m_chain.get_genesis(); clear(); + m_chain.set_genesis(genesis_id); + m_pending_key_images = pending_key_images; m_tx_keys = tx_keys; m_account = acc_tmp; m_password = pass; @@ -7858,6 +7867,100 @@ bool wallet2::is_need_to_split_outputs() return !is_in_hardfork_zone(ZANO_HARDFORK_04_ZARCANUM); } //---------------------------------------------------------------------------------------------------- +void wallet2::restore_key_images_in_wo_wallet(const std::wstring& filename, const std::string& password) const +{ + WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX(!m_watch_only, "restore_key_images_in_wo_wallet can only be used in non watch-only wallet"); + bool r = false; + + // load the given watch-only wallet + wallet2 wo; + wo.load(filename, password); + WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX(wo.is_watch_only(), epee::string_encoding::wstring_to_utf8(filename) << " is not a watch-only wallet"); + if (m_account.get_keys().view_secret_key != wo.get_account().get_keys().view_secret_key || + m_account.get_public_address() != wo.get_account().get_public_address()) + { + WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX(false, epee::string_encoding::wstring_to_utf8(filename) << " has keys that differ from this wallet's keys; wrong wallet?"); + } + + // + // 1. Find missing key images and calculate them using secret spend key. Populate missing_ki_items container. + // + struct missing_ki_item + { + crypto::public_key tx_pub_key; + crypto::public_key out_pub_key; + uint64_t output_index; + uint64_t transfer_index; + crypto::key_image ki; + }; + + std::set transfer_indices_to_include; + for(auto el : wo.m_pending_key_images) + { + const crypto::key_image& ki = el.second; + auto it = wo.m_key_images.find(ki); + WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(it != wo.m_key_images.end(), "restore_key_images_in_wo_wallet: m_key_images inconsistency, ki: " << ki); + size_t transfer_index = it->second; + transfer_indices_to_include.insert(transfer_index); + WLT_LOG_L1("restore_key_images_in_wo_wallet: transfer " << transfer_index << " is in m_pending_key_images, included"); + } + + for(auto el : wo.m_transfers) + { + size_t transfer_index = el.first; + if (el.second.m_key_image == null_ki) + { + transfer_indices_to_include.insert(transfer_index); + WLT_LOG_L1("restore_key_images_in_wo_wallet: ki is null for ti " << transfer_index << ", included"); + } + } + + // now in transfer_indices_to_include we have ordered and unique list of transfer indices + std::vector missing_ki_items; + std::set pk_uniqueness_set; + for(size_t transfer_index : transfer_indices_to_include) + { + const auto& td = wo.m_transfers.at(transfer_index); + auto& item = missing_ki_items.emplace_back(); + item.output_index = td.m_internal_output_index; + crypto::public_key out_pub_key{}; + r = get_out_pub_key_from_tx_out_v(td.output(), out_pub_key); + WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(r, "restore_key_images_in_wo_wallet failed for ti: " << transfer_index); + WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(pk_uniqueness_set.insert(out_pub_key).second, "restore_key_images_in_wo_wallet: out pub key in not unique: " << out_pub_key << ", ti: " << transfer_index); + item.out_pub_key = out_pub_key; + item.transfer_index = transfer_index; + item.tx_pub_key = get_tx_pub_key_from_extra(td.m_ptx_wallet_info->m_tx); + WLT_LOG_L0("restore_key_images_in_wo_wallet: including: " << item.out_pub_key << ", " << transfer_index); + + // calculate key image + keypair ephemeral{}; + generate_key_image_helper(m_account.get_keys(), item.tx_pub_key, item.output_index, ephemeral, item.ki); + WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX(ephemeral.pub == item.out_pub_key, "restore_key_images_in_wo_wallet: out pub key missmatch, ti: " << transfer_index); + }; + + // + // 2. Actually restore key images in the 'wo' object. + // + r = wo.m_pending_key_images_file_container.clear(); + WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX(r, "restore_key_images_in_wo_wallet: pending ki container clearing failed"); + wo.m_pending_key_images.clear(); + + for(size_t i = 0; i < missing_ki_items.size(); ++i) + { + const auto& item = missing_ki_items[i]; + auto& td = wo.m_transfers[item.transfer_index]; // item.transfer_index validity was checked above + + td.m_key_image = item.ki; // TODO: it's unclear whether we need to update m_transfers[].m_key_image since later I decided to clear history to trigger resync later. Probably, no. -- sowle + r = wo.m_pending_key_images.insert(std::make_pair(item.out_pub_key, item.ki)).second; + WLT_THROW_IF_FALSE_WALLET_INT_ERR_EX(r, "restore_key_images_in_wo_wallet: insert failed, out_pub_key: " << item.out_pub_key << ", i: " << i); + wo.m_pending_key_images_file_container.push_back(out_key_to_ki{item.out_pub_key, item.ki}); + LOG_PRINT_L0("restore_key_images_in_wo_wallet: added #" << i << " ti: " << item.transfer_index << ", pk: " << item.out_pub_key << ", ki: " << item.ki); + } + + wo.reset_history(); + wo.store(); +} +//---------------------------------------------------------------------------------------------------- void wallet2::prepare_tx_destinations(const assets_selection_context& needed_money_map, detail::split_strategy_id_t destination_split_strategy_id, const tx_dust_policy& dust_policy, diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 4d3fa218..c0b08519 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -262,7 +262,7 @@ namespace tools wallet2(const wallet2&) = delete; public: wallet2(); - virtual ~wallet2() {} + virtual ~wallet2(); static std::string transfer_flags_to_str(uint32_t flags); @@ -617,6 +617,8 @@ namespace tools void submit_transfer_files(const std::string& signed_tx_file, currency::transaction& tx); void submit_externally_signed_asset_tx(const currency::finalized_tx& ft, const crypto::generic_schnorr_sig_s& gss_sig, bool unlock_transfers_on_fail, currency::transaction& result_tx, bool& transfers_unlocked); void submit_externally_signed_asset_tx(const currency::finalized_tx& ft, const crypto::eth_signature& eth_sig, bool unlock_transfers_on_fail, currency::transaction& result_tx, bool& transfers_unlocked); + + void restore_key_images_in_wo_wallet(const std::wstring& filename, const std::string& password) const; void sweep_below(size_t fake_outs_count, const currency::account_public_address& destination_addr, uint64_t threshold_amount, const currency::payment_id_t& payment_id, uint64_t fee, size_t& outs_total, uint64_t& amount_total, size_t& outs_swept, uint64_t& amount_swept, currency::transaction* p_result_tx = nullptr, std::string* p_filename_or_unsigned_tx_blob_str = nullptr);