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.
This commit is contained in:
parent
5feebb4d87
commit
c7b96b91ed
|
@ -323,23 +323,60 @@ 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=" +
|
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()));
|
std::to_string(o) + ", total_outs=" + std::to_string(tx.vout.size()));
|
||||||
|
|
||||||
|
crypto::key_image ki;
|
||||||
|
cryptonote::keypair in_ephemeral;
|
||||||
|
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");
|
||||||
|
|
||||||
|
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>());
|
m_transfers.push_back(boost::value_initialized<transfer_details>());
|
||||||
transfer_details& td = m_transfers.back();
|
transfer_details& td = m_transfers.back();
|
||||||
td.m_block_height = height;
|
td.m_block_height = height;
|
||||||
td.m_internal_output_index = o;
|
td.m_internal_output_index = o;
|
||||||
td.m_global_output_index = res.o_indexes[o];
|
td.m_global_output_index = res.o_indexes[o];
|
||||||
td.m_tx = tx;
|
td.m_tx = tx;
|
||||||
|
td.m_key_image = ki;
|
||||||
td.m_spent = false;
|
td.m_spent = false;
|
||||||
cryptonote::keypair in_ephemeral;
|
|
||||||
cryptonote::generate_key_image_helper(m_account.get_keys(), tx_pub_key, o, in_ephemeral, td.m_key_image);
|
|
||||||
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;
|
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));
|
LOG_PRINT_L0("Received money: " << print_money(td.amount()) << ", with tx: " << get_transaction_hash(tx));
|
||||||
if (0 != m_callback)
|
if (0 != m_callback)
|
||||||
m_callback->on_money_received(height, td.m_tx, td.m_internal_output_index);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue