aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormoneromooo-monero <moneromooo-monero@users.noreply.github.com>2016-01-31 16:23:54 +0000
committermoneromooo-monero <moneromooo-monero@users.noreply.github.com>2016-02-05 16:24:23 +0000
commitc7b96b91ed33afc666afed3d960597c3e9628790 (patch)
tree962a8e366a8e6af00029c43634760fd1a4789792
parentMerge pull request #636 (diff)
downloadmonero-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.cpp61
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);
+ }
}
}
}