wallet2: ensure outputs are processed only once

This should be proof against any way one might get to multiple
processing, such as generating the same derivation from the
same pubkey, etc
This commit is contained in:
moneromooo-monero 2018-07-08 11:01:13 +01:00
parent eed4dba880
commit fc39d3b23c
No known key found for this signature in database
GPG key ID: 686F07454D6CEFC3
2 changed files with 18 additions and 14 deletions

View file

@ -1057,6 +1057,16 @@ void wallet2::check_acc_out_precomp(const tx_out &o, const crypto::key_derivatio
tx_scan_info.error = false; tx_scan_info.error = false;
} }
//---------------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------------
void wallet2::check_acc_out_precomp_once(const tx_out &o, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, size_t i, const is_out_data *is_out_data, tx_scan_info_t &tx_scan_info, bool &already_seen) const
{
tx_scan_info.received = boost::none;
if (already_seen)
return;
check_acc_out_precomp(o, derivation, additional_derivations, i, is_out_data, tx_scan_info);
if (tx_scan_info.received)
already_seen = true;
}
//----------------------------------------------------------------------------------------------------
static uint64_t decodeRct(const rct::rctSig & rv, const crypto::key_derivation &derivation, unsigned int i, rct::key & mask, hw::device &hwdev) static uint64_t decodeRct(const rct::rctSig & rv, const crypto::key_derivation &derivation, unsigned int i, rct::key & mask, hw::device &hwdev)
{ {
crypto::secret_key scalar1; crypto::secret_key scalar1;
@ -1173,7 +1183,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
// Don't try to extract tx public key if tx has no ouputs // Don't try to extract tx public key if tx has no ouputs
size_t pk_index = 0; size_t pk_index = 0;
std::vector<tx_scan_info_t> tx_scan_info(tx.vout.size()); std::vector<tx_scan_info_t> tx_scan_info(tx.vout.size());
std::unordered_set<crypto::public_key> public_keys_seen; std::deque<bool> output_found(tx.vout.size(), false);
while (!tx.vout.empty()) while (!tx.vout.empty())
{ {
// if tx.vout is not empty, we loop through all tx pubkeys // if tx.vout is not empty, we loop through all tx pubkeys
@ -1194,13 +1204,6 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
error::wallet_internal_error, "tx_cache_data is out of sync"); error::wallet_internal_error, "tx_cache_data is out of sync");
} }
if (public_keys_seen.find(pub_key_field.pub_key) != public_keys_seen.end())
{
MWARNING("The same transaction pubkey is present more than once, ignoring extra instance");
continue;
}
public_keys_seen.insert(pub_key_field.pub_key);
int num_vouts_received = 0; int num_vouts_received = 0;
tx_pub_key = pub_key_field.pub_key; tx_pub_key = pub_key_field.pub_key;
tools::threadpool& tpool = tools::threadpool::getInstance(); tools::threadpool& tpool = tools::threadpool::getInstance();
@ -1264,7 +1267,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
} }
else if (miner_tx && m_refresh_type == RefreshOptimizeCoinbase) else if (miner_tx && m_refresh_type == RefreshOptimizeCoinbase)
{ {
check_acc_out_precomp(tx.vout[0], derivation, additional_derivations, 0, is_out_data_ptr, tx_scan_info[0]); check_acc_out_precomp_once(tx.vout[0], derivation, additional_derivations, 0, is_out_data_ptr, tx_scan_info[0], output_found[0]);
THROW_WALLET_EXCEPTION_IF(tx_scan_info[0].error, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys()); THROW_WALLET_EXCEPTION_IF(tx_scan_info[0].error, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys());
// this assumes that the miner tx pays a single address // this assumes that the miner tx pays a single address
@ -1274,8 +1277,8 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
// the first one was already checked // the first one was already checked
for (size_t i = 1; i < tx.vout.size(); ++i) for (size_t i = 1; i < tx.vout.size(); ++i)
{ {
tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp, this, std::cref(tx.vout[i]), std::cref(derivation), std::cref(additional_derivations), i, tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp_once, this, std::cref(tx.vout[i]), std::cref(derivation), std::cref(additional_derivations), i,
std::cref(is_out_data_ptr), std::ref(tx_scan_info[i])), true); std::cref(is_out_data_ptr), std::ref(tx_scan_info[i]), std::ref(output_found[i])), true);
} }
waiter.wait(&tpool); waiter.wait(&tpool);
// then scan all outputs from 0 // then scan all outputs from 0
@ -1297,8 +1300,8 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
{ {
for (size_t i = 0; i < tx.vout.size(); ++i) for (size_t i = 0; i < tx.vout.size(); ++i)
{ {
tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp, this, std::cref(tx.vout[i]), std::cref(derivation), std::cref(additional_derivations), i, tpool.submit(&waiter, boost::bind(&wallet2::check_acc_out_precomp_once, this, std::cref(tx.vout[i]), std::cref(derivation), std::cref(additional_derivations), i,
std::cref(is_out_data_ptr), std::ref(tx_scan_info[i])), true); std::cref(is_out_data_ptr), std::ref(tx_scan_info[i]), std::ref(output_found[i])), true);
} }
waiter.wait(&tpool); waiter.wait(&tpool);
@ -1319,7 +1322,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote
{ {
for (size_t i = 0; i < tx.vout.size(); ++i) for (size_t i = 0; i < tx.vout.size(); ++i)
{ {
check_acc_out_precomp(tx.vout[i], derivation, additional_derivations, i, is_out_data_ptr, tx_scan_info[i]); check_acc_out_precomp_once(tx.vout[i], derivation, additional_derivations, i, is_out_data_ptr, tx_scan_info[i], output_found[i]);
THROW_WALLET_EXCEPTION_IF(tx_scan_info[i].error, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys()); THROW_WALLET_EXCEPTION_IF(tx_scan_info[i].error, error::acc_outs_lookup_error, tx, tx_pub_key, m_account.get_keys());
if (tx_scan_info[i].received) if (tx_scan_info[i].received)
{ {

View file

@ -1181,6 +1181,7 @@ namespace tools
crypto::hash get_payment_id(const pending_tx &ptx) const; crypto::hash get_payment_id(const pending_tx &ptx) const;
void check_acc_out_precomp(const cryptonote::tx_out &o, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, size_t i, tx_scan_info_t &tx_scan_info) const; void check_acc_out_precomp(const cryptonote::tx_out &o, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, size_t i, tx_scan_info_t &tx_scan_info) const;
void check_acc_out_precomp(const cryptonote::tx_out &o, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, size_t i, const is_out_data *is_out_data, tx_scan_info_t &tx_scan_info) const; void check_acc_out_precomp(const cryptonote::tx_out &o, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, size_t i, const is_out_data *is_out_data, tx_scan_info_t &tx_scan_info) const;
void check_acc_out_precomp_once(const cryptonote::tx_out &o, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, size_t i, const is_out_data *is_out_data, tx_scan_info_t &tx_scan_info, bool &already_seen) const;
void parse_block_round(const cryptonote::blobdata &blob, cryptonote::block &bl, crypto::hash &bl_id, bool &error) const; void parse_block_round(const cryptonote::blobdata &blob, cryptonote::block &bl, crypto::hash &bl_id, bool &error) const;
uint64_t get_upper_transaction_size_limit() const; uint64_t get_upper_transaction_size_limit() const;
std::vector<uint64_t> get_unspent_amounts_vector() const; std::vector<uint64_t> get_unspent_amounts_vector() const;