diff options
Diffstat (limited to 'src/wallet')
| -rw-r--r-- | src/wallet/wallet2.cpp | 203 | ||||
| -rw-r--r-- | src/wallet/wallet2.h | 7 | ||||
| -rw-r--r-- | src/wallet/wallet_errors.h | 2 |
3 files changed, 109 insertions, 103 deletions
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 61f49481e..4c83dd890 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -3105,6 +3105,7 @@ void read_pool_txs(const cryptonote::COMMAND_RPC_GET_TRANSACTIONS::request &req, MDEBUG("Reading pool txs"); if (res.txs.size() == req.txs_hashes.size()) { + const std::unordered_set<crypto::hash> txid_set(txids.begin(), txids.end()); for (const auto &tx_entry: res.txs) { if (tx_entry.in_pool) @@ -3115,9 +3116,7 @@ void read_pool_txs(const cryptonote::COMMAND_RPC_GET_TRANSACTIONS::request &req, if (get_pruned_tx(tx_entry, tx, tx_hash)) { - const std::vector<crypto::hash>::const_iterator i = std::find_if(txids.begin(), txids.end(), - [tx_hash](const crypto::hash &e) { return e == tx_hash; }); - if (i != txids.end()) + if (txid_set.count(tx_hash) > 0) { txs.push_back(std::make_tuple(tx, tx_hash, tx_entry.double_spend_seen)); } @@ -3362,11 +3361,14 @@ void wallet2::process_parsed_blocks(uint64_t start_height, const std::vector<cry { THROW_WALLET_EXCEPTION_IF(txidx >= tx_cache_data.size(), error::wallet_internal_error, "txidx out of range"); const cryptonote::transaction& tx = parsed_blocks[i].block.miner_tx; - const size_t n_vouts = (m_refresh_type == RefreshType::RefreshOptimizeCoinbase && tx.version < 2) ? 1 : tx.vout.size(); - if (parsed_blocks[i].block.major_version >= hf_version_view_tags) - geniods.push_back(geniod_params{ tx, n_vouts, txidx }); - else - tpool.submit(&waiter, [&, n_vouts, txidx](){ geniod(tx, n_vouts, txidx); }, true); + const size_t n_vouts = (m_refresh_type == RefreshType::RefreshOptimizeCoinbase && tx.version < 2 && !tx.vout.empty()) ? 1 : tx.vout.size(); + if (n_vouts > 0) + { + if (parsed_blocks[i].block.major_version >= hf_version_view_tags) + geniods.push_back(geniod_params{ tx, n_vouts, txidx }); + else + tpool.submit(&waiter, [&, n_vouts, txidx](){ geniod(tx, n_vouts, txidx); }, true); + } } ++txidx; for (size_t j = 0; j < parsed_blocks[i].txes.size(); ++j) @@ -3566,21 +3568,18 @@ void wallet2::pull_and_parse_next_blocks(bool first, bool try_incremental, uint6 void wallet2::remove_obsolete_pool_txs(const std::vector<crypto::hash> &tx_hashes, bool remove_if_found) { + remove_obsolete_pool_txs(std::unordered_set<crypto::hash>(tx_hashes.begin(), tx_hashes.end()), remove_if_found); +} + +void wallet2::remove_obsolete_pool_txs(const std::unordered_set<crypto::hash> &tx_hashes, bool remove_if_found) +{ // remove pool txes to us that aren't in the pool anymore (remove_if_found = false), // or remove pool txes to us that were reported as removed (remove_if_found = true) std::unordered_multimap<crypto::hash, wallet2::pool_payment_details>::iterator uit = m_unconfirmed_payments.begin(); while (uit != m_unconfirmed_payments.end()) { const crypto::hash &txid = uit->second.m_pd.m_tx_hash; - bool found = false; - for (const auto &it2: tx_hashes) - { - if (it2 == txid) - { - found = true; - break; - } - } + const bool found = tx_hashes.count(txid) > 0; auto pit = uit++; if ((!remove_if_found && !found) || (remove_if_found && found)) { @@ -3596,17 +3595,9 @@ void wallet2::remove_obsolete_pool_txs(const std::vector<crypto::hash> &tx_hashe // Code that is common to 'update_pool_state_by_pool_query' and 'update_pool_state_from_pool_data': // Check wether a tx in the pool is worthy of processing because we did not see it // yet or because it is "interesting" out of special circumstances -bool wallet2::accept_pool_tx_for_processing(const crypto::hash &txid) +bool wallet2::accept_pool_tx_for_processing(const crypto::hash &txid, const std::unordered_set<crypto::hash> &payments_tx_hashes) { - bool txid_found_in_up = false; - for (const auto &up: m_unconfirmed_payments) - { - if (up.second.m_pd.m_tx_hash == txid) - { - txid_found_in_up = true; - break; - } - } + const bool txid_found_in_up = payments_tx_hashes.count(txid) > 0; if (m_scanned_pool_txs[0].find(txid) != m_scanned_pool_txs[0].end() || m_scanned_pool_txs[1].find(txid) != m_scanned_pool_txs[1].end()) { // if it's for us, we want to keep track of whether we saw a double spend, so don't bail out @@ -3619,30 +3610,25 @@ bool wallet2::accept_pool_tx_for_processing(const crypto::hash &txid) if (!txid_found_in_up) { LOG_PRINT_L1("Found new pool tx: " << txid); - bool found = false; - for (const auto &i: m_unconfirmed_txs) + const auto i = m_unconfirmed_txs.find(txid); + bool sent_by_us = i != m_unconfirmed_txs.end(); + if (sent_by_us) { - if (i.first == txid) + const unconfirmed_transfer_details& utd = i->second; + for (const auto& dst : utd.m_dests) { - found = true; - // if this is a payment to yourself at a different subaddress account, don't skip it - // so that you can see the incoming pool tx with 'show_transfers' on that receiving subaddress account - const unconfirmed_transfer_details& utd = i.second; - for (const auto& dst : utd.m_dests) + auto subaddr_index = m_subaddresses.find(dst.addr.m_spend_public_key); + if (subaddr_index != m_subaddresses.end() && subaddr_index->second.major != utd.m_subaddr_account) { - auto subaddr_index = m_subaddresses.find(dst.addr.m_spend_public_key); - if (subaddr_index != m_subaddresses.end() && subaddr_index->second.major != utd.m_subaddr_account) - { - found = false; - break; - } + // Payment to ourselves at a different subaddress account: + // process it so the receiving account can show the incoming pool tx. + sent_by_us = false; + break; } - break; } } - if (!found) + if (!sent_by_us) { - // not one of those we sent ourselves return true; } else @@ -3813,19 +3799,14 @@ void wallet2::update_pool_state_by_pool_query(std::vector<std::tuple<cryptonote: // remove any pending tx that's not in the pool const auto now = std::chrono::system_clock::now(); std::unordered_map<crypto::hash, wallet2::unconfirmed_transfer_details>::iterator it = m_unconfirmed_txs.begin(); + + const std::unordered_set<crypto::hash> pool_set(res.tx_hashes.begin(), res.tx_hashes.end()); + while (it != m_unconfirmed_txs.end()) { const crypto::hash &txid = it->first; MDEBUG("Checking m_unconfirmed_txs entry " << txid); - bool found = false; - for (const auto &it2: res.tx_hashes) - { - if (it2 == txid) - { - found = true; - break; - } - } + const bool found = pool_set.count(txid) > 0; auto pit = it++; process_unconfirmed_transfer(false, txid, pit->second, found, now, refreshed); MDEBUG("New state of that entry: " << pit->second.m_state); @@ -3837,15 +3818,21 @@ void wallet2::update_pool_state_by_pool_query(std::vector<std::tuple<cryptonote: // the in transfers list instead (or nowhere if it just // disappeared without being mined) if (refreshed) - remove_obsolete_pool_txs(res.tx_hashes, false); + remove_obsolete_pool_txs(pool_set, false); MTRACE("update_pool_state_by_pool_query done second loop"); + std::unordered_set<crypto::hash> payments_tx_hashes; + payments_tx_hashes.reserve(m_unconfirmed_payments.size()); + for (const auto &p: m_unconfirmed_payments) + payments_tx_hashes.insert(p.second.m_pd.m_tx_hash); + // gather txids of new pool txes to us std::vector<crypto::hash> txids; + txids.reserve(res.tx_hashes.size()); for (const auto &txid: res.tx_hashes) { - if (accept_pool_tx_for_processing(txid)) + if (accept_pool_tx_for_processing(txid, payments_tx_hashes)) txids.push_back(txid); } @@ -3872,6 +3859,11 @@ void wallet2::update_pool_state_from_pool_data(bool incremental, const std::vect m_encrypt_keys_after_refresh.reset(); }); + std::unordered_set<crypto::hash> added_pool_txids; + added_pool_txids.reserve(added_pool_txs.size()); + for (const auto &pool_tx: added_pool_txs) + added_pool_txids.insert(std::get<1>(pool_tx)); + if (refreshed) { if (incremental) @@ -3884,16 +3876,8 @@ void wallet2::update_pool_state_from_pool_data(bool incremental, const std::vect } else { - // Delete from the list of unconfirmed payments what we don't find anymore in the pool; a bit - // unfortunate that we have to build a new vector with ids first, but better than copying and - // modifying the code of 'remove_obsolete_pool_txs' here - std::vector<crypto::hash> txids; - txids.reserve(added_pool_txs.size()); - for (const auto &pool_tx: added_pool_txs) - { - txids.push_back(std::get<1>(pool_tx)); - } - remove_obsolete_pool_txs(txids, false); + // Delete from the list of unconfirmed payments what we don't find anymore in the pool + remove_obsolete_pool_txs(added_pool_txids, false); } } @@ -3904,15 +3888,7 @@ void wallet2::update_pool_state_from_pool_data(bool incremental, const std::vect { const crypto::hash &txid = it->first; MDEBUG("Checking m_unconfirmed_txs entry " << txid); - bool found = false; - for (const auto &pool_tx: added_pool_txs) - { - if (std::get<1>(pool_tx) == txid) - { - found = true; - break; - } - } + const bool found = added_pool_txids.count(txid) > 0; auto pit = it++; process_unconfirmed_transfer(incremental, txid, pit->second, found, now, refreshed); MDEBUG("Resulting state of that entry: " << pit->second.m_state); @@ -3922,9 +3898,13 @@ void wallet2::update_pool_state_from_pool_data(bool incremental, const std::vect // if we work incrementally and thus see only new pool txs since last time we asked it should // be rare that we know already about one of those, but check nevertheless process_txs.clear(); + std::unordered_set<crypto::hash> payments_tx_hashes; + payments_tx_hashes.reserve(m_unconfirmed_payments.size()); + for (const auto &p: m_unconfirmed_payments) + payments_tx_hashes.insert(p.second.m_pd.m_tx_hash); for (const auto &pool_tx: added_pool_txs) { - if (accept_pool_tx_for_processing(std::get<1>(pool_tx))) + if (accept_pool_tx_for_processing(std::get<1>(pool_tx), payments_tx_hashes)) { process_txs.push_back(pool_tx); } @@ -8155,14 +8135,6 @@ std::string wallet2::save_multisig_tx(multisig_tx_set txs) { LOG_PRINT_L0("saving " << txs.m_ptx.size() << " multisig transactions"); - // txes generated, get rid of used k values - for (size_t n = 0; n < txs.m_ptx.size(); ++n) - for (size_t idx: txs.m_ptx[n].construction_data.selected_transfers) - { - memwipe(m_transfers[idx].m_multisig_k.data(), m_transfers[idx].m_multisig_k.size() * sizeof(m_transfers[idx].m_multisig_k[0])); - m_transfers[idx].m_multisig_k.clear(); - } - // zero out some data we don't want to share for (auto &ptx: txs.m_ptx) { @@ -8190,6 +8162,11 @@ std::string wallet2::save_multisig_tx(multisig_tx_set txs) } LOG_PRINT_L2("Saving multisig unsigned tx data: " << oss.str()); std::string ciphertext = encrypt_with_view_secret_key(oss.str()); + + // The transaction creator has already signed, so do not expose the txset + // until the corresponding one-time nonce erasure is stored. + clear_multisig_k_and_store(txs); + return std::string(MULTISIG_UNSIGNED_TX_PREFIX) + ciphertext; } //---------------------------------------------------------------------------------------------------- @@ -8348,8 +8325,12 @@ bool wallet2::load_multisig_tx_from_file(const std::string &filename, multisig_t return true; } //---------------------------------------------------------------------------------------------------- -bool wallet2::sign_multisig_tx(multisig_tx_set &exported_txs, std::vector<crypto::hash> &txids) +bool wallet2::sign_multisig_tx(multisig_tx_set &exported_txs_inout, std::vector<crypto::hash> &txids) { + multisig_tx_set exported_txs = exported_txs_inout; + std::vector<crypto::hash> signed_txids; + std::vector<std::pair<crypto::hash, size_t>> signed_tx_key_indices; + THROW_WALLET_EXCEPTION_IF(exported_txs.m_ptx.empty(), error::wallet_internal_error, "No tx found"); const crypto::public_key local_signer = get_multisig_signer_public_key(); @@ -8363,8 +8344,6 @@ bool wallet2::sign_multisig_tx(multisig_tx_set &exported_txs, std::vector<crypto THROW_WALLET_EXCEPTION_IF(frozen(exported_txs), error::wallet_internal_error, "Will not sign multisig tx containing frozen outputs") - txids.clear(); - // The 'exported_txs' contains a set of different transactions for the multisig group to try to sign. Each of those // transactions has a set of 'signing attempts' corresponding to all the possible signing groups within the multisig. // - Here, we will partially sign as many of those signing attempts as possible, for each proposed transaction. @@ -8474,25 +8453,26 @@ bool wallet2::sign_multisig_tx(multisig_tx_set &exported_txs, std::vector<crypto "Unable to finalize the transaction: the ignore sets for these tx attempts seem to be malformed."); const crypto::hash txid = get_transaction_hash(ptx.tx); if (store_tx_info()) - { - m_tx_keys[txid] = ptx.tx_key; - m_additional_tx_keys[txid] = ptx.additional_tx_keys; - } - txids.push_back(txid); + signed_tx_key_indices.emplace_back(txid, n); + signed_txids.push_back(txid); } } - // signatures generated, get rid of any unused k values (must do export_multisig() to make more tx attempts with the - // inputs in the transactions worked on here) - for (size_t n = 0; n < exported_txs.m_ptx.size(); ++n) - for (size_t idx: exported_txs.m_ptx[n].construction_data.selected_transfers) - { - memwipe(m_transfers[idx].m_multisig_k.data(), m_transfers[idx].m_multisig_k.size() * sizeof(m_transfers[idx].m_multisig_k[0])); - m_transfers[idx].m_multisig_k.clear(); - } + exported_txs.m_signers.insert(local_signer); - exported_txs.m_signers.insert(get_multisig_signer_public_key()); + // Do not expose signatures until all nonce material for the selected inputs + // has been erased from the wallet cache. + clear_multisig_k_and_store(exported_txs); + for (const auto &entry: signed_tx_key_indices) + { + const auto &ptx = exported_txs.m_ptx[entry.second]; + m_tx_keys[entry.first] = ptx.tx_key; + m_additional_tx_keys[entry.first] = ptx.additional_tx_keys; + } + + exported_txs_inout = std::move(exported_txs); + txids = std::move(signed_txids); return true; } //---------------------------------------------------------------------------------------------------- @@ -9285,6 +9265,8 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> // check we're clear enough of rct start, to avoid corner cases below THROW_WALLET_EXCEPTION_IF(rct_offsets.size() <= CRYPTONOTE_DEFAULT_TX_SPENDABLE_AGE, error::get_output_distribution, "Not enough rct outputs"); + THROW_WALLET_EXCEPTION_IF(!std::is_sorted(rct_offsets.begin(), rct_offsets.end()), + error::get_output_distribution, "Daemon reports non-monotonic rct output distribution"); THROW_WALLET_EXCEPTION_IF(rct_offsets.back() <= max_rct_index, error::get_output_distribution, "Daemon reports suspicious number of rct outputs"); } @@ -15223,6 +15205,27 @@ void wallet2::get_multisig_k(size_t idx, const std::unordered_set<rct::key> &use THROW_WALLET_EXCEPTION(tools::error::multisig_export_needed); } //---------------------------------------------------------------------------------------------------- +void wallet2::clear_multisig_k_and_store(const multisig_tx_set &txs) +{ + // Must succeed before any txset produced with these nonces is exposed. + bool changed = false; + for (const auto &ptx: txs.m_ptx) + { + for (size_t idx: ptx.construction_data.selected_transfers) + { + std::vector<rct::key> &multisig_k = m_transfers[idx].m_multisig_k; + if (multisig_k.empty()) + continue; + memwipe(multisig_k.data(), multisig_k.size() * sizeof(multisig_k[0])); + multisig_k.clear(); + changed = true; + } + } + + if (changed) + store(); +} +//---------------------------------------------------------------------------------------------------- rct::multisig_kLRki wallet2::get_multisig_kLRki(size_t n, const rct::key &k) const { CHECK_AND_ASSERT_THROW_MES(n < m_transfers.size(), "Bad m_transfers index"); diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index cedfecbeb..b18e9d8c6 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -31,6 +31,7 @@ #pragma once #include <memory> +#include <unordered_set> #include <boost/program_options/options_description.hpp> #include <boost/program_options/variables_map.hpp> @@ -1211,7 +1212,7 @@ private: bool load_multisig_tx(cryptonote::blobdata blob, multisig_tx_set &exported_txs, std::function<bool(const multisig_tx_set&)> accept_func = NULL); bool load_multisig_tx_from_file(const std::string &filename, multisig_tx_set &exported_txs, std::function<bool(const multisig_tx_set&)> accept_func = NULL); bool sign_multisig_tx_from_file(const std::string &filename, std::vector<crypto::hash> &txids, std::function<bool(const multisig_tx_set&)> accept_func); - bool sign_multisig_tx(multisig_tx_set &exported_txs, std::vector<crypto::hash> &txids); + bool sign_multisig_tx(multisig_tx_set &exported_txs_inout, std::vector<crypto::hash> &txids); bool sign_multisig_tx_to_file(multisig_tx_set &exported_txs, const std::string &filename, std::vector<crypto::hash> &txids); std::vector<pending_tx> create_unmixable_sweep_transactions(); void discard_unmixable_outputs(); @@ -1649,6 +1650,7 @@ private: void update_pool_state(std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> &process_txs, bool refreshed = false, bool try_incremental = false); void process_pool_state(const std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> &txs); void remove_obsolete_pool_txs(const std::vector<crypto::hash> &tx_hashes, bool remove_if_found); + void remove_obsolete_pool_txs(const std::unordered_set<crypto::hash> &tx_hashes, bool remove_if_found); std::string encrypt(const char *plaintext, size_t len, const crypto::secret_key &skey, bool authenticated = true) const; std::string encrypt(const epee::span<char> &span, const crypto::secret_key &skey, bool authenticated = true) const; @@ -1869,7 +1871,7 @@ private: void fast_refresh(uint64_t stop_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history, bool force = false); void pull_and_parse_next_blocks(bool first, bool try_incremental, uint64_t start_height, uint64_t &blocks_start_height, std::list<crypto::hash> &short_chain_history, const std::vector<cryptonote::block_complete_entry> &prev_blocks, const std::vector<parsed_block> &prev_parsed_blocks, std::vector<cryptonote::block_complete_entry> &blocks, std::vector<parsed_block> &parsed_blocks, bool &last, bool &error, std::exception_ptr &exception); void process_parsed_blocks(uint64_t start_height, const std::vector<cryptonote::block_complete_entry> &blocks, const std::vector<parsed_block> &parsed_blocks, uint64_t& blocks_added, std::map<std::pair<uint64_t, uint64_t>, size_t> *output_tracker_cache = NULL); - bool accept_pool_tx_for_processing(const crypto::hash &txid); + bool accept_pool_tx_for_processing(const crypto::hash &txid, const std::unordered_set<crypto::hash> &payments_tx_hashes); void process_unconfirmed_transfer(bool incremental, const crypto::hash &txid, wallet2::unconfirmed_transfer_details &tx_details, bool seen_in_pool, std::chrono::system_clock::time_point now, bool refreshed); void process_pool_info_extent(const cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::response &res, std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> &process_txs, bool refreshed); void update_pool_state_by_pool_query(std::vector<std::tuple<cryptonote::transaction, crypto::hash, bool>> &process_txs, bool refreshed = false); @@ -1908,6 +1910,7 @@ private: rct::multisig_kLRki get_multisig_composite_kLRki(size_t n, const std::unordered_set<crypto::public_key> &ignore_set, std::unordered_set<rct::key> &used_L, std::unordered_set<rct::key> &new_used_L) const; rct::multisig_kLRki get_multisig_kLRki(size_t n, const rct::key &k) const; void get_multisig_k(size_t idx, const std::unordered_set<rct::key> &used_L, rct::key &nonce); + void clear_multisig_k_and_store(const multisig_tx_set &txs); void update_multisig_rescan_info(const std::vector<std::vector<rct::key>> &multisig_k, const std::vector<std::vector<tools::wallet2::multisig_info>> &info, size_t n); bool add_rings(const crypto::chacha_key &key, const cryptonote::transaction_prefix &tx); bool add_rings(const cryptonote::transaction_prefix &tx); diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h index c54cd3499..e0a767159 100644 --- a/src/wallet/wallet_errors.h +++ b/src/wallet/wallet_errors.h @@ -440,7 +440,7 @@ namespace tools struct out_of_hashchain_bounds_error : public refresh_error { explicit out_of_hashchain_bounds_error(std::string&& loc) - : refresh_error(std::move(loc), "Index out of bounds of of hashchain") + : refresh_error(std::move(loc), "Index out of bounds of hashchain") { } |
