From c50ade514fab0f48908e4b940fbf2ca08216a67a Mon Sep 17 00:00:00 2001
From: 0xFFFC0000 <0xFFFC0000@proton.me>
Date: Sun, 18 Feb 2024 18:36:52 +0000
Subject: [PATCH] Daemon-specific proxy for the wallet-rpc. 1. Daemon-specific
 proxy is exclusive with global proxy (--proxy). 2. If you set global proxy
 (--proxy) you cannot set daemon-specific proxy. 3. If you don't set global
 proxy, you can set proxy (or not set) proxy for each daemon connection with
 the proxy field in jsonrpc to the wallet-rpc.

---
 src/wallet/wallet2.cpp                       | 13 +++++++++++--
 src/wallet/wallet2.h                         | 10 +++++++++-
 src/wallet/wallet_rpc_server.cpp             |  9 ++++++++-
 src/wallet/wallet_rpc_server_commands_defs.h |  2 ++
 src/wallet/wallet_rpc_server_error_codes.h   |  1 +
 5 files changed, 31 insertions(+), 4 deletions(-)

diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp
index 336f4e159..ab6c86b04 100644
--- a/src/wallet/wallet2.cpp
+++ b/src/wallet/wallet2.cpp
@@ -1267,6 +1267,11 @@ bool wallet2::has_stagenet_option(const boost::program_options::variables_map& v
   return command_line::get_arg(vm, options().stagenet);
 }
 
+bool wallet2::has_proxy_option() const
+{
+  return !m_proxy.empty();
+}
+
 std::string wallet2::device_name_option(const boost::program_options::variables_map& vm)
 {
   return command_line::get_arg(vm, options().hw_device);
@@ -1351,12 +1356,15 @@ std::unique_ptr<wallet2> wallet2::make_dummy(const boost::program_options::varia
 }
 
 //----------------------------------------------------------------------------------------------------
-bool wallet2::set_daemon(std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, bool trusted_daemon, epee::net_utils::ssl_options_t ssl_options)
+bool wallet2::set_daemon(std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, bool trusted_daemon, epee::net_utils::ssl_options_t ssl_options, const std::string& proxy)
 {
   boost::lock_guard<boost::recursive_mutex> lock(m_daemon_rpc_mutex);
 
   if(m_http_client->is_connected())
     m_http_client->disconnect();
+  CHECK_AND_ASSERT_MES2(m_proxy.empty() || proxy.empty() , "It is not possible to set global proxy (--proxy) and daemon specific proxy together.");
+  if(m_proxy.empty())
+    CHECK_AND_ASSERT_MES(set_proxy(proxy), false, "failed to set proxy address");
   const bool changed = m_daemon_address != daemon_address;
   m_daemon_address = std::move(daemon_address);
   m_daemon_login = std::move(daemon_login);
@@ -1386,7 +1394,8 @@ bool wallet2::set_proxy(const std::string &address)
 //----------------------------------------------------------------------------------------------------
 bool wallet2::init(std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, const std::string &proxy_address, uint64_t upper_transaction_weight_limit, bool trusted_daemon, epee::net_utils::ssl_options_t ssl_options)
 {
-  CHECK_AND_ASSERT_MES(set_proxy(proxy_address), false, "failed to set proxy address");
+  m_proxy = proxy_address;
+  CHECK_AND_ASSERT_MES(set_proxy(m_proxy), false, "failed to set proxy address");
   m_checkpoints.init_default_checkpoints(m_nettype);
   m_is_initialized = true;
   m_upper_transaction_weight_limit = upper_transaction_weight_limit;
diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h
index 21ca48464..310aadb94 100644
--- a/src/wallet/wallet2.h
+++ b/src/wallet/wallet2.h
@@ -962,6 +962,12 @@ private:
 
     std::string path() const;
 
+    /*!
+     * \brief has_proxy_option      Check the global proxy (--proxy) has been defined or not.
+     * \return                      returns bool representing the global proxy (--proxy).
+     */
+    bool has_proxy_option() const;
+
     /*!
      * \brief verifies given password is correct for default wallet keys file
      */
@@ -992,7 +998,8 @@ private:
       epee::net_utils::ssl_options_t ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_autodetect);
     bool set_daemon(std::string daemon_address = "http://localhost:8080",
       boost::optional<epee::net_utils::http::login> daemon_login = boost::none, bool trusted_daemon = true,
-      epee::net_utils::ssl_options_t ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_autodetect);
+      epee::net_utils::ssl_options_t ssl_options = epee::net_utils::ssl_support_t::e_ssl_support_autodetect,
+      const std::string &proxy = "");
     bool set_proxy(const std::string &address);
 
     void stop() { m_run.store(false, std::memory_order_relaxed); m_message_store.stop(); }
@@ -1771,6 +1778,7 @@ private:
     cryptonote::account_base m_account;
     boost::optional<epee::net_utils::http::login> m_daemon_login;
     std::string m_daemon_address;
+    std::string m_proxy;
     std::string m_wallet_file;
     std::string m_keys_file;
     std::string m_mms_file;
diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp
index 0cc42ae3f..1d835a86b 100644
--- a/src/wallet/wallet_rpc_server.cpp
+++ b/src/wallet/wallet_rpc_server.cpp
@@ -4406,6 +4406,13 @@ namespace tools
       er.message = "Command unavailable in restricted mode.";
       return false;
     }
+
+    if (m_wallet->has_proxy_option() && !req.proxy.empty())
+    {
+      er.code = WALLET_RPC_ERROR_CODE_PROXY_ALREADY_DEFINED;
+      er.message = "It is not possible to set daemon specific proxy when --proxy is defined.";
+      return false;
+    }
    
     std::vector<std::vector<uint8_t>> ssl_allowed_fingerprints;
     ssl_allowed_fingerprints.reserve(req.ssl_allowed_fingerprints.size());
@@ -4449,7 +4456,7 @@ namespace tools
     if (!req.username.empty() || !req.password.empty())
       daemon_login.emplace(req.username, req.password);
 
-    if (!m_wallet->set_daemon(req.address, daemon_login, req.trusted, std::move(ssl_options)))
+    if (!m_wallet->set_daemon(req.address, daemon_login, req.trusted, std::move(ssl_options), req.proxy))
     {
       er.code = WALLET_RPC_ERROR_CODE_NO_DAEMON_CONNECTION;
       er.message = std::string("Unable to set daemon");
diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h
index 33afefb80..2a1e5ac41 100644
--- a/src/wallet/wallet_rpc_server_commands_defs.h
+++ b/src/wallet/wallet_rpc_server_commands_defs.h
@@ -2581,6 +2581,7 @@ namespace wallet_rpc
       std::string ssl_ca_file;
       std::vector<std::string> ssl_allowed_fingerprints;
       bool ssl_allow_any_cert;
+      std::string proxy;
 
       BEGIN_KV_SERIALIZE_MAP()
         KV_SERIALIZE(address)
@@ -2593,6 +2594,7 @@ namespace wallet_rpc
         KV_SERIALIZE(ssl_ca_file)
         KV_SERIALIZE(ssl_allowed_fingerprints)
         KV_SERIALIZE_OPT(ssl_allow_any_cert, false)
+        KV_SERIALIZE_OPT(proxy, (std::string)"")
       END_KV_SERIALIZE_MAP()
     };
     typedef epee::misc_utils::struct_init<request_t> request;
diff --git a/src/wallet/wallet_rpc_server_error_codes.h b/src/wallet/wallet_rpc_server_error_codes.h
index 8e3bdf650..2988f52ad 100644
--- a/src/wallet/wallet_rpc_server_error_codes.h
+++ b/src/wallet/wallet_rpc_server_error_codes.h
@@ -79,3 +79,4 @@
 #define WALLET_RPC_ERROR_CODE_ZERO_AMOUNT            -46
 #define WALLET_RPC_ERROR_CODE_INVALID_SIGNATURE_TYPE -47
 #define WALLET_RPC_ERROR_CODE_DISABLED               -48
+#define WALLET_RPC_ERROR_CODE_PROXY_ALREADY_DEFINED  -49