From 38cdf01c643df677a0c65b8932d62c3f6eb4e62b Mon Sep 17 00:00:00 2001 From: Crypto City Date: Fri, 5 Nov 2021 19:18:10 +0000 Subject: [PATCH] wallet2: speedup large tx construction: batch ringdb lookups 3.3 seconds -> 2.8 seconds on a test case --- src/wallet/ringdb.cpp | 22 +++++++++++++++++++++- src/wallet/ringdb.h | 1 + src/wallet/wallet2.cpp | 39 +++++++++++++++++++++++++++++++++++---- src/wallet/wallet2.h | 1 + 4 files changed, 58 insertions(+), 5 deletions(-) diff --git a/src/wallet/ringdb.cpp b/src/wallet/ringdb.cpp index d5d06f16b..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 &outs) +bool ringdb::get_rings(const crypto::chacha_key &chacha_key, const std::vector &key_images, std::vector> &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 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,6 +398,15 @@ bool ringdb::get_ring(const crypto::chacha_key &chacha_key, const crypto::key_im return true; } +bool ringdb::get_ring(const crypto::chacha_key &chacha_key, const crypto::key_image &key_image, std::vector &outs) +{ + std::vector> all_outs; + if (!get_rings(chacha_key, std::vector(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>> &rings, bool relative) { MDB_txn *txn; diff --git a/src/wallet/ringdb.h b/src/wallet/ringdb.h index 7b02f4605..bdecdba37 100644 --- a/src/wallet/ringdb.h +++ b/src/wallet/ringdb.h @@ -49,6 +49,7 @@ namespace tools bool remove_rings(const crypto::chacha_key &chacha_key, const std::vector &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 &outs); + bool get_rings(const crypto::chacha_key &chacha_key, const std::vector &key_images, std::vector> &all_outs); bool set_ring(const crypto::chacha_key &chacha_key, const crypto::key_image &key_image, const std::vector &outs, bool relative); bool set_rings(const crypto::chacha_key &chacha_key, const std::vector>> &rings, bool relative); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index e77e3bafd..3df67aac1 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 &key_images, std::vector> &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>> &outs) { for (auto i: m_confirmed_txs) @@ -8126,6 +8134,25 @@ void wallet2::get_outs(std::vector> } } + std::vector ring_key_images; + ring_key_images.reserve(selected_transfers.size()); + std::unordered_map> 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> 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); @@ -8248,9 +8275,12 @@ void wallet2::get_outs(std::vector> // if we have a known ring, use it if (td.m_key_image_known && !td.m_key_image_partial) { - std::vector 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 &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 " + @@ -8520,9 +8550,10 @@ void wallet2::get_outs(std::vector> // then pick outs from an existing ring, if any if (td.m_key_image_known && !td.m_key_image_partial) { - std::vector 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 &ring = it->second; for (uint64_t out: ring) { if (out < num_outs) diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 082b1d4e9..e9e5bc95e 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1558,6 +1558,7 @@ private: const std::string get_ring_database() const { return m_ring_database; } bool get_ring(const crypto::key_image &key_image, std::vector &outs); bool get_rings(const crypto::hash &txid, std::vector>> &outs); + bool get_rings(const crypto::chacha_key &key, const std::vector &key_images, std::vector> &outs); bool set_ring(const crypto::key_image &key_image, const std::vector &outs, bool relative); bool set_rings(const std::vector>> &rings, bool relative); bool unset_ring(const std::vector &key_images);