diff --git a/src/cryptoadvance/specter/controller.py b/src/cryptoadvance/specter/controller.py index 7e908ac8b..31c3895fa 100644 --- a/src/cryptoadvance/specter/controller.py +++ b/src/cryptoadvance/specter/controller.py @@ -277,7 +277,7 @@ def login(): # TODO: This way both "User" and "user" will pass as usernames, should there be strict check on that here? Or should we keep it like this? username = request.form["username"] password = request.form["password"] - user = User.get_user_by_name(app.specter, username) + user = app.specter.user_manager.get_user_by_username(username) if user: if verify_password(user.password, password): app.login(user.id) @@ -320,9 +320,7 @@ def register(): password = hash_password(request.form["password"]) otp = request.form["otp"] user_id = alias(username) - if User.get_user(app.specter, user_id) or User.get_user_by_name( - app.specter, username - ): + if app.specter.user_manager.get_user(user_id): flash("Username is already taken, please choose another one", "error") return redirect("register?otp={}".format(otp)) if app.specter.burn_new_user_otp(otp): @@ -336,7 +334,7 @@ def register(): "hwi_bridge_url": "/hwi/api/", } user = User(user_id, username, password, config) - user.save_info(app.specter) + app.specter.add_user(user) flash( "You have registered successfully, \ please login with your new account to start using Specter" @@ -356,7 +354,6 @@ please request a new link from the node operator.", def logout(): logout_user() flash("You were logged out", "info") - app.specter.clear_user_session() return redirect("login") @@ -592,7 +589,7 @@ def auth_settings(): new_otp = -1 users = None if current_user.is_admin and auth == "usernamepassword": - users = [user for user in User.get_all_users(app.specter) if not user.is_admin] + users = [user for user in app.specter.user_manager.users if not user.is_admin] if request.method == "POST": action = request.form["action"] @@ -607,7 +604,7 @@ def auth_settings(): auth = request.form["auth"] if specter_username: if current_user.username != specter_username: - if User.get_user_by_name(app.specter, specter_username): + if app.specter.user_manager.get_user(specter_username): flash( "Username is already taken, please choose another one", "error", @@ -631,7 +628,7 @@ def auth_settings(): if auth == "usernamepassword": users = [ user - for user in User.get_all_users(app.specter) + for user in app.specter.user_manager.users if not user.is_admin ] else: @@ -661,23 +658,11 @@ def auth_settings(): ) elif action == "deleteuser": delete_user = request.form["deleteuser"] + user = app.specter.user_manager.get_user(delete_user) if current_user.is_admin: - user = User.get_user(app.specter, delete_user) - if user: - user.delete(app.specter) - users = [ - user - for user in User.get_all_users(app.specter) - if not user.is_admin - ] - flash( - "User {} was deleted successfully".format(user.username), "info" - ) - else: - flash( - "Error: failed to delete user, invalid user ID was given", - "error", - ) + app.specter.delete_user(user) + users.remove(user) + flash("User {} was deleted successfully".format(user.username), "info") else: flash("Error: Only the admin account can delete users", "error") return render_template( @@ -979,7 +964,7 @@ def new_wallet(wallet_type): if "use_explorer" in request.form: explorer = app.specter.get_default_explorer() wallet.rescanutxo(explorer) - app.specter._info["utxorescan"] = 1 + app.specter.info["utxorescan"] = 1 app.specter.utxorescanwallet = wallet.alias else: app.logger.info("Rescanning Blockchain ...") @@ -1103,7 +1088,7 @@ def singlesig_setup_wizard(): if "use_explorer" in request.form: explorer = app.specter.get_default_explorer() wallet.rescanutxo(explorer) - app.specter._info["utxorescan"] = 1 + app.specter.info["utxorescan"] = 1 app.specter.utxorescanwallet = wallet.alias return redirect(url_for("wallet", wallet_alias=wallet.alias)) return render_template( @@ -1218,12 +1203,12 @@ def txout_set_info(): @login_required def get_scantxoutset_status(): status = app.specter.rpc.scantxoutset("status", []) - app.specter._info["utxorescan"] = status.get("progress", None) if status else None - if app.specter._info["utxorescan"] is None: + app.specter.info["utxorescan"] = status.get("progress", None) if status else None + if app.specter.info["utxorescan"] is None: app.specter.utxorescanwallet = None return { - "active": app.specter._info["utxorescan"] is not None, - "progress": app.specter._info["utxorescan"], + "active": app.specter.info["utxorescan"] is not None, + "progress": app.specter.info["utxorescan"], } @@ -1513,11 +1498,11 @@ def wallet_settings(wallet_alias): if "use_explorer" in request.form: explorer = app.specter.get_default_explorer() wallet.rescanutxo(explorer) - app.specter._info["utxorescan"] = 1 + app.specter.info["utxorescan"] = 1 app.specter.utxorescanwallet = wallet.alias elif action == "abortrescanutxo": app.specter.abortrescanutxo() - app.specter._info["utxorescan"] = None + app.specter.info["utxorescan"] = None app.specter.utxorescanwallet = None elif action == "keypoolrefill": delta = int(request.form["keypooladd"]) diff --git a/src/cryptoadvance/specter/device.py b/src/cryptoadvance/specter/device.py index 1f69853ca..883aabe98 100644 --- a/src/cryptoadvance/specter/device.py +++ b/src/cryptoadvance/specter/device.py @@ -18,6 +18,7 @@ class Device: hwi_support = False supports_hwi_toggle_passphrase = False supports_hwi_multisig_display_address = False + hot_wallet = False def __init__(self, name, alias, keys, fullpath, manager): """ diff --git a/src/cryptoadvance/specter/device_manager.py b/src/cryptoadvance/specter/device_manager.py index 08f599b6b..ef968767d 100644 --- a/src/cryptoadvance/specter/device_manager.py +++ b/src/cryptoadvance/specter/device_manager.py @@ -6,7 +6,7 @@ from .rpc import get_default_datadir from .devices import __all__ as device_classes from .devices.generic import GenericDevice # default device type -from .persistence import write_device, delete_json_file +from .persistence import write_device, delete_json_file, delete_folder logger = logging.getLogger(__name__) @@ -95,3 +95,10 @@ class DeviceManager: @property def supported_devices(self): return device_classes + + def delete(self, specter): + """Deletes all the devices""" + for d in self.devices: + device = self.devices[d] + self.remove_device(device) + delete_folder(self.data_folder) diff --git a/src/cryptoadvance/specter/persistence.py b/src/cryptoadvance/specter/persistence.py index 77f71228d..c8f97929e 100644 --- a/src/cryptoadvance/specter/persistence.py +++ b/src/cryptoadvance/specter/persistence.py @@ -9,6 +9,7 @@ import logging from contextlib import nullcontext from flask import current_app as app from .util.shell import run_shell +import shutil logger = logging.getLogger(__name__) diff --git a/src/cryptoadvance/specter/server.py b/src/cryptoadvance/specter/server.py index b3a3e8817..e4de11ec8 100644 --- a/src/cryptoadvance/specter/server.py +++ b/src/cryptoadvance/specter/server.py @@ -66,7 +66,7 @@ def init_app(app, hwibridge=False, specter=None): @login_manager.user_loader def user_loader(id): - return User.get_user(specter, id) + return specter.user_manager.get_user(id) def login(id): login_user(user_loader(id)) diff --git a/src/cryptoadvance/specter/specter.py b/src/cryptoadvance/specter/specter.py index 5ce569158..3b47d959c 100644 --- a/src/cryptoadvance/specter/specter.py +++ b/src/cryptoadvance/specter/specter.py @@ -13,8 +13,9 @@ from requests.exceptions import ConnectionError from .rpc import BitcoinRPC from .device_manager import DeviceManager from .wallet_manager import WalletManager +from .user_manager import UserManager from .persistence import write_json_file, read_json_file -from flask_login import current_user +from .user import User import threading logger = logging.getLogger(__name__) @@ -66,7 +67,6 @@ def get_rpc(conf, old_rpc=None): class Specter: """ A central Object mostly holding app-settings """ - CONFIG_FILE_NAME = "config.json" # use this lock for all fs operations lock = threading.Lock() @@ -74,14 +74,21 @@ class Specter: if data_folder.startswith("~"): data_folder = os.path.expanduser(data_folder) data_folder = os.path.abspath(data_folder) + + # creating folders if they don't exist + if not os.path.isdir(data_folder): + os.makedirs(data_folder) + self.data_folder = data_folder + self.rpc = None - self.device_manager = None - self.wallet_manager = None - self._current_version = None + self.user_manager = UserManager(self) self.file_config = None # what comes from config file self.arg_config = config # what comes from arguments + + # wallet that is currently rescnning with utxorescan + # can be only one at a time self.utxorescanwallet = None # default config @@ -105,42 +112,36 @@ class Specter: "validate_merkle_proofs": False, } - # creating folders if they don't exist - if not os.path.isdir(data_folder): - os.makedirs(data_folder) + # health check: loads config, tests rpc + # also loads and checks wallets for all users + self.check(check_all=True) - self.is_checking = False - # health check: loads config and tests rpc - self.check() - - @property - def bitcoin_datadir(self): - if "datadir" in self.config["rpc"]: - return os.path.expanduser(self.config["rpc"]["datadir"]) - return get_default_datadir() - - def check(self, user=current_user): - # if config.json file exists - load from it - if os.path.isfile(os.path.join(self.data_folder, "config.json")): - with self.lock: - self.file_config = read_json_file( - os.path.join(self.data_folder, "config.json") - ) - deep_update(self.config, self.file_config) - # otherwise - create one and assign unique id - else: - if self.config["uid"] == "": - self.config["uid"] = ( - random.randint(0, 256 ** 8).to_bytes(8, "big").hex() - ) - self._save() - - # init arguments - deep_update(self.config, self.arg_config) # override loaded config + def check(self, user=None, check_all=False): + """ + Checks and updates everything for a particular user: + - config if changed + - rpc including check if it's connected + - node info + - wallet manager + - device manager + """ + # find proper user + user = self.user_manager.get_user(user) + # check if config file have changed + self.check_config() # update rpc if something doesn't work if self.rpc is None or not self.rpc.test_connection(): self.rpc = get_rpc(self.config["rpc"], self.rpc) + + self.check_node_info() + if not check_all: + self.check_for_user(user) + else: + for u in self.user_manager.users: + self.check_for_user(u) + + def check_node_info(self): self._is_configured = self.rpc is not None self._is_running = False if self._is_configured: @@ -187,47 +188,106 @@ class Specter: if not self._is_running: self._info["chain"] = None - chain = self._info["chain"] - if hasattr(user, "is_admin"): - user_folder_id = "_" + user.id if user and not user.is_admin else "" - else: - user_folder_id = "" + def get_user_folder_id(self, user=None): + """ + Returns the suffix for the user wallets and devices. + User can be either a flask_login user or a string. + """ + user = self.user_manager.get_user(user) + if not user.is_admin: + return "_" + user.id + return "" - if self.config["auth"] != "usernamepassword" or ( - user and not user.is_anonymous + def check_wallet_manager(self, user=None): + """Updates wallet manager for a particular user""" + user = self.user_manager.get_user(user) + wallets_rpcpath = "specter%s" % self.config["uid"] + wallets_folder = os.path.join(self.data_folder, f"wallets{user.folder_id}") + # if chain, user or data folder changed + wallet_manager = user.wallet_manager + if ( + wallet_manager is None + or wallet_manager.data_folder != self.data_folder + or wallet_manager.rpc_path != wallets_rpcpath + or wallet_manager.chain != self.chain ): - if self.device_manager is None: - self.device_manager = DeviceManager( - os.path.join(self.data_folder, "devices{}".format(user_folder_id)) - ) - else: - self.device_manager.update( - data_folder=os.path.join( - self.data_folder, "devices{}".format(user_folder_id) - ) - ) + wallet_manager = WalletManager( + wallets_folder, + self.rpc, + self.chain, + self.device_manager, + path=wallets_rpcpath, + ) + user.wallet_manager = wallet_manager + else: + wallet_manager.update(wallets_folder, self.rpc, chain=self.chain) - wallets_path = "specter%s" % self.config["uid"] - # if chain, user or data folder changed - if ( - self.wallet_manager is None - or self.wallet_manager.data_folder != self.data_folder - or self.wallet_manager.rpc_path != wallets_path - or self.wallet_manager.chain != chain - ): - self.wallet_manager = WalletManager( - os.path.join(self.data_folder, "wallets{}".format(user_folder_id)), - self.rpc, - chain, - self.device_manager, - path=wallets_path, - ) - else: - self.wallet_manager.update( - os.path.join(self.data_folder, "wallets{}".format(user_folder_id)), - self.rpc, - chain=chain, + def check_device_manager(self, user=None): + """Updates device manager for a particular user""" + user = self.user_manager.get_user(user) + devices_folder = os.path.join(self.data_folder, f"devices{user.folder_id}") + device_manager = user.device_manager + if device_manager is None: + device_manager = DeviceManager(devices_folder) + user.device_manager = device_manager + else: + device_manager.update(data_folder=devices_folder) + + def check_config(self): + """ + Updates config if file config have changed. + Priority (low to high): + - existing / default config + - file config from config.json + - arg_config passed in constructor + """ + + # if config.json file exists - load from it + if os.path.isfile(self.config_fname): + with self.lock: + self.file_config = read_json_file(self.config_fname) + deep_update(self.config, self.file_config) + # otherwise - create one and assign unique id + else: + # unique id of specter + if self.config["uid"] == "": + self.config["uid"] = ( + random.randint(0, 256 ** 8).to_bytes(8, "big").hex() ) + self._save() + + # config from constructor overrides file config + deep_update(self.config, self.arg_config) + + def check_for_user(self, user=None): + """ + Performs device and wallet manager check for particular user + """ + user = self.user_manager.get_user(user) + self.check_device_manager(user) + self.check_wallet_manager(user) + + def add_user(self, user): + if user in self.user_manager.users: + return + user.wallet_manager = None + user.device_manager = None + self.user_manager.add_user(user) + self.check_for_user(user) + + def delete_user(self, user): + if user not in self.user_manager.users: + return + user = self.user_manager.get_user(user) + user.wallet_manager.delete(self) + user.device_manager.delete(self) + self.user_manager.delete_user(user) + + @property + def bitcoin_datadir(self): + if "datadir" in self.config["rpc"]: + return os.path.expanduser(self.config["rpc"]["datadir"]) + return get_default_datadir() def abortrescanutxo(self): self.rpc.scantxoutset("abort", []) @@ -236,10 +296,6 @@ class Specter: self._info["utxorescan"] = None self.utxorescanwallet = None - def clear_user_session(self): - self.device_manager = None - self.wallet_manager = None - def test_rpc(self, **kwargs): conf = copy.deepcopy(self.config["rpc"]) conf.update(kwargs) @@ -297,10 +353,14 @@ class Specter: def _save(self): write_json_file( self.config, - os.path.join(self.data_folder, self.CONFIG_FILE_NAME), + self.config_fname, lock=self.lock, ) + @property + def config_fname(self): + return os.path.join(self.data_folder, "config.json") + def update_rpc(self, **kwargs): need_update = False for k in kwargs: @@ -310,7 +370,7 @@ class Specter: if need_update: self.rpc = get_rpc(self.config["rpc"], None) self._save() - self.check() + self.check(check_all=True) return self.rpc is not None def update_auth(self, auth): @@ -321,6 +381,7 @@ class Specter: def update_explorer(self, explorer, user): """ update the block explorers urls """ + user = self.user_manager.get_user(user) # we don't know what chain to change if not self.chain: return @@ -337,6 +398,7 @@ class Specter: def update_hwi_bridge_url(self, url, user): """ update the hwi bridge url to use """ + user = self.user_manager.get_user(user) if url and not url.endswith("/"): # make sure the urls end with a "/" url += "/" @@ -351,21 +413,21 @@ class Specter: if not url.endswith("/hwi/api/"): url += "hwi/api/" - if user.id == "admin": + if user.is_admin: self.config["hwi_bridge_url"] = url self._save() else: user.set_hwi_bridge_url(self, url) def update_unit(self, unit, user): - if user.id == "admin": + if user.is_admin: self.config["unit"] = unit self._save() else: user.set_unit(self, unit) def update_merkleproof_settings(self, validate_bool): - if validate_bool is True and self._info.get("pruned") is True: + if validate_bool is True and self.info.get("pruned") is True: validate_bool = False logger.warning("Cannot enable merkleproof setting on pruned node.") @@ -449,50 +511,39 @@ class Specter: def chain(self): return self._info["chain"] + @property + def user_config(self): + return self.config if self.user.is_admin else self.user.config + @property def explorer(self): - # TODO: Unify for user and admin - if (not current_user or current_user.is_anonymous) or current_user.is_admin: - if "explorers" in self.config and self.chain in self.config["explorers"]: - return self.config["explorers"][self.chain] - else: - return "" - else: - if ( - "explorers" in current_user.config - and self.chain in current_user.config["explorers"] - ): - return current_user.config["explorers"][self.chain] - else: - return "" + return self.user_config.get("explorers", {}).get(self.chain, "") @property def hwi_bridge_url(self): - # TODO: Unify for user and admin - if (not current_user or current_user.is_anonymous) or current_user.is_admin: - if "hwi_bridge_url" in self.config: - return self.config["hwi_bridge_url"] - else: - return "" - else: - if "hwi_bridge_url" in current_user.config: - return current_user.config["hwi_bridge_url"] - else: - return "" + return self.user_config.get("hwi_bridge_url", "") @property def unit(self): - # TODO: Unify for user and admin - if (not current_user or current_user.is_anonymous) or current_user.is_admin: - if "unit" in self.config: - return self.config["unit"] - else: - return "btc" - else: - if "unit" in current_user.config: - return current_user.config["unit"] - else: - return "btc" + return self.user_config.get("unit", "btc") + + @property + def admin(self): + for u in self.users: + if u.is_admin: + return u + + @property + def user(self): + return self.user_manager.user + + @property + def device_manager(self): + return self.user.device_manager + + @property + def wallet_manager(self): + return self.user.wallet_manager def specter_backup_file(self): memory_file = BytesIO() diff --git a/src/cryptoadvance/specter/templates/wallet/history/components/total_wallet_balances.jinja b/src/cryptoadvance/specter/templates/wallet/history/components/total_wallet_balances.jinja index 9ade856b8..5fd0e5e6c 100644 --- a/src/cryptoadvance/specter/templates/wallet/history/components/total_wallet_balances.jinja +++ b/src/cryptoadvance/specter/templates/wallet/history/components/total_wallet_balances.jinja @@ -22,7 +22,7 @@ {% if wallet.rescan_progress or specter.utxorescanwallet == wallet.alias %} -

Rescanning blockchain: {{ "%.2f"|format(specter._info["utxorescan"] if specter.utxorescanwallet == wallet.alias else wallet.rescan_progress * 100) }}%

+

Rescanning blockchain: {{ "%.2f"|format(specter.info["utxorescan"] if specter.utxorescanwallet == wallet.alias else wallet.rescan_progress * 100) }}%


Total balance and transactions history may show outdated data during scanning.
Please wait until the scanning is complete before you start using the wallet. diff --git a/src/cryptoadvance/specter/user.py b/src/cryptoadvance/specter/user.py index 0a3ebf55a..f4b679c7b 100644 --- a/src/cryptoadvance/specter/user.py +++ b/src/cryptoadvance/specter/user.py @@ -33,29 +33,6 @@ def verify_password(stored_password, provided_password): return pwdhash == binascii.a2b_base64(stored_password["pwdhash"]) -def get_users_json(specter): - users = [ - { - "id": "admin", - "username": "admin", - "password": hash_password("admin"), - "is_admin": True, - } - ] - - # if users.json file exists - load from it - if os.path.isfile(os.path.join(specter.data_folder, "users.json")): - users = read_json_file(os.path.join(specter.data_folder, "users.json")) - # otherwise - create one and assign unique id - else: - save_users_json(specter, users) - return users - - -def save_users_json(specter, users): - write_json_file(users, os.path.join(specter.data_folder, "users.json")) - - class User(UserMixin): def __init__(self, id, username, password, config, is_admin=False): self.id = id @@ -63,6 +40,15 @@ class User(UserMixin): self.password = password self.config = config self.is_admin = is_admin + self.wallet_manager = None + self.device_manager = None + self.manager = None + + @property + def folder_id(self): + if self.is_admin: + return "" + return f"_{self.id}" @classmethod def from_json(cls, user_dict): @@ -86,31 +72,6 @@ class User(UserMixin): except: raise SpecterError("Unable to parse user JSON.") - @classmethod - def get_user(cls, specter, id): - users = get_users_json(specter) - for user_dict in users: - user = User.from_json(user_dict) - if user.id == id: - return user - - @classmethod - def get_user_by_name(cls, specter, username): - users = get_users_json(specter) - for user_dict in users: - user = User.from_json(user_dict) - if user.username == username: - return user - - @classmethod - def get_all_users(cls, specter): - users_dicts = get_users_json(specter) - users = [] - for user_dict in users_dicts: - user = User.from_json(user_dict) - users.append(user) - return users - @property def json(self): user_dict = { @@ -124,20 +85,17 @@ class User(UserMixin): return user_dict def save_info(self, specter, delete=False): - users = get_users_json(specter) - existing = False - for i in range(len(users)): - if users[i]["id"] == self.id: - if not delete: - users[i] = self.json - existing = True - else: - del users[i] - break - if not existing and not delete: - users.append(self.json) + if self.manager is None: + self.manager = specter.user_manager + users = self.manager.users + existing = self in users - save_users_json(specter, users) + # update specter users + if not existing and not delete: + specter.add_user(self) + if existing and delete: + specter.delete_user(self) + self.manager.save() def set_explorer(self, specter, explorer): self.config["explorers"][specter.chain] = explorer @@ -152,12 +110,23 @@ class User(UserMixin): self.save_info(specter) def delete(self, specter): - devices_datadir_path = os.path.join( - os.path.join(specter.data_folder, "devices_{}".format(self.id)) - ) - wallets_datadir_path = os.path.join( - os.path.join(specter.data_folder, "wallets_{}".format(self.id)) - ) - delete_folder(devices_datadir_path) - delete_folder(wallets_datadir_path) + # we delete wallet manager and device manager in save_info self.save_info(specter, delete=True) + + def __eq__(self, other): + if isinstance(other, str): + return self.id == other + return self.id == other.id + + def __ne__(self, other): + return not self.__eq__(other) + + def __hash__(self): + # to make lookups in dicts by user id + return hash(self.id) + + def __str__(self): + return self.id + + def __repr__(self): + return f"User({self.id})" diff --git a/src/cryptoadvance/specter/user_manager.py b/src/cryptoadvance/specter/user_manager.py new file mode 100644 index 000000000..7daa511ba --- /dev/null +++ b/src/cryptoadvance/specter/user_manager.py @@ -0,0 +1,105 @@ +import os +import json +import logging +from .helpers import alias, load_jsons, fslock +from .persistence import ( + read_json_file, + write_json_file, + delete_json_file, + delete_folder, +) +from .user import User, hash_password +from flask_login import current_user + +logger = logging.getLogger(__name__) + + +class UserManager: + """ + The UserManager can manage users + """ + + # of them via json-files in an empty data folder + def __init__(self, specter): + self.specter = specter + self.data_folder = specter.data_folder + self.update() + + @property + def users_file(self): + return os.path.join(self.data_folder, "users.json") + + def load_users(self): + # if users.json file exists - load from it + if os.path.isfile(self.users_file): + users = read_json_file(self.users_file) + # otherwise - create one and assign unique id + else: + users = [ + { + "id": "admin", + "username": "admin", + "password": hash_password("admin"), + "is_admin": True, + } + ] + # convert to User instances + self.users = [User.from_json(u) for u in users] + if not os.path.isfile(self.users_file): + self.save() + + def update(self): + self.load_users() + + def save(self): + users_json = [u.json for u in self.users] + write_json_file(users_json, self.users_file) + + def add_user(self, user): + if user not in self.users: + self.users.append(user) + user.manager = self + self.save() # save files + return self.get_user(user) + + @property + def admin(self): + """There is always one admin""" + for u in self.users: + if u.is_admin: + return u + + def get_user(self, user=None): + """ + Converts from flask_login user to a User in the system. + Admin by default. + """ + # get by string + if user is None: + user = current_user + if isinstance(user, str): + return self.get_by_uid(user) + if user and not user.is_anonymous: + if user in self.users: + return self.get_by_uid(user.id) + return self.admin + + def get_user_by_username(self, username): + for u in self.users: + if u.username == username: + return u + + @property + def user(self): + """User in current context, admin if no context""" + return self.get_user(current_user) + + def get_by_uid(self, uid): + for u in self.users: + if u.id == uid: + return u + logger.error("Could not find user %s" % uid) + + def delete_user(self, user): + self.users.remove(user) + self.save() diff --git a/src/cryptoadvance/specter/wallet_manager.py b/src/cryptoadvance/specter/wallet_manager.py index 35660d236..9a43423b0 100644 --- a/src/cryptoadvance/specter/wallet_manager.py +++ b/src/cryptoadvance/specter/wallet_manager.py @@ -6,7 +6,7 @@ from .helpers import alias, load_jsons from .rpc import get_default_datadir, RpcError from .specter_error import SpecterError from .wallet import Wallet -from .persistence import delete_json_file +from .persistence import delete_json_file, delete_folder logger = logging.getLogger() @@ -287,3 +287,10 @@ Silently ignored!" for tx in txlist: result.append(tx) return list(reversed(sorted(result, key=lambda tx: tx["time"]))) + + def delete(self, specter): + """Deletes all the wallets""" + for w in self.wallets: + wallet = self.wallets[w] + self.delete_wallet(wallet, specter.bitcoin_datadir, specter.chain) + delete_folder(self.data_folder)