Merge pull request #4889

f42263eb wallet: adds rescan_bc option with preserving key images (Dusan Klinec)
This commit is contained in:
Riccardo Spagni
2019-03-19 10:55:45 +02:00
4 changed files with 160 additions and 26 deletions
+89 -17
View File
@@ -3222,6 +3222,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.
@@ -5468,8 +5488,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();
@@ -5477,25 +5501,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
@@ -11392,6 +11407,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)
@@ -12490,5 +12506,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;
}
}
}