diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 47fc544..74d1fb7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,6 +9,9 @@ concurrency: env: BIN_DIR: /tmp/cached_bin TEST_RELOAD_PATH: /tmp/test_basicswap + BSX_SELENIUM_DRIVER: firefox-ci + XMR_RPC_USER: xmr_user + XMR_RPC_PWD: xmr_pwd jobs: ci: @@ -24,8 +27,18 @@ jobs: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | + if [ $(dpkg-query -W -f='${Status}' firefox 2>/dev/null | grep -c "ok installed") -eq 0 ]; then + install -d -m 0755 /etc/apt/keyrings + wget -q https://packages.mozilla.org/apt/repo-signing-key.gpg -O- | sudo tee /etc/apt/keyrings/packages.mozilla.org.asc > /dev/null + echo "deb [signed-by=/etc/apt/keyrings/packages.mozilla.org.asc] https://packages.mozilla.org/apt mozilla main" | sudo tee -a /etc/apt/sources.list.d/mozilla.list > /dev/null + echo "Package: *" | sudo tee /etc/apt/preferences.d/mozilla + echo "Pin: origin packages.mozilla.org" | sudo tee -a /etc/apt/preferences.d/mozilla + echo "Pin-Priority: 1000" | sudo tee -a /etc/apt/preferences.d/mozilla + sudo apt-get update + sudo apt-get install -y firefox + fi python -m pip install --upgrade pip - pip install flake8 codespell pytest + pip install -e .[dev] pip install -r requirements.txt --require-hashes - name: Install run: | @@ -33,13 +46,16 @@ jobs: # Print the core versions to a file for caching basicswap-prepare --version --withcoins=bitcoin | tail -n +2 > core_versions.txt cat core_versions.txt - - name: Running flake8 + - name: Run flake8 run: | flake8 --ignore=E203,E501,W503 --exclude=basicswap/contrib,basicswap/interface/contrib,.eggs,.tox,bin/install_certifi.py - - name: Running codespell + - name: Run codespell run: | codespell --check-filenames --disable-colors --quiet-level=7 --ignore-words=tests/lint/spelling.ignore-words.txt -S .git,.eggs,.tox,pgp,*.pyc,*basicswap/contrib,*basicswap/interface/contrib,*mnemonics.py,bin/install_certifi.py,*basicswap/static - - name: Running test_other + - name: Run black + run: | + black --check --diff --exclude="contrib" . + - name: Run test_other run: | pytest tests/basicswap/test_other.py - name: Cache coin cores @@ -55,17 +71,33 @@ jobs: name: Running basicswap-prepare run: | basicswap-prepare --bindir="$BIN_DIR" --preparebinonly --withcoins=particl,bitcoin,monero - - name: Running test_xmr + - name: Run test_xmr run: | export PYTHONPATH=$(pwd) export PARTICL_BINDIR="$BIN_DIR/particl" export BITCOIN_BINDIR="$BIN_DIR/bitcoin" export XMR_BINDIR="$BIN_DIR/monero" pytest tests/basicswap/test_btc_xmr.py::TestBTC -k "test_003_api or test_02_a_leader_recover_a_lock_tx" - - name: Running test_encrypted_xmr_reload + - name: Run test_encrypted_xmr_reload run: | export PYTHONPATH=$(pwd) export TEST_PATH=${TEST_RELOAD_PATH} mkdir -p ${TEST_PATH}/bin cp -r $BIN_DIR/* ${TEST_PATH}/bin/ pytest tests/basicswap/extended/test_encrypted_xmr_reload.py + - name: Run selenium tests + run: | + export TEST_PATH=/tmp/test_persistent + mkdir -p ${TEST_PATH}/bin + cp -r $BIN_DIR/* ${TEST_PATH}/bin/ + export PYTHONPATH=$(pwd) + python tests/basicswap/extended/test_xmr_persistent.py > /tmp/log.txt 2>&1 & PID=$! + echo "Starting test_xmr_persistent, PID $PID" + until curl -s -f -o /dev/null "http://localhost:12701/json/coins" + do + tail -n 1 /tmp/log.txt + sleep 2 + done + echo "Running test_settings.py" + python tests/basicswap/selenium/test_settings.py + kill -9 $PID diff --git a/basicswap/bin/run.py b/basicswap/bin/run.py index d23c73b..ddcfbea 100755 --- a/basicswap/bin/run.py +++ b/basicswap/bin/run.py @@ -271,7 +271,9 @@ def getCoreBinArgs(coin_id: int, coin_settings, prepare=False, use_tor_proxy=Fal return extra_args -def runClient(fp, data_dir, chain, start_only_coins): +def runClient( + fp, data_dir: str, chain: str, start_only_coins: bool, log_prefix: str = "BasicSwap" +): global swap_client, logger daemons = [] pids = [] @@ -296,7 +298,7 @@ def runClient(fp, data_dir, chain, start_only_coins): with open(settings_path) as fs: settings = json.load(fs) - swap_client = BasicSwap(fp, data_dir, settings, chain) + swap_client = BasicSwap(fp, data_dir, settings, chain, log_name=log_prefix) logger = swap_client.log if os.path.exists(pids_path): @@ -434,7 +436,7 @@ def runClient(fp, data_dir, chain, start_only_coins): ) ) pid = daemons[-1].handle.pid - swap_client.log.info("Started {} {}".format(filename, pid)) + swap_client.log.info(f"Started {filename} {pid}") continue # /decred @@ -529,7 +531,7 @@ def runClient(fp, data_dir, chain, start_only_coins): closed_pids = [] for d in daemons: - swap_client.log.info("Interrupting {}".format(d.handle.pid)) + swap_client.log.info(f"Interrupting {d.handle.pid}") try: d.handle.send_signal( signal.CTRL_C_EVENT if os.name == "nt" else signal.SIGINT @@ -561,7 +563,9 @@ def runClient(fp, data_dir, chain, start_only_coins): def printVersion(): - logger.info("Basicswap version: %s", __version__) + logger.info( + f"Basicswap version: {__version__}", + ) def printHelp(): @@ -569,9 +573,7 @@ def printHelp(): print("\n--help, -h Print help.") print("--version, -v Print version.") print( - "--datadir=PATH Path to basicswap data directory, default:{}.".format( - cfg.BASICSWAP_DATADIR - ) + f"--datadir=PATH Path to basicswap data directory, default:{cfg.BASICSWAP_DATADIR}." ) print("--mainnet Run in mainnet mode.") print("--testnet Run in testnet mode.") @@ -579,16 +581,18 @@ def printHelp(): print( "--startonlycoin Only start the provides coin daemon/s, use this if a chain requires extra processing." ) + print("--logprefix Specify log prefix.") def main(): data_dir = None chain = "mainnet" start_only_coins = set() + log_prefix: str = "BasicSwap" for v in sys.argv[1:]: if len(v) < 2 or v[0] != "-": - logger.warning("Unknown argument %s", v) + logger.warning(f"Unknown argument {v}") continue s = v.split("=") @@ -613,6 +617,9 @@ def main(): if name == "datadir": data_dir = os.path.expanduser(s[1]) continue + if name == "logprefix": + log_prefix = s[1] + continue if name == "startonlycoin": for coin in [s.lower() for s in s[1].split(",")]: if is_known_coin(coin) is False: @@ -620,7 +627,7 @@ def main(): start_only_coins.add(coin) continue - logger.warning("Unknown argument %s", v) + logger.warning(f"Unknown argument {v}") if os.name == "nt": logger.warning( @@ -629,8 +636,8 @@ def main(): if data_dir is None: data_dir = os.path.join(os.path.expanduser(cfg.BASICSWAP_DATADIR)) - logger.info("Using datadir: %s", data_dir) - logger.info("Chain: %s", chain) + logger.info(f"Using datadir: {data_dir}") + logger.info(f"Chain: {chain}") if not os.path.exists(data_dir): os.makedirs(data_dir) @@ -639,7 +646,7 @@ def main(): logger.info( os.path.basename(sys.argv[0]) + ", version: " + __version__ + "\n\n" ) - runClient(fp, data_dir, chain, start_only_coins) + runClient(fp, data_dir, chain, start_only_coins, log_prefix) print("Done.") return swap_client.fail_code if swap_client is not None else 0 diff --git a/basicswap/js_server.py b/basicswap/js_server.py index 5e799fd..35f89fc 100644 --- a/basicswap/js_server.py +++ b/basicswap/js_server.py @@ -327,9 +327,7 @@ def formatBids(swap_client, bids, filters) -> bytes: amount_to = None if ci_to: - amount_to = ci_to.format_amount( - (b[4] * b[10]) // ci_from.COIN() - ) + amount_to = ci_to.format_amount((b[4] * b[10]) // ci_from.COIN()) bid_data = { "bid_id": b[2].hex(), @@ -343,14 +341,13 @@ def formatBids(swap_client, bids, filters) -> bytes: "bid_rate": swap_client.ci(b[14]).format_amount(b[10]), "bid_state": strBidState(b[5]), "addr_from": b[11], - "addr_to": offer.addr_to if offer else None + "addr_to": offer.addr_to if offer else None, } if with_extra_info: - bid_data.update({ - "tx_state_a": strTxState(b[7]), - "tx_state_b": strTxState(b[8]) - }) + bid_data.update( + {"tx_state_a": strTxState(b[7]), "tx_state_b": strTxState(b[8])} + ) rv.append(bid_data) return bytes(json.dumps(rv), "UTF-8") @@ -983,20 +980,17 @@ def js_readurl(self, url_split, post_string, is_json) -> bytes: def js_active(self, url_split, post_string, is_json) -> bytes: swap_client = self.server.swap_client swap_client.checkSystemStatus() - filters = { - "sort_by": "created_at", - "sort_dir": "desc" - } + filters = {"sort_by": "created_at", "sort_dir": "desc"} EXCLUDED_STATES = [ - 'Completed', - 'Expired', - 'Timed-out', - 'Abandoned', - 'Failed, refunded', - 'Failed, swiped', - 'Failed', - 'Error', - 'received' + "Completed", + "Expired", + "Timed-out", + "Abandoned", + "Failed, refunded", + "Failed, swiped", + "Failed", + "Error", + "received", ] all_bids = [] @@ -1018,8 +1012,8 @@ def js_active(self, url_split, post_string, is_json) -> bytes: "offer_id": bid[3].hex(), "created_at": bid[0], "bid_state": bid_state, - "tx_state_a": tx_state_a if tx_state_a else 'None', - "tx_state_b": tx_state_b if tx_state_b else 'None', + "tx_state_a": tx_state_a if tx_state_a else "None", + "tx_state_b": tx_state_b if tx_state_b else "None", "coin_from": swap_client.ci(bid[9]).coin_name(), "coin_to": swap_client.ci(offer.coin_to).coin_name(), "amount_from": swap_client.ci(bid[9]).format_amount(bid[4]), @@ -1029,9 +1023,9 @@ def js_active(self, url_split, post_string, is_json) -> bytes: "addr_from": bid[11], "status": { "main": bid_state, - "initial_tx": tx_state_a if tx_state_a else 'None', - "payment_tx": tx_state_b if tx_state_b else 'None' - } + "initial_tx": tx_state_a if tx_state_a else "None", + "payment_tx": tx_state_b if tx_state_b else "None", + }, } all_bids.append(swap_data) except Exception: diff --git a/basicswap/network.py b/basicswap/network.py index 0208c6d..7e424a3 100644 --- a/basicswap/network.py +++ b/basicswap/network.py @@ -6,17 +6,17 @@ # file LICENSE or http://www.opensource.org/licenses/mit-license.php. """ - Message 2 bytes msg_class, 4 bytes length, [ 2 bytes msg_type, payload ] +Message 2 bytes msg_class, 4 bytes length, [ 2 bytes msg_type, payload ] - Handshake procedure: - node0 connecting to node1 - node0 send_handshake - node1 process_handshake - node1 send_ping - With a version field - node0 recv_ping - Both nodes are initialised +Handshake procedure: + node0 connecting to node1 + node0 send_handshake + node1 process_handshake + node1 send_ping - With a version field + node0 recv_ping + Both nodes are initialised - XChaCha20_Poly1305 mac is 16bytes +XChaCha20_Poly1305 mac is 16bytes """ import time diff --git a/basicswap/ui/page_bids.py b/basicswap/ui/page_bids.py index d2de338..fb1dea8 100644 --- a/basicswap/ui/page_bids.py +++ b/basicswap/ui/page_bids.py @@ -151,7 +151,9 @@ def page_bid(self, url_split, post_string): ) -def page_bids(self, url_split, post_string, sent=False, available=False, received=False): +def page_bids( + self, url_split, post_string, sent=False, available=False, received=False +): server = self.server swap_client = server.swap_client swap_client.checkSystemStatus() diff --git a/basicswap/ui/util.py b/basicswap/ui/util.py index e4c36ac..96e3b13 100644 --- a/basicswap/ui/util.py +++ b/basicswap/ui/util.py @@ -145,7 +145,7 @@ def get_data_with_pagination(data, filters): offset = filters.get("offset", 0) limit = filters.get("limit", PAGE_LIMIT) - return data[offset:offset + limit] + return data[offset : offset + limit] def getTxIdHex(bid, tx_type, suffix): diff --git a/pyproject.toml b/pyproject.toml index d21c9e1..dbcc09f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -35,7 +35,7 @@ dev = [ "pip-tools", "pytest", "ruff", - "black", + "black==24.10.0", "selenium", ] diff --git a/tests/basicswap/common_xmr.py b/tests/basicswap/common_xmr.py index 97312f1..808ac7b 100644 --- a/tests/basicswap/common_xmr.py +++ b/tests/basicswap/common_xmr.py @@ -572,7 +572,12 @@ class XmrTestBase(TestBase): def run_thread(self, client_id): client_path = os.path.join(TEST_PATH, "client{}".format(client_id)) - testargs = ["basicswap-run", "-datadir=" + client_path, "-regtest"] + testargs = [ + "basicswap-run", + "-datadir=" + client_path, + "-regtest", + f"-logprefix=BSX{client_id}", + ] with patch.object(sys, "argv", testargs): runSystem.main() diff --git a/tests/basicswap/extended/test_encrypted_xmr_reload.py b/tests/basicswap/extended/test_encrypted_xmr_reload.py index 03f3715..445a4f8 100644 --- a/tests/basicswap/extended/test_encrypted_xmr_reload.py +++ b/tests/basicswap/extended/test_encrypted_xmr_reload.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Copyright (c) 2020-2023 tecnovert -# Copyright (c) 2024 The Basicswap developers +# Copyright (c) 2024-2025 The Basicswap developers # Distributed under the MIT software license, see the accompanying # file LICENSE or http://www.opensource.org/licenses/mit-license.php. diff --git a/tests/basicswap/extended/test_wallet_init.py b/tests/basicswap/extended/test_wallet_init.py index fa8fd53..a8432a7 100644 --- a/tests/basicswap/extended/test_wallet_init.py +++ b/tests/basicswap/extended/test_wallet_init.py @@ -66,7 +66,12 @@ class Test(unittest.TestCase): def run_thread(self, client_id): client_path = os.path.join(TEST_PATH, "client{}".format(client_id)) - testargs = ["basicswap-run", "-datadir=" + client_path, "-regtest"] + testargs = [ + "basicswap-run", + "-datadir=" + client_path, + "-regtest", + f"-logprefix=BSX{client_id}", + ] with patch.object(sys, "argv", testargs): runSystem.main() diff --git a/tests/basicswap/extended/test_wallet_restore.py b/tests/basicswap/extended/test_wallet_restore.py index 67a688d..af507bb 100644 --- a/tests/basicswap/extended/test_wallet_restore.py +++ b/tests/basicswap/extended/test_wallet_restore.py @@ -114,7 +114,12 @@ class Test(TestBase): def run_thread(self, client_id): client_path = os.path.join(TEST_PATH, "client{}".format(client_id)) - testargs = ["basicswap-run", "-datadir=" + client_path, "-regtest"] + testargs = [ + "basicswap-run", + "-datadir=" + client_path, + "-regtest", + f"-logprefix=BSX{client_id}", + ] with patch.object(sys, "argv", testargs): runSystem.main() diff --git a/tests/basicswap/extended/test_xmr_persistent.py b/tests/basicswap/extended/test_xmr_persistent.py index ee63497..b99cb9d 100644 --- a/tests/basicswap/extended/test_xmr_persistent.py +++ b/tests/basicswap/extended/test_xmr_persistent.py @@ -7,7 +7,6 @@ # file LICENSE or http://www.opensource.org/licenses/mit-license.php. """ -export RESET_TEST=true export TEST_PATH=/tmp/test_persistent mkdir -p ${TEST_PATH}/bin cp -r ~/tmp/basicswap_bin/* ${TEST_PATH}/bin @@ -20,6 +19,10 @@ python tests/basicswap/extended/test_xmr_persistent.py # Copy coin releases to permanent storage for faster subsequent startups cp -r ${TEST_PATH}/bin/ ~/tmp/basicswap_bin/ + +# Continue existing chains with +export RESET_TEST=false + """ import json @@ -62,7 +65,7 @@ from basicswap.interface.dcr.rpc import callrpc as callrpc_dcr import basicswap.bin.run as runSystem test_path = os.path.expanduser(os.getenv("TEST_PATH", "/tmp/test_persistent")) -RESET_TEST = make_boolean(os.getenv("RESET_TEST", "false")) +RESET_TEST = make_boolean(os.getenv("RESET_TEST", "true")) PORT_OFS = int(os.getenv("PORT_OFS", 1)) UI_PORT = 12700 + PORT_OFS @@ -225,7 +228,12 @@ def signal_handler(self, sig, frame): def run_thread(self, client_id): client_path = os.path.join(test_path, "client{}".format(client_id)) - testargs = ["basicswap-run", "-datadir=" + client_path, "-regtest"] + testargs = [ + "basicswap-run", + "-datadir=" + client_path, + "-regtest", + f"-logprefix=BSX{client_id}", + ] with patch.object(sys, "argv", testargs): runSystem.main() @@ -399,7 +407,7 @@ def start_processes(self): # Wait for height, or sequencelock is thrown off by genesis blocktime num_blocks = 3 - logging.info("Waiting for Particl chain height %d", num_blocks) + logging.info(f"Waiting for Particl chain height {num_blocks}") for i in range(60): if self.delay_event.is_set(): raise ValueError("Test stopped.") @@ -448,7 +456,7 @@ class BaseTestWithPrepare(unittest.TestCase): if os.path.exists(test_path) and not RESET_TEST: logging.info(f"Continuing with existing directory: {test_path}") else: - logging.info("Preparing %d nodes.", NUM_NODES) + logging.info(f"Preparing {NUM_NODES} nodes.") prepare_nodes( NUM_NODES, TEST_COINS_LIST, diff --git a/tests/basicswap/selenium/util.py b/tests/basicswap/selenium/util.py index a166a25..74708ec 100644 --- a/tests/basicswap/selenium/util.py +++ b/tests/basicswap/selenium/util.py @@ -2,7 +2,7 @@ # -*- coding: utf-8 -*- # Copyright (c) 2023 tecnovert -# Copyright (c) 2024 The Basicswap developers +# Copyright (c) 2024-2025 The Basicswap developers # Distributed under the MIT software license, see the accompanying # file LICENSE or http://www.opensource.org/licenses/mit-license.php. @@ -21,7 +21,17 @@ def get_driver(): if BSX_SELENIUM_DRIVER == "firefox": from selenium.webdriver import Firefox, FirefoxOptions - driver = Firefox(options=FirefoxOptions()) + options = FirefoxOptions() + driver = Firefox(options=options) + elif BSX_SELENIUM_DRIVER == "firefox-ci": + from selenium.webdriver import Firefox, FirefoxOptions + + options = FirefoxOptions() + options.headless = True + options.add_argument("start-maximized") + options.add_argument("--headless") + options.add_argument("--no-sandbox") + driver = Firefox(options=options) elif BSX_SELENIUM_DRIVER == "chrome": from selenium.webdriver import Chrome, ChromeOptions @@ -32,7 +42,6 @@ def get_driver(): driver = Safari(options=SafariOptions()) else: raise ValueError("Unknown driver " + BSX_SELENIUM_DRIVER) - return driver diff --git a/tests/basicswap/test_reload.py b/tests/basicswap/test_reload.py index a4a7a50..2807530 100644 --- a/tests/basicswap/test_reload.py +++ b/tests/basicswap/test_reload.py @@ -80,7 +80,12 @@ class Test(unittest.TestCase): def run_thread(self, client_id): client_path = os.path.join(TEST_PATH, "client{}".format(client_id)) - testargs = ["basicswap-run", "-datadir=" + client_path, "-regtest"] + testargs = [ + "basicswap-run", + "-datadir=" + client_path, + "-regtest", + f"-logprefix=BSX{client_id}", + ] with patch.object(sys, "argv", testargs): runSystem.main() diff --git a/tests/basicswap/test_xmr.py b/tests/basicswap/test_xmr.py index f85f8f5..0b80682 100644 --- a/tests/basicswap/test_xmr.py +++ b/tests/basicswap/test_xmr.py @@ -660,7 +660,7 @@ class BaseTest(unittest.TestCase): basicswap_dir, settings, "regtest", - log_name="BasicSwap{}".format(i), + log_name=f"BasicSwap{i}", ) cls.swap_clients.append(sc) sc.setDaemonPID(Coins.BTC, cls.btc_daemons[i].handle.pid)