mirror of
https://github.com/cryptoadvance/specter-desktop.git
synced 2026-08-13 12:33:29 +02:00
Bugfix: Incomplete wallet deletion (#1950)
Also: * threading for testing via specter config * helpful output for flaky tests Co-authored-by: k9ert <kim@swanbitcoin.com>
This commit is contained in:
parent
aba2afe9bd
commit
a729ae7ae2
19 changed files with 456 additions and 115 deletions
|
|
@ -6,6 +6,8 @@ markers =
|
|||
slow: mark test as slow.
|
||||
elm: mark test as elementsd dependent
|
||||
bottleneck: mark a test as so ressource intensive that it can create a bottleneck where the test just fails due to a lack of ressources
|
||||
threading: test needs threading to work
|
||||
|
||||
# If you need live logging to debug, uncomment the next line
|
||||
# log_cli = True
|
||||
# Then set the desired logging level on the command line, for example:
|
||||
|
|
|
|||
|
|
@ -67,6 +67,9 @@ class ConfigManager(GenericDataManager):
|
|||
"hide_sensitive_info": False,
|
||||
"autohide_sensitive_info_timeout_minutes": None,
|
||||
"autologout_timeout_hours": 4,
|
||||
"testing": {
|
||||
"allow_threading_for_testing": False,
|
||||
},
|
||||
# TODO: remove
|
||||
"bitcoind": False,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,13 @@
|
|||
import logging
|
||||
import os
|
||||
import sys
|
||||
import pathlib
|
||||
import shutil
|
||||
import threading
|
||||
import traceback
|
||||
from typing import Dict
|
||||
|
||||
from flask_babel import lazy_gettext as _
|
||||
from cryptoadvance.specter.rpc import BitcoinRPC
|
||||
|
||||
from cryptoadvance.specter.key import Key
|
||||
|
||||
from ..helpers import add_dicts, alias, is_liquid, load_jsons
|
||||
|
|
@ -37,7 +36,7 @@ class WalletManager:
|
|||
chain,
|
||||
device_manager,
|
||||
path="specter",
|
||||
allow_threading=True,
|
||||
allow_threading_for_testing=False,
|
||||
):
|
||||
self.data_folder = data_folder
|
||||
self.chain = chain
|
||||
|
|
@ -52,7 +51,7 @@ class WalletManager:
|
|||
self.wallets = {}
|
||||
# A way to communicate failed wallets to the outside
|
||||
self.bitcoin_core_version_raw = bitcoin_core_version_raw
|
||||
self.allow_threading = allow_threading
|
||||
self.allow_threading_for_testing = allow_threading_for_testing
|
||||
# define different wallet classes for liquid and bitcoin
|
||||
self.WalletClass = LWallet if is_liquid(chain) else Wallet
|
||||
self.update(data_folder, rpc, chain)
|
||||
|
|
@ -108,15 +107,29 @@ class WalletManager:
|
|||
and self.rpc is not None
|
||||
and self.chain is not None
|
||||
):
|
||||
if self.allow_threading and use_threading:
|
||||
t = threading.Thread(
|
||||
target=self._update,
|
||||
args=(wallets_update_list,),
|
||||
)
|
||||
|
||||
t.start()
|
||||
if "pytest" in sys.modules:
|
||||
if self.allow_threading_for_testing:
|
||||
logger.info("Using threads in updating the wallet manager.")
|
||||
t = threading.Thread(
|
||||
target=self._update,
|
||||
args=(wallets_update_list,),
|
||||
)
|
||||
t.start()
|
||||
else:
|
||||
logger.info("Not using threads in updating the wallet manager.")
|
||||
self._update(wallets_update_list)
|
||||
else:
|
||||
self._update(wallets_update_list)
|
||||
if use_threading:
|
||||
logger.info("Using threads in updating the wallet manager.")
|
||||
t = threading.Thread(
|
||||
target=self._update,
|
||||
args=(wallets_update_list,),
|
||||
)
|
||||
|
||||
t.start()
|
||||
else:
|
||||
logger.info("Not using threads in updating the wallet manager.")
|
||||
self._update(wallets_update_list)
|
||||
else:
|
||||
self.is_loading = False
|
||||
logger.warning(
|
||||
|
|
@ -134,7 +147,7 @@ class WalletManager:
|
|||
* and, on the Specter side, the wallet objects of those unloaded wallets are reinitialised
|
||||
"""
|
||||
logger.info(
|
||||
f"Started Updating Wallets with {len(wallets_update_list.values())} wallets"
|
||||
f"Started updating wallets with {len(wallets_update_list.values())} wallets"
|
||||
)
|
||||
# list of wallets in the dict
|
||||
existing_names = list(self.wallets.keys())
|
||||
|
|
@ -147,7 +160,7 @@ class WalletManager:
|
|||
|
||||
wallet_alias = wallets_update_list[wallet]["alias"]
|
||||
wallet_name = wallets_update_list[wallet]["name"]
|
||||
logger.info(f" Updating wallet {wallet_name}")
|
||||
logger.info(f"Updating wallet {wallet_name}")
|
||||
# wallet from json not yet loaded in Bitcoin Core?!
|
||||
if os.path.join(self.rpc_path, wallet_alias) not in loaded_wallets:
|
||||
try:
|
||||
|
|
@ -348,27 +361,43 @@ class WalletManager:
|
|||
else:
|
||||
raise ("Failed to create new wallet")
|
||||
|
||||
def delete_wallet(
|
||||
self, wallet, bitcoin_datadir=get_default_datadir(), chain="main"
|
||||
):
|
||||
logger.info("Deleting {}".format(wallet.alias))
|
||||
wallet_rpc_path = os.path.join(self.rpc_path, wallet.alias)
|
||||
self.rpc.unloadwallet(wallet_rpc_path)
|
||||
# Try deleting wallet folder
|
||||
if bitcoin_datadir:
|
||||
if chain != "main":
|
||||
bitcoin_datadir = os.path.join(bitcoin_datadir, chain)
|
||||
candidates = [
|
||||
os.path.join(bitcoin_datadir, wallet_rpc_path),
|
||||
os.path.join(bitcoin_datadir, "wallets", wallet_rpc_path),
|
||||
]
|
||||
for path in candidates:
|
||||
shutil.rmtree(path, ignore_errors=True)
|
||||
|
||||
# Delete files
|
||||
wallet.delete_files()
|
||||
del self.wallets[wallet.name]
|
||||
self.update()
|
||||
def delete_wallet(self, wallet, node=None) -> tuple:
|
||||
"""Returns a tuple with two Booleans, indicating whether the wallet was deleted on the Specter side and/or on the node side."""
|
||||
logger.info(f"Deleting {wallet.alias}")
|
||||
specter_wallet_deleted = False
|
||||
node_wallet_file_deleted = False
|
||||
# Make first sure that we can unload the wallet in Bitcoin Core
|
||||
try:
|
||||
wallet_rpc_path = os.path.join(
|
||||
self.rpc_path, wallet.alias
|
||||
) # e.g. specter/jade_wallet
|
||||
logger.debug(f"The wallet_rpc_path is: {wallet_rpc_path}")
|
||||
self.rpc.unloadwallet(wallet_rpc_path)
|
||||
# Delete the wallet.json and backups
|
||||
try:
|
||||
wallet.delete_files()
|
||||
# Remove the wallet instance
|
||||
del self.wallets[wallet.name]
|
||||
self.update()
|
||||
specter_wallet_deleted = True
|
||||
except KeyError:
|
||||
raise SpecterError(
|
||||
f"The wallet {wallet.name} has already been deleted."
|
||||
)
|
||||
except SpecterInternalException as sie:
|
||||
logger.exception(
|
||||
f"Could not delete the wallet {wallet.name} in Specter due to {sie}"
|
||||
)
|
||||
# Also delete the wallet file on the node if possible
|
||||
if node:
|
||||
if node.delete_wallet_file(wallet):
|
||||
node_wallet_file_deleted = True
|
||||
except RpcError:
|
||||
raise SpecterError(
|
||||
"Unable to unload the wallet on the node. Aborting the deletion of the wallet ..."
|
||||
)
|
||||
deleted = (specter_wallet_deleted, node_wallet_file_deleted)
|
||||
return deleted
|
||||
|
||||
def rename_wallet(self, wallet, name):
|
||||
logger.info("Renaming {}".format(wallet.alias))
|
||||
|
|
@ -469,7 +498,7 @@ class WalletManager:
|
|||
"""Deletes all the wallets"""
|
||||
for w in list(self.wallets.keys()):
|
||||
wallet = self.wallets[w]
|
||||
self.delete_wallet(wallet, specter.bitcoin_datadir, specter.chain)
|
||||
self.delete_wallet(wallet)
|
||||
delete_folder(self.data_folder)
|
||||
|
||||
@classmethod
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import json
|
|||
import logging
|
||||
import os
|
||||
from os import path
|
||||
import shutil
|
||||
|
||||
from embit.liquid.networks import get_network
|
||||
from flask import render_template
|
||||
|
|
@ -17,7 +18,7 @@ from .rpc import (
|
|||
autodetect_rpc_confs,
|
||||
get_default_datadir,
|
||||
)
|
||||
from .specter_error import BrokenCoreConnectionException
|
||||
from .specter_error import SpecterError, BrokenCoreConnectionException
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -534,6 +535,44 @@ class Node(AbstractNode):
|
|||
def is_liquid(self):
|
||||
return is_liquid(self.chain)
|
||||
|
||||
def delete_wallet_file(self, wallet) -> bool:
|
||||
"""Deleting the wallet file located on the node. This only works if the node is on the same machine as Specter.
|
||||
Returns True if the wallet file could be deleted, otherwise returns False."""
|
||||
datadir = ""
|
||||
if self.datadir == "":
|
||||
# In case someone did not toggle the auto-detect but still used the default location.
|
||||
# When you set up a new node and deactivate the auto-detect, the datadir is set to an empty string.
|
||||
logger.debug(
|
||||
f"The node datadir before get_default_datadir is: {self.datadir}"
|
||||
)
|
||||
datadir = get_default_datadir(self.node_type)
|
||||
logger.debug(f"The node datadir after get_default_datadir is: {datadir}")
|
||||
else:
|
||||
datadir = self.datadir
|
||||
wallet_file_removed = False
|
||||
path = ""
|
||||
# Check whether wallet was really unloaded
|
||||
wallet_rpc_path = os.path.join(wallet.manager.rpc_path, wallet.alias)
|
||||
# If we can unload the wallet via RPC it had not been unloaded properly before by the wallet manager
|
||||
try:
|
||||
self.rpc.unloadwallet(wallet_rpc_path)
|
||||
raise SpecterError(
|
||||
"Trying to delete the wallet file on the node but the wallet had not been unloaded properly."
|
||||
)
|
||||
except RpcError:
|
||||
pass
|
||||
if self.chain != "main":
|
||||
path = os.path.join(datadir, f"{self.chain}/wallets", wallet_rpc_path)
|
||||
else:
|
||||
path = os.path.join(datadir, wallet_rpc_path)
|
||||
try:
|
||||
shutil.rmtree(path, ignore_errors=False)
|
||||
logger.debug(f"Removing wallet file at: {path}")
|
||||
wallet_file_removed = True
|
||||
except FileNotFoundError:
|
||||
logger.debug(f"Could not find any wallet file at: {path}")
|
||||
return wallet_file_removed
|
||||
|
||||
@property
|
||||
def is_running(self):
|
||||
if self._network_info["version"] == 999999:
|
||||
|
|
@ -619,6 +658,10 @@ class Node(AbstractNode):
|
|||
return self._node_type
|
||||
return "BTC"
|
||||
|
||||
@property
|
||||
def default_datadir(self):
|
||||
return get_default_datadir(self.node_type)
|
||||
|
||||
@rpc.setter
|
||||
def rpc(self, value):
|
||||
if hasattr(self, "_rpc") and self._rpc != value:
|
||||
|
|
|
|||
|
|
@ -850,11 +850,28 @@ def settings(wallet_alias):
|
|||
)
|
||||
wallet.getdata()
|
||||
elif action == "deletewallet":
|
||||
app.specter.wallet_manager.delete_wallet(
|
||||
wallet, app.specter.bitcoin_datadir, app.specter.chain
|
||||
)
|
||||
response = redirect(url_for("index"))
|
||||
return response
|
||||
deleted = app.specter.wallet_manager.delete_wallet(wallet, app.specter.node)
|
||||
# deleted is a tuple: (specter_wallet_deleted, core_wallet_file_deleted)
|
||||
if deleted == (True, True):
|
||||
flash(
|
||||
_("Wallet in Specter and wallet file on node deleted successfully.")
|
||||
)
|
||||
elif deleted == (True, False):
|
||||
flash(
|
||||
_(
|
||||
"Wallet in Specter deleted successfully but wallet file on node could not be removed automatically."
|
||||
)
|
||||
)
|
||||
elif deleted == (False, True):
|
||||
flash(
|
||||
_(
|
||||
"Deletion of wallet in Specter failed, but wallet on node was removed."
|
||||
),
|
||||
"error",
|
||||
)
|
||||
else:
|
||||
flash(_("Deletion of wallet failed."), "error")
|
||||
return redirect(url_for("index"))
|
||||
elif action == "rename":
|
||||
wallet_name = request.form["newtitle"]
|
||||
if not wallet_name:
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@
|
|||
</div>
|
||||
<span id="datadir-container">
|
||||
<br>
|
||||
{{ _("Bitcoin Core data directory path:") }}<br><input type="text" id="datadir" name="datadir" type="text" value="{{ node.datadir }}" style="margin-bottom: 15px;">
|
||||
{{ _("Bitcoin Core data directory path:") }}<br><input type="text" id="datadir" name="datadir" type="text" value="{{ node.default_datadir }}" style="margin-bottom: 15px;">
|
||||
</span>
|
||||
<br>
|
||||
<div id="rpc_settings">
|
||||
|
|
|
|||
|
|
@ -299,6 +299,9 @@ class User(UserMixin):
|
|||
self.specter.chain,
|
||||
self.device_manager,
|
||||
path=wallets_rpcpath,
|
||||
allow_threading_for_testing=self.specter.config["testing"][
|
||||
"allow_threading_for_testing"
|
||||
],
|
||||
)
|
||||
self._wallet_manager = wallet_manager
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -10,7 +10,9 @@ import traceback
|
|||
|
||||
import pytest
|
||||
from cryptoadvance.specter.config import TestConfig
|
||||
from cryptoadvance.specter.node import Node
|
||||
from cryptoadvance.specter.managers.device_manager import DeviceManager
|
||||
from cryptoadvance.specter.managers.node_manager import NodeManager
|
||||
from cryptoadvance.specter.managers.user_manager import UserManager
|
||||
from cryptoadvance.specter.process_controller.bitcoind_controller import (
|
||||
BitcoindPlainController,
|
||||
|
|
@ -20,7 +22,11 @@ from cryptoadvance.specter.process_controller.elementsd_controller import (
|
|||
)
|
||||
from cryptoadvance.specter.server import SpecterFlask, create_app, init_app
|
||||
from cryptoadvance.specter.specter import Specter
|
||||
from cryptoadvance.specter.specter_error import SpecterError
|
||||
from cryptoadvance.specter.specter_error import (
|
||||
BrokenCoreConnectionException,
|
||||
SpecterError,
|
||||
handle_exception,
|
||||
)
|
||||
from cryptoadvance.specter.user import User, hash_password
|
||||
from cryptoadvance.specter.util.common import str2bool
|
||||
from cryptoadvance.specter.util.shell import which
|
||||
|
|
@ -29,6 +35,7 @@ from cryptoadvance.specter.util.wallet_importer import WalletImporter
|
|||
logger = logging.getLogger(__name__)
|
||||
|
||||
pytest_plugins = [
|
||||
# "conftest_visibility",
|
||||
"fix_ghost_machine",
|
||||
"fix_keys_and_seeds",
|
||||
"fix_devices_and_wallets",
|
||||
|
|
@ -186,6 +193,7 @@ def bitcoin_regtest(docker, request):
|
|||
try:
|
||||
assert bitcoind_regtest.get_rpc().test_connection()
|
||||
assert not bitcoind_regtest.datadir is None
|
||||
assert bitcoind_regtest.datadir is not ""
|
||||
yield bitcoind_regtest
|
||||
finally:
|
||||
bitcoind_regtest.stop_bitcoind()
|
||||
|
|
@ -205,6 +213,48 @@ def bitcoin_regtest2(docker, request):
|
|||
bitcoind_regtest.stop_bitcoind()
|
||||
|
||||
|
||||
@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)
|
||||
node = Node.from_json(
|
||||
{
|
||||
"autodetect": False,
|
||||
"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",
|
||||
},
|
||||
manager=NodeManager(data_folder=nodes_folder),
|
||||
default_fullpath=os.path.join(nodes_folder, "standard_node.json"),
|
||||
)
|
||||
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
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def elements_elreg(request):
|
||||
elements_elreg = instantiate_elementsd_controller(request, extra_args=None)
|
||||
|
|
@ -218,9 +268,7 @@ def elements_elreg(request):
|
|||
@pytest.fixture
|
||||
def empty_data_folder():
|
||||
# Make sure that this folder never ever gets a reasonable non-testing use-case
|
||||
with tempfile.TemporaryDirectory(
|
||||
prefix="specter_home_tmp_", ignore_cleanup_errors=True
|
||||
) as data_folder:
|
||||
with tempfile.TemporaryDirectory(prefix="specter_home_tmp_") as data_folder:
|
||||
yield data_folder
|
||||
|
||||
|
||||
|
|
@ -458,12 +506,12 @@ def device_manager(devices_filled_data_folder):
|
|||
|
||||
|
||||
@pytest.fixture
|
||||
def specter_regtest_configured(bitcoin_regtest, devices_filled_data_folder):
|
||||
def specter_regtest_configured(bitcoin_regtest, devices_filled_data_folder, node):
|
||||
assert bitcoin_regtest.get_rpc().test_connection()
|
||||
config = {
|
||||
"rpc": {
|
||||
"autodetect": False,
|
||||
"datadir": "",
|
||||
"datadir": bitcoin_regtest.datadir,
|
||||
"user": bitcoin_regtest.rpcconn.rpcuser,
|
||||
"password": bitcoin_regtest.rpcconn.rpcpassword,
|
||||
"port": bitcoin_regtest.rpcconn.rpcport,
|
||||
|
|
@ -473,6 +521,61 @@ def specter_regtest_configured(bitcoin_regtest, devices_filled_data_folder):
|
|||
"auth": {
|
||||
"method": "rpcpasswordaspin",
|
||||
},
|
||||
"testing": {
|
||||
"allow_threading_for_testing": False,
|
||||
},
|
||||
}
|
||||
specter = Specter(data_folder=devices_filled_data_folder, config=config)
|
||||
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,
|
||||
"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": True,
|
||||
},
|
||||
}
|
||||
specter = Specter(data_folder=devices_filled_data_folder, config=config)
|
||||
assert specter.chain == "regtest"
|
||||
|
|
@ -500,9 +603,7 @@ def specter_regtest_configured(bitcoin_regtest, devices_filled_data_folder):
|
|||
# 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, bitcoin_datadir=bitcoin_regtest.datadir, chain="regtest"
|
||||
)
|
||||
user.wallet_manager.delete_wallet(wallet, node)
|
||||
|
||||
|
||||
def specter_app_with_config(config={}, specter=None):
|
||||
|
|
|
|||
83
tests/conftest_visibility.py
Normal file
83
tests/conftest_visibility.py
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
""" A toolkit for hunting flaky tests. For now, this way of running pytest gave more or less consistent
|
||||
bad results:
|
||||
clear && pytest tests/test_managers_wallet.py tests/test_cli_server.py tests/test_managers_device.py \
|
||||
tests/test_managers_wallet.py tests/test_node.py tests/test_node_controller.py tests/test_rest.py
|
||||
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import traceback
|
||||
import threading
|
||||
import sys
|
||||
from cryptoadvance.specter.specter_error import (
|
||||
BrokenCoreConnectionException,
|
||||
SpecterError,
|
||||
)
|
||||
from cryptoadvance.specter.process_controller.bitcoind_controller import (
|
||||
BitcoindPlainController,
|
||||
)
|
||||
from cryptoadvance.specter.rpc import RpcError
|
||||
from cryptoadvance.specter.specter_error import SpecterError
|
||||
|
||||
|
||||
def should_intercept(call):
|
||||
"""Should return a boolean whether the visibility output should be done.
|
||||
This needs to be more and more restricted overtime as we hopefully
|
||||
have less and less flaky tests in the future and the normal output is enough.
|
||||
"""
|
||||
return not (
|
||||
isinstance(call.excinfo.value, RpcError)
|
||||
or isinstance(call.excinfo.value, SpecterError)
|
||||
)
|
||||
|
||||
|
||||
@pytest.hookimpl(hookwrapper=True)
|
||||
def pytest_exception_interact(node, call, report):
|
||||
"""Making more clever investigations in case of Exceptions"""
|
||||
if should_intercept(call):
|
||||
print()
|
||||
print("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
|
||||
print(
|
||||
f"intercepting: {call.excinfo.value.__class__.__name__} while {report.when}"
|
||||
)
|
||||
print_exception(node, call, report)
|
||||
print_threaddump(node, call, report)
|
||||
print_debug_logs(node, call, report)
|
||||
print("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
|
||||
yield
|
||||
|
||||
|
||||
def print_exception(node, call, report):
|
||||
"""prints a stacktrace of the intercepted Exception"""
|
||||
print("XXXXXXXXXXXXXXXXXX_EXCEPTION_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
|
||||
traceback.print_exception(call.excinfo.value)
|
||||
print("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
|
||||
|
||||
|
||||
def print_threaddump(node, call, report):
|
||||
"""prints a threaddump, a list of stacktraces from all threads"""
|
||||
print("XXXXXXXXXXXXXXXXXX_THREADDUMP_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
|
||||
for th in threading.enumerate():
|
||||
print(th)
|
||||
traceback.print_stack(sys._current_frames()[th.ident])
|
||||
print()
|
||||
print("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
|
||||
|
||||
|
||||
def print_debug_logs(node, call, report):
|
||||
"""prints the debug-logs of all the Regtest instances"""
|
||||
print(node)
|
||||
if isinstance(call.excinfo.value, BrokenCoreConnectionException):
|
||||
print("XXXXXXXXXXXXXXXXXX_DEBUG.LOG_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
|
||||
regtests = []
|
||||
for fixture_name in node.fixturenames:
|
||||
regtest = node.funcargs.get(fixture_name)
|
||||
if isinstance(regtest, BitcoindPlainController):
|
||||
regtests.append(regtest)
|
||||
for regtest in regtests:
|
||||
print("---------------------------------------------------------")
|
||||
print(
|
||||
f"{regtest} in DATADIR {regtest.datadir} on PORT {regtest.rpcconn.rpcport}"
|
||||
)
|
||||
print(regtest.get_debug_log())
|
||||
print("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
|
||||
|
|
@ -4,6 +4,7 @@ import time
|
|||
|
||||
from cryptoadvance.specter.util.mnemonic import generate_mnemonic
|
||||
from cryptoadvance.specter.process_controller.node_controller import NodeController
|
||||
from cryptoadvance.specter.managers.wallet_manager import WalletManager
|
||||
from cryptoadvance.specter.specter import Specter
|
||||
from cryptoadvance.specter.wallet import Wallet, Device
|
||||
|
||||
|
|
@ -180,3 +181,19 @@ def funded_taproot_wallet(
|
|||
funded_taproot_wallet.getnewaddress()
|
||||
) # default value are 20 BTC
|
||||
return funded_taproot_wallet
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def wallet(devices_filled_data_folder, device_manager, node):
|
||||
"""An ordinary wallet without private keys"""
|
||||
wm = WalletManager(
|
||||
200100,
|
||||
devices_filled_data_folder,
|
||||
node._get_rpc(),
|
||||
"regtest",
|
||||
device_manager,
|
||||
)
|
||||
device = device_manager.get_by_alias("trezor")
|
||||
wm.create_wallet("test_wallet", 1, "wpkh", [device.keys[5]], [device])
|
||||
wallet = wm.wallets["test_wallet"]
|
||||
return wallet
|
||||
|
|
|
|||
|
|
@ -54,6 +54,4 @@ def specter_testnet_configured(bitcoin_regtest, devices_filled_data_folder):
|
|||
# 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, bitcoin_datadir=bitcoin_regtest.datadir, chain="regtest"
|
||||
)
|
||||
user.wallet_manager.delete_wallet(wallet)
|
||||
|
|
|
|||
|
|
@ -111,7 +111,6 @@ def test_device_wallets(
|
|||
bitcoin_regtest.get_rpc(),
|
||||
"regtest",
|
||||
device_manager,
|
||||
allow_threading=False,
|
||||
)
|
||||
device = device_manager.get_by_alias("trezor")
|
||||
assert len(device.wallets(wm)) == 0
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ from cryptoadvance.specter.util.descriptor import AddChecksum, Descriptor
|
|||
from cryptoadvance.specter.util.wallet_importer import WalletImporter
|
||||
from conftest import instantiate_bitcoind_controller
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@pytest.mark.slow
|
||||
def test_WalletManager(
|
||||
|
|
@ -26,6 +28,8 @@ def test_WalletManager(
|
|||
devices_filled_data_folder,
|
||||
device_manager,
|
||||
bitcoin_regtest,
|
||||
node,
|
||||
node_with_empty_datadir,
|
||||
):
|
||||
wm = WalletManager(
|
||||
200100,
|
||||
|
|
@ -33,8 +37,8 @@ def test_WalletManager(
|
|||
bitcoin_regtest.get_rpc(),
|
||||
"regtest",
|
||||
device_manager,
|
||||
allow_threading=False,
|
||||
)
|
||||
assert wm.rpc_path == "specter"
|
||||
# A wallet-creation needs a device
|
||||
device = device_manager.get_by_alias("trezor")
|
||||
assert device != None
|
||||
|
|
@ -80,19 +84,40 @@ def test_WalletManager(
|
|||
assert multisig_wallet.name == "new_name_test_wallet"
|
||||
assert wm.wallets_names == ["a_test_wallet", "new_name_test_wallet"]
|
||||
|
||||
# you can also delete a wallet by passing it to the wallet manager's `delete_wallet` method
|
||||
# it will delete the json and attempt to remove it from Bitcoin Core
|
||||
# You can also delete a wallet by passing it to the wallet manager's `delete_wallet` method
|
||||
# It will delete the json and attempt to remove it from Bitcoin Core
|
||||
wallet_fullpath = multisig_wallet.fullpath
|
||||
assert os.path.exists(wallet_fullpath)
|
||||
wm.delete_wallet(multisig_wallet)
|
||||
assert not os.path.exists(wallet_fullpath)
|
||||
# This deletion should also remove the wallet file on the node
|
||||
assert wm.delete_wallet(multisig_wallet, node) == (True, True)
|
||||
assert len(wm.wallets) == 1
|
||||
|
||||
wm.update()
|
||||
assert not os.path.exists(wallet_fullpath)
|
||||
# Let's artificially unload the wallet in Core before trying to delete it via the Wallet Manager, should raise a SpecterError
|
||||
wallet_rpc_path = os.path.join(wm.rpc_path, wallet.alias)
|
||||
wm.rpc.unloadwallet(wallet_rpc_path)
|
||||
with pytest.raises(
|
||||
SpecterError,
|
||||
match="Unable to unload the wallet on the node. Aborting the deletion of the wallet ...",
|
||||
):
|
||||
wm.delete_wallet(wallet, node)
|
||||
# Check that the wallet wasn't deleted in Specter because of the RpcError
|
||||
assert wm.wallets_names == ["a_test_wallet"]
|
||||
# The following deletion should not remove the wallet file on the node
|
||||
assert node_with_empty_datadir.datadir == ""
|
||||
wm.rpc.loadwallet(wallet_rpc_path) # we need to load the wallet again
|
||||
assert wm.delete_wallet(wallet, node_with_empty_datadir) == (True, False)
|
||||
assert len(wm.wallets) == 0
|
||||
# The wallet in Specter was already deleted, so trying to delete it again should raise a SpecterError
|
||||
wm.rpc.loadwallet(wallet_rpc_path) # we need to load the wallet again
|
||||
with pytest.raises(
|
||||
SpecterError, match="The wallet a_test_wallet has already been deleted."
|
||||
):
|
||||
assert wm.delete_wallet(wallet)
|
||||
|
||||
|
||||
@pytest.mark.slow
|
||||
@pytest.mark.bottleneck
|
||||
@pytest.mark.threading
|
||||
def test_WalletManager_2_nodes(
|
||||
docker,
|
||||
request,
|
||||
|
|
@ -109,35 +134,34 @@ def test_WalletManager_2_nodes(
|
|||
bitcoin_regtest.get_rpc(),
|
||||
"regtest",
|
||||
device_manager,
|
||||
allow_threading=True,
|
||||
allow_threading_for_testing=True,
|
||||
)
|
||||
# A wallet-creation needs a device
|
||||
# Wallet creation needs a device
|
||||
device = device_manager.get_by_alias("trezor")
|
||||
assert device != None
|
||||
# Lets's create a wallet with the WalletManager
|
||||
wm.create_wallet("a_test_wallet", 1, "wpkh", [device.keys[5]], [device])
|
||||
assert wm.wallets_names == ["a_test_wallet"]
|
||||
|
||||
# A WalletManager implicitely uses the chain as a kind of index
|
||||
wm.update(chain="regtest2", rpc=bitcoin_regtest2.get_rpc(), use_threading=False)
|
||||
assert wm.wallets_names == []
|
||||
wm.create_wallet("a_regtest2_test_wallet", 1, "wpkh", [device.keys[5]], [device])
|
||||
assert wm.wallets_names == ["a_regtest2_test_wallet"]
|
||||
wm.create_wallet(
|
||||
"a_second_regtest2_test_wallet", 1, "wpkh", [device.keys[5]], [device]
|
||||
first_wallet = wm.create_wallet(
|
||||
"a_test_wallet", 1, "wpkh", [device.keys[5]], [device]
|
||||
)
|
||||
assert wm.wallets_names == [
|
||||
"a_regtest2_test_wallet",
|
||||
"a_second_regtest2_test_wallet",
|
||||
]
|
||||
|
||||
# you can switch bettween chains witht he update-method
|
||||
wm.update(chain="regtest", rpc=bitcoin_regtest.get_rpc())
|
||||
assert wm.wallets_names == ["a_test_wallet"]
|
||||
|
||||
# Should also use with threading
|
||||
wm.update(chain="regtest2", rpc=bitcoin_regtest2.get_rpc(), use_threading=True)
|
||||
assert wm.wallets_names == ["a_test_wallet"]
|
||||
assert wm.chain == "regtest"
|
||||
assert wm.working_folder.endswith("regtest")
|
||||
assert wm.rpc.port == 18543
|
||||
# Change the rpc - this only works with a different chain!
|
||||
wm.update(rpc=bitcoin_regtest2.get_rpc(), chain="regtest2")
|
||||
# A WalletManager uses the chain as an index
|
||||
assert list(wm.rpcs.keys()) == [
|
||||
"regtest",
|
||||
"regtest2",
|
||||
] # wm.rpcs looks like this: {'regtest': <BitcoinRpc http://localhost:18543>, 'regtest2': <BitcoinRpc http://localhost:18544>}
|
||||
assert wm.rpc.port == 18544
|
||||
assert wm.wallets_names == []
|
||||
assert wm.chain == "regtest2"
|
||||
assert wm.working_folder.endswith("regtest2")
|
||||
second_wallet = wm.create_wallet(
|
||||
"a_regtest2_test_wallet", 1, "wpkh", [device.keys[5]], [device]
|
||||
)
|
||||
# Note: "regtest2" is recognised by the get_network() from embit as Liquid, that is why there is an error in the logs saying the Bitcoin address is not valid since a Liquid address is derived.
|
||||
assert wm.wallets_names == ["a_regtest2_test_wallet"]
|
||||
|
||||
|
||||
def test_WalletManager_check_duplicate_keys(empty_data_folder):
|
||||
|
|
@ -147,7 +171,6 @@ def test_WalletManager_check_duplicate_keys(empty_data_folder):
|
|||
MagicMock(), # needs rpc
|
||||
"regtest",
|
||||
None,
|
||||
allow_threading=False,
|
||||
)
|
||||
key1 = Key(
|
||||
"[f3e6eaff/84h/0h/0h]xpub6C5cCQfycZrPJnNg6cDdUU5efJrab8thRQDBxSSB4gP2J3xGdWu8cqiLvPZkejtuaY9LursCn6Es9PqHgLhBktW8217BomGDVBAJjUms8iG",
|
||||
|
|
@ -215,7 +238,6 @@ def test_wallet_sortedmulti(
|
|||
bitcoin_regtest.get_rpc(),
|
||||
"regtest",
|
||||
device_manager,
|
||||
allow_threading=False,
|
||||
)
|
||||
device = device_manager.get_by_alias("trezor")
|
||||
second_device = device_manager.get_by_alias("specter")
|
||||
|
|
@ -270,7 +292,6 @@ def test_wallet_labeling(bitcoin_regtest, devices_filled_data_folder, device_man
|
|||
bitcoin_regtest.get_rpc(),
|
||||
"regtest",
|
||||
device_manager,
|
||||
allow_threading=False,
|
||||
)
|
||||
# A wallet-creation needs a device
|
||||
device = device_manager.get_by_alias("specter")
|
||||
|
|
@ -330,7 +351,6 @@ def test_wallet_change_addresses(
|
|||
bitcoin_regtest.get_rpc(),
|
||||
"regtest",
|
||||
device_manager,
|
||||
allow_threading=False,
|
||||
)
|
||||
# A wallet-creation needs a device
|
||||
device = device_manager.get_by_alias("specter")
|
||||
|
|
@ -359,7 +379,7 @@ def test_wallet_change_addresses(
|
|||
# See: https://github.com/bitcoin/bitcoin/issues/14654
|
||||
|
||||
|
||||
def test_singlesig_wallet_backup_and_restore(caplog, specter_regtest_configured):
|
||||
def test_singlesig_wallet_backup_and_restore(caplog, specter_regtest_configured, node):
|
||||
"""
|
||||
Single-sig wallets should be able to be backed up and re-imported with or without
|
||||
the "devices" attr in the json backup.
|
||||
|
|
@ -394,7 +414,7 @@ def test_singlesig_wallet_backup_and_restore(caplog, specter_regtest_configured)
|
|||
assert "devices" in wallet_backup
|
||||
|
||||
# Clear everything out as if we've never seen this wallet or device before
|
||||
wallet_manager.delete_wallet(wallet)
|
||||
wallet_manager.delete_wallet(wallet, node)
|
||||
device_manager.remove_device(device, wallet_manager=wallet_manager)
|
||||
assert wallet.name not in wallet_manager.wallets_names
|
||||
assert device.name not in device_manager.devices_names
|
||||
|
|
@ -451,7 +471,7 @@ def test_singlesig_wallet_backup_and_restore(caplog, specter_regtest_configured)
|
|||
del wallet_backup["devices"]
|
||||
|
||||
# Clear everything out as if we've never seen this wallet or device before
|
||||
wallet_manager.delete_wallet(wallet)
|
||||
wallet_manager.delete_wallet(wallet, node)
|
||||
device_manager.remove_device(device, wallet_manager=wallet_manager)
|
||||
assert wallet.name not in wallet_manager.wallets_names
|
||||
assert device.name not in device_manager.devices_names
|
||||
|
|
@ -505,7 +525,7 @@ def test_singlesig_wallet_backup_and_restore(caplog, specter_regtest_configured)
|
|||
|
||||
|
||||
def test_multisig_wallet_backup_and_restore(
|
||||
bitcoin_regtest, caplog, specter_regtest_configured
|
||||
bitcoin_regtest, caplog, specter_regtest_configured, node
|
||||
):
|
||||
"""
|
||||
Multisig wallets should be able to be backed up and re-imported
|
||||
|
|
@ -574,7 +594,7 @@ def test_multisig_wallet_backup_and_restore(
|
|||
assert "devices" in wallet_backup
|
||||
|
||||
# Clear everything out as if we've never seen this wallet or device before
|
||||
wallet_manager.delete_wallet(wallet)
|
||||
wallet_manager.delete_wallet(wallet, node)
|
||||
device_manager.remove_device(device, wallet_manager=wallet_manager)
|
||||
assert wallet.name not in wallet_manager.wallets_names
|
||||
assert device.name not in device_manager.devices_names
|
||||
|
|
@ -634,7 +654,7 @@ def test_multisig_wallet_backup_and_restore(
|
|||
del wallet_backup["devices"]
|
||||
|
||||
# Clear everything out as if we've never seen this wallet or device before
|
||||
wallet_manager.delete_wallet(wallet)
|
||||
wallet_manager.delete_wallet(wallet, node)
|
||||
for device_names in device_manager.devices:
|
||||
device = device_manager.devices[device_names]
|
||||
device_manager.remove_device(device, wallet_manager=wallet_manager)
|
||||
|
|
@ -689,3 +709,19 @@ def test_multisig_wallet_backup_and_restore(
|
|||
|
||||
# We restored the wallet's utxos
|
||||
assert wallet.amount_total == 3.3
|
||||
|
||||
|
||||
def test_threading(specter_regtest_configured_with_threading):
|
||||
assert (
|
||||
specter_regtest_configured_with_threading.config["testing"][
|
||||
"allow_threading_for_testing"
|
||||
]
|
||||
== True
|
||||
)
|
||||
device = specter_regtest_configured_with_threading.device_manager.get_by_alias(
|
||||
"trezor"
|
||||
)
|
||||
wm = specter_regtest_configured_with_threading.wallet_manager
|
||||
wallet = wm.create_wallet("test_wallet", 1, "wpkh", [device.keys[5]], [device])
|
||||
assert wm.wallets_names == ["test_wallet"]
|
||||
assert wm.data_folder.endswith("wallets")
|
||||
|
|
|
|||
|
|
@ -5,10 +5,12 @@ import tempfile
|
|||
from cryptoadvance.specter.node import Node
|
||||
from cryptoadvance.specter.managers.node_manager import NodeManager
|
||||
from cryptoadvance.specter.helpers import is_liquid
|
||||
from cryptoadvance.specter.specter_error import SpecterError
|
||||
|
||||
from mock import MagicMock, call, patch
|
||||
|
||||
|
||||
def test_Node_btc(bitcoin_regtest):
|
||||
def test_Node_btc(bitcoin_regtest, wallet):
|
||||
with tempfile.TemporaryDirectory("_some_datafolder_tmp") as data_folder:
|
||||
node = Node.from_json(
|
||||
{
|
||||
|
|
@ -66,6 +68,18 @@ def test_Node_btc(bitcoin_regtest):
|
|||
# {'version': 200100, 'subversion': '/Satoshi:0.20.1/', 'protocolversion': 70015, 'localservices': '0000000000000409', 'localservicesnames': ['NETWORK', 'WITNESS', 'NETWORK_LIMITED'], 'localrelay': True, 'timeoffset': 0, 'networkactive': True, 'connections': 0, 'networks': [{'name': 'ipv4', 'limited': False, 'reachable': True, 'proxy': '', 'proxy_randomize_credentials': False}, {'name': 'ipv6', 'limited': False, 'reachable': True, 'proxy': '', 'proxy_randomize_credentials': False}, {'name': 'onion', 'limited': True, 'reachable': False, 'proxy': '', 'proxy_randomize_credentials': False}], 'relayfee': 1e-05, 'incrementalfee': 1e-05, 'localaddresses': [{'address': '2a02:810d:d00:7700:233e:a7e:ded8:f2da', 'port': 18542, 'score': 1}, {'address': '2a02:810d:d00:7700:29ec:5c5b:196b:78b2', 'port': 18542, 'score': 1}], 'warnings': ''}
|
||||
assert node.network_info["connections"] == 0
|
||||
assert node.network_info["warnings"] == ""
|
||||
# Testing deleting the wallet file on the node
|
||||
with pytest.raises(
|
||||
SpecterError,
|
||||
match="Trying to delete the wallet file on the node but the wallet had not been unloaded properly.",
|
||||
):
|
||||
node.delete_wallet_file(wallet)
|
||||
# No error raised now anymore since the wallet was unloaded
|
||||
# Should return False because of datadir being "" which translates to the default datadir which the tests are not using.
|
||||
node.delete_wallet_file(wallet) == False
|
||||
# Update the datadir to the correct one
|
||||
node.update_rpc(datadir=bitcoin_regtest.datadir)
|
||||
node.delete_wallet_file(wallet) == True
|
||||
|
||||
|
||||
@pytest.mark.elm
|
||||
|
|
|
|||
|
|
@ -30,6 +30,10 @@ def almost_equal(a: Number, b: Number, precision: float = 0.01) -> bool:
|
|||
|
||||
def test_rr_psbt_get(client, specter_regtest_configured, bitcoin_regtest, caplog):
|
||||
create_a_simple_wallet(specter_regtest_configured, bitcoin_regtest)
|
||||
assert (
|
||||
specter_regtest_configured.config["testing"]["allow_threading_for_testing"]
|
||||
== False
|
||||
) # Tests fail with threading
|
||||
caplog.set_level(logging.DEBUG)
|
||||
""" testing the registration """
|
||||
# Unauthorized
|
||||
|
|
@ -172,18 +176,17 @@ def create_a_simple_wallet(specter: Specter, bitcoin_regtest):
|
|||
)
|
||||
username = payload["username"]
|
||||
someuser = specter.user_manager.get_user_by_username(username)
|
||||
assert not someuser.wallet_manager.working_folder is None
|
||||
wm = someuser.wallet_manager
|
||||
assert not wm.working_folder is None
|
||||
# Create a Wallet
|
||||
wallet_json = '{"label": "a_simple_wallet", "blockheight": 0, "descriptor": "wpkh([1ef4e492/84h/1h/0h]tpubDC5EUwdy9WWpzqMWKNhVmXdMgMbi4ywxkdysRdNr1MdM4SCfVLbNtsFvzY6WKSuzsaVAitj6FmP6TugPuNT6yKZDLsHrSwMd816TnqX7kuc/0/*)#xp8lv5nr", "devices": [{"type": "trezor", "label": "trezor"}]} '
|
||||
wallet_importer = WalletImporter(
|
||||
wallet_json, specter, device_manager=someuser.device_manager
|
||||
)
|
||||
dm = someuser.device_manager
|
||||
wallet_importer = WalletImporter(wallet_json, specter, device_manager=dm)
|
||||
wallet_importer.create_nonexisting_signers(
|
||||
someuser.device_manager,
|
||||
dm,
|
||||
{"unknown_cosigner_0_name": "trezor", "unknown_cosigner_0_type": "trezor"},
|
||||
)
|
||||
dm: DeviceManager = someuser.device_manager
|
||||
wallet = wallet_importer.create_wallet(someuser.wallet_manager)
|
||||
wallet = wallet_importer.create_wallet(wm)
|
||||
try:
|
||||
# fund it with some coins
|
||||
bitcoin_regtest.testcoin_faucet(address=wallet.getnewaddress())
|
||||
|
|
|
|||
|
|
@ -97,7 +97,6 @@ def test_abandon_purged_tx(
|
|||
rpc,
|
||||
"regtest",
|
||||
device_manager,
|
||||
allow_threading=False,
|
||||
)
|
||||
|
||||
# Create a new device that can sign psbts (Bitcoin Core hot wallet)
|
||||
|
|
|
|||
|
|
@ -511,9 +511,7 @@ def specter_regtest_configured(bitcoin_regtest, devices_filled_data_folder):
|
|||
# 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, bitcoin_datadir=bitcoin_regtest.datadir, chain="regtest"
|
||||
)
|
||||
user.wallet_manager.delete_wallet(wallet)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
|
|
|||
|
|
@ -470,9 +470,7 @@ def specter_regtest_configured(bitcoin_regtest, devices_filled_data_folder):
|
|||
# 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, bitcoin_datadir=bitcoin_regtest.datadir, chain="regtest"
|
||||
)
|
||||
user.wallet_manager.delete_wallet(wallet)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
|
|
|||
|
|
@ -470,9 +470,7 @@ def specter_regtest_configured(bitcoin_regtest, devices_filled_data_folder):
|
|||
# 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, bitcoin_datadir=bitcoin_regtest.datadir, chain="regtest"
|
||||
)
|
||||
user.wallet_manager.delete_wallet(wallet)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue