This commit is contained in:
tecnovert 2025-04-02 18:45:45 +00:00 committed by GitHub
commit 89270bc4ac
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 209 additions and 48 deletions

View file

@ -283,18 +283,23 @@ TOR_PROXY_HOST = os.getenv("TOR_PROXY_HOST", "127.0.0.1")
TOR_PROXY_PORT = int(os.getenv("TOR_PROXY_PORT", 9050)) TOR_PROXY_PORT = int(os.getenv("TOR_PROXY_PORT", 9050))
TOR_CONTROL_PORT = int(os.getenv("TOR_CONTROL_PORT", 9051)) TOR_CONTROL_PORT = int(os.getenv("TOR_CONTROL_PORT", 9051))
TOR_DNS_PORT = int(os.getenv("TOR_DNS_PORT", 5353)) 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"
) def setTorrcVars():
TORRC_PROXY_HOST = os.getenv( global TOR_CONTROL_LISTEN_INTERFACE, TORRC_PROXY_HOST, TORRC_CONTROL_HOST, TORRC_DNS_HOST
"TORRC_PROXY_HOST", "127.0.0.1" if BSX_LOCAL_TOR else "0.0.0.0" TOR_CONTROL_LISTEN_INTERFACE = os.getenv(
) "TOR_CONTROL_LISTEN_INTERFACE", "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_PROXY_HOST = os.getenv(
) "TORRC_PROXY_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" 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( TEST_TOR_PROXY = toBool(
os.getenv("TEST_TOR_PROXY", "true") os.getenv("TEST_TOR_PROXY", "true")
@ -2073,7 +2078,69 @@ def load_config(config_path):
if not os.path.exists(config_path): if not os.path.exists(config_path):
exitWithError("{} does not exist".format(config_path)) exitWithError("{} does not exist".format(config_path))
with open(config_path) as fs: with open(config_path) as fs:
return json.load(fs) settings = json.load(fs)
BSX_ALLOW_ENV_OVERRIDE = toBool(os.getenv("BSX_ALLOW_ENV_OVERRIDE", "false"))
saved_env_var_settings = [
("setup_docker_mode", "BSX_DOCKER_MODE"),
("setup_local_tor", "BSX_LOCAL_TOR"),
("setup_tor_control_listen_interface", "TOR_CONTROL_LISTEN_INTERFACE"),
("setup_torrc_proxy_host", "TORRC_PROXY_HOST"),
("setup_torrc_control_host", "TORRC_CONTROL_HOST"),
("setup_torrc_dns_host", "TORRC_DNS_HOST"),
("tor_proxy_host", "TOR_PROXY_HOST"),
("tor_proxy_port", "TOR_PROXY_PORT"),
("tor_control_port", "TOR_CONTROL_PORT"),
]
for setting in saved_env_var_settings:
config_name, env_name = setting
env_value = globals()[env_name]
saved_config_value = settings.get(config_name, env_value)
if saved_config_value != env_value:
if os.getenv(env_name):
# If the env var was manually set override the saved config if allowed else fail.
if BSX_ALLOW_ENV_OVERRIDE:
logger.warning(
f"Env var {env_name} differs from saved config '{config_name}', overriding."
)
else:
print(
f"Env var {env_name} differs from saved config '{config_name}', set 'BSX_ALLOW_ENV_OVERRIDE' to override.",
file=sys.stderr,
)
sys.exit(1)
else:
logger.info(f"Setting {env_name} from saved config '{config_name}'.")
globals()[env_name] = saved_config_value
# Recalculate env vars that depend on the changed var
if env_name == "BSX_LOCAL_TOR":
setTorrcVars()
return settings
def save_config(config_path, settings, add_options: bool = True) -> None:
if add_options is True:
if os.getenv("BSX_DOCKER_MODE") or "docker_mode" not in settings:
settings["setup_docker_mode"] = BSX_DOCKER_MODE
if os.getenv("BSX_LOCAL_TOR") or "local_tor" not in settings:
settings["setup_local_tor"] = BSX_LOCAL_TOR
# Add to settings only if manually set
if os.getenv("TOR_CONTROL_LISTEN_INTERFACE"):
settings["setup_tor_control_listen_interface"] = (
TOR_CONTROL_LISTEN_INTERFACE
)
if os.getenv("TORRC_PROXY_HOST"):
settings["setup_torrc_proxy_host"] = TORRC_PROXY_HOST
if os.getenv("TORRC_CONTROL_HOST"):
settings["setup_torrc_control_host"] = TORRC_CONTROL_HOST
if os.getenv("TORRC_DNS_HOST"):
settings["setup_torrc_dns_host"] = TORRC_DNS_HOST
with open(config_path, "w") as fp:
json.dump(settings, fp, indent=4)
def signal_handler(sig, frame): def signal_handler(sig, frame):
@ -2143,6 +2210,7 @@ def ensure_coin_valid(coin: str, test_disabled: bool = True) -> None:
def main(): def main():
global use_tor_proxy, with_coins_changed global use_tor_proxy, with_coins_changed
setTorrcVars()
data_dir = None data_dir = None
bin_dir = None bin_dir = None
port_offset = None port_offset = None
@ -2735,9 +2803,7 @@ def main():
settings, coin, tor_control_password, enable=True, extra_opts=extra_opts settings, coin, tor_control_password, enable=True, extra_opts=extra_opts
) )
with open(config_path, "w") as fp: save_config(config_path, settings)
json.dump(settings, fp, indent=4)
logger.info("Done.") logger.info("Done.")
return 0 return 0
@ -2754,9 +2820,7 @@ def main():
extra_opts=extra_opts, extra_opts=extra_opts,
) )
with open(config_path, "w") as fp: save_config(config_path, settings)
json.dump(settings, fp, indent=4)
logger.info("Done.") logger.info("Done.")
return 0 return 0
@ -2781,9 +2845,7 @@ def main():
if "manage_wallet_daemon" in coin_settings: if "manage_wallet_daemon" in coin_settings:
coin_settings["manage_wallet_daemon"] = False coin_settings["manage_wallet_daemon"] = False
with open(config_path, "w") as fp: save_config(config_path, settings)
json.dump(settings, fp, indent=4)
logger.info("Done.") logger.info("Done.")
return 0 return 0
@ -2805,8 +2867,7 @@ def main():
coin_settings["manage_daemon"] = True coin_settings["manage_daemon"] = True
if "manage_wallet_daemon" in coin_settings: if "manage_wallet_daemon" in coin_settings:
coin_settings["manage_wallet_daemon"] = True coin_settings["manage_wallet_daemon"] = True
with open(config_path, "w") as fp: save_config(config_path, settings)
json.dump(settings, fp, indent=4)
logger.info("Done.") logger.info("Done.")
return 0 return 0
exitWithError("{} is already in the settings file".format(add_coin)) exitWithError("{} is already in the settings file".format(add_coin))
@ -2821,7 +2882,6 @@ def main():
test_particl_encryption(data_dir, settings, chain, use_tor_proxy) test_particl_encryption(data_dir, settings, chain, use_tor_proxy)
settings["chainclients"][add_coin] = chainclients[add_coin] settings["chainclients"][add_coin] = chainclients[add_coin]
settings["use_tor_proxy"] = use_tor_proxy
if not no_cores: if not no_cores:
prepareCore(add_coin, known_coins[add_coin], settings, data_dir, extra_opts) prepareCore(add_coin, known_coins[add_coin], settings, data_dir, extra_opts)
@ -2843,8 +2903,7 @@ def main():
use_tor_proxy, use_tor_proxy,
) )
with open(config_path, "w") as fp: save_config(config_path, settings)
json.dump(settings, fp, indent=4)
logger.info(f"Done. Coin {add_coin} successfully added.") logger.info(f"Done. Coin {add_coin} successfully added.")
return 0 return 0
@ -2863,8 +2922,7 @@ def main():
if c not in settings["chainclients"]: if c not in settings["chainclients"]:
settings["chainclients"][c] = chainclients[c] settings["chainclients"][c] = chainclients[c]
elif upgrade_cores: elif upgrade_cores:
with open(config_path) as fs: settings = load_config(config_path)
settings = json.load(fs)
with_coins_start = with_coins with_coins_start = with_coins
if not with_coins_changed: if not with_coins_changed:
@ -2913,8 +2971,8 @@ def main():
# Run second loop to update, so all versions are logged together. # Run second loop to update, so all versions are logged together.
# Backup settings # Backup settings
old_config_path = config_path[:-5] + "_" + str(int(time.time())) + ".json" old_config_path = config_path[:-5] + "_" + str(int(time.time())) + ".json"
with open(old_config_path, "w") as fp: save_config(old_config_path, settings, add_options=False)
json.dump(settings, fp, indent=4)
for c in with_coins: for c in with_coins:
prepareCore(c, known_coins[c], settings, data_dir, extra_opts) prepareCore(c, known_coins[c], settings, data_dir, extra_opts)
current_coin_settings = chainclients[c] current_coin_settings = chainclients[c]
@ -2927,8 +2985,7 @@ def main():
settings["chainclients"][c][ settings["chainclients"][c][
"core_version_group" "core_version_group"
] = current_version_group ] = current_version_group
with open(config_path, "w") as fp: save_config(config_path, settings)
json.dump(settings, fp, indent=4)
logger.info("Done.") logger.info("Done.")
return 0 return 0
@ -2978,8 +3035,7 @@ def main():
for c in with_coins: for c in with_coins:
prepareDataDir(c, settings, chain, particl_wallet_mnemonic, extra_opts) prepareDataDir(c, settings, chain, particl_wallet_mnemonic, extra_opts)
with open(config_path, "w") as fp: save_config(config_path, settings)
json.dump(settings, fp, indent=4)
if particl_wallet_mnemonic == "none": if particl_wallet_mnemonic == "none":
logger.info("Done.") logger.info("Done.")

View file

@ -197,7 +197,7 @@ class Test(unittest.TestCase):
logger.handlers = [] logger.handlers = []
logger.setLevel(logging.INFO) # DEBUG shows many messages from requests.post logger.setLevel(logging.INFO) # DEBUG shows many messages from requests.post
formatter = logging.Formatter("%(asctime)s %(levelname)s : %(message)s") formatter = logging.Formatter("%(asctime)s %(levelname)s : %(message)s")
stream_stdout = logging.StreamHandler() stream_stdout = logging.StreamHandler(sys.stdout)
stream_stdout.setFormatter(formatter) stream_stdout.setFormatter(formatter)
logger.addHandler(stream_stdout) logger.addHandler(stream_stdout)

View file

@ -6,30 +6,34 @@
# Distributed under the MIT software license, see the accompanying # Distributed under the MIT software license, see the accompanying
# file LICENSE or http://www.opensource.org/licenses/mit-license.php. # file LICENSE or http://www.opensource.org/licenses/mit-license.php.
import os import importlib
import sys
import json import json
import shutil
import logging import logging
import unittest
import threading
import multiprocessing import multiprocessing
import os
import shutil
import sys
import threading
import unittest
from io import StringIO from io import StringIO
from unittest.mock import patch from unittest.mock import patch
import basicswap.config as cfg import basicswap.config as cfg
from tests.basicswap.util import ( from tests.basicswap.util import (
make_boolean,
read_json_api, read_json_api,
waitForServer, waitForServer,
) )
TEST_DELETE_DIRS = make_boolean(os.getenv("TEST_DELETE_DIRS", True))
bin_path = os.path.expanduser(os.getenv("TEST_BIN_PATH", "")) bin_path = os.path.expanduser(os.getenv("TEST_BIN_PATH", ""))
test_base_path = os.path.expanduser(os.getenv("TEST_PREPARE_PATH", "~/test_basicswap")) test_base_path = os.path.expanduser(os.getenv("TEST_PREPARE_PATH", "~/test_basicswap"))
test_path_plain = os.path.join(test_base_path, "plain") test_path_plain = os.path.join(test_base_path, "plain")
test_path_encrypted = os.path.join(test_base_path, "encrypted") test_path_encrypted = os.path.join(test_base_path, "encrypted")
test_path_encrypt = os.path.join(test_base_path, "encrypt") test_path_encrypt = os.path.join(test_base_path, "encrypt")
delay_event = threading.Event() delay_event = threading.Event()
logger = logging.getLogger() logger = logging.getLogger()
logger.level = logging.DEBUG logger.level = logging.DEBUG
@ -59,14 +63,28 @@ def start_run(args, env_pairs=[]):
class Test(unittest.TestCase): class Test(unittest.TestCase):
@classmethod
def setUpClass(cls):
super(Test, cls).setUpClass()
# Reset env vars
reset_vars = ["BSX_ALLOW_ENV_OVERRIDE", "BSX_DOCKER_MODE", "BSX_LOCAL_TOR"]
for var_name in reset_vars:
if var_name in os.environ:
del os.environ[var_name]
@classmethod @classmethod
def tearDownClass(self): def tearDownClass(self):
try: try:
for test_dir in (test_path_plain, test_path_encrypted, test_path_encrypt): if TEST_DELETE_DIRS:
if os.path.exists(test_dir): for test_dir in (
shutil.rmtree(test_dir) test_path_plain,
test_path_encrypted,
test_path_encrypt,
):
if os.path.exists(test_dir):
shutil.rmtree(test_dir)
except Exception as ex: except Exception as ex:
logger.warning("tearDownClass %s", str(ex)) logger.warning(f"tearDownClass {ex}")
super(Test, self).tearDownClass() super(Test, self).tearDownClass()
def test_plain(self): def test_plain(self):
@ -146,6 +164,7 @@ class Test(unittest.TestCase):
self.assertTrue( self.assertTrue(
settings["chainclients"]["bitcoin"]["connection_type"] == "rpc" settings["chainclients"]["bitcoin"]["connection_type"] == "rpc"
) )
assert settings.get("use_tor", False) is False
logging.info("notorproxy") logging.info("notorproxy")
testargs = [ testargs = [
@ -165,8 +184,89 @@ class Test(unittest.TestCase):
"--usetorproxy and --notorproxy together" in fake_stderr.getvalue() "--usetorproxy and --notorproxy together" in fake_stderr.getvalue()
) )
logger.info("Test persistent setup config.")
with open(config_path) as fs:
settings = json.load(fs)
assert settings.get("use_tor", False) is False
assert settings["setup_docker_mode"] is False
assert settings["setup_local_tor"] is False
assert "setup_tor_control_listen_interface" not in settings
assert "setup_torrc_dns_host" not in settings
os.environ["BSX_LOCAL_TOR"] = "true"
# Reimport to reset globals and set env
importlib.reload(prepareSystem)
testargs = [
"basicswap-prepare",
"-datadir=" + test_path_plain,
"--enabletor",
]
with patch("sys.stderr", new=StringIO()) as fake_stderr:
with patch.object(sys, "argv", testargs):
with self.assertRaises(SystemExit) as cm:
prepareSystem.main()
self.assertEqual(cm.exception.code, 1)
self.assertTrue(
"Env var BSX_LOCAL_TOR differs from saved config"
in fake_stderr.getvalue()
)
os.environ["BSX_ALLOW_ENV_OVERRIDE"] = "true"
os.environ["TOR_CONTROL_LISTEN_INTERFACE"] = "127.1.1.1"
importlib.reload(prepareSystem)
with patch.object(sys, "argv", testargs):
prepareSystem.main()
with open(config_path) as fs:
settings = json.load(fs)
assert settings.get("use_tor", False) is True
assert settings["setup_docker_mode"] is False
assert settings["setup_local_tor"] is True
assert settings["setup_tor_control_listen_interface"] == "127.1.1.1"
assert "setup_torrc_dns_host" not in settings
particl_config_path = os.path.join(
test_path_plain, "particl", "particl.conf"
)
with open(particl_config_path) as fs:
particl_conf = fs.read()
assert "bind=127.1.1.1:" in particl_conf
testargs = [
"basicswap-prepare",
"-datadir=" + test_path_plain,
"-disablecoin=bitcoin",
]
with patch.object(sys, "argv", testargs):
prepareSystem.main()
os.environ["TOR_PROXY_HOST"] = "127.2.2.2"
del os.environ["BSX_ALLOW_ENV_OVERRIDE"]
importlib.reload(prepareSystem)
testargs = [
"basicswap-prepare",
"-datadir=" + test_path_plain,
"-addcoin=bitcoin",
"-notorproxy", # Disable TOR connection check
]
with patch("sys.stderr", new=StringIO()) as fake_stderr:
with patch.object(sys, "argv", testargs):
with self.assertRaises(SystemExit) as cm:
prepareSystem.main()
self.assertEqual(cm.exception.code, 1)
self.assertTrue(
"Env var TOR_PROXY_HOST differs from saved config"
in fake_stderr.getvalue()
)
finally: finally:
del prepareSystem del prepareSystem # Does not cause next import to refresh globals and env (tested v3.13)
def test_encrypted(self): def test_encrypted(self):
if os.path.exists(test_path_encrypted): if os.path.exists(test_path_encrypted):

View file

@ -12,6 +12,7 @@ import os
import random import random
import shutil import shutil
import signal import signal
import sys
import threading import threading
import time import time
import traceback import traceback
@ -350,7 +351,7 @@ class BaseTest(unittest.TestCase):
logger.handlers = [] logger.handlers = []
logger.setLevel(logging.INFO) # DEBUG shows many messages from requests.post logger.setLevel(logging.INFO) # DEBUG shows many messages from requests.post
formatter = logging.Formatter("%(asctime)s %(levelname)s : %(message)s") formatter = logging.Formatter("%(asctime)s %(levelname)s : %(message)s")
stream_stdout = logging.StreamHandler() stream_stdout = logging.StreamHandler(sys.stdout)
stream_stdout.setFormatter(formatter) stream_stdout.setFormatter(formatter)
logger.addHandler(stream_stdout) logger.addHandler(stream_stdout)

View file

@ -19,7 +19,11 @@ REQUIRED_SETTINGS = {
} }
def make_boolean(s): def make_boolean(s) -> bool:
if isinstance(s, bool):
return s
if isinstance(s, int):
return False if s == 0 else True
return s.lower() in ["1", "true"] return s.lower() in ["1", "true"]