diff --git a/README.md b/README.md
index 62020e9e3..55d053dcc 100755
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
- [Specter Desktop](#specter-desktop)
- [DISCLAIMER](#disclaimer)
- - [Video Walkthrough](#video-walkthrough)
+ - [Documentation and Video Walkthrough](#documentation-and-video-walkthrough)
- [Why?](#why)
- [Help wanted: Do you like Specter?](#help-wanted-do-you-like-specter)
- [How to run](#how-to-run)
@@ -27,7 +27,6 @@
The Crypto Anarchist Manifesto - Timothy C. May - Sun, 22 Nov 92 12:11:24 PST
[](https://cirrus-ci.com/github/cryptoadvance/specter-desktop)
-[](https://donate.specter.solutions/apps/3k77BAT6zshCGNd3i7gw9WKwXQy1/pos)
## DISCLAIMER
@@ -35,8 +34,10 @@ This software might be ready to be used but at your own risk.
If something doesn't work open an issue here or ask a question in our [Telegram group](https://t.me/spectersupport).
-## Video Walkthrough
- [Watch here](https://www.youtube.com/watch?v=v3SEp0SkOWs)
+## Documentation and Video Walkthrough
+
+*  [Watch here](https://www.youtube.com/watch?v=v3SEp0SkOWs)
+* [documentation](https://docs.specter.solutions/desktop/)
## Why?
diff --git a/docs/development.md b/docs/development.md
index 41e5627d2..0e15b8f47 100644
--- a/docs/development.md
+++ b/docs/development.md
@@ -65,7 +65,7 @@ sudo yum -y install libusb libudev-devel libffi libffi-devel openssl-devel && su
```
#### Windows
-* Install python 3.8.x by downloading from [python.org](https://www.python.org/downloads/windows/)
+* Install python 3.10.x by downloading from [python.org](https://www.python.org/downloads/windows/)
_Do NOT install python from the Microsoft Store! It runs in a different execution environment that creates enormous headaches!_
@@ -85,8 +85,7 @@ sudo yum -y install libusb libudev-devel libffi libffi-devel openssl-devel && su
### Set up virtualenv
-Specter is using `hwi-2.1.0` which by now supports higher Python versions than Specter itself. Specter currently supports Python 3.7-3.9.
-If you have Python 3.10 as your global version then be sure to also install an old Python version and pass it to `virtualenv` (e.g. `virtualenv --python=python3.8 .env`) or use pyenv.
+Specter is using `hwi-2.1.0` which by now supports higher Python versions than Specter itself. Specter currently supports Python 3.9 and 3.10.
```sh
git clone https://github.com/cryptoadvance/specter-desktop.git
@@ -113,6 +112,7 @@ Run the server:
cd specter-desktop
python3 -m cryptoadvance.specter server --config DevelopmentConfig --debug
```
+After that, Specter will be available at http://127.0.0.1:25441/.
#### If `pip install` fails on `cryptography==3.4.x`
diff --git a/docs/faq.md b/docs/faq.md
index 9b1eaee04..a34f462a9 100644
--- a/docs/faq.md
+++ b/docs/faq.md
@@ -380,13 +380,13 @@ To check (before and/or afterwards) your installed version, you can use: `pip3 s
### Raspiblitz
You might want to wait until raspiblitz is providing an update. It takes longer but if you're not technically literate, that might be a better option. However, up from Version 1.6.1, Raspiblitz offers an update-possibility in the menu. Prior to that or as a part of troubleshooting-procedure, you can also do something like this:
```
-sudo su - bitcoin
-cd .specter/
+sudo su - specter
. ./.env/bin/activate
pip3 list | grep specter
pip3 install cryptoadvance.specter --upgrade
pip3 list | grep specter
-service cryptoadvance-specter restart
+exit
+sudo systemctl restart specter
```
### umbrel
diff --git a/package-lock.json b/package-lock.json
index 5e9cbb47a..a8a2a84b9 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3589,9 +3589,9 @@
}
},
"node_modules/minimatch": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
- "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dependencies": {
"brace-expansion": "^1.1.7"
},
@@ -8856,9 +8856,9 @@
"dev": true
},
"minimatch": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
- "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"requires": {
"brace-expansion": "^1.1.7"
}
diff --git a/pyinstaller/electron/package-lock.json b/pyinstaller/electron/package-lock.json
index d2dea0caa..1bd79874e 100644
--- a/pyinstaller/electron/package-lock.json
+++ b/pyinstaller/electron/package-lock.json
@@ -1410,9 +1410,9 @@
}
},
"node_modules/filelist/node_modules/minimatch": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz",
- "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==",
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.2.tgz",
+ "integrity": "sha512-bNH9mmM9qsJ2X4r2Nat1B//1dJVcn3+iBLa3IgqJ7EbGaDNepL9QSHOxN4ng33s52VMMhhIfgCYDk3C4ZmlDAg==",
"dev": true,
"dependencies": {
"brace-expansion": "^2.0.1"
@@ -1885,13 +1885,10 @@
"integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
},
"node_modules/json5": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz",
- "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==",
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
"dev": true,
- "dependencies": {
- "minimist": "^1.2.5"
- },
"bin": {
"json5": "lib/cli.js"
},
@@ -2079,9 +2076,9 @@
}
},
"node_modules/minimatch": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
- "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
"dependencies": {
"brace-expansion": "^1.1.7"
@@ -4488,9 +4485,9 @@
}
},
"minimatch": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz",
- "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==",
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.2.tgz",
+ "integrity": "sha512-bNH9mmM9qsJ2X4r2Nat1B//1dJVcn3+iBLa3IgqJ7EbGaDNepL9QSHOxN4ng33s52VMMhhIfgCYDk3C4ZmlDAg==",
"dev": true,
"requires": {
"brace-expansion": "^2.0.1"
@@ -4859,13 +4856,10 @@
"integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
},
"json5": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz",
- "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==",
- "dev": true,
- "requires": {
- "minimist": "^1.2.5"
- }
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "dev": true
},
"jsonfile": {
"version": "4.0.0",
@@ -5012,9 +5006,9 @@
"dev": true
},
"minimatch": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
- "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
"requires": {
"brace-expansion": "^1.1.7"
diff --git a/src/cryptoadvance/specter/server_endpoints/settings.py b/src/cryptoadvance/specter/server_endpoints/settings.py
index 735a9064c..5ddd1b94b 100644
--- a/src/cryptoadvance/specter/server_endpoints/settings.py
+++ b/src/cryptoadvance/specter/server_endpoints/settings.py
@@ -14,6 +14,7 @@ from pathlib import Path
import cryptography
import pgpy
import requests
+from cryptoadvance.specter.util.wallet_importer import WalletImporter
from flask import Blueprint, Flask
from flask import current_app as app
from flask import jsonify, redirect, render_template, request, send_file, url_for
@@ -29,7 +30,7 @@ from ..helpers import (
from ..persistence import write_devices, write_wallet
from ..server_endpoints import flash
from ..services.service import callbacks
-from ..specter_error import handle_exception
+from ..specter_error import SpecterError, handle_exception
from ..user import UserSecretException
from ..util.sha256sum import sha256sum
from ..util.shell import get_last_lines_from_file
@@ -149,74 +150,58 @@ def general():
app.specter.device_manager.update()
rescanning = False
+ logger.info(f"Importing {len(restore_wallets)} wallets ...")
+ counter = {
+ "success": 0,
+ "specific_error": 0,
+ "unspecific_error": 0,
+ "rescan_error": 0,
+ }
for wallet in restore_wallets:
- try:
- app.specter.wallet_manager.rpc.createwallet(
- os.path.join(
- app.specter.wallet_manager.rpc_path, wallet["alias"]
- ),
- True,
- )
- except Exception as e:
- # if wallet already exists in Bitcoin Core
- # continue with the existing one
- if "already exists" not in str(e):
- flash(
- _("Failed to import wallet {}, error: {}").format(
- wallet["name"], e
- ),
- "error",
- )
- handle_exception(e)
- continue
- logger.debug(
- f"Wallet {wallet['alias']} already exists, skipping creation"
- )
- write_wallet(wallet)
- app.specter.wallet_manager.update(use_threading=False)
try:
- wallet_obj = app.specter.wallet_manager.get_by_alias(
- wallet["alias"]
+ logger.info(f"Importing wallet {wallet['name']}")
+ wallet_importer = WalletImporter(json.dumps(wallet), app.specter)
+ wallet_importer.create_wallet(app.specter.wallet_manager)
+ wallet_importer.rescan_as_needed(app.specter)
+ counter["success"] += 1
+ except SpecterError as se:
+ error_type = (
+ "rescan_error" if "rescan" in str(se) else "specific_error"
)
- wallet_obj.keypoolrefill(0, wallet_obj.IMPORT_KEYPOOL, change=False)
- wallet_obj.keypoolrefill(0, wallet_obj.IMPORT_KEYPOOL, change=True)
- wallet_obj.import_labels(wallet.get("labels", {}))
- try:
- wallet_obj.rpc.rescanblockchain(
- wallet["blockheight"]
- if "blockheight" in wallet
- else get_startblock_by_chain(app.specter),
- no_wait=True,
- )
- app.logger.info("Rescanning Blockchain ...")
- rescanning = True
- except Exception as e:
- app.logger.exception(
- "Exception while rescanning blockchain for wallet {}: {}".format(
- wallet["alias"], e
- ),
- e,
- )
- flash(
- _("Failed to perform rescan for wallet: {}").format(e),
- "error",
- )
- wallet_obj.getdata()
+ flash(f"Wallet '{wallet.get('name')}': {se} (skipped)", "error")
+ counter[error_type] += 1
except Exception as e:
flash(
- _("Failed to import wallet {}").format(wallet["name"]), "error"
+ f"Error while importing wallet {wallet['name']}, check logs for details! (skipped)",
+ "error",
)
- handle_exception(e)
- flash(_("Specter data was successfully loaded from backup"), "info")
+ logger.exception(
+ f"Error while importing wallet {wallet['name']}: {e}"
+ )
+ counter["unspecific_error"] += 1
+
+ counter["errors_sum"] = (
+ counter["rescan_error"]
+ + counter["specific_error"]
+ + counter["unspecific_error"]
+ )
+ if counter["success"] > 0:
+ message = f"{counter['success']} wallets successfully restored. "
+
+ else:
+ message = f"Sorry, this doesn't went that well. "
+ if counter["errors_sum"] > 0:
+ message += "however, we had " if counter["success"] > 0 else "We had "
+ message += f"""
+ Successful imports: {counter["success"]}
+ Specific errors: {counter["specific_error"]}
+ Unspecific errors: {counter["unspecific_error"]} (create an issue about it on github)
+ Rescan issues: {counter["rescan_error"]}
+ """
if rescanning:
- flash(
- _(
- "Wallets are rescanning for transactions history.\n\
-This may take a few hours to complete."
- ),
- "info",
- )
+ message += "Wallets are rescanning for transactions history. This may take a few hours to complete."
+ flash(message, "info")
return render_template(
"settings/general_settings.jinja",
diff --git a/src/cryptoadvance/specter/templates/settings/general_settings.jinja b/src/cryptoadvance/specter/templates/settings/general_settings.jinja
index edbc6b706..82a3a20ac 100644
--- a/src/cryptoadvance/specter/templates/settings/general_settings.jinja
+++ b/src/cryptoadvance/specter/templates/settings/general_settings.jinja
@@ -197,13 +197,21 @@
reader.onload = function(e) {
if (files[i].webkitRelativePath.indexOf('devices/') != -1) {
console.log(files[i].name);
- devices.push(JSON.parse(reader.result));
+ try {
+ devices.push(JSON.parse(reader.result));
+ } catch {
+ showError(`Could not load Device ${files[i].name}: not valid json! This Device will be skipped for import which might affect corresponding Wallets.`)
+ }
document.getElementById('restoredevices').value = JSON.stringify(devices);
document.getElementById('filesloaded').innerHTML = `
Found ${devices.length} devices and ${wallets.length} wallets:`;
document.getElementById('restore').style.display = 'block';
} else if (files[i].webkitRelativePath.indexOf('wallets/') != -1) {
console.log(files[i].name);
- wallets.push(JSON.parse(reader.result));
+ try {
+ wallets.push(JSON.parse(reader.result));
+ } catch {
+ showError(`Could not load Wallet ${files[i].name}: not valid json! This wallet will be skipped for import.`)
+ }
document.getElementById('restorewallets').value = JSON.stringify(wallets);
document.getElementById('filesloaded').innerHTML = `
Found ${devices.length} devices and ${wallets.length} wallets:`;
document.getElementById('restore').style.display = 'block';
diff --git a/src/cryptoadvance/specter/util/wallet_importer.py b/src/cryptoadvance/specter/util/wallet_importer.py
index 6651c6dee..e3e406e1d 100644
--- a/src/cryptoadvance/specter/util/wallet_importer.py
+++ b/src/cryptoadvance/specter/util/wallet_importer.py
@@ -50,8 +50,8 @@ class WalletImporter:
try:
self.descriptor = DescriptorCls.from_string(recv_descriptor)
self.check_descriptor()
- except Exception as e:
- raise SpecterError(f"Invalid wallet descriptor: {e}")
+ except ValueError as e:
+ raise SpecterError(f"{e}: {recv_descriptor}")
if self.wallet_name in specter.wallet_manager.wallets_names:
raise SpecterError(f"Wallet with the same name already exists")
(
@@ -242,7 +242,7 @@ class WalletImporter:
)
except Exception as e:
logger.exception(e)
- raise SpecterError(f"Failed to create wallet: {e}")
+ raise SpecterError(f"Failed to create wallet: {e} (check logs for details)")
logger.info(f"Created Wallet {self.wallet}")
self.wallet.keypoolrefill(0, self.wallet.IMPORT_KEYPOOL, change=False)
self.wallet.keypoolrefill(0, self.wallet.IMPORT_KEYPOOL, change=True)
@@ -333,7 +333,7 @@ class WalletImporter:
wallet_data["x1/"]["xpub"]
)
else:
- raise Exception('"xpub" not found in "x1/" in Electrum backup json')
+ raise SpecterError('"xpub" not found in "x1/" in Electrum backup json')
required_sigs = int(wallet_data.get("wallet_type").split("of")[0])
recv_descriptor = "{}(sortedmulti({},{}))".format(
@@ -482,7 +482,7 @@ class WalletImporter:
wallet_data["keystore"]["xpub"], is_multisig=False
)
else:
- raise Exception(
+ raise SpecterError(
'"xpub" not found in "keystore" in Electrum backup json'
)
recv_descriptor = "{}({})".format(
@@ -517,6 +517,13 @@ class WalletImporter:
wallet_name = wallet_data.get("label", "Imported Wallet")
recv_descriptor = wallet_data.get("descriptor", None)
+ if wallet_name is None:
+ raise SpecterError(
+ f"Couldn't find 'name' in wallet json (alias: {wallet_data.get('alias','also not existing')})."
+ )
+ if recv_descriptor is None:
+ raise SpecterError("Couldn't find 'recv_descriptor' in wallet json.")
+
return (wallet_name, recv_descriptor, cosigners_types)
@classmethod
diff --git a/src/cryptoadvance/specter/wallet.py b/src/cryptoadvance/specter/wallet.py
index 3e7037ffe..fba8f08b1 100644
--- a/src/cryptoadvance/specter/wallet.py
+++ b/src/cryptoadvance/specter/wallet.py
@@ -1454,6 +1454,7 @@ class Wallet:
else self.rpc.getbalances()["watchonly"]
)
except Exception as e:
+ handle_exception(e)
raise SpecterError(f"was not able to get wallet_balance because {e}")
self.balance = balance
return self.balance