From c7b96b91ed33afc666afed3d960597c3e9628790 Mon Sep 17 00:00:00 2001 From: moneromooo-monero Date: Sun, 31 Jan 2016 16:23:54 +0000 Subject: [PATCH] wallet: check a key image isn't already present when adding one If it is, it points to reuse of a tx key, which isn't meant to happen. If it does, a key image collision means that only one of those outputs is spendable, so the wallet selects the larger amount, unless that output was spent already. This causes a discrepancy betewen reported received inputs and payment total. Since tx keys are 256 bits, this should never happen except if done on purpose, or if a sender uses a bad PRNG. --- src/wallet/wallet2.cpp | 61 +++++++++++++++++++++++++++++++++--------- 1 file changed, 49 insertions(+), 12 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 40f372c3..ed6d3842 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -323,22 +323,59 @@ void wallet2::process_new_transaction(const cryptonote::transaction& tx, uint64_ THROW_WALLET_EXCEPTION_IF(tx.vout.size() <= o, error::wallet_internal_error, "wrong out in transaction: internal index=" + std::to_string(o) + ", total_outs=" + std::to_string(tx.vout.size())); - m_transfers.push_back(boost::value_initialized()); - transfer_details& td = m_transfers.back(); - td.m_block_height = height; - td.m_internal_output_index = o; - td.m_global_output_index = res.o_indexes[o]; - td.m_tx = tx; - td.m_spent = false; + crypto::key_image ki; cryptonote::keypair in_ephemeral; - cryptonote::generate_key_image_helper(m_account.get_keys(), tx_pub_key, o, in_ephemeral, td.m_key_image); + cryptonote::generate_key_image_helper(m_account.get_keys(), tx_pub_key, o, in_ephemeral, ki); THROW_WALLET_EXCEPTION_IF(in_ephemeral.pub != boost::get(tx.vout[o].target).key, error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key"); - m_key_images[td.m_key_image] = m_transfers.size()-1; - LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << get_transaction_hash(tx)); - if (0 != m_callback) - m_callback->on_money_received(height, td.m_tx, td.m_internal_output_index); + auto kit = m_key_images.find(ki); + THROW_WALLET_EXCEPTION_IF(kit != m_key_images.end() && kit->second >= m_transfers.size(), + error::wallet_internal_error, std::string("Unexpected transfer index from key image: ") + + "got " + (kit == m_key_images.end() ? "" : boost::lexical_cast(kit->second)) + + ", m_transfers.size() is " + boost::lexical_cast(m_transfers.size())); + if (kit == m_key_images.end()) + { + m_transfers.push_back(boost::value_initialized()); + transfer_details& td = m_transfers.back(); + td.m_block_height = height; + td.m_internal_output_index = o; + td.m_global_output_index = res.o_indexes[o]; + td.m_tx = tx; + td.m_key_image = ki; + td.m_spent = false; + m_key_images[td.m_key_image] = m_transfers.size()-1; + LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << get_transaction_hash(tx)); + if (0 != m_callback) + m_callback->on_money_received(height, td.m_tx, td.m_internal_output_index); + } + else if (m_transfers[kit->second].m_spent || m_transfers[kit->second].amount() >= tx.vout[o].amount) + { + LOG_ERROR("key image " << epee::string_tools::pod_to_hex(ki) + << " from received " << print_money(tx.vout[o].amount) << " output already exists with " + << (m_transfers[kit->second].m_spent ? "spent" : "unspent") << " " + << print_money(m_transfers[kit->second].amount()) << ", received output ignored"); + } + else + { + LOG_ERROR("key image " << epee::string_tools::pod_to_hex(ki) + << " from received " << print_money(tx.vout[o].amount) << " output already exists with " + << print_money(m_transfers[kit->second].amount()) << ", replacing with new output"); + // The new larger output replaced a previous smaller one + tx_money_got_in_outs -= tx.vout[o].amount; + + transfer_details &td = m_transfers[kit->second]; + td.m_block_height = height; + td.m_internal_output_index = o; + td.m_global_output_index = res.o_indexes[o]; + td.m_tx = tx; + THROW_WALLET_EXCEPTION_IF(td.m_key_image != ki, error::wallet_internal_error, "Inconsistent key images"); + THROW_WALLET_EXCEPTION_IF(td.m_spent, error::wallet_internal_error, "Inconsistent spent status"); + + LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << get_transaction_hash(tx)); + if (0 != m_callback) + m_callback->on_money_received(height, td.m_tx, td.m_internal_output_index); + } } } }