From 5550c0a876961d6212067de5c1433e44255ba8ca Mon Sep 17 00:00:00 2001 From: SNeedlewoods Date: Thu, 20 Mar 2025 20:37:27 +0100 Subject: [PATCH] fix: multisig stale data after failed refresh --- src/wallet/wallet2.cpp | 63 ++++++++++++++++++------------------------ src/wallet/wallet2.h | 4 +-- 2 files changed, 29 insertions(+), 38 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 6bb5cc7f91..65ec6fd7c9 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1187,8 +1187,6 @@ void wallet_device_callback::on_progress(const hw::device_progress& event) wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended, std::unique_ptr http_client_factory): m_http_client(http_client_factory->create()), - m_multisig_rescan_info(NULL), - m_multisig_rescan_k(NULL), m_upper_transaction_weight_limit(0), m_run(true), m_callback(0), @@ -2186,7 +2184,7 @@ void wallet2::scan_output(const cryptonote::transaction &tx, bool miner_tx, cons THROW_WALLET_EXCEPTION_IF(i >= tx.vout.size(), error::wallet_internal_error, "Invalid vout index"); // if keys are encrypted, ask for password - if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only && !m_multisig_rescan_k && !m_background_syncing) + if (m_ask_password == AskPasswordToDecrypt && !m_unattended && !m_watch_only && m_multisig_rescan_k.empty() && !m_background_syncing) { static critical_section password_lock; CRITICAL_REGION_LOCAL(password_lock); @@ -2550,10 +2548,10 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote (*output_tracker_cache)[std::make_pair(tx.vout[o].amount, td.m_global_output_index)] = m_transfers.size() - 1; if (m_multisig) { - THROW_WALLET_EXCEPTION_IF(!m_multisig_rescan_k && m_multisig_rescan_info, + THROW_WALLET_EXCEPTION_IF(m_multisig_rescan_k.empty() && !m_multisig_rescan_info.empty(), error::wallet_internal_error, "NULL m_multisig_rescan_k"); - if (m_multisig_rescan_info && m_multisig_rescan_info->front().size() >= m_transfers.size()) - update_multisig_rescan_info(*m_multisig_rescan_k, *m_multisig_rescan_info, m_transfers.size() - 1); + if (!m_multisig_rescan_info.empty() && m_multisig_rescan_info.front().size() >= m_transfers.size()) + update_multisig_rescan_info(m_multisig_rescan_k, m_multisig_rescan_info, m_transfers.size() - 1); } LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << txid); if (!ignore_callbacks && 0 != m_callback) @@ -2625,10 +2623,10 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote (*output_tracker_cache)[std::make_pair(tx.vout[o].amount, td.m_global_output_index)] = kit->second; if (m_multisig) { - THROW_WALLET_EXCEPTION_IF(!m_multisig_rescan_k && m_multisig_rescan_info, + THROW_WALLET_EXCEPTION_IF(m_multisig_rescan_k.empty() && !m_multisig_rescan_info.empty(), error::wallet_internal_error, "NULL m_multisig_rescan_k"); - if (m_multisig_rescan_info && m_multisig_rescan_info->front().size() >= m_transfers.size()) - update_multisig_rescan_info(*m_multisig_rescan_k, *m_multisig_rescan_info, m_transfers.size() - 1); + if (!m_multisig_rescan_info.empty() && m_multisig_rescan_info.front().size() >= m_transfers.size()) + update_multisig_rescan_info(m_multisig_rescan_k, m_multisig_rescan_info, m_transfers.size() - 1); } THROW_WALLET_EXCEPTION_IF(td.get_public_key() != tx_scan_info[o].in_ephemeral.pub, error::wallet_internal_error, "Inconsistent public keys"); THROW_WALLET_EXCEPTION_IF(td.m_spent, error::wallet_internal_error, "Inconsistent spent status"); @@ -4261,6 +4259,12 @@ void wallet2::refresh(bool trusted_daemon, uint64_t start_height, uint64_t & blo if (m_background_syncing || m_is_background_wallet) m_background_sync_data.first_refresh_done = true; + m_multisig_rescan_info = std::vector>{}; + for (auto &v: m_multisig_rescan_k) + memwipe(v.data(), v.size() * sizeof(v[0])); + + m_multisig_rescan_k = std::vector>{}; + LOG_PRINT_L1("Refresh done, blocks received: " << blocks_fetched << ", balance (all accounts): " << print_money(balance_all(false)) << ", unlocked: " << print_money(unlocked_balance_all(false))); } //---------------------------------------------------------------------------------------------------- @@ -15387,7 +15391,9 @@ size_t wallet2::import_multisig(std::vector blobs) { CHECK_AND_ASSERT_THROW_MES(m_multisig, "Wallet is not multisig"); - std::vector> info; + if (!m_multisig_rescan_k.empty() && !m_multisig_rescan_info.empty()) + refresh(false); + std::unordered_set seen; for (cryptonote::blobdata &data: blobs) { @@ -15453,20 +15459,18 @@ size_t wallet2::import_multisig(std::vector blobs) } MINFO(boost::format("%u outputs found") % boost::lexical_cast(i.size())); - info.push_back(std::move(i)); + m_multisig_rescan_info.push_back(std::move(i)); } - CHECK_AND_ASSERT_THROW_MES(info.size() + 1 <= m_multisig_signers.size() && info.size() + 1 >= m_multisig_threshold, "Wrong number of multisig sources"); + CHECK_AND_ASSERT_THROW_MES(m_multisig_rescan_info.size() + 1 <= m_multisig_signers.size() && m_multisig_rescan_info.size() + 1 >= m_multisig_threshold, "Wrong number of multisig sources"); - std::vector> k; - auto wiper = epee::misc_utils::create_scope_leave_handler([&](){for (auto &v: k) memwipe(v.data(), v.size() * sizeof(v[0]));}); - k.reserve(m_transfers.size()); + m_multisig_rescan_k.reserve(m_transfers.size()); for (const auto &td: m_transfers) - k.push_back(td.m_multisig_k); + m_multisig_rescan_k.push_back(td.m_multisig_k); // how many outputs we're going to update size_t n_outputs = m_transfers.size(); - for (const auto &pi: info) + for (const auto &pi: m_multisig_rescan_info) if (pi.size() < n_outputs) n_outputs = pi.size(); @@ -15474,7 +15478,7 @@ size_t wallet2::import_multisig(std::vector blobs) return 0; // check signers are consistent - for (const auto &pi: info) + for (const auto &pi: m_multisig_rescan_info) { CHECK_AND_ASSERT_THROW_MES(std::find(m_multisig_signers.begin(), m_multisig_signers.end(), pi[0].m_signer) != m_multisig_signers.end(), "Signer is not a member of this multisig wallet"); @@ -15483,13 +15487,13 @@ size_t wallet2::import_multisig(std::vector blobs) } // trim data we don't have info for from all participants - for (auto &pi: info) + for (auto &pi: m_multisig_rescan_info) pi.resize(n_outputs); // sort by signer - if (!info.empty() && !info.front().empty()) + if (!m_multisig_rescan_info.empty() && !m_multisig_rescan_info.front().empty()) { - std::sort(info.begin(), info.end(), [](const std::vector &i0, const std::vector &i1){ return memcmp(&i0[0].m_signer, &i1[0].m_signer, sizeof(i0[0].m_signer)) < 0; }); + std::sort(m_multisig_rescan_info.begin(), m_multisig_rescan_info.end(), [](const std::vector &i0, const std::vector &i1){ return memcmp(&i0[0].m_signer, &i1[0].m_signer, sizeof(i0[0].m_signer)) < 0; }); } // first pass to determine where to detach the blockchain @@ -15505,24 +15509,11 @@ size_t wallet2::import_multisig(std::vector blobs) for (size_t n = 0; n < n_outputs && n < m_transfers.size(); ++n) { - update_multisig_rescan_info(k, info, n); + update_multisig_rescan_info(m_multisig_rescan_k, m_multisig_rescan_info, n); } - m_multisig_rescan_k = &k; - m_multisig_rescan_info = &info; - try - { - refresh(false); - } - catch (...) - { - m_multisig_rescan_info = NULL; - m_multisig_rescan_k = NULL; - throw; - } - m_multisig_rescan_info = NULL; - m_multisig_rescan_k = NULL; + refresh(false); return n_outputs; } diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 2f4ad52f1f..f4b5fa6368 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1981,8 +1981,8 @@ private: std::vector m_address_book; std::pair, std::vector> m_account_tags; uint64_t m_upper_transaction_weight_limit; //TODO: auto-calc this value or request from daemon, now use some fixed value - const std::vector> *m_multisig_rescan_info; - const std::vector> *m_multisig_rescan_k; + std::vector> m_multisig_rescan_info; + std::vector> m_multisig_rescan_k; serializable_unordered_map m_cold_key_images; std::atomic m_run;