2019-11-25 18:02:30 +01:00
|
|
|
import atexit
|
2022-02-11 14:54:30 +01:00
|
|
|
import code
|
2019-12-12 15:35:21 +01:00
|
|
|
import json
|
2019-12-18 17:50:52 +01:00
|
|
|
import logging
|
2019-12-12 15:35:21 +01:00
|
|
|
import os
|
2023-01-31 11:06:29 +01:00
|
|
|
import shutil
|
2022-02-11 14:54:30 +01:00
|
|
|
import signal
|
|
|
|
|
import sys
|
2019-11-25 18:02:30 +01:00
|
|
|
import tempfile
|
2022-02-11 14:54:30 +01:00
|
|
|
import traceback
|
2019-11-25 18:02:30 +01:00
|
|
|
|
2021-05-20 18:26:10 +03:00
|
|
|
import pytest
|
2023-01-31 11:06:29 +01:00
|
|
|
|
2022-02-11 14:54:30 +01:00
|
|
|
from cryptoadvance.specter.config import TestConfig
|
2021-05-20 18:26:10 +03:00
|
|
|
from cryptoadvance.specter.managers.device_manager import DeviceManager
|
2022-11-14 21:26:32 +01:00
|
|
|
from cryptoadvance.specter.managers.node_manager import NodeManager
|
2022-01-13 04:47:28 -06:00
|
|
|
from cryptoadvance.specter.managers.user_manager import UserManager
|
2023-01-31 11:06:29 +01:00
|
|
|
from cryptoadvance.specter.node import Node
|
2021-05-20 18:26:10 +03:00
|
|
|
from cryptoadvance.specter.process_controller.bitcoind_controller import (
|
2020-09-30 11:42:30 +02:00
|
|
|
BitcoindPlainController,
|
|
|
|
|
)
|
2021-06-16 15:33:35 +02:00
|
|
|
from cryptoadvance.specter.process_controller.elementsd_controller import (
|
|
|
|
|
ElementsPlainController,
|
|
|
|
|
)
|
2022-01-13 04:47:28 -06:00
|
|
|
from cryptoadvance.specter.server import SpecterFlask, create_app, init_app
|
2021-05-20 18:26:10 +03:00
|
|
|
from cryptoadvance.specter.specter import Specter
|
2022-11-14 21:26:32 +01:00
|
|
|
from cryptoadvance.specter.specter_error import (
|
|
|
|
|
BrokenCoreConnectionException,
|
|
|
|
|
SpecterError,
|
|
|
|
|
handle_exception,
|
|
|
|
|
)
|
2021-11-11 08:25:00 -06:00
|
|
|
from cryptoadvance.specter.user import User, hash_password
|
2021-09-19 22:33:13 +02:00
|
|
|
from cryptoadvance.specter.util.common import str2bool
|
2021-10-13 09:05:26 +02:00
|
|
|
from cryptoadvance.specter.util.shell import which
|
2022-02-11 14:54:30 +01:00
|
|
|
from cryptoadvance.specter.util.wallet_importer import WalletImporter
|
2019-12-19 15:01:07 +01:00
|
|
|
|
2021-10-08 19:16:59 +02:00
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
2022-05-05 18:35:56 +02:00
|
|
|
pytest_plugins = [
|
2022-11-22 21:07:36 +01:00
|
|
|
"conftest_visibility",
|
2022-05-05 18:35:56 +02:00
|
|
|
"fix_ghost_machine",
|
|
|
|
|
"fix_keys_and_seeds",
|
|
|
|
|
"fix_devices_and_wallets",
|
|
|
|
|
"fix_testnet",
|
|
|
|
|
]
|
2021-01-30 11:36:59 +01:00
|
|
|
|
2021-09-19 22:33:13 +02:00
|
|
|
# This is from https://stackoverflow.com/questions/132058/showing-the-stack-trace-from-a-running-python-application
|
|
|
|
|
# it enables stopping a hanging test via sending the pytest-process a SIGUSR2 (12)
|
|
|
|
|
# kill 12 pid-of-pytest
|
|
|
|
|
# In the article they claim to open a debug-console which didn't work for me but at least
|
|
|
|
|
# you get a stacktrace in the output.
|
|
|
|
|
def debug(sig, frame):
|
|
|
|
|
"""Interrupt running process, and provide a python prompt for
|
|
|
|
|
interactive debugging."""
|
|
|
|
|
d = {"_frame": frame} # Allow access to frame object.
|
|
|
|
|
d.update(frame.f_globals) # Unless shadowed by global
|
|
|
|
|
d.update(frame.f_locals)
|
|
|
|
|
|
|
|
|
|
i = code.InteractiveConsole(d)
|
|
|
|
|
message = "Signal received : entering python shell.\nTraceback:\n"
|
|
|
|
|
message += "".join(traceback.format_stack(frame))
|
|
|
|
|
i.interact(message)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def listen():
|
|
|
|
|
signal.signal(signal.SIGUSR2, debug) # Register handler
|
|
|
|
|
|
2019-12-12 15:35:21 +01:00
|
|
|
|
2019-12-05 17:42:15 +01:00
|
|
|
def pytest_addoption(parser):
|
2020-09-30 11:42:30 +02:00
|
|
|
"""Internally called to add options to pytest
|
|
|
|
|
see pytest_generate_tests(metafunc) on how to check that
|
2021-09-19 22:33:13 +02:00
|
|
|
Also used to register the SIGUSR2 (12) as decribed in conftest.py
|
2020-09-30 11:42:30 +02:00
|
|
|
"""
|
|
|
|
|
parser.addoption(
|
|
|
|
|
"--bitcoind-version",
|
|
|
|
|
action="store",
|
2020-11-26 20:25:21 +01:00
|
|
|
default="v0.20.1",
|
2021-05-20 18:26:10 +03:00
|
|
|
help="Version of bitcoind (something which works with git checkout ...)",
|
|
|
|
|
)
|
2021-09-19 22:33:13 +02:00
|
|
|
parser.addoption(
|
|
|
|
|
"--bitcoind-log-stdout",
|
|
|
|
|
action="store",
|
|
|
|
|
default=False,
|
|
|
|
|
help="Whether bitcoind should log to stdout (default:False)",
|
|
|
|
|
)
|
2021-05-20 18:26:10 +03:00
|
|
|
parser.addoption(
|
|
|
|
|
"--elementsd-version",
|
|
|
|
|
action="store",
|
|
|
|
|
default="master",
|
|
|
|
|
help="Version of elementsd (something which works with git checkout ...)",
|
2020-09-30 11:42:30 +02:00
|
|
|
)
|
2026-05-22 09:08:28 +02:00
|
|
|
parser.addoption(
|
|
|
|
|
"--run-jade-hardware",
|
|
|
|
|
action="store_true",
|
|
|
|
|
default=False,
|
|
|
|
|
help="Run tests marked jade_hardware (real Jade attached + operator).",
|
|
|
|
|
)
|
2021-09-19 22:33:13 +02:00
|
|
|
listen()
|
2020-09-30 11:42:30 +02:00
|
|
|
|
2019-12-05 17:42:15 +01:00
|
|
|
|
2026-05-22 09:08:28 +02:00
|
|
|
def pytest_collection_modifyitems(config, items):
|
|
|
|
|
if config.getoption("--run-jade-hardware"):
|
|
|
|
|
return
|
|
|
|
|
skip = pytest.mark.skip(reason="opt-in via --run-jade-hardware")
|
|
|
|
|
for item in items:
|
|
|
|
|
if item.get_closest_marker("jade_hardware") is not None:
|
|
|
|
|
item.add_marker(skip)
|
|
|
|
|
|
|
|
|
|
|
2019-12-05 17:42:15 +01:00
|
|
|
def pytest_generate_tests(metafunc):
|
2020-09-30 11:42:30 +02:00
|
|
|
# ToDo: use custom compiled version of bitcoind
|
2019-12-05 17:42:15 +01:00
|
|
|
# E.g. test again bitcoind version [currentRelease] + master-branch
|
|
|
|
|
if "docker" in metafunc.fixturenames:
|
|
|
|
|
if metafunc.config.getoption("docker"):
|
|
|
|
|
# That's a list because we could do both (see above) but currently that doesn't make sense in that context
|
2021-07-08 20:10:14 +02:00
|
|
|
metafunc.parametrize("docker", [True], scope="session")
|
2019-12-05 17:42:15 +01:00
|
|
|
else:
|
2021-07-08 20:10:14 +02:00
|
|
|
metafunc.parametrize("docker", [False], scope="session")
|
2019-12-05 17:42:15 +01:00
|
|
|
|
2020-09-30 11:42:30 +02:00
|
|
|
|
2022-06-20 12:44:58 +02:00
|
|
|
def instantiate_bitcoind_controller(
|
2023-01-16 18:33:59 +01:00
|
|
|
request, rpcport=18543, extra_args=[]
|
2022-06-20 12:44:58 +02:00
|
|
|
) -> BitcoindPlainController:
|
2020-09-30 11:42:30 +02:00
|
|
|
# logging.getLogger().setLevel(logging.DEBUG)
|
2020-06-14 14:16:35 +02:00
|
|
|
requested_version = request.config.getoption("--bitcoind-version")
|
2021-09-19 22:33:13 +02:00
|
|
|
log_stdout = str2bool(request.config.getoption("--bitcoind-log-stdout"))
|
2023-01-16 18:33:59 +01:00
|
|
|
if os.path.isfile("tests/bitcoin/src/bitcoind"):
|
|
|
|
|
bitcoind_controller = BitcoindPlainController(
|
|
|
|
|
bitcoind_path="tests/bitcoin/src/bitcoind", rpcport=rpcport
|
|
|
|
|
) # always prefer the self-compiled bitcoind if existing
|
|
|
|
|
elif os.path.isfile("tests/bitcoin/bin/bitcoind"):
|
|
|
|
|
bitcoind_controller = BitcoindPlainController(
|
|
|
|
|
bitcoind_path="tests/bitcoin/bin/bitcoind", rpcport=rpcport
|
|
|
|
|
) # next take the self-installed binary if existing
|
2019-12-05 17:42:15 +01:00
|
|
|
else:
|
2023-01-16 18:33:59 +01:00
|
|
|
bitcoind_controller = BitcoindPlainController(
|
|
|
|
|
rpcport=rpcport
|
|
|
|
|
) # Alternatively take the one on the path for now
|
2021-03-06 04:40:09 -06:00
|
|
|
bitcoind_controller.start_bitcoind(
|
2021-09-19 22:33:13 +02:00
|
|
|
cleanup_at_exit=True,
|
|
|
|
|
cleanup_hard=True,
|
|
|
|
|
extra_args=extra_args,
|
|
|
|
|
log_stdout=log_stdout,
|
2021-03-06 04:40:09 -06:00
|
|
|
)
|
2021-07-08 20:10:14 +02:00
|
|
|
assert not bitcoind_controller.datadir is None
|
2020-06-14 14:16:35 +02:00
|
|
|
running_version = bitcoind_controller.version()
|
|
|
|
|
requested_version = request.config.getoption("--bitcoind-version")
|
2021-05-20 18:26:10 +03:00
|
|
|
assert running_version == requested_version, (
|
2023-02-15 14:58:33 +01:00
|
|
|
"Please make sure that the Bitcoind-version (%s) matches with the version in pyproject.toml (%s)"
|
2021-05-20 18:26:10 +03:00
|
|
|
% (running_version, requested_version)
|
2020-09-30 11:42:30 +02:00
|
|
|
)
|
2020-01-09 15:18:49 +01:00
|
|
|
return bitcoind_controller
|
2019-12-19 15:01:07 +01:00
|
|
|
|
|
|
|
|
|
2021-06-16 15:33:35 +02:00
|
|
|
def instantiate_elementsd_controller(request, rpcport=18643, extra_args=[]):
|
|
|
|
|
if os.path.isfile("tests/elements/src/elementsd"):
|
|
|
|
|
elementsd_controller = ElementsPlainController(
|
|
|
|
|
elementsd_path="tests/elements/src/elementsd", rpcport=rpcport
|
|
|
|
|
) # always prefer the self-compiled bitcoind if existing
|
|
|
|
|
elif os.path.isfile("tests/elements/bin/elementsd"):
|
|
|
|
|
elementsd_controller = ElementsPlainController(
|
|
|
|
|
elementsd_path="tests/elements/bin/elementsd", rpcport=rpcport
|
|
|
|
|
) # next take the self-installed binary if existing
|
|
|
|
|
else:
|
|
|
|
|
elementsd_controller = ElementsPlainController(
|
|
|
|
|
rpcport=rpcport
|
|
|
|
|
) # Alternatively take the one on the path for now
|
|
|
|
|
elementsd_controller.start_elementsd(
|
|
|
|
|
cleanup_at_exit=True, cleanup_hard=True, extra_args=extra_args
|
|
|
|
|
)
|
2021-07-08 20:10:14 +02:00
|
|
|
assert not elementsd_controller.datadir is None
|
2021-06-16 15:33:35 +02:00
|
|
|
running_version = elementsd_controller.version()
|
|
|
|
|
requested_version = request.config.getoption("--elementsd-version")
|
|
|
|
|
assert running_version == requested_version, (
|
2023-02-15 14:58:33 +01:00
|
|
|
"Please make sure that the elementsd-version (%s) matches with the version in pyproject.toml (%s)"
|
2021-06-16 15:33:35 +02:00
|
|
|
% (running_version, requested_version)
|
|
|
|
|
)
|
|
|
|
|
return elementsd_controller
|
|
|
|
|
|
|
|
|
|
|
2021-10-13 09:05:26 +02:00
|
|
|
# Below this point are fixtures. Fixtures have a scope. Check about scopes here:
|
|
|
|
|
# https://docs.pytest.org/en/6.2.x/fixture.html#scope-sharing-fixtures-across-classes-modules-packages-or-session
|
|
|
|
|
# possible values: function, class, module, package or session.
|
|
|
|
|
# The nodes are of scope session. All else is the default (function)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture(scope="session")
|
|
|
|
|
def bitcoind_path():
|
|
|
|
|
if os.path.isfile("tests/bitcoin/src/bitcoind"):
|
|
|
|
|
return "tests/bitcoin/src/bitcoind"
|
|
|
|
|
elif os.path.isfile("tests/bitcoin/bin/bitcoind"):
|
|
|
|
|
return "tests/bitcoin/bin/bitcoind"
|
|
|
|
|
else:
|
|
|
|
|
return which("bitcoind")
|
|
|
|
|
|
|
|
|
|
|
2021-07-08 20:10:14 +02:00
|
|
|
@pytest.fixture(scope="session")
|
2023-01-16 18:33:59 +01:00
|
|
|
def bitcoin_regtest(request) -> BitcoindPlainController:
|
|
|
|
|
bitcoind_regtest = instantiate_bitcoind_controller(request, extra_args=None)
|
2021-07-08 20:10:14 +02:00
|
|
|
try:
|
|
|
|
|
assert bitcoind_regtest.get_rpc().test_connection()
|
|
|
|
|
assert not bitcoind_regtest.datadir is None
|
2022-11-14 21:26:32 +01:00
|
|
|
assert bitcoind_regtest.datadir is not ""
|
2021-07-08 20:10:14 +02:00
|
|
|
yield bitcoind_regtest
|
|
|
|
|
finally:
|
|
|
|
|
bitcoind_regtest.stop_bitcoind()
|
2021-06-16 15:33:35 +02:00
|
|
|
|
|
|
|
|
|
2022-06-20 12:44:58 +02:00
|
|
|
@pytest.fixture(scope="session")
|
2023-01-16 18:33:59 +01:00
|
|
|
def bitcoin_regtest2(request) -> BitcoindPlainController:
|
2022-06-20 12:44:58 +02:00
|
|
|
"""If a test needs two nodes ..."""
|
|
|
|
|
bitcoind_regtest = instantiate_bitcoind_controller(
|
2024-05-16 22:23:51 +02:00
|
|
|
request, rpcport=18545, extra_args=None
|
2022-06-20 12:44:58 +02:00
|
|
|
)
|
|
|
|
|
try:
|
|
|
|
|
assert bitcoind_regtest.get_rpc().test_connection()
|
|
|
|
|
assert not bitcoind_regtest.datadir is None
|
|
|
|
|
yield bitcoind_regtest
|
|
|
|
|
finally:
|
|
|
|
|
bitcoind_regtest.stop_bitcoind()
|
|
|
|
|
|
|
|
|
|
|
2022-11-14 21:26:32 +01:00
|
|
|
@pytest.fixture
|
|
|
|
|
def node(empty_data_folder, bitcoin_regtest):
|
|
|
|
|
nodes_folder = empty_data_folder + "/nodes"
|
|
|
|
|
if not os.path.isdir(nodes_folder):
|
|
|
|
|
os.makedirs(nodes_folder)
|
2022-11-23 17:50:25 +01:00
|
|
|
nm = NodeManager(data_folder=nodes_folder)
|
2023-01-31 11:06:29 +01:00
|
|
|
node: Node = nm.add_external_node(
|
2022-11-23 17:50:25 +01:00
|
|
|
"BTC",
|
|
|
|
|
"Standard node",
|
|
|
|
|
False,
|
|
|
|
|
bitcoin_regtest.datadir,
|
|
|
|
|
bitcoin_regtest.rpcconn.rpcuser,
|
|
|
|
|
bitcoin_regtest.rpcconn.rpcpassword,
|
|
|
|
|
bitcoin_regtest.rpcconn.rpcport,
|
|
|
|
|
bitcoin_regtest.rpcconn._ipaddress,
|
|
|
|
|
"http",
|
|
|
|
|
)
|
2023-01-31 11:06:29 +01:00
|
|
|
assert node.rpc.test_connection()
|
2022-11-23 17:50:25 +01:00
|
|
|
return node
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
|
def node_with_different_port(empty_data_folder, bitcoin_regtest):
|
|
|
|
|
nodes_folder = empty_data_folder + "/nodes"
|
|
|
|
|
if not os.path.isdir(nodes_folder):
|
|
|
|
|
os.makedirs(nodes_folder)
|
|
|
|
|
nm = NodeManager(data_folder=nodes_folder)
|
|
|
|
|
node = nm.add_external_node(
|
|
|
|
|
"BTC",
|
|
|
|
|
"Node with a different port",
|
|
|
|
|
False,
|
|
|
|
|
"",
|
|
|
|
|
bitcoin_regtest.rpcconn.rpcuser,
|
|
|
|
|
bitcoin_regtest.rpcconn.rpcpassword,
|
|
|
|
|
18333,
|
|
|
|
|
bitcoin_regtest.rpcconn._ipaddress,
|
|
|
|
|
"http",
|
2022-11-14 21:26:32 +01:00
|
|
|
)
|
|
|
|
|
return node
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
|
def node_with_empty_datadir(empty_data_folder, bitcoin_regtest):
|
|
|
|
|
nodes_folder = empty_data_folder + "/nodes"
|
|
|
|
|
if not os.path.isdir(nodes_folder):
|
|
|
|
|
os.makedirs(nodes_folder)
|
|
|
|
|
node = Node.from_json(
|
|
|
|
|
{
|
|
|
|
|
"autodetect": False,
|
|
|
|
|
"datadir": "",
|
|
|
|
|
"user": bitcoin_regtest.rpcconn.rpcuser,
|
|
|
|
|
"password": bitcoin_regtest.rpcconn.rpcpassword,
|
|
|
|
|
"port": bitcoin_regtest.rpcconn.rpcport,
|
|
|
|
|
"host": bitcoin_regtest.rpcconn.ipaddress,
|
|
|
|
|
"protocol": "http",
|
|
|
|
|
},
|
|
|
|
|
manager=NodeManager(data_folder=nodes_folder),
|
|
|
|
|
default_fullpath=os.path.join(nodes_folder, "node_with_empty_datadir.json"),
|
|
|
|
|
)
|
|
|
|
|
return node
|
|
|
|
|
|
|
|
|
|
|
2021-07-08 20:10:14 +02:00
|
|
|
@pytest.fixture(scope="session")
|
2021-06-16 15:33:35 +02:00
|
|
|
def elements_elreg(request):
|
2021-07-08 20:10:14 +02:00
|
|
|
elements_elreg = instantiate_elementsd_controller(request, extra_args=None)
|
|
|
|
|
try:
|
|
|
|
|
yield elements_elreg
|
|
|
|
|
assert not elements_elreg.datadir is None
|
|
|
|
|
finally:
|
|
|
|
|
elements_elreg.stop_elementsd()
|
2021-03-06 04:40:09 -06:00
|
|
|
|
|
|
|
|
|
2019-12-19 15:01:07 +01:00
|
|
|
@pytest.fixture
|
|
|
|
|
def empty_data_folder():
|
|
|
|
|
# Make sure that this folder never ever gets a reasonable non-testing use-case
|
2022-11-22 21:07:36 +01:00
|
|
|
with tempfile.TemporaryDirectory(
|
|
|
|
|
prefix="specter_home_tmp_", ignore_cleanup_errors=False
|
|
|
|
|
) as data_folder:
|
2021-04-15 11:04:47 +02:00
|
|
|
yield data_folder
|
2019-12-19 15:01:07 +01:00
|
|
|
|
2020-09-30 11:42:30 +02:00
|
|
|
|
2019-12-28 10:27:54 +01:00
|
|
|
@pytest.fixture
|
2020-01-09 15:18:49 +01:00
|
|
|
def devices_filled_data_folder(empty_data_folder):
|
2022-11-22 21:07:36 +01:00
|
|
|
devices_folder = empty_data_folder + "/devices"
|
|
|
|
|
if not os.path.isdir(devices_folder):
|
|
|
|
|
os.makedirs(devices_folder)
|
2023-01-31 11:06:29 +01:00
|
|
|
|
|
|
|
|
shutil.copy2(
|
|
|
|
|
"./tests/misc_testdata/trezor_device.json",
|
|
|
|
|
empty_data_folder + "/devices/trezor.json",
|
|
|
|
|
)
|
|
|
|
|
shutil.copy2(
|
|
|
|
|
"./tests/misc_testdata/specter_device.json",
|
|
|
|
|
empty_data_folder + "/devices/specter.json",
|
|
|
|
|
)
|
2020-09-30 11:42:30 +02:00
|
|
|
return empty_data_folder # no longer empty, though
|
|
|
|
|
|
2020-01-09 15:18:49 +01:00
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
|
def wallets_filled_data_folder(devices_filled_data_folder):
|
2022-04-02 12:55:21 +02:00
|
|
|
simple_json = """
|
2019-12-28 10:27:54 +01:00
|
|
|
{
|
|
|
|
|
"alias": "simple",
|
|
|
|
|
"fullpath": "/home/kim/.specter/wallets/regtest/simple.json",
|
|
|
|
|
"name": "Simple",
|
|
|
|
|
"address_index": 0,
|
|
|
|
|
"keypool": 5,
|
|
|
|
|
"address": "bcrt1qcatuhg0gll3h7py4cmn53rjjn9xlsqfwj3zcej",
|
|
|
|
|
"change_index": 0,
|
|
|
|
|
"change_address": "bcrt1qt28v03278lmmxllys89acddp2p5y4zds94944n",
|
|
|
|
|
"change_keypool": 5,
|
|
|
|
|
"type": "simple",
|
|
|
|
|
"description": "Single (Segwit)",
|
2021-07-08 20:10:14 +02:00
|
|
|
"keys": [{
|
2019-12-28 10:27:54 +01:00
|
|
|
"derivation": "m/84h/1h/0h",
|
|
|
|
|
"original": "vpub5Y35MNUT8sUR2SnRCU9A9S6z1JDACMTuNnM8WHXvuS7hCwuVuoRAWJGpi66Yo8evGPiecN26oLqx19xf57mqVQjiYb9hbb4QzbNmFfsS9ko",
|
|
|
|
|
"fingerprint": "1ef4e492",
|
|
|
|
|
"type": "wpkh",
|
|
|
|
|
"xpub": "tpubDC5EUwdy9WWpzqMWKNhVmXdMgMbi4ywxkdysRdNr1MdM4SCfVLbNtsFvzY6WKSuzsaVAitj6FmP6TugPuNT6yKZDLsHrSwMd816TnqX7kuc"
|
2021-07-08 20:10:14 +02:00
|
|
|
}],
|
2019-12-28 10:27:54 +01:00
|
|
|
"recv_descriptor": "wpkh([1ef4e492/84h/1h/0h]tpubDC5EUwdy9WWpzqMWKNhVmXdMgMbi4ywxkdysRdNr1MdM4SCfVLbNtsFvzY6WKSuzsaVAitj6FmP6TugPuNT6yKZDLsHrSwMd816TnqX7kuc/0/*)#xp8lv5nr",
|
|
|
|
|
"change_descriptor": "wpkh([1ef4e492/84h/1h/0h]tpubDC5EUwdy9WWpzqMWKNhVmXdMgMbi4ywxkdysRdNr1MdM4SCfVLbNtsFvzY6WKSuzsaVAitj6FmP6TugPuNT6yKZDLsHrSwMd816TnqX7kuc/1/*)#h4z73prm",
|
|
|
|
|
"device": "Trezor",
|
|
|
|
|
"device_type": "trezor",
|
|
|
|
|
"address_type": "bech32"
|
|
|
|
|
}
|
2020-09-30 11:42:30 +02:00
|
|
|
"""
|
2022-04-02 12:55:21 +02:00
|
|
|
another_wallet_json = """
|
|
|
|
|
{
|
|
|
|
|
"name": "sdsd",
|
|
|
|
|
"alias": "sdsd",
|
|
|
|
|
"description": "Single (Segwit)",
|
|
|
|
|
"address_type": "bech32",
|
|
|
|
|
"address": "bcrt1q4h86vfanswhsle63hw2muv9h5a45cg2878uez5",
|
|
|
|
|
"address_index": 0,
|
|
|
|
|
"change_address": "bcrt1qxsj28ddr95xvp7xjyzkkfq6qknrn4kap30zkut",
|
|
|
|
|
"change_index": 0,
|
|
|
|
|
"keypool": 60,
|
|
|
|
|
"change_keypool": 20,
|
|
|
|
|
"recv_descriptor": "wpkh([41490ec7/84h/1h/0h]tpubDCTPz7KwyetfhQNMSWiK34pPR2zSTsTybrMPgRVAzouNLqtgsv51o81KjccmTbjkWJ8mVhRJM1LxZD6AfRH2635tHpHeCAKW446iwADNv7C/0/*)#rn833s5g",
|
|
|
|
|
"change_descriptor": "wpkh([41490ec7/84h/1h/0h]tpubDCTPz7KwyetfhQNMSWiK34pPR2zSTsTybrMPgRVAzouNLqtgsv51o81KjccmTbjkWJ8mVhRJM1LxZD6AfRH2635tHpHeCAKW446iwADNv7C/1/*)#j8zsv9ys",
|
|
|
|
|
"keys": [
|
|
|
|
|
{
|
|
|
|
|
"original": "vpub5YRErYARy1rFj1oGKc9yQyJ1jybtbEyvDziem5eFttPiVMbXJNtoQZ2DTAcowHUfu7NFPAiJtaop6TNRqAbkc8GPVY9VLp2HveP2PygjuYh",
|
|
|
|
|
"fingerprint": "41490ec7",
|
|
|
|
|
"derivation": "m/84h/1h/0h",
|
|
|
|
|
"type": "wpkh",
|
|
|
|
|
"purpose": "#0 Single Sig (Segwit)",
|
|
|
|
|
"xpub": "tpubDCTPz7KwyetfhQNMSWiK34pPR2zSTsTybrMPgRVAzouNLqtgsv51o81KjccmTbjkWJ8mVhRJM1LxZD6AfRH2635tHpHeCAKW446iwADNv7C"
|
|
|
|
|
}
|
|
|
|
|
],
|
|
|
|
|
"devices": [
|
|
|
|
|
"dsds"
|
|
|
|
|
],
|
|
|
|
|
"sigs_required": 1,
|
|
|
|
|
"blockheight": 0,
|
|
|
|
|
"pending_psbts": {},
|
|
|
|
|
"frozen_utxo": [],
|
|
|
|
|
"last_block": "187e2db380eb6d901efd87188f00c7074506c9c3813b8ecec7300ecc4e55eb46"
|
|
|
|
|
}
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
os.makedirs(os.path.join(devices_filled_data_folder, "wallets", "regtest"))
|
|
|
|
|
with open(
|
|
|
|
|
os.path.join(devices_filled_data_folder, "wallets", "regtest", "simple.json"),
|
|
|
|
|
"w",
|
|
|
|
|
) as json_file:
|
|
|
|
|
json_file.write(simple_json)
|
|
|
|
|
os.makedirs(os.path.join(devices_filled_data_folder, "wallets_someuser", "regtest"))
|
|
|
|
|
with open(
|
|
|
|
|
os.path.join(
|
|
|
|
|
devices_filled_data_folder, "wallets_someuser", "regtest", "simple.json"
|
|
|
|
|
),
|
|
|
|
|
"w",
|
|
|
|
|
) as json_file:
|
|
|
|
|
json_file.write(another_wallet_json)
|
2020-09-30 11:42:30 +02:00
|
|
|
return devices_filled_data_folder # and with wallets obviously
|
|
|
|
|
|
2020-01-09 15:18:49 +01:00
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
|
def device_manager(devices_filled_data_folder):
|
2020-09-30 11:42:30 +02:00
|
|
|
return DeviceManager(os.path.join(devices_filled_data_folder, "devices"))
|
|
|
|
|
|
2019-12-28 10:27:54 +01:00
|
|
|
|
2022-01-13 04:47:28 -06:00
|
|
|
# @pytest.fixture
|
|
|
|
|
# def user_manager(empty_data_folder) -> UserManager:
|
|
|
|
|
# """A UserManager having users alice, bob and eve"""
|
|
|
|
|
# specter = Specter(data_folder=empty_data_folder)
|
|
|
|
|
# user_manager = UserManager(specter=specter)
|
|
|
|
|
# config = {}
|
|
|
|
|
# user_manager.get_user("admin").decrypt_user_secret("admin")
|
|
|
|
|
# user_manager.create_user(
|
|
|
|
|
# user_id="alice",
|
|
|
|
|
# username="alice",
|
|
|
|
|
# plaintext_password="plain_pass_alice",
|
|
|
|
|
# config=config,
|
|
|
|
|
# )
|
|
|
|
|
# user_manager.create_user(
|
|
|
|
|
# user_id="bob",
|
|
|
|
|
# username="bob",
|
|
|
|
|
# plaintext_password="plain_pass_bob",
|
|
|
|
|
# config=config,
|
|
|
|
|
# )
|
|
|
|
|
# user_manager.create_user(
|
|
|
|
|
# user_id="eve",
|
|
|
|
|
# username="eve",
|
|
|
|
|
# plaintext_password="plain_pass_eve",
|
|
|
|
|
# config=config,
|
|
|
|
|
# )
|
|
|
|
|
# return user_manager
|
|
|
|
|
|
|
|
|
|
|
2019-12-19 15:01:07 +01:00
|
|
|
@pytest.fixture
|
2022-11-14 21:26:32 +01:00
|
|
|
def specter_regtest_configured(bitcoin_regtest, devices_filled_data_folder, node):
|
2021-07-08 20:10:14 +02:00
|
|
|
assert bitcoin_regtest.get_rpc().test_connection()
|
2019-12-19 15:01:07 +01:00
|
|
|
config = {
|
|
|
|
|
"rpc": {
|
|
|
|
|
"autodetect": False,
|
2022-11-14 21:26:32 +01:00
|
|
|
"datadir": bitcoin_regtest.datadir,
|
|
|
|
|
"user": bitcoin_regtest.rpcconn.rpcuser,
|
|
|
|
|
"password": bitcoin_regtest.rpcconn.rpcpassword,
|
|
|
|
|
"port": bitcoin_regtest.rpcconn.rpcport,
|
|
|
|
|
"host": bitcoin_regtest.rpcconn.ipaddress,
|
|
|
|
|
"protocol": "http",
|
|
|
|
|
},
|
|
|
|
|
"auth": {
|
|
|
|
|
"method": "rpcpasswordaspin",
|
|
|
|
|
},
|
|
|
|
|
"testing": {
|
|
|
|
|
"allow_threading_for_testing": False,
|
|
|
|
|
},
|
|
|
|
|
}
|
2023-01-31 11:06:29 +01:00
|
|
|
specter: Specter = Specter(
|
2022-11-30 04:13:16 -08:00
|
|
|
data_folder=devices_filled_data_folder, config=config, checker_threads=False
|
|
|
|
|
)
|
2023-01-31 11:06:29 +01:00
|
|
|
assert specter.active_node_alias == "bitcoin_core"
|
|
|
|
|
assert specter.node_manager.active_node.alias == "bitcoin_core"
|
2022-11-14 21:26:32 +01:00
|
|
|
assert specter.chain == "regtest"
|
|
|
|
|
# Create a User
|
|
|
|
|
someuser = specter.user_manager.add_user(
|
|
|
|
|
User.from_json(
|
|
|
|
|
user_dict={
|
|
|
|
|
"id": "someuser",
|
|
|
|
|
"username": "someuser",
|
|
|
|
|
"password": hash_password("somepassword"),
|
|
|
|
|
"config": {},
|
|
|
|
|
"is_admin": False,
|
|
|
|
|
"services": None,
|
|
|
|
|
},
|
|
|
|
|
specter=specter,
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
specter.user_manager.save()
|
|
|
|
|
specter.check()
|
|
|
|
|
|
|
|
|
|
assert not specter.wallet_manager.working_folder is None
|
|
|
|
|
try:
|
|
|
|
|
yield specter
|
|
|
|
|
finally:
|
|
|
|
|
# End all threads
|
|
|
|
|
# Deleting all Wallets (this will also purge them on core)
|
|
|
|
|
for user in specter.user_manager.users:
|
|
|
|
|
for wallet in list(user.wallet_manager.wallets.values()):
|
|
|
|
|
user.wallet_manager.delete_wallet(wallet, node)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
|
def specter_regtest_configured_with_threading(
|
|
|
|
|
bitcoin_regtest, devices_filled_data_folder, node
|
|
|
|
|
):
|
|
|
|
|
assert bitcoin_regtest.get_rpc().test_connection()
|
|
|
|
|
config = {
|
|
|
|
|
"rpc": {
|
|
|
|
|
"autodetect": False,
|
|
|
|
|
"datadir": bitcoin_regtest.datadir,
|
2020-01-09 15:18:49 +01:00
|
|
|
"user": bitcoin_regtest.rpcconn.rpcuser,
|
|
|
|
|
"password": bitcoin_regtest.rpcconn.rpcpassword,
|
|
|
|
|
"port": bitcoin_regtest.rpcconn.rpcport,
|
|
|
|
|
"host": bitcoin_regtest.rpcconn.ipaddress,
|
2020-09-30 11:42:30 +02:00
|
|
|
"protocol": "http",
|
2019-12-19 15:01:07 +01:00
|
|
|
},
|
2021-01-13 20:23:31 +13:00
|
|
|
"auth": {
|
|
|
|
|
"method": "rpcpasswordaspin",
|
|
|
|
|
},
|
2022-11-14 21:26:32 +01:00
|
|
|
"testing": {
|
|
|
|
|
"allow_threading_for_testing": True,
|
|
|
|
|
},
|
2019-12-19 15:01:07 +01:00
|
|
|
}
|
2020-02-27 00:28:50 +00:00
|
|
|
specter = Specter(data_folder=devices_filled_data_folder, config=config)
|
2021-07-08 20:10:14 +02:00
|
|
|
assert specter.chain == "regtest"
|
|
|
|
|
# Create a User
|
2021-11-11 08:25:00 -06:00
|
|
|
someuser = specter.user_manager.add_user(
|
2021-07-08 20:10:14 +02:00
|
|
|
User.from_json(
|
2021-11-11 08:25:00 -06:00
|
|
|
user_dict={
|
2021-07-08 20:10:14 +02:00
|
|
|
"id": "someuser",
|
|
|
|
|
"username": "someuser",
|
2021-11-11 08:25:00 -06:00
|
|
|
"password": hash_password("somepassword"),
|
2021-07-08 20:10:14 +02:00
|
|
|
"config": {},
|
|
|
|
|
"is_admin": False,
|
2021-11-11 08:25:00 -06:00
|
|
|
"services": None,
|
2021-07-08 20:10:14 +02:00
|
|
|
},
|
2021-11-11 08:25:00 -06:00
|
|
|
specter=specter,
|
2021-07-08 20:10:14 +02:00
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
specter.user_manager.save()
|
2020-02-27 00:28:50 +00:00
|
|
|
specter.check()
|
2021-07-08 20:10:14 +02:00
|
|
|
|
2021-04-23 20:50:50 +02:00
|
|
|
assert not specter.wallet_manager.working_folder is None
|
2021-07-08 20:10:14 +02:00
|
|
|
try:
|
|
|
|
|
yield specter
|
|
|
|
|
finally:
|
|
|
|
|
# Deleting all Wallets (this will also purge them on core)
|
|
|
|
|
for user in specter.user_manager.users:
|
|
|
|
|
for wallet in list(user.wallet_manager.wallets.values()):
|
2022-11-14 21:26:32 +01:00
|
|
|
user.wallet_manager.delete_wallet(wallet, node)
|
2020-02-27 00:28:50 +00:00
|
|
|
|
|
|
|
|
|
2022-02-11 14:54:30 +01:00
|
|
|
def specter_app_with_config(config={}, specter=None):
|
|
|
|
|
"""helper-function to create SpecterFlasks"""
|
|
|
|
|
if isinstance(config, dict):
|
|
|
|
|
tempClass = type("tempClass", (TestConfig,), {})
|
|
|
|
|
for key, value in config.items():
|
|
|
|
|
setattr(tempClass, key, value)
|
|
|
|
|
# service_manager will expect the class to be defined as a direct property of the module:
|
|
|
|
|
if hasattr(sys.modules[__name__], "tempClass"):
|
|
|
|
|
delattr(sys.modules[__name__], "tempClass")
|
|
|
|
|
assert not hasattr(sys.modules[__name__], "tempClass")
|
|
|
|
|
setattr(sys.modules[__name__], "tempClass", tempClass)
|
|
|
|
|
assert hasattr(sys.modules[__name__], "tempClass")
|
|
|
|
|
assert getattr(sys.modules[__name__], "tempClass") == tempClass
|
|
|
|
|
config = tempClass
|
|
|
|
|
app = create_app(config=config)
|
2020-02-27 00:28:50 +00:00
|
|
|
app.app_context().push()
|
2020-09-30 11:42:30 +02:00
|
|
|
app.config["TESTING"] = True
|
2020-02-27 00:28:50 +00:00
|
|
|
app.testing = True
|
2020-07-25 00:06:34 +03:00
|
|
|
app.tor_service_id = None
|
|
|
|
|
app.tor_enabled = False
|
2022-02-11 14:54:30 +01:00
|
|
|
init_app(app, specter=specter)
|
2020-02-27 00:28:50 +00:00
|
|
|
return app
|
|
|
|
|
|
2020-09-30 11:42:30 +02:00
|
|
|
|
2022-02-11 14:54:30 +01:00
|
|
|
@pytest.fixture
|
|
|
|
|
def app(specter_regtest_configured) -> SpecterFlask:
|
|
|
|
|
"""the Flask-App, but uninitialized"""
|
|
|
|
|
return specter_app_with_config(
|
|
|
|
|
config="cryptoadvance.specter.config.TestConfig",
|
|
|
|
|
specter=specter_regtest_configured,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2022-01-13 04:47:28 -06:00
|
|
|
@pytest.fixture
|
|
|
|
|
def app_no_node(empty_data_folder) -> SpecterFlask:
|
2022-11-30 04:13:16 -08:00
|
|
|
specter = Specter(data_folder=empty_data_folder, checker_threads=False)
|
2022-01-13 04:47:28 -06:00
|
|
|
app = create_app(config="cryptoadvance.specter.config.TestConfig")
|
|
|
|
|
app.app_context().push()
|
|
|
|
|
app.config["TESTING"] = True
|
|
|
|
|
app.testing = True
|
|
|
|
|
app.tor_service_id = None
|
|
|
|
|
app.tor_enabled = False
|
|
|
|
|
init_app(app, specter=specter)
|
|
|
|
|
return app
|
|
|
|
|
|
|
|
|
|
|
2020-02-27 00:28:50 +00:00
|
|
|
@pytest.fixture
|
|
|
|
|
def client(app):
|
2021-04-26 14:09:42 +02:00
|
|
|
"""a test_client from an initialized Flask-App"""
|
2020-02-27 00:28:50 +00:00
|
|
|
return app.test_client()
|