aboutsummaryrefslogtreecommitdiff
path: root/src/wallet/wallet2.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet/wallet2.cpp')
-rw-r--r--src/wallet/wallet2.cpp203
1 files changed, 103 insertions, 100 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");