diff options
Diffstat (limited to 'src/wallet')
-rw-r--r-- | src/wallet/ringdb.cpp | 39 | ||||
-rw-r--r-- | src/wallet/ringdb.h | 2 | ||||
-rw-r--r-- | src/wallet/wallet2.cpp | 124 | ||||
-rw-r--r-- | src/wallet/wallet2.h | 12 |
4 files changed, 132 insertions, 45 deletions
diff --git a/src/wallet/ringdb.cpp b/src/wallet/ringdb.cpp index bfbbbaeb7..7e4f12f5b 100644 --- a/src/wallet/ringdb.cpp +++ b/src/wallet/ringdb.cpp @@ -344,12 +344,15 @@ bool ringdb::remove_rings(const crypto::chacha_key &chacha_key, const cryptonote return remove_rings(chacha_key, key_images); } -bool ringdb::get_ring(const crypto::chacha_key &chacha_key, const crypto::key_image &key_image, std::vector<uint64_t> &outs) +bool ringdb::get_rings(const crypto::chacha_key &chacha_key, const std::vector<crypto::key_image> &key_images, std::vector<std::vector<uint64_t>> &all_outs) { MDB_txn *txn; int dbr; bool tx_active = false; + all_outs.clear(); + all_outs.reserve(key_images.size()); + dbr = resize_env(env, filename.c_str(), 0); THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to set env map size: " + std::string(mdb_strerror(dbr))); dbr = mdb_txn_begin(env, NULL, 0, &txn); @@ -357,6 +360,10 @@ bool ringdb::get_ring(const crypto::chacha_key &chacha_key, const crypto::key_im epee::misc_utils::auto_scope_leave_caller txn_dtor = epee::misc_utils::create_scope_leave_handler([&](){if (tx_active) mdb_txn_abort(txn);}); tx_active = true; + for (size_t i = 0; i < key_images.size(); ++i) + { + const crypto::key_image &key_image = key_images[i]; + MDB_val key, data; std::string key_ciphertext = encrypt(key_image, chacha_key, 0); key.mv_data = (void*)key_ciphertext.data(); @@ -367,6 +374,7 @@ bool ringdb::get_ring(const crypto::chacha_key &chacha_key, const crypto::key_im return false; THROW_WALLET_EXCEPTION_IF(data.mv_size <= 0, tools::error::wallet_internal_error, "Invalid ring data size"); + std::vector<uint64_t> outs; bool try_v0 = false; std::string data_plaintext = decrypt(std::string((const char*)data.mv_data, data.mv_size), key_image, chacha_key, 1); try { outs = decompress_ring(data_plaintext, V1TAG); if (outs.empty()) try_v0 = true; } @@ -380,6 +388,9 @@ bool ringdb::get_ring(const crypto::chacha_key &chacha_key, const crypto::key_im MDEBUG("Relative: " << boost::join(outs | boost::adaptors::transformed([](uint64_t out){return std::to_string(out);}), " ")); outs = cryptonote::relative_output_offsets_to_absolute(outs); MDEBUG("Absolute: " << boost::join(outs | boost::adaptors::transformed([](uint64_t out){return std::to_string(out);}), " ")); + all_outs.push_back(std::move(outs)); + + } dbr = mdb_txn_commit(txn); THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to commit txn getting ring from database: " + std::string(mdb_strerror(dbr))); @@ -387,20 +398,33 @@ bool ringdb::get_ring(const crypto::chacha_key &chacha_key, const crypto::key_im return true; } -bool ringdb::set_ring(const crypto::chacha_key &chacha_key, const crypto::key_image &key_image, const std::vector<uint64_t> &outs, bool relative) +bool ringdb::get_ring(const crypto::chacha_key &chacha_key, const crypto::key_image &key_image, std::vector<uint64_t> &outs) +{ + std::vector<std::vector<uint64_t>> all_outs; + if (!get_rings(chacha_key, std::vector<crypto::key_image>(1, key_image), all_outs)) + return false; + outs = std::move(all_outs.front()); + return true; +} + +bool ringdb::set_rings(const crypto::chacha_key &chacha_key, const std::vector<std::pair<crypto::key_image, std::vector<uint64_t>>> &rings, bool relative) { MDB_txn *txn; int dbr; bool tx_active = false; - dbr = resize_env(env, filename.c_str(), outs.size() * 64); + size_t n_outs = 0; + for (const auto &e: rings) + n_outs += e.second.size(); + dbr = resize_env(env, filename.c_str(), n_outs * 64); THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to set env map size: " + std::string(mdb_strerror(dbr))); dbr = mdb_txn_begin(env, NULL, 0, &txn); THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to create LMDB transaction: " + std::string(mdb_strerror(dbr))); epee::misc_utils::auto_scope_leave_caller txn_dtor = epee::misc_utils::create_scope_leave_handler([&](){if (tx_active) mdb_txn_abort(txn);}); tx_active = true; - store_relative_ring(txn, dbi_rings, key_image, relative ? outs : cryptonote::absolute_output_offsets_to_relative(outs), chacha_key); + for (const auto &e: rings) + store_relative_ring(txn, dbi_rings, e.first, relative ? e.second : cryptonote::absolute_output_offsets_to_relative(e.second), chacha_key); dbr = mdb_txn_commit(txn); THROW_WALLET_EXCEPTION_IF(dbr, tools::error::wallet_internal_error, "Failed to commit txn setting ring to database: " + std::string(mdb_strerror(dbr))); @@ -408,6 +432,13 @@ bool ringdb::set_ring(const crypto::chacha_key &chacha_key, const crypto::key_im return true; } +bool ringdb::set_ring(const crypto::chacha_key &chacha_key, const crypto::key_image &key_image, const std::vector<uint64_t> &outs, bool relative) +{ + std::vector<std::pair<crypto::key_image, std::vector<uint64_t>>> rings; + rings.push_back(std::make_pair(key_image, outs)); + return set_rings(chacha_key, rings, relative); +} + bool ringdb::blackball_worker(const std::vector<std::pair<uint64_t, uint64_t>> &outputs, int op) { MDB_txn *txn; diff --git a/src/wallet/ringdb.h b/src/wallet/ringdb.h index e9941bf94..bdecdba37 100644 --- a/src/wallet/ringdb.h +++ b/src/wallet/ringdb.h @@ -49,7 +49,9 @@ namespace tools bool remove_rings(const crypto::chacha_key &chacha_key, const std::vector<crypto::key_image> &key_images); bool remove_rings(const crypto::chacha_key &chacha_key, const cryptonote::transaction_prefix &tx); bool get_ring(const crypto::chacha_key &chacha_key, const crypto::key_image &key_image, std::vector<uint64_t> &outs); + bool get_rings(const crypto::chacha_key &chacha_key, const std::vector<crypto::key_image> &key_images, std::vector<std::vector<uint64_t>> &all_outs); bool set_ring(const crypto::chacha_key &chacha_key, const crypto::key_image &key_image, const std::vector<uint64_t> &outs, bool relative); + bool set_rings(const crypto::chacha_key &chacha_key, const std::vector<std::pair<crypto::key_image, std::vector<uint64_t>>> &rings, bool relative); bool blackball(const std::pair<uint64_t, uint64_t> &output); bool blackball(const std::vector<std::pair<uint64_t, uint64_t>> &outputs); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index f4a5a5855..1eeb893c0 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -7606,6 +7606,14 @@ bool wallet2::get_ring(const crypto::chacha_key &key, const crypto::key_image &k catch (const std::exception &e) { return false; } } +bool wallet2::get_rings(const crypto::chacha_key &key, const std::vector<crypto::key_image> &key_images, std::vector<std::vector<uint64_t>> &outs) +{ + if (!m_ringdb) + return false; + try { return m_ringdb->get_rings(key, key_images, outs); } + catch (const std::exception &e) { return false; } +} + bool wallet2::get_rings(const crypto::hash &txid, std::vector<std::pair<crypto::key_image, std::vector<uint64_t>>> &outs) { for (auto i: m_confirmed_txs) @@ -7644,6 +7652,15 @@ bool wallet2::set_ring(const crypto::key_image &key_image, const std::vector<uin catch (const std::exception &e) { return false; } } +bool wallet2::set_rings(const std::vector<std::pair<crypto::key_image, std::vector<uint64_t>>> &rings, bool relative) +{ + if (!m_ringdb) + return false; + + try { return m_ringdb->set_rings(get_ringdb_key(), rings, relative); } + catch (const std::exception &e) { return false; } +} + bool wallet2::unset_ring(const std::vector<crypto::key_image> &key_images) { if (!m_ringdb) @@ -7818,7 +7835,7 @@ bool wallet2::is_keys_file_locked() const return m_keys_file_locker->locked(); } -bool wallet2::tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, uint64_t global_index, const crypto::public_key& output_public_key, const rct::key& mask, uint64_t real_index, bool unlocked) const +bool wallet2::tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, uint64_t global_index, const crypto::public_key& output_public_key, const rct::key& mask, uint64_t real_index, bool unlocked, std::unordered_set<crypto::public_key> &valid_public_keys_cache) const { if (!unlocked) // don't add locked outs return false; @@ -7829,16 +7846,18 @@ bool wallet2::tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_out if (std::find(outs.back().begin(), outs.back().end(), item) != outs.back().end()) // don't add duplicates return false; // check the keys are valid - if (!rct::isInMainSubgroup(rct::pk2rct(output_public_key))) + if (valid_public_keys_cache.find(output_public_key) == valid_public_keys_cache.end() && !rct::isInMainSubgroup(rct::pk2rct(output_public_key))) { MWARNING("Key " << output_public_key << " at index " << global_index << " is not in the main subgroup"); return false; } - if (!rct::isInMainSubgroup(mask)) + valid_public_keys_cache.insert(output_public_key); + if (valid_public_keys_cache.find(rct::rct2pk(mask)) == valid_public_keys_cache.end() && !rct::isInMainSubgroup(mask)) { MWARNING("Commitment " << mask << " at index " << global_index << " is not in the main subgroup"); return false; } + valid_public_keys_cache.insert(rct::rct2pk(mask)); // if (is_output_blackballed(output_public_key)) // don't add blackballed outputs // return false; outs.back().push_back(item); @@ -7871,7 +7890,6 @@ void wallet2::light_wallet_get_outs(std::vector<std::vector<tools::wallet2::get_ m_daemon_rpc_mutex.unlock(); THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "get_random_outs"); THROW_WALLET_EXCEPTION_IF(ores.amount_outs.empty() , error::wallet_internal_error, "No outputs received from light wallet node. Error: " + ores.Error); - size_t n_outs = 0; for (const auto &e: ores.amount_outs) n_outs += e.outputs.size(); } // Check if we got enough outputs for each amount @@ -7882,6 +7900,7 @@ void wallet2::light_wallet_get_outs(std::vector<std::vector<tools::wallet2::get_ MDEBUG("selected transfers size: " << selected_transfers.size()); + std::unordered_set<crypto::public_key> valid_public_keys_cache; for(size_t idx: selected_transfers) { // Create new index @@ -7933,7 +7952,7 @@ void wallet2::light_wallet_get_outs(std::vector<std::vector<tools::wallet2::get_ if(!light_wallet_parse_rct_str(ores.amount_outs[amount_key].outputs[i].rct, tx_public_key, 0, mask, rct_commit, false)) rct_commit = rct::zeroCommit(td.amount()); - if (tx_add_fake_output(outs, global_index, tx_public_key, rct_commit, td.m_global_output_index, true)) { + if (tx_add_fake_output(outs, global_index, tx_public_key, rct_commit, td.m_global_output_index, true, valid_public_keys_cache)) { MDEBUG("added fake output " << ores.amount_outs[amount_key].outputs[i].public_key); MDEBUG("index " << global_index); } @@ -7970,12 +7989,12 @@ std::pair<std::set<uint64_t>, size_t> outs_unique(const std::vector<std::vector< return std::make_pair(std::move(unique), total); } -void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count, bool rct) +void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count, bool rct, std::unordered_set<crypto::public_key> &valid_public_keys_cache) { std::vector<uint64_t> rct_offsets; for (size_t attempts = 3; attempts > 0; --attempts) { - get_outs(outs, selected_transfers, fake_outputs_count, rct_offsets); + get_outs(outs, selected_transfers, fake_outputs_count, rct_offsets, valid_public_keys_cache); if (!rct) return; @@ -7997,7 +8016,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> THROW_WALLET_EXCEPTION(error::wallet_internal_error, tr("Transaction sanity check failed")); } -void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count, std::vector<uint64_t> &rct_offsets) +void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count, std::vector<uint64_t> &rct_offsets, std::unordered_set<crypto::public_key> &valid_public_keys_cache) { LOG_PRINT_L2("fake_outputs_count: " << fake_outputs_count); outs.clear(); @@ -8041,6 +8060,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::request req_t = AUTO_VAL_INIT(req_t); cryptonote::COMMAND_RPC_GET_OUTPUT_HISTOGRAM::response resp_t = AUTO_VAL_INIT(resp_t); // request histogram for all outputs, except 0 if we have the rct distribution + req_t.amounts.reserve(selected_transfers.size()); for(size_t idx: selected_transfers) if (!m_transfers[idx].is_rct() || !has_rct_distribution) req_t.amounts.push_back(m_transfers[idx].is_rct() ? 0 : m_transfers[idx].amount()); @@ -8068,6 +8088,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> { cryptonote::COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::request req_t = AUTO_VAL_INIT(req_t); cryptonote::COMMAND_RPC_GET_OUTPUT_DISTRIBUTION::response resp_t = AUTO_VAL_INIT(resp_t); + req_t.amounts.reserve(req_t.amounts.size() + selected_transfers.size()); for(size_t idx: selected_transfers) req_t.amounts.push_back(m_transfers[idx].is_rct() ? 0 : m_transfers[idx].amount()); std::sort(req_t.amounts.begin(), req_t.amounts.end()); @@ -8114,6 +8135,25 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> } } + std::vector<crypto::key_image> ring_key_images; + ring_key_images.reserve(selected_transfers.size()); + std::unordered_map<crypto::key_image, std::vector<uint64_t>> existing_rings; + for(size_t idx: selected_transfers) + { + const transfer_details &td = m_transfers[idx]; + if (td.m_key_image_known && !td.m_key_image_partial) + ring_key_images.push_back(td.m_key_image); + } + if (!ring_key_images.empty()) + { + std::vector<std::vector<uint64_t>> all_outs; + if (get_rings(get_ringdb_key(), ring_key_images, all_outs)) + { + for (size_t i = 0; i < ring_key_images.size(); ++i) + existing_rings[ring_key_images[i]] = std::move(all_outs[i]); + } + } + // we ask for more, to have spares if some outputs are still locked size_t base_requested_outputs_count = (size_t)((fake_outputs_count + 1) * 1.5 + 1); LOG_PRINT_L2("base_requested_outputs_count: " << base_requested_outputs_count); @@ -8127,6 +8167,8 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> gamma.reset(new gamma_picker(rct_offsets)); size_t num_selected_transfers = 0; + req.outputs.reserve(selected_transfers.size() * (base_requested_outputs_count + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW)); + daemon_resp.outs.reserve(selected_transfers.size() * (base_requested_outputs_count + CRYPTONOTE_MINED_MONEY_UNLOCK_WINDOW)); for(size_t idx: selected_transfers) { ++num_selected_transfers; @@ -8236,9 +8278,12 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> // if we have a known ring, use it if (td.m_key_image_known && !td.m_key_image_partial) { - std::vector<uint64_t> ring; - if (get_ring(get_ringdb_key(), td.m_key_image, ring)) + + const auto it = existing_rings.find(td.m_key_image); + const bool has_ring = it != existing_rings.end(); + if (has_ring) { + const std::vector<uint64_t> &ring = it->second; MINFO("This output has a known ring, reusing (size " << ring.size() << ")"); THROW_WALLET_EXCEPTION_IF(ring.size() > fake_outputs_count + 1, error::wallet_internal_error, "An output in this transaction was previously spent on another chain with ring size " + @@ -8438,7 +8483,9 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> COMMAND_RPC_GET_OUTPUTS_BIN::request chunk_req = AUTO_VAL_INIT(chunk_req); COMMAND_RPC_GET_OUTPUTS_BIN::response chunk_daemon_resp = AUTO_VAL_INIT(chunk_daemon_resp); chunk_req.get_txid = false; - for (size_t i = 0; i < std::min<size_t>(req.outputs.size() - offset, chunk_size); ++i) + const size_t this_chunk_size = std::min<size_t>(req.outputs.size() - offset, chunk_size); + chunk_req.outputs.reserve(this_chunk_size); + for (size_t i = 0; i < this_chunk_size; ++i) chunk_req.outputs.push_back(req.outputs[offset + i]); const boost::lock_guard<boost::recursive_mutex> lock{m_daemon_rpc_mutex}; @@ -8508,9 +8555,10 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> // then pick outs from an existing ring, if any if (td.m_key_image_known && !td.m_key_image_partial) { - std::vector<uint64_t> ring; - if (get_ring(get_ringdb_key(), td.m_key_image, ring)) + const auto it = existing_rings.find(td.m_key_image); + if (it != existing_rings.end()) { + const std::vector<uint64_t> &ring = it->second; for (uint64_t out: ring) { if (out < num_outs) @@ -8524,7 +8572,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> if (req.outputs[i].index == out) { LOG_PRINT_L2("Index " << i << "/" << requested_outputs_count << ": idx " << req.outputs[i].index << " (real " << td.m_global_output_index << "), unlocked " << daemon_resp.outs[i].unlocked << ", key " << daemon_resp.outs[i].key << " (from existing ring)"); - tx_add_fake_output(outs, req.outputs[i].index, daemon_resp.outs[i].key, daemon_resp.outs[i].mask, td.m_global_output_index, daemon_resp.outs[i].unlocked); + tx_add_fake_output(outs, req.outputs[i].index, daemon_resp.outs[i].key, daemon_resp.outs[i].mask, td.m_global_output_index, daemon_resp.outs[i].unlocked, valid_public_keys_cache); found = true; break; } @@ -8549,7 +8597,7 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> { size_t i = base + order[o]; LOG_PRINT_L2("Index " << i << "/" << requested_outputs_count << ": idx " << req.outputs[i].index << " (real " << td.m_global_output_index << "), unlocked " << daemon_resp.outs[i].unlocked << ", key " << daemon_resp.outs[i].key); - tx_add_fake_output(outs, req.outputs[i].index, daemon_resp.outs[i].key, daemon_resp.outs[i].mask, td.m_global_output_index, daemon_resp.outs[i].unlocked); + tx_add_fake_output(outs, req.outputs[i].index, daemon_resp.outs[i].key, daemon_resp.outs[i].mask, td.m_global_output_index, daemon_resp.outs[i].unlocked, valid_public_keys_cache); } if (outs.back().size() < fake_outputs_count + 1) { @@ -8577,6 +8625,8 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> } // save those outs in the ringdb for reuse + std::vector<std::pair<crypto::key_image, std::vector<uint64_t>>> rings; + rings.reserve(selected_transfers.size()); for (size_t i = 0; i < selected_transfers.size(); ++i) { const size_t idx = selected_transfers[i]; @@ -8586,14 +8636,15 @@ void wallet2::get_outs(std::vector<std::vector<tools::wallet2::get_outs_entry>> ring.reserve(outs[i].size()); for (const auto &e: outs[i]) ring.push_back(std::get<0>(e)); - if (!set_ring(td.m_key_image, ring, false)) - MERROR("Failed to set ring for " << td.m_key_image); + rings.push_back(std::make_pair(td.m_key_image, std::move(ring))); } + if (!set_rings(rings, false)) + MERROR("Failed to set rings"); } template<typename T> void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_entry>& dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count, - std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, + std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, std::unordered_set<crypto::public_key> &valid_public_keys_cache, uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx, bool use_view_tags) { @@ -8631,7 +8682,7 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent THROW_WALLET_EXCEPTION_IF(subaddr_account != m_transfers[*i].m_subaddr_index.major, error::wallet_internal_error, "the tx uses funds from multiple accounts"); if (outs.empty()) - get_outs(outs, selected_transfers, fake_outputs_count, false); // may throw + get_outs(outs, selected_transfers, fake_outputs_count, false, valid_public_keys_cache); // may throw //prepare inputs LOG_PRINT_L2("preparing outputs"); @@ -8755,7 +8806,7 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent } void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry> dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count, - std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, + std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, std::unordered_set<crypto::public_key> &valid_public_keys_cache, uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx, const rct::RCTConfig &rct_config, bool use_view_tags) { using namespace cryptonote; @@ -8849,7 +8900,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry THROW_WALLET_EXCEPTION_IF(subaddr_account != m_transfers[*i].m_subaddr_index.major, error::wallet_internal_error, "the tx uses funds from multiple accounts"); if (outs.empty()) - get_outs(outs, selected_transfers, fake_outputs_count, all_rct); // may throw + get_outs(outs, selected_transfers, fake_outputs_count, all_rct, valid_public_keys_cache); // may throw //prepare inputs LOG_PRINT_L2("preparing outputs"); @@ -9674,7 +9725,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp std::vector<std::pair<uint32_t, std::vector<size_t>>> unused_transfers_indices_per_subaddr; std::vector<std::pair<uint32_t, std::vector<size_t>>> unused_dust_indices_per_subaddr; uint64_t needed_money; - uint64_t accumulated_fee, accumulated_outputs, accumulated_change; + uint64_t accumulated_fee, accumulated_change; struct TX { std::vector<size_t> selected_transfers; std::vector<cryptonote::tx_destination_entry> dsts; @@ -9735,6 +9786,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp bulletproof_plus ? 4 : 3 }; const bool use_view_tags = use_fork_rules(get_view_tag_fork(), 0); + std::unordered_set<crypto::public_key> valid_public_keys_cache; const uint64_t base_fee = get_base_fee(priority); const uint64_t fee_quantization_mask = get_fee_quantization_mask(); @@ -9865,7 +9917,6 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp // start with an empty tx txes.push_back(TX()); accumulated_fee = 0; - accumulated_outputs = 0; accumulated_change = 0; adding_fee = false; needed_fee = 0; @@ -9878,8 +9929,6 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp // the destination, and one for change. LOG_PRINT_L2("checking preferred"); std::vector<size_t> preferred_inputs; - uint64_t rct_outs_needed = 2 * (fake_outs_count + 1); - rct_outs_needed += 100; // some fudge factor since we don't know how many are locked if (use_rct) { // this is used to build a tx that's 1 or 2 inputs, and 2 outputs, which @@ -9984,7 +10033,6 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp // add this output to the list to spend tx.selected_transfers.push_back(idx); uint64_t available_amount = td.amount(); - accumulated_outputs += available_amount; // clear any fake outs we'd already gathered, since we'll need a new set outs.clear(); @@ -10112,10 +10160,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp LOG_PRINT_L2("Trying to create a tx now, with " << tx.dsts.size() << " outputs and " << tx.selected_transfers.size() << " inputs"); if (use_rct) - transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, + transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, valid_public_keys_cache, unlock_time, needed_fee, extra, test_tx, test_ptx, rct_config, use_view_tags); else - transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, + transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, valid_public_keys_cache, unlock_time, needed_fee, extra, detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx, use_view_tags); auto txBlob = t_serializable_object_to_blob(test_ptx.tx); needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_quantization_mask); @@ -10137,10 +10185,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp LOG_PRINT_L2("We made a tx, adjusting fee and saving it, we need " << print_money(needed_fee) << " and we have " << print_money(test_ptx.fee)); while (needed_fee > test_ptx.fee) { if (use_rct) - transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, + transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, valid_public_keys_cache, unlock_time, needed_fee, extra, test_tx, test_ptx, rct_config, use_view_tags); else - transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, + transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, valid_public_keys_cache, unlock_time, needed_fee, extra, detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx, use_view_tags); txBlob = t_serializable_object_to_blob(test_ptx.tx); needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_quantization_mask); @@ -10206,6 +10254,7 @@ skip_tx: tx.selected_transfers, /* const std::list<size_t> selected_transfers */ fake_outs_count, /* CONST size_t fake_outputs_count, */ tx.outs, /* MOD std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, */ + valid_public_keys_cache, unlock_time, /* CONST uint64_t unlock_time, */ tx.needed_fee, /* CONST uint64_t fee, */ extra, /* const std::vector<uint8_t>& extra, */ @@ -10218,6 +10267,7 @@ skip_tx: tx.selected_transfers, fake_outs_count, tx.outs, + valid_public_keys_cache, unlock_time, tx.needed_fee, extra, @@ -10335,6 +10385,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_all(uint64_t below THROW_WALLET_EXCEPTION_IF(tx_weight_one_ring > tx_weight_two_rings, error::wallet_internal_error, "Estimated tx weight with 1 input is larger than with 2 inputs!"); const size_t tx_weight_per_ring = tx_weight_two_rings - tx_weight_one_ring; const uint64_t fractional_threshold = (base_fee * tx_weight_per_ring) / (use_per_byte_fee ? 1 : 1024); + std::unordered_set<crypto::public_key> valid_public_keys_cache; THROW_WALLET_EXCEPTION_IF(unlocked_balance(subaddr_account, false) == 0, error::wallet_internal_error, "No unlocked balance in the specified account"); @@ -10416,6 +10467,7 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton hw::device &hwdev = m_account.get_device(); boost::unique_lock<hw::device> hwdev_lock (hwdev); hw::reset_mode rst(hwdev); + std::unordered_set<crypto::public_key> valid_public_keys_cache; uint64_t accumulated_fee, accumulated_outputs, accumulated_change; struct TX { @@ -10518,10 +10570,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton LOG_PRINT_L2("Trying to create a tx now, with " << tx.dsts.size() << " destinations and " << tx.selected_transfers.size() << " outputs"); if (use_rct) - transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, + transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, valid_public_keys_cache, unlock_time, needed_fee, extra, test_tx, test_ptx, rct_config, use_view_tags); else - transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, + transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, valid_public_keys_cache, unlock_time, needed_fee, extra, detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx, use_view_tags); auto txBlob = t_serializable_object_to_blob(test_ptx.tx); needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_quantization_mask); @@ -10555,10 +10607,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton dt.amount = dt_amount + dt_residue; } if (use_rct) - transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, + transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, outs, valid_public_keys_cache, unlock_time, needed_fee, extra, test_tx, test_ptx, rct_config, use_view_tags); else - transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, unlock_time, needed_fee, extra, + transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, outs, valid_public_keys_cache, unlock_time, needed_fee, extra, detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx, use_view_tags); txBlob = t_serializable_object_to_blob(test_ptx.tx); needed_fee = calculate_fee(use_per_byte_fee, test_ptx.tx, txBlob.size(), base_fee, fee_quantization_mask); @@ -10594,10 +10646,10 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_from(const crypton cryptonote::transaction test_tx; pending_tx test_ptx; if (use_rct) { - transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, tx.outs, unlock_time, tx.needed_fee, extra, + transfer_selected_rct(tx.dsts, tx.selected_transfers, fake_outs_count, tx.outs, valid_public_keys_cache, unlock_time, tx.needed_fee, extra, test_tx, test_ptx, rct_config, use_view_tags); } else { - transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, tx.outs, unlock_time, tx.needed_fee, extra, + transfer_selected(tx.dsts, tx.selected_transfers, fake_outs_count, tx.outs, valid_public_keys_cache, unlock_time, tx.needed_fee, extra, detail::digit_split_strategy, tx_dust_policy(::config::DEFAULT_DUST_THRESHOLD), test_tx, test_ptx, use_view_tags); } auto txBlob = t_serializable_object_to_blob(test_ptx.tx); diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 660e6a14b..e9e5bc95e 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1001,10 +1001,10 @@ private: uint64_t unlocked_balance_all(bool strict, uint64_t *blocks_to_unlock = NULL, uint64_t *time_to_unlock = NULL); template<typename T> void transfer_selected(const std::vector<cryptonote::tx_destination_entry>& dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count, - std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, + std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, std::unordered_set<crypto::public_key> &valid_public_keys_cache, uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx &ptx, const bool use_view_tags); void transfer_selected_rct(std::vector<cryptonote::tx_destination_entry> dsts, const std::vector<size_t>& selected_transfers, size_t fake_outputs_count, - std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, + std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, std::unordered_set<crypto::public_key> &valid_public_keys_cache, uint64_t unlock_time, uint64_t fee, const std::vector<uint8_t>& extra, cryptonote::transaction& tx, pending_tx &ptx, const rct::RCTConfig &rct_config, const bool use_view_tags); void commit_tx(pending_tx& ptx_vector); @@ -1558,7 +1558,9 @@ private: const std::string get_ring_database() const { return m_ring_database; } bool get_ring(const crypto::key_image &key_image, std::vector<uint64_t> &outs); bool get_rings(const crypto::hash &txid, std::vector<std::pair<crypto::key_image, std::vector<uint64_t>>> &outs); + bool get_rings(const crypto::chacha_key &key, const std::vector<crypto::key_image> &key_images, std::vector<std::vector<uint64_t>> &outs); bool set_ring(const crypto::key_image &key_image, const std::vector<uint64_t> &outs, bool relative); + bool set_rings(const std::vector<std::pair<crypto::key_image, std::vector<uint64_t>>> &rings, bool relative); bool unset_ring(const std::vector<crypto::key_image> &key_images); bool unset_ring(const crypto::hash &txid); bool find_and_save_rings(bool force = true); @@ -1665,9 +1667,9 @@ private: void set_unspent(size_t idx); bool is_spent(const transfer_details &td, bool strict = true) const; bool is_spent(size_t idx, bool strict = true) const; - void get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count, bool rct); - void get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count, std::vector<uint64_t> &rct_offsets); - bool tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, uint64_t global_index, const crypto::public_key& tx_public_key, const rct::key& mask, uint64_t real_index, bool unlocked) const; + void get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count, bool rct, std::unordered_set<crypto::public_key> &valid_public_keys_cache); + void get_outs(std::vector<std::vector<get_outs_entry>> &outs, const std::vector<size_t> &selected_transfers, size_t fake_outputs_count, std::vector<uint64_t> &rct_offsets, std::unordered_set<crypto::public_key> &valid_public_keys_cache); + bool tx_add_fake_output(std::vector<std::vector<tools::wallet2::get_outs_entry>> &outs, uint64_t global_index, const crypto::public_key& tx_public_key, const rct::key& mask, uint64_t real_index, bool unlocked, std::unordered_set<crypto::public_key> &valid_public_keys_cache) const; bool should_pick_a_second_output(bool use_rct, size_t n_transfers, const std::vector<size_t> &unused_transfers_indices, const std::vector<size_t> &unused_dust_indices) const; std::vector<size_t> get_only_rct(const std::vector<size_t> &unused_dust_indices, const std::vector<size_t> &unused_transfers_indices) const; void scan_output(const cryptonote::transaction &tx, bool miner_tx, const crypto::public_key &tx_pub_key, size_t i, tx_scan_info_t &tx_scan_info, int &num_vouts_received, std::unordered_map<cryptonote::subaddress_index, uint64_t> &tx_money_got_in_outs, std::vector<size_t> &outs, bool pool); |