diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index e92c9888..5dcb638e 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -842,7 +842,7 @@ namespace tools { const currency::txout_htlc& hltc = out_get_htlc(out_v); //mark this as spent - td.m_flags |= WALLET_TRANSFER_DETAIL_FLAG_SPENT; + td.m_flags |= WALLET_TRANSFER_DETAIL_FLAG_SPENT|WALLET_TRANSFER_DETAIL_CONCISE_MODE_PRESERVE; //create entry for htlc input htlc_expiration_trigger het = AUTO_VAL_INIT(het); het.is_wallet_owns_redeem = (out_key == hltc.pkey_redeem) ? true : false; @@ -2028,10 +2028,27 @@ namespace tools "wrong daemon response: m_start_height=" + std::to_string(res.start_height) + " not less than local blockchain size=" + std::to_string(get_blockchain_current_size())); - handle_pulled_blocks(blocks_added, stop, res, full_reset_needed); + try + { + handle_pulled_blocks(blocks_added, stop, res, full_reset_needed); + } + catch (const tools::error::wallet_error_resync_needed& /*v*/) + { + full_reset_needed = true; + m_full_resync_requested_at_h = get_blockchain_current_size() - blocks_added; + } + if (full_reset_needed) { + //back up m_unconfirmed_txs + //back up std::unordered_map m_tx_keys; + unconfirmed_txs_container tmp_unconfirmed = m_unconfirmed_txs; + tx_secrete_keys_container tmp_secrete_keys = m_tx_keys; + crypto::hash genesis = m_chain.get_genesis(); reset_all(); + m_chain.set_genesis(genesis); + m_unconfirmed_txs = tmp_unconfirmed; + m_tx_keys = tmp_secrete_keys; } } @@ -2120,8 +2137,9 @@ namespace tools } //TODO: take into account date of wallet creation //reorganize - if (m_concise_mode && m_chain.get_blockchain_current_size() - (last_matched_index+1) > m_wallet_concise_mode_max_reorg_blocks) + if (m_concise_mode && m_chain.get_blockchain_current_size() - (last_matched_index) > m_wallet_concise_mode_max_reorg_blocks) { + m_full_resync_requested_at_h = m_chain.get_blockchain_current_size() - (last_matched_index + 1); wallet_reset_needed = true; return; } @@ -2858,6 +2876,7 @@ namespace tools { load_whitelisted_tokens_if_not_loaded(); + bool had_full_reset = false; received_money = false; blocks_fetched = 0; size_t added_blocks = 0; @@ -2882,6 +2901,8 @@ namespace tools return; } reset_count++; + m_height_of_start_sync = 0; + had_full_reset = true; continue; } blocks_fetched += added_blocks; @@ -2919,7 +2940,12 @@ namespace tools handle_expiration_list(tx_expiration_ts_median); handle_contract_expirations(tx_expiration_ts_median); m_found_free_amounts.clear(); - trim_wallet(); + truncate_wallet(); + } + if (had_full_reset) + { + blocks_fetched = get_blockchain_current_size() - m_full_resync_requested_at_h; + m_full_resync_requested_at_h = 0; } @@ -3792,28 +3818,28 @@ namespace tools return true; } //---------------------------------------------------------------------------------------------------- - bool wallet2::trim_wallet() + bool wallet2::truncate_wallet() { if (m_concise_mode) { std::list items_to_remove; for (auto& tr : m_transfers) { - if (tr.second.is_spent()) + if (tr.second.is_spent() && tr.second.m_spent_height != 0 && !(tr.second.m_flags & WALLET_TRANSFER_DETAIL_CONCISE_MODE_PRESERVE) ) { - if (m_concise_mode && tr.second.m_spent_height + m_wallet_concise_mode_max_reorg_blocks < m_chain.get_top_block_height()) + if (tr.second.m_spent_height + m_wallet_concise_mode_max_reorg_blocks < m_chain.get_top_block_height()) { items_to_remove.push_back(tr.first); } } } - return trim_transfers_and_history(items_to_remove); + return truncate_transfers_and_history(items_to_remove); } return true; } //---------------------------------------------------------------------------------------------------- - bool wallet2::trim_transfers_and_history(const std::list& items_to_remove) + bool wallet2::truncate_transfers_and_history(const std::list& items_to_remove) { //delete from m_transfers for (auto item : items_to_remove) @@ -6070,6 +6096,10 @@ namespace tools { for (auto htlc_entry : m_active_htlcs_txid) { + //auto it = m_transfers.find(htlc_entry.second); + //if (it == m_transfers.end()) + // continue; + //const transfer_details& td = it->second; const transfer_details& td = m_transfers.at(htlc_entry.second); if (only_redeem_txs && !(td.m_flags & WALLET_TRANSFER_DETAIL_FLAG_HTLC_REDEEM)) { diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index f8922d54..25220784 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -136,9 +136,9 @@ namespace tools std::unordered_map m_key_images; std::vector m_transfer_history; std::unordered_map m_unconfirmed_in_transfers; - std::unordered_map m_unconfirmed_txs; + unconfirmed_txs_container m_unconfirmed_txs; std::unordered_set m_unconfirmed_multisig_transfers; - std::unordered_map m_tx_keys; + tx_secrete_keys_container m_tx_keys; std::unordered_map m_own_asset_descriptors; std::unordered_map m_custom_assets; //assets that manually added by user mutable std::unordered_map m_whitelisted_assets; //assets that whitelisted @@ -577,8 +577,8 @@ namespace tools currency::transaction &escrow_template_tx); bool check_connection(); - bool trim_transfers_and_history(const std::list& items_to_remove); - bool trim_wallet(); + bool truncate_transfers_and_history(const std::list& items_to_remove); + bool truncate_wallet(); // PoS mining void do_pos_mining_prepare_entry(mining_context& cxt, const transfer_details& td); @@ -976,7 +976,7 @@ private: std::atomic m_concise_mode = true; //in this mode the wallet don't keep spent entries in m_transfers as well as m_recent_transfers longer then 100 entries uint64_t m_last_known_daemon_height = 0; uint64_t m_wallet_concise_mode_max_reorg_blocks = 5;//WALLET_CONCISE_MODE_MAX_REORG_BLOCKS; - + uint64_t m_full_resync_requested_at_h = 0; //this needed to access wallets state in coretests, for creating abnormal blocks and tranmsactions friend class test_generator; @@ -1089,7 +1089,13 @@ namespace tools if (tr_index != UINT64_MAX) { - transfer_details& td = m_transfers.at(tr_index); + auto it_tr = m_transfers.find(tr_index); + if (it_tr == m_transfers.end()) + { + throw tools::error::wallet_error_resync_needed(); + } + transfer_details& td = it_tr->second; + ptc.total_balance_change[td.get_asset_id()] -= td.amount(); if (td.is_native_coin()) { diff --git a/src/wallet/wallet2_base.h b/src/wallet/wallet2_base.h index 1917db17..116a27e1 100644 --- a/src/wallet/wallet2_base.h +++ b/src/wallet/wallet2_base.h @@ -52,6 +52,7 @@ #define WALLET_TRANSFER_DETAIL_FLAG_MINED_TRANSFER uint32_t(1 << 3) #define WALLET_TRANSFER_DETAIL_FLAG_COLD_SIG_RESERVATION uint32_t(1 << 4) // transfer is reserved for cold-signing (unsigned tx was created and passed for signing) #define WALLET_TRANSFER_DETAIL_FLAG_HTLC_REDEEM uint32_t(1 << 5) // for htlc keeps info if this htlc belong as redeem or as refund +#define WALLET_TRANSFER_DETAIL_CONCISE_MODE_PRESERVE uint32_t(1 << 6) // do not truncate this output with CONCISE mode @@ -516,6 +517,8 @@ namespace tools typedef std::map > free_amounts_cache_type; typedef std::unordered_map free_assets_amounts_cache_type; typedef std::unordered_map, uint64_t> amount_gindex_to_transfer_id_container; // maps [amount; gindex] -> tid + typedef std::unordered_map tx_secrete_keys_container; + typedef std::unordered_map unconfirmed_txs_container; }// namespace tools diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h index f8b4e7fe..636f7bf6 100644 --- a/src/wallet/wallet_errors.h +++ b/src/wallet/wallet_errors.h @@ -321,6 +321,10 @@ namespace tools const currency::transaction m_tx; }; //---------------------------------------------------------------------------------------------------- + struct wallet_error_resync_needed + { + }; + struct tx_parse_error : public refresh_error { explicit tx_parse_error(std::string&& loc, const currency::blobdata& tx_blob) diff --git a/tests/core_tests/escrow_wallet_altchain_test.cpp b/tests/core_tests/escrow_wallet_altchain_test.cpp index a59063c2..514ebc82 100644 --- a/tests/core_tests/escrow_wallet_altchain_test.cpp +++ b/tests/core_tests/escrow_wallet_altchain_test.cpp @@ -317,7 +317,8 @@ bool escrow_altchain_meta_impl::c1(currency::core& c, size_t ev_index, const std alice_wlt->scan_tx_pool(stub); size_t blocks_fetched = 0; alice_wlt->refresh(blocks_fetched); - CHECK_AND_ASSERT_MES(blocks_fetched == se.expected_blocks, false, "Alice got " << blocks_fetched << " after refresh, but " << se.expected_blocks << " is expected"); + //fetched blocks disabled since resync might happened on different situation and number of blocks_fetched might be unexpected + //CHECK_AND_ASSERT_MES(blocks_fetched == se.expected_blocks, false, "Alice got " << blocks_fetched << " after refresh, but " << se.expected_blocks << " is expected"); LOG_PRINT_GREEN("Alice's transfers:" << ENDL << alice_wlt->dump_trunsfers(), LOG_LEVEL_1); if (se.a_balance != UINT64_MAX) { @@ -335,7 +336,8 @@ bool escrow_altchain_meta_impl::c1(currency::core& c, size_t ev_index, const std bob_wlt->scan_tx_pool(stub); blocks_fetched = 0; bob_wlt->refresh(blocks_fetched); - CHECK_AND_ASSERT_MES(blocks_fetched == se.expected_blocks, false, "Bob got " << blocks_fetched << " after refresh, but " << se.expected_blocks << " is expected"); + //fetched blocks disabled since resync might happened on different situation and number of blocks_fetched might be unexpected + //CHECK_AND_ASSERT_MES(blocks_fetched == se.expected_blocks, false, "Bob got " << blocks_fetched << " after refresh, but " << se.expected_blocks << " is expected"); LOG_PRINT_GREEN("Bob's transfers:" << ENDL << bob_wlt->dump_trunsfers(), LOG_LEVEL_1); if (se.b_balance != UINT64_MAX) { diff --git a/tests/core_tests/wallet_test_core_proxy.cpp b/tests/core_tests/wallet_test_core_proxy.cpp index db0fd0fc..b7da7b2e 100644 --- a/tests/core_tests/wallet_test_core_proxy.cpp +++ b/tests/core_tests/wallet_test_core_proxy.cpp @@ -89,7 +89,7 @@ bool wallet_test_core_proxy::call_COMMAND_RPC_GET_BLOCKS_FAST(const currency::CO rsp.current_height = m_blocks.size(); rsp.status = API_RETURN_CODE_OK; - if (!m_first_call) + if (!m_first_call && rsp.start_height != 0 /*second condition needed for re-sync in concise_mode*/) { m_first_call = true; return true; // respond with empty blocks on second call to gracefully stop wallet refreshing diff --git a/tests/core_tests/wallet_tests.cpp b/tests/core_tests/wallet_tests.cpp index e6bc9f5b..bb822de1 100644 --- a/tests/core_tests/wallet_tests.cpp +++ b/tests/core_tests/wallet_tests.cpp @@ -1490,6 +1490,9 @@ bool gen_wallet_decrypted_attachments::generate(std::vector& e REWIND_BLOCKS_N(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); CREATE_TEST_WALLET(alice_wlt, alice_acc, blk_0); + //disable concise because this test count on on_transfer callbacks and resync cause firing on_transfer() for previous transactions + alice_wlt->set_concise_mode(false); + REFRESH_TEST_WALLET_AT_GEN_TIME(events, alice_wlt, blk_0r, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); // these attachments will be used across all the transactions in this test