diff options
author | moneromooo-monero <moneromooo-monero@users.noreply.github.com> | 2016-01-31 16:23:54 +0000 |
---|---|---|
committer | moneromooo-monero <moneromooo-monero@users.noreply.github.com> | 2016-02-05 16:24:23 +0000 |
commit | c7b96b91ed33afc666afed3d960597c3e9628790 (patch) | |
tree | 962a8e366a8e6af00029c43634760fd1a4789792 | |
parent | Merge pull request #636 (diff) | |
download | monero-c7b96b91ed33afc666afed3d960597c3e9628790.tar.xz |
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.
-rw-r--r-- | src/wallet/wallet2.cpp | 61 |
1 files changed, 49 insertions, 12 deletions
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 40f372c35..ed6d3842d 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>()); - 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<cryptonote::txout_to_key>(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() ? "<none>" : boost::lexical_cast<std::string>(kit->second)) + + ", m_transfers.size() is " + boost::lexical_cast<std::string>(m_transfers.size())); + if (kit == m_key_images.end()) + { + m_transfers.push_back(boost::value_initialized<transfer_details>()); + 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); + } } } } |