From def7aae1ec1b4ff14f5577779088863b4e206f6d Mon Sep 17 00:00:00 2001
From: tecnovert <tecnovert@users.noreply.github.com>
Date: Tue, 14 Jan 2025 06:04:47 +0000
Subject: [PATCH] Tor port fixes (#215)

* Set bind for BCH when using tor

* prepare: Set local tor control host when not in docker mode.

* Unlink tor hosts from BSX_DOCKER_MODE and add BSX_LOCAL_TOR.
---
 basicswap/bin/prepare.py | 27 ++++++++++++++++-----------
 basicswap/bin/run.py     | 13 +++++++++----
 2 files changed, 25 insertions(+), 15 deletions(-)

diff --git a/basicswap/bin/prepare.py b/basicswap/bin/prepare.py
index 11c405e..eb2655d 100755
--- a/basicswap/bin/prepare.py
+++ b/basicswap/bin/prepare.py
@@ -164,6 +164,7 @@ if not len(logger.handlers):
     logger.addHandler(logging.StreamHandler(sys.stdout))
 
 BSX_DOCKER_MODE = toBool(os.getenv("BSX_DOCKER_MODE", "false"))
+BSX_LOCAL_TOR = toBool(os.getenv("BSX_LOCAL_TOR", "false"))
 BSX_TEST_MODE = toBool(os.getenv("BSX_TEST_MODE", "false"))
 BSX_UPDATE_UNMANAGED = toBool(
     os.getenv("BSX_UPDATE_UNMANAGED", "true")
@@ -267,6 +268,11 @@ TOR_PROXY_HOST = os.getenv("TOR_PROXY_HOST", "127.0.0.1")
 TOR_PROXY_PORT = int(os.getenv("TOR_PROXY_PORT", 9050))
 TOR_CONTROL_PORT = int(os.getenv("TOR_CONTROL_PORT", 9051))
 TOR_DNS_PORT = int(os.getenv("TOR_DNS_PORT", 5353))
+TOR_CONTROL_LISTEN_INTERFACE = os.getenv("TOR_CONTROL_LISTEN_INTERFACE", "127.0.0.1" if BSX_LOCAL_TOR else "0.0.0.0")
+TORRC_PROXY_HOST = os.getenv("TORRC_PROXY_HOST", "127.0.0.1" if BSX_LOCAL_TOR else "0.0.0.0")
+TORRC_CONTROL_HOST = os.getenv("TORRC_CONTROL_HOST", "127.0.0.1" if BSX_LOCAL_TOR else "0.0.0.0")
+TORRC_DNS_HOST = os.getenv("TORRC_DNS_HOST", "127.0.0.1" if BSX_LOCAL_TOR else "0.0.0.0")
+
 TEST_TOR_PROXY = toBool(
     os.getenv("TEST_TOR_PROXY", "true")
 )  # Expects a known exit node
@@ -1092,9 +1098,9 @@ def writeTorSettings(fp, coin, coin_settings, tor_control_password):
     fp.write(f"torcontrol={TOR_PROXY_HOST}:{TOR_CONTROL_PORT}\n")
 
     if coin_settings["core_version_group"] >= 21:
-        fp.write(f"bind=0.0.0.0:{onionport}=onion\n")
+        fp.write(f"bind={TOR_CONTROL_LISTEN_INTERFACE}:{onionport}=onion\n")
     else:
-        fp.write(f"bind=0.0.0.0:{onionport}\n")
+        fp.write(f"bind={TOR_CONTROL_LISTEN_INTERFACE}:{onionport}\n")
 
 
 def prepareDataDir(coin, settings, chain, particl_mnemonic, extra_opts={}):
@@ -1416,9 +1422,9 @@ def write_torrc(data_dir, tor_control_password):
 
     tor_control_hash = rfc2440_hash_password(tor_control_password)
     with open(torrc_path, "w") as fp:
-        fp.write(f"SocksPort 0.0.0.0:{TOR_PROXY_PORT}\n")
-        fp.write(f"ControlPort 0.0.0.0:{TOR_CONTROL_PORT}\n")
-        fp.write(f"DNSPort 0.0.0.0:{TOR_DNS_PORT}\n")
+        fp.write(f"SocksPort {TORRC_PROXY_HOST}:{TOR_PROXY_PORT}\n")
+        fp.write(f"ControlPort {TORRC_CONTROL_HOST}:{TOR_CONTROL_PORT}\n")
+        fp.write(f"DNSPort {TORRC_DNS_HOST}:{TOR_DNS_PORT}\n")
         fp.write(f"HashedControlPassword {tor_control_hash}\n")
 
 
@@ -2669,13 +2675,12 @@ def main():
     )
     if os.path.exists(config_path):
         if prepare_bin_only:
-            with open(config_path) as fs:
-                settings = json.load(fs)
+            settings = load_config(config_path)
 
-                # Add temporary default config for any coins that have not been added
-                for c in with_coins:
-                    if c not in settings["chainclients"]:
-                        settings["chainclients"][c] = chainclients[c]
+            # Add temporary default config for any coins that have not been added
+            for c in with_coins:
+                if c not in settings["chainclients"]:
+                    settings["chainclients"][c] = chainclients[c]
         elif upgrade_cores:
             with open(config_path) as fs:
                 settings = json.load(fs)
diff --git a/basicswap/bin/run.py b/basicswap/bin/run.py
index 01db546..3400941 100755
--- a/basicswap/bin/run.py
+++ b/basicswap/bin/run.py
@@ -247,18 +247,23 @@ def getWalletBinName(coin_id: int, coin_settings, default_name: str) -> str:
     ) + (".exe" if os.name == "nt" else "")
 
 
-def getCoreBinArgs(coin_id: int, coin_settings, prepare=False):
+def getCoreBinArgs(coin_id: int, coin_settings, prepare=False, use_tor_proxy=False):
     extra_args = []
     if "config_filename" in coin_settings:
         extra_args.append("--conf=" + coin_settings["config_filename"])
     if "port" in coin_settings:
-        extra_args.append("--port=" + str(int(coin_settings["port"])))
+        if prepare is False and use_tor_proxy:
+            if coin_id == Coins.BCH:
+                # Without this BCH (27.1) will bind to the default BTC port, even with proxy set
+                extra_args.append("--bind=127.0.0.1:" + str(int(coin_settings["port"])))
+        else:
+            extra_args.append("--port=" + str(int(coin_settings["port"])))
 
     # BTC versions from v28 fail to start if the onionport is in use.
     # As BCH may use port 8334, disable it here.
     # When tor is enabled a bind option for the onionport will be added to bitcoin.conf.
     # https://github.com/bitcoin/bitcoin/blob/master/doc/release-notes/release-notes-28.0.md?plain=1#L84
-    if not prepare and coin_id == Coins.BTC:
+    if prepare is False and use_tor_proxy is False and coin_id == Coins.BTC:
         port: int = coin_settings.get("port", 8333)
         extra_args.append(f"--bind=0.0.0.0:{port}")
     return extra_args
@@ -435,7 +440,7 @@ def runClient(fp, data_dir, chain, start_only_coins):
                 swap_client.log.info(f"Starting {display_name} daemon")
 
                 filename: str = getCoreBinName(coin_id, v, c + "d")
-                extra_opts = getCoreBinArgs(coin_id, v)
+                extra_opts = getCoreBinArgs(coin_id, v, use_tor_proxy=swap_client.use_tor_proxy)
                 daemons.append(
                     startDaemon(v["datadir"], v["bindir"], filename, opts=extra_opts)
                 )