diff options
Diffstat (limited to 'src/wallet/wallet2.cpp')
-rw-r--r-- | src/wallet/wallet2.cpp | 346 |
1 files changed, 276 insertions, 70 deletions
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 6353285b3..5e862df06 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2014-2018, The Monero Project +// Copyright (c) 2014-2019, The Monero Project // // All rights reserved. // @@ -793,9 +793,8 @@ uint64_t calculate_fee(bool use_per_byte_fee, const cryptonote::transaction &tx, return calculate_fee(base_fee, blob_size, fee_multiplier); } -crypto::hash8 get_short_payment_id(const tools::wallet2::pending_tx &ptx, hw::device &hwdev) +bool get_short_payment_id(crypto::hash8 &payment_id8, const tools::wallet2::pending_tx &ptx, hw::device &hwdev) { - crypto::hash8 payment_id8 = null_hash8; std::vector<tx_extra_field> tx_extra_fields; parse_tx_extra(ptx.tx.extra, tx_extra_fields); // ok if partially parsed cryptonote::tx_extra_nonce extra_nonce; @@ -806,19 +805,19 @@ crypto::hash8 get_short_payment_id(const tools::wallet2::pending_tx &ptx, hw::de if (ptx.dests.empty()) { MWARNING("Encrypted payment id found, but no destinations public key, cannot decrypt"); - return crypto::null_hash8; + return false; } - hwdev.decrypt_payment_id(payment_id8, ptx.dests[0].addr.m_view_public_key, ptx.tx_key); + return hwdev.decrypt_payment_id(payment_id8, ptx.dests[0].addr.m_view_public_key, ptx.tx_key); } } - return payment_id8; + return false; } tools::wallet2::tx_construction_data get_construction_data_with_decrypted_short_payment_id(const tools::wallet2::pending_tx &ptx, hw::device &hwdev) { tools::wallet2::tx_construction_data construction_data = ptx.construction_data; - crypto::hash8 payment_id = get_short_payment_id(ptx,hwdev); - if (payment_id != null_hash8) + crypto::hash8 payment_id = null_hash8; + if (get_short_payment_id(payment_id, ptx, hwdev)) { // Remove encrypted remove_field_from_tx_extra(construction_data.extra, typeid(cryptonote::tx_extra_nonce)); @@ -929,22 +928,30 @@ wallet_keys_unlocker::~wallet_keys_unlocker() } } -void wallet_device_callback::on_button_request() +void wallet_device_callback::on_button_request(uint64_t code) { if (wallet) - wallet->on_button_request(); + wallet->on_device_button_request(code); } -void wallet_device_callback::on_pin_request(epee::wipeable_string & pin) +boost::optional<epee::wipeable_string> wallet_device_callback::on_pin_request() { if (wallet) - wallet->on_pin_request(pin); + return wallet->on_device_pin_request(); + return boost::none; } -void wallet_device_callback::on_passphrase_request(bool on_device, epee::wipeable_string & passphrase) +boost::optional<epee::wipeable_string> wallet_device_callback::on_passphrase_request(bool on_device) { if (wallet) - wallet->on_passphrase_request(on_device, passphrase); + return wallet->on_device_passphrase_request(on_device); + return boost::none; +} + +void wallet_device_callback::on_progress(const hw::device_progress& event) +{ + if (wallet) + wallet->on_device_progress(event); } wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended): @@ -1495,11 +1502,17 @@ void wallet2::scan_output(const cryptonote::transaction &tx, bool miner_tx, cons } THROW_WALLET_EXCEPTION_IF(std::find(outs.begin(), outs.end(), i) != outs.end(), error::wallet_internal_error, "Same output cannot be added twice"); - outs.push_back(i); if (tx_scan_info.money_transfered == 0 && !miner_tx) { tx_scan_info.money_transfered = tools::decodeRct(tx.rct_signatures, tx_scan_info.received->derivation, i, tx_scan_info.mask, m_account.get_device()); } + if (tx_scan_info.money_transfered == 0) + { + MERROR("Invalid output amount, skipping"); + tx_scan_info.error = true; + return; + } + outs.push_back(i); THROW_WALLET_EXCEPTION_IF(tx_money_got_in_outs[tx_scan_info.received->index] >= std::numeric_limits<uint64_t>::max() - tx_scan_info.money_transfered, error::wallet_internal_error, "Overflow in received amounts"); tx_money_got_in_outs[tx_scan_info.received->index] += tx_scan_info.money_transfered; @@ -2878,6 +2891,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo std::vector<parsed_block> parsed_blocks; bool refreshed = false; std::shared_ptr<std::map<std::pair<uint64_t, uint64_t>, size_t>> output_tracker_cache; + hw::device &hwdev = m_account.get_device(); // pull the first set of blocks get_short_chain_history(short_chain_history, (m_first_refresh_done || trusted_daemon) ? 1 : FIRST_REFRESH_GRANULARITY); @@ -3034,6 +3048,7 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo LOG_PRINT_L1("Failed to check pending transactions"); } + hwdev.computing_key_images(false); m_first_refresh_done = true; LOG_PRINT_L1("Refresh done, blocks received: " << blocks_fetched << ", balance (all accounts): " << print_money(balance_all()) << ", unlocked: " << print_money(unlocked_balance_all())); @@ -3217,6 +3232,26 @@ bool wallet2::clear() m_device_last_key_image_sync = 0; return true; } +//---------------------------------------------------------------------------------------------------- +void wallet2::clear_soft(bool keep_key_images) +{ + m_blockchain.clear(); + m_transfers.clear(); + if (!keep_key_images) + m_key_images.clear(); + m_pub_keys.clear(); + m_unconfirmed_txs.clear(); + m_payments.clear(); + m_confirmed_txs.clear(); + m_unconfirmed_payments.clear(); + m_scanned_pool_txs[0].clear(); + m_scanned_pool_txs[1].clear(); + + cryptonote::block b; + generate_genesis(b); + m_blockchain.push_back(get_block_hash(b)); + m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx); +} /*! * \brief Stores wallet information to wallet file. @@ -5463,8 +5498,12 @@ void wallet2::rescan_spent() } } //---------------------------------------------------------------------------------------------------- -void wallet2::rescan_blockchain(bool hard, bool refresh) +void wallet2::rescan_blockchain(bool hard, bool refresh, bool keep_key_images) { + CHECK_AND_ASSERT_THROW_MES(!hard || !keep_key_images, "Cannot preserve key images on hard rescan"); + const size_t transfers_cnt = m_transfers.size(); + crypto::hash transfers_hash{}; + if(hard) { clear(); @@ -5472,25 +5511,16 @@ void wallet2::rescan_blockchain(bool hard, bool refresh) } else { - m_blockchain.clear(); - m_transfers.clear(); - m_key_images.clear(); - m_pub_keys.clear(); - m_unconfirmed_txs.clear(); - m_payments.clear(); - m_confirmed_txs.clear(); - m_unconfirmed_payments.clear(); - m_scanned_pool_txs[0].clear(); - m_scanned_pool_txs[1].clear(); - - cryptonote::block b; - generate_genesis(b); - m_blockchain.push_back(get_block_hash(b)); - m_last_block_reward = cryptonote::get_outs_money_amount(b.miner_tx); + if (keep_key_images && refresh) + hash_m_transfers((int64_t) transfers_cnt, transfers_hash); + clear_soft(keep_key_images); } if (refresh) this->refresh(false); + + if (refresh && keep_key_images) + finish_rescan_bc_keep_key_images(transfers_cnt, transfers_hash); } //---------------------------------------------------------------------------------------------------- bool wallet2::is_transfer_unlocked(const transfer_details& td) const @@ -6329,17 +6359,17 @@ bool wallet2::save_multisig_tx(const std::vector<pending_tx>& ptx_vector, const return epee::file_io_utils::save_string_to_file(filename, ciphertext); } //---------------------------------------------------------------------------------------------------- -bool wallet2::load_multisig_tx(cryptonote::blobdata s, multisig_tx_set &exported_txs, std::function<bool(const multisig_tx_set&)> accept_func) +bool wallet2::parse_multisig_tx_from_str(std::string multisig_tx_st, multisig_tx_set &exported_txs) const { const size_t magiclen = strlen(MULTISIG_UNSIGNED_TX_PREFIX); - if (strncmp(s.c_str(), MULTISIG_UNSIGNED_TX_PREFIX, magiclen)) + if (strncmp(multisig_tx_st.c_str(), MULTISIG_UNSIGNED_TX_PREFIX, magiclen)) { LOG_PRINT_L0("Bad magic from multisig tx data"); return false; } try { - s = decrypt_with_view_secret_key(std::string(s, magiclen)); + multisig_tx_st = decrypt_with_view_secret_key(std::string(multisig_tx_st, magiclen)); } catch (const std::exception &e) { @@ -6348,7 +6378,7 @@ bool wallet2::load_multisig_tx(cryptonote::blobdata s, multisig_tx_set &exported } try { - std::istringstream iss(s); + std::istringstream iss(multisig_tx_st); boost::archive::portable_binary_iarchive ar(iss); ar >> exported_txs; } @@ -6370,6 +6400,17 @@ bool wallet2::load_multisig_tx(cryptonote::blobdata s, multisig_tx_set &exported CHECK_AND_ASSERT_MES(ptx.construction_data.sources.size() == ptx.tx.vin.size(), false, "Mismatched sources/vin sizes"); } + return true; +} +//---------------------------------------------------------------------------------------------------- +bool wallet2::load_multisig_tx(cryptonote::blobdata s, multisig_tx_set &exported_txs, std::function<bool(const multisig_tx_set&)> accept_func) +{ + if(!parse_multisig_tx_from_str(s, exported_txs)) + { + LOG_PRINT_L0("Failed to parse multisig transaction from string"); + return false; + } + LOG_PRINT_L1("Loaded multisig tx unsigned data from binary: " << exported_txs.m_ptx.size() << " transactions"); for (auto &ptx: exported_txs.m_ptx) LOG_PRINT_L0(cryptonote::obj_to_json_str(ptx.tx)); @@ -9154,6 +9195,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp { const size_t estimated_rct_tx_weight = estimate_tx_weight(use_rct, tx.selected_transfers.size(), fake_outs_count, tx.dsts.size()+1, extra.size(), bulletproof); try_tx = dsts.empty() || (estimated_rct_tx_weight >= TX_WEIGHT_TARGET(upper_transaction_weight_limit)); + THROW_WALLET_EXCEPTION_IF(try_tx && tx.dsts.empty(), error::tx_too_big, estimated_rct_tx_weight, upper_transaction_weight_limit); } } @@ -9649,6 +9691,7 @@ void wallet2::cold_sign_tx(const std::vector<pending_tx>& ptx_vector, signed_tx_ hw::wallet_shim wallet_shim; setup_shim(&wallet_shim, this); aux_data.tx_recipients = dsts_info; + aux_data.bp_version = use_fork_rules(HF_VERSION_SMALLER_BP, -10) ? 2 : 1; dev_cold->tx_sign(&wallet_shim, txs, exported_txs, aux_data); tx_device_aux = aux_data.tx_device_aux; @@ -9865,7 +9908,7 @@ void wallet2::discard_unmixable_outputs() } } -bool wallet2::get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys) const +bool wallet2::get_tx_key_cached(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys) const { additional_tx_keys.clear(); const std::unordered_map<crypto::hash, crypto::secret_key>::const_iterator i = m_tx_keys.find(txid); @@ -9878,6 +9921,82 @@ bool wallet2::get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, s return true; } //---------------------------------------------------------------------------------------------------- +bool wallet2::get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys) +{ + bool r = get_tx_key_cached(txid, tx_key, additional_tx_keys); + if (r) + { + return true; + } + + auto & hwdev = get_account().get_device(); + + // So far only Cold protocol devices are supported. + if (hwdev.device_protocol() != hw::device::PROTOCOL_COLD) + { + return false; + } + + const auto tx_data_it = m_tx_device.find(txid); + if (tx_data_it == m_tx_device.end()) + { + MDEBUG("Aux data not found for txid: " << txid); + return false; + } + + auto dev_cold = dynamic_cast<::hw::device_cold*>(&hwdev); + CHECK_AND_ASSERT_THROW_MES(dev_cold, "Device does not implement cold signing interface"); + if (!dev_cold->is_get_tx_key_supported()) + { + MDEBUG("get_tx_key not supported by the device"); + return false; + } + + hw::device_cold::tx_key_data_t tx_key_data; + dev_cold->load_tx_key_data(tx_key_data, tx_data_it->second); + + // Load missing tx prefix hash + if (tx_key_data.tx_prefix_hash.empty()) + { + COMMAND_RPC_GET_TRANSACTIONS::request req; + COMMAND_RPC_GET_TRANSACTIONS::response res; + req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid)); + req.decode_as_json = false; + req.prune = true; + m_daemon_rpc_mutex.lock(); + bool ok = epee::net_utils::invoke_http_json("/gettransactions", req, res, m_http_client); + m_daemon_rpc_mutex.unlock(); + THROW_WALLET_EXCEPTION_IF(!ok || (res.txs.size() != 1 && res.txs_as_hex.size() != 1), + error::wallet_internal_error, "Failed to get transaction from daemon"); + + cryptonote::transaction tx; + crypto::hash tx_hash{}; + cryptonote::blobdata tx_data; + crypto::hash tx_prefix_hash{}; + ok = string_tools::parse_hexstr_to_binbuff(res.txs_as_hex.front(), tx_data); + THROW_WALLET_EXCEPTION_IF(!ok, error::wallet_internal_error, "Failed to parse transaction from daemon"); + THROW_WALLET_EXCEPTION_IF(!cryptonote::parse_and_validate_tx_from_blob(tx_data, tx, tx_hash, tx_prefix_hash), + error::wallet_internal_error, "Failed to validate transaction from daemon"); + THROW_WALLET_EXCEPTION_IF(tx_hash != txid, error::wallet_internal_error, + "Failed to get the right transaction from daemon"); + + tx_key_data.tx_prefix_hash = std::string(tx_prefix_hash.data, 32); + } + + std::vector<crypto::secret_key> tx_keys; + dev_cold->get_tx_key(tx_keys, tx_key_data, m_account.get_keys().m_view_secret_key); + if (tx_keys.empty()) + { + return false; + } + + tx_key = tx_keys[0]; + tx_keys.erase(tx_keys.begin()); + additional_tx_keys = tx_keys; + + return true; +} +//---------------------------------------------------------------------------------------------------- void wallet2::set_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys) { // fetch tx from daemon and check if secret keys agree with corresponding public keys @@ -10280,7 +10399,8 @@ std::string wallet2::get_tx_proof(const crypto::hash &txid, const cryptonote::ac { crypto::secret_key tx_key; std::vector<crypto::secret_key> additional_tx_keys; - THROW_WALLET_EXCEPTION_IF(!get_tx_key(txid, tx_key, additional_tx_keys), error::wallet_internal_error, "Tx secret key wasn't found in the wallet file."); + bool found_tx_key = get_tx_key(txid, tx_key, additional_tx_keys); + THROW_WALLET_EXCEPTION_IF(!found_tx_key, error::wallet_internal_error, "Tx secret key wasn't found in the wallet file."); const size_t num_sigs = 1 + additional_tx_keys.size(); shared_secret.resize(num_sigs); @@ -11019,15 +11139,6 @@ crypto::public_key wallet2::get_tx_pub_key_from_received_outs(const tools::walle size_t pk_index = 0; hw::device &hwdev = m_account.get_device(); - const std::vector<crypto::public_key> additional_tx_pub_keys = get_additional_tx_pub_keys_from_extra(td.m_tx); - std::vector<crypto::key_derivation> additional_derivations; - for (size_t i = 0; i < additional_tx_pub_keys.size(); ++i) - { - additional_derivations.push_back({}); - bool r = hwdev.generate_key_derivation(additional_tx_pub_keys[i], keys.m_view_secret_key, additional_derivations.back()); - THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, "Failed to generate key derivation"); - } - while (find_tx_extra_field_by_type(tx_extra_fields, pub_key_field, pk_index++)) { const crypto::public_key tx_pub_key = pub_key_field.pub_key; crypto::key_derivation derivation; @@ -11037,16 +11148,15 @@ crypto::public_key wallet2::get_tx_pub_key_from_received_outs(const tools::walle for (size_t i = 0; i < td.m_tx.vout.size(); ++i) { tx_scan_info_t tx_scan_info; - check_acc_out_precomp(td.m_tx.vout[i], derivation, additional_derivations, i, tx_scan_info); + check_acc_out_precomp(td.m_tx.vout[i], derivation, {}, i, tx_scan_info); if (!tx_scan_info.error && tx_scan_info.received) return tx_pub_key; } } - // we found no key yielding an output - THROW_WALLET_EXCEPTION_IF(true, error::wallet_internal_error, - "Public key yielding at least one output wasn't found in the transaction extra"); - return crypto::null_pkey; + // we found no key yielding an output, but it might be in the additional + // tx pub keys only, which we do not need to check, so return the first one + return tx_pub_key; } bool wallet2::export_key_images(const std::string &filename) const @@ -11288,6 +11398,17 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag } PERF_TIMER_STOP(import_key_images_C); + // accumulate outputs before the updated data + for(size_t i = 0; i < offset; ++i) + { + const transfer_details &td = m_transfers[i]; + uint64_t amount = td.amount(); + if (td.m_spent) + spent += amount; + else + unspent += amount; + } + PERF_TIMER_START(import_key_images_D); for(size_t i = 0; i < signed_key_images.size(); ++i) { @@ -11397,6 +11518,7 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag auto it = m_key_images.find(boost::get<cryptonote::txin_to_key>(in).k_image); if (it != m_key_images.end()) { + THROW_WALLET_EXCEPTION_IF(it->second >= m_transfers.size(), error::wallet_internal_error, std::string("Key images cache contains illegal transfer offset: ") + std::to_string(it->second) + std::string(" m_transfers.size() = ") + std::to_string(m_transfers.size())); const transfer_details& td = m_transfers[it->second]; uint64_t amount = boost::get<cryptonote::txin_to_key>(in).amount; if (amount > 0) @@ -11450,32 +11572,52 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag PERF_TIMER_STOP(import_key_images_G); } - return m_transfers[signed_key_images.size() - 1].m_block_height; + // this can be 0 if we do not know the height + return m_transfers[signed_key_images.size() + offset - 1].m_block_height; } -bool wallet2::import_key_images(std::vector<crypto::key_image> key_images) +bool wallet2::import_key_images(std::vector<crypto::key_image> key_images, size_t offset, boost::optional<std::unordered_set<size_t>> selected_transfers) { - if (key_images.size() > m_transfers.size()) + if (key_images.size() + offset > m_transfers.size()) { LOG_PRINT_L1("More key images returned that we know outputs for"); return false; } - for (size_t i = 0; i < key_images.size(); ++i) + for (size_t ki_idx = 0; ki_idx < key_images.size(); ++ki_idx) { - transfer_details &td = m_transfers[i]; - if (td.m_key_image_known && !td.m_key_image_partial && td.m_key_image != key_images[i]) - LOG_PRINT_L0("WARNING: imported key image differs from previously known key image at index " << i << ": trusting imported one"); - td.m_key_image = key_images[i]; - m_key_images[m_transfers[i].m_key_image] = i; + const size_t transfer_idx = ki_idx + offset; + if (selected_transfers && selected_transfers.get().find(transfer_idx) == selected_transfers.get().end()) + continue; + + transfer_details &td = m_transfers[transfer_idx]; + if (td.m_key_image_known && !td.m_key_image_partial && td.m_key_image != key_images[ki_idx]) + LOG_PRINT_L0("WARNING: imported key image differs from previously known key image at index " << ki_idx << ": trusting imported one"); + td.m_key_image = key_images[ki_idx]; + m_key_images[td.m_key_image] = transfer_idx; td.m_key_image_known = true; td.m_key_image_request = false; td.m_key_image_partial = false; - m_pub_keys[m_transfers[i].get_public_key()] = i; + m_pub_keys[td.get_public_key()] = transfer_idx; } return true; } +bool wallet2::import_key_images(signed_tx_set & signed_tx, size_t offset, bool only_selected_transfers) +{ + std::unordered_set<size_t> selected_transfers; + if (only_selected_transfers) + { + for (const pending_tx & ptx : signed_tx.ptx) + { + for (const size_t s: ptx.selected_transfers) + selected_transfers.insert(s); + } + } + + return import_key_images(signed_tx.key_images, offset, only_selected_transfers ? boost::make_optional(selected_transfers) : boost::none); +} + wallet2::payment_container wallet2::export_payments() const { payment_container payments; @@ -12456,22 +12598,30 @@ wallet_device_callback * wallet2::get_device_callback() } return m_device_callback.get(); }//---------------------------------------------------------------------------------------------------- -void wallet2::on_button_request() +void wallet2::on_device_button_request(uint64_t code) { - if (0 != m_callback) - m_callback->on_button_request(); + if (nullptr != m_callback) + m_callback->on_device_button_request(code); } //---------------------------------------------------------------------------------------------------- -void wallet2::on_pin_request(epee::wipeable_string & pin) +boost::optional<epee::wipeable_string> wallet2::on_device_pin_request() { - if (0 != m_callback) - m_callback->on_pin_request(pin); + if (nullptr != m_callback) + return m_callback->on_device_pin_request(); + return boost::none; } //---------------------------------------------------------------------------------------------------- -void wallet2::on_passphrase_request(bool on_device, epee::wipeable_string & passphrase) +boost::optional<epee::wipeable_string> wallet2::on_device_passphrase_request(bool on_device) { - if (0 != m_callback) - m_callback->on_passphrase_request(on_device, passphrase); + if (nullptr != m_callback) + return m_callback->on_device_passphrase_request(on_device); + return boost::none; +} +//---------------------------------------------------------------------------------------------------- +void wallet2::on_device_progress(const hw::device_progress& event) +{ + if (nullptr != m_callback) + m_callback->on_device_progress(event); } //---------------------------------------------------------------------------------------------------- std::string wallet2::get_rpc_status(const std::string &s) const @@ -12495,5 +12645,61 @@ void wallet2::throw_on_rpc_response_error(const boost::optional<std::string> &st THROW_WALLET_EXCEPTION_IF(*status == CORE_RPC_STATUS_BUSY, tools::error::daemon_busy, method); THROW_WALLET_EXCEPTION_IF(*status != CORE_RPC_STATUS_OK, tools::error::wallet_generic_rpc_error, method, m_trusted_daemon ? *status : "daemon error"); } +//---------------------------------------------------------------------------------------------------- +void wallet2::hash_m_transfer(const transfer_details & transfer, crypto::hash &hash) const +{ + KECCAK_CTX state; + keccak_init(&state); + keccak_update(&state, (const uint8_t *) transfer.m_txid.data, sizeof(transfer.m_txid.data)); + keccak_update(&state, (const uint8_t *) transfer.m_internal_output_index, sizeof(transfer.m_internal_output_index)); + keccak_update(&state, (const uint8_t *) transfer.m_global_output_index, sizeof(transfer.m_global_output_index)); + keccak_update(&state, (const uint8_t *) transfer.m_amount, sizeof(transfer.m_amount)); + keccak_finish(&state, (uint8_t *) hash.data); +} +//---------------------------------------------------------------------------------------------------- +uint64_t wallet2::hash_m_transfers(int64_t transfer_height, crypto::hash &hash) const +{ + CHECK_AND_ASSERT_THROW_MES(transfer_height > (int64_t)m_transfers.size(), "Hash height is greater than number of transfers"); + + KECCAK_CTX state; + crypto::hash tmp_hash{}; + uint64_t current_height = 0; + + keccak_init(&state); + for(const transfer_details & transfer : m_transfers){ + if (transfer_height >= 0 && current_height >= (uint64_t)transfer_height){ + break; + } + hash_m_transfer(transfer, tmp_hash); + keccak_update(&state, (const uint8_t *) transfer.m_block_height, sizeof(transfer.m_block_height)); + keccak_update(&state, (const uint8_t *) tmp_hash.data, sizeof(tmp_hash.data)); + current_height += 1; + } + + keccak_finish(&state, (uint8_t *) hash.data); + return current_height; +} +//---------------------------------------------------------------------------------------------------- +void wallet2::finish_rescan_bc_keep_key_images(uint64_t transfer_height, const crypto::hash &hash) +{ + // Compute hash of m_transfers, if differs there had to be BC reorg. + crypto::hash new_transfers_hash{}; + hash_m_transfers((int64_t) transfer_height, new_transfers_hash); + + if (new_transfers_hash != hash) + { + // Soft-Reset to avoid inconsistency in case of BC reorg. + clear_soft(false); // keep_key_images works only with soft reset. + THROW_WALLET_EXCEPTION_IF(true, error::wallet_internal_error, "Transfers changed during rescan, soft or hard rescan is needed"); + } + + // Restore key images in m_transfers from m_key_images + for(auto it = m_key_images.begin(); it != m_key_images.end(); it++) + { + THROW_WALLET_EXCEPTION_IF(it->second >= m_transfers.size(), error::wallet_internal_error, "Key images cache contains illegal transfer offset"); + m_transfers[it->second].m_key_image = it->first; + m_transfers[it->second].m_key_image_known = true; + } +} } |