From dc24312bc3c8c9d865e4576631832c81d6dd212d Mon Sep 17 00:00:00 2001 From: jeffro256 Date: Mon, 12 Jun 2023 01:30:38 -0500 Subject: wallet: respect frozen key images in multisig wallets [RELEASE] Before this change, if a multisig peer asked you to sign a transaction with a frozen enote, the wallet will do it without any error or warning. This change makes it so that wallets will refuse to sign multisig transactions with frozen enotes. Disclaimer: This PR was generously funded by @LocalMonero. --- src/wallet/wallet2.cpp | 41 +++++++++++++++++++++++++++++++++++++++-- src/wallet/wallet2.h | 1 + 2 files changed, 40 insertions(+), 2 deletions(-) (limited to 'src/wallet') diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 63e87e52e..05efd8dc8 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -1748,6 +1748,36 @@ bool wallet2::frozen(size_t idx) const return td.m_frozen; } //---------------------------------------------------------------------------------------------------- +bool wallet2::frozen(const multisig_tx_set& txs) const +{ + // Each call to frozen(const key_image&) is O(N), so if we didn't use batching like we did here, + // this op would be O(M * N) instead of O(M + N). N = # wallet transfers, M = # key images in set. + // Step 1. Collect all key images from all pending txs into set + std::unordered_set kis_to_sign; + for (const auto& ptx : txs.m_ptx) + { + const tools::wallet2::tx_construction_data& cd = ptx.construction_data; + CHECK_AND_ASSERT_THROW_MES(cd.sources.size() == ptx.tx.vin.size(), "mismatched multisg tx set source sizes"); + for (size_t src_idx = 0; src_idx < cd.sources.size(); ++src_idx) + { + // Check that the key images are consistent between tx vin and construction data + const crypto::key_image multisig_ki = rct::rct2ki(cd.sources[src_idx].multisig_kLRki.ki); + CHECK_AND_ASSERT_THROW_MES(ptx.tx.vin[src_idx].type() == typeid(cryptonote::txin_to_key), "multisig tx cannot be miner"); + const crypto::key_image vin_ki = boost::get(ptx.tx.vin[src_idx]).k_image; + CHECK_AND_ASSERT_THROW_MES(multisig_ki == vin_ki, "Mismatched key image b/t vin and construction data"); + + // Add key image to set + kis_to_sign.insert(multisig_ki); + } + } + // Step 2. Scan all transfers for frozen key images + for (const auto& td : m_transfers) + if (td.m_frozen && kis_to_sign.count(td.m_key_image)) + return true; + + return false; +} +//---------------------------------------------------------------------------------------------------- void wallet2::freeze(const crypto::key_image &ki) { freeze(get_transfer_details(ki)); @@ -1768,8 +1798,13 @@ size_t wallet2::get_transfer_details(const crypto::key_image &ki) const for (size_t idx = 0; idx < m_transfers.size(); ++idx) { const transfer_details &td = m_transfers[idx]; - if (td.m_key_image_known && td.m_key_image == ki) - return idx; + if (td.m_key_image == ki) + { + if (td.m_key_image_known) + return idx; + else if (td.m_key_image_partial) + CHECK_AND_ASSERT_THROW_MES(false, "Transfer detail lookups are not allowed for multisig partial key images"); + } } CHECK_AND_ASSERT_THROW_MES(false, "Key image not found"); } @@ -7265,6 +7300,8 @@ bool wallet2::sign_multisig_tx(multisig_tx_set &exported_txs, std::vector