From f2c4c39971f25adaecde377ad325856ea785b9ac Mon Sep 17 00:00:00 2001
From: moneromooo-monero <moneromooo-monero@users.noreply.github.com>
Date: Sat, 10 Feb 2018 10:38:33 +0000
Subject: [PATCH] wallet2: speed up subaddress generation (by about a third)

---
 .../cryptonote_format_utils.cpp               | 39 +++++++++++++++++++
 .../cryptonote_format_utils.h                 |  1 +
 src/wallet/wallet2.cpp                        | 29 +++++++-------
 src/wallet/wallet2.h                          |  1 +
 4 files changed, 55 insertions(+), 15 deletions(-)

diff --git a/src/cryptonote_basic/cryptonote_format_utils.cpp b/src/cryptonote_basic/cryptonote_format_utils.cpp
index aab4f380c..c668ceae5 100644
--- a/src/cryptonote_basic/cryptonote_format_utils.cpp
+++ b/src/cryptonote_basic/cryptonote_format_utils.cpp
@@ -172,6 +172,45 @@ namespace cryptonote
     return m;
   }
   //---------------------------------------------------------------
+  std::vector<crypto::public_key> get_subaddress_spend_public_keys(const cryptonote::account_keys &keys, uint32_t account, uint32_t begin, uint32_t end)
+  {
+    CHECK_AND_ASSERT_THROW_MES(begin <= end, "begin > end");
+
+    std::vector<crypto::public_key> pkeys;
+    pkeys.reserve(end - begin);
+    cryptonote::subaddress_index index = {account, begin};
+
+    ge_p3 p3;
+    ge_cached cached;
+    CHECK_AND_ASSERT_THROW_MES(ge_frombytes_vartime(&p3, (const unsigned char*)keys.m_account_address.m_spend_public_key.data) == 0,
+        "ge_frombytes_vartime failed to convert spend public key");
+    ge_p3_to_cached(&cached, &p3);
+
+    for (uint32_t idx = begin; idx < end; ++idx)
+    {
+      index.minor = idx;
+      if (index.is_zero())
+      {
+        pkeys.push_back(keys.m_account_address.m_spend_public_key);
+        continue;
+      }
+      const crypto::secret_key m = cryptonote::get_subaddress_secret_key(keys.m_view_secret_key, index);
+
+      // M = m*G
+      ge_scalarmult_base(&p3, (const unsigned char*)m.data);
+
+      // D = B + M
+      crypto::public_key D;
+      ge_p1p1 p1p1;
+      ge_add(&p1p1, &p3, &cached);
+      ge_p1p1_to_p3(&p3, &p1p1);
+      ge_p3_tobytes((unsigned char*)D.data, &p3);
+
+      pkeys.push_back(D);
+    }
+    return pkeys;
+  }
+  //---------------------------------------------------------------
   bool generate_key_image_helper(const account_keys& ack, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, const crypto::public_key& out_key, const crypto::public_key& tx_public_key, const std::vector<crypto::public_key>& additional_tx_public_keys, size_t real_output_index, keypair& in_ephemeral, crypto::key_image& ki)
   {
     crypto::key_derivation recv_derivation = AUTO_VAL_INIT(recv_derivation);
diff --git a/src/cryptonote_basic/cryptonote_format_utils.h b/src/cryptonote_basic/cryptonote_format_utils.h
index 29e180c41..07a3ac92a 100644
--- a/src/cryptonote_basic/cryptonote_format_utils.h
+++ b/src/cryptonote_basic/cryptonote_format_utils.h
@@ -93,6 +93,7 @@ namespace cryptonote
   bool get_tx_fee(const transaction& tx, uint64_t & fee);
   uint64_t get_tx_fee(const transaction& tx);
   crypto::secret_key get_subaddress_secret_key(const crypto::secret_key& a, const subaddress_index& index);
+  std::vector<crypto::public_key> get_subaddress_spend_public_keys(const cryptonote::account_keys &keys, uint32_t account, uint32_t begin, uint32_t end);
   bool generate_key_image_helper(const account_keys& ack, const std::unordered_map<crypto::public_key, subaddress_index>& subaddresses, const crypto::public_key& out_key, const crypto::public_key& tx_public_key, const std::vector<crypto::public_key>& additional_tx_public_keys, size_t real_output_index, keypair& in_ephemeral, crypto::key_image& ki);
   bool generate_key_image_helper_precomp(const account_keys& ack, const crypto::public_key& out_key, const crypto::key_derivation& recv_derivation, size_t real_output_index, const subaddress_index& received_index, keypair& in_ephemeral, crypto::key_image& ki);
   void get_blob_hash(const blobdata& blob, crypto::hash& res);
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 7dc8a1e47..a17f0ffc6 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -889,14 +889,13 @@ void wallet2::expand_subaddresses(const cryptonote::subaddress_index& index)
     cryptonote::subaddress_index index2;
     for (index2.major = m_subaddress_labels.size(); index2.major < index.major + m_subaddress_lookahead_major; ++index2.major)
     {
-      for (index2.minor = 0; index2.minor < (index2.major == index.major ? index.minor : 0) + m_subaddress_lookahead_minor; ++index2.minor)
+      const uint32_t end = (index2.major == index.major ? index.minor : 0) + m_subaddress_lookahead_minor;
+      const std::vector<crypto::public_key> pkeys = cryptonote::get_subaddress_spend_public_keys(m_account.get_keys(), index2.major, 0, end);
+      for (index2.minor = 0; index2.minor < end; ++index2.minor)
       {
-        if (m_subaddresses_inv.count(index2) == 0)
-        {
-          crypto::public_key D = get_subaddress_spend_public_key(index2);
-          m_subaddresses[D] = index2;
-          m_subaddresses_inv[index2] = D;
-        }
+         const crypto::public_key &D = pkeys[index2.minor];
+         m_subaddresses[D] = index2;
+         m_subaddresses_inv[index2] = D;
       }
     }
     m_subaddress_labels.resize(index.major + 1, {"Untitled account"});
@@ -905,15 +904,15 @@ void wallet2::expand_subaddresses(const cryptonote::subaddress_index& index)
   else if (m_subaddress_labels[index.major].size() <= index.minor)
   {
     // add new subaddresses
-    cryptonote::subaddress_index index2 = index;
-    for (index2.minor = m_subaddress_labels[index.major].size(); index2.minor < index.minor + m_subaddress_lookahead_minor; ++index2.minor)
+    const uint32_t end = index.minor + m_subaddress_lookahead_minor;
+    const uint32_t begin = m_subaddress_labels[index.major].size();
+    cryptonote::subaddress_index index2 = {index.major, begin};
+    const std::vector<crypto::public_key> pkeys = cryptonote::get_subaddress_spend_public_keys(m_account.get_keys(), index2.major, index2.minor, end);
+    for (; index2.minor < end; ++index2.minor)
     {
-      if (m_subaddresses_inv.count(index2) == 0)
-      {
-        crypto::public_key D = get_subaddress_spend_public_key(index2);
-        m_subaddresses[D] = index2;
-        m_subaddresses_inv[index2] = D;
-      }
+       const crypto::public_key &D = pkeys[index2.minor - begin];
+       m_subaddresses[D] = index2;
+       m_subaddresses_inv[index2] = D;
     }
     m_subaddress_labels[index.major].resize(index.minor + 1);
   }
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index f768581b2..e92abbc69 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -602,6 +602,7 @@ namespace tools
     cryptonote::account_public_address get_subaddress(const cryptonote::subaddress_index& index) const;
     cryptonote::account_public_address get_address() const { return get_subaddress({0,0}); }
     crypto::public_key get_subaddress_spend_public_key(const cryptonote::subaddress_index& index) const;
+    std::vector<crypto::public_key> get_subaddress_spend_public_keys(uint32_t account, uint32_t begin, uint32_t end) const;
     std::string get_subaddress_as_str(const cryptonote::subaddress_index& index) const;
     std::string get_address_as_str() const { return get_subaddress_as_str({0, 0}); }
     std::string get_integrated_address_as_str(const crypto::hash8& payment_id) const;